diff --git libnetutil/netutil.cc libnetutil/netutil.cc index 1f74752..f4c0246 100644 --- libnetutil/netutil.cc +++ libnetutil/netutil.cc @@ -2990,38 +2990,81 @@ static int set_sockaddr(struct sockaddr_storage *ss, int af, void *data) { return 0; } -/* Does route_dst using the Linux-specific rtnetlink interface. See rtnetlink(3) - and rtnetlink(7). */ -static int route_dst_netlink(const struct sockaddr_storage *dst, - struct route_nfo *rnfo, const char *device, - const struct sockaddr_storage *spoofss) { - struct sockaddr_nl snl; - struct msghdr msg; - struct iovec iov; - struct nlmsghdr *nlmsg; +/* Add rtattrs to a netlink message specifying a source or destination address. + rta_type must be RTA_SRC or RTA_DST. This function adds either 1 or 2 + rtattrs: it always adds either an RTA_SRC or RTA_DST, depending on rta_type. + It also adds either RTA_IIF or RTA_OIF if the address family is AF_INET6 and + the sockaddr_in6 has a non-zero sin6_scope_id. */ +static void add_rtattr_addr(struct nlmsghdr *nlmsg, + struct rtattr **rtattr, unsigned int *len, + unsigned char rta_type, + const struct sockaddr_storage *ss) { struct rtmsg *rtmsg; - struct rtattr *rtattr; - unsigned char buf[512]; const void *addr; size_t addrlen; int ifindex; - int fd, rc, len; + + assert(rta_type == RTA_SRC || rta_type == RTA_DST); ifindex = 0; - if (dst->ss_family == AF_INET) { - addr = &((struct sockaddr_in *) dst)->sin_addr.s_addr; + if (ss->ss_family == AF_INET) { + addr = &((struct sockaddr_in *) ss)->sin_addr.s_addr; addrlen = IP_ADDR_LEN; - } else if (dst->ss_family == AF_INET6) { - const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) dst; + } else if (ss->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ss; addr = sin6->sin6_addr.s6_addr; addrlen = IP6_ADDR_LEN; ifindex = sin6->sin6_scope_id; } else { - netutil_fatal("%s: unknown address family %d", __func__, dst->ss_family); + netutil_fatal("%s: unknown address family %d", __func__, ss->ss_family); } + rtmsg = (struct rtmsg *) (nlmsg + 1); + if (rta_type == RTA_SRC) + rtmsg->rtm_src_len = addrlen * 8; + else + rtmsg->rtm_dst_len = addrlen * 8; + + /* Add an rtattr for the address. */ + (*rtattr)->rta_type = rta_type; + (*rtattr)->rta_len = RTA_LENGTH(addrlen); + assert(RTA_OK(*rtattr, *len)); + memcpy(RTA_DATA(*rtattr), addr, addrlen); + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + (*rtattr)->rta_len; + *rtattr = RTA_NEXT(*rtattr, len); + + /* Specific interface (sin6_scope_id) requested? */ + if (ifindex > 0) { + /* Add an rtattr for the interface. */ + if (rta_type == RTA_SRC) + (*rtattr)->rta_type = RTA_IIF; + else + (*rtattr)->rta_type = RTA_OIF; + (*rtattr)->rta_len = RTA_LENGTH(sizeof(uint32_t)); + assert(RTA_OK(*rtattr, *len)); + *(uint32_t *) RTA_DATA(*rtattr) = ifindex; + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + (*rtattr)->rta_len; + *rtattr = RTA_NEXT(*rtattr, len); + } +} + +/* Does route_dst using the Linux-specific rtnetlink interface. See rtnetlink(3) + and rtnetlink(7). */ +static int route_dst_netlink(const struct sockaddr_storage *dst, + struct route_nfo *rnfo, const char *device, + const struct sockaddr_storage *spoofss) { + struct sockaddr_nl snl; + struct msghdr msg; + struct iovec iov; + struct nlmsghdr *nlmsg; + struct rtmsg *rtmsg; + struct rtattr *rtattr; + unsigned char buf[512]; + unsigned int len; + int fd, rc; + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd == -1) netutil_fatal("%s: cannot create AF_NETLINK socket: %s", __func__, strerror(errno)); @@ -3044,27 +3087,15 @@ static int route_dst_netlink(const struct sockaddr_storage *dst, rtmsg = (struct rtmsg *) (nlmsg + 1); rtmsg->rtm_family = dst->ss_family; - rtmsg->rtm_dst_len = addrlen * 8; rtattr = RTM_RTA(rtmsg); - len = sizeof(buf) - ((unsigned char *) rtattr - buf); - - /* Add an rtattr for destination address. */ - rtattr->rta_type = RTA_DST; - rtattr->rta_len = RTA_LENGTH(addrlen); - assert(RTA_OK(rtattr, len)); - memcpy(RTA_DATA(rtattr), addr, addrlen); - nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + rtattr->rta_len; + len = sizeof(buf) - ((unsigned char *) RTM_RTA(rtmsg) - buf); - /* Specific interface (sin6_scope_id) requested? */ - if (ifindex > 0) { - /* Add an rtattr for outgoing interface. */ - rtattr = RTA_NEXT(rtattr, len); - rtattr->rta_type = RTA_OIF; - rtattr->rta_len = RTA_LENGTH(sizeof(uint32_t)); - assert(RTA_OK(rtattr, len)); - *(uint32_t *) RTA_DATA(rtattr) = ifindex; - nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + rtattr->rta_len; + /* Add rtattrs for destination address and interface. */ + add_rtattr_addr(nlmsg, &rtattr, &len, RTA_DST, dst); + if (spoofss != NULL) { + /* Add rtattrs for source address and interface. */ + add_rtattr_addr(nlmsg, &rtattr, &len, RTA_SRC, spoofss); } iov.iov_base = nlmsg;