Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os 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>

+1310 -1
+79
Documentation/userspace-api/netlink/intro-specs.rst
··· 78 78 The code generation itself is performed by ``tools/net/ynl/ynl-gen-c.py`` 79 79 but it takes a few arguments so calling it directly for each file 80 80 quickly becomes tedious. 81 + 82 + YNL lib 83 + ======= 84 + 85 + ``tools/net/ynl/lib/`` contains an implementation of a C library 86 + (based on libmnl) which integrates with code generated by 87 + ``tools/net/ynl/ynl-gen-c.py`` to create easy to use netlink wrappers. 88 + 89 + YNL basics 90 + ---------- 91 + 92 + The YNL library consists of two parts - the generic code (functions 93 + prefix by ``ynl_``) and per-family auto-generated code (prefixed 94 + with the name of the family). 95 + 96 + To create a YNL socket call ynl_sock_create() passing the family 97 + struct (family structs are exported by the auto-generated code). 98 + ynl_sock_destroy() closes the socket. 99 + 100 + YNL requests 101 + ------------ 102 + 103 + Steps for issuing YNL requests are best explained on an example. 104 + All the functions and types in this example come from the auto-generated 105 + code (for the netdev family in this case): 106 + 107 + .. code-block:: c 108 + 109 + // 0. Request and response pointers 110 + struct netdev_dev_get_req *req; 111 + struct netdev_dev_get_rsp *d; 112 + 113 + // 1. Allocate a request 114 + req = netdev_dev_get_req_alloc(); 115 + // 2. Set request parameters (as needed) 116 + netdev_dev_get_req_set_ifindex(req, ifindex); 117 + 118 + // 3. Issues the request 119 + d = netdev_dev_get(ys, req); 120 + // 4. Free the request arguments 121 + netdev_dev_get_req_free(req); 122 + // 5. Error check (the return value from step 3) 123 + if (!d) { 124 + // 6. Print the YNL-generated error 125 + fprintf(stderr, "YNL: %s\n", ys->err.msg); 126 + return -1; 127 + } 128 + 129 + // ... do stuff with the response @d 130 + 131 + // 7. Free response 132 + netdev_dev_get_rsp_free(d); 133 + 134 + YNL dumps 135 + --------- 136 + 137 + Performing dumps follows similar pattern as requests. 138 + Dumps return a list of objects terminated by a special marker, 139 + or NULL on error. Use ``ynl_dump_foreach()`` to iterate over 140 + the result. 141 + 142 + YNL notifications 143 + ----------------- 144 + 145 + YNL lib supports using the same socket for notifications and 146 + requests. In case notifications arrive during processing of a request 147 + they are queued internally and can be retrieved at a later time. 148 + 149 + To subscribed to notifications use ``ynl_subscribe()``. 150 + The notifications have to be read out from the socket, 151 + ``ynl_socket_get_fd()`` returns the underlying socket fd which can 152 + be plugged into appropriate asynchronous IO API like ``poll``, 153 + or ``select``. 154 + 155 + Notifications can be retrieved using ``ynl_ntf_dequeue()`` and have 156 + to be freed using ``ynl_ntf_free()``. Since we don't know the notification 157 + type upfront the notifications are returned as ``struct ynl_ntf_base_type *`` 158 + and user is expected to cast them to the appropriate full type based 159 + on the ``cmd`` member.
+19
tools/net/ynl/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + SUBDIRS = lib generated samples 4 + 5 + all: $(SUBDIRS) 6 + 7 + $(SUBDIRS): 8 + @if [ -f "$@/Makefile" ] ; then \ 9 + $(MAKE) -C $@ ; \ 10 + fi 11 + 12 + clean hardclean: 13 + @for dir in $(SUBDIRS) ; do \ 14 + if [ -f "$$dir/Makefile" ] ; then \ 15 + $(MAKE) -C $$dir $@; \ 16 + fi \ 17 + done 18 + 19 + .PHONY: clean all $(SUBDIRS)
+45
tools/net/ynl/generated/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + CC=gcc 4 + CFLAGS=-std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow \ 5 + -I../lib/ 6 + ifeq ("$(DEBUG)","1") 7 + CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan 8 + endif 9 + 10 + TOOL:=../ynl-gen-c.py 11 + 12 + GENS:= 13 + SRCS=$(patsubst %,%-user.c,${GENS}) 14 + HDRS=$(patsubst %,%-user.h,${GENS}) 15 + OBJS=$(patsubst %,%-user.o,${GENS}) 16 + 17 + all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) regen 18 + 19 + protos.a: $(OBJS) 20 + @echo -e "\tAR $@" 21 + @ar rcs $@ $(OBJS) 22 + 23 + %-user.h: ../../../../Documentation/netlink/specs/%.yaml $(TOOL) 24 + @echo -e "\tGEN $@" 25 + @$(TOOL) --mode user --header --spec $< > $@ 26 + 27 + %-user.c: ../../../../Documentation/netlink/specs/%.yaml $(TOOL) 28 + @echo -e "\tGEN $@" 29 + @$(TOOL) --mode user --source --spec $< > $@ 30 + 31 + %-user.o: %-user.c %-user.h 32 + @echo -e "\tCC $@" 33 + @$(COMPILE.c) -c -o $@ $< 34 + 35 + clean: 36 + rm -f *.o 37 + 38 + hardclean: clean 39 + rm -f *.c *.h *.a 40 + 41 + regen: 42 + @../ynl-regen.sh 43 + 44 + .PHONY: all clean hardclean regen 45 + .DEFAULT_GOAL: all
+28
tools/net/ynl/lib/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + CC=gcc 4 + CFLAGS=-std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow 5 + ifeq ("$(DEBUG)","1") 6 + CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan 7 + endif 8 + 9 + SRCS=$(wildcard *.c) 10 + OBJS=$(patsubst %.c,%.o,${SRCS}) 11 + 12 + include $(wildcard *.d) 13 + 14 + all: ynl.a 15 + 16 + ynl.a: $(OBJS) 17 + ar rcs $@ $(OBJS) 18 + clean: 19 + rm -f *.o *.d *~ 20 + 21 + hardclean: clean 22 + rm -f *.a 23 + 24 + %.o: %.c 25 + $(COMPILE.c) -MMD -c -o $@ $< 26 + 27 + .PHONY: all clean 28 + .DEFAULT_GOAL=all
+901
tools/net/ynl/lib/ynl.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 + #include <errno.h> 3 + #include <poll.h> 4 + #include <string.h> 5 + #include <stdlib.h> 6 + #include <linux/types.h> 7 + 8 + #include <libmnl/libmnl.h> 9 + #include <linux/genetlink.h> 10 + 11 + #include "ynl.h" 12 + 13 + #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*arr)) 14 + 15 + #define __yerr_msg(yse, _msg...) \ 16 + ({ \ 17 + struct ynl_error *_yse = (yse); \ 18 + \ 19 + if (_yse) { \ 20 + snprintf(_yse->msg, sizeof(_yse->msg) - 1, _msg); \ 21 + _yse->msg[sizeof(_yse->msg) - 1] = 0; \ 22 + } \ 23 + }) 24 + 25 + #define __yerr_code(yse, _code...) \ 26 + ({ \ 27 + struct ynl_error *_yse = (yse); \ 28 + \ 29 + if (_yse) { \ 30 + _yse->code = _code; \ 31 + } \ 32 + }) 33 + 34 + #define __yerr(yse, _code, _msg...) \ 35 + ({ \ 36 + __yerr_msg(yse, _msg); \ 37 + __yerr_code(yse, _code); \ 38 + }) 39 + 40 + #define __perr(yse, _msg) __yerr(yse, errno, _msg) 41 + 42 + #define yerr_msg(_ys, _msg...) __yerr_msg(&(_ys)->err, _msg) 43 + #define yerr(_ys, _code, _msg...) __yerr(&(_ys)->err, _code, _msg) 44 + #define perr(_ys, _msg) __yerr(&(_ys)->err, errno, _msg) 45 + 46 + /* -- Netlink boiler plate */ 47 + static int 48 + ynl_err_walk_report_one(struct ynl_policy_nest *policy, unsigned int type, 49 + char *str, int str_sz, int *n) 50 + { 51 + if (!policy) { 52 + if (*n < str_sz) 53 + *n += snprintf(str, str_sz, "!policy"); 54 + return 1; 55 + } 56 + 57 + if (type > policy->max_attr) { 58 + if (*n < str_sz) 59 + *n += snprintf(str, str_sz, "!oob"); 60 + return 1; 61 + } 62 + 63 + if (!policy->table[type].name) { 64 + if (*n < str_sz) 65 + *n += snprintf(str, str_sz, "!name"); 66 + return 1; 67 + } 68 + 69 + if (*n < str_sz) 70 + *n += snprintf(str, str_sz - *n, 71 + ".%s", policy->table[type].name); 72 + return 0; 73 + } 74 + 75 + static int 76 + ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off, 77 + struct ynl_policy_nest *policy, char *str, int str_sz, 78 + struct ynl_policy_nest **nest_pol) 79 + { 80 + unsigned int astart_off, aend_off; 81 + const struct nlattr *attr; 82 + unsigned int data_len; 83 + unsigned int type; 84 + bool found = false; 85 + int n = 0; 86 + 87 + if (!policy) { 88 + if (n < str_sz) 89 + n += snprintf(str, str_sz, "!policy"); 90 + return n; 91 + } 92 + 93 + data_len = end - start; 94 + 95 + mnl_attr_for_each_payload(start, data_len) { 96 + astart_off = (char *)attr - (char *)start; 97 + aend_off = astart_off + mnl_attr_get_payload_len(attr); 98 + if (aend_off <= off) 99 + continue; 100 + 101 + found = true; 102 + break; 103 + } 104 + if (!found) 105 + return 0; 106 + 107 + off -= astart_off; 108 + 109 + type = mnl_attr_get_type(attr); 110 + 111 + if (ynl_err_walk_report_one(policy, type, str, str_sz, &n)) 112 + return n; 113 + 114 + if (!off) { 115 + if (nest_pol) 116 + *nest_pol = policy->table[type].nest; 117 + return n; 118 + } 119 + 120 + if (!policy->table[type].nest) { 121 + if (n < str_sz) 122 + n += snprintf(str, str_sz, "!nest"); 123 + return n; 124 + } 125 + 126 + off -= sizeof(struct nlattr); 127 + start = mnl_attr_get_payload(attr); 128 + end = start + mnl_attr_get_payload_len(attr); 129 + 130 + return n + ynl_err_walk(ys, start, end, off, policy->table[type].nest, 131 + &str[n], str_sz - n, nest_pol); 132 + } 133 + 134 + #define NLMSGERR_ATTR_MISS_TYPE (NLMSGERR_ATTR_POLICY + 1) 135 + #define NLMSGERR_ATTR_MISS_NEST (NLMSGERR_ATTR_POLICY + 2) 136 + #define NLMSGERR_ATTR_MAX (NLMSGERR_ATTR_MAX + 2) 137 + 138 + static int 139 + ynl_ext_ack_check(struct ynl_sock *ys, const struct nlmsghdr *nlh, 140 + unsigned int hlen) 141 + { 142 + const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {}; 143 + char miss_attr[sizeof(ys->err.msg)]; 144 + char bad_attr[sizeof(ys->err.msg)]; 145 + const struct nlattr *attr; 146 + const char *str = NULL; 147 + 148 + if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) 149 + return MNL_CB_OK; 150 + 151 + mnl_attr_for_each(attr, nlh, hlen) { 152 + unsigned int len, type; 153 + 154 + len = mnl_attr_get_payload_len(attr); 155 + type = mnl_attr_get_type(attr); 156 + 157 + if (type > NLMSGERR_ATTR_MAX) 158 + continue; 159 + 160 + tb[type] = attr; 161 + 162 + switch (type) { 163 + case NLMSGERR_ATTR_OFFS: 164 + case NLMSGERR_ATTR_MISS_TYPE: 165 + case NLMSGERR_ATTR_MISS_NEST: 166 + if (len != sizeof(__u32)) 167 + return MNL_CB_ERROR; 168 + break; 169 + case NLMSGERR_ATTR_MSG: 170 + str = mnl_attr_get_payload(attr); 171 + if (str[len - 1]) 172 + return MNL_CB_ERROR; 173 + break; 174 + default: 175 + break; 176 + } 177 + } 178 + 179 + bad_attr[0] = '\0'; 180 + miss_attr[0] = '\0'; 181 + 182 + if (tb[NLMSGERR_ATTR_OFFS]) { 183 + unsigned int n, off; 184 + void *start, *end; 185 + 186 + ys->err.attr_offs = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]); 187 + 188 + n = snprintf(bad_attr, sizeof(bad_attr), "%sbad attribute: ", 189 + str ? " (" : ""); 190 + 191 + start = mnl_nlmsg_get_payload_offset(ys->nlh, 192 + sizeof(struct genlmsghdr)); 193 + end = mnl_nlmsg_get_payload_tail(ys->nlh); 194 + 195 + off = ys->err.attr_offs; 196 + off -= sizeof(struct nlmsghdr); 197 + off -= sizeof(struct genlmsghdr); 198 + 199 + n += ynl_err_walk(ys, start, end, off, ys->req_policy, 200 + &bad_attr[n], sizeof(bad_attr) - n, NULL); 201 + 202 + if (n >= sizeof(bad_attr)) 203 + n = sizeof(bad_attr) - 1; 204 + bad_attr[n] = '\0'; 205 + } 206 + if (tb[NLMSGERR_ATTR_MISS_TYPE]) { 207 + struct ynl_policy_nest *nest_pol = NULL; 208 + unsigned int n, off, type; 209 + void *start, *end; 210 + int n2; 211 + 212 + type = mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_TYPE]); 213 + 214 + n = snprintf(miss_attr, sizeof(miss_attr), "%smissing attribute: ", 215 + bad_attr[0] ? ", " : (str ? " (" : "")); 216 + 217 + start = mnl_nlmsg_get_payload_offset(ys->nlh, 218 + sizeof(struct genlmsghdr)); 219 + end = mnl_nlmsg_get_payload_tail(ys->nlh); 220 + 221 + nest_pol = ys->req_policy; 222 + if (tb[NLMSGERR_ATTR_MISS_NEST]) { 223 + off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_NEST]); 224 + off -= sizeof(struct nlmsghdr); 225 + off -= sizeof(struct genlmsghdr); 226 + 227 + n += ynl_err_walk(ys, start, end, off, ys->req_policy, 228 + &miss_attr[n], sizeof(miss_attr) - n, 229 + &nest_pol); 230 + } 231 + 232 + n2 = 0; 233 + ynl_err_walk_report_one(nest_pol, type, &miss_attr[n], 234 + sizeof(miss_attr) - n, &n2); 235 + n += n2; 236 + 237 + if (n >= sizeof(miss_attr)) 238 + n = sizeof(miss_attr) - 1; 239 + miss_attr[n] = '\0'; 240 + } 241 + 242 + /* Implicitly depend on ys->err.code already set */ 243 + if (str) 244 + yerr_msg(ys, "Kernel %s: '%s'%s%s%s", 245 + ys->err.code ? "error" : "warning", 246 + str, bad_attr, miss_attr, 247 + bad_attr[0] || miss_attr[0] ? ")" : ""); 248 + else if (bad_attr[0] || miss_attr[0]) 249 + yerr_msg(ys, "Kernel %s: %s%s", 250 + ys->err.code ? "error" : "warning", 251 + bad_attr, miss_attr); 252 + 253 + return MNL_CB_OK; 254 + } 255 + 256 + static int ynl_cb_error(const struct nlmsghdr *nlh, void *data) 257 + { 258 + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); 259 + struct ynl_parse_arg *yarg = data; 260 + unsigned int hlen; 261 + int code; 262 + 263 + code = err->error >= 0 ? err->error : -err->error; 264 + yarg->ys->err.code = code; 265 + errno = code; 266 + 267 + hlen = sizeof(*err); 268 + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) 269 + hlen += mnl_nlmsg_get_payload_len(&err->msg); 270 + 271 + ynl_ext_ack_check(yarg->ys, nlh, hlen); 272 + 273 + return code ? MNL_CB_ERROR : MNL_CB_STOP; 274 + } 275 + 276 + static int ynl_cb_done(const struct nlmsghdr *nlh, void *data) 277 + { 278 + struct ynl_parse_arg *yarg = data; 279 + int err; 280 + 281 + err = *(int *)NLMSG_DATA(nlh); 282 + if (err < 0) { 283 + yarg->ys->err.code = -err; 284 + errno = -err; 285 + 286 + ynl_ext_ack_check(yarg->ys, nlh, sizeof(int)); 287 + 288 + return MNL_CB_ERROR; 289 + } 290 + return MNL_CB_STOP; 291 + } 292 + 293 + static int ynl_cb_noop(const struct nlmsghdr *nlh, void *data) 294 + { 295 + return MNL_CB_OK; 296 + } 297 + 298 + mnl_cb_t ynl_cb_array[NLMSG_MIN_TYPE] = { 299 + [NLMSG_NOOP] = ynl_cb_noop, 300 + [NLMSG_ERROR] = ynl_cb_error, 301 + [NLMSG_DONE] = ynl_cb_done, 302 + [NLMSG_OVERRUN] = ynl_cb_noop, 303 + }; 304 + 305 + /* Attribute validation */ 306 + 307 + int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr) 308 + { 309 + struct ynl_policy_attr *policy; 310 + unsigned int type, len; 311 + unsigned char *data; 312 + 313 + data = mnl_attr_get_payload(attr); 314 + len = mnl_attr_get_payload_len(attr); 315 + type = mnl_attr_get_type(attr); 316 + if (type > yarg->rsp_policy->max_attr) { 317 + yerr(yarg->ys, YNL_ERROR_INTERNAL, 318 + "Internal error, validating unknown attribute"); 319 + return -1; 320 + } 321 + 322 + policy = &yarg->rsp_policy->table[type]; 323 + 324 + switch (policy->type) { 325 + case YNL_PT_REJECT: 326 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 327 + "Rejected attribute (%s)", policy->name); 328 + return -1; 329 + case YNL_PT_IGNORE: 330 + break; 331 + case YNL_PT_U8: 332 + if (len == sizeof(__u8)) 333 + break; 334 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 335 + "Invalid attribute (u8 %s)", policy->name); 336 + return -1; 337 + case YNL_PT_U16: 338 + if (len == sizeof(__u16)) 339 + break; 340 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 341 + "Invalid attribute (u16 %s)", policy->name); 342 + return -1; 343 + case YNL_PT_U32: 344 + if (len == sizeof(__u32)) 345 + break; 346 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 347 + "Invalid attribute (u32 %s)", policy->name); 348 + return -1; 349 + case YNL_PT_U64: 350 + if (len == sizeof(__u64)) 351 + break; 352 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 353 + "Invalid attribute (u64 %s)", policy->name); 354 + return -1; 355 + case YNL_PT_FLAG: 356 + /* Let flags grow into real attrs, why not.. */ 357 + break; 358 + case YNL_PT_NEST: 359 + if (!len || len >= sizeof(*attr)) 360 + break; 361 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 362 + "Invalid attribute (nest %s)", policy->name); 363 + return -1; 364 + case YNL_PT_BINARY: 365 + if (!policy->len || len == policy->len) 366 + break; 367 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 368 + "Invalid attribute (binary %s)", policy->name); 369 + return -1; 370 + case YNL_PT_NUL_STR: 371 + if ((!policy->len || len <= policy->len) && !data[len - 1]) 372 + break; 373 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 374 + "Invalid attribute (string %s)", policy->name); 375 + return -1; 376 + default: 377 + yerr(yarg->ys, YNL_ERROR_ATTR_INVALID, 378 + "Invalid attribute (unknown %s)", policy->name); 379 + return -1; 380 + } 381 + 382 + return 0; 383 + } 384 + 385 + /* Generic code */ 386 + 387 + static void ynl_err_reset(struct ynl_sock *ys) 388 + { 389 + ys->err.code = 0; 390 + ys->err.attr_offs = 0; 391 + ys->err.msg[0] = 0; 392 + } 393 + 394 + struct nlmsghdr *ynl_msg_start(struct ynl_sock *ys, __u32 id, __u16 flags) 395 + { 396 + struct nlmsghdr *nlh; 397 + 398 + ynl_err_reset(ys); 399 + 400 + nlh = ys->nlh = mnl_nlmsg_put_header(ys->tx_buf); 401 + nlh->nlmsg_type = id; 402 + nlh->nlmsg_flags = flags; 403 + nlh->nlmsg_seq = ++ys->seq; 404 + 405 + return nlh; 406 + } 407 + 408 + struct nlmsghdr * 409 + ynl_gemsg_start(struct ynl_sock *ys, __u32 id, __u16 flags, 410 + __u8 cmd, __u8 version) 411 + { 412 + struct genlmsghdr gehdr; 413 + struct nlmsghdr *nlh; 414 + void *data; 415 + 416 + nlh = ynl_msg_start(ys, id, flags); 417 + 418 + memset(&gehdr, 0, sizeof(gehdr)); 419 + gehdr.cmd = cmd; 420 + gehdr.version = version; 421 + 422 + data = mnl_nlmsg_put_extra_header(nlh, sizeof(gehdr)); 423 + memcpy(data, &gehdr, sizeof(gehdr)); 424 + 425 + return nlh; 426 + } 427 + 428 + void ynl_msg_start_req(struct ynl_sock *ys, __u32 id) 429 + { 430 + ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK); 431 + } 432 + 433 + void ynl_msg_start_dump(struct ynl_sock *ys, __u32 id) 434 + { 435 + ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); 436 + } 437 + 438 + struct nlmsghdr * 439 + ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version) 440 + { 441 + return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK, cmd, version); 442 + } 443 + 444 + struct nlmsghdr * 445 + ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version) 446 + { 447 + return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, 448 + cmd, version); 449 + } 450 + 451 + int ynl_recv_ack(struct ynl_sock *ys, int ret) 452 + { 453 + if (!ret) { 454 + yerr(ys, YNL_ERROR_EXPECT_ACK, 455 + "Expecting an ACK but nothing received"); 456 + return -1; 457 + } 458 + 459 + ret = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE); 460 + if (ret < 0) { 461 + perr(ys, "Socket receive failed"); 462 + return ret; 463 + } 464 + return mnl_cb_run(ys->rx_buf, ret, ys->seq, ys->portid, 465 + ynl_cb_null, ys); 466 + } 467 + 468 + int ynl_cb_null(const struct nlmsghdr *nlh, void *data) 469 + { 470 + struct ynl_parse_arg *yarg = data; 471 + 472 + yerr(yarg->ys, YNL_ERROR_UNEXPECT_MSG, 473 + "Received a message when none were expected"); 474 + 475 + return MNL_CB_ERROR; 476 + } 477 + 478 + /* Init/fini and genetlink boiler plate */ 479 + static int 480 + ynl_get_family_info_mcast(struct ynl_sock *ys, const struct nlattr *mcasts) 481 + { 482 + const struct nlattr *entry, *attr; 483 + unsigned int i; 484 + 485 + mnl_attr_for_each_nested(attr, mcasts) 486 + ys->n_mcast_groups++; 487 + 488 + if (!ys->n_mcast_groups) 489 + return 0; 490 + 491 + ys->mcast_groups = calloc(ys->n_mcast_groups, 492 + sizeof(*ys->mcast_groups)); 493 + if (!ys->mcast_groups) 494 + return MNL_CB_ERROR; 495 + 496 + i = 0; 497 + mnl_attr_for_each_nested(entry, mcasts) { 498 + mnl_attr_for_each_nested(attr, entry) { 499 + if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GRP_ID) 500 + ys->mcast_groups[i].id = mnl_attr_get_u32(attr); 501 + if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GRP_NAME) { 502 + strncpy(ys->mcast_groups[i].name, 503 + mnl_attr_get_str(attr), 504 + GENL_NAMSIZ - 1); 505 + ys->mcast_groups[i].name[GENL_NAMSIZ - 1] = 0; 506 + } 507 + } 508 + } 509 + 510 + return 0; 511 + } 512 + 513 + static int ynl_get_family_info_cb(const struct nlmsghdr *nlh, void *data) 514 + { 515 + struct ynl_parse_arg *yarg = data; 516 + struct ynl_sock *ys = yarg->ys; 517 + const struct nlattr *attr; 518 + bool found_id = true; 519 + 520 + mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) { 521 + if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GROUPS) 522 + if (ynl_get_family_info_mcast(ys, attr)) 523 + return MNL_CB_ERROR; 524 + 525 + if (mnl_attr_get_type(attr) != CTRL_ATTR_FAMILY_ID) 526 + continue; 527 + 528 + if (mnl_attr_get_payload_len(attr) != sizeof(__u16)) { 529 + yerr(ys, YNL_ERROR_ATTR_INVALID, "Invalid family ID"); 530 + return MNL_CB_ERROR; 531 + } 532 + 533 + ys->family_id = mnl_attr_get_u16(attr); 534 + found_id = true; 535 + } 536 + 537 + if (!found_id) { 538 + yerr(ys, YNL_ERROR_ATTR_MISSING, "Family ID missing"); 539 + return MNL_CB_ERROR; 540 + } 541 + return MNL_CB_OK; 542 + } 543 + 544 + static int ynl_sock_read_family(struct ynl_sock *ys, const char *family_name) 545 + { 546 + struct ynl_parse_arg yarg = { .ys = ys, }; 547 + struct nlmsghdr *nlh; 548 + int err; 549 + 550 + nlh = ynl_gemsg_start_req(ys, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 1); 551 + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name); 552 + 553 + err = mnl_socket_sendto(ys->sock, nlh, nlh->nlmsg_len); 554 + if (err < 0) { 555 + perr(ys, "failed to request socket family info"); 556 + return err; 557 + } 558 + 559 + err = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE); 560 + if (err <= 0) { 561 + perr(ys, "failed to receive the socket family info"); 562 + return err; 563 + } 564 + err = mnl_cb_run2(ys->rx_buf, err, ys->seq, ys->portid, 565 + ynl_get_family_info_cb, &yarg, 566 + ynl_cb_array, ARRAY_SIZE(ynl_cb_array)); 567 + if (err < 0) { 568 + free(ys->mcast_groups); 569 + perr(ys, "failed to receive the socket family info - no such family?"); 570 + return err; 571 + } 572 + 573 + return ynl_recv_ack(ys, err); 574 + } 575 + 576 + struct ynl_sock * 577 + ynl_sock_create(const struct ynl_family *yf, struct ynl_error *yse) 578 + { 579 + struct ynl_sock *ys; 580 + int one = 1; 581 + 582 + ys = malloc(sizeof(*ys) + 2 * MNL_SOCKET_BUFFER_SIZE); 583 + if (!ys) 584 + return NULL; 585 + memset(ys, 0, sizeof(*ys)); 586 + 587 + ys->family = yf; 588 + ys->tx_buf = &ys->raw_buf[0]; 589 + ys->rx_buf = &ys->raw_buf[MNL_SOCKET_BUFFER_SIZE]; 590 + ys->ntf_last_next = &ys->ntf_first; 591 + 592 + ys->sock = mnl_socket_open(NETLINK_GENERIC); 593 + if (!ys->sock) { 594 + __perr(yse, "failed to create a netlink socket"); 595 + goto err_free_sock; 596 + } 597 + 598 + if (mnl_socket_setsockopt(ys->sock, NETLINK_CAP_ACK, 599 + &one, sizeof(one))) { 600 + __perr(yse, "failed to enable netlink ACK"); 601 + goto err_close_sock; 602 + } 603 + if (mnl_socket_setsockopt(ys->sock, NETLINK_EXT_ACK, 604 + &one, sizeof(one))) { 605 + __perr(yse, "failed to enable netlink ext ACK"); 606 + goto err_close_sock; 607 + } 608 + 609 + ys->seq = random(); 610 + ys->portid = mnl_socket_get_portid(ys->sock); 611 + 612 + if (ynl_sock_read_family(ys, yf->name)) { 613 + if (yse) 614 + memcpy(yse, &ys->err, sizeof(*yse)); 615 + goto err_close_sock; 616 + } 617 + 618 + return ys; 619 + 620 + err_close_sock: 621 + mnl_socket_close(ys->sock); 622 + err_free_sock: 623 + free(ys); 624 + return NULL; 625 + } 626 + 627 + void ynl_sock_destroy(struct ynl_sock *ys) 628 + { 629 + struct ynl_ntf_base_type *ntf; 630 + 631 + mnl_socket_close(ys->sock); 632 + while ((ntf = ynl_ntf_dequeue(ys))) 633 + ynl_ntf_free(ntf); 634 + free(ys->mcast_groups); 635 + free(ys); 636 + } 637 + 638 + /* YNL multicast handling */ 639 + 640 + void ynl_ntf_free(struct ynl_ntf_base_type *ntf) 641 + { 642 + ntf->free(ntf); 643 + } 644 + 645 + int ynl_subscribe(struct ynl_sock *ys, const char *grp_name) 646 + { 647 + unsigned int i; 648 + int err; 649 + 650 + for (i = 0; i < ys->n_mcast_groups; i++) 651 + if (!strcmp(ys->mcast_groups[i].name, grp_name)) 652 + break; 653 + if (i == ys->n_mcast_groups) { 654 + yerr(ys, ENOENT, "Multicast group '%s' not found", grp_name); 655 + return -1; 656 + } 657 + 658 + err = mnl_socket_setsockopt(ys->sock, NETLINK_ADD_MEMBERSHIP, 659 + &ys->mcast_groups[i].id, 660 + sizeof(ys->mcast_groups[i].id)); 661 + if (err < 0) { 662 + perr(ys, "Subscribing to multicast group failed"); 663 + return -1; 664 + } 665 + 666 + return 0; 667 + } 668 + 669 + int ynl_socket_get_fd(struct ynl_sock *ys) 670 + { 671 + return mnl_socket_get_fd(ys->sock); 672 + } 673 + 674 + struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys) 675 + { 676 + struct ynl_ntf_base_type *ntf; 677 + 678 + if (!ynl_has_ntf(ys)) 679 + return NULL; 680 + 681 + ntf = ys->ntf_first; 682 + ys->ntf_first = ntf->next; 683 + if (ys->ntf_last_next == &ntf->next) 684 + ys->ntf_last_next = &ys->ntf_first; 685 + 686 + return ntf; 687 + } 688 + 689 + static int ynl_ntf_parse(struct ynl_sock *ys, const struct nlmsghdr *nlh) 690 + { 691 + struct ynl_parse_arg yarg = { .ys = ys, }; 692 + const struct ynl_ntf_info *info; 693 + struct ynl_ntf_base_type *rsp; 694 + struct genlmsghdr *gehdr; 695 + int ret; 696 + 697 + gehdr = mnl_nlmsg_get_payload(nlh); 698 + if (gehdr->cmd >= ys->family->ntf_info_size) 699 + return MNL_CB_ERROR; 700 + info = &ys->family->ntf_info[gehdr->cmd]; 701 + if (!info->cb) 702 + return MNL_CB_ERROR; 703 + 704 + rsp = calloc(1, info->alloc_sz); 705 + rsp->free = info->free; 706 + yarg.data = rsp->data; 707 + yarg.rsp_policy = info->policy; 708 + 709 + ret = info->cb(nlh, &yarg); 710 + if (ret <= MNL_CB_STOP) 711 + goto err_free; 712 + 713 + rsp->family = nlh->nlmsg_type; 714 + rsp->cmd = gehdr->cmd; 715 + 716 + *ys->ntf_last_next = rsp; 717 + ys->ntf_last_next = &rsp->next; 718 + 719 + return MNL_CB_OK; 720 + 721 + err_free: 722 + info->free(rsp); 723 + return MNL_CB_ERROR; 724 + } 725 + 726 + static int ynl_ntf_trampoline(const struct nlmsghdr *nlh, void *data) 727 + { 728 + return ynl_ntf_parse((struct ynl_sock *)data, nlh); 729 + } 730 + 731 + int ynl_ntf_check(struct ynl_sock *ys) 732 + { 733 + ssize_t len; 734 + int err; 735 + 736 + do { 737 + /* libmnl doesn't let us pass flags to the recv to make 738 + * it non-blocking so we need to poll() or peek() :| 739 + */ 740 + struct pollfd pfd = { }; 741 + 742 + pfd.fd = mnl_socket_get_fd(ys->sock); 743 + pfd.events = POLLIN; 744 + err = poll(&pfd, 1, 1); 745 + if (err < 1) 746 + return err; 747 + 748 + len = mnl_socket_recvfrom(ys->sock, ys->rx_buf, 749 + MNL_SOCKET_BUFFER_SIZE); 750 + if (len < 0) 751 + return len; 752 + 753 + err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid, 754 + ynl_ntf_trampoline, ys, 755 + ynl_cb_array, NLMSG_MIN_TYPE); 756 + if (err < 0) 757 + return err; 758 + } while (err > 0); 759 + 760 + return 0; 761 + } 762 + 763 + /* YNL specific helpers used by the auto-generated code */ 764 + 765 + struct ynl_dump_list_type *YNL_LIST_END = (void *)(0xb4d123); 766 + 767 + void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd) 768 + { 769 + yerr(ys, YNL_ERROR_UNKNOWN_NTF, 770 + "Unknown notification message type '%d'", cmd); 771 + } 772 + 773 + int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg) 774 + { 775 + yerr(yarg->ys, YNL_ERROR_INV_RESP, "Error parsing response: %s", msg); 776 + return MNL_CB_ERROR; 777 + } 778 + 779 + static int 780 + ynl_check_alien(struct ynl_sock *ys, const struct nlmsghdr *nlh, __u32 rsp_cmd) 781 + { 782 + struct genlmsghdr *gehdr; 783 + 784 + if (mnl_nlmsg_get_payload_len(nlh) < sizeof(*gehdr)) { 785 + yerr(ys, YNL_ERROR_INV_RESP, 786 + "Kernel responded with truncated message"); 787 + return -1; 788 + } 789 + 790 + gehdr = mnl_nlmsg_get_payload(nlh); 791 + if (gehdr->cmd != rsp_cmd) 792 + return ynl_ntf_parse(ys, nlh); 793 + 794 + return 0; 795 + } 796 + 797 + static int ynl_req_trampoline(const struct nlmsghdr *nlh, void *data) 798 + { 799 + struct ynl_req_state *yrs = data; 800 + int ret; 801 + 802 + ret = ynl_check_alien(yrs->yarg.ys, nlh, yrs->rsp_cmd); 803 + if (ret) 804 + return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK; 805 + 806 + return yrs->cb(nlh, &yrs->yarg); 807 + } 808 + 809 + int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh, 810 + struct ynl_req_state *yrs) 811 + { 812 + ssize_t len; 813 + int err; 814 + 815 + err = mnl_socket_sendto(ys->sock, req_nlh, req_nlh->nlmsg_len); 816 + if (err < 0) 817 + return err; 818 + 819 + do { 820 + len = mnl_socket_recvfrom(ys->sock, ys->rx_buf, 821 + MNL_SOCKET_BUFFER_SIZE); 822 + if (len < 0) 823 + return len; 824 + 825 + err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid, 826 + ynl_req_trampoline, yrs, 827 + ynl_cb_array, NLMSG_MIN_TYPE); 828 + if (err < 0) 829 + return err; 830 + } while (err > 0); 831 + 832 + return 0; 833 + } 834 + 835 + static int ynl_dump_trampoline(const struct nlmsghdr *nlh, void *data) 836 + { 837 + struct ynl_dump_state *ds = data; 838 + struct ynl_dump_list_type *obj; 839 + struct ynl_parse_arg yarg = {}; 840 + int ret; 841 + 842 + ret = ynl_check_alien(ds->ys, nlh, ds->rsp_cmd); 843 + if (ret) 844 + return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK; 845 + 846 + obj = calloc(1, ds->alloc_sz); 847 + if (!obj) 848 + return MNL_CB_ERROR; 849 + 850 + if (!ds->first) 851 + ds->first = obj; 852 + if (ds->last) 853 + ds->last->next = obj; 854 + ds->last = obj; 855 + 856 + yarg.ys = ds->ys; 857 + yarg.rsp_policy = ds->rsp_policy; 858 + yarg.data = &obj->data; 859 + 860 + return ds->cb(nlh, &yarg); 861 + } 862 + 863 + static void *ynl_dump_end(struct ynl_dump_state *ds) 864 + { 865 + if (!ds->first) 866 + return YNL_LIST_END; 867 + 868 + ds->last->next = YNL_LIST_END; 869 + return ds->first; 870 + } 871 + 872 + int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh, 873 + struct ynl_dump_state *yds) 874 + { 875 + ssize_t len; 876 + int err; 877 + 878 + err = mnl_socket_sendto(ys->sock, req_nlh, req_nlh->nlmsg_len); 879 + if (err < 0) 880 + return err; 881 + 882 + do { 883 + len = mnl_socket_recvfrom(ys->sock, ys->rx_buf, 884 + MNL_SOCKET_BUFFER_SIZE); 885 + if (len < 0) 886 + goto err_close_list; 887 + 888 + err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid, 889 + ynl_dump_trampoline, yds, 890 + ynl_cb_array, NLMSG_MIN_TYPE); 891 + if (err < 0) 892 + goto err_close_list; 893 + } while (err > 0); 894 + 895 + yds->first = ynl_dump_end(yds); 896 + return 0; 897 + 898 + err_close_list: 899 + yds->first = ynl_dump_end(yds); 900 + return -1; 901 + }
+237
tools/net/ynl/lib/ynl.h
··· 1 + // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 + #ifndef __YNL_C_H 3 + #define __YNL_C_H 1 4 + 5 + #include <stddef.h> 6 + #include <libmnl/libmnl.h> 7 + #include <linux/genetlink.h> 8 + #include <linux/types.h> 9 + 10 + struct mnl_socket; 11 + struct nlmsghdr; 12 + 13 + /* 14 + * User facing code 15 + */ 16 + 17 + struct ynl_ntf_base_type; 18 + struct ynl_ntf_info; 19 + struct ynl_sock; 20 + 21 + enum ynl_error_code { 22 + YNL_ERROR_NONE = 0, 23 + __YNL_ERRNO_END = 4096, 24 + YNL_ERROR_INTERNAL, 25 + YNL_ERROR_EXPECT_ACK, 26 + YNL_ERROR_EXPECT_MSG, 27 + YNL_ERROR_UNEXPECT_MSG, 28 + YNL_ERROR_ATTR_MISSING, 29 + YNL_ERROR_ATTR_INVALID, 30 + YNL_ERROR_UNKNOWN_NTF, 31 + YNL_ERROR_INV_RESP, 32 + }; 33 + 34 + /** 35 + * struct ynl_error - error encountered by YNL 36 + * @code: errno (low values) or YNL error code (enum ynl_error_code) 37 + * @attr_offs: offset of bad attribute (for very advanced users) 38 + * @msg: error message 39 + * 40 + * Error information for when YNL operations fail. 41 + * Users should interact with the err member of struct ynl_sock directly. 42 + * The main exception to that rule is ynl_sock_create(). 43 + */ 44 + struct ynl_error { 45 + enum ynl_error_code code; 46 + unsigned int attr_offs; 47 + char msg[512]; 48 + }; 49 + 50 + /** 51 + * struct ynl_family - YNL family info 52 + * Family description generated by codegen. Pass to ynl_sock_create(). 53 + */ 54 + struct ynl_family { 55 + /* private: */ 56 + const char *name; 57 + const struct ynl_ntf_info *ntf_info; 58 + unsigned int ntf_info_size; 59 + }; 60 + 61 + /** 62 + * struct ynl_sock - YNL wrapped netlink socket 63 + * @err: YNL error descriptor, cleared on every request. 64 + */ 65 + struct ynl_sock { 66 + struct ynl_error err; 67 + 68 + /* private: */ 69 + const struct ynl_family *family; 70 + struct mnl_socket *sock; 71 + __u32 seq; 72 + __u32 portid; 73 + __u16 family_id; 74 + 75 + unsigned int n_mcast_groups; 76 + struct { 77 + unsigned int id; 78 + char name[GENL_NAMSIZ]; 79 + } *mcast_groups; 80 + 81 + struct ynl_ntf_base_type *ntf_first; 82 + struct ynl_ntf_base_type **ntf_last_next; 83 + 84 + struct nlmsghdr *nlh; 85 + struct ynl_policy_nest *req_policy; 86 + unsigned char *tx_buf; 87 + unsigned char *rx_buf; 88 + unsigned char raw_buf[]; 89 + }; 90 + 91 + struct ynl_sock * 92 + ynl_sock_create(const struct ynl_family *yf, struct ynl_error *e); 93 + void ynl_sock_destroy(struct ynl_sock *ys); 94 + 95 + #define ynl_dump_foreach(dump, iter) \ 96 + for (typeof(dump->obj) *iter = &dump->obj; \ 97 + !ynl_dump_obj_is_last(iter); \ 98 + iter = ynl_dump_obj_next(iter)) 99 + 100 + int ynl_subscribe(struct ynl_sock *ys, const char *grp_name); 101 + int ynl_socket_get_fd(struct ynl_sock *ys); 102 + int ynl_ntf_check(struct ynl_sock *ys); 103 + 104 + /** 105 + * ynl_has_ntf() - check if socket has *parsed* notifications 106 + * @ys: active YNL socket 107 + * 108 + * Note that this does not take into account notifications sitting 109 + * in netlink socket, just the notifications which have already been 110 + * read and parsed (e.g. during a ynl_ntf_check() call). 111 + */ 112 + static inline bool ynl_has_ntf(struct ynl_sock *ys) 113 + { 114 + return ys->ntf_last_next != &ys->ntf_first; 115 + } 116 + struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys); 117 + 118 + void ynl_ntf_free(struct ynl_ntf_base_type *ntf); 119 + 120 + /* 121 + * YNL internals / low level stuff 122 + */ 123 + 124 + /* Generic mnl helper code */ 125 + 126 + enum ynl_policy_type { 127 + YNL_PT_REJECT = 1, 128 + YNL_PT_IGNORE, 129 + YNL_PT_NEST, 130 + YNL_PT_FLAG, 131 + YNL_PT_BINARY, 132 + YNL_PT_U8, 133 + YNL_PT_U16, 134 + YNL_PT_U32, 135 + YNL_PT_U64, 136 + YNL_PT_NUL_STR, 137 + }; 138 + 139 + struct ynl_policy_attr { 140 + enum ynl_policy_type type; 141 + unsigned int len; 142 + const char *name; 143 + struct ynl_policy_nest *nest; 144 + }; 145 + 146 + struct ynl_policy_nest { 147 + unsigned int max_attr; 148 + struct ynl_policy_attr *table; 149 + }; 150 + 151 + struct ynl_parse_arg { 152 + struct ynl_sock *ys; 153 + struct ynl_policy_nest *rsp_policy; 154 + void *data; 155 + }; 156 + 157 + struct ynl_dump_list_type { 158 + struct ynl_dump_list_type *next; 159 + unsigned char data[] __attribute__ ((aligned (8))); 160 + }; 161 + extern struct ynl_dump_list_type *YNL_LIST_END; 162 + 163 + static inline bool ynl_dump_obj_is_last(void *obj) 164 + { 165 + unsigned long uptr = (unsigned long)obj; 166 + 167 + uptr -= offsetof(struct ynl_dump_list_type, data); 168 + return uptr == (unsigned long)YNL_LIST_END; 169 + } 170 + 171 + static inline void *ynl_dump_obj_next(void *obj) 172 + { 173 + unsigned long uptr = (unsigned long)obj; 174 + struct ynl_dump_list_type *list; 175 + 176 + uptr -= offsetof(struct ynl_dump_list_type, data); 177 + list = (void *)uptr; 178 + uptr = (unsigned long)list->next; 179 + uptr += offsetof(struct ynl_dump_list_type, data); 180 + 181 + return (void *)uptr; 182 + } 183 + 184 + struct ynl_ntf_base_type { 185 + __u16 family; 186 + __u8 cmd; 187 + struct ynl_ntf_base_type *next; 188 + void (*free)(struct ynl_ntf_base_type *ntf); 189 + unsigned char data[] __attribute__ ((aligned (8))); 190 + }; 191 + 192 + extern mnl_cb_t ynl_cb_array[NLMSG_MIN_TYPE]; 193 + 194 + struct nlmsghdr * 195 + ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version); 196 + struct nlmsghdr * 197 + ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version); 198 + 199 + int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr); 200 + 201 + int ynl_recv_ack(struct ynl_sock *ys, int ret); 202 + int ynl_cb_null(const struct nlmsghdr *nlh, void *data); 203 + 204 + /* YNL specific helpers used by the auto-generated code */ 205 + 206 + struct ynl_req_state { 207 + struct ynl_parse_arg yarg; 208 + mnl_cb_t cb; 209 + __u32 rsp_cmd; 210 + }; 211 + 212 + struct ynl_dump_state { 213 + struct ynl_sock *ys; 214 + struct ynl_policy_nest *rsp_policy; 215 + void *first; 216 + struct ynl_dump_list_type *last; 217 + size_t alloc_sz; 218 + mnl_cb_t cb; 219 + __u32 rsp_cmd; 220 + }; 221 + 222 + struct ynl_ntf_info { 223 + struct ynl_policy_nest *policy; 224 + mnl_cb_t cb; 225 + size_t alloc_sz; 226 + void (*free)(struct ynl_ntf_base_type *ntf); 227 + }; 228 + 229 + int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh, 230 + struct ynl_req_state *yrs); 231 + int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh, 232 + struct ynl_dump_state *yds); 233 + 234 + void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd); 235 + int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg); 236 + 237 + #endif
+1 -1
tools/net/ynl/ynl-regen.sh
··· 14 14 15 15 KDIR=$(dirname $(dirname $(dirname $(dirname $(realpath $0))))) 16 16 17 - files=$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\)') 17 + files=$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\|user\)') 18 18 for f in $files; do 19 19 # params: 0 1 2 3 20 20 # $YAML YNL-GEN kernel $mode