From 3f2873d42c4d7e7dba32b6e64a3687d43928bc8e Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Tue, 14 May 2013 11:28:47 +0100 Subject: [PATCH] Handle IPv4 interface-address labels in Linux. --- CHANGELOG | 9 +++++++++ src/bpf.c | 2 +- src/dhcp.c | 14 +++++++++----- src/dnsmasq.h | 1 + src/forward.c | 3 ++- src/lease.c | 3 ++- src/netlink.c | 7 +++++-- src/network.c | 39 +++++++++++++++++++++++++++++++-------- src/tftp.c | 3 ++- 9 files changed, 62 insertions(+), 19 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f6ce80e..7aa0024 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,15 @@ version 2.67 Fix --dhcp-match, --dhcp-vendorclass and --dhcp-userclass to work with BOOTP and well as DHCP. Thanks to Peter Korsgaard for spotting the problem. + + Handle IPv4 interface-address labels in Linux. These are + often used to emulate the old IP-alias addresses. Before, + using --interface=eth0 would service all the addresses of + eth0, including ones configured as aliases, which appear + in ifconfig as eth0:0. Now, only addresses with the label + eth0 are active. This is not backwards compatible: if you + want to continue to bind the aliases too, you need to add + eg. --interface=eth0:0 to the config. version 2.66 diff --git a/src/bpf.c b/src/bpf.c index 02a3abb..e75b0c6 100644 --- a/src/bpf.c +++ b/src/bpf.c @@ -123,7 +123,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr; else broadcast.s_addr = 0; - if (!((*callback)(addr, iface_index, netmask, broadcast, parm))) + if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm))) goto err; } #ifdef HAVE_IPV6 diff --git a/src/dhcp.c b/src/dhcp.c index dd25632..333a327 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -28,9 +28,9 @@ struct match_param { struct in_addr netmask, broadcast, addr; }; -static int complete_context(struct in_addr local, int if_index, +static int complete_context(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam); -static int check_listen_addrs(struct in_addr local, int if_index, +static int check_listen_addrs(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam); static int make_fd(int port) @@ -287,7 +287,7 @@ void dhcp_packet(time_t now, int pxe_fd) iface_addr = match.addr; /* make sure secondary address gets priority in case there is more than one address on the interface in the same subnet */ - complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm); + complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm); } if (!iface_enumerate(AF_INET, &parm, complete_context)) @@ -411,12 +411,14 @@ void dhcp_packet(time_t now, int pxe_fd) } /* check against secondary interface addresses */ -static int check_listen_addrs(struct in_addr local, int if_index, +static int check_listen_addrs(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) { struct match_param *param = vparam; struct iname *tmp; + (void) label; + if (if_index == param->ind) { for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) @@ -444,11 +446,13 @@ static int check_listen_addrs(struct in_addr local, int if_index, Note that the current chain may be superceded later for configured hosts or those coming via gateways. */ -static int complete_context(struct in_addr local, int if_index, +static int complete_context(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) { struct dhcp_context *context; struct iface_param *param = vparam; + + (void)label; for (context = daemon->dhcp; context; context = context->next) { diff --git a/src/dnsmasq.h b/src/dnsmasq.h index e177cea..8866dd8 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1030,6 +1030,7 @@ void create_bound_listeners(int die); int is_dad_listeners(void); int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns); int loopback_exception(int fd, int family, struct all_addr *addr, char *name); +int label_exception(int index, int family, struct all_addr *addr); int fix_fd(int fd); int tcp_interface(int fd, int af); struct in_addr get_ifaddr(char *intr); diff --git a/src/forward.c b/src/forward.c index 78495ca..28fe9eb 100644 --- a/src/forward.c +++ b/src/forward.c @@ -789,7 +789,8 @@ void receive_query(struct listener *listen, time_t now) { if (!option_bool(OPT_CLEVERBIND)) enumerate_interfaces(); - if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name)) + if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) && + !label_exception(if_index, listen->family, &dst_addr)) return; } diff --git a/src/lease.c b/src/lease.c index a4560ba..b85cf57 100644 --- a/src/lease.c +++ b/src/lease.c @@ -345,11 +345,12 @@ void lease_update_file(time_t now) } -static int find_interface_v4(struct in_addr local, int if_index, +static int find_interface_v4(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) { struct dhcp_lease *lease; + (void) label; (void) broadcast; (void) vparam; diff --git a/src/netlink.c b/src/netlink.c index 0881b71..78d0926 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -215,7 +215,8 @@ int iface_enumerate(int family, void *parm, int (*callback)()) if (ifa->ifa_family == AF_INET) { struct in_addr netmask, addr, broadcast; - + char *label = NULL; + netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen)); addr.s_addr = 0; broadcast.s_addr = 0; @@ -226,12 +227,14 @@ int iface_enumerate(int family, void *parm, int (*callback)()) addr = *((struct in_addr *)(rta+1)); else if (rta->rta_type == IFA_BROADCAST) broadcast = *((struct in_addr *)(rta+1)); + else if (rta->rta_type == IFA_LABEL) + label = RTA_DATA(rta); rta = RTA_NEXT(rta, len1); } if (addr.s_addr && callback_ok) - if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm))) + if (!((*callback)(addr, ifa->ifa_index, label, netmask, broadcast, parm))) callback_ok = 0; } #ifdef HAVE_IPV6 diff --git a/src/network.c b/src/network.c index 792914b..473e85f 100644 --- a/src/network.c +++ b/src/network.c @@ -204,7 +204,27 @@ int loopback_exception(int fd, int family, struct all_addr *addr, char *name) return 0; } -static int iface_allowed(struct irec **irecp, int if_index, +/* If we're configured with something like --interface=eth0:0 then we'll listen correctly + on the relevant address, but the name of the arrival interface, derived from the + index won't match the config. Check that we found an interface address for the arrival + interface: daemon->interfaces must be up-to-date. */ +int label_exception(int index, int family, struct all_addr *addr) +{ + struct irec *iface; + + /* labels only supported on IPv4 addresses. */ + if (family != AF_INET) + return 0; + + for (iface = daemon->interfaces; iface; iface = iface->next) + if (iface->index == index && iface->addr.sa.sa_family == AF_INET && + iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr) + return 1; + + return 0; +} + +static int iface_allowed(struct irec **irecp, int if_index, char *label, union mysockaddr *addr, struct in_addr netmask, int dad) { struct irec *iface; @@ -242,8 +262,8 @@ static int iface_allowed(struct irec **irecp, int if_index, loopback = ifr.ifr_flags & IFF_LOOPBACK; if (loopback) - dhcp_ok = 0; - + dhcp_ok = 0; + if (ioctl(fd, SIOCGIFMTU, &ifr) != -1) mtu = ifr.ifr_mtu; @@ -272,13 +292,16 @@ static int iface_allowed(struct irec **irecp, int if_index, } } + if (!label) + label = ifr.ifr_name; + if (addr->sa.sa_family == AF_INET && - !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, &auth_dns)) + !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, label, &auth_dns)) return 1; #ifdef HAVE_IPV6 if (addr->sa.sa_family == AF_INET6 && - !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, &auth_dns)) + !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, label, &auth_dns)) return 1; #endif @@ -348,11 +371,11 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix, addr.in6.sin6_port = htons(daemon->port); addr.in6.sin6_scope_id = if_index; - return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, !!(flags & IFACE_TENTATIVE)); + return iface_allowed((struct irec **)vparam, if_index, NULL, &addr, netmask, !!(flags & IFACE_TENTATIVE)); } #endif -static int iface_allowed_v4(struct in_addr local, int if_index, +static int iface_allowed_v4(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) { union mysockaddr addr; @@ -366,7 +389,7 @@ static int iface_allowed_v4(struct in_addr local, int if_index, addr.in.sin_addr = local; addr.in.sin_port = htons(daemon->port); - return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, 0); + return iface_allowed((struct irec **)vparam, if_index, label, &addr, netmask, 0); } int enumerate_interfaces(void) diff --git a/src/tftp.c b/src/tftp.c index 960b1ee..d7d050f 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -202,7 +202,8 @@ void tftp_request(struct listener *listen, time_t now) { if (!option_bool(OPT_CLEVERBIND)) enumerate_interfaces(); - if (!loopback_exception(listen->tftpfd, listen->family, &addra, name)) + if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) && + !label_exception(if_index, listen->family, &addra) ) return; } -- 1.8.1.4