Commit 2dc47640 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'tools-ynl-user-space-c'

Jakub Kicinski says:

====================
tools: ynl: user space C

Use the code gen which is already in tree to generate a user space
library for a handful of simple families. I find YNL C quite useful
in some WIP projects, and I think others may find it useful, too.
I was hoping someone will pick this work up and finish it...
but it seems that Python YNL has largely stolen the thunder.
Python may not be great for selftest, tho, and actually this lib
is more fully-featured. The Python script was meant as a quick demo,
funny how those things go.

v2: https://lore.kernel.org/all/20230604175843.662084-1-kuba@kernel.org/
v1: https://lore.kernel.org/all/20230603052547.631384-1-kuba@kernel.org/
====================

Link: https://lore.kernel.org/r/20230605190108.809439-1-kuba@kernel.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ae91f7e4 ee0202e2
......@@ -78,3 +78,82 @@ to see other examples.
The code generation itself is performed by ``tools/net/ynl/ynl-gen-c.py``
but it takes a few arguments so calling it directly for each file
quickly becomes tedious.
YNL lib
=======
``tools/net/ynl/lib/`` contains an implementation of a C library
(based on libmnl) which integrates with code generated by
``tools/net/ynl/ynl-gen-c.py`` to create easy to use netlink wrappers.
YNL basics
----------
The YNL library consists of two parts - the generic code (functions
prefix by ``ynl_``) and per-family auto-generated code (prefixed
with the name of the family).
To create a YNL socket call ynl_sock_create() passing the family
struct (family structs are exported by the auto-generated code).
ynl_sock_destroy() closes the socket.
YNL requests
------------
Steps for issuing YNL requests are best explained on an example.
All the functions and types in this example come from the auto-generated
code (for the netdev family in this case):
.. code-block:: c
// 0. Request and response pointers
struct netdev_dev_get_req *req;
struct netdev_dev_get_rsp *d;
// 1. Allocate a request
req = netdev_dev_get_req_alloc();
// 2. Set request parameters (as needed)
netdev_dev_get_req_set_ifindex(req, ifindex);
// 3. Issues the request
d = netdev_dev_get(ys, req);
// 4. Free the request arguments
netdev_dev_get_req_free(req);
// 5. Error check (the return value from step 3)
if (!d) {
// 6. Print the YNL-generated error
fprintf(stderr, "YNL: %s\n", ys->err.msg);
return -1;
}
// ... do stuff with the response @d
// 7. Free response
netdev_dev_get_rsp_free(d);
YNL dumps
---------
Performing dumps follows similar pattern as requests.
Dumps return a list of objects terminated by a special marker,
or NULL on error. Use ``ynl_dump_foreach()`` to iterate over
the result.
YNL notifications
-----------------
YNL lib supports using the same socket for notifications and
requests. In case notifications arrive during processing of a request
they are queued internally and can be retrieved at a later time.
To subscribed to notifications use ``ynl_subscribe()``.
The notifications have to be read out from the socket,
``ynl_socket_get_fd()`` returns the underlying socket fd which can
be plugged into appropriate asynchronous IO API like ``poll``,
or ``select``.
Notifications can be retrieved using ``ynl_ntf_dequeue()`` and have
to be freed using ``ynl_ntf_free()``. Since we don't know the notification
type upfront the notifications are returned as ``struct ynl_ntf_base_type *``
and user is expected to cast them to the appropriate full type based
on the ``cmd`` member.
# SPDX-License-Identifier: GPL-2.0
SUBDIRS = lib generated samples
all: $(SUBDIRS)
$(SUBDIRS):
@if [ -f "$@/Makefile" ] ; then \
$(MAKE) -C $@ ; \
fi
clean hardclean:
@for dir in $(SUBDIRS) ; do \
if [ -f "$$dir/Makefile" ] ; then \
$(MAKE) -C $$dir $@; \
fi \
done
.PHONY: clean all $(SUBDIRS)
# SPDX-License-Identifier: GPL-2.0
CC=gcc
CFLAGS=-std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow \
-I../lib/
ifeq ("$(DEBUG)","1")
CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan
endif
TOOL:=../ynl-gen-c.py
GENS:=fou netdev
SRCS=$(patsubst %,%-user.c,${GENS})
HDRS=$(patsubst %,%-user.h,${GENS})
OBJS=$(patsubst %,%-user.o,${GENS})
all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) regen
protos.a: $(OBJS)
@echo -e "\tAR $@"
@ar rcs $@ $(OBJS)
%-user.h: ../../../../Documentation/netlink/specs/%.yaml $(TOOL)
@echo -e "\tGEN $@"
@$(TOOL) --mode user --header --spec $< > $@
%-user.c: ../../../../Documentation/netlink/specs/%.yaml $(TOOL)
@echo -e "\tGEN $@"
@$(TOOL) --mode user --source --spec $< > $@
%-user.o: %-user.c %-user.h
@echo -e "\tCC $@"
@$(COMPILE.c) -c -o $@ $<
clean:
rm -f *.o
hardclean: clean
rm -f *.c *.h *.a
regen:
@../ynl-regen.sh
.PHONY: all clean hardclean regen
.DEFAULT_GOAL: all
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/fou.yaml */
/* YNL-GEN user source */
#include <stdlib.h>
#include "fou-user.h"
#include "ynl.h"
#include <linux/fou.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
/* Enums */
static const char * const fou_op_strmap[] = {
[FOU_CMD_UNSPEC] = "unspec",
[FOU_CMD_ADD] = "add",
[FOU_CMD_DEL] = "del",
[FOU_CMD_GET] = "get",
};
const char *fou_op_str(int op)
{
if (op < 0 || op >= (int)MNL_ARRAY_SIZE(fou_op_strmap))
return NULL;
return fou_op_strmap[op];
}
static const char * const fou_encap_type_strmap[] = {
[0] = "unspec",
[1] = "direct",
[2] = "gue",
};
const char *fou_encap_type_str(int value)
{
if (value < 0 || value >= (int)MNL_ARRAY_SIZE(fou_encap_type_strmap))
return NULL;
return fou_encap_type_strmap[value];
}
/* Policies */
extern struct ynl_policy_nest fou_nest;
struct ynl_policy_attr fou_policy[FOU_ATTR_MAX + 1] = {
[FOU_ATTR_UNSPEC] = { .name = "unspec", .type = YNL_PT_REJECT, },
[FOU_ATTR_PORT] = { .name = "port", .type = YNL_PT_U16, },
[FOU_ATTR_AF] = { .name = "af", .type = YNL_PT_U8, },
[FOU_ATTR_IPPROTO] = { .name = "ipproto", .type = YNL_PT_U8, },
[FOU_ATTR_TYPE] = { .name = "type", .type = YNL_PT_U8, },
[FOU_ATTR_REMCSUM_NOPARTIAL] = { .name = "remcsum_nopartial", .type = YNL_PT_FLAG, },
[FOU_ATTR_LOCAL_V4] = { .name = "local_v4", .type = YNL_PT_U32, },
[FOU_ATTR_LOCAL_V6] = { .name = "local_v6", .type = YNL_PT_BINARY,},
[FOU_ATTR_PEER_V4] = { .name = "peer_v4", .type = YNL_PT_U32, },
[FOU_ATTR_PEER_V6] = { .name = "peer_v6", .type = YNL_PT_BINARY,},
[FOU_ATTR_PEER_PORT] = { .name = "peer_port", .type = YNL_PT_U16, },
[FOU_ATTR_IFINDEX] = { .name = "ifindex", .type = YNL_PT_U32, },
};
struct ynl_policy_nest fou_nest = {
.max_attr = FOU_ATTR_MAX,
.table = fou_policy,
};
/* Common nested types */
/* ============== FOU_CMD_ADD ============== */
/* FOU_CMD_ADD - do */
void fou_add_req_free(struct fou_add_req *req)
{
free(req->local_v6);
free(req->peer_v6);
free(req);
}
int fou_add(struct ynl_sock *ys, struct fou_add_req *req)
{
struct nlmsghdr *nlh;
int err;
nlh = ynl_gemsg_start_req(ys, ys->family_id, FOU_CMD_ADD, 1);
ys->req_policy = &fou_nest;
if (req->_present.port)
mnl_attr_put_u16(nlh, FOU_ATTR_PORT, req->port);
if (req->_present.ipproto)
mnl_attr_put_u8(nlh, FOU_ATTR_IPPROTO, req->ipproto);
if (req->_present.type)
mnl_attr_put_u8(nlh, FOU_ATTR_TYPE, req->type);
if (req->_present.remcsum_nopartial)
mnl_attr_put(nlh, FOU_ATTR_REMCSUM_NOPARTIAL, 0, NULL);
if (req->_present.local_v4)
mnl_attr_put_u32(nlh, FOU_ATTR_LOCAL_V4, req->local_v4);
if (req->_present.peer_v4)
mnl_attr_put_u32(nlh, FOU_ATTR_PEER_V4, req->peer_v4);
if (req->_present.local_v6_len)
mnl_attr_put(nlh, FOU_ATTR_LOCAL_V6, req->_present.local_v6_len, req->local_v6);
if (req->_present.peer_v6_len)
mnl_attr_put(nlh, FOU_ATTR_PEER_V6, req->_present.peer_v6_len, req->peer_v6);
if (req->_present.peer_port)
mnl_attr_put_u16(nlh, FOU_ATTR_PEER_PORT, req->peer_port);
if (req->_present.ifindex)
mnl_attr_put_u32(nlh, FOU_ATTR_IFINDEX, req->ifindex);
err = ynl_exec(ys, nlh, NULL);
if (err < 0)
return -1;
return 0;
}
/* ============== FOU_CMD_DEL ============== */
/* FOU_CMD_DEL - do */
void fou_del_req_free(struct fou_del_req *req)
{
free(req->local_v6);
free(req->peer_v6);
free(req);
}
int fou_del(struct ynl_sock *ys, struct fou_del_req *req)
{
struct nlmsghdr *nlh;
int err;
nlh = ynl_gemsg_start_req(ys, ys->family_id, FOU_CMD_DEL, 1);
ys->req_policy = &fou_nest;
if (req->_present.af)
mnl_attr_put_u8(nlh, FOU_ATTR_AF, req->af);
if (req->_present.ifindex)
mnl_attr_put_u32(nlh, FOU_ATTR_IFINDEX, req->ifindex);
if (req->_present.port)
mnl_attr_put_u16(nlh, FOU_ATTR_PORT, req->port);
if (req->_present.peer_port)
mnl_attr_put_u16(nlh, FOU_ATTR_PEER_PORT, req->peer_port);
if (req->_present.local_v4)
mnl_attr_put_u32(nlh, FOU_ATTR_LOCAL_V4, req->local_v4);
if (req->_present.peer_v4)
mnl_attr_put_u32(nlh, FOU_ATTR_PEER_V4, req->peer_v4);
if (req->_present.local_v6_len)
mnl_attr_put(nlh, FOU_ATTR_LOCAL_V6, req->_present.local_v6_len, req->local_v6);
if (req->_present.peer_v6_len)
mnl_attr_put(nlh, FOU_ATTR_PEER_V6, req->_present.peer_v6_len, req->peer_v6);
err = ynl_exec(ys, nlh, NULL);
if (err < 0)
return -1;
return 0;
}
/* ============== FOU_CMD_GET ============== */
/* FOU_CMD_GET - do */
void fou_get_req_free(struct fou_get_req *req)
{
free(req->local_v6);
free(req->peer_v6);
free(req);
}
void fou_get_rsp_free(struct fou_get_rsp *rsp)
{
free(rsp->local_v6);
free(rsp->peer_v6);
free(rsp);
}
int fou_get_rsp_parse(const struct nlmsghdr *nlh, void *data)
{
struct ynl_parse_arg *yarg = data;
const struct nlattr *attr;
struct fou_get_rsp *dst;
dst = yarg->data;
mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
if (mnl_attr_get_type(attr) == FOU_ATTR_PORT) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.port = 1;
dst->port = mnl_attr_get_u16(attr);
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_IPPROTO) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.ipproto = 1;
dst->ipproto = mnl_attr_get_u8(attr);
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_TYPE) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.type = 1;
dst->type = mnl_attr_get_u8(attr);
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_REMCSUM_NOPARTIAL) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.remcsum_nopartial = 1;
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_LOCAL_V4) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.local_v4 = 1;
dst->local_v4 = mnl_attr_get_u32(attr);
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_PEER_V4) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.peer_v4 = 1;
dst->peer_v4 = mnl_attr_get_u32(attr);
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_LOCAL_V6) {
unsigned int len;
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
len = mnl_attr_get_payload_len(attr);
dst->_present.local_v6_len = len;
dst->local_v6 = malloc(len);
memcpy(dst->local_v6, mnl_attr_get_payload(attr), len);
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_PEER_V6) {
unsigned int len;
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
len = mnl_attr_get_payload_len(attr);
dst->_present.peer_v6_len = len;
dst->peer_v6 = malloc(len);
memcpy(dst->peer_v6, mnl_attr_get_payload(attr), len);
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_PEER_PORT) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.peer_port = 1;
dst->peer_port = mnl_attr_get_u16(attr);
}
else if (mnl_attr_get_type(attr) == FOU_ATTR_IFINDEX) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.ifindex = 1;
dst->ifindex = mnl_attr_get_u32(attr);
}
}
return MNL_CB_OK;
}
struct fou_get_rsp *fou_get(struct ynl_sock *ys, struct fou_get_req *req)
{
struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };
struct fou_get_rsp *rsp;
struct nlmsghdr *nlh;
int err;
nlh = ynl_gemsg_start_req(ys, ys->family_id, FOU_CMD_GET, 1);
ys->req_policy = &fou_nest;
yrs.yarg.rsp_policy = &fou_nest;
if (req->_present.af)
mnl_attr_put_u8(nlh, FOU_ATTR_AF, req->af);
if (req->_present.ifindex)
mnl_attr_put_u32(nlh, FOU_ATTR_IFINDEX, req->ifindex);
if (req->_present.port)
mnl_attr_put_u16(nlh, FOU_ATTR_PORT, req->port);
if (req->_present.peer_port)
mnl_attr_put_u16(nlh, FOU_ATTR_PEER_PORT, req->peer_port);
if (req->_present.local_v4)
mnl_attr_put_u32(nlh, FOU_ATTR_LOCAL_V4, req->local_v4);
if (req->_present.peer_v4)
mnl_attr_put_u32(nlh, FOU_ATTR_PEER_V4, req->peer_v4);
if (req->_present.local_v6_len)
mnl_attr_put(nlh, FOU_ATTR_LOCAL_V6, req->_present.local_v6_len, req->local_v6);
if (req->_present.peer_v6_len)
mnl_attr_put(nlh, FOU_ATTR_PEER_V6, req->_present.peer_v6_len, req->peer_v6);
rsp = calloc(1, sizeof(*rsp));
yrs.yarg.data = rsp;
yrs.cb = fou_get_rsp_parse;
yrs.rsp_cmd = FOU_CMD_GET;
err = ynl_exec(ys, nlh, &yrs);
if (err < 0)
goto err_free;
return rsp;
err_free:
fou_get_rsp_free(rsp);
return NULL;
}
/* FOU_CMD_GET - dump */
void fou_get_list_free(struct fou_get_list *rsp)
{
struct fou_get_list *next = rsp;
while ((void *)next != YNL_LIST_END) {
rsp = next;
next = rsp->next;
free(rsp->obj.local_v6);
free(rsp->obj.peer_v6);
free(rsp);
}
}
struct fou_get_list *fou_get_dump(struct ynl_sock *ys)
{
struct ynl_dump_state yds = {};
struct nlmsghdr *nlh;
int err;
yds.ys = ys;
yds.alloc_sz = sizeof(struct fou_get_list);
yds.cb = fou_get_rsp_parse;
yds.rsp_cmd = FOU_CMD_GET;
yds.rsp_policy = &fou_nest;
nlh = ynl_gemsg_start_dump(ys, ys->family_id, FOU_CMD_GET, 1);
err = ynl_exec_dump(ys, nlh, &yds);
if (err < 0)
goto free_list;
return yds.first;
free_list:
fou_get_list_free(yds.first);
return NULL;
}
const struct ynl_family ynl_fou_family = {
.name = "fou",
};
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/fou.yaml */
/* YNL-GEN user header */
#ifndef _LINUX_FOU_GEN_H
#define _LINUX_FOU_GEN_H
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>
#include <linux/fou.h>
struct ynl_sock;
extern const struct ynl_family ynl_fou_family;
/* Enums */
const char *fou_op_str(int op);
const char *fou_encap_type_str(int value);
/* Common nested types */
/* ============== FOU_CMD_ADD ============== */
/* FOU_CMD_ADD - do */
struct fou_add_req {
struct {
__u32 port:1;
__u32 ipproto:1;
__u32 type:1;
__u32 remcsum_nopartial:1;
__u32 local_v4:1;
__u32 peer_v4:1;
__u32 local_v6_len;
__u32 peer_v6_len;
__u32 peer_port:1;
__u32 ifindex:1;
} _present;
__u16 port /* big-endian */;
__u8 ipproto;
__u8 type;
__u32 local_v4;
__u32 peer_v4;
void *local_v6;
void *peer_v6;
__u16 peer_port /* big-endian */;
__s32 ifindex;
};
static inline struct fou_add_req *fou_add_req_alloc(void)
{
return calloc(1, sizeof(struct fou_add_req));
}
void fou_add_req_free(struct fou_add_req *req);
static inline void
fou_add_req_set_port(struct fou_add_req *req, __u16 port /* big-endian */)
{
req->_present.port = 1;
req->port = port;
}
static inline void
fou_add_req_set_ipproto(struct fou_add_req *req, __u8 ipproto)
{
req->_present.ipproto = 1;
req->ipproto = ipproto;
}
static inline void fou_add_req_set_type(struct fou_add_req *req, __u8 type)
{
req->_present.type = 1;
req->type = type;
}
static inline void fou_add_req_set_remcsum_nopartial(struct fou_add_req *req)
{
req->_present.remcsum_nopartial = 1;
}
static inline void
fou_add_req_set_local_v4(struct fou_add_req *req, __u32 local_v4)
{
req->_present.local_v4 = 1;
req->local_v4 = local_v4;
}
static inline void
fou_add_req_set_peer_v4(struct fou_add_req *req, __u32 peer_v4)
{
req->_present.peer_v4 = 1;
req->peer_v4 = peer_v4;
}
static inline void
fou_add_req_set_local_v6(struct fou_add_req *req, const void *local_v6,
size_t len)
{
free(req->local_v6);
req->local_v6 = malloc(req->_present.local_v6_len);
memcpy(req->local_v6, local_v6, req->_present.local_v6_len);
}
static inline void
fou_add_req_set_peer_v6(struct fou_add_req *req, const void *peer_v6,
size_t len)
{
free(req->peer_v6);
req->peer_v6 = malloc(req->_present.peer_v6_len);
memcpy(req->peer_v6, peer_v6, req->_present.peer_v6_len);
}
static inline void
fou_add_req_set_peer_port(struct fou_add_req *req,
__u16 peer_port /* big-endian */)
{
req->_present.peer_port = 1;
req->peer_port = peer_port;
}
static inline void
fou_add_req_set_ifindex(struct fou_add_req *req, __s32 ifindex)
{
req->_present.ifindex = 1;
req->ifindex = ifindex;
}
/*
* Add port.
*/
int fou_add(struct ynl_sock *ys, struct fou_add_req *req);
/* ============== FOU_CMD_DEL ============== */
/* FOU_CMD_DEL - do */
struct fou_del_req {
struct {
__u32 af:1;
__u32 ifindex:1;
__u32 port:1;
__u32 peer_port:1;
__u32 local_v4:1;
__u32 peer_v4:1;
__u32 local_v6_len;
__u32 peer_v6_len;
} _present;
__u8 af;
__s32 ifindex;
__u16 port /* big-endian */;
__u16 peer_port /* big-endian */;
__u32 local_v4;
__u32 peer_v4;
void *local_v6;
void *peer_v6;
};
static inline struct fou_del_req *fou_del_req_alloc(void)
{
return calloc(1, sizeof(struct fou_del_req));
}
void fou_del_req_free(struct fou_del_req *req);
static inline void fou_del_req_set_af(struct fou_del_req *req, __u8 af)
{
req->_present.af = 1;
req->af = af;
}
static inline void
fou_del_req_set_ifindex(struct fou_del_req *req, __s32 ifindex)
{
req->_present.ifindex = 1;
req->ifindex = ifindex;
}
static inline void
fou_del_req_set_port(struct fou_del_req *req, __u16 port /* big-endian */)
{
req->_present.port = 1;
req->port = port;
}
static inline void
fou_del_req_set_peer_port(struct fou_del_req *req,
__u16 peer_port /* big-endian */)
{
req->_present.peer_port = 1;
req->peer_port = peer_port;
}
static inline void
fou_del_req_set_local_v4(struct fou_del_req *req, __u32 local_v4)
{
req->_present.local_v4 = 1;
req->local_v4 = local_v4;
}
static inline void
fou_del_req_set_peer_v4(struct fou_del_req *req, __u32 peer_v4)
{
req->_present.peer_v4 = 1;
req->peer_v4 = peer_v4;
}
static inline void
fou_del_req_set_local_v6(struct fou_del_req *req, const void *local_v6,
size_t len)
{
free(req->local_v6);
req->local_v6 = malloc(req->_present.local_v6_len);
memcpy(req->local_v6, local_v6, req->_present.local_v6_len);
}
static inline void
fou_del_req_set_peer_v6(struct fou_del_req *req, const void *peer_v6,
size_t len)
{
free(req->peer_v6);
req->peer_v6 = malloc(req->_present.peer_v6_len);
memcpy(req->peer_v6, peer_v6, req->_present.peer_v6_len);
}
/*
* Delete port.
*/
int fou_del(struct ynl_sock *ys, struct fou_del_req *req);
/* ============== FOU_CMD_GET ============== */
/* FOU_CMD_GET - do */
struct fou_get_req {
struct {
__u32 af:1;
__u32 ifindex:1;
__u32 port:1;
__u32 peer_port:1;
__u32 local_v4:1;
__u32 peer_v4:1;
__u32 local_v6_len;
__u32 peer_v6_len;
} _present;
__u8 af;
__s32 ifindex;
__u16 port /* big-endian */;
__u16 peer_port /* big-endian */;
__u32 local_v4;
__u32 peer_v4;
void *local_v6;
void *peer_v6;
};
static inline struct fou_get_req *fou_get_req_alloc(void)
{
return calloc(1, sizeof(struct fou_get_req));
}
void fou_get_req_free(struct fou_get_req *req);
static inline void fou_get_req_set_af(struct fou_get_req *req, __u8 af)
{
req->_present.af = 1;
req->af = af;
}
static inline void
fou_get_req_set_ifindex(struct fou_get_req *req, __s32 ifindex)
{
req->_present.ifindex = 1;
req->ifindex = ifindex;
}
static inline void
fou_get_req_set_port(struct fou_get_req *req, __u16 port /* big-endian */)
{
req->_present.port = 1;
req->port = port;
}
static inline void
fou_get_req_set_peer_port(struct fou_get_req *req,
__u16 peer_port /* big-endian */)
{
req->_present.peer_port = 1;
req->peer_port = peer_port;
}
static inline void
fou_get_req_set_local_v4(struct fou_get_req *req, __u32 local_v4)
{
req->_present.local_v4 = 1;
req->local_v4 = local_v4;
}
static inline void
fou_get_req_set_peer_v4(struct fou_get_req *req, __u32 peer_v4)
{
req->_present.peer_v4 = 1;
req->peer_v4 = peer_v4;
}
static inline void
fou_get_req_set_local_v6(struct fou_get_req *req, const void *local_v6,
size_t len)
{
free(req->local_v6);
req->local_v6 = malloc(req->_present.local_v6_len);
memcpy(req->local_v6, local_v6, req->_present.local_v6_len);
}
static inline void
fou_get_req_set_peer_v6(struct fou_get_req *req, const void *peer_v6,
size_t len)
{
free(req->peer_v6);
req->peer_v6 = malloc(req->_present.peer_v6_len);
memcpy(req->peer_v6, peer_v6, req->_present.peer_v6_len);
}
struct fou_get_rsp {
struct {
__u32 port:1;
__u32 ipproto:1;
__u32 type:1;
__u32 remcsum_nopartial:1;
__u32 local_v4:1;
__u32 peer_v4:1;
__u32 local_v6_len;
__u32 peer_v6_len;
__u32 peer_port:1;
__u32 ifindex:1;
} _present;
__u16 port /* big-endian */;
__u8 ipproto;
__u8 type;
__u32 local_v4;
__u32 peer_v4;
void *local_v6;
void *peer_v6;
__u16 peer_port /* big-endian */;
__s32 ifindex;
};
void fou_get_rsp_free(struct fou_get_rsp *rsp);
/*
* Get tunnel info.
*/
struct fou_get_rsp *fou_get(struct ynl_sock *ys, struct fou_get_req *req);
/* FOU_CMD_GET - dump */
struct fou_get_list {
struct fou_get_list *next;
struct fou_get_rsp obj __attribute__ ((aligned (8)));
};
void fou_get_list_free(struct fou_get_list *rsp);
struct fou_get_list *fou_get_dump(struct ynl_sock *ys);
#endif /* _LINUX_FOU_GEN_H */
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/netdev.yaml */
/* YNL-GEN user source */
#include <stdlib.h>
#include "netdev-user.h"
#include "ynl.h"
#include <linux/netdev.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
/* Enums */
static const char * const netdev_op_strmap[] = {
[NETDEV_CMD_DEV_GET] = "dev-get",
[NETDEV_CMD_DEV_ADD_NTF] = "dev-add-ntf",
[NETDEV_CMD_DEV_DEL_NTF] = "dev-del-ntf",
[NETDEV_CMD_DEV_CHANGE_NTF] = "dev-change-ntf",
};
const char *netdev_op_str(int op)
{
if (op < 0 || op >= (int)MNL_ARRAY_SIZE(netdev_op_strmap))
return NULL;
return netdev_op_strmap[op];
}
static const char * const netdev_xdp_act_strmap[] = {
[0] = "basic",
[1] = "redirect",
[2] = "ndo-xmit",
[3] = "xsk-zerocopy",
[4] = "hw-offload",
[5] = "rx-sg",
[6] = "ndo-xmit-sg",
};
const char *netdev_xdp_act_str(enum netdev_xdp_act value)
{
value = ffs(value) - 1;
if (value < 0 || value >= (int)MNL_ARRAY_SIZE(netdev_xdp_act_strmap))
return NULL;
return netdev_xdp_act_strmap[value];
}
/* Policies */
extern struct ynl_policy_nest netdev_dev_nest;
struct ynl_policy_attr netdev_dev_policy[NETDEV_A_DEV_MAX + 1] = {
[NETDEV_A_DEV_IFINDEX] = { .name = "ifindex", .type = YNL_PT_U32, },
[NETDEV_A_DEV_PAD] = { .name = "pad", .type = YNL_PT_IGNORE, },
[NETDEV_A_DEV_XDP_FEATURES] = { .name = "xdp-features", .type = YNL_PT_U64, },
};
struct ynl_policy_nest netdev_dev_nest = {
.max_attr = NETDEV_A_DEV_MAX,
.table = netdev_dev_policy,
};
/* Common nested types */
/* ============== NETDEV_CMD_DEV_GET ============== */
/* NETDEV_CMD_DEV_GET - do */
void netdev_dev_get_req_free(struct netdev_dev_get_req *req)
{
free(req);
}
void netdev_dev_get_rsp_free(struct netdev_dev_get_rsp *rsp)
{
free(rsp);
}
int netdev_dev_get_rsp_parse(const struct nlmsghdr *nlh, void *data)
{
struct ynl_parse_arg *yarg = data;
struct netdev_dev_get_rsp *dst;
const struct nlattr *attr;
dst = yarg->data;
mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
if (mnl_attr_get_type(attr) == NETDEV_A_DEV_IFINDEX) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.ifindex = 1;
dst->ifindex = mnl_attr_get_u32(attr);
}
else if (mnl_attr_get_type(attr) == NETDEV_A_DEV_XDP_FEATURES) {
if (ynl_attr_validate(yarg, attr))
return MNL_CB_ERROR;
dst->_present.xdp_features = 1;
dst->xdp_features = mnl_attr_get_u64(attr);
}
}
return MNL_CB_OK;
}
struct netdev_dev_get_rsp *
netdev_dev_get(struct ynl_sock *ys, struct netdev_dev_get_req *req)
{
struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };
struct netdev_dev_get_rsp *rsp;
struct nlmsghdr *nlh;
int err;
nlh = ynl_gemsg_start_req(ys, ys->family_id, NETDEV_CMD_DEV_GET, 1);
ys->req_policy = &netdev_dev_nest;
yrs.yarg.rsp_policy = &netdev_dev_nest;
if (req->_present.ifindex)
mnl_attr_put_u32(nlh, NETDEV_A_DEV_IFINDEX, req->ifindex);
rsp = calloc(1, sizeof(*rsp));
yrs.yarg.data = rsp;
yrs.cb = netdev_dev_get_rsp_parse;
yrs.rsp_cmd = NETDEV_CMD_DEV_GET;
err = ynl_exec(ys, nlh, &yrs);
if (err < 0)
goto err_free;
return rsp;
err_free:
netdev_dev_get_rsp_free(rsp);
return NULL;
}
/* NETDEV_CMD_DEV_GET - dump */
void netdev_dev_get_list_free(struct netdev_dev_get_list *rsp)
{
struct netdev_dev_get_list *next = rsp;
while ((void *)next != YNL_LIST_END) {
rsp = next;
next = rsp->next;
free(rsp);
}
}
struct netdev_dev_get_list *netdev_dev_get_dump(struct ynl_sock *ys)
{
struct ynl_dump_state yds = {};
struct nlmsghdr *nlh;
int err;
yds.ys = ys;
yds.alloc_sz = sizeof(struct netdev_dev_get_list);
yds.cb = netdev_dev_get_rsp_parse;
yds.rsp_cmd = NETDEV_CMD_DEV_GET;
yds.rsp_policy = &netdev_dev_nest;
nlh = ynl_gemsg_start_dump(ys, ys->family_id, NETDEV_CMD_DEV_GET, 1);
err = ynl_exec_dump(ys, nlh, &yds);
if (err < 0)
goto free_list;
return yds.first;
free_list:
netdev_dev_get_list_free(yds.first);
return NULL;
}
/* NETDEV_CMD_DEV_GET - notify */
void netdev_dev_get_ntf_free(struct netdev_dev_get_ntf *rsp)
{
free(rsp);
}
/* --------------- Common notification parsing --------------- */
struct ynl_ntf_base_type *netdev_ntf_parse(struct ynl_sock *ys)
{
struct ynl_parse_arg yarg = { .ys = ys, };
struct ynl_ntf_base_type *rsp;
struct genlmsghdr *genlh;
struct nlmsghdr *nlh;
mnl_cb_t parse;
int len, err;
len = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);
if (len < (ssize_t)(sizeof(*nlh) + sizeof(*genlh)))
return NULL;
nlh = (struct nlmsghdr *)ys->rx_buf;
genlh = mnl_nlmsg_get_payload(nlh);
switch (genlh->cmd) {
case NETDEV_CMD_DEV_ADD_NTF:
case NETDEV_CMD_DEV_DEL_NTF:
case NETDEV_CMD_DEV_CHANGE_NTF:
rsp = calloc(1, sizeof(struct netdev_dev_get_ntf));
parse = netdev_dev_get_rsp_parse;
yarg.rsp_policy = &netdev_dev_nest;
rsp->free = (void *)netdev_dev_get_ntf_free;
break;
default:
ynl_error_unknown_notification(ys, genlh->cmd);
return NULL;
}
yarg.data = rsp->data;
err = mnl_cb_run2(ys->rx_buf, len, 0, 0, parse, &yarg,
ynl_cb_array, NLMSG_MIN_TYPE);
if (err < 0)
goto err_free;
rsp->family = nlh->nlmsg_type;
rsp->cmd = genlh->cmd;
return rsp;
err_free:
free(rsp);
return NULL;
}
static const struct ynl_ntf_info netdev_ntf_info[] = {
[NETDEV_CMD_DEV_ADD_NTF] = {
.alloc_sz = sizeof(struct netdev_dev_get_ntf),
.cb = netdev_dev_get_rsp_parse,
.policy = &netdev_dev_nest,
.free = (void *)netdev_dev_get_ntf_free,
},
[NETDEV_CMD_DEV_DEL_NTF] = {
.alloc_sz = sizeof(struct netdev_dev_get_ntf),
.cb = netdev_dev_get_rsp_parse,
.policy = &netdev_dev_nest,
.free = (void *)netdev_dev_get_ntf_free,
},
[NETDEV_CMD_DEV_CHANGE_NTF] = {
.alloc_sz = sizeof(struct netdev_dev_get_ntf),
.cb = netdev_dev_get_rsp_parse,
.policy = &netdev_dev_nest,
.free = (void *)netdev_dev_get_ntf_free,
},
};
const struct ynl_family ynl_netdev_family = {
.name = "netdev",
.ntf_info = netdev_ntf_info,
.ntf_info_size = MNL_ARRAY_SIZE(netdev_ntf_info),
};
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/netdev.yaml */
/* YNL-GEN user header */
#ifndef _LINUX_NETDEV_GEN_H
#define _LINUX_NETDEV_GEN_H
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>
#include <linux/netdev.h>
struct ynl_sock;
extern const struct ynl_family ynl_netdev_family;
/* Enums */
const char *netdev_op_str(int op);
const char *netdev_xdp_act_str(enum netdev_xdp_act value);
/* Common nested types */
/* ============== NETDEV_CMD_DEV_GET ============== */
/* NETDEV_CMD_DEV_GET - do */
struct netdev_dev_get_req {
struct {
__u32 ifindex:1;
} _present;
__u32 ifindex;
};
static inline struct netdev_dev_get_req *netdev_dev_get_req_alloc(void)
{
return calloc(1, sizeof(struct netdev_dev_get_req));
}
void netdev_dev_get_req_free(struct netdev_dev_get_req *req);
static inline void
netdev_dev_get_req_set_ifindex(struct netdev_dev_get_req *req, __u32 ifindex)
{
req->_present.ifindex = 1;
req->ifindex = ifindex;
}
struct netdev_dev_get_rsp {
struct {
__u32 ifindex:1;
__u32 xdp_features:1;
} _present;
__u32 ifindex;
__u64 xdp_features;
};
void netdev_dev_get_rsp_free(struct netdev_dev_get_rsp *rsp);
/*
* Get / dump information about a netdev.
*/
struct netdev_dev_get_rsp *
netdev_dev_get(struct ynl_sock *ys, struct netdev_dev_get_req *req);
/* NETDEV_CMD_DEV_GET - dump */
struct netdev_dev_get_list {
struct netdev_dev_get_list *next;
struct netdev_dev_get_rsp obj __attribute__ ((aligned (8)));
};
void netdev_dev_get_list_free(struct netdev_dev_get_list *rsp);
struct netdev_dev_get_list *netdev_dev_get_dump(struct ynl_sock *ys);
/* NETDEV_CMD_DEV_GET - notify */
struct netdev_dev_get_ntf {
__u16 family;
__u8 cmd;
struct ynl_ntf_base_type *next;
void (*free)(struct netdev_dev_get_ntf *ntf);
struct netdev_dev_get_rsp obj __attribute__ ((aligned (8)));
};
void netdev_dev_get_ntf_free(struct netdev_dev_get_ntf *rsp);
/* --------------- Common notification parsing --------------- */
struct ynl_ntf_base_type *netdev_ntf_parse(struct ynl_sock *ys);
#endif /* _LINUX_NETDEV_GEN_H */
# SPDX-License-Identifier: GPL-2.0
CC=gcc
CFLAGS=-std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow
ifeq ("$(DEBUG)","1")
CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan
endif
SRCS=$(wildcard *.c)
OBJS=$(patsubst %.c,%.o,${SRCS})
include $(wildcard *.d)
all: ynl.a
ynl.a: $(OBJS)
ar rcs $@ $(OBJS)
clean:
rm -f *.o *.d *~
hardclean: clean
rm -f *.a
%.o: %.c
$(COMPILE.c) -MMD -c -o $@ $<
.PHONY: all clean
.DEFAULT_GOAL=all
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
#ifndef __YNL_C_H
#define __YNL_C_H 1
#include <stddef.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
#include <linux/types.h>
struct mnl_socket;
struct nlmsghdr;
/*
* User facing code
*/
struct ynl_ntf_base_type;
struct ynl_ntf_info;
struct ynl_sock;
enum ynl_error_code {
YNL_ERROR_NONE = 0,
__YNL_ERRNO_END = 4096,
YNL_ERROR_INTERNAL,
YNL_ERROR_EXPECT_ACK,
YNL_ERROR_EXPECT_MSG,
YNL_ERROR_UNEXPECT_MSG,
YNL_ERROR_ATTR_MISSING,
YNL_ERROR_ATTR_INVALID,
YNL_ERROR_UNKNOWN_NTF,
YNL_ERROR_INV_RESP,
};
/**
* struct ynl_error - error encountered by YNL
* @code: errno (low values) or YNL error code (enum ynl_error_code)
* @attr_offs: offset of bad attribute (for very advanced users)
* @msg: error message
*
* Error information for when YNL operations fail.
* Users should interact with the err member of struct ynl_sock directly.
* The main exception to that rule is ynl_sock_create().
*/
struct ynl_error {
enum ynl_error_code code;
unsigned int attr_offs;
char msg[512];
};
/**
* struct ynl_family - YNL family info
* Family description generated by codegen. Pass to ynl_sock_create().
*/
struct ynl_family {
/* private: */
const char *name;
const struct ynl_ntf_info *ntf_info;
unsigned int ntf_info_size;
};
/**
* struct ynl_sock - YNL wrapped netlink socket
* @err: YNL error descriptor, cleared on every request.
*/
struct ynl_sock {
struct ynl_error err;
/* private: */
const struct ynl_family *family;
struct mnl_socket *sock;
__u32 seq;
__u32 portid;
__u16 family_id;
unsigned int n_mcast_groups;
struct {
unsigned int id;
char name[GENL_NAMSIZ];
} *mcast_groups;
struct ynl_ntf_base_type *ntf_first;
struct ynl_ntf_base_type **ntf_last_next;
struct nlmsghdr *nlh;
struct ynl_policy_nest *req_policy;
unsigned char *tx_buf;
unsigned char *rx_buf;
unsigned char raw_buf[];
};
struct ynl_sock *
ynl_sock_create(const struct ynl_family *yf, struct ynl_error *e);
void ynl_sock_destroy(struct ynl_sock *ys);
#define ynl_dump_foreach(dump, iter) \
for (typeof(dump->obj) *iter = &dump->obj; \
!ynl_dump_obj_is_last(iter); \
iter = ynl_dump_obj_next(iter))
int ynl_subscribe(struct ynl_sock *ys, const char *grp_name);
int ynl_socket_get_fd(struct ynl_sock *ys);
int ynl_ntf_check(struct ynl_sock *ys);
/**
* ynl_has_ntf() - check if socket has *parsed* notifications
* @ys: active YNL socket
*
* Note that this does not take into account notifications sitting
* in netlink socket, just the notifications which have already been
* read and parsed (e.g. during a ynl_ntf_check() call).
*/
static inline bool ynl_has_ntf(struct ynl_sock *ys)
{
return ys->ntf_last_next != &ys->ntf_first;
}
struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys);
void ynl_ntf_free(struct ynl_ntf_base_type *ntf);
/*
* YNL internals / low level stuff
*/
/* Generic mnl helper code */
enum ynl_policy_type {
YNL_PT_REJECT = 1,
YNL_PT_IGNORE,
YNL_PT_NEST,
YNL_PT_FLAG,
YNL_PT_BINARY,
YNL_PT_U8,
YNL_PT_U16,
YNL_PT_U32,
YNL_PT_U64,
YNL_PT_NUL_STR,
};
struct ynl_policy_attr {
enum ynl_policy_type type;
unsigned int len;
const char *name;
struct ynl_policy_nest *nest;
};
struct ynl_policy_nest {
unsigned int max_attr;
struct ynl_policy_attr *table;
};
struct ynl_parse_arg {
struct ynl_sock *ys;
struct ynl_policy_nest *rsp_policy;
void *data;
};
struct ynl_dump_list_type {
struct ynl_dump_list_type *next;
unsigned char data[] __attribute__ ((aligned (8)));
};
extern struct ynl_dump_list_type *YNL_LIST_END;
static inline bool ynl_dump_obj_is_last(void *obj)
{
unsigned long uptr = (unsigned long)obj;
uptr -= offsetof(struct ynl_dump_list_type, data);
return uptr == (unsigned long)YNL_LIST_END;
}
static inline void *ynl_dump_obj_next(void *obj)
{
unsigned long uptr = (unsigned long)obj;
struct ynl_dump_list_type *list;
uptr -= offsetof(struct ynl_dump_list_type, data);
list = (void *)uptr;
uptr = (unsigned long)list->next;
uptr += offsetof(struct ynl_dump_list_type, data);
return (void *)uptr;
}
struct ynl_ntf_base_type {
__u16 family;
__u8 cmd;
struct ynl_ntf_base_type *next;
void (*free)(struct ynl_ntf_base_type *ntf);
unsigned char data[] __attribute__ ((aligned (8)));
};
extern mnl_cb_t ynl_cb_array[NLMSG_MIN_TYPE];
struct nlmsghdr *
ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version);
struct nlmsghdr *
ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version);
int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr);
int ynl_recv_ack(struct ynl_sock *ys, int ret);
int ynl_cb_null(const struct nlmsghdr *nlh, void *data);
/* YNL specific helpers used by the auto-generated code */
struct ynl_req_state {
struct ynl_parse_arg yarg;
mnl_cb_t cb;
__u32 rsp_cmd;
};
struct ynl_dump_state {
struct ynl_sock *ys;
struct ynl_policy_nest *rsp_policy;
void *first;
struct ynl_dump_list_type *last;
size_t alloc_sz;
mnl_cb_t cb;
__u32 rsp_cmd;
};
struct ynl_ntf_info {
struct ynl_policy_nest *policy;
mnl_cb_t cb;
size_t alloc_sz;
void (*free)(struct ynl_ntf_base_type *ntf);
};
int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
struct ynl_req_state *yrs);
int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
struct ynl_dump_state *yds);
void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd);
int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg);
#endif
# SPDX-License-Identifier: GPL-2.0
CC=gcc
CFLAGS=-std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow \
-I../lib/ -I../generated/
ifeq ("$(DEBUG)","1")
CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan
endif
LDLIBS=-lmnl ../lib/ynl.a ../generated/protos.a
SRCS=$(wildcard *.c)
BINS=$(patsubst %.c,%,${SRCS})
include $(wildcard *.d)
all: $(BINS)
$(BINS): ../lib/ynl.a ../generated/protos.a
clean:
rm -f *.o *.d *~
hardclean: clean
rm -f $(BINS)
.PHONY: all clean
.DEFAULT_GOAL=all
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <string.h>
#include <ynl.h>
#include <net/if.h>
#include "netdev-user.h"
/* netdev genetlink family code sample
* This sample shows off basics of the netdev family but also notification
* handling, hence the somewhat odd UI. We subscribe to notifications first
* then wait for ifc selection, so the socket may already accumulate
* notifications as we wait. This allows us to test that YNL can handle
* requests and notifications getting interleaved.
*/
static void netdev_print_device(struct netdev_dev_get_rsp *d, unsigned int op)
{
char ifname[IF_NAMESIZE];
const char *name;
if (!d->_present.ifindex)
return;
name = if_indextoname(d->ifindex, ifname);
if (name)
printf("%8s", name);
printf("[%d]\t", d->ifindex);
if (!d->_present.xdp_features)
return;
printf("%llx:", d->xdp_features);
for (int i = 0; d->xdp_features > 1U << i; i++) {
if (d->xdp_features & (1U << i))
printf(" %s", netdev_xdp_act_str(1 << i));
}
name = netdev_op_str(op);
if (name)
printf(" (ntf: %s)", name);
printf("\n");
}
int main(int argc, char **argv)
{
struct netdev_dev_get_list *devs;
struct ynl_ntf_base_type *ntf;
struct ynl_error yerr;
struct ynl_sock *ys;
int ifindex = 0;
if (argc > 1)
ifindex = strtol(argv[1], NULL, 0);
ys = ynl_sock_create(&ynl_netdev_family, &yerr);
if (!ys) {
fprintf(stderr, "YNL: %s\n", yerr.msg);
return 1;
}
if (ynl_subscribe(ys, "mgmt"))
goto err_close;
printf("Select ifc ($ifindex; or 0 = dump; or -2 ntf check): ");
scanf("%d", &ifindex);
if (ifindex > 0) {
struct netdev_dev_get_req *req;
struct netdev_dev_get_rsp *d;
req = netdev_dev_get_req_alloc();
netdev_dev_get_req_set_ifindex(req, ifindex);
d = netdev_dev_get(ys, req);
netdev_dev_get_req_free(req);
if (!d)
goto err_close;
netdev_print_device(d, 0);
netdev_dev_get_rsp_free(d);
} else if (!ifindex) {
devs = netdev_dev_get_dump(ys);
if (!devs)
goto err_close;
ynl_dump_foreach(devs, d)
netdev_print_device(d, 0);
netdev_dev_get_list_free(devs);
} else if (ifindex == -2) {
ynl_ntf_check(ys);
}
while ((ntf = ynl_ntf_dequeue(ys))) {
netdev_print_device((struct netdev_dev_get_rsp *)&ntf->data,
ntf->cmd);
ynl_ntf_free(ntf);
}
ynl_sock_destroy(ys);
return 0;
err_close:
fprintf(stderr, "YNL: %s\n", ys->err.msg);
ynl_sock_destroy(ys);
return 2;
}
......@@ -944,8 +944,9 @@ class CodeWriter:
def _is_cond(cls, line):
return line.startswith('if') or line.startswith('while') or line.startswith('for')
def p(self, line, add_ind=0):
def p(self, line, add_ind=0, eat_nl=False):
if self._nl:
if not eat_nl:
self._out.write('\n')
self._nl = False
ind = self._ind
......@@ -971,7 +972,7 @@ class CodeWriter:
if line and line[0] not in {';', ','}:
line = ' ' + line
self._ind -= 1
self.p('}' + line)
self.p('}' + line, eat_nl=True)
def write_doc_line(self, doc, indent=True):
words = doc.split()
......
......@@ -14,7 +14,7 @@ done
KDIR=$(dirname $(dirname $(dirname $(dirname $(realpath $0)))))
files=$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\)')
files=$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\|user\)')
for f in $files; do
# params: 0 1 2 3
# $YAML YNL-GEN kernel $mode
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment