Skip to content

Commit

Permalink
tools: ynl: user space helpers
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jakub Kicinski committed Jun 6, 2023
1 parent a99bfdf commit 86878f1
Show file tree
Hide file tree
Showing 7 changed files with 1,310 additions and 1 deletion.
79 changes: 79 additions & 0 deletions Documentation/userspace-api/netlink/intro-specs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 19 additions & 0 deletions tools/net/ynl/Makefile
Original file line number Diff line number Diff line change
@@ -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 changes: 45 additions & 0 deletions tools/net/ynl/generated/Makefile
Original file line number Diff line number Diff line change
@@ -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 changes: 28 additions & 0 deletions tools/net/ynl/lib/Makefile
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 86878f1

Please sign in to comment.