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