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:
329 /* No Linux analogue */
339 rtnl_newlink_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
340 struct nl_writer *nw)
342 if (!nlmsg_copy_header(hdr, nw))
345 struct ifinfomsg *ifinfo;
346 ifinfo = nlmsg_copy_next_header(hdr, nw, struct ifinfomsg);
348 ifinfo->ifi_family = bsd_to_linux_domain(ifinfo->ifi_family);
349 /* Convert interface type */
350 switch (ifinfo->ifi_type) {
352 ifinfo->ifi_type = LINUX_ARPHRD_ETHER;
355 ifinfo->ifi_flags = rtnl_if_flags_to_linux(ifinfo->ifi_flags);
357 /* Copy attributes unchanged */
358 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifinfomsg), nw))
361 /* make ip(8) happy */
362 if (!nlattr_add_string(nw, IFLA_QDISC, "noqueue"))
365 if (!nlattr_add_u32(nw, IFLA_TXQLEN, 1000))
369 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
374 rtnl_newaddr_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
375 struct nl_writer *nw)
377 if (!nlmsg_copy_header(hdr, nw))
380 struct ifaddrmsg *ifamsg;
381 ifamsg = nlmsg_copy_next_header(hdr, nw, struct ifaddrmsg);
383 ifamsg->ifa_family = bsd_to_linux_domain(ifamsg->ifa_family);
384 /* XXX: fake ifa_flags? */
386 /* Copy attributes unchanged */
387 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifaddrmsg), nw))
391 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
396 rtnl_newneigh_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
397 struct nl_writer *nw)
399 if (!nlmsg_copy_header(hdr, nw))
403 ndm = nlmsg_copy_next_header(hdr, nw, struct ndmsg);
405 ndm->ndm_family = bsd_to_linux_domain(ndm->ndm_family);
407 /* Copy attributes unchanged */
408 if (!nlmsg_copy_all_nla(hdr, sizeof(struct ndmsg), nw))
412 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
417 rtnl_newroute_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
418 struct nl_writer *nw)
420 if (!nlmsg_copy_header(hdr, nw))
424 rtm = nlmsg_copy_next_header(hdr, nw, struct rtmsg);
425 rtm->rtm_family = bsd_to_linux_domain(rtm->rtm_family);
429 int hdrlen = NETLINK_ALIGN(sizeof(struct rtmsg));
430 int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
431 struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
433 NLA_FOREACH(nla, nla_head, attrs_len) {
434 struct rtattr *rta = (struct rtattr *)nla;
435 //RT_LOG(LOG_DEBUG, "READING attr %d len %d", nla->nla_type, nla->nla_len);
436 if (rta->rta_len < sizeof(struct rtattr)) {
440 switch (rta->rta_type) {
444 fibnum = _rta_get_uint32(rta);
447 RT_LOG(LOG_DEBUG3, "XFIBNUM %u", fibnum);
448 if (!nlattr_add_u32(nw, NL_RTA_TABLE, fibnum))
453 if (!nlmsg_copy_nla(nla, nw))
460 RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
465 rtnl_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
467 RT_LOG(LOG_DEBUG2, "Got message type %d", hdr->nlmsg_type);
469 switch (hdr->nlmsg_type) {
473 return (rtnl_newlink_to_linux(hdr, nlp, nw));
476 return (rtnl_newaddr_to_linux(hdr, nlp, nw));
477 case NL_RTM_NEWROUTE:
478 case NL_RTM_DELROUTE:
479 return (rtnl_newroute_to_linux(hdr, nlp, nw));
480 case NL_RTM_NEWNEIGH:
481 case NL_RTM_DELNEIGH:
482 case NL_RTM_GETNEIGH:
483 return (rtnl_newneigh_to_linux(hdr, nlp, nw));
485 RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
487 return (handle_default_out(hdr, nw));
492 nlmsg_error_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
494 if (!nlmsg_copy_header(hdr, nw))
497 struct nlmsgerr *nlerr;
498 nlerr = nlmsg_copy_next_header(hdr, nw, struct nlmsgerr);
499 nlerr->error = bsd_to_linux_errno(nlerr->error);
501 int copied_len = sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr);
502 if (hdr->nlmsg_len == copied_len) {
508 * CAP_ACK was not set. Original request needs to be translated.
509 * XXX: implement translation of the original message
511 RT_LOG(LOG_DEBUG, "[WARN] Passing ack message type %d untranslated",
512 nlerr->msg.nlmsg_type);
513 char *dst_payload, *src_payload;
514 int copy_len = hdr->nlmsg_len - copied_len;
515 dst_payload = nlmsg_reserve_data(nw, NLMSG_ALIGN(copy_len), char);
517 src_payload = (char *)hdr + copied_len;
519 memcpy(dst_payload, src_payload, copy_len);
526 nlmsg_to_linux(int netlink_family, struct nlmsghdr *hdr, struct nlpcb *nlp,
527 struct nl_writer *nw)
529 if (hdr->nlmsg_type < NLMSG_MIN_TYPE) {
530 switch (hdr->nlmsg_type) {
532 return (nlmsg_error_to_linux(hdr, nlp, nw));
536 return (handle_default_out(hdr, nw));
538 RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
540 return (handle_default_out(hdr, nw));
544 switch (netlink_family) {
546 return (rtnl_to_linux(hdr, nlp, nw));
548 return (handle_default_out(hdr, nw));
553 nlmsgs_to_linux(int netlink_family, char *buf, int data_length, struct nlpcb *nlp)
555 RT_LOG(LOG_DEBUG3, "LINUX: get %p size %d", buf, data_length);
556 struct nl_writer nw = {};
558 struct mbuf *m = NULL;
559 if (!nlmsg_get_chain_writer(&nw, data_length, &m)) {
560 RT_LOG(LOG_DEBUG, "unable to setup chain writer for size %d",
565 /* Assume correct headers. Buffer IS mutable */
567 for (int offset = 0; offset + sizeof(struct nlmsghdr) <= data_length;) {
568 struct nlmsghdr *hdr = (struct nlmsghdr *)&buf[offset];
569 int msglen = NLMSG_ALIGN(hdr->nlmsg_len);
572 if (!nlmsg_to_linux(netlink_family, hdr, nlp, &nw)) {
573 RT_LOG(LOG_DEBUG, "failed to process msg type %d",
581 RT_LOG(LOG_DEBUG3, "Processed %d messages, chain size %d", count,
582 m ? m_length(m, NULL) : 0);
588 mbufs_to_linux(int netlink_family, struct mbuf *m, struct nlpcb *nlp)
590 /* XXX: easiest solution, not optimized for performance */
591 int data_length = m_length(m, NULL);
592 char *buf = malloc(data_length, M_LINUX, M_NOWAIT);
594 RT_LOG(LOG_DEBUG, "unable to allocate %d bytes, dropping message",
599 m_copydata(m, 0, data_length, buf);
602 m = nlmsgs_to_linux(netlink_family, buf, data_length, nlp);
608 static struct linux_netlink_provider linux_netlink_v1 = {
609 .mbufs_to_linux = mbufs_to_linux,
610 .msgs_to_linux = nlmsgs_to_linux,
611 .msg_from_linux = nlmsg_from_linux,
615 linux_netlink_register(void)
617 linux_netlink_p = &linux_netlink_v1;
621 linux_netlink_deregister(void)
623 linux_netlink_p = NULL;