xref: /freebsd/sys/netlink/route/neigh.c (revision 0aa2700123e22c2b0a977375e087dc2759b8e980)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
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/socket.h>
35 #include <sys/syslog.h>
36 
37 #include <net/if.h>
38 #include <net/if_llatbl.h>
39 #include <netlink/netlink.h>
40 #include <netlink/netlink_ctl.h>
41 #include <netlink/netlink_route.h>
42 #include <netlink/route/route_var.h>
43 
44 #include <netinet6/in6_var.h>		/* nd6.h requires this */
45 #include <netinet6/nd6.h>		/* nd6 state machine */
46 #include <netinet6/scope6_var.h>	/* scope deembedding */
47 
48 #define	DEBUG_MOD_NAME	nl_neigh
49 #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
50 #include <netlink/netlink_debug.h>
51 _DECLARE_DEBUG(LOG_DEBUG);
52 
53 static int lle_families[] = { AF_INET, AF_INET6 };
54 
55 static eventhandler_tag lle_event_p;
56 
57 struct netlink_walkargs {
58 	struct nl_writer *nw;
59 	struct nlmsghdr hdr;
60 	struct nlpcb *so;
61 	struct ifnet *ifp;
62 	int family;
63 	int error;
64 	int count;
65 	int dumped;
66 };
67 
68 static int
69 lle_state_to_nl_state(int family, struct llentry *lle)
70 {
71 	int state = lle->ln_state;
72 
73 	switch (family) {
74 	case AF_INET:
75 		if (lle->la_flags & (LLE_STATIC | LLE_IFADDR))
76 			state = 1;
77 		switch (state) {
78 		case 0: /* ARP_LLINFO_INCOMPLETE */
79 			return (NUD_INCOMPLETE);
80 		case 1: /* ARP_LLINFO_REACHABLE  */
81 			return (NUD_REACHABLE);
82 		case 2: /* ARP_LLINFO_VERIFY */
83 			return (NUD_PROBE);
84 		}
85 		break;
86 	case AF_INET6:
87 		switch (state) {
88 		case ND6_LLINFO_INCOMPLETE:
89 			return (NUD_INCOMPLETE);
90 		case ND6_LLINFO_REACHABLE:
91 			return (NUD_REACHABLE);
92 		case ND6_LLINFO_STALE:
93 			return (NUD_STALE);
94 		case ND6_LLINFO_DELAY:
95 			return (NUD_DELAY);
96 		case ND6_LLINFO_PROBE:
97 			return (NUD_PROBE);
98 		}
99 		break;
100 	}
101 
102 	return (NUD_NONE);
103 }
104 
105 static uint32_t
106 lle_flags_to_nl_flags(const struct llentry *lle)
107 {
108 	uint32_t nl_flags = 0;
109 
110 	if (lle->la_flags & LLE_IFADDR)
111 		nl_flags |= NTF_SELF;
112 	if (lle->la_flags & LLE_PUB)
113 		nl_flags |= NTF_PROXY;
114 	if (lle->la_flags & LLE_STATIC)
115 		nl_flags |= NTF_STICKY;
116 	if (lle->ln_router != 0)
117 		nl_flags |= NTF_ROUTER;
118 
119 	return (nl_flags);
120 }
121 
122 static int
123 dump_lle_locked(struct llentry *lle, void *arg)
124 {
125 	struct netlink_walkargs *wa = (struct netlink_walkargs *)arg;
126 	struct nlmsghdr *hdr = &wa->hdr;
127 	struct nl_writer *nw = wa->nw;
128 	struct ndmsg *ndm;
129 	union {
130 		struct in_addr	in;
131 		struct in6_addr	in6;
132 	} addr;
133 
134 	IF_DEBUG_LEVEL(LOG_DEBUG2) {
135 		char llebuf[NHOP_PRINT_BUFSIZE];
136 		llentry_print_buf_lltable(lle, llebuf, sizeof(llebuf));
137 		NL_LOG(LOG_DEBUG2, "dumping %s", llebuf);
138 	}
139 
140 	if (!nlmsg_reply(nw, hdr, sizeof(struct ndmsg)))
141 		goto enomem;
142 
143 	ndm = nlmsg_reserve_object(nw, struct ndmsg);
144 	ndm->ndm_family = wa->family;
145 	ndm->ndm_ifindex = wa->ifp->if_index;
146 	ndm->ndm_state = lle_state_to_nl_state(wa->family, lle);
147 	ndm->ndm_flags = lle_flags_to_nl_flags(lle);
148 
149 	switch (wa->family) {
150 #ifdef INET
151 	case AF_INET:
152 		addr.in = lle->r_l3addr.addr4;
153 		nlattr_add(nw, NDA_DST, 4, &addr);
154 		break;
155 #endif
156 #ifdef INET6
157 	case AF_INET6:
158 		addr.in6 = lle->r_l3addr.addr6;
159 		in6_clearscope(&addr.in6);
160 		nlattr_add(nw, NDA_DST, 16, &addr);
161 		break;
162 #endif
163 	}
164 
165 	if (lle->r_flags & RLLE_VALID) {
166 		/* Has L2 */
167 		int addrlen = wa->ifp->if_addrlen;
168 		nlattr_add(nw, NDA_LLADDR, addrlen, lle->ll_addr);
169 	}
170 
171 	nlattr_add_u32(nw, NDA_PROBES, lle->la_asked);
172 
173 	struct nda_cacheinfo *cache;
174 	cache = nlmsg_reserve_attr(nw, NDA_CACHEINFO, struct nda_cacheinfo);
175 	if (cache == NULL)
176 		goto enomem;
177 	/* TODO: provide confirmed/updated */
178 	cache->ndm_refcnt = lle->lle_refcnt;
179 
180         if (nlmsg_end(nw))
181 		return (0);
182 enomem:
183         NL_LOG(LOG_DEBUG, "unable to dump lle state (ENOMEM)");
184         nlmsg_abort(nw);
185         return (ENOMEM);
186 }
187 
188 static int
189 dump_lle(struct lltable *llt, struct llentry *lle, void *arg)
190 {
191 	int error;
192 
193 	LLE_RLOCK(lle);
194 	error = dump_lle_locked(lle, arg);
195 	LLE_RUNLOCK(lle);
196 	return (error);
197 }
198 
199 static bool
200 dump_llt(struct lltable *llt, struct netlink_walkargs *wa)
201 {
202 	lltable_foreach_lle(llt, dump_lle, wa);
203 
204 	return (true);
205 }
206 
207 static int
208 dump_llts_iface(struct netlink_walkargs *wa, struct ifnet *ifp, int family)
209 {
210 	int error = 0;
211 
212 	wa->ifp = ifp;
213 	for (int i = 0; i < sizeof(lle_families) / sizeof(int); i++) {
214 		int fam = lle_families[i];
215 		struct lltable *llt = lltable_get(ifp, fam);
216 		if (llt != NULL && (family == 0 || family == fam)) {
217 			wa->count++;
218 			wa->family = fam;
219 			if (!dump_llt(llt, wa)) {
220 				error = ENOMEM;
221 				break;
222 			}
223 			wa->dumped++;
224 		}
225 	}
226 	return (error);
227 }
228 
229 static int
230 dump_llts(struct netlink_walkargs *wa, struct ifnet *ifp, int family)
231 {
232 	NL_LOG(LOG_DEBUG, "Start dump ifp=%s family=%d", ifp ? if_name(ifp) : "NULL", family);
233 
234 	wa->hdr.nlmsg_flags |= NLM_F_MULTI;
235 
236 	if (ifp != NULL) {
237 		dump_llts_iface(wa, ifp, family);
238 	} else {
239 		CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
240 			dump_llts_iface(wa, ifp, family);
241 		}
242 	}
243 
244 	NL_LOG(LOG_DEBUG, "End dump, iterated %d dumped %d", wa->count, wa->dumped);
245 
246 	if (!nlmsg_end_dump(wa->nw, wa->error, &wa->hdr)) {
247                 NL_LOG(LOG_DEBUG, "Unable to add new message");
248                 return (ENOMEM);
249         }
250 
251 	return (0);
252 }
253 
254 static int
255 get_lle(struct netlink_walkargs *wa, struct ifnet *ifp, int family, struct sockaddr *dst)
256 {
257 	struct lltable *llt = lltable_get(ifp, family);
258 	if (llt == NULL)
259 		return (ESRCH);
260 
261 #ifdef INET6
262 	if (dst->sa_family == AF_INET6) {
263 		struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
264 
265 		if (IN6_IS_SCOPE_LINKLOCAL(&dst6->sin6_addr))
266 			in6_set_unicast_scopeid(&dst6->sin6_addr, ifp->if_index);
267 	}
268 #endif
269 	struct llentry *lle = lla_lookup(llt, LLE_UNLOCKED, dst);
270 	if (lle == NULL)
271 		return (ESRCH);
272 
273 	wa->ifp = ifp;
274 	wa->family = family;
275 
276 	return (dump_lle(llt, lle, wa));
277 }
278 
279 struct nl_parsed_neigh {
280 	struct sockaddr	*nda_dst;
281 	struct ifnet	*nda_ifp;
282 	struct nlattr	*nda_lladdr;
283 	uint32_t	ndm_flags;
284 	uint16_t	ndm_state;
285 	uint8_t		ndm_family;
286 };
287 
288 #define	_IN(_field)	offsetof(struct ndmsg, _field)
289 #define	_OUT(_field)	offsetof(struct nl_parsed_neigh, _field)
290 static struct nlfield_parser nlf_p_neigh[] = {
291 	{ .off_in = _IN(ndm_family), .off_out = _OUT(ndm_family), .cb = nlf_get_u8 },
292 	{ .off_in = _IN(ndm_flags), .off_out = _OUT(ndm_flags), .cb = nlf_get_u8_u32 },
293 	{ .off_in = _IN(ndm_state), .off_out = _OUT(ndm_state), .cb = nlf_get_u16 },
294 	{ .off_in = _IN(ndm_ifindex), .off_out = _OUT(nda_ifp), .cb = nlf_get_ifpz },
295 };
296 
297 static struct nlattr_parser nla_p_neigh[] = {
298 	{ .type = NDA_DST, .off = _OUT(nda_dst), .cb = nlattr_get_ip },
299 	{ .type = NDA_LLADDR, .off = _OUT(nda_lladdr), .cb = nlattr_get_nla },
300 	{ .type = NDA_IFINDEX, .off = _OUT(nda_ifp), .cb = nlattr_get_ifp },
301 	{ .type = NDA_FLAGS_EXT, .off = _OUT(ndm_flags), .cb = nlattr_get_uint32 },
302 };
303 #undef _IN
304 #undef _OUT
305 NL_DECLARE_PARSER(ndmsg_parser, struct ndmsg, nlf_p_neigh, nla_p_neigh);
306 
307 
308 /*
309  * type=RTM_NEWNEIGH, flags=NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, seq=1661941473, pid=0},
310  * {ndm_family=AF_INET6, ndm_ifindex=if_nametoindex("enp0s31f6"), ndm_state=NUD_PERMANENT, ndm_flags=0, ndm_type=RTN_UNSPEC},
311  * [
312  *  {{nla_len=20, nla_type=NDA_DST}, inet_pton(AF_INET6, "2a01:4f8:13a:70c::3")},
313  *  {{nla_len=10, nla_type=NDA_LLADDR}, 20:4e:71:62:ae:f2}]}, iov_len=60}
314  */
315 
316 static int
317 rtnl_handle_newneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt)
318 {
319 	int error;
320 
321 	struct nl_parsed_neigh attrs = {};
322 	error = nl_parse_nlmsg(hdr, &ndmsg_parser, npt, &attrs);
323 	if (error != 0)
324 		return (error);
325 
326 	if (attrs.nda_ifp == NULL || attrs.nda_dst == NULL || attrs.nda_lladdr == NULL) {
327 		if (attrs.nda_ifp == NULL)
328 			NLMSG_REPORT_ERR_MSG(npt, "NDA_IFINDEX / ndm_ifindex not set");
329 		if (attrs.nda_dst == NULL)
330 			NLMSG_REPORT_ERR_MSG(npt, "NDA_DST not set");
331 		if (attrs.nda_lladdr == NULL)
332 			NLMSG_REPORT_ERR_MSG(npt, "NDA_LLADDR not set");
333 		return (EINVAL);
334 	}
335 
336 	if (attrs.nda_dst->sa_family != attrs.ndm_family) {
337 		NLMSG_REPORT_ERR_MSG(npt,
338 		    "NDA_DST family (%d) is different from ndm_family (%d)",
339 		    attrs.nda_dst->sa_family, attrs.ndm_family);
340 		return (EINVAL);
341 	}
342 
343 	int addrlen = attrs.nda_ifp->if_addrlen;
344 	if (attrs.nda_lladdr->nla_len != sizeof(struct nlattr) + addrlen) {
345 		NLMSG_REPORT_ERR_MSG(npt,
346 		    "NDA_LLADDR address length (%ld) is different from expected (%d)",
347 		    attrs.nda_lladdr->nla_len - sizeof(struct nlattr), addrlen);
348 		return (EINVAL);
349 	}
350 
351 	if (attrs.ndm_state != NUD_PERMANENT) {
352 		NLMSG_REPORT_ERR_MSG(npt, "ndm_state %d not supported", attrs.ndm_state);
353 		return (ENOTSUP);
354 	}
355 
356 	const uint16_t supported_flags = NTF_PROXY | NTF_STICKY;
357 	if ((attrs.ndm_flags & supported_flags) != attrs.ndm_flags) {
358 		NLMSG_REPORT_ERR_MSG(npt, "ndm_flags %X not supported",
359 		    attrs.ndm_flags &~ supported_flags);
360 		return (ENOTSUP);
361 	}
362 
363 	/* Replacement requires new entry creation anyway */
364 	if ((hdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_REPLACE)) == 0)
365 		return (ENOTSUP);
366 
367 	struct lltable *llt = lltable_get(attrs.nda_ifp, attrs.ndm_family);
368 	if (llt == NULL)
369 		return (EAFNOSUPPORT);
370 
371 
372 	uint8_t linkhdr[LLE_MAX_LINKHDR];
373 	size_t linkhdrsize = sizeof(linkhdr);
374 	int lladdr_off = 0;
375 	if (lltable_calc_llheader(attrs.nda_ifp, attrs.ndm_family,
376 	    (char *)(attrs.nda_lladdr + 1), linkhdr, &linkhdrsize, &lladdr_off) != 0) {
377 		NLMSG_REPORT_ERR_MSG(npt, "unable to calculate lle prepend data");
378 		return (EINVAL);
379 	}
380 
381 	int lle_flags = LLE_STATIC | ((attrs.ndm_flags & NTF_PROXY) ? LLE_PUB : 0);
382 	struct llentry *lle = lltable_alloc_entry(llt, lle_flags, attrs.nda_dst);
383 	if (lle == NULL)
384 		return (ENOMEM);
385 	lltable_set_entry_addr(attrs.nda_ifp, lle, linkhdr, linkhdrsize, lladdr_off);
386 
387 	/* llentry created, try to insert or update :*/
388 	IF_AFDATA_WLOCK(attrs.nda_ifp);
389 	LLE_WLOCK(lle);
390 	struct llentry *lle_tmp = lla_lookup(llt, LLE_EXCLUSIVE, attrs.nda_dst);
391 	if (lle_tmp != NULL) {
392 		if (hdr->nlmsg_flags & NLM_F_EXCL) {
393 			LLE_WUNLOCK(lle_tmp);
394 			lle_tmp = NULL;
395 			error = EEXIST;
396 		} else if (hdr->nlmsg_flags & NLM_F_REPLACE) {
397 			lltable_unlink_entry(llt, lle_tmp);
398 			lltable_link_entry(llt, lle);
399 		} else
400 			error = EEXIST;
401 	} else {
402 		if (hdr->nlmsg_flags & NLM_F_CREATE)
403 			lltable_link_entry(llt, lle);
404 		else
405 			error = ENOENT;
406 	}
407 	IF_AFDATA_WUNLOCK(attrs.nda_ifp);
408 
409 	if (error != 0) {
410 		if (lle != NULL)
411 			llentry_free(lle);
412 		return (error);
413 	}
414 
415 	if (lle_tmp != NULL)
416 		llentry_free(lle_tmp);
417 
418 	/* XXX: We're inside epoch */
419 	EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED);
420 	LLE_WUNLOCK(lle);
421 
422 	return (0);
423 }
424 
425 static int
426 rtnl_handle_delneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt)
427 {
428 	int error;
429 
430 	struct nl_parsed_neigh attrs = {};
431 	error = nl_parse_nlmsg(hdr, &ndmsg_parser, npt, &attrs);
432 	if (error != 0)
433 		return (error);
434 
435 	if (attrs.nda_dst == NULL) {
436 		NLMSG_REPORT_ERR_MSG(npt, "NDA_DST not set");
437 		return (EINVAL);
438 	}
439 
440 	if (attrs.nda_ifp == NULL) {
441 		NLMSG_REPORT_ERR_MSG(npt, "no ifindex provided");
442 		return (EINVAL);
443 	}
444 
445 	struct lltable *llt = lltable_get(attrs.nda_ifp, attrs.ndm_family);
446 	if (llt == NULL)
447 		return (EAFNOSUPPORT);
448 
449 	IF_AFDATA_WLOCK(attrs.nda_ifp);
450 	struct llentry *lle = lla_lookup(llt, LLE_EXCLUSIVE, attrs.nda_dst);
451 	if (lle != NULL) {
452 		if ((lle->la_flags & LLE_IFADDR) != 0) {
453 			LLE_WUNLOCK(lle);
454 			lle = NULL;
455 			error = EPERM;
456 		} else
457 			lltable_unlink_entry(llt, lle);
458 	} else
459 		error = ENOENT;
460 	IF_AFDATA_WUNLOCK(attrs.nda_ifp);
461 
462 	if (error == 0 && lle != NULL)
463 		EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED);
464 
465 	if (lle != NULL)
466 		llentry_free(lle);
467 
468 	return (error);
469 }
470 
471 static int
472 rtnl_handle_getneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt)
473 {
474 	int error;
475 
476 	struct nl_parsed_neigh attrs = {};
477 	error = nl_parse_nlmsg(hdr, &ndmsg_parser, npt, &attrs);
478 	if (error != 0)
479 		return (error);
480 
481 	if (attrs.nda_dst != NULL && attrs.nda_ifp == NULL) {
482 		NLMSG_REPORT_ERR_MSG(npt, "has NDA_DST but no ifindex provided");
483 		return (EINVAL);
484 	}
485 
486 	struct netlink_walkargs wa = {
487 		.so = nlp,
488 		.nw = npt->nw,
489 		.hdr.nlmsg_pid = hdr->nlmsg_pid,
490 		.hdr.nlmsg_seq = hdr->nlmsg_seq,
491 		.hdr.nlmsg_flags = hdr->nlmsg_flags,
492 		.hdr.nlmsg_type = NL_RTM_NEWNEIGH,
493 	};
494 
495 	if (attrs.nda_dst == NULL)
496 		error = dump_llts(&wa, attrs.nda_ifp, attrs.ndm_family);
497 	else
498 		error = get_lle(&wa, attrs.nda_ifp, attrs.ndm_family, attrs.nda_dst);
499 
500 	return (error);
501 }
502 
503 static const struct rtnl_cmd_handler cmd_handlers[] = {
504 	{
505 		.cmd = NL_RTM_NEWNEIGH,
506 		.name = "RTM_NEWNEIGH",
507 		.cb = &rtnl_handle_newneigh,
508 	},
509 	{
510 		.cmd = NL_RTM_DELNEIGH,
511 		.name = "RTM_DELNEIGH",
512 		.cb = &rtnl_handle_delneigh,
513 		.priv = PRIV_NET_ROUTE,
514 	},
515 	{
516 		.cmd = NL_RTM_GETNEIGH,
517 		.name = "RTM_GETNEIGH",
518 		.cb = &rtnl_handle_getneigh,
519 		.priv = PRIV_NET_ROUTE,
520 	}
521 };
522 
523 static void
524 rtnl_lle_event(void *arg __unused, struct llentry *lle, int evt)
525 {
526 	struct ifnet *ifp;
527 	int family;
528 
529 	LLE_WLOCK_ASSERT(lle);
530 
531 	ifp = lltable_get_ifp(lle->lle_tbl);
532 	family = lltable_get_af(lle->lle_tbl);
533 
534 	if (family != AF_INET && family != AF_INET6)
535 		return;
536 
537 	int nlmsgs_type = evt == LLENTRY_RESOLVED ? NL_RTM_NEWNEIGH : NL_RTM_DELNEIGH;
538 
539 	struct nl_writer nw = {};
540 	if (!nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_ROUTE, RTNLGRP_NEIGH)) {
541 		NL_LOG(LOG_DEBUG, "error allocating group writer");
542 		return;
543 	}
544 
545 	struct netlink_walkargs wa = {
546 		.hdr.nlmsg_type = nlmsgs_type,
547 		.nw = &nw,
548 		.ifp = ifp,
549 		.family = family,
550 	};
551 
552 	dump_lle_locked(lle, &wa);
553 	nlmsg_flush(&nw);
554 }
555 
556 static const struct nlhdr_parser *all_parsers[] = { &ndmsg_parser };
557 
558 void
559 rtnl_neighs_init()
560 {
561 	NL_VERIFY_PARSERS(all_parsers);
562 	rtnl_register_messages(cmd_handlers, NL_ARRAY_LEN(cmd_handlers));
563 	lle_event_p = EVENTHANDLER_REGISTER(lle_event, rtnl_lle_event, NULL,
564 	    EVENTHANDLER_PRI_ANY);
565 }
566 
567 void
568 rtnl_neighs_destroy()
569 {
570 	EVENTHANDLER_DEREGISTER(lle_event, lle_event_p);
571 }
572