diff --git a/CHANGELOG b/CHANGELOG index 8d4b289..b029bc8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -70,6 +70,12 @@ version 2.92 is functional when client and server networks aren't mutually route-able. + Fix failure to add client MAC address to queries in TCP mode. + The options which cause dnsmasq to decorate a DNS query with the MAC + address on the originating client can fail when the query is sent + using TCP. Thanks to Bruno Ravara for spotting and + characterising this bug. + version 2.91 Fix spurious "resource limit exceeded messages". Thanks to diff --git a/src/arp.c b/src/arp.c index cd57c92..a74c2cd 100644 --- a/src/arp.c +++ b/src/arp.c @@ -111,22 +111,26 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now) again: - /* If the database is less then INTERVAL old, look in there */ - if (difftime(now, last) < INTERVAL) + /* If the database is less then INTERVAL old, look in there. + + If we're a child process, we always rely on the existing cache we + inherited from the parent, since we don't have a netlink socket. + */ + if (difftime(now, last) < INTERVAL || daemon->pipe_to_parent != -1) { /* addr == NULL -> just make cache up-to-date */ if (!addr) return 0; - + for (arp = arps; arp; arp = arp->next) { if (addr->sa.sa_family != arp->family) continue; - + if (arp->family == AF_INET && arp->addr.addr4.s_addr != addr->in.sin_addr.s_addr) continue; - + if (arp->family == AF_INET6 && !IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, &addr->in6.sin6_addr)) continue; @@ -141,6 +145,10 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now) } } + /* Not in cache in child, no go. */ + if (daemon->pipe_to_parent != -1) + return 0; + /* Not found, try the kernel */ if (!updated) { diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 9c37e5a..0908858 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -2033,9 +2033,24 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot) if (!option_bool(OPT_DEBUG)) { + /* The code in edns0.c qthat decorates queries with the source MAC address depends + on the code in arp.c, which populates a cache with the contents of the ARP table + using netlink. Since the child process can't use netlink, we pre-populate + the cache with the ARP table entry for our source here, including a negative entry + if there is nothing for our address in the ARP table. + + When the edns0 code calls find_mac() in the child process, it will + get the correct answer from the cache inherited from the parent + without having to use netlink to consult the kernel ARP table. + + edns0_needs_mac() simply calls find_mac if any EDNS0 options + which need a MAC address are enabled. */ + + edns0_needs_mac(&tcp_addr, now); + if (pipe(pipefd) == -1) goto closeconandreturn; /* pipe failed */ - + if ((p = fork()) == -1) { /* fork failed */ diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 6d2f67c..254bacd 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1918,6 +1918,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, int optno, unsigned char *opt, size_t optlen, int set_do, int replace); size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit); +void edns0_needs_mac(union mysockaddr *addr, time_t now); size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, time_t now, int *cacheable); int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer); diff --git a/src/edns0.c b/src/edns0.c index 2f03291..b4ef82c 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -265,6 +265,15 @@ static void encoder(unsigned char *in, char *out) out[3] = char64(in[2]); } +/* This function needs to call find_mac if any option which requires a MAC address is enabled + and used below. If you add a new MAC consumer, modify this, otherwise your + new EDNS0 option won't work in TCP mode. */ +void edns0_needs_mac(union mysockaddr *addr, time_t now) +{ + if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX) || option_bool(OPT_ADD_MAC)) + find_mac(addr, NULL, 0, now); +} + /* OPT_ADD_MAC = MAC is added (if available) OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed OPT_STRIP_MAC = MAC is removed */ @@ -562,3 +571,4 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l return plen; } +