From 144c6bcd95e9681955db4554278be2133dbd9b54 Mon Sep 17 00:00:00 2001 From: logwang Date: Wed, 14 Jun 2017 15:25:35 +0800 Subject: [PATCH] Add tool: route. This commit ports FreeBSD `route` to F-Stack, can be used to set the route table. --- example/Makefile | 2 +- lib/Makefile | 3 +- lib/ff_api.h | 29 + lib/ff_api.symlist | 4 +- lib/ff_dpdk_if.c | 21 +- lib/ff_msg.h | 9 + lib/ff_route.c | 733 ++++++++ lib/ff_syscall_wrapper.c | 82 +- tools/Makefile | 2 +- tools/README.md | 46 + tools/compat/compat.h | 16 + tools/compat/ff_ipc.c | 2 +- tools/compat/getopt.c | 150 ++ tools/compat/include/arpa/inet.h | 6 + tools/compat/include/net/if_arp.h | 116 ++ tools/compat/include/netinet/if_ether.h | 111 ++ tools/ifconfig/ifconfig.h | 4 - tools/prog.mk | 10 +- tools/route/Makefile | 29 + tools/route/keywords | 57 + tools/route/route.8 | 506 ++++++ tools/route/route.c | 2110 +++++++++++++++++++++++ 22 files changed, 4028 insertions(+), 20 deletions(-) create mode 100644 lib/ff_route.c create mode 100644 tools/compat/getopt.c create mode 100644 tools/compat/include/net/if_arp.h create mode 100644 tools/compat/include/netinet/if_ether.h create mode 100644 tools/route/Makefile create mode 100644 tools/route/keywords create mode 100644 tools/route/route.8 create mode 100644 tools/route/route.c diff --git a/example/Makefile b/example/Makefile index 8f399eb0f..2811f4c52 100644 --- a/example/Makefile +++ b/example/Makefile @@ -16,7 +16,7 @@ LIBS+= -Wl,--no-whole-archive -lrt -lm -ldl -lcrypto -pthread TARGET="helloworld" all: - cc -O -gdwarf-2 -I../lib -o ${TARGET} main.c ${LIBS} + cc -O -gdwarf-2 -g -I../lib -o ${TARGET} main.c ${LIBS} .PHONY: clean clean: diff --git a/lib/Makefile b/lib/Makefile index 8a00e4e74..666bea5dd 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -129,7 +129,8 @@ FF_SRCS+= \ ff_subr_prf.c \ ff_vfs_ops.c \ ff_veth.c \ - ff_epoll.c + ff_epoll.c \ + ff_route.c FF_HOST_SRCS+= \ ff_host_interface.c \ diff --git a/lib/ff_api.h b/lib/ff_api.h index 760b161a2..07216e740 100644 --- a/lib/ff_api.h +++ b/lib/ff_api.h @@ -33,6 +33,7 @@ extern "C" { #include #include #include +#include #include "ff_event.h" #include "ff_errno.h" @@ -102,6 +103,34 @@ int ff_kqueue(void); int ff_kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); +/* route api begin */ +enum FF_ROUTE_CTL { + FF_ROUTE_ADD, + FF_ROUTE_DEL, + FF_ROUTE_CHANGE, +}; + +enum FF_ROUTE_FLAG { + FF_RTF_HOST, + FF_RTF_GATEWAY, +}; + +/* + * On success, 0 is returned. + * On error, -1 is returned, and errno is set appropriately. + */ +int ff_route_ctl(enum FF_ROUTE_CTL req, enum FF_ROUTE_FLAG flag, + struct linux_sockaddr *dst, struct linux_sockaddr *gw, + struct linux_sockaddr *netmask); + +/* + * This is used in handling ff_msg. + * The data is a pointer to struct rt_msghdr. + */ +int ff_rtioctl(int fib, void *data, unsigned *plen, unsigned maxlen); + +/* route api end */ + #ifdef __cplusplus } #endif diff --git a/lib/ff_api.symlist b/lib/ff_api.symlist index 9545a677c..003e92474 100644 --- a/lib/ff_api.symlist +++ b/lib/ff_api.symlist @@ -40,4 +40,6 @@ ff_mbuf_gethdr ff_mbuf_get ff_mbuf_free ff_mbuf_copydata -ff_mbuf_tx_offload \ No newline at end of file +ff_mbuf_tx_offload +ff_route_ctl +ff_rtioctl diff --git a/lib/ff_dpdk_if.c b/lib/ff_dpdk_if.c index 9a60f88af..60b082c57 100644 --- a/lib/ff_dpdk_if.c +++ b/lib/ff_dpdk_if.c @@ -721,7 +721,7 @@ init_port_start(void) } if (rte_eal_process_type() != RTE_PROC_PRIMARY) { - return 0; + continue; } /* Currently, proc id 1:1 map to queue id per port. */ @@ -852,18 +852,19 @@ ff_veth_input(const struct ff_dpdk_if_context *ctx, struct rte_mbuf *pkt) return; } - pkt = pkt->next; + struct rte_mbuf *pn = pkt->next; void *prev = hdr; - while(pkt != NULL) { + while(pn != NULL) { data = rte_pktmbuf_mtod(pkt, void*); len = rte_pktmbuf_data_len(pkt); void *mb = ff_mbuf_get(prev, data, len); if (mb == NULL) { ff_mbuf_free(hdr); + rte_pktmbuf_free(pkt); return; } - pkt = pkt->next; + pn = pn->next; prev = mb; } @@ -1004,6 +1005,15 @@ done: rte_ring_enqueue(msg_ring[proc_id].ring[1], msg); } +static inline void +handle_route_msg(struct ff_msg *msg, uint16_t proc_id) +{ + msg->result = ff_rtioctl(msg->route.fib, msg->route.data, + &msg->route.len, msg->route.maxlen); + + rte_ring_enqueue(msg_ring[proc_id].ring[1], msg); +} + static inline void handle_default_msg(struct ff_msg *msg, uint16_t proc_id) { @@ -1021,6 +1031,9 @@ handle_msg(struct ff_msg *msg, uint16_t proc_id) case FF_IOCTL: handle_ioctl_msg(msg, proc_id); break; + case FF_ROUTE: + handle_route_msg(msg, proc_id); + break; default: handle_default_msg(msg, proc_id); break; diff --git a/lib/ff_msg.h b/lib/ff_msg.h index 690d89f67..c6ef4d386 100644 --- a/lib/ff_msg.h +++ b/lib/ff_msg.h @@ -38,6 +38,7 @@ enum FF_MSG_TYPE { FF_UNKNOWN = 0, FF_SYSCTL, FF_IOCTL, + FF_ROUTE, }; struct ff_sysctl_args { @@ -54,6 +55,13 @@ struct ff_ioctl_args { void *data; }; +struct ff_route_args { + int fib; + unsigned len; + unsigned maxlen; + void *data; +}; + #define MAX_MSG_BUF_SIZE 10240 /* structure of ipc msg */ @@ -69,6 +77,7 @@ struct ff_msg { union { struct ff_sysctl_args sysctl; struct ff_ioctl_args ioctl; + struct ff_route_args route; }; } __attribute__((packed)) __rte_cache_aligned; diff --git a/lib/ff_route.c b/lib/ff_route.c new file mode 100644 index 000000000..1d64b0615 --- /dev/null +++ b/lib/ff_route.c @@ -0,0 +1,733 @@ +/* + * Copyright (c) 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * 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. + * 4. 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. + * + * Copied part from FreeBSD rtsock.c. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ff_api.h" +#include "ff_host_interface.h" + +#ifndef _SOCKADDR_UNION_DEFINED +#define _SOCKADDR_UNION_DEFINED +/* + * The union of all possible address formats we handle. + */ +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +#endif /* _SOCKADDR_UNION_DEFINED */ + +static struct sockaddr sa_zero = { sizeof(sa_zero), AF_INET, }; + +struct walkarg { + int w_tmemsize; + int w_op, w_arg; + caddr_t w_tmem; + struct sysctl_req *w_req; +}; + +static int +rtm_get_jailed(struct rt_addrinfo *info, struct ifnet *ifp, + struct rtentry *rt, union sockaddr_union *saun, struct ucred *cred) +{ + + /* First, see if the returned address is part of the jail. */ + if (prison_if(cred, rt->rt_ifa->ifa_addr) == 0) { + info->rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; + return (0); + } + + switch (info->rti_info[RTAX_DST]->sa_family) { +#ifdef INET + case AF_INET: + { + struct in_addr ia; + struct ifaddr *ifa; + int found; + + found = 0; + /* + * Try to find an address on the given outgoing interface + * that belongs to the jail. + */ + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + struct sockaddr *sa; + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET) + continue; + ia = ((struct sockaddr_in *)sa)->sin_addr; + if (prison_check_ip4(cred, &ia) == 0) { + found = 1; + break; + } + } + IF_ADDR_RUNLOCK(ifp); + if (!found) { + /* + * As a last resort return the 'default' jail address. + */ + ia = ((struct sockaddr_in *)rt->rt_ifa->ifa_addr)-> + sin_addr; + if (prison_get_ip4(cred, &ia) != 0) + return (ESRCH); + } + bzero(&saun->sin, sizeof(struct sockaddr_in)); + saun->sin.sin_len = sizeof(struct sockaddr_in); + saun->sin.sin_family = AF_INET; + saun->sin.sin_addr.s_addr = ia.s_addr; + info->rti_info[RTAX_IFA] = (struct sockaddr *)&saun->sin; + break; + } +#endif +#ifdef INET6 + case AF_INET6: + { + struct in6_addr ia6; + struct ifaddr *ifa; + int found; + + found = 0; + /* + * Try to find an address on the given outgoing interface + * that belongs to the jail. + */ + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + struct sockaddr *sa; + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET6) + continue; + bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr, + &ia6, sizeof(struct in6_addr)); + if (prison_check_ip6(cred, &ia6) == 0) { + found = 1; + break; + } + } + IF_ADDR_RUNLOCK(ifp); + if (!found) { + /* + * As a last resort return the 'default' jail address. + */ + ia6 = ((struct sockaddr_in6 *)rt->rt_ifa->ifa_addr)-> + sin6_addr; + if (prison_get_ip6(cred, &ia6) != 0) + return (ESRCH); + } + bzero(&saun->sin6, sizeof(struct sockaddr_in6)); + saun->sin6.sin6_len = sizeof(struct sockaddr_in6); + saun->sin6.sin6_family = AF_INET6; + bcopy(&ia6, &saun->sin6.sin6_addr, sizeof(struct in6_addr)); + if (sa6_recoverscope(&saun->sin6) != 0) + return (ESRCH); + info->rti_info[RTAX_IFA] = (struct sockaddr *)&saun->sin6; + break; + } +#endif + default: + return (ESRCH); + } + return (0); +} + +/* + * Extract the addresses of the passed sockaddrs. + * Do a little sanity checking so as to avoid bad memory references. + * This data is derived straight from userland. + */ +static int +rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo) +{ + struct sockaddr *sa; + int i; + + for (i = 0; i < RTAX_MAX && cp < cplim; i++) { + if ((rtinfo->rti_addrs & (1 << i)) == 0) + continue; + sa = (struct sockaddr *)cp; + /* + * It won't fit. + */ + if (cp + sa->sa_len > cplim) + return (EINVAL); + /* + * there are no more.. quit now + * If there are more bits, they are in error. + * I've seen this. route(1) can evidently generate these. + * This causes kernel to core dump. + * for compatibility, If we see this, point to a safe address. + */ + if (sa->sa_len == 0) { + rtinfo->rti_info[i] = &sa_zero; + return (0); /* should be EINVAL but for compat */ + } + /* accept it */ +#ifdef INET6 + if (sa->sa_family == AF_INET6) + sa6_embedscope((struct sockaddr_in6 *)sa, + V_ip6_use_defzone); +#endif + rtinfo->rti_info[i] = sa; + cp += SA_SIZE(sa); + } + return (0); +} + +/* + * Writes information related to @rtinfo object to preallocated buffer. + * Stores needed size in @plen. If @w is NULL, calculates size without + * writing. + * Used for sysctl dumps and rtsock answers (RTM_DEL/RTM_GET) generation. + * + * Returns 0 on success. + * + */ +static int +rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, struct walkarg *w, int *plen) +{ + int i; + int len, buflen = 0, dlen; + caddr_t cp = NULL; + struct rt_msghdr *rtm = NULL; +#ifdef INET6 + struct sockaddr_storage ss; + struct sockaddr_in6 *sin6; +#endif + + switch (type) { + + case RTM_DELADDR: + case RTM_NEWADDR: + if (w != NULL && w->w_op == NET_RT_IFLISTL) { +#ifdef COMPAT_FREEBSD32 + if (w->w_req->flags & SCTL_MASK32) + len = sizeof(struct ifa_msghdrl32); + else +#endif + len = sizeof(struct ifa_msghdrl); + } else + len = sizeof(struct ifa_msghdr); + break; + + case RTM_IFINFO: +#ifdef COMPAT_FREEBSD32 + if (w != NULL && w->w_req->flags & SCTL_MASK32) { + if (w->w_op == NET_RT_IFLISTL) + len = sizeof(struct if_msghdrl32); + else + len = sizeof(struct if_msghdr32); + break; + } +#endif + if (w != NULL && w->w_op == NET_RT_IFLISTL) + len = sizeof(struct if_msghdrl); + else + len = sizeof(struct if_msghdr); + break; + + case RTM_NEWMADDR: + len = sizeof(struct ifma_msghdr); + break; + + default: + len = sizeof(struct rt_msghdr); + } + + if (w != NULL) { + rtm = (struct rt_msghdr *)w->w_tmem; + buflen = w->w_tmemsize - len; + cp = (caddr_t)w->w_tmem + len; + } + + rtinfo->rti_addrs = 0; + for (i = 0; i < RTAX_MAX; i++) { + struct sockaddr *sa; + + if ((sa = rtinfo->rti_info[i]) == NULL) + continue; + rtinfo->rti_addrs |= (1 << i); + dlen = SA_SIZE(sa); + if (cp != NULL && buflen >= dlen) { +#ifdef INET6 + if (V_deembed_scopeid && sa->sa_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)&ss; + bcopy(sa, sin6, sizeof(*sin6)); + if (sa6_recoverscope(sin6) == 0) + sa = (struct sockaddr *)sin6; + } +#endif + bcopy((caddr_t)sa, cp, (unsigned)dlen); + cp += dlen; + buflen -= dlen; + } else if (cp != NULL) { + /* + * Buffer too small. Count needed size + * and return with error. + */ + cp = NULL; + } + + len += dlen; + } + + if (cp != NULL) { + dlen = ALIGN(len) - len; + if (buflen < dlen) + cp = NULL; + else + buflen -= dlen; + } + len = ALIGN(len); + + if (cp != NULL) { + /* fill header iff buffer is large enough */ + rtm->rtm_version = RTM_VERSION; + rtm->rtm_type = type; + rtm->rtm_msglen = len; + } + + *plen = len; + + if (w != NULL && cp == NULL) + return (ENOBUFS); + + return (0); +} + +/* + * Fill in @dmask with valid netmask leaving original @smask + * intact. Mostly used with radix netmasks. + */ +static struct sockaddr * +rtsock_fix_netmask(struct sockaddr *dst, struct sockaddr *smask, + struct sockaddr_storage *dmask) +{ + if (dst == NULL || smask == NULL) + return (NULL); + + memset(dmask, 0, dst->sa_len); + memcpy(dmask, smask, smask->sa_len); + dmask->ss_len = dst->sa_len; + dmask->ss_family = dst->sa_family; + + return ((struct sockaddr *)dmask); +} + +static void +rt_getmetrics(const struct rtentry *rt, struct rt_metrics *out) +{ + + bzero(out, sizeof(*out)); + out->rmx_mtu = rt->rt_mtu; + out->rmx_weight = rt->rt_weight; + out->rmx_pksent = counter_u64_fetch(rt->rt_pksent); + /* Kernel -> userland timebase conversion. */ + out->rmx_expire = rt->rt_expire ? + rt->rt_expire - time_uptime + time_second : 0; +} + +int +ff_rtioctl(int fibnum, void *data, unsigned *plen, unsigned maxlen) +{ + struct rt_msghdr *rtm = NULL; + struct rtentry *rt = NULL; + struct rib_head *rnh; + struct rt_addrinfo info; + union sockaddr_union saun; + sa_family_t saf = AF_UNSPEC; + struct sockaddr_storage ss; + struct walkarg w; + int error = 0, alloc_len = 0, len; + struct ifnet *ifp = NULL; + +#ifdef INET6 + struct sockaddr_in6 *sin6; + int i, rti_need_deembed = 0; +#endif + +#define senderr(e) { error = e; goto flush;} + + len = *plen; + /* + * Most of current messages are in range 200-240 bytes, + * minimize possible re-allocation on reply using larger size + * buffer aligned on 1k boundaty. + */ + alloc_len = roundup2(len, 1024); + if ((rtm = malloc(alloc_len, M_TEMP, M_NOWAIT)) == NULL) + senderr(ENOBUFS); + bcopy(data, (caddr_t)rtm, len); + + if (len < sizeof(*rtm) || len != rtm->rtm_msglen) + senderr(EINVAL); + + bzero(&info, sizeof(info)); + bzero(&w, sizeof(w)); + + if (rtm->rtm_version != RTM_VERSION) + senderr(EPROTONOSUPPORT); + + /* + * Starting from here, it is possible + * to alter original message and insert + * caller PID and error value. + */ + + rtm->rtm_pid = curproc->p_pid; + info.rti_addrs = rtm->rtm_addrs; + + info.rti_mflags = rtm->rtm_inits; + info.rti_rmx = &rtm->rtm_rmx; + + /* + * rt_xaddrs() performs s6_addr[2] := sin6_scope_id for AF_INET6 + * link-local address because rtrequest requires addresses with + * embedded scope id. + */ + if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, &info)) + senderr(EINVAL); + + info.rti_flags = rtm->rtm_flags; + if (info.rti_info[RTAX_DST] == NULL || + info.rti_info[RTAX_DST]->sa_family >= AF_MAX || + (info.rti_info[RTAX_GATEWAY] != NULL && + info.rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX)) + senderr(EINVAL); + saf = info.rti_info[RTAX_DST]->sa_family; + + /* + * The given gateway address may be an interface address. + * For example, issuing a "route change" command on a route + * entry that was created from a tunnel, and the gateway + * address given is the local end point. In this case the + * RTF_GATEWAY flag must be cleared or the destination will + * not be reachable even though there is no error message. + */ + if (info.rti_info[RTAX_GATEWAY] != NULL && + info.rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) { + struct rt_addrinfo ginfo; + struct sockaddr *gdst; + + bzero(&ginfo, sizeof(ginfo)); + bzero(&ss, sizeof(ss)); + ss.ss_len = sizeof(ss); + + ginfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ss; + gdst = info.rti_info[RTAX_GATEWAY]; + + /* + * A host route through the loopback interface is + * installed for each interface adddress. In pre 8.0 + * releases the interface address of a PPP link type + * is not reachable locally. This behavior is fixed as + * part of the new L2/L3 redesign and rewrite work. The + * signature of this interface address route is the + * AF_LINK sa_family type of the rt_gateway, and the + * rt_ifp has the IFF_LOOPBACK flag set. + */ + if (rib_lookup_info(fibnum, gdst, NHR_REF, 0, &ginfo) == 0) { + if (ss.ss_family == AF_LINK && + ginfo.rti_ifp->if_flags & IFF_LOOPBACK) { + info.rti_flags &= ~RTF_GATEWAY; + info.rti_flags |= RTF_GWFLAG_COMPAT; + } + rib_free_info(&ginfo); + } + } + + switch (rtm->rtm_type) { + struct rtentry *saved_nrt; + + case RTM_ADD: + case RTM_CHANGE: + if (info.rti_info[RTAX_GATEWAY] == NULL) + senderr(EINVAL); + saved_nrt = NULL; + + /* support for new ARP code */ + if (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK && + (rtm->rtm_flags & RTF_LLDATA) != 0) { + error = lla_rt_output(rtm, &info); +#ifdef INET6 + if (error == 0) + rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; +#endif + break; + } + error = rtrequest1_fib(rtm->rtm_type, &info, &saved_nrt, + fibnum); + if (error == 0 && saved_nrt != NULL) { +#ifdef INET6 + rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; +#endif + RT_LOCK(saved_nrt); + rtm->rtm_index = saved_nrt->rt_ifp->if_index; + RT_REMREF(saved_nrt); + RT_UNLOCK(saved_nrt); + } + break; + + case RTM_DELETE: + saved_nrt = NULL; + /* support for new ARP code */ + if (info.rti_info[RTAX_GATEWAY] && + (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) && + (rtm->rtm_flags & RTF_LLDATA) != 0) { + error = lla_rt_output(rtm, &info); +#ifdef INET6 + if (error == 0) + rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; +#endif + break; + } + error = rtrequest1_fib(RTM_DELETE, &info, &saved_nrt, fibnum); + if (error == 0) { + RT_LOCK(saved_nrt); + rt = saved_nrt; + goto report; + } +#ifdef INET6 + /* rt_msg2() will not be used when RTM_DELETE fails. */ + rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; +#endif + break; + + case RTM_GET: + rnh = rt_tables_get_rnh(fibnum, saf); + if (rnh == NULL) + senderr(EAFNOSUPPORT); + + RIB_RLOCK(rnh); + + if (info.rti_info[RTAX_NETMASK] == NULL && + rtm->rtm_type == RTM_GET) { + /* + * Provide logest prefix match for + * address lookup (no mask). + * 'route -n get addr' + */ + rt = (struct rtentry *) rnh->rnh_matchaddr( + info.rti_info[RTAX_DST], &rnh->head); + } else + rt = (struct rtentry *) rnh->rnh_lookup( + info.rti_info[RTAX_DST], + info.rti_info[RTAX_NETMASK], &rnh->head); + + if (rt == NULL) { + RIB_RUNLOCK(rnh); + senderr(ESRCH); + } +#ifdef RADIX_MPATH + /* + * for RTM_CHANGE/LOCK, if we got multipath routes, + * we require users to specify a matching RTAX_GATEWAY. + * + * for RTM_GET, gate is optional even with multipath. + * if gate == NULL the first match is returned. + * (no need to call rt_mpath_matchgate if gate == NULL) + */ + if (rt_mpath_capable(rnh) && + (rtm->rtm_type != RTM_GET || info.rti_info[RTAX_GATEWAY])) { + rt = rt_mpath_matchgate(rt, info.rti_info[RTAX_GATEWAY]); + if (!rt) { + RIB_RUNLOCK(rnh); + senderr(ESRCH); + } + } +#endif + /* + * If performing proxied L2 entry insertion, and + * the actual PPP host entry is found, perform + * another search to retrieve the prefix route of + * the local end point of the PPP link. + */ + if (rtm->rtm_flags & RTF_ANNOUNCE) { + struct sockaddr laddr; + + if (rt->rt_ifp != NULL && + rt->rt_ifp->if_type == IFT_PROPVIRTUAL) { + struct ifaddr *ifa; + + ifa = ifa_ifwithnet(info.rti_info[RTAX_DST], 1, + RT_ALL_FIBS); + if (ifa != NULL) + rt_maskedcopy(ifa->ifa_addr, + &laddr, + ifa->ifa_netmask); + } else + rt_maskedcopy(rt->rt_ifa->ifa_addr, + &laddr, + rt->rt_ifa->ifa_netmask); + /* + * refactor rt and no lock operation necessary + */ + rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr, + &rnh->head); + if (rt == NULL) { + RIB_RUNLOCK(rnh); + senderr(ESRCH); + } + } + RT_LOCK(rt); + RT_ADDREF(rt); + RIB_RUNLOCK(rnh); + +report: + RT_LOCK_ASSERT(rt); + if ((rt->rt_flags & RTF_HOST) == 0 + ? jailed_without_vnet(curthread->td_ucred) + : prison_if(curthread->td_ucred, + rt_key(rt)) != 0) { + RT_UNLOCK(rt); + senderr(ESRCH); + } + info.rti_info[RTAX_DST] = rt_key(rt); + info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; + info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt), + rt_mask(rt), &ss); + info.rti_info[RTAX_GENMASK] = 0; + if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) { + ifp = rt->rt_ifp; + if (ifp) { + info.rti_info[RTAX_IFP] = + ifp->if_addr->ifa_addr; + error = rtm_get_jailed(&info, ifp, rt, + &saun, curthread->td_ucred); + if (error != 0) { + RT_UNLOCK(rt); + senderr(error); + } + if (ifp->if_flags & IFF_POINTOPOINT) + info.rti_info[RTAX_BRD] = + rt->rt_ifa->ifa_dstaddr; + rtm->rtm_index = ifp->if_index; + } else { + info.rti_info[RTAX_IFP] = NULL; + info.rti_info[RTAX_IFA] = NULL; + } + } else if ((ifp = rt->rt_ifp) != NULL) { + rtm->rtm_index = ifp->if_index; + } + + /* Check if we need to realloc storage */ + rtsock_msg_buffer(rtm->rtm_type, &info, NULL, &len); + if (len > maxlen) { + RT_UNLOCK(rt); + senderr(ENOBUFS); + } + + if (len > alloc_len) { + struct rt_msghdr *new_rtm; + new_rtm = malloc(len, M_TEMP, M_NOWAIT); + if (new_rtm == NULL) { + RT_UNLOCK(rt); + senderr(ENOBUFS); + } + bcopy(rtm, new_rtm, rtm->rtm_msglen); + free(rtm, M_TEMP); + rtm = new_rtm; + alloc_len = len; + } + + w.w_tmem = (caddr_t)rtm; + w.w_tmemsize = alloc_len; + rtsock_msg_buffer(rtm->rtm_type, &info, &w, &len); + + if (rt->rt_flags & RTF_GWFLAG_COMPAT) + rtm->rtm_flags = RTF_GATEWAY | + (rt->rt_flags & ~RTF_GWFLAG_COMPAT); + else + rtm->rtm_flags = rt->rt_flags; + rt_getmetrics(rt, &rtm->rtm_rmx); + rtm->rtm_addrs = info.rti_addrs; + + RT_UNLOCK(rt); + break; + + default: + senderr(EOPNOTSUPP); + } + +flush: + if (rt != NULL) + RTFREE(rt); + + if (rtm != NULL) { +#ifdef INET6 + if (rti_need_deembed) { + /* sin6_scope_id is recovered before sending rtm. */ + sin6 = (struct sockaddr_in6 *)&ss; + for (i = 0; i < RTAX_MAX; i++) { + if (info.rti_info[i] == NULL) + continue; + if (info.rti_info[i]->sa_family != AF_INET6) + continue; + bcopy(info.rti_info[i], sin6, sizeof(*sin6)); + if (sa6_recoverscope(sin6) == 0) + bcopy(sin6, info.rti_info[i], + sizeof(*sin6)); + } + } +#endif + if (error != 0) + rtm->rtm_errno = error; + else + rtm->rtm_flags |= RTF_DONE; + + bcopy((caddr_t)rtm, data, rtm->rtm_msglen); + *plen = rtm->rtm_msglen; + free(rtm, M_TEMP); + } + + return (error); +} diff --git a/lib/ff_syscall_wrapper.c b/lib/ff_syscall_wrapper.c index da3f4d3e5..ad1638a29 100644 --- a/lib/ff_syscall_wrapper.c +++ b/lib/ff_syscall_wrapper.c @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -258,7 +259,7 @@ so_opt_convert(int optname) case LINUX_SO_PROTOCOL: return SO_PROTOCOL; default: - return (-1); + return optname; } } @@ -285,7 +286,7 @@ ip_opt_convert(int optname) case LINUX_IP_DROP_MEMBERSHIP: return IP_DROP_MEMBERSHIP; default: - return (-1); + return optname; } } @@ -306,7 +307,7 @@ tcp_opt_convert(int optname) case LINUX_TCP_MD5SIG: return TCP_MD5SIG; default: - return (-1); + return optname; } } @@ -321,7 +322,7 @@ linux2freebsd_opt(int level, int optname) case IPPROTO_TCP: return tcp_opt_convert(optname); default: - return (-1); + return optname; } } @@ -1002,3 +1003,76 @@ kern_fail: ff_os_errno(rc); return (-1); } + +int +ff_route_ctl(enum FF_ROUTE_CTL req, enum FF_ROUTE_FLAG flag, + struct linux_sockaddr *dst, struct linux_sockaddr *gw, + struct linux_sockaddr *netmask) + +{ + struct sockaddr sa_gw, sa_dst, sa_nm; + struct sockaddr *psa_gw, *psa_dst, *psa_nm; + int rtreq, rtflag; + int rc; + + switch (req) { + case FF_ROUTE_ADD: + rtreq = RTM_ADD; + break; + case FF_ROUTE_DEL: + rtreq = RTM_DELETE; + break; + case FF_ROUTE_CHANGE: + rtreq = RTM_CHANGE; + break; + default: + rc = EINVAL; + goto kern_fail; + } + + switch (flag) { + case FF_RTF_HOST: + rtflag = RTF_HOST; + break; + case FF_RTF_GATEWAY: + rtflag = RTF_GATEWAY; + break; + default: + rc = EINVAL; + goto kern_fail; + }; + + if (gw != NULL) { + psa_gw = &sa_gw; + linux2freebsd_sockaddr(gw, sizeof(*gw), psa_gw); + } else { + psa_gw = NULL; + } + + if (dst != NULL) { + psa_dst = &sa_dst; + linux2freebsd_sockaddr(dst, sizeof(*dst), psa_dst); + } else { + psa_dst = NULL; + } + + if (netmask != NULL) { + psa_nm = &sa_nm; + linux2freebsd_sockaddr(netmask, sizeof(*netmask), psa_nm); + } else { + psa_nm = NULL; + } + + rc = rtrequest_fib(rtreq, psa_dst, psa_gw, psa_nm, rtflag, + NULL, RT_DEFAULT_FIB); + + if (rc != 0) + goto kern_fail; + + return (rc); + +kern_fail: + ff_os_errno(rc); + return (-1); +} + diff --git a/tools/Makefile b/tools/Makefile index c0957fb8e..ee47229b5 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,4 +1,4 @@ -SUBDIRS=compat sysctl ifconfig +SUBDIRS=compat sysctl ifconfig route all: for d in $(SUBDIRS); do ( cd $$d; $(MAKE) all ) ; done diff --git a/tools/README.md b/tools/README.md index 4ee666718..1e50054d5 100644 --- a/tools/README.md +++ b/tools/README.md @@ -39,6 +39,52 @@ jail ``` For more details, see [Manual page](https://www.freebsd.org/cgi/man.cgi?ifconfig). +# route +Usage: +``` +route -p [-46dnqtv] command [[modifiers] args] +``` +Examples: +``` + Add a default route: + + ./route -p 0 add -net 0.0.0.0/0 192.168.1.1 + + A shorter version of adding a default route can also be written as: + + ./route -p 0 add default 192.168.1.1 + + Add a static route to the 172.16.10.0/24 network via the 172.16.1.1 gate- + way: + + ./route -p 0 add -net 172.16.10.0/24 172.16.1.1 + + Change the gateway of an already established static route in the routing + table: + + ./route -p 0 change -net 172.16.10.0/24 172.16.1.2 + + Display the route for a destination network: + + ./route -p 0 show 172.16.10.0 + + Delete a static route from the routing table: + + ./route -p 0 delete -net 172.16.10.0/24 172.16.1.2 + + Remove all routes from the routing table: + + ./route -p 0 flush + + FreeBSD uses `netstat -rn ` to list the route table which we havn't ported, + you can execute the following command instead, `-d` means debug mode, `-v` + means verbose. + ./route -p 0 -d -v flush +``` +Note that, if you want to modify the route table, you must use `-p` to execute the same command for each f-stack process. + +For more details, see [Manual page](https://www.freebsd.org/cgi/man.cgi?route). + # how to implement a custom tool for communicating with F-Stack process Add a new FF_MSG_TYPE in ff_msg.h: diff --git a/tools/compat/compat.h b/tools/compat/compat.h index 1016ea480..a84140070 100644 --- a/tools/compat/compat.h +++ b/tools/compat/compat.h @@ -29,6 +29,22 @@ #include +#ifndef __dead2 +#define __dead2 __attribute__((__noreturn__)) +#endif + +#ifndef __dead +#define __dead __dead2 +#endif + +#ifndef nitems +#define nitems(x) (sizeof((x)) / sizeof((x)[0])) +#endif + +#ifndef __FBSDID +#define __FBSDID(s) /* nothing */ +#endif + void *reallocf(void *ptr, size_t size); int feature_present(const char *feature); diff --git a/tools/compat/ff_ipc.c b/tools/compat/ff_ipc.c index 02dad1c05..f659b7407 100644 --- a/tools/compat/ff_ipc.c +++ b/tools/compat/ff_ipc.c @@ -61,7 +61,7 @@ ff_ipc_init(void) char *dpdk_argv[] = { "ff-ipc", "-c1", "-n4", "--proc-type=secondary", - /* RTE_LOG_ERR */ + /* RTE_LOG_WARNING */ "--log-level=5", }; diff --git a/tools/compat/getopt.c b/tools/compat/getopt.c new file mode 100644 index 000000000..070db524a --- /dev/null +++ b/tools/compat/getopt.c @@ -0,0 +1,150 @@ +/* $NetBSD: getopt.c,v 1.29 2014/06/05 22:00:22 christos Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994 + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif /* LIBC_SCCS and not lint */ +#include +#ifndef FSTACK +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#endif +#include +#include +#include +#include +#ifndef FSTACK +#include "un-namespace.h" + +#include "libc_private.h" +#endif + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(int nargc, char * const nargv[], const char *ostr) +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || *place == 0) { /* update scanning pointer */ + optreset = 0; + place = nargv[optind]; + if (optind >= nargc || *place++ != '-') { + /* Argument is absent or is not an option */ + place = EMSG; + return (-1); + } + optopt = *place++; + if (optopt == '-' && *place == 0) { + /* "--" => end of options */ + ++optind; + place = EMSG; + return (-1); + } + if (optopt == 0) { + /* Solitary '-', treat as a '-' option + if the program (eg su) is looking for it. */ + place = EMSG; + if (strchr(ostr, '-') == NULL) + return (-1); + optopt = '-'; + } + } else + optopt = *place++; + + /* See if option letter is one the caller wanted... */ + if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) { + if (*place == 0) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, +#ifndef FSTACK + "%s: illegal option -- %c\n", _getprogname(), +#else + "illegal option -- %c\n", +#endif + optopt); + return (BADCH); + } + + /* Does this option need an argument? */ + if (oli[1] != ':') { + /* don't need argument */ + optarg = NULL; + if (*place == 0) + ++optind; + } else { + /* Option-argument is either the rest of this argument or the + entire next argument. */ + if (*place) + optarg = place; + else if (oli[2] == ':') + /* + * GNU Extension, for optional arguments if the rest of + * the argument is empty, we return NULL + */ + optarg = NULL; + else if (nargc > ++optind) + optarg = nargv[optind]; + else { + /* option-argument absent */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, +#ifndef FSTACK + "%s: option requires an argument -- %c\n", + _getprogname(), optopt); +#else + "option requires an argument -- %c\n", optopt); +#endif + return (BADCH); + } + place = EMSG; + ++optind; + } + return (optopt); /* return option letter */ +} diff --git a/tools/compat/include/arpa/inet.h b/tools/compat/include/arpa/inet.h index 7a56246b0..c3e5461c8 100644 --- a/tools/compat/include/arpa/inet.h +++ b/tools/compat/include/arpa/inet.h @@ -125,6 +125,12 @@ int inet_pton(int, const char * __restrict, void * __restrict); int inet_aton(const char *, struct in_addr *); +in_addr_t inet_lnaof(struct in_addr); +struct in_addr inet_makeaddr(in_addr_t, in_addr_t); +char * inet_neta(in_addr_t, char *, size_t); +in_addr_t inet_netof(struct in_addr); +in_addr_t inet_network(const char *); + #endif /* !_ARPA_INET_H_ */ /*! \file */ diff --git a/tools/compat/include/net/if_arp.h b/tools/compat/include/net/if_arp.h new file mode 100644 index 000000000..455f9542d --- /dev/null +++ b/tools/compat/include/net/if_arp.h @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 1986, 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. + * 4. 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. + * + * @(#)if_arp.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD$ + */ + +#ifndef _NET_IF_ARP_H_ +#define _NET_IF_ARP_H_ + +/* + * Address Resolution Protocol. + * + * See RFC 826 for protocol description. ARP packets are variable + * in size; the arphdr structure defines the fixed-length portion. + * Protocol type values are the same as those for 10 Mb/s Ethernet. + * It is followed by the variable-sized fields ar_sha, arp_spa, + * arp_tha and arp_tpa in that order, according to the lengths + * specified. Field names used correspond to RFC 826. + */ +struct arphdr { + u_short ar_hrd; /* format of hardware address */ +#define ARPHRD_ETHER 1 /* ethernet hardware format */ +#define ARPHRD_IEEE802 6 /* token-ring hardware format */ +#define ARPHRD_ARCNET 7 /* arcnet hardware format */ +#define ARPHRD_FRELAY 15 /* frame relay hardware format */ +#define ARPHRD_IEEE1394 24 /* firewire hardware format */ +#define ARPHRD_INFINIBAND 32 /* infiniband hardware format */ + u_short ar_pro; /* format of protocol address */ + u_char ar_hln; /* length of hardware address */ + u_char ar_pln; /* length of protocol address */ + u_short ar_op; /* one of: */ +#define ARPOP_REQUEST 1 /* request to resolve address */ +#define ARPOP_REPLY 2 /* response to previous request */ +#define ARPOP_REVREQUEST 3 /* request protocol address given hardware */ +#define ARPOP_REVREPLY 4 /* response giving protocol address */ +#define ARPOP_INVREQUEST 8 /* request to identify peer */ +#define ARPOP_INVREPLY 9 /* response identifying peer */ +/* + * The remaining fields are variable in size, + * according to the sizes above. + */ +#ifdef COMMENT_ONLY + u_char ar_sha[]; /* sender hardware address */ + u_char ar_spa[]; /* sender protocol address */ + u_char ar_tha[]; /* target hardware address */ + u_char ar_tpa[]; /* target protocol address */ +#endif +}; + +#define ar_sha(ap) (((caddr_t)((ap)+1)) + 0) +#define ar_spa(ap) (((caddr_t)((ap)+1)) + (ap)->ar_hln) +#define ar_tha(ap) (((caddr_t)((ap)+1)) + (ap)->ar_hln + (ap)->ar_pln) +#define ar_tpa(ap) (((caddr_t)((ap)+1)) + 2*(ap)->ar_hln + (ap)->ar_pln) + +#define arphdr_len2(ar_hln, ar_pln) \ + (sizeof(struct arphdr) + 2*(ar_hln) + 2*(ar_pln)) +#define arphdr_len(ap) (arphdr_len2((ap)->ar_hln, (ap)->ar_pln)) + +/* + * ARP ioctl request + */ +struct arpreq { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ +}; +/* arp_flags and at_flags field values */ +#define ATF_INUSE 0x01 /* entry in use */ +#define ATF_COM 0x02 /* completed entry (enaddr valid) */ +#define ATF_PERM 0x04 /* permanent entry */ +#define ATF_PUBL 0x08 /* publish entry (respond for other host) */ +#define ATF_USETRAILERS 0x10 /* has requested trailers */ + +struct arpstat { + /* Normal things that happen: */ + uint64_t txrequests; /* # of ARP requests sent by this host. */ + uint64_t txreplies; /* # of ARP replies sent by this host. */ + uint64_t rxrequests; /* # of ARP requests received by this host. */ + uint64_t rxreplies; /* # of ARP replies received by this host. */ + uint64_t received; /* # of ARP packets received by this host. */ + + uint64_t arp_spares[4]; /* For either the upper or lower half. */ + /* Abnormal event and error counting: */ + uint64_t dropped; /* # of packets dropped waiting for a reply. */ + uint64_t timeouts; /* # of times with entries removed */ + /* due to timeout. */ + uint64_t dupips; /* # of duplicate IPs detected. */ +}; + +#endif /* !_NET_IF_ARP_H_ */ diff --git a/tools/compat/include/netinet/if_ether.h b/tools/compat/include/netinet/if_ether.h new file mode 100644 index 000000000..aed1e67ee --- /dev/null +++ b/tools/compat/include/netinet/if_ether.h @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 1982, 1986, 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. + * 4. 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. + * + * @(#)if_ether.h 8.3 (Berkeley) 5/2/95 + * $FreeBSD$ + */ + +#ifndef _NETINET_IF_ETHER_H_ +#define _NETINET_IF_ETHER_H_ + +#include +#include + +/* + * Macro to map an IP multicast address to an Ethernet multicast address. + * The high-order 25 bits of the Ethernet address are statically assigned, + * and the low-order 23 bits are taken from the low end of the IP address. + */ +#define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr) \ + /* struct in_addr *ipaddr; */ \ + /* u_char enaddr[ETHER_ADDR_LEN]; */ \ +{ \ + (enaddr)[0] = 0x01; \ + (enaddr)[1] = 0x00; \ + (enaddr)[2] = 0x5e; \ + (enaddr)[3] = ((const u_char *)ipaddr)[1] & 0x7f; \ + (enaddr)[4] = ((const u_char *)ipaddr)[2]; \ + (enaddr)[5] = ((const u_char *)ipaddr)[3]; \ +} +/* + * Macro to map an IP6 multicast address to an Ethernet multicast address. + * The high-order 16 bits of the Ethernet address are statically assigned, + * and the low-order 32 bits are taken from the low end of the IP6 address. + */ +#define ETHER_MAP_IPV6_MULTICAST(ip6addr, enaddr) \ +/* struct in6_addr *ip6addr; */ \ +/* u_char enaddr[ETHER_ADDR_LEN]; */ \ +{ \ + (enaddr)[0] = 0x33; \ + (enaddr)[1] = 0x33; \ + (enaddr)[2] = ((const u_char *)ip6addr)[12]; \ + (enaddr)[3] = ((const u_char *)ip6addr)[13]; \ + (enaddr)[4] = ((const u_char *)ip6addr)[14]; \ + (enaddr)[5] = ((const u_char *)ip6addr)[15]; \ +} + +/* + * Ethernet Address Resolution Protocol. + * + * See RFC 826 for protocol description. Structure below is adapted + * to resolving internet addresses. Field names used correspond to + * RFC 826. + */ +struct ether_arp { + struct arphdr ea_hdr; /* fixed-size header */ + u_char arp_sha[ETHER_ADDR_LEN]; /* sender hardware address */ + u_char arp_spa[4]; /* sender protocol address */ + u_char arp_tha[ETHER_ADDR_LEN]; /* target hardware address */ + u_char arp_tpa[4]; /* target protocol address */ +}; +#define arp_hrd ea_hdr.ar_hrd +#define arp_pro ea_hdr.ar_pro +#define arp_hln ea_hdr.ar_hln +#define arp_pln ea_hdr.ar_pln +#define arp_op ea_hdr.ar_op + +#ifndef BURN_BRIDGES /* Can be used by third party software. */ +struct sockaddr_inarp { + u_char sin_len; + u_char sin_family; + u_short sin_port; + struct in_addr sin_addr; + struct in_addr sin_srcaddr; + u_short sin_tos; + u_short sin_other; +#define SIN_PROXY 1 +}; +#endif /* !BURN_BRIDGES */ + +/* + * IP and ethernet specific routing flags + */ +#define RTF_USETRAILERS RTF_PROTO1 /* use trailers */ +#define RTF_ANNOUNCE RTF_PROTO2 /* announce new arp entry */ + +#endif diff --git a/tools/ifconfig/ifconfig.h b/tools/ifconfig/ifconfig.h index eb33c00f6..816235aff 100644 --- a/tools/ifconfig/ifconfig.h +++ b/tools/ifconfig/ifconfig.h @@ -49,10 +49,6 @@ int fake_close(int fd); #define socket(a, b, c) fake_socket((a), (b), (c)) #define close(a) fake_close((a)) -#ifndef nitems -#define nitems(x) (sizeof((x)) / sizeof((x)[0])) -#endif - #endif struct afswtch; diff --git a/tools/prog.mk b/tools/prog.mk index 517877860..7de1158d7 100644 --- a/tools/prog.mk +++ b/tools/prog.mk @@ -35,7 +35,9 @@ SRCS= ${PROG}.c endif endif -OBJS+= $(patsubst %.cc,%.o,$(patsubst %.c,%.o,${SRCS})) +HEADERS+= $(filter %.h,${SRCS}) +OBJS+= $(patsubst %.c,%.o, $(filter %.c,${SRCS})) +OBJS+= $(patsubst %.cc,%.o, $(filter %.cc,${SRCS})) ifeq ($(FF_DPDK),) FF_DPDK=${TOPDIR}/dpdk/x86_64-native-linuxapp-gcc @@ -57,7 +59,9 @@ CXXFLAGS+= ${FF_PROG_CFLAGS} LIBS+= ${FF_PROG_LIBS} -${PROG}: ${OBJS} +CLEANFILES+= ${PROG} ${OBJS} + +${PROG}: ${HEADERS} ${OBJS} ifdef PROG_CXX ${CXX} ${CXXFLAGS} ${LDFLAGS} -o $@ ${OBJS} ${LIBS} else @@ -65,6 +69,6 @@ else endif clean: - @rm -f ${PROG} ${OBJS} + @rm -f ${CLEANFILES} all: ${PROG} diff --git a/tools/route/Makefile b/tools/route/Makefile new file mode 100644 index 000000000..8c9b73a6f --- /dev/null +++ b/tools/route/Makefile @@ -0,0 +1,29 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 +# $FreeBSD$ + +TOPDIR?=${CURDIR}/../.. +include ${TOPDIR}/tools/opts.mk + +PROG= route +MAN= route.8 +SRCS= route.c keywords.h +WARNS?= 3 +CLEANFILES+=keywords.h + +CFLAGS+= -DNS +ifneq (${MK_INET_SUPPORT},"no") +CFLAGS+= -DINET +endif +ifneq (${MK_INET6_SUPPORT},"no") +CFLAGS+= -DINET6 +endif +CFLAGS+= -I. + +include ${TOPDIR}/tools/prog.mk + +keywords.h: keywords + LC_ALL=C awk '!/^#|^$$/ { \ + printf "#define\tK_%s\t%d\n\t{\"%s\", K_%s},\n", \ + toupper($$1), ++L, $$1, toupper($$1); \ + }' < ${CURDIR}/keywords > $@ + diff --git a/tools/route/keywords b/tools/route/keywords new file mode 100644 index 000000000..82edc4669 --- /dev/null +++ b/tools/route/keywords @@ -0,0 +1,57 @@ +# @(#)keywords 8.2 (Berkeley) 3/19/94 +# $FreeBSD$ + +4 +6 +add +blackhole +change +cloning +del +delete +dst +expire +fib +flush +gateway +genmask +get +host +hopcount +iface +interface +ifa +ifp +inet +inet6 +iso +link +llinfo +lock +lockrest +mask +monitor +mtu +net +netmask +nostatic +nostick +osi +prefixlen +proto1 +proto2 +proxy +recvpipe +reject +rtt +rttvar +sa +sendpipe +show +ssthresh +static +sticky +weight +x25 +xns +xresolve diff --git a/tools/route/route.8 b/tools/route/route.8 new file mode 100644 index 000000000..5e6f78b4e --- /dev/null +++ b/tools/route/route.8 @@ -0,0 +1,506 @@ +.\" Copyright (c) 1983, 1991, 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. +.\" 4. 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. +.\" +.\" @(#)route.8 8.3 (Berkeley) 3/19/94 +.\" $FreeBSD$ +.\" +.Dd November 11, 2014 +.Dt ROUTE 8 +.Os +.Sh NAME +.Nm route +.Nd manually manipulate the routing tables +.Sh SYNOPSIS +.Nm +.Op Fl dnqtv +.Ar command +.Oo +.Op Ar modifiers +.Ar args +.Oc +.Sh DESCRIPTION +The +.Nm +utility is used to manually manipulate the network +routing tables. +It normally is not needed, as a +system routing table management daemon, such as +.Xr routed 8 , +should tend to this task. +.Pp +The +.Nm +utility supports a limited number of general options, +but a rich command language, enabling the user to specify +any arbitrary request that could be delivered via the +programmatic interface discussed in +.Xr route 4 . +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl 4 +Specify +.Cm inet +address family as family hint for subcommands. +.It Fl 6 +Specify +.Cm inet +address family as family hint for subcommands. +.It Fl d +Run in debug-only mode, i.e., do not actually modify the routing table. +.It Fl n +Bypass attempts to print host and network names symbolically +when reporting actions. +(The process of translating between symbolic +names and numerical equivalents can be quite time consuming, and +may require correct operation of the network; thus it may be expedient +to forget this, especially when attempting to repair networking operations). +.It Fl t +Run in test-only mode. +.Pa /dev/null +is used instead of a socket. +.It Fl v +(verbose) Print additional details. +.It Fl q +Suppress all output from the +.Cm add , change , delete , +and +.Cm flush +commands. +.El +.Pp +The +.Nm +utility provides the following commands: +.Pp +.Bl -tag -width Fl -compact +.It Cm add +Add a route. +.It Cm flush +Remove all routes. +.It Cm delete +Delete a specific route. +.It Cm del +Another name for the +.Cm delete +command. +.It Cm change +Change aspects of a route (such as its gateway). +.It Cm get +Lookup and display the route for a destination. +.It Cm monitor +Continuously report any changes to the routing information base, +routing lookup misses, or suspected network partitionings. +.It Cm show +Another name for the +.Cm get +command. +.El +.Pp +The monitor command has the syntax: +.Pp +.Bd -ragged -offset indent -compact +.Nm +.Op Fl n +.Cm monitor Op Fl fib Ar number +.Ed +.Pp +The flush command has the syntax: +.Pp +.Bd -ragged -offset indent -compact +.Nm +.Oo Fl n Cm flush Oc Oo Ar family Oc Op Fl fib Ar number +.Ed +.Pp +If the +.Cm flush +command is specified, +.Nm +will ``flush'' the routing tables of all gateway entries. +When the address family may is specified by any of the +.Fl osi , +.Fl xns , +.Fl inet6 , +or +.Fl inet +modifiers, only routes having destinations with addresses in the +delineated family will be deleted. +Additionally, +.Fl 4 +or +.Fl 6 +can be used as aliases for +.Fl inet +and +.Fl inet6 +modifiers. +When a +.Fl fib +option is specified, the operation will be applied to +the specified FIB +.Pq routing table . +.Pp +The other commands have the following syntax: +.Pp +.Bd -ragged -offset indent -compact +.Nm +.Op Fl n +.Ar command +.Op Fl net No \&| Fl host +.Ar destination gateway +.Op Ar netmask +.Op Fl fib Ar number +.Ed +.Pp +where +.Ar destination +is the destination host or network, +.Ar gateway +is the next-hop intermediary via which packets should be routed. +Routes to a particular host may be distinguished from those to +a network by interpreting the Internet address specified as the +.Ar destination +argument. +The optional modifiers +.Fl net +and +.Fl host +force the destination to be interpreted as a network or a host, respectively. +Otherwise, if the +.Ar destination +has a +.Dq local address part +of +INADDR_ANY +.Pq Li 0.0.0.0 , +or if the +.Ar destination +is the symbolic name of a network, then the route is +assumed to be to a network; otherwise, it is presumed to be a +route to a host. +Optionally, the +.Ar destination +could also be specified in the +.Ar net Ns / Ns Ar bits +format. +.Pp +For example, +.Li 128.32 +is interpreted as +.Fl host Li 128.0.0.32 ; +.Li 128.32.130 +is interpreted as +.Fl host Li 128.32.0.130 ; +.Fl net Li 128.32 +is interpreted as +.Li 128.32.0.0; +.Fl net Li 128.32.130 +is interpreted as +.Li 128.32.130.0; +and +.Li 192.168.64/20 +is interpreted as +.Fl net Li 192.168.64 Fl netmask Li 255.255.240.0 . +.Pp +A +.Ar destination +of +.Ar default +is a synonym for the default route. +For +.Li IPv4 +it is +.Fl net Fl inet Li 0.0.0.0 , +and for +.Li IPv6 +it is +.Fl net Fl inet6 Li :: . +.Pp +If the destination is directly reachable +via an interface requiring +no intermediary system to act as a gateway, the +.Fl interface +modifier should be specified; +the gateway given is the address of this host on the common network, +indicating the interface to be used for transmission. +Alternately, if the interface is point to point the name of the interface +itself may be given, in which case the route remains valid even +if the local or remote addresses change. +.Pp +The optional modifiers +.Fl xns , +.Fl osi , +and +.Fl link +specify that all subsequent addresses are in the +.Tn XNS +or +.Tn OSI +address families, +or are specified as link-level addresses, +and the names must be numeric specifications rather than +symbolic names. +.Pp +The optional +.Fl netmask +modifier is intended +to achieve the effect of an +.Tn OSI +.Tn ESIS +redirect with the netmask option, +or to manually add subnet routes with +netmasks different from that of the implied network interface +(as would otherwise be communicated using the OSPF or ISIS routing protocols). +One specifies an additional ensuing address parameter +(to be interpreted as a network mask). +The implicit network mask generated in the AF_INET case +can be overridden by making sure this option follows the destination parameter. +.Pp +For +.Dv AF_INET6 , +the +.Fl prefixlen +qualifier +is available instead of the +.Fl mask +qualifier because non-continuous masks are not allowed in IPv6. +For example, +.Fl prefixlen Li 32 +specifies network mask of +.Li ffff:ffff:0000:0000:0000:0000:0000:0000 +to be used. +The default value of prefixlen is 64 to get along with +the aggregatable address. +But 0 is assumed if +.Cm default +is specified. +Note that the qualifier works only for +.Dv AF_INET6 +address family. +.Pp +Routes have associated flags which influence operation of the protocols +when sending to destinations matched by the routes. +These flags may be set (or sometimes cleared) +by indicating the following corresponding modifiers: +.Bd -literal +-xresolve RTF_XRESOLVE - emit mesg on use (for external lookup) +-iface ~RTF_GATEWAY - destination is directly reachable +-static RTF_STATIC - manually added route +-nostatic ~RTF_STATIC - pretend route added by kernel or daemon +-reject RTF_REJECT - emit an ICMP unreachable when matched +-blackhole RTF_BLACKHOLE - silently discard pkts (during updates) +-proto1 RTF_PROTO1 - set protocol specific routing flag #1 +-proto2 RTF_PROTO2 - set protocol specific routing flag #2 +.Ed +.Pp +The optional modifiers +.Fl rtt , +.Fl rttvar , +.Fl sendpipe , +.Fl recvpipe , +.Fl mtu , +.Fl hopcount , +.Fl expire , +and +.Fl ssthresh +provide initial values to quantities maintained in the routing entry +by transport level protocols, such as TCP or TP4. +These may be individually locked by preceding each such modifier to +be locked by +the +.Fl lock +meta-modifier, or one can +specify that all ensuing metrics may be locked by the +.Fl lockrest +meta-modifier. +.Pp +Note that +.Fl expire +accepts expiration time of the route as the number of seconds since the +Epoch +.Pq see Xr time 3 . +When the first character of the number is +.Dq + +or +.Dq - , +it is interpreted as a value relative to the current time. +.Pp +The optional modifier +.Fl fib Ar number +specifies that the command will be applied to a non-default FIB. +The +.Ar number +must be smaller than the +.Va net.fibs +.Xr sysctl 8 +MIB. +When this modifier is not specified, +or a negative number is specified, +the default FIB shown in the +.Va net.my_fibnum +.Xr sysctl 8 +MIB will be used. +.Pp +The +.Ar number +allows multiple FIBs by a comma-separeted list and/or range +specification. +The +.Qq Fl fib Li 2,4,6 +means the FIB number 2, 4, and 6. +The +.Qq Fl fib Li 1,3-5,6 +means the 1, 3, 4, 5, and 6. +.Pp +In a +.Cm change +or +.Cm add +command where the destination and gateway are not sufficient to specify +the route (as in the +.Tn ISO +case where several interfaces may have the +same address), the +.Fl ifp +or +.Fl ifa +modifiers may be used to determine the interface or interface address. +.Pp +All symbolic names specified for a +.Ar destination +or +.Ar gateway +are looked up first as a host name using +.Xr gethostbyname 3 . +If this lookup fails, +.Xr getnetbyname 3 +is then used to interpret the name as that of a network. +.Pp +The +.Nm +utility uses a routing socket and the new message types +.Dv RTM_ADD , RTM_DELETE , RTM_GET , +and +.Dv RTM_CHANGE . +As such, only the super-user may modify +the routing tables. +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Add a default route to the network routing table. +This will send all packets for destinations not available in the routing table +to the default gateway at 192.168.1.1: +.Pp +.Dl route add -net 0.0.0.0/0 192.168.1.1 +.Pp +A shorter version of adding a default route can also be written as: +.Pp +.Dl route add default 192.168.1.1 +.Pp +Add a static route to the 172.16.10.0/24 network via the 172.16.1.1 gateway: +.Pp +.Dl route add -net 172.16.10.0/24 172.16.1.1 +.Pp +Change the gateway of an already established static route in the routing table: +.Pp +.Dl route change -net 172.16.10.0/24 172.16.1.2 +.Pp +Display the route for a destination network: +.Pp +.Dl route show 172.16.10.0 +.Pp +Delete a static route from the routing table: +.Pp +.Dl route delete -net 172.16.10.0/24 172.16.1.2 +.Pp +Remove all routes from the routing table: +.Pp +.Dl route flush +.Sh DIAGNOSTICS +.Bl -diag +.It "add [host \&| network ] %s: gateway %s flags %x" +The specified route is being added to the tables. +The +values printed are from the routing table entry supplied +in the +.Xr ioctl 2 +call. +If the gateway address used was not the primary address of the gateway +(the first one returned by +.Xr gethostbyname 3 ) , +the gateway address is printed numerically as well as symbolically. +.It "delete [ host \&| network ] %s: gateway %s flags %x" +As above, but when deleting an entry. +.It "%s %s done" +When the +.Cm flush +command is specified, each routing table entry deleted +is indicated with a message of this form. +.It "Network is unreachable" +An attempt to add a route failed because the gateway listed was not +on a directly-connected network. +The next-hop gateway must be given. +.It "not in table" +A delete operation was attempted for an entry which +was not present in the tables. +.It "routing table overflow" +An add operation was attempted, but the system was +low on resources and was unable to allocate memory +to create the new entry. +.It "gateway uses the same route" +A +.Cm change +operation resulted in a route whose gateway uses the +same route as the one being changed. +The next-hop gateway should be reachable through a different route. +.El +.Sh SEE ALSO +.\".Xr esis 4 , +.Xr netintro 4 , +.Xr route 4 , +.Xr arp 8 , +.Xr routed 8 +.\".Xr XNSrouted 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.2 . +.Sh BUGS +The first paragraph may have slightly exaggerated +.Xr routed 8 Ns 's +abilities. +.Pp +Currently, routes with the +.Dv RTF_BLACKHOLE +flag set need to have the gateway set to an instance of the +.Xr lo 4 +driver, using the +.Fl iface +option, for the flag to have any effect; unless IP fast forwarding +is enabled, in which case the meaning of the flag will always +be honored. diff --git a/tools/route/route.c b/tools/route/route.c new file mode 100644 index 000000000..bbd4a8000 --- /dev/null +++ b/tools/route/route.c @@ -0,0 +1,2110 @@ +/* + * Copyright (c) 1983, 1989, 1991, 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. + * 4. 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 copyright[] = +"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95"; +#endif +#endif /* not lint */ + +#include +#ifndef FSTACK +__FBSDID("$FreeBSD$"); +#endif + +#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 + +#ifdef FSTACK +#include "compat.h" +#include "ff_ipc.h" + +static int shutdown_rd; +static int sofib; + +static int +fake_socket(int domain, int type, int protocol) +{ + return 0; +} + +static int +fake_shutdown(int fd, int how) +{ + if (how == SHUT_RD) { + shutdown_rd = 1; + } + return 0; +} + +static int +fake_setsockopt(int sockfd, int level, int optname, + const void *optval, socklen_t optlen) +{ + return 0; +} + +static int +rtioctl(char *data, unsigned len, unsigned read_len) +{ + struct ff_msg *msg, *retmsg = NULL; + unsigned maxlen; + + msg = ff_ipc_msg_alloc(); + if (msg == NULL) { + errno = ENOMEM; + return -1; + } + + if (len > msg->buf_len) { + errno = EINVAL; + ff_ipc_msg_free(msg); + return -1; + } + + if (read_len > msg->buf_len) { + read_len = msg->buf_len; + } + + maxlen = read_len ? read_len : len; + + msg->msg_type = FF_ROUTE; + msg->route.fib = sofib; + msg->route.len = len; + msg->route.maxlen = maxlen; + msg->route.data = msg->buf_addr; + memcpy(msg->route.data, data, len); + msg->buf_addr += len; + + int ret = ff_ipc_send(msg); + if (ret < 0) { + errno = EPIPE; + ff_ipc_msg_free(msg); + return -1; + } + + do { + if (retmsg != NULL) { + ff_ipc_msg_free(retmsg); + } + ret = ff_ipc_recv(&retmsg); + if (ret < 0) { + errno = EPIPE; + ff_ipc_msg_free(msg); + return -1; + } + } while (msg != retmsg); + + if (retmsg->result == 0) { + ret = retmsg->route.len; + + if (!shutdown_rd && read_len > 0) { + memcpy(data, retmsg->route.data, retmsg->route.len); + } + } else { + ret = -1; + errno = retmsg->result; + } + + ff_ipc_msg_free(msg); + + return ret; +} + +#define socket(a, b, c) fake_socket((a), (b), (c)) +#define shutdown(a, b) fake_shutdown((a), (b)) +#define setsockopt(a, b, c, d, e) fake_setsockopt((a), (b), (c), (d), (e)) + +#define write(a, b, c) rtioctl((b), (c), (0)) + +#define CLOCK_REALTIME_FAST CLOCK_REALTIME + +#endif + +struct fibl { + TAILQ_ENTRY(fibl) fl_next; + + int fl_num; + int fl_error; + int fl_errno; +}; + +static struct keytab { + const char *kt_cp; + int kt_i; +} const keywords[] = { +#include "keywords.h" + {0, 0} +}; + +static struct sockaddr_storage so[RTAX_MAX]; +static int pid, rtm_addrs; +static int s; +#ifndef FSTACK +static int nflag, af, qflag, tflag; +#else +static int nflag = 1; +static int af, qflag, tflag; +#endif +static int verbose, aflen; +static int locking, lockrest, debugonly; +static struct rt_metrics rt_metrics; +static u_long rtm_inits; +static uid_t uid; +static int defaultfib; +static int numfibs; +static char domain[MAXHOSTNAMELEN + 1]; +static bool domain_initialized; +static int rtm_seq; +static char rt_line[NI_MAXHOST]; +static char net_line[MAXHOSTNAMELEN + 1]; + +static struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +static TAILQ_HEAD(fibl_head_t, fibl) fibl_head; + +static void printb(int, const char *); +static void flushroutes(int argc, char *argv[]); +static int flushroutes_fib(int); +static int getaddr(int, char *, struct hostent **, int); +static int keyword(const char *); +#ifdef INET +static void inet_makenetandmask(u_long, struct sockaddr_in *, + struct sockaddr_in *, u_long); +#endif +#ifdef INET6 +static int inet6_makenetandmask(struct sockaddr_in6 *, const char *); +#endif +#ifndef FSTACK +static void interfaces(void); +static void monitor(int, char*[]); +#endif +static const char *netname(struct sockaddr *); +static void newroute(int, char **); +static int newroute_fib(int, char *, int); +static void pmsg_addrs(char *, int, size_t); +static void pmsg_common(struct rt_msghdr *, size_t); +static int prefixlen(const char *); +static void print_getmsg(struct rt_msghdr *, int, int); +static void print_rtmsg(struct rt_msghdr *, size_t); +static const char *routename(struct sockaddr *); +static int rtmsg(int, int, int); +static void set_metric(char *, int); +static int set_sofib(int); +static void sockaddr(char *, struct sockaddr *, size_t); +static void sodump(struct sockaddr *, const char *); +static int fiboptlist_csv(const char *, struct fibl_head_t *); +static int fiboptlist_range(const char *, struct fibl_head_t *); + +static void usage(const char *) __dead2; + +static void +usage(const char *cp) +{ + if (cp != NULL) + warnx("bad keyword: %s", cp); +#ifndef FSTACK + errx(EX_USAGE, "usage: route [-46dnqtv] command [[modifiers] args]"); +#else + errx(EX_USAGE, "usage: route -p [-46dnqtv] command [[modifiers] args]"); +#endif + /* NOTREACHED */ +} + +int +main(int argc, char **argv) +{ + int ch; + size_t len; + + if (argc < 2) + usage(NULL); + +#ifndef FSTACK + while ((ch = getopt(argc, argv, "46nqdtv")) != -1) +#else + while ((ch = getopt(argc, argv, "46nqdtvp:")) != -1) +#endif + switch(ch) { + case '4': +#ifdef INET + af = AF_INET; + aflen = sizeof(struct sockaddr_in); +#else + errx(1, "IPv4 support is not compiled in"); +#endif + break; + case '6': +#ifdef INET6 + af = AF_INET6; + aflen = sizeof(struct sockaddr_in6); +#else + errx(1, "IPv6 support is not compiled in"); +#endif + break; + case 'n': + nflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'v': + verbose = 1; + break; + case 't': + tflag = 1; + break; + case 'd': + debugonly = 1; + break; +#ifdef FSTACK + case 'p': + ff_set_proc_id(atoi(optarg)); + break; +#endif + case '?': + default: + usage(NULL); + } + argc -= optind; + argv += optind; + + pid = getpid(); + uid = geteuid(); + if (tflag) + s = open(_PATH_DEVNULL, O_WRONLY, 0); + else + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + err(EX_OSERR, "socket"); + + len = sizeof(numfibs); + if (sysctlbyname("net.fibs", (void *)&numfibs, &len, NULL, 0) == -1) + numfibs = -1; + + len = sizeof(defaultfib); + if (numfibs != -1 && + sysctlbyname("net.my_fibnum", (void *)&defaultfib, &len, NULL, + 0) == -1) + defaultfib = -1; + + if (*argv != NULL) + switch (keyword(*argv)) { + case K_GET: + case K_SHOW: + uid = 0; + /* FALLTHROUGH */ + + case K_CHANGE: + case K_ADD: + case K_DEL: + case K_DELETE: + newroute(argc, argv); + /* NOTREACHED */ + + case K_MONITOR: +#ifndef FSTACK + monitor(argc, argv); +#else + usage(*argv); +#endif + /* NOTREACHED */ + + case K_FLUSH: + flushroutes(argc, argv); + exit(0); + /* NOTREACHED */ + } + usage(*argv); + /* NOTREACHED */ +} + +static int +set_sofib(int fib) +{ + + if (fib < 0) + return (0); +#ifdef FSTACK + sofib = fib; +#endif + return (setsockopt(s, SOL_SOCKET, SO_SETFIB, (void *)&fib, + sizeof(fib))); +} + +static int +fiboptlist_range(const char *arg, struct fibl_head_t *flh) +{ + struct fibl *fl; + char *str0, *str, *token, *endptr; + int fib[2], i, error; + + str0 = str = strdup(arg); + error = 0; + i = 0; + while ((token = strsep(&str, "-")) != NULL) { + switch (i) { + case 0: + case 1: + errno = 0; + fib[i] = strtol(token, &endptr, 0); + if (errno == 0) { + if (*endptr != '\0' || + fib[i] < 0 || + (numfibs != -1 && fib[i] > numfibs - 1)) + errno = EINVAL; + } + if (errno) + error = 1; + break; + default: + error = 1; + } + if (error) + goto fiboptlist_range_ret; + i++; + } + if (fib[0] >= fib[1]) { + error = 1; + goto fiboptlist_range_ret; + } + for (i = fib[0]; i <= fib[1]; i++) { + fl = calloc(1, sizeof(*fl)); + if (fl == NULL) { + error = 1; + goto fiboptlist_range_ret; + } + fl->fl_num = i; + TAILQ_INSERT_TAIL(flh, fl, fl_next); + } +fiboptlist_range_ret: + free(str0); + return (error); +} + +#define ALLSTRLEN 64 +static int +fiboptlist_csv(const char *arg, struct fibl_head_t *flh) +{ + struct fibl *fl; + char *str0, *str, *token, *endptr; + int fib, error; + + str0 = str = NULL; + if (strcmp("all", arg) == 0) { + str = calloc(1, ALLSTRLEN); + if (str == NULL) { + error = 1; + goto fiboptlist_csv_ret; + } + if (numfibs > 1) + snprintf(str, ALLSTRLEN - 1, "%d-%d", 0, numfibs - 1); + else + snprintf(str, ALLSTRLEN - 1, "%d", 0); + } else if (strcmp("default", arg) == 0) { + str0 = str = calloc(1, ALLSTRLEN); + if (str == NULL) { + error = 1; + goto fiboptlist_csv_ret; + } + snprintf(str, ALLSTRLEN - 1, "%d", defaultfib); + } else + str0 = str = strdup(arg); + + error = 0; + while ((token = strsep(&str, ",")) != NULL) { + if (*token != '-' && strchr(token, '-') != NULL) { + error = fiboptlist_range(token, flh); + if (error) + goto fiboptlist_csv_ret; + } else { + errno = 0; + fib = strtol(token, &endptr, 0); + if (errno == 0) { + if (*endptr != '\0' || + fib < 0 || + (numfibs != -1 && fib > numfibs - 1)) + errno = EINVAL; + } + if (errno) { + error = 1; + goto fiboptlist_csv_ret; + } + fl = calloc(1, sizeof(*fl)); + if (fl == NULL) { + error = 1; + goto fiboptlist_csv_ret; + } + fl->fl_num = fib; + TAILQ_INSERT_TAIL(flh, fl, fl_next); + } + } +fiboptlist_csv_ret: + if (str0 != NULL) + free(str0); + return (error); +} + +/* + * Purge all entries in the routing tables not + * associated with network interfaces. + */ +static void +flushroutes(int argc, char *argv[]) +{ + struct fibl *fl; + int error; + + if (uid != 0 && !debugonly && !tflag) + errx(EX_NOPERM, "must be root to alter routing table"); + shutdown(s, SHUT_RD); /* Don't want to read back our messages */ + + TAILQ_INIT(&fibl_head); + while (argc > 1) { + argc--; + argv++; + if (**argv != '-') + usage(*argv); + switch (keyword(*argv + 1)) { +#ifdef INET + case K_4: + case K_INET: + af = AF_INET; + break; +#endif +#ifdef INET6 + case K_6: + case K_INET6: + af = AF_INET6; + break; +#endif + case K_LINK: + af = AF_LINK; + break; + case K_FIB: + if (!--argc) + usage(*argv); + error = fiboptlist_csv(*++argv, &fibl_head); + if (error) + errx(EX_USAGE, "invalid fib number: %s", *argv); + break; + default: + usage(*argv); + } + } + if (TAILQ_EMPTY(&fibl_head)) { + error = fiboptlist_csv("default", &fibl_head); + if (error) + errx(EX_OSERR, "fiboptlist_csv failed."); + } + TAILQ_FOREACH(fl, &fibl_head, fl_next) + flushroutes_fib(fl->fl_num); +} + +static int +flushroutes_fib(int fib) +{ + struct rt_msghdr *rtm; + size_t needed; + char *buf, *next, *lim; + int mib[7], rlen, seqno, count = 0; + int error; + + error = set_sofib(fib); + if (error) { + warn("fib number %d is ignored", fib); + return (error); + } + +retry: + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = AF_UNSPEC; + mib[4] = NET_RT_DUMP; + mib[5] = 0; /* no flags */ + mib[6] = fib; + if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) + err(EX_OSERR, "route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + errx(EX_OSERR, "malloc failed"); + if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) { + if (errno == ENOMEM && count++ < 10) { + warnx("Routing table grew, retrying"); + sleep(1); + free(buf); + goto retry; + } + err(EX_OSERR, "route-sysctl-get"); + } + lim = buf + needed; + if (verbose) + (void)printf("Examining routing table from sysctl\n"); + seqno = 0; /* ??? */ + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)next; + if (verbose) + print_rtmsg(rtm, rtm->rtm_msglen); + if ((rtm->rtm_flags & RTF_GATEWAY) == 0) + continue; + if (af != 0) { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + + if (sa->sa_family != af) + continue; + } + if (debugonly) + continue; + rtm->rtm_type = RTM_DELETE; + rtm->rtm_seq = seqno; + rlen = write(s, next, rtm->rtm_msglen); + if (rlen < 0 && errno == EPERM) + err(1, "write to routing socket"); + if (rlen < (int)rtm->rtm_msglen) { + warn("write to routing socket"); + (void)printf("got only %d for rlen\n", rlen); + free(buf); + goto retry; + break; + } + seqno++; + if (qflag) + continue; + if (verbose) + print_rtmsg(rtm, rlen); + else { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + + printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ? + routename(sa) : netname(sa)); + sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa); + printf("%-20.20s ", routename(sa)); + if (fib >= 0) + printf("-fib %-3d ", fib); + printf("done\n"); + } + } + return (error); +} + +static const char * +routename(struct sockaddr *sa) +{ + struct sockaddr_dl *sdl; + const char *cp; + int n; + + if (!domain_initialized) { + domain_initialized = true; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = strchr(domain, '.'))) { + domain[MAXHOSTNAMELEN] = '\0'; + (void)strcpy(domain, cp + 1); + } else + domain[0] = '\0'; + } + + /* If the address is zero-filled, use "default". */ + if (sa->sa_len == 0 && nflag == 0) + return ("default"); +#if defined(INET) || defined(INET6) + switch (sa->sa_family) { +#ifdef INET + case AF_INET: + /* If the address is zero-filled, use "default". */ + if (nflag == 0 && + ((struct sockaddr_in *)(void *)sa)->sin_addr.s_addr == + INADDR_ANY) + return("default"); + break; +#endif +#ifdef INET6 + case AF_INET6: + /* If the address is zero-filled, use "default". */ + if (nflag == 0 && + IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(void *)sa)->sin6_addr)) + return("default"); + break; +#endif + } +#endif + + switch (sa->sa_family) { +#if defined(INET) || defined(INET6) +#ifdef INET + case AF_INET: +#endif +#ifdef INET6 + case AF_INET6: +#endif + { + struct sockaddr_storage ss; + int error; + char *p; + + memset(&ss, 0, sizeof(ss)); + if (sa->sa_len == 0) + ss.ss_family = sa->sa_family; + else + memcpy(&ss, sa, sa->sa_len); + /* Expand sa->sa_len because it could be shortened. */ + if (sa->sa_family == AF_INET) + ss.ss_len = sizeof(struct sockaddr_in); + else if (sa->sa_family == AF_INET6) + ss.ss_len = sizeof(struct sockaddr_in6); +#ifndef FSTACK + error = getnameinfo((struct sockaddr *)&ss, ss.ss_len, + rt_line, sizeof(rt_line), NULL, 0, + (nflag == 0) ? 0 : NI_NUMERICHOST); +#else + error = 0; + struct sockaddr_in *sin = (struct sockaddr_in *)&ss; + const char *dst = inet_ntop(AF_INET, &sin->sin_addr, rt_line, sizeof(rt_line)); + if (dst == NULL) { + error = EAI_NONAME; + } +#endif + if (error) { + warnx("getnameinfo(): %s", gai_strerror(error)); + strncpy(rt_line, "invalid", sizeof(rt_line)); + } + + /* Remove the domain part if any. */ + p = strchr(rt_line, '.'); + if (p != NULL && strcmp(p + 1, domain) == 0) + *p = '\0'; + + return (rt_line); + break; + } +#endif + case AF_LINK: + sdl = (struct sockaddr_dl *)(void *)sa; + + if (sdl->sdl_nlen == 0 && + sdl->sdl_alen == 0 && + sdl->sdl_slen == 0) { + n = snprintf(rt_line, sizeof(rt_line), "link#%d", + sdl->sdl_index); + if (n > (int)sizeof(rt_line)) + rt_line[0] = '\0'; + return (rt_line); + } else + return (link_ntoa(sdl)); + break; + + default: + { + u_short *sp = (u_short *)(void *)sa; + u_short *splim = sp + ((sa->sa_len + 1) >> 1); + char *cps = rt_line + sprintf(rt_line, "(%d)", sa->sa_family); + char *cpe = rt_line + sizeof(rt_line); + + while (++sp < splim && cps < cpe) /* start with sa->sa_data */ + if ((n = snprintf(cps, cpe - cps, " %x", *sp)) > 0) + cps += n; + else + *cps = '\0'; + break; + } + } + return (rt_line); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be that of a net, not a host. + */ +static const char * +netname(struct sockaddr *sa) +{ + struct sockaddr_dl *sdl; + int n; +#ifdef INET +#ifndef FSTACK + struct netent *np = NULL; + const char *cp = NULL; + u_long i; +#else + const char *cp = NULL; +#endif +#endif + + switch (sa->sa_family) { +#ifdef INET + case AF_INET: + { + struct in_addr in; + + in = ((struct sockaddr_in *)(void *)sa)->sin_addr; +#ifndef FSTACK + i = in.s_addr = ntohl(in.s_addr); +#else + in.s_addr = ntohl(in.s_addr); +#endif + if (in.s_addr == 0) + cp = "default"; +#ifndef FSTACK + else if (!nflag) { + np = getnetbyaddr(i, AF_INET); + if (np != NULL) + cp = np->n_name; + } +#endif +#define C(x) (unsigned)((x) & 0xff) + if (cp != NULL) + strncpy(net_line, cp, sizeof(net_line)); + else if ((in.s_addr & 0xffffff) == 0) + (void)sprintf(net_line, "%u", C(in.s_addr >> 24)); + else if ((in.s_addr & 0xffff) == 0) + (void)sprintf(net_line, "%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16)); + else if ((in.s_addr & 0xff) == 0) + (void)sprintf(net_line, "%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8)); + else + (void)sprintf(net_line, "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), + C(in.s_addr)); +#undef C + break; + } +#endif +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; + int niflags = 0; + + memset(&sin6, 0, sizeof(sin6)); + memcpy(&sin6, sa, sa->sa_len); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + if (nflag) + niflags |= NI_NUMERICHOST; + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + net_line, sizeof(net_line), NULL, 0, niflags) != 0) + strncpy(net_line, "invalid", sizeof(net_line)); + + return(net_line); + } +#endif + case AF_LINK: + sdl = (struct sockaddr_dl *)(void *)sa; + + if (sdl->sdl_nlen == 0 && + sdl->sdl_alen == 0 && + sdl->sdl_slen == 0) { + n = snprintf(net_line, sizeof(net_line), "link#%d", + sdl->sdl_index); + if (n > (int)sizeof(net_line)) + net_line[0] = '\0'; + return (net_line); + } else + return (link_ntoa(sdl)); + break; + + default: + { + u_short *sp = (u_short *)(void *)sa->sa_data; + u_short *splim = sp + ((sa->sa_len + 1)>>1); + char *cps = net_line + sprintf(net_line, "af %d:", sa->sa_family); + char *cpe = net_line + sizeof(net_line); + + while (sp < splim && cps < cpe) + if ((n = snprintf(cps, cpe - cps, " %x", *sp++)) > 0) + cps += n; + else + *cps = '\0'; + break; + } + } + return (net_line); +} + +static void +set_metric(char *value, int key) +{ + int flag = 0; + char *endptr; + u_long noval, *valp = &noval; + + switch (key) { +#define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break + caseof(K_MTU, RTV_MTU, rmx_mtu); + caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); + caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); + caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); + caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); + caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); + caseof(K_RTT, RTV_RTT, rmx_rtt); + caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); + caseof(K_WEIGHT, RTV_WEIGHT, rmx_weight); + } + rtm_inits |= flag; + if (lockrest || locking) + rt_metrics.rmx_locks |= flag; + if (locking) + locking = 0; + errno = 0; + *valp = strtol(value, &endptr, 0); + if (errno == 0 && *endptr != '\0') + errno = EINVAL; + if (errno) + err(EX_USAGE, "%s", value); + if (flag & RTV_EXPIRE && (value[0] == '+' || value[0] == '-')) { + struct timespec ts; + + clock_gettime(CLOCK_REALTIME_FAST, &ts); + *valp += ts.tv_sec; + } +} + +#define F_ISHOST 0x01 +#define F_FORCENET 0x02 +#define F_FORCEHOST 0x04 +#define F_PROXY 0x08 +#define F_INTERFACE 0x10 + +static void +newroute(int argc, char **argv) +{ + struct hostent *hp; + struct fibl *fl; + char *cmd; + const char *dest, *gateway, *errmsg; + int key, error, flags, nrflags, fibnum; + + if (uid != 0 && !debugonly && !tflag) + errx(EX_NOPERM, "must be root to alter routing table"); + dest = NULL; + gateway = NULL; + flags = RTF_STATIC; + nrflags = 0; + hp = NULL; + TAILQ_INIT(&fibl_head); + + cmd = argv[0]; + if (*cmd != 'g' && *cmd != 's') + shutdown(s, SHUT_RD); /* Don't want to read back our messages */ + while (--argc > 0) { + if (**(++argv)== '-') { + switch (key = keyword(1 + *argv)) { + case K_LINK: + af = AF_LINK; + aflen = sizeof(struct sockaddr_dl); + break; +#ifdef INET + case K_4: + case K_INET: + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + break; +#endif +#ifdef INET6 + case K_6: + case K_INET6: + af = AF_INET6; + aflen = sizeof(struct sockaddr_in6); + break; +#endif + case K_SA: + af = PF_ROUTE; + aflen = sizeof(struct sockaddr_storage); + break; + case K_IFACE: + case K_INTERFACE: + nrflags |= F_INTERFACE; + break; + case K_NOSTATIC: + flags &= ~RTF_STATIC; + break; + case K_LOCK: + locking = 1; + break; + case K_LOCKREST: + lockrest = 1; + break; + case K_HOST: + nrflags |= F_FORCEHOST; + break; + case K_REJECT: + flags |= RTF_REJECT; + break; + case K_BLACKHOLE: + flags |= RTF_BLACKHOLE; + break; + case K_PROTO1: + flags |= RTF_PROTO1; + break; + case K_PROTO2: + flags |= RTF_PROTO2; + break; + case K_PROXY: + nrflags |= F_PROXY; + break; + case K_XRESOLVE: + flags |= RTF_XRESOLVE; + break; + case K_STATIC: + flags |= RTF_STATIC; + break; + case K_STICKY: + flags |= RTF_STICKY; + break; + case K_NOSTICK: + flags &= ~RTF_STICKY; + break; + case K_FIB: + if (!--argc) + usage(NULL); + error = fiboptlist_csv(*++argv, &fibl_head); + if (error) + errx(EX_USAGE, + "invalid fib number: %s", *argv); + break; + case K_IFA: + if (!--argc) + usage(NULL); + getaddr(RTAX_IFA, *++argv, 0, nrflags); + break; + case K_IFP: + if (!--argc) + usage(NULL); + getaddr(RTAX_IFP, *++argv, 0, nrflags); + break; + case K_GENMASK: + if (!--argc) + usage(NULL); + getaddr(RTAX_GENMASK, *++argv, 0, nrflags); + break; + case K_GATEWAY: + if (!--argc) + usage(NULL); + getaddr(RTAX_GATEWAY, *++argv, 0, nrflags); + gateway = *argv; + break; + case K_DST: + if (!--argc) + usage(NULL); + if (getaddr(RTAX_DST, *++argv, &hp, nrflags)) + nrflags |= F_ISHOST; + dest = *argv; + break; + case K_NETMASK: + if (!--argc) + usage(NULL); + getaddr(RTAX_NETMASK, *++argv, 0, nrflags); + /* FALLTHROUGH */ + case K_NET: + nrflags |= F_FORCENET; + break; + case K_PREFIXLEN: + if (!--argc) + usage(NULL); + if (prefixlen(*++argv) == -1) { + nrflags &= ~F_FORCENET; + nrflags |= F_ISHOST; + } else { + nrflags |= F_FORCENET; + nrflags &= ~F_ISHOST; + } + break; + case K_MTU: + case K_HOPCOUNT: + case K_EXPIRE: + case K_RECVPIPE: + case K_SENDPIPE: + case K_SSTHRESH: + case K_RTT: + case K_RTTVAR: + case K_WEIGHT: + if (!--argc) + usage(NULL); + set_metric(*++argv, key); + break; + default: + usage(1+*argv); + } + } else { + if ((rtm_addrs & RTA_DST) == 0) { + dest = *argv; + if (getaddr(RTAX_DST, *argv, &hp, nrflags)) + nrflags |= F_ISHOST; + } else if ((rtm_addrs & RTA_GATEWAY) == 0) { + gateway = *argv; + getaddr(RTAX_GATEWAY, *argv, &hp, nrflags); + } else { + getaddr(RTAX_NETMASK, *argv, 0, nrflags); + nrflags |= F_FORCENET; + } + } + } + + /* Do some sanity checks on resulting request */ + if (so[RTAX_DST].ss_len == 0) { + warnx("destination parameter required"); + usage(NULL); + } + + if (so[RTAX_NETMASK].ss_len != 0 && + so[RTAX_DST].ss_family != so[RTAX_NETMASK].ss_family) { + warnx("destination and netmask family need to be the same"); + usage(NULL); + } + + if (nrflags & F_FORCEHOST) { + nrflags |= F_ISHOST; +#ifdef INET6 + if (af == AF_INET6) { + rtm_addrs &= ~RTA_NETMASK; + memset(&so[RTAX_NETMASK], 0, sizeof(so[RTAX_NETMASK])); + } +#endif + } + if (nrflags & F_FORCENET) + nrflags &= ~F_ISHOST; + flags |= RTF_UP; + if (nrflags & F_ISHOST) + flags |= RTF_HOST; + if ((nrflags & F_INTERFACE) == 0) + flags |= RTF_GATEWAY; + if (nrflags & F_PROXY) + flags |= RTF_ANNOUNCE; + if (dest == NULL) + dest = ""; + if (gateway == NULL) + gateway = ""; + + if (TAILQ_EMPTY(&fibl_head)) { + error = fiboptlist_csv("default", &fibl_head); + if (error) + errx(EX_OSERR, "fiboptlist_csv failed."); + } + error = 0; + TAILQ_FOREACH(fl, &fibl_head, fl_next) { + fl->fl_error = newroute_fib(fl->fl_num, cmd, flags); + if (fl->fl_error) + fl->fl_errno = errno; + error += fl->fl_error; + } + if (*cmd == 'g' || *cmd == 's') + exit(error); + + error = 0; + if (!qflag) { + fibnum = 0; + TAILQ_FOREACH(fl, &fibl_head, fl_next) { + if (fl->fl_error == 0) + fibnum++; + } + if (fibnum > 0) { + int firstfib = 1; + + printf("%s %s %s", cmd, + (nrflags & F_ISHOST) ? "host" : "net", dest); + if (*gateway) + printf(": gateway %s", gateway); + + if (numfibs > 1) { + TAILQ_FOREACH(fl, &fibl_head, fl_next) { + if (fl->fl_error == 0 + && fl->fl_num >= 0) { + if (firstfib) { + printf(" fib "); + firstfib = 0; + } + printf("%d", fl->fl_num); + if (fibnum-- > 1) + printf(","); + } + } + } + printf("\n"); + } + + fibnum = 0; + TAILQ_FOREACH(fl, &fibl_head, fl_next) { + if (fl->fl_error != 0) { + printf("%s %s %s", cmd, (nrflags & F_ISHOST) + ? "host" : "net", dest); + if (*gateway) + printf(": gateway %s", gateway); + + if (fl->fl_num >= 0) + printf(" fib %d", fl->fl_num); + + switch (fl->fl_errno) { + case ESRCH: + errmsg = "not in table"; + break; + case EBUSY: + errmsg = "entry in use"; + break; + case ENOBUFS: + errmsg = "not enough memory"; + break; + case EADDRINUSE: + /* + * handle recursion avoidance + * in rt_setgate() + */ + errmsg = "gateway uses the same route"; + break; + case EEXIST: + errmsg = "route already in table"; + break; + default: + errmsg = strerror(fl->fl_errno); + break; + } + printf(": %s\n", errmsg); + error = 1; + } + } + } + exit(error); +} + +static int +newroute_fib(int fib, char *cmd, int flags) +{ + int error; + + error = set_sofib(fib); + if (error) { + warn("fib number %d is ignored", fib); + return (error); + } + + error = rtmsg(*cmd, flags, fib); + return (error); +} + +#ifdef INET +static void +inet_makenetandmask(u_long net, struct sockaddr_in *sin, + struct sockaddr_in *sin_mask, u_long bits) +{ + u_long mask = 0; + + rtm_addrs |= RTA_NETMASK; + + /* + * MSB of net should be meaningful. 0/0 is exception. + */ + if (net > 0) + while ((net & 0xff000000) == 0) + net <<= 8; + + /* + * If no /xx was specified we must calculate the + * CIDR address. + */ + if ((bits == 0) && (net != 0)) { + u_long i, j; + + for(i = 0, j = 0xff; i < 4; i++) { + if (net & j) { + break; + } + j <<= 8; + } + /* i holds the first non zero bit */ + bits = 32 - (i*8); + } + if (bits != 0) + mask = 0xffffffff << (32 - bits); + + sin->sin_addr.s_addr = htonl(net); + sin_mask->sin_addr.s_addr = htonl(mask); + sin_mask->sin_len = sizeof(struct sockaddr_in); + sin_mask->sin_family = AF_INET; +} +#endif + +#ifdef INET6 +/* + * XXX the function may need more improvement... + */ +static int +inet6_makenetandmask(struct sockaddr_in6 *sin6, const char *plen) +{ + + if (plen == NULL) { + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) && + sin6->sin6_scope_id == 0) + plen = "0"; + } + + if (plen == NULL || strcmp(plen, "128") == 0) + return (1); + rtm_addrs |= RTA_NETMASK; + prefixlen(plen); + return (0); +} +#endif + +/* + * Interpret an argument as a network address of some kind, + * returning 1 if a host address, 0 if a network address. + */ +static int +getaddr(int idx, char *str, struct hostent **hpp, int nrflags) +{ + struct sockaddr *sa; +#if defined(INET) + struct sockaddr_in *sin; + struct hostent *hp; +#ifndef FSTACK + struct netent *np; +#endif + u_long val; + char *q; +#elif defined(INET6) + char *q; +#endif + + if (idx < 0 || idx >= RTAX_MAX) + usage("internal error"); + if (af == 0) { +#if defined(INET) + af = AF_INET; + aflen = sizeof(struct sockaddr_in); +#elif defined(INET6) + af = AF_INET6; + aflen = sizeof(struct sockaddr_in6); +#else + af = AF_LINK; + aflen = sizeof(struct sockaddr_dl); +#endif + } +#ifndef INET + hpp = NULL; +#endif + rtm_addrs |= (1 << idx); + sa = (struct sockaddr *)&so[idx]; + sa->sa_family = af; + sa->sa_len = aflen; + + switch (idx) { + case RTAX_GATEWAY: + if (nrflags & F_INTERFACE) { + struct ifaddrs *ifap, *ifa; + struct sockaddr_dl *sdl0 = (struct sockaddr_dl *)(void *)sa; + struct sockaddr_dl *sdl = NULL; + + if (getifaddrs(&ifap)) + err(EX_OSERR, "getifaddrs"); + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + if (strcmp(str, ifa->ifa_name) != 0) + continue; + + sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr; + } + /* If we found it, then use it */ + if (sdl != NULL) { + /* + * Note that we need to copy before calling + * freeifaddrs(). + */ + memcpy(sdl0, sdl, sdl->sdl_len); + } + freeifaddrs(ifap); + if (sdl != NULL) + return(1); + else + errx(EX_DATAERR, + "interface '%s' does not exist", str); + } + break; + case RTAX_IFP: + sa->sa_family = AF_LINK; + break; + } + if (strcmp(str, "default") == 0) { + /* + * Default is net 0.0.0.0/0 + */ + switch (idx) { + case RTAX_DST: + nrflags |= F_FORCENET; + getaddr(RTAX_NETMASK, str, 0, nrflags); + break; + } + return (0); + } + switch (sa->sa_family) { +#ifdef INET6 + case AF_INET6: + { + struct addrinfo hints, *res; + int ecode; + + q = NULL; + if (idx == RTAX_DST && (q = strchr(str, '/')) != NULL) + *q = '\0'; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = sa->sa_family; + hints.ai_socktype = SOCK_DGRAM; + ecode = getaddrinfo(str, NULL, &hints, &res); + if (ecode != 0 || res->ai_family != AF_INET6 || + res->ai_addrlen != sizeof(struct sockaddr_in6)) + errx(EX_OSERR, "%s: %s", str, gai_strerror(ecode)); + memcpy(sa, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + if (q != NULL) + *q++ = '/'; + if (idx == RTAX_DST) + return (inet6_makenetandmask((struct sockaddr_in6 *)(void *)sa, q)); + return (0); + } +#endif /* INET6 */ + case AF_LINK: + link_addr(str, (struct sockaddr_dl *)(void *)sa); + return (1); + + case PF_ROUTE: + sockaddr(str, sa, sizeof(struct sockaddr_storage)); + return (1); +#ifdef INET + case AF_INET: +#endif + default: + break; + } + +#ifdef INET + sin = (struct sockaddr_in *)(void *)sa; + if (hpp == NULL) + hpp = &hp; + *hpp = NULL; + + q = strchr(str,'/'); + if (q != NULL && idx == RTAX_DST) { + *q = '\0'; + if ((val = inet_network(str)) != INADDR_NONE) { + inet_makenetandmask(val, sin, + (struct sockaddr_in *)&so[RTAX_NETMASK], + strtoul(q+1, 0, 0)); + return (0); + } + *q = '/'; + } + if ((idx != RTAX_DST || (nrflags & F_FORCENET) == 0) && + inet_aton(str, &sin->sin_addr)) { + val = sin->sin_addr.s_addr; + if (idx != RTAX_DST || nrflags & F_FORCEHOST || + inet_lnaof(sin->sin_addr) != INADDR_ANY) + return (1); + else { + val = ntohl(val); + goto netdone; + } + } + if (idx == RTAX_DST && (nrflags & F_FORCEHOST) == 0 && + ((val = inet_network(str)) != INADDR_NONE +#ifndef FSTACK + || ((np = getnetbyname(str)) != NULL && (val = np->n_net) != 0) +#endif + )){ + +netdone: + inet_makenetandmask(val, sin, + (struct sockaddr_in *)&so[RTAX_NETMASK], 0); + return (0); + } + +#ifndef FSTACK + hp = gethostbyname(str); + if (hp != NULL) { + *hpp = hp; + sin->sin_family = hp->h_addrtype; + memmove((char *)&sin->sin_addr, hp->h_addr, + MIN((size_t)hp->h_length, sizeof(sin->sin_addr))); + return (1); + } +#endif +#endif + errx(EX_NOHOST, "bad address: %s", str); +} + +static int +prefixlen(const char *str) +{ + int len = atoi(str), q, r; + int max; + char *p; + + rtm_addrs |= RTA_NETMASK; + switch (af) { +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *sin6 = + (struct sockaddr_in6 *)&so[RTAX_NETMASK]; + + max = 128; + p = (char *)&sin6->sin6_addr; + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + break; + } +#endif +#ifdef INET + case AF_INET: + { + struct sockaddr_in *sin = + (struct sockaddr_in *)&so[RTAX_NETMASK]; + + max = 32; + p = (char *)&sin->sin_addr; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + break; + } +#endif + default: + errx(EX_OSERR, "prefixlen not supported in this af"); + } + + if (len < 0 || max < len) + errx(EX_USAGE, "%s: invalid prefixlen", str); + + q = len >> 3; + r = len & 7; + memset((void *)p, 0, max / 8); + if (q > 0) + memset((void *)p, 0xff, q); + if (r > 0) + *((u_char *)p + q) = (0xff00 >> r) & 0xff; + if (len == max) + return (-1); + else + return (len); +} + +#ifndef FSTACK +static void +interfaces(void) +{ + size_t needed; + int mib[6]; + char *buf, *lim, *next, count = 0; + struct rt_msghdr *rtm; + +retry2: + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = AF_UNSPEC; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) + err(EX_OSERR, "route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + errx(EX_OSERR, "malloc failed"); + if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) { + if (errno == ENOMEM && count++ < 10) { + warnx("Routing table grew, retrying"); + sleep(1); + free(buf); + goto retry2; + } + err(EX_OSERR, "actual retrieval of interface table"); + } + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)next; + print_rtmsg(rtm, rtm->rtm_msglen); + } +} + +static void +monitor(int argc, char *argv[]) +{ + int n, fib, error; + char msg[2048], *endptr; + + fib = defaultfib; + while (argc > 1) { + argc--; + argv++; + if (**argv != '-') + usage(*argv); + switch (keyword(*argv + 1)) { + case K_FIB: + if (!--argc) + usage(*argv); + errno = 0; + fib = strtol(*++argv, &endptr, 0); + if (errno == 0) { + if (*endptr != '\0' || + fib < 0 || + (numfibs != -1 && fib > numfibs - 1)) + errno = EINVAL; + } + if (errno) + errx(EX_USAGE, "invalid fib number: %s", *argv); + break; + default: + usage(*argv); + } + } + error = set_sofib(fib); + if (error) + errx(EX_USAGE, "invalid fib number: %d", fib); + + verbose = 1; + if (debugonly) { + interfaces(); + exit(0); + } + for (;;) { + time_t now; + n = read(s, msg, 2048); + now = time(NULL); + (void)printf("\ngot message of size %d on %s", n, ctime(&now)); + print_rtmsg((struct rt_msghdr *)(void *)msg, n); + } +} +#endif + +static int +rtmsg(int cmd, int flags, int fib) +{ + int rlen; + char *cp = m_rtmsg.m_space; + int l; + +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) { \ + l = (((struct sockaddr *)&(u))->sa_len == 0) ? \ + sizeof(long) : \ + 1 + ((((struct sockaddr *)&(u))->sa_len - 1) \ + | (sizeof(long) - 1)); \ + memmove(cp, (char *)&(u), l); \ + cp += l; \ + if (verbose) \ + sodump((struct sockaddr *)&(u), #w); \ + } + + errno = 0; + memset(&m_rtmsg, 0, sizeof(m_rtmsg)); + if (cmd == 'a') + cmd = RTM_ADD; + else if (cmd == 'c') + cmd = RTM_CHANGE; + else if (cmd == 'g' || cmd == 's') { + cmd = RTM_GET; + if (so[RTAX_IFP].ss_family == 0) { + so[RTAX_IFP].ss_family = AF_LINK; + so[RTAX_IFP].ss_len = sizeof(struct sockaddr_dl); + rtm_addrs |= RTA_IFP; + } + } else + cmd = RTM_DELETE; +#define rtm m_rtmsg.m_rtm + rtm.rtm_type = cmd; + rtm.rtm_flags = flags; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++rtm_seq; + rtm.rtm_addrs = rtm_addrs; + rtm.rtm_rmx = rt_metrics; + rtm.rtm_inits = rtm_inits; + + NEXTADDR(RTA_DST, so[RTAX_DST]); + NEXTADDR(RTA_GATEWAY, so[RTAX_GATEWAY]); + NEXTADDR(RTA_NETMASK, so[RTAX_NETMASK]); + NEXTADDR(RTA_GENMASK, so[RTAX_GENMASK]); + NEXTADDR(RTA_IFP, so[RTAX_IFP]); + NEXTADDR(RTA_IFA, so[RTAX_IFA]); + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + if (verbose) + print_rtmsg(&rtm, l); + if (debugonly) + return (0); +#ifndef FSTACK + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { +#else + if (cmd == RTM_GET) { + rlen = rtioctl((char *)&m_rtmsg, l, sizeof(m_rtmsg)); + } else { + rlen = write(s, (char *)&m_rtmsg, l); + } + if (rlen < 0) { +#endif + switch (errno) { + case EPERM: + err(1, "writing to routing socket"); + break; + case ESRCH: + warnx("route has not been found"); + break; + case EEXIST: + /* Handled by newroute() */ + break; + default: + warn("writing to routing socket"); + } + return (-1); + } + if (cmd == RTM_GET) { +#ifndef FSTACK + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != rtm_seq || rtm.rtm_pid != pid)); +#else + l = rlen; +#endif + if (l < 0) + warn("read from routing socket"); + else + print_getmsg(&rtm, l, fib); + } +#undef rtm + return (0); +} + +static const char *const msgtypes[] = { + "", + "RTM_ADD: Add Route", + "RTM_DELETE: Delete Route", + "RTM_CHANGE: Change Metrics or flags", + "RTM_GET: Report Metrics", + "RTM_LOSING: Kernel Suspects Partitioning", + "RTM_REDIRECT: Told to use different route", + "RTM_MISS: Lookup failed on this address", + "RTM_LOCK: fix specified metrics", + "RTM_OLDADD: caused by SIOCADDRT", + "RTM_OLDDEL: caused by SIOCDELRT", + "RTM_RESOLVE: Route created by cloning", + "RTM_NEWADDR: address being added to iface", + "RTM_DELADDR: address being removed from iface", + "RTM_IFINFO: iface status change", + "RTM_NEWMADDR: new multicast group membership on iface", + "RTM_DELMADDR: multicast group membership removed from iface", + "RTM_IFANNOUNCE: interface arrival/departure", + "RTM_IEEE80211: IEEE 802.11 wireless event", +}; + +static const char metricnames[] = + "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire" + "\1mtu"; +static const char routeflags[] = + "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE" + "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" + "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3" + "\024FIXEDMTU\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY"; +static const char ifnetflags[] = + "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" + "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" + "\017LINK2\020MULTICAST"; +static const char addrnames[] = + "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; + +static const char errfmt[] = + "\n%s: truncated route message, only %zu bytes left\n"; + +static void +print_rtmsg(struct rt_msghdr *rtm, size_t msglen) +{ + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; +#ifdef RTM_NEWMADDR + struct ifma_msghdr *ifmam; +#endif + struct if_announcemsghdr *ifan; + const char *state; + + if (verbose == 0) + return; + if (rtm->rtm_version != RTM_VERSION) { + (void)printf("routing message version %d not understood\n", + rtm->rtm_version); + return; + } + if (rtm->rtm_type < nitems(msgtypes)) + (void)printf("%s: ", msgtypes[rtm->rtm_type]); + else + (void)printf("unknown type %d: ", rtm->rtm_type); + (void)printf("len %d, ", rtm->rtm_msglen); + +#define REQUIRE(x) do { \ + if (msglen < sizeof(x)) \ + goto badlen; \ + else \ + msglen -= sizeof(x); \ + } while (0) + + switch (rtm->rtm_type) { + case RTM_IFINFO: + REQUIRE(struct if_msghdr); + ifm = (struct if_msghdr *)rtm; + (void)printf("if# %d, ", ifm->ifm_index); + switch (ifm->ifm_data.ifi_link_state) { + case LINK_STATE_DOWN: + state = "down"; + break; + case LINK_STATE_UP: + state = "up"; + break; + default: + state = "unknown"; + break; + } + (void)printf("link: %s, flags:", state); + printb(ifm->ifm_flags, ifnetflags); + pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs, msglen); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + REQUIRE(struct ifa_msghdr); + ifam = (struct ifa_msghdr *)rtm; + (void)printf("metric %d, flags:", ifam->ifam_metric); + printb(ifam->ifam_flags, routeflags); + pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs, msglen); + break; +#ifdef RTM_NEWMADDR + case RTM_NEWMADDR: + case RTM_DELMADDR: + REQUIRE(struct ifma_msghdr); + ifmam = (struct ifma_msghdr *)rtm; + pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs, msglen); + break; +#endif + case RTM_IFANNOUNCE: + REQUIRE(struct if_announcemsghdr); + ifan = (struct if_announcemsghdr *)rtm; + (void)printf("if# %d, what: ", ifan->ifan_index); + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + (void)printf("arrival"); + break; + case IFAN_DEPARTURE: + printf("departure"); + break; + default: + printf("#%d", ifan->ifan_what); + break; + } + printf("\n"); + fflush(stdout); + break; + + default: + printf("pid: %ld, seq %d, errno %d, flags:", + (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); + printb(rtm->rtm_flags, routeflags); + pmsg_common(rtm, msglen); + } + + return; + +badlen: + (void)printf(errfmt, __func__, msglen); +#undef REQUIRE +} + +static void +print_getmsg(struct rt_msghdr *rtm, int msglen, int fib) +{ + struct sockaddr *sp[RTAX_MAX]; + struct timespec ts; + char *cp; + int i; + + memset(sp, 0, sizeof(sp)); + (void)printf(" route to: %s\n", + routename((struct sockaddr *)&so[RTAX_DST])); + if (rtm->rtm_version != RTM_VERSION) { + warnx("routing message version %d not understood", + rtm->rtm_version); + return; + } + if (rtm->rtm_msglen > msglen) { + warnx("message length mismatch, in packet %d, returned %d", + rtm->rtm_msglen, msglen); + return; + } + if (rtm->rtm_errno) { + errno = rtm->rtm_errno; + warn("message indicates error %d", errno); + return; + } + cp = ((char *)(rtm + 1)); + for (i = 0; i < RTAX_MAX; i++) + if (rtm->rtm_addrs & (1 << i)) { + sp[i] = (struct sockaddr *)cp; + cp += SA_SIZE((struct sockaddr *)cp); + } + if ((rtm->rtm_addrs & RTA_IFP) && + (sp[RTAX_IFP]->sa_family != AF_LINK || + ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_nlen == 0)) + sp[RTAX_IFP] = NULL; + if (sp[RTAX_DST]) + (void)printf("destination: %s\n", routename(sp[RTAX_DST])); + if (sp[RTAX_NETMASK]) + (void)printf(" mask: %s\n", routename(sp[RTAX_NETMASK])); + if (sp[RTAX_GATEWAY] && (rtm->rtm_flags & RTF_GATEWAY)) + (void)printf(" gateway: %s\n", routename(sp[RTAX_GATEWAY])); + if (fib >= 0) + (void)printf(" fib: %u\n", (unsigned int)fib); + if (sp[RTAX_IFP]) + (void)printf(" interface: %.*s\n", + ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_nlen, + ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_data); + (void)printf(" flags: "); + printb(rtm->rtm_flags, routeflags); + +#define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') +#define msec(u) (((u) + 500) / 1000) /* usec to msec */ + printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe", + "sendpipe", "ssthresh", "rtt,msec", "mtu ", "weight", "expire"); + printf("%8lu%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); + printf("%8lu%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); + printf("%8lu%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); + printf("%8lu%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); + printf("%8lu%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); + printf("%8lu%c ", rtm->rtm_rmx.rmx_weight, lock(WEIGHT)); + if (rtm->rtm_rmx.rmx_expire > 0) + clock_gettime(CLOCK_REALTIME_FAST, &ts); + else + ts.tv_sec = 0; + printf("%8ld%c\n", (long)(rtm->rtm_rmx.rmx_expire - ts.tv_sec), + lock(EXPIRE)); +#undef lock +#undef msec +#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) + if (verbose) + pmsg_common(rtm, msglen); + else if (rtm->rtm_addrs &~ RTA_IGN) { + (void)printf("sockaddrs: "); + printb(rtm->rtm_addrs, addrnames); + putchar('\n'); + } +#undef RTA_IGN +} + +static void +pmsg_common(struct rt_msghdr *rtm, size_t msglen) +{ + + (void)printf("\nlocks: "); + printb(rtm->rtm_rmx.rmx_locks, metricnames); + (void)printf(" inits: "); + printb(rtm->rtm_inits, metricnames); + if (msglen > sizeof(struct rt_msghdr)) + pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs, + msglen - sizeof(struct rt_msghdr)); + else + (void)fflush(stdout); +} + +static void +pmsg_addrs(char *cp, int addrs, size_t len) +{ + struct sockaddr *sa; + int i; + + if (addrs == 0) { + (void)putchar('\n'); + return; + } + (void)printf("\nsockaddrs: "); + printb(addrs, addrnames); + putchar('\n'); + for (i = 0; i < RTAX_MAX; i++) + if (addrs & (1 << i)) { + sa = (struct sockaddr *)cp; + if (len == 0 || len < SA_SIZE(sa)) { + (void)printf(errfmt, __func__, len); + break; + } + (void)printf(" %s", routename(sa)); + len -= SA_SIZE(sa); + cp += SA_SIZE(sa); + } + (void)putchar('\n'); + (void)fflush(stdout); +} + +static void +printb(int b, const char *str) +{ + int i; + int gotsome = 0; + + if (b == 0) + return; + while ((i = *str++) != 0) { + if (b & (1 << (i-1))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + putchar(i); + gotsome = 1; + for (; (i = *str) > 32; str++) + putchar(i); + } else + while (*str > 32) + str++; + } + if (gotsome) + putchar('>'); +} + +int +keyword(const char *cp) +{ + const struct keytab *kt = keywords; + + while (kt->kt_cp != NULL && strcmp(kt->kt_cp, cp) != 0) + kt++; + return (kt->kt_i); +} + +static void +sodump(struct sockaddr *sa, const char *which) +{ +#ifdef INET6 + char nbuf[INET6_ADDRSTRLEN]; +#endif + + switch (sa->sa_family) { + case AF_LINK: + (void)printf("%s: link %s; ", which, + link_ntoa((struct sockaddr_dl *)(void *)sa)); + break; +#ifdef INET + case AF_INET: + (void)printf("%s: inet %s; ", which, + inet_ntoa(((struct sockaddr_in *)(void *)sa)->sin_addr)); + break; +#endif +#ifdef INET6 + case AF_INET6: + (void)printf("%s: inet6 %s; ", which, inet_ntop(sa->sa_family, + &((struct sockaddr_in6 *)(void *)sa)->sin6_addr, nbuf, + sizeof(nbuf))); + break; +#endif + } + (void)fflush(stdout); +} + +/* States*/ +#define VIRGIN 0 +#define GOTONE 1 +#define GOTTWO 2 +/* Inputs */ +#define DIGIT (4*0) +#define END (4*1) +#define DELIM (4*2) + +static void +sockaddr(char *addr, struct sockaddr *sa, size_t size) +{ + char *cp = (char *)sa; + char *cplim = cp + size; + int byte = 0, state = VIRGIN, new = 0 /* foil gcc */; + + memset(cp, 0, size); + cp++; + do { + if ((*addr >= '0') && (*addr <= '9')) { + new = *addr - '0'; + } else if ((*addr >= 'a') && (*addr <= 'f')) { + new = *addr - 'a' + 10; + } else if ((*addr >= 'A') && (*addr <= 'F')) { + new = *addr - 'A' + 10; + } else if (*addr == '\0') + state |= END; + else + state |= DELIM; + addr++; + switch (state /* | INPUT */) { + case GOTTWO | DIGIT: + *cp++ = byte; /*FALLTHROUGH*/ + case VIRGIN | DIGIT: + state = GOTONE; byte = new; continue; + case GOTONE | DIGIT: + state = GOTTWO; byte = new + (byte << 4); continue; + default: /* | DELIM */ + state = VIRGIN; *cp++ = byte; byte = 0; continue; + case GOTONE | END: + case GOTTWO | END: + *cp++ = byte; /* FALLTHROUGH */ + case VIRGIN | END: + break; + } + break; + } while (cp < cplim); + sa->sa_len = cp - (char *)sa; +}