From: Simon Kelley Date: Sun, 22 Jan 2012 16:05:15 +0000 (+0000) Subject: It compiles and it allocates a lease! No renewals yet. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=52b92f4db8133c4b9957ab8e74720eb2cb3207e0;p=people%2Fms%2Fdnsmasq.git It compiles and it allocates a lease! No renewals yet. --- diff --git a/src/NOTES b/src/NOTES index 7f084af..63d09bb 100644 --- a/src/NOTES +++ b/src/NOTES @@ -1,2 +1,15 @@ 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/dhcp.c b/src/dhcp.c index f9febc6..d6cf9ce 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -120,8 +120,7 @@ void dhcp_init(void) check_dhcp_hosts(1); - daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet); - daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len); + expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet)); } ssize_t recv_dhcp_packet(int fd, struct msghdr *msg) diff --git a/src/dhcp6.c b/src/dhcp6.c index 3aec5ce..4b8a29d 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -24,10 +24,10 @@ struct iface_param { }; static int join_multicast(struct in6_addr *local, int prefix, - int scope, int if_index, void *vparam); + int scope, int if_index, int dad, void *vparam); static int complete_context6(struct in6_addr *local, int prefix, - int scope, int if_index, void *vparam); + int scope, int if_index, int dad, void *vparam); void dhcp6_init(void) { @@ -58,10 +58,15 @@ void dhcp6_init(void) 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, - int scope, int if_index, void *vparam) + int scope, int if_index, int dad, void *vparam) { char ifrn_name[IFNAMSIZ]; struct ipv6_mreq mreq; @@ -71,7 +76,10 @@ static int join_multicast(struct in6_addr *local, int prefix, struct iname *tmp; (void)prefix; - (void)scope; /* warnings */ + + /* scope == link */ + if (scope != 253) + return 1; if (!indextoname(fd, if_index, ifrn_name)) return 0; @@ -85,7 +93,7 @@ static int join_multicast(struct in6_addr *local, int prefix, return 1; /* weird libvirt-inspired access control */ - for (context = daemon->dhcp; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) if (!context->interface || strcmp(context->interface, ifrn_name) == 0) break; @@ -93,14 +101,14 @@ static int join_multicast(struct in6_addr *local, int prefix, return 1; mreq.ipv6mr_interface = if_index; - inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &maddr); + inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr); - if (!setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) + if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) return 0; - inet_pton(AF_INET6, ALL_SERVERS, &maddr); + inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr); - if (!setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) + if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) return 0; return 1; @@ -135,7 +143,7 @@ void dhcp6_packet(time_t now) msg.msg_iov = &daemon->dhcp_packet; msg.msg_iovlen = 1; - if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg) == -1) || sz <= 4) + if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1 || sz <= 4) return; for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) @@ -153,7 +161,7 @@ void dhcp6_packet(time_t now) if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name)) return; -ls -l + if (!iface_check(AF_INET6, (struct all_addr *)&dest, ifr.ifr_name)) return; @@ -162,7 +170,7 @@ ls -l return; /* weird libvirt-inspired access control */ - for (context = daemon->dhcp; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0) break; @@ -170,7 +178,7 @@ ls -l return; /* unlinked contexts are marked by context->current == context */ - for (context = daemon->dhcp; context; context = context->next) + for (context = daemon->dhcp6; context; context = context->next) context->current = context; parm.current = NULL; @@ -182,7 +190,7 @@ ls -l lease_prune(NULL, now); /* lose any expired leases */ msg.msg_iov = &daemon->dhcp_packet; - sz = dhcp6_reply(parm.current, sz); + sz = dhcp6_reply(parm.current, sz, now); /* ifr.ifr_name, if_index, (size_t)sz, now, unicast_dest, &is_inform, pxe_fd, iface_addr); */ lease_update_file(now); @@ -193,15 +201,15 @@ ls -l } static int complete_context6(struct in6_addr *local, int prefix, - int scope, int if_index, void *vparam) + int scope, int if_index, int dad, void *vparam) { struct dhcp_context *context; struct iface_param *param = vparam; - + (void)scope; /* warning */ + for (context = daemon->dhcp6; context; context = context->next) { - if ((context->flags & CONTEXT_IPV6) && - prefix == context->prefix && + if (prefix == context->prefix && is_same_net6(local, &context->start6, prefix) && is_same_net6(local, &context->end6, prefix)) { @@ -215,6 +223,141 @@ static int complete_context6(struct in6_addr *local, int prefix, } return 1; } + +struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr) +{ + struct dhcp_config *config; + + for (config = configs; config; config = config->next) + if ((config->flags & CONFIG_ADDR6) && + is_same_net6(&config->addr6, net, prefix) && + (prefix == 128 || addr6part(&config->addr6) == addr)) + return config; + + return NULL; +} + +int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, + 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. + Try to return from contexts which match netids first. + + Note that we assume the address prefix lengths are 64 or greater, so we can + get by with 64 bit arithmetic. +*/ + + u64 start, addr; + struct dhcp_context *c, *d; + int i, pass; + u64 j; + + /* hash hwaddr: use the SDBM hashing algorithm. This works + for MAC addresses, let's see how it manages with client-ids! */ + for (j = 0, i = 0; i < clid_len; i++) + j += clid[i] + (j << 6) + (j << 16) - j; + + for (pass = 0; pass <= 1; pass++) + for (c = context; c; c = c->current) + if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) + continue; + else if (!match_netid(c->filter, netids, pass)) + continue; + else + { + start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6))); + + /* iterate until we find a free address. */ + addr = start; + + do { + /* eliminate addresses in use by the server. */ + for (d = context; d; d = d->current) + if (addr == addr6part(&d->router6)) + break; + + if (!d && + !lease6_find_by_addr(&c->start6, c->prefix, addr) && + !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr)) + { + *ans = c->start6; + setaddr6part (ans, addr); + return 1; + } + + addr++; + + if (addr == addr6part(&c->end6) + 1) + addr = addr6part(&c->start6); + + } while (addr != start); + } + + return 0; +} + +struct dhcp_context *address6_available(struct dhcp_context *context, + struct in6_addr *taddr, + struct dhcp_netid *netids) +{ + u64 start, end, addr = addr6part(taddr); + struct dhcp_context *tmp; + + for (tmp = context; tmp; tmp = tmp->current) + { + start = addr6part(&tmp->start6); + end = addr6part(&tmp->end6); + + if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) && + is_same_net6(&context->start6, taddr, context->prefix) && + is_same_net6(&context->end6, taddr, context->prefix) && + addr >= start && + addr <= end && + match_netid(tmp->filter, netids, 1)) + return tmp; + } + + return NULL; +} + +struct dhcp_context *narrow_context6(struct dhcp_context *context, + struct in6_addr *taddr, + struct dhcp_netid *netids) +{ + /* We start of with a set of possible contexts, all on the current physical interface. + These are chained on ->current. + Here we have an address, and return the actual context correponding to that + address. Note that none may fit, if the address came a dhcp-host and is outside + any dhcp-range. In that case we return a static range if possible, or failing that, + any context on the correct subnet. (If there's more than one, this is a dodgy + configuration: maybe there should be a warning.) */ + + struct dhcp_context *tmp; + + if (!(tmp = address6_available(context, taddr, netids))) + { + for (tmp = context; tmp; tmp = tmp->current) + if (match_netid(tmp->filter, netids, 1) && + is_same_net6(taddr, &tmp->start6, tmp->prefix) && + (tmp->flags & CONTEXT_STATIC)) + break; + + if (!tmp) + for (tmp = context; tmp; tmp = tmp->current) + if (match_netid(tmp->filter, netids, 1) && + is_same_net6(taddr, &tmp->start6, tmp->prefix) && + !(tmp->flags & CONTEXT_PROXY)) + break; + } + + /* Only one context allowed now */ + if (tmp) + tmp->current = NULL; + + return tmp; +} + #endif diff --git a/src/dhcp6_protocol.h b/src/dhcp6_protocol.h index 262e825..590f7b5 100644 --- a/src/dhcp6_protocol.h +++ b/src/dhcp6_protocol.h @@ -53,3 +53,12 @@ #define OPTION6_INTERFACE_ID 18 #define OPTION6_RECONFIGURE_MSG 19 #define OPTION6_RECONF_ACCEPT 20 + + +#define DHCP6SUCCESS 0 +#define DHCP6UNSPEC 1 +#define DHCP6NOADDRS 2 +#define DHCP6NOBINDING 3 +#define DHCP6NOTONLINK 4 +#define DHCP6USEMULTI 5 + diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 0b17df3..18d0514 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -87,7 +87,7 @@ int main (int argc, char **argv) #ifdef HAVE_DHCP if (!daemon->lease_file) { - if (daemon->dhcp) + if (daemon->dhcp || daemon->dhcp6) daemon->lease_file = LEASEFILE; } #endif @@ -137,13 +137,18 @@ int main (int argc, char **argv) now = dnsmasq_time(); #ifdef HAVE_DHCP - if (daemon->dhcp) + if (daemon->dhcp || daemon->dhcp6) { /* 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. */ lease_init(now); - dhcp_init(); + if (daemon->dhcp) + dhcp_init(); +#ifdef HAVE_DHCP6 + if (daemon->dhcp6) + dhcp6_init(); +#endif } #endif @@ -189,7 +194,7 @@ int main (int argc, char **argv) #if defined(HAVE_SCRIPT) /* Note getpwnam returns static storage */ - if (daemon->dhcp && daemon->scriptuser && + if ((daemon->dhcp || daemon->dhcp6) && daemon->scriptuser && (daemon->lease_change_command || daemon->luascript)) { if ((ent_pw = getpwnam(daemon->scriptuser))) @@ -341,7 +346,7 @@ int main (int argc, char **argv) /* if we are to run scripts, we need to fork a helper before dropping root. */ daemon->helperfd = -1; #ifdef HAVE_SCRIPT - if (daemon->dhcp && (daemon->lease_change_command || daemon->luascript)) + if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript)) daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); #endif @@ -481,25 +486,51 @@ int main (int argc, char **argv) my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs); #ifdef HAVE_DHCP - if (daemon->dhcp) + if (daemon->dhcp || daemon->dhcp6) { struct dhcp_context *dhcp_tmp; - - for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) + int family = AF_INET; + dhcp_tmp = daemon->dhcp; + + again: + for (; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) { + void *start = &dhcp_tmp->start; + void *end = &dhcp_tmp->end; + +#ifdef HAVE_DHCP6 + if (family == AF_INET6) + { + start = &dhcp_tmp->start6; + end = &dhcp_tmp->end6; + } +#endif + prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time); - strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start)); + inet_ntop(family, start, daemon->dhcp_buff, 256); + inet_ntop(family, end, daemon->dhcp_buff3, 256); my_syslog(MS_DHCP | LOG_INFO, (dhcp_tmp->flags & CONTEXT_STATIC) ? _("DHCP, static leases only on %.0s%s, lease time %s") : (dhcp_tmp->flags & CONTEXT_PROXY) ? _("DHCP, proxy on subnet %.0s%s%.0s") : _("DHCP, IP range %s -- %s, lease time %s"), - daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2); + daemon->dhcp_buff, daemon->dhcp_buff3, daemon->dhcp_buff2); + } + +#ifdef HAVE_DHCP6 + if (family == AF_INET) + { + family = AF_INET6; + dhcp_tmp = daemon->dhcp6; + goto again; } +#endif + } #endif + #ifdef HAVE_TFTP if (daemon->tftp_unlimited || daemon->tftp_interfaces) { @@ -604,6 +635,14 @@ int main (int argc, char **argv) } #endif +#ifdef HAVE_DHCP6 + if (daemon->dhcp6) + { + FD_SET(daemon->dhcp6fd, &rset); + bump_maxfd(daemon->dhcp6fd, &maxfd); + } +#endif + #ifdef HAVE_LINUX_NETWORK FD_SET(daemon->netlinkfd, &rset); bump_maxfd(daemon->netlinkfd, &maxfd); @@ -699,6 +738,14 @@ int main (int argc, char **argv) dhcp_packet(now, 1); } +#ifdef HAVE_DHCP6 + if (daemon->dhcp6) + { + if (FD_ISSET(daemon->dhcp6fd, &rset)) + dhcp6_packet(now); + } +#endif + # ifdef HAVE_SCRIPT if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset)) helper_write(); @@ -860,7 +907,7 @@ static void async_event(int pipe, time_t now) case EVENT_ALARM: #ifdef HAVE_DHCP - if (daemon->dhcp) + if (daemon->dhcp || daemon->dhcp6) { lease_prune(NULL, now); lease_update_file(now); @@ -1018,7 +1065,7 @@ void clear_cache_and_reload(time_t now) cache_reload(); #ifdef HAVE_DHCP - if (daemon->dhcp) + if (daemon->dhcp || daemon->dhcp6) { if (option_bool(OPT_ETHERS)) dhcp_read_ethers(); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 62b06d0..bd18296 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -54,6 +54,7 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; +typedef unsigned long long u64; #include "dns_protocol.h" #include "dhcp_protocol.h" @@ -448,8 +449,8 @@ struct dhcp_lease { #ifdef HAVE_BROKEN_RTC unsigned int length; #endif - int hwaddr_len, hwaddr_type; - unsigned char hwaddr[DHCP_CHADDR_MAX]; + int hwaddr_len, hwaddr_type; /* hw_type used for iaid in v6 */ + unsigned char hwaddr[DHCP_CHADDR_MAX]; /* also IPv6 address */ struct in_addr addr, override, giaddr; unsigned char *extradata; unsigned int extradata_len, extradata_size; @@ -489,6 +490,9 @@ struct dhcp_config { unsigned char *clid; /* clientid */ char *hostname, *domain; struct dhcp_netid_list *netid; +#ifdef HAVE_DHCP6 + struct in6_addr addr6; +#endif struct in_addr addr; time_t decline_time; unsigned int lease_time; @@ -506,6 +510,7 @@ struct dhcp_config { #define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */ #define CONFIG_DECLINED 1024 /* address declined by client */ #define CONFIG_BANK 2048 /* from dhcp hosts file */ +#define CONFIG_ADDR6 4096 struct dhcp_opt { int opt, len, flags; @@ -588,6 +593,7 @@ struct dhcp_context { struct in_addr start, end; /* range of available addresses */ #ifdef HAVE_DHCP6 struct in6_addr start6, end6; /* range of available addresses */ + struct in6_addr local6, router6; int prefix; #endif int flags; @@ -600,7 +606,6 @@ struct dhcp_context { #define CONTEXT_NETMASK 2 #define CONTEXT_BRDCAST 4 #define CONTEXT_PROXY 8 -#define CONTEXT_IPV6 16 struct ping_result { struct in_addr addr; @@ -681,7 +686,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; + struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6; struct dhcp_vendor *dhcp_vendors; struct dhcp_mac *dhcp_macs; struct dhcp_boot *boot_config; @@ -814,6 +819,8 @@ time_t dnsmasq_time(void); int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask); #ifdef HAVE_IPV6 int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen); +u64 addr6part(struct in6_addr *addr); +void setaddr6part(struct in6_addr *addr, u64 host); #endif int retry_send(void); void prettyprint_time(char *buf, unsigned int t); @@ -908,9 +915,11 @@ char *get_domain(struct in_addr addr); void lease_update_file(time_t now); void lease_update_dns(); void lease_init(time_t now); -struct dhcp_lease *lease_allocate4(struct in_addr addr); +struct dhcp_lease *lease4_allocate(struct in_addr addr); #ifdef HAVE_DHCP6 -struct dhcp_lease *lease_allocate6(struct in6_addr *addrp); +struct dhcp_lease *lease6_allocate(struct in6_addr *addrp); +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); @@ -991,8 +1000,22 @@ int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, int istcp, unsigned int *markp); #endif +/* dhcp6.c */ +#ifdef HAVE_DHCP6 +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); +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); +#endif + /* rfc3315.c */ #ifdef HAVE_DHCP6 void make_duid(time_t now); -size_t dhcp6_reply(struct dhcp_context *context, size_t sz); +size_t dhcp6_reply(struct dhcp_context *context, size_t sz, time_t now); #endif diff --git a/src/lease.c b/src/lease.c index 747f451..96cfeb3 100644 --- a/src/lease.c +++ b/src/lease.c @@ -85,7 +85,9 @@ void lease_init(time_t now) daemon->dhcp_buff, daemon->packet) == 5) { #ifdef HAVE_DHCP6 - if (!v6pass) + if (v6pass) + hw_type = atoi(daemon->dhcp_buff2); + else #endif { hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); @@ -107,10 +109,10 @@ void lease_init(time_t now) #ifdef HAVE_DHCP6 if (v6pass) - lease = lease_allocate6(&addr.addr.addr6); + lease = lease6_allocate(&addr.addr.addr6); else #endif - lease = lease_allocate4(addr.addr.addr4); + lease = lease4_allocate(addr.addr.addr4); if (!lease) die (_("too many stored leases"), NULL, EC_MISC); @@ -288,7 +290,7 @@ void lease_update_file(time_t now) inet_ntop(AF_INET6, lease->hwaddr, daemon->addrbuff, ADDRSTRLEN); - ourprintf(&err, "* %s ", daemon->addrbuff); + ourprintf(&err, "%u %s ", lease->hwaddr_type, daemon->addrbuff); ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); if (lease->clid && lease->clid_len != 0) @@ -431,6 +433,44 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr) return NULL; } +#ifdef HAVE_DHCP6 +struct dhcp_lease *lease6_find_by_client(unsigned char *clid, int clid_len, int iaid) +{ + struct dhcp_lease *lease; + + for (lease = leases; lease; lease = lease->next) + { + if (!lease->is_ipv6) + continue; + + if (lease->hwaddr_type == iaid && + lease->clid && clid_len == lease->clid_len && + memcmp(clid, lease->clid, clid_len) == 0) + return lease; + } + + return NULL; +} + +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) + continue; + + if (is_same_net6((struct in6_addr *)lease->hwaddr, net, prefix) && + (prefix == 128 || addr6part((struct in6_addr *)lease->hwaddr) == addr)) + return lease; + } + + return NULL; +} + +#endif + /* Find largest assigned address in context */ struct in_addr lease_find_max_addr(struct dhcp_context *context) { @@ -474,7 +514,7 @@ static struct dhcp_lease *lease_allocate(void) return lease; } -struct dhcp_lease *lease_allocate4(struct in_addr addr) +struct dhcp_lease *lease4_allocate(struct in_addr addr) { struct dhcp_lease *lease = lease_allocate(); lease->addr = addr; @@ -484,7 +524,7 @@ struct dhcp_lease *lease_allocate4(struct in_addr addr) } #ifdef HAVE_DHCP6 -struct dhcp_lease *lease_allocate6(struct in6_addr *addrp) +struct dhcp_lease *lease6_allocate(struct in6_addr *addrp) { struct dhcp_lease *lease = lease_allocate(); memcpy(lease->hwaddr, addrp, sizeof(*addrp)) ; diff --git a/src/netlink.c b/src/netlink.c index 7f1aae4..8aca1d1 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -230,8 +230,8 @@ int iface_enumerate(int family, void *parm, int (*callback)()) } if (addrp) - if (!((*callback)(addrp, ifa->ifa_prefixlen, ifa->ifa_index, - ifa->ifa_index, ifa->ifa_flags & IFA_F_TENTATIVE, parm))) + if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), + (int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm))) return 0; } #endif diff --git a/src/network.c b/src/network.c index e1212d9..32bd517 100644 --- a/src/network.c +++ b/src/network.c @@ -274,9 +274,10 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix, { union mysockaddr addr; struct in_addr netmask; /* dummy */ - - (void)prefix; /* warning */ netmask.s_addr = 0; + + (void)prefix; /* warning */ + (void)scope; /* warning */ memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN @@ -285,7 +286,7 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix, addr.in6.sin6_family = AF_INET6; addr.in6.sin6_addr = *local; addr.in6.sin6_port = htons(daemon->port); - addr.in6.sin6_scope_id = scope; + addr.in6.sin6_scope_id = if_index; return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, dad); } diff --git a/src/option.c b/src/option.c index 6a6cea9..b0ae1e8 100644 --- a/src/option.c +++ b/src/option.c @@ -1870,18 +1870,9 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) char *cp, *a[5] = { NULL, NULL, NULL, NULL, NULL }; struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context)); - new->next = daemon->dhcp; + memset (new, 0, sizeof(*new)); new->lease_time = DEFLEASE; - new->addr_epoch = 0; - new->netmask.s_addr = 0; - new->broadcast.s_addr = 0; - new->router.s_addr = 0; - new->local.s_addr = 0; - new->netid.net = NULL; - new->filter = NULL; - new->flags = 0; - new->interface = NULL; - + gen_prob = _("bad dhcp-range"); if (!arg) @@ -1893,7 +1884,9 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) while(1) { for (cp = arg; *cp; cp++) - if (!(*cp == ' ' || *cp == '.' || (*cp >='0' && *cp <= '9'))) + if (!(*cp == ' ' || *cp == '.' || *cp == ':' || + (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') || + (*cp >='0' && *cp <= '9'))) break; if (*cp != ',' && (comma = split(arg))) @@ -1929,44 +1922,86 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line) if (!(a[k] = split(a[k-1]))) break; - if ((k < 2) || ((new->start.s_addr = inet_addr(a[0])) == (in_addr_t)-1)) - option = '?'; - else if (strcmp(a[1], "static") == 0) - { - new->end = new->start; - new->flags |= CONTEXT_STATIC; - } - else if (strcmp(a[1], "proxy") == 0) - { - new->end = new->start; - new->flags |= CONTEXT_PROXY; - } - else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1) + if (k < 2) option = '?'; - - if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr)) + else if (inet_pton(AF_INET, a[0], &new->start)) { - struct in_addr tmp = new->start; - new->start = new->end; - new->end = tmp; + new->next = daemon->dhcp; + daemon->dhcp = new; + if (strcmp(a[1], "static") == 0) + { + new->end = new->start; + new->flags |= CONTEXT_STATIC; + } + else if (strcmp(a[1], "proxy") == 0) + { + new->end = new->start; + new->flags |= CONTEXT_PROXY; + } + else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1) + option = '?'; + + if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr)) + { + struct in_addr tmp = new->start; + new->start = new->end; + new->end = tmp; + } + + if (option != '?' && k >= 3 && strchr(a[2], '.') && + ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1)) + { + new->flags |= CONTEXT_NETMASK; + leasepos = 3; + if (!is_same_net(new->start, new->end, new->netmask)) + problem = _("inconsistent DHCP range"); + } + + if (k >= 4 && strchr(a[3], '.') && + ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1)) + { + new->flags |= CONTEXT_BRDCAST; + leasepos = 4; + } } - - if (option != '?' && k >= 3 && strchr(a[2], '.') && - ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1)) +#ifdef HAVE_DHCP6 + else if (inet_pton(AF_INET6, a[0], &new->start6)) { - new->flags |= CONTEXT_NETMASK; - leasepos = 3; - if (!is_same_net(new->start, new->end, new->netmask)) + new->next = daemon->dhcp6; + new->prefix = 64; /* default */ + daemon->dhcp6 = new; + if (strcmp(a[1], "static") == 0) + { + new->end = new->start; + new->flags |= CONTEXT_STATIC; + } + else if (!inet_pton(AF_INET6, a[1], &new->end6)) + option = '?'; + + /* bare integer < 128 is prefix value */ + if (option != '?' && k >= 3) + { + int pref; + for (cp = a[2]; *cp; cp++) + if (!(*cp >= '0' && *cp <= '9')) + break; + if (!*cp && (pref = atoi(a[2])) <= 128) + { + new->prefix = pref; + leasepos = 3; + } + } + if (!is_same_net6(&new->start6, &new->end6, new->prefix)) problem = _("inconsistent DHCP range"); + + if (addr6part(&new->start6) > addr6part(&new->end6)) + { + struct in6_addr tmp = new->start6; + new->start6 = new->end6; + new->end6 = tmp; + } } - daemon->dhcp = new; - - if (k >= 4 && strchr(a[3], '.') && - ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1)) - { - new->flags |= CONTEXT_BRDCAST; - leasepos = 4; - } +#endif if (k >= leasepos+1) { diff --git a/src/rfc2131.c b/src/rfc2131.c index 4042724..0e075f9 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -481,7 +481,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, if (!message && !lease && - (!(lease = lease_allocate4(mess->yiaddr)))) + (!(lease = lease4_allocate(mess->yiaddr)))) message = _("no leases left"); if (!message) @@ -1189,7 +1189,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, else if (!lease) { - if ((lease = lease_allocate4(mess->yiaddr))) + if ((lease = lease4_allocate(mess->yiaddr))) do_classes = 1; else message = _("no leases left"); diff --git a/src/rfc3315.c b/src/rfc3315.c index bb9df69..ae90eea 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -23,6 +23,7 @@ static size_t outpacket_counter; 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); void make_duid(time_t now) { @@ -96,9 +97,8 @@ void *opt6_next(void *opts, void *end) return opts + opt_len; } - -#define opt6_len(opt) ((int)(((unsigned short *)(opt))[1])) -#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4u+(unsigned int)(i)])) +#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) @@ -115,40 +115,73 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size) } /* - daemon->outpacket_counter = 4; message type and ID - - elapsed time: - int o = new_opt(OPTION_ELAPSED_TIME); - put_opt_short(o, 100) + 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); - IA_NA + + to go back and fill in fields int o = new_opt(OPTION_IA_NA); - put_opt_long(o, IAID); - put_opt_long(o, T1); - put_opt_long(0, T2); + 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); + */ -void end_opt6(int container) +static void end_opt6(int container) { void *p = daemon->outpacket.iov_base + container + 2; u16 len = outpacket_counter - container - 4 ; PUTSHORT(len, p); } + +static int save_counter(int newval) +{ + int ret = outpacket_counter; + if (newval != -1) + outpacket_counter = newval; + + return ret; +} + + static void *expand(size_t headroom) { @@ -164,7 +197,7 @@ static void *expand(size_t headroom) return NULL; } -int new_opt6(int opt) +static int new_opt6(int opt) { int ret = outpacket_counter; void *p; @@ -181,7 +214,7 @@ int new_opt6(int opt) -void *put_opt6(void *data, size_t len) +static void *put_opt6(void *data, size_t len) { void *p; @@ -191,15 +224,15 @@ void *put_opt6(void *data, size_t len) return p; } -void put_opt6_long(unsigned int val) +static void put_opt6_long(unsigned int val) { void *p; if (( p = expand(4))) - PUTLONG(p, val); + PUTLONG(val, p); } -void put_opt6_short(unsigned int val) +static void put_opt6_short(unsigned int val) { void *p; @@ -207,92 +240,231 @@ void put_opt6_short(unsigned int val) PUTSHORT(val, p); } -void put_opt6_byte(unsigned int val) +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) +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; - - outpacket_counter = 4; /* skip message type and transaction-id */ + 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; + return 0; + clid = opt6_ptr(opt, 0); + clid_len = opt6_len(opt); o = new_opt6(OPTION6_CLIENT_ID); - put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); + 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); - - if ((opt = opt6_find(packet_options, end, OPTION6_IA_NA, 12))) - { - while (opt = opt6_find(opt, 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 iaid = opt6_uint(opt, 0, 4); - unsigned int t1 = opt6_uint(opt, 4, 4); - unsigned int t2 = opt6_uint(opt, 8, 4); - - - if (ia_option) - while ((ia_option = ia_option, ia_end, OPTION6_IAADDR, 24)) + + 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) { - /* do address option */ + struct in6_addr alloced_addr, *addrp = NULL; - ia_option = opt6_next(ia_option, ia_end); - } - else - { - /* no preferred address call address allocate */ - - } - - opt = opt6_next(opt, end); - } - } - else if ((opt = opt6_find(packet_options, end, OPTION6_IA_TA, 4))) - while (opt = opt6_find(opt, end, OPTION6_IA_TA, 4)) - { - void *ia_end = opt6_ptr(opt, opt6_len(opt)); - void *ia_option = opt6_find(opt6_ptr(opt, 4), ia_end, OPTION6_IAADDR, 24); - - unsigned int iaid = opt6_uint(opt, 0, 4); + 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; + } + } + } - if (ia_option) - while ((ia_option = ia_option, ia_end, OPTION6_IAADDR, 24)) - { - /* do address option */ - - ia_option = opt6_next(ia_option, ia_end); - } - else + /* same again for TA */ + for (opt = packet_options; opt; opt = opt6_find(opt6_next(opt, end), end, OPTION6_IA_TA, 4)) { - /* no preferred address */ - } - - opt = opt6_next(opt, end); + + do_options6(context, opt6_find(packet_options, end, OPTION6_ORO, 0)); + + } - else - return; /* no IA_NA and no IA_TA */ + + } + + 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 diff --git a/src/util.c b/src/util.c index 6769353..789bdd4 100644 --- a/src/util.c +++ b/src/util.c @@ -336,6 +336,29 @@ int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen) return 0; } +/* return least signigicant 64 bits if IPv6 address */ +u64 addr6part(struct in6_addr *addr) +{ + int i; + u64 ret = 0; + + for (i = 8; i < 16; i++) + ret = (ret << 8) + addr->s6_addr[i]; + + return ret; +} + +void setaddr6part(struct in6_addr *addr, u64 host) +{ + int i; + + for (i = 15; i >= 8; i--) + { + addr->s6_addr[i] = host; + host = host >> 8; + } +} + #endif