diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2004-04-23 22:21:21 +0100 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2012-01-05 17:31:10 +0000 |
commit | a84fa1d085ae80a6b9466c5d2219b01210c8ed51 (patch) | |
tree | b69e6a2b1e5ae301dd6940aaba569ea8243d2af1 | |
parent | 33820b7ed94cee97017eba9ec695951944c96a4d (diff) | |
download | dnsmasq-a84fa1d085ae80a6b9466c5d2219b01210c8ed51.tar.gz |
import of dnsmasq-2.7.tar.gzv2.7
-rw-r--r-- | CHANGELOG | 43 | ||||
-rw-r--r-- | dnsmasq-rh.spec | 2 | ||||
-rw-r--r-- | dnsmasq-suse.spec | 2 | ||||
-rw-r--r-- | dnsmasq.8 | 18 | ||||
-rw-r--r-- | dnsmasq.conf.example | 6 | ||||
-rw-r--r-- | src/config.h | 2 | ||||
-rw-r--r-- | src/dhcp.c | 47 | ||||
-rw-r--r-- | src/dnsmasq.c | 19 | ||||
-rw-r--r-- | src/dnsmasq.h | 28 | ||||
-rw-r--r-- | src/network.c | 55 | ||||
-rw-r--r-- | src/option.c | 86 | ||||
-rw-r--r-- | src/rfc1035.c | 2 | ||||
-rw-r--r-- | src/rfc2131.c | 158 | ||||
-rw-r--r-- | src/util.c | 5 |
14 files changed, 310 insertions, 163 deletions
@@ -916,12 +916,49 @@ release 2.6 comment, a '#' character must now be a the start of a line or preceded by whitespace. Thanks to Christian Haggstrom for the bug report. - - +release 2.7 + Allow the dhcp-host specification of id:* which makes + dnsmasq ignore any client-id. This is useful to ensure + that a dual-boot machine sees the same lease when one OS + gives a client-id and the other doesn't. It's also useful + when PXE boot DHCP does not use client IDs but the OS it boots + does. Thanks to Grzegorz Nosek for suggesting this enhancement. + No longer assume that ciaddr is zero in received DHCPDISCOVER + messages, just for security against broken clients. + Set default of siaddr field to the address of the machine running + dnsmasq when not explicitly set using dhcp-boot + option. This is the ISC dhcpd behaviour. - + Send T1 and T2 options in DHCPOFFER packets. This is required + by the DHCP client in some JetDirect printers. Thanks + to Paul Mattal for work on this. + + Fixed bug with DHCP on OpenBSD reported by Dominique Jacquel. + The code which added loopback interfaces to the list + was confusing the DHCP code, which expected one interface only. + Solved by adding loopback interfaces to address list instead. + Add dhcp-vendorclass option to allow options to be sent only + to certain classes of clients. + + Tweaked option search code so that if a netid-qualified + option is used, any unqualified option is ignored. + Changed the method of picking new dynamic IP + addresses. This used to use the next consecutive + address as long it was free, now it uses a hash + from the client hardware address. This reduces the amount + of address movement for clients which let their lease + expire and allows consecutive DHCPOFFERS to the same host + to (almost always) be for the same address, without + storing state before a lease is granted. + + Tweaked option handling code to return all possible + options rather than none when DHCP "requested options" + field is missing. This fixes interoperability with + ancient IBM LANMAN DHCP clients. Thanks to Jim Louvau for + help with this. + diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec index 847a267..59982ae 100644 --- a/dnsmasq-rh.spec +++ b/dnsmasq-rh.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.6 +Version: 2.7 Release: 1 Copyright: GPL Group: System Environment/Daemons diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec index 984cb3e..9268a46 100644 --- a/dnsmasq-suse.spec +++ b/dnsmasq-suse.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.6 +Version: 2.7 Release: 1 Copyright: GPL Group: Productivity/Networking/DNS/Servers @@ -283,7 +283,7 @@ addresses given via .B dhcp-host or from /etc/ethers will be served. .TP -.B \-G, --dhcp-host=[[<hwaddr>]|[id:<client_id>]][net:<netid>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore] +.B \-G, --dhcp-host=[[<hwaddr>]|[id:[<client_id>][*]]][net:<netid>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore] Specify per host parameters for the DHCP server. This allows a machine with a particular hardware address to be always allocated the same hostname, IP address and lease time. A hostname specified like this @@ -305,7 +305,10 @@ hardware addresses to identify hosts by prefixing with 'id:'. Thus: .B --dhcp-host=id:01:02:03:04,..... refers to the host with client identifier 01:02:03:04. It is also allowed to specify the client ID as text, like this: -.B --dhcp-host=id:clientidastext,..... +.B --dhcp-host=id:clientidastext,..... +The special option id:* means "ignore any client-id +and use MAC addresses only." This is useful when a client presents a client-id sometimes +but not others. If a name appears in /etc/hosts, the associated address can be allocated to a DHCP lease, but only if a .B --dhcp-host @@ -340,7 +343,7 @@ specfied in RFC2132. For example, to set the default route option to 192.168.4.4, do .B --dhcp-option=3,192.168.4.4 and to set the time-server address to 192.168.0.4, do -.B dhcp-option=42,192.168.0.4 +.B --dhcp-option=42,192.168.0.4 The special address 0.0.0.0 is taken to mean "the address of the machine running dnsmasq". Data types allowed are comma seperated dotted-quad IP addresses, a decimal number, colon-seperated hex digits @@ -353,6 +356,15 @@ possible to generate the correct data type; it is quite possible to persuade dnsmasq to generate illegal DHCP packets with injudicious use of this flag. .TP +.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class> +Map from a vendor-class string to a network id. Most DHCP clients provide a +"vendor class" which represents, in some sense, the type of host. This options +maps vendor classes to network ids, so that DHCP options may be selectively delivered +to different classes of hosts. For example +.B dhcp-vendorclass=printers,Hewlett-Packard JetDirect +will allow options to be set only for HP printers like so: +.B --dhcp-option=printers,3,192.168.4.4 +.TP .B \-M, --dhcp-boot=<filename>,[<servername>[,<server address>]] Set BOOTP options to be returned by the DHCP server. These are needed for machines which network boot, and tell the machine where to collect diff --git a/dnsmasq.conf.example b/dnsmasq.conf.example index ce3bcde..f4d5969 100644 --- a/dnsmasq.conf.example +++ b/dnsmasq.conf.example @@ -161,6 +161,12 @@ filterwin2k # address is 11:22:33:44:55:66 #dhcp-host=11:22:33:44:55:66,ignore +# Ignore any client-id presented by the machine with ethernet +# address 11:22:33:44:55:66. This is useful to prevent a machine +# being treated differently when running under different OS's or +# between PXE boot and OS boot. +#dhcp-host=11:22:33:44:55:66,id:* + # Send extra options which are tagged as "red" to # the machine with ethernet address 11:22:33:44:55:66 #dhcp-host=11:22:33:44:55:66,net:red diff --git a/src/config.h b/src/config.h index ad9e08d..b6bf746 100644 --- a/src/config.h +++ b/src/config.h @@ -12,7 +12,7 @@ /* Author's email: simon@thekelleys.org.uk */ -#define VERSION "2.6" +#define VERSION "2.7" #define FTABSIZ 150 /* max number of outstanding requests */ #define TIMEOUT 20 /* drop queries after TIMEOUT seconds */ @@ -60,7 +60,8 @@ void dhcp_init(int *fdp, int* rfdp) } void dhcp_packet(struct dhcp_context *contexts, char *packet, - struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs, + struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs, + struct dhcp_vendor *vendors, time_t now, char *namebuff, char *domain_suffix, char *dhcp_file, char *dhcp_sname, struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd, @@ -181,7 +182,7 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet, /* we can use the interface netmask if either the packet came direct, or it came via a relay listening on the same network. This sounds unlikely, but it happens with win4lin. */ - if ((source.s_addr & iface_netmask.s_addr) != (iface_addr.s_addr & iface_netmask.s_addr)) + if (!is_same_net(source, iface_addr, iface_netmask)) iface_netmask.s_addr = 0; else if (ioctl(dhcp_fd, SIOCGIFBRDADDR, &ifr) != -1) iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; @@ -193,8 +194,8 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet, struct in_addr netmask = context->netmask.s_addr ? context->netmask : iface_netmask; if (netmask.s_addr && - (source.s_addr & netmask.s_addr) == (context->start.s_addr & netmask.s_addr) && - (source.s_addr & netmask.s_addr) == (context->end.s_addr & netmask.s_addr)) + is_same_net(source, context->start, netmask) && + is_same_net(source, context->end, netmask)) break; } @@ -225,7 +226,7 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet, DHCP broadcast, either this machine or a relay. In the special case that the relay is on the same network as us, we set the default route to us, not the relay. This is the win4lin scenario again. */ - if ((source.s_addr & context->netmask.s_addr) == (iface_addr.s_addr & context->netmask.s_addr)) + if (is_same_net(source, iface_addr, context->netmask)) router = iface_addr; else router = source; @@ -233,8 +234,8 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet, lease_prune(NULL, now); /* lose any expired leases */ newlen = dhcp_reply(context, iface_addr, ifr.ifr_name, ifr.ifr_mtu, rawpacket, sz, now, namebuff, - dhcp_opts, dhcp_configs, domain_suffix, dhcp_file, - dhcp_sname, dhcp_next_server, router); + dhcp_opts, dhcp_configs, vendors, domain_suffix, + dhcp_file, dhcp_sname, dhcp_next_server, router); lease_update_file(0, now); lease_update_dns(); @@ -350,7 +351,6 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet, } } - int address_available(struct dhcp_context *context, struct in_addr taddr) { /* Check is an address is OK for this network, ie @@ -379,38 +379,47 @@ int address_available(struct dhcp_context *context, struct in_addr taddr) } int address_allocate(struct dhcp_context *context, struct dhcp_config *configs, - struct in_addr *addrp) + struct in_addr *addrp, unsigned char *hwaddr) { /* Find a free address: exlude anything in use and anything allocated to a particular hwaddr/clientid/hostname in our configuration */ struct dhcp_config *config; - struct in_addr start = context->last; + struct in_addr start, addr ; + int i, j; /* start == end means no dynamic leases. */ if (context->end.s_addr == context->start.s_addr) return 0; - + + /* pick a seed based on hwaddr then iterate until we find a free address. */ + for (j = 0, i = 0; i < ETHER_ADDR_LEN; i++) + j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16); + + start.s_addr = addr.s_addr = + htonl(ntohl(context->start.s_addr) + + (j % (ntohl(context->end.s_addr) - ntohl(context->start.s_addr)))); + do { - if (context->last.s_addr == context->end.s_addr) - context->last = context->start; + if (addr.s_addr == context->end.s_addr) + addr = context->start; else - context->last.s_addr = htonl(ntohl(context->last.s_addr) + 1); + addr.s_addr = htonl(ntohl(addr.s_addr) + 1); - if (!lease_find_by_addr(context->last)) + if (!lease_find_by_addr(addr)) { for (config = configs; config; config = config->next) - if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == context->last.s_addr) + if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) break; if (!config) { - *addrp = context->last; + *addrp = addr; return 1; } } - } while (context->last.s_addr != start.s_addr); + } while (addr.s_addr != start.s_addr); return 0; } @@ -421,7 +430,7 @@ static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config * return 1; if (!(config->flags & CONFIG_ADDR)) return 1; - if ((config->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr)) + if (is_same_net(config->addr, context->start, context->netmask)) return 1; return 0; diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 3169e55..fff2ff7 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -66,6 +66,7 @@ int main (int argc, char **argv) struct dhcp_context *dhcp_tmp, *dhcp = NULL; struct dhcp_config *dhcp_configs = NULL; struct dhcp_opt *dhcp_options = NULL; + struct dhcp_vendor *dhcp_vendors = NULL; char *dhcp_file = NULL, *dhcp_sname = NULL; struct in_addr dhcp_next_server; int leasefd = -1, dhcpfd = -1, dhcp_raw_fd = -1; @@ -109,16 +110,10 @@ int main (int argc, char **argv) &username, &groupname, &domain_suffix, &runfile, &if_names, &if_addrs, &if_except, &bogus_addr, &serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts, - &dhcp, &dhcp_configs, &dhcp_options, + &dhcp, &dhcp_configs, &dhcp_options, &dhcp_vendors, &dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime, &doctors); - /* if we cannot support binding the wildcard address, set the "bind only - interfaces in use" option */ -#ifndef HAVE_UDP_SRC_DST - options |= OPT_NOWILD; -#endif - if (!lease_file) { if (dhcp) @@ -129,7 +124,13 @@ int main (int argc, char **argv) die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL); #endif - interfaces = enumerate_interfaces(if_names, if_addrs, if_except, port); +#ifndef HAVE_UDP_SRC_DST + /* if we cannot support binding the wildcard address, set the "bind only + interfaces in use" option */ + options |= OPT_NOWILD; +#endif + + interfaces = enumerate_interfaces(&if_names, &if_addrs, if_except, port); if (options & OPT_NOWILD) listeners = create_bound_listeners(interfaces); else @@ -397,7 +398,7 @@ int main (int argc, char **argv) dnamebuff, last_server, bogus_addr, doctors); if (dhcp && FD_ISSET(dhcpfd, &rset)) - dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs, + dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs, dhcp_vendors, now, dnamebuff, domain_suffix, dhcp_file, dhcp_sname, dhcp_next_server, dhcpfd, dhcp_raw_fd, if_names, if_addrs, if_except); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index edb10ad..0021d4e 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -130,7 +130,7 @@ struct crec { }; #define F_IMMORTAL 1 -#define F_CONFIG 2 +#define F_CONFIG 2 #define F_REVERSE 4 #define F_FORWARD 8 #define F_DHCP 16 @@ -255,18 +255,25 @@ struct dhcp_config { #define CONFIG_NAME 16 #define CONFIG_ADDR 32 #define CONFIG_NETID 64 +#define CONFIG_NOCLID 128 struct dhcp_opt { int opt, len, is_addr; unsigned char *val; char *netid; struct dhcp_opt *next; - }; +}; + +struct dhcp_vendor { + int len; + char *data, *net; + struct dhcp_vendor *next; +}; struct dhcp_context { unsigned int lease_time; struct in_addr netmask, broadcast; - struct in_addr start, end, last; /* range of available addresses */ + struct in_addr start, end; /* range of available addresses */ char *netid; struct dhcp_context *next; }; @@ -341,6 +348,8 @@ int sa_len(union mysockaddr *addr); int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2); int hostname_isequal(unsigned char *a, unsigned char *b); time_t dnsmasq_time(int fd); +int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask); + /* option.c */ unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resolv_file, char **mxname, char **mxtarget, char **lease_file, @@ -349,7 +358,8 @@ unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resol struct iname **if_names, struct iname **if_addrs, struct iname **if_except, struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize, int *port, int *query_port, unsigned long *local_ttl, char **addn_hosts, - struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf, struct dhcp_opt **opts, + struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf, + struct dhcp_opt **opts, struct dhcp_vendor **dhcp_vendors, char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server, int *maxleases, unsigned int *min_leasetime, struct doctor **doctors); @@ -367,8 +377,8 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname /* network.c */ struct server *reload_servers(char *fname, char *buff, struct server *servers, int query_port); struct server *check_servers(struct server *new, struct irec *interfaces, struct serverfd **sfds); -struct irec *enumerate_interfaces(struct iname *names, - struct iname *addrs, +struct irec *enumerate_interfaces(struct iname **names, + struct iname **addrs, struct iname *except, int port); struct listener *create_wildcard_listeners(int port); @@ -377,13 +387,14 @@ struct listener *create_bound_listeners(struct irec *interfaces); void dhcp_init(int *fdp, int* rfdp); void dhcp_packet(struct dhcp_context *contexts, char *packet, struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs, + struct dhcp_vendor *vendors, time_t now, char *namebuff, char *domain_suffix, char *dhcp_file, char *dhcp_sname, struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd, struct iname *names, struct iname *addrs, struct iname *except); int address_available(struct dhcp_context *context, struct in_addr addr); int address_allocate(struct dhcp_context *context, struct dhcp_config *configs, - struct in_addr *addrp); + struct in_addr *addrp, unsigned char *hwaddr); struct dhcp_config *find_config(struct dhcp_config *configs, struct dhcp_context *context, unsigned char *clid, int clid_len, @@ -391,6 +402,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs, struct dhcp_config *read_ethers(struct dhcp_config *configs, char *buff); void dhcp_update_configs(struct dhcp_config *configs); struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff); + /* lease.c */ void lease_update_file(int force, time_t now); void lease_update_dns(void); @@ -404,6 +416,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len); struct dhcp_lease *lease_find_by_addr(struct in_addr addr); void lease_prune(struct dhcp_lease *target, time_t now); void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain); + /* rfc2131.c */ int dhcp_reply(struct dhcp_context *context, struct in_addr iface_addr, @@ -412,6 +425,7 @@ int dhcp_reply(struct dhcp_context *context, struct udp_dhcp_packet *rawpacket, unsigned int sz, time_t now, char *namebuff, struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs, + struct dhcp_vendor *vendors, char *domain_suffix, char *dhcp_file, char *dhcp_sname, struct in_addr dhcp_next_server, struct in_addr router); diff --git a/src/network.c b/src/network.c index 54edf9d..e2f0f83 100644 --- a/src/network.c +++ b/src/network.c @@ -25,7 +25,7 @@ static struct irec *add_iface(struct irec *list, char *name, union mysockaddr *a if (except) for (tmp = except; tmp; tmp = tmp->next) if (tmp->name && strcmp(tmp->name, name) == 0) - return NULL; + return list; /* we may need to check the whitelist */ if (names || addrs) @@ -38,31 +38,31 @@ static struct irec *add_iface(struct irec *list, char *name, union mysockaddr *a if (sockaddr_isequal(&tmp->addr, addr)) break; if (!tmp) - return NULL; + return list; } /* check whether the interface IP has been added already it is possible to have multiple interfaces with the same address */ - for (; list; list = list->next) - if (sockaddr_isequal(&list->addr, addr)) + for (iface = list; iface; iface = iface->next) + if (sockaddr_isequal(&iface->addr, addr)) break; - if (list) - return NULL; + if (iface) + return list; /* If OK, add it to the head of the list */ iface = safe_malloc(sizeof(struct irec)); iface->addr = *addr; - + iface->next = list; return iface; } -struct irec *enumerate_interfaces(struct iname *names, - struct iname *addrs, +struct irec *enumerate_interfaces(struct iname **names, + struct iname **addrs, struct iname *except, int port) { - struct irec *iface = NULL, *new; + struct irec *iface = NULL; char *buf, *ptr; struct ifreq *ifr = NULL; struct ifconf ifc; @@ -137,22 +137,19 @@ struct irec *enumerate_interfaces(struct iname *names, die("ioctl error getting interface flags: %m", NULL); /* If we are restricting the set of interfaces to use, make - sure that loopback interfaces are in that set. */ - if (names && (ifr->ifr_flags & IFF_LOOPBACK)) + sure that loopback interfaces are in that set. Note that + this is done as addresses rather than interface names so + as not to confuse the no-IPRECVIF workaround on the DHCP code */ + if (*names && (ifr->ifr_flags & IFF_LOOPBACK)) { struct iname *lo = safe_malloc(sizeof(struct iname)); - lo->name = safe_string_alloc(ifr->ifr_name); - lo->next = names->next; - names->next = lo; - } - - if ((new = add_iface(iface, ifr->ifr_name, - &addr, names, addrs, except))) - { - new->next = iface; - iface = new; + lo->addr = addr; + lo->next = *addrs; + *addrs = lo; } + iface = add_iface(iface, ifr->ifr_name, &addr, *names, *addrs, except); + #if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6) /* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */ /* This code snarfed from net-tools 1.60 and certainly linux specific, though @@ -194,11 +191,17 @@ struct irec *enumerate_interfaces(struct iname *names, fclose(f); } - if (found && (new = add_iface(iface, ifr->ifr_name, - &addr6, names, addrs, except))) + if (found) { - new->next = iface; - iface = new; + if (*names && (ifr->ifr_flags & IFF_LOOPBACK)) + { + struct iname *lo = safe_malloc(sizeof(struct iname)); + lo->addr = addr6; + lo->next = *addrs; + *addrs = lo; + } + + iface = add_iface(iface, ifr->ifr_name, &addr6, *names, *addrs, except); } } #endif /* LINUX */ diff --git a/src/option.c b/src/option.c index 2aab234..b1fef2f 100644 --- a/src/option.c +++ b/src/option.c @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley +/* dnsmasq is Copyright (c) 2000 - 2004 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ struct myoption { int val; }; -#define OPTSTRING "ZDNLERzowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:" +#define OPTSTRING "ZDNLERzowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:" static struct myoption opts[] = { {"version", 0, 0, 'v'}, @@ -70,6 +70,7 @@ static struct myoption opts[] = { {"bind-interfaces", 0, 0, 'z'}, {"read-ethers", 0, 0, 'Z' }, {"alias", 1, 0, 'V' }, + {"dhcp-vendorclass", 1, 0, 'U'}, {0, 0, 0, 0} }; @@ -139,6 +140,7 @@ static char *usage = "-t, --mx-target=host_name Specify the host in an MX reply.\n" "-T, --local-ttl=time Specify time-to-live in seconds for replies from /etc/hosts.\n" "-u, --user=username Change to this user after startup. (defaults to " CHUSER ").\n" +"-U, --dhcp-vendorclass=<id>,<class> Map DHCP vendor class to option set.\n" "-v, --version Display dnsmasq version.\n" "-V, --alias=addr,addr,mask Translate IPv4 addresses from upstream servers.\n" "-w, --help Display this message.\n" @@ -155,7 +157,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso struct iname **if_names, struct iname **if_addrs, struct iname **if_except, struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize, int *port, int *query_port, unsigned long *local_ttl, char **addn_hosts, struct dhcp_context **dhcp, - struct dhcp_config **dhcp_conf, struct dhcp_opt **dhcp_opts, char **dhcp_file, + struct dhcp_config **dhcp_conf, struct dhcp_opt **dhcp_opts, struct dhcp_vendor **dhcp_vendors, char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server, int *dhcp_max, unsigned int *min_leasetime, struct doctor **doctors) { @@ -726,7 +728,6 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso } } - new->last = new->start; if (new->lease_time < *min_leasetime) *min_leasetime = new->lease_time; break; @@ -760,40 +761,45 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso (arg[1] == 'd' || arg[1] == 'D') && arg[2] == ':') { - int len; - arg += 3; /* dump id: */ - if (strchr(arg, ':')) + if (arg[3] == '*') + new->flags |= CONFIG_NOCLID; + else { - /* decode hex in place */ - char *p = arg, *q = arg, *r; - while (*p) + int len; + arg += 3; /* dump id: */ + if (strchr(arg, ':')) { - for (r = p; *r && *r != ':'; r++); - if (*r) + /* decode hex in place */ + char *p = arg, *q = arg, *r; + while (*p) { - if (r != p) + for (r = p; *r && *r != ':'; r++); + if (*r) { - *r = 0; - *(q++) = strtol(p, NULL, 16); + if (r != p) + { + *r = 0; + *(q++) = strtol(p, NULL, 16); + } + p = r+1; + } + else + { + if (*p) + *(q++) = strtol(p, NULL, 16); + break; } - p = r+1; - } - else - { - if (*p) - *(q++) = strtol(p, NULL, 16); - break; } + len = q - arg; } - len = q - arg; + else + len = strlen(arg); + + new->flags |= CONFIG_CLID; + new->clid_len = len; + new->clid = safe_malloc(len); + memcpy(new->clid, arg, len); } - else - len = strlen(arg); - - new->flags |= CONFIG_CLID; - new->clid_len = len; - new->clid = safe_malloc(len); - memcpy(new->clid, arg, len); } else if ((arg[0] == 'n' || arg[0] == 'N') && (arg[1] == 'e' || arg[1] == 'E') && @@ -1057,6 +1063,26 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso break; } + case 'U': + { + char *comma; + + if (!(comma = strchr(optarg, ','))) + option = '?'; + else + { + struct dhcp_vendor *new = safe_malloc(sizeof(struct dhcp_vendor)); + *comma = 0; + new->net = safe_string_alloc(optarg); + new->len = strlen(comma+1); + new->data = safe_malloc(new->len); + memcpy(new->data, comma+1, new->len); + new->next = *dhcp_vendors; + *dhcp_vendors = new; + } + break; + } + case 'V': { char *a[3] = { NULL, NULL, NULL }; diff --git a/src/rfc1035.c b/src/rfc1035.c index 4b9d086..4f3df70 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -443,7 +443,7 @@ void extract_neg_addrs(HEADER *header, unsigned int qlen, char *name, time_t now static void dns_doctor(struct doctor *doctor, struct in_addr *addr) { for (; doctor; doctor = doctor->next) - if ((doctor->in.s_addr & doctor->mask.s_addr) == (addr->s_addr & doctor->mask.s_addr)) + if (is_same_net(doctor->in, *addr, doctor->mask)) { addr->s_addr &= ~doctor->mask.s_addr; addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); diff --git a/src/rfc2131.c b/src/rfc2131.c index 1b7364d..dcba4b3 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -35,6 +35,7 @@ #define OPTION_MAXMESSAGE 57 #define OPTION_T1 58 #define OPTION_T2 59 +#define OPTION_VENDOR_ID 60 #define OPTION_CLIENT_ID 61 #define OPTION_END 255 @@ -77,6 +78,7 @@ int dhcp_reply(struct dhcp_context *context, struct udp_dhcp_packet *rawpacket, unsigned int sz, time_t now, char *namebuff, struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs, + struct dhcp_vendor *vendors, char *domain_suffix, char *dhcp_file, char *dhcp_sname, struct in_addr dhcp_next_server, struct in_addr router) { @@ -93,7 +95,9 @@ int dhcp_reply(struct dhcp_context *context, unsigned int renewal_time, expires_time, def_time; struct dhcp_config *config; char *netid; - + struct in_addr addr; + unsigned short fuzz = 0; + if (mess->op != BOOTREQUEST || mess->hlen != ETHER_ADDR_LEN || mess->cookie != htonl(DHCP_COOKIE)) @@ -137,18 +141,7 @@ int dhcp_reply(struct dhcp_context *context, clid = mess->chaddr; clid_len = 0; } - - /* do we have a lease in store? */ - lease = lease_find_by_client(clid, clid_len); - - if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS))) - { - int len = option_len(opt); - req_options = namebuff; - memcpy(req_options, option_ptr(opt), len); - req_options[len] = OPTION_END; - } - + if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) && have_config(config, CONFIG_NAME)) hostname = config->hostname; @@ -177,13 +170,34 @@ int dhcp_reply(struct dhcp_context *context, else *dot = 0; /* truncate */ } + /* search again now we have a hostname */ + config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname); } } - - /* search again now we have a hostname */ - config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname); + def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; - netid = have_config(config, CONFIG_NETID) ? config->netid : context->netid; + + netid = context->netid; + if (have_config(config, CONFIG_NETID)) + netid = config->netid; + else if ((opt = option_find(mess, sz, OPTION_VENDOR_ID))) + { + struct dhcp_vendor *vendor; + for (vendor = vendors; vendor; vendor = vendor->next) + if (vendor->len == option_len(opt) && + memcmp(vendor->data, option_ptr(opt), vendor->len) == 0) + netid = vendor->net; + } + + /* Can have setting to ignore the client ID for a particular MAC address or hostname */ + if (have_config(config, CONFIG_NOCLID)) + { + clid = mess->chaddr; + clid_len = 0; + } + + /* do we have a lease in store? */ + lease = lease_find_by_client(clid, clid_len); if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) { @@ -203,11 +217,19 @@ int dhcp_reply(struct dhcp_context *context, else expires_time = def_time; } - + + if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS))) + { + int len = option_len(opt); + req_options = namebuff; + memcpy(req_options, option_ptr(opt), len); + req_options[len] = OPTION_END; + } + if (!(opt = option_find(mess, sz, OPTION_MESSAGE_TYPE))) return 0; - switch (opt[2]) + switch (option_uint(opt, 1)) { case DHCPDECLINE: if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) || @@ -261,39 +283,48 @@ int dhcp_reply(struct dhcp_context *context, case DHCPDISCOVER: if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP))) - mess->yiaddr = option_addr(opt); + addr = option_addr(opt); if (have_config(config, CONFIG_DISABLE)) message = "ignored"; else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr)) mess->yiaddr = config->addr; - else if (lease && - ((lease->addr.s_addr & context->netmask.s_addr) == - (context->start.s_addr & context->netmask.s_addr))) + else if (lease && is_same_net(lease->addr, context->start, context->netmask)) mess->yiaddr = lease->addr; - else if ((!opt || !address_available(context, mess->yiaddr)) && - !address_allocate(context, dhcp_configs, &mess->yiaddr)) + else if (opt && address_available(context, addr)) + mess->yiaddr = addr; + else if (!address_allocate(context, dhcp_configs, &mess->yiaddr, mess->chaddr)) message = "no address available"; - - log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, iface_name, message); + + log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message); + if (message) return 0; - + + /* ensure that we send the reply by steam even if a buggy client sets this. */ + mess->ciaddr.s_addr = 0; bootp_option_put(mess, dhcp_file, dhcp_sname); - mess->siaddr = dhcp_next_server; + mess->siaddr = dhcp_next_server.s_addr ? dhcp_next_server : iface_addr; p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER); p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time); + /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */ + if (expires_time != 0xffffffff) + { + p = option_put(p, end, OPTION_T1, 4, (expires_time/2)); + p = option_put(p, end, OPTION_T2, 4, ((expires_time * 7)/8)); + } p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix, NULL, router, iface_addr, iface_mtu, netid); p = option_put(p, end, OPTION_END, 0, 0); log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL); return p - (unsigned char *)mess; - case DHCPREQUEST: - if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP))) + if (have_config(config, CONFIG_DISABLE)) + message = "disabled"; + else if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP))) { /* SELECTING or INIT_REBOOT */ mess->yiaddr = option_addr(opt); @@ -333,15 +364,17 @@ int dhcp_reply(struct dhcp_context *context, mess->yiaddr = mess->ciaddr; if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr) message = "lease not found"; + + /* desynchronise renewals */ + fuzz = rand16(); + while (fuzz > (renewal_time/16)) + fuzz = fuzz/2; } /* If a machine moves networks whilst it has a lease, we catch that here. */ - if ((mess->yiaddr.s_addr & context->netmask.s_addr) != (context->start.s_addr & context->netmask.s_addr)) + if (!message && !is_same_net(mess->yiaddr, context->start, context->netmask)) message = "wrong network"; - if (have_config(config, CONFIG_DISABLE)) - message = "disabled"; - log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL); if (message) @@ -364,15 +397,12 @@ int dhcp_reply(struct dhcp_context *context, lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time); bootp_option_put(mess, dhcp_file, dhcp_sname); - mess->siaddr = dhcp_next_server; + mess->siaddr = dhcp_next_server.s_addr ? dhcp_next_server : iface_addr; p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time); if (renewal_time != 0xffffffff) { - unsigned short fuzz = rand16(); - while (fuzz > (renewal_time/16)) - fuzz = fuzz/2; p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz); p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz); } @@ -383,12 +413,12 @@ int dhcp_reply(struct dhcp_context *context, case DHCPINFORM: if (have_config(config, CONFIG_DISABLE)) - { - log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, "ignored"); - return 0; - } + message = "ignored"; - log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, NULL); + log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, message); + + if (message || mess->ciaddr.s_addr == 0) + return 0; p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); @@ -546,6 +576,10 @@ static int in_list(unsigned char *list, int opt) { int i; + /* If no requested options, send everything, not nothing. */ + if (!list) + return 1; + for (i = 0; list[i] != OPTION_END; i++) if (opt == list[i]) return 1; @@ -555,11 +589,15 @@ static int in_list(unsigned char *list, int opt) static struct dhcp_opt *option_find2(char *netid, struct dhcp_opt *opts, int opt) { - for (; opts; opts = opts->next) - if (opts->opt == opt && - (!opts->netid || (netid && strcmp(opts->netid, netid) == 0))) - return opts; - return NULL; + struct dhcp_opt *tmp; + + for (tmp = opts; tmp; tmp = tmp->next) + if (tmp->opt == opt && + ((!netid && !tmp->netid) || + (netid && tmp->netid && strcmp(tmp->netid, netid) == 0))) + return tmp; + + return netid ? option_find2(NULL, opts, opt) : NULL; } static unsigned char *do_req_options(struct dhcp_context *context, @@ -571,11 +609,8 @@ static unsigned char *do_req_options(struct dhcp_context *context, struct in_addr iface_addr, int iface_mtu, char *netid) { - int i; - - if (!req_options) - return p; - + struct dhcp_opt *opt; + if (in_list(req_options, OPTION_MAXMESSAGE)) p = option_put(p, end, OPTION_MAXMESSAGE, 2, DNSMASQ_PACKETSZ > iface_mtu ? @@ -607,14 +642,13 @@ static unsigned char *do_req_options(struct dhcp_context *context, if (hostname && in_list(req_options, OPTION_HOSTNAME)) p = option_put_string(p, end, OPTION_HOSTNAME, hostname); - for (i = 0; req_options[i] != OPTION_END; i++) + for (opt=config_opts; opt; opt = opt->next) { - struct dhcp_opt *opt; - - if (req_options[i] == OPTION_HOSTNAME || - req_options[i] == OPTION_MAXMESSAGE || - !(opt = option_find2(netid, config_opts, req_options[i])) || - (p + opt->len + 3 >= end)) + if (opt->opt == OPTION_HOSTNAME || + opt->opt == OPTION_MAXMESSAGE || + !in_list(req_options, opt->opt) || + opt != option_find2(netid, config_opts, opt->opt) || + p + opt->len + 3 >= end) continue; /* For the options we have default values on @@ -631,7 +665,7 @@ static unsigned char *do_req_options(struct dhcp_context *context, *(p++) = opt->len; if (opt->len == 0) continue; - + if (opt->is_addr) { int j; @@ -228,3 +228,8 @@ time_t dnsmasq_time(int fd) return time(NULL); #endif } + +int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask) +{ + return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr); +} |