diff --git a/sbin/ifconfig/af_inet.c b/sbin/ifconfig/af_inet.c index 83b605e8c4cb..bcf2a8ab0ffd 100644 --- a/sbin/ifconfig/af_inet.c +++ b/sbin/ifconfig/af_inet.c @@ -1,582 +1,582 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" #ifdef WITHOUT_NETLINK static struct in_aliasreq in_addreq; static struct ifreq in_ridreq; #else struct in_px { struct in_addr addr; int plen; bool addrset; bool maskset; }; struct in_pdata { struct in_px addr; struct in_px dst_addr; struct in_px brd_addr; uint32_t flags; uint32_t vhid; }; static struct in_pdata in_add, in_del; #endif static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ extern char *f_inet, *f_addr; static void print_addr(struct sockaddr_in *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error) inet_ntop(AF_INET, &sin->sin_addr, addr_buf, sizeof(addr_buf)); printf("\tinet %s", addr_buf); } #ifdef WITHOUT_NETLINK static void in_status(if_ctx *ctx __unused, const struct ifaddrs *ifa) { struct sockaddr_in *sin, null_sin = {}; sin = satosin(ifa->ifa_addr); if (sin == NULL) return; print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = satosin(ifa->ifa_dstaddr); if (sin == NULL) sin = &null_sin; printf(" --> %s", inet_ntoa(sin->sin_addr)); } sin = satosin(ifa->ifa_netmask); if (sin == NULL) sin = &null_sin; if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) { int cidr = 32; unsigned long smask; smask = ntohl(sin->sin_addr.s_addr); while ((smask & 1) == 0) { smask = smask >> 1; cidr--; if (cidr == 0) break; } printf("/%d", cidr); } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) printf(" netmask %s", inet_ntoa(sin->sin_addr)); else printf(" netmask 0x%lx", (unsigned long)ntohl(sin->sin_addr.s_addr)); if (ifa->ifa_flags & IFF_BROADCAST) { sin = satosin(ifa->ifa_broadaddr); if (sin != NULL && sin->sin_addr.s_addr != 0) printf(" broadcast %s", inet_ntoa(sin->sin_addr)); } print_vhid(ifa); putchar('\n'); } #else static struct in_addr get_mask(int plen) { struct in_addr a; a.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0); return (a); } static void in_status_nl(if_ctx *ctx __unused, if_link_t *link, if_addr_t *ifa) { struct sockaddr_in *sin = satosin(ifa->ifa_local); int plen = ifa->ifa_prefixlen; print_addr(sin); if (link->ifi_flags & IFF_POINTOPOINT) { struct sockaddr_in *dst = satosin(ifa->ifa_address); printf(" --> %s", inet_ntoa(dst->sin_addr)); } if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) { printf("/%d", plen); } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) printf(" netmask %s", inet_ntoa(get_mask(plen))); else printf(" netmask 0x%lx", (unsigned long)ntohl(get_mask(plen).s_addr)); if ((link->ifi_flags & IFF_BROADCAST) && plen != 0) { struct sockaddr_in *brd = satosin(ifa->ifa_broadcast); if (brd != NULL) printf(" broadcast %s", inet_ntoa(brd->sin_addr)); } if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } #endif #ifdef WITHOUT_NETLINK #define SIN(x) ((struct sockaddr_in *) &(x)) static struct sockaddr_in *sintab[] = { SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr), SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr) }; static void in_copyaddr(if_ctx *ctx __unused, int to, int from) { memcpy(sintab[to], sintab[from], sizeof(struct sockaddr_in)); } static void in_getaddr(const char *s, int which) { struct sockaddr_in *sin = sintab[which]; struct hostent *hp; struct netent *np; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { const char *errstr; /* address is `name/masklen' */ int masklen = 0; struct sockaddr_in *min = sintab[MASK]; *p = '\0'; if (!isdigit(*(p + 1))) errstr = "invalid"; else masklen = (int)strtonum(p + 1, 0, 32, &errstr); if (errstr != NULL) { *p = '/'; errx(1, "%s: bad value (width %s)", s, errstr); } min->sin_family = AF_INET; min->sin_len = sizeof(*min); min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & 0xffffffff); } } if (inet_aton(s, &sin->sin_addr)) return; if ((hp = gethostbyname(s)) != NULL) bcopy(hp->h_addr, (char *)&sin->sin_addr, MIN((size_t)hp->h_length, sizeof(sin->sin_addr))); else if ((np = getnetbyname(s)) != NULL) sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); else errx(1, "%s: bad value", s); } #else static struct in_px *sintab_nl[] = { &in_del.addr, /* RIDADDR */ &in_add.addr, /* ADDR */ NULL, /* MASK */ &in_add.dst_addr, /* DSTADDR*/ &in_add.brd_addr, /* BRDADDR*/ }; static void in_copyaddr(if_ctx *ctx __unused, int to, int from) { sintab_nl[to]->addr = sintab_nl[from]->addr; sintab_nl[to]->addrset = sintab_nl[from]->addrset; } static void in_getip(const char *addr_str, struct in_addr *ip) { struct hostent *hp; struct netent *np; if (inet_aton(addr_str, ip)) return; if ((hp = gethostbyname(addr_str)) != NULL) bcopy(hp->h_addr, (char *)ip, MIN((size_t)hp->h_length, sizeof(ip))); else if ((np = getnetbyname(addr_str)) != NULL) *ip = inet_makeaddr(np->n_net, INADDR_ANY); else errx(1, "%s: bad value", addr_str); } static void in_getaddr(const char *s, int which) { struct in_px *px = sintab_nl[which]; if (which == MASK) { struct in_px *px_addr = sintab_nl[ADDR]; struct in_addr mask = {}; in_getip(s, &mask); px_addr->plen = __bitcount32(mask.s_addr); px_addr->maskset = true; return; } if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { const char *errstr; /* address is `name/masklen' */ int masklen; *p = '\0'; if (!isdigit(*(p + 1))) errstr = "invalid"; else masklen = (int)strtonum(p + 1, 0, 32, &errstr); if (errstr != NULL) { *p = '/'; errx(1, "%s: bad value (width %s)", s, errstr); } px->plen = masklen; px->maskset = true; } } in_getip(s, &px->addr); px->addrset = true; } /* * Deletes the first found IPv4 interface address for the interface. * * This function provides SIOCDIFADDR semantics missing in Netlink. * When no valid IPv4 address is specified (sin_family or sin_len is wrong) to * the SIOCDIFADDR call, it deletes the first found IPv4 address on the interface. * 'ifconfig IFNAME inet addr/prefix' relies on that behavior, as it * executes empty SIOCDIFADDR before adding a new address. */ static int in_delete_first_nl(if_ctx *ctx) { struct nlmsghdr *hdr; struct ifaddrmsg *ifahdr; uint32_t nlmsg_seq; struct in_addr addr; struct snl_writer nw = {}; struct snl_errmsg_data e = {}; struct snl_state *ss = ctx->io_ss; bool found = false; uint32_t ifindex = if_nametoindex_nl(ss, ctx->ifname); if (ifindex == 0) { /* No interface with the desired name, nothing to delete */ return (EADDRNOTAVAIL); } snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, NL_RTM_GETADDR); hdr->nlmsg_flags |= NLM_F_DUMP; ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_index = ifindex; - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (EINVAL); nlmsg_seq = hdr->nlmsg_seq; while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { struct snl_parsed_addr attrs = {}; if (snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs)) { addr = satosin(attrs.ifa_local)->sin_addr; ifindex = attrs.ifa_index; found = true; break; } else return (EINVAL); } if (e.error != 0) { if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } if (!found) return (0); /* Try to delete the found address */ snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, NL_RTM_DELADDR); ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_index = ifindex; snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &addr); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (EINVAL); memset(&e, 0, sizeof(e)); snl_read_reply_code(ss, hdr->nlmsg_seq, &e); if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } static int in_exec_nl(if_ctx *ctx, unsigned long action, void *data) { struct in_pdata *pdata = (struct in_pdata *)data; struct snl_writer nw = {}; if (action == NL_RTM_DELADDR && !pdata->addr.addrset) return (in_delete_first_nl(ctx)); snl_init_writer(ctx->io_ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_prefixlen = pdata->addr.plen; ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, ctx->ifname); snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &pdata->addr.addr); if (action == NL_RTM_NEWADDR && pdata->dst_addr.addrset) snl_add_msg_attr_ip4(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); if (action == NL_RTM_NEWADDR && pdata->brd_addr.addrset) snl_add_msg_attr_ip4(&nw, IFA_BROADCAST, &pdata->brd_addr.addr); int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); if (pdata->vhid != 0) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); - if (!snl_finalize_msg(&nw) || !snl_send_message(ctx->io_ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e); if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } static void in_setdefaultmask_nl(void) { struct in_px *px = sintab_nl[ADDR]; in_addr_t i = ntohl(px->addr.s_addr); /* * If netmask isn't supplied, use historical default. * This is deprecated for interfaces other than loopback * or point-to-point; warn in other cases. In the future * we should return an error rather than warning. */ if (IN_CLASSA(i)) px->plen = IN_CLASSA_NSHIFT; else if (IN_CLASSB(i)) px->plen = IN_CLASSB_NSHIFT; else px->plen = IN_CLASSC_NSHIFT; px->maskset = true; } #endif static void warn_nomask(int ifflags) { if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { warnx("WARNING: setting interface address without mask " "is deprecated,\ndefault mask may not be correct."); } } static void in_postproc(if_ctx *ctx __unused, int newaddr, int ifflags) { #ifdef WITHOUT_NETLINK if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) { warn_nomask(ifflags); } #else if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) { warn_nomask(ifflags); in_setdefaultmask_nl(); } #endif } static void in_status_tunnel(if_ctx *ctx) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct ifreq ifr; const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ctx->ifname, IFNAMSIZ); if (ioctl_ctx(ctx, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0) return; if (sa->sa_family != AF_INET) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl_ctx(ctx, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0) return; if (sa->sa_family != AF_INET) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet %s --> %s\n", src, dst); } static void in_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres) { struct in_aliasreq addreq; memset(&addreq, 0, sizeof(addreq)); strlcpy(addreq.ifra_name, ctx->ifname, IFNAMSIZ); memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl_ctx(ctx, SIOCSIFPHYADDR, &addreq) < 0) warn("SIOCSIFPHYADDR"); } static void in_set_vhid(int vhid) { #ifdef WITHOUT_NETLINK in_addreq.ifra_vhid = vhid; #else in_add.vhid = (uint32_t)vhid; #endif } static struct afswtch af_inet = { .af_name = "inet", .af_af = AF_INET, #ifdef WITHOUT_NETLINK .af_status = in_status, #else .af_status = in_status_nl, #endif .af_getaddr = in_getaddr, .af_copyaddr = in_copyaddr, .af_postproc = in_postproc, .af_status_tunnel = in_status_tunnel, .af_settunnel = in_set_tunnel, .af_setvhid = in_set_vhid, #ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR, .af_aifaddr = SIOCAIFADDR, .af_ridreq = &in_ridreq, .af_addreq = &in_addreq, .af_exec = af_exec_ioctl, #else .af_difaddr = NL_RTM_DELADDR, .af_aifaddr = NL_RTM_NEWADDR, .af_ridreq = &in_del, .af_addreq = &in_add, .af_exec = in_exec_nl, #endif }; static __constructor void inet_ctor(void) { #ifndef RESCUE if (!feature_present("inet")) return; #endif af_register(&af_inet); } diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index a0138e9b3de6..b3da21758982 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -1,793 +1,793 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Define ND6_INFINITE_LIFETIME */ #include "ifconfig.h" #include "ifconfig_netlink.h" #ifndef WITHOUT_NETLINK struct in6_px { struct in6_addr addr; int plen; bool set; }; struct in6_pdata { struct in6_px addr; struct in6_px dst_addr; struct in6_addrlifetime lifetime; uint32_t flags; uint32_t vhid; }; static struct in6_pdata in6_del; static struct in6_pdata in6_add = { .lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME }, }; #else static struct in6_ifreq in6_ridreq; static struct in6_aliasreq in6_addreq = { .ifra_flags = 0, .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; #endif static int ip6lifetime; #ifdef WITHOUT_NETLINK static int prefix(void *, int); #endif static char *sec2str(time_t); static int explicit_prefix = 0; extern char *f_inet6, *f_addr; extern void setnd6flags(if_ctx *, const char *, int); extern void setnd6defif(if_ctx *,const char *, int); extern void nd6_status(if_ctx *); static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ static void setifprefixlen(if_ctx *ctx __netlink_unused, const char *addr, int dummy __unused) { #ifdef WITHOUT_NETLINK const struct afswtch *afp = ctx->afp; if (afp->af_getprefix != NULL) afp->af_getprefix(addr, MASK); #else int plen = strtol(addr, NULL, 10); if ((plen < 0) || (plen > 128)) errx(1, "%s: bad value", addr); in6_add.addr.plen = plen; #endif explicit_prefix = 1; } static void setip6flags(if_ctx *ctx, const char *dummyaddr __unused, int flag) { const struct afswtch *afp = ctx->afp; if (afp->af_af != AF_INET6) err(1, "address flags can be set only for inet6 addresses"); #ifdef WITHOUT_NETLINK if (flag < 0) in6_addreq.ifra_flags &= ~(-flag); else in6_addreq.ifra_flags |= flag; #else if (flag < 0) in6_add.flags &= ~(-flag); else in6_add.flags |= flag; #endif } static void setip6lifetime(if_ctx *ctx, const char *cmd, const char *val) { const struct afswtch *afp = ctx->afp; struct timespec now; time_t newval; char *ep; #ifdef WITHOUT_NETLINK struct in6_addrlifetime *lifetime = &in6_addreq.ifra_lifetime; #else struct in6_addrlifetime *lifetime = &in6_add.lifetime; #endif clock_gettime(CLOCK_MONOTONIC_FAST, &now); newval = (time_t)strtoul(val, &ep, 0); if (val == ep) errx(1, "invalid %s", cmd); if (afp->af_af != AF_INET6) errx(1, "%s not allowed for the AF", cmd); if (strcmp(cmd, "vltime") == 0) { lifetime->ia6t_expire = now.tv_sec + newval; lifetime->ia6t_vltime = newval; } else if (strcmp(cmd, "pltime") == 0) { lifetime->ia6t_preferred = now.tv_sec + newval; lifetime->ia6t_pltime = newval; } } static void setip6pltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "pltime", seconds); } static void setip6vltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "vltime", seconds); } static void setip6eui64(if_ctx *ctx, const char *cmd, int dummy __unused) { const struct afswtch *afp = ctx->afp; struct ifaddrs *ifap, *ifa; const struct sockaddr_in6 *sin6 = NULL; const struct in6_addr *lladdr = NULL; struct in6_addr *in6; if (afp->af_af != AF_INET6) errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); #ifdef WITHOUT_NETLINK in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; #else in6 = &in6_add.addr.addr; #endif if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) errx(EXIT_FAILURE, "interface index is already filled"); if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, ctx->ifname) == 0) { sin6 = (const struct sockaddr_in6 *)satosin6(ifa->ifa_addr); if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { lladdr = &sin6->sin6_addr; break; } } } if (!lladdr) errx(EXIT_FAILURE, "could not determine link local address"); memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); freeifaddrs(ifap); } static void print_addr(struct sockaddr_in6 *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf("\tinet6 %s", addr_buf); } static void print_p2p(struct sockaddr_in6 *sin) { int error; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf(" --> %s", addr_buf); } static void print_mask(int plen) { if (f_inet6 != NULL && strcmp(f_inet6, "cidr") == 0) printf("/%d", plen); else printf(" prefixlen %d", plen); } static void print_flags(int flags6) { if ((flags6 & IN6_IFF_ANYCAST) != 0) printf(" anycast"); if ((flags6 & IN6_IFF_TENTATIVE) != 0) printf(" tentative"); if ((flags6 & IN6_IFF_DUPLICATED) != 0) printf(" duplicated"); if ((flags6 & IN6_IFF_DETACHED) != 0) printf(" detached"); if ((flags6 & IN6_IFF_DEPRECATED) != 0) printf(" deprecated"); if ((flags6 & IN6_IFF_AUTOCONF) != 0) printf(" autoconf"); if ((flags6 & IN6_IFF_TEMPORARY) != 0) printf(" temporary"); if ((flags6 & IN6_IFF_PREFER_SOURCE) != 0) printf(" prefer_source"); } static void print_lifetime(const char *prepend, time_t px_time, struct timespec *now) { printf(" %s", prepend); if (px_time == 0) printf(" infty"); printf(" %s", px_time < now->tv_sec ? "0" : sec2str(px_time - now->tv_sec)); } #ifdef WITHOUT_NETLINK static void in6_status(if_ctx *ctx, const struct ifaddrs *ifa) { struct sockaddr_in6 *sin, null_sin = {}; struct in6_ifreq ifr6; int s6; u_int32_t flags6; struct in6_addrlifetime lifetime; sin = satosin6(ifa->ifa_addr); if (sin == NULL) return; strlcpy(ifr6.ifr_name, ctx->ifname, sizeof(ifr6.ifr_name)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_INET6,SOCK_DGRAM)"); return; } ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFAFLAG_IN6)"); close(s6); return; } flags6 = ifr6.ifr_ifru.ifru_flags6; memset(&lifetime, 0, sizeof(lifetime)); ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFALIFETIME_IN6)"); close(s6); return; } lifetime = ifr6.ifr_ifru.ifru_lifetime; close(s6); print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = satosin6(ifa->ifa_dstaddr); /* * some of the interfaces do not have valid destination * address. */ if (sin != NULL && sin->sin6_family == AF_INET6) print_p2p(sin); } sin = satosin6(ifa->ifa_netmask); if (sin == NULL) sin = &null_sin; print_mask(prefix(&sin->sin6_addr, sizeof(struct in6_addr))); print_flags(flags6); if ((satosin6(ifa->ifa_addr))->sin6_scope_id) printf(" scopeid 0x%x", (satosin6(ifa->ifa_addr))->sin6_scope_id); if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { struct timespec now; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", lifetime.ia6t_preferred, &now); print_lifetime("vltime", lifetime.ia6t_expire, &now); } print_vhid(ifa); putchar('\n'); } #else static void show_lifetime(struct ifa_cacheinfo *ci) { struct timespec now; uint32_t pl, vl; if (ci == NULL) return; int count = ci->ifa_prefered != ND6_INFINITE_LIFETIME; count += ci->ifa_valid != ND6_INFINITE_LIFETIME; if (count == 0) return; pl = (ci->ifa_prefered == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_prefered; vl = (ci->ifa_valid == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_valid; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", pl + now.tv_sec, &now); print_lifetime("vltime", vl + now.tv_sec, &now); } static void in6_status_nl(if_ctx *ctx __unused, if_link_t *link __unused, if_addr_t *ifa) { int plen = ifa->ifa_prefixlen; uint32_t scopeid; if (ifa->ifa_local == NULL) { /* Non-P2P address */ scopeid = satosin6(ifa->ifa_address)->sin6_scope_id; print_addr(satosin6(ifa->ifa_address)); } else { scopeid = satosin6(ifa->ifa_local)->sin6_scope_id; print_addr(satosin6(ifa->ifa_local)); print_p2p(satosin6(ifa->ifa_address)); } print_mask(plen); print_flags(ifa->ifaf_flags); if (scopeid != 0) printf(" scopeid 0x%x", scopeid); show_lifetime(ifa->ifa_cacheinfo); if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } static struct in6_px *sin6tab_nl[] = { &in6_del.addr, /* RIDADDR */ &in6_add.addr, /* ADDR */ NULL, /* MASK */ &in6_add.dst_addr, /* DSTADDR*/ }; static void in6_copyaddr(if_ctx *ctx __unused, int to, int from) { sin6tab_nl[to]->addr = sin6tab_nl[from]->addr; sin6tab_nl[to]->set = sin6tab_nl[from]->set; } static void in6_getaddr(const char *addr_str, int which) { struct in6_px *px = sin6tab_nl[which]; px->set = true; px->plen = 128; if (which == ADDR) { char *p = NULL; if((p = strrchr(addr_str, '/')) != NULL) { *p = '\0'; int plen = strtol(p + 1, NULL, 10); if (plen < 0 || plen > 128) errx(1, "%s: bad value", p + 1); px->plen = plen; explicit_prefix = 1; } } struct addrinfo hints = { .ai_family = AF_INET6 }; struct addrinfo *res; int error = getaddrinfo(addr_str, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, addr_str, &px->addr) != 1) errx(1, "%s: bad value", addr_str); } else { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)(void *)res->ai_addr; px->addr = sin6->sin6_addr; freeaddrinfo(res); } } static int in6_exec_nl(if_ctx *ctx, unsigned long action, void *data) { struct in6_pdata *pdata = (struct in6_pdata *)data; struct snl_writer nw = {}; snl_init_writer(ctx->io_ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET6; ifahdr->ifa_prefixlen = pdata->addr.plen; ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, ctx->ifname); snl_add_msg_attr_ip6(&nw, IFA_LOCAL, &pdata->addr.addr); if (action == NL_RTM_NEWADDR && pdata->dst_addr.set) snl_add_msg_attr_ip6(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); struct ifa_cacheinfo ci = { .ifa_prefered = pdata->lifetime.ia6t_pltime, .ifa_valid = pdata->lifetime.ia6t_vltime, }; snl_add_msg_attr(&nw, IFA_CACHEINFO, sizeof(ci), &ci); int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); if (pdata->vhid != 0) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); - if (!snl_finalize_msg(&nw) || !snl_send_message(ctx->io_ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e); return (e.error); } #endif #ifdef WITHOUT_NETLINK static struct sockaddr_in6 *sin6tab[] = { &in6_ridreq.ifr_addr, &in6_addreq.ifra_addr, &in6_addreq.ifra_prefixmask, &in6_addreq.ifra_dstaddr }; static void in6_copyaddr(if_ctx *ctx __unused, int to, int from) { memcpy(sin6tab[to], sin6tab[from], sizeof(struct sockaddr_in6)); } static void in6_getprefix(const char *plen, int which) { struct sockaddr_in6 *sin = sin6tab[which]; u_char *cp; int len = atoi(plen); if ((len < 0) || (len > 128)) errx(1, "%s: bad value", plen); sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if ((len == 0) || (len == 128)) { memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); return; } memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) *cp++ = 0xff; *cp = 0xff << (8 - len); } static void in6_getaddr(const char *s, int which) { struct sockaddr_in6 *sin = sin6tab[which]; struct addrinfo hints, *res; int error = -1; sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { *p = '\0'; in6_getprefix(p + 1, MASK); explicit_prefix = 1; } } if (sin->sin6_family == AF_INET6) { bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_INET6; error = getaddrinfo(s, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) errx(1, "%s: bad value", s); } else { bcopy(res->ai_addr, sin, res->ai_addrlen); freeaddrinfo(res); } } } static int prefix(void *val, int size) { u_char *name = (u_char *)val; int byte, bit, plen = 0; for (byte = 0; byte < size; byte++, plen += 8) if (name[byte] != 0xff) break; if (byte == size) return (plen); for (bit = 7; bit != 0; bit--, plen++) if (!(name[byte] & (1 << bit))) break; for (; bit != 0; bit--) if (name[byte] & (1 << bit)) return(0); byte++; for (; byte < size; byte++) if (name[byte]) return(0); return (plen); } #endif static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; if (0) { days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; p += sprintf(p, "%dd", days); } if (!first || hours) { first = 0; p += sprintf(p, "%dh", hours); } if (!first || mins) { first = 0; p += sprintf(p, "%dm", mins); } sprintf(p, "%ds", secs); } else sprintf(result, "%lu", (unsigned long)total); return(result); } static void in6_postproc(if_ctx *ctx, int newaddr __unused, int ifflags __unused) { if (explicit_prefix == 0) { /* Aggregatable address architecture defines all prefixes are 64. So, it is convenient to set prefixlen to 64 if it is not specified. */ setifprefixlen(ctx, "64", 0); /* in6_getprefix("64", MASK) if MASK is available here... */ } } static void in6_status_tunnel(if_ctx *ctx) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct in6_ifreq in6_ifr; const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; memset(&in6_ifr, 0, sizeof(in6_ifr)); strlcpy(in6_ifr.ifr_name, ctx->ifname, sizeof(in6_ifr.ifr_name)); if (ioctl_ctx(ctx, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl_ctx(ctx, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet6 %s --> %s\n", src, dst); } static void in6_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres) { struct in6_aliasreq in6_req = {}; strlcpy(in6_req.ifra_name, ctx->ifname, sizeof(in6_req.ifra_name)); memcpy(&in6_req.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&in6_req.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl_ctx(ctx, SIOCSIFPHYADDR_IN6, &in6_req) < 0) warn("SIOCSIFPHYADDR_IN6"); } static void in6_set_vhid(int vhid) { #ifdef WITHOUT_NETLINK in6_addreq.ifra_vhid = vhid; #else in6_add.vhid = (uint32_t)vhid; #endif } static struct cmd inet6_cmds[] = { DEF_CMD_ARG("prefixlen", setifprefixlen), DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("prefer_source",IN6_IFF_PREFER_SOURCE, setip6flags), DEF_CMD("-prefer_source",-IN6_IFF_PREFER_SOURCE,setip6flags), DEF_CMD("accept_rtadv", ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("no_radr", ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("-no_radr", -ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("defaultif", 1, setnd6defif), DEF_CMD("-defaultif", -1, setnd6defif), DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("auto_linklocal",ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("no_prefer_iface",ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("-no_prefer_iface",-ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("no_dad", ND6_IFF_NO_DAD, setnd6flags), DEF_CMD("-no_dad", -ND6_IFF_NO_DAD, setnd6flags), DEF_CMD_ARG("pltime", setip6pltime), DEF_CMD_ARG("vltime", setip6vltime), DEF_CMD("eui64", 0, setip6eui64), #ifdef EXPERIMENTAL DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), #endif }; static struct afswtch af_inet6 = { .af_name = "inet6", .af_af = AF_INET6, #ifdef WITHOUT_NETLINK .af_status = in6_status, #else .af_status = in6_status_nl, #endif .af_getaddr = in6_getaddr, .af_copyaddr = in6_copyaddr, #ifdef WITHOUT_NETLINK .af_getprefix = in6_getprefix, #endif .af_other_status = nd6_status, .af_postproc = in6_postproc, .af_status_tunnel = in6_status_tunnel, .af_settunnel = in6_set_tunnel, .af_setvhid = in6_set_vhid, #ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR_IN6, .af_aifaddr = SIOCAIFADDR_IN6, .af_ridreq = &in6_addreq, .af_addreq = &in6_addreq, .af_exec = af_exec_ioctl, #else .af_difaddr = NL_RTM_DELADDR, .af_aifaddr = NL_RTM_NEWADDR, .af_ridreq = &in6_add, .af_addreq = &in6_add, .af_exec = in6_exec_nl, #endif }; static void in6_Lopt_cb(const char *arg __unused) { ip6lifetime++; /* print IPv6 address lifetime */ } static struct option in6_Lopt = { .opt = "L", .opt_usage = "[-L]", .cb = in6_Lopt_cb }; static __constructor void inet6_ctor(void) { size_t i; #ifndef RESCUE if (!feature_present("inet6")) return; #endif for (i = 0; i < nitems(inet6_cmds); i++) cmd_register(&inet6_cmds[i]); af_register(&af_inet6); opt_register(&in6_Lopt); } diff --git a/sbin/ifconfig/ifconfig_netlink.c b/sbin/ifconfig/ifconfig_netlink.c index 76dd99307f31..7be340e18ab5 100644 --- a/sbin/ifconfig/ifconfig_netlink.c +++ b/sbin/ifconfig/ifconfig_netlink.c @@ -1,478 +1,478 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2022 Alexander V. Chernikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" static const char *IFFBITS[] = { "UP", /* 00:0x1 IFF_UP*/ "BROADCAST", /* 01:0x2 IFF_BROADCAST*/ "DEBUG", /* 02:0x4 IFF_DEBUG*/ "LOOPBACK", /* 03:0x8 IFF_LOOPBACK*/ "POINTOPOINT", /* 04:0x10 IFF_POINTOPOINT*/ "NEEDSEPOCH", /* 05:0x20 IFF_NEEDSEPOCH*/ "RUNNING", /* 06:0x40 IFF_DRV_RUNNING*/ "NOARP", /* 07:0x80 IFF_NOARP*/ "PROMISC", /* 08:0x100 IFF_PROMISC*/ "ALLMULTI", /* 09:0x200 IFF_ALLMULTI*/ "DRV_OACTIVE", /* 10:0x400 IFF_DRV_OACTIVE*/ "SIMPLEX", /* 11:0x800 IFF_SIMPLEX*/ "LINK0", /* 12:0x1000 IFF_LINK0*/ "LINK1", /* 13:0x2000 IFF_LINK1*/ "LINK2", /* 14:0x4000 IFF_LINK2*/ "MULTICAST", /* 15:0x8000 IFF_MULTICAST*/ "CANTCONFIG", /* 16:0x10000 IFF_CANTCONFIG*/ "PPROMISC", /* 17:0x20000 IFF_PPROMISC*/ "MONITOR", /* 18:0x40000 IFF_MONITOR*/ "STATICARP", /* 19:0x80000 IFF_STATICARP*/ "STICKYARP", /* 20:0x100000 IFF_STICKYARP*/ "DYING", /* 21:0x200000 IFF_DYING*/ "RENAMING", /* 22:0x400000 IFF_RENAMING*/ "NOGROUP", /* 23:0x800000 IFF_NOGROUP*/ "LOWER_UP", /* 24:0x1000000 IFF_NETLINK_1*/ }; static void print_bits(const char *btype, uint32_t *v, const int v_count, const char **names, const int n_count) { int num = 0; for (int i = 0; i < v_count * 32; i++) { bool is_set = v[i / 32] & (1U << (i % 32)); if (is_set) { if (num++ == 0) printf("<"); if (num != 1) printf(","); if (i < n_count) printf("%s", names[i]); else printf("%s_%d", btype, i); } } if (num > 0) printf(">"); } static void nl_init_socket(struct snl_state *ss) { if (snl_init(ss, NETLINK_ROUTE)) return; if (modfind("netlink") == -1 && errno == ENOENT) { /* Try to load */ if (kldload("netlink") == -1) err(1, "netlink is not loaded and load attempt failed"); if (snl_init(ss, NETLINK_ROUTE)) return; } err(1, "unable to open netlink socket"); } int ifconfig_nl(if_ctx *ctx, int iscreate, const struct afswtch *uafp) { struct snl_state ss = {}; nl_init_socket(&ss); ctx->io_ss = &ss; int error = ifconfig_ioctl(ctx, iscreate, uafp); snl_free(&ss); ctx->io_ss = NULL; return (error); } struct ifa { struct ifa *next; uint32_t idx; struct snl_parsed_addr addr; }; struct iface { struct snl_parsed_link link; struct ifa *ifa; uint32_t ifa_count; uint32_t idx; }; struct ifmap { uint32_t size; uint32_t count; struct iface **ifaces; }; /* * Returns ifmap ifindex->snl_parsed_link. * Memory is allocated using snl temporary buffers */ static struct ifmap * prepare_ifmap(struct snl_state *ss) { struct snl_writer nw = {}; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); hdr->nlmsg_flags |= NLM_F_DUMP; snl_reserve_msg_object(&nw, struct ifinfomsg); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (NULL); uint32_t nlmsg_seq = hdr->nlmsg_seq; struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap)); struct snl_errmsg_data e = {}; while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { struct iface *iface = snl_allocz(ss, sizeof(*iface)); if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link)) continue; if (iface->link.ifi_index >= ifmap->size) { size_t new_size = MAX(ifmap->size, 32); while (new_size <= iface->link.ifi_index + 1) new_size *= 2; struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *)); memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *)); ifmap->ifaces = ifaces; ifmap->size = new_size; } ifmap->ifaces[iface->link.ifi_index] = iface; ifmap->count++; iface->idx = ifmap->count; } return (ifmap); } uint32_t if_nametoindex_nl(struct snl_state *ss, const char *ifname) { struct snl_writer nw = {}; struct snl_parsed_link_simple link = {}; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); snl_reserve_msg_object(&nw, struct ifinfomsg); snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (0); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr->nlmsg_type != NL_RTM_NEWLINK) return (0); if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link)) return (0); return (link.ifi_index); } static void prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap) { struct snl_writer nw = {}; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR); hdr->nlmsg_flags |= NLM_F_DUMP; snl_reserve_msg_object(&nw, struct ifaddrmsg); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return; uint32_t nlmsg_seq = hdr->nlmsg_seq; struct snl_errmsg_data e = {}; uint32_t count = 0; while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { struct ifa *ifa = snl_allocz(ss, sizeof(*ifa)); if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr)) continue; const uint32_t ifindex = ifa->addr.ifa_index; if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL) continue; struct iface *iface = ifmap->ifaces[ifindex]; ifa->next = iface->ifa; ifa->idx = ++count; iface->ifa = ifa; iface->ifa_count++; } } static bool match_iface(struct ifconfig_args *args, struct iface *iface) { if_link_t *link = &iface->link; if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname)) return (false); if (!match_if_flags(args, link->ifi_flags)) return (false); if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup)) return (false); if (args->afp == NULL) return (true); if (!strcmp(args->afp->af_name, "ether")) { if (link->ifla_address == NULL) return (false); struct sockaddr_dl sdl = { .sdl_len = sizeof(struct sockaddr_dl), .sdl_family = AF_LINK, .sdl_type = link->ifi_type, .sdl_alen = NLA_DATA_LEN(link->ifla_address), }; return (match_ether(&sdl)); } for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) { if (args->afp->af_af == ifa->addr.ifa_family) return (true); } return (false); } /* Sort according to the kernel-provided order */ static int cmp_iface(const void *_a, const void *_b) { const struct iface *a = *((const void * const *)_a); const struct iface *b = *((const void * const *)_b); return ((a->idx > b->idx) * 2 - 1); } static int cmp_ifaddr(const void *_a, const void *_b) { const struct ifa *a = *((const void * const *)_a); const struct ifa *b = *((const void * const *)_b); if (a->addr.ifa_family != b->addr.ifa_family) return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1); return ((a->idx > b->idx) * 2 - 1); } static void sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface) { if (iface->ifa_count == 0) return; struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *)); struct ifa *ifa = iface->ifa; for (uint32_t i = 0; i < iface->ifa_count; i++) { struct ifa *ifa_next = ifa->next; sorted_ifaddrs[i] = ifa; ifa->next = NULL; ifa = ifa_next; } qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr); ifa = sorted_ifaddrs[0]; iface->ifa = ifa; for (uint32_t i = 1; i < iface->ifa_count; i++) { ifa->next = sorted_ifaddrs[i]; ifa = sorted_ifaddrs[i]; } } static void print_ifcaps(if_ctx *ctx, if_link_t *link) { uint32_t sz_u32 = roundup2(link->iflaf_caps.nla_bitset_size, 32) / 32; if (sz_u32 > 0) { uint32_t *caps = link->iflaf_caps.nla_bitset_value; printf("\toptions=%x", caps[0]); print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names)); putchar('\n'); } if (ctx->args->supmedia && sz_u32 > 0) { uint32_t *caps = link->iflaf_caps.nla_bitset_mask; printf("\tcapabilities=%x", caps[0]); print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names)); putchar('\n'); } } static void status_nl(if_ctx *ctx, struct iface *iface) { if_link_t *link = &iface->link; struct ifconfig_args *args = ctx->args; printf("%s: ", link->ifla_ifname); printf("flags=%x", link->ifi_flags); print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS)); print_metric(ctx); printf(" mtu %d\n", link->ifla_mtu); if (link->ifla_ifalias != NULL) printf("\tdescription: %s\n", link->ifla_ifalias); print_ifcaps(ctx, link); tunnel_status(ctx); if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) { /* Start with link-level */ const struct afswtch *p = af_getbyfamily(AF_LINK); if (p != NULL && link->ifla_address != NULL) p->af_status(ctx, link, NULL); } sort_iface_ifaddrs(ctx->io_ss, iface); for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) { if (args->allfamilies) { const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family); if (p != NULL) p->af_status(ctx, link, &ifa->addr); } else if (args->afp->af_af == ifa->addr.ifa_family) { const struct afswtch *p = args->afp; p->af_status(ctx, link, &ifa->addr); } } /* TODO: convert to netlink */ if (args->allfamilies) af_other_status(ctx); else if (args->afp->af_other_status != NULL) args->afp->af_other_status(ctx); print_ifstatus(ctx); if (args->verbose > 0) sfp_status(ctx); } static int get_local_socket(void) { int s = socket(AF_LOCAL, SOCK_DGRAM, 0); if (s < 0) err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL); return (s); } void list_interfaces_nl(struct ifconfig_args *args) { struct snl_state ss = {}; struct ifconfig_context _ctx = { .args = args, .io_s = get_local_socket(), .io_ss = &ss, }; struct ifconfig_context *ctx = &_ctx; nl_init_socket(&ss); struct ifmap *ifmap = prepare_ifmap(&ss); struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *)); for (uint32_t i = 0, num = 0; i < ifmap->size; i++) { if (ifmap->ifaces[i] != NULL) { sorted_ifaces[num++] = ifmap->ifaces[i]; if (num == ifmap->count) break; } } qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface); prepare_ifaddrs(&ss, ifmap); for (uint32_t i = 0, num = 0; i < ifmap->count; i++) { struct iface *iface = sorted_ifaces[i]; if (!match_iface(args, iface)) continue; ctx->ifname = iface->link.ifla_ifname; if (args->namesonly) { if (num++ != 0) printf(" "); fputs(iface->link.ifla_ifname, stdout); } else if (args->argc == 0) status_nl(ctx, iface); else ifconfig_ioctl(ctx, 0, args->afp); } if (args->namesonly) printf("\n"); close(ctx->io_s); snl_free(&ss); } diff --git a/sbin/route/route_netlink.c b/sbin/route/route_netlink.c index 0dbd90151e10..28766b062e18 100644 --- a/sbin/route/route_netlink.c +++ b/sbin/route/route_netlink.c @@ -1,908 +1,908 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char *routename(struct sockaddr *); const char *netname(struct sockaddr *); void printb(int, const char *); extern const char routeflags[]; extern int verbose, debugonly; int rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs, struct sockaddr_storage *so, struct rt_metrics *rt_metrics); int flushroutes_fib_nl(int fib, int af); void monitor_nl(int fib); struct nl_helper; struct snl_msg_info; static void print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *dst); static void print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo); #define s6_addr32 __u6_addr.__u6_addr32 #define bitcount32(x) __bitcount32((uint32_t)(x)) static int inet6_get_plen(const struct in6_addr *addr) { return (bitcount32(addr->s6_addr32[0]) + bitcount32(addr->s6_addr32[1]) + bitcount32(addr->s6_addr32[2]) + bitcount32(addr->s6_addr32[3])); } static void ip6_writemask(struct in6_addr *addr6, uint8_t mask) { uint32_t *cp; for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) *cp++ = 0xFFFFFFFF; if (mask > 0) *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); } static struct sockaddr * get_netmask(struct snl_state *ss, int family, int plen) { if (family == AF_INET) { if (plen == 32) return (NULL); struct sockaddr_in *sin = snl_allocz(ss, sizeof(*sin)); sin->sin_len = sizeof(*sin); sin->sin_family = family; sin->sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0); return (struct sockaddr *)sin; } else if (family == AF_INET6) { if (plen == 128) return (NULL); struct sockaddr_in6 *sin6 = snl_allocz(ss, sizeof(*sin6)); sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = family; ip6_writemask(&sin6->sin6_addr, plen); return (struct sockaddr *)sin6; } return (NULL); } static void nl_init_socket(struct snl_state *ss) { if (snl_init(ss, NETLINK_ROUTE)) return; if (modfind("netlink") == -1 && errno == ENOENT) { /* Try to load */ if (kldload("netlink") == -1) err(1, "netlink is not loaded and load attempt failed"); if (snl_init(ss, NETLINK_ROUTE)) return; } err(1, "unable to open netlink socket"); } struct nl_helper { struct snl_state ss_cmd; }; static void nl_helper_init(struct nl_helper *h) { nl_init_socket(&h->ss_cmd); } static void nl_helper_free(struct nl_helper *h) { snl_free(&h->ss_cmd); } static struct sockaddr * get_addr(struct sockaddr_storage *so, int rtm_addrs, int addr_type) { struct sockaddr *sa = NULL; if (rtm_addrs & (1 << addr_type)) sa = (struct sockaddr *)&so[addr_type]; return (sa); } static int rtmsg_nl_int(struct nl_helper *h, int cmd, int rtm_flags, int fib, int rtm_addrs, struct sockaddr_storage *so, struct rt_metrics *rt_metrics) { struct snl_state *ss = &h->ss_cmd; struct snl_writer nw; int nl_type = 0, nl_flags = 0; snl_init_writer(ss, &nw); switch (cmd) { case RTSOCK_RTM_ADD: nl_type = RTM_NEWROUTE; nl_flags = NLM_F_CREATE | NLM_F_APPEND; /* Do append by default */ break; case RTSOCK_RTM_CHANGE: nl_type = RTM_NEWROUTE; nl_flags = NLM_F_REPLACE; break; case RTSOCK_RTM_DELETE: nl_type = RTM_DELROUTE; break; case RTSOCK_RTM_GET: nl_type = RTM_GETROUTE; break; default: exit(1); } struct sockaddr *dst = get_addr(so, rtm_addrs, RTAX_DST); struct sockaddr *mask = get_addr(so, rtm_addrs, RTAX_NETMASK); struct sockaddr *gw = get_addr(so, rtm_addrs, RTAX_GATEWAY); if (dst == NULL) return (EINVAL); struct nlmsghdr *hdr = snl_create_msg_request(&nw, nl_type); hdr->nlmsg_flags |= nl_flags; int plen = 0; int rtm_type = RTN_UNICAST; switch (dst->sa_family) { case AF_INET: { struct sockaddr_in *mask4 = (struct sockaddr_in *)mask; if ((rtm_flags & RTF_HOST) == 0 && mask4 != NULL) plen = bitcount32(mask4->sin_addr.s_addr); else plen = 32; break; } case AF_INET6: { struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask; if ((rtm_flags & RTF_HOST) == 0 && mask6 != NULL) plen = inet6_get_plen(&mask6->sin6_addr); else plen = 128; break; } default: return (ENOTSUP); } if (rtm_flags & RTF_REJECT) rtm_type = RTN_PROHIBIT; else if (rtm_flags & RTF_BLACKHOLE) rtm_type = RTN_BLACKHOLE; struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); rtm->rtm_family = dst->sa_family; rtm->rtm_protocol = RTPROT_STATIC; rtm->rtm_type = rtm_type; rtm->rtm_dst_len = plen; /* Request exact prefix match if mask is set */ if ((cmd == RTSOCK_RTM_GET) && (mask != NULL)) rtm->rtm_flags = RTM_F_PREFIX; snl_add_msg_attr_ip(&nw, RTA_DST, dst); snl_add_msg_attr_u32(&nw, RTA_TABLE, fib); uint32_t rta_oif = 0; if (gw != NULL) { if (rtm_flags & RTF_GATEWAY) { if (gw->sa_family == dst->sa_family) snl_add_msg_attr_ip(&nw, RTA_GATEWAY, gw); else snl_add_msg_attr_ipvia(&nw, RTA_VIA, gw); if (gw->sa_family == AF_INET6) { struct sockaddr_in6 *gw6 = (struct sockaddr_in6 *)gw; if (IN6_IS_ADDR_LINKLOCAL(&gw6->sin6_addr)) rta_oif = gw6->sin6_scope_id; } } else { /* Should be AF_LINK */ struct sockaddr_dl *sdl = (struct sockaddr_dl *)gw; if (sdl->sdl_index != 0) rta_oif = sdl->sdl_index; } } if (dst->sa_family == AF_INET6 && rta_oif == 0) { struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst; if (IN6_IS_ADDR_LINKLOCAL(&dst6->sin6_addr)) rta_oif = dst6->sin6_scope_id; } if (rta_oif != 0) snl_add_msg_attr_u32(&nw, RTA_OIF, rta_oif); if (rtm_flags != 0) snl_add_msg_attr_u32(&nw, NL_RTA_RTFLAGS, rtm_flags); if (rt_metrics->rmx_mtu > 0) { int off = snl_add_msg_attr_nested(&nw, RTA_METRICS); snl_add_msg_attr_u32(&nw, RTAX_MTU, rt_metrics->rmx_mtu); snl_end_attr_nested(&nw, off); } if (rt_metrics->rmx_weight > 0) snl_add_msg_attr_u32(&nw, NL_RTA_WEIGHT, rt_metrics->rmx_weight); - if (snl_finalize_msg(&nw) && snl_send_message(ss, hdr)) { + if ((hdr = snl_finalize_msg(&nw)) && snl_send_message(ss, hdr)) { struct snl_errmsg_data e = {}; hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (nl_type == NL_RTM_GETROUTE) { if (hdr->nlmsg_type == NL_RTM_NEWROUTE) print_getmsg(h, hdr, dst); else { snl_parse_errmsg(ss, hdr, &e); if (e.error == ESRCH) warn("route has not been found"); else warn("message indicates error %d", e.error); } return (0); } if (snl_parse_errmsg(ss, hdr, &e)) return (e.error); } return (EINVAL); } int rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs, struct sockaddr_storage *so, struct rt_metrics *rt_metrics) { struct nl_helper h = {}; nl_helper_init(&h); int error = rtmsg_nl_int(&h, cmd, rtm_flags, fib, rtm_addrs, so, rt_metrics); nl_helper_free(&h); return (error); } static void get_ifdata(struct nl_helper *h, uint32_t ifindex, struct snl_parsed_link_simple *link) { struct snl_state *ss = &h->ss_cmd; struct snl_writer nw; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK); struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg); if (ifmsg != NULL) ifmsg->ifi_index = ifindex; - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return; hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr != NULL && hdr->nlmsg_type == RTM_NEWLINK) { snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link); } if (link->ifla_ifname == NULL) { char ifname[16]; snprintf(ifname, sizeof(ifname), "if#%u", ifindex); int len = strlen(ifname); char *buf = snl_allocz(ss, len + 1); strlcpy(buf, ifname, len + 1); link->ifla_ifname = buf; } } static void print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *dst) { struct snl_state *ss = &h->ss_cmd; struct timespec ts; struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT }; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) return; struct snl_parsed_link_simple link = {}; get_ifdata(h, r.rta_oif, &link); if (r.rtax_mtu == 0) r.rtax_mtu = link.ifla_mtu; r.rta_rtflags |= (RTF_UP | RTF_DONE); (void)printf(" route to: %s\n", routename(dst)); if (r.rta_dst) (void)printf("destination: %s\n", routename(r.rta_dst)); struct sockaddr *mask = get_netmask(ss, r.rtm_family, r.rtm_dst_len); if (mask) (void)printf(" mask: %s\n", routename(mask)); if (r.rta_gw && (r.rta_rtflags & RTF_GATEWAY)) (void)printf(" gateway: %s\n", routename(r.rta_gw)); (void)printf(" fib: %u\n", (unsigned int)r.rta_table); if (link.ifla_ifname) (void)printf(" interface: %s\n", link.ifla_ifname); (void)printf(" flags: "); printb(r.rta_rtflags, routeflags); struct rt_metrics rmx = { .rmx_mtu = r.rtax_mtu, .rmx_weight = r.rtax_weight, .rmx_expire = r.rta_expire, }; printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe", "sendpipe", "ssthresh", "rtt,msec", "mtu ", "weight", "expire"); printf("%8lu ", rmx.rmx_recvpipe); printf("%8lu ", rmx.rmx_sendpipe); printf("%8lu ", rmx.rmx_ssthresh); printf("%8lu ", 0UL); printf("%8lu ", rmx.rmx_mtu); printf("%8lu ", rmx.rmx_weight); if (rmx.rmx_expire > 0) clock_gettime(CLOCK_REALTIME_FAST, &ts); else ts.tv_sec = 0; printf("%8ld \n", (long)(rmx.rmx_expire - ts.tv_sec)); } static void print_prefix(struct nl_helper *h, char *buf, int bufsize, struct sockaddr *sa, int plen) { int sz = 0; if (sa == NULL) { snprintf(buf, bufsize, ""); return; } switch (sa->sa_family) { case AF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *)sa; char abuf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf)); sz = snprintf(buf, bufsize, "%s", abuf); break; } case AF_INET6: { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; char abuf[INET6_ADDRSTRLEN]; char *ifname = NULL; inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf)); if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { struct snl_parsed_link_simple link = {}; if (sin6->sin6_scope_id != 0) { get_ifdata(h, sin6->sin6_scope_id, &link); ifname = link.ifla_ifname; } } if (ifname == NULL) sz = snprintf(buf, bufsize, "%s", abuf); else sz = snprintf(buf, bufsize, "%s%%%s", abuf, ifname); break; } default: snprintf(buf, bufsize, "unknown_af#%d", sa->sa_family); plen = -1; } if (plen >= 0) snprintf(buf + sz, bufsize - sz, "/%d", plen); } static int print_line_prefix(struct nlmsghdr *hdr, struct snl_msg_info *cinfo, const char *cmd, const char *name) { struct timespec tp; struct tm tm; char buf[32]; clock_gettime(CLOCK_REALTIME, &tp); localtime_r(&tp.tv_sec, &tm); strftime(buf, sizeof(buf), "%T", &tm); int len = printf("%s.%03ld PID %4u %s %s ", buf, tp.tv_nsec / 1000000, cinfo->process_id, cmd, name); return (len); } static const char * get_action_name(struct nlmsghdr *hdr, int new_cmd) { if (hdr->nlmsg_type == new_cmd) { //return ((hdr->nlmsg_flags & NLM_F_REPLACE) ? "replace" : "add"); return ("add/repl"); } else return ("delete"); } static void print_nlmsg_route_nhop(struct nl_helper *h, struct snl_parsed_route *r, struct rta_mpath_nh *nh, bool first) { // gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0 if (nh->gw != NULL) { char gwbuf[128]; print_prefix(h, gwbuf, sizeof(gwbuf), nh->gw, -1); printf("gw %s ", gwbuf); } if (nh->ifindex != 0) { struct snl_parsed_link_simple link = {}; get_ifdata(h, nh->ifindex, &link); if (nh->rtax_mtu == 0) nh->rtax_mtu = link.ifla_mtu; printf("iface %s ", link.ifla_ifname); if (nh->rtax_mtu != 0) printf("mtu %d ", nh->rtax_mtu); } if (first) { switch (r->rtm_family) { case AF_INET: printf("table inet.%d", r->rta_table); break; case AF_INET6: printf("table inet6.%d", r->rta_table); break; } } printf("\n"); } static void print_nlmsg_route(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo) { struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT }; struct snl_state *ss = &h->ss_cmd; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) return; // 20:19:41.333 add route 10.0.0.0/24 gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0 const char *cmd = get_action_name(hdr, RTM_NEWROUTE); int len = print_line_prefix(hdr, cinfo, cmd, "route"); char buf[128]; print_prefix(h, buf, sizeof(buf), r.rta_dst, r.rtm_dst_len); len += strlen(buf) + 1; printf("%s ", buf); switch (r.rtm_type) { case RTN_BLACKHOLE: printf("blackhole\n"); return; case RTN_UNREACHABLE: printf("unreach(reject)\n"); return; case RTN_PROHIBIT: printf("prohibit(reject)\n"); return; } if (r.rta_multipath.num_nhops != 0) { bool first = true; memset(buf, ' ', sizeof(buf)); buf[len] = '\0'; for (uint32_t i = 0; i < r.rta_multipath.num_nhops; i++) { struct rta_mpath_nh *nh = r.rta_multipath.nhops[i]; if (!first) printf("%s", buf); print_nlmsg_route_nhop(h, &r, nh, first); first = false; } } else { struct rta_mpath_nh nh = { .gw = r.rta_gw, .ifindex = r.rta_oif, .rtax_mtu = r.rtax_mtu, }; print_nlmsg_route_nhop(h, &r, &nh, true); } } static const char *operstate[] = { "UNKNOWN", /* 0, IF_OPER_UNKNOWN */ "NOTPRESENT", /* 1, IF_OPER_NOTPRESENT */ "DOWN", /* 2, IF_OPER_DOWN */ "LLDOWN", /* 3, IF_OPER_LOWERLAYERDOWN */ "TESTING", /* 4, IF_OPER_TESTING */ "DORMANT", /* 5, IF_OPER_DORMANT */ "UP", /* 6, IF_OPER_UP */ }; static void print_nlmsg_link(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo) { struct snl_parsed_link l = {}; struct snl_state *ss = &h->ss_cmd; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &l)) return; // 20:19:41.333 add iface#3 vtnet0 admin UP oper UP mtu 1500 table inet.0 const char *cmd = get_action_name(hdr, RTM_NEWLINK); print_line_prefix(hdr, cinfo, cmd, "iface"); printf("iface#%u %s ", l.ifi_index, l.ifla_ifname); printf("admin %s ", (l.ifi_flags & IFF_UP) ? "UP" : "DOWN"); if (l.ifla_operstate < NL_ARRAY_LEN(operstate)) printf("oper %s ", operstate[l.ifla_operstate]); if (l.ifla_mtu > 0) printf("mtu %u ", l.ifla_mtu); printf("\n"); } static void print_nlmsg_addr(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo) { struct snl_parsed_addr attrs = {}; struct snl_state *ss = &h->ss_cmd; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs)) return; // add addr 192.168.1.1/24 iface vtnet0 const char *cmd = get_action_name(hdr, RTM_NEWADDR); print_line_prefix(hdr, cinfo, cmd, "addr"); char buf[128]; struct sockaddr *addr = attrs.ifa_local ? attrs.ifa_local : attrs.ifa_address; print_prefix(h, buf, sizeof(buf), addr, attrs.ifa_prefixlen); printf("%s ", buf); struct snl_parsed_link_simple link = {}; get_ifdata(h, attrs.ifa_index, &link); if (link.ifi_flags & IFF_POINTOPOINT) { char buf[64]; print_prefix(h, buf, sizeof(buf), attrs.ifa_address, -1); printf("-> %s ", buf); } printf("iface %s ", link.ifla_ifname); printf("\n"); } static const char *nudstate[] = { "INCOMPLETE", /* 0x01(0) */ "REACHABLE", /* 0x02(1) */ "STALE", /* 0x04(2) */ "DELAY", /* 0x08(3) */ "PROBE", /* 0x10(4) */ "FAILED", /* 0x20(5) */ }; #define NUD_INCOMPLETE 0x01 /* No lladdr, address resolution in progress */ #define NUD_REACHABLE 0x02 /* reachable & recently resolved */ #define NUD_STALE 0x04 /* has lladdr but it's stale */ #define NUD_DELAY 0x08 /* has lladdr, is stale, probes delayed */ #define NUD_PROBE 0x10 /* has lladdr, is stale, probes sent */ #define NUD_FAILED 0x20 /* unused */ static void print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo) { struct snl_parsed_neigh attrs = {}; struct snl_state *ss = &h->ss_cmd; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_neigh_parser, &attrs)) return; // add addr 192.168.1.1 state %s lladdr %s iface vtnet0 const char *cmd = get_action_name(hdr, RTM_NEWNEIGH); print_line_prefix(hdr, cinfo, cmd, "neigh"); char buf[128]; print_prefix(h, buf, sizeof(buf), attrs.nda_dst, -1); printf("%s ", buf); struct snl_parsed_link_simple link = {}; get_ifdata(h, attrs.nda_ifindex, &link); for (unsigned int i = 0; i < NL_ARRAY_LEN(nudstate); i++) { if ((1 << i) & attrs.ndm_state) { printf("state %s ", nudstate[i]); break; } } if (attrs.nda_lladdr != NULL) { int if_type = link.ifi_type; if ((if_type == IFT_ETHER || if_type == IFT_L2VLAN || if_type == IFT_BRIDGE) && NLA_DATA_LEN(attrs.nda_lladdr) == ETHER_ADDR_LEN) { struct ether_addr *ll; ll = (struct ether_addr *)NLA_DATA(attrs.nda_lladdr); printf("lladdr %s ", ether_ntoa(ll)); } else { struct sockaddr_dl sdl = { .sdl_len = sizeof(sdl), .sdl_family = AF_LINK, .sdl_index = attrs.nda_ifindex, .sdl_type = if_type, .sdl_alen = NLA_DATA_LEN(attrs.nda_lladdr), }; if (sdl.sdl_alen < sizeof(sdl.sdl_data)) { void *ll = NLA_DATA(attrs.nda_lladdr); memcpy(sdl.sdl_data, ll, sdl.sdl_alen); printf("lladdr %s ", link_ntoa(&sdl)); } } } if (link.ifla_ifname != NULL) printf("iface %s ", link.ifla_ifname); printf("\n"); } static void print_nlmsg_generic(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo) { const char *cmd = get_action_name(hdr, 0); print_line_prefix(hdr, cinfo, cmd, "unknown message"); printf(" type %u\n", hdr->nlmsg_type); } static void print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo) { switch (hdr->nlmsg_type) { case RTM_NEWLINK: case RTM_DELLINK: print_nlmsg_link(h, hdr, cinfo); break; case RTM_NEWADDR: case RTM_DELADDR: print_nlmsg_addr(h, hdr, cinfo); break; case RTM_NEWROUTE: case RTM_DELROUTE: print_nlmsg_route(h, hdr, cinfo); break; case RTM_NEWNEIGH: case RTM_DELNEIGH: print_nlmsg_neigh(h, hdr, cinfo); break; default: print_nlmsg_generic(h, hdr, cinfo); } snl_clear_lb(&h->ss_cmd); } void monitor_nl(int fib) { struct snl_state ss_event = {}; struct nl_helper h; nl_init_socket(&ss_event); nl_helper_init(&h); int groups[] = { RTNLGRP_LINK, RTNLGRP_NEIGH, RTNLGRP_NEXTHOP, #ifdef INET RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV4_ROUTE, #endif #ifdef INET6 RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE, #endif }; int optval = 1; socklen_t optlen = sizeof(optval); setsockopt(ss_event.fd, SOL_NETLINK, NETLINK_MSG_INFO, &optval, optlen); for (unsigned int i = 0; i < NL_ARRAY_LEN(groups); i++) { int error; int optval = groups[i]; socklen_t optlen = sizeof(optval); error = setsockopt(ss_event.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &optval, optlen); if (error != 0) warn("Unable to subscribe to group %d", optval); } struct snl_msg_info attrs = {}; struct nlmsghdr *hdr; while ((hdr = snl_read_message_dbg(&ss_event, &attrs)) != NULL) { print_nlmsg(&h, hdr, &attrs); snl_clear_lb(&h.ss_cmd); snl_clear_lb(&ss_event); } snl_free(&ss_event); nl_helper_free(&h); exit(0); } static void print_flushed_route(struct snl_parsed_route *r, struct sockaddr *gw) { struct sockaddr *sa = r->rta_dst; printf("%-20.20s ", r->rta_rtflags & RTF_HOST ? routename(sa) : netname(sa)); sa = gw; printf("%-20.20s ", routename(sa)); printf("-fib %-3d ", r->rta_table); printf("done\n"); } static int flushroute_one(struct nl_helper *h, struct snl_parsed_route *r) { struct snl_state *ss = &h->ss_cmd; struct snl_errmsg_data e = {}; struct snl_writer nw; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_DELROUTE); struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); rtm->rtm_family = r->rtm_family; rtm->rtm_dst_len = r->rtm_dst_len; snl_add_msg_attr_u32(&nw, RTA_TABLE, r->rta_table); snl_add_msg_attr_ip(&nw, RTA_DST, r->rta_dst); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (ENOMEM); if (!snl_read_reply_code(ss, hdr->nlmsg_seq, &e)) { return (e.error); if (e.error == EPERM) errc(1, e.error, "RTM_DELROUTE failed:"); else warnc(e.error, "RTM_DELROUTE failed:"); return (true); }; if (verbose) { struct snl_msg_info attrs = {}; print_nlmsg(h, hdr, &attrs); } else { if (r->rta_multipath.num_nhops != 0) { for (uint32_t i = 0; i < r->rta_multipath.num_nhops; i++) { struct rta_mpath_nh *nh = r->rta_multipath.nhops[i]; print_flushed_route(r, nh->gw); } } else print_flushed_route(r, r->rta_gw); } return (0); } int flushroutes_fib_nl(int fib, int af) { struct snl_state ss = {}; struct snl_writer nw; struct nl_helper h = {}; nl_init_socket(&ss); snl_init_writer(&ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETROUTE); hdr->nlmsg_flags |= NLM_F_DUMP; struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); rtm->rtm_family = af; snl_add_msg_attr_u32(&nw, RTA_TABLE, fib); - if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { snl_free(&ss); return (EINVAL); } struct snl_errmsg_data e = {}; uint32_t nlm_seq = hdr->nlmsg_seq; nl_helper_init(&h); while ((hdr = snl_read_reply_multi(&ss, nlm_seq, &e)) != NULL) { struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT }; int error; if (!snl_parse_nlmsg(&ss, hdr, &snl_rtm_route_parser, &r)) continue; if (verbose) { struct snl_msg_info attrs = {}; print_nlmsg(&h, hdr, &attrs); } if (r.rta_table != (uint32_t)fib || r.rtm_family != af) continue; if ((r.rta_rtflags & RTF_GATEWAY) == 0) continue; if (debugonly) continue; if ((error = flushroute_one(&h, &r)) != 0) { if (error == EPERM) errc(1, error, "RTM_DELROUTE failed:"); else warnc(error, "RTM_DELROUTE failed:"); } snl_clear_lb(&h.ss_cmd); } snl_free(&ss); nl_helper_free(&h); return (e.error); } diff --git a/sys/netlink/netlink_snl_generic.h b/sys/netlink/netlink_snl_generic.h index 1324cf3da17a..0a2913c9155e 100644 --- a/sys/netlink/netlink_snl_generic.h +++ b/sys/netlink/netlink_snl_generic.h @@ -1,134 +1,134 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Alexander V. Chernikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _NETLINK_NETLINK_SNL_GENERIC_H_ #define _NETLINK_NETLINK_SNL_GENERIC_H_ #include #include #include /* Genetlink helpers */ static inline struct nlmsghdr * snl_create_genl_msg_request(struct snl_writer *nw, int genl_family, uint8_t genl_cmd) { assert(nw->hdr == NULL); struct nlmsghdr *hdr = snl_reserve_msg_object(nw, struct nlmsghdr); hdr->nlmsg_type = genl_family; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nw->hdr = hdr; struct genlmsghdr *ghdr = snl_reserve_msg_object(nw, struct genlmsghdr); ghdr->cmd = genl_cmd; return (hdr); } static struct snl_field_parser snl_fp_genl[] = {}; #define SNL_DECLARE_GENL_PARSER(_name, _np) SNL_DECLARE_PARSER(_name,\ struct genlmsghdr, snl_fp_genl, _np) struct snl_genl_ctrl_mcast_group { uint32_t mcast_grp_id; char *mcast_grp_name; }; struct snl_genl_ctrl_mcast_groups { uint32_t num_groups; struct snl_genl_ctrl_mcast_group **groups; }; #define _OUT(_field) offsetof(struct snl_genl_ctrl_mcast_group, _field) static struct snl_attr_parser _nla_p_getmc[] = { { .type = CTRL_ATTR_MCAST_GRP_NAME, .off = _OUT(mcast_grp_name), .cb = snl_attr_get_string }, { .type = CTRL_ATTR_MCAST_GRP_ID, .off = _OUT(mcast_grp_id), .cb = snl_attr_get_uint32 }, }; #undef _OUT SNL_DECLARE_ATTR_PARSER_EXT(_genl_ctrl_mc_parser, sizeof(struct snl_genl_ctrl_mcast_group), _nla_p_getmc, NULL); struct _getfamily_attrs { uint16_t family_id; char *family_name; struct snl_genl_ctrl_mcast_groups mcast_groups; }; #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct _getfamily_attrs, _field) static struct snl_attr_parser _nla_p_getfam[] = { { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(family_id), .cb = snl_attr_get_uint16 }, { .type = CTRL_ATTR_FAMILY_NAME, .off = _OUT(family_name), .cb = snl_attr_get_string }, { .type = CTRL_ATTR_MCAST_GROUPS, .off = _OUT(mcast_groups), .cb = snl_attr_get_parray, .arg = &_genl_ctrl_mc_parser, }, }; #undef _IN #undef _OUT SNL_DECLARE_GENL_PARSER(_genl_ctrl_getfam_parser, _nla_p_getfam); static bool snl_get_genl_family_info(struct snl_state *ss, const char *family_name, struct _getfamily_attrs *attrs) { struct snl_writer nw; struct nlmsghdr *hdr; memset(attrs, 0, sizeof(*attrs)); snl_init_writer(ss, &nw); hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL, CTRL_CMD_GETFAMILY); snl_add_msg_attr_string(&nw, CTRL_ATTR_FAMILY_NAME, family_name); - if (snl_finalize_msg(&nw) == NULL || !snl_send_message(ss, hdr)) + if ((hdr = snl_finalize_msg(&nw)) == NULL || !snl_send_message(ss, hdr)) return (false); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) { if (snl_parse_nlmsg(ss, hdr, &_genl_ctrl_getfam_parser, attrs)) return (true); } return (false); } static inline uint16_t snl_get_genl_family(struct snl_state *ss, const char *family_name) { struct _getfamily_attrs attrs = {}; snl_get_genl_family_info(ss, family_name, &attrs); return (attrs.family_id); } static const struct snl_hdr_parser *snl_all_genl_parsers[] = { &_genl_ctrl_getfam_parser, &_genl_ctrl_mc_parser, }; #endif diff --git a/tests/sys/netlink/test_snl_generic.c b/tests/sys/netlink/test_snl_generic.c index f3c11daf19e1..d84d3f88f487 100644 --- a/tests/sys/netlink/test_snl_generic.c +++ b/tests/sys/netlink/test_snl_generic.c @@ -1,116 +1,116 @@ #include #include #include #include #include #include #include "netlink/netlink_snl.h" #include "netlink/netlink_snl_generic.h" #include static void require_netlink(void) { if (modfind("netlink") == -1) atf_tc_skip("netlink module not loaded"); } ATF_TC(snl_verify_genl_parsers); ATF_TC_HEAD(snl_verify_genl_parsers, tc) { atf_tc_set_md_var(tc, "descr", "Tests snl(3) generic parsers are correct"); } ATF_TC_BODY(snl_verify_genl_parsers, tc) { SNL_VERIFY_PARSERS(snl_all_genl_parsers); } ATF_TC(test_snl_get_genl_family_success); ATF_TC_HEAD(test_snl_get_genl_family_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests successfull resolution of the 'nlctrl' family"); } ATF_TC_BODY(test_snl_get_genl_family_success, tc) { struct snl_state ss; require_netlink(); if (!snl_init(&ss, NETLINK_GENERIC)) atf_tc_fail("snl_init() failed"); ATF_CHECK_EQ(snl_get_genl_family(&ss, "nlctrl"), GENL_ID_CTRL); } ATF_TC(test_snl_get_genl_family_failure); ATF_TC_HEAD(test_snl_get_genl_family_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests unsuccessfull resolution of 'no-such-family' family"); } ATF_TC_BODY(test_snl_get_genl_family_failure, tc) { struct snl_state ss; require_netlink(); if (!snl_init(&ss, NETLINK_GENERIC)) atf_tc_fail("snl_init() failed"); ATF_CHECK_EQ(snl_get_genl_family(&ss, "no-such-family"), 0); } ATF_TC(test_snl_get_genl_family_groups); ATF_TC_HEAD(test_snl_get_genl_family_groups, tc) { atf_tc_set_md_var(tc, "descr", "Tests getting 'nlctrl' groups"); } ATF_TC_BODY(test_snl_get_genl_family_groups, tc) { struct snl_state ss; struct snl_writer nw; struct nlmsghdr *hdr; require_netlink(); if (!snl_init(&ss, NETLINK_GENERIC)) atf_tc_fail("snl_init() failed"); snl_init_writer(&ss, &nw); hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL, CTRL_CMD_GETFAMILY); snl_add_msg_attr_string(&nw, CTRL_ATTR_FAMILY_NAME, "nlctrl"); - snl_finalize_msg(&nw); + hdr = snl_finalize_msg(&nw); snl_send_message(&ss, hdr); hdr = snl_read_reply(&ss, hdr->nlmsg_seq); ATF_CHECK(hdr != NULL); ATF_CHECK(hdr->nlmsg_type != NLMSG_ERROR); struct _getfamily_attrs attrs = {}; ATF_CHECK(snl_parse_nlmsg(&ss, hdr, &_genl_ctrl_getfam_parser, &attrs)); ATF_CHECK_EQ(attrs.mcast_groups.num_groups, 1); struct snl_genl_ctrl_mcast_group *group = attrs.mcast_groups.groups[0]; ATF_CHECK(group->mcast_grp_id > 0); ATF_CHECK(!strcmp(group->mcast_grp_name, "notify")); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, snl_verify_genl_parsers); ATF_TP_ADD_TC(tp, test_snl_get_genl_family_success); ATF_TP_ADD_TC(tp, test_snl_get_genl_family_failure); ATF_TP_ADD_TC(tp, test_snl_get_genl_family_groups); return (atf_no_error()); } diff --git a/usr.sbin/arp/arp_netlink.c b/usr.sbin/arp/arp_netlink.c index 40b5367f330d..d78f380af04b 100644 --- a/usr.sbin/arp/arp_netlink.c +++ b/usr.sbin/arp/arp_netlink.c @@ -1,442 +1,442 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arp.h" #define RTF_ANNOUNCE RTF_PROTO2 static void nl_init_socket(struct snl_state *ss) { if (snl_init(ss, NETLINK_ROUTE)) return; if (modfind("netlink") == -1 && errno == ENOENT) { /* Try to load */ if (kldload("netlink") == -1) err(1, "netlink is not loaded and load attempt failed"); if (snl_init(ss, NETLINK_ROUTE)) return; } err(1, "unable to open netlink socket"); } static bool get_link_info(struct snl_state *ss, uint32_t ifindex, struct snl_parsed_link_simple *link) { struct snl_writer nw; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg); if (ifmsg != NULL) ifmsg->ifi_index = ifindex; - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (false); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK) return (false); if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link)) return (false); return (true); } static bool has_l2(struct snl_state *ss, uint32_t ifindex) { struct snl_parsed_link_simple link = {}; if (!get_link_info(ss, ifindex, &link)) return (false); return (valid_type(link.ifi_type) != 0); } static uint32_t get_myfib(void) { uint32_t fibnum = 0; size_t len = sizeof(fibnum); sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0); return (fibnum); } static int guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr) { struct snl_writer nw; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE); struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); rtm->rtm_family = AF_INET; struct sockaddr_in dst = { .sin_family = AF_INET, .sin_addr = addr }; snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)&dst); snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (0); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr->nlmsg_type != NL_RTM_NEWROUTE) { /* No route found, unable to guess ifindex */ return (0); } struct snl_parsed_route r = {}; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) return (0); if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY)) return (0); /* Check if the interface is of supported type */ if (has_l2(ss, r.rta_oif)) return (r.rta_oif); /* Check the case when we matched the loopback route for P2P */ snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP); snl_reserve_msg_object(&nw, struct nhmsg); int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD); snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id); snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET); snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum); snl_end_attr_nested(&nw, off); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (0); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) { /* No nexthop found, unable to guess ifindex */ return (0); } struct snl_parsed_nhop nh = {}; if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh)) return (0); return (nh.nhaf_aif); } static uint32_t fix_ifindex(struct snl_state *ss, uint32_t ifindex, struct in_addr addr) { if (ifindex == 0) ifindex = guess_ifindex(ss, get_myfib(), addr); return (ifindex); } static void print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link) { const char *host; struct hostent *hp; struct sockaddr_in *addr = (struct sockaddr_in *)neigh->nda_dst; xo_open_instance("arp-cache"); if (!opts.nflag) hp = gethostbyaddr((caddr_t)&(addr->sin_addr), sizeof(addr->sin_addr), AF_INET); else hp = 0; if (hp) host = hp->h_name; else { host = "?"; if (h_errno == TRY_AGAIN) opts.nflag = true; } xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host, inet_ntoa(addr->sin_addr)); if (neigh->nda_lladdr != NULL) { struct sockaddr_dl sdl = { .sdl_family = AF_LINK, .sdl_type = link->ifi_type, .sdl_len = sizeof(struct sockaddr_dl), .sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr), }; memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen); if ((sdl.sdl_type == IFT_ETHER || sdl.sdl_type == IFT_L2VLAN || sdl.sdl_type == IFT_BRIDGE) && sdl.sdl_alen == ETHER_ADDR_LEN) xo_emit("{:mac-address/%s}", ether_ntoa((struct ether_addr *)LLADDR(&sdl))); else { xo_emit("{:mac-address/%s}", link_ntoa(&sdl)); } } else xo_emit("{d:/(incomplete)}{en:incomplete/true}"); xo_emit(" on {:interface/%s}", link->ifla_ifname); if (neigh->ndaf_next_ts == 0) xo_emit("{d:/ permanent}{en:permanent/true}"); else { time_t expire_time; struct timeval now; gettimeofday(&now, 0); if ((expire_time = neigh->ndaf_next_ts - now.tv_sec) > 0) xo_emit(" expires in {:expires/%d} seconds", (int)expire_time); else xo_emit("{d:/ expired}{en:expired/true}"); } if (neigh->ndm_flags & NTF_PROXY) xo_emit("{d:/ published}{en:published/true}"); switch(link->ifi_type) { case IFT_ETHER: xo_emit(" [{:type/ethernet}]"); break; case IFT_FDDI: xo_emit(" [{:type/fddi}]"); break; case IFT_ATM: xo_emit(" [{:type/atm}]"); break; case IFT_L2VLAN: xo_emit(" [{:type/vlan}]"); break; case IFT_IEEE1394: xo_emit(" [{:type/firewire}]"); break; case IFT_BRIDGE: xo_emit(" [{:type/bridge}]"); break; case IFT_INFINIBAND: xo_emit(" [{:type/infiniband}]"); break; default: break; } xo_emit("\n"); xo_close_instance("arp-cache"); } int print_entries_nl(uint32_t ifindex, struct in_addr addr) { struct snl_state ss_req = {}, ss_cmd = {}; struct snl_parsed_link_simple link = {}; struct snl_writer nw; nl_init_socket(&ss_req); snl_init_writer(&ss_req, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH); struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); if (ndmsg != NULL) { ndmsg->ndm_family = AF_INET; /* let kernel filter results by interface if provided */ ndmsg->ndm_ifindex = ifindex; } - if (!snl_finalize_msg(&nw) || !snl_send_message(&ss_req, hdr)) { + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) { snl_free(&ss_req); return (0); } uint32_t nlmsg_seq = hdr->nlmsg_seq; struct snl_errmsg_data e = {}; int count = 0; nl_init_socket(&ss_cmd); while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) { struct snl_parsed_neigh neigh = {}; struct sockaddr_in *neighaddr; if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh)) continue; if (neigh.nda_ifindex != link.ifi_index) { snl_clear_lb(&ss_cmd); memset(&link, 0, sizeof(link)); if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link)) continue; } /* filter results based on host if provided */ neighaddr = (struct sockaddr_in *)neigh.nda_dst; if (addr.s_addr && (addr.s_addr != neighaddr->sin_addr.s_addr)) continue; print_entry(&neigh, &link); count++; snl_clear_lb(&ss_req); } snl_free(&ss_req); snl_free(&ss_cmd); return (count); } int delete_nl(uint32_t ifindex, char *host) { struct snl_state ss = {}; struct snl_writer nw; struct sockaddr_in *dst; dst = getaddr(host); if (dst == NULL) return (1); nl_init_socket(&ss); ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr); if (ifindex == 0) { xo_warnx("delete: cannot locate %s", host); snl_free(&ss); return (0); } snl_init_writer(&ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH); struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); if (ndmsg != NULL) { ndmsg->ndm_family = AF_INET; ndmsg->ndm_ifindex = ifindex; } snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); - if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { snl_free(&ss); return (1); } struct snl_errmsg_data e = {}; snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); if (e.error != 0) { if (e.error_str != NULL) xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str); else xo_warnx("delete %s: %s", host, strerror(e.error)); } else printf("%s (%s) deleted\n", host, inet_ntoa(dst->sin_addr)); snl_free(&ss); return (e.error != 0); } int set_nl(uint32_t ifindex, struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host) { struct snl_state ss = {}; struct snl_writer nw; nl_init_socket(&ss); ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr); if (ifindex == 0) { xo_warnx("delete: cannot locate %s", host); snl_free(&ss); return (0); } if (opts.expire_time != 0) opts.flags &= ~RTF_STATIC; snl_init_writer(&ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH); hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); if (ndmsg != NULL) { uint8_t nl_flags = 0; ndmsg->ndm_family = AF_INET; ndmsg->ndm_ifindex = ifindex; ndmsg->ndm_state = (opts.flags & RTF_STATIC) ? NUD_PERMANENT : NUD_NONE; if (opts.flags & RTF_ANNOUNCE) nl_flags |= NTF_PROXY; if (opts.flags & RTF_STATIC) nl_flags |= NTF_STICKY; ndmsg->ndm_flags = nl_flags; } snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl)); if (opts.expire_time != 0) { struct timeval now; gettimeofday(&now, 0); int off = snl_add_msg_attr_nested(&nw, NDA_FREEBSD); snl_add_msg_attr_u32(&nw, NDAF_NEXT_STATE_TS, now.tv_sec + opts.expire_time); snl_end_attr_nested(&nw, off); } - if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { snl_free(&ss); return (1); } struct snl_errmsg_data e = {}; snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); if (e.error != 0) { if (e.error_str != NULL) xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str); else xo_warnx("set %s: %s", host, strerror(e.error)); } snl_free(&ss); return (e.error != 0); } diff --git a/usr.sbin/ndp/ndp_netlink.c b/usr.sbin/ndp/ndp_netlink.c index 954d16995b5a..79bdec2356d0 100644 --- a/usr.sbin/ndp/ndp_netlink.c +++ b/usr.sbin/ndp/ndp_netlink.c @@ -1,513 +1,513 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ndp.h" #define RTF_ANNOUNCE RTF_PROTO2 static void nl_init_socket(struct snl_state *ss) { if (snl_init(ss, NETLINK_ROUTE)) return; if (modfind("netlink") == -1 && errno == ENOENT) { /* Try to load */ if (kldload("netlink") == -1) err(1, "netlink is not loaded and load attempt failed"); if (snl_init(ss, NETLINK_ROUTE)) return; } err(1, "unable to open netlink socket"); } static bool get_link_info(struct snl_state *ss, uint32_t ifindex, struct snl_parsed_link_simple *link) { struct snl_writer nw; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg); if (ifmsg != NULL) ifmsg->ifi_index = ifindex; - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (false); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK) return (false); if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link)) return (false); return (true); } static bool has_l2(struct snl_state *ss, uint32_t ifindex) { struct snl_parsed_link_simple link = {}; if (!get_link_info(ss, ifindex, &link)) return (false); return (valid_type(link.ifi_type) != 0); } static uint32_t get_myfib(void) { uint32_t fibnum = 0; size_t len = sizeof(fibnum); sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0); return (fibnum); } static void ip6_writemask(struct in6_addr *addr6, uint8_t mask) { uint32_t *cp; for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) *cp++ = 0xFFFFFFFF; if (mask > 0) *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); } #define s6_addr32 __u6_addr.__u6_addr32 #define IN6_MASK_ADDR(a, m) do { \ (a)->s6_addr32[0] &= (m)->s6_addr32[0]; \ (a)->s6_addr32[1] &= (m)->s6_addr32[1]; \ (a)->s6_addr32[2] &= (m)->s6_addr32[2]; \ (a)->s6_addr32[3] &= (m)->s6_addr32[3]; \ } while (0) static int guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 *dst) { struct snl_writer nw; if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) return (dst->sin6_scope_id); else if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) return (0); snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE); struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); rtm->rtm_family = AF_INET6; snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)dst); snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (0); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr->nlmsg_type != NL_RTM_NEWROUTE) { /* No route found, unable to guess ifindex */ return (0); } struct snl_parsed_route r = {}; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) return (0); if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY)) return (0); /* Check if the interface is of supported type */ if (has_l2(ss, r.rta_oif)) return (r.rta_oif); /* Check the case when we matched the loopback route for P2P */ snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP); snl_reserve_msg_object(&nw, struct nhmsg); int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD); snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id); snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET); snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum); snl_end_attr_nested(&nw, off); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (0); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) { /* No nexthop found, unable to guess ifindex */ return (0); } struct snl_parsed_nhop nh = {}; if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh)) return (0); return (nh.nhaf_aif); } static uint32_t fix_ifindex(struct snl_state *ss, uint32_t ifindex, const struct sockaddr_in6 *sa) { if (ifindex == 0) ifindex = guess_ifindex(ss, get_myfib(), sa); return (ifindex); } static void print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link) { struct timeval now; char host_buf[NI_MAXHOST]; int addrwidth; int llwidth; int ifwidth; char *ifname; getnameinfo(neigh->nda_dst, sizeof(struct sockaddr_in6), host_buf, sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); gettimeofday(&now, 0); if (opts.tflag) ts_print(&now); struct sockaddr_dl sdl = { .sdl_family = AF_LINK, .sdl_type = link->ifi_type, .sdl_len = sizeof(struct sockaddr_dl), }; if (neigh->nda_lladdr) { sdl.sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr), memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen); } addrwidth = strlen(host_buf); if (addrwidth < W_ADDR) addrwidth = W_ADDR; llwidth = strlen(ether_str(&sdl)); if (W_ADDR + W_LL - addrwidth > llwidth) llwidth = W_ADDR + W_LL - addrwidth; ifname = link->ifla_ifname; ifwidth = strlen(ifname); if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; xo_open_instance("neighbor-cache"); /* Compose format string for libxo, as it doesn't support *.* */ char xobuf[200]; snprintf(xobuf, sizeof(xobuf), "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}", addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth); xo_emit(xobuf, host_buf, ether_str(&sdl), ifname); /* Print neighbor discovery specific information */ time_t expire = (time_t)neigh->ndaf_next_ts; int expire_in = expire - now.tv_sec; if (expire > now.tv_sec) xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in); else if (expire == 0) xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent"); else xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in); const char *lle_state = ""; switch (neigh->ndm_state) { case NUD_INCOMPLETE: lle_state = "I"; break; case NUD_REACHABLE: lle_state = "R"; break; case NUD_STALE: lle_state = "S"; break; case NUD_DELAY: lle_state = "D"; break; case NUD_PROBE: lle_state = "P"; break; case NUD_FAILED: lle_state = "F"; break; default: lle_state = "N"; break; } xo_emit(" {:neighbor-state/%s}", lle_state); bool isrouter = neigh->ndm_flags & NTF_ROUTER; /* * other flags. R: router, P: proxy, W: ?? */ char flgbuf[8]; snprintf(flgbuf, sizeof(flgbuf), "%s%s", isrouter ? "R" : "", (neigh->ndm_flags & NTF_PROXY) ? "p" : ""); xo_emit(" {:nd-flags/%s}", flgbuf); if (neigh->nda_probes != 0) xo_emit("{u:/ %d}", neigh->nda_probes); xo_emit("\n"); xo_close_instance("neighbor-cache"); } int print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag) { struct snl_state ss_req = {}, ss_cmd = {}; struct snl_parsed_link_simple link = {}; struct snl_writer nw; nl_init_socket(&ss_req); snl_init_writer(&ss_req, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH); struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); if (ndmsg != NULL) { ndmsg->ndm_family = AF_INET6; ndmsg->ndm_ifindex = ifindex; } - if (!snl_finalize_msg(&nw) || !snl_send_message(&ss_req, hdr)) { + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) { snl_free(&ss_req); return (0); } uint32_t nlmsg_seq = hdr->nlmsg_seq; struct snl_errmsg_data e = {}; int count = 0; nl_init_socket(&ss_cmd); /* Print header */ if (!opts.tflag && !cflag) { char xobuf[200]; snprintf(xobuf, sizeof(xobuf), "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n", W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF); xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags"); } xo_open_list("neighbor-cache"); while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) { struct snl_parsed_neigh neigh = {}; if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh)) continue; if (neigh.nda_ifindex != link.ifi_index) { snl_clear_lb(&ss_cmd); memset(&link, 0, sizeof(link)); if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link)) continue; } /* TODO: embed LL in the parser */ struct sockaddr_in6 *dst = (struct sockaddr_in6 *)neigh.nda_dst; if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) dst->sin6_scope_id = neigh.nda_ifindex; if (addr != NULL) { if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, &dst->sin6_addr) == 0 || addr->sin6_scope_id != dst->sin6_scope_id) continue; } print_entry(&neigh, &link); if (cflag) { char dst_str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str)); delete_nl(neigh.nda_ifindex, dst_str); } count++; snl_clear_lb(&ss_req); } xo_close_list("neighbor-cache"); snl_free(&ss_req); snl_free(&ss_cmd); return (count); } int delete_nl(uint32_t ifindex, char *host) { struct snl_state ss = {}; struct snl_writer nw; struct sockaddr_in6 dst; int gai_error = getaddr(host, &dst); if (gai_error) { xo_warnx("%s: %s", host, gai_strerror(gai_error)); return 1; } nl_init_socket(&ss); ifindex = fix_ifindex(&ss, ifindex, &dst); if (ifindex == 0) { xo_warnx("delete: cannot locate %s", host); snl_free(&ss); return (0); } snl_init_writer(&ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH); struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); if (ndmsg != NULL) { ndmsg->ndm_family = AF_INET6; ndmsg->ndm_ifindex = ifindex; } snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst); - if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { snl_free(&ss); return (1); } struct snl_errmsg_data e = {}; snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); if (e.error != 0) { if (e.error_str != NULL) xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str); else xo_warnx("delete %s: %s", host, strerror(e.error)); } else { char host_buf[NI_MAXHOST]; char ifix_buf[IFNAMSIZ]; getnameinfo((struct sockaddr *)&dst, dst.sin6_len, host_buf, sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); char *ifname = if_indextoname(ifindex, ifix_buf); if (ifname == NULL) { strlcpy(ifix_buf, "?", sizeof(ifix_buf)); ifname = ifix_buf; } char abuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf)); xo_open_instance("neighbor-cache"); xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf); xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname); xo_close_instance("neighbor-cache"); } snl_free(&ss); return (e.error != 0); } int set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host) { struct snl_state ss = {}; struct snl_writer nw; nl_init_socket(&ss); ifindex = fix_ifindex(&ss, ifindex, dst); if (ifindex == 0) { xo_warnx("delete: cannot locate %s", host); snl_free(&ss); return (0); } snl_init_writer(&ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH); hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); if (ndmsg != NULL) { uint8_t nl_flags = NTF_STICKY; ndmsg->ndm_family = AF_INET6; ndmsg->ndm_ifindex = ifindex; ndmsg->ndm_state = NUD_PERMANENT; if (opts.flags & RTF_ANNOUNCE) nl_flags |= NTF_PROXY; ndmsg->ndm_flags = nl_flags; } snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl)); - if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { snl_free(&ss); return (1); } struct snl_errmsg_data e = {}; snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); if (e.error != 0) { if (e.error_str != NULL) xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str); else xo_warnx("set %s: %s", host, strerror(e.error)); } snl_free(&ss); return (e.error != 0); }