xref: /freebsd/sys/compat/linux/linux_netlink.c (revision 5cc15fef2656b078982d453b8915379b47ff0e88)
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
58 valid_rta_size(const struct rtattr *rta, int sz)
59 {
60 	return (NL_RTA_DATA_LEN(rta) == sz);
61 }
62 
63 static bool
64 valid_rta_u32(const struct rtattr *rta)
65 {
66 	return (valid_rta_size(rta, sizeof(uint32_t)));
67 }
68 
69 static uint32_t
70 _rta_get_uint32(const struct rtattr *rta)
71 {
72 	return (*((const uint32_t *)NL_RTA_DATA_CONST(rta)));
73 }
74 
75 static int
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
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
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
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
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
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
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 *
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
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
248 nlmsg_translate_ifname_nla(struct nlmsghdr *hdr, struct nlattr *nla,
249     struct nl_writer *nw)
250 {
251 	struct ifinfomsg *ifinfo;
252 	char ifname[LINUX_IFNAMSIZ];
253 
254 	ifinfo = (struct ifinfomsg *)(hdr + 1);
255 	if (ifname_bsd_to_linux_idx(ifinfo->ifi_index, ifname,
256 	    sizeof(ifname)) <= 0)
257 		return (nlmsg_copy_nla(nla, nw));
258 	return (nlattr_add_string(nw, IFLA_IFNAME, ifname));
259 }
260 
261 #define	LINUX_NLA_UNHANDLED	-1
262 /*
263  * Translate a FreeBSD attribute to a Linux attribute.
264  * Returns LINUX_NLA_UNHANDLED when the attribute is not processed
265  * and the caller must take care of it, otherwise the result is returned.
266  */
267 static int
268 nlmsg_translate_all_nla(struct nlmsghdr *hdr, struct nlattr *nla,
269     struct nl_writer *nw)
270 {
271 
272 	switch (hdr->nlmsg_type) {
273 	case NL_RTM_NEWLINK:
274 	case NL_RTM_DELLINK:
275 	case NL_RTM_GETLINK:
276 		switch (nla->nla_type) {
277 		case IFLA_IFNAME:
278 			return (nlmsg_translate_ifname_nla(hdr, nla, nw));
279 		default:
280 			break;
281 		}
282 	default:
283 		break;
284 	}
285 	return (LINUX_NLA_UNHANDLED);
286 }
287 
288 static bool
289 nlmsg_copy_all_nla(struct nlmsghdr *hdr, int raw_hdrlen, struct nl_writer *nw)
290 {
291 	struct nlattr *nla;
292 	int ret;
293 
294 	int hdrlen = NETLINK_ALIGN(raw_hdrlen);
295 	int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
296 	struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
297 
298 	NLA_FOREACH(nla, nla_head, attrs_len) {
299 		RT_LOG(LOG_DEBUG3, "reading attr %d len %d", nla->nla_type, nla->nla_len);
300 		if (nla->nla_len < sizeof(struct nlattr)) {
301 			return (false);
302 		}
303 		ret = nlmsg_translate_all_nla(hdr, nla, nw);
304 		if (ret == LINUX_NLA_UNHANDLED)
305 			ret = nlmsg_copy_nla(nla, nw);
306 		if (!ret)
307 			return (false);
308 	}
309 	return (true);
310 }
311 #undef LINUX_NLA_UNHANDLED
312 
313 static unsigned int
314 rtnl_if_flags_to_linux(unsigned int if_flags)
315 {
316 	unsigned int result = 0;
317 
318 	for (int i = 0; i < 31; i++) {
319 		unsigned int flag = 1 << i;
320 		if (!(flag & if_flags))
321 			continue;
322 		switch (flag) {
323 		case IFF_UP:
324 		case IFF_BROADCAST:
325 		case IFF_DEBUG:
326 		case IFF_LOOPBACK:
327 		case IFF_POINTOPOINT:
328 		case IFF_DRV_RUNNING:
329 		case IFF_NOARP:
330 		case IFF_PROMISC:
331 		case IFF_ALLMULTI:
332 			result |= flag;
333 			break;
334 		case IFF_NEEDSEPOCH:
335 		case IFF_DRV_OACTIVE:
336 		case IFF_SIMPLEX:
337 		case IFF_LINK0:
338 		case IFF_LINK1:
339 		case IFF_LINK2:
340 		case IFF_CANTCONFIG:
341 		case IFF_PPROMISC:
342 		case IFF_MONITOR:
343 		case IFF_STATICARP:
344 		case IFF_STICKYARP:
345 		case IFF_DYING:
346 			/* No Linux analogue */
347 			break;
348 		case IFF_LOWER_UP:	/* IFF_NETLINK_1 */
349 			result |= LINUX_IFF_LOWER_UP;
350 			break;
351 		case IFF_MULTICAST:
352 			result |= LINUX_IFF_MULTICAST;
353 		}
354 	}
355 	return (result);
356 }
357 
358 static bool
359 rtnl_newlink_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
360     struct nl_writer *nw)
361 {
362 	if (!nlmsg_copy_header(hdr, nw))
363 		return (false);
364 
365 	struct ifinfomsg *ifinfo;
366 	ifinfo = nlmsg_copy_next_header(hdr, nw, struct ifinfomsg);
367 
368 	ifinfo->ifi_family = bsd_to_linux_domain(ifinfo->ifi_family);
369 	/* Convert interface type */
370 	switch (ifinfo->ifi_type) {
371 	case IFT_ETHER:
372 	case IFT_BRIDGE:
373 	case IFT_L2VLAN:
374 		ifinfo->ifi_type = LINUX_ARPHRD_ETHER;
375 		break;
376 	case IFT_LOOP:
377 		ifinfo->ifi_type = LINUX_ARPHRD_LOOPBACK;
378 		break;
379 	case IFT_STF:
380 		ifinfo->ifi_type = LINUX_ARPHRD_SIT;
381 		break;
382 	}
383 	ifinfo->ifi_flags = rtnl_if_flags_to_linux(ifinfo->ifi_flags);
384 
385 	/* Copy attributes unchanged */
386 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifinfomsg), nw))
387 		return (false);
388 
389 	/* make ip(8) happy */
390 	if (!nlattr_add_string(nw, IFLA_QDISC, "noqueue"))
391 		return (false);
392 
393 	if (!nlattr_add_u32(nw, IFLA_TXQLEN, 1000))
394 		return (false);
395 
396 	nlmsg_end(nw);
397 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
398 	return (true);
399 }
400 
401 static bool
402 rtnl_newaddr_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
403     struct nl_writer *nw)
404 {
405 	if (!nlmsg_copy_header(hdr, nw))
406 		return (false);
407 
408 	struct ifaddrmsg *ifamsg;
409 	ifamsg = nlmsg_copy_next_header(hdr, nw, struct ifaddrmsg);
410 
411 	ifamsg->ifa_family = bsd_to_linux_domain(ifamsg->ifa_family);
412 	/* XXX: fake ifa_flags? */
413 
414 	/* Copy attributes unchanged */
415 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifaddrmsg), nw))
416 		return (false);
417 
418 	nlmsg_end(nw);
419 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
420 	return (true);
421 }
422 
423 static bool
424 rtnl_newneigh_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
425     struct nl_writer *nw)
426 {
427 	if (!nlmsg_copy_header(hdr, nw))
428 		return (false);
429 
430 	struct ndmsg *ndm;
431 	ndm = nlmsg_copy_next_header(hdr, nw, struct ndmsg);
432 
433 	ndm->ndm_family = bsd_to_linux_domain(ndm->ndm_family);
434 
435 	/* Copy attributes unchanged */
436 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ndmsg), nw))
437 		return (false);
438 
439 	nlmsg_end(nw);
440 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
441 	return (true);
442 }
443 
444 static bool
445 rtnl_newroute_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
446     struct nl_writer *nw)
447 {
448 	if (!nlmsg_copy_header(hdr, nw))
449 		return (false);
450 
451 	struct rtmsg *rtm;
452 	rtm = nlmsg_copy_next_header(hdr, nw, struct rtmsg);
453 	rtm->rtm_family = bsd_to_linux_domain(rtm->rtm_family);
454 
455 	struct nlattr *nla;
456 
457 	int hdrlen = NETLINK_ALIGN(sizeof(struct rtmsg));
458 	int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
459 	struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
460 
461 	NLA_FOREACH(nla, nla_head, attrs_len) {
462 		struct rtattr *rta = (struct rtattr *)nla;
463 		//RT_LOG(LOG_DEBUG, "READING attr %d len %d", nla->nla_type, nla->nla_len);
464 		if (rta->rta_len < sizeof(struct rtattr)) {
465 			break;
466 		}
467 
468 		switch (rta->rta_type) {
469 		case NL_RTA_TABLE:
470 			{
471 				uint32_t fibnum;
472 				fibnum = _rta_get_uint32(rta);
473 				if (fibnum == 0)
474 					fibnum = 254;
475 				RT_LOG(LOG_DEBUG3, "XFIBNUM %u", fibnum);
476 				if (!nlattr_add_u32(nw, NL_RTA_TABLE, fibnum))
477 					return (false);
478 			}
479 			break;
480 		default:
481 			if (!nlmsg_copy_nla(nla, nw))
482 				return (false);
483 			break;
484 		}
485 	}
486 
487 	nlmsg_end(nw);
488 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
489 	return (true);
490 }
491 
492 static bool
493 rtnl_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
494 {
495 	RT_LOG(LOG_DEBUG2, "Got message type %d", hdr->nlmsg_type);
496 
497 	switch (hdr->nlmsg_type) {
498 	case NL_RTM_NEWLINK:
499 	case NL_RTM_DELLINK:
500 	case NL_RTM_GETLINK:
501 		return (rtnl_newlink_to_linux(hdr, nlp, nw));
502 	case NL_RTM_NEWADDR:
503 	case NL_RTM_DELADDR:
504 		return (rtnl_newaddr_to_linux(hdr, nlp, nw));
505 	case NL_RTM_NEWROUTE:
506 	case NL_RTM_DELROUTE:
507 		return (rtnl_newroute_to_linux(hdr, nlp, nw));
508 	case NL_RTM_NEWNEIGH:
509 	case NL_RTM_DELNEIGH:
510 	case NL_RTM_GETNEIGH:
511 		return (rtnl_newneigh_to_linux(hdr, nlp, nw));
512 	default:
513 		RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
514 		    hdr->nlmsg_type);
515 		return (handle_default_out(hdr, nw));
516 	}
517 }
518 
519 static bool
520 nlmsg_error_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
521 {
522 	if (!nlmsg_copy_header(hdr, nw))
523 		return (false);
524 
525 	struct nlmsgerr *nlerr;
526 	nlerr = nlmsg_copy_next_header(hdr, nw, struct nlmsgerr);
527 	nlerr->error = bsd_to_linux_errno(nlerr->error);
528 
529 	int copied_len = sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr);
530 	if (hdr->nlmsg_len == copied_len) {
531 		nlmsg_end(nw);
532 		return (true);
533 	}
534 
535 	/*
536 	 * CAP_ACK was not set. Original request needs to be translated.
537 	 * XXX: implement translation of the original message
538 	 */
539 	RT_LOG(LOG_DEBUG, "[WARN] Passing ack message type %d untranslated",
540 	    nlerr->msg.nlmsg_type);
541 	char *dst_payload, *src_payload;
542 	int copy_len = hdr->nlmsg_len - copied_len;
543 	dst_payload = nlmsg_reserve_data(nw, NLMSG_ALIGN(copy_len), char);
544 
545 	src_payload = (char *)hdr + copied_len;
546 
547 	memcpy(dst_payload, src_payload, copy_len);
548 	nlmsg_end(nw);
549 
550 	return (true);
551 }
552 
553 static bool
554 nlmsg_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
555 {
556 	if (hdr->nlmsg_type < NLMSG_MIN_TYPE) {
557 		switch (hdr->nlmsg_type) {
558 		case NLMSG_ERROR:
559 			return (nlmsg_error_to_linux(hdr, nlp, nw));
560 		case NLMSG_NOOP:
561 		case NLMSG_DONE:
562 		case NLMSG_OVERRUN:
563 			return (handle_default_out(hdr, nw));
564 		default:
565 			RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
566 			    hdr->nlmsg_type);
567 			return (handle_default_out(hdr, nw));
568 		}
569 	}
570 
571 	switch (nlp->nl_proto) {
572 	case NETLINK_ROUTE:
573 		return (rtnl_to_linux(hdr, nlp, nw));
574 	default:
575 		return (handle_default_out(hdr, nw));
576 	}
577 }
578 
579 static struct nl_buf *
580 nlmsgs_to_linux(struct nl_buf *orig, struct nlpcb *nlp, const struct ifnet *ifp)
581 {
582 	struct nl_writer nw;
583 	u_int offset, msglen;
584 
585 	if (__predict_false(!nl_writer_unicast(&nw,
586 	    orig->datalen + SCRATCH_BUFFER_SIZE, nlp, false)))
587 		return (NULL);
588 
589 	nw.ifp = ifp;
590 	/* Assume correct headers. Buffer IS mutable */
591 	for (offset = 0;
592 	    offset + sizeof(struct nlmsghdr) <= orig->datalen;
593 	    offset += msglen) {
594 		struct nlmsghdr *hdr = (struct nlmsghdr *)&orig->data[offset];
595 
596 		msglen = NLMSG_ALIGN(hdr->nlmsg_len);
597 		if (!nlmsg_to_linux(hdr, nlp, &nw)) {
598 			RT_LOG(LOG_DEBUG, "failed to process msg type %d",
599 			    hdr->nlmsg_type);
600 			nl_buf_free(nw.buf);
601 			return (NULL);
602 		}
603 	}
604 
605 	RT_LOG(LOG_DEBUG3, "%p: in %u bytes %u messages", __func__,
606 	    nw.buf->datalen, nw.num_messages);
607 
608 	return (nw.buf);
609 }
610 
611 static struct linux_netlink_provider linux_netlink_v1 = {
612 	.msgs_to_linux = nlmsgs_to_linux,
613 	.msg_from_linux = nlmsg_from_linux,
614 };
615 
616 void
617 linux_netlink_register(void)
618 {
619 	linux_netlink_p = &linux_netlink_v1;
620 }
621 
622 void
623 linux_netlink_deregister(void)
624 {
625 	linux_netlink_p = NULL;
626 }
627