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