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; }