From 33702ab1f829789183cbaf6b1c39eee7ff15d744 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 28 Dec 2015 23:17:15 +0000 Subject: [PATCH] First complete version of DNS-client-id EDNS0 and ARP tracking code. --- src/arp.c | 148 +++++++++++++++++++++++++++------------------ src/config.h | 2 +- src/dhcp6.c | 6 +- src/dns-protocol.h | 2 + src/dnsmasq.c | 23 ++++--- src/dnsmasq.h | 25 +++++--- src/dnssec.c | 2 +- src/edns0.c | 72 ++++++++++++++++++---- src/forward.c | 109 ++++++++++++++++----------------- src/helper.c | 66 +++++++++++++++++--- src/option.c | 9 +++ src/rfc3315.c | 7 ++- 12 files changed, 309 insertions(+), 162 deletions(-) diff --git a/src/arp.c b/src/arp.c index b624dac..f41cdec 100644 --- a/src/arp.c +++ b/src/arp.c @@ -16,26 +16,31 @@ #include "dnsmasq.h" -#define ARP_FREE 0 -#define ARP_FOUND 1 -#define ARP_NEW 2 -#define ARP_EMPTY 3 +/* Time between forced re-loads from kernel. */ +#define INTERVAL 90 + +#define ARP_MARK 0 +#define ARP_FOUND 1 /* Confirmed */ +#define ARP_NEW 2 /* Newly created */ +#define ARP_EMPTY 3 /* No MAC addr */ struct arp_record { - short hwlen, status; + unsigned short hwlen, status; int family; unsigned char hwaddr[DHCP_CHADDR_MAX]; struct all_addr addr; struct arp_record *next; }; -static struct arp_record *arps = NULL, *old = NULL; +static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL; +static time_t last = 0; static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) { - int match = 0; struct arp_record *arp; + (void)parmv; + if (maclen > DHCP_CHADDR_MAX) return 1; @@ -58,16 +63,18 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p } #endif - if (arp->status != ARP_EMPTY && arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) - arp->status = ARP_FOUND; - else + if (arp->status == ARP_EMPTY) { - /* existing address, MAC changed or arrived new. */ + /* existing address, was negative. */ arp->status = ARP_NEW; arp->hwlen = maclen; - arp->family = family; memcpy(arp->hwaddr, mac, maclen); } + else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) + /* Existing entry matches - confirm. */ + arp->status = ARP_FOUND; + else + continue; break; } @@ -75,10 +82,10 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p if (!arp) { /* New entry */ - if (old) + if (freelist) { - arp = old; - old = old->next; + arp = freelist; + freelist = freelist->next; } else if (!(arp = whine_malloc(sizeof(struct arp_record)))) return 1; @@ -101,81 +108,72 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p } /* If in lazy mode, we cache absence of ARP entries. */ -int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy) +int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now) { struct arp_record *arp, **up; int updated = 0; again: - for (arp = arps; arp; arp = arp->next) - { - if (addr->sa.sa_family == arp->family) - { - if (arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr) - continue; - } + /* If the database is less then INTERVAL old, look in there */ + if (difftime(now, last) < INTERVAL) + for (arp = arps; arp; arp = arp->next) + { + if (addr->sa.sa_family == arp->family) + { + if (arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr) + continue; + } #ifdef HAVE_IPV6 - else - { - if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr)) - continue; - } + else + { + if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr)) + continue; + } #endif - - /* Only accept poitive entries unless in lazy mode. */ - if (arp->status != ARP_EMPTY || lazy || updated) - { - if (mac && arp->hwlen != 0) - memcpy(mac, arp->hwaddr, arp->hwlen); - return arp->hwlen; - } - } - + + /* Only accept poitive entries unless in lazy mode. */ + if (arp->status != ARP_EMPTY || lazy || updated) + { + if (mac && arp->hwlen != 0) + memcpy(mac, arp->hwaddr, arp->hwlen); + return arp->hwlen; + } + } + /* Not found, try the kernel */ if (!updated) { updated = 1; - + last = now; + /* Mark all non-negative entries */ for (arp = arps, up = &arps; arp; arp = arp->next) if (arp->status != ARP_EMPTY) - arp->status = ARP_FREE; + arp->status = ARP_MARK; iface_enumerate(AF_UNSPEC, NULL, filter_mac); - /* Remove all unconfirmed entries to old list, announce new ones. */ + /* Remove all unconfirmed entries to old list. */ for (arp = arps, up = &arps; arp; arp = arp->next) - if (arp->status == ARP_FREE) + if (arp->status == ARP_MARK) { *up = arp->next; arp->next = old; old = arp; } else - { - up = &arp->next; - if (arp->status == ARP_NEW) - { - char a[ADDRSTRLEN], m[ADDRSTRLEN]; - union mysockaddr pa; - pa.sa.sa_family = arp->family; - pa.in.sin_addr.s_addr = arp->addr.addr.addr4.s_addr; - prettyprint_addr(&pa, a); - print_mac(m, arp->hwaddr, arp->hwlen); - my_syslog(LOG_INFO, _("new arp: %s %s"), a, m); - } - } - + up = &arp->next; + goto again; } /* record failure, so we don't consult the kernel each time we're asked for this address */ - if (old) + if (freelist) { - arp = old; - old = old->next; + arp = freelist; + freelist = freelist->next; } else arp = whine_malloc(sizeof(struct arp_record)); @@ -198,4 +196,36 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy) return 0; } +int do_arp_script_run(void) +{ + struct arp_record *arp; + + /* Notify any which went, then move to free list */ + if (old) + { +#ifdef HAVE_SCRIPT + if (option_bool(OPT_DNS_CLIENT)) + queue_arp(ACTION_ARP_OLD, old->hwaddr, old->hwlen, old->family, &old->addr); +#endif + arp = old; + old = arp->next; + arp->next = freelist; + freelist = arp; + return 1; + } + + for (arp = arps; arp; arp = arp->next) + if (arp->status == ARP_NEW) + { +#ifdef HAVE_SCRIPT + if (option_bool(OPT_DNS_CLIENT)) + queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp->family, &arp->addr); +#endif + arp->status = ARP_FOUND; + return 1; + } + + return 0; +} + diff --git a/src/config.h b/src/config.h index f75fe9d..309be6b 100644 --- a/src/config.h +++ b/src/config.h @@ -337,7 +337,7 @@ HAVE_SOCKADDR_SA_LEN #define HAVE_DHCP #endif -#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK) +#if defined(NO_SCRIPT) || defined(NO_FORK) #undef HAVE_SCRIPT #undef HAVE_LUASCRIPT #endif diff --git a/src/dhcp6.c b/src/dhcp6.c index 7b1a7c7..0e2e171 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -220,7 +220,7 @@ void dhcp6_packet(time_t now) 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); + relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now); return; } @@ -250,7 +250,7 @@ void dhcp6_packet(time_t now) } } -void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep) +void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now) { /* Recieving a packet from a host does not populate the neighbour cache, so we send a neighbour discovery request if we can't @@ -280,7 +280,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi { struct timespec ts; - if ((maclen = find_mac(&addr, mac, 0)) != 0) + if ((maclen = find_mac(&addr, mac, 0, now)) != 0) break; sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr)); diff --git a/src/dns-protocol.h b/src/dns-protocol.h index 6cf5158..addfa9e 100644 --- a/src/dns-protocol.h +++ b/src/dns-protocol.h @@ -77,6 +77,8 @@ #define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */ #define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */ +#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */ +#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */ struct dns_header { u16 id; diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 45761cc..229693f 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -245,8 +245,11 @@ int main (int argc, char **argv) /* Note that order matters here, we must call lease_init before creating any file descriptors which shouldn't be leaked to the lease-script init process. We need to call common_init - before lease_init to allocate buffers it uses.*/ - if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6) + before lease_init to allocate buffers it uses. + The script subsystrm relies on DHCP buffers, hence the last two + conditions below. */ + if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || + daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC)) { dhcp_common_init(); if (daemon->dhcp || daemon->doing_dhcp6) @@ -553,8 +556,9 @@ int main (int argc, char **argv) /* if we are to run scripts, we need to fork a helper before dropping root. */ daemon->helperfd = -1; #ifdef HAVE_SCRIPT - if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript)) - daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); + if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC)) && + (daemon->lease_change_command || daemon->luascript)) + daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); #endif if (!option_bool(OPT_DEBUG) && getuid() == 0) @@ -914,9 +918,9 @@ int main (int argc, char **argv) poll_listen(piperead, POLLIN); -#ifdef HAVE_DHCP -# ifdef HAVE_SCRIPT - while (helper_buf_empty() && do_script_run(now)); +#ifdef HAVE_SCRIPT + while (helper_buf_empty() && do_script_run(now)); + while (helper_buf_empty() && do_arp_script_run()); # ifdef HAVE_TFTP while (helper_buf_empty() && do_tftp_script_run()); @@ -924,16 +928,17 @@ int main (int argc, char **argv) if (!helper_buf_empty()) poll_listen(daemon->helperfd, POLLOUT); -# else +#else /* need this for other side-effects */ while (do_script_run(now)); + while (do_arp_script_run(now)); # ifdef HAVE_TFTP while (do_tftp_script_run()); # endif -# endif #endif + /* must do this just before select(), when we know no more calls to my_syslog() can occur */ diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 4459594..fec0f8d 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -235,7 +235,8 @@ struct event_desc { #define OPT_LOOP_DETECT 50 #define OPT_EXTRALOG 51 #define OPT_TFTP_NO_FAIL 52 -#define OPT_LAST 53 +#define OPT_DNS_CLIENT 53 +#define OPT_LAST 54 /* extra flags for my_syslog, we use a couple of facilities since they are known not to occupy the same bits as priorities, no matter how syslog.h is set up. */ @@ -633,6 +634,8 @@ struct frec { #define ACTION_OLD 3 #define ACTION_ADD 4 #define ACTION_TFTP 5 +#define ACTION_ARP 6 +#define ACTION_ARP_OLD 7 #define LEASE_NEW 1 /* newly created */ #define LEASE_CHANGED 2 /* modified */ @@ -948,6 +951,7 @@ extern struct daemon { int cachesize, ftabsize; int port, query_port, min_port; unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl; + char *dns_client_id; struct hostsfile *addn_hosts; struct dhcp_context *dhcp, *dhcp6; struct ra_interface *ra_interfaces; @@ -1135,7 +1139,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut); #endif /* dnssec.c */ -size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz); +size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz); int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, @@ -1372,6 +1376,8 @@ void queue_script(int action, struct dhcp_lease *lease, #ifdef HAVE_TFTP void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer); #endif +void queue_arp(int action, unsigned char *mac, int maclen, + int family, struct all_addr *addr); int helper_buf_empty(void); #endif @@ -1408,7 +1414,7 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct void make_duid(time_t now); void dhcp_construct_contexts(time_t now); void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, - unsigned int *maclenp, unsigned int *mactypep); + unsigned int *maclenp, unsigned int *mactypep, time_t now); #endif /* rfc3315.c */ @@ -1416,7 +1422,8 @@ 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, u32 scope_id); +void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, + u32 scope_id, time_t now); unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); #endif @@ -1512,11 +1519,11 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last); size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do); -size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3); -size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source); -size_t add_do_bit(struct dns_header *header, size_t plen, char *limit); +size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit); +size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, + union mysockaddr *source, time_t now, int *check_subnet); int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer); /* arp.c */ -int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy); - +int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now); +int do_arp_script_run(void); diff --git a/src/dnssec.c b/src/dnssec.c index ed2d3fe..918a2dc 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -2173,7 +2173,7 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen) } } -size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, +size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz) { unsigned char *p; diff --git a/src/edns0.c b/src/edns0.c index 9d8c0b9..12e0210 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -94,13 +94,6 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t return ret; } - -struct macparm { - unsigned char *limit; - struct dns_header *header; - size_t plen; - union mysockaddr *l3; -}; size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do) @@ -208,19 +201,54 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l return p - (unsigned char *)header; } -size_t add_do_bit(struct dns_header *header, size_t plen, char *limit) +size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit) { return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1); } -size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3) +static unsigned char char64(unsigned char c) +{ + return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f]; +} + +static void encoder(unsigned char *in, char *out) +{ + out[0] = char64(in[0]>>2); + out[1] = char64((in[0]<<4) | (in[1]>>4)); + out[2] = char64((in[1]<<2) | (in[2]>>6)); + out[3] = char64(in[2]); +} + +static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now) +{ + int maclen; + unsigned char mac[DHCP_CHADDR_MAX]; + char encode[8]; /* handle 6 byte MACs */ + + if ((maclen = find_mac(l3, mac, 1, now)) == 6) + { + encoder(mac, encode); + encoder(mac+3, encode+4); + + plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, 8, 0); + } + + if (daemon->dns_client_id) + plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, + (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0); + + return plen; +} + + +static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now) { int maclen; unsigned char mac[DHCP_CHADDR_MAX]; - if ((maclen = find_mac(l3, mac, 1)) != 0) + if ((maclen = find_mac(l3, mac, 1, now)) != 0) plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0); - + return plen; } @@ -296,7 +324,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) return len + 4; } -size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source) +static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source) { /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ @@ -344,3 +372,23 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe return 1; } + +size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, + union mysockaddr *source, time_t now, int *check_subnet) +{ + *check_subnet = 0; + + if (option_bool(OPT_ADD_MAC)) + plen = add_mac(header, plen, limit, source, now); + + if (option_bool(OPT_DNS_CLIENT)) + plen = add_dns_client(header, plen, limit, source, now); + + if (option_bool(OPT_CLIENT_SUBNET)) + { + plen = add_source_addr(header, plen, limit, source); + *check_subnet = 1; + } + + return plen; +} diff --git a/src/forward.c b/src/forward.c index c0e4d9a..911f46e 100644 --- a/src/forward.c +++ b/src/forward.c @@ -388,36 +388,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, if (!flags && forward) { struct server *firstsentto = start; - int forwarded = 0; + int subnet, forwarded = 0; size_t edns0_len; /* If a query is retried, use the log_id for the retry when logging the answer. */ forward->log_id = daemon->log_id; - if (option_bool(OPT_ADD_MAC)) + edns0_len = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet); + + if (edns0_len != plen) { - size_t new = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source); - if (new != plen) - { - plen = new; - forward->flags |= FREC_ADDED_PHEADER; - } + plen = edns0_len; + forward->flags |= FREC_ADDED_PHEADER; + + if (subnet) + forward->flags |= FREC_HAS_SUBNET; } - - if (option_bool(OPT_CLIENT_SUBNET)) - { - size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source); - if (new != plen) - { - plen = new; - forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER; - } - } - + #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { - size_t new = add_do_bit(header, plen, ((char *) header) + PACKETSZ); + size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ); if (new != plen) forward->flags |= FREC_ADDED_PHEADER; @@ -607,15 +598,30 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server } else { + unsigned short udpsz; + /* If upstream is advertising a larger UDP packet size than we allow, trim it so that we don't get overlarge requests for the client. We can't do this for signed packets. */ - unsigned short udpsz; - unsigned char *psave = sizep; - GETSHORT(udpsz, sizep); if (udpsz > daemon->edns_pktsz) - PUTSHORT(daemon->edns_pktsz, psave); + { + sizep -= 2; + PUTSHORT(daemon->edns_pktsz, sizep); + } + +#ifdef HAVE_DNSSEC + /* If the client didn't set the do bit, but we did, reset it. */ + if (option_bool(OPT_DNSSEC_VALID) && !do_bit) + { + unsigned short flags; + sizep += 2; /* skip RCODE */ + GETSHORT(flags, sizep); + flags &= ~0x8000; + sizep -= 2; + PUTSHORT(flags, sizep); + } +#endif } } } @@ -674,14 +680,11 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server } #ifdef HAVE_DNSSEC - if (bogusanswer && !(header->hb4 & HB4_CD)) + if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG)) { - if (!option_bool(OPT_DNSSEC_DEBUG)) - { - /* Bogus reply, turn into SERVFAIL */ - SET_RCODE(header, SERVFAIL); - munged = 1; - } + /* Bogus reply, turn into SERVFAIL */ + SET_RCODE(header, SERVFAIL); + munged = 1; } if (option_bool(OPT_DNSSEC_VALID)) @@ -802,7 +805,7 @@ void reply_query(int fd, int family, time_t now) if (forward->flags |= FREC_AD_QUESTION) header->hb4 |= HB4_AD; if (forward->flags & FREC_DO_QUESTION) - add_do_bit(header, nn, (char *)pheader + plen); + add_do_bit(header, nn, (unsigned char *)pheader + plen); forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION); return; } @@ -927,13 +930,13 @@ void reply_query(int fd, int family, time_t now) if (status == STAT_NEED_KEY) { new->flags |= FREC_DNSKEY_QUERY; - nn = dnssec_generate_query(header, ((char *) header) + server->edns_pktsz, + nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz, daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz); } else { new->flags |= FREC_DS_QUERY; - nn = dnssec_generate_query(header,((char *) header) + server->edns_pktsz, + nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz, daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); } if ((hash = hash_questions(header, nn, daemon->namebuff))) @@ -1434,7 +1437,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si break; } - m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class, + m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz); *length = htons(m); @@ -1548,8 +1551,6 @@ unsigned char *tcp_request(int confd, time_t now, daemon->log_display_id = ++daemon->log_id; daemon->log_source_addr = &peer_addr; - check_subnet = 0; - /* save state of "cd" flag in query */ if ((checking_disabled = header->hb4 & HB4_CD)) no_cache_dnssec = 1; @@ -1627,20 +1628,14 @@ unsigned char *tcp_request(int confd, time_t now, struct all_addr *addrp = NULL; int type = 0; char *domain = NULL; - - if (option_bool(OPT_ADD_MAC)) - size = add_mac(header, size, ((char *) header) + 65536, &peer_addr); - - if (option_bool(OPT_CLIENT_SUBNET)) - { - size_t new = add_source_addr(header, size, ((char *) header) + 65536, &peer_addr); - if (size != new) - { - size = new; - check_subnet = 1; - } - } + size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet); + if (size != new_size) + { + added_pheader = 1; + size = new_size; + } + if (gotname) flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); @@ -1715,20 +1710,20 @@ unsigned char *tcp_request(int confd, time_t now, } #ifdef HAVE_DNSSEC - added_pheader = 0; if (option_bool(OPT_DNSSEC_VALID)) { - size_t new_size = add_do_bit(header, size, ((char *) header) + 65536); + new_size = add_do_bit(header, size, ((unsigned char *) header) + 65536); + + if (size != new_size) + { + added_pheader = 1; + size = new_size; + } /* For debugging, set Checking Disabled, otherwise, have the upstream check too, this allows it to select auth servers when one is returning bad data. */ if (option_bool(OPT_DNSSEC_DEBUG)) header->hb4 |= HB4_CD; - - if (size != new_size) - added_pheader = 1; - - size = new_size; } #endif } diff --git a/src/helper.c b/src/helper.c index 1fee72d..517cfd9 100644 --- a/src/helper.c +++ b/src/helper.c @@ -219,7 +219,18 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) action_str = "tftp"; is6 = (data.flags != AF_INET); } - else + else if (data.action == ACTION_ARP) + { + action_str = "arp"; + is6 = (data.flags != AF_INET); + } + else if (data.action == ACTION_ARP_OLD) + { + action_str = "arp-old"; + is6 = (data.flags != AF_INET); + data.action = ACTION_ARP; + } + else continue; @@ -321,6 +332,22 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ } } + else if (data.action == ACTION_ARP) + { + lua_getglobal(lua, "arp"); + if (lua_type(lua, -1) != LUA_TFUNCTION) + lua_pop(lua, 1); /* arp function optional */ + else + { + lua_pushstring(lua, action_str); /* arg1 - action */ + lua_newtable(lua); /* arg2 - data table */ + lua_pushstring(lua, daemon->addrbuff); + lua_setfield(lua, -2, "client_address"); + lua_pushstring(lua, daemon->dhcp_buff); + lua_setfield(lua, -2, "mac_address"); + lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ + } + } else { lua_getglobal(lua, "lease"); /* function to call */ @@ -478,7 +505,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) continue; } - if (data.action != ACTION_TFTP) + if (data.action != ACTION_TFTP && data.action != ACTION_ARP) { #ifdef HAVE_DHCP6 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err); @@ -550,10 +577,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err); if (data.action == ACTION_OLD_HOSTNAME) hostname = NULL; - } - - my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err); - + + my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err); + } /* we need to have the event_fd around if exec fails */ if ((i = fcntl(event_fd, F_GETFD)) != -1) fcntl(event_fd, F_SETFD, i | FD_CLOEXEC); @@ -563,8 +589,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) if (err == 0) { execl(daemon->lease_change_command, - p ? p+1 : daemon->lease_change_command, - action_str, is6 ? daemon->packet : daemon->dhcp_buff, + p ? p+1 : daemon->lease_change_command, action_str, + (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL); err = errno; } @@ -760,6 +786,30 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer) } #endif +void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr) +{ + /* no script */ + if (daemon->helperfd == -1) + return; + + buff_alloc(sizeof(struct script_data)); + memset(buf, 0, sizeof(struct script_data)); + + buf->action = action; + buf->hwaddr_len = maclen; + buf->hwaddr_type = ARPHRD_ETHER; + if ((buf->flags = family) == AF_INET) + buf->addr = addr->addr.addr4; +#ifdef HAVE_IPV6 + else + buf->addr6 = addr->addr.addr6; +#endif + + memcpy(buf->hwaddr, mac, maclen); + + bytes_in_buf = sizeof(struct script_data); +} + int helper_buf_empty(void) { return bytes_in_buf == 0; diff --git a/src/option.c b/src/option.c index 71beb98..f359bc5 100644 --- a/src/option.c +++ b/src/option.c @@ -154,6 +154,7 @@ struct myoption { #define LOPT_HOST_INOTIFY 342 #define LOPT_DNSSEC_STAMP 343 #define LOPT_TFTP_NO_FAIL 344 +#define LOPT_DNS_CLIENT_ID 355 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -281,6 +282,7 @@ static const struct myoption opts[] = { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND }, { "add-mac", 0, 0, LOPT_ADD_MAC }, { "add-subnet", 2, 0, LOPT_ADD_SBNET }, + { "add-dns-client", 2, 0 , LOPT_DNS_CLIENT_ID }, { "proxy-dnssec", 0, 0, LOPT_DNSSEC }, { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR }, { "conntrack", 0, 0, LOPT_CONNTRACK }, @@ -446,6 +448,7 @@ static struct { { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL }, { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL }, { LOPT_ADD_SBNET, ARG_ONE, "[,]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL }, + { LOPT_DNS_CLIENT_ID, ARG_ONE, "", gettext_noop("Add client identification to forwarded DNS queries."), NULL }, { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL }, { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL }, { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL }, @@ -2150,6 +2153,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } break; + case LOPT_DNS_CLIENT_ID: /* --add-dns-client */ + set_option_bool(OPT_DNS_CLIENT); + if (arg) + daemon->dns_client_id = opt_string_alloc(arg); + break; + case 'u': /* --user */ daemon->username = opt_string_alloc(arg); break; diff --git a/src/rfc3315.c b/src/rfc3315.c index 3ed8623..31bb41b 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -130,7 +130,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, MAC address from the local ND cache. */ if (!state->link_address) - get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type); + get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now); else { struct dhcp_context *c; @@ -2054,7 +2054,8 @@ 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) +void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, + struct in6_addr *peer_address, u32 scope_id, time_t now) { /* ->local is same value for all relays on ->current chain */ @@ -2068,7 +2069,7 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer unsigned char mac[DHCP_CHADDR_MAX]; inet_pton(AF_INET6, ALL_SERVERS, &multicast); - get_client_mac(peer_address, scope_id, mac, &maclen, &mactype); + get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now); /* source address == relay address */ from.addr.addr6 = relay->local.addr.addr6;