mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
tools: ynl: user space helpers
Add "fixed" part of the user space Netlink Spec-based library. This will get linked with the protocol implementations to form a full API. Acked-by: Willem de Bruijn <willemb@google.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -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.
|
||||
|
||||
19
tools/net/ynl/Makefile
Normal file
19
tools/net/ynl/Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
# 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)
|
||||
45
tools/net/ynl/generated/Makefile
Normal file
45
tools/net/ynl/generated/Makefile
Normal file
@@ -0,0 +1,45 @@
|
||||
# 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:=
|
||||
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
|
||||
28
tools/net/ynl/lib/Makefile
Normal file
28
tools/net/ynl/lib/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
# 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
|
||||
901
tools/net/ynl/lib/ynl.c
Normal file
901
tools/net/ynl/lib/ynl.c
Normal file
File diff suppressed because it is too large
Load Diff
237
tools/net/ynl/lib/ynl.h
Normal file
237
tools/net/ynl/lib/ynl.h
Normal file
@@ -0,0 +1,237 @@
|
||||
// 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user