forked from mirrors/linux
		
	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:
		
							parent
							
								
									a99bfdf647
								
							
						
					
					
						commit
						86878f14d7
					
				
					 7 changed files with 1310 additions and 1 deletions
				
			
		| 
						 | 
					@ -78,3 +78,82 @@ to see other examples.
 | 
				
			||||||
The code generation itself is performed by ``tools/net/ynl/ynl-gen-c.py``
 | 
					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
 | 
					but it takes a few arguments so calling it directly for each file
 | 
				
			||||||
quickly becomes tedious.
 | 
					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
									
								
							| 
						 | 
					@ -0,0 +1,901 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <poll.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libmnl/libmnl.h>
 | 
				
			||||||
 | 
					#include <linux/genetlink.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ynl.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ARRAY_SIZE(arr)		(sizeof(arr) / sizeof(*arr))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define __yerr_msg(yse, _msg...)					\
 | 
				
			||||||
 | 
						({								\
 | 
				
			||||||
 | 
							struct ynl_error *_yse = (yse);				\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
							if (_yse) {						\
 | 
				
			||||||
 | 
								snprintf(_yse->msg, sizeof(_yse->msg) - 1,  _msg); \
 | 
				
			||||||
 | 
								_yse->msg[sizeof(_yse->msg) - 1] = 0;		\
 | 
				
			||||||
 | 
							}							\
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define __yerr_code(yse, _code...)		\
 | 
				
			||||||
 | 
						({					\
 | 
				
			||||||
 | 
							struct ynl_error *_yse = (yse);	\
 | 
				
			||||||
 | 
											\
 | 
				
			||||||
 | 
							if (_yse) {			\
 | 
				
			||||||
 | 
								_yse->code = _code;	\
 | 
				
			||||||
 | 
							}				\
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define __yerr(yse, _code, _msg...)		\
 | 
				
			||||||
 | 
						({					\
 | 
				
			||||||
 | 
							__yerr_msg(yse, _msg);		\
 | 
				
			||||||
 | 
							__yerr_code(yse, _code);	\
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define __perr(yse, _msg)		__yerr(yse, errno, _msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define yerr_msg(_ys, _msg...)		__yerr_msg(&(_ys)->err, _msg)
 | 
				
			||||||
 | 
					#define yerr(_ys, _code, _msg...)	__yerr(&(_ys)->err, _code, _msg)
 | 
				
			||||||
 | 
					#define perr(_ys, _msg)			__yerr(&(_ys)->err, errno, _msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* -- Netlink boiler plate */
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					ynl_err_walk_report_one(struct ynl_policy_nest *policy, unsigned int type,
 | 
				
			||||||
 | 
								char *str, int str_sz, int *n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!policy) {
 | 
				
			||||||
 | 
							if (*n < str_sz)
 | 
				
			||||||
 | 
								*n += snprintf(str, str_sz, "!policy");
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (type > policy->max_attr) {
 | 
				
			||||||
 | 
							if (*n < str_sz)
 | 
				
			||||||
 | 
								*n += snprintf(str, str_sz, "!oob");
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!policy->table[type].name) {
 | 
				
			||||||
 | 
							if (*n < str_sz)
 | 
				
			||||||
 | 
								*n += snprintf(str, str_sz, "!name");
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (*n < str_sz)
 | 
				
			||||||
 | 
							*n += snprintf(str, str_sz - *n,
 | 
				
			||||||
 | 
								       ".%s", policy->table[type].name);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
 | 
				
			||||||
 | 
						     struct ynl_policy_nest *policy, char *str, int str_sz,
 | 
				
			||||||
 | 
						     struct ynl_policy_nest **nest_pol)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int astart_off, aend_off;
 | 
				
			||||||
 | 
						const struct nlattr *attr;
 | 
				
			||||||
 | 
						unsigned int data_len;
 | 
				
			||||||
 | 
						unsigned int type;
 | 
				
			||||||
 | 
						bool found = false;
 | 
				
			||||||
 | 
						int n = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!policy) {
 | 
				
			||||||
 | 
							if (n < str_sz)
 | 
				
			||||||
 | 
								n += snprintf(str, str_sz, "!policy");
 | 
				
			||||||
 | 
							return n;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data_len = end - start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mnl_attr_for_each_payload(start, data_len) {
 | 
				
			||||||
 | 
							astart_off = (char *)attr - (char *)start;
 | 
				
			||||||
 | 
							aend_off = astart_off + mnl_attr_get_payload_len(attr);
 | 
				
			||||||
 | 
							if (aend_off <= off)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							found = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!found)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						off -= astart_off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type = mnl_attr_get_type(attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ynl_err_walk_report_one(policy, type, str, str_sz, &n))
 | 
				
			||||||
 | 
							return n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!off) {
 | 
				
			||||||
 | 
							if (nest_pol)
 | 
				
			||||||
 | 
								*nest_pol = policy->table[type].nest;
 | 
				
			||||||
 | 
							return n;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!policy->table[type].nest) {
 | 
				
			||||||
 | 
							if (n < str_sz)
 | 
				
			||||||
 | 
								n += snprintf(str, str_sz, "!nest");
 | 
				
			||||||
 | 
							return n;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						off -= sizeof(struct nlattr);
 | 
				
			||||||
 | 
						start =  mnl_attr_get_payload(attr);
 | 
				
			||||||
 | 
						end = start + mnl_attr_get_payload_len(attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return n + ynl_err_walk(ys, start, end, off, policy->table[type].nest,
 | 
				
			||||||
 | 
									&str[n], str_sz - n, nest_pol);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NLMSGERR_ATTR_MISS_TYPE (NLMSGERR_ATTR_POLICY + 1)
 | 
				
			||||||
 | 
					#define NLMSGERR_ATTR_MISS_NEST (NLMSGERR_ATTR_POLICY + 2)
 | 
				
			||||||
 | 
					#define NLMSGERR_ATTR_MAX (NLMSGERR_ATTR_MAX + 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					ynl_ext_ack_check(struct ynl_sock *ys, const struct nlmsghdr *nlh,
 | 
				
			||||||
 | 
							  unsigned int hlen)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
 | 
				
			||||||
 | 
						char miss_attr[sizeof(ys->err.msg)];
 | 
				
			||||||
 | 
						char bad_attr[sizeof(ys->err.msg)];
 | 
				
			||||||
 | 
						const struct nlattr *attr;
 | 
				
			||||||
 | 
						const char *str = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
 | 
				
			||||||
 | 
							return MNL_CB_OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mnl_attr_for_each(attr, nlh, hlen) {
 | 
				
			||||||
 | 
							unsigned int len, type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							len = mnl_attr_get_payload_len(attr);
 | 
				
			||||||
 | 
							type = mnl_attr_get_type(attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (type > NLMSGERR_ATTR_MAX)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tb[type] = attr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (type) {
 | 
				
			||||||
 | 
							case NLMSGERR_ATTR_OFFS:
 | 
				
			||||||
 | 
							case NLMSGERR_ATTR_MISS_TYPE:
 | 
				
			||||||
 | 
							case NLMSGERR_ATTR_MISS_NEST:
 | 
				
			||||||
 | 
								if (len != sizeof(__u32))
 | 
				
			||||||
 | 
									return MNL_CB_ERROR;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case NLMSGERR_ATTR_MSG:
 | 
				
			||||||
 | 
								str = mnl_attr_get_payload(attr);
 | 
				
			||||||
 | 
								if (str[len - 1])
 | 
				
			||||||
 | 
									return MNL_CB_ERROR;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bad_attr[0] = '\0';
 | 
				
			||||||
 | 
						miss_attr[0] = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tb[NLMSGERR_ATTR_OFFS]) {
 | 
				
			||||||
 | 
							unsigned int n, off;
 | 
				
			||||||
 | 
							void *start, *end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ys->err.attr_offs = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							n = snprintf(bad_attr, sizeof(bad_attr), "%sbad attribute: ",
 | 
				
			||||||
 | 
								     str ? " (" : "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							start = mnl_nlmsg_get_payload_offset(ys->nlh,
 | 
				
			||||||
 | 
											     sizeof(struct genlmsghdr));
 | 
				
			||||||
 | 
							end = mnl_nlmsg_get_payload_tail(ys->nlh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							off = ys->err.attr_offs;
 | 
				
			||||||
 | 
							off -= sizeof(struct nlmsghdr);
 | 
				
			||||||
 | 
							off -= sizeof(struct genlmsghdr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							n += ynl_err_walk(ys, start, end, off, ys->req_policy,
 | 
				
			||||||
 | 
									  &bad_attr[n], sizeof(bad_attr) - n, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (n >= sizeof(bad_attr))
 | 
				
			||||||
 | 
								n = sizeof(bad_attr) - 1;
 | 
				
			||||||
 | 
							bad_attr[n] = '\0';
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (tb[NLMSGERR_ATTR_MISS_TYPE]) {
 | 
				
			||||||
 | 
							struct ynl_policy_nest *nest_pol = NULL;
 | 
				
			||||||
 | 
							unsigned int n, off, type;
 | 
				
			||||||
 | 
							void *start, *end;
 | 
				
			||||||
 | 
							int n2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							type = mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_TYPE]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							n = snprintf(miss_attr, sizeof(miss_attr), "%smissing attribute: ",
 | 
				
			||||||
 | 
								     bad_attr[0] ? ", " : (str ? " (" : ""));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							start = mnl_nlmsg_get_payload_offset(ys->nlh,
 | 
				
			||||||
 | 
											     sizeof(struct genlmsghdr));
 | 
				
			||||||
 | 
							end = mnl_nlmsg_get_payload_tail(ys->nlh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nest_pol = ys->req_policy;
 | 
				
			||||||
 | 
							if (tb[NLMSGERR_ATTR_MISS_NEST]) {
 | 
				
			||||||
 | 
								off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_NEST]);
 | 
				
			||||||
 | 
								off -= sizeof(struct nlmsghdr);
 | 
				
			||||||
 | 
								off -= sizeof(struct genlmsghdr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								n += ynl_err_walk(ys, start, end, off, ys->req_policy,
 | 
				
			||||||
 | 
										  &miss_attr[n], sizeof(miss_attr) - n,
 | 
				
			||||||
 | 
										  &nest_pol);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							n2 = 0;
 | 
				
			||||||
 | 
							ynl_err_walk_report_one(nest_pol, type, &miss_attr[n],
 | 
				
			||||||
 | 
										sizeof(miss_attr) - n, &n2);
 | 
				
			||||||
 | 
							n += n2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (n >= sizeof(miss_attr))
 | 
				
			||||||
 | 
								n = sizeof(miss_attr) - 1;
 | 
				
			||||||
 | 
							miss_attr[n] = '\0';
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Implicitly depend on ys->err.code already set */
 | 
				
			||||||
 | 
						if (str)
 | 
				
			||||||
 | 
							yerr_msg(ys, "Kernel %s: '%s'%s%s%s",
 | 
				
			||||||
 | 
								 ys->err.code ? "error" : "warning",
 | 
				
			||||||
 | 
								 str, bad_attr, miss_attr,
 | 
				
			||||||
 | 
								 bad_attr[0] || miss_attr[0] ? ")" : "");
 | 
				
			||||||
 | 
						else if (bad_attr[0] || miss_attr[0])
 | 
				
			||||||
 | 
							yerr_msg(ys, "Kernel %s: %s%s",
 | 
				
			||||||
 | 
								 ys->err.code ? "error" : "warning",
 | 
				
			||||||
 | 
								 bad_attr, miss_attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return MNL_CB_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_cb_error(const struct nlmsghdr *nlh, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
 | 
				
			||||||
 | 
						struct ynl_parse_arg *yarg = data;
 | 
				
			||||||
 | 
						unsigned int hlen;
 | 
				
			||||||
 | 
						int code;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						code = err->error >= 0 ? err->error : -err->error;
 | 
				
			||||||
 | 
						yarg->ys->err.code = code;
 | 
				
			||||||
 | 
						errno = code;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hlen = sizeof(*err);
 | 
				
			||||||
 | 
						if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
 | 
				
			||||||
 | 
							hlen += mnl_nlmsg_get_payload_len(&err->msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ynl_ext_ack_check(yarg->ys, nlh, hlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return code ? MNL_CB_ERROR : MNL_CB_STOP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_cb_done(const struct nlmsghdr *nlh, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_parse_arg *yarg = data;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = *(int *)NLMSG_DATA(nlh);
 | 
				
			||||||
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							yarg->ys->err.code = -err;
 | 
				
			||||||
 | 
							errno = -err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ynl_ext_ack_check(yarg->ys, nlh, sizeof(int));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return MNL_CB_ERROR;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return MNL_CB_STOP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_cb_noop(const struct nlmsghdr *nlh, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return MNL_CB_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mnl_cb_t ynl_cb_array[NLMSG_MIN_TYPE] = {
 | 
				
			||||||
 | 
						[NLMSG_NOOP]	= ynl_cb_noop,
 | 
				
			||||||
 | 
						[NLMSG_ERROR]	= ynl_cb_error,
 | 
				
			||||||
 | 
						[NLMSG_DONE]	= ynl_cb_done,
 | 
				
			||||||
 | 
						[NLMSG_OVERRUN]	= ynl_cb_noop,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Attribute validation */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_policy_attr *policy;
 | 
				
			||||||
 | 
						unsigned int type, len;
 | 
				
			||||||
 | 
						unsigned char *data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data = mnl_attr_get_payload(attr);
 | 
				
			||||||
 | 
						len = mnl_attr_get_payload_len(attr);
 | 
				
			||||||
 | 
						type = mnl_attr_get_type(attr);
 | 
				
			||||||
 | 
						if (type > yarg->rsp_policy->max_attr) {
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_INTERNAL,
 | 
				
			||||||
 | 
							     "Internal error, validating unknown attribute");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						policy = &yarg->rsp_policy->table[type];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (policy->type) {
 | 
				
			||||||
 | 
						case YNL_PT_REJECT:
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Rejected attribute (%s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						case YNL_PT_IGNORE:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case YNL_PT_U8:
 | 
				
			||||||
 | 
							if (len == sizeof(__u8))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Invalid attribute (u8 %s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						case YNL_PT_U16:
 | 
				
			||||||
 | 
							if (len == sizeof(__u16))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Invalid attribute (u16 %s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						case YNL_PT_U32:
 | 
				
			||||||
 | 
							if (len == sizeof(__u32))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Invalid attribute (u32 %s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						case YNL_PT_U64:
 | 
				
			||||||
 | 
							if (len == sizeof(__u64))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Invalid attribute (u64 %s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						case YNL_PT_FLAG:
 | 
				
			||||||
 | 
							/* Let flags grow into real attrs, why not.. */
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case YNL_PT_NEST:
 | 
				
			||||||
 | 
							if (!len || len >= sizeof(*attr))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Invalid attribute (nest %s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						case YNL_PT_BINARY:
 | 
				
			||||||
 | 
							if (!policy->len || len == policy->len)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Invalid attribute (binary %s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						case YNL_PT_NUL_STR:
 | 
				
			||||||
 | 
							if ((!policy->len || len <= policy->len) && !data[len - 1])
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Invalid attribute (string %s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
 | 
				
			||||||
 | 
							     "Invalid attribute (unknown %s)", policy->name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Generic code */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void ynl_err_reset(struct ynl_sock *ys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ys->err.code = 0;
 | 
				
			||||||
 | 
						ys->err.attr_offs = 0;
 | 
				
			||||||
 | 
						ys->err.msg[0] = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct nlmsghdr *ynl_msg_start(struct ynl_sock *ys, __u32 id, __u16 flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nlmsghdr *nlh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ynl_err_reset(ys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nlh = ys->nlh = mnl_nlmsg_put_header(ys->tx_buf);
 | 
				
			||||||
 | 
						nlh->nlmsg_type	= id;
 | 
				
			||||||
 | 
						nlh->nlmsg_flags = flags;
 | 
				
			||||||
 | 
						nlh->nlmsg_seq = ++ys->seq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nlh;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct nlmsghdr *
 | 
				
			||||||
 | 
					ynl_gemsg_start(struct ynl_sock *ys, __u32 id, __u16 flags,
 | 
				
			||||||
 | 
							__u8 cmd, __u8 version)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct genlmsghdr gehdr;
 | 
				
			||||||
 | 
						struct nlmsghdr *nlh;
 | 
				
			||||||
 | 
						void *data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nlh = ynl_msg_start(ys, id, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(&gehdr, 0, sizeof(gehdr));
 | 
				
			||||||
 | 
						gehdr.cmd = cmd;
 | 
				
			||||||
 | 
						gehdr.version = version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data = mnl_nlmsg_put_extra_header(nlh, sizeof(gehdr));
 | 
				
			||||||
 | 
						memcpy(data, &gehdr, sizeof(gehdr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nlh;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ynl_msg_start_req(struct ynl_sock *ys, __u32 id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ynl_msg_start_dump(struct ynl_sock *ys, __u32 id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct nlmsghdr *
 | 
				
			||||||
 | 
					ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK, cmd, version);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct nlmsghdr *
 | 
				
			||||||
 | 
					ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
 | 
				
			||||||
 | 
								       cmd, version);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_recv_ack(struct ynl_sock *ys, int ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!ret) {
 | 
				
			||||||
 | 
							yerr(ys, YNL_ERROR_EXPECT_ACK,
 | 
				
			||||||
 | 
							     "Expecting an ACK but nothing received");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);
 | 
				
			||||||
 | 
						if (ret < 0) {
 | 
				
			||||||
 | 
							perr(ys, "Socket receive failed");
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mnl_cb_run(ys->rx_buf, ret, ys->seq, ys->portid,
 | 
				
			||||||
 | 
								  ynl_cb_null, ys);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_cb_null(const struct nlmsghdr *nlh, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_parse_arg *yarg = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						yerr(yarg->ys, YNL_ERROR_UNEXPECT_MSG,
 | 
				
			||||||
 | 
						     "Received a message when none were expected");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return MNL_CB_ERROR;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Init/fini and genetlink boiler plate */
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					ynl_get_family_info_mcast(struct ynl_sock *ys, const struct nlattr *mcasts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct nlattr *entry, *attr;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mnl_attr_for_each_nested(attr, mcasts)
 | 
				
			||||||
 | 
							ys->n_mcast_groups++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ys->n_mcast_groups)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ys->mcast_groups = calloc(ys->n_mcast_groups,
 | 
				
			||||||
 | 
									  sizeof(*ys->mcast_groups));
 | 
				
			||||||
 | 
						if (!ys->mcast_groups)
 | 
				
			||||||
 | 
							return MNL_CB_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i = 0;
 | 
				
			||||||
 | 
						mnl_attr_for_each_nested(entry, mcasts) {
 | 
				
			||||||
 | 
							mnl_attr_for_each_nested(attr, entry) {
 | 
				
			||||||
 | 
								if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GRP_ID)
 | 
				
			||||||
 | 
									ys->mcast_groups[i].id = mnl_attr_get_u32(attr);
 | 
				
			||||||
 | 
								if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GRP_NAME) {
 | 
				
			||||||
 | 
									strncpy(ys->mcast_groups[i].name,
 | 
				
			||||||
 | 
										mnl_attr_get_str(attr),
 | 
				
			||||||
 | 
										GENL_NAMSIZ - 1);
 | 
				
			||||||
 | 
									ys->mcast_groups[i].name[GENL_NAMSIZ - 1] = 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_get_family_info_cb(const struct nlmsghdr *nlh, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_parse_arg *yarg = data;
 | 
				
			||||||
 | 
						struct ynl_sock *ys = yarg->ys;
 | 
				
			||||||
 | 
						const struct nlattr *attr;
 | 
				
			||||||
 | 
						bool found_id = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
 | 
				
			||||||
 | 
							if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GROUPS)
 | 
				
			||||||
 | 
								if (ynl_get_family_info_mcast(ys, attr))
 | 
				
			||||||
 | 
									return MNL_CB_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (mnl_attr_get_type(attr) != CTRL_ATTR_FAMILY_ID)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (mnl_attr_get_payload_len(attr) != sizeof(__u16)) {
 | 
				
			||||||
 | 
								yerr(ys, YNL_ERROR_ATTR_INVALID, "Invalid family ID");
 | 
				
			||||||
 | 
								return MNL_CB_ERROR;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ys->family_id = mnl_attr_get_u16(attr);
 | 
				
			||||||
 | 
							found_id = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!found_id) {
 | 
				
			||||||
 | 
							yerr(ys, YNL_ERROR_ATTR_MISSING, "Family ID missing");
 | 
				
			||||||
 | 
							return MNL_CB_ERROR;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return MNL_CB_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_sock_read_family(struct ynl_sock *ys, const char *family_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_parse_arg yarg = { .ys = ys, };
 | 
				
			||||||
 | 
						struct nlmsghdr *nlh;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nlh = ynl_gemsg_start_req(ys, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 1);
 | 
				
			||||||
 | 
						mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mnl_socket_sendto(ys->sock, nlh, nlh->nlmsg_len);
 | 
				
			||||||
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							perr(ys, "failed to request socket family info");
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);
 | 
				
			||||||
 | 
						if (err <= 0) {
 | 
				
			||||||
 | 
							perr(ys, "failed to receive the socket family info");
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = mnl_cb_run2(ys->rx_buf, err, ys->seq, ys->portid,
 | 
				
			||||||
 | 
								  ynl_get_family_info_cb, &yarg,
 | 
				
			||||||
 | 
								  ynl_cb_array, ARRAY_SIZE(ynl_cb_array));
 | 
				
			||||||
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							free(ys->mcast_groups);
 | 
				
			||||||
 | 
							perr(ys, "failed to receive the socket family info - no such family?");
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ynl_recv_ack(ys, err);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ynl_sock *
 | 
				
			||||||
 | 
					ynl_sock_create(const struct ynl_family *yf, struct ynl_error *yse)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_sock *ys;
 | 
				
			||||||
 | 
						int one = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ys = malloc(sizeof(*ys) + 2 * MNL_SOCKET_BUFFER_SIZE);
 | 
				
			||||||
 | 
						if (!ys)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						memset(ys, 0, sizeof(*ys));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ys->family = yf;
 | 
				
			||||||
 | 
						ys->tx_buf = &ys->raw_buf[0];
 | 
				
			||||||
 | 
						ys->rx_buf = &ys->raw_buf[MNL_SOCKET_BUFFER_SIZE];
 | 
				
			||||||
 | 
						ys->ntf_last_next = &ys->ntf_first;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ys->sock = mnl_socket_open(NETLINK_GENERIC);
 | 
				
			||||||
 | 
						if (!ys->sock) {
 | 
				
			||||||
 | 
							__perr(yse, "failed to create a netlink socket");
 | 
				
			||||||
 | 
							goto err_free_sock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mnl_socket_setsockopt(ys->sock, NETLINK_CAP_ACK,
 | 
				
			||||||
 | 
									  &one, sizeof(one))) {
 | 
				
			||||||
 | 
							__perr(yse, "failed to enable netlink ACK");
 | 
				
			||||||
 | 
							goto err_close_sock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (mnl_socket_setsockopt(ys->sock, NETLINK_EXT_ACK,
 | 
				
			||||||
 | 
									  &one, sizeof(one))) {
 | 
				
			||||||
 | 
							__perr(yse, "failed to enable netlink ext ACK");
 | 
				
			||||||
 | 
							goto err_close_sock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ys->seq = random();
 | 
				
			||||||
 | 
						ys->portid = mnl_socket_get_portid(ys->sock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ynl_sock_read_family(ys, yf->name)) {
 | 
				
			||||||
 | 
							if (yse)
 | 
				
			||||||
 | 
								memcpy(yse, &ys->err, sizeof(*yse));
 | 
				
			||||||
 | 
							goto err_close_sock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err_close_sock:
 | 
				
			||||||
 | 
						mnl_socket_close(ys->sock);
 | 
				
			||||||
 | 
					err_free_sock:
 | 
				
			||||||
 | 
						free(ys);
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ynl_sock_destroy(struct ynl_sock *ys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_ntf_base_type *ntf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mnl_socket_close(ys->sock);
 | 
				
			||||||
 | 
						while ((ntf = ynl_ntf_dequeue(ys)))
 | 
				
			||||||
 | 
							ynl_ntf_free(ntf);
 | 
				
			||||||
 | 
						free(ys->mcast_groups);
 | 
				
			||||||
 | 
						free(ys);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* YNL multicast handling */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ynl_ntf_free(struct ynl_ntf_base_type *ntf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ntf->free(ntf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_subscribe(struct ynl_sock *ys, const char *grp_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ys->n_mcast_groups; i++)
 | 
				
			||||||
 | 
							if (!strcmp(ys->mcast_groups[i].name, grp_name))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
						if (i == ys->n_mcast_groups) {
 | 
				
			||||||
 | 
							yerr(ys, ENOENT, "Multicast group '%s' not found", grp_name);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mnl_socket_setsockopt(ys->sock, NETLINK_ADD_MEMBERSHIP,
 | 
				
			||||||
 | 
									    &ys->mcast_groups[i].id,
 | 
				
			||||||
 | 
									    sizeof(ys->mcast_groups[i].id));
 | 
				
			||||||
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							perr(ys, "Subscribing to multicast group failed");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_socket_get_fd(struct ynl_sock *ys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return mnl_socket_get_fd(ys->sock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_ntf_base_type *ntf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ynl_has_ntf(ys))
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ntf = ys->ntf_first;
 | 
				
			||||||
 | 
						ys->ntf_first = ntf->next;
 | 
				
			||||||
 | 
						if (ys->ntf_last_next == &ntf->next)
 | 
				
			||||||
 | 
							ys->ntf_last_next = &ys->ntf_first;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ntf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_ntf_parse(struct ynl_sock *ys, const struct nlmsghdr *nlh)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_parse_arg yarg = { .ys = ys, };
 | 
				
			||||||
 | 
						const struct ynl_ntf_info *info;
 | 
				
			||||||
 | 
						struct ynl_ntf_base_type *rsp;
 | 
				
			||||||
 | 
						struct genlmsghdr *gehdr;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gehdr = mnl_nlmsg_get_payload(nlh);
 | 
				
			||||||
 | 
						if (gehdr->cmd >= ys->family->ntf_info_size)
 | 
				
			||||||
 | 
							return MNL_CB_ERROR;
 | 
				
			||||||
 | 
						info = &ys->family->ntf_info[gehdr->cmd];
 | 
				
			||||||
 | 
						if (!info->cb)
 | 
				
			||||||
 | 
							return MNL_CB_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rsp = calloc(1, info->alloc_sz);
 | 
				
			||||||
 | 
						rsp->free = info->free;
 | 
				
			||||||
 | 
						yarg.data = rsp->data;
 | 
				
			||||||
 | 
						yarg.rsp_policy = info->policy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = info->cb(nlh, &yarg);
 | 
				
			||||||
 | 
						if (ret <= MNL_CB_STOP)
 | 
				
			||||||
 | 
							goto err_free;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rsp->family = nlh->nlmsg_type;
 | 
				
			||||||
 | 
						rsp->cmd = gehdr->cmd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*ys->ntf_last_next = rsp;
 | 
				
			||||||
 | 
						ys->ntf_last_next = &rsp->next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return MNL_CB_OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err_free:
 | 
				
			||||||
 | 
						info->free(rsp);
 | 
				
			||||||
 | 
						return MNL_CB_ERROR;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_ntf_trampoline(const struct nlmsghdr *nlh, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ynl_ntf_parse((struct ynl_sock *)data, nlh);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_ntf_check(struct ynl_sock *ys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ssize_t len;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							/* libmnl doesn't let us pass flags to the recv to make
 | 
				
			||||||
 | 
							 * it non-blocking so we need to poll() or peek() :|
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							struct pollfd pfd = { };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pfd.fd = mnl_socket_get_fd(ys->sock);
 | 
				
			||||||
 | 
							pfd.events = POLLIN;
 | 
				
			||||||
 | 
							err = poll(&pfd, 1, 1);
 | 
				
			||||||
 | 
							if (err < 1)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
 | 
				
			||||||
 | 
										  MNL_SOCKET_BUFFER_SIZE);
 | 
				
			||||||
 | 
							if (len < 0)
 | 
				
			||||||
 | 
								return len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
 | 
				
			||||||
 | 
									  ynl_ntf_trampoline, ys,
 | 
				
			||||||
 | 
									  ynl_cb_array, NLMSG_MIN_TYPE);
 | 
				
			||||||
 | 
							if (err < 0)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
						} while (err > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* YNL specific helpers used by the auto-generated code */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ynl_dump_list_type *YNL_LIST_END = (void *)(0xb4d123);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						yerr(ys, YNL_ERROR_UNKNOWN_NTF,
 | 
				
			||||||
 | 
						     "Unknown notification message type '%d'", cmd);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						yerr(yarg->ys, YNL_ERROR_INV_RESP, "Error parsing response: %s", msg);
 | 
				
			||||||
 | 
						return MNL_CB_ERROR;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					ynl_check_alien(struct ynl_sock *ys, const struct nlmsghdr *nlh, __u32 rsp_cmd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct genlmsghdr *gehdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mnl_nlmsg_get_payload_len(nlh) < sizeof(*gehdr)) {
 | 
				
			||||||
 | 
							yerr(ys, YNL_ERROR_INV_RESP,
 | 
				
			||||||
 | 
							     "Kernel responded with truncated message");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gehdr = mnl_nlmsg_get_payload(nlh);
 | 
				
			||||||
 | 
						if (gehdr->cmd != rsp_cmd)
 | 
				
			||||||
 | 
							return ynl_ntf_parse(ys, nlh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_req_trampoline(const struct nlmsghdr *nlh, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_req_state *yrs = data;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = ynl_check_alien(yrs->yarg.ys, nlh, yrs->rsp_cmd);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return yrs->cb(nlh, &yrs->yarg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
 | 
				
			||||||
 | 
						     struct ynl_req_state *yrs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ssize_t len;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mnl_socket_sendto(ys->sock, req_nlh, req_nlh->nlmsg_len);
 | 
				
			||||||
 | 
						if (err < 0)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
 | 
				
			||||||
 | 
										  MNL_SOCKET_BUFFER_SIZE);
 | 
				
			||||||
 | 
							if (len < 0)
 | 
				
			||||||
 | 
								return len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
 | 
				
			||||||
 | 
									  ynl_req_trampoline, yrs,
 | 
				
			||||||
 | 
									  ynl_cb_array, NLMSG_MIN_TYPE);
 | 
				
			||||||
 | 
							if (err < 0)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
						} while (err > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ynl_dump_trampoline(const struct nlmsghdr *nlh, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ynl_dump_state *ds = data;
 | 
				
			||||||
 | 
						struct ynl_dump_list_type *obj;
 | 
				
			||||||
 | 
						struct ynl_parse_arg yarg = {};
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = ynl_check_alien(ds->ys, nlh, ds->rsp_cmd);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						obj = calloc(1, ds->alloc_sz);
 | 
				
			||||||
 | 
						if (!obj)
 | 
				
			||||||
 | 
							return MNL_CB_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ds->first)
 | 
				
			||||||
 | 
							ds->first = obj;
 | 
				
			||||||
 | 
						if (ds->last)
 | 
				
			||||||
 | 
							ds->last->next = obj;
 | 
				
			||||||
 | 
						ds->last = obj;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						yarg.ys = ds->ys;
 | 
				
			||||||
 | 
						yarg.rsp_policy = ds->rsp_policy;
 | 
				
			||||||
 | 
						yarg.data = &obj->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ds->cb(nlh, &yarg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void *ynl_dump_end(struct ynl_dump_state *ds)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!ds->first)
 | 
				
			||||||
 | 
							return YNL_LIST_END;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ds->last->next = YNL_LIST_END;
 | 
				
			||||||
 | 
						return ds->first;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
 | 
				
			||||||
 | 
							  struct ynl_dump_state *yds)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ssize_t len;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mnl_socket_sendto(ys->sock, req_nlh, req_nlh->nlmsg_len);
 | 
				
			||||||
 | 
						if (err < 0)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
 | 
				
			||||||
 | 
										  MNL_SOCKET_BUFFER_SIZE);
 | 
				
			||||||
 | 
							if (len < 0)
 | 
				
			||||||
 | 
								goto err_close_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
 | 
				
			||||||
 | 
									  ynl_dump_trampoline, yds,
 | 
				
			||||||
 | 
									  ynl_cb_array, NLMSG_MIN_TYPE);
 | 
				
			||||||
 | 
							if (err < 0)
 | 
				
			||||||
 | 
								goto err_close_list;
 | 
				
			||||||
 | 
						} while (err > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						yds->first = ynl_dump_end(yds);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err_close_list:
 | 
				
			||||||
 | 
						yds->first = ynl_dump_end(yds);
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										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)))))
 | 
					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
 | 
					for f in $files; do
 | 
				
			||||||
    # params:     0       1      2     3
 | 
					    # params:     0       1      2     3
 | 
				
			||||||
    #         $YAML YNL-GEN kernel $mode
 | 
					    #         $YAML YNL-GEN kernel $mode
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue