xref: /freebsd/sys/compat/linux/linux_netlink.c (revision 2780e5f43d5b0e8b155472300ee63816a660780e)
17c40e2d5SAlexander V. Chernikov /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
37c40e2d5SAlexander V. Chernikov  *
47c40e2d5SAlexander V. Chernikov  * Copyright (c) 2022 Alexander V. Chernikov
57c40e2d5SAlexander V. Chernikov  *
67c40e2d5SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
77c40e2d5SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
87c40e2d5SAlexander V. Chernikov  * are met:
97c40e2d5SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
107c40e2d5SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
117c40e2d5SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
127c40e2d5SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
137c40e2d5SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
147c40e2d5SAlexander V. Chernikov  *
157c40e2d5SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
167c40e2d5SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
177c40e2d5SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
187c40e2d5SAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
197c40e2d5SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
207c40e2d5SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
217c40e2d5SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
227c40e2d5SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
237c40e2d5SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
247c40e2d5SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
257c40e2d5SAlexander V. Chernikov  * SUCH DAMAGE.
267c40e2d5SAlexander V. Chernikov  */
277c40e2d5SAlexander V. Chernikov 
287c40e2d5SAlexander V. Chernikov #include "opt_inet.h"
297c40e2d5SAlexander V. Chernikov #include "opt_inet6.h"
30d8e53d94SDmitry Chagin 
317c40e2d5SAlexander V. Chernikov #include <sys/types.h>
32d8e53d94SDmitry Chagin #include <sys/ck.h>
33d8e53d94SDmitry Chagin #include <sys/lock.h>
347c40e2d5SAlexander V. Chernikov #include <sys/socket.h>
35d8e53d94SDmitry Chagin #include <sys/vnode.h>
367c40e2d5SAlexander V. Chernikov 
377c40e2d5SAlexander V. Chernikov #include <net/if.h>
387c40e2d5SAlexander V. Chernikov #include <net/if_dl.h>
397c40e2d5SAlexander V. Chernikov #include <net/route.h>
407c40e2d5SAlexander V. Chernikov #include <net/route/nhop.h>
417c40e2d5SAlexander V. Chernikov #include <net/route/route_ctl.h>
427c40e2d5SAlexander V. Chernikov #include <netlink/netlink.h>
437c40e2d5SAlexander V. Chernikov #include <netlink/netlink_ctl.h>
447c40e2d5SAlexander V. Chernikov #include <netlink/netlink_linux.h>
4517083b94SGleb Smirnoff #include <netlink/netlink_var.h>
467c40e2d5SAlexander V. Chernikov #include <netlink/netlink_route.h>
477c40e2d5SAlexander V. Chernikov 
487c40e2d5SAlexander V. Chernikov #include <compat/linux/linux.h>
497c40e2d5SAlexander V. Chernikov #include <compat/linux/linux_common.h>
507c40e2d5SAlexander V. Chernikov #include <compat/linux/linux_util.h>
517c40e2d5SAlexander V. Chernikov 
527c40e2d5SAlexander V. Chernikov #define	DEBUG_MOD_NAME	nl_linux
537c40e2d5SAlexander V. Chernikov #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
547c40e2d5SAlexander V. Chernikov #include <netlink/netlink_debug.h>
55fa554de7SKristof Provost _DECLARE_DEBUG(LOG_INFO);
567c40e2d5SAlexander V. Chernikov 
577c40e2d5SAlexander V. Chernikov static bool
valid_rta_size(const struct rtattr * rta,int sz)587c40e2d5SAlexander V. Chernikov valid_rta_size(const struct rtattr *rta, int sz)
597c40e2d5SAlexander V. Chernikov {
607c40e2d5SAlexander V. Chernikov 	return (NL_RTA_DATA_LEN(rta) == sz);
617c40e2d5SAlexander V. Chernikov }
627c40e2d5SAlexander V. Chernikov 
637c40e2d5SAlexander V. Chernikov static bool
valid_rta_u32(const struct rtattr * rta)647c40e2d5SAlexander V. Chernikov valid_rta_u32(const struct rtattr *rta)
657c40e2d5SAlexander V. Chernikov {
667c40e2d5SAlexander V. Chernikov 	return (valid_rta_size(rta, sizeof(uint32_t)));
677c40e2d5SAlexander V. Chernikov }
687c40e2d5SAlexander V. Chernikov 
697c40e2d5SAlexander V. Chernikov static uint32_t
_rta_get_uint32(const struct rtattr * rta)707c40e2d5SAlexander V. Chernikov _rta_get_uint32(const struct rtattr *rta)
717c40e2d5SAlexander V. Chernikov {
727c40e2d5SAlexander V. Chernikov 	return (*((const uint32_t *)NL_RTA_DATA_CONST(rta)));
737c40e2d5SAlexander V. Chernikov }
747c40e2d5SAlexander V. Chernikov 
75b977dd1eSGleb Smirnoff static int
rtnl_neigh_from_linux(struct nlmsghdr * hdr,struct nl_pstate * npt)767c40e2d5SAlexander V. Chernikov rtnl_neigh_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
777c40e2d5SAlexander V. Chernikov {
787c40e2d5SAlexander V. Chernikov 	struct ndmsg *ndm = (struct ndmsg *)(hdr + 1);
79b977dd1eSGleb Smirnoff 	sa_family_t f;
807c40e2d5SAlexander V. Chernikov 
81b977dd1eSGleb Smirnoff 	if (hdr->nlmsg_len < sizeof(struct nlmsghdr) + sizeof(struct ndmsg))
82b977dd1eSGleb Smirnoff 		return (EBADMSG);
83b977dd1eSGleb Smirnoff 	if ((f = linux_to_bsd_domain(ndm->ndm_family)) == AF_UNKNOWN)
84b977dd1eSGleb Smirnoff 		return (EPFNOSUPPORT);
857c40e2d5SAlexander V. Chernikov 
86b977dd1eSGleb Smirnoff 	ndm->ndm_family = f;
87b977dd1eSGleb Smirnoff 
88b977dd1eSGleb Smirnoff 	return (0);
897c40e2d5SAlexander V. Chernikov }
907c40e2d5SAlexander V. Chernikov 
91b977dd1eSGleb Smirnoff static int
rtnl_ifaddr_from_linux(struct nlmsghdr * hdr,struct nl_pstate * npt)927c40e2d5SAlexander V. Chernikov rtnl_ifaddr_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
937c40e2d5SAlexander V. Chernikov {
947c40e2d5SAlexander V. Chernikov 	struct ifaddrmsg *ifam = (struct ifaddrmsg *)(hdr + 1);
95b977dd1eSGleb Smirnoff 	sa_family_t f;
967c40e2d5SAlexander V. Chernikov 
97*2780e5f4SGleb Smirnoff 	if (hdr->nlmsg_len < sizeof(struct nlmsghdr) +
98*2780e5f4SGleb Smirnoff 	    offsetof(struct ifaddrmsg, ifa_family) + sizeof(ifam->ifa_family))
99b977dd1eSGleb Smirnoff 		return (EBADMSG);
100b977dd1eSGleb Smirnoff 	if ((f = linux_to_bsd_domain(ifam->ifa_family)) == AF_UNKNOWN)
101b977dd1eSGleb Smirnoff 		return (EPFNOSUPPORT);
1027c40e2d5SAlexander V. Chernikov 
103b977dd1eSGleb Smirnoff 	ifam->ifa_family = f;
104b977dd1eSGleb Smirnoff 
105b977dd1eSGleb Smirnoff 	return (0);
1067c40e2d5SAlexander V. Chernikov }
1077c40e2d5SAlexander V. Chernikov 
108b977dd1eSGleb Smirnoff /*
109b977dd1eSGleb Smirnoff  * XXX: in case of error state of hdr is inconsistent.
110b977dd1eSGleb Smirnoff  */
111b977dd1eSGleb Smirnoff static int
rtnl_route_from_linux(struct nlmsghdr * hdr,struct nl_pstate * npt)1127c40e2d5SAlexander V. Chernikov rtnl_route_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
1137c40e2d5SAlexander V. Chernikov {
1147c40e2d5SAlexander V. Chernikov 	/* Tweak address families and default fib only */
1157c40e2d5SAlexander V. Chernikov 	struct rtmsg *rtm = (struct rtmsg *)(hdr + 1);
1167c40e2d5SAlexander V. Chernikov 	struct nlattr *nla, *nla_head;
1177c40e2d5SAlexander V. Chernikov 	int attrs_len;
118b977dd1eSGleb Smirnoff 	sa_family_t f;
1197c40e2d5SAlexander V. Chernikov 
120b977dd1eSGleb Smirnoff 	if (hdr->nlmsg_len < sizeof(struct nlmsghdr) + sizeof(struct rtmsg))
121b977dd1eSGleb Smirnoff 		return (EBADMSG);
122b977dd1eSGleb Smirnoff 	if ((f = linux_to_bsd_domain(rtm->rtm_family)) == AF_UNKNOWN)
123b977dd1eSGleb Smirnoff 		return (EPFNOSUPPORT);
124b977dd1eSGleb Smirnoff 	rtm->rtm_family = f;
1257c40e2d5SAlexander V. Chernikov 
1267c40e2d5SAlexander V. Chernikov 	if (rtm->rtm_table == 254)
1277c40e2d5SAlexander V. Chernikov 		rtm->rtm_table = 0;
1287c40e2d5SAlexander V. Chernikov 
1297c40e2d5SAlexander V. Chernikov 	attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr);
1307c40e2d5SAlexander V. Chernikov 	attrs_len -= NETLINK_ALIGN(sizeof(struct rtmsg));
1317c40e2d5SAlexander V. Chernikov 	nla_head = (struct nlattr *)((char *)rtm + NETLINK_ALIGN(sizeof(struct rtmsg)));
1327c40e2d5SAlexander V. Chernikov 
1337c40e2d5SAlexander V. Chernikov 	NLA_FOREACH(nla, nla_head, attrs_len) {
1347c40e2d5SAlexander V. Chernikov 		RT_LOG(LOG_DEBUG3, "GOT type %d len %d total %d",
1357c40e2d5SAlexander V. Chernikov 		    nla->nla_type, nla->nla_len, attrs_len);
1367c40e2d5SAlexander V. Chernikov 		struct rtattr *rta = (struct rtattr *)nla;
1377c40e2d5SAlexander V. Chernikov 		if (rta->rta_len < sizeof(struct rtattr)) {
1387c40e2d5SAlexander V. Chernikov 			break;
1397c40e2d5SAlexander V. Chernikov 		}
1407c40e2d5SAlexander V. Chernikov 		switch (rta->rta_type) {
1417c40e2d5SAlexander V. Chernikov 		case NL_RTA_TABLE:
1427c40e2d5SAlexander V. Chernikov 			if (!valid_rta_u32(rta))
143b977dd1eSGleb Smirnoff 				return (EBADMSG);
1447c40e2d5SAlexander V. Chernikov 			rtm->rtm_table = 0;
1457c40e2d5SAlexander V. Chernikov 			uint32_t fibnum = _rta_get_uint32(rta);
1467c40e2d5SAlexander V. Chernikov 			RT_LOG(LOG_DEBUG3, "GET RTABLE: %u", fibnum);
1477c40e2d5SAlexander V. Chernikov 			if (fibnum == 254) {
1487c40e2d5SAlexander V. Chernikov 				*((uint32_t *)NL_RTA_DATA(rta)) = 0;
1497c40e2d5SAlexander V. Chernikov 			}
1507c40e2d5SAlexander V. Chernikov 			break;
1517c40e2d5SAlexander V. Chernikov 		}
1527c40e2d5SAlexander V. Chernikov 	}
1537c40e2d5SAlexander V. Chernikov 
154b977dd1eSGleb Smirnoff 	return (0);
1557c40e2d5SAlexander V. Chernikov }
1567c40e2d5SAlexander V. Chernikov 
157b977dd1eSGleb Smirnoff static int
rtnl_from_linux(struct nlmsghdr * hdr,struct nl_pstate * npt)1587c40e2d5SAlexander V. Chernikov rtnl_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
1597c40e2d5SAlexander V. Chernikov {
160b977dd1eSGleb Smirnoff 
1617c40e2d5SAlexander V. Chernikov 	switch (hdr->nlmsg_type) {
1627c40e2d5SAlexander V. Chernikov 	case NL_RTM_GETROUTE:
1637c40e2d5SAlexander V. Chernikov 	case NL_RTM_NEWROUTE:
1647c40e2d5SAlexander V. Chernikov 	case NL_RTM_DELROUTE:
1657c40e2d5SAlexander V. Chernikov 		return (rtnl_route_from_linux(hdr, npt));
1667c40e2d5SAlexander V. Chernikov 	case NL_RTM_GETNEIGH:
1677c40e2d5SAlexander V. Chernikov 		return (rtnl_neigh_from_linux(hdr, npt));
1687c40e2d5SAlexander V. Chernikov 	case NL_RTM_GETADDR:
1697c40e2d5SAlexander V. Chernikov 		return (rtnl_ifaddr_from_linux(hdr, npt));
1707c40e2d5SAlexander V. Chernikov 	/* Silence warning for the messages where no translation is required */
1717c40e2d5SAlexander V. Chernikov 	case NL_RTM_NEWLINK:
1727c40e2d5SAlexander V. Chernikov 	case NL_RTM_DELLINK:
1737c40e2d5SAlexander V. Chernikov 	case NL_RTM_GETLINK:
1747c40e2d5SAlexander V. Chernikov 		break;
1757c40e2d5SAlexander V. Chernikov 	default:
1767c40e2d5SAlexander V. Chernikov 		RT_LOG(LOG_DEBUG, "Passing message type %d untranslated",
1777c40e2d5SAlexander V. Chernikov 		    hdr->nlmsg_type);
178b977dd1eSGleb Smirnoff 		/* XXXGL: maybe return error? */
1797c40e2d5SAlexander V. Chernikov 	}
1807c40e2d5SAlexander V. Chernikov 
181b977dd1eSGleb Smirnoff 	return (0);
1827c40e2d5SAlexander V. Chernikov }
1837c40e2d5SAlexander V. Chernikov 
184b977dd1eSGleb Smirnoff static int
nlmsg_from_linux(int netlink_family,struct nlmsghdr ** hdr,struct nl_pstate * npt)185b977dd1eSGleb Smirnoff nlmsg_from_linux(int netlink_family, struct nlmsghdr **hdr,
1867c40e2d5SAlexander V. Chernikov     struct nl_pstate *npt)
1877c40e2d5SAlexander V. Chernikov {
1887c40e2d5SAlexander V. Chernikov 	switch (netlink_family) {
1897c40e2d5SAlexander V. Chernikov 	case NETLINK_ROUTE:
190b977dd1eSGleb Smirnoff 		return (rtnl_from_linux(*hdr, npt));
1917c40e2d5SAlexander V. Chernikov 	}
1927c40e2d5SAlexander V. Chernikov 
193b977dd1eSGleb Smirnoff 	return (0);
1947c40e2d5SAlexander V. Chernikov }
1957c40e2d5SAlexander V. Chernikov 
1967c40e2d5SAlexander V. Chernikov 
1977c40e2d5SAlexander V. Chernikov /************************************************************
1987c40e2d5SAlexander V. Chernikov  * Kernel -> Linux
1997c40e2d5SAlexander V. Chernikov  ************************************************************/
2007c40e2d5SAlexander V. Chernikov 
2017c40e2d5SAlexander V. Chernikov static bool
handle_default_out(struct nlmsghdr * hdr,struct nl_writer * nw)2027c40e2d5SAlexander V. Chernikov handle_default_out(struct nlmsghdr *hdr, struct nl_writer *nw)
2037c40e2d5SAlexander V. Chernikov {
2047c40e2d5SAlexander V. Chernikov 	char *out_hdr;
2057c40e2d5SAlexander V. Chernikov 	out_hdr = nlmsg_reserve_data(nw, NLMSG_ALIGN(hdr->nlmsg_len), char);
2067c40e2d5SAlexander V. Chernikov 
2077c40e2d5SAlexander V. Chernikov 	if (out_hdr != NULL) {
2087c40e2d5SAlexander V. Chernikov 		memcpy(out_hdr, hdr, hdr->nlmsg_len);
20917083b94SGleb Smirnoff 		nw->num_messages++;
2107c40e2d5SAlexander V. Chernikov 		return (true);
2117c40e2d5SAlexander V. Chernikov 	}
2127c40e2d5SAlexander V. Chernikov 	return (false);
2137c40e2d5SAlexander V. Chernikov }
2147c40e2d5SAlexander V. Chernikov 
2157c40e2d5SAlexander V. Chernikov static bool
nlmsg_copy_header(struct nlmsghdr * hdr,struct nl_writer * nw)2167c40e2d5SAlexander V. Chernikov nlmsg_copy_header(struct nlmsghdr *hdr, struct nl_writer *nw)
2177c40e2d5SAlexander V. Chernikov {
2187c40e2d5SAlexander V. Chernikov 	return (nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, hdr->nlmsg_type,
2197c40e2d5SAlexander V. Chernikov 	    hdr->nlmsg_flags, 0));
2207c40e2d5SAlexander V. Chernikov }
2217c40e2d5SAlexander V. Chernikov 
2227c40e2d5SAlexander V. Chernikov static void *
_nlmsg_copy_next_header(struct nlmsghdr * hdr,struct nl_writer * nw,int sz)2237c40e2d5SAlexander V. Chernikov _nlmsg_copy_next_header(struct nlmsghdr *hdr, struct nl_writer *nw, int sz)
2247c40e2d5SAlexander V. Chernikov {
2257c40e2d5SAlexander V. Chernikov 	void *next_hdr = nlmsg_reserve_data(nw, sz, void);
2267c40e2d5SAlexander V. Chernikov 	memcpy(next_hdr, hdr + 1, NLMSG_ALIGN(sz));
2277c40e2d5SAlexander V. Chernikov 
2287c40e2d5SAlexander V. Chernikov 	return (next_hdr);
2297c40e2d5SAlexander V. Chernikov }
2307c40e2d5SAlexander V. Chernikov #define	nlmsg_copy_next_header(_hdr, _ns, _t)	\
2317c40e2d5SAlexander V. Chernikov 	((_t *)(_nlmsg_copy_next_header(_hdr, _ns, sizeof(_t))))
2327c40e2d5SAlexander V. Chernikov 
2337c40e2d5SAlexander V. Chernikov static bool
nlmsg_copy_nla(const struct nlattr * nla_orig,struct nl_writer * nw)2347c40e2d5SAlexander V. Chernikov nlmsg_copy_nla(const struct nlattr *nla_orig, struct nl_writer *nw)
2357c40e2d5SAlexander V. Chernikov {
2367c40e2d5SAlexander V. Chernikov 	struct nlattr *nla = nlmsg_reserve_data(nw, nla_orig->nla_len, struct nlattr);
2377c40e2d5SAlexander V. Chernikov 	if (nla != NULL) {
2387c40e2d5SAlexander V. Chernikov 		memcpy(nla, nla_orig, nla_orig->nla_len);
2397c40e2d5SAlexander V. Chernikov 		return (true);
2407c40e2d5SAlexander V. Chernikov 	}
2417c40e2d5SAlexander V. Chernikov 	return (false);
2427c40e2d5SAlexander V. Chernikov }
2437c40e2d5SAlexander V. Chernikov 
244e55e4a6bSDmitry Chagin /*
245e55e4a6bSDmitry Chagin  * Translate a FreeBSD interface name to a Linux interface name.
246e55e4a6bSDmitry Chagin  */
247e55e4a6bSDmitry Chagin static bool
nlmsg_translate_ifname_nla(struct nlattr * nla,struct nl_writer * nw)248e55e4a6bSDmitry Chagin nlmsg_translate_ifname_nla(struct nlattr *nla, struct nl_writer *nw)
249e55e4a6bSDmitry Chagin {
250e55e4a6bSDmitry Chagin 	char ifname[LINUX_IFNAMSIZ];
251e55e4a6bSDmitry Chagin 
252e55e4a6bSDmitry Chagin 	if (ifname_bsd_to_linux_name((char *)(nla + 1), ifname,
253e55e4a6bSDmitry Chagin 	    sizeof(ifname)) <= 0)
254e55e4a6bSDmitry Chagin 		return (false);
255e55e4a6bSDmitry Chagin 	return (nlattr_add_string(nw, IFLA_IFNAME, ifname));
256e55e4a6bSDmitry Chagin }
257e55e4a6bSDmitry Chagin 
258e55e4a6bSDmitry Chagin #define	LINUX_NLA_UNHANDLED	-1
259e55e4a6bSDmitry Chagin /*
260e55e4a6bSDmitry Chagin  * Translate a FreeBSD attribute to a Linux attribute.
261e55e4a6bSDmitry Chagin  * Returns LINUX_NLA_UNHANDLED when the attribute is not processed
262e55e4a6bSDmitry Chagin  * and the caller must take care of it, otherwise the result is returned.
263e55e4a6bSDmitry Chagin  */
264e55e4a6bSDmitry Chagin static int
nlmsg_translate_all_nla(struct nlmsghdr * hdr,struct nlattr * nla,struct nl_writer * nw)265e55e4a6bSDmitry Chagin nlmsg_translate_all_nla(struct nlmsghdr *hdr, struct nlattr *nla,
266e55e4a6bSDmitry Chagin     struct nl_writer *nw)
267e55e4a6bSDmitry Chagin {
268e55e4a6bSDmitry Chagin 
269e55e4a6bSDmitry Chagin 	switch (hdr->nlmsg_type) {
270e55e4a6bSDmitry Chagin 	case NL_RTM_NEWLINK:
271e55e4a6bSDmitry Chagin 	case NL_RTM_DELLINK:
272e55e4a6bSDmitry Chagin 	case NL_RTM_GETLINK:
273e55e4a6bSDmitry Chagin 		switch (nla->nla_type) {
274e55e4a6bSDmitry Chagin 		case IFLA_IFNAME:
275e55e4a6bSDmitry Chagin 			return (nlmsg_translate_ifname_nla(nla, nw));
276e55e4a6bSDmitry Chagin 		default:
277e55e4a6bSDmitry Chagin 			break;
278e55e4a6bSDmitry Chagin 		}
279e55e4a6bSDmitry Chagin 	default:
280e55e4a6bSDmitry Chagin 		break;
281e55e4a6bSDmitry Chagin 	}
282e55e4a6bSDmitry Chagin 	return (LINUX_NLA_UNHANDLED);
283e55e4a6bSDmitry Chagin }
284e55e4a6bSDmitry Chagin 
2857c40e2d5SAlexander V. Chernikov static bool
nlmsg_copy_all_nla(struct nlmsghdr * hdr,int raw_hdrlen,struct nl_writer * nw)2867c40e2d5SAlexander V. Chernikov nlmsg_copy_all_nla(struct nlmsghdr *hdr, int raw_hdrlen, struct nl_writer *nw)
2877c40e2d5SAlexander V. Chernikov {
2887c40e2d5SAlexander V. Chernikov 	struct nlattr *nla;
289e55e4a6bSDmitry Chagin 	int ret;
2907c40e2d5SAlexander V. Chernikov 
2917c40e2d5SAlexander V. Chernikov 	int hdrlen = NETLINK_ALIGN(raw_hdrlen);
2927c40e2d5SAlexander V. Chernikov 	int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
2937c40e2d5SAlexander V. Chernikov 	struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
2947c40e2d5SAlexander V. Chernikov 
2957c40e2d5SAlexander V. Chernikov 	NLA_FOREACH(nla, nla_head, attrs_len) {
2967c40e2d5SAlexander V. Chernikov 		RT_LOG(LOG_DEBUG3, "reading attr %d len %d", nla->nla_type, nla->nla_len);
2977c40e2d5SAlexander V. Chernikov 		if (nla->nla_len < sizeof(struct nlattr)) {
2987c40e2d5SAlexander V. Chernikov 			return (false);
2997c40e2d5SAlexander V. Chernikov 		}
300e55e4a6bSDmitry Chagin 		ret = nlmsg_translate_all_nla(hdr, nla, nw);
301e55e4a6bSDmitry Chagin 		if (ret == LINUX_NLA_UNHANDLED)
302e55e4a6bSDmitry Chagin 			ret = nlmsg_copy_nla(nla, nw);
303e55e4a6bSDmitry Chagin 		if (!ret)
3047c40e2d5SAlexander V. Chernikov 			return (false);
3057c40e2d5SAlexander V. Chernikov 	}
3067c40e2d5SAlexander V. Chernikov 	return (true);
3077c40e2d5SAlexander V. Chernikov }
308e55e4a6bSDmitry Chagin #undef LINUX_NLA_UNHANDLED
3097c40e2d5SAlexander V. Chernikov 
3107c40e2d5SAlexander V. Chernikov static unsigned int
rtnl_if_flags_to_linux(unsigned int if_flags)3117c40e2d5SAlexander V. Chernikov rtnl_if_flags_to_linux(unsigned int if_flags)
3127c40e2d5SAlexander V. Chernikov {
3137c40e2d5SAlexander V. Chernikov 	unsigned int result = 0;
3147c40e2d5SAlexander V. Chernikov 
3157c40e2d5SAlexander V. Chernikov 	for (int i = 0; i < 31; i++) {
3167c40e2d5SAlexander V. Chernikov 		unsigned int flag = 1 << i;
3177c40e2d5SAlexander V. Chernikov 		if (!(flag & if_flags))
3187c40e2d5SAlexander V. Chernikov 			continue;
3197c40e2d5SAlexander V. Chernikov 		switch (flag) {
3207c40e2d5SAlexander V. Chernikov 		case IFF_UP:
3217c40e2d5SAlexander V. Chernikov 		case IFF_BROADCAST:
3227c40e2d5SAlexander V. Chernikov 		case IFF_DEBUG:
3237c40e2d5SAlexander V. Chernikov 		case IFF_LOOPBACK:
3247c40e2d5SAlexander V. Chernikov 		case IFF_POINTOPOINT:
3257c40e2d5SAlexander V. Chernikov 		case IFF_DRV_RUNNING:
3267c40e2d5SAlexander V. Chernikov 		case IFF_NOARP:
3277c40e2d5SAlexander V. Chernikov 		case IFF_PROMISC:
3287c40e2d5SAlexander V. Chernikov 		case IFF_ALLMULTI:
3297c40e2d5SAlexander V. Chernikov 			result |= flag;
3307c40e2d5SAlexander V. Chernikov 			break;
331a6b55ee6SGleb Smirnoff 		case IFF_NEEDSEPOCH:
3327c40e2d5SAlexander V. Chernikov 		case IFF_DRV_OACTIVE:
3337c40e2d5SAlexander V. Chernikov 		case IFF_SIMPLEX:
3347c40e2d5SAlexander V. Chernikov 		case IFF_LINK0:
3357c40e2d5SAlexander V. Chernikov 		case IFF_LINK1:
3367c40e2d5SAlexander V. Chernikov 		case IFF_LINK2:
3377c40e2d5SAlexander V. Chernikov 		case IFF_CANTCONFIG:
3387c40e2d5SAlexander V. Chernikov 		case IFF_PPROMISC:
3397c40e2d5SAlexander V. Chernikov 		case IFF_MONITOR:
3407c40e2d5SAlexander V. Chernikov 		case IFF_STATICARP:
3417c40e2d5SAlexander V. Chernikov 		case IFF_STICKYARP:
3427c40e2d5SAlexander V. Chernikov 		case IFF_DYING:
3437c40e2d5SAlexander V. Chernikov 		case IFF_RENAMING:
3447c40e2d5SAlexander V. Chernikov 			/* No Linux analogue */
3457c40e2d5SAlexander V. Chernikov 			break;
3467c40e2d5SAlexander V. Chernikov 		case IFF_MULTICAST:
3477c40e2d5SAlexander V. Chernikov 			result |= 1 << 12;
3487c40e2d5SAlexander V. Chernikov 		}
3497c40e2d5SAlexander V. Chernikov 	}
3507c40e2d5SAlexander V. Chernikov 	return (result);
3517c40e2d5SAlexander V. Chernikov }
3527c40e2d5SAlexander V. Chernikov 
3537c40e2d5SAlexander V. Chernikov static bool
rtnl_newlink_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)3547c40e2d5SAlexander V. Chernikov rtnl_newlink_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
3557c40e2d5SAlexander V. Chernikov     struct nl_writer *nw)
3567c40e2d5SAlexander V. Chernikov {
3577c40e2d5SAlexander V. Chernikov 	if (!nlmsg_copy_header(hdr, nw))
3587c40e2d5SAlexander V. Chernikov 		return (false);
3597c40e2d5SAlexander V. Chernikov 
3607c40e2d5SAlexander V. Chernikov 	struct ifinfomsg *ifinfo;
3617c40e2d5SAlexander V. Chernikov 	ifinfo = nlmsg_copy_next_header(hdr, nw, struct ifinfomsg);
3627c40e2d5SAlexander V. Chernikov 
3637c40e2d5SAlexander V. Chernikov 	ifinfo->ifi_family = bsd_to_linux_domain(ifinfo->ifi_family);
3647c40e2d5SAlexander V. Chernikov 	/* Convert interface type */
3657c40e2d5SAlexander V. Chernikov 	switch (ifinfo->ifi_type) {
3667c40e2d5SAlexander V. Chernikov 	case IFT_ETHER:
367200fe6e3SDmitry Chagin 		ifinfo->ifi_type = LINUX_ARPHRD_ETHER;
3687c40e2d5SAlexander V. Chernikov 		break;
3697c40e2d5SAlexander V. Chernikov 	}
3707c40e2d5SAlexander V. Chernikov 	ifinfo->ifi_flags = rtnl_if_flags_to_linux(ifinfo->ifi_flags);
3717c40e2d5SAlexander V. Chernikov 
3727c40e2d5SAlexander V. Chernikov 	/* Copy attributes unchanged */
3737c40e2d5SAlexander V. Chernikov 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifinfomsg), nw))
3747c40e2d5SAlexander V. Chernikov 		return (false);
3757c40e2d5SAlexander V. Chernikov 
3767c40e2d5SAlexander V. Chernikov 	/* make ip(8) happy */
3777c40e2d5SAlexander V. Chernikov 	if (!nlattr_add_string(nw, IFLA_QDISC, "noqueue"))
3787c40e2d5SAlexander V. Chernikov 		return (false);
3797c40e2d5SAlexander V. Chernikov 
3807c40e2d5SAlexander V. Chernikov 	if (!nlattr_add_u32(nw, IFLA_TXQLEN, 1000))
3817c40e2d5SAlexander V. Chernikov 		return (false);
3827c40e2d5SAlexander V. Chernikov 
3837c40e2d5SAlexander V. Chernikov 	nlmsg_end(nw);
3847c40e2d5SAlexander V. Chernikov 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
3857c40e2d5SAlexander V. Chernikov 	return (true);
3867c40e2d5SAlexander V. Chernikov }
3877c40e2d5SAlexander V. Chernikov 
3887c40e2d5SAlexander V. Chernikov static bool
rtnl_newaddr_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)3897c40e2d5SAlexander V. Chernikov rtnl_newaddr_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
3907c40e2d5SAlexander V. Chernikov     struct nl_writer *nw)
3917c40e2d5SAlexander V. Chernikov {
3927c40e2d5SAlexander V. Chernikov 	if (!nlmsg_copy_header(hdr, nw))
3937c40e2d5SAlexander V. Chernikov 		return (false);
3947c40e2d5SAlexander V. Chernikov 
3957c40e2d5SAlexander V. Chernikov 	struct ifaddrmsg *ifamsg;
3967c40e2d5SAlexander V. Chernikov 	ifamsg = nlmsg_copy_next_header(hdr, nw, struct ifaddrmsg);
3977c40e2d5SAlexander V. Chernikov 
3987c40e2d5SAlexander V. Chernikov 	ifamsg->ifa_family = bsd_to_linux_domain(ifamsg->ifa_family);
3997c40e2d5SAlexander V. Chernikov 	/* XXX: fake ifa_flags? */
4007c40e2d5SAlexander V. Chernikov 
4017c40e2d5SAlexander V. Chernikov 	/* Copy attributes unchanged */
4027c40e2d5SAlexander V. Chernikov 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifaddrmsg), nw))
4037c40e2d5SAlexander V. Chernikov 		return (false);
4047c40e2d5SAlexander V. Chernikov 
4057c40e2d5SAlexander V. Chernikov 	nlmsg_end(nw);
4067c40e2d5SAlexander V. Chernikov 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
4077c40e2d5SAlexander V. Chernikov 	return (true);
4087c40e2d5SAlexander V. Chernikov }
4097c40e2d5SAlexander V. Chernikov 
4107c40e2d5SAlexander V. Chernikov static bool
rtnl_newneigh_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)4117c40e2d5SAlexander V. Chernikov rtnl_newneigh_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
4127c40e2d5SAlexander V. Chernikov     struct nl_writer *nw)
4137c40e2d5SAlexander V. Chernikov {
4147c40e2d5SAlexander V. Chernikov 	if (!nlmsg_copy_header(hdr, nw))
4157c40e2d5SAlexander V. Chernikov 		return (false);
4167c40e2d5SAlexander V. Chernikov 
4177c40e2d5SAlexander V. Chernikov 	struct ndmsg *ndm;
4187c40e2d5SAlexander V. Chernikov 	ndm = nlmsg_copy_next_header(hdr, nw, struct ndmsg);
4197c40e2d5SAlexander V. Chernikov 
4207c40e2d5SAlexander V. Chernikov 	ndm->ndm_family = bsd_to_linux_domain(ndm->ndm_family);
4217c40e2d5SAlexander V. Chernikov 
4227c40e2d5SAlexander V. Chernikov 	/* Copy attributes unchanged */
4237c40e2d5SAlexander V. Chernikov 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ndmsg), nw))
4247c40e2d5SAlexander V. Chernikov 		return (false);
4257c40e2d5SAlexander V. Chernikov 
4267c40e2d5SAlexander V. Chernikov 	nlmsg_end(nw);
4277c40e2d5SAlexander V. Chernikov 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
4287c40e2d5SAlexander V. Chernikov 	return (true);
4297c40e2d5SAlexander V. Chernikov }
4307c40e2d5SAlexander V. Chernikov 
4317c40e2d5SAlexander V. Chernikov static bool
rtnl_newroute_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)4327c40e2d5SAlexander V. Chernikov rtnl_newroute_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
4337c40e2d5SAlexander V. Chernikov     struct nl_writer *nw)
4347c40e2d5SAlexander V. Chernikov {
4357c40e2d5SAlexander V. Chernikov 	if (!nlmsg_copy_header(hdr, nw))
4367c40e2d5SAlexander V. Chernikov 		return (false);
4377c40e2d5SAlexander V. Chernikov 
4387c40e2d5SAlexander V. Chernikov 	struct rtmsg *rtm;
4397c40e2d5SAlexander V. Chernikov 	rtm = nlmsg_copy_next_header(hdr, nw, struct rtmsg);
4407c40e2d5SAlexander V. Chernikov 	rtm->rtm_family = bsd_to_linux_domain(rtm->rtm_family);
4417c40e2d5SAlexander V. Chernikov 
4427c40e2d5SAlexander V. Chernikov 	struct nlattr *nla;
4437c40e2d5SAlexander V. Chernikov 
4447c40e2d5SAlexander V. Chernikov 	int hdrlen = NETLINK_ALIGN(sizeof(struct rtmsg));
4457c40e2d5SAlexander V. Chernikov 	int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
4467c40e2d5SAlexander V. Chernikov 	struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
4477c40e2d5SAlexander V. Chernikov 
4487c40e2d5SAlexander V. Chernikov 	NLA_FOREACH(nla, nla_head, attrs_len) {
4497c40e2d5SAlexander V. Chernikov 		struct rtattr *rta = (struct rtattr *)nla;
4507c40e2d5SAlexander V. Chernikov 		//RT_LOG(LOG_DEBUG, "READING attr %d len %d", nla->nla_type, nla->nla_len);
4517c40e2d5SAlexander V. Chernikov 		if (rta->rta_len < sizeof(struct rtattr)) {
4527c40e2d5SAlexander V. Chernikov 			break;
4537c40e2d5SAlexander V. Chernikov 		}
4547c40e2d5SAlexander V. Chernikov 
4557c40e2d5SAlexander V. Chernikov 		switch (rta->rta_type) {
4567c40e2d5SAlexander V. Chernikov 		case NL_RTA_TABLE:
4577c40e2d5SAlexander V. Chernikov 			{
4587c40e2d5SAlexander V. Chernikov 				uint32_t fibnum;
4597c40e2d5SAlexander V. Chernikov 				fibnum = _rta_get_uint32(rta);
4607c40e2d5SAlexander V. Chernikov 				if (fibnum == 0)
4617c40e2d5SAlexander V. Chernikov 					fibnum = 254;
4627c40e2d5SAlexander V. Chernikov 				RT_LOG(LOG_DEBUG3, "XFIBNUM %u", fibnum);
4637c40e2d5SAlexander V. Chernikov 				if (!nlattr_add_u32(nw, NL_RTA_TABLE, fibnum))
4647c40e2d5SAlexander V. Chernikov 					return (false);
4657c40e2d5SAlexander V. Chernikov 			}
4667c40e2d5SAlexander V. Chernikov 			break;
4677c40e2d5SAlexander V. Chernikov 		default:
4687c40e2d5SAlexander V. Chernikov 			if (!nlmsg_copy_nla(nla, nw))
4697c40e2d5SAlexander V. Chernikov 				return (false);
4707c40e2d5SAlexander V. Chernikov 			break;
4717c40e2d5SAlexander V. Chernikov 		}
4727c40e2d5SAlexander V. Chernikov 	}
4737c40e2d5SAlexander V. Chernikov 
4747c40e2d5SAlexander V. Chernikov 	nlmsg_end(nw);
4757c40e2d5SAlexander V. Chernikov 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
4767c40e2d5SAlexander V. Chernikov 	return (true);
4777c40e2d5SAlexander V. Chernikov }
4787c40e2d5SAlexander V. Chernikov 
4797c40e2d5SAlexander V. Chernikov static bool
rtnl_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)4807c40e2d5SAlexander V. Chernikov rtnl_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
4817c40e2d5SAlexander V. Chernikov {
4827c40e2d5SAlexander V. Chernikov 	RT_LOG(LOG_DEBUG2, "Got message type %d", hdr->nlmsg_type);
4837c40e2d5SAlexander V. Chernikov 
4847c40e2d5SAlexander V. Chernikov 	switch (hdr->nlmsg_type) {
4857c40e2d5SAlexander V. Chernikov 	case NL_RTM_NEWLINK:
4867c40e2d5SAlexander V. Chernikov 	case NL_RTM_DELLINK:
4877c40e2d5SAlexander V. Chernikov 	case NL_RTM_GETLINK:
4887c40e2d5SAlexander V. Chernikov 		return (rtnl_newlink_to_linux(hdr, nlp, nw));
4897c40e2d5SAlexander V. Chernikov 	case NL_RTM_NEWADDR:
4907c40e2d5SAlexander V. Chernikov 	case NL_RTM_DELADDR:
4917c40e2d5SAlexander V. Chernikov 		return (rtnl_newaddr_to_linux(hdr, nlp, nw));
4927c40e2d5SAlexander V. Chernikov 	case NL_RTM_NEWROUTE:
4937c40e2d5SAlexander V. Chernikov 	case NL_RTM_DELROUTE:
4947c40e2d5SAlexander V. Chernikov 		return (rtnl_newroute_to_linux(hdr, nlp, nw));
4957c40e2d5SAlexander V. Chernikov 	case NL_RTM_NEWNEIGH:
4967c40e2d5SAlexander V. Chernikov 	case NL_RTM_DELNEIGH:
4977c40e2d5SAlexander V. Chernikov 	case NL_RTM_GETNEIGH:
4987c40e2d5SAlexander V. Chernikov 		return (rtnl_newneigh_to_linux(hdr, nlp, nw));
4997c40e2d5SAlexander V. Chernikov 	default:
5007c40e2d5SAlexander V. Chernikov 		RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
5017c40e2d5SAlexander V. Chernikov 		    hdr->nlmsg_type);
5027c40e2d5SAlexander V. Chernikov 		return (handle_default_out(hdr, nw));
5037c40e2d5SAlexander V. Chernikov 	}
5047c40e2d5SAlexander V. Chernikov }
5057c40e2d5SAlexander V. Chernikov 
5067c40e2d5SAlexander V. Chernikov static bool
nlmsg_error_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)5077c40e2d5SAlexander V. Chernikov nlmsg_error_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
5087c40e2d5SAlexander V. Chernikov {
5097c40e2d5SAlexander V. Chernikov 	if (!nlmsg_copy_header(hdr, nw))
5107c40e2d5SAlexander V. Chernikov 		return (false);
5117c40e2d5SAlexander V. Chernikov 
5127c40e2d5SAlexander V. Chernikov 	struct nlmsgerr *nlerr;
5137c40e2d5SAlexander V. Chernikov 	nlerr = nlmsg_copy_next_header(hdr, nw, struct nlmsgerr);
5147c40e2d5SAlexander V. Chernikov 	nlerr->error = bsd_to_linux_errno(nlerr->error);
5157c40e2d5SAlexander V. Chernikov 
5167c40e2d5SAlexander V. Chernikov 	int copied_len = sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr);
5177c40e2d5SAlexander V. Chernikov 	if (hdr->nlmsg_len == copied_len) {
5187c40e2d5SAlexander V. Chernikov 		nlmsg_end(nw);
5197c40e2d5SAlexander V. Chernikov 		return (true);
5207c40e2d5SAlexander V. Chernikov 	}
5217c40e2d5SAlexander V. Chernikov 
5227c40e2d5SAlexander V. Chernikov 	/*
5237c40e2d5SAlexander V. Chernikov 	 * CAP_ACK was not set. Original request needs to be translated.
5247c40e2d5SAlexander V. Chernikov 	 * XXX: implement translation of the original message
5257c40e2d5SAlexander V. Chernikov 	 */
5267c40e2d5SAlexander V. Chernikov 	RT_LOG(LOG_DEBUG, "[WARN] Passing ack message type %d untranslated",
5277c40e2d5SAlexander V. Chernikov 	    nlerr->msg.nlmsg_type);
5287c40e2d5SAlexander V. Chernikov 	char *dst_payload, *src_payload;
5297c40e2d5SAlexander V. Chernikov 	int copy_len = hdr->nlmsg_len - copied_len;
5307c40e2d5SAlexander V. Chernikov 	dst_payload = nlmsg_reserve_data(nw, NLMSG_ALIGN(copy_len), char);
5317c40e2d5SAlexander V. Chernikov 
5327c40e2d5SAlexander V. Chernikov 	src_payload = (char *)hdr + copied_len;
5337c40e2d5SAlexander V. Chernikov 
5347c40e2d5SAlexander V. Chernikov 	memcpy(dst_payload, src_payload, copy_len);
5357c40e2d5SAlexander V. Chernikov 	nlmsg_end(nw);
5367c40e2d5SAlexander V. Chernikov 
5377c40e2d5SAlexander V. Chernikov 	return (true);
5387c40e2d5SAlexander V. Chernikov }
5397c40e2d5SAlexander V. Chernikov 
5407c40e2d5SAlexander V. Chernikov static bool
nlmsg_to_linux(struct nlmsghdr * hdr,struct nlpcb * nlp,struct nl_writer * nw)54117083b94SGleb Smirnoff nlmsg_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
5427c40e2d5SAlexander V. Chernikov {
5437c40e2d5SAlexander V. Chernikov 	if (hdr->nlmsg_type < NLMSG_MIN_TYPE) {
5447c40e2d5SAlexander V. Chernikov 		switch (hdr->nlmsg_type) {
5457c40e2d5SAlexander V. Chernikov 		case NLMSG_ERROR:
5467c40e2d5SAlexander V. Chernikov 			return (nlmsg_error_to_linux(hdr, nlp, nw));
5477c40e2d5SAlexander V. Chernikov 		case NLMSG_NOOP:
5487c40e2d5SAlexander V. Chernikov 		case NLMSG_DONE:
5497c40e2d5SAlexander V. Chernikov 		case NLMSG_OVERRUN:
5507c40e2d5SAlexander V. Chernikov 			return (handle_default_out(hdr, nw));
5517c40e2d5SAlexander V. Chernikov 		default:
5527c40e2d5SAlexander V. Chernikov 			RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
5537c40e2d5SAlexander V. Chernikov 			    hdr->nlmsg_type);
5547c40e2d5SAlexander V. Chernikov 			return (handle_default_out(hdr, nw));
5557c40e2d5SAlexander V. Chernikov 		}
5567c40e2d5SAlexander V. Chernikov 	}
5577c40e2d5SAlexander V. Chernikov 
55817083b94SGleb Smirnoff 	switch (nlp->nl_proto) {
5597c40e2d5SAlexander V. Chernikov 	case NETLINK_ROUTE:
5607c40e2d5SAlexander V. Chernikov 		return (rtnl_to_linux(hdr, nlp, nw));
5617c40e2d5SAlexander V. Chernikov 	default:
5627c40e2d5SAlexander V. Chernikov 		return (handle_default_out(hdr, nw));
5637c40e2d5SAlexander V. Chernikov 	}
5647c40e2d5SAlexander V. Chernikov }
5657c40e2d5SAlexander V. Chernikov 
56617083b94SGleb Smirnoff static bool
nlmsgs_to_linux(struct nl_writer * nw,struct nlpcb * nlp)56717083b94SGleb Smirnoff nlmsgs_to_linux(struct nl_writer *nw, struct nlpcb *nlp)
5687c40e2d5SAlexander V. Chernikov {
56917083b94SGleb Smirnoff 	struct nl_buf *nb, *orig;
57041ce9c8bSGleb Smirnoff 	u_int offset, msglen, orig_messages;
5717c40e2d5SAlexander V. Chernikov 
57217083b94SGleb Smirnoff 	RT_LOG(LOG_DEBUG3, "%p: in %u bytes %u messages", __func__,
57317083b94SGleb Smirnoff 	    nw->buf->datalen, nw->num_messages);
57417083b94SGleb Smirnoff 
57517083b94SGleb Smirnoff 	orig = nw->buf;
57617083b94SGleb Smirnoff 	nb = nl_buf_alloc(orig->datalen + SCRATCH_BUFFER_SIZE, M_NOWAIT);
57717083b94SGleb Smirnoff 	if (__predict_false(nb == NULL))
57817083b94SGleb Smirnoff 		return (false);
57917083b94SGleb Smirnoff 	nw->buf = nb;
58017083b94SGleb Smirnoff 	orig_messages = nw->num_messages;
58117083b94SGleb Smirnoff 	nw->num_messages = 0;
5827c40e2d5SAlexander V. Chernikov 
5837c40e2d5SAlexander V. Chernikov 	/* Assume correct headers. Buffer IS mutable */
58417083b94SGleb Smirnoff 	for (offset = 0;
58517083b94SGleb Smirnoff 	    offset + sizeof(struct nlmsghdr) <= orig->datalen;
58617083b94SGleb Smirnoff 	    offset += msglen) {
58717083b94SGleb Smirnoff 		struct nlmsghdr *hdr = (struct nlmsghdr *)&orig->data[offset];
5887c40e2d5SAlexander V. Chernikov 
58917083b94SGleb Smirnoff 		msglen = NLMSG_ALIGN(hdr->nlmsg_len);
59017083b94SGleb Smirnoff 		if (!nlmsg_to_linux(hdr, nlp, nw)) {
5917c40e2d5SAlexander V. Chernikov 			RT_LOG(LOG_DEBUG, "failed to process msg type %d",
5927c40e2d5SAlexander V. Chernikov 			    hdr->nlmsg_type);
59317083b94SGleb Smirnoff 			nl_buf_free(nb);
59441ce9c8bSGleb Smirnoff 			nw->buf = orig;
59541ce9c8bSGleb Smirnoff 			nw->num_messages = orig_messages;
59617083b94SGleb Smirnoff 			return (false);
5977c40e2d5SAlexander V. Chernikov 		}
5987c40e2d5SAlexander V. Chernikov 	}
5997c40e2d5SAlexander V. Chernikov 
60017083b94SGleb Smirnoff 	MPASS(nw->num_messages == orig_messages);
60117083b94SGleb Smirnoff 	MPASS(nw->buf == nb);
60217083b94SGleb Smirnoff 	nl_buf_free(orig);
60317083b94SGleb Smirnoff 	RT_LOG(LOG_DEBUG3, "%p: out %u bytes", __func__, offset);
6047c40e2d5SAlexander V. Chernikov 
60517083b94SGleb Smirnoff 	return (true);
6067c40e2d5SAlexander V. Chernikov }
6077c40e2d5SAlexander V. Chernikov 
6087c40e2d5SAlexander V. Chernikov static struct linux_netlink_provider linux_netlink_v1 = {
6097c40e2d5SAlexander V. Chernikov 	.msgs_to_linux = nlmsgs_to_linux,
6107c40e2d5SAlexander V. Chernikov 	.msg_from_linux = nlmsg_from_linux,
6117c40e2d5SAlexander V. Chernikov };
6127c40e2d5SAlexander V. Chernikov 
6137c40e2d5SAlexander V. Chernikov void
linux_netlink_register(void)61443d0c2ddSEd Maste linux_netlink_register(void)
6157c40e2d5SAlexander V. Chernikov {
6167c40e2d5SAlexander V. Chernikov 	linux_netlink_p = &linux_netlink_v1;
6177c40e2d5SAlexander V. Chernikov }
6187c40e2d5SAlexander V. Chernikov 
6197c40e2d5SAlexander V. Chernikov void
linux_netlink_deregister(void)62043d0c2ddSEd Maste linux_netlink_deregister(void)
6217c40e2d5SAlexander V. Chernikov {
6227c40e2d5SAlexander V. Chernikov 	linux_netlink_p = NULL;
6237c40e2d5SAlexander V. Chernikov }
624