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