conf: Avoid getifaddrs(), split L2/L3 address fetching, get filtered dumps

getifaddrs() needs to allocate heap memory, and gets a ton of results
we don't need. Use explicit netlink messages with "strict checking"
instead.

While at it, separate L2/L3 address handling, so that we don't fetch
MAC addresses for IPv6, and also use netlink instead of ioctl() to
get the MAC address.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
Stefano Brivio 2021-10-10 01:09:25 +02:00
parent e871fa9f22
commit 580581fd96
2 changed files with 179 additions and 133 deletions

276
conf.c
View file

@ -267,14 +267,27 @@ overlap:
} }
/** /**
* struct nl_request - Netlink request filled and sent by get_routes() * nl_req() - Send netlink request and read response, doesn't return on failure
* @nlh: Netlink message header * @buf: Buffer for response (BUFSIZ long)
* @rtm: Routing Netlink message * @req: Request with netlink header
* @len: Request length
*/ */
struct nl_request { static void nl_req(char *buf, void *req, ssize_t len)
struct nlmsghdr nlh; {
struct rtmsg rtm; int s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE), v = 1;
}; struct sockaddr_nl addr = { .nl_family = AF_NETLINK, };
if (s < 0 ||
setsockopt(s, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &v, sizeof(v)) ||
bind(s, (struct sockaddr *)&addr, sizeof(addr)) ||
(send(s, req, len, 0) < len) ||
(recv(s, buf, BUFSIZ, 0) < 0)) {
perror("netlink recv");
exit(EXIT_FAILURE);
}
close(s);
}
/** /**
* get_routes() - Get default route and fill in routable interface name * get_routes() - Get default route and fill in routable interface name
@ -282,57 +295,33 @@ struct nl_request {
*/ */
static void get_routes(struct ctx *c) static void get_routes(struct ctx *c)
{ {
struct nl_request req = { struct { struct nlmsghdr nlh; struct rtmsg rtm; } req = {
.nlh.nlmsg_type = RTM_GETROUTE, .nlh.nlmsg_type = RTM_GETROUTE,
.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_EXCL, .nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_EXCL,
.nlh.nlmsg_len = sizeof(struct nl_request), .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
.nlh.nlmsg_seq = 1, .nlh.nlmsg_seq = 1,
.rtm.rtm_family = AF_INET, .rtm.rtm_family = AF_INET,
.rtm.rtm_table = RT_TABLE_MAIN, .rtm.rtm_table = RT_TABLE_MAIN,
.rtm.rtm_scope = RT_SCOPE_UNIVERSE, .rtm.rtm_scope = RT_SCOPE_UNIVERSE,
.rtm.rtm_type = RTN_UNICAST, .rtm.rtm_type = RTN_UNICAST,
}; };
struct sockaddr_nl addr = { char ifn[IFNAMSIZ], buf[BUFSIZ];
.nl_family = AF_NETLINK, struct nlmsghdr *nh;
};
struct nlmsghdr *nlh;
int s, n, na, v4, v6;
char ifn[IFNAMSIZ];
struct rtattr *rta; struct rtattr *rta;
struct rtmsg *rtm; struct rtmsg *rtm;
char buf[BUFSIZ]; int n, na, v4, v6;
if (!c->v4 && !c->v6) if (!c->v4 && !c->v6)
v4 = v6 = -1; v4 = v6 = -1;
else else
v6 = -!(v4 = -c->v4); v6 = -!(v4 = -c->v4);
s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (s < 0) {
perror("netlink socket");
goto out;
}
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("netlink bind");
goto out;
}
v6: v6:
if (send(s, &req, sizeof(req), 0) < 0) { nl_req(buf, &req, sizeof(req));
perror("netlink send"); nh = (struct nlmsghdr *)buf;
goto out;
}
n = recv(s, &buf, sizeof(buf), 0); for ( ; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) {
if (n < 0) { rtm = (struct rtmsg *)NLMSG_DATA(nh);
perror("netlink recv");
goto out;
}
nlh = (struct nlmsghdr *)buf;
for ( ; NLMSG_OK(nlh, n); nlh = NLMSG_NEXT(nlh, n)) {
rtm = (struct rtmsg *)NLMSG_DATA(nlh);
if (rtm->rtm_dst_len || if (rtm->rtm_dst_len ||
(rtm->rtm_family != AF_INET && rtm->rtm_family != AF_INET6)) (rtm->rtm_family != AF_INET && rtm->rtm_family != AF_INET6))
@ -342,7 +331,7 @@ v6:
if (*c->ifn) { if (*c->ifn) {
*ifn = 0; *ifn = 0;
for (rta = (struct rtattr *)RTM_RTA(rtm), for (rta = (struct rtattr *)RTM_RTA(rtm),
na = RTM_PAYLOAD(nlh); na = RTM_PAYLOAD(nh);
RTA_OK(rta, na); rta = RTA_NEXT(rta, na)) { RTA_OK(rta, na); rta = RTA_NEXT(rta, na)) {
if (rta->rta_type != RTA_OIF) if (rta->rta_type != RTA_OIF)
continue; continue;
@ -355,7 +344,7 @@ v6:
goto next; goto next;
} }
for (rta = (struct rtattr *)RTM_RTA(rtm), na = RTM_PAYLOAD(nlh); for (rta = (struct rtattr *)RTM_RTA(rtm), na = RTM_PAYLOAD(nh);
RTA_OK(rta, na); rta = RTA_NEXT(rta, na)) { RTA_OK(rta, na); rta = RTA_NEXT(rta, na)) {
if (!*c->ifn && rta->rta_type == RTA_OIF) if (!*c->ifn && rta->rta_type == RTA_OIF)
if_indextoname(*(unsigned *)RTA_DATA(rta), ifn); if_indextoname(*(unsigned *)RTA_DATA(rta), ifn);
@ -380,23 +369,18 @@ v6:
} }
next: next:
if (nlh->nlmsg_type == NLMSG_DONE) if (nh->nlmsg_type == NLMSG_DONE)
break; break;
} }
if (v6 == -1) { if (v6 < 0 && req.rtm.rtm_family == AF_INET) {
req.rtm.rtm_family = AF_INET6; req.rtm.rtm_family = AF_INET6;
req.nlh.nlmsg_seq++; req.nlh.nlmsg_seq++;
recv(s, &buf, sizeof(buf), 0);
v6--;
goto v6; goto v6;
} else if (v6 < 0) { } else if (v6 < 0) {
v6 = 0; v6 = 0;
} }
out:
close(s);
if ((v4 <= 0 && v6 <= 0) || (!*c->ifn && !*ifn)) { if ((v4 <= 0 && v6 <= 0) || (!*c->ifn && !*ifn)) {
err("No routing information"); err("No routing information");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -409,80 +393,98 @@ out:
} }
/** /**
* get_addrs() - Fetch MAC, IP addresses, masks of external routable interface * get_l3_addrs() - Fetch IP addresses of external routable interface
* @c: Execution context * @c: Execution context
*/ */
static void get_addrs(struct ctx *c) static void get_l3_addrs(struct ctx *c)
{ {
struct ifreq ifr = { struct { struct nlmsghdr nlh; struct ifaddrmsg ifa; } req = {
.ifr_addr.sa_family = AF_INET, .nlh.nlmsg_type = RTM_GETADDR,
.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
.nlh.nlmsg_seq = 1,
.ifa.ifa_family = AF_INET,
.ifa.ifa_index = if_nametoindex(c->ifn),
}; };
struct ifaddrs *ifaddr, *ifa; struct ifaddrmsg *ifa;
int s, v4 = 0, v6 = 0; struct nlmsghdr *nh;
struct rtattr *rta;
int n, na, v4, v6;
char buf[BUFSIZ];
if (getifaddrs(&ifaddr) == -1) { if (c->v4) {
perror("getifaddrs"); v4 = -1;
goto out; if ((c->addr4_seen = c->addr4))
}
if (c->addr4) {
c->addr4_seen = c->addr4;
v4 = 1; v4 = 1;
} }
if (c->v6) {
v6 = -2;
if (!IN6_IS_ADDR_UNSPECIFIED(&c->addr6)) { if (!IN6_IS_ADDR_UNSPECIFIED(&c->addr6)) {
memcpy(&c->addr6_seen, &c->addr6, sizeof(c->addr6)); memcpy(&c->addr6_seen, &c->addr6, sizeof(c->addr6));
memcpy(&c->addr6_ll_seen, &c->addr6, sizeof(c->addr6)); memcpy(&c->addr6_ll_seen, &c->addr6, sizeof(c->addr6));
v6 = 1; v6 = -1;
}
} }
/* Fill in any missing information */ next_v:
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { if (v4 < 0)
struct sockaddr_in *in_addr; req.ifa.ifa_family = AF_INET;
struct sockaddr_in *in_mask; else if (v6 < 0)
struct sockaddr_in6 *in6_addr; req.ifa.ifa_family = AF_INET6;
else
goto mask_only;
if (strcmp(ifa->ifa_name, c->ifn)) nl_req(buf, &req, sizeof(req));
nh = (struct nlmsghdr *)buf;
for ( ; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) {
if (nh->nlmsg_type != RTM_NEWADDR)
goto next;
ifa = (struct ifaddrmsg *)NLMSG_DATA(nh);
for (rta = (struct rtattr *)IFA_RTA(ifa), na = RTM_PAYLOAD(nh);
RTA_OK(rta, na); rta = RTA_NEXT(rta, na)) {
if (rta->rta_type != IFA_ADDRESS)
continue; continue;
if (!ifa->ifa_addr) if (v4 < 0) {
continue; memcpy(&c->addr4, RTA_DATA(rta),
sizeof(c->addr4));
in_addr = (struct sockaddr_in *)ifa->ifa_addr; memcpy(&c->addr4_seen, RTA_DATA(rta),
if (ifa->ifa_addr->sa_family == AF_INET && !c->addr4) { sizeof(c->addr4_seen));
c->addr4_seen = c->addr4 = in_addr->sin_addr.s_addr;
v4 = 1; v4 = 1;
} } else if (v6 < 0) {
if (v6 == -2 &&
if (ifa->ifa_addr->sa_family == AF_INET && !c->mask4 && ifa->ifa_scope == RT_SCOPE_UNIVERSE) {
in_addr->sin_addr.s_addr == c->addr4) { memcpy(&c->addr6, RTA_DATA(rta),
in_mask = (struct sockaddr_in *)ifa->ifa_netmask;
c->mask4 = in_mask->sin_addr.s_addr;
}
if (ifa->ifa_addr->sa_family == AF_INET6) {
in6_addr = (struct sockaddr_in6 *)ifa->ifa_addr;
if (IN6_IS_ADDR_LINKLOCAL(&in6_addr->sin6_addr) &&
IN6_IS_ADDR_UNSPECIFIED(&c->addr6_ll)) {
memcpy(&c->addr6_ll, &in6_addr->sin6_addr,
sizeof(c->addr6_ll));
} else if (IN6_IS_ADDR_UNSPECIFIED(&c->addr6)) {
memcpy(&c->addr6, &in6_addr->sin6_addr,
sizeof(c->addr6)); sizeof(c->addr6));
memcpy(&c->addr6_seen, &in6_addr->sin6_addr, memcpy(&c->addr6_seen, RTA_DATA(rta),
sizeof(c->addr6_seen));
memcpy(&c->addr6_ll_seen, &in6_addr->sin6_addr,
sizeof(c->addr6_seen)); sizeof(c->addr6_seen));
memcpy(&c->addr6_ll_seen, RTA_DATA(rta),
sizeof(c->addr6_ll_seen));
} else if (ifa->ifa_scope == RT_SCOPE_LINK) {
memcpy(&c->addr6_ll, RTA_DATA(rta),
sizeof(c->addr6_ll));
}
if (!IN6_IS_ADDR_UNSPECIFIED(&c->addr6) &&
!IN6_IS_ADDR_UNSPECIFIED(&c->addr6_ll))
v6 = 1; v6 = 1;
} }
} }
next:
if (nh->nlmsg_type == NLMSG_DONE)
break;
} }
freeifaddrs(ifaddr); if (v4 >= 0 && v6 < 0)
goto next_v;
if (v4 < c->v4 || v6 < c->v6) if (v4 < c->v4 || v6 < c->v6)
goto out; goto out;
mask_only:
if (v4 && !c->mask4) { if (v4 && !c->mask4) {
if (IN_CLASSA(ntohl(c->addr4))) if (IN_CLASSA(ntohl(c->addr4)))
c->mask4 = htonl(IN_CLASSA_NET); c->mask4 = htonl(IN_CLASSA_NET);
@ -494,31 +496,71 @@ static void get_addrs(struct ctx *c)
c->mask4 = 0xffffffff; c->mask4 = 0xffffffff;
} }
if (!memcmp(c->mac, ((uint8_t [ETH_ALEN]){ 0 }), ETH_ALEN)) {
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("socket SIOCGIFHWADDR");
goto out;
}
strncpy(ifr.ifr_name, c->ifn, IF_NAMESIZE);
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) {
perror("SIOCGIFHWADDR");
goto out;
}
close(s);
memcpy(c->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
}
memset(&c->mac_guest, 0xff, sizeof(c->mac_guest));
return; return;
out: out:
err("Couldn't get addresses for routable interface"); err("Couldn't get addresses for routable interface");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/**
* get_l2_addr() - Fetch hardware addresses of external routable interface
* @c: Execution context
*/
static void get_l2_addr(struct ctx *c)
{
struct { struct nlmsghdr nlh; struct ifinfomsg ifi; } req = {
.nlh.nlmsg_type = RTM_GETLINK,
.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP_FILTERED,
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.nlh.nlmsg_seq = 1,
.ifi.ifi_family = AF_UNSPEC,
.ifi.ifi_index = if_nametoindex(c->ifn),
};
struct ifinfomsg *ifi;
struct nlmsghdr *nh;
struct rtattr *rta;
char buf[BUFSIZ];
int n, na;
if (memcmp(c->mac, ((uint8_t [ETH_ALEN]){ 0 }), ETH_ALEN))
goto mac_guest;
nl_req(buf, &req, sizeof(req));
nh = (struct nlmsghdr *)buf;
for ( ; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) {
if (nh->nlmsg_type != RTM_NEWLINK)
goto next;
ifi = (struct ifinfomsg *)NLMSG_DATA(nh);
for (rta = (struct rtattr *)IFLA_RTA(ifi), na = RTM_PAYLOAD(nh);
RTA_OK(rta, na); rta = RTA_NEXT(rta, na)) {
if (rta->rta_type != IFLA_ADDRESS)
continue;
memcpy(c->mac, RTA_DATA(rta), ETH_ALEN);
break;
}
next:
if (nh->nlmsg_type == NLMSG_DONE)
break;
}
if (!memcmp(c->mac, ((uint8_t [ETH_ALEN]){ 0 }), ETH_ALEN))
goto out;
mac_guest:
if (memcmp(c->mac_guest, ((uint8_t [ETH_ALEN]){ 0 }), ETH_ALEN))
memset(&c->mac_guest, 0xff, sizeof(c->mac_guest));
return;
out:
err("Couldn't get hardware address for routable interface");
exit(EXIT_FAILURE);
}
/** /**
* get_dns() - Get nameserver addresses from local /etc/resolv.conf * get_dns() - Get nameserver addresses from local /etc/resolv.conf
* @c: Execution context * @c: Execution context
@ -888,6 +930,8 @@ void conf_print(struct ctx *c)
inet_ntop(AF_INET6, &c->addr6, buf6, sizeof(buf6))); inet_ntop(AF_INET6, &c->addr6, buf6, sizeof(buf6)));
info(" router: %s", info(" router: %s",
inet_ntop(AF_INET6, &c->gw6, buf6, sizeof(buf6))); inet_ntop(AF_INET6, &c->gw6, buf6, sizeof(buf6)));
info(" our link-local: %s",
inet_ntop(AF_INET6, &c->addr6_ll, buf6, sizeof(buf6)));
for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->dns6[i]); i++) { for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->dns6[i]); i++) {
if (!i) if (!i)
@ -1290,7 +1334,9 @@ void conf(struct ctx *c, int argc, char **argv)
} }
get_routes(c); get_routes(c);
get_addrs(c); get_l3_addrs(c);
if (c->v4)
get_l2_addr(c);
if (c->mode == MODE_PASTA && dns4 == c->dns4 && dns6 == c->dns6) if (c->mode == MODE_PASTA && dns4 == c->dns4 && dns6 == c->dns6)
c->no_dns = 1; c->no_dns = 1;

View file

@ -98,7 +98,7 @@ enum passt_modes {
* @fd_tap_listen: File descriptor for listening AF_UNIX socket, if any * @fd_tap_listen: File descriptor for listening AF_UNIX socket, if any
* @fd_tap: File descriptor for AF_UNIX socket or tuntap device * @fd_tap: File descriptor for AF_UNIX socket or tuntap device
* @mac: Host MAC address * @mac: Host MAC address
* @mac_guest: Guest MAC address * @mac_guest: MAC address of guest or namespace, seen or configured
* @v4: Enable IPv4 transport * @v4: Enable IPv4 transport
* @addr4: IPv4 address for external, routable interface * @addr4: IPv4 address for external, routable interface
* @addr4_seen: Latest IPv4 address seen as source from tap * @addr4_seen: Latest IPv4 address seen as source from tap