From 2e47fc296d0cb9030f5445933c43d5acc2acd110 Mon Sep 17 00:00:00 2001 From: victek Date: Thu, 19 Sep 2013 13:20:11 +0200 Subject: [PATCH] dnsmasq: Update to v2.67test14. --- release/src/router/dnsmasq/.gitignore | 13 -- release/src/router/dnsmasq/CHANGELOG | 13 ++ release/src/router/dnsmasq/Makefile | 25 ++- release/src/router/dnsmasq/VERSION | 2 +- release/src/router/dnsmasq/bld/bloat-o-meter | 130 ++++++++++++++++ release/src/router/dnsmasq/debian/changelog | 1 + release/src/router/dnsmasq/debian/readme | 4 +- release/src/router/dnsmasq/debian/resolvconf | 15 +- release/src/router/dnsmasq/man/dnsmasq.8 | 32 ++++ release/src/router/dnsmasq/src/dhcp-common.c | 12 +- release/src/router/dnsmasq/src/dhcp.c | 206 ++++++++++++++++++------ release/src/router/dnsmasq/src/dhcp6.c | 225 ++++++++++++++++----------- release/src/router/dnsmasq/src/dnsmasq.c | 91 ++++++----- release/src/router/dnsmasq/src/dnsmasq.h | 24 ++- release/src/router/dnsmasq/src/netlink.c | 13 +- release/src/router/dnsmasq/src/network.c | 14 +- release/src/router/dnsmasq/src/option.c | 38 ++++- release/src/router/dnsmasq/src/outpacket.c | 4 +- release/src/router/dnsmasq/src/rfc3315.c | 170 +++++++++++++++++--- 19 files changed, 776 insertions(+), 256 deletions(-) delete mode 100644 release/src/router/dnsmasq/.gitignore create mode 100755 release/src/router/dnsmasq/bld/bloat-o-meter diff --git a/release/src/router/dnsmasq/.gitignore b/release/src/router/dnsmasq/.gitignore deleted file mode 100644 index f357b6e549..0000000000 --- a/release/src/router/dnsmasq/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -src/*.o -src/*.mo -src/dnsmasq.pot -src/dnsmasq -src/.configured -contrib/wrt/dhcp_lease_time -contrib/wrt/dhcp_release -debian/base/ -debian/daemon/ -debian/files -debian/substvars -debian/utils-substvars -debian/utils/ diff --git a/release/src/router/dnsmasq/CHANGELOG b/release/src/router/dnsmasq/CHANGELOG index 78b93164cf..14ac3d64ab 100644 --- a/release/src/router/dnsmasq/CHANGELOG +++ b/release/src/router/dnsmasq/CHANGELOG @@ -101,6 +101,19 @@ version 2.67 (provide TFTP to the same interfaces we provide DHCP to) is retained. Thanks to Lonnie Abelbeck for the suggestion. + Add --dhcp-relay config option. Many thanks to vtsl.net + for sponsoring this development. + + Fix crash with empty tag: in --dhcp-range. Thanks to + Kaspar Schleiser for the bug report. + + Add "baseline" and "bloatcheck" makefile targets, for + revealing size changes during development. Thanks to + Vladislav Grishenko for the patch. + + Cope with DHCPv6 clients which send REQUESTs without + address options - treat them as SOLICIT with rapid commit. + version 2.66 Add the ability to act as an authoritative DNS diff --git a/release/src/router/dnsmasq/Makefile b/release/src/router/dnsmasq/Makefile index fe63aeee3e..b4178bcf67 100644 --- a/release/src/router/dnsmasq/Makefile +++ b/release/src/router/dnsmasq/Makefile @@ -77,10 +77,14 @@ all : $(BUILDDIR) build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs)" \ -f $(top)/Makefile dnsmasq -clean : - rm -f *~ $(BUILDDIR)/*.mo contrib/*/*~ */*~ $(BUILDDIR)/*.pot +mostly_clean : + rm -f $(BUILDDIR)/*.mo $(BUILDDIR)/*.pot rm -f $(BUILDDIR)/.configured $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq - rm -rf core */core + +clean : mostly_clean + rm -f $(BUILDDIR)/dnsmasq_baseline + rm -f core */core + rm -f *~ contrib/*/*~ */*~ install : all install-common @@ -113,6 +117,16 @@ merge : $(BUILDDIR): mkdir -p $(BUILDDIR) +# rules below are helpers for size tracking + +baseline : mostly_clean all + @cd $(BUILDDIR) && \ + mv dnsmasq dnsmasq_baseline + +bloatcheck : $(BUILDDIR)/dnsmasq_baseline mostly_clean all + @cd $(BUILDDIR) && \ + $(top)/bld/bloat-o-meter dnsmasq_baseline dnsmasq; \ + size dnsmasq_baseline dnsmasq # rules below are targets in recusive makes with cwd=$(BUILDDIR) @@ -126,7 +140,7 @@ $(objs:.o=.c) $(hdrs): .c.o: $(CC) $(CFLAGS) $(COPTS) $(i18n) $(build_cflags) $(RPM_OPT_FLAGS) -c $< -dnsmasq : .configured $(hdrs) $(objs) +dnsmasq : .configured $(hdrs) $(objs) $(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS) dnsmasq.pot : $(objs:.o=.c) $(hdrs) @@ -135,5 +149,4 @@ dnsmasq.pot : $(objs:.o=.c) $(hdrs) %.mo : $(top)/$(PO)/%.po dnsmasq.pot $(MSGMERGE) -o - $(top)/$(PO)/$*.po dnsmasq.pot | $(MSGFMT) -o $*.mo - - -.PHONY : all clean install install-common all-i18n install-i18n merge +.PHONY : all clean mostly_clean install install-common all-i18n install-i18n merge baseline bloatcheck diff --git a/release/src/router/dnsmasq/VERSION b/release/src/router/dnsmasq/VERSION index 998eb1f640..454923cf75 100644 --- a/release/src/router/dnsmasq/VERSION +++ b/release/src/router/dnsmasq/VERSION @@ -1 +1 @@ -$Format:%d$ +2.67test14 diff --git a/release/src/router/dnsmasq/bld/bloat-o-meter b/release/src/router/dnsmasq/bld/bloat-o-meter new file mode 100755 index 0000000000..6db2a5e58c --- /dev/null +++ b/release/src/router/dnsmasq/bld/bloat-o-meter @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# Copyright 2004 Matt Mackall +# +# Inspired by perl Bloat-O-Meter (c) 1997 by Andi Kleen +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +import sys, os#, re + +def usage(): + sys.stderr.write("usage: %s [-t] file1 file2\n" % sys.argv[0]) + sys.exit(-1) + +f1, f2 = (None, None) +flag_timing, dashes = (False, False) + +for f in sys.argv[1:]: + if f.startswith("-"): + if f == "--": # sym_args + dashes = True + break + if f == "-t": # timings + flag_timing = True + else: + if not os.path.exists(f): + sys.stderr.write("Error: file '%s' does not exist\n" % f) + usage() + if f1 is None: + f1 = f + elif f2 is None: + f2 = f +if flag_timing: + import time +if f1 is None or f2 is None: + usage() + +sym_args = " ".join(sys.argv[3 + flag_timing + dashes:]) +def getsizes(file): + sym, alias, lut = {}, {}, {} + for l in os.popen("readelf -W -s %s %s" % (sym_args, file)).readlines(): + l = l.strip() + if not (len(l) and l[0].isdigit() and len(l.split()) == 8): + continue + num, value, size, typ, bind, vis, ndx, name = l.split() + if ndx == "UND": continue # skip undefined + if typ in ["SECTION", "FILES"]: continue # skip sections and files + if "." in name: name = "static." + name.split(".")[0] + value = int(value, 16) + size = int(size, 16) if size.startswith('0x') else int(size) + if vis != "DEFAULT" and bind != "GLOBAL": # see if it is an alias + alias[(value, size)] = {"name" : name} + else: + sym[name] = {"addr" : value, "size": size} + lut[(value, size)] = 0 + for addr, sz in iter(alias.keys()): + # If the non-GLOBAL sym has an implementation elsewhere then + # it's an alias, disregard it. + if not (addr, sz) in lut: + # If this non-GLOBAL sym does not have an implementation at + # another address, then treat it as a normal symbol. + sym[alias[(addr, sz)]["name"]] = {"addr" : addr, "size": sz} + for l in os.popen("readelf -W -S " + file).readlines(): + x = l.split() + if len(x)<6: continue + # Should take these into account too! + #if x[1] not in [".text", ".rodata", ".symtab", ".strtab"]: continue + if x[1] not in [".rodata"]: continue + sym[x[1]] = {"addr" : int(x[3], 16), "size" : int(x[5], 16)} + return sym + +if flag_timing: + start_t1 = int(time.time() * 1e9) +old = getsizes(f1) +if flag_timing: + end_t1 = int(time.time() * 1e9) + start_t2 = int(time.time() * 1e9) +new = getsizes(f2) +if flag_timing: + end_t2 = int(time.time() * 1e9) + start_t3 = int(time.time() * 1e9) +grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0 +delta, common = [], {} + +for name in iter(old.keys()): + if name in new: + common[name] = 1 + +for name in old: + if name not in common: + remove += 1 + sz = old[name]["size"] + down += sz + delta.append((-sz, name)) + +for name in new: + if name not in common: + add += 1 + sz = new[name]["size"] + up += sz + delta.append((sz, name)) + +for name in common: + d = new[name].get("size", 0) - old[name].get("size", 0) + if d>0: grow, up = grow+1, up+d + elif d<0: shrink, down = shrink+1, down-d + else: + continue + delta.append((d, name)) + +delta.sort() +delta.reverse() +if flag_timing: + end_t3 = int(time.time() * 1e9) + +print("%-48s %7s %7s %+7s" % ("function", "old", "new", "delta")) +for d, n in delta: + if d: + old_sz = old.get(n, {}).get("size", "-") + new_sz = new.get(n, {}).get("size", "-") + print("%-48s %7s %7s %+7d" % (n, old_sz, new_sz, d)) +print("-"*78) +total="(add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s)%%sTotal: %s bytes"\ + % (add, remove, grow, shrink, up, -down, up-down) +print(total % (" "*(80-len(total)))) +if flag_timing: + print("\n%d/%d; %d Parse origin/new; processing nsecs" % + (end_t1-start_t1, end_t2-start_t2, end_t3-start_t3)) + print("total nsecs: %d" % (end_t3-start_t1)) diff --git a/release/src/router/dnsmasq/debian/changelog b/release/src/router/dnsmasq/debian/changelog index f0ddd66ecf..20f0a7d30c 100644 --- a/release/src/router/dnsmasq/debian/changelog +++ b/release/src/router/dnsmasq/debian/changelog @@ -1,6 +1,7 @@ dnsmasq (2.67-1) unstable; urgency=low * New upstream. + * Update resolvconf script. (closes: #720732) -- Simon Kelley Wed, 4 Aug 2013 14:53:22 +0000 diff --git a/release/src/router/dnsmasq/debian/readme b/release/src/router/dnsmasq/debian/readme index cb5f7c901a..73705d82ed 100644 --- a/release/src/router/dnsmasq/debian/readme +++ b/release/src/router/dnsmasq/debian/readme @@ -64,7 +64,9 @@ Notes on configuring dnsmasq as packaged for Debian. noi18n : omit translations and internationalisation support. noidn : omit international domain name support, must be combined with noi18n to be effective. - + gitversion : set the version of the produced packages from the + git-derived versioning information on the source, + rather the the debian changelog. (9) Dnsmasq comes as three packages - dnsmasq-utils, dnsmasq-base and dnsmasq. Dnsmasq-base provides the dnsmasq executable and diff --git a/release/src/router/dnsmasq/debian/resolvconf b/release/src/router/dnsmasq/debian/resolvconf index 80f3a64225..c15cb29e88 100644 --- a/release/src/router/dnsmasq/debian/resolvconf +++ b/release/src/router/dnsmasq/debian/resolvconf @@ -16,8 +16,7 @@ set -e RUN_DIR="/var/run/dnsmasq" RSLVRLIST_FILE="${RUN_DIR}/resolv.conf" TMP_FILE="${RSLVRLIST_FILE}_new.$$" -MY_RECORD_NAME="lo.dnsmasq" -DNSCRYPT_RECORD_NAME="lo.dnscrypt" +MY_NAME_FOR_RESOLVCONF="dnsmasq" [ -x /usr/sbin/dnsmasq ] || exit 0 [ -x /lib/resolvconf/list-records ] || exit 1 @@ -46,14 +45,14 @@ if [ ! -d "$RUN_DIR" ] && ! mkdir --parents --mode=0755 "$RUN_DIR" ; then fi RSLVCNFFILES="" -for F in $(/lib/resolvconf/list-records --after "$MY_RECORD_NAME") ; do +for F in $(/lib/resolvconf/list-records --after "lo.$MY_NAME_FOR_RESOLVCONF") ; do case "$F" in - "$MY_RECORD_NAME") - # Omit + "lo.$MY_NAME_FOR_RESOLVCONF") + # Omit own record ;; - "$DNSCRYPT_RECORD_NAME") - # Dnscrypt, I only have eyes for you - RSLVCNFFILES="$DNSCRYPT_RECORD_NAME" + lo.*) + # Include no more records after one for a local nameserver + RSLVCNFFILES="${RSLVCNFFILES:+$RSLVCNFFILES }$F" break ;; *) diff --git a/release/src/router/dnsmasq/man/dnsmasq.8 b/release/src/router/dnsmasq/man/dnsmasq.8 index 4357d994fe..05733b8da9 100644 --- a/release/src/router/dnsmasq/man/dnsmasq.8 +++ b/release/src/router/dnsmasq/man/dnsmasq.8 @@ -973,6 +973,38 @@ DHCP options. This make extra space available in the DHCP packet for options but can, rarely, confuse old or broken clients. This flag forces "simple and safe" behaviour to avoid problems in such a case. .TP +.B --dhcp-relay=,[,,[enterprise:,] Map from a vendor-class string to a tag. Most DHCP clients provide a "vendor class" which represents, in some sense, the type of host. This option diff --git a/release/src/router/dnsmasq/src/dhcp-common.c b/release/src/router/dnsmasq/src/dhcp-common.c index e5e136aa20..939f25a831 100644 --- a/release/src/router/dnsmasq/src/dhcp-common.c +++ b/release/src/router/dnsmasq/src/dhcp-common.c @@ -757,6 +757,16 @@ void log_context(int family, struct dhcp_context *context) #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 diff --git a/release/src/router/dnsmasq/src/dhcp.c b/release/src/router/dnsmasq/src/dhcp.c index b95a4bab87..573de5b08c 100644 --- a/release/src/router/dnsmasq/src/dhcp.c +++ b/release/src/router/dnsmasq/src/dhcp.c @@ -20,6 +20,8 @@ struct iface_param { struct dhcp_context *current; + struct dhcp_relay *relay; + struct in_addr relay_local; int ind; }; @@ -32,6 +34,8 @@ static int complete_context(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam); static int check_listen_addrs(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam); +static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index); +static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface); static int make_fd(int port) { @@ -132,6 +136,8 @@ void dhcp_packet(time_t now, int pxe_fd) int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd; struct dhcp_packet *mess; struct dhcp_context *context; + struct dhcp_relay *relay; + int is_relay_reply = 0; struct iname *tmp; struct ifreq ifr; struct msghdr msg; @@ -250,57 +256,86 @@ void dhcp_packet(time_t now, int pxe_fd) unicast_dest = 1; #endif - ifr.ifr_addr.sa_family = AF_INET; - if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 ) - iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; - else + if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name))) { - my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name); - return; + /* Reply from server, using us as relay. */ + iface_index = relay->iface_index; + if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name)) + return; + is_relay_reply = 1; + iov.iov_len = sz; +#ifdef HAVE_LINUX_NETWORK + strncpy(arp_req.arp_dev, ifr.ifr_name, 16); +#endif } - - for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) - return; - - /* unlinked contexts are marked by context->current == context */ - for (context = daemon->dhcp; context; context = context->next) - context->current = context; - - parm.current = NULL; - parm.ind = iface_index; - - if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL)) + else { - /* If we failed to match the primary address of the interface, see if we've got a --listen-address - for a secondary */ - struct match_param match; + ifr.ifr_addr.sa_family = AF_INET; + if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 ) + iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; + else + { + my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name); + return; + } + + for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) + if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) + return; + + /* unlinked contexts/relays are marked by context->current == context */ + for (context = daemon->dhcp; context; context = context->next) + context->current = context; + + for (relay = daemon->relay4; relay; relay = relay->next) + relay->current = relay; - match.matched = 0; - match.ind = iface_index; + parm.current = NULL; + parm.relay = NULL; + parm.relay_local.s_addr = 0; + parm.ind = iface_index; - if (!daemon->if_addrs || - !iface_enumerate(AF_INET, &match, check_listen_addrs) || - !match.matched) + if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL)) + { + /* If we failed to match the primary address of the interface, see if we've got a --listen-address + for a secondary */ + struct match_param match; + + match.matched = 0; + match.ind = iface_index; + + if (!daemon->if_addrs || + !iface_enumerate(AF_INET, &match, check_listen_addrs) || + !match.matched) + return; + + iface_addr = match.addr; + /* make sure secondary address gets priority in case + there is more than one address on the interface in the same subnet */ + complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm); + } + + if (!iface_enumerate(AF_INET, &parm, complete_context)) + return; + + /* We're relaying this request */ + if (parm.relay_local.s_addr != 0 && + relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index)) + return; + + /* May have configured relay, but not DHCP server */ + if (!daemon->dhcp) return; - iface_addr = match.addr; - /* make sure secondary address gets priority in case - there is more than one address on the interface in the same subnet */ - complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm); - } + lease_prune(NULL, now); /* lose any expired leases */ + iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, + now, unicast_dest, &is_inform, pxe_fd, iface_addr); + lease_update_file(now); + lease_update_dns(0); - if (!iface_enumerate(AF_INET, &parm, complete_context)) - return; - - lease_prune(NULL, now); /* lose any expired leases */ - iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, - now, unicast_dest, &is_inform, pxe_fd, iface_addr); - lease_update_file(now); - lease_update_dns(0); - - if (iov.iov_len == 0) - return; + if (iov.iov_len == 0) + return; + } msg.msg_name = &dest; msg.msg_namelen = sizeof(dest); @@ -321,7 +356,7 @@ void dhcp_packet(time_t now, int pxe_fd) if (mess->ciaddr.s_addr != 0) dest.sin_addr = mess->ciaddr; } - else if (mess->giaddr.s_addr) + else if (mess->giaddr.s_addr && !is_relay_reply) { /* Send to BOOTP relay */ dest.sin_port = htons(daemon->dhcp_server_port); @@ -334,7 +369,7 @@ void dhcp_packet(time_t now, int pxe_fd) source port too, and send back to that. If we're replying to a DHCPINFORM, trust the source address always. */ if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) || - dest.sin_port == 0 || dest.sin_addr.s_addr == 0) + dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply) { dest.sin_port = htons(daemon->dhcp_client_port); dest.sin_addr = mess->ciaddr; @@ -450,6 +485,7 @@ static int complete_context(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) { struct dhcp_context *context; + struct dhcp_relay *relay; struct iface_param *param = vparam; (void)label; @@ -495,6 +531,15 @@ static int complete_context(struct in_addr local, int if_index, char *label, } } + for (relay = daemon->relay4; relay; relay = relay->next) + if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay && + (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr)) + { + relay->current = param->relay; + param->relay = relay; + param->relay_local = local; + } + return 1; } @@ -988,5 +1033,74 @@ char *host_from_dns(struct in_addr addr) return NULL; } -#endif +static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index) +{ + /* ->local is same value for all relays on ->current chain */ + struct all_addr from; + + if (mess->op != BOOTREQUEST) + return 0; + + /* source address == relay address */ + from.addr.addr4 = relay->local.addr.addr4; + + /* already gatewayed ? */ + if (mess->giaddr.s_addr) + { + /* if so check if by us, to stomp on loops. */ + if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr) + return 1; + } + else + { + /* plug in our address */ + mess->giaddr.s_addr = relay->local.addr.addr4.s_addr; + } + + if ((mess->hops++) > 20) + return 1; + for (; relay; relay = relay->current) + { + union mysockaddr to; + + to.sa.sa_family = AF_INET; + to.in.sin_addr = relay->server.addr.addr4; + to.in.sin_port = htons(daemon->dhcp_server_port); + + send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0); + + if (option_bool(OPT_LOG_OPTS)) + { + inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4)); + } + + /* Save this for replies */ + relay->iface_index = iface_index; + } + + return 1; +} + + +static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface) +{ + struct dhcp_relay *relay; + + if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY) + return NULL; + + for (relay = daemon->relay4; relay; relay = relay->next) + { + if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr) + { + if (!relay->interface || wildcard_match(relay->interface, arrival_interface)) + return relay->iface_index != 0 ? relay : NULL; + } + } + + return NULL; +} + +#endif diff --git a/release/src/router/dnsmasq/src/dhcp6.c b/release/src/router/dnsmasq/src/dhcp6.c index 89af7dd9bd..35bb74829b 100644 --- a/release/src/router/dnsmasq/src/dhcp6.c +++ b/release/src/router/dnsmasq/src/dhcp6.c @@ -20,7 +20,8 @@ struct iface_param { struct dhcp_context *current; - struct in6_addr fallback; + struct dhcp_relay *relay; + struct in6_addr fallback, relay_local; int ind, addr_match; }; @@ -87,6 +88,7 @@ void dhcp6_init(void) void dhcp6_packet(time_t now) { struct dhcp_context *context; + struct dhcp_relay *relay; struct iface_param parm; struct cmsghdr *cmptr; struct msghdr msg; @@ -126,56 +128,75 @@ void dhcp6_packet(time_t now) if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name)) return; - - for (tmp = daemon->if_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) - return; - - for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) - return; - - parm.current = NULL; - parm.ind = if_index; - parm.addr_match = 0; - memset(&parm.fallback, 0, IN6ADDRSZ); - for (context = daemon->dhcp6; context; context = context->next) - if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0) - { - /* wildcard context for DHCP-stateless only */ - parm.current = context; - context->current = NULL; - } - else - { - /* unlinked contexts are marked by context->current == context */ - context->current = context; - memset(&context->local6, 0, IN6ADDRSZ); - } - - if (!iface_enumerate(AF_INET6, &parm, complete_context6)) - return; - - if (daemon->if_names || daemon->if_addrs) + if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0) { - for (tmp = daemon->if_names; tmp; tmp = tmp->next) + for (tmp = daemon->if_except; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) - break; + return; + + for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) + if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) + return; + + parm.current = NULL; + parm.relay = NULL; + memset(&parm.relay_local, 0, IN6ADDRSZ); + parm.ind = if_index; + parm.addr_match = 0; + memset(&parm.fallback, 0, IN6ADDRSZ); + + for (context = daemon->dhcp6; context; context = context->next) + if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0) + { + /* wildcard context for DHCP-stateless only */ + parm.current = context; + context->current = NULL; + } + else + { + /* unlinked contexts are marked by context->current == context */ + context->current = context; + memset(&context->local6, 0, IN6ADDRSZ); + } - if (!tmp && !parm.addr_match) + for (relay = daemon->relay6; relay; relay = relay->next) + relay->current = relay; + + if (!iface_enumerate(AF_INET6, &parm, complete_context6)) return; + + if (daemon->if_names || daemon->if_addrs) + { + + for (tmp = daemon->if_names; tmp; tmp = tmp->next) + if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) + break; + + if (!tmp && !parm.addr_match) + return; + } + + if (parm.relay) + { + relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id); + return; + } + + /* May have configured relay, but not DHCP server */ + if (!daemon->doing_dhcp6) + return; + + lease_prune(NULL, now); /* lose any expired leases */ + + port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, + sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now); + + lease_update_file(now); + lease_update_dns(0); } - - lease_prune(NULL, now); /* lose any expired leases */ - - port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, - sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now); - - lease_update_file(now); - lease_update_dns(0); - + /* The port in the source address of the original request should be correct, but at least once client sends from the server port, so we explicitly send to the client port to a client, and the @@ -194,70 +215,84 @@ static int complete_context6(struct in6_addr *local, int prefix, unsigned int valid, void *vparam) { struct dhcp_context *context; + struct dhcp_relay *relay; struct iface_param *param = vparam; struct iname *tmp; (void)scope; /* warning */ - if (if_index == param->ind && - !IN6_IS_ADDR_LOOPBACK(local) && - !IN6_IS_ADDR_LINKLOCAL(local) && - !IN6_IS_ADDR_MULTICAST(local)) + if (if_index == param->ind) { - /* if we have --listen-address config, see if the - arrival interface has a matching address. */ - for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) - if (tmp->addr.sa.sa_family == AF_INET6 && - IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local)) - param->addr_match = 1; - - /* Determine a globally address on the arrival interface, even - if we have no matching dhcp-context, because we're only - allocating on remote subnets via relays. This - is used as a default for the DNS server option. */ - param->fallback = *local; - - for (context = daemon->dhcp6; context; context = context->next) + if (!IN6_IS_ADDR_LOOPBACK(local) && + !IN6_IS_ADDR_LINKLOCAL(local) && + !IN6_IS_ADDR_MULTICAST(local)) { - if ((context->flags & CONTEXT_DHCP) && - !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) && - prefix == context->prefix && - is_same_net6(local, &context->start6, prefix) && - is_same_net6(local, &context->end6, prefix)) + /* if we have --listen-address config, see if the + arrival interface has a matching address. */ + for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) + if (tmp->addr.sa.sa_family == AF_INET6 && + IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local)) + param->addr_match = 1; + + /* Determine a globally address on the arrival interface, even + if we have no matching dhcp-context, because we're only + allocating on remote subnets via relays. This + is used as a default for the DNS server option. */ + param->fallback = *local; + + for (context = daemon->dhcp6; context; context = context->next) { - - - /* link it onto the current chain if we've not seen it before */ - if (context->current == context) + if ((context->flags & CONTEXT_DHCP) && + !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) && + prefix == context->prefix && + is_same_net6(local, &context->start6, prefix) && + is_same_net6(local, &context->end6, prefix)) { - struct dhcp_context *tmp, **up; - - /* use interface values only for contructed contexts */ - if (!(context->flags & CONTEXT_CONSTRUCTED)) - preferred = valid = 0xffffffff; - else if (flags & IFACE_DEPRECATED) - preferred = 0; - - if (context->flags & CONTEXT_DEPRECATE) - preferred = 0; - /* order chain, longest preferred time first */ - for (up = ¶m->current, tmp = param->current; tmp; tmp = tmp->current) - if (tmp->preferred <= preferred) - break; - else - up = &tmp->current; - context->current = *up; - *up = context; - context->local6 = *local; - context->preferred = preferred; - context->valid = valid; + /* link it onto the current chain if we've not seen it before */ + if (context->current == context) + { + struct dhcp_context *tmp, **up; + + /* use interface values only for contructed contexts */ + if (!(context->flags & CONTEXT_CONSTRUCTED)) + preferred = valid = 0xffffffff; + else if (flags & IFACE_DEPRECATED) + preferred = 0; + + if (context->flags & CONTEXT_DEPRECATE) + preferred = 0; + + /* order chain, longest preferred time first */ + for (up = ¶m->current, tmp = param->current; tmp; tmp = tmp->current) + if (tmp->preferred <= preferred) + break; + else + up = &tmp->current; + + context->current = *up; + *up = context; + context->local6 = *local; + context->preferred = preferred; + context->valid = valid; + } } } } + + for (relay = daemon->relay6; relay; relay = relay->next) + if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay && + (IN6_IS_ADDR_UNSPECIFIED(¶m->relay_local) || IN6_ARE_ADDR_EQUAL(local, ¶m->relay_local))) + { + relay->current = param->relay; + param->relay = relay; + param->relay_local = *local; + } + } - return 1; + + return 1; } struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr) @@ -539,7 +574,9 @@ static int construct_worker(struct in6_addr *local, int prefix, } else if ((addr6part(local) == addr6part(&template->start6) || - addr6part(local) == addr6part(&template->end6)) && + addr6part(local) == addr6part(&template->end6) || + (IN6_IS_ADDR_UNSPECIFIED(&template->start6) && + IFACE_PERMANENT == (flags & (IFACE_PERMANENT | IFACE_DEPRECATED)))) && wildcard_match(template->template_interface, ifrn_name)) { start6 = *local; diff --git a/release/src/router/dnsmasq/src/dnsmasq.c b/release/src/router/dnsmasq/src/dnsmasq.c index 6c52616266..5556fd6ee0 100644 --- a/release/src/router/dnsmasq/src/dnsmasq.c +++ b/release/src/router/dnsmasq/src/dnsmasq.c @@ -96,6 +96,7 @@ int main (int argc, char **argv) cap_user_data_t data = NULL; #endif struct dhcp_context *context; + struct dhcp_relay *relay; #ifdef LOCALEDIR setlocale(LC_ALL, ""); @@ -210,50 +211,47 @@ int main (int argc, char **argv) daemon->soa_sn = now; #endif -#ifdef HAVE_DHCP - if (daemon->dhcp || daemon->dhcp6) - { +#ifdef HAVE_DHCP6 + if (daemon->dhcp6) + { + daemon->doing_ra = option_bool(OPT_RA); -# ifdef HAVE_DHCP6 - if (daemon->dhcp6) + for (context = daemon->dhcp6; context; context = context->next) { - daemon->doing_ra = option_bool(OPT_RA); - - for (context = daemon->dhcp6; context; context = context->next) - { - if (context->flags & CONTEXT_DHCP) - daemon->doing_dhcp6 = 1; - if (context->flags & CONTEXT_RA) - daemon->doing_ra = 1; + if (context->flags & CONTEXT_DHCP) + daemon->doing_dhcp6 = 1; + if (context->flags & CONTEXT_RA) + daemon->doing_ra = 1; #ifndef HAVE_LINUX_NETWORK - if (context->flags & CONTEXT_TEMPLATE) - die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF); + if (context->flags & CONTEXT_TEMPLATE) + die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF); #endif - } } -# endif - - /* 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. We need to call common_init - before lease_init to allocate buffers it uses.*/ + } +#endif + +#ifdef HAVE_DHCP + /* 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. We need to call common_init + before lease_init to allocate buffers it uses.*/ + if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6) + { + dhcp_common_init(); if (daemon->dhcp || daemon->doing_dhcp6) - { - dhcp_common_init(); - lease_init(now); - } - - if (daemon->dhcp) - dhcp_init(); - + lease_init(now); + } + + if (daemon->dhcp || daemon->relay4) + dhcp_init(); + # ifdef HAVE_DHCP6 - if (daemon->doing_ra) - ra_init(now); - - if (daemon->doing_dhcp6) - dhcp6_init(); + if (daemon->doing_ra) + ra_init(now); + + if (daemon->doing_dhcp6 || daemon->relay6) + dhcp6_init(); # endif - } #endif @@ -285,14 +283,15 @@ int main (int argc, char **argv) /* after enumerate_interfaces() */ if (daemon->dhcp) { - bindtodevice(daemon->dhcpfd); + if (!daemon->relay4) + bindtodevice(daemon->dhcpfd); if (daemon->enable_pxe) bindtodevice(daemon->pxefd); } #endif #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6) - if (daemon->doing_dhcp6) + if (daemon->doing_dhcp6 && !daemon->relay6) bindtodevice(daemon->dhcp6fd); #endif } @@ -301,7 +300,7 @@ int main (int argc, char **argv) #ifdef HAVE_DHCP6 /* after enumerate_interfaces() */ - if (daemon->doing_dhcp6 || daemon->doing_ra) + if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra) join_multicast(1); #endif @@ -685,10 +684,16 @@ int main (int argc, char **argv) for (context = daemon->dhcp; context; context = context->next) log_context(AF_INET, context); + for (relay = daemon->relay4; relay; relay = relay->next) + log_relay(AF_INET, relay); + # ifdef HAVE_DHCP6 for (context = daemon->dhcp6; context; context = context->next) log_context(AF_INET6, context); + for (relay = daemon->relay6; relay; relay = relay->next) + log_relay(AF_INET6, relay); + if (daemon->doing_dhcp6 || daemon->doing_ra) dhcp_construct_contexts(now); @@ -793,7 +798,7 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP - if (daemon->dhcp) + if (daemon->dhcp || daemon->relay4) { FD_SET(daemon->dhcpfd, &rset); bump_maxfd(daemon->dhcpfd, &maxfd); @@ -806,7 +811,7 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP6 - if (daemon->doing_dhcp6) + if (daemon->doing_dhcp6 || daemon->relay6) { FD_SET(daemon->dhcp6fd, &rset); bump_maxfd(daemon->dhcp6fd, &maxfd); @@ -918,7 +923,7 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP - if (daemon->dhcp) + if (daemon->dhcp || daemon->relay4) { if (FD_ISSET(daemon->dhcpfd, &rset)) dhcp_packet(now, 0); @@ -927,7 +932,7 @@ int main (int argc, char **argv) } #ifdef HAVE_DHCP6 - if (daemon->doing_dhcp6 && FD_ISSET(daemon->dhcp6fd, &rset)) + if ((daemon->doing_dhcp6 || daemon->relay6) && FD_ISSET(daemon->dhcp6fd, &rset)) dhcp6_packet(now); if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset)) diff --git a/release/src/router/dnsmasq/src/dnsmasq.h b/release/src/router/dnsmasq/src/dnsmasq.h index b660ef78bc..6680f072d3 100644 --- a/release/src/router/dnsmasq/src/dnsmasq.h +++ b/release/src/router/dnsmasq/src/dnsmasq.h @@ -224,12 +224,12 @@ struct event_desc { #define OPT_FAST_RA 41 #ifdef HAVE_QUIET_DHCP //Originally a TOMATO option - #define OPT_LAST 42 - #define OPT_QUIET_DHCP 43 - #define OPT_QUIET_DHCP6 44 - #define OPT_QUIET_RA 45 -#else - #define OPT_LAST 42 +#define OPT_QUIET_DHCP 42 +#define OPT_QUIET_DHCP6 43 +#define OPT_QUIET_RA 44 +#define OPT_LAST 45 +#else +#define OPT_LAST 42 #endif //HAVE_QUIET_DHCP @@ -409,6 +409,7 @@ union mysockaddr { /* bits in flag param to IPv6 callbacks from iface_enumerate() */ #define IFACE_TENTATIVE 1 #define IFACE_DEPRECATED 2 +#define IFACE_PERMANENT 4 #define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */ @@ -783,6 +784,12 @@ struct tftp_prefix { struct tftp_prefix *next; }; +struct dhcp_relay { + struct all_addr local, server; + char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */ + int iface_index; /* working - interface in which requests arrived, for return */ + struct dhcp_relay *current, *next; +}; extern struct daemon { /* datastuctures representing the command-line and @@ -832,6 +839,7 @@ extern struct daemon { struct pxe_service *pxe_services; struct tag_if *tag_if; struct addr_list *override_relays; + struct dhcp_relay *relay4, *relay6; int override; int enable_pxe; int doing_ra, doing_dhcp6; @@ -1231,6 +1239,9 @@ void dhcp_construct_contexts(time_t now); #ifdef HAVE_DHCP6 unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, struct in6_addr *fallback, size_t sz, int is_multicast, time_t now); +void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id); + +unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); #endif /* dhcp-common.c */ @@ -1257,6 +1268,7 @@ void bindtodevice(int fd); void display_opts6(void); # endif void log_context(int family, struct dhcp_context *context); +void log_relay(int family, struct dhcp_relay *relay); #endif /* outpacket.c */ diff --git a/release/src/router/dnsmasq/src/netlink.c b/release/src/router/dnsmasq/src/netlink.c index 5e4d924fd8..2bcaf0185d 100644 --- a/release/src/router/dnsmasq/src/netlink.c +++ b/release/src/router/dnsmasq/src/netlink.c @@ -265,6 +265,9 @@ int iface_enumerate(int family, void *parm, int (*callback)()) if (ifa->ifa_flags & IFA_F_DEPRECATED) flags |= IFACE_DEPRECATED; + + if (ifa->ifa_flags & IFA_F_PERMANENT) + flags |= IFACE_PERMANENT; if (addrp && callback_ok) if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), @@ -399,18 +402,18 @@ static int nl_async(struct nlmsghdr *h) static void nl_newaddress(time_t now) { - if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->doing_ra) + if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra) enumerate_interfaces(0); if (option_bool(OPT_CLEVERBIND)) create_bound_listeners(0); #ifdef HAVE_DHCP6 + if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra) + join_multicast(0); + if (daemon->doing_dhcp6 || daemon->doing_ra) - { - join_multicast(0); - dhcp_construct_contexts(now); - } + dhcp_construct_contexts(now); if (daemon->doing_dhcp6) lease_find_interfaces(now); diff --git a/release/src/router/dnsmasq/src/network.c b/release/src/router/dnsmasq/src/network.c index 7a5d49e660..8e62538887 100644 --- a/release/src/router/dnsmasq/src/network.c +++ b/release/src/router/dnsmasq/src/network.c @@ -443,7 +443,7 @@ static int iface_allowed_v4(struct in_addr local, int if_index, char *label, int enumerate_interfaces(int reset) { static struct addrlist *spare = NULL; - static int done = 0; + static int done = 0, active = 0; struct iface_param param; int errsave, ret = 1; struct addrlist *addr, *tmp; @@ -451,18 +451,21 @@ int enumerate_interfaces(int reset) /* Do this max once per select cycle - also inhibits netlink socket use in TCP child processes. */ - + if (reset) { done = 0; return 1; } - if (done) + if (done || active) return 1; done = 1; + /* protect against recusive calls from iface_enumerate(); */ + active = 1; + if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return 0; @@ -504,7 +507,8 @@ int enumerate_interfaces(int reset) errno = errsave; spare = param.spare; - + active = 0; + return ret; } @@ -851,7 +855,7 @@ void join_multicast(int dienow) inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr); - if (daemon->doing_dhcp6 && + if ((daemon->doing_dhcp6 || daemon->relay6) && setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) err = 1; diff --git a/release/src/router/dnsmasq/src/option.c b/release/src/router/dnsmasq/src/option.c index f7c5c0d27f..70d43fe955 100644 --- a/release/src/router/dnsmasq/src/option.c +++ b/release/src/router/dnsmasq/src/option.c @@ -133,11 +133,12 @@ struct myoption { #define LOPT_PREF_CLSS 321 #endif #define LOPT_FAST_RA 322 +#define LOPT_RELAY 323 #ifdef HAVE_QUIET_DHCP //Originally TOMATO option -#define LOPT_QUIET_DHCP 323 -#define LOPT_QUIET_DHCP6 324 -#define LOPT_QUIET_RA 325 +#define LOPT_QUIET_DHCP 324 +#define LOPT_QUIET_DHCP6 325 +#define LOPT_QUIET_RA 326 #endif #ifdef HAVE_GETOPT_LONG @@ -277,6 +278,7 @@ static const struct myoption opts[] = { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS }, #endif { "force-fast-ra", 0, 0, LOPT_FAST_RA }, + { "dhcp-relay", 1, 0, LOPT_RELAY }, #ifdef HAVE_QUIET_DHCP //Originally TOMATO option { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP }, { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 }, @@ -400,6 +402,7 @@ static struct { { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL }, { LOPT_GEN_NAMES, ARG_DUP, "[=tag:]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL}, { LOPT_PROXY, ARG_DUP, "[=]...", gettext_noop("Use these DHCP relays as full proxies."), NULL }, + { LOPT_RELAY, ARG_DUP, ",[,]", gettext_noop("Relay DHCP requests to a remote server"), NULL}, { LOPT_CNAME, ARG_DUP, ",", gettext_noop("Specify alias name for LOCAL DNS name."), NULL }, { LOPT_PXE_PROMT, ARG_DUP, ",[]", gettext_noop("Prompt to send to PXE clients."), NULL }, { LOPT_PXE_SERV, ARG_DUP, "", gettext_noop("Boot service for PXE menu."), NULL }, @@ -2350,7 +2353,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid)); tt->net = opt_string_alloc(arg+4); tt->next = new->filter; - new->filter = tt; + /* ignore empty tag */ + if (tt->net) + new->filter = tt; } else { @@ -3194,6 +3199,31 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma arg = comma; } break; + + case LOPT_RELAY: /* --dhcp-relay */ + { + struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay)); + comma = split(arg); + new->interface = opt_string_alloc(split(comma)); + new->iface_index = 0; + if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server)) + { + new->next = daemon->relay4; + daemon->relay4 = new; + } +#ifdef HAVE_DHCP6 + else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server)) + { + new->next = daemon->relay6; + daemon->relay6 = new; + } +#endif + else + ret_err(_("Bad dhcp-relay")); + + break; + } + #endif #ifdef HAVE_DHCP6 diff --git a/release/src/router/dnsmasq/src/outpacket.c b/release/src/router/dnsmasq/src/outpacket.c index 7f11cd3bcf..9d64c01ef6 100644 --- a/release/src/router/dnsmasq/src/outpacket.c +++ b/release/src/router/dnsmasq/src/outpacket.c @@ -70,9 +70,9 @@ void *put_opt6(void *data, size_t len) { void *p; - if ((p = expand(len))) + if ((p = expand(len)) && data) memcpy(p, data, len); - + return p; } diff --git a/release/src/router/dnsmasq/src/rfc3315.c b/release/src/router/dnsmasq/src/rfc3315.c index 16d666f3c5..8537564ac3 100644 --- a/release/src/router/dnsmasq/src/rfc3315.c +++ b/release/src/router/dnsmasq/src/rfc3315.c @@ -21,7 +21,7 @@ struct state { unsigned char *clid; - int clid_len, iaid, ia_type, interface, hostname_auth; + int clid_len, iaid, ia_type, interface, hostname_auth, lease_allocate; char *client_hostname, *hostname, *domain, *send_domain; struct dhcp_context *context; struct in6_addr *link_address; @@ -55,7 +55,7 @@ static void mark_context_used(struct state *state, struct dhcp_context *context, static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr); static int check_address(struct state *state, struct in6_addr *addr); static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, - unsigned int *min_time, struct in6_addr *addr, int update_lease, time_t now); + unsigned int *min_time, struct in6_addr *addr, time_t now); static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now); static int add_local_addrs(struct dhcp_context *context); static struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, struct dhcp_context *context, int do_refresh); @@ -155,7 +155,8 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid ** return 0; /* copy header stuff into reply message and set type to reply */ - outmsgtypep = put_opt6(inbuff, 34); + if (!(outmsgtypep = put_opt6(inbuff, 34))) + return 0; *outmsgtypep = DHCP6RELAYREPL; /* look for relay options and set tags if found. */ @@ -225,6 +226,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh state.end = inbuff + sz; state.clid = NULL; state.clid_len = 0; + state.lease_allocate = 0; state.context_tags = NULL; state.tags = tags; state.link_address = link_address; @@ -252,7 +254,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh state.tags = &v6_id; /* copy over transaction-id, and save pointer to message type */ - outmsgtypep = put_opt6(inbuff, 4); + if (!(outmsgtypep = put_opt6(inbuff, 4))) + return 0; start_opts = save_counter(-1); state.xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16; @@ -538,26 +541,28 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh case DHCP6SOLICIT: { - void *rapid_commit = opt6_find(state.packet_options, state.end, OPTION6_RAPID_COMMIT, 0); int address_assigned = 0; /* tags without all prefix-class tags */ struct dhcp_netid *solicit_tags = tagif; struct dhcp_context *c; - if (rapid_commit) + *outmsgtypep = DHCP6ADVERTISE; + + if (opt6_find(state.packet_options, state.end, OPTION6_RAPID_COMMIT, 0)) { + *outmsgtypep = DHCP6REPLY; + state.lease_allocate = 1; o = new_opt6(OPTION6_RAPID_COMMIT); end_opt6(o); } + + log6_packet(&state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL); - /* set reply message type */ - *outmsgtypep = rapid_commit ? DHCP6REPLY : DHCP6ADVERTISE; + request_no_address: #ifdef HAVE_QUIET_DHCP if (!option_bool(OPT_QUIET_DHCP6)) #endif - log6_packet(&state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL); - if (ignore) return 0; @@ -672,7 +677,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, c, lease_time, ia_option, &min_time, req_addr, rapid_commit != NULL, now); + add_address(&state, c, lease_time, ia_option, &min_time, req_addr, now); mark_context_used(&state, context, req_addr); get_context_tag(&state, c); address_assigned = 1; @@ -696,7 +701,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, c, lease_time, NULL, &min_time, &addr, rapid_commit != NULL, now); + add_address(&state, c, lease_time, NULL, &min_time, &addr, now); mark_context_used(&state, context, &addr); get_context_tag(&state, c); address_assigned = 1; @@ -713,7 +718,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, c, c->lease_time, NULL, &min_time, req_addr, rapid_commit != NULL, now); + add_address(&state, c, c->lease_time, NULL, &min_time, req_addr, now); mark_context_used(&state, context, req_addr); get_context_tag(&state, c); address_assigned = 1; @@ -727,7 +732,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, c, c->lease_time, NULL, &min_time, &addr, rapid_commit != NULL, now); + add_address(&state, c, c->lease_time, NULL, &min_time, &addr, now); mark_context_used(&state, context, &addr); get_context_tag(&state, c); address_assigned = 1; @@ -770,9 +775,11 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh case DHCP6REQUEST: { int address_assigned = 0; - + int start = save_counter(-1); + /* set reply message type */ *outmsgtypep = DHCP6REPLY; + state.lease_allocate = 1; #ifdef HAVE_QUIET_DHCP if (!option_bool(OPT_QUIET_DHCP6)) @@ -790,7 +797,15 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (!check_ia(&state, opt, &ia_end, &ia_option)) continue; - + + if (!ia_option) + { + /* If we get a request with a IA_*A without addresses, treat it exactly like + a SOLICT with rapid commit set. */ + save_counter(start); + goto request_no_address; + } + o = build_ia(&state, &t1cntr); for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) @@ -836,7 +851,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, dynamic, lease_time, ia_option, &min_time, req_addr, 1, now); + add_address(&state, dynamic, lease_time, ia_option, &min_time, req_addr, now); get_context_tag(&state, dynamic); address_assigned = 1; } @@ -1539,7 +1554,7 @@ static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz) } static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, - unsigned int *min_time, struct in6_addr *addr, int do_update, time_t now) + unsigned int *min_time, struct in6_addr *addr, time_t now) { unsigned int valid_time = 0, preferred_time = 0; int o = new_opt6(OPTION6_IAADDR); @@ -1569,7 +1584,7 @@ static void add_address(struct state *state, struct dhcp_context *context, unsig end_opt6(o); - if (do_update) + if (state->lease_allocate) update_leases(state, context, addr, valid_time, now); if ((lease = lease6_find_by_addr(addr, 128, 0))) @@ -1596,8 +1611,7 @@ static void add_address(struct state *state, struct dhcp_context *context, unsig #ifdef HAVE_QUIET_DHCP if (!option_bool(OPT_QUIET_DHCP6)) #endif - log6_packet(state, do_update ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname); - + log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname); } static void mark_context_used(struct state *state, struct dhcp_context *context, struct in6_addr *addr) @@ -1951,4 +1965,118 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size) return ret; } +void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id) +{ + /* ->local is same value for all relays on ->current chain */ + + struct all_addr from; + unsigned char *header; + unsigned char *inbuff = daemon->dhcp_packet.iov_base; + int msg_type = *inbuff; + int hopcount; + struct in6_addr multicast; + + inet_pton(AF_INET6, ALL_SERVERS, &multicast); + + /* source address == relay address */ + from.addr.addr6 = relay->local.addr.addr6; + + /* Get hop count from nested relayed message */ + if (msg_type == DHCP6RELAYFORW) + hopcount = *((unsigned char *)inbuff+1) + 1; + else + hopcount = 0; + + /* RFC 3315 HOP_COUNT_LIMIT */ + if (hopcount > 32) + return; + + save_counter(0); + + if ((header = put_opt6(NULL, 34))) + { + int o; + + header[0] = DHCP6RELAYFORW; + header[1] = hopcount; + memcpy(&header[2], &relay->local.addr.addr6, IN6ADDRSZ); + memcpy(&header[18], peer_address, IN6ADDRSZ); + + o = new_opt6(OPTION6_RELAY_MSG); + put_opt6(inbuff, sz); + end_opt6(o); + + for (; relay; relay = relay->current) + { + union mysockaddr to; + + to.sa.sa_family = AF_INET6; + to.in6.sin6_addr = relay->server.addr.addr6; + to.in6.sin6_port = htons(DHCPV6_SERVER_PORT); + to.in6.sin6_flowinfo = 0; + to.in6.sin6_scope_id = 0; + + if (IN6_ARE_ADDR_EQUAL(&relay->server.addr.addr6, &multicast)) + { + int multicast_iface; + if (!relay->interface || strchr(relay->interface, '*') || + (multicast_iface = if_nametoindex(relay->interface)) == 0 || + setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1) + my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface")); + } + + send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(0), &to, &from, 0); + + if (option_bool(OPT_LOG_OPTS)) + { + inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN); + inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff); + } + + /* Save this for replies */ + relay->iface_index = scope_id; + } + } +} + +unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) +{ + struct dhcp_relay *relay; + struct in6_addr link; + unsigned char *inbuff = daemon->dhcp_packet.iov_base; + + /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option + which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */ + + if (sz < 38 || *inbuff != DHCP6RELAYREPL) + return 0; + + memcpy(&link, &inbuff[2], IN6ADDRSZ); + + for (relay = daemon->relay6; relay; relay = relay->next) + if (IN6_ARE_ADDR_EQUAL(&link, &relay->local.addr.addr6) && + (!relay->interface || wildcard_match(relay->interface, arrival_interface))) + break; + + save_counter(0); + + if (relay) + { + void *opt, *opts = inbuff + 34; + void *end = inbuff + sz; + for (opt = opts; opt; opt = opt6_next(opt, end)) + if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0) + { + int encap_type = *((unsigned char *)opt6_ptr(opt, 0)); + put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); + memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ); + peer->sin6_scope_id = relay->iface_index; + return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT; + } + } + + return 0; +} + #endif -- 2.11.4.GIT