1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 Alexander V. Chernikov
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include "opt_inet.h"
29 #include "opt_inet6.h"
30
31 #include <sys/types.h>
32 #include <sys/ck.h>
33 #include <sys/lock.h>
34 #include <sys/socket.h>
35 #include <sys/vnode.h>
36
37 #include <net/if.h>
38 #include <net/if_dl.h>
39 #include <net/route.h>
40 #include <net/route/nhop.h>
41 #include <net/route/route_ctl.h>
42 #include <netlink/netlink.h>
43 #include <netlink/netlink_ctl.h>
44 #include <netlink/netlink_linux.h>
45 #include <netlink/netlink_var.h>
46 #include <netlink/netlink_route.h>
47
48 #include <compat/linux/linux.h>
49 #include <compat/linux/linux_common.h>
50 #include <compat/linux/linux_util.h>
51
52 #define DEBUG_MOD_NAME nl_linux
53 #define DEBUG_MAX_LEVEL LOG_DEBUG3
54 #include <netlink/netlink_debug.h>
55 _DECLARE_DEBUG(LOG_INFO);
56
57 static bool
valid_rta_size(const struct rtattr * rta,int sz)58 valid_rta_size(const struct rtattr *rta, int sz)
59 {
60 return (NL_RTA_DATA_LEN(rta) == sz);
61 }
62
63 static bool
valid_rta_u32(const struct rtattr * rta)64 valid_rta_u32(const struct rtattr *rta)
65 {
66 return (valid_rta_size(rta, sizeof(uint32_t)));
67 }
68
69 static uint32_t
_rta_get_uint32(const struct rtattr * rta)70 _rta_get_uint32(const struct rtattr *rta)
71 {
72 return (*((const uint32_t *)NL_RTA_DATA_CONST(rta)));
73 }
74
75 static int
rtnl_neigh_from_linux(struct nlmsghdr * hdr,struct nl_pstate * npt)76 rtnl_neigh_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
77 {
78 struct ndmsg *ndm = (struct ndmsg *)(hdr + 1);
79 sa_family_t f;
80
81 if (hdr->nlmsg_len < sizeof(struct nlmsghdr) + sizeof(struct ndmsg))
82 return (EBADMSG);
83 if ((f = linux_to_bsd_domain(ndm->ndm_family)) == AF_UNKNOWN)
84 return (EPFNOSUPPORT);
85
86 ndm->ndm_family = f;
87
88 return (0);
89 }
90
91 static int
rtnl_ifaddr_from_linux(struct nlmsghdr * hdr,struct nl_pstate * npt)92 rtnl_ifaddr_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
93 {
94 struct ifaddrmsg *ifam = (struct ifaddrmsg *)(hdr + 1);
95 sa_family_t f;
96
97 if (hdr->nlmsg_len < sizeof(struct nlmsghdr) +
98 offsetof(struct ifaddrmsg, ifa_family) + sizeof(ifam->ifa_family))
99 return (EBADMSG);
100 if ((f = linux_to_bsd_domain(ifam->ifa_family)) == AF_UNKNOWN)
101 return (EPFNOSUPPORT);
102
103 ifam->ifa_family = f;
104
105 return (0);
106 }
107
108 /*
109 * XXX: in case of error state of hdr is inconsistent.
110 */
111 static int
rtnl_route_from_linux(struct nlmsghdr * hdr,struct nl_pstate * npt)112 rtnl_route_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
113 {
114 /* Tweak address families and default fib only */
115 struct rtmsg *rtm = (struct rtmsg *)(hdr + 1);
116 struct nlattr *nla, *nla_head;
117 int attrs_len;
118 sa_family_t f;
119
120 if (hdr->nlmsg_len < sizeof(struct nlmsghdr) + sizeof(struct rtmsg))
121 return (EBADMSG);
122 if ((f = linux_to_bsd_domain(rtm->rtm_family)) == AF_UNKNOWN)
123 return (EPFNOSUPPORT);
124 rtm->rtm_family = f;
125
126 if (rtm->rtm_table == 254)
127 rtm->rtm_table = 0;
128
129 attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr);
130 attrs_len -= NETLINK_ALIGN(sizeof(struct rtmsg));
131 nla_head = (struct nlattr *)((char *)rtm + NETLINK_ALIGN(sizeof(struct rtmsg)));
132
133 NLA_FOREACH(nla, nla_head, attrs_len) {
134 RT_LOG(LOG_DEBUG3, "GOT type %d len %d total %d",
135 nla->nla_type, nla->nla_len, attrs_len);
136 struct rtattr *rta = (struct rtattr *)nla;
137 if (rta->rta_len < sizeof(struct rtattr)) {
138 break;
139 }
140 switch (rta->rta_type) {
141 case NL_RTA_TABLE:
142 if (!valid_rta_u32(rta))
143 return (EBADMSG);
144 rtm->rtm_table = 0;
145 uint32_t fibnum = _rta_get_uint32(rta);
146 RT_LOG(LOG_DEBUG3, "GET RTABLE: %u", fibnum);
147 if (fibnum == 254) {
148 *((uint32_t *)NL_RTA_DATA(rta)) = 0;
149 }
150 break;
151 }
152 }
153
154 return (0);
155 }
156
157 static int
rtnl_from_linux(struct nlmsghdr * hdr,struct nl_pstate * npt)158 rtnl_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
159 {
160
161 switch (hdr->nlmsg_type) {
162 case NL_RTM_GETROUTE:
163 case NL_RTM_NEWROUTE:
164 case NL_RTM_DELROUTE:
165 return (rtnl_route_from_linux(hdr, npt));
166 case NL_RTM_GETNEIGH:
167 return (rtnl_neigh_from_linux(hdr, npt));
168 case NL_RTM_GETADDR:
169 return (rtnl_ifaddr_from_linux(hdr, npt));
170 /* Silence warning for the messages where no translation is required */
171 case NL_RTM_NEWLINK:
172 case NL_RTM_DELLINK:
173 case NL_RTM_GETLINK:
174 break;
175 default:
176 RT_LOG(LOG_DEBUG, "Passing message type %d untranslated",
177 hdr->nlmsg_type);
178 /* XXXGL: maybe return error? */
179 }
180
181 return (0);
182 }
183
184 static int
nlmsg_from_linux(int netlink_family,struct nlmsghdr ** hdr,struct nl_pstate * npt)185 nlmsg_from_linux(int netlink_family, struct nlmsghdr **hdr,
186 struct nl_pstate *npt)
187 {
188 switch (netlink_family) {
189 case NETLINK_ROUTE:
190 return (rtnl_from_linux(*hdr, npt));
191 }
192
193 return (0);
194 }
195
196
197 /************************************************************
198 * Kernel -> Linux
199 ************************************************************/
200
201 static bool
handle_default_out(struct nlmsghdr * hdr,struct nl_writer * nw)202 handle_default_out(struct nlmsghdr *hdr, struct nl_writer *nw)
203 {
204 char *out_hdr;
205 out_hdr = nlmsg_reserve_data(nw, NLMSG_ALIGN(hdr->nlmsg_len), char);
206
207 if (out_hdr != NULL) {
208 memcpy(out_hdr, hdr, hdr->nlmsg_len);
209 nw->num_messages++;
210 return (true);
211 }
212 return (false);
213 }
214
215 static bool
nlmsg_copy_header(struct nlmsghdr * hdr,struct nl_writer * nw)216 nlmsg_copy_header(struct nlmsghdr *hdr, struct nl_writer *nw)
217 {
218 return (nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, hdr->nlmsg_type,
219 hdr->nlmsg_flags, 0));
220 }
221
222 static void *
_nlmsg_copy_next_header(struct nlmsghdr * hdr,struct nl_writer * nw,int sz)223 _nlmsg_copy_next_header(struct nlmsghdr *hdr, struct nl_writer *nw, int sz)
224 {
225 void *next_hdr = nlmsg_reserve_data(nw, sz, void);
226 memcpy(next_hdr, hdr + 1, NLMSG_ALIGN(sz));
227
228 return (next_hdr);
229 }
230 #define nlmsg_copy_next_header(_hdr, _ns, _t) \
231 ((_t *)(_nlmsg_copy_next_header(_hdr, _ns, sizeof(_t))))
232
233 static bool
nlmsg_copy_nla(const struct nlattr * nla_orig,struct nl_writer * nw)234 nlmsg_copy_nla(const struct nlattr *nla_orig, struct nl_writer *nw)
235 {
236 struct nlattr *nla = nlmsg_reserve_data(nw, nla_orig->nla_len, struct nlattr);
237 if (nla != NULL) {
238 memcpy(nla, nla_orig, nla_orig->nla_len);
239 return (true);
240 }
241 return (false);
242 }
243
244 /*
245 * Translate a FreeBSD interface name to a Linux interface name.
246 */
247 static bool
nlmsg_translate_ifname_nla(struct nlattr * nla,struct nl_writer * nw)248 nlmsg_translate_ifname_nla(struct nlattr *nla, struct nl_writer *nw)
249 {
250 char ifname[LINUX_IFNAMSIZ];
251
252 if (nw->ifp == NULL)
253 return (false);
254 (void)ifname_bsd_to_linux_ifp(nw->ifp, ifname, sizeof(ifname));
255 return (nlattr_add_string(nw, IFLA_IFNAME, ifname));
256 }
257
258 #define LINUX_NLA_UNHANDLED -1
259 /*
260 * Translate a FreeBSD attribute to a Linux attribute.
261 * Returns LINUX_NLA_UNHANDLED when the attribute is not processed
262 * and the caller must take care of it, otherwise the result is returned.
263 */
264 static int
nlmsg_translate_all_nla(struct nlmsghdr * hdr,struct nlattr * nla,struct nl_writer * nw)265 nlmsg_translate_all_nla(struct nlmsghdr *hdr, struct nlattr *nla,
266 struct nl_writer *nw)
267 {
268
269 switch (hdr->nlmsg_type) {
270 case NL_RTM_NEWLINK:
271 case NL_RTM_DELLINK:
272 case NL_RTM_GETLINK:
273 switch (nla->nla_type) {
274 case IFLA_IFNAME:
275 return (nlmsg_translate_ifname_nla(nla, nw));
276 default:
277 break;
278 }
279 default:
280 break;
281 }
282 return (LINUX_NLA_UNHANDLED);
283 }
284
285 static bool
nlmsg_copy_all_nla(struct nlmsghdr * hdr,int raw_hdrlen,struct nl_writer * nw)286 nlmsg_copy_all_nla(struct nlmsghdr *hdr, int raw_hdrlen, struct nl_writer *nw)
287 {
288 struct nlattr *nla;
289 int ret;
290
291 int hdrlen = NETLINK_ALIGN(raw_hdrlen);
292 int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
293 struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
294
295 NLA_FOREACH(nla, nla_head, attrs_len) {
296 RT_LOG(LOG_DEBUG3, "reading attr %d len %d", nla->nla_type, nla->nla_len);
297 if (nla->nla_len < sizeof(struct nlattr)) {
298 return (false);
299 }
300 ret = nlmsg_translate_all_nla(hdr, nla, nw);
301 if (ret == LINUX_NLA_UNHANDLED)
302 ret = nlmsg_copy_nla(nla, nw);
303 if (!ret)
304 return (false);
305 }
306 return (true);
307 }
308 #undef LINUX_NLA_UNHANDLED
309
310 static unsigned int
rtnl_if_flags_to_linux(unsigned int if_flags)311 rtnl_if_flags_to_linux(unsigned int if_flags)
312 {
313 unsigned int result = 0;
314
315 for (int i = 0; i < 31; i++) {
316 unsigned int flag = 1 << i;
317 if (!(flag & if_flags))
318 continue;
319 switch (flag) {
320 case IFF_UP:
321 case IFF_BROADCAST:
322 case IFF_DEBUG:
323 case IFF_LOOPBACK:
324 case IFF_POINTOPOINT:
325 case IFF_DRV_RUNNING:
326 case IFF_NOARP:
327 case IFF_PROMISC:
328 case IFF_ALLMULTI:
329 result |= flag;
330 break;
331 case IFF_NEEDSEPOCH:
332 case IFF_DRV_OACTIVE:
333 case IFF_SIMPLEX:
334 case IFF_LINK0:
335 case IFF_LINK1:
336 case IFF_LINK2:
337 case IFF_CANTCONFIG:
338 case IFF_PPROMISC:
339 case IFF_MONITOR:
340 case IFF_STATICARP:
341 case IFF_STICKYARP:
342 case IFF_DYING:
343 /* No Linux analogue */
344 break;
345 case IFF_LOWER_UP: /* IFF_NETLINK_1 */
346 result |= LINUX_IFF_LOWER_UP;
347 break;
348 case IFF_MULTICAST:
349 result |= LINUX_IFF_MULTICAST;
350 }
351 }
352 return (result);
353 }
354
355 static bool
rtnl_newlink_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)356 rtnl_newlink_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
357 struct nl_writer *nw)
358 {
359 if (!nlmsg_copy_header(hdr, nw))
360 return (false);
361
362 struct ifinfomsg *ifinfo;
363 ifinfo = nlmsg_copy_next_header(hdr, nw, struct ifinfomsg);
364
365 ifinfo->ifi_family = bsd_to_linux_domain(ifinfo->ifi_family);
366 /* Convert interface type */
367 switch (ifinfo->ifi_type) {
368 case IFT_ETHER:
369 case IFT_BRIDGE:
370 case IFT_L2VLAN:
371 ifinfo->ifi_type = LINUX_ARPHRD_ETHER;
372 break;
373 case IFT_LOOP:
374 ifinfo->ifi_type = LINUX_ARPHRD_LOOPBACK;
375 break;
376 case IFT_STF:
377 ifinfo->ifi_type = LINUX_ARPHRD_SIT;
378 break;
379 }
380 ifinfo->ifi_flags = rtnl_if_flags_to_linux(ifinfo->ifi_flags);
381
382 /* Copy attributes unchanged */
383 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifinfomsg), nw))
384 return (false);
385
386 /* make ip(8) happy */
387 if (!nlattr_add_string(nw, IFLA_QDISC, "noqueue"))
388 return (false);
389
390 if (!nlattr_add_u32(nw, IFLA_TXQLEN, 1000))
391 return (false);
392
393 nlmsg_end(nw);
394 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
395 return (true);
396 }
397
398 static bool
rtnl_newaddr_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)399 rtnl_newaddr_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
400 struct nl_writer *nw)
401 {
402 if (!nlmsg_copy_header(hdr, nw))
403 return (false);
404
405 struct ifaddrmsg *ifamsg;
406 ifamsg = nlmsg_copy_next_header(hdr, nw, struct ifaddrmsg);
407
408 ifamsg->ifa_family = bsd_to_linux_domain(ifamsg->ifa_family);
409 /* XXX: fake ifa_flags? */
410
411 /* Copy attributes unchanged */
412 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifaddrmsg), nw))
413 return (false);
414
415 nlmsg_end(nw);
416 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
417 return (true);
418 }
419
420 static bool
rtnl_newneigh_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)421 rtnl_newneigh_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
422 struct nl_writer *nw)
423 {
424 if (!nlmsg_copy_header(hdr, nw))
425 return (false);
426
427 struct ndmsg *ndm;
428 ndm = nlmsg_copy_next_header(hdr, nw, struct ndmsg);
429
430 ndm->ndm_family = bsd_to_linux_domain(ndm->ndm_family);
431
432 /* Copy attributes unchanged */
433 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ndmsg), nw))
434 return (false);
435
436 nlmsg_end(nw);
437 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
438 return (true);
439 }
440
441 static bool
rtnl_newroute_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)442 rtnl_newroute_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
443 struct nl_writer *nw)
444 {
445 if (!nlmsg_copy_header(hdr, nw))
446 return (false);
447
448 struct rtmsg *rtm;
449 rtm = nlmsg_copy_next_header(hdr, nw, struct rtmsg);
450 rtm->rtm_family = bsd_to_linux_domain(rtm->rtm_family);
451
452 struct nlattr *nla;
453
454 int hdrlen = NETLINK_ALIGN(sizeof(struct rtmsg));
455 int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
456 struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
457
458 NLA_FOREACH(nla, nla_head, attrs_len) {
459 struct rtattr *rta = (struct rtattr *)nla;
460 //RT_LOG(LOG_DEBUG, "READING attr %d len %d", nla->nla_type, nla->nla_len);
461 if (rta->rta_len < sizeof(struct rtattr)) {
462 break;
463 }
464
465 switch (rta->rta_type) {
466 case NL_RTA_TABLE:
467 {
468 uint32_t fibnum;
469 fibnum = _rta_get_uint32(rta);
470 if (fibnum == 0)
471 fibnum = 254;
472 RT_LOG(LOG_DEBUG3, "XFIBNUM %u", fibnum);
473 if (!nlattr_add_u32(nw, NL_RTA_TABLE, fibnum))
474 return (false);
475 }
476 break;
477 default:
478 if (!nlmsg_copy_nla(nla, nw))
479 return (false);
480 break;
481 }
482 }
483
484 nlmsg_end(nw);
485 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
486 return (true);
487 }
488
489 static bool
rtnl_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)490 rtnl_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
491 {
492 RT_LOG(LOG_DEBUG2, "Got message type %d", hdr->nlmsg_type);
493
494 switch (hdr->nlmsg_type) {
495 case NL_RTM_NEWLINK:
496 case NL_RTM_DELLINK:
497 case NL_RTM_GETLINK:
498 return (rtnl_newlink_to_linux(hdr, nlp, nw));
499 case NL_RTM_NEWADDR:
500 case NL_RTM_DELADDR:
501 return (rtnl_newaddr_to_linux(hdr, nlp, nw));
502 case NL_RTM_NEWROUTE:
503 case NL_RTM_DELROUTE:
504 return (rtnl_newroute_to_linux(hdr, nlp, nw));
505 case NL_RTM_NEWNEIGH:
506 case NL_RTM_DELNEIGH:
507 case NL_RTM_GETNEIGH:
508 return (rtnl_newneigh_to_linux(hdr, nlp, nw));
509 default:
510 RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
511 hdr->nlmsg_type);
512 return (handle_default_out(hdr, nw));
513 }
514 }
515
516 static bool
nlmsg_error_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)517 nlmsg_error_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
518 {
519 if (!nlmsg_copy_header(hdr, nw))
520 return (false);
521
522 struct nlmsgerr *nlerr;
523 nlerr = nlmsg_copy_next_header(hdr, nw, struct nlmsgerr);
524 nlerr->error = bsd_to_linux_errno(nlerr->error);
525
526 int copied_len = sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr);
527 if (hdr->nlmsg_len == copied_len) {
528 nlmsg_end(nw);
529 return (true);
530 }
531
532 /*
533 * CAP_ACK was not set. Original request needs to be translated.
534 * XXX: implement translation of the original message
535 */
536 RT_LOG(LOG_DEBUG, "[WARN] Passing ack message type %d untranslated",
537 nlerr->msg.nlmsg_type);
538 char *dst_payload, *src_payload;
539 int copy_len = hdr->nlmsg_len - copied_len;
540 dst_payload = nlmsg_reserve_data(nw, NLMSG_ALIGN(copy_len), char);
541
542 src_payload = (char *)hdr + copied_len;
543
544 memcpy(dst_payload, src_payload, copy_len);
545 nlmsg_end(nw);
546
547 return (true);
548 }
549
550 static bool
nlmsg_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)551 nlmsg_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
552 {
553 if (hdr->nlmsg_type < NLMSG_MIN_TYPE) {
554 switch (hdr->nlmsg_type) {
555 case NLMSG_ERROR:
556 return (nlmsg_error_to_linux(hdr, nlp, nw));
557 case NLMSG_NOOP:
558 case NLMSG_DONE:
559 case NLMSG_OVERRUN:
560 return (handle_default_out(hdr, nw));
561 default:
562 RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
563 hdr->nlmsg_type);
564 return (handle_default_out(hdr, nw));
565 }
566 }
567
568 switch (nlp->nl_proto) {
569 case NETLINK_ROUTE:
570 return (rtnl_to_linux(hdr, nlp, nw));
571 default:
572 return (handle_default_out(hdr, nw));
573 }
574 }
575
576 static struct nl_buf *
nlmsgs_to_linux(struct nl_buf * orig,struct nlpcb * nlp,const struct ifnet * ifp)577 nlmsgs_to_linux(struct nl_buf *orig, struct nlpcb *nlp, const struct ifnet *ifp)
578 {
579 struct nl_writer nw;
580 u_int offset, msglen;
581
582 if (__predict_false(!nl_writer_unicast(&nw,
583 orig->datalen + SCRATCH_BUFFER_SIZE, nlp, false)))
584 return (NULL);
585
586 nw.ifp = ifp;
587 /* Assume correct headers. Buffer IS mutable */
588 for (offset = 0;
589 offset + sizeof(struct nlmsghdr) <= orig->datalen;
590 offset += msglen) {
591 struct nlmsghdr *hdr = (struct nlmsghdr *)&orig->data[offset];
592
593 msglen = NLMSG_ALIGN(hdr->nlmsg_len);
594 if (!nlmsg_to_linux(hdr, nlp, &nw)) {
595 RT_LOG(LOG_DEBUG, "failed to process msg type %d",
596 hdr->nlmsg_type);
597 nl_buf_free(nw.buf);
598 return (NULL);
599 }
600 }
601
602 RT_LOG(LOG_DEBUG3, "%p: in %u bytes %u messages", __func__,
603 nw.buf->datalen, nw.num_messages);
604
605 return (nw.buf);
606 }
607
608 static struct linux_netlink_provider linux_netlink_v1 = {
609 .msgs_to_linux = nlmsgs_to_linux,
610 .msg_from_linux = nlmsg_from_linux,
611 };
612
613 void
linux_netlink_register(void)614 linux_netlink_register(void)
615 {
616 linux_netlink_p = &linux_netlink_v1;
617 }
618
619 void
linux_netlink_deregister(void)620 linux_netlink_deregister(void)
621 {
622 linux_netlink_p = NULL;
623 }
624