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
29 #include "opt_inet6.h"
30 #include "opt_netlink.h"
32 #include <sys/types.h>
35 #include <sys/malloc.h>
36 #include <sys/rmlock.h>
37 #include <sys/socket.h>
38 #include <sys/vnode.h>
41 #include <net/if_dl.h>
42 #include <net/route.h>
43 #include <net/route/nhop.h>
44 #include <net/route/route_ctl.h>
45 #include <netlink/netlink.h>
46 #include <netlink/netlink_ctl.h>
47 #include <netlink/netlink_linux.h>
48 #include <netlink/netlink_route.h>
50 #include <compat/linux/linux.h>
51 #include <compat/linux/linux_common.h>
52 #include <compat/linux/linux_util.h>
54 #define DEBUG_MOD_NAME nl_linux
55 #define DEBUG_MAX_LEVEL LOG_DEBUG3
56 #include <netlink/netlink_debug.h>
57 _DECLARE_DEBUG(LOG_INFO);
60 valid_rta_size(const struct rtattr *rta, int sz)
62 return (NL_RTA_DATA_LEN(rta) == sz);
66 valid_rta_u32(const struct rtattr *rta)
68 return (valid_rta_size(rta, sizeof(uint32_t)));
72 _rta_get_uint32(const struct rtattr *rta)
74 return (*((const uint32_t *)NL_RTA_DATA_CONST(rta)));
77 static struct nlmsghdr *
78 rtnl_neigh_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
80 struct ndmsg *ndm = (struct ndmsg *)(hdr + 1);
82 if (hdr->nlmsg_len >= sizeof(struct nlmsghdr) + sizeof(struct ndmsg))
83 ndm->ndm_family = linux_to_bsd_domain(ndm->ndm_family);
88 static struct nlmsghdr *
89 rtnl_ifaddr_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
91 struct ifaddrmsg *ifam = (struct ifaddrmsg *)(hdr + 1);
93 if (hdr->nlmsg_len >= sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg))
94 ifam->ifa_family = linux_to_bsd_domain(ifam->ifa_family);
99 static struct nlmsghdr *
100 rtnl_route_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
102 /* Tweak address families and default fib only */
103 struct rtmsg *rtm = (struct rtmsg *)(hdr + 1);
104 struct nlattr *nla, *nla_head;
107 rtm->rtm_family = linux_to_bsd_domain(rtm->rtm_family);
109 if (rtm->rtm_table == 254)
112 attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr);
113 attrs_len -= NETLINK_ALIGN(sizeof(struct rtmsg));
114 nla_head = (struct nlattr *)((char *)rtm + NETLINK_ALIGN(sizeof(struct rtmsg)));
116 NLA_FOREACH(nla, nla_head, attrs_len) {
117 RT_LOG(LOG_DEBUG3, "GOT type %d len %d total %d",
118 nla->nla_type, nla->nla_len, attrs_len);
119 struct rtattr *rta = (struct rtattr *)nla;
120 if (rta->rta_len < sizeof(struct rtattr)) {
123 switch (rta->rta_type) {
125 if (!valid_rta_u32(rta))
128 uint32_t fibnum = _rta_get_uint32(rta);
129 RT_LOG(LOG_DEBUG3, "GET RTABLE: %u", fibnum);
131 *((uint32_t *)NL_RTA_DATA(rta)) = 0;
141 static struct nlmsghdr *
142 rtnl_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
144 switch (hdr->nlmsg_type) {
145 case NL_RTM_GETROUTE:
146 case NL_RTM_NEWROUTE:
147 case NL_RTM_DELROUTE:
148 return (rtnl_route_from_linux(hdr, npt));
149 case NL_RTM_GETNEIGH:
150 return (rtnl_neigh_from_linux(hdr, npt));
152 return (rtnl_ifaddr_from_linux(hdr, npt));
153 /* Silence warning for the messages where no translation is required */
159 RT_LOG(LOG_DEBUG, "Passing message type %d untranslated",
166 static struct nlmsghdr *
167 nlmsg_from_linux(int netlink_family, struct nlmsghdr *hdr,
168 struct nl_pstate *npt)
170 switch (netlink_family) {
172 return (rtnl_from_linux(hdr, npt));
179 /************************************************************
181 ************************************************************/
184 handle_default_out(struct nlmsghdr *hdr, struct nl_writer *nw)
187 out_hdr = nlmsg_reserve_data(nw, NLMSG_ALIGN(hdr->nlmsg_len), char);
189 if (out_hdr != NULL) {
190 memcpy(out_hdr, hdr, hdr->nlmsg_len);
197 nlmsg_copy_header(struct nlmsghdr *hdr, struct nl_writer *nw)
199 return (nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, hdr->nlmsg_type,
200 hdr->nlmsg_flags, 0));
204 _nlmsg_copy_next_header(struct nlmsghdr *hdr, struct nl_writer *nw, int sz)
206 void *next_hdr = nlmsg_reserve_data(nw, sz, void);
207 memcpy(next_hdr, hdr + 1, NLMSG_ALIGN(sz));
211 #define nlmsg_copy_next_header(_hdr, _ns, _t) \
212 ((_t *)(_nlmsg_copy_next_header(_hdr, _ns, sizeof(_t))))
215 nlmsg_copy_nla(const struct nlattr *nla_orig, struct nl_writer *nw)
217 struct nlattr *nla = nlmsg_reserve_data(nw, nla_orig->nla_len, struct nlattr);
219 memcpy(nla, nla_orig, nla_orig->nla_len);
226 * Translate a FreeBSD interface name to a Linux interface name.
229 nlmsg_translate_ifname_nla(struct nlattr *nla, struct nl_writer *nw)
231 char ifname[LINUX_IFNAMSIZ];
233 if (ifname_bsd_to_linux_name((char *)(nla + 1), ifname,
234 sizeof(ifname)) <= 0)
236 return (nlattr_add_string(nw, IFLA_IFNAME, ifname));
239 #define LINUX_NLA_UNHANDLED -1
241 * Translate a FreeBSD attribute to a Linux attribute.
242 * Returns LINUX_NLA_UNHANDLED when the attribute is not processed
243 * and the caller must take care of it, otherwise the result is returned.
246 nlmsg_translate_all_nla(struct nlmsghdr *hdr, struct nlattr *nla,
247 struct nl_writer *nw)
250 switch (hdr->nlmsg_type) {
254 switch (nla->nla_type) {
256 return (nlmsg_translate_ifname_nla(nla, nw));
263 return (LINUX_NLA_UNHANDLED);
267 nlmsg_copy_all_nla(struct nlmsghdr *hdr, int raw_hdrlen, struct nl_writer *nw)
272 int hdrlen = NETLINK_ALIGN(raw_hdrlen);
273 int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
274 struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
276 NLA_FOREACH(nla, nla_head, attrs_len) {
277 RT_LOG(LOG_DEBUG3, "reading attr %d len %d", nla->nla_type, nla->nla_len);
278 if (nla->nla_len < sizeof(struct nlattr)) {
281 ret = nlmsg_translate_all_nla(hdr, nla, nw);
282 if (ret == LINUX_NLA_UNHANDLED)
283 ret = nlmsg_copy_nla(nla, nw);
289 #undef LINUX_NLA_UNHANDLED
292 rtnl_if_flags_to_linux(unsigned int if_flags)
294 unsigned int result = 0;
296 for (int i = 0; i < 31; i++) {
297 unsigned int flag = 1 << i;
298 if (!(flag & if_flags))
305 case IFF_POINTOPOINT:
306 case IFF_DRV_RUNNING:
313 case IFF_DRV_OACTIVE:
325 /* No Linux analogue */
335 rtnl_newlink_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
336 struct nl_writer *nw)
338 if (!nlmsg_copy_header(hdr, nw))
341 struct ifinfomsg *ifinfo;
342 ifinfo = nlmsg_copy_next_header(hdr, nw, struct ifinfomsg);
344 ifinfo->ifi_family = bsd_to_linux_domain(ifinfo->ifi_family);
345 /* Convert interface type */
346 switch (ifinfo->ifi_type) {
348 ifinfo->ifi_type = LINUX_ARPHRD_ETHER;
351 ifinfo->ifi_flags = rtnl_if_flags_to_linux(ifinfo->ifi_flags);
353 /* Copy attributes unchanged */
354 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifinfomsg), nw))
357 /* make ip(8) happy */
358 if (!nlattr_add_string(nw, IFLA_QDISC, "noqueue"))
361 if (!nlattr_add_u32(nw, IFLA_TXQLEN, 1000))
365 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
370 rtnl_newaddr_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
371 struct nl_writer *nw)
373 if (!nlmsg_copy_header(hdr, nw))
376 struct ifaddrmsg *ifamsg;
377 ifamsg = nlmsg_copy_next_header(hdr, nw, struct ifaddrmsg);
379 ifamsg->ifa_family = bsd_to_linux_domain(ifamsg->ifa_family);
380 /* XXX: fake ifa_flags? */
382 /* Copy attributes unchanged */
383 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifaddrmsg), nw))
387 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
392 rtnl_newneigh_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
393 struct nl_writer *nw)
395 if (!nlmsg_copy_header(hdr, nw))
399 ndm = nlmsg_copy_next_header(hdr, nw, struct ndmsg);
401 ndm->ndm_family = bsd_to_linux_domain(ndm->ndm_family);
403 /* Copy attributes unchanged */
404 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ndmsg), nw))
408 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
413 rtnl_newroute_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
414 struct nl_writer *nw)
416 if (!nlmsg_copy_header(hdr, nw))
420 rtm = nlmsg_copy_next_header(hdr, nw, struct rtmsg);
421 rtm->rtm_family = bsd_to_linux_domain(rtm->rtm_family);
425 int hdrlen = NETLINK_ALIGN(sizeof(struct rtmsg));
426 int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
427 struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
429 NLA_FOREACH(nla, nla_head, attrs_len) {
430 struct rtattr *rta = (struct rtattr *)nla;
431 //RT_LOG(LOG_DEBUG, "READING attr %d len %d", nla->nla_type, nla->nla_len);
432 if (rta->rta_len < sizeof(struct rtattr)) {
436 switch (rta->rta_type) {
440 fibnum = _rta_get_uint32(rta);
443 RT_LOG(LOG_DEBUG3, "XFIBNUM %u", fibnum);
444 if (!nlattr_add_u32(nw, NL_RTA_TABLE, fibnum))
449 if (!nlmsg_copy_nla(nla, nw))
456 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
461 rtnl_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
463 RT_LOG(LOG_DEBUG2, "Got message type %d", hdr->nlmsg_type);
465 switch (hdr->nlmsg_type) {
469 return (rtnl_newlink_to_linux(hdr, nlp, nw));
472 return (rtnl_newaddr_to_linux(hdr, nlp, nw));
473 case NL_RTM_NEWROUTE:
474 case NL_RTM_DELROUTE:
475 return (rtnl_newroute_to_linux(hdr, nlp, nw));
476 case NL_RTM_NEWNEIGH:
477 case NL_RTM_DELNEIGH:
478 case NL_RTM_GETNEIGH:
479 return (rtnl_newneigh_to_linux(hdr, nlp, nw));
481 RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
483 return (handle_default_out(hdr, nw));
488 nlmsg_error_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
490 if (!nlmsg_copy_header(hdr, nw))
493 struct nlmsgerr *nlerr;
494 nlerr = nlmsg_copy_next_header(hdr, nw, struct nlmsgerr);
495 nlerr->error = bsd_to_linux_errno(nlerr->error);
497 int copied_len = sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr);
498 if (hdr->nlmsg_len == copied_len) {
504 * CAP_ACK was not set. Original request needs to be translated.
505 * XXX: implement translation of the original message
507 RT_LOG(LOG_DEBUG, "[WARN] Passing ack message type %d untranslated",
508 nlerr->msg.nlmsg_type);
509 char *dst_payload, *src_payload;
510 int copy_len = hdr->nlmsg_len - copied_len;
511 dst_payload = nlmsg_reserve_data(nw, NLMSG_ALIGN(copy_len), char);
513 src_payload = (char *)hdr + copied_len;
515 memcpy(dst_payload, src_payload, copy_len);
522 nlmsg_to_linux(int netlink_family, struct nlmsghdr *hdr, struct nlpcb *nlp,
523 struct nl_writer *nw)
525 if (hdr->nlmsg_type < NLMSG_MIN_TYPE) {
526 switch (hdr->nlmsg_type) {
528 return (nlmsg_error_to_linux(hdr, nlp, nw));
532 return (handle_default_out(hdr, nw));
534 RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
536 return (handle_default_out(hdr, nw));
540 switch (netlink_family) {
542 return (rtnl_to_linux(hdr, nlp, nw));
544 return (handle_default_out(hdr, nw));
549 nlmsgs_to_linux(int netlink_family, char *buf, int data_length, struct nlpcb *nlp)
551 RT_LOG(LOG_DEBUG3, "LINUX: get %p size %d", buf, data_length);
552 struct nl_writer nw = {};
554 struct mbuf *m = NULL;
555 if (!nlmsg_get_chain_writer(&nw, data_length, &m)) {
556 RT_LOG(LOG_DEBUG, "unable to setup chain writer for size %d",
561 /* Assume correct headers. Buffer IS mutable */
563 for (int offset = 0; offset + sizeof(struct nlmsghdr) <= data_length;) {
564 struct nlmsghdr *hdr = (struct nlmsghdr *)&buf[offset];
565 int msglen = NLMSG_ALIGN(hdr->nlmsg_len);
568 if (!nlmsg_to_linux(netlink_family, hdr, nlp, &nw)) {
569 RT_LOG(LOG_DEBUG, "failed to process msg type %d",
577 RT_LOG(LOG_DEBUG3, "Processed %d messages, chain size %d", count,
578 m ? m_length(m, NULL) : 0);
584 mbufs_to_linux(int netlink_family, struct mbuf *m, struct nlpcb *nlp)
586 /* XXX: easiest solution, not optimized for performance */
587 int data_length = m_length(m, NULL);
588 char *buf = malloc(data_length, M_LINUX, M_NOWAIT);
590 RT_LOG(LOG_DEBUG, "unable to allocate %d bytes, dropping message",
595 m_copydata(m, 0, data_length, buf);
598 m = nlmsgs_to_linux(netlink_family, buf, data_length, nlp);
604 static struct linux_netlink_provider linux_netlink_v1 = {
605 .mbufs_to_linux = mbufs_to_linux,
606 .msgs_to_linux = nlmsgs_to_linux,
607 .msg_from_linux = nlmsg_from_linux,
611 linux_netlink_register(void)
613 linux_netlink_p = &linux_netlink_v1;
617 linux_netlink_deregister(void)
619 linux_netlink_p = NULL;