From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- libdnet-stripped/src/route-bsd.c | 703 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 703 insertions(+) create mode 100644 libdnet-stripped/src/route-bsd.c (limited to 'libdnet-stripped/src/route-bsd.c') diff --git a/libdnet-stripped/src/route-bsd.c b/libdnet-stripped/src/route-bsd.c new file mode 100644 index 0000000..2ca799c --- /dev/null +++ b/libdnet-stripped/src/route-bsd.c @@ -0,0 +1,703 @@ +/* + * route-bsd.c + * + * Copyright (c) 2001 Dug Song + * Copyright (c) 1999 Masaki Hirabaru + * + * $Id: route-bsd.c 555 2005-02-10 05:18:38Z dugsong $ + */ + +#include "config.h" + +#include +#include +#include +#ifdef HAVE_SYS_SYSCTL_H +#include +#endif +#ifdef HAVE_STREAMS_MIB2 +#include +#include +#include +#include +#include +#include +#undef IP_ADDR_LEN +#include +#elif defined(HAVE_STREAMS_ROUTE) +#include +#include +#endif +#ifdef HAVE_GETKERNINFO +#include +#endif + +#define route_t oroute_t /* XXX - unixware */ +#include +#undef route_t +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "dnet.h" + +#if defined(RT_ROUNDUP) && defined(__NetBSD__) +/* NetBSD defines this macro rounding to 64-bit boundaries. + http://fxr.watson.org/fxr/ident?v=NETBSD;i=RT_ROUNDUP */ +#define ROUNDUP(a) RT_ROUNDUP(a) +#else +/* Unix Network Programming, 3rd edition says that sockaddr structures in + rt_msghdr should be padded so their addresses start on a multiple of + sizeof(u_long). But on 64-bit Mac OS X 10.6 at least, this is false. Apple's + netstat code uses 4-byte padding, not 8-byte. This is relevant for IPv6 + addresses, for which sa_len == 28. + http://www.opensource.apple.com/source/network_cmds/network_cmds-329.2.2/netstat.tproj/route.c */ +#ifdef __APPLE__ +#define RT_MSGHDR_ALIGNMENT sizeof(uint32_t) +#else +#define RT_MSGHDR_ALIGNMENT sizeof(unsigned long) +#endif +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (RT_MSGHDR_ALIGNMENT - 1))) : RT_MSGHDR_ALIGNMENT) +#endif + +#ifdef HAVE_SOCKADDR_SA_LEN +#define NEXTSA(s) \ + ((struct sockaddr *)((u_char *)(s) + ROUNDUP((s)->sa_len))) +#else +#define NEXTSA(s) \ + ((struct sockaddr *)((u_char *)(s) + ROUNDUP(sizeof(*(s))))) +#endif + +struct route_handle { + int fd; + int seq; +#ifdef HAVE_STREAMS_MIB2 + int ip_fd; +#endif +}; + +#ifdef DEBUG +static void +route_msg_print(struct rt_msghdr *rtm) +{ + printf("v: %d type: 0x%x flags: 0x%x addrs: 0x%x pid: %d seq: %d\n", + rtm->rtm_version, rtm->rtm_type, rtm->rtm_flags, + rtm->rtm_addrs, rtm->rtm_pid, rtm->rtm_seq); +} +#endif + +static int +route_msg(route_t *r, int type, char intf_name[INTF_NAME_LEN], struct addr *dst, struct addr *gw) +{ + struct addr net; + struct rt_msghdr *rtm; + struct sockaddr *sa; + u_char buf[BUFSIZ]; + pid_t pid; + int len; + + memset(buf, 0, sizeof(buf)); + + rtm = (struct rt_msghdr *)buf; + rtm->rtm_version = RTM_VERSION; + if ((rtm->rtm_type = type) != RTM_DELETE) + rtm->rtm_flags = RTF_UP; + rtm->rtm_addrs = RTA_DST; + rtm->rtm_seq = ++r->seq; + + /* Destination */ + sa = (struct sockaddr *)(rtm + 1); + if (addr_net(dst, &net) < 0 || addr_ntos(&net, sa) < 0) + return (-1); + sa = NEXTSA(sa); + + /* Gateway */ + if (gw != NULL && type != RTM_GET) { + rtm->rtm_flags |= RTF_GATEWAY; + rtm->rtm_addrs |= RTA_GATEWAY; + if (addr_ntos(gw, sa) < 0) + return (-1); + sa = NEXTSA(sa); + } + /* Netmask */ + if (dst->addr_ip == IP_ADDR_ANY || dst->addr_bits < IP_ADDR_BITS) { + rtm->rtm_addrs |= RTA_NETMASK; + if (addr_btos(dst->addr_bits, sa) < 0) + return (-1); + sa = NEXTSA(sa); + } else + rtm->rtm_flags |= RTF_HOST; + + rtm->rtm_msglen = (u_char *)sa - buf; +#ifdef DEBUG + route_msg_print(rtm); +#endif +#ifdef HAVE_STREAMS_ROUTE + if (ioctl(r->fd, RTSTR_SEND, rtm) < 0) + return (-1); +#else + if (write(r->fd, buf, rtm->rtm_msglen) < 0) + return (-1); + + pid = getpid(); + + while (type == RTM_GET && (len = read(r->fd, buf, sizeof(buf))) > 0) { + if (len < (int)sizeof(*rtm)) { + return (-1); + } + if (rtm->rtm_type == type && rtm->rtm_pid == pid && + rtm->rtm_seq == r->seq) { + if (rtm->rtm_errno) { + errno = rtm->rtm_errno; + return (-1); + } + break; + } + } +#endif + if (type == RTM_GET && (rtm->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == + (RTA_DST|RTA_GATEWAY)) { + sa = (struct sockaddr *)(rtm + 1); + sa = NEXTSA(sa); + + if (addr_ston(sa, gw) < 0 || gw->addr_type != ADDR_TYPE_IP) { + errno = ESRCH; + return (-1); + } + + if (intf_name != NULL) { + char namebuf[IF_NAMESIZE]; + + if (if_indextoname(rtm->rtm_index, namebuf) == NULL) { + errno = ESRCH; + return (-1); + } + strlcpy(intf_name, namebuf, INTF_NAME_LEN); + } + } + return (0); +} + +route_t * +route_open(void) +{ + route_t *r; + + if ((r = calloc(1, sizeof(*r))) != NULL) { + r->fd = -1; +#ifdef HAVE_STREAMS_MIB2 + if ((r->ip_fd = open(IP_DEV_NAME, O_RDWR)) < 0) + return (route_close(r)); +#endif +#ifdef HAVE_STREAMS_ROUTE + if ((r->fd = open("/dev/route", O_RDWR, 0)) < 0) +#else + if ((r->fd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) +#endif + return (route_close(r)); + } + return (r); +} + +int +route_add(route_t *r, const struct route_entry *entry) +{ + struct route_entry rtent; + + memcpy(&rtent, entry, sizeof(rtent)); + + if (route_msg(r, RTM_ADD, NULL, &rtent.route_dst, &rtent.route_gw) < 0) + return (-1); + + return (0); +} + +int +route_delete(route_t *r, const struct route_entry *entry) +{ + struct route_entry rtent; + + memcpy(&rtent, entry, sizeof(rtent)); + + if (route_get(r, &rtent) < 0) + return (-1); + + if (route_msg(r, RTM_DELETE, NULL, &rtent.route_dst, &rtent.route_gw) < 0) + return (-1); + + return (0); +} + +int +route_get(route_t *r, struct route_entry *entry) +{ + if (route_msg(r, RTM_GET, entry->intf_name, &entry->route_dst, &entry->route_gw) < 0) + return (-1); + entry->intf_name[0] = '\0'; + entry->metric = 0; + + return (0); +} + +#if defined(HAVE_SYS_SYSCTL_H) || defined(HAVE_STREAMS_ROUTE) || defined(HAVE_GETKERNINFO) +/* This wrapper around addr_ston, on failure, checks for a gateway address + * family of AF_LINK, and if it finds one, stores an all-zero address of the + * same type as dst. The all-zero address is a convention for same-subnet + * routing table entries. */ +static int +addr_ston_gateway(const struct addr *dst, + const struct sockaddr *sa, struct addr *a) +{ + int rc; + + rc = addr_ston(sa, a); + if (rc == 0) + return rc; + +#ifdef HAVE_NET_IF_DL_H +# ifdef AF_LINK + if (sa->sa_family == AF_LINK) { + memset(a, 0, sizeof(*a)); + a->addr_type = dst->addr_type; + return (0); + } +# endif +#endif + + return (-1); +} + +int +route_loop(route_t *r, route_handler callback, void *arg) +{ + struct rt_msghdr *rtm; + struct route_entry entry; + struct sockaddr *sa; + char *buf, *lim, *next; + int ret; +#ifdef HAVE_SYS_SYSCTL_H + int mib[6] = { CTL_NET, PF_ROUTE, 0, 0 /* XXX */, NET_RT_DUMP, 0 }; + size_t len; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return (-1); + + if (len == 0) + return (0); + + if ((buf = malloc(len)) == NULL) + return (-1); + + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + free(buf); + return (-1); + } + lim = buf + len; + next = buf; +#elif defined(HAVE_GETKERNINFO) + int len = getkerninfo(KINFO_RT_DUMP,0,0,0); + + if (len == 0) + return (0); + + if ((buf = malloc(len)) == NULL) + return (-1); + + if (getkerninfo(KINFO_RT_DUMP,buf,&len,0) < 0) { + free(buf); + return (-1); + } + lim = buf + len; + next = buf; +#else /* HAVE_STREAMS_ROUTE */ + struct rt_giarg giarg, *gp; + + memset(&giarg, 0, sizeof(giarg)); + giarg.gi_op = KINFO_RT_DUMP; + + if (ioctl(r->fd, RTSTR_GETROUTE, &giarg) < 0) + return (-1); + + if ((buf = malloc(giarg.gi_size)) == NULL) + return (-1); + + gp = (struct rt_giarg *)buf; + gp->gi_size = giarg.gi_size; + gp->gi_op = KINFO_RT_DUMP; + gp->gi_where = buf; + gp->gi_arg = RTF_UP | RTF_GATEWAY; + + if (ioctl(r->fd, RTSTR_GETROUTE, buf) < 0) { + free(buf); + return (-1); + } + lim = buf + gp->gi_size; + next = buf + sizeof(giarg); +#endif + /* This loop assumes that RTA_DST, RTA_GATEWAY, and RTA_NETMASK have the + * values, 1, 2, and 4 respectively. Cf. Unix Network Programming, + * p. 494, function get_rtaddrs. */ + for (ret = 0; next < lim; next += rtm->rtm_msglen) { + char namebuf[IF_NAMESIZE]; + sa_family_t sfam; + rtm = (struct rt_msghdr *)next; + sa = (struct sockaddr *)(rtm + 1); + /* peek at address family */ + sfam = sa->sa_family; + + if (if_indextoname(rtm->rtm_index, namebuf) == NULL) + continue; + strlcpy(entry.intf_name, namebuf, sizeof(entry.intf_name)); + + if ((rtm->rtm_addrs & RTA_DST) == 0) + /* Need a destination. */ + continue; + if (addr_ston(sa, &entry.route_dst) < 0) + continue; + + if ((rtm->rtm_addrs & RTA_GATEWAY) == 0) + /* Need a gateway. */ + continue; + sa = NEXTSA(sa); + if (addr_ston_gateway(&entry.route_dst, sa, &entry.route_gw) < 0) + continue; + + if (entry.route_dst.addr_type != entry.route_gw.addr_type || + (entry.route_dst.addr_type != ADDR_TYPE_IP && + entry.route_dst.addr_type != ADDR_TYPE_IP6)) + continue; + + if (rtm->rtm_addrs & RTA_NETMASK) { + sa = NEXTSA(sa); + /* FreeBSD for IPv6 uses a different AF for netmasks. Force the same one. */ + sa->sa_family = sfam; + if (addr_stob(sa, &entry.route_dst.addr_bits) < 0) + continue; + } + + entry.metric = 0; + + if ((ret = callback(&entry, arg)) != 0) + break; + } + free(buf); + + return (ret); +} +#elif defined(HAVE_STREAMS_MIB2) + +#ifdef IRE_DEFAULT /* This means Solaris 5.6 */ +/* I'm not sure if they are compatible, though -- masaki */ +#define IRE_ROUTE IRE_CACHE +#define IRE_ROUTE_REDIRECT IRE_HOST_REDIRECT +#endif /* IRE_DEFAULT */ + +int +route_loop(route_t *r, route_handler callback, void *arg) +{ + struct route_entry entry; + struct strbuf msg; + struct T_optmgmt_req *tor; + struct T_optmgmt_ack *toa; + struct T_error_ack *tea; + struct opthdr *opt; + u_char buf[8192]; + int flags, rc, rtable, ret; + + tor = (struct T_optmgmt_req *)buf; + toa = (struct T_optmgmt_ack *)buf; + tea = (struct T_error_ack *)buf; + + tor->PRIM_type = T_OPTMGMT_REQ; + tor->OPT_offset = sizeof(*tor); + tor->OPT_length = sizeof(*opt); + tor->MGMT_flags = T_CURRENT; + + opt = (struct opthdr *)(tor + 1); + opt->level = MIB2_IP; + opt->name = opt->len = 0; + + msg.maxlen = sizeof(buf); + msg.len = sizeof(*tor) + sizeof(*opt); + msg.buf = buf; + + if (putmsg(r->ip_fd, &msg, NULL, 0) < 0) + return (-1); + + opt = (struct opthdr *)(toa + 1); + msg.maxlen = sizeof(buf); + + for (;;) { + mib2_ipRouteEntry_t *rt, *rtend; + + flags = 0; + if ((rc = getmsg(r->ip_fd, &msg, NULL, &flags)) < 0) + return (-1); + + /* See if we're finished. */ + if (rc == 0 && + msg.len >= sizeof(*toa) && + toa->PRIM_type == T_OPTMGMT_ACK && + toa->MGMT_flags == T_SUCCESS && opt->len == 0) + break; + + if (msg.len >= sizeof(*tea) && tea->PRIM_type == T_ERROR_ACK) + return (-1); + + if (rc != MOREDATA || msg.len < (int)sizeof(*toa) || + toa->PRIM_type != T_OPTMGMT_ACK || + toa->MGMT_flags != T_SUCCESS) + return (-1); + + rtable = (opt->level == MIB2_IP && opt->name == MIB2_IP_21); + + msg.maxlen = sizeof(buf) - (sizeof(buf) % sizeof(*rt)); + msg.len = 0; + flags = 0; + + do { + struct sockaddr_in sin; + + rc = getmsg(r->ip_fd, NULL, &msg, &flags); + + if (rc != 0 && rc != MOREDATA) + return (-1); + + if (!rtable) + continue; + + rt = (mib2_ipRouteEntry_t *)msg.buf; + rtend = (mib2_ipRouteEntry_t *)(msg.buf + msg.len); + + sin.sin_family = AF_INET; + + for ( ; rt < rtend; rt++) { + if ((rt->ipRouteInfo.re_ire_type & + (IRE_BROADCAST|IRE_ROUTE_REDIRECT| + IRE_LOCAL|IRE_ROUTE)) != 0 || + rt->ipRouteNextHop == IP_ADDR_ANY) + continue; + + entry.intf_name[0] = '\0'; + + sin.sin_addr.s_addr = rt->ipRouteNextHop; + addr_ston((struct sockaddr *)&sin, + &entry.route_gw); + + sin.sin_addr.s_addr = rt->ipRouteDest; + addr_ston((struct sockaddr *)&sin, + &entry.route_dst); + + sin.sin_addr.s_addr = rt->ipRouteMask; + addr_stob((struct sockaddr *)&sin, + &entry.route_dst.addr_bits); + + entry.metric = 0; + + if ((ret = callback(&entry, arg)) != 0) + return (ret); + } + } while (rc == MOREDATA); + } + + tor = (struct T_optmgmt_req *)buf; + toa = (struct T_optmgmt_ack *)buf; + tea = (struct T_error_ack *)buf; + + tor->PRIM_type = T_OPTMGMT_REQ; + tor->OPT_offset = sizeof(*tor); + tor->OPT_length = sizeof(*opt); + tor->MGMT_flags = T_CURRENT; + + opt = (struct opthdr *)(tor + 1); + opt->level = MIB2_IP6; + opt->name = opt->len = 0; + + msg.maxlen = sizeof(buf); + msg.len = sizeof(*tor) + sizeof(*opt); + msg.buf = buf; + + if (putmsg(r->ip_fd, &msg, NULL, 0) < 0) + return (-1); + + opt = (struct opthdr *)(toa + 1); + msg.maxlen = sizeof(buf); + + for (;;) { + mib2_ipv6RouteEntry_t *rt, *rtend; + + flags = 0; + if ((rc = getmsg(r->ip_fd, &msg, NULL, &flags)) < 0) + return (-1); + + /* See if we're finished. */ + if (rc == 0 && + msg.len >= sizeof(*toa) && + toa->PRIM_type == T_OPTMGMT_ACK && + toa->MGMT_flags == T_SUCCESS && opt->len == 0) + break; + + if (msg.len >= sizeof(*tea) && tea->PRIM_type == T_ERROR_ACK) + return (-1); + + if (rc != MOREDATA || msg.len < (int)sizeof(*toa) || + toa->PRIM_type != T_OPTMGMT_ACK || + toa->MGMT_flags != T_SUCCESS) + return (-1); + + rtable = (opt->level == MIB2_IP6 && opt->name == MIB2_IP6_ROUTE); + + msg.maxlen = sizeof(buf) - (sizeof(buf) % sizeof(*rt)); + msg.len = 0; + flags = 0; + + do { + struct sockaddr_in6 sin6; + + rc = getmsg(r->ip_fd, NULL, &msg, &flags); + + if (rc != 0 && rc != MOREDATA) + return (-1); + + if (!rtable) + continue; + + rt = (mib2_ipv6RouteEntry_t *)msg.buf; + rtend = (mib2_ipv6RouteEntry_t *)(msg.buf + msg.len); + + sin6.sin6_family = AF_INET6; + + for ( ; rt < rtend; rt++) { + if ((rt->ipv6RouteInfo.re_ire_type & + (IRE_BROADCAST|IRE_ROUTE_REDIRECT| + IRE_LOCAL|IRE_ROUTE)) != 0 || + memcmp(&rt->ipv6RouteNextHop, IP6_ADDR_UNSPEC, IP6_ADDR_LEN) == 0) + continue; + + entry.intf_name[0] = '\0'; + + sin6.sin6_addr = rt->ipv6RouteNextHop; + addr_ston((struct sockaddr *)&sin6, + &entry.route_gw); + + sin6.sin6_addr = rt->ipv6RouteDest; + addr_ston((struct sockaddr *)&sin6, + &entry.route_dst); + + entry.route_dst.addr_bits = rt->ipv6RoutePfxLength; + + if ((ret = callback(&entry, arg)) != 0) + return (ret); + } + } while (rc == MOREDATA); + } + return (0); +} +#elif defined(HAVE_NET_RADIX_H) +/* XXX - Tru64, others? */ +#include + +static int +_kread(int fd, void *addr, void *buf, int len) +{ + if (lseek(fd, (off_t)addr, SEEK_SET) == (off_t)-1L) + return (-1); + return (read(fd, buf, len) == len ? 0 : -1); +} + +static int +_radix_walk(int fd, struct radix_node *rn, route_handler callback, void *arg) +{ + struct radix_node rnode; + struct rtentry rt; + struct sockaddr_in sin; + struct route_entry entry; + int ret = 0; + again: + _kread(fd, rn, &rnode, sizeof(rnode)); + if (rnode.rn_b < 0) { + if (!(rnode.rn_flags & RNF_ROOT)) { + entry.intf_name[0] = '\0'; + _kread(fd, rn, &rt, sizeof(rt)); + _kread(fd, rt_key(&rt), &sin, sizeof(sin)); + addr_ston((struct sockaddr *)&sin, &entry.route_dst); + if (!(rt.rt_flags & RTF_HOST)) { + _kread(fd, rt_mask(&rt), &sin, sizeof(sin)); + addr_stob((struct sockaddr *)&sin, + &entry.route_dst.addr_bits); + } + _kread(fd, rt.rt_gateway, &sin, sizeof(sin)); + addr_ston((struct sockaddr *)&sin, &entry.route_gw); + entry.metric = 0; + if ((ret = callback(&entry, arg)) != 0) + return (ret); + } + if ((rn = rnode.rn_dupedkey)) + goto again; + } else { + rn = rnode.rn_r; + if ((ret = _radix_walk(fd, rnode.rn_l, callback, arg)) != 0) + return (ret); + if ((ret = _radix_walk(fd, rn, callback, arg)) != 0) + return (ret); + } + return (ret); +} + +int +route_loop(route_t *r, route_handler callback, void *arg) +{ + struct radix_node_head *rnh, head; + struct nlist nl[2]; + int fd, ret = 0; + + memset(nl, 0, sizeof(nl)); + nl[0].n_name = "radix_node_head"; + + if (knlist(nl) < 0 || nl[0].n_type == 0 || + (fd = open("/dev/kmem", O_RDONLY, 0)) < 0) + return (-1); + + for (_kread(fd, (void *)nl[0].n_value, &rnh, sizeof(rnh)); + rnh != NULL; rnh = head.rnh_next) { + _kread(fd, rnh, &head, sizeof(head)); + /* XXX - only IPv4 for now... */ + if (head.rnh_af == AF_INET) { + if ((ret = _radix_walk(fd, head.rnh_treetop, + callback, arg)) != 0) + break; + } + } + close(fd); + return (ret); +} +#else +int +route_loop(route_t *r, route_handler callback, void *arg) +{ + errno = ENOSYS; + return (-1); +} +#endif + +route_t * +route_close(route_t *r) +{ + if (r != NULL) { +#ifdef HAVE_STREAMS_MIB2 + if (r->ip_fd >= 0) + close(r->ip_fd); +#endif + if (r->fd >= 0) + close(r->fd); + free(r); + } + return (NULL); +} -- cgit v1.2.3