diff options
Diffstat (limited to 'src/dhcp-common.c')
-rw-r--r-- | src/dhcp-common.c | 905 |
1 files changed, 905 insertions, 0 deletions
diff --git a/src/dhcp-common.c b/src/dhcp-common.c new file mode 100644 index 0000000..bc48f41 --- /dev/null +++ b/src/dhcp-common.c @@ -0,0 +1,905 @@ +/* dnsmasq is Copyright (c) 2000-2015 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 <http://www.gnu.org/licenses/>. +*/ + +#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 == msg->msg_iov->iov_len) + { + if (!expand_buf(msg->msg_iov, sz + 100)) + return -1; + } + else + { + expand_buf(msg->msg_iov, 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; + struct dhcp_opt *tmp; + + /* 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 priority 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); + + /* reset stuff with tag:!<tag> which now matches. */ + for (opt = opts; opt; opt = opt->next) + if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) && + (opt->flags & DHOPT_TAGOK) && + !match_netid(opt->netid, tagif, 0)) + opt->flags &= ~DHOPT_TAGOK; + + 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) + { + 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); + } + + /* Finally, eliminate duplicate options later in the chain, and therefore earlier in the config file. */ + for (opt = opts; opt; opt = opt->next) + if (opt->flags & DHOPT_TAGOK) + for (tmp = opt->next; tmp; tmp = tmp->next) + if (tmp->opt == opt->opt) + tmp->flags &= ~DHOPT_TAGOK; + + 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); + } +} + +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; +} + +int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type) +{ + struct hwaddr_config *conf_addr; + + for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next) + if (conf_addr->wildcard_mask == 0 && + conf_addr->hwaddr_len == len && + (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) && + memcmp(conf_addr->hwaddr, hwaddr, len) == 0) + return 1; + + return 0; +} + +static int is_config_in_context(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_ADDR | CONFIG_ADDR6))) + return 1; + +#ifdef HAVE_DHCP6 + if ((context->flags & CONTEXT_V6) && (config->flags & CONFIG_WILDCARD)) + return 1; +#endif + + for (; context; context = context->current) +#ifdef HAVE_DHCP6 + if (context->flags & CONTEXT_V6) + { + if ((config->flags & CONFIG_ADDR6) && is_same_net6(&config->addr6, &context->start6, context->prefix)) + return 1; + } + else +#endif + if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask)) + return 1; + + return 0; +} + +struct dhcp_config *find_config(struct dhcp_config *configs, + struct dhcp_context *context, + unsigned char *clid, int clid_len, + unsigned char *hwaddr, int hw_len, + int hw_type, char *hostname) +{ + int count, new; + struct dhcp_config *config, *candidate; + struct hwaddr_config *conf_addr; + + if (clid) + for (config = configs; config; config = config->next) + if (config->flags & CONFIG_CLID) + { + if (config->clid_len == clid_len && + memcmp(config->clid, clid, clid_len) == 0 && + is_config_in_context(context, config)) + return config; + + /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and + cope with that here. This is IPv4 only. context==NULL implies IPv4, + see lease_update_from_configs() */ + if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1 && + memcmp(config->clid, clid+1, clid_len-1) == 0 && + is_config_in_context(context, config)) + return config; + } + + + if (hwaddr) + for (config = configs; config; config = config->next) + if (config_has_mac(config, hwaddr, hw_len, hw_type) && + is_config_in_context(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_config_in_context(context, config)) + return config; + + + if (!hwaddr) + return NULL; + + /* use match with fewest wildcard octets */ + for (candidate = NULL, count = 0, config = configs; config; config = config->next) + if (is_config_in_context(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; +} + +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, *conf_tmp; + 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 && + (!(conf_tmp = config_find_by_address(configs, crec->addr.addr.addr.addr4)) || conf_tmp == config)) + { + config->addr = crec->addr.addr.addr.addr4; + config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS; + continue; + } + +#ifdef HAVE_DHCP6 + if (prot == AF_INET6 && + (!(conf_tmp = config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) || conf_tmp == config)) + { + memcpy(&config->addr6, &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 + +} + +#ifdef HAVE_LINUX_NETWORK +char *whichdevice(void) +{ + /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE + to that device. This is for the use case of (eg) OpenStack, which runs a new + dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE, + individual processes don't always see the packets they should. + SO_BINDTODEVICE is only available Linux. + + Note that if wildcards are used in --interface, or --interface is not used at all, + or a configured interface doesn't yet exist, then more interfaces may arrive later, + so we can't safely assert there is only one interface and proceed. +*/ + + struct irec *iface, *found; + struct iname *if_tmp; + + if (!daemon->if_names) + return NULL; + + for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) + if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*'))) + return NULL; + + for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next) + if (iface->dhcp_ok) + { + if (!found) + found = iface; + else if (strcmp(found->name, iface->name) != 0) + return NULL; /* more than one. */ + } + + if (found) + return found->name; + + return NULL; +} + +void bindtodevice(char *device, int fd) +{ + struct ifreq ifr; + + strcpy(ifr.ifr_name, device); + /* only allowed by root. */ + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 && + errno != EPERM) + die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET); +} +#endif + +static const struct opttab_t { + char *name; + u16 val, size; +} opttab[] = { + { "netmask", 1, OT_ADDR_LIST }, + { "time-offset", 2, 4 }, + { "router", 3, OT_ADDR_LIST }, + { "dns-server", 6, OT_ADDR_LIST }, + { "log-server", 7, OT_ADDR_LIST }, + { "lpr-server", 9, OT_ADDR_LIST }, + { "hostname", 12, OT_INTERNAL | OT_NAME }, + { "boot-file-size", 13, 2 | OT_DEC }, + { "domain-name", 15, OT_NAME }, + { "swap-server", 16, OT_ADDR_LIST }, + { "root-path", 17, OT_NAME }, + { "extension-path", 18, OT_NAME }, + { "ip-forward-enable", 19, 1 }, + { "non-local-source-routing", 20, 1 }, + { "policy-filter", 21, OT_ADDR_LIST }, + { "max-datagram-reassembly", 22, 2 | OT_DEC }, + { "default-ttl", 23, 1 | OT_DEC }, + { "mtu", 26, 2 | OT_DEC }, + { "all-subnets-local", 27, 1 }, + { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST }, + { "router-discovery", 31, 1 }, + { "router-solicitation", 32, OT_ADDR_LIST }, + { "static-route", 33, OT_ADDR_LIST }, + { "trailer-encapsulation", 34, 1 }, + { "arp-timeout", 35, 4 | OT_DEC }, + { "ethernet-encap", 36, 1 }, + { "tcp-ttl", 37, 1 }, + { "tcp-keepalive", 38, 4 | OT_DEC }, + { "nis-domain", 40, OT_NAME }, + { "nis-server", 41, OT_ADDR_LIST }, + { "ntp-server", 42, OT_ADDR_LIST }, + { "vendor-encap", 43, OT_INTERNAL }, + { "netbios-ns", 44, OT_ADDR_LIST }, + { "netbios-dd", 45, OT_ADDR_LIST }, + { "netbios-nodetype", 46, 1 }, + { "netbios-scope", 47, 0 }, + { "x-windows-fs", 48, OT_ADDR_LIST }, + { "x-windows-dm", 49, OT_ADDR_LIST }, + { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST }, + { "lease-time", 51, OT_INTERNAL | OT_TIME }, + { "option-overload", 52, OT_INTERNAL }, + { "message-type", 53, OT_INTERNAL | OT_DEC }, + { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST }, + { "parameter-request", 55, OT_INTERNAL }, + { "message", 56, OT_INTERNAL }, + { "max-message-size", 57, OT_INTERNAL }, + { "T1", 58, OT_TIME}, + { "T2", 59, OT_TIME}, + { "vendor-class", 60, 0 }, + { "client-id", 61, OT_INTERNAL }, + { "nis+-domain", 64, OT_NAME }, + { "nis+-server", 65, OT_ADDR_LIST }, + { "tftp-server", 66, OT_NAME }, + { "bootfile-name", 67, OT_NAME }, + { "mobile-ip-home", 68, OT_ADDR_LIST }, + { "smtp-server", 69, OT_ADDR_LIST }, + { "pop3-server", 70, OT_ADDR_LIST }, + { "nntp-server", 71, OT_ADDR_LIST }, + { "irc-server", 74, OT_ADDR_LIST }, + { "user-class", 77, 0 }, + { "FQDN", 81, OT_INTERNAL }, + { "agent-id", 82, OT_INTERNAL }, + { "client-arch", 93, 2 | OT_DEC }, + { "client-interface-id", 94, 0 }, + { "client-machine-id", 97, 0 }, + { "subnet-select", 118, OT_INTERNAL }, + { "domain-search", 119, OT_RFC1035_NAME }, + { "sip-server", 120, 0 }, + { "classless-static-route", 121, 0 }, + { "vendor-id-encap", 125, 0 }, + { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */ + { NULL, 0, 0 } +}; + +#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 | OT_DEC }, + { "unicast", 12, OT_INTERNAL }, + { "status", 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 }, + { "information-refresh-time", 32, OT_TIME }, + { "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 + + + +void display_opts(void) +{ + int i; + + printf(_("Known DHCP options:\n")); + + for (i = 0; opttab[i].name; i++) + if (!(opttab[i].size & OT_INTERNAL)) + printf("%3d %s\n", opttab[i].val, opttab[i].name); +} + +#ifdef HAVE_DHCP6 +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 + +int lookup_dhcp_opt(int prot, char *name) +{ + const struct opttab_t *t; + int i; + + (void)prot; + +#ifdef HAVE_DHCP6 + if (prot == AF_INET6) + t = opttab6; + else +#endif + t = opttab; + + for (i = 0; t[i].name; i++) + if (strcasecmp(t[i].name, name) == 0) + return t[i].val; + + return -1; +} + +int lookup_dhcp_len(int prot, int val) +{ + const struct opttab_t *t; + int i; + + (void)prot; + +#ifdef HAVE_DHCP6 + if (prot == AF_INET6) + t = opttab6; + else +#endif + t = opttab; + + for (i = 0; t[i].name; i++) + if (val == t[i].val) + return t[i].size & ~OT_DEC; + + return 0; +} + +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 (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++] = '.'; + } + } + 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 if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0) + { + unsigned int dec = 0; + + for (i = 0; i < opt_len; i++) + dec = (dec << 8) | val[i]; + + if (ot[o].size & OT_TIME) + prettyprint_time(buf, dec); + else + sprintf(buf, "%u", dec); + } + else + nodecode = 1; + } + break; + } + + if (opt_len != 0 && buf && (!ot[o].name || nodecode)) + { + int trunc = 0; + if (opt_len > 14) + { + trunc = 1; + opt_len = 14; + } + print_mac(buf, val, opt_len); + if (trunc) + strncat(buf, "...", buf_len - strlen(buf)); + + + } + + return ot[o].name ? ot[o].name : ""; + +} + +void log_context(int family, struct dhcp_context *context) +{ + /* Cannot use dhcp_buff* for RA contexts */ + + void *start = &context->start; + void *end = &context->end; + char *template = "", *p = daemon->namebuff; + + *p = 0; + +#ifdef HAVE_DHCP6 + if (family == AF_INET6) + { + struct in6_addr subnet = context->start6; + if (!(context->flags & CONTEXT_TEMPLATE)) + setaddr6part(&subnet, 0); + inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN); + start = &context->start6; + end = &context->end6; + } +#endif + + if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE)) + strcpy(daemon->namebuff, _(", prefix deprecated")); + else + { + p += sprintf(p, _(", lease time ")); + prettyprint_time(p, context->lease_time); + p += strlen(p); + } + +#ifdef HAVE_DHCP6 + if (context->flags & CONTEXT_CONSTRUCTED) + { + char ifrn_name[IFNAMSIZ]; + + template = p; + p += sprintf(p, ", "); + + if (indextoname(daemon->icmp6fd, context->if_index, ifrn_name)) + sprintf(p, "%s for %s", (context->flags & CONTEXT_OLD) ? "old prefix" : "constructed", ifrn_name); + } + else if (context->flags & CONTEXT_TEMPLATE && !(context->flags & CONTEXT_RA_STATELESS)) + { + template = p; + p += sprintf(p, ", "); + + sprintf(p, "template for %s", context->template_interface); + } +#endif + + if (!(context->flags & CONTEXT_OLD) && + ((context->flags & CONTEXT_DHCP) || family == AF_INET)) + { +#ifdef HAVE_DHCP6 + if (context->flags & CONTEXT_RA_STATELESS) + { + if (context->flags & CONTEXT_TEMPLATE) + strncpy(daemon->dhcp_buff, context->template_interface, 256); + else + strcpy(daemon->dhcp_buff, daemon->addrbuff); + } + else +#endif + inet_ntop(family, start, daemon->dhcp_buff, 256); + inet_ntop(family, end, daemon->dhcp_buff3, 256); + my_syslog(MS_DHCP | LOG_INFO, + (context->flags & CONTEXT_RA_STATELESS) ? + _("%s stateless on %s%.0s%.0s%s") : + (context->flags & CONTEXT_STATIC) ? + _("%s, static leases only on %.0s%s%s%.0s") : + (context->flags & CONTEXT_PROXY) ? + _("%s, proxy on subnet %.0s%s%.0s%.0s") : + _("%s, IP range %s -- %s%s%.0s"), + (family != AF_INET) ? "DHCPv6" : "DHCP", + daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template); + } + +#ifdef HAVE_DHCP6 + if (context->flags & CONTEXT_TEMPLATE) + { + strcpy(daemon->addrbuff, context->template_interface); + template = ""; + } + + if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD)) + my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template); + + if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6)) + my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template); +#endif + +} + +void log_relay(int family, struct dhcp_relay *relay) +{ + inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN); + inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN); + + if (relay->interface) + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface); + else + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff); +} + +#endif |