From 4cb1b32009a7707a1c6f8761094dda9d33284290 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 6 Feb 2012 14:30:41 +0000 Subject: [PATCH 1/7] Clean compile, basic DHCPv6 functionality is there. TODO hostname handling. update DHCP6 configs from dns parse domain=, pretty-print counted string options. DECLINE messages lease-script fro DHCPv6 --- Makefile | 6 +- bld/Android.mk | 3 +- src/NOTES | 15 - src/bpf.c | 20 +- src/cache.c | 65 ++- src/config.h | 2 +- src/dhcp-common.c | 213 +++++++ src/dhcp.c | 97 +-- src/dhcp6.c | 200 ++++++- src/dhcp6_protocol.h | 5 +- src/dnsmasq.c | 4 +- src/dnsmasq.h | 65 ++- src/lease.c | 193 ++++-- src/netlink.c | 4 +- src/option.c | 380 ++++++++++-- src/rfc2131.c | 140 +---- src/rfc3315.c | 1331 ++++++++++++++++++++++++++++++++---------- 17 files changed, 2025 insertions(+), 718 deletions(-) delete mode 100644 src/NOTES create mode 100644 src/dhcp-common.c diff --git a/Makefile b/Makefile index 9105dce..be9840b 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ VERSION= -DVERSION='\"`../bld/get-version`\"' OBJS = cache.o rfc1035.o util.o option.o forward.o network.o \ dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \ - helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o + helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o dhcp-common.o all : @cd $(SRC) && $(MAKE) \ @@ -88,10 +88,10 @@ merge : # rules below are targets in recusive makes with cwd=$(SRC) -.c.o: +.c.o: $(CC) $(CFLAGS) $(COPTS) $(I18N) $(BUILD_CFLAGS) $(RPM_OPT_FLAGS) -c $< -dnsmasq : $(OBJS) +dnsmasq : $(OBJS) $(CC) $(LDFLAGS) -o $@ $(OBJS) $(BUILD_LIBS) $(LIBS) dnsmasq.pot : $(OBJS:.o=.c) dnsmasq.h config.h diff --git a/bld/Android.mk b/bld/Android.mk index f02657e..413b325 100644 --- a/bld/Android.mk +++ b/bld/Android.mk @@ -6,7 +6,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \ forward.c helper.c lease.c log.c \ netlink.c network.c option.c rfc1035.c \ - rfc2131.c tftp.c util.c conntrack.c + rfc2131.c tftp.c util.c conntrack.c \ + dhcp6.c rfc3315.c dhcp-common.c LOCAL_MODULE := dnsmasq diff --git a/src/NOTES b/src/NOTES deleted file mode 100644 index 63d09bb..0000000 --- a/src/NOTES +++ /dev/null @@ -1,15 +0,0 @@ -Worry about IPv6 leases and DUID in script-storage. - -dhcpv6-range -dhcpv6-option -dhcpv6-option-force -dhcpv6-script ? -dhcpv6-optsfile -dhcpv6-hostsfile - -dhcp-host = -[][,id:|*][,net:][,][\[ipv6addr\]][,][,][,ignore] - -IPv6 address like [2001:db8:do::2] - - diff --git a/src/bpf.c b/src/bpf.c index 96462b2..4251313 100644 --- a/src/bpf.c +++ b/src/bpf.c @@ -153,7 +153,25 @@ int iface_enumerate(int family, void *parm, int (*callback)()) ifr = (struct ifreq *)ifreq.iov_base; memcpy(ifr, ptr, len); - + +#ifdef HAVE_DHCP6 + if (family == AF_LOCAL) + { + unsigned int flags; + if (ioctl(fd, SIOCGIFFLAGS, ifr) != -1) + { + flags = ifr.ifr_flags; + ifr->ifr_addr.sa_family = AF_LINK; + if (ioctl(fd, SIOCGIFADDR, ifr) != -1 && + !((*callback)((unsigned int) htons(ETHERTYPE_IP), + (unsigned int)link->ifi_flags, + LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN, parm))) + goto err; + } + continue; + } +#endif + if (ifr->ifr_addr.sa_family == family) { if (family == AF_INET) diff --git a/src/cache.c b/src/cache.c index 77a4348..1226165 100644 --- a/src/cache.c +++ b/src/cache.c @@ -782,7 +782,7 @@ static int read_hostsfile(char *filename, int index, int cache_size, struct crec { flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6; addrlen = IN6ADDRSZ; - domain_suffix = daemon->domain_suffix; + domain_suffix = get_domain6(&addr.addr.addr6); } #else if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1) @@ -920,6 +920,24 @@ char *get_domain(struct in_addr addr) return daemon->domain_suffix; } + +#ifdef HAVE_IPV6 +char *get_domain6(struct in6_addr *addr) +{ + struct cond_domain6 *c; + + u64 addrpart = addr6part(addr); + + for (c = daemon->cond_domain6; c; c = c->next) + if (is_same_net6(addr, &c->start, 64) && + addrpart >= addr6part(&c->start) && + addrpart <= addr6part(&c->end)) + return c->domain; + + return daemon->domain_suffix; +} +#endif + #ifdef HAVE_DHCP struct in_addr a_record_from_hosts(char *name, time_t now) { @@ -953,15 +971,24 @@ void cache_unhash_dhcp(void) up = &cache->hash_next; } -void cache_add_dhcp_entry(char *host_name, - struct in_addr *host_address, time_t ttd) +void cache_add_dhcp_entry(char *host_name, int prot, + struct all_addr *host_address, time_t ttd) { struct crec *crec = NULL, *aliasc; - unsigned short flags = F_NAMEP | F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE; + unsigned short flags = F_IPV4; int in_hosts = 0; struct cname *a; + size_t addrlen = sizeof(struct in_addr); + +#ifdef HAVE_IPV6 + if (prot == AF_INET6) + { + flags = F_IPV6; + addrlen = sizeof(struct in6_addr); + } +#endif - while ((crec = cache_find_by_name(crec, host_name, 0, F_IPV4 | F_CNAME))) + while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME))) { /* check all addresses associated with name */ if (crec->flags & F_HOSTS) @@ -969,23 +996,25 @@ void cache_add_dhcp_entry(char *host_name, /* if in hosts, don't need DHCP record */ in_hosts = 1; + inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN); if (crec->flags & F_CNAME) + my_syslog(MS_DHCP | LOG_WARNING, _("%s is a CNAME, not giving it to the DHCP lease of %s"), - host_name, inet_ntoa(*host_address)); - else if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr) + host_name, daemon->addrbuff); + else if (memcmp(&crec->addr.addr, host_address, addrlen) != 0) { - strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4)); + inet_ntop(prot, &crec->addr.addr, daemon->namebuff, MAXDNAME); my_syslog(MS_DHCP | LOG_WARNING, _("not giving name %s to the DHCP lease of %s because " "the name exists in %s with address %s"), - host_name, inet_ntoa(*host_address), + host_name, daemon->addrbuff, record_source(crec->uid), daemon->namebuff); } } else if (!(crec->flags & F_DHCP)) { - cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD)); + cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD)); /* scan_free deletes all addresses associated with name */ break; } @@ -994,14 +1023,16 @@ void cache_add_dhcp_entry(char *host_name, if (in_hosts) return; - if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4))) + if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags))) { if (crec->flags & F_NEG) - cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE); - else - /* avoid multiple reverse mappings */ - flags &= ~F_REVERSE; + { + flags |= F_REVERSE; + cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags); + } } + else + flags |= F_REVERSE; if ((crec = dhcp_spare)) dhcp_spare = dhcp_spare->next; @@ -1010,12 +1041,12 @@ void cache_add_dhcp_entry(char *host_name, if (crec) /* malloc may fail */ { - crec->flags = flags; + crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD; if (ttd == 0) crec->flags |= F_IMMORTAL; else crec->ttd = ttd; - crec->addr.addr.addr.addr4 = *host_address; + crec->addr.addr = *host_address; crec->name.namep = host_name; crec->uid = uid++; cache_hash(crec); diff --git a/src/config.h b/src/config.h index 3aecf0b..63e7488 100644 --- a/src/config.h +++ b/src/config.h @@ -116,7 +116,7 @@ RESOLVFILE has no library dependencies other than libc */ #define HAVE_DHCP -/* #define HAVE_DHCP6 */ +#define HAVE_DHCP6 #define HAVE_TFTP #define HAVE_SCRIPT /* #define HAVE_LUASCRIPT */ diff --git a/src/dhcp-common.c b/src/dhcp-common.c new file mode 100644 index 0000000..3c5349d --- /dev/null +++ b/src/dhcp-common.c @@ -0,0 +1,213 @@ +/* dnsmasq is Copyright (c) 2000-2012 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 + the Free Software Foundation; version 2 dated June, 1991, or + (at your option) version 3 dated 29 June, 2007. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "dnsmasq.h" + +#ifdef HAVE_DHCP + +void dhcp_common_init(void) +{ + /* These each hold a DHCP option max size 255 + and get a terminating zero added */ + daemon->dhcp_buff = safe_malloc(256); + daemon->dhcp_buff2 = safe_malloc(256); + daemon->dhcp_buff3 = safe_malloc(256); + + /* dhcp_packet is used by v4 and v6, outpacket only by v6 + sizeof(struct dhcp_packet) is as good an initial size as any, + even for v6 */ + expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet)); +#ifdef HAVE_DHCP6 + if (daemon->dhcp6) + expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet)); +#endif +} + +ssize_t recv_dhcp_packet(int fd, struct msghdr *msg) +{ + ssize_t sz; + + while (1) + { + msg->msg_flags = 0; + while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR); + + if (sz == -1) + return -1; + + if (!(msg->msg_flags & MSG_TRUNC)) + break; + + /* Very new Linux kernels return the actual size needed, + older ones always return truncated size */ + if ((size_t)sz == daemon->dhcp_packet.iov_len) + { + if (!expand_buf(&daemon->dhcp_packet, sz + 100)) + return -1; + } + else + { + expand_buf(&daemon->dhcp_packet, sz); + break; + } + } + + while ((sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR); + + return (msg->msg_flags & MSG_TRUNC) ? -1 : sz; +} + +struct dhcp_netid *run_tag_if(struct dhcp_netid *tags) +{ + struct tag_if *exprs; + struct dhcp_netid_list *list; + + for (exprs = daemon->tag_if; exprs; exprs = exprs->next) + if (match_netid(exprs->tag, tags, 1)) + for (list = exprs->set; list; list = list->next) + { + list->list->next = tags; + tags = list->list; + } + + return tags; +} + + +struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts) +{ + struct dhcp_netid *tagif = run_tag_if(tags); + struct dhcp_opt *opt; + + /* flag options which are valid with the current tag set (sans context tags) */ + for (opt = opts; opt; opt = opt->next) + { + opt->flags &= ~DHOPT_TAGOK; + if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) && + match_netid(opt->netid, tagif, 0)) + opt->flags |= DHOPT_TAGOK; + } + + /* now flag options which are valid, including the context tags, + otherwise valid options are inhibited if we found a higher priotity one above */ + if (context_tags) + { + struct dhcp_netid *last_tag; + + for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next); + last_tag->next = tags; + tagif = run_tag_if(context_tags); + + for (opt = opts; opt; opt = opt->next) + if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && + match_netid(opt->netid, tagif, 0)) + { + struct dhcp_opt *tmp; + for (tmp = opts; tmp; tmp = tmp->next) + if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK)) + break; + if (!tmp) + opt->flags |= DHOPT_TAGOK; + } + } + + /* now flag untagged options which are not overridden by tagged ones */ + for (opt = opts; opt; opt = opt->next) + if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid) + { + struct dhcp_opt *tmp; + for (tmp = opts; tmp; tmp = tmp->next) + if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK)) + break; + if (!tmp) + opt->flags |= DHOPT_TAGOK; + else if (!tmp->netid) + my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt); + } + + return tagif; +} + +/* Is every member of check matched by a member of pool? + If tagnotneeded, untagged is OK */ +int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded) +{ + struct dhcp_netid *tmp1; + + if (!check && !tagnotneeded) + return 0; + + for (; check; check = check->next) + { + /* '#' for not is for backwards compat. */ + if (check->net[0] != '!' && check->net[0] != '#') + { + for (tmp1 = pool; tmp1; tmp1 = tmp1->next) + if (strcmp(check->net, tmp1->net) == 0) + break; + if (!tmp1) + return 0; + } + else + for (tmp1 = pool; tmp1; tmp1 = tmp1->next) + if (strcmp((check->net)+1, tmp1->net) == 0) + return 0; + } + return 1; +} + +/* return domain or NULL if none. */ +char *strip_hostname(char *hostname) +{ + char *dot = strchr(hostname, '.'); + + if (!dot) + return NULL; + + *dot = 0; /* truncate */ + if (strlen(dot+1) != 0) + return dot+1; + + return NULL; +} + +void log_tags(struct dhcp_netid *netid, u32 xid) +{ + if (netid && option_bool(OPT_LOG_OPTS)) + { + char *s = daemon->namebuff; + for (*s = 0; netid; netid = netid->next) + { + /* kill dupes. */ + struct dhcp_netid *n; + + for (n = netid->next; n; n = n->next) + if (strcmp(netid->net, n->net) == 0) + break; + + if (!n) + { + strncat (s, netid->net, (MAXDNAME-1) - strlen(s)); + if (netid->next) + strncat (s, ", ", (MAXDNAME-1) - strlen(s)); + } + } + my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s); + } +} + + +#endif diff --git a/src/dhcp.c b/src/dhcp.c index d6cf9ce..9650831 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -119,42 +119,6 @@ void dhcp_init(void) #endif check_dhcp_hosts(1); - - expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet)); -} - -ssize_t recv_dhcp_packet(int fd, struct msghdr *msg) -{ - ssize_t sz; - - while (1) - { - msg->msg_flags = 0; - while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR); - - if (sz == -1) - return -1; - - if (!(msg->msg_flags & MSG_TRUNC)) - break; - - /* Very new Linux kernels return the actual size needed, - older ones always return truncated size */ - if ((size_t)sz == daemon->dhcp_packet.iov_len) - { - if (!expand_buf(&daemon->dhcp_packet, sz + 100)) - return -1; - } - else - { - expand_buf(&daemon->dhcp_packet, sz); - break; - } - } - - while ((sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR); - - return (msg->msg_flags & MSG_TRUNC) ? -1 : sz; } void dhcp_packet(time_t now, int pxe_fd) @@ -610,50 +574,6 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i return NULL; } -/* Is every member of check matched by a member of pool? - If tagnotneeded, untagged is OK */ -int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded) -{ - struct dhcp_netid *tmp1; - - if (!check && !tagnotneeded) - return 0; - - for (; check; check = check->next) - { - /* '#' for not is for backwards compat. */ - if (check->net[0] != '!' && check->net[0] != '#') - { - for (tmp1 = pool; tmp1; tmp1 = tmp1->next) - if (strcmp(check->net, tmp1->net) == 0) - break; - if (!tmp1) - return 0; - } - else - for (tmp1 = pool; tmp1; tmp1 = tmp1->next) - if (strcmp((check->net)+1, tmp1->net) == 0) - return 0; - } - return 1; -} - -struct dhcp_netid *run_tag_if(struct dhcp_netid *tags) -{ - struct tag_if *exprs; - struct dhcp_netid_list *list; - - for (exprs = daemon->tag_if; exprs; exprs = exprs->next) - if (match_netid(exprs->tag, tags, 1)) - for (list = exprs->set; list; list = list->next) - { - list->list->next = tags; - tags = list->list; - } - - return tags; -} - int address_allocate(struct dhcp_context *context, struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct dhcp_netid *netids, time_t now) @@ -849,7 +769,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs, is_addr_in_context(context, config)) return config; - /* use match with fewest wildcast octets */ + /* use match with fewest wildcard octets */ for (candidate = NULL, count = 0, config = configs; config; config = config->next) if (is_addr_in_context(context, config)) for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next) @@ -1145,20 +1065,5 @@ char *host_from_dns(struct in_addr addr) return NULL; } -/* return domain or NULL if none. */ -char *strip_hostname(char *hostname) -{ - char *dot = strchr(hostname, '.'); - - if (!dot) - return NULL; - - *dot = 0; /* truncate */ - if (strlen(dot+1) != 0) - return dot+1; - - return NULL; -} - #endif diff --git a/src/dhcp6.c b/src/dhcp6.c index 4b8a29d..66a176c 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -23,27 +23,40 @@ struct iface_param { int ind; }; +struct listen_param { + int fd_or_iface; + struct listen_param *next; +}; + static int join_multicast(struct in6_addr *local, int prefix, int scope, int if_index, int dad, void *vparam); static int complete_context6(struct in6_addr *local, int prefix, int scope, int if_index, int dad, void *vparam); +static int make_duid1(unsigned int type, unsigned int flags, char *mac, + size_t maclen, void *parm); + void dhcp6_init(void) { int fd; struct sockaddr_in6 saddr; + struct listen_param *listenp, listen; +#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6) int class = IPTOS_CLASS_CS6; +#endif if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 || +#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6) setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 || +#endif !fix_fd(fd) || !set_ipv6pktinfo(fd)) die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET); memset(&saddr, 0, sizeof(saddr)); #ifdef HAVE_SOCKADDR_SA_LEN - saddr.sin6_len = sizeof(addr.in6); + saddr.sin6_len = sizeof(struct sockaddr_in6); #endif saddr.sin6_family = AF_INET6; saddr.sin6_addr = in6addr_any; @@ -53,16 +66,18 @@ void dhcp6_init(void) die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET); /* join multicast groups on each interface we're interested in */ - if (!iface_enumerate(AF_INET6, &fd, join_multicast)) + listen.fd_or_iface = fd; + listen.next = NULL; + if (!iface_enumerate(AF_INET6, &listen, join_multicast)) die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET); + for (listenp = listen.next; listenp; ) + { + struct listen_param *tmp = listenp->next; + free(listenp); + listenp = tmp; + } daemon->dhcp6fd = fd; - - /* If we've already inited DHCPv4, this becomes a no-op, - othewise sizeof(struct dhcp_packet) is as good an initial - size as any. */ - expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet)); - expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet)); } static int join_multicast(struct in6_addr *local, int prefix, @@ -70,20 +85,25 @@ static int join_multicast(struct in6_addr *local, int prefix, { char ifrn_name[IFNAMSIZ]; struct ipv6_mreq mreq; - struct in6_addr maddr; - int fd = *((int *)vparam); + struct listen_param *listenp, *param = vparam; + int fd = param->fd_or_iface; struct dhcp_context *context; struct iname *tmp; (void)prefix; + (void)scope; + (void)dad; + + /* record which interfaces we join on, so + that we do it at most one per interface, even when they + have multiple addresses */ + for (listenp = param->next; listenp; listenp = listenp->next) + if (if_index == listenp->fd_or_iface) + return 1; - /* scope == link */ - if (scope != 253) - return 1; - if (!indextoname(fd, if_index, ifrn_name)) return 0; - + /* Are we doing DHCP on this interface? */ if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name)) return 1; @@ -111,8 +131,12 @@ static int join_multicast(struct in6_addr *local, int prefix, if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) return 0; + listenp = whine_malloc(sizeof(struct listen_param)); + listenp->fd_or_iface = if_index; + listenp->next = param->next; + param->next = listenp; + return 1; - } @@ -176,7 +200,7 @@ void dhcp6_packet(time_t now) if (!context) return; - + /* unlinked contexts are marked by context->current == context */ for (context = daemon->dhcp6; context; context = context->next) context->current = context; @@ -190,14 +214,15 @@ void dhcp6_packet(time_t now) lease_prune(NULL, now); /* lose any expired leases */ msg.msg_iov = &daemon->dhcp_packet; - sz = dhcp6_reply(parm.current, sz, now); + sz = dhcp6_reply(parm.current, ifr.ifr_name, sz, IN6_IS_ADDR_MULTICAST(&from), now); /* ifr.ifr_name, if_index, (size_t)sz, now, unicast_dest, &is_inform, pxe_fd, iface_addr); */ lease_update_file(now); lease_update_dns(); if (sz != 0) - send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, sz, &from, &dest, if_index); + while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, sz, 0, (struct sockaddr *)&from, sizeof(from)) && + retry_send()); } static int complete_context6(struct in6_addr *local, int prefix, @@ -205,11 +230,16 @@ static int complete_context6(struct in6_addr *local, int prefix, { struct dhcp_context *context; struct iface_param *param = vparam; + (void)scope; /* warning */ + (void)dad; for (context = daemon->dhcp6; context; context = context->next) { if (prefix == context->prefix && + !IN6_IS_ADDR_LOOPBACK(local) && + !IN6_IS_ADDR_LINKLOCAL(local) && + !IN6_IS_ADDR_MULTICAST(local) && is_same_net6(local, &context->start6, prefix) && is_same_net6(local, &context->end6, prefix)) { @@ -218,6 +248,7 @@ static int complete_context6(struct in6_addr *local, int prefix, { context->current = param->current; param->current = context; + context->local6 = *local; } } } @@ -238,7 +269,7 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct } int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, - struct dhcp_netid *netids, struct in6_addr *ans) + int serial, struct dhcp_netid *netids, struct in6_addr *ans) { /* Find a free address: exclude anything in use and anything allocated to a particular hwaddr/clientid/hostname in our configuration. @@ -266,7 +297,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl continue; else { - start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6))); + start = addr6part(&c->start6) + ((j + c->addr_epoch + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6))); /* iterate until we find a free address. */ addr = start; @@ -358,6 +389,133 @@ struct dhcp_context *narrow_context6(struct dhcp_context *context, return tmp; } +static int is_addr_in_context6(struct dhcp_context *context, struct dhcp_config *config) +{ + if (!context) /* called via find_config() from lease_update_from_configs() */ + return 1; + if (!(config->flags & CONFIG_ADDR6)) + return 1; + for (; context; context = context->current) + if (is_same_net6(&config->addr6, &context->start6, context->prefix)) + return 1; + + return 0; +} + + +struct dhcp_config *find_config6(struct dhcp_config *configs, + struct dhcp_context *context, + unsigned char *duid, int duid_len, + char *hostname) +{ + int count, new; + struct dhcp_config *config; + struct hwaddr_config *conf_addr; + unsigned char *hwaddr = NULL; + int duid_type, hw_len = 0, hw_type = 0; + + if (duid) + { + for (config = configs; config; config = config->next) + if (config->flags & CONFIG_CLID) + { + if (config->clid_len == duid_len && + memcmp(config->clid, duid, duid_len) == 0 && + is_addr_in_context6(context, config)) + return config; + } + + /* DHCPv6 doesn't deal in MAC addresses per-se, but some DUIDs do include + MAC addresses, so we try and parse them out here. Not that there is only one + DUID per host and it's created using any one of the MACs, so this is no + good no good for multihomed hosts. */ + hwaddr = duid; + GETSHORT(duid_type, hwaddr); + if (duid_type == 1 || duid_type == 3) + { + GETSHORT(hw_type, hwaddr); + if (duid_type == 1) + hwaddr += 4; /* skip time */ + hw_len = duid_len - 8; + } + + if (hwaddr) + for (config = configs; config; config = config->next) + if (config_has_mac(config, hwaddr, hw_len, hw_type) && + is_addr_in_context6(context, config)) + return config; + } + + if (hostname && context) + for (config = configs; config; config = config->next) + if ((config->flags & CONFIG_NAME) && + hostname_isequal(config->hostname, hostname) && + is_addr_in_context6(context, config)) + return config; + + /* use match with fewest wildcard octets */ + if (hwaddr) + { + struct dhcp_config *candidate; + + for (candidate = NULL, count = 0, config = configs; config; config = config->next) + if (is_addr_in_context6(context, config)) + for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next) + if (conf_addr->wildcard_mask != 0 && + conf_addr->hwaddr_len == hw_len && + (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) && + (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count) + { + count = new; + candidate = config; + } + + return candidate; + } + + return NULL; +} + +void make_duid(time_t now) +{ + /* rebase epoch to 1/1/2000 */ + time_t newnow = now - 946684800; + iface_enumerate(AF_LOCAL, &newnow, make_duid1); + + if (!daemon->duid) + die("Cannot create DHCPv6 server DUID", NULL, EC_MISC); +} + +static int make_duid1(unsigned int type, unsigned int flags, char *mac, + size_t maclen, void *parm) +{ + /* create DUID as specified in RFC3315. We use the MAC of the + first interface we find that isn't loopback or P-to-P */ + + unsigned char *p; + + if (flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) + return 1; + + daemon->duid = p = safe_malloc(maclen + 8); + daemon->duid_len = maclen + 8; + +#ifdef HAVE_BROKEN_RTC + PUTSHORT(3, p); /* DUID_LL */ +#else + PUTSHORT(1, p); /* DUID_LLT */ +#endif + + PUTSHORT(type, p); /* address type */ + +#ifndef HAVE_BROKEN_RTC + PUTLONG(*((time_t *)parm), p); /* time */ +#endif + + memcpy(p, mac, maclen); + + return 0; +} #endif diff --git a/src/dhcp6_protocol.h b/src/dhcp6_protocol.h index 590f7b5..7910920 100644 --- a/src/dhcp6_protocol.h +++ b/src/dhcp6_protocol.h @@ -53,7 +53,10 @@ #define OPTION6_INTERFACE_ID 18 #define OPTION6_RECONFIGURE_MSG 19 #define OPTION6_RECONF_ACCEPT 20 - +#define OPTION6_DNS_SERVER 23 +#define OPTION6_REMOTE_ID 37 +#define OPTION6_SUBSCRIBER_ID 38 +#define OPTION6_FQDN 39 #define DHCP6SUCCESS 0 #define DHCP6UNSPEC 1 diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 9f4adb6..b01c3fc 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -150,7 +150,9 @@ 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. */ + to the lease-script init process. We need to call common_init + before lease_init to allocate buffers it uses.*/ + dhcp_common_init(); lease_init(now); if (daemon->dhcp) dhcp_init(); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index bd18296..dfe8cfb 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -436,15 +436,20 @@ struct frec { #define ACTION_OLD 3 #define ACTION_ADD 4 +#define LEASE_NEW 1 /* newly created */ +#define LEASE_CHANGED 2 /* modified */ +#define LEASE_AUX_CHANGED 4 /* CLID or expiry changed */ +#define LEASE_AUTH_NAME 8 /* hostname came from config, not from client */ +#define LEASE_USED 16 /* used this DHCPv6 transaction */ +#define LEASE_NA 32 /* IPv6 no-temporary lease */ +#define LEASE_TA 64 /* IPv6 temporary lease */ + struct dhcp_lease { int clid_len; /* length of client identifier */ unsigned char *clid; /* clientid */ char *hostname, *fqdn; /* name from client-hostname option or config */ char *old_hostname; /* hostname before it moved to another lease */ - char auth_name; /* hostname came from config, not from client */ - char new; /* newly created */ - char changed; /* modified */ - char aux_changed; /* CLID or expiry changed */ + int flags; time_t expires; /* lease expiry */ #ifdef HAVE_BROKEN_RTC unsigned int length; @@ -455,9 +460,6 @@ struct dhcp_lease { unsigned char *extradata; unsigned int extradata_len, extradata_size; int last_interface; -#ifdef HAVE_DHCP6 - char is_ipv6; -#endif struct dhcp_lease *next; }; @@ -500,6 +502,8 @@ struct dhcp_config { struct dhcp_config *next; }; +#define have_config(config, mask) ((config) && ((config)->flags & (mask))) + #define CONFIG_DISABLE 1 #define CONFIG_CLID 2 #define CONFIG_TIME 8 @@ -537,6 +541,7 @@ struct dhcp_opt { #define DHOPT_VENDOR_MATCH 1024 #define DHOPT_RFC3925 2048 #define DHOPT_TAGOK 4096 +#define DHOPT_ADDR6 8192 struct dhcp_boot { char *file, *sname, *tftp_sname; @@ -586,6 +591,12 @@ struct cond_domain { struct cond_domain *next; }; +struct cond_domain6 { + char *domain; + struct in6_addr start, end; + struct cond_domain6 *next; +}; + struct dhcp_context { unsigned int lease_time, addr_epoch; struct in_addr netmask, broadcast; @@ -672,6 +683,7 @@ extern struct daemon { int group_set, osport; char *domain_suffix; struct cond_domain *cond_domain; + struct cond_domain6 *cond_domain6; char *runfile; char *lease_change_command; struct iname *if_names, *if_addrs, *if_except, *dhcp_except; @@ -776,12 +788,15 @@ void cache_start_insert(void); struct crec *cache_insert(char *name, struct all_addr *addr, time_t now, unsigned long ttl, unsigned short flags); void cache_reload(void); -void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd); +void cache_add_dhcp_entry(char *host_name, int prot, struct all_addr *host_address, time_t ttd); struct in_addr a_record_from_hosts(char *name, time_t now); void cache_unhash_dhcp(void); void dump_cache(time_t now); char *cache_get_name(struct crec *crecp); char *get_domain(struct in_addr addr); +#ifdef HAVE_IPV6 +char *get_domain6(struct in6_addr *addr); +#endif /* rfc1035.c */ unsigned int extract_request(struct dns_header *header, size_t qlen, @@ -845,7 +860,8 @@ void flush_log(void); /* option.c */ void read_opts (int argc, char **argv, char *compile_opts); -char *option_string(unsigned char opt, int *is_ip, int *is_name); +char *option_string(int prot, unsigned int opt, unsigned char *val, + int opt_len, char *buf, int buf_len); void reread_dhcp(void); void set_option_bool(unsigned int opt); struct hostsfile *expand_filelist(struct hostsfile *list); @@ -883,18 +899,15 @@ int set_ipv6pktinfo(int fd); #ifdef HAVE_DHCP void dhcp_init(void); void dhcp_packet(time_t now, int pxe_fd); -ssize_t recv_dhcp_packet(int fd, struct msghdr *msg); struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr, struct dhcp_netid *netids); struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr, struct dhcp_netid *netids); -int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly); int address_allocate(struct dhcp_context *context, struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct dhcp_netid *netids, time_t now); -struct dhcp_netid *run_tag_if(struct dhcp_netid *input); int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type); struct dhcp_config *find_config(struct dhcp_config *configs, struct dhcp_context *context, @@ -905,7 +918,6 @@ void dhcp_update_configs(struct dhcp_config *configs); void dhcp_read_ethers(void); void check_dhcp_hosts(int fatal); struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr); -char *strip_hostname(char *hostname); char *host_from_dns(struct in_addr addr); char *get_domain(struct in_addr addr); #endif @@ -917,13 +929,14 @@ void lease_update_dns(); void lease_init(time_t now); struct dhcp_lease *lease4_allocate(struct in_addr addr); #ifdef HAVE_DHCP6 -struct dhcp_lease *lease6_allocate(struct in6_addr *addrp); +struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type); +struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len, + int lease_type, int iaid, struct in6_addr *addr); struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr); -struct dhcp_lease *lease6_find_by_client(unsigned char *clid, int clid_len, int iaid); #endif void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, unsigned char *clid, int hw_len, int hw_type, int clid_len); -void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth); +void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain); void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now); void lease_set_interface(struct dhcp_lease *lease, int interface); struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type, @@ -1005,17 +1018,31 @@ int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, void dhcp6_init(void); void dhcp6_packet(time_t now); int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, - struct dhcp_netid *netids, struct in6_addr *ans); + int serial, struct dhcp_netid *netids, struct in6_addr *ans); struct dhcp_context *address6_available(struct dhcp_context *context, struct in6_addr *taddr, struct dhcp_netid *netids); struct dhcp_context *narrow_context6(struct dhcp_context *context, struct in6_addr *taddr, struct dhcp_netid *netids); +struct dhcp_config *find_config6(struct dhcp_config *configs, + struct dhcp_context *context, + unsigned char *duid, int duid_len, + char *hostname); +void make_duid(time_t now); #endif /* rfc3315.c */ #ifdef HAVE_DHCP6 -void make_duid(time_t now); -size_t dhcp6_reply(struct dhcp_context *context, size_t sz, time_t now); +size_t dhcp6_reply(struct dhcp_context *context, char *iface_name, size_t sz, int is_multicast, time_t now); #endif + +/* dhcp-common.c */ +void dhcp_common_init(void); +ssize_t recv_dhcp_packet(int fd, struct msghdr *msg); +struct dhcp_netid *run_tag_if(struct dhcp_netid *input); +struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, + struct dhcp_opt *opts); +int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly); +char *strip_hostname(char *hostname); +void log_tags(struct dhcp_netid *netid, u32 xid); diff --git a/src/lease.c b/src/lease.c index 96cfeb3..ef3c6cc 100644 --- a/src/lease.c +++ b/src/lease.c @@ -30,14 +30,9 @@ void lease_init(time_t now) FILE *leasestream; #ifdef HAVE_DHCP6 int v6pass = 0; + int lease_type = 0; #endif - /* These each hold a DHCP option max size 255 - and get a terminating zero added */ - daemon->dhcp_buff = safe_malloc(256); - daemon->dhcp_buff2 = safe_malloc(256); - daemon->dhcp_buff3 = safe_malloc(256); - leases_left = daemon->dhcp_max; if (option_bool(OPT_LEASE_RO)) @@ -86,7 +81,18 @@ void lease_init(time_t now) { #ifdef HAVE_DHCP6 if (v6pass) - hw_type = atoi(daemon->dhcp_buff2); + { + char *s = daemon->dhcp_buff2; + if (s[0] == 'T') + { + lease_type = LEASE_TA; + s++; + } + else + lease_type = LEASE_NA; + + hw_type = atoi(s); + } else #endif { @@ -109,7 +115,7 @@ void lease_init(time_t now) #ifdef HAVE_DHCP6 if (v6pass) - lease = lease6_allocate(&addr.addr.addr6); + lease = lease6_allocate(&addr.addr.addr6, lease_type); else #endif lease = lease4_allocate(addr.addr.addr4); @@ -130,16 +136,24 @@ void lease_init(time_t now) #endif #ifdef HAVE_DHCP6 - if (!v6pass) + if (v6pass) + lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len); + else #endif lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len); if (strcmp(daemon->dhcp_buff, "*") != 0) - lease_set_hostname(lease, daemon->dhcp_buff, 0); - + { +#ifdef HAVE_DHCP6 + if (v6pass) + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr)); + else +#endif + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr)); + } /* set these correctly: the "old" events are generated later from the startup synthesised SIGHUP. */ - lease->new = lease->changed = 0; + lease->flags |= ~(LEASE_NEW | LEASE_CHANGED); } #ifdef HAVE_DHCP6 @@ -196,17 +210,17 @@ void lease_update_from_configs(void) struct dhcp_lease *lease; struct dhcp_config *config; char *name; - + for (lease = leases; lease; lease = lease->next) if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && (config->flags & CONFIG_NAME) && (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr)) - lease_set_hostname(lease, config->hostname, 1); + lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr)); else if ((name = host_from_dns(lease->addr))) - lease_set_hostname(lease, name, 1); /* updates auth flag only */ + lease_set_hostname(lease, name, 1, get_domain(lease->addr)); /* updates auth flag only */ } - + static void ourprintf(int *errp, char *format, ...) { va_list ap; @@ -234,7 +248,7 @@ void lease_update_file(time_t now) { #ifdef HAVE_DHCP6 - if (lease->is_ipv6) + if (lease->flags & (LEASE_TA | LEASE_NA)) continue; #endif @@ -279,7 +293,7 @@ void lease_update_file(time_t now) for (lease = leases; lease; lease = lease->next) { - if (!lease->is_ipv6) + if (!(lease->flags & (LEASE_TA | LEASE_NA))) continue; #ifdef HAVE_BROKEN_RTC @@ -290,7 +304,8 @@ void lease_update_file(time_t now) inet_ntop(AF_INET6, lease->hwaddr, daemon->addrbuff, ADDRSTRLEN); - ourprintf(&err, "%u %s ", lease->hwaddr_type, daemon->addrbuff); + ourprintf(&err, "%s%u %s ", (lease->flags & LEASE_TA) ? "T" : "", + lease->hwaddr_type, daemon->addrbuff); ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); if (lease->clid && lease->clid_len != 0) @@ -343,11 +358,21 @@ void lease_update_dns(void) for (lease = leases; lease; lease = lease->next) { + int prot = AF_INET; +#ifdef HAVE_DHCP6 + if (lease->flags & (LEASE_TA | LEASE_NA)) + prot = AF_INET6; +#endif + if (lease->fqdn) - cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires); + cache_add_dhcp_entry(lease->fqdn, prot, + prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->hwaddr, + lease->expires); if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) - cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires); + cache_add_dhcp_entry(lease->hostname, prot, + prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->hwaddr, + lease->expires); } dns_dirty = 0; @@ -391,7 +416,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h for (lease = leases; lease; lease = lease->next) { #ifdef HAVE_DHCP6 - if (lease->is_ipv6) + if (lease->flags & (LEASE_TA | LEASE_NA)) continue; #endif if (lease->clid && clid_len == lease->clid_len && @@ -402,7 +427,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h for (lease = leases; lease; lease = lease->next) { #ifdef HAVE_DHCP6 - if (lease->is_ipv6) + if (lease->flags & (LEASE_TA | LEASE_NA)) continue; #endif if ((!lease->clid || !clid) && @@ -423,7 +448,7 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr) for (lease = leases; lease; lease = lease->next) { #ifdef HAVE_DHCP6 - if (lease->is_ipv6) + if (lease->flags & (LEASE_TA | LEASE_NA)) continue; #endif if (lease->addr.s_addr == addr.s_addr) @@ -434,19 +459,36 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr) } #ifdef HAVE_DHCP6 -struct dhcp_lease *lease6_find_by_client(unsigned char *clid, int clid_len, int iaid) +/* addr or clid may be NULL for "don't care, both NULL resets "USED" flags both + set activates USED check */ +struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len, + int lease_type, int iaid, struct in6_addr *addr) { struct dhcp_lease *lease; for (lease = leases; lease; lease = lease->next) { - if (!lease->is_ipv6) - continue; + if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid) + continue; - if (lease->hwaddr_type == iaid && - lease->clid && clid_len == lease->clid_len && - memcmp(clid, lease->clid, clid_len) == 0) - return lease; + if (clid && addr && (lease->flags & LEASE_USED)) + continue; + + if (addr && memcmp(lease->hwaddr, addr, IN6ADDRSZ) != 0) + continue; + + if (clid && + (clid_len != lease->clid_len || + memcmp(clid, lease->clid, clid_len) != 0)) + continue; + + if (clid || addr) + { + lease->flags |= LEASE_USED; + return lease; + } + else + lease->flags &= ~LEASE_USED; } return NULL; @@ -455,12 +497,12 @@ struct dhcp_lease *lease6_find_by_client(unsigned char *clid, int clid_len, int struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr) { struct dhcp_lease *lease; - + for (lease = leases; lease; lease = lease->next) { - if (!lease->is_ipv6) + if (!(lease->flags & (LEASE_TA | LEASE_NA))) continue; - + if (is_same_net6((struct in6_addr *)lease->hwaddr, net, prefix) && (prefix == 128 || addr6part((struct in6_addr *)lease->hwaddr) == addr)) return lease; @@ -468,7 +510,6 @@ struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 add return NULL; } - #endif /* Find largest assigned address in context */ @@ -481,7 +522,7 @@ struct in_addr lease_find_max_addr(struct dhcp_context *context) for (lease = leases; lease; lease = lease->next) { #ifdef HAVE_DHCP6 - if (lease->is_ipv6) + if (lease->flags & (LEASE_TA | LEASE_NA)) continue; #endif if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) && @@ -500,7 +541,7 @@ static struct dhcp_lease *lease_allocate(void) return NULL; memset(lease, 0, sizeof(struct dhcp_lease)); - lease->new = 1; + lease->flags = LEASE_NEW; lease->expires = 1; #ifdef HAVE_BROKEN_RTC lease->length = 0xffffffff; /* illegal value */ @@ -524,11 +565,11 @@ struct dhcp_lease *lease4_allocate(struct in_addr addr) } #ifdef HAVE_DHCP6 -struct dhcp_lease *lease6_allocate(struct in6_addr *addrp) +struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type) { struct dhcp_lease *lease = lease_allocate(); memcpy(lease->hwaddr, addrp, sizeof(*addrp)) ; - lease->is_ipv6 = 1; + lease->flags |= lease_type; return lease; } @@ -549,7 +590,8 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now) dns_dirty = 1; lease->expires = exp; #ifndef HAVE_BROKEN_RTC - lease->aux_changed = file_dirty = 1; + lease->flags |= LEASE_AUX_CHANGED; + file_dirty = 1; #endif } @@ -569,10 +611,12 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, hw_type != lease->hwaddr_type || (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0)) { - memcpy(lease->hwaddr, hwaddr, hw_len); + if (hw_len != 0) + memcpy(lease->hwaddr, hwaddr, hw_len); lease->hwaddr_len = hw_len; lease->hwaddr_type = hw_type; - lease->changed = file_dirty = 1; /* run script on change */ + lease->flags |= LEASE_CHANGED; + file_dirty = 1; /* run script on change */ } /* only update clid when one is available, stops packets @@ -585,14 +629,18 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, if (lease->clid_len != clid_len) { - lease->aux_changed = file_dirty = 1; + lease->flags |= LEASE_AUX_CHANGED; + file_dirty = 1; free(lease->clid); if (!(lease->clid = whine_malloc(clid_len))) return; } else if (memcmp(lease->clid, clid, clid_len) != 0) - lease->aux_changed = file_dirty = 1; - + { + lease->flags |= LEASE_AUX_CHANGED; + file_dirty = 1; + } + lease->clid_len = clid_len; memcpy(lease->clid, clid, clid_len); } @@ -608,7 +656,7 @@ static void kill_name(struct dhcp_lease *lease) free(lease->old_hostname); /* If we know the fqdn, pass that. The helper will derive the - unqualified name from it, free the unqulaified name here. */ + unqualified name from it, free the unqualified name here. */ if (lease->fqdn) { @@ -621,14 +669,15 @@ static void kill_name(struct dhcp_lease *lease) lease->hostname = lease->fqdn = NULL; } -void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) +void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain) { struct dhcp_lease *lease_tmp; char *new_name = NULL, *new_fqdn = NULL; if (lease->hostname && name && hostname_isequal(lease->hostname, name)) { - lease->auth_name = auth; + if (auth) + lease->flags |= LEASE_AUTH_NAME; return; } @@ -638,19 +687,21 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) /* If a machine turns up on a new net without dropping the old lease, or two machines claim the same name, then we end up with two interfaces with the same name. Check for that here and remove the name from the old lease. + Note that IPv6 leases are different. All the leases to the same DUID are + allowed the same name. + Don't allow a name from the client to override a name from dnsmasq config. */ if (name) { if ((new_name = whine_malloc(strlen(name) + 1))) { - char *suffix = get_domain(lease->addr); strcpy(new_name, name); - if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2))) + if (domain && (new_fqdn = whine_malloc(strlen(new_name) + strlen(domain) + 2))) { strcpy(new_fqdn, name); strcat(new_fqdn, "."); - strcat(new_fqdn, suffix); + strcat(new_fqdn, domain); } } @@ -659,7 +710,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) { if (option_bool(OPT_DHCP_FQDN)) { - if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) ) + if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn)) continue; } else @@ -667,8 +718,22 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) ) continue; } - - if (lease_tmp->auth_name && !auth) + + if (lease->flags & (LEASE_TA | LEASE_NA)) + { + if (!(lease_tmp->flags & (LEASE_TA | LEASE_NA))) + continue; + + /* another lease for the saem DUID is OK for IPv6 */ + if (lease->clid_len == lease_tmp->clid_len && + lease->clid && lease_tmp->clid && + memcmp(lease->clid, lease_tmp->clid, lease->clid_len) == 0) + continue; + } + else if (lease_tmp->flags & (LEASE_TA | LEASE_NA)) + continue; + + if ((lease_tmp->flags & LEASE_AUTH_NAME) && !auth) { free(new_name); free(new_fqdn); @@ -685,11 +750,13 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) lease->hostname = new_name; lease->fqdn = new_fqdn; - lease->auth_name = auth; + + if (auth) + lease->flags |= LEASE_AUTH_NAME; file_dirty = 1; dns_dirty = 1; - lease->changed = 1; /* run script on change */ + lease->flags |= LEASE_CHANGED; /* run script on change */ } void lease_set_interface(struct dhcp_lease *lease, int interface) @@ -698,7 +765,7 @@ void lease_set_interface(struct dhcp_lease *lease, int interface) return; lease->last_interface = interface; - lease->changed = 1; + lease->flags |= LEASE_CHANGED; } void rerun_scripts(void) @@ -706,7 +773,7 @@ void rerun_scripts(void) struct dhcp_lease *lease; for (lease = leases; lease; lease = lease->next) - lease->changed = 1; + lease->flags |= LEASE_CHANGED; } /* deleted leases get transferred to the old_leases list. @@ -772,18 +839,18 @@ int do_script_run(time_t now) } for (lease = leases; lease; lease = lease->next) - if (lease->new || lease->changed || - (lease->aux_changed && option_bool(OPT_LEASE_RO))) + if ((lease->flags & (LEASE_NEW | LEASE_CHANGED)) || + ((lease->flags & LEASE_AUX_CHANGED) && option_bool(OPT_LEASE_RO))) { #ifdef HAVE_SCRIPT - queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, + queue_script((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease, lease->fqdn ? lease->fqdn : lease->hostname, now); #endif #ifdef HAVE_DBUS - emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease, + emit_dbus_signal((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease, lease->fqdn ? lease->fqdn : lease->hostname); #endif - lease->new = lease->changed = lease->aux_changed = 0; + lease->flags &= ~(LEASE_NEW | LEASE_CHANGED | LEASE_AUX_CHANGED); /* this is used for the "add" call, then junked, since they're not in the database */ free(lease->extradata); diff --git a/src/netlink.c b/src/netlink.c index 8aca1d1..d6515d4 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -282,7 +282,9 @@ int iface_enumerate(int family, void *parm, int (*callback)()) rta = RTA_NEXT(rta, len1); } - if (mac && !((*callback)(link->ifi_type, link->ifi_flags, mac, maclen, parm))) + if (mac && !((*callback)((unsigned int)link->ifi_type, + (unsigned int)link->ifi_flags, + mac, maclen, parm))) return 0; } #endif diff --git a/src/option.c b/src/option.c index b0ae1e8..c807a8f 100644 --- a/src/option.c +++ b/src/option.c @@ -363,15 +363,16 @@ static struct { }; #ifdef HAVE_DHCP -#define OT_ADDR_LIST 0x80 -#define OT_RFC1035_NAME 0x40 -#define OT_INTERNAL 0x20 -#define OT_NAME 0x10 +#define OT_ADDR_LIST 0x8000 +#define OT_RFC1035_NAME 0x4000 +#define OT_INTERNAL 0x2000 +#define OT_NAME 0x1000 +#define OT_CSTRING 0x0800 -static const struct { +static const struct opttab_t { char *name; - unsigned char val, size; + u16 val, size; } opttab[] = { { "netmask", 1, OT_ADDR_LIST }, { "time-offset", 2, 4 }, @@ -422,7 +423,7 @@ static const struct { { "T1", 58, OT_INTERNAL }, { "T2", 59, OT_INTERNAL }, { "vendor-class", 60, 0 }, - { "client-id", 61,OT_INTERNAL }, + { "client-id", 61, OT_INTERNAL }, { "nis+-domain", 64, OT_NAME }, { "nis+-server", 65, OT_ADDR_LIST }, { "tftp-server", 66, OT_NAME }, @@ -447,21 +448,125 @@ static const struct { { NULL, 0, 0 } }; -char *option_string(unsigned char opt, int *is_ip, int *is_name) -{ - int i; +#ifdef HAVE_DHCP6 +static const struct opttab_t opttab6[] = { + { "client-id", 1, OT_INTERNAL }, + { "server-id", 2, OT_INTERNAL }, + { "ia-na", 3, OT_INTERNAL }, + { "ia-ta", 4, OT_INTERNAL }, + { "iaaddr", 5, OT_INTERNAL }, + { "oro", 6, OT_INTERNAL }, + { "preference", 7, OT_INTERNAL }, + { "unicast", 12, OT_INTERNAL }, + { "status-code", 13, OT_INTERNAL }, + { "rapid-commit", 14, OT_INTERNAL }, + { "user-class", 15, OT_INTERNAL | OT_CSTRING }, + { "vendor-class", 16, OT_INTERNAL | OT_CSTRING }, + { "vendor-opts", 17, OT_INTERNAL }, + { "sip-server-domain", 21, OT_RFC1035_NAME }, + { "sip-server", 22, OT_ADDR_LIST }, + { "dns-server", 23, OT_ADDR_LIST }, + { "domain-search", 24, OT_RFC1035_NAME }, + { "nis-server", 27, OT_ADDR_LIST }, + { "nis+-server", 28, OT_ADDR_LIST }, + { "nis-domain", 29, OT_RFC1035_NAME }, + { "nis+-domain", 30, OT_RFC1035_NAME }, + { "sntp-server", 31, OT_ADDR_LIST }, + { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME }, + { "ntp-server", 56, OT_ADDR_LIST }, + { "bootfile-url", 59, OT_NAME }, + { "bootfile-param", 60, OT_CSTRING }, + { NULL, 0, 0 } +}; +#endif - for (i = 0; opttab[i].name; i++) - if (opttab[i].val == opt) + +char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len) +{ + int o, i, j, nodecode = 0; + const struct opttab_t *ot = opttab; + +#ifdef HAVE_DHCP6 + if (prot == AF_INET6) + ot = opttab6; +#endif + + for (o = 0; ot[o].name; o++) + if (ot[o].val == opt) { - if (is_ip) - *is_ip = !!(opttab[i].size & OT_ADDR_LIST); - if (is_name) - *is_name = !!(opttab[i].size & OT_NAME); - return opttab[i].name; + if (buf) + { + memset(buf, 0, buf_len); + + if (ot[o].size & OT_ADDR_LIST) + { + struct all_addr addr; + int addr_len = INADDRSZ; + +#ifdef HAVE_DHCP6 + if (prot == AF_INET6) + addr_len = IN6ADDRSZ; +#endif + for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len) + { + if (i != 0) + strncat(buf, ", ", buf_len - strlen(buf)); + /* align */ + memcpy(&addr, &val[i], addr_len); + inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN); + strncat(buf, daemon->addrbuff, buf_len - strlen(buf)); + } + } + else if (ot[o].size & OT_NAME) + for (i = 0, j = 0; i < opt_len && j < buf_len ; i++) + { + char c = val[i]; + if (isprint((int)c)) + buf[j++] = c; + } +#ifdef HAVE_DHCP6 + /* We don't handle compressed rfc1035 names, so no good in IPv4 land */ + else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6) + { + i = 0, j = 0; + while (i < opt_len && val[i] != 0) + { + int k, l = i + val[i] + 1; + for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++) + { + char c = val[k]; + if (isprint((int)c)) + buf[j++] = c; + } + i = l; + if (val[i] != 0 && j < buf_len) + buf[j++] = '.'; + } + } +#endif + else + nodecode = 1; + } + break; } - return NULL; + if (buf && (!ot[o].name || nodecode)) + { + int trunc = 0; + if (opt_len > 13) + { + trunc = 1; + opt_len = 13; + } + print_mac(buf, val, opt_len); + if (trunc) + strncat(buf, "...", buf_len - strlen(buf)); + + + } + + return ot[o].name ? ot[o].name : ""; + } #endif @@ -699,6 +804,19 @@ static void display_opts(void) printf("%3d %s\n", opttab[i].val, opttab[i].name); } +#ifdef HAVE_DHCP6 +static void display_opts6(void) +{ + int i; + printf(_("Known DHCPv6 options:\n")); + + for (i = 0; opttab6[i].name; i++) + if (!(opttab6[i].size & OT_INTERNAL)) + printf("%3d %s\n", opttab6[i].val, opttab6[i].name); +} +#endif + + static int is_tag_prefix(char *arg) { if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg)) @@ -720,10 +838,11 @@ static char *parse_dhcp_opt(char *arg, int flags) { struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt)); char lenchar = 0, *cp; - int i, addrs, digs, is_addr, is_hex, is_dec, is_string, dots; + int i, addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots; char *comma = NULL, *problem = NULL; struct dhcp_netid *np = NULL; - unsigned char opt_len = 0; + u16 opt_len = 0; + int is6 = 0; new->len = 0; new->flags = flags; @@ -759,6 +878,32 @@ static char *parse_dhcp_opt(char *arg, int flags) /* option: must follow tag and vendor string. */ break; } +#ifdef HAVE_DHCP6 + else if (strstr(arg, "option6:") == arg) + { + for (cp = arg+8; *cp; cp++) + if (*cp < '0' || *cp > '9') + break; + + if (!*cp) + { + new->opt = atoi(arg+8); + opt_len = 0; + } + else + for (i = 0; opttab6[i].name; i++) + if (!(opttab6[i].size & OT_INTERNAL) && + strcasecmp(opttab6[i].name, arg+8) == 0) + { + new->opt = opttab6[i].val; + opt_len = opttab6[i].size; + break; + } + /* option6:| must follow tag and vendor string. */ + is6 = 1; + break; + } +#endif else if (strstr(arg, "vendor:") == arg) { new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7); @@ -793,17 +938,36 @@ static char *parse_dhcp_opt(char *arg, int flags) arg = comma; } - - if (opt_len == 0 && - !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925))) - for (i = 0; opttab[i].name; i++) - if (new->opt == opttab[i].val) - { - opt_len = opttab[i].size; - if (opt_len & OT_INTERNAL) - opt_len = 0; - break; - } + +#ifdef HAVE_DHCP6 + if (is6) + { + if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE)) + problem = _("unsupported encapsulation for IPv6 option"); + + if (opt_len == 0 && + !(new->flags & DHOPT_RFC3925)) + for (i = 0; opttab6[i].name; i++) + if (new->opt == opttab6[i].val) + { + opt_len = opttab6[i].size; + if (opt_len & OT_INTERNAL) + opt_len = 0; + break; + } + } + else +#endif + if (opt_len == 0 && + !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925))) + for (i = 0; opttab[i].name; i++) + if (new->opt == opttab[i].val) + { + opt_len = opttab[i].size; + if (opt_len & OT_INTERNAL) + opt_len = 0; + break; + } /* option may be missing with rfc3925 match */ if (new->opt == 0) @@ -813,7 +977,7 @@ static char *parse_dhcp_opt(char *arg, int flags) /* characterise the value */ char c; int found_dig = 0; - is_addr = is_hex = is_dec = is_string = 1; + is_addr = is_addr6 = is_hex = is_dec = is_string = 1; addrs = digs = 1; dots = 0; for (cp = comma; (c = *cp); cp++) @@ -829,17 +993,17 @@ static char *parse_dhcp_opt(char *arg, int flags) } else if (c == '/') { - is_dec = is_hex = 0; + is_addr6 = is_dec = is_hex = 0; if (cp == comma) /* leading / means a pathname */ is_addr = 0; } else if (c == '.') { - is_dec = is_hex = 0; + is_addr6 =is_dec = is_hex = 0; dots++; } else if (c == '-') - is_hex = is_addr = 0; + is_hex = is_addr = is_addr6 = 0; else if (c == ' ') is_dec = is_hex = 0; else if (!(c >='0' && c <= '9')) @@ -856,25 +1020,33 @@ static char *parse_dhcp_opt(char *arg, int flags) if (!((c >='A' && c <= 'F') || (c >='a' && c <= 'f') || (c == '*' && (flags & DHOPT_MATCH)))) - is_hex = 0; + { + is_hex = 0; + if (c != '[' && c != ']') + is_addr6 = 0; + } } else found_dig = 1; if (!found_dig) is_dec = is_addr = 0; - + /* We know that some options take addresses */ if (opt_len & OT_ADDR_LIST) { is_string = is_dec = is_hex = 0; - if (!is_addr || dots == 0) + + if (!is6 && (!is_addr || dots == 0)) problem = _("bad IP address"); + + if (is6 && !is_addr6) + problem = _("bad IPv6 address"); } /* or names */ - else if (opt_len & (OT_NAME | OT_RFC1035_NAME)) - is_addr = is_dec = is_hex = 0; - + else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING)) + is_addr6 = is_addr = is_dec = is_hex = 0; + if (is_hex && digs > 1) { new->len = digs; @@ -908,7 +1080,7 @@ static char *parse_dhcp_opt(char *arg, int flags) for (i=0; ilen; i++) new->val[i] = val>>((new->len - i - 1)*8); } - else if (is_addr) + else if (is_addr && !is6) { struct in_addr in; unsigned char *op; @@ -953,11 +1125,37 @@ static char *parse_dhcp_opt(char *arg, int flags) } new->len = op - new->val; } + else if (is_addr6 && is6) + { + unsigned char *op; + new->val = op = opt_malloc(16 * addrs); + new->flags |= DHOPT_ADDR6; + while (addrs--) + { + cp = comma; + comma = split(cp); + + /* check for [1234::7] */ + if (*cp == '[') + cp++; + if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']') + cp[strlen(cp)-1] = 0; + + if (inet_pton(AF_INET6, cp, op)) + { + op += IN6ADDRSZ; + continue; + } + + problem = _("bad IPv6 address"); + } + new->len = op - new->val; + } else if (is_string) { - /* text arg */ + /* text arg */ if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) && - !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925))) + !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925))) { /* dns search, RFC 3397, or SIP, RFC 3361 */ unsigned char *q, *r, *tail; @@ -1030,6 +1228,63 @@ static char *parse_dhcp_opt(char *arg, int flags) new->len = (int) len + header_size; new->val = m; } +#ifdef HAVE_DHCP6 + else if (comma && (opt_len & OT_CSTRING)) + { + /* length fields are two bytes so need 16 bits for each string */ + int commas = 1; + unsigned char *p, *newp; + + for(i = 0; comma[i]; i++) + if (comma[i] == ',') + commas++; + + newp = opt_malloc(strlen(comma)+(2*commas)); + p = newp; + arg = comma; + comma = split(arg); + + while (arg && *arg) + { + u16 len = strlen(arg); + PUTSHORT(len, p); + memcpy(p, arg, len); + p += len; + + arg = comma; + comma = split(arg); + } + + new->val = newp; + new->len = p - newp; + } + else if (comma && (opt_len & OT_RFC1035_NAME)) + { + int commas = 1; + unsigned char *p, *newp; + + for(i = 0; comma[i]; i++) + if (comma[i] == ',') + commas++; + + newp = opt_malloc(strlen(comma)+(2*commas)); + p = newp; + arg = comma; + comma = split(arg); + + while (arg && *arg) + { + p = do_rfc1035_name(p, arg); + *p++ = 0; + + arg = comma; + comma = split(arg); + } + + new->val = newp; + new->len = p - newp; + } +#endif else { new->len = strlen(comma); @@ -1040,9 +1295,10 @@ static char *parse_dhcp_opt(char *arg, int flags) } } - if ((new->len > 255) || + if (!is6 && + ((new->len > 255) || (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) || - (new->len > 250 && (new->flags & DHOPT_RFC3925))) + (new->len > 250 && (new->flags & DHOPT_RFC3925)))) problem = _("dhcp-option too long"); if (!problem) @@ -1059,7 +1315,12 @@ static char *parse_dhcp_opt(char *arg, int flags) daemon->dhcp_match = new; } } - else + else if (is6) + { + new->next = daemon->dhcp_opts6; + daemon->dhcp_opts6 = new; + } + else { new->next = daemon->dhcp_opts; daemon->dhcp_opts = new; @@ -1989,6 +2250,8 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) { new->prefix = pref; leasepos = 3; + if (new->prefix < 64) + problem = _("prefix must be at least 64"); } } if (!is_same_net6(&new->start6, &new->end6, new->prefix)) @@ -2109,6 +2372,18 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) } else if (strstr(arg, "tag:") == arg) problem = _("cannot match tags in --dhcp-host"); +#ifdef HAVE_DHCP6 + else if (arg[0] == '[' && arg[strlen(arg)-1] == ']') + { + arg[strlen(arg)-1] = 0; + arg++; + + if (!inet_pton(AF_INET6, arg, &new->addr6)) + problem = _("bad IPv6 address"); + + new->flags |= CONFIG_ADDR6; + } +#endif else { struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config)); @@ -3338,12 +3613,17 @@ void read_opts(int argc, char **argv, char *compile_opts) testmode = 1; else if (option == 'w') { - if (argc != 3 || strcmp(argv[2], "dhcp") != 0) - do_usage(); #ifdef HAVE_DHCP - else + if (argc == 3 && strcmp(argv[2], "dhcp") == 0) display_opts(); +#ifdef HAVE_DHCP6 + else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0) + display_opts6(); #endif + else +#endif + do_usage(); + exit(0); } else if (option == 'v') diff --git a/src/rfc2131.c b/src/rfc2131.c index 0e075f9..fcd9bf8 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -18,7 +18,6 @@ #ifdef HAVE_DHCP -#define have_config(config, mask) ((config) && ((config)->flags & (mask))) #define option_len(opt) ((int)(((unsigned char *)(opt))[1])) #define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)])) @@ -35,13 +34,11 @@ static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, in static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt, char *string, int null_term); static struct in_addr option_addr(unsigned char *opt); -static struct in_addr option_addr_arr(unsigned char *opt, int offset); static unsigned int option_uint(unsigned char *opt, int i, int size); static void log_packet(char *type, void *addr, unsigned char *ext_mac, int mac_len, char *interface, char *string, u32 xid); static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize); static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize); -static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess); static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end); static void clear_packet(struct dhcp_packet *mess, unsigned char *end); static void do_options(struct dhcp_context *context, @@ -468,7 +465,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, tagif_netid = run_tag_if(&context->netid); } - log_tags(tagif_netid, mess); + log_tags(tagif_netid, ntohl(mess->xid)); if (!message && !nailed) { @@ -490,7 +487,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0); if (hostname) - lease_set_hostname(lease, hostname, 1); + lease_set_hostname(lease, hostname, 1, get_domain(lease->addr)); /* infinite lease unless nailed in dhcp-host line. */ lease_set_expires(lease, have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff, @@ -819,7 +816,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid); - log_tags(tagif_netid, mess); + log_tags(tagif_netid, ntohl(mess->xid)); return dhcp_packet_size(mess, agent_id, real_end); } @@ -871,7 +868,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid); - log_tags(tagif_netid, mess); + log_tags(tagif_netid, ntohl(mess->xid)); return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end); } } @@ -1009,7 +1006,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, tagif_netid = run_tag_if(&context->netid); } - log_tags(tagif_netid, mess); + log_tags(tagif_netid, ntohl(mess->xid)); log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid); @@ -1223,7 +1220,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, tagif_netid = run_tag_if( &context->netid); } - log_tags(tagif_netid, mess); + log_tags(tagif_netid, ntohl(mess->xid)); #ifdef HAVE_SCRIPT if (do_classes && daemon->lease_change_command) @@ -1233,7 +1230,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, if (mess->giaddr.s_addr) lease->giaddr = mess->giaddr; - lease->changed = 1; + lease->flags |= LEASE_CHANGED; free(lease->extradata); lease->extradata = NULL; lease->extradata_size = lease->extradata_len = 0; @@ -1310,7 +1307,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, } if (hostname) - lease_set_hostname(lease, hostname, hostname_auth); + lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr)); lease_set_expires(lease, time, now); lease_set_interface(lease, int_index); @@ -1364,10 +1361,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, if (context && context->netid.net) { context->netid.next = netid; - tagif_netid = run_tag_if( &context->netid); + tagif_netid = run_tag_if(&context->netid); } - log_tags(tagif_netid, mess); + log_tags(tagif_netid, ntohl(mess->xid)); log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid); @@ -1601,30 +1598,10 @@ static void log_options(unsigned char *start, u32 xid) { while (*start != OPTION_END) { - int is_ip, is_name, i; - char *text = option_string(start[0], &is_ip, &is_name); - unsigned char trunc = option_len(start); + char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME); - if (is_ip) - for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ) - { - if (i != 0) - strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff)); - strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff)); - } - else if (!is_name || !sanitise(start, daemon->namebuff)) - { - if (trunc > 13) - trunc = 13; - print_mac(daemon->namebuff, option_ptr(start, 0), trunc); - } - - my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s", - ntohl(xid), option_len(start), start[0], - text ? ":" : "", text ? text : "", - trunc == 0 ? "" : " ", - trunc == 0 ? "" : daemon->namebuff, - trunc == option_len(start) ? "" : "..."); + my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s", + ntohl(xid), option_len(start), start[0], optname, daemon->namebuff); start += start[1] + 2; } } @@ -1679,22 +1656,17 @@ static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt return NULL; } -static struct in_addr option_addr_arr(unsigned char *opt, int offset) +static struct in_addr option_addr(unsigned char *opt) { - /* this worries about unaligned data in the option. */ + /* this worries about unaligned data in the option. */ /* struct in_addr is network byte order */ struct in_addr ret; - memcpy(&ret, option_ptr(opt, offset), INADDRSZ); + memcpy(&ret, option_ptr(opt, 0), INADDRSZ); return ret; } -static struct in_addr option_addr(unsigned char *opt) -{ - return option_addr_arr(opt, 0); -} - static unsigned int option_uint(unsigned char *opt, int offset, int size) { /* this worries about unaligned data and byte order */ @@ -1729,31 +1701,6 @@ static unsigned char *find_overload(struct dhcp_packet *mess) return NULL; } -static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess) -{ - if (netid && option_bool(OPT_LOG_OPTS)) - { - char *s = daemon->namebuff; - for (*s = 0; netid; netid = netid->next) - { - /* kill dupes. */ - struct dhcp_netid *n; - - for (n = netid->next; n; n = n->next) - if (strcmp(netid->net, n->net) == 0) - break; - - if (!n) - { - strncat (s, netid->net, (MAXDNAME-1) - strlen(s)); - if (netid->next) - strncat (s, ", ", (MAXDNAME-1) - strlen(s)); - } - } - my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s); - } -} - static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end) { unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32)); @@ -2216,49 +2163,8 @@ static void do_options(struct dhcp_context *context, struct dhcp_netid *tagif; struct dhcp_netid_list *id_list; - /* flag options which are valid with the current tag set (sans context tags) */ - tagif = run_tag_if(netid); - for (opt = config_opts; opt; opt = opt->next) - { - opt->flags &= ~DHOPT_TAGOK; - if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) && - match_netid(opt->netid, tagif, 0)) - opt->flags |= DHOPT_TAGOK; - } - - /* now flag options which are valid, including the context tags, - otherwise valid options are inhibited if we found a higher priotity one above */ - if (context && context->netid.net) - { - context->netid.next = netid; - tagif = run_tag_if(&context->netid); - - for (opt = config_opts; opt; opt = opt->next) - if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && - match_netid(opt->netid, tagif, 0)) - { - struct dhcp_opt *tmp; - for (tmp = config_opts; tmp; tmp = tmp->next) - if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK)) - break; - if (!tmp) - opt->flags |= DHOPT_TAGOK; - } - } - - /* now flag untagged options which are not overridden by tagged ones */ - for (opt = config_opts; opt; opt = opt->next) - if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid) - { - struct dhcp_opt *tmp; - for (tmp = config_opts; tmp; tmp = tmp->next) - if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK)) - break; - if (!tmp) - opt->flags |= DHOPT_TAGOK; - else if (!tmp->netid) - my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt); - } + /* filter options based on tags, those we want get DHOPT_TAGOK bit set */ + tagif = option_filter(netid, &context->netid, config_opts); if (config_domain && (!domain || !hostname_isequal(domain, config_domain))) my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname); @@ -2269,12 +2175,12 @@ static void do_options(struct dhcp_context *context, char *q = daemon->namebuff; for (i = 0; req_options[i] != OPTION_END; i++) { - char *s = option_string(req_options[i], NULL, NULL); + char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0); q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s%s%s", req_options[i], - s ? ":" : "", - s ? s : "", + strlen(s) != 0 ? ":" : "", + s, req_options[i+1] == OPTION_END ? "" : ", "); if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40) { @@ -2511,8 +2417,8 @@ static void do_options(struct dhcp_context *context, /* Now send options to be encapsulated in arbitrary options, eg dhcp-option=encap:172,17,....... - Also hand vendor-identifying vendor-encapsulated options, - dhcp-option = rfc3925-encap:13,17,....... + Also handle vendor-identifying vendor-encapsulated options, + dhcp-option = vi-encap:13,17,....... The may be more that one "outer" to do, so group all the options which match each outer in turn. */ for (opt = config_opts; opt; opt = opt->next) diff --git a/src/rfc3315.c b/src/rfc3315.c index ae90eea..67aa624 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -20,41 +20,1031 @@ #ifdef HAVE_DHCP6 static size_t outpacket_counter; +static void end_opt6(int container); +static int save_counter(int newval); +static void *expand(size_t headroom); +static int new_opt6(int opt); +static void *put_opt6(void *data, size_t len); +static void put_opt6_short(unsigned int val); +static void put_opt6_long(unsigned int val); +static void put_opt6_string(char *s); -static int make_duid1(unsigned short type, unsigned int flags, char *mac, - size_t maclen, void *parm); -static void do_options6(struct dhcp_context *context, void *oro); +static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, + struct dhcp_context *context, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now); +static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, + struct dhcp_context *context, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now); +static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string); -void make_duid(time_t now) +static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize); +static void *opt6_next(void *opts, void *end); +static unsigned int opt6_uint(unsigned char *opt, int offset, int size); +#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2))) +#define opt6_type(opt) (opt6_uint(opt, -4, 2)) +#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)])) + + + +size_t dhcp6_reply(struct dhcp_context *context, char *iface_name, size_t sz, int is_unicast, time_t now) { - iface_enumerate(AF_LOCAL, &now, make_duid1); + struct dhcp_netid *relay_tags = NULL; + struct dhcp_vendor *vendor; - if (!daemon->duid) - die("Cannot create DHCPv6 server DUID", NULL, EC_MISC); -} - -static int make_duid1(unsigned short type, unsigned int flags, char *mac, - size_t maclen, void *parm) -{ - /* create DUID as specified in RFC3315. We use the MAC of the - first interface we find that isn't loopback or P-to-P */ + /* Mark these so we only match each at most once, to avoid tangled linked lists */ + for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) + vendor->netid.next = &vendor->netid; - unsigned char *p; - - if (flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) - return 1; + outpacket_counter = 0; - daemon->duid = p = safe_malloc(maclen + 8); - daemon->duid_len = maclen + 8; - PUTSHORT(1, p); /* DUID_LLT */ - PUTSHORT(type, p); /* address type */ - PUTLONG(*((time_t *)parm), p); /* time */ - memcpy(p, mac, maclen); + if (dhcp6_maybe_relay(NULL, &relay_tags, context, iface_name, daemon->dhcp_packet.iov_base, sz, is_unicast, now)) + return outpacket_counter; return 0; } -void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize) +/* This cost me blood to write, it will probably cost you blood to understand - srk. */ +static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, + struct dhcp_context *context, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now) +{ + void *end = inbuff + sz; + void *opts = inbuff + 34; + int msg_type = *((unsigned char *)inbuff); + unsigned char *outmsgtypep; + void *opt; + struct dhcp_vendor *vendor; + + /* if not an encaplsulated relayed message, just do the stuff */ + if (msg_type != DHCP6RELAYFORW) + { + /* if link_address != NULL if points to the link address field of the + innermost nested RELAYFORW message, which is where we find the + address of the network on which we can allocate an address. + Recalculate the available contexts using that information. */ + + if (link_address) + { + struct dhcp_context *c; + context = NULL; + + for (c = daemon->dhcp6; c; c = c->next) + if (!IN6_IS_ADDR_LOOPBACK(link_address) && + !IN6_IS_ADDR_LINKLOCAL(link_address) && + !IN6_IS_ADDR_MULTICAST(link_address) && + is_same_net6(link_address, &c->start6, c->prefix) && + is_same_net6(link_address, &c->end6, c->prefix)) + { + c->current = context; + context = c; + } + + if (!context) + { + inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_WARNING, + _("no address range available for DHCPv6 request from relay at %s"), + daemon->addrbuff); + return 0; + } + } + + if (!context) + { + my_syslog(MS_DHCP | LOG_WARNING, + _("no address range available for DHCPv6 request via %s"), iface_name); + return 0; + } + + return dhcp6_no_relay(msg_type, *relay_tagsp, context, iface_name, inbuff, sz, is_unicast, now); + } + + /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option + which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */ + if (sz < 38) + return 0; + + /* copy header stuff into reply message and set type to reply */ + outmsgtypep = put_opt6(inbuff, 34); + *outmsgtypep = DHCP6RELAYREPL; + + /* look for relay options and set tags if found. */ + for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) + { + int mopt; + + if (vendor->match_type == MATCH_SUBSCRIBER) + mopt = OPTION6_SUBSCRIBER_ID; + else if (vendor->match_type == MATCH_REMOTE) + mopt = OPTION6_REMOTE_ID; + else + continue; + + if ((opt = opt6_find(opts, end, mopt, 1)) && + vendor->len == opt6_len(opt) && + memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 && + vendor->netid.next != &vendor->netid) + { + vendor->netid.next = *relay_tagsp; + *relay_tagsp = &vendor->netid; + break; + } + } + + for (opt = opts; opt; opt = opt6_next(opt, end)) + { + int o = new_opt6(opt6_type(opt)); + if (opt6_type(opt) == OPTION6_RELAY_MSG) + { + struct in6_addr link_address; + /* the packet data is unaligned, copy to aligned storage */ + memcpy(&link_address, inbuff + 2, IN6ADDRSZ); + /* Not, zero is_unicast since that is now known to refer to the + relayed packet, not the original sent by the client */ + if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, iface_name, opt6_ptr(opt, 0), opt6_len(opt), 0, now)) + return 0; + } + else + put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); + end_opt6(o); + } + + return 1; +} + +static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, + struct dhcp_context *context, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now) +{ + void *packet_options = inbuff + 4; + void *end = inbuff + sz; + void *opt, *oro; + int i, o, o1; + unsigned char *clid = NULL; + int clid_len = 0, start_opts; + struct dhcp_netid *tagif, *context_tags = NULL; + char *client_hostname= NULL, *hostname = NULL, *domain= NULL; + struct dhcp_config *config = NULL; + struct dhcp_netid known_id; + int done_dns = 0, hostname_auth = 0, do_encap = 0; + unsigned char *outmsgtypep; + struct dhcp_opt *opt_cfg; + struct dhcp_vendor *vendor; + struct dhcp_context *context_tmp; + unsigned int xid; + unsigned int fqdn_flags = 0x01; /* default to send if we recieve no FQDN option */ + + /* copy over transaction-id, and save pointer to message type */ + outmsgtypep = put_opt6(inbuff, 4); + start_opts = save_counter(-1); + xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16; + + /* We're going to be linking tags from all context we use. + mark them as unused so we don't link one twice and break the list */ + for (context_tmp = context; context_tmp; context_tmp = context_tmp->current) + { + context->netid.next = &context->netid; + + if (option_bool(OPT_LOG_OPTS)) + { + inet_ntop(AF_INET6, &context_tmp->start6, daemon->dhcp_buff, ADDRSTRLEN); + inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN); + if (context_tmp->flags & (CONTEXT_STATIC)) + my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"), + xid, daemon->dhcp_buff, context_tmp->prefix); + else + my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"), + xid, daemon->dhcp_buff, daemon->dhcp_buff2); + } + } + + if ((opt = opt6_find(packet_options, end, OPTION6_CLIENT_ID, 1))) + { + clid = opt6_ptr(opt, 0); + clid_len = opt6_len(opt); + o = new_opt6(OPTION6_CLIENT_ID); + put_opt6(clid, clid_len); + end_opt6(o); + } + else if (msg_type != DHCP6IREQ) + return 0; + + /* server-id must match except for SOLICIT and CONFIRM messages */ + if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && + (!(opt = opt6_find(packet_options, end, OPTION6_SERVER_ID, 1)) || + opt6_len(opt) != daemon->duid_len || + memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) + return 0; + + o = new_opt6(OPTION6_SERVER_ID); + put_opt6(daemon->duid, daemon->duid_len); + end_opt6(o); + + if (is_unicast && + (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE)) + + { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6USEMULTI); + put_opt6_string("Use multicast"); + end_opt6(o1); + return 1; + } + + /* match vendor and user class options */ + for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) + { + int mopt; + + if (vendor->match_type == MATCH_VENDOR) + mopt = OPTION6_VENDOR_CLASS; + else if (vendor->match_type == MATCH_USER) + mopt = OPTION6_USER_CLASS; + else + continue; + + if ((opt = opt6_find(packet_options, end, mopt, 2))) + { + void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt)); + for (enc_opt = opt6_ptr(opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) + for (i = 0; i <= (opt6_len(enc_opt) - vendor->len); i++) + if (memcmp(vendor->data, opt6_ptr(enc_opt, i), vendor->len) == 0) + { + vendor->netid.next = tags; + tags = &vendor->netid; + break; + } + } + } + + if ((opt = opt6_find(packet_options, end, OPTION6_FQDN, 1))) + { + /* RFC4704 refers */ + int len = opt6_len(opt) - 1; + + fqdn_flags = opt6_uint(opt, 0, 1); + + /* Always force update, since the client has no way to do it itself. */ + if (!option_bool(OPT_FQDN_UPDATE) && !(fqdn_flags & 0x01)) + fqdn_flags |= 0x03; + + fqdn_flags &= ~0x04; + + if (len != 0 && len < 255) + { + unsigned char *pp, *op = opt6_ptr(opt, 1); + char *pq = daemon->dhcp_buff; + + pp = op; + while (*op != 0 && ((op + (*op) + 1) - pp) < len) + { + memcpy(pq, op+1, *op); + pq += *op; + op += (*op)+1; + *(pq++) = '.'; + } + + if (pq != daemon->dhcp_buff) + pq--; + *pq = 0; + + if (legal_hostname(daemon->dhcp_buff)) + { + client_hostname = daemon->dhcp_buff; + if (option_bool(OPT_LOG_OPTS)) + my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), xid, client_hostname); + } + } + } + + if (clid) + { + config = find_config6(daemon->dhcp_conf, context, clid, clid_len, NULL); + + if (have_config(config, CONFIG_NAME)) + { + hostname = config->hostname; + domain = config->domain; + hostname_auth = 1; + } + else if (client_hostname) + { + domain = strip_hostname(client_hostname); + /* TODO verify legal domain */ + + if (strlen(client_hostname) != 0) + { + hostname = client_hostname; + if (!config) + { + /* Search again now we have a hostname. + Only accept configs without CLID and HWADDR here, (they won't match) + to avoid impersonation by name. */ + struct dhcp_config *new = find_config6(daemon->dhcp_conf, context, NULL, 0, hostname); + if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) + config = new; + } + } + } + } + + if (config) + { + struct dhcp_netid_list *list; + + for (list = config->netid; list; list = list->next) + { + list->list->next = tags; + tags = list->list; + } + + /* set "known" tag for known hosts */ + known_id.net = "known"; + known_id.next = tags; + tags = &known_id; + } + + + + switch (msg_type) + { + case DHCP6SOLICIT: + case DHCP6REQUEST: + { + void *rapid_commit = opt6_find(packet_options, end, OPTION6_RAPID_COMMIT, 0); + int make_lease = (msg_type == DHCP6REQUEST || rapid_commit); + int serial = 0, used_config = 0; + + if (rapid_commit) + { + o = new_opt6(OPTION6_RAPID_COMMIT); + end_opt6(o); + } + + /* set reply message type */ + *outmsgtypep = make_lease ? DHCP6REPLY : DHCP6ADVERTISE; + + log6_packet(msg_type == DHCP6SOLICIT ? "DHCPSOLICIT" : "DHCPREQUEST", + clid, clid_len, NULL, xid, iface_name, NULL); + + for (opt = packet_options; opt; opt = opt6_next(opt, end)) + { + int iaid, ia_type = opt6_type(opt); + void *ia_option, *ia_end; + unsigned int min_time = 0xffffffff; + int t1cntr; + int address_assigned = 0; + + if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA) + continue; + + if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12) + continue; + + if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4) + continue; + + iaid = opt6_uint(opt, 0, 4); + ia_end = opt6_ptr(opt, opt6_len(opt)); + ia_option = opt6_find(opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4), ia_end, OPTION6_IAADDR, 24); + + /* reset "USED" flags on leases */ + lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL); + + o = new_opt6(ia_type); + put_opt6_long(iaid); + if (ia_type == OPTION6_IA_NA) + { + /* save pointer */ + t1cntr = save_counter(-1); + /* so we can fill these in later */ + put_opt6_long(0); + put_opt6_long(0); + } + + while (1) + { + struct in6_addr alloced_addr, *addrp = NULL; + u32 preferred_time = 0; + struct dhcp_lease *lease = NULL; + + if (ia_option) + { + struct in6_addr *req_addr = opt6_ptr(ia_option, 0); + preferred_time = opt6_uint(ia_option, 16, 4); + + if (!address6_available(context, req_addr, tags)) + { + if (msg_type == DHCP6REQUEST) + { + /* host has a lease, but it's not on the correct link */ + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOTONLINK); + put_opt6_string("Not on link"); + end_opt6(o1); + } + } + else if ((lease = lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, + iaid, req_addr)) && + (clid_len != lease->clid_len || + memcmp(clid, lease->clid, clid_len) != 0)) + { + /* Address leased to another DUID */ + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6UNSPEC); + put_opt6_string("Address in use"); + end_opt6(o1); + } + else + addrp = req_addr; + } + else + { + /* must have an address to CONFIRM */ + if (msg_type == DHCP6REQUEST && ia_type == OPTION6_IA_NA) + return 0; + + /* Don't used configured addresses for temporary leases. */ + if (have_config(config, CONFIG_ADDR6) && !used_config && ia_type == OPTION6_IA_NA) + { + used_config = 1; + addrp = &config->addr6; + } + /* existing lease */ + else if ((lease = lease6_find(clid, clid_len, + ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL))) + addrp = (struct in6_addr *)&lease->hwaddr; + else if (address6_allocate(context, clid, clid_len, serial++, tags, &alloced_addr)) + addrp = &alloced_addr; + } + + if (addrp) + { + unsigned int lease_time; + struct dhcp_context *this_context; + struct dhcp_config *valid_config = config; + + /* don't use a config to set lease time if it specifies an address which isn't this. */ + if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, addrp, IN6ADDRSZ) != 0) + valid_config = NULL; + + address_assigned = 1; + + /* shouldn't ever fail */ + if ((this_context = narrow_context6(context, addrp, tags))) + { + /* get tags from context if we've not used it before */ + if (this_context->netid.next != &this_context->netid && this_context->netid.net) + { + this_context->netid.next = context_tags; + context_tags = &this_context->netid; + } + + lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time; + + if (ia_option) + { + if (preferred_time < 120u ) + preferred_time = 120u; /* sanity */ + if (lease_time == 0xffffffff || (preferred_time != 0xffffffff && preferred_time < lease_time)) + lease_time = preferred_time; + } + + if (lease_time < min_time) + min_time = lease_time; + + /* May fail to create lease */ + if (!lease && make_lease) + lease = lease6_allocate(addrp, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA); + + if (lease) + { + lease_set_expires(lease, lease_time, now); + lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len); + if (hostname && ia_type == OPTION6_IA_NA) + lease_set_hostname(lease, hostname, hostname_auth, domain); + } + + if (lease || !make_lease) + { + o1 = new_opt6(OPTION6_IAADDR); + put_opt6(addrp, sizeof(*addrp)); + put_opt6_long(lease_time); + put_opt6_long(lease_time); + end_opt6(o1); + + log6_packet( make_lease ? "DHCPREPLY" : "DHCPADVERTISE", + clid, clid_len, addrp, xid, iface_name, hostname); + } + + } + } + + + if (!ia_option || + !(ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))) + { + if (address_assigned) + { + if (ia_type == OPTION6_IA_NA) + { + /* go back an fill in fields in IA_NA option */ + unsigned int t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2; + unsigned int t2 = min_time == 0xffffffff ? 0xffffffff : (min_time/8) * 7; + int sav = save_counter(t1cntr); + put_opt6_long(t1); + put_opt6_long(t2); + save_counter(sav); + } + } + else + { + /* no address, return erro */ + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOADDRS); + put_opt6_string("No addresses available"); + end_opt6(o1); + } + + end_opt6(o); + + break; + } + } + } + + break; + } + + case DHCP6RENEW: + { + /* set reply message type */ + *outmsgtypep = DHCP6REPLY; + + log6_packet("DHCPRENEW", clid, clid_len, NULL, xid, iface_name, NULL); + + for (opt = packet_options; opt; opt = opt6_next(opt, end)) + { + int ia_type = opt6_type(opt); + void *ia_option, *ia_end; + unsigned int min_time = 0xffffffff; + int t1cntr = 0, iacntr; + unsigned int iaid; + + if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA) + continue; + + if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12) + continue; + + if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4) + continue; + + iaid = opt6_uint(opt, 0, 4); + + o = new_opt6(ia_type); + put_opt6_long(iaid); + if (ia_type == OPTION6_IA_NA) + { + /* save pointer */ + t1cntr = save_counter(-1); + /* so we can fill these in later */ + put_opt6_long(0); + put_opt6_long(0); + } + + iacntr = save_counter(-1); + + /* reset "USED" flags on leases */ + lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL); + + ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4); + ia_end = opt6_ptr(opt, opt6_len(opt)); + + for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24); + ia_option; + ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { + struct dhcp_lease *lease = NULL; + struct in6_addr *req_addr = opt6_ptr(ia_option, 0); + u32 preferred_time = opt6_uint(ia_option, 16, 4); + unsigned int lease_time; + struct dhcp_context *this_context; + struct dhcp_config *valid_config = config; + + /* don't use a config to set lease time if it specifies an address which isn't this. */ + if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, req_addr, IN6ADDRSZ) != 0) + valid_config = NULL; + + if (!(lease = lease6_find(clid, clid_len, + ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, + iaid, req_addr))) + { + /* If the server cannot find a client entry for the IA the server + returns the IA containing no addresses with a Status Code option set + to NoBinding in the Reply message. */ + save_counter(iacntr); + t1cntr = 0; + + log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, "lease not found"); + + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOBINDING); + put_opt6_string("No binding found"); + end_opt6(o1); + break; + } + + if (!address6_available(context, req_addr, tags) || + !(this_context = narrow_context6(context, req_addr, tags))) + lease_time = 0; + else + { + /* get tags from context if we've not used it before */ + if (this_context->netid.next != &this_context->netid && this_context->netid.net) + { + this_context->netid.next = context_tags; + context_tags = &this_context->netid; + } + + lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time; + + if (preferred_time < 120u ) + preferred_time = 120u; /* sanity */ + if (lease_time == 0xffffffff || (preferred_time != 0xffffffff && preferred_time < lease_time)) + lease_time = preferred_time; + + lease_set_expires(lease, lease_time, now); + if (ia_type == OPTION6_IA_NA && hostname) + lease_set_hostname(lease, hostname, hostname_auth, domain); + + if (lease_time < min_time) + min_time = lease_time; + } + + log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, hostname); + + o1 = new_opt6(OPTION6_IAADDR); + put_opt6(req_addr, sizeof(*req_addr)); + put_opt6_long(lease_time); + put_opt6_long(lease_time); + end_opt6(o1); + } + + if (t1cntr != 0) + { + /* go back an fill in fields in IA_NA option */ + unsigned int t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2; + unsigned int t2 = min_time == 0xffffffff ? 0xffffffff : (min_time/8) * 7; + int sav = save_counter(t1cntr); + put_opt6_long(t1); + put_opt6_long(t2); + save_counter(sav); + } + + end_opt6(o); + } + break; + + } + + case DHCP6CONFIRM: + { + /* set reply message type */ + *outmsgtypep = DHCP6REPLY; + + log6_packet("DHCPCONFIRM", clid, clid_len, NULL, xid, iface_name, NULL); + + for (opt = packet_options; opt; opt = opt6_next(opt, end)) + { + int ia_type = opt6_type(opt); + void *ia_option, *ia_end; + + if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA) + continue; + + if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12) + continue; + + if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4) + continue; + + ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4); + ia_end = opt6_ptr(opt, opt6_len(opt)); + + for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24); + ia_option; + ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { + struct in6_addr *req_addr = opt6_ptr(ia_option, 0); + + if (!address6_available(context, req_addr, tags)) + { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOTONLINK); + put_opt6_string("Confirm failed"); + end_opt6(o1); + return 1; + } + + log6_packet("DHCPREPLY", clid, clid_len, req_addr, xid, iface_name, hostname); + } + } + + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6SUCCESS ); + put_opt6_string("All addresses still on link"); + end_opt6(o1); + return 1; + } + + case DHCP6IREQ: + { + *outmsgtypep = DHCP6REPLY; + break; + } + + + case DHCP6RELEASE: + { + /* set reply message type */ + *outmsgtypep = DHCP6REPLY; + + log6_packet("DHCPRELEASE", clid, clid_len, NULL, xid, iface_name, NULL); + + for (opt = packet_options; opt; opt = opt6_next(opt, end)) + { + int iaid, ia_type = opt6_type(opt); + void *ia_option, *ia_end; + int made_ia = 0; + + if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA) + continue; + + if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12) + continue; + + if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4) + continue; + + iaid = opt6_uint(opt, 0, 4); + ia_end = opt6_ptr(opt, opt6_len(opt)); + ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4); + + /* reset "USED" flags on leases */ + lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL); + + for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24); + ia_option; + ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { + struct dhcp_lease *lease; + + if ((lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, + iaid, opt6_ptr(ia_option, 0)))) + lease_prune(lease, now); + else + { + if (!made_ia) + { + o = new_opt6(ia_type); + put_opt6_long(iaid); + if (ia_type == OPTION6_IA_NA) + { + put_opt6_long(0); + put_opt6_long(0); + } + made_ia = 1; + } + + o1 = new_opt6(OPTION6_IAADDR); + put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ); + put_opt6_long(0); + put_opt6_long(0); + end_opt6(o1); + } + } + + if (made_ia) + { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOBINDING); + put_opt6_string("No binding found"); + end_opt6(o1); + + end_opt6(o); + } + } + + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6SUCCESS); + put_opt6_string("Release received"); + end_opt6(o1); + + return 1; + } + } + + + /* filter options based on tags, those we want get DHOPT_TAGOK bit set */ + tagif = option_filter(tags, context_tags, daemon->dhcp_opts6); + + oro = opt6_find(packet_options, end, OPTION6_ORO, 0); + + for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) + { + /* netids match and not encapsulated? */ + if (!(opt_cfg->flags & DHOPT_TAGOK)) + continue; + + if (!(opt_cfg->flags & DHOPT_FORCE) && oro) + { + for (i = 0; i < opt6_len(oro) - 1; i += 2) + if (opt6_uint(oro, i, 2) == (unsigned)opt_cfg->opt) + break; + + /* option not requested */ + if (i >= opt6_len(oro) - 1) + continue; + } + + if (opt_cfg->opt == OPTION6_DNS_SERVER) + { + done_dns = 1; + if (opt_cfg->len == 0) + continue; + } + + o = new_opt6(opt_cfg->opt); + /* Maye be empty */ + if (opt_cfg->val) + put_opt6(opt_cfg->val, opt_cfg->len); + end_opt6(o); + + } + + if (!done_dns) + { + o = new_opt6(OPTION6_DNS_SERVER); + put_opt6(&context->local6, IN6ADDRSZ); + end_opt6(o); + } + + /* handle vendor-identifying vendor-encapsulated options, + dhcp-option = vi-encap:13,17,....... */ + for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) + opt_cfg->flags &= ~DHOPT_ENCAP_DONE; + + + if (oro) + for (i = 0; i < opt6_len(oro) - 1; i += 2) + if (opt6_uint(oro, i, 2) == OPTION6_VENDOR_OPTS) + do_encap = 1; + + for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) + { + if (opt_cfg->flags & DHOPT_RFC3925) + { + int found = 0; + struct dhcp_opt *oc; + + if (opt_cfg->flags & DHOPT_ENCAP_DONE) + continue; + + for (oc = daemon->dhcp_opts6; oc; oc = oc->next) + { + oc->flags &= ~DHOPT_ENCAP_MATCH; + + if (!(oc->flags & DHOPT_RFC3925) || opt_cfg->u.encap != oc->u.encap) + continue; + + oc->flags |= DHOPT_ENCAP_DONE; + if (match_netid(oc->netid, tagif, 1)) + { + /* option requested/forced? */ + if (!oro || do_encap || (oc->flags & DHOPT_FORCE)) + { + oc->flags |= DHOPT_ENCAP_MATCH; + found = 1; + } + } + } + + if (found) + { + o = new_opt6(OPTION6_VENDOR_OPTS); + put_opt6_long(opt_cfg->u.encap); + + for (oc = daemon->dhcp_opts6; oc; oc = oc->next) + if (oc->flags & DHOPT_ENCAP_MATCH) + { + o1 = new_opt6(oc->opt); + put_opt6(oc->val, oc->len); + end_opt6(o1); + } + end_opt6(o); + } + } + } + + if (hostname) + { + unsigned char *p; + size_t len = strlen(hostname); + + if (domain) + len += strlen(domain) + 1; + + o = new_opt6(OPTION6_FQDN); + p = expand(len + 3); + *(p++) = fqdn_flags; + p = do_rfc1035_name(p, hostname); + if (domain) + p = do_rfc1035_name(p, domain); + *p = 0; + end_opt6(o); + } + + + /* logging */ + if (option_bool(OPT_LOG_OPTS) && oro) + { + char *q = daemon->namebuff; + for (i = 0; i < opt6_len(oro) - 1; i += 2) + { + char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0); + q += snprintf(q, MAXDNAME - (q - daemon->namebuff), + "%d%s%s%s", + opt6_uint(oro, i, 2), + strlen(s) != 0 ? ":" : "", + s, + (i > opt6_len(oro) - 3) ? "" : ", "); + if ( i > opt6_len(oro) - 3 || (q - daemon->namebuff) > 40) + { + q = daemon->namebuff; + my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), xid, daemon->namebuff); + } + } + } + + log_tags(tagif, xid); + + if (option_bool(OPT_LOG_OPTS)) + { + int end_opts = save_counter(-1); + + /* must have created at least one option */ + if (start_opts != end_opts) + for (opt = daemon->outpacket.iov_base + start_opts; opt; opt = opt6_next(opt, daemon->outpacket.iov_base + end_opts)) + { + int offset = 0; + char *optname; + + /* account for flag byte on FQDN */ + if (opt6_type(opt) == OPTION6_FQDN) + offset = 1; + + optname = option_string(AF_INET6, opt6_type(opt), opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME); + + my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s", + xid, opt6_len(opt), opt6_type(opt), optname, daemon->namebuff); + } + } + + return 1; + +} + +static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string) +{ + /* avoid buffer overflow */ + if (clid_len > 100) + clid_len = 100; + + print_mac(daemon->namebuff, clid, clid_len); + + if (addr) + { + inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, 255); + strcat(daemon->dhcp_buff2, " "); + } + else + daemon->dhcp_buff2[0] = 0; + + if(option_bool(OPT_LOG_OPTS)) + my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s %s%s", + xid, + type, + iface, + daemon->namebuff, + daemon->dhcp_buff2, + string ? string : ""); + else + my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s %s%s", + type, + iface, + daemon->namebuff, + daemon->dhcp_buff2, + string ? string : ""); +} + +static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize) { u16 opt, opt_len; void *start; @@ -81,7 +1071,7 @@ void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsiz } } -void *opt6_next(void *opts, void *end) +static void *opt6_next(void *opts, void *end) { u16 opt_len; @@ -96,10 +1086,6 @@ void *opt6_next(void *opts, void *end) return opts + opt_len; } - -#define opt6_len(opt) (opt6_uint(opt, -2, 2)) -#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)])) - static unsigned int opt6_uint(unsigned char *opt, int offset, int size) { @@ -112,57 +1098,7 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size) ret = (ret << 8) | *p++; return ret; -} - -/* - set of routines to build arbitrarily nested options: eg - - int o = new_opt(OPTION_IA_NA); - put_opt_long(IAID); - put_opt_long(T1); - put_opt_long(T2); - int o1 = new_opt(OPTION_IAADDR); - put_opt(o1, &addr, sizeof(addr)); - put_opt_long(preferred_lifetime); - put_opt_long(valid_lifetime); - finalise_opt(o1); - finalise_opt(o); - - - to go back and fill in fields - - int o = new_opt(OPTION_IA_NA); - put_opt_long(IAID); - int t1sav = save_counter(-1); - put_opt_long(0); - put_opt_long(0); - - int o1 = new_opt(OPTION_IAADDR); - put_opt(o1, &addr, sizeof(addr)); - put_opt_long(o1, preferred_lifetime); - put_opt_long(o1, valid_lifetime); - finalise_opt(o1); - - int sav = save_counter(t1sav); - put_opt_long(T1); - save_counter(sav); - finalise_opt(o); - - - to abandon an option - - int o = new_opt(OPTION_IA_NA); - put_opt_long(IAID); - put_opt_long(T1); - put_opt_long(T2); - if (err) - save_counter(o); - -*/ - - - - +} static void end_opt6(int container) { @@ -172,7 +1108,7 @@ static void end_opt6(int container) PUTSHORT(len, p); } -static int save_counter(int newval) +static int save_counter(int newval) { int ret = outpacket_counter; if (newval != -1) @@ -181,8 +1117,6 @@ static int save_counter(int newval) return ret; } - - static void *expand(size_t headroom) { void *ret; @@ -211,14 +1145,11 @@ static int new_opt6(int opt) return ret; } - - - static void *put_opt6(void *data, size_t len) { void *p; - if (data && (p = expand(len))) + if ((p = expand(len))) memcpy(p, data, len); return p; @@ -228,7 +1159,7 @@ static void put_opt6_long(unsigned int val) { void *p; - if (( p = expand(4))) + if ((p = expand(4))) PUTLONG(val, p); } @@ -240,231 +1171,9 @@ static void put_opt6_short(unsigned int val) PUTSHORT(val, p); } -static void put_opt6_byte(unsigned int val) -{ - void *p; - - if ((p = expand(1))) - *((unsigned char *)p) = val; -} - static void put_opt6_string(char *s) { put_opt6(s, strlen(s)); } - -size_t dhcp6_reply(struct dhcp_context *context, size_t sz, time_t now) -{ - void *packet_options = ((void *)daemon->dhcp_packet.iov_base) + 4; - void *end = ((void *)daemon->dhcp_packet.iov_base) + sz; - void *na_option, *na_end; - void *opt, *p; - int o, msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base); - int make_lease = (msg_type == DHCP6REQUEST || opt6_find(packet_options, end, OPTION6_RAPID_COMMIT, 0)); - unsigned char *clid; - int clid_len; - struct dhcp_netid *tags; - - /* copy over transaction-id */ - memcpy(daemon->outpacket.iov_base, daemon->dhcp_packet.iov_base, 4); - /* set reply message type */ - *((unsigned char *)daemon->outpacket.iov_base) = make_lease ? DHCP6REPLY : DHCP6ADVERTISE; - /* skip message type and transaction-id */ - outpacket_counter = 4; - - if (!(opt = opt6_find(packet_options, end, OPTION6_CLIENT_ID, 1))) - return 0; - - clid = opt6_ptr(opt, 0); - clid_len = opt6_len(opt); - o = new_opt6(OPTION6_CLIENT_ID); - put_opt6(clid, clid_len); - end_opt6(o); - - /* server-id must match except for SOLICIT meesages */ - if (msg_type != DHCP6SOLICIT && - (!(opt = opt6_find(packet_options, end, OPTION6_SERVER_ID, 1)) || - opt6_len(opt) != daemon->duid_len || - memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) - return 0; - - o = new_opt6(OPTION6_SERVER_ID); - put_opt6(daemon->duid, daemon->duid_len); - end_opt6(o); - - switch (msg_type) - { - case DHCP6SOLICIT: - case DHCP6REQUEST: - { - u16 *req_options = NULL; - - for (opt = opt6_find(packet_options, end, OPTION6_IA_NA, 12); - opt; - opt = opt6_find(opt6_next(opt, end), end, OPTION6_IA_NA, 12)) - { - void *ia_end = opt6_ptr(opt, opt6_len(opt)); - void *ia_option = opt6_find(opt6_ptr(opt, 12), ia_end, OPTION6_IAADDR, 24); - unsigned int min_time = 0xffffffff; - int t1cntr; - unsigned int iaid = opt6_uint(opt, 0, 4); - int address_assigned = 0; - struct dhcp_lease *lease = NULL; - - o = new_opt6(OPTION6_IA_NA); - put_opt6_long(iaid); - /* save pointer */ - t1cntr = save_counter(-1); - /* so we can fill these in later */ - put_opt6_long(0); - put_opt6_long(0); - - - while (1) - { - struct in6_addr alloced_addr, *addrp = NULL; - - if (ia_option) - { - struct in6_addr *req_addr = opt6_ptr(ia_option, 0); - u32 preferred_lifetime = opt6_uint(ia_option, 16, 4); - u32 valid_lifetime = opt6_uint(ia_option, 20, 4); - - if ((lease = lease6_find_by_addr(req_addr, 128, 0))) - { - /* check if existing lease for host */ - if (clid_len == lease->clid_len && - memcmp(clid, lease->clid, clid_len) == 0) - addrp = req_addr; - } - else if (address6_available(context, req_addr, tags)) - addrp = req_addr; - } - else - { - /* must have an address to CONFIRM */ - if (msg_type == DHCP6REQUEST) - return 0; - - /* existing lease */ - if ((lease = lease6_find_by_client(clid, clid_len, iaid))) - addrp = (struct in6_addr *)&lease->hwaddr; - else if (address6_allocate(context, clid, clid_len, tags, &alloced_addr)) - addrp = &alloced_addr; - } - - if (addrp) - { - unsigned int lease_time; - address_assigned = 1; - - context = narrow_context6(context, addrp, tags); - lease_time = context->lease_time; - if (lease_time < min_time) - min_time = lease_time; - - /* May fail to create lease */ - if (!lease && make_lease) - lease = lease6_allocate(addrp); - - if (lease) - { - lease_set_expires(lease, lease_time, now); - lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len); - } - - if (lease || !make_lease) - { - int o1 = new_opt6(OPTION6_IAADDR); - put_opt6(addrp, sizeof(*addrp)); - put_opt6_long(lease_time); - put_opt6_long(lease_time); - end_opt6(o1); - } - - } - - - if (!ia_option || - !(ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))) - { - if (address_assigned) - { - /* go back an fill in fields in IA_NA option */ - unsigned int t1 = min_time == 0xffffffff ? 0xffffffff : min_time/2; - unsigned int t2 = min_time == 0xffffffff ? 0xffffffff : (min_time/8) * 7; - int sav = save_counter(t1cntr); - put_opt6_long(t1); - put_opt6_long(t2); - save_counter(sav); - } - else - { - /* no address, return erro */ - int o1 = new_opt6(OPTION6_STATUS_CODE); - put_opt6_short(DHCP6NOADDRS); - put_opt6_string("No addresses available"); - end_opt6(o1); - } - - end_opt6(o); - break; - } - } - } - - /* same again for TA */ - for (opt = packet_options; opt; opt = opt6_find(opt6_next(opt, end), end, OPTION6_IA_TA, 4)) - { - } - - do_options6(context, opt6_find(packet_options, end, OPTION6_ORO, 0)); - - - } - - } - - return outpacket_counter; - -} - - -/* TODO tags to select options, and encapsualted options. */ -static void do_options6(struct dhcp_context *context, void *oro) -{ - unsigned char *req_options = NULL; - int req_options_len, i, o; - struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts6; - - if (oro) - { - req_options = opt6_ptr(oro, 0); - req_options_len = opt6_len(oro); - } - - for (opt = config_opts; opt; opt = opt->next) - { - if (req_options) - { - /* required options are not aligned... */ - for (i = 0; i < req_options_len - 1; i += 2) - if (((req_options[i] << 8) | req_options[i+1]) == opt->opt) - break; - - /* option not requested */ - if (i == req_options_len) - continue; - } - - o = new_opt6(opt->opt); - put_opt6(opt->val, opt->len); - end_opt6(o); - } -} - - - #endif - From 70c5e3e0761b788dc3c2717fb31f26e1cb08b853 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 6 Feb 2012 22:05:15 +0000 Subject: [PATCH 2/7] DHCPDECLINE handling, domain handling, more complete address selection. --- src/dnsmasq.h | 2 +- src/lease.c | 13 +++-- src/rfc2131.c | 19 +++---- src/rfc3315.c | 152 +++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 156 insertions(+), 30 deletions(-) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index dfe8cfb..5ac1e99 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -936,7 +936,7 @@ struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 add #endif void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, unsigned char *clid, int hw_len, int hw_type, int clid_len); -void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain); +void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain); void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now); void lease_set_interface(struct dhcp_lease *lease, int interface); struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type, diff --git a/src/lease.c b/src/lease.c index ef3c6cc..691ee61 100644 --- a/src/lease.c +++ b/src/lease.c @@ -146,10 +146,10 @@ void lease_init(time_t now) { #ifdef HAVE_DHCP6 if (v6pass) - lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr)); + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL); else #endif - lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr)); + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL); } /* set these correctly: the "old" events are generated later from the startup synthesised SIGHUP. */ @@ -216,9 +216,9 @@ void lease_update_from_configs(void) lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && (config->flags & CONFIG_NAME) && (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr)) - lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr)); + lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr), NULL); else if ((name = host_from_dns(lease->addr))) - lease_set_hostname(lease, name, 1, get_domain(lease->addr)); /* updates auth flag only */ + lease_set_hostname(lease, name, 1, get_domain(lease->addr), NULL); /* updates auth flag only */ } static void ourprintf(int *errp, char *format, ...) @@ -669,10 +669,13 @@ static void kill_name(struct dhcp_lease *lease) lease->hostname = lease->fqdn = NULL; } -void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain) +void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain) { struct dhcp_lease *lease_tmp; char *new_name = NULL, *new_fqdn = NULL; + + if (config_domain && (!domain || !hostname_isequal(domain, config_domain))) + my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, name); if (lease->hostname && name && hostname_isequal(lease->hostname, name)) { diff --git a/src/rfc2131.c b/src/rfc2131.c index fcd9bf8..d897087 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -46,7 +46,7 @@ static void do_options(struct dhcp_context *context, unsigned char *real_end, unsigned char *req_options, char *hostname, - char *domain, char *config_domain, + char *config_domain, struct dhcp_netid *netid, struct in_addr subnet_addr, unsigned char fqdn_flags, @@ -487,7 +487,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0); if (hostname) - lease_set_hostname(lease, hostname, 1, get_domain(lease->addr)); + lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain); /* infinite lease unless nailed in dhcp-host line. */ lease_set_expires(lease, have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff, @@ -496,7 +496,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, clear_packet(mess, end); do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), - domain, netid, subnet_addr, 0, 0, 0, NULL, 0, now); + netid, subnet_addr, 0, 0, 0, NULL, 0, now); } } @@ -1022,7 +1022,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, option_put(mess, end, OPTION_T2, 4, (time*7)/8); } do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), - domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); + netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); return dhcp_packet_size(mess, agent_id, real_end); @@ -1307,7 +1307,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, } if (hostname) - lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr)); + lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain); lease_set_expires(lease, time, now); lease_set_interface(lease, int_index); @@ -1331,7 +1331,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz); } do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), - domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); + netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); } return dhcp_packet_size(mess, agent_id, real_end); @@ -1391,7 +1391,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, } do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr), - domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); + netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); *is_inform = 1; /* handle reply differently */ return dhcp_packet_size(mess, agent_id, real_end); @@ -2144,7 +2144,7 @@ static void do_options(struct dhcp_context *context, unsigned char *end, unsigned char *req_options, char *hostname, - char *domain, char *config_domain, + char *domain, struct dhcp_netid *netid, struct in_addr subnet_addr, unsigned char fqdn_flags, @@ -2166,9 +2166,6 @@ static void do_options(struct dhcp_context *context, /* filter options based on tags, those we want get DHOPT_TAGOK bit set */ tagif = option_filter(netid, &context->netid, config_opts); - if (config_domain && (!domain || !hostname_isequal(domain, config_domain))) - my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname); - /* logging */ if (option_bool(OPT_LOG_OPTS) && req_options) { diff --git a/src/rfc3315.c b/src/rfc3315.c index 67aa624..43cb832 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -20,6 +20,7 @@ #ifdef HAVE_DHCP6 static size_t outpacket_counter; + static void end_opt6(int container); static int save_counter(int newval); static void *expand(size_t headroom); @@ -179,7 +180,8 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, unsigned char *clid = NULL; int clid_len = 0, start_opts; struct dhcp_netid *tagif, *context_tags = NULL; - char *client_hostname= NULL, *hostname = NULL, *domain= NULL; + char *client_hostname= NULL, *hostname = NULL; + char *domain = NULL, *send_domain = NULL; struct dhcp_config *config = NULL; struct dhcp_netid known_id; int done_dns = 0, hostname_auth = 0, do_encap = 0; @@ -326,8 +328,7 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, else if (client_hostname) { domain = strip_hostname(client_hostname); - /* TODO verify legal domain */ - + if (strlen(client_hostname) != 0) { hostname = client_hostname; @@ -364,6 +365,9 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, switch (msg_type) { + default: + return 0; + case DHCP6SOLICIT: case DHCP6REQUEST: { @@ -463,14 +467,32 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, /* Don't used configured addresses for temporary leases. */ if (have_config(config, CONFIG_ADDR6) && !used_config && ia_type == OPTION6_IA_NA) { + struct dhcp_lease *ltmp = lease6_find_by_addr(&config->addr6, 128, 0); + used_config = 1; - addrp = &config->addr6; + inet_ntop(AF_INET6, &config->addr6, daemon->addrbuff, ADDRSTRLEN); + + if (ltmp && ltmp->clid && + (ltmp->clid_len != clid_len || memcmp(ltmp->clid, clid, clid_len) != 0)) + my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"), + daemon->addrbuff, print_mac(daemon->namebuff, ltmp->clid, ltmp->clid_len)); + else if (have_config(config, CONFIG_DECLINED) && + difftime(now, config->decline_time) < (float)DECLINE_BACKOFF) + my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), + daemon->addrbuff); + else + addrp = &config->addr6; } + /* existing lease */ - else if ((lease = lease6_find(clid, clid_len, - ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL))) + if (!addrp && + (lease = lease6_find(clid, clid_len, + ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL)) && + address6_available(context, (struct in6_addr *)&lease->hwaddr, tags) && + !config_find_by_address6(daemon->dhcp_conf, (struct in6_addr *)&lease->hwaddr)) addrp = (struct in6_addr *)&lease->hwaddr; - else if (address6_allocate(context, clid, clid_len, serial++, tags, &alloced_addr)) + + if (!addrp && address6_allocate(context, clid, clid_len, serial++, tags, &alloced_addr)) addrp = &alloced_addr; } @@ -518,8 +540,16 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, lease_set_expires(lease, lease_time, now); lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len); if (hostname && ia_type == OPTION6_IA_NA) - lease_set_hostname(lease, hostname, hostname_auth, domain); + { + char *addr_domain = get_domain6(addrp); + if (!send_domain) + send_domain = addr_domain; + lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain); + } } + else if (!send_domain) + send_domain = get_domain6(addrp); + if (lease || !make_lease) { @@ -672,7 +702,12 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, lease_set_expires(lease, lease_time, now); if (ia_type == OPTION6_IA_NA && hostname) - lease_set_hostname(lease, hostname, hostname_auth, domain); + { + char *addr_domain = get_domain6(req_addr); + if (!send_domain) + send_domain = addr_domain; + lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain); + } if (lease_time < min_time) min_time = lease_time; @@ -839,6 +874,97 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, return 1; } + + case DHCP6DECLINE: + { + /* set reply message type */ + *outmsgtypep = DHCP6REPLY; + + log6_packet("DHCPDECLINE", clid, clid_len, NULL, xid, iface_name, NULL); + + for (opt = packet_options; opt; opt = opt6_next(opt, end)) + { + int iaid, ia_type = opt6_type(opt); + void *ia_option, *ia_end; + int made_ia = 0; + + if (ia_type != OPTION6_IA_NA && ia_type != OPTION6_IA_TA) + continue; + + if (ia_type == OPTION6_IA_NA && opt6_len(opt) < 12) + continue; + + if (ia_type == OPTION6_IA_TA && opt6_len(opt) < 4) + continue; + + iaid = opt6_uint(opt, 0, 4); + ia_end = opt6_ptr(opt, opt6_len(opt)); + ia_option = opt6_ptr(opt, ia_type == OPTION6_IA_NA ? 12 : 4); + + /* reset "USED" flags on leases */ + lease6_find(NULL, 0, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL); + + for (ia_option = opt6_find(ia_option, ia_end, OPTION6_IAADDR, 24); + ia_option; + ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { + struct dhcp_lease *lease; + struct in6_addr *addrp = opt6_ptr(ia_option, 0); + + if (have_config(config, CONFIG_ADDR6) && + memcmp(&config->addr6, addrp, IN6ADDRSZ) == 0) + { + prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF); + inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), + daemon->addrbuff, daemon->dhcp_buff); + config->flags |= CONFIG_DECLINED; + config->decline_time = now; + } + else + /* make sure this host gets a different address next time. */ + for (; context; context = context->current) + context->addr_epoch++; + + if ((lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, + iaid, opt6_ptr(ia_option, 0)))) + lease_prune(lease, now); + else + { + if (!made_ia) + { + o = new_opt6(ia_type); + put_opt6_long(iaid); + if (ia_type == OPTION6_IA_NA) + { + put_opt6_long(0); + put_opt6_long(0); + } + made_ia = 1; + } + + o1 = new_opt6(OPTION6_IAADDR); + put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ); + put_opt6_long(0); + put_opt6_long(0); + end_opt6(o1); + } + } + + if (made_ia) + { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOBINDING); + put_opt6_string("No binding found"); + end_opt6(o1); + + end_opt6(o); + } + + } + return 1; + } + } @@ -948,15 +1074,15 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, unsigned char *p; size_t len = strlen(hostname); - if (domain) - len += strlen(domain) + 1; + if (send_domain) + len += strlen(send_domain) + 1; o = new_opt6(OPTION6_FQDN); p = expand(len + 3); *(p++) = fqdn_flags; p = do_rfc1035_name(p, hostname); - if (domain) - p = do_rfc1035_name(p, domain); + if (send_domain) + p = do_rfc1035_name(p, send_domain); *p = 0; end_opt6(o); } From d74942a03d9858eb5aa477b97fe6a3a5ebbf5216 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Tue, 7 Feb 2012 20:51:56 +0000 Subject: [PATCH 3/7] IPv6 address range parsing for --domain. Counted string DHCP option type printing --- src/cache.c | 14 +++--- src/dnsmasq.h | 11 ++--- src/option.c | 129 ++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 126 insertions(+), 28 deletions(-) diff --git a/src/cache.c b/src/cache.c index 1226165..896132b 100644 --- a/src/cache.c +++ b/src/cache.c @@ -913,7 +913,8 @@ char *get_domain(struct in_addr addr) struct cond_domain *c; for (c = daemon->cond_domain; c; c = c->next) - if (ntohl(addr.s_addr) >= ntohl(c->start.s_addr) && + if (!c->is6 && + ntohl(addr.s_addr) >= ntohl(c->start.s_addr) && ntohl(addr.s_addr) <= ntohl(c->end.s_addr)) return c->domain; @@ -924,14 +925,15 @@ char *get_domain(struct in_addr addr) #ifdef HAVE_IPV6 char *get_domain6(struct in6_addr *addr) { - struct cond_domain6 *c; + struct cond_domain *c; u64 addrpart = addr6part(addr); - for (c = daemon->cond_domain6; c; c = c->next) - if (is_same_net6(addr, &c->start, 64) && - addrpart >= addr6part(&c->start) && - addrpart <= addr6part(&c->end)) + for (c = daemon->cond_domain; c; c = c->next) + if (c->is6 && + is_same_net6(addr, &c->start6, 64) && + addrpart >= addr6part(&c->start6) && + addrpart <= addr6part(&c->end6)) return c->domain; return daemon->domain_suffix; diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 5ac1e99..722ab09 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -588,15 +588,13 @@ struct dhcp_bridge { struct cond_domain { char *domain; struct in_addr start, end; +#ifdef HAVE_IPV6 + struct in6_addr start6, end6; +#endif + int is6; struct cond_domain *next; }; -struct cond_domain6 { - char *domain; - struct in6_addr start, end; - struct cond_domain6 *next; -}; - struct dhcp_context { unsigned int lease_time, addr_epoch; struct in_addr netmask, broadcast; @@ -683,7 +681,6 @@ extern struct daemon { int group_set, osport; char *domain_suffix; struct cond_domain *cond_domain; - struct cond_domain6 *cond_domain6; char *runfile; char *lease_change_command; struct iname *if_names, *if_addrs, *if_except, *dhcp_except; diff --git a/src/option.c b/src/option.c index c807a8f..fbe1204 100644 --- a/src/option.c +++ b/src/option.c @@ -543,6 +543,30 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, buf[j++] = '.'; } } + else if ((ot[o].size & OT_CSTRING)) + { + int k, len; + unsigned char *p; + + i = 0, j = 0; + while (1) + { + p = &val[i]; + GETSHORT(len, p); + for (k = 0; k < len && j < buf_len; k++) + { + char c = *p++; + if (isprint((int)c)) + buf[j++] = c; + } + i += len +2; + if (i >= opt_len) + break; + + if (j < buf_len) + buf[j++] = ','; + } + } #endif else nodecode = 1; @@ -1628,14 +1652,15 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) unhide_metas(comma); if ((netpart = split_chr(comma, '/'))) { - int msize, mask; + int msize; + arg = split(netpart); - if ((new->start.s_addr = inet_addr(comma)) == (in_addr_t)-1 || - !atoi_check(netpart, &msize)) + if (!atoi_check(netpart, &msize)) option = '?'; - else + else if (inet_pton(AF_INET, comma, &new->start)) { - mask = (1 << (32 - msize)) - 1; + int mask = (1 << (32 - msize)) - 1; + new->is6 = 0; new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask); new->end.s_addr = new->start.s_addr | htonl(mask); if (arg) @@ -1677,19 +1702,93 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) } } } - } - else if ((arg = split(comma))) - { - if ((new->start.s_addr = inet_addr(comma)) == (in_addr_t)-1 || - (new->end.s_addr = inet_addr(arg)) == (in_addr_t)-1) +#ifdef HAVE_IPV6 + else if (inet_pton(AF_INET6, comma, &new->start6)) + { + u64 mask = (1LLU << (128 - msize)) - 1LLU; + u64 addrpart = addr6part(&new->start6); + new->is6 = 1; + + /* prefix==64 overflows the mask calculation above */ + if (msize == 64) + mask = (u64)-1LL; + + new->end6 = new->start6; + setaddr6part(&new->start6, addrpart & ~mask); + setaddr6part(&new->end6, addrpart | mask); + + if (msize < 64) + option = '?'; + else if (arg) + { + /* generate the equivalent of + local=// + local=/xxx.yyy.zzz.ip6.arpa/ */ + + if (strcmp(arg, "local") != 0 || (msize & 4 != 0)) + option = '?'; + else + { + struct server *serv = opt_malloc(sizeof(struct server)); + in_addr_t a = ntohl(new->start.s_addr) >> 8; + char *p; + + memset(serv, 0, sizeof(struct server)); + serv->domain = d; + serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; + serv->next = daemon->servers; + daemon->servers = serv; + + serv = opt_malloc(sizeof(struct server)); + memset(serv, 0, sizeof(struct server)); + p = serv->domain = opt_malloc(73); /* strlen("32*ip6.arpa")+1 */ + + for (i = msize-1; i >= 0; i -= 4) + { + int dig = ((unsigned char *)&new->start6)[i>>3]; + p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4); + } + p += sprintf(p, "ip6.arpa"); + + serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; + serv->next = daemon->servers; + daemon->servers = serv; + } + } + } +#endif + else option = '?'; } - else if ((new->start.s_addr = new->end.s_addr = inet_addr(comma)) == (in_addr_t)-1) - option = '?'; + else + { + arg = split(comma); + if (inet_pton(AF_INET, comma, &new->start)) + { + new->is6 = 0; + if (!arg) + new->end.s_addr = new->start.s_addr; + else if (!inet_pton(AF_INET, arg, &new->end)) + option = '?'; + } +#ifdef HAVE_IPV6 + else if (inet_pton(AF_INET6, comma, &new->start6)) + { + new->is6 = 1; + if (!arg) + memcpy(&new->end6, &new->start6, IN6ADDRSZ); + else if (!inet_pton(AF_INET6, arg, &new->end6)) + option = '?'; + } +#endif + else + option = '?'; - new->domain = d; - new->next = daemon->cond_domain; - daemon->cond_domain = new; + new->domain = d; + new->next = daemon->cond_domain; + daemon->cond_domain = new; + + } } else daemon->domain_suffix = d; From 3634c54e8df8b0f3a673f3db8fd03aec8fb84736 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 8 Feb 2012 14:22:37 +0000 Subject: [PATCH 4/7] dhcp-ignore and dhcp-match implemented for DHCPv6 now. --- src/dhcp-common.c | 30 +++++++++++++++++++++ src/dnsmasq.h | 3 ++- src/option.c | 5 ++++ src/rfc2131.c | 32 ----------------------- src/rfc3315.c | 66 ++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 100 insertions(+), 36 deletions(-) diff --git a/src/dhcp-common.c b/src/dhcp-common.c index 3c5349d..bd5be6c 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -209,5 +209,35 @@ void log_tags(struct dhcp_netid *netid, u32 xid) } } +int match_bytes(struct dhcp_opt *o, unsigned char *p, int len) +{ + int i; + + if (o->len > len) + return 0; + + if (o->len == 0) + return 1; + + if (o->flags & DHOPT_HEX) + { + if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask)) + return 1; + } + else + for (i = 0; i <= (len - o->len); ) + { + if (memcmp(o->val, p + i, o->len) == 0) + return 1; + + if (o->flags & DHOPT_STRING) + i++; + else + i += o->len; + } + + return 0; +} + #endif diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 722ab09..f83f440 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -695,7 +695,7 @@ extern struct daemon { struct hostsfile *addn_hosts; struct dhcp_context *dhcp, *dhcp6; struct dhcp_config *dhcp_conf; - struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6; + struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6; struct dhcp_vendor *dhcp_vendors; struct dhcp_mac *dhcp_macs; struct dhcp_boot *boot_config; @@ -1043,3 +1043,4 @@ struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *con int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly); char *strip_hostname(char *hostname); void log_tags(struct dhcp_netid *netid, u32 xid); +int match_bytes(struct dhcp_opt *o, unsigned char *p, int len); diff --git a/src/option.c b/src/option.c index fbe1204..a2841d8 100644 --- a/src/option.c +++ b/src/option.c @@ -1333,6 +1333,11 @@ static char *parse_dhcp_opt(char *arg, int flags) !new->netid || new->netid->next) problem = _("illegal dhcp-match"); + else if (is6) + { + new->next = daemon->dhcp_match6; + daemon->dhcp_match6 = new; + } else { new->next = daemon->dhcp_match; diff --git a/src/rfc2131.c b/src/rfc2131.c index d897087..581e7f5 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -26,7 +26,6 @@ static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, si static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt); #endif -static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len); static int sanitise(unsigned char *opt, char *buf); static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback); static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt); @@ -1400,37 +1399,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, return 0; } -static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len) -{ - int i; - - if (o->len > len) - return 0; - - if (o->len == 0) - return 1; - - if (o->flags & DHOPT_HEX) - { - if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask)) - return 1; - } - else - for (i = 0; i <= (len - o->len); ) - { - if (memcmp(o->val, p + i, o->len) == 0) - return 1; - - if (o->flags & DHOPT_STRING) - i++; - else - i += o->len; - } - - return 0; -} - - /* find a good value to use as MAC address for logging and address-allocation hashing. This is normally just the chaddr field from the DHCP packet, but eg Firewire will have hlen == 0 and use the client-id instead. diff --git a/src/rfc3315.c b/src/rfc3315.c index 43cb832..93d4fef 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -189,7 +189,7 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, struct dhcp_opt *opt_cfg; struct dhcp_vendor *vendor; struct dhcp_context *context_tmp; - unsigned int xid; + unsigned int xid, ignore = 0; unsigned int fqdn_flags = 0x01; /* default to send if we recieve no FQDN option */ /* copy over transaction-id, and save pointer to message type */ @@ -274,6 +274,48 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, } } } + + /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match. + Otherwise assume the option is an array, and look for a matching element. + If no data given, existance of the option is enough. This code handles + V-I opts too. */ + for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next) + { + unsigned int len, elen, match = 0; + size_t offset, o2; + + if (opt_cfg->flags & DHOPT_RFC3925) + { + for (opt = opt6_find(packet_options, end, OPTION6_VENDOR_OPTS, 4); + opt; + opt = opt6_find(opt6_next(opt, end), end, OPTION6_VENDOR_OPTS, 4)) + { + void *vopt; + void *vend = opt6_ptr(opt, opt6_len(opt)); + + for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0); + vopt; + vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0)) + if (match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt))) + break; + } + if (match) + break; + } + else + { + if (!(opt = opt6_find(packet_options, end, opt_cfg->opt, 1))) + continue; + + match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt)); + } + + if (match) + { + opt_cfg->netid->next = tags; + tags = opt_cfg->netid; + } + } if ((opt = opt6_find(packet_options, end, OPTION6_FQDN, 1))) { @@ -359,9 +401,22 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, known_id.net = "known"; known_id.next = tags; tags = &known_id; + + if (have_config(config, CONFIG_DISABLE)) + ignore = 1; } - + /* if all the netids in the ignore list are present, ignore this client */ + if (daemon->dhcp_ignore) + { + struct dhcp_netid_list *id_list; + + tagif = run_tag_if(tags); + + for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next) + if (match_netid(id_list->list, tagif, 0)) + ignore = 1; + } switch (msg_type) { @@ -385,7 +440,10 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, *outmsgtypep = make_lease ? DHCP6REPLY : DHCP6ADVERTISE; log6_packet(msg_type == DHCP6SOLICIT ? "DHCPSOLICIT" : "DHCPREQUEST", - clid, clid_len, NULL, xid, iface_name, NULL); + clid, clid_len, NULL, xid, iface_name, ignore ? "ignored" : NULL); + + if (ignore) + return 0; for (opt = packet_options; opt; opt = opt6_next(opt, end)) { @@ -791,6 +849,8 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, case DHCP6IREQ: { + if (ignore) + return 0; *outmsgtypep = DHCP6REPLY; break; } From ceae00dddf2a752f702bd25bb00607f37967c810 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 9 Feb 2012 21:28:14 +0000 Subject: [PATCH 5/7] lease script should work with IPv6 now. --- src/dhcp-common.c | 120 +++++++++++++++++++++++++++++++- src/dhcp.c | 79 --------------------- src/dhcp6.c | 2 +- src/dnsmasq.h | 15 ++-- src/helper.c | 174 +++++++++++++++++++++++++++++++++++----------- src/lease.c | 161 +++++++++++++++++++++++------------------- src/option.c | 5 +- src/rfc2131.c | 48 ++----------- src/rfc3315.c | 104 +++++++++++++++++++++------ 9 files changed, 442 insertions(+), 266 deletions(-) diff --git a/src/dhcp-common.c b/src/dhcp-common.c index bd5be6c..2e40447 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -238,6 +238,124 @@ int match_bytes(struct dhcp_opt *o, unsigned char *p, int len) return 0; } - + +void check_dhcp_hosts(int fatal) +{ + /* If the same IP appears in more than one host config, then DISCOVER + for one of the hosts will get the address, but REQUEST will be NAKed, + since the address is reserved by the other one -> protocol loop. + Also check that FQDNs match the domain we are using. */ + + struct dhcp_config *configs, *cp; + + for (configs = daemon->dhcp_conf; configs; configs = configs->next) + { + char *domain; + + if ((configs->flags & DHOPT_BANK) || fatal) + { + for (cp = configs->next; cp; cp = cp->next) + if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr) + { + if (fatal) + die(_("duplicate IP address %s in dhcp-config directive."), + inet_ntoa(cp->addr), EC_BADCONF); + else + my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."), + inet_ntoa(cp->addr), daemon->dhcp_hosts_file); + configs->flags &= ~CONFIG_ADDR; + } + + /* split off domain part */ + if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname))) + configs->domain = domain; + } + } +} + +void dhcp_update_configs(struct dhcp_config *configs) +{ + /* Some people like to keep all static IP addresses in /etc/hosts. + This goes through /etc/hosts and sets static addresses for any DHCP config + records which don't have an address and whose name matches. + We take care to maintain the invariant that any IP address can appear + in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, + restore the status-quo ante first. */ + + struct dhcp_config *config; + struct crec *crec; + int prot = AF_INET; + + for (config = configs; config; config = config->next) + if (config->flags & CONFIG_ADDR_HOSTS) + config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS); + +#ifdef HAVE_DHCP6 + again: +#endif + + if (daemon->port != 0) + for (config = configs; config; config = config->next) + { + int conflags = CONFIG_ADDR; + int cacheflags = F_IPV4; + +#ifdef HAVE_DHCP6 + if (prot == AF_INET6) + { + conflags = CONFIG_ADDR6; + cacheflags = F_IPV6; + } +#endif + if (!(config->flags & conflags) && + (config->flags & CONFIG_NAME) && + (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) && + (crec->flags & F_HOSTS)) + { + if (cache_find_by_name(crec, config->hostname, 0, cacheflags)) + { + /* use primary (first) address */ + while (crec && !(crec->flags & F_REVERSE)) + crec = cache_find_by_name(crec, config->hostname, 0, cacheflags); + if (!crec) + continue; /* should be never */ + inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), + config->hostname, daemon->addrbuff); + } + + if (prot == AF_INET && !config_find_by_address(configs, crec->addr.addr.addr.addr4)) + { + config->addr = crec->addr.addr.addr.addr4; + config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS; + continue; + } + +#ifdef HAVE_DHCP6 + if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 129, 0)) + { + memcpy(config->hwaddr, &crec->addr.addr.addr.addr6, IN6ADDRSZ); + config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS; + continue; + } +#endif + + inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), + daemon->addrbuff, config->hostname); + + + } + } + +#ifdef HAVE_DHCP6 + if (prot == AF_INET) + { + prot = AF_INET6; + goto again; + } +#endif + +} #endif diff --git a/src/dhcp.c b/src/dhcp.c index 9650831..b95752c 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -946,85 +946,6 @@ void dhcp_read_ethers(void) my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count); } -void check_dhcp_hosts(int fatal) -{ - /* If the same IP appears in more than one host config, then DISCOVER - for one of the hosts will get the address, but REQUEST will be NAKed, - since the address is reserved by the other one -> protocol loop. - Also check that FQDNs match the domain we are using. */ - - struct dhcp_config *configs, *cp; - - for (configs = daemon->dhcp_conf; configs; configs = configs->next) - { - char *domain; - - if ((configs->flags & DHOPT_BANK) || fatal) - { - for (cp = configs->next; cp; cp = cp->next) - if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr) - { - if (fatal) - die(_("duplicate IP address %s in dhcp-config directive."), - inet_ntoa(cp->addr), EC_BADCONF); - else - my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."), - inet_ntoa(cp->addr), daemon->dhcp_hosts_file); - configs->flags &= ~CONFIG_ADDR; - } - - /* split off domain part */ - if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname))) - configs->domain = domain; - } - } -} - -void dhcp_update_configs(struct dhcp_config *configs) -{ - /* Some people like to keep all static IP addresses in /etc/hosts. - This goes through /etc/hosts and sets static addresses for any DHCP config - records which don't have an address and whose name matches. - We take care to maintain the invariant that any IP address can appear - in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, - restore the status-quo ante first. */ - - struct dhcp_config *config; - struct crec *crec; - - for (config = configs; config; config = config->next) - if (config->flags & CONFIG_ADDR_HOSTS) - config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS); - - - if (daemon->port != 0) - for (config = configs; config; config = config->next) - if (!(config->flags & CONFIG_ADDR) && - (config->flags & CONFIG_NAME) && - (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) && - (crec->flags & F_HOSTS)) - { - if (cache_find_by_name(crec, config->hostname, 0, F_IPV4)) - { - /* use primary (first) address */ - while (crec && !(crec->flags & F_REVERSE)) - crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4); - if (!crec) - continue; /* should be never */ - my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), - config->hostname, inet_ntoa(crec->addr.addr.addr.addr4)); - } - - if (config_find_by_address(configs, crec->addr.addr.addr.addr4)) - my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), - inet_ntoa(crec->addr.addr.addr.addr4), config->hostname); - else - { - config->addr = crec->addr.addr.addr.addr4; - config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS; - } - } -} /* If we've not found a hostname any other way, try and see if there's one in /etc/hosts for this address. If it has a domain part, that must match the set domain and diff --git a/src/dhcp6.c b/src/dhcp6.c index 66a176c..94cff5b 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -214,7 +214,7 @@ void dhcp6_packet(time_t now) lease_prune(NULL, now); /* lose any expired leases */ msg.msg_iov = &daemon->dhcp_packet; - sz = dhcp6_reply(parm.current, ifr.ifr_name, sz, IN6_IS_ADDR_MULTICAST(&from), now); + sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, sz, IN6_IS_ADDR_MULTICAST(&from), now); /* ifr.ifr_name, if_index, (size_t)sz, now, unicast_dest, &is_inform, pxe_fd, iface_addr); */ lease_update_file(now); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index f83f440..e0d33f6 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -911,12 +911,9 @@ struct dhcp_config *find_config(struct dhcp_config *configs, unsigned char *clid, int clid_len, unsigned char *hwaddr, int hw_len, int hw_type, char *hostname); -void dhcp_update_configs(struct dhcp_config *configs); void dhcp_read_ethers(void); -void check_dhcp_hosts(int fatal); struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr); char *host_from_dns(struct in_addr addr); -char *get_domain(struct in_addr addr); #endif /* lease.c */ @@ -944,6 +941,10 @@ void lease_prune(struct dhcp_lease *target, time_t now); void lease_update_from_configs(void); int do_script_run(time_t now); void rerun_scripts(void); +#ifdef HAVE_SCRIPT +void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, + unsigned int len, int delim); +#endif #endif /* rfc2131.c */ @@ -1026,15 +1027,18 @@ struct dhcp_config *find_config6(struct dhcp_config *configs, struct dhcp_context *context, unsigned char *duid, int duid_len, char *hostname); +struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, + int prefix, u64 addr); void make_duid(time_t now); #endif /* rfc3315.c */ #ifdef HAVE_DHCP6 -size_t dhcp6_reply(struct dhcp_context *context, char *iface_name, size_t sz, int is_multicast, time_t now); +size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, size_t sz, int is_multicast, time_t now); #endif /* dhcp-common.c */ +#ifdef HAVE_DHCP void dhcp_common_init(void); ssize_t recv_dhcp_packet(int fd, struct msghdr *msg); struct dhcp_netid *run_tag_if(struct dhcp_netid *input); @@ -1044,3 +1048,6 @@ int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly); char *strip_hostname(char *hostname); void log_tags(struct dhcp_netid *netid, u32 xid); int match_bytes(struct dhcp_opt *o, unsigned char *p, int len); +void dhcp_update_configs(struct dhcp_config *configs); +void check_dhcp_hosts(int fatal); +#endif diff --git a/src/helper.c b/src/helper.c index c86364f..db5f097 100644 --- a/src/helper.c +++ b/src/helper.c @@ -46,8 +46,9 @@ static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, struct script_data { - unsigned char action, hwaddr_len, hwaddr_type; - unsigned char clid_len, hostname_len, ed_len; + int flags; + int action, hwaddr_len, hwaddr_type; + int clid_len, hostname_len, ed_len; struct in_addr addr, giaddr; unsigned int remaining_time; #ifdef HAVE_BROKEN_RTC @@ -57,6 +58,7 @@ struct script_data #endif unsigned char hwaddr[DHCP_CHADDR_MAX]; char interface[IF_NAMESIZE]; + }; static struct script_data *buf = NULL; @@ -173,7 +175,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) char *p, *action_str, *hostname = NULL, *domain = NULL; unsigned char *buf = (unsigned char *)daemon->namebuff; unsigned char *end, *extradata, *alloc_buff = NULL; - int err = 0; + int is6, err = 0; free(alloc_buff); @@ -199,17 +201,34 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) action_str = "old"; else continue; + + is6 = !!(data.flags & (LEASE_TA | LEASE_NA)); - /* stringify MAC into dhcp_buff */ - p = daemon->dhcp_buff; - if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) - p += sprintf(p, "%.2x-", data.hwaddr_type); - for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++) - { - p += sprintf(p, "%.2x", data.hwaddr[i]); - if (i != data.hwaddr_len - 1) - p += sprintf(p, ":"); - } + if (!is6) + { + /* stringify MAC into dhcp_buff */ + p = daemon->dhcp_buff; + if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) + p += sprintf(p, "%.2x-", data.hwaddr_type); + for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++) + { + p += sprintf(p, "%.2x", data.hwaddr[i]); + if (i != data.hwaddr_len - 1) + p += sprintf(p, ":"); + } + } +#ifdef HAVE_DHCP6 + else + { + /* duid not MAC for IPv6 */ + for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++) + { + p += sprintf(p, "%.2x", buf[i]); + if (i != data.clid_len - 1) + p += sprintf(p, ":"); + } + } +#endif /* expiry or length into dhcp_buff2 */ #ifdef HAVE_BROKEN_RTC @@ -228,12 +247,26 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) continue; /* CLID into packet */ - for (p = daemon->packet, i = 0; i < data.clid_len; i++) + if (!is6) + for (p = daemon->packet, i = 0; i < data.clid_len; i++) + { + p += sprintf(p, "%.2x", buf[i]); + if (i != data.clid_len - 1) + p += sprintf(p, ":"); + } +#ifdef HAVE_DHCP6 + else { - p += sprintf(p, "%.2x", buf[i]); - if (i != data.clid_len - 1) - p += sprintf(p, ":"); + /* or IAID and server DUID for IPv6 */ + sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type); + for (p = daemon->packet, i = 0; i < daemon->duid_len; i++) + { + p += sprintf(p, "%.2x", daemon->duid[i]); + if (i != daemon->duid_len - 1) + p += sprintf(p, ":"); + } } +#endif buf += data.clid_len; @@ -253,14 +286,29 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) extradata = buf + data.hostname_len; + if (!is6) + inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN); +#ifdef HAVE_DHCP6 + else + inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN); +#endif + #ifdef HAVE_LUASCRIPT if (daemon->luascript) { lua_getglobal(lua, "lease"); /* function to call */ lua_pushstring(lua, action_str); /* arg1 - action */ lua_newtable(lua); /* arg2 - data table */ - - if (data.clid_len != 0) + + if (is6) + { + lua_pushstring(lua, daemon->packet); + lua_setfield(lua, -2, "duid"); + lua_pushstring(lua, daemon->dhcp_buff3); + lua_setfield(lua, -2, "iaid"); + } + + if (!is6 && data.clid_len != 0) { lua_pushstring(lua, daemon->packet); lua_setfield(lua, -2, "client_id"); @@ -294,20 +342,36 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) end = extradata + data.ed_len; buf = extradata; - buf = grab_extradata_lua(buf, end, "vendor_class"); + + if (!is6) + buf = grab_extradata_lua(buf, end, "vendor_class"); +#ifdef HAVE_DHCP6 + else + for (i = 0; i < data.hwaddr_len; i++) + { + sprintf(daemon->dhcp_buff2, "vendor_class%i", i); + buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2); + } +#endif + buf = grab_extradata_lua(buf, end, "supplied_hostname"); - buf = grab_extradata_lua(buf, end, "cpewan_oui"); - buf = grab_extradata_lua(buf, end, "cpewan_serial"); - buf = grab_extradata_lua(buf, end, "cpewan_class"); + + if (!is6) + { + buf = grab_extradata_lua(buf, end, "cpewan_oui"); + buf = grab_extradata_lua(buf, end, "cpewan_serial"); + buf = grab_extradata_lua(buf, end, "cpewan_class"); + } + buf = grab_extradata_lua(buf, end, "tags"); - + for (i = 0; buf; i++) { sprintf(daemon->dhcp_buff2, "user_class%i", i); buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2); } - if (data.giaddr.s_addr != 0) + if (!is6 && data.giaddr.s_addr != 0) { lua_pushstring(lua, inet_ntoa(data.giaddr)); lua_setfield(lua, -2, "relay_address"); @@ -325,10 +389,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) lua_setfield(lua, -2, "old_hostname"); } - lua_pushstring(lua, daemon->dhcp_buff); - lua_setfield(lua, -2, "mac_address"); - - lua_pushstring(lua, inet_ntoa(data.addr)); + if (!is6) + { + lua_pushstring(lua, daemon->dhcp_buff); + lua_setfield(lua, -2, "mac_address"); + } + + lua_pushstring(lua, daemon->addrbuff); lua_setfield(lua, -2, "ip_address"); lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ @@ -372,7 +439,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) continue; } - if (data.clid_len != 0) + if (is6) + { + my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err); + my_setenv("DNSMASQ_DUID", daemon->packet, &err); + } + + if (!is6 && data.clid_len != 0) my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err); if (strlen(data.interface) != 0) @@ -389,11 +462,27 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) end = extradata + data.ed_len; buf = extradata; - buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err); + + if (!is6) + buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err); +#ifdef HAVE_DHCP6 + else + for (i = 0; i < data.hwaddr_len; i++) + { + sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i); + buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err); + } +#endif + buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err); - buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err); - buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err); - buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err); + + if (!is6) + { + buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err); + buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err); + buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err); + } + buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err); for (i = 0; buf; i++) @@ -402,7 +491,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err); } - if (data.giaddr.s_addr != 0) + if (!is6 && data.giaddr.s_addr != 0) my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err); if (data.action != ACTION_DEL && data.remaining_time != 0) @@ -427,7 +516,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) { execl(daemon->lease_change_command, p ? p+1 : daemon->lease_change_command, - action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL); + action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL); err = errno; } /* failed, send event so the main process logs the problem */ @@ -493,7 +582,13 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n unsigned char *p; size_t size; unsigned int hostname_len = 0, clid_len = 0, ed_len = 0; - + int fd = daemon->dhcpfd; + +#ifdef HAVE_DHCP6 + if (!daemon->dhcp) + fd = daemon->dhcp6fd; +#endif + /* no script */ if (daemon->helperfd == -1) return; @@ -524,6 +619,7 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n } buf->action = action; + buf->flags = lease->flags; buf->hwaddr_len = lease->hwaddr_len; buf->hwaddr_type = lease->hwaddr_type; buf->clid_len = clid_len; @@ -531,8 +627,8 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n buf->hostname_len = hostname_len; buf->addr = lease->addr; buf->giaddr = lease->giaddr; - memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len); - if (!indextoname(daemon->dhcpfd, lease->last_interface, buf->interface)) + memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX); + if (!indextoname(fd, lease->last_interface, buf->interface)) buf->interface[0] = 0; #ifdef HAVE_BROKEN_RTC diff --git a/src/lease.c b/src/lease.c index 691ee61..999c2fe 100644 --- a/src/lease.c +++ b/src/lease.c @@ -28,13 +28,9 @@ void lease_init(time_t now) struct dhcp_lease *lease; int clid_len, hw_len, hw_type; FILE *leasestream; -#ifdef HAVE_DHCP6 - int v6pass = 0; - int lease_type = 0; -#endif leases_left = daemon->dhcp_max; - + if (option_bool(OPT_LEASE_RO)) { /* run " init" once to get the @@ -68,58 +64,68 @@ void lease_init(time_t now) rewind(leasestream); } -#ifdef HAVE_DHCP6 - again: -#endif - /* client-id max length is 255 which is 255*2 digits + 254 colons borrow DNS packet buffer which is always larger than 1000 bytes */ if (leasestream) - while (fscanf(leasestream, "%lu %255s %64s %255s %764s", - &ei, daemon->dhcp_buff2, daemon->namebuff, - daemon->dhcp_buff, daemon->packet) == 5) + while (fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2) == 2) { + if (strcmp(daemon->dhcp_buff3, "duid") == 0) + { + daemon->duid_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, 130, NULL, NULL); + daemon->duid = safe_malloc(daemon->duid_len); + memcpy(daemon->duid, daemon->dhcp_buff2, daemon->duid_len); + continue; + } + + ei = atol(daemon->dhcp_buff3); + + if (fscanf(leasestream, " %64s %255s %764s", + daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3) + break; + + clid_len = 0; + if (strcmp(daemon->packet, "*") != 0) + clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL); + + if (inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4) && + (lease = lease4_allocate(addr.addr.addr4))) + { + hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); + /* For backwards compatibility, no explict MAC address type means ether. */ + if (hw_type == 0 && hw_len != 0) + hw_type = ARPHRD_ETHER; + + lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len); + + if (strcmp(daemon->dhcp_buff, "*") != 0) + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL); + } #ifdef HAVE_DHCP6 - if (v6pass) + else if (inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6)) { char *s = daemon->dhcp_buff2; + int lease_type = LEASE_NA; + if (s[0] == 'T') { lease_type = LEASE_TA; s++; } - else - lease_type = LEASE_NA; hw_type = atoi(s); + + if ((lease = lease6_allocate(&addr.addr.addr6, lease_type))) + { + lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len); + + if (strcmp(daemon->dhcp_buff, "*") != 0) + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL); + } } - else #endif - { - hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); - /* For backwards compatibility, no explict MAC address type means ether. */ - if (hw_type == 0 && hw_len != 0) - hw_type = ARPHRD_ETHER; - } - -#ifdef HAVE_DHCP6 - if (v6pass) - inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6); else -#endif - inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4); + break; - clid_len = 0; - if (strcmp(daemon->packet, "*") != 0) - clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL); - -#ifdef HAVE_DHCP6 - if (v6pass) - lease = lease6_allocate(&addr.addr.addr6, lease_type); - else -#endif - lease = lease4_allocate(addr.addr.addr4); - if (!lease) die (_("too many stored leases"), NULL, EC_MISC); @@ -134,44 +140,17 @@ void lease_init(time_t now) even when sizeof(time_t) == 8 */ lease->expires = (time_t)ei; #endif - -#ifdef HAVE_DHCP6 - if (v6pass) - lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len); - else -#endif - lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len); - if (strcmp(daemon->dhcp_buff, "*") != 0) - { -#ifdef HAVE_DHCP6 - if (v6pass) - lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL); - else -#endif - lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL); - } /* set these correctly: the "old" events are generated later from the startup synthesised SIGHUP. */ - lease->flags |= ~(LEASE_NEW | LEASE_CHANGED); + lease->flags &= ~(LEASE_NEW | LEASE_CHANGED); } #ifdef HAVE_DHCP6 - if (!v6pass) - { - if (fscanf(leasestream, "duid %255s", daemon->dhcp_buff) == 1) - { - daemon->duid_len = parse_hex(daemon->dhcp_buff, (unsigned char *)daemon->dhcp_buff, 130, NULL, NULL); - daemon->duid = safe_malloc(daemon->duid_len); - memcpy(daemon->duid, daemon->dhcp_buff, daemon->duid_len ); - v6pass = 1; - goto again; - } - - /* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */ - if (daemon->dhcp6) - make_duid(now); - } + /* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */ + if (!daemon->duid && daemon->dhcp6) + make_duid(now); + #endif #ifdef HAVE_SCRIPT @@ -727,7 +706,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *do if (!(lease_tmp->flags & (LEASE_TA | LEASE_NA))) continue; - /* another lease for the saem DUID is OK for IPv6 */ + /* another lease for the same DUID is OK for IPv6 */ if (lease->clid_len == lease_tmp->clid_len && lease->clid && lease_tmp->clid && memcmp(lease->clid, lease_tmp->clid, lease->clid_len) == 0) @@ -865,6 +844,44 @@ int do_script_run(time_t now) return 0; /* nothing to do */ } +#ifdef HAVE_SCRIPT +void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim) +{ + unsigned int i; + + /* check for embeded NULLs */ + for (i = 0; i < len; i++) + if (data[i] == 0) + { + len = i; + break; + } + + if ((lease->extradata_size - lease->extradata_len) < (len + 1)) + { + size_t newsz = lease->extradata_len + len + 100; + unsigned char *new = whine_malloc(newsz); + + if (!new) + return; + + if (lease->extradata) + { + memcpy(new, lease->extradata, lease->extradata_len); + free(lease->extradata); + } + + lease->extradata = new; + lease->extradata_size = newsz; + } + + if (len != 0) + memcpy(lease->extradata + lease->extradata_len, data, len); + lease->extradata[lease->extradata_len + len] = delim; + lease->extradata_len += len + 1; +} +#endif + #endif diff --git a/src/option.c b/src/option.c index a2841d8..a9abbc7 100644 --- a/src/option.c +++ b/src/option.c @@ -1730,14 +1730,13 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) local=// local=/xxx.yyy.zzz.ip6.arpa/ */ - if (strcmp(arg, "local") != 0 || (msize & 4 != 0)) + if (strcmp(arg, "local") != 0 || ((msize & 4) != 0)) option = '?'; else { struct server *serv = opt_malloc(sizeof(struct server)); - in_addr_t a = ntohl(new->start.s_addr) >> 8; char *p; - + memset(serv, 0, sizeof(struct server)); serv->domain = d; serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; diff --git a/src/rfc2131.c b/src/rfc2131.c index 581e7f5..3123dc5 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -22,7 +22,6 @@ #define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)])) #ifdef HAVE_SCRIPT -static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim); static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt); #endif @@ -1252,7 +1251,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, if (strcmp(n->net, n1->net) == 0) break; if (!n1) - add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); + lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); } if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1))) @@ -1262,7 +1261,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, /* If the user-class option started as counted strings, the first byte will be zero. */ if (len != 0 && ucp[0] == 0) ucp++, len--; - add_extradata_data(lease, ucp, len, 0); + lease_add_extradata(lease, ucp, len, 0); } } #endif @@ -1484,51 +1483,12 @@ static int sanitise(unsigned char *opt, char *buf) } #ifdef HAVE_SCRIPT -static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim) -{ - if ((lease->extradata_size - lease->extradata_len) < (len + 1)) - { - size_t newsz = lease->extradata_len + len + 100; - unsigned char *new = whine_malloc(newsz); - - if (!new) - return; - - if (lease->extradata) - { - memcpy(new, lease->extradata, lease->extradata_len); - free(lease->extradata); - } - - lease->extradata = new; - lease->extradata_size = newsz; - } - - if (len != 0) - memcpy(lease->extradata + lease->extradata_len, data, len); - lease->extradata[lease->extradata_len + len] = delim; - lease->extradata_len += len + 1; -} - static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt) { if (!opt) - add_extradata_data(lease, NULL, 0, 0); + lease_add_extradata(lease, NULL, 0, 0); else - { - size_t i, len = option_len(opt); - unsigned char *ucp = option_ptr(opt, 0); - - /* check for embeded NULLs */ - for (i = 0; i < len; i++) - if (ucp[i] == 0) - { - len = i; - break; - } - - add_extradata_data(lease, ucp, len, 0); - } + lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0); } #endif diff --git a/src/rfc3315.c b/src/rfc3315.c index 93d4fef..8e0f1cc 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -30,10 +30,10 @@ static void put_opt6_short(unsigned int val); static void put_opt6_long(unsigned int val); static void put_opt6_string(char *s); -static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, - struct dhcp_context *context, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now); -static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, - struct dhcp_context *context, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now); +static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context, + int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now); +static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, struct dhcp_context *context, + int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now); static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string); static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize); @@ -45,7 +45,7 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size); -size_t dhcp6_reply(struct dhcp_context *context, char *iface_name, size_t sz, int is_unicast, time_t now) +size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, size_t sz, int is_unicast, time_t now) { struct dhcp_netid *relay_tags = NULL; struct dhcp_vendor *vendor; @@ -56,15 +56,15 @@ size_t dhcp6_reply(struct dhcp_context *context, char *iface_name, size_t sz, in outpacket_counter = 0; - if (dhcp6_maybe_relay(NULL, &relay_tags, context, iface_name, daemon->dhcp_packet.iov_base, sz, is_unicast, now)) + if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, daemon->dhcp_packet.iov_base, sz, is_unicast, now)) return outpacket_counter; return 0; } /* This cost me blood to write, it will probably cost you blood to understand - srk. */ -static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, - struct dhcp_context *context, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now) +static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context, + int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now) { void *end = inbuff + sz; void *opts = inbuff + 34; @@ -114,7 +114,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid ** return 0; } - return dhcp6_no_relay(msg_type, *relay_tagsp, context, iface_name, inbuff, sz, is_unicast, now); + return dhcp6_no_relay(msg_type, *relay_tagsp, context, interface, iface_name, inbuff, sz, is_unicast, now); } /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option @@ -159,7 +159,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid ** memcpy(&link_address, inbuff + 2, IN6ADDRSZ); /* Not, zero is_unicast since that is now known to refer to the relayed packet, not the original sent by the client */ - if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, iface_name, opt6_ptr(opt, 0), opt6_len(opt), 0, now)) + if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, interface, iface_name, opt6_ptr(opt, 0), opt6_len(opt), 0, now)) return 0; } else @@ -170,8 +170,8 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid ** return 1; } -static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, - struct dhcp_context *context, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now) +static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, struct dhcp_context *context, + int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now) { void *packet_options = inbuff + 4; void *end = inbuff + sz; @@ -183,7 +183,7 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, char *client_hostname= NULL, *hostname = NULL; char *domain = NULL, *send_domain = NULL; struct dhcp_config *config = NULL; - struct dhcp_netid known_id; + struct dhcp_netid known_id, iface_id; int done_dns = 0, hostname_auth = 0, do_encap = 0; unsigned char *outmsgtypep; struct dhcp_opt *opt_cfg; @@ -192,6 +192,11 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, unsigned int xid, ignore = 0; unsigned int fqdn_flags = 0x01; /* default to send if we recieve no FQDN option */ + /* set tag with name == interface */ + iface_id.net = iface_name; + iface_id.next = tags; + tags = &iface_id; + /* copy over transaction-id, and save pointer to message type */ outmsgtypep = put_opt6(inbuff, 4); start_opts = save_counter(-1); @@ -281,9 +286,8 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, V-I opts too. */ for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next) { - unsigned int len, elen, match = 0; - size_t offset, o2; - + int match = 0; + if (opt_cfg->flags & DHOPT_RFC3925) { for (opt = opt6_find(packet_options, end, OPTION6_VENDOR_OPTS, 4); @@ -296,7 +300,7 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0); vopt; vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0)) - if (match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt))) + if ((match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt)))) break; } if (match) @@ -547,7 +551,7 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, (lease = lease6_find(clid, clid_len, ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, iaid, NULL)) && address6_available(context, (struct in6_addr *)&lease->hwaddr, tags) && - !config_find_by_address6(daemon->dhcp_conf, (struct in6_addr *)&lease->hwaddr)) + !config_find_by_address6(daemon->dhcp_conf, (struct in6_addr *)&lease->hwaddr, 128, 0)) addrp = (struct in6_addr *)&lease->hwaddr; if (!addrp && address6_allocate(context, clid, clid_len, serial++, tags, &alloced_addr)) @@ -597,6 +601,7 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, { lease_set_expires(lease, lease_time, now); lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len); + lease_set_interface(lease, interface); if (hostname && ia_type == OPTION6_IA_NA) { char *addr_domain = get_domain6(addrp); @@ -604,6 +609,58 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, send_domain = addr_domain; lease_set_hostname(lease, hostname, hostname_auth, addr_domain, domain); } + +#ifdef HAVE_SCRIPT + if (daemon->lease_change_command) + { + void *class_opt; + lease->flags |= LEASE_CHANGED; + free(lease->extradata); + lease->extradata = NULL; + lease->extradata_size = lease->extradata_len = 0; + lease->hwaddr_len = 0; /* surrogate for no of vendor classes */ + + if ((class_opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 2))) + { + void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt)); + for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) + { + lease->hwaddr_len++; + lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0); + } + } + + lease_add_extradata(lease, (unsigned char *)client_hostname, + client_hostname ? strlen(client_hostname) : 0, 0); + + /* space-concat tag set */ + if (!tags) + lease_add_extradata(lease, NULL, 0, 0); + else + { + struct dhcp_netid *n; + + for (n = run_tag_if(tags); n; n = n->next) + { + struct dhcp_netid *n1; + /* kill dupes */ + for (n1 = n->next; n1; n1 = n1->next) + if (strcmp(n->net, n1->net) == 0) + break; + if (!n1) + lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); + } + } + + if ((class_opt = opt6_find(packet_options, end, OPTION6_USER_CLASS, 2))) + { + void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt)); + for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) + lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0); + } + } +#endif + } else if (!send_domain) send_domain = get_domain6(addrp); @@ -739,8 +796,9 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, break; } - if (!address6_available(context, req_addr, tags) || - !(this_context = narrow_context6(context, req_addr, tags))) + tagif = run_tag_if(tags); + if (!address6_available(context, req_addr, tagif) || + !(this_context = narrow_context6(context, req_addr, tagif))) lease_time = 0; else { @@ -827,7 +885,7 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, { struct in6_addr *req_addr = opt6_ptr(ia_option, 0); - if (!address6_available(context, req_addr, tags)) + if (!address6_available(context, req_addr, run_tag_if(tags))) { o1 = new_opt6(OPTION6_STATUS_CODE); put_opt6_short(DHCP6NOTONLINK); @@ -974,10 +1032,10 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, if (have_config(config, CONFIG_ADDR6) && memcmp(&config->addr6, addrp, IN6ADDRSZ) == 0) { - prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF); + prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF); inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), - daemon->addrbuff, daemon->dhcp_buff); + daemon->addrbuff, daemon->dhcp_buff3); config->flags |= CONFIG_DECLINED; config->decline_time = now; } From a5c72ab51d1093bdddae69296ff4c829e6f2eeac Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 10 Feb 2012 13:42:47 +0000 Subject: [PATCH 6/7] DHCPv6 vendor class option includes an enterprise number. Handle that. --- src/dnsmasq.h | 3 ++- src/helper.c | 16 +++++++++++----- src/option.c | 12 ++++++++++++ src/rfc3315.c | 33 ++++++++++++++++++++++++++------- 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index e0d33f6..3068f5f 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -566,7 +566,8 @@ struct pxe_service { /* vendorclass, userclass, remote-id or cicuit-id */ struct dhcp_vendor { - int len, match_type, option; + int len, match_type; + unsigned int enterprise; char *data; struct dhcp_netid netid; struct dhcp_vendor *next; diff --git a/src/helper.c b/src/helper.c index db5f097..4e92d25 100644 --- a/src/helper.c +++ b/src/helper.c @@ -467,11 +467,17 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err); #ifdef HAVE_DHCP6 else - for (i = 0; i < data.hwaddr_len; i++) - { - sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i); - buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err); - } + { + if (data.hwaddr_len != 0) + { + buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err); + for (i = 0; i < data.hwaddr_len - 1; i++) + { + sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i); + buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err); + } + } + } #endif buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err); diff --git a/src/option.c b/src/option.c index a9abbc7..2840670 100644 --- a/src/option.c +++ b/src/option.c @@ -2870,6 +2870,18 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) new->netid.net = opt_string_alloc(set_prefix(arg)); /* check for hex string - must digits may include : must not have nothing else, only allowed for agent-options. */ + + arg = comma; + if ((comma = split(arg))) + { + if (option != 'U' || strstr(arg, "enterprise:") != arg) + option = '?'; + else + new->enterprise = atoi(arg+11); + } + else + comma = arg; + for (p = (unsigned char *)comma; *p; p++) if (isxdigit(*p)) dig = 1; diff --git a/src/rfc3315.c b/src/rfc3315.c index 8e0f1cc..7f5c35f 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -269,7 +269,20 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, struct dhcp_con if ((opt = opt6_find(packet_options, end, mopt, 2))) { void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt)); - for (enc_opt = opt6_ptr(opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) + int offset = 0; + + if (mopt == OPTION6_VENDOR_CLASS) + { + if (opt6_len(opt) < 4) + continue; + + if (vendor->enterprise != opt6_uint(opt, 0, 4)) + continue; + + offset = 4; + } + + for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) for (i = 0; i <= (opt6_len(enc_opt) - vendor->len); i++) if (memcmp(vendor->data, opt6_ptr(enc_opt, i), vendor->len) == 0) { @@ -620,14 +633,20 @@ static int dhcp6_no_relay(int msg_type, struct dhcp_netid *tags, struct dhcp_con lease->extradata_size = lease->extradata_len = 0; lease->hwaddr_len = 0; /* surrogate for no of vendor classes */ - if ((class_opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 2))) + if ((class_opt = opt6_find(packet_options, end, OPTION6_VENDOR_CLASS, 4))) { void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt)); - for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) - { - lease->hwaddr_len++; - lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0); - } + lease->hwaddr_len++; + /* send enterprise number first */ + sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4)); + lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0); + + if (opt6_len(class_opt) >= 6) + for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) + { + lease->hwaddr_len++; + lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0); + } } lease_add_extradata(lease, (unsigned char *)client_hostname, From 3d7b550f522863754a99e122d5b58a9be670107a Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 10 Feb 2012 17:35:37 +0000 Subject: [PATCH 7/7] missed DHCP6 conditional compilation. --- src/lease.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lease.c b/src/lease.c index 999c2fe..b5f9926 100644 --- a/src/lease.c +++ b/src/lease.c @@ -69,6 +69,7 @@ void lease_init(time_t now) if (leasestream) while (fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2) == 2) { +#ifdef HAVE_DHCP6 if (strcmp(daemon->dhcp_buff3, "duid") == 0) { daemon->duid_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, 130, NULL, NULL); @@ -76,7 +77,8 @@ void lease_init(time_t now) memcpy(daemon->duid, daemon->dhcp_buff2, daemon->duid_len); continue; } - +#endif + ei = atol(daemon->dhcp_buff3); if (fscanf(leasestream, " %64s %255s %764s", @@ -150,7 +152,6 @@ void lease_init(time_t now) /* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */ if (!daemon->duid && daemon->dhcp6) make_duid(now); - #endif #ifdef HAVE_SCRIPT