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
ynl_err_walk_report_one(struct ynl_policy_nest * policy,unsigned int type,char * str,int str_sz,int * n)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
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)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
ynl_ext_ack_check(struct ynl_sock * ys,const struct nlmsghdr * nlh,unsigned int hlen)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
ynl_cb_error(const struct nlmsghdr * nlh,void * data)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
ynl_cb_done(const struct nlmsghdr * nlh,void * data)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
ynl_cb_noop(const struct nlmsghdr * nlh,void * data)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
ynl_attr_validate(struct ynl_parse_arg * yarg,const struct nlattr * attr)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
ynl_err_reset(struct ynl_sock * ys)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
ynl_msg_start(struct ynl_sock * ys,__u32 id,__u16 flags)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 *
ynl_gemsg_start(struct ynl_sock * ys,__u32 id,__u16 flags,__u8 cmd,__u8 version)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
ynl_msg_start_req(struct ynl_sock * ys,__u32 id)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
ynl_msg_start_dump(struct ynl_sock * ys,__u32 id)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 *
ynl_gemsg_start_req(struct ynl_sock * ys,__u32 id,__u8 cmd,__u8 version)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 *
ynl_gemsg_start_dump(struct ynl_sock * ys,__u32 id,__u8 cmd,__u8 version)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
ynl_recv_ack(struct ynl_sock * ys,int ret)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
ynl_cb_null(const struct nlmsghdr * nlh,void * data)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
ynl_get_family_info_mcast(struct ynl_sock * ys,const struct nlattr * mcasts)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
ynl_get_family_info_cb(const struct nlmsghdr * nlh,void * data)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
ynl_sock_read_family(struct ynl_sock * ys,const char * family_name)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 *
ynl_sock_create(const struct ynl_family * yf,struct ynl_error * yse)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
ynl_sock_destroy(struct ynl_sock * ys)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
ynl_ntf_free(struct ynl_ntf_base_type * ntf)640 void ynl_ntf_free(struct ynl_ntf_base_type *ntf)
641 {
642 ntf->free(ntf);
643 }
644
ynl_subscribe(struct ynl_sock * ys,const char * grp_name)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
ynl_socket_get_fd(struct ynl_sock * ys)669 int ynl_socket_get_fd(struct ynl_sock *ys)
670 {
671 return mnl_socket_get_fd(ys->sock);
672 }
673
ynl_ntf_dequeue(struct ynl_sock * ys)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
ynl_ntf_parse(struct ynl_sock * ys,const struct nlmsghdr * nlh)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
ynl_ntf_trampoline(const struct nlmsghdr * nlh,void * data)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
ynl_ntf_check(struct ynl_sock * ys)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
ynl_error_unknown_notification(struct ynl_sock * ys,__u8 cmd)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
ynl_error_parse(struct ynl_parse_arg * yarg,const char * msg)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
ynl_check_alien(struct ynl_sock * ys,const struct nlmsghdr * nlh,__u32 rsp_cmd)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
ynl_req_trampoline(const struct nlmsghdr * nlh,void * data)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
ynl_exec(struct ynl_sock * ys,struct nlmsghdr * req_nlh,struct ynl_req_state * yrs)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
ynl_dump_trampoline(const struct nlmsghdr * nlh,void * data)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
ynl_dump_end(struct ynl_dump_state * ds)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
ynl_exec_dump(struct ynl_sock * ys,struct nlmsghdr * req_nlh,struct ynl_dump_state * yds)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 }
902