diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2022-02-01 00:18:44 +0000 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2022-02-02 18:28:27 +0000 |
commit | 7fbf1cce7b63e366138cb336d60020885d60f848 (patch) | |
tree | edfda9e27b5dbdf1d6ea20542fbf0bedc0d73f9c | |
parent | dbceeb4178af80619c0e319f71c4466daeedec82 (diff) | |
download | dnsmasq-2.87test6.tar.gz |
Improve the performance of DHCP relay.v2.87test6
On machines with many interfaces, enumerating them
via netlink on each packet reciept is slow,
and unneccesary. All we need is the local address->interface
mapping, which can be cached in the relay structures.
-rw-r--r-- | src/dhcp.c | 179 | ||||
-rw-r--r-- | src/dhcp6.c | 57 | ||||
-rw-r--r-- | src/dnsmasq.h | 4 | ||||
-rw-r--r-- | src/network.c | 11 | ||||
-rw-r--r-- | src/rfc3315.c | 163 |
5 files changed, 212 insertions, 202 deletions
@@ -20,8 +20,6 @@ struct iface_param { struct dhcp_context *current; - struct dhcp_relay *relay; - struct in_addr relay_local; int ind; }; @@ -34,7 +32,7 @@ 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, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam); -static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index); +static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz); static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface); static int make_fd(int port) @@ -307,12 +305,7 @@ void dhcp_packet(time_t now, int pxe_fd) for (context = daemon->dhcp; context; context = context->next) context->current = context; - for (relay = daemon->relay4; relay; relay = relay->next) - relay->current = relay; - parm.current = NULL; - parm.relay = NULL; - parm.relay_local.s_addr = 0; parm.ind = iface_index; if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL)) @@ -334,15 +327,19 @@ void dhcp_packet(time_t now, int pxe_fd) there is more than one address on the interface in the same subnet */ complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm); } + + if (relay_upstream4(iface_index, mess, (size_t)sz)) + return; if (!iface_enumerate(AF_INET, &parm, complete_context)) return; - /* We're relaying this request */ - if (parm.relay_local.s_addr != 0 && - relay_upstream4(parm.relay, mess, (size_t)sz, iface_index)) + /* Check for a relay again after iface_enumerate/complete_context has had + chance to fill in relay->iface_index fields. This handles first time through + and any changes in interface config. */ + if (relay_upstream4(iface_index, mess, (size_t)sz)) return; - + /* May have configured relay, but not DHCP server */ if (!daemon->dhcp) return; @@ -631,14 +628,9 @@ static int complete_context(struct in_addr local, int if_index, char *label, } for (relay = daemon->relay4; relay; relay = relay->next) - if (if_index == param->ind && relay->local.addr4.s_addr == local.s_addr && relay->current == relay && - (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr)) - { - relay->current = param->relay; - param->relay = relay; - param->relay_local = local; - } - + if (relay->local.addr4.s_addr == local.s_addr) + relay->iface_index = if_index; + return 1; } @@ -1079,85 +1071,96 @@ char *host_from_dns(struct in_addr addr) return NULL; } -static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index) +static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz) { - /* ->local is same value for all relays on ->current chain */ - union all_addr from; - + struct in_addr giaddr = mess->giaddr; + u8 hops = mess->hops; + struct dhcp_relay *relay; + if (mess->op != BOOTREQUEST) return 0; - /* source address == relay address */ - from.addr4 = relay->local.addr4; - - /* already gatewayed ? */ - if (mess->giaddr.s_addr) - { - /* if so check if by us, to stomp on loops. */ - if (mess->giaddr.s_addr == relay->local.addr4.s_addr) - return 1; - } - else - { - /* plug in our address */ - mess->giaddr.s_addr = relay->local.addr4.s_addr; - } + for (relay = daemon->relay4; relay; relay = relay->next) + if (relay->iface_index != 0 && relay->iface_index == iface_index) + break; - if ((mess->hops++) > 20) - return 1; + /* No relay config. */ + if (!relay) + return 0; + + for (; relay; relay = relay->next) + if (relay->iface_index != 0 && relay->iface_index == iface_index) + { + union mysockaddr to; + union all_addr from; - for (; relay; relay = relay->current) - { - union mysockaddr to; - - to.sa.sa_family = AF_INET; - to.in.sin_addr = relay->server.addr4; - to.in.sin_port = htons(daemon->dhcp_server_port); + mess->hops = hops; + mess->giaddr = giaddr; + + if ((mess->hops++) > 20) + continue; + + /* source address == relay address */ + from.addr4 = relay->local.addr4; - /* Broadcasting to server. */ - if (relay->server.addr4.s_addr == 0) + /* already gatewayed ? */ + if (giaddr.s_addr) + { + /* if so check if by us, to stomp on loops. */ + if (giaddr.s_addr == relay->local.addr4.s_addr) + continue; + } + else + { + /* plug in our address */ + mess->giaddr.s_addr = relay->local.addr4.s_addr; + } + + to.sa.sa_family = AF_INET; + to.in.sin_addr = relay->server.addr4; + to.in.sin_port = htons(daemon->dhcp_server_port); + + /* Broadcasting to server. */ + if (relay->server.addr4.s_addr == 0) + { + struct ifreq ifr; + + if (relay->interface) + safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE); + + if (!relay->interface || strchr(relay->interface, '*') || + ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1) + { + my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface); + continue; + } + + to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; + } + +#ifdef HAVE_DUMPFILE { - struct ifreq ifr; - - if (relay->interface) - safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE); - - if (!relay->interface || strchr(relay->interface, '*') || - ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1) - { - my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface); - return 1; - } - - to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; + union mysockaddr fromsock; + fromsock.in.sin_port = htons(daemon->dhcp_server_port); + fromsock.in.sin_addr = from.addr4; + fromsock.sa.sa_family = AF_INET; + + dump_packet(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, 0); } - -#ifdef HAVE_DUMPFILE - { - union mysockaddr fromsock; - fromsock.in.sin_port = htons(daemon->dhcp_server_port); - fromsock.in.sin_addr = from.addr4; - fromsock.sa.sa_family = AF_INET; +#endif - dump_packet(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, 0); + send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0); + + if (option_bool(OPT_LOG_OPTS)) + { + inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN); + if (relay->server.addr4.s_addr == 0) + snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface); + else + inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ); + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2); + } } -#endif - - send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0); - - if (option_bool(OPT_LOG_OPTS)) - { - inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN); - if (relay->server.addr4.s_addr == 0) - snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface); - else - inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ); - my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2); - } - - /* Save this for replies */ - relay->iface_index = iface_index; - } return 1; } diff --git a/src/dhcp6.c b/src/dhcp6.c index a343740..edb87a4 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -22,8 +22,7 @@ struct iface_param { struct dhcp_context *current; - struct dhcp_relay *relay; - struct in6_addr fallback, relay_local, ll_addr, ula_addr; + struct in6_addr fallback, ll_addr, ula_addr; int ind, addr_match; }; @@ -90,7 +89,6 @@ void dhcp6_init(void) void dhcp6_packet(time_t now) { struct dhcp_context *context; - struct dhcp_relay *relay; struct iface_param parm; struct cmsghdr *cmptr; struct msghdr msg; @@ -105,7 +103,8 @@ void dhcp6_packet(time_t now) struct iname *tmp; unsigned short port; struct in6_addr dst_addr; - + struct in6_addr all_servers; + memset(&dst_addr, 0, sizeof(dst_addr)); msg.msg_control = control_u.control6; @@ -164,8 +163,6 @@ void dhcp6_packet(time_t now) return; parm.current = NULL; - parm.relay = NULL; - memset(&parm.relay_local, 0, IN6ADDRSZ); parm.ind = if_index; parm.addr_match = 0; memset(&parm.fallback, 0, IN6ADDRSZ); @@ -210,12 +207,24 @@ void dhcp6_packet(time_t now) memset(&context->local6, 0, IN6ADDRSZ); } - for (relay = daemon->relay6; relay; relay = relay->next) - relay->current = relay; + /* Ignore requests sent to the ALL_SERVERS multicast address for relay when + we're listening there for DHCPv6 server reasons. */ + inet_pton(AF_INET6, ALL_SERVERS, &all_servers); + + if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) && + relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now)) + return; if (!iface_enumerate(AF_INET6, &parm, complete_context6)) return; + /* Check for a relay again after iface_enumerate/complete_context has had + chance to fill in relay->iface_index fields. This handles first time through + and any changes in interface config. */ + if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) && + relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now)) + return; + if (daemon->if_names || daemon->if_addrs) { @@ -227,19 +236,6 @@ void dhcp6_packet(time_t now) return; } - if (parm.relay) - { - /* Ignore requests sent to the ALL_SERVERS multicast address for relay when - we're listening there for DHCPv6 server reasons. */ - struct in6_addr all_servers; - - inet_pton(AF_INET6, ALL_SERVERS, &all_servers); - - if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers)) - relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now); - return; - } - /* May have configured relay, but not DHCP server */ if (!daemon->doing_dhcp6) return; @@ -326,6 +322,7 @@ static int complete_context6(struct in6_addr *local, int prefix, struct dhcp_relay *relay; struct iface_param *param = vparam; struct iname *tmp; + int match = !daemon->if_addrs; (void)scope; /* warning */ @@ -347,7 +344,7 @@ static int complete_context6(struct in6_addr *local, int prefix, for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) if (tmp->addr.sa.sa_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local)) - param->addr_match = 1; + match = param->addr_match = 1; /* Determine a globally address on the arrival interface, even if we have no matching dhcp-context, because we're only @@ -419,16 +416,12 @@ static int complete_context6(struct in6_addr *local, int prefix, } } } - - for (relay = daemon->relay6; relay; relay = relay->next) - if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay && - (IN6_IS_ADDR_UNSPECIFIED(¶m->relay_local) || IN6_ARE_ADDR_EQUAL(local, ¶m->relay_local))) - { - relay->current = param->relay; - param->relay = relay; - param->relay_local = *local; - } - + + if (match) + for (relay = daemon->relay6; relay; relay = relay->next) + if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6)) + relay->iface_index = if_index; + return 1; } diff --git a/src/dnsmasq.h b/src/dnsmasq.h index ad2837e..2ffe808 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1089,7 +1089,7 @@ struct dhcp_relay { struct snoop_record *next; } *snoop_records; #endif - struct dhcp_relay *current, *next; + struct dhcp_relay *next; }; extern struct daemon { @@ -1682,7 +1682,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr, size_t sz, struct in6_addr *client_addr, time_t now); -void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, +int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address, u32 scope_id, time_t now); int relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); diff --git a/src/network.c b/src/network.c index 7cd3371..68aaa6c 100644 --- a/src/network.c +++ b/src/network.c @@ -1743,6 +1743,8 @@ int reload_servers(char *fname) /* Called when addresses are added or deleted from an interface */ void newaddress(time_t now) { + struct dhcp_relay *relay; + (void)now; if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) || @@ -1751,6 +1753,12 @@ void newaddress(time_t now) if (option_bool(OPT_CLEVERBIND)) create_bound_listeners(0); + +#ifdef HAVE_DHCP + /* clear cache of subnet->relay index */ + for (relay = daemon->relay4; relay; relay = relay->next) + relay->iface_index = 0; +#endif #ifdef HAVE_DHCP6 if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra) @@ -1761,5 +1769,8 @@ void newaddress(time_t now) if (daemon->doing_dhcp6) lease_find_interfaces(now); + + for (relay = daemon->relay6; relay; relay = relay->next) + relay->iface_index = 0; #endif } diff --git a/src/rfc3315.c b/src/rfc3315.c index 6c55672..cee8382 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -2100,110 +2100,113 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size) return ret; } -void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, - struct in6_addr *peer_address, u32 scope_id, time_t now) +int relay_upstream6(int iface_index, ssize_t sz, + struct in6_addr *peer_address, u32 scope_id, time_t now) { - /* ->local is same value for all relays on ->current chain */ - - union all_addr from; unsigned char *header; unsigned char *inbuff = daemon->dhcp_packet.iov_base; int msg_type = *inbuff; - int hopcount; + int hopcount, o; struct in6_addr multicast; unsigned int maclen, mactype; unsigned char mac[DHCP_CHADDR_MAX]; + struct dhcp_relay *relay; + + for (relay = daemon->relay6; relay; relay = relay->next) + if (relay->iface_index != 0 && relay->iface_index == iface_index) + break; + /* No relay config. */ + if (!relay) + return 0; + inet_pton(AF_INET6, ALL_SERVERS, &multicast); get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now); - - /* source address == relay address */ - from.addr6 = relay->local.addr6; - + /* Get hop count from nested relayed message */ if (msg_type == DHCP6RELAYFORW) hopcount = *((unsigned char *)inbuff+1) + 1; else hopcount = 0; - /* RFC 3315 HOP_COUNT_LIMIT */ - if (hopcount > 32) - return; - reset_counter(); - if ((header = put_opt6(NULL, 34))) + /* RFC 3315 HOP_COUNT_LIMIT */ + if (hopcount > 32 || !(header = put_opt6(NULL, 34))) + return 1; + + header[0] = DHCP6RELAYFORW; + header[1] = hopcount; + memcpy(&header[18], peer_address, IN6ADDRSZ); + + /* RFC-6939 */ + if (maclen != 0) { - int o; - - header[0] = DHCP6RELAYFORW; - header[1] = hopcount; - memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ); - memcpy(&header[18], peer_address, IN6ADDRSZ); - - /* RFC-6939 */ - if (maclen != 0) - { - o = new_opt6(OPTION6_CLIENT_MAC); - put_opt6_short(mactype); - put_opt6(mac, maclen); - end_opt6(o); - } - - o = new_opt6(OPTION6_RELAY_MSG); - put_opt6(inbuff, sz); + o = new_opt6(OPTION6_CLIENT_MAC); + put_opt6_short(mactype); + put_opt6(mac, maclen); end_opt6(o); - - for (; relay; relay = relay->current) - { - union mysockaddr to; - - to.sa.sa_family = AF_INET6; - to.in6.sin6_addr = relay->server.addr6; - to.in6.sin6_port = htons(DHCPV6_SERVER_PORT); - to.in6.sin6_flowinfo = 0; - to.in6.sin6_scope_id = 0; - - if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) - { - int multicast_iface; - if (!relay->interface || strchr(relay->interface, '*') || - (multicast_iface = if_nametoindex(relay->interface)) == 0 || - setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1) - { - my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast DHCP relay via interface %s"), relay->interface); - return; - } - } - -#ifdef HAVE_DUMPFILE + } + + o = new_opt6(OPTION6_RELAY_MSG); + put_opt6(inbuff, sz); + end_opt6(o); + + for (; relay; relay = relay->next) + if (relay->iface_index != 0 && relay->iface_index == iface_index) + { + union mysockaddr to; + union all_addr from; + + /* source address == relay address */ + from.addr6 = relay->local.addr6; + memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ); + + to.sa.sa_family = AF_INET6; + to.in6.sin6_addr = relay->server.addr6; + to.in6.sin6_port = htons(DHCPV6_SERVER_PORT); + to.in6.sin6_flowinfo = 0; + to.in6.sin6_scope_id = 0; + + if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) { - union mysockaddr fromsock; - fromsock.in6.sin6_port = htons(DHCPV6_SERVER_PORT); - fromsock.in6.sin6_addr = from.addr6; - fromsock.sa.sa_family = AF_INET6; - fromsock.in6.sin6_flowinfo = 0; - fromsock.in6.sin6_scope_id = 0; - - dump_packet(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), &fromsock, &to, 0); + int multicast_iface; + if (!relay->interface || strchr(relay->interface, '*') || + (multicast_iface = if_nametoindex(relay->interface)) == 0 || + setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1) + { + my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast DHCP relay via interface %s"), relay->interface); + continue; + } } -#endif - send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0); + +#ifdef HAVE_DUMPFILE + { + union mysockaddr fromsock; + fromsock.in6.sin6_port = htons(DHCPV6_SERVER_PORT); + fromsock.in6.sin6_addr = from.addr6; + fromsock.sa.sa_family = AF_INET6; + fromsock.in6.sin6_flowinfo = 0; + fromsock.in6.sin6_scope_id = 0; - if (option_bool(OPT_LOG_OPTS)) - { - inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN); - if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) - snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface); - else - inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); - my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff); - } - - /* Save this for replies */ - relay->iface_index = scope_id; + dump_packet(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), &fromsock, &to, 0); } - } +#endif + send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0); + + if (option_bool(OPT_LOG_OPTS)) + { + inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN); + if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) + snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface); + else + inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff); + } + + } + + return 1; } int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) |