2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2022 Alexander V. Chernikov
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
32 #include "opt_inet6.h"
33 #include "opt_netlink.h"
35 #include <sys/types.h>
38 #include <sys/malloc.h>
39 #include <sys/rmlock.h>
40 #include <sys/socket.h>
41 #include <sys/vnode.h>
44 #include <net/if_dl.h>
45 #include <net/route.h>
46 #include <net/route/nhop.h>
47 #include <net/route/route_ctl.h>
48 #include <netlink/netlink.h>
49 #include <netlink/netlink_ctl.h>
50 #include <netlink/netlink_linux.h>
51 #include <netlink/netlink_route.h>
53 #include <compat/linux/linux.h>
54 #include <compat/linux/linux_common.h>
55 #include <compat/linux/linux_util.h>
57 #define DEBUG_MOD_NAME nl_linux
58 #define DEBUG_MAX_LEVEL LOG_DEBUG3
59 #include <netlink/netlink_debug.h>
60 _DECLARE_DEBUG(LOG_INFO);
63 valid_rta_size(const struct rtattr *rta, int sz)
65 return (NL_RTA_DATA_LEN(rta) == sz);
69 valid_rta_u32(const struct rtattr *rta)
71 return (valid_rta_size(rta, sizeof(uint32_t)));
75 _rta_get_uint32(const struct rtattr *rta)
77 return (*((const uint32_t *)NL_RTA_DATA_CONST(rta)));
80 static struct nlmsghdr *
81 rtnl_neigh_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
83 struct ndmsg *ndm = (struct ndmsg *)(hdr + 1);
85 if (hdr->nlmsg_len >= sizeof(struct nlmsghdr) + sizeof(struct ndmsg))
86 ndm->ndm_family = linux_to_bsd_domain(ndm->ndm_family);
91 static struct nlmsghdr *
92 rtnl_ifaddr_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
94 struct ifaddrmsg *ifam = (struct ifaddrmsg *)(hdr + 1);
96 if (hdr->nlmsg_len >= sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg))
97 ifam->ifa_family = linux_to_bsd_domain(ifam->ifa_family);
102 static struct nlmsghdr *
103 rtnl_route_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
105 /* Tweak address families and default fib only */
106 struct rtmsg *rtm = (struct rtmsg *)(hdr + 1);
107 struct nlattr *nla, *nla_head;
110 rtm->rtm_family = linux_to_bsd_domain(rtm->rtm_family);
112 if (rtm->rtm_table == 254)
115 attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr);
116 attrs_len -= NETLINK_ALIGN(sizeof(struct rtmsg));
117 nla_head = (struct nlattr *)((char *)rtm + NETLINK_ALIGN(sizeof(struct rtmsg)));
119 NLA_FOREACH(nla, nla_head, attrs_len) {
120 RT_LOG(LOG_DEBUG3, "GOT type %d len %d total %d",
121 nla->nla_type, nla->nla_len, attrs_len);
122 struct rtattr *rta = (struct rtattr *)nla;
123 if (rta->rta_len < sizeof(struct rtattr)) {
126 switch (rta->rta_type) {
128 if (!valid_rta_u32(rta))
131 uint32_t fibnum = _rta_get_uint32(rta);
132 RT_LOG(LOG_DEBUG3, "GET RTABLE: %u", fibnum);
134 *((uint32_t *)NL_RTA_DATA(rta)) = 0;
144 static struct nlmsghdr *
145 rtnl_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
147 switch (hdr->nlmsg_type) {
148 case NL_RTM_GETROUTE:
149 case NL_RTM_NEWROUTE:
150 case NL_RTM_DELROUTE:
151 return (rtnl_route_from_linux(hdr, npt));
152 case NL_RTM_GETNEIGH:
153 return (rtnl_neigh_from_linux(hdr, npt));
155 return (rtnl_ifaddr_from_linux(hdr, npt));
156 /* Silence warning for the messages where no translation is required */
162 RT_LOG(LOG_DEBUG, "Passing message type %d untranslated",
169 static struct nlmsghdr *
170 nlmsg_from_linux(int netlink_family, struct nlmsghdr *hdr,
171 struct nl_pstate *npt)
173 switch (netlink_family) {
175 return (rtnl_from_linux(hdr, npt));
182 /************************************************************
184 ************************************************************/
187 handle_default_out(struct nlmsghdr *hdr, struct nl_writer *nw)
190 out_hdr = nlmsg_reserve_data(nw, NLMSG_ALIGN(hdr->nlmsg_len), char);
192 if (out_hdr != NULL) {
193 memcpy(out_hdr, hdr, hdr->nlmsg_len);
200 nlmsg_copy_header(struct nlmsghdr *hdr, struct nl_writer *nw)
202 return (nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, hdr->nlmsg_type,
203 hdr->nlmsg_flags, 0));
207 _nlmsg_copy_next_header(struct nlmsghdr *hdr, struct nl_writer *nw, int sz)
209 void *next_hdr = nlmsg_reserve_data(nw, sz, void);
210 memcpy(next_hdr, hdr + 1, NLMSG_ALIGN(sz));
214 #define nlmsg_copy_next_header(_hdr, _ns, _t) \
215 ((_t *)(_nlmsg_copy_next_header(_hdr, _ns, sizeof(_t))))
218 nlmsg_copy_nla(const struct nlattr *nla_orig, struct nl_writer *nw)
220 struct nlattr *nla = nlmsg_reserve_data(nw, nla_orig->nla_len, struct nlattr);
222 memcpy(nla, nla_orig, nla_orig->nla_len);
229 * Translate a FreeBSD interface name to a Linux interface name.
232 nlmsg_translate_ifname_nla(struct nlattr *nla, struct nl_writer *nw)
234 char ifname[LINUX_IFNAMSIZ];
236 if (ifname_bsd_to_linux_name((char *)(nla + 1), ifname,
237 sizeof(ifname)) <= 0)
239 return (nlattr_add_string(nw, IFLA_IFNAME, ifname));
242 #define LINUX_NLA_UNHANDLED -1
244 * Translate a FreeBSD attribute to a Linux attribute.
245 * Returns LINUX_NLA_UNHANDLED when the attribute is not processed
246 * and the caller must take care of it, otherwise the result is returned.
249 nlmsg_translate_all_nla(struct nlmsghdr *hdr, struct nlattr *nla,
250 struct nl_writer *nw)
253 switch (hdr->nlmsg_type) {
257 switch (nla->nla_type) {
259 return (nlmsg_translate_ifname_nla(nla, nw));
266 return (LINUX_NLA_UNHANDLED);
270 nlmsg_copy_all_nla(struct nlmsghdr *hdr, int raw_hdrlen, struct nl_writer *nw)
275 int hdrlen = NETLINK_ALIGN(raw_hdrlen);
276 int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
277 struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
279 NLA_FOREACH(nla, nla_head, attrs_len) {
280 RT_LOG(LOG_DEBUG3, "reading attr %d len %d", nla->nla_type, nla->nla_len);
281 if (nla->nla_len < sizeof(struct nlattr)) {
284 ret = nlmsg_translate_all_nla(hdr, nla, nw);
285 if (ret == LINUX_NLA_UNHANDLED)
286 ret = nlmsg_copy_nla(nla, nw);
292 #undef LINUX_NLA_UNHANDLED
295 rtnl_if_flags_to_linux(unsigned int if_flags)
297 unsigned int result = 0;
299 for (int i = 0; i < 31; i++) {
300 unsigned int flag = 1 << i;
301 if (!(flag & if_flags))
308 case IFF_POINTOPOINT:
309 case IFF_DRV_RUNNING:
316 case IFF_DRV_OACTIVE:
328 /* No Linux analogue */
338 rtnl_newlink_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
339 struct nl_writer *nw)
341 if (!nlmsg_copy_header(hdr, nw))
344 struct ifinfomsg *ifinfo;
345 ifinfo = nlmsg_copy_next_header(hdr, nw, struct ifinfomsg);
347 ifinfo->ifi_family = bsd_to_linux_domain(ifinfo->ifi_family);
348 /* Convert interface type */
349 switch (ifinfo->ifi_type) {
351 ifinfo->ifi_type = LINUX_ARPHRD_ETHER;
354 ifinfo->ifi_flags = rtnl_if_flags_to_linux(ifinfo->ifi_flags);
356 /* Copy attributes unchanged */
357 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifinfomsg), nw))
360 /* make ip(8) happy */
361 if (!nlattr_add_string(nw, IFLA_QDISC, "noqueue"))
364 if (!nlattr_add_u32(nw, IFLA_TXQLEN, 1000))
368 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
373 rtnl_newaddr_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
374 struct nl_writer *nw)
376 if (!nlmsg_copy_header(hdr, nw))
379 struct ifaddrmsg *ifamsg;
380 ifamsg = nlmsg_copy_next_header(hdr, nw, struct ifaddrmsg);
382 ifamsg->ifa_family = bsd_to_linux_domain(ifamsg->ifa_family);
383 /* XXX: fake ifa_flags? */
385 /* Copy attributes unchanged */
386 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifaddrmsg), nw))
390 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
395 rtnl_newneigh_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
396 struct nl_writer *nw)
398 if (!nlmsg_copy_header(hdr, nw))
402 ndm = nlmsg_copy_next_header(hdr, nw, struct ndmsg);
404 ndm->ndm_family = bsd_to_linux_domain(ndm->ndm_family);
406 /* Copy attributes unchanged */
407 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ndmsg), nw))
411 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
416 rtnl_newroute_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
417 struct nl_writer *nw)
419 if (!nlmsg_copy_header(hdr, nw))
423 rtm = nlmsg_copy_next_header(hdr, nw, struct rtmsg);
424 rtm->rtm_family = bsd_to_linux_domain(rtm->rtm_family);
428 int hdrlen = NETLINK_ALIGN(sizeof(struct rtmsg));
429 int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
430 struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
432 NLA_FOREACH(nla, nla_head, attrs_len) {
433 struct rtattr *rta = (struct rtattr *)nla;
434 //RT_LOG(LOG_DEBUG, "READING attr %d len %d", nla->nla_type, nla->nla_len);
435 if (rta->rta_len < sizeof(struct rtattr)) {
439 switch (rta->rta_type) {
443 fibnum = _rta_get_uint32(rta);
446 RT_LOG(LOG_DEBUG3, "XFIBNUM %u", fibnum);
447 if (!nlattr_add_u32(nw, NL_RTA_TABLE, fibnum))
452 if (!nlmsg_copy_nla(nla, nw))
459 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
464 rtnl_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
466 RT_LOG(LOG_DEBUG2, "Got message type %d", hdr->nlmsg_type);
468 switch (hdr->nlmsg_type) {
472 return (rtnl_newlink_to_linux(hdr, nlp, nw));
475 return (rtnl_newaddr_to_linux(hdr, nlp, nw));
476 case NL_RTM_NEWROUTE:
477 case NL_RTM_DELROUTE:
478 return (rtnl_newroute_to_linux(hdr, nlp, nw));
479 case NL_RTM_NEWNEIGH:
480 case NL_RTM_DELNEIGH:
481 case NL_RTM_GETNEIGH:
482 return (rtnl_newneigh_to_linux(hdr, nlp, nw));
484 RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
486 return (handle_default_out(hdr, nw));
491 nlmsg_error_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
493 if (!nlmsg_copy_header(hdr, nw))
496 struct nlmsgerr *nlerr;
497 nlerr = nlmsg_copy_next_header(hdr, nw, struct nlmsgerr);
498 nlerr->error = bsd_to_linux_errno(nlerr->error);
500 int copied_len = sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr);
501 if (hdr->nlmsg_len == copied_len) {
507 * CAP_ACK was not set. Original request needs to be translated.
508 * XXX: implement translation of the original message
510 RT_LOG(LOG_DEBUG, "[WARN] Passing ack message type %d untranslated",
511 nlerr->msg.nlmsg_type);
512 char *dst_payload, *src_payload;
513 int copy_len = hdr->nlmsg_len - copied_len;
514 dst_payload = nlmsg_reserve_data(nw, NLMSG_ALIGN(copy_len), char);
516 src_payload = (char *)hdr + copied_len;
518 memcpy(dst_payload, src_payload, copy_len);
525 nlmsg_to_linux(int netlink_family, struct nlmsghdr *hdr, struct nlpcb *nlp,
526 struct nl_writer *nw)
528 if (hdr->nlmsg_type < NLMSG_MIN_TYPE) {
529 switch (hdr->nlmsg_type) {
531 return (nlmsg_error_to_linux(hdr, nlp, nw));
535 return (handle_default_out(hdr, nw));
537 RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
539 return (handle_default_out(hdr, nw));
543 switch (netlink_family) {
545 return (rtnl_to_linux(hdr, nlp, nw));
547 return (handle_default_out(hdr, nw));
552 nlmsgs_to_linux(int netlink_family, char *buf, int data_length, struct nlpcb *nlp)
554 RT_LOG(LOG_DEBUG3, "LINUX: get %p size %d", buf, data_length);
555 struct nl_writer nw = {};
557 struct mbuf *m = NULL;
558 if (!nlmsg_get_chain_writer(&nw, data_length, &m)) {
559 RT_LOG(LOG_DEBUG, "unable to setup chain writer for size %d",
564 /* Assume correct headers. Buffer IS mutable */
566 for (int offset = 0; offset + sizeof(struct nlmsghdr) <= data_length;) {
567 struct nlmsghdr *hdr = (struct nlmsghdr *)&buf[offset];
568 int msglen = NLMSG_ALIGN(hdr->nlmsg_len);
571 if (!nlmsg_to_linux(netlink_family, hdr, nlp, &nw)) {
572 RT_LOG(LOG_DEBUG, "failed to process msg type %d",
580 RT_LOG(LOG_DEBUG3, "Processed %d messages, chain size %d", count,
581 m ? m_length(m, NULL) : 0);
587 mbufs_to_linux(int netlink_family, struct mbuf *m, struct nlpcb *nlp)
589 /* XXX: easiest solution, not optimized for performance */
590 int data_length = m_length(m, NULL);
591 char *buf = malloc(data_length, M_LINUX, M_NOWAIT);
593 RT_LOG(LOG_DEBUG, "unable to allocate %d bytes, dropping message",
598 m_copydata(m, 0, data_length, buf);
601 m = nlmsgs_to_linux(netlink_family, buf, data_length, nlp);
607 static struct linux_netlink_provider linux_netlink_v1 = {
608 .mbufs_to_linux = mbufs_to_linux,
609 .msgs_to_linux = nlmsgs_to_linux,
610 .msg_from_linux = nlmsg_from_linux,
614 linux_netlink_register(void)
616 linux_netlink_p = &linux_netlink_v1;
620 linux_netlink_deregister(void)
622 linux_netlink_p = NULL;