Index: head/sys/net/route.h =================================================================== --- head/sys/net/route.h +++ head/sys/net/route.h @@ -448,10 +448,10 @@ void rt_ifmsg(struct ifnet *); void rt_missmsg(int, struct rt_addrinfo *, int, int); void rt_missmsg_fib(int, struct rt_addrinfo *, int, int, int); -void rt_newaddrmsg(int, struct ifaddr *, int, struct rtentry *); -void rt_newaddrmsg_fib(int, struct ifaddr *, int, struct rtentry *, int); +void rt_newaddrmsg_fib(int, struct ifaddr *, struct rtentry *, int); int rt_addrmsg(int, struct ifaddr *, int); -int rt_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); +int rt_routemsg(int, struct rtentry *, struct ifnet *ifp, int, int); +int rt_routemsg_info(int, struct rt_addrinfo *, int); void rt_newmaddrmsg(int, struct ifmultiaddr *); int rt_setgate(struct rtentry *, struct sockaddr *, struct sockaddr *); void rt_maskedcopy(struct sockaddr *, struct sockaddr *, struct sockaddr *); @@ -460,7 +460,8 @@ u_int rt_tables_get_gen(int table, int fam); int rtsock_addrmsg(int, struct ifaddr *, int); -int rtsock_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); +int rtsock_routemsg(int, struct rtentry *, struct ifnet *ifp, int, int); +int rtsock_routemsg_info(int, struct rt_addrinfo *, int); /* * Note the following locking behavior: Index: head/sys/net/route.c =================================================================== --- head/sys/net/route.c +++ head/sys/net/route.c @@ -2147,7 +2147,7 @@ #endif RT_ADDREF(rt); RT_UNLOCK(rt); - rt_newaddrmsg_fib(cmd, ifa, error, rt, fibnum); + rt_newaddrmsg_fib(cmd, ifa, rt, fibnum); RT_LOCK(rt); RT_REMREF(rt); if (cmd == RTM_DELETE) { @@ -2233,17 +2233,16 @@ } /* - * Announce route addition/removal. - * Users of this function MUST validate input data BEFORE calling. - * However we have to be able to handle invalid data: - * if some userland app sends us "invalid" route message (invalid mask, - * no dst, wrong address families, etc...) we need to pass it back - * to app (and any other rtsock consumers) with rtm_errno field set to - * non-zero value. + * Announce kernel-originated route addition/removal to rtsock based on @rt data. + * cmd: RTM_ cmd + * @rt: valid rtentry + * @ifp: target route interface + * @fibnum: fib id or RT_ALL_FIBS + * * Returns 0 on success. */ int -rt_routemsg(int cmd, struct ifnet *ifp, int error, struct rtentry *rt, +rt_routemsg(int cmd, struct rtentry *rt, struct ifnet *ifp, int rti_addrs, int fibnum) { @@ -2255,23 +2254,39 @@ KASSERT(rt_key(rt) != NULL, (":%s: rt_key must be supplied", __func__)); - return (rtsock_routemsg(cmd, ifp, error, rt, fibnum)); + return (rtsock_routemsg(cmd, rt, ifp, 0, fibnum)); } -void -rt_newaddrmsg(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt) +/* + * Announce kernel-originated route addition/removal to rtsock based on @rt data. + * cmd: RTM_ cmd + * @info: addrinfo structure with valid data. + * @fibnum: fib id or RT_ALL_FIBS + * + * Returns 0 on success. + */ +int +rt_routemsg_info(int cmd, struct rt_addrinfo *info, int fibnum) { - rt_newaddrmsg_fib(cmd, ifa, error, rt, RT_ALL_FIBS); + KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE, + ("unexpected cmd %d", cmd)); + + KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), + ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); + + KASSERT(info->rti_info[RTAX_DST] != NULL, (":%s: RTAX_DST must be supplied", __func__)); + + return (rtsock_routemsg_info(cmd, info, fibnum)); } + /* * This is called to generate messages from the routing socket * indicating a network interface has had addresses associated with it. */ void -rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt, - int fibnum) +rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, struct rtentry *rt, int fibnum) { KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, @@ -2282,10 +2297,10 @@ if (cmd == RTM_ADD) { rt_addrmsg(cmd, ifa, fibnum); if (rt != NULL) - rt_routemsg(cmd, ifa->ifa_ifp, error, rt, fibnum); + rt_routemsg(cmd, rt, ifa->ifa_ifp, 0, fibnum); } else { if (rt != NULL) - rt_routemsg(cmd, ifa->ifa_ifp, error, rt, fibnum); + rt_routemsg(cmd, rt, ifa->ifa_ifp, 0, fibnum); rt_addrmsg(cmd, ifa, fibnum); } } Index: head/sys/net/rtsock.c =================================================================== --- head/sys/net/rtsock.c +++ head/sys/net/rtsock.c @@ -1450,7 +1450,7 @@ info.rti_info[RTAX_IFA] = sa = ifa->ifa_addr; info.rti_info[RTAX_IFP] = ifp->if_addr->ifa_addr; info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask( - info.rti_info[RTAX_IFP], ifa->ifa_netmask, &ss); + info.rti_info[RTAX_IFA], ifa->ifa_netmask, &ss); info.rti_info[RTAX_BRD] = ifa->ifa_dstaddr; if ((m = rtsock_msg_mbuf(ncmd, &info)) == NULL) return (ENOBUFS); @@ -1471,46 +1471,69 @@ } /* - * Announce route addition/removal. - * Please do not call directly, use rt_routemsg(). - * Note that @rt data MAY be inconsistent/invalid: - * if some userland app sends us "invalid" route message (invalid mask, - * no dst, wrong address families, etc...) we need to pass it back - * to app (and any other rtsock consumers) with rtm_errno field set to - * non-zero value. + * Announce route addition/removal to rtsock based on @rt data. + * Callers are advives to use rt_routemsg() instead of using this + * function directly. + * Assume @rt data is consistent. * * Returns 0 on success. */ int -rtsock_routemsg(int cmd, struct ifnet *ifp, int error, struct rtentry *rt, +rtsock_routemsg(int cmd, struct rtentry *rt, struct ifnet *ifp, int rti_addrs, int fibnum) { - struct rt_addrinfo info; - struct sockaddr *sa; - struct mbuf *m; - struct rt_msghdr *rtm; struct sockaddr_storage ss; + struct rt_addrinfo info; if (V_route_cb.any_count == 0) return (0); bzero((caddr_t)&info, sizeof(info)); - info.rti_info[RTAX_DST] = sa = rt_key(rt); - info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(sa, rt_mask(rt), &ss); + info.rti_info[RTAX_DST] = rt_key(rt); + info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt), rt_mask(rt), &ss); info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; - if ((m = rtsock_msg_mbuf(cmd, &info)) == NULL) + info.rti_flags = rt->rt_flags; + info.rti_ifp = ifp; + + return (rtsock_routemsg_info(cmd, &info, fibnum)); +} + +int +rtsock_routemsg_info(int cmd, struct rt_addrinfo *info, int fibnum) +{ + struct rt_msghdr *rtm; + struct sockaddr *sa; + struct mbuf *m; + + if (V_route_cb.any_count == 0) + return (0); + + if (info->rti_flags & RTF_HOST) + info->rti_info[RTAX_NETMASK] = NULL; + + m = rtsock_msg_mbuf(cmd, info); + if (m == NULL) return (ENOBUFS); - rtm = mtod(m, struct rt_msghdr *); - rtm->rtm_index = ifp->if_index; - rtm->rtm_flags |= rt->rt_flags; - rtm->rtm_errno = error; - rtm->rtm_addrs = info.rti_addrs; if (fibnum != RT_ALL_FIBS) { + KASSERT(fibnum >= 0 && fibnum < rt_numfibs, ("%s: fibnum out " + "of range 0 <= %d < %d", __func__, fibnum, rt_numfibs)); M_SETFIB(m, fibnum); m->m_flags |= RTS_FILTER_FIB; } + rtm = mtod(m, struct rt_msghdr *); + rtm->rtm_addrs = info->rti_addrs; + if (info->rti_ifp != NULL) + rtm->rtm_index = info->rti_ifp->if_index; + /* Add RTF_DONE to indicate command 'completion' required by API */ + info->rti_flags |= RTF_DONE; + /* Reported routes has to be up */ + if (cmd == RTM_ADD || cmd == RTM_CHANGE) + info->rti_flags |= RTF_UP; + rtm->rtm_flags = info->rti_flags; + + sa = info->rti_info[RTAX_DST]; rt_dispatch(m, sa ? sa->sa_family : AF_UNSPEC); return (0); Index: head/sys/netinet6/in6.c =================================================================== --- head/sys/netinet6/in6.c +++ head/sys/netinet6/in6.c @@ -168,30 +168,35 @@ void in6_newaddrmsg(struct in6_ifaddr *ia, int cmd) { + struct rt_addrinfo info; + struct ifaddr *ifa; struct sockaddr_dl gateway; - struct sockaddr_in6 mask, addr; - struct rtentry rt; int fibnum; + ifa = &ia->ia_ifa; + /* - * initialize for rtmsg generation + * Prepare info data for the host route. + * This code mimics one from ifa_maintain_loopback_route(). */ - bzero(&gateway, sizeof(gateway)); - gateway.sdl_len = sizeof(gateway); - gateway.sdl_family = AF_LINK; + bzero(&info, sizeof(struct rt_addrinfo)); + info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC | RTF_PINNED; + info.rti_info[RTAX_DST] = ifa->ifa_addr; + info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&gateway; + link_init_sdl(ifa->ifa_ifp, (struct sockaddr *)&gateway, ifa->ifa_ifp->if_type); + if (cmd != RTM_DELETE) + info.rti_ifp = V_loif; - bzero(&rt, sizeof(rt)); - rt.rt_gateway = (struct sockaddr *)&gateway; - memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); - rt_mask(&rt) = (struct sockaddr *)&mask; - rt_key(&rt) = (struct sockaddr *)&addr; - rt.rt_flags = RTF_HOST | RTF_STATIC; - if (cmd == RTM_ADD) - rt.rt_flags |= RTF_UP; + fibnum = V_rt_add_addr_allfibs ? RT_ALL_FIBS : ia62ifa(ia)->ifa_ifp->if_fib; - /* Announce arrival of local address to this FIB. */ - rt_newaddrmsg_fib(cmd, &ia->ia_ifa, 0, &rt, fibnum); + + if (cmd == RTM_ADD) { + rt_addrmsg(cmd, &ia->ia_ifa, fibnum); + rt_routemsg_info(cmd, &info, fibnum); + } else if (cmd == RTM_DELETE) { + rt_routemsg_info(cmd, &info, fibnum); + rt_addrmsg(cmd, &ia->ia_ifa, fibnum); + } } int Index: head/sys/netinet6/nd6_rtr.c =================================================================== --- head/sys/netinet6/nd6_rtr.c +++ head/sys/netinet6/nd6_rtr.c @@ -609,30 +609,8 @@ static void nd6_rtmsg(int cmd, struct rtentry *rt) { - struct rt_addrinfo info; - struct ifnet *ifp; - struct ifaddr *ifa; - bzero((caddr_t)&info, sizeof(info)); - info.rti_info[RTAX_DST] = rt_key(rt); - info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; - info.rti_info[RTAX_NETMASK] = rt_mask(rt); - ifp = rt->rt_ifp; - if (ifp != NULL) { - struct epoch_tracker et; - - NET_EPOCH_ENTER(et); - ifa = CK_STAILQ_FIRST(&ifp->if_addrhead); - info.rti_info[RTAX_IFP] = ifa->ifa_addr; - ifa_ref(ifa); - NET_EPOCH_EXIT(et); - info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; - } else - ifa = NULL; - - rt_missmsg_fib(cmd, &info, rt->rt_flags, 0, rt->rt_fibnum); - if (ifa != NULL) - ifa_free(ifa); + rt_routemsg(cmd, rt, rt->rt_ifp, 0, rt->rt_fibnum); } /* PFXRTR */ Index: head/tests/sys/net/routing/rtsock_common.h =================================================================== --- head/tests/sys/net/routing/rtsock_common.h +++ head/tests/sys/net/routing/rtsock_common.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -594,7 +595,18 @@ rtsock_read_rtm(int fd, char *buffer, size_t buflen) { ssize_t len; + struct pollfd pfd; + int poll_delay = 5 * 1000; /* 5 seconds */ + /* Check for the data available to read first */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN; + + if (poll(&pfd, 1, poll_delay) == 0) + ATF_REQUIRE_MSG(1 == 0, "rtsock read timed out (%d seconds passed)", + poll_delay / 1000); + len = read(fd, buffer, buflen); int my_errno = errno; ATF_REQUIRE_MSG(len > 0, "rtsock read failed: %s", strerror(my_errno)); @@ -706,35 +718,36 @@ } static void -_validate_route_message(struct rt_msghdr *rtm) +_validate_message_sockaddrs(char *buffer, int rtm_len, size_t offset, int rtm_addrs) { struct sockaddr *sa; - size_t parsed_len = sizeof(struct rt_msghdr); - int len = rtm->rtm_msglen; + size_t parsed_len = offset; - sa = (struct sockaddr *)(rtm + 1); + /* Offset denotes initial header size */ + sa = (struct sockaddr *)(buffer + offset); for (int i = 0; i < RTAX_MAX; i++) { - if ((rtm->rtm_addrs & (1 << i)) == 0) + if ((rtm_addrs & (1 << i)) == 0) continue; parsed_len += SA_SIZE(sa); - RTSOCK_ATF_REQUIRE_MSG(rtm, parsed_len <= len, - "SA %d: len %d exceeds msg size %d", i, (int)sa->sa_len, len); + RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, parsed_len <= rtm_len, + "SA %d: len %d exceeds msg size %d", i, (int)sa->sa_len, rtm_len); if (sa->sa_family == AF_LINK) { struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; int data_len = sdl->sdl_nlen + sdl->sdl_alen; data_len += offsetof(struct sockaddr_dl, sdl_data); - RTSOCK_ATF_REQUIRE_MSG(rtm, data_len <= len, - "AF_LINK data size exceeds total len: %u vs %u", - data_len, len); + RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, + data_len <= rtm_len, + "AF_LINK data size exceeds total len: %u vs %u, nlen=%d alen=%d", + data_len, rtm_len, sdl->sdl_nlen, sdl->sdl_alen); } sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); } - RTSOCK_ATF_REQUIRE_MSG(rtm, parsed_len == rtm->rtm_msglen, + RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, parsed_len == rtm_len, "message len != parsed len: expected %d parsed %d", - rtm->rtm_msglen, (int)parsed_len); + rtm_len, (int)parsed_len); } /* @@ -758,9 +771,36 @@ case RTM_ADD: case RTM_DELETE: case RTM_CHANGE: - _validate_route_message(rtm); + _validate_message_sockaddrs(buffer, rtm->rtm_msglen, + sizeof(struct rt_msghdr), rtm->rtm_addrs); break; + case RTM_DELADDR: + case RTM_NEWADDR: + _validate_message_sockaddrs(buffer, rtm->rtm_msglen, + sizeof(struct ifa_msghdr), ((struct ifa_msghdr *)buffer)->ifam_addrs); + break; } +} + +void +rtsock_validate_pid_ours(struct rt_msghdr *rtm) +{ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == getpid(), "expected pid %d, got %d", + getpid(), rtm->rtm_pid); +} + +void +rtsock_validate_pid_user(struct rt_msghdr *rtm) +{ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid > 0, "expected non-zero pid, got %d", + rtm->rtm_pid); +} + +void +rtsock_validate_pid_kernel(struct rt_msghdr *rtm) +{ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == 0, "expected zero pid, got %d", + rtm->rtm_pid); } #endif Index: head/tests/sys/net/routing/rtsock_print.h =================================================================== --- head/tests/sys/net/routing/rtsock_print.h +++ head/tests/sys/net/routing/rtsock_print.h @@ -40,12 +40,20 @@ #define RTSOCK_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do { \ if (!(_cond)) { \ printf("-- CONDITION FAILED, rtm dump --\n\n");\ - rtsock_print_rtm(_rtm); \ + rtsock_print_message(_rtm); \ } \ ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__); \ } while (0); +#define RTSOCKHD_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do { \ + if (!(_cond)) { \ + printf("-- CONDITION FAILED, rtm hexdump--\n\n");\ + rtsock_print_message_hd(_rtm); \ + } \ + ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__); \ +} while (0); + /* from route.c */ static const char *const msgtypes[] = { "", @@ -145,7 +153,7 @@ unsigned char v; int repeat_count = 0; for (int i = 0; i < len; i++) { - if (last_char && *last_char == data[i]) { + if (last_char && *last_char == data[i] && data[i] == 0x00) { repeat_count++; continue; } @@ -157,9 +165,9 @@ v = ((const unsigned char *)data)[i]; if (last_char == NULL) - _PRINTX("%02X", v); + _PRINTX("x%02X", v); else - _PRINTX(", %02X", v); + _PRINTX(", x%02X", v); last_char = &data[i]; repeat_count = 1; @@ -259,6 +267,19 @@ printf("%s: len %hu, pid: %d, seq %d, errno %d, flags: %s\n", msgtypes[rtm->rtm_type], rtm->rtm_msglen, rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno, flags_buf); + if (rtm->rtm_inits > 0) { + _printb(flags_buf, sizeof(flags_buf), rtm->rtm_inits, metricnames); + printf("metrics: %s\n", flags_buf); + if (rtm->rtm_inits & RTV_MTU) + printf("mtu: %lu\n", rtm->rtm_rmx.rmx_mtu); + if (rtm->rtm_inits & RTV_EXPIRE) { + struct timeval tv; + gettimeofday(&tv, NULL); + printf("expire: %d (%lu raw)\n", + (int)(rtm->rtm_rmx.rmx_expire - tv.tv_sec), rtm->rtm_rmx.rmx_expire); + } + } + _printb(flags_buf, sizeof(flags_buf), rtm->rtm_addrs, addrnames); printf("sockaddrs: 0x%X %s\n", rtm->rtm_addrs, flags_buf); @@ -275,6 +296,81 @@ printf("\n"); +} + +void +rtsock_print_ifa(struct ifa_msghdr *ifam) +{ + struct timeval tv; + struct tm tm_res; + char buf[64]; + + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &tm_res); + strftime(buf, sizeof(buf), "%F %T", &tm_res); + printf("Got message of size %hu on %s\n", ifam->ifam_msglen, buf); + + char flags_buf[256]; + _printb(flags_buf, sizeof(flags_buf), ifam->ifam_flags, routeflags); + + printf("%s: len %hu, ifindex: %d, flags: %s\n", msgtypes[ifam->ifam_type], + ifam->ifam_msglen, ifam->ifam_index, flags_buf); + + _printb(flags_buf, sizeof(flags_buf), ifam->ifam_addrs, addrnames); + printf("sockaddrs: 0x%X %s\n", ifam->ifam_addrs, flags_buf); + + char *ptr = (char *)(ifam + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (ifam->ifam_addrs & (1 << i)) { + struct sockaddr *sa = (struct sockaddr *)ptr; + sa_print(sa, 1); + + /* add */ + ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); + } + } + + printf("\n"); + +} + +void +rtsock_print_message_hd(struct rt_msghdr *rtm) +{ + struct timeval tv; + struct tm tm_res; + char buf[64]; + char dumpbuf[2048]; + + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &tm_res); + strftime(buf, sizeof(buf), "%F %T", &tm_res); + printf("Got message type %s of size %hu on %s\n", + rtsock_print_cmdtype(rtm->rtm_type), + rtm->rtm_msglen, buf); + + sa_print_hd(dumpbuf, sizeof(dumpbuf), (char *)rtm, rtm->rtm_msglen); + printf(" %s\n", dumpbuf); +} + +void +rtsock_print_message(struct rt_msghdr *rtm) +{ + + switch (rtm->rtm_type) { + case RTM_GET: + case RTM_ADD: + case RTM_DELETE: + case RTM_CHANGE: + rtsock_print_rtm(rtm); + break; + case RTM_DELADDR: + case RTM_NEWADDR: + rtsock_print_ifa((struct ifa_msghdr *)rtm); + break; + default: + printf("unknown rt message type %X\n", rtm->rtm_type); + } } #endif Index: head/tests/sys/net/routing/test_rtsock_l3.c =================================================================== --- head/tests/sys/net/routing/test_rtsock_l3.c +++ head/tests/sys/net/routing/test_rtsock_l3.c @@ -29,9 +29,14 @@ #include "rtsock_common.h" #include "rtsock_config.h" +#include "sys/types.h" +#include +#include +#include "net/bpf.h" + static inline struct rtsock_test_config * -presetup_ipv6(const atf_tc_t *tc) +presetup_ipv6_iface(const atf_tc_t *tc) { struct rtsock_test_config *c; int ret; @@ -44,6 +49,17 @@ ret = iface_enable_ipv6(c->ifname); ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifname); + return (c); +} + +static inline struct rtsock_test_config * +presetup_ipv6(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = presetup_ipv6_iface(tc); + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); c->rtsock_fd = rtsock_setup_socket(); @@ -52,16 +68,15 @@ } static inline struct rtsock_test_config * -presetup_ipv4(const atf_tc_t *tc) +presetup_ipv4_iface(const atf_tc_t *tc) { struct rtsock_test_config *c; int ret; c = config_setup(tc); - /* assumes ifconfig doing IFF_UP */ - ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); - ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + ret = iface_turn_up(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname); /* Actually open interface, so kernel writes won't fail */ if (c->autocreated_interface) { @@ -69,6 +84,21 @@ ATF_REQUIRE_MSG(ret >= 0, "unable to open interface %s", c->ifname); } + return (c); +} + +static inline struct rtsock_test_config * +presetup_ipv4(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = presetup_ipv4_iface(tc); + + /* assumes ifconfig doing IFF_UP */ + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + c->rtsock_fd = rtsock_setup_socket(); return (c); @@ -158,8 +188,6 @@ ret = sa_equal_msg(sa, gw, msg, sizeof(msg)); RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); } - - RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid > 0, "expected non-zero pid"); } static void @@ -169,9 +197,22 @@ "expected ifindex %d, got %d", ifindex, rtm->rtm_index); RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == rtm_flags, - "expected flags: %X, got %X", rtm_flags, rtm->rtm_flags); + "expected flags: 0x%X, got 0x%X", rtm_flags, rtm->rtm_flags); } +static void +verify_link_gateway(struct rt_msghdr *rtm, int ifindex) +{ + struct sockaddr *sa; + struct sockaddr_dl *sdl; + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set"); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family); + sdl = (struct sockaddr_dl *)sa; + RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == ifindex, "GW ifindex is %d", sdl->sdl_index); +} + /* TESTS */ #define DECLARE_TEST_VARS \ @@ -185,6 +226,16 @@ #define DESCRIBE_ROOT_TEST(_msg) config_describe_root_test(tc, _msg) #define CLEANUP_AFTER_TEST config_generic_cleanup(config_setup(tc)) +#define RTM_DECLARE_ROOT_TEST(_name, _descr) \ +ATF_TC_WITH_CLEANUP(_name); \ +ATF_TC_HEAD(_name, tc) \ +{ \ + DESCRIBE_ROOT_TEST(_descr); \ +} \ +ATF_TC_CLEANUP(_name, tc) \ +{ \ + CLEANUP_AFTER_TEST; \ +} ATF_TC_WITH_CLEANUP(rtm_get_v4_exact_success); ATF_TC_HEAD(rtm_get_v4_exact_success, tc) @@ -219,6 +270,7 @@ verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED); /* Explicitly verify gateway for the interface route */ + verify_link_gateway(rtm, c->ifindex); sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set"); RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family); @@ -247,7 +299,7 @@ rtsock_send_rtm(c->rtsock_fd, rtm); - rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); /* * RTM_GET: Report Metrics: len 312, pid: 67074, seq 1, errno 0, flags: @@ -503,8 +555,432 @@ CLEANUP_AFTER_TEST; } +ATF_TC_WITH_CLEANUP(rtm_add_v4_temporal1_success); +ATF_TC_HEAD(rtm_add_v4_temporal1_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv4 route expiration with expire time set"); +} +ATF_TC_BODY(rtm_add_v4_temporal1_success, tc) +{ + DECLARE_TEST_VARS; + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + /* Set expire time to now */ + struct timeval tv; + gettimeofday(&tv, NULL); + rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1; + rtm->rtm_inits |= RTV_EXPIRE; + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD"); + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set"); + + /* The next should be route deletion */ + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + /* TODO: add RTF_DONE */ + verify_route_message_extra(rtm, c->ifindex, RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_add_v4_temporal1_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_add_v6_temporal1_success); +ATF_TC_HEAD(rtm_add_v6_temporal1_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW"); +} + +ATF_TC_BODY(rtm_add_v6_temporal1_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + /* Set expire time to now */ + struct timeval tv; + gettimeofday(&tv, NULL); + rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1; + rtm->rtm_inits |= RTV_EXPIRE; + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD"); + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set"); + + /* The next should be route deletion */ + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + + /* XXX: Currently kernel sets RTF_UP automatically but does NOT report it in the reply */ + /* TODO: add RTF_DONE */ + verify_route_message_extra(rtm, c->ifindex, RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_add_v6_temporal1_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +/* Interface address messages tests */ + +RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_hostroute_success, + "Tests validness for /128 host route announce after ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v6_gu_ifa_hostroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + /* + * There will be multiple. + * RTM_ADD without llinfo. + */ + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLINFO) == 0)) + break; + } + /* This should be a message for the host route */ + + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->addr6, NULL, NULL); + rtsock_validate_pid_kernel(rtm); + /* No netmask should be set */ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set"); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_UP | RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED; + verify_route_message_extra(rtm, if_nametoindex("lo0"), expected_rt_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_prefixroute_success, + "Tests validness for the prefix route announce after ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v6_gu_ifa_prefixroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + /* + * Multiple RTM_ADD messages will be generated: + * 1) lladdr mapping (RTF_LLDATA) + * 2) host route (one w/o netmask) + * 3) prefix route + */ + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) + break; + } + + /* This should be a message for the prefix route */ + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net6, + (struct sockaddr *)&c->mask6, NULL); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + /* TODO: PINNED? */ + int expected_rt_flags = RTF_UP | RTF_DONE; + verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_ordered_success, + "Tests ordering of the messages for IPv6 global unicast ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v6_gu_ifa_ordered_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + int count = 0, tries = 0; + + enum msgtype { + MSG_IFADDR, + MSG_HOSTROUTE, + MSG_PREFIXROUTE, + MSG_MAX, + }; + + int msg_array[MSG_MAX]; + + bzero(msg_array, sizeof(msg_array)); + + while (count < 3 && tries < 20) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + tries++; + /* Classify */ + if (rtm->rtm_type == RTM_NEWADDR) { + RLOG("MSG_IFADDR: %d", count); + msg_array[MSG_IFADDR] = count++; + continue; + } + + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) { + RLOG("MSG_PREFIXROUTE: %d", count); + msg_array[MSG_PREFIXROUTE] = count++; + continue; + } + + if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLDATA) == 0)) { + RLOG("MSG_HOSTROUTE: %d", count); + msg_array[MSG_HOSTROUTE] = count++; + continue; + } + + RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type), + tries); + } + + /* TODO: verify multicast */ + ATF_REQUIRE_MSG(count == 3, "Received only %d/3 messages", count); + ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first"); +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_hostroute_success, + "Tests validness for /128 host route removal after ifaddr removal"); + +ATF_TC_BODY(rtm_del_v6_gu_ifa_hostroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_delete_addr(c->ifname, c->addr6_str); + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + if ((rtm->rtm_type == RTM_DELETE) && + ((rtm->rtm_flags & RTF_LLINFO) == 0) && + rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL) + break; + } + /* This should be a message for the host route */ + + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->addr6, NULL, NULL); + rtsock_validate_pid_kernel(rtm); + /* No netmask should be set */ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set"); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + /* XXX: consider passing ifindex in rtm_index as done in RTM_ADD. */ + int expected_rt_flags = RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED; + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == expected_rt_flags, + "expected rtm flags: 0x%X, got 0x%X", expected_rt_flags, rtm->rtm_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_prefixroute_success, + "Tests validness for the prefix route removal after ifaddr assignment"); + +ATF_TC_BODY(rtm_del_v6_gu_ifa_prefixroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_delete_addr(c->ifname, c->addr6_str); + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + /* Find RTM_DELETE with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) + break; + } + + /* This should be a message for the prefix route */ + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net6, + (struct sockaddr *)&c->mask6, NULL); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_DONE; + verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_prefixroute_success, + "Tests validness for the prefix route announce after ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v4_gu_ifa_prefixroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + /* + * Multiple RTM_ADD messages will be generated: + * 1) lladdr mapping (RTF_LLDATA) + * 3) prefix route + */ + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) + break; + } + + /* This should be a message for the prefix route */ + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED; + verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_ordered_success, + "Tests ordering of the messages for IPv4 unicast ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v4_gu_ifa_ordered_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + + int count = 0, tries = 0; + + enum msgtype { + MSG_IFADDR, + MSG_PREFIXROUTE, + MSG_MAX, + }; + + int msg_array[MSG_MAX]; + + bzero(msg_array, sizeof(msg_array)); + + while (count < 2 && tries < 20) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + tries++; + /* Classify */ + if (rtm->rtm_type == RTM_NEWADDR) { + RLOG("MSG_IFADDR: %d", count); + msg_array[MSG_IFADDR] = count++; + continue; + } + + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) { + RLOG("MSG_PREFIXROUTE: %d", count); + msg_array[MSG_PREFIXROUTE] = count++; + continue; + } + + RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type), + tries); + } + + /* TODO: verify multicast */ + ATF_REQUIRE_MSG(count == 2, "Received only %d/2 messages", count); + ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first"); +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v4_gu_ifa_prefixroute_success, + "Tests validness for the prefix route removal after ifaddr assignment"); + +ATF_TC_BODY(rtm_del_v4_gu_ifa_prefixroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4_iface(tc); + + + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_delete_addr(c->ifname, c->addr4_str); + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) + break; + } + + /* This should be a message for the prefix route */ + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_DONE | RTF_PINNED; + verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); +} + + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, rtm_get_v4_exact_success); @@ -515,6 +991,14 @@ ATF_TP_ADD_TC(tp, rtm_del_v4_prefix_nogw_success); ATF_TP_ADD_TC(tp, rtm_add_v6_gu_gw_gu_direct_success); ATF_TP_ADD_TC(tp, rtm_del_v6_gu_prefix_nogw_success); + /* ifaddr tests */ + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_hostroute_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_prefixroute_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_ordered_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_hostroute_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_prefixroute_success); + ATF_TP_ADD_TC(tp, rtm_add_v4_gu_ifa_ordered_success); + ATF_TP_ADD_TC(tp, rtm_del_v4_gu_ifa_prefixroute_success); return (atf_no_error()); }