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