xref: /freebsd/sys/compat/linux/linux_netlink.c (revision c7a063741720ef81d4caa4613242579d12f1d605)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2022 Alexander V. Chernikov
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 #include "opt_inet.h"
31 #include "opt_inet6.h"
32 #include <sys/types.h>
33 #include <sys/malloc.h>
34 #include <sys/rmlock.h>
35 #include <sys/socket.h>
36 #include <sys/ck.h>
37 
38 #include <net/if.h>
39 #include <net/if_dl.h>
40 #include <net/route.h>
41 #include <net/route/nhop.h>
42 #include <net/route/route_ctl.h>
43 #include <netlink/netlink.h>
44 #include <netlink/netlink_ctl.h>
45 #include <netlink/netlink_linux.h>
46 #include <netlink/netlink_route.h>
47 
48 #include <compat/linux/linux.h>
49 #include <compat/linux/linux_common.h>
50 #include <compat/linux/linux_util.h>
51 
52 #define	DEBUG_MOD_NAME	nl_linux
53 #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
54 #include <netlink/netlink_debug.h>
55 _DECLARE_DEBUG(LOG_DEBUG);
56 
57 static bool
58 valid_rta_size(const struct rtattr *rta, int sz)
59 {
60 	return (NL_RTA_DATA_LEN(rta) == sz);
61 }
62 
63 static bool
64 valid_rta_u32(const struct rtattr *rta)
65 {
66 	return (valid_rta_size(rta, sizeof(uint32_t)));
67 }
68 
69 static uint32_t
70 _rta_get_uint32(const struct rtattr *rta)
71 {
72 	return (*((const uint32_t *)NL_RTA_DATA_CONST(rta)));
73 }
74 
75 static struct nlmsghdr *
76 rtnl_neigh_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
77 {
78 	struct ndmsg *ndm = (struct ndmsg *)(hdr + 1);
79 
80 	if (hdr->nlmsg_len >= sizeof(struct nlmsghdr) + sizeof(struct ndmsg))
81 		ndm->ndm_family = linux_to_bsd_domain(ndm->ndm_family);
82 
83 	return (hdr);
84 }
85 
86 static struct nlmsghdr *
87 rtnl_ifaddr_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
88 {
89 	struct ifaddrmsg *ifam = (struct ifaddrmsg *)(hdr + 1);
90 
91 	if (hdr->nlmsg_len >= sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg))
92 		ifam->ifa_family = linux_to_bsd_domain(ifam->ifa_family);
93 
94 	return (hdr);
95 }
96 
97 static struct nlmsghdr *
98 rtnl_route_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
99 {
100 	/* Tweak address families and default fib only */
101 	struct rtmsg *rtm = (struct rtmsg *)(hdr + 1);
102 	struct nlattr *nla, *nla_head;
103 	int attrs_len;
104 
105 	rtm->rtm_family = linux_to_bsd_domain(rtm->rtm_family);
106 
107 	if (rtm->rtm_table == 254)
108 		rtm->rtm_table = 0;
109 
110 	attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr);
111 	attrs_len -= NETLINK_ALIGN(sizeof(struct rtmsg));
112 	nla_head = (struct nlattr *)((char *)rtm + NETLINK_ALIGN(sizeof(struct rtmsg)));
113 
114 	NLA_FOREACH(nla, nla_head, attrs_len) {
115 		RT_LOG(LOG_DEBUG3, "GOT type %d len %d total %d",
116 		    nla->nla_type, nla->nla_len, attrs_len);
117 		struct rtattr *rta = (struct rtattr *)nla;
118 		if (rta->rta_len < sizeof(struct rtattr)) {
119 			break;
120 		}
121 		switch (rta->rta_type) {
122 		case NL_RTA_TABLE:
123 			if (!valid_rta_u32(rta))
124 				goto done;
125 			rtm->rtm_table = 0;
126 			uint32_t fibnum = _rta_get_uint32(rta);
127 			RT_LOG(LOG_DEBUG3, "GET RTABLE: %u", fibnum);
128 			if (fibnum == 254) {
129 				*((uint32_t *)NL_RTA_DATA(rta)) = 0;
130 			}
131 			break;
132 		}
133 	}
134 
135 done:
136 	return (hdr);
137 }
138 
139 static struct nlmsghdr *
140 rtnl_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
141 {
142 	switch (hdr->nlmsg_type) {
143 	case NL_RTM_GETROUTE:
144 	case NL_RTM_NEWROUTE:
145 	case NL_RTM_DELROUTE:
146 		return (rtnl_route_from_linux(hdr, npt));
147 	case NL_RTM_GETNEIGH:
148 		return (rtnl_neigh_from_linux(hdr, npt));
149 	case NL_RTM_GETADDR:
150 		return (rtnl_ifaddr_from_linux(hdr, npt));
151 	/* Silence warning for the messages where no translation is required */
152 	case NL_RTM_NEWLINK:
153 	case NL_RTM_DELLINK:
154 	case NL_RTM_GETLINK:
155 		break;
156 	default:
157 		RT_LOG(LOG_DEBUG, "Passing message type %d untranslated",
158 		    hdr->nlmsg_type);
159 	}
160 
161 	return (hdr);
162 }
163 
164 static struct nlmsghdr *
165 nlmsg_from_linux(int netlink_family, struct nlmsghdr *hdr,
166     struct nl_pstate *npt)
167 {
168 	switch (netlink_family) {
169 	case NETLINK_ROUTE:
170 		return (rtnl_from_linux(hdr, npt));
171 	}
172 
173 	return (hdr);
174 }
175 
176 
177 /************************************************************
178  * Kernel -> Linux
179  ************************************************************/
180 
181 static bool
182 handle_default_out(struct nlmsghdr *hdr, struct nl_writer *nw)
183 {
184 	char *out_hdr;
185 	out_hdr = nlmsg_reserve_data(nw, NLMSG_ALIGN(hdr->nlmsg_len), char);
186 
187 	if (out_hdr != NULL) {
188 		memcpy(out_hdr, hdr, hdr->nlmsg_len);
189 		return (true);
190 	}
191 	return (false);
192 }
193 
194 static bool
195 nlmsg_copy_header(struct nlmsghdr *hdr, struct nl_writer *nw)
196 {
197 	return (nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, hdr->nlmsg_type,
198 	    hdr->nlmsg_flags, 0));
199 }
200 
201 static void *
202 _nlmsg_copy_next_header(struct nlmsghdr *hdr, struct nl_writer *nw, int sz)
203 {
204 	void *next_hdr = nlmsg_reserve_data(nw, sz, void);
205 	memcpy(next_hdr, hdr + 1, NLMSG_ALIGN(sz));
206 
207 	return (next_hdr);
208 }
209 #define	nlmsg_copy_next_header(_hdr, _ns, _t)	\
210 	((_t *)(_nlmsg_copy_next_header(_hdr, _ns, sizeof(_t))))
211 
212 static bool
213 nlmsg_copy_nla(const struct nlattr *nla_orig, struct nl_writer *nw)
214 {
215 	struct nlattr *nla = nlmsg_reserve_data(nw, nla_orig->nla_len, struct nlattr);
216 	if (nla != NULL) {
217 		memcpy(nla, nla_orig, nla_orig->nla_len);
218 		return (true);
219 	}
220 	return (false);
221 }
222 
223 static bool
224 nlmsg_copy_all_nla(struct nlmsghdr *hdr, int raw_hdrlen, struct nl_writer *nw)
225 {
226 	struct nlattr *nla;
227 
228 	int hdrlen = NETLINK_ALIGN(raw_hdrlen);
229 	int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
230 	struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
231 
232 	NLA_FOREACH(nla, nla_head, attrs_len) {
233 		RT_LOG(LOG_DEBUG3, "reading attr %d len %d", nla->nla_type, nla->nla_len);
234 		if (nla->nla_len < sizeof(struct nlattr)) {
235 			return (false);
236 		}
237 		if (!nlmsg_copy_nla(nla, nw))
238 			return (false);
239 	}
240 	return (true);
241 }
242 
243 static unsigned int
244 rtnl_if_flags_to_linux(unsigned int if_flags)
245 {
246 	unsigned int result = 0;
247 
248 	for (int i = 0; i < 31; i++) {
249 		unsigned int flag = 1 << i;
250 		if (!(flag & if_flags))
251 			continue;
252 		switch (flag) {
253 		case IFF_UP:
254 		case IFF_BROADCAST:
255 		case IFF_DEBUG:
256 		case IFF_LOOPBACK:
257 		case IFF_POINTOPOINT:
258 		case IFF_DRV_RUNNING:
259 		case IFF_NOARP:
260 		case IFF_PROMISC:
261 		case IFF_ALLMULTI:
262 			result |= flag;
263 			break;
264 		case IFF_KNOWSEPOCH:
265 		case IFF_DRV_OACTIVE:
266 		case IFF_SIMPLEX:
267 		case IFF_LINK0:
268 		case IFF_LINK1:
269 		case IFF_LINK2:
270 		case IFF_CANTCONFIG:
271 		case IFF_PPROMISC:
272 		case IFF_MONITOR:
273 		case IFF_STATICARP:
274 		case IFF_STICKYARP:
275 		case IFF_DYING:
276 		case IFF_RENAMING:
277 		case IFF_NOGROUP:
278 			/* No Linux analogue */
279 			break;
280 		case IFF_MULTICAST:
281 			result |= 1 << 12;
282 		}
283 	}
284 	return (result);
285 }
286 
287 static bool
288 rtnl_newlink_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
289     struct nl_writer *nw)
290 {
291 	if (!nlmsg_copy_header(hdr, nw))
292 		return (false);
293 
294 	struct ifinfomsg *ifinfo;
295 	ifinfo = nlmsg_copy_next_header(hdr, nw, struct ifinfomsg);
296 
297 	ifinfo->ifi_family = bsd_to_linux_domain(ifinfo->ifi_family);
298 	/* Convert interface type */
299 	switch (ifinfo->ifi_type) {
300 	case IFT_ETHER:
301 		ifinfo->ifi_type = 1; // ARPHRD_ETHER
302 		break;
303 	}
304 	ifinfo->ifi_flags = rtnl_if_flags_to_linux(ifinfo->ifi_flags);
305 
306 	/* Copy attributes unchanged */
307 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifinfomsg), nw))
308 		return (false);
309 
310 	/* make ip(8) happy */
311 	if (!nlattr_add_string(nw, IFLA_QDISC, "noqueue"))
312 		return (false);
313 
314 	if (!nlattr_add_u32(nw, IFLA_TXQLEN, 1000))
315 		return (false);
316 
317 	nlmsg_end(nw);
318 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
319 	return (true);
320 }
321 
322 static bool
323 rtnl_newaddr_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
324     struct nl_writer *nw)
325 {
326 	if (!nlmsg_copy_header(hdr, nw))
327 		return (false);
328 
329 	struct ifaddrmsg *ifamsg;
330 	ifamsg = nlmsg_copy_next_header(hdr, nw, struct ifaddrmsg);
331 
332 	ifamsg->ifa_family = bsd_to_linux_domain(ifamsg->ifa_family);
333 	/* XXX: fake ifa_flags? */
334 
335 	/* Copy attributes unchanged */
336 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifaddrmsg), nw))
337 		return (false);
338 
339 	nlmsg_end(nw);
340 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
341 	return (true);
342 }
343 
344 static bool
345 rtnl_newneigh_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
346     struct nl_writer *nw)
347 {
348 	if (!nlmsg_copy_header(hdr, nw))
349 		return (false);
350 
351 	struct ndmsg *ndm;
352 	ndm = nlmsg_copy_next_header(hdr, nw, struct ndmsg);
353 
354 	ndm->ndm_family = bsd_to_linux_domain(ndm->ndm_family);
355 
356 	/* Copy attributes unchanged */
357 	if (!nlmsg_copy_all_nla(hdr, sizeof(struct ndmsg), nw))
358 		return (false);
359 
360 	nlmsg_end(nw);
361 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
362 	return (true);
363 }
364 
365 static bool
366 rtnl_newroute_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
367     struct nl_writer *nw)
368 {
369 	if (!nlmsg_copy_header(hdr, nw))
370 		return (false);
371 
372 	struct rtmsg *rtm;
373 	rtm = nlmsg_copy_next_header(hdr, nw, struct rtmsg);
374 	rtm->rtm_family = bsd_to_linux_domain(rtm->rtm_family);
375 
376 	struct nlattr *nla;
377 
378 	int hdrlen = NETLINK_ALIGN(sizeof(struct rtmsg));
379 	int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
380 	struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
381 
382 	NLA_FOREACH(nla, nla_head, attrs_len) {
383 		struct rtattr *rta = (struct rtattr *)nla;
384 		//RT_LOG(LOG_DEBUG, "READING attr %d len %d", nla->nla_type, nla->nla_len);
385 		if (rta->rta_len < sizeof(struct rtattr)) {
386 			break;
387 		}
388 
389 		switch (rta->rta_type) {
390 		case NL_RTA_TABLE:
391 			{
392 				uint32_t fibnum;
393 				fibnum = _rta_get_uint32(rta);
394 				if (fibnum == 0)
395 					fibnum = 254;
396 				RT_LOG(LOG_DEBUG3, "XFIBNUM %u", fibnum);
397 				if (!nlattr_add_u32(nw, NL_RTA_TABLE, fibnum))
398 					return (false);
399 			}
400 			break;
401 		default:
402 			if (!nlmsg_copy_nla(nla, nw))
403 				return (false);
404 			break;
405 		}
406 	}
407 
408 	nlmsg_end(nw);
409 	RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
410 	return (true);
411 }
412 
413 static bool
414 rtnl_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
415 {
416 	RT_LOG(LOG_DEBUG2, "Got message type %d", hdr->nlmsg_type);
417 
418 	switch (hdr->nlmsg_type) {
419 	case NL_RTM_NEWLINK:
420 	case NL_RTM_DELLINK:
421 	case NL_RTM_GETLINK:
422 		return (rtnl_newlink_to_linux(hdr, nlp, nw));
423 	case NL_RTM_NEWADDR:
424 	case NL_RTM_DELADDR:
425 		return (rtnl_newaddr_to_linux(hdr, nlp, nw));
426 	case NL_RTM_NEWROUTE:
427 	case NL_RTM_DELROUTE:
428 		return (rtnl_newroute_to_linux(hdr, nlp, nw));
429 	case NL_RTM_NEWNEIGH:
430 	case NL_RTM_DELNEIGH:
431 	case NL_RTM_GETNEIGH:
432 		return (rtnl_newneigh_to_linux(hdr, nlp, nw));
433 	default:
434 		RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
435 		    hdr->nlmsg_type);
436 		return (handle_default_out(hdr, nw));
437 	}
438 }
439 
440 static bool
441 nlmsg_error_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
442 {
443 	if (!nlmsg_copy_header(hdr, nw))
444 		return (false);
445 
446 	struct nlmsgerr *nlerr;
447 	nlerr = nlmsg_copy_next_header(hdr, nw, struct nlmsgerr);
448 	nlerr->error = bsd_to_linux_errno(nlerr->error);
449 
450 	int copied_len = sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr);
451 	if (hdr->nlmsg_len == copied_len) {
452 		nlmsg_end(nw);
453 		return (true);
454 	}
455 
456 	/*
457 	 * CAP_ACK was not set. Original request needs to be translated.
458 	 * XXX: implement translation of the original message
459 	 */
460 	RT_LOG(LOG_DEBUG, "[WARN] Passing ack message type %d untranslated",
461 	    nlerr->msg.nlmsg_type);
462 	char *dst_payload, *src_payload;
463 	int copy_len = hdr->nlmsg_len - copied_len;
464 	dst_payload = nlmsg_reserve_data(nw, NLMSG_ALIGN(copy_len), char);
465 
466 	src_payload = (char *)hdr + copied_len;
467 
468 	memcpy(dst_payload, src_payload, copy_len);
469 	nlmsg_end(nw);
470 
471 	return (true);
472 }
473 
474 static bool
475 nlmsg_to_linux(int netlink_family, struct nlmsghdr *hdr, struct nlpcb *nlp,
476     struct nl_writer *nw)
477 {
478 	if (hdr->nlmsg_type < NLMSG_MIN_TYPE) {
479 		switch (hdr->nlmsg_type) {
480 		case NLMSG_ERROR:
481 			return (nlmsg_error_to_linux(hdr, nlp, nw));
482 		case NLMSG_NOOP:
483 		case NLMSG_DONE:
484 		case NLMSG_OVERRUN:
485 			return (handle_default_out(hdr, nw));
486 		default:
487 			RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
488 			    hdr->nlmsg_type);
489 			return (handle_default_out(hdr, nw));
490 		}
491 	}
492 
493 	switch (netlink_family) {
494 	case NETLINK_ROUTE:
495 		return (rtnl_to_linux(hdr, nlp, nw));
496 	default:
497 		return (handle_default_out(hdr, nw));
498 	}
499 }
500 
501 static struct mbuf *
502 nlmsgs_to_linux(int netlink_family, char *buf, int data_length, struct nlpcb *nlp)
503 {
504 	RT_LOG(LOG_DEBUG3, "LINUX: get %p size %d", buf, data_length);
505 	struct nl_writer nw = {};
506 
507 	struct mbuf *m = NULL;
508 	if (!nlmsg_get_chain_writer(&nw, data_length, &m)) {
509 		RT_LOG(LOG_DEBUG, "unable to setup chain writer for size %d",
510 		    data_length);
511 		return (NULL);
512 	}
513 
514 	/* Assume correct headers. Buffer IS mutable */
515 	int count = 0;
516 	for (int offset = 0; offset + sizeof(struct nlmsghdr) <= data_length;) {
517 		struct nlmsghdr *hdr = (struct nlmsghdr *)&buf[offset];
518 		int msglen = NLMSG_ALIGN(hdr->nlmsg_len);
519 		count++;
520 
521 		if (!nlmsg_to_linux(netlink_family, hdr, nlp, &nw)) {
522 			RT_LOG(LOG_DEBUG, "failed to process msg type %d",
523 			    hdr->nlmsg_type);
524 			m_freem(m);
525 			return (NULL);
526 		}
527 		offset += msglen;
528 	}
529 	nlmsg_flush(&nw);
530 	RT_LOG(LOG_DEBUG3, "Processed %d messages, chain size %d", count,
531 	    m ? m_length(m, NULL) : 0);
532 
533 	return (m);
534 }
535 
536 static struct mbuf *
537 mbufs_to_linux(int netlink_family, struct mbuf *m, struct nlpcb *nlp)
538 {
539 	/* XXX: easiest solution, not optimized for performance */
540 	int data_length = m_length(m, NULL);
541 	char *buf = malloc(data_length, M_LINUX, M_NOWAIT);
542 	if (buf == NULL) {
543 		RT_LOG(LOG_DEBUG, "unable to allocate %d bytes, dropping message",
544 		    data_length);
545 		m_freem(m);
546 		return (NULL);
547 	}
548 	m_copydata(m, 0, data_length, buf);
549 	m_freem(m);
550 
551 	m = nlmsgs_to_linux(netlink_family, buf, data_length, nlp);
552 	free(buf, M_LINUX);
553 
554 	return (m);
555 }
556 
557 static struct linux_netlink_provider linux_netlink_v1 = {
558 	.mbufs_to_linux = mbufs_to_linux,
559 	.msgs_to_linux = nlmsgs_to_linux,
560 	.msg_from_linux = nlmsg_from_linux,
561 };
562 
563 void
564 linux_netlink_register(void)
565 {
566 	linux_netlink_p = &linux_netlink_v1;
567 }
568 
569 void
570 linux_netlink_deregister(void)
571 {
572 	linux_netlink_p = NULL;
573 }
574