diff options
author | Francis Dupont <fdupont@isc.org> | 2017-12-23 01:18:23 +0100 |
---|---|---|
committer | Francis Dupont <fdupont@isc.org> | 2017-12-23 01:18:23 +0100 |
commit | 563f0b8aef9558a900f215dfccc571eab99a0223 (patch) | |
tree | 4d5ce8719fe415d089569f5f98ca84c944a42aaf | |
parent | 0d6d300fec8b26c9e854095a4a29158186b69172 (diff) | |
download | isc-dhcp-563f0b8aef9558a900f215dfccc571eab99a0223.tar.gz |
Merged rt44535 (relay port)
-rw-r--r-- | RELNOTES | 13 | ||||
-rw-r--r-- | common/bpf.c | 57 | ||||
-rw-r--r-- | common/discover.c | 43 | ||||
-rw-r--r-- | common/dlpi.c | 4 | ||||
-rw-r--r-- | common/lpf.c | 19 | ||||
-rw-r--r-- | common/nit.c | 4 | ||||
-rw-r--r-- | common/packet.c | 11 | ||||
-rw-r--r-- | common/raw.c | 4 | ||||
-rw-r--r-- | common/socket.c | 70 | ||||
-rw-r--r-- | common/tables.c | 16 | ||||
-rw-r--r-- | common/upf.c | 3 | ||||
-rwxr-xr-x | configure | 25 | ||||
-rw-r--r-- | configure.ac | 19 | ||||
-rw-r--r-- | configure.ac+lt | 19 | ||||
-rw-r--r-- | configure.ac-base | 19 | ||||
-rw-r--r-- | configure.ac-lt | 19 | ||||
-rw-r--r-- | doc/DHCPv4-over-DHCPv6 | 7 | ||||
-rw-r--r-- | includes/config.h.in | 3 | ||||
-rw-r--r-- | includes/dhcp.h | 2 | ||||
-rw-r--r-- | includes/dhcp6.h | 2 | ||||
-rw-r--r-- | includes/dhcpd.h | 8 | ||||
-rw-r--r-- | relay/dhcrelay.8 | 20 | ||||
-rw-r--r-- | relay/dhcrelay.c | 154 | ||||
-rw-r--r-- | server/dhcp.c | 35 | ||||
-rw-r--r-- | server/dhcpleasequery.c | 7 | ||||
-rw-r--r-- | server/dhcpv6.c | 158 | ||||
-rw-r--r-- | server/stables.c | 1 |
27 files changed, 685 insertions, 57 deletions
@@ -312,6 +312,19 @@ dhcp-users@lists.isc.org. when building with --enable-use-sockets and --enable-ipv4-pktinfo. [ISC-Bugs #36118] +(to be finalized before code freeze next year) +- Added experimental support for relay port (draft-ietf-dhc-relay-port-10.txt) + feature for DHCPv4, DHCPv6 and DHCPv4-over-DHCPv6. As the code points + were not yet assigned by IANA temporary (next free) values are used. + Relay port had be enabled at compile time via --enable-relay-port and + is fully backward compatible, i.e. works with previous implementations + of servers and relays, of course in this case using legacy ports. + A new --rp <relay-port> command line option specifies to dhcrelay + an alternate source port for upstream (i.e. toward the server) messages. +(update this) + Thanks to Naiming Shen and Enke Chen for submitting patches. + [ISC-Bugs #44535] + Changes since 4.3.6 (Bugs): - Corrected an issue where the server would return a client's previously diff --git a/common/bpf.c b/common/bpf.c index dc41f809..16076fea 100644 --- a/common/bpf.c +++ b/common/bpf.c @@ -192,12 +192,51 @@ struct bpf_insn dhcp_bpf_filter [] = { BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ /* If we passed all the tests, ask for the whole packet. */ - BPF_STMT(BPF_RET+BPF_K, (u_int)-1), + BPF_STMT (BPF_RET + BPF_K, (u_int)-1), /* Otherwise, drop it. */ - BPF_STMT(BPF_RET+BPF_K, 0), + BPF_STMT (BPF_RET + BPF_K, 0), }; +#if defined(RELAY_PORT) +/* + * For relay port extension + */ +struct bpf_insn dhcp_bpf_relay_filter [] = { + /* Make sure this is an IP packet... */ + BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10), + + /* Make sure it's a UDP packet... */ + BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8), + + /* Make sure this isn't a fragment... */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), + + /* Get the IP header length... */ + BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14), + + /* Make sure it's to the right port... */ + BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 2, 0), /* patch */ + + /* relay can have an alternative port... */ + BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ + + /* If we passed all the tests, ask for the whole packet. */ + BPF_STMT (BPF_RET + BPF_K, (u_int)-1), + + /* Otherwise, drop it. */ + BPF_STMT (BPF_RET + BPF_K, 0), +}; + +int dhcp_bpf_relay_filter_len = + sizeof dhcp_bpf_relay_filter / sizeof (struct bpf_insn); +#endif + #if defined (DEC_FDDI) struct bpf_insn *bpf_fddi_filter = NULL; #endif @@ -309,7 +348,19 @@ void if_register_receive (info) /* Patch the server port into the BPF program... XXX changes to filter program may require changes to the insn number(s) used below! XXX */ - dhcp_bpf_filter [8].k = ntohs (local_port); +#if defined(RELAY_PORT) + if (relay_port) { + /* + * If user defined relay UDP port, we need to filter + * also on the user UDP port. + */ + p.bf_len = dhcp_bpf_relay_filter_len; + p.bf_insns = dhcp_bpf_relay_filter; + + dhcp_bpf_relay_filter [10].k = ntohs (relay_port); + } +#endif + p.bf_insns [8].k = ntohs (local_port); if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0) log_fatal ("Can't install packet filter program: %m"); diff --git a/common/discover.c b/common/discover.c index f81d63f7..6ef88529 100644 --- a/common/discover.c +++ b/common/discover.c @@ -44,6 +44,7 @@ int interfaces_invalidated; int quiet_interface_discovery; u_int16_t local_port; u_int16_t remote_port; +u_int16_t relay_port = 0; int dhcpv4_over_dhcpv6 = 0; int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); int (*dhcp_interface_discovery_hook) (struct interface_info *); @@ -581,6 +582,10 @@ discover_interfaces(int state) { int ir; isc_result_t status; int wifcount = 0; +#ifdef RELAY_PORT + int updone = 0; + int downdone = 0; +#endif static int setup_fallback = 0; @@ -946,9 +951,39 @@ discover_interfaces(int state) { switch (local_family) { #ifdef DHCPv6 case AF_INET6: +#ifdef RELAY_PORT +#define UPSTREAM(ifp) \ + ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) +#define DOWNSTREAM(ifp) \ + ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM) + + if (relay_port) { + /* + * The normal IPv6 relay only needs one + * socket as long as we find an interface. + * When user relay port is defined, and we + * have two different UDP ports. One to + * receive from DHCP client with port 547, + * and the other is user defined for sending + * to the server or upstream relay agent. + * Thus we need to register sockets for one + * upstream and one downstream interfaces. + */ + if (updone && UPSTREAM(tmp)) + continue; + if (downdone && DOWNSTREAM(tmp)) + continue; + } +#endif status = omapi_register_io_object((omapi_object_t *)tmp, if_readsocket, 0, got_one_v6, 0, 0); +#ifdef RELAY_PORT + if (UPSTREAM(tmp)) + updone++; + else + downdone++; +#endif break; #endif /* DHCPv6 */ case AF_INET: @@ -970,8 +1005,12 @@ discover_interfaces(int state) { * dynamically adding and removing interfaces, but * we're well beyond that point in terms of mess. */ - if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) && - (local_family == AF_INET6)) + if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) + && (local_family == AF_INET6) +#if defined(RELAY_PORT) + && ((relay_port == 0) || (updone && downdone)) +#endif + ) break; #endif } /* for (tmp = interfaces; ... */ diff --git a/common/dlpi.c b/common/dlpi.c index b459dc6d..3990bf12 100644 --- a/common/dlpi.c +++ b/common/dlpi.c @@ -409,6 +409,10 @@ void if_deregister_send (info) XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the NIT program! XXX */ +#if defined(RELAY_PORT) +#error "Relay port is not yet supported for DLPI" +#endif + void if_register_receive (info) struct interface_info *info; { diff --git a/common/lpf.c b/common/lpf.c index 6e6ba014..82a279bb 100644 --- a/common/lpf.c +++ b/common/lpf.c @@ -177,6 +177,11 @@ void if_deregister_send (info) extern struct sock_filter dhcp_bpf_filter []; extern int dhcp_bpf_filter_len; +#if defined(RELAY_PORT) +extern struct sock_filter dhcp_bpf_relay_filter []; +extern int dhcp_bpf_relay_filter_len; +#endif + #if defined (HAVE_TR_SUPPORT) extern struct sock_filter dhcp_bpf_tr_filter []; extern int dhcp_bpf_tr_filter_len; @@ -256,7 +261,19 @@ static void lpf_gen_filter_setup (info) /* Patch the server port into the LPF program... XXX changes to filter program may require changes to the insn number(s) used below! XXX */ - dhcp_bpf_filter [8].k = ntohs ((short)local_port); +#if defined(RELAY_PORT) + if (relay_port) { + /* + * If user defined relay UDP port, we need to filter + * also on the user UDP port. + */ + p.len = dhcp_bpf_relay_filter_len; + p.filter = dhcp_bpf_relay_filter; + + dhcp_bpf_relay_filter [10].k = ntohs (relay_port); + } +#endif + dhcp_bpf_filter [8].k = ntohs (local_port); if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p, sizeof p) < 0) { diff --git a/common/nit.c b/common/nit.c index 1e14ebb7..d822c151 100644 --- a/common/nit.c +++ b/common/nit.c @@ -172,6 +172,10 @@ void if_deregister_send (info) XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the NIT program! XXX */ +#if defined(RELAY_PORT) +#error "Relay port is not yet supported for NIT" +#endif + void if_register_receive (info) struct interface_info *info; { diff --git a/common/packet.c b/common/packet.c index d6907fe0..f3f62c19 100644 --- a/common/packet.c +++ b/common/packet.c @@ -167,6 +167,12 @@ void assemble_udp_ip_header (interface, buf, bufix, /* Fill out the UDP header */ udp.uh_sport = local_port; /* XXX */ udp.uh_dport = port; /* XXX */ +#if defined(RELAY_PORT) + /* Change to relay port defined if sending to server */ + if (relay_port && (port == htons(67))) { + udp.uh_sport = relay_port; + } +#endif udp.uh_ulen = htons(sizeof(udp) + len); memset (&udp.uh_sum, 0, sizeof udp.uh_sum); @@ -296,7 +302,12 @@ decode_udp_ip_header(struct interface_info *interface, return -1; /* Is it to the port we're serving? */ +#if defined(RELAY_PORT) + if ((udp.uh_dport != local_port) && + ((relay_port == 0) || (udp.uh_dport != relay_port))) +#else if (udp.uh_dport != local_port) +#endif return -1; #endif /* USERLAND_FILTER */ diff --git a/common/raw.c b/common/raw.c index 3ad0bfd2..5c88b25e 100644 --- a/common/raw.c +++ b/common/raw.c @@ -55,14 +55,14 @@ void if_register_send (info) /* Set up the address we're going to connect to. */ name.sin_family = AF_INET; - name.sin_port = local_port; + name.sin_port = relay_port ? relay_port : local_port; name.sin_addr.s_addr = htonl (INADDR_BROADCAST); memset (name.sin_zero, 0, sizeof (name.sin_zero)); /* List addresses on which we're listening. */ if (!quiet_interface_discovery) log_info ("Sending on %s, port %d", - piaddr (info -> address), htons (local_port)); + piaddr (info -> address), htons (name.sin_port)); if ((sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) log_fatal ("Can't create dhcp socket: %m"); diff --git a/common/socket.c b/common/socket.c index 8879bc20..483eb9c3 100644 --- a/common/socket.c +++ b/common/socket.c @@ -65,6 +65,10 @@ static int no_global_v6_socket = 0; static unsigned int global_v6_socket_references = 0; static int global_v6_socket = -1; +#if defined(RELAY_PORT) +static unsigned int relay_port_v6_socket_references = 0; +static int relay_port_v6_socket = -1; +#endif static void if_register_multicast(struct interface_info *info); #endif @@ -157,6 +161,11 @@ if_register_socket(struct interface_info *info, int family, addr6 = (struct sockaddr_in6 *)&name; addr6->sin6_family = AF_INET6; addr6->sin6_port = local_port; +#if defined(RELAY_PORT) + if (relay_port && + ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) + addr6->sin6_port = relay_port; +#endif /* A server feature */ if (bind_local_address6) { memcpy(&addr6->sin6_addr, @@ -187,7 +196,7 @@ if_register_socket(struct interface_info *info, int family, default: addr = (struct sockaddr_in *)&name; addr->sin_family = AF_INET; - addr->sin_port = local_port; + addr->sin_port = relay_port ? relay_port : local_port; memcpy(&addr->sin_addr, &local_address, sizeof(addr->sin_addr)); @@ -496,6 +505,10 @@ if_register6(struct interface_info *info, int do_multicast) { log_fatal("Impossible condition at %s:%d", MDL); } +#if defined(RELAY_PORT) + if (!relay_port || + ((info->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)) { +#endif if (global_v6_socket_references == 0) { global_v6_socket = if_register_socket(info, AF_INET6, &req_multi, NULL); @@ -527,6 +540,30 @@ if_register6(struct interface_info *info, int do_multicast) { info->wfdesc = global_v6_socket; global_v6_socket_references++; +#if defined(RELAY_PORT) + } else { + /* + * If relay port is defined, we need to register one + * IPv6 UPD socket to handle upstream server or relay agent + * with a non-547 UDP local port. + */ + if ((relay_port_v6_socket_references == 0) && + ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) { + relay_port_v6_socket = if_register_socket(info, AF_INET6, + &req_multi, NULL); + if (relay_port_v6_socket < 0) { + log_fatal("Impossible condition at %s:%d", MDL); + } else { + log_info("Bound to relay port *:%d", + (int) ntohs(relay_port)); + } + } + info->rfdesc = relay_port_v6_socket; + info->wfdesc = relay_port_v6_socket; + relay_port_v6_socket_references++; + } +#endif + if (req_multi) if_register_multicast(info); @@ -617,6 +654,16 @@ if_deregister6(struct interface_info *info) { global_v6_socket_references--; info->rfdesc = -1; info->wfdesc = -1; +#if defined(RELAY_PORT) + } else if (relay_port && + (info->rfdesc == relay_port_v6_socket) && + (info->wfdesc == relay_port_v6_socket) && + (relay_port_v6_socket_references > 0)) { + /* Dereference the relay port v6 socket. */ + relay_port_v6_socket_references--; + info->rfdesc = -1; + info->wfdesc = -1; +#endif } else { log_fatal("Impossible condition at %s:%d", MDL); } @@ -633,12 +680,23 @@ if_deregister6(struct interface_info *info) { } } - if (!no_global_v6_socket && - (global_v6_socket_references == 0)) { - close(global_v6_socket); - global_v6_socket = -1; + if (!no_global_v6_socket) { + if (global_v6_socket_references == 0) { + close(global_v6_socket); + global_v6_socket = -1; - log_info("Unbound from *:%d", ntohs(local_port)); + log_info("Unbound from *:%d", + (int) ntohs(local_port)); + } +#if defined(RELAY_PORT) + if (relay_port && (relay_port_v6_socket_references == 0)) { + close(relay_port_v6_socket); + relay_port_v6_socket = -1; + + log_info("Unbound from relay port *:%d", + (int) ntohs(relay_port)); + } +#endif } } #endif /* DHCPv6 */ diff --git a/common/tables.c b/common/tables.c index 2350e803..b3d5ae66 100644 --- a/common/tables.c +++ b/common/tables.c @@ -557,12 +557,6 @@ static struct option dhcpv6_options[] = { { "solmax-rt", "L", &dhcpv6_universe, 82, 1 }, { "inf-max-rt", "L", &dhcpv6_universe, 83, 1 }, #endif -#if defined(RFC7710_OPTIONS) - { "v6-captive-portal", "t", &dhcpv6_universe, 103, 1 }, -#endif -#if defined(RFC6153_OPTIONS) - { "ipv6-address-andsf", "6A", &dhcpv6_universe, 143, 1 }, -#endif /* RFC7341 OPTIONS */ #if defined(RFC7341_OPTIONS) @@ -570,6 +564,16 @@ static struct option dhcpv6_options[] = { { "dhcp4-o-dhcp6-server", "6A", &dhcpv6_universe, 88, 1 }, #endif +#if defined(RFC7710_OPTIONS) + { "v6-captive-portal", "t", &dhcpv6_universe, 103, 1 }, +#endif + + { "relay-source-port", "S", &dhcpv6_universe, 135, 1 }, + +#if defined(RFC6153_OPTIONS) + { "ipv6-address-andsf", "6A", &dhcpv6_universe, 143, 1 }, +#endif + { NULL, NULL, NULL, 0, 0 } }; diff --git a/common/upf.c b/common/upf.c index 1b49301e..9785879a 100644 --- a/common/upf.c +++ b/common/upf.c @@ -156,6 +156,9 @@ void if_deregister_send (info) XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the UPF program! XXX */ +#if defined(RELAY_PORT) +#error "Relay port is not yet supported for UPF" +#endif void if_register_receive (info) struct interface_info *info; @@ -768,6 +768,7 @@ enable_tracing enable_delayed_ack enable_dhcpv6 enable_dhcpv4o6 +enable_relay_port enable_paranoia enable_early_chroot enable_ipv4_pktinfo @@ -1446,6 +1447,7 @@ Optional Features: --enable-dhcpv6 enable support for DHCPv6 (default is yes) --enable-dhcpv4o6 enable support for DHCPv4-over-DHCPv6 (default is no) + --enable-relay-port enable support for relay port (default is no) --enable-paranoia enable support for chroot/setuid (default is no) --enable-early-chroot enable chrooting prior to configuration (default is no) @@ -5487,6 +5489,19 @@ else enable_dhcpv4o6="no" fi +# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature. +# Check whether --enable-relay-port was given. +if test "${enable_relay_port+set}" = set; then : + enableval=$enable_relay_port; +fi + +# Relay port is off by default (for now) +if test "$enable_relay_port" = "yes"; then + +$as_echo "#define RELAY_PORT 1" >>confdefs.h + +fi + # PARANOIA is off by default (until we can test it with all features) # Check whether --enable-paranoia was given. if test "${enable_paranoia+set}" = set; then : @@ -6207,6 +6222,7 @@ fi done # needed for linux/filter.h on old systems +relay_port_supported="no" ac_fn_c_check_header_compile "$LINENO" "linux/filter.h" "ac_cv_header_linux_filter_h" " #ifdef HAVE_LINUX_TYPES_H #include <linux/types.h> @@ -6223,6 +6239,7 @@ then $as_echo "#define HAVE_LPF 1" >>confdefs.h + relay_port_supported="yes" else ac_fn_c_check_header_mongrel "$LINENO" "sys/dlpi.h" "ac_cv_header_sys_dlpi_h" "$ac_includes_default" if test "x$ac_cv_header_sys_dlpi_h" = xyes; then : @@ -6247,10 +6264,17 @@ fi $as_echo "#define HAVE_BPF 1" >>confdefs.h + relay_port_supported="yes" fi fi fi +if test "$enable_relay_port" = "yes"; then + if test "$relay_port_supported" != "yes"; then + as_fn_error $? "--enable-relay-port requires BPF or LPF" "$LINENO" 5 + fi +fi + # SIOCGLIFCONF uses some transport structures. Trick is not all platforms # use the same structures. We like to use 'struct lifconf' and 'struct # lifreq', but we'll use these other structures if they're present. HPUX @@ -9014,6 +9038,7 @@ Features: dhcpv6: $enable_dhcpv6 delayed-ack: $enable_delayed_ack dhcpv4o6: $enable_dhcpv4o6 + relay-port: $enable_relay_port Developer: ATF unittests : $atf_path diff --git a/configure.ac b/configure.ac index 52e7bb4e..62593d3f 100644 --- a/configure.ac +++ b/configure.ac @@ -177,6 +177,15 @@ else enable_dhcpv4o6="no" fi +# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature. +AC_ARG_ENABLE(relay-port, + AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)])) +# Relay port is off by default (for now) +if test "$enable_relay_port" = "yes"; then + AC_DEFINE([RELAY_PORT], [1], + [Define to 1 to include relay port support.]) +fi + # PARANOIA is off by default (until we can test it with all features) AC_ARG_ENABLE(paranoia, AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)])) @@ -521,6 +530,7 @@ AC_CHECK_HEADERS(ifaddrs.h) # figure out what IPv4 interface code to use AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems +relay_port_supported="no" AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, , [ #ifdef HAVE_LINUX_TYPES_H @@ -531,6 +541,7 @@ if test -n "$DO_LPF" then AC_DEFINE([HAVE_LPF], [1], [Define to 1 to use the Linux Packet Filter interface code.]) + relay_port_supported="yes" else AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1) if test -n "$DO_DLPI" @@ -544,10 +555,17 @@ else AC_DEFINE([HAVE_BPF], [1], [Define to 1 to use the Berkeley Packet Filter interface code.]) + relay_port_supported="yes" fi fi fi +if test "$enable_relay_port" = "yes"; then + if test "$relay_port_supported" != "yes"; then + AC_MSG_ERROR([--enable-relay-port requires BPF or LPF]) + fi +fi + # SIOCGLIFCONF uses some transport structures. Trick is not all platforms # use the same structures. We like to use 'struct lifconf' and 'struct # lifreq', but we'll use these other structures if they're present. HPUX @@ -1035,6 +1053,7 @@ Features: dhcpv6: $enable_dhcpv6 delayed-ack: $enable_delayed_ack dhcpv4o6: $enable_dhcpv4o6 + relay-port: $enable_relay_port Developer: ATF unittests : $atf_path diff --git a/configure.ac+lt b/configure.ac+lt index e8c0b16a..ede7ec04 100644 --- a/configure.ac+lt +++ b/configure.ac+lt @@ -178,6 +178,15 @@ else enable_dhcpv4o6="no" fi +# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature. +AC_ARG_ENABLE(relay-port, + AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)])) +# Relay port is off by default (for now) +if test "$enable_relay_port" = "yes"; then + AC_DEFINE([RELAY_PORT], [1], + [Define to 1 to include relay port support.]) +fi + # PARANOIA is off by default (until we can test it with all features) AC_ARG_ENABLE(paranoia, AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)])) @@ -522,6 +531,7 @@ AC_CHECK_HEADERS(ifaddrs.h) # figure out what IPv4 interface code to use AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems +relay_port_supported="no" AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, , [ #ifdef HAVE_LINUX_TYPES_H @@ -532,6 +542,7 @@ if test -n "$DO_LPF" then AC_DEFINE([HAVE_LPF], [1], [Define to 1 to use the Linux Packet Filter interface code.]) + relay_port_supported="yes" else AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1) if test -n "$DO_DLPI" @@ -545,10 +556,17 @@ else AC_DEFINE([HAVE_BPF], [1], [Define to 1 to use the Berkeley Packet Filter interface code.]) + relay_port_supported="yes" fi fi fi +if test "$enable_relay_port" = "yes"; then + if test "$relay_port_supported" != "yes"; then + AC_MSG_ERROR([--enable-relay-port requires BPF or LPF]) + fi +fi + # SIOCGLIFCONF uses some transport structures. Trick is not all platforms # use the same structures. We like to use 'struct lifconf' and 'struct # lifreq', but we'll use these other structures if they're present. HPUX @@ -1040,6 +1058,7 @@ Features: dhcpv6: $enable_dhcpv6 delayed-ack: $enable_delayed_ack dhcpv4o6: $enable_dhcpv4o6 + relay-port: $enable_relay_port Developer: ATF unittests : $atf_path diff --git a/configure.ac-base b/configure.ac-base index 89d3fbff..c6136ca9 100644 --- a/configure.ac-base +++ b/configure.ac-base @@ -183,6 +183,15 @@ else enable_dhcpv4o6="no" fi +# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature. +AC_ARG_ENABLE(relay-port, + AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)])) +# Relay port is off by default (for now) +if test "$enable_relay_port" = "yes"; then + AC_DEFINE([RELAY_PORT], [1], + [Define to 1 to include relay port support.]) +fi + # PARANOIA is off by default (until we can test it with all features) AC_ARG_ENABLE(paranoia, AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)])) @@ -527,6 +536,7 @@ AC_CHECK_HEADERS(ifaddrs.h) # figure out what IPv4 interface code to use AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems +relay_port_supported="no" AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, , [ #ifdef HAVE_LINUX_TYPES_H @@ -537,6 +547,7 @@ if test -n "$DO_LPF" then AC_DEFINE([HAVE_LPF], [1], [Define to 1 to use the Linux Packet Filter interface code.]) + relay_port_supported="yes" else AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1) if test -n "$DO_DLPI" @@ -550,10 +561,17 @@ else AC_DEFINE([HAVE_BPF], [1], [Define to 1 to use the Berkeley Packet Filter interface code.]) + relay_port_supported="yes" fi fi fi +if test "$enable_relay_port" = "yes"; then + if test "$relay_port_supported" != "yes"; then + AC_MSG_ERROR([--enable-relay-port requires BPF or LPF]) + fi +fi + # SIOCGLIFCONF uses some transport structures. Trick is not all platforms # use the same structures. We like to use 'struct lifconf' and 'struct # lifreq', but we'll use these other structures if they're present. HPUX @@ -1076,6 +1094,7 @@ Features: dhcpv6: $enable_dhcpv6 delayed-ack: $enable_delayed_ack dhcpv4o6: $enable_dhcpv4o6 + relay-port: $enable_relay_port Developer: ATF unittests : $atf_path diff --git a/configure.ac-lt b/configure.ac-lt index 52e7bb4e..62593d3f 100644 --- a/configure.ac-lt +++ b/configure.ac-lt @@ -177,6 +177,15 @@ else enable_dhcpv4o6="no" fi +# Relay port (draft-ietf-dhc-relay-port-10.txt) optional compile-time feature. +AC_ARG_ENABLE(relay-port, + AS_HELP_STRING([--enable-relay-port],[enable support for relay port (default is no)])) +# Relay port is off by default (for now) +if test "$enable_relay_port" = "yes"; then + AC_DEFINE([RELAY_PORT], [1], + [Define to 1 to include relay port support.]) +fi + # PARANOIA is off by default (until we can test it with all features) AC_ARG_ENABLE(paranoia, AS_HELP_STRING([--enable-paranoia],[enable support for chroot/setuid (default is no)])) @@ -521,6 +530,7 @@ AC_CHECK_HEADERS(ifaddrs.h) # figure out what IPv4 interface code to use AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems +relay_port_supported="no" AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, , [ #ifdef HAVE_LINUX_TYPES_H @@ -531,6 +541,7 @@ if test -n "$DO_LPF" then AC_DEFINE([HAVE_LPF], [1], [Define to 1 to use the Linux Packet Filter interface code.]) + relay_port_supported="yes" else AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1) if test -n "$DO_DLPI" @@ -544,10 +555,17 @@ else AC_DEFINE([HAVE_BPF], [1], [Define to 1 to use the Berkeley Packet Filter interface code.]) + relay_port_supported="yes" fi fi fi +if test "$enable_relay_port" = "yes"; then + if test "$relay_port_supported" != "yes"; then + AC_MSG_ERROR([--enable-relay-port requires BPF or LPF]) + fi +fi + # SIOCGLIFCONF uses some transport structures. Trick is not all platforms # use the same structures. We like to use 'struct lifconf' and 'struct # lifreq', but we'll use these other structures if they're present. HPUX @@ -1035,6 +1053,7 @@ Features: dhcpv6: $enable_dhcpv6 delayed-ack: $enable_delayed_ack dhcpv4o6: $enable_dhcpv4o6 + relay-port: $enable_relay_port Developer: ATF unittests : $atf_path diff --git a/doc/DHCPv4-over-DHCPv6 b/doc/DHCPv4-over-DHCPv6 index 2d82a057..415ea041 100644 --- a/doc/DHCPv4-over-DHCPv6 +++ b/doc/DHCPv4-over-DHCPv6 @@ -219,3 +219,10 @@ to start the server. Finally note in the configuration file the use of the shared-network to connect the DHCPv4 and DHCPv6 subnets. + +USE WITH DHCPv6 RELAY(s) +If the DHCPv6 infrastructure uses one (or more) relay because the client +and the server are not on the same link the best choice is to put the +first (closest to client) relay address in the dhcp4-o-dhcp6-server +option so the same path between the DHCPv6 client part and server part +will be used for DHCPv6 and DHCPv4-over-DHCPv6 traffic. diff --git a/includes/config.h.in b/includes/config.h.in index 28a4cfa9..1608f671 100644 --- a/includes/config.h.in +++ b/includes/config.h.in @@ -157,6 +157,9 @@ /* Define to any value to include Ari's PARANOIA patch. */ #undef PARANOIA +/* Define to 1 to include relay port support. */ +#undef RELAY_PORT + /* The size of `struct iaddr *', as computed by sizeof. */ #undef SIZEOF_STRUCT_IADDR_P diff --git a/includes/dhcp.h b/includes/dhcp.h index c9c482d0..0a74137e 100644 --- a/includes/dhcp.h +++ b/includes/dhcp.h @@ -183,6 +183,8 @@ struct dhcp_packet { #define RAI_REMOTE_ID 2 #define RAI_AGENT_ID 3 #define RAI_LINK_SELECT 5 +/* not yet assigned but next free value */ +#define RAI_RELAY_PORT 19 /* FQDN suboptions: */ #define FQDN_NO_CLIENT_UPDATE 1 diff --git a/includes/dhcp6.h b/includes/dhcp6.h index b51bac26..c78501c6 100644 --- a/includes/dhcp6.h +++ b/includes/dhcp6.h @@ -115,6 +115,8 @@ #define D6O_V6_PCP_SERVER 86 /* RFC7291 */ #define D6O_DHCPV4_MSG 87 /* RFC7341 */ #define D6O_DHCP4_O_DHCP6_SERVER 88 /* RFC7341 */ +/* not yet assigned but next free value */ +#define D6O_RELAY_SOURCE_PORT 135 /* I-D */ /* * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007, 5460. diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 044aca93..90456b76 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -469,9 +469,12 @@ struct packet { */ isc_boolean_t unicast; - /* Propogates server value SV_ECHO_CLIENT_ID so it is available + /* Propagates server value SV_ECHO_CLIENT_ID so it is available * in cons_options() */ int sv_echo_client_id; + + /* Relay port check */ + isc_boolean_t relay_source_port; }; /* @@ -2440,6 +2443,8 @@ void eval_network_statements(struct option_state **options, struct packet *packet, struct group *network_group); +u_int16_t dhcp_check_relayport(struct packet *packet); + /* dhcpleasequery.c */ void dhcpleasequery (struct packet *, int); void dhcpv6_leasequery (struct data_string *, struct packet *); @@ -2833,6 +2838,7 @@ extern int bind_local_address6; extern u_int16_t local_port; extern u_int16_t remote_port; +extern u_int16_t relay_port; extern int dhcpv4_over_dhcpv6; extern int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); diff --git a/relay/dhcrelay.8 b/relay/dhcrelay.8 index 22567ee5..53cd28bf 100644 --- a/relay/dhcrelay.8 +++ b/relay/dhcrelay.8 @@ -43,6 +43,9 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent [ .B -p .I port +| +.B -rp +.I relay-port ] [ .B -c @@ -112,6 +115,9 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent [ .B -p .I port +| +.B -rp +.I relay-port ] [ .B -c @@ -174,7 +180,7 @@ the command line, to which DHCP/BOOTP queries should be relayed. .PP \fIOptions available for both DHCPv4 and DHCPv6:\fR .TP --c COUNT +-c \fIcount\fR Maximum hop count. When forwarding packets, dhcrelay discards packets which have reached a hop count of COUNT. Default is 10. Maximum is 255. .TP @@ -182,9 +188,17 @@ which have reached a hop count of COUNT. Default is 10. Maximum is 255. Force dhcrelay to run as a foreground process. Useful when running dhcrelay under a debugger, or running out of inittab on System V systems. .TP --p PORT +-p \fIport\fR Listen and transmit on port PORT. This is mostly useful for debugging purposes. Default is port 67 for DHCPv4/BOOTP, or port 547 for DHCPv6. +Incompatible with \fB-rp\fR. +.TP +-rp \fIrelay-port\fR +Alternative source port for upstream (i.e toward the server) messages +with DHCPv4 RAI relay-port sub-option or DHCPv6 relay-source-port +option. Relay port support is only available if the code was compiled +with (./configure --enable-relay-port) and requires LPF or BPF link +layer access. .TP -q Quiet mode. Prevents dhcrelay6 from printing its network configuration @@ -209,7 +223,7 @@ be the printable name of the interface on which the client request was received. The client supports inclusion of a Remote ID suboption as well, but this is not used by default. .TP --A LENGTH +-A \fIlength\fR Specify the maximum packet size to send to a DHCPv4/BOOTP server. This might be done to allow sufficient space for addition of relay agent options while still fitting into the Ethernet MTU size. diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c index 897a7766..aaedd8ff 100644 --- a/relay/dhcrelay.c +++ b/relay/dhcrelay.c @@ -152,8 +152,30 @@ static const char url[] = char *progname; #ifdef DHCPv6 +#ifdef RELAY_PORT #define DHCRELAY_USAGE \ -"Usage: %s [-4] [-d] [-q] [-a] [-D]\n"\ +"Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \ +" [-A <length>] [-c <hops>]\n" \ +" [-p <port> | -rp <relay-port>]\n" \ +" [-pf <pid-file>] [--no-pid]\n"\ +" [-m append|replace|forward|discard]\n" \ +" [-i interface0 [ ... -i interfaceN]\n" \ +" [-iu interface0 [ ... -iu interfaceN]\n" \ +" [-id interface0 [ ... -id interfaceN]\n" \ +" [-U interface]\n" \ +" server0 [ ... serverN]\n\n" \ +" %s -6 [-d] [-q] [-I] [-c <hops>]\n" \ +" [-p <port> | -rp <relay-port>]\n" \ +" [-pf <pid-file>] [--no-pid]\n" \ +" [-s <subscriber-id>]\n" \ +" -l lower0 [ ... -l lowerN]\n" \ +" -u upper0 [ ... -u upperN]\n" \ +" lower (client link): [address%%]interface[#index]\n" \ +" upper (server link): [address%%]interface\n\n" \ +" %s {--version|--help|-h}" +#else +#define DHCRELAY_USAGE \ +"Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \ " [-A <length>] [-c <hops>] [-p <port>]\n" \ " [-pf <pid-file>] [--no-pid]\n"\ " [-m append|replace|forward|discard]\n" \ @@ -170,6 +192,20 @@ char *progname; " lower (client link): [address%%]interface[#index]\n" \ " upper (server link): [address%%]interface\n\n" \ " %s {--version|--help|-h}" +#endif +#else /* !DHCPv6 */ +#ifdef RELAY_PORT +#define DHCRELAY_USAGE \ +"Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>]\n" \ +" [-p <port> | -rp <relay-port>]\n" \ +" [-pf <pid-file>] [--no-pid]\n" \ +" [-m append|replace|forward|discard]\n" \ +" [-i interface0 [ ... -i interfaceN]\n" \ +" [-iu interface0 [ ... -iu interfaceN]\n" \ +" [-id interface0 [ ... -id interfaceN]\n" \ +" [-U interface]\n" \ +" server0 [ ... serverN]\n\n" \ +" %s {--version|--help|-h}" #else #define DHCRELAY_USAGE \ "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \ @@ -182,6 +218,7 @@ char *progname; " server0 [ ... serverN]\n\n" \ " %s {--version|--help|-h}" #endif +#endif /*! * @@ -199,6 +236,12 @@ char *progname; * \return Nothing */ static const char use_noarg[] = "No argument for command: %s"; +#ifdef RELAY_PORT +static const char use_port_defined[] = "Port already set, %s inappropriate"; +#if !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE) +static const char bpf_sock_support[] = "Only LPF and BPF are supported: %s"; +#endif +#endif #ifdef DHCPv6 static const char use_badproto[] = "Protocol already set, %s inappropriate"; static const char use_v4command[] = "Command not used for DHCPv6: %s"; @@ -236,6 +279,9 @@ main(int argc, char **argv) { int quiet = 0; int fd; int i; +#ifdef RELAY_PORT + int port_defined = 0; +#endif #ifdef DHCPv6 struct stream_list *sl = NULL; int local_family_set = 0; @@ -349,9 +395,26 @@ main(int argc, char **argv) { } else if (!strcmp(argv[i], "-p")) { if (++i == argc) usage(use_noarg, argv[i-1]); +#ifdef RELAY_PORT + if (port_defined) + usage(use_port_defined, argv[i-1]); + port_defined = 1; +#endif local_port = validate_port(argv[i]); log_debug("binding to user-specified port %d", ntohs(local_port)); +#ifdef RELAY_PORT + } else if (!strcmp(argv[i], "-rp")) { + if (++i == argc) + usage(use_noarg, argv[i-1]); + if (port_defined) + usage(use_port_defined, argv[i-1]); + port_defined = 1; + relay_port = validate_port(argv[i]); + log_debug("binding to user-specified relay port %d", + ntohs(relay_port)); + add_agent_options = 1; +#endif } else if (!strcmp(argv[i], "-c")) { int hcount; if (++i == argc) @@ -572,6 +635,12 @@ main(int argc, char **argv) { } } +#if defined(RELAY_PORT) && \ + !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE) + if (relay_port && (local_family == AF_INET)) + usage(bpf_sock_support, "-rp"); +#endif + /* * If the user didn't specify a pid file directly * find one from environment variables or defaults @@ -1259,6 +1328,12 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, optlen += 6; } +#ifdef RELAY_PORT + if (relay_port) { + optlen += 2; + } +#endif + /* We do not support relay option fragmenting(multiple options to * support an option data exceeding 255 bytes). */ @@ -1303,6 +1378,14 @@ add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, log_debug ("Adding link selection suboption" " with addr: %s", inet_ntoa(giaddr)); } + +#ifdef RELAY_PORT + /* draft-ietf-dhc-relay-port-10.txt section 5.1 */ + if (relay_port) { + *sp++ = RAI_RELAY_PORT; + *sp++ = 0u; + } +#endif } else { ++agent_option_errors; log_error("No room in packet (used %d of %d) " @@ -1548,6 +1631,9 @@ setup_streams(void) { static const int required_forw_opts[] = { D6O_INTERFACE_ID, D6O_SUBSCRIBER_ID, +#if defined(RELAY_PORT) + D6O_RELAY_SOURCE_PORT, +#endif D6O_RELAY_MSG, 0 }; @@ -1562,6 +1648,7 @@ process_up6(struct packet *packet, struct stream_list *dp) { struct dhcpv6_relay_packet *relay; struct option_state *opts; struct stream_list *up; + u_int16_t relay_client_port = 0; /* Check if the message should be relayed to the server. */ switch (packet->dhcpv6_msg_type) { @@ -1622,6 +1709,10 @@ process_up6(struct packet *packet, struct stream_list *dp) { } memset(&relay->link_address, 0, 16); } + + if (packet->client_port != htons(547)) { + relay_client_port = packet->client_port; + } } else { relay->hop_count = 0; if (!dp) @@ -1674,6 +1765,30 @@ process_up6(struct packet *packet, struct stream_list *dp) { } +#if defined(RELAY_PORT) + /* + * If we use a non-547 UDP source port or if we have received + * from a downstream relay agent uses a non-547 port, we need + * to include the RELAY-SOURCE-PORT option. The "Downstream + * UDP Port" field value in the option allow us to send + * relay-reply message back to the downstream relay agent + * with the correct UDP source port. + */ + if (relay_port || relay_client_port) { + if (!save_option_buffer(&dhcpv6_universe, opts, NULL, + (unsigned char *) &relay_client_port, + sizeof(u_int16_t), + D6O_RELAY_SOURCE_PORT, 0)) { + log_error("Can't save relay-source-port."); + option_state_dereference(&opts, MDL); + return; + } + } +#else + /* Avoid unused but set warning, */ + (void)(relay_client_port); +#endif + /* Add the relay-msg carrying the packet. */ if (!save_option_buffer(&dhcpv6_universe, opts, NULL, (unsigned char *) packet->raw, @@ -1708,6 +1823,9 @@ process_down6(struct packet *packet) { struct data_string relay_msg; const struct dhcpv6_packet *msg; struct data_string if_id; +#if defined(RELAY_PORT) + struct data_string down_port; +#endif struct sockaddr_in6 to; struct iaddr peer; @@ -1729,6 +1847,9 @@ process_down6(struct packet *packet) { /* Inits. */ memset(&relay_msg, 0, sizeof(relay_msg)); memset(&if_id, 0, sizeof(if_id)); +#if defined(RELAY_PORT) + memset(&down_port, 0, sizeof(down_port)); +#endif memset(&to, 0, sizeof(to)); to.sin6_family = AF_INET6; #ifdef HAVE_SA_LEN @@ -1799,6 +1920,37 @@ process_down6(struct packet *packet) { /* Relay-Reply of for another relay, not a client. */ case DHCPV6_RELAY_REPL: to.sin6_port = local_port; + +#if defined(RELAY_PORT) + oc = lookup_option(&dhcpv6_universe, packet->options, + D6O_RELAY_SOURCE_PORT); + if (oc != NULL) { + u_int16_t down_relay_port; + + memset(&down_port, 0, sizeof(down_port)); + if (!evaluate_option_cache(&down_port, packet, NULL, + NULL, packet->options, NULL, + &global_scope, oc, MDL) || + (down_port.len != sizeof(u_int16_t))) { + log_info("Can't evaluate down " + "relay-source-port."); + goto cleanup; + } + memcpy(&down_relay_port, down_port.data, + sizeof(u_int16_t)); + /* + * If the down_relay_port value is non-zero, + * that means our downstream relay agent uses + * a non-547 UDP source port sending + * relay-forw message to us. We need to use + * the same UDP port sending reply back. + */ + if (down_relay_port) { + to.sin6_port = down_relay_port; + } + } +#endif + /* Fall into: */ case DHCPV6_ADVERTISE: diff --git a/server/dhcp.c b/server/dhcp.c index f16d001d..a0080e52 100644 --- a/server/dhcp.c +++ b/server/dhcp.c @@ -1063,6 +1063,20 @@ void dhcpdecline (packet, ms_nulltp) lease_dereference (&lease, MDL); } +#if defined(RELAY_PORT) +u_int16_t dhcp_check_relayport(packet) + struct packet *packet; +{ + if (lookup_option(&agent_universe, + packet->options, + RAI_RELAY_PORT) != NULL) { + return (packet->client_port); + } + + return (0); +} +#endif + void dhcpinform (packet, ms_nulltp) struct packet *packet; int ms_nulltp; @@ -1084,6 +1098,7 @@ void dhcpinform (packet, ms_nulltp) struct interface_info *interface; int result, h_m_client_ip = 0; struct host_decl *host = NULL, *hp = NULL, *h; + u_int16_t relay_port = 0; #if defined (DEBUG_INFORM_HOST) int h_w_fixed_addr = 0; #endif @@ -1146,6 +1161,10 @@ void dhcpinform (packet, ms_nulltp) return; } +#if defined(RELAY_PORT) + relay_port = dhcp_check_relayport(packet); +#endif + /* Find the subnet that the client is on. * CC: Do the link selection / subnet selection */ @@ -1696,7 +1715,7 @@ void dhcpinform (packet, ms_nulltp) */ if (!raw.ciaddr.s_addr && gip.len) { memcpy(&to.sin_addr, gip.iabuf, 4); - to.sin_port = local_port; + to.sin_port = relay_port ? relay_port : local_port; raw.flags |= htons(BOOTP_BROADCAST); } else { gip.len = 0; @@ -1753,6 +1772,7 @@ void nak_lease (packet, cip, network_group) unsigned char nak = DHCPNAK; struct packet outgoing; unsigned i; + u_int16_t relay_port = 0; struct option_state *options = (struct option_state *)0; struct option_cache *oc = (struct option_cache *)0; struct option_state *eval_options = NULL; @@ -1781,6 +1801,10 @@ void nak_lease (packet, cip, network_group) save_option (&dhcp_universe, options, oc); option_cache_dereference (&oc, MDL); +#if defined(RELAY_PORT) + relay_port = dhcp_check_relayport(packet); +#endif + /* Set DHCP_MESSAGE to whatever the message is */ if (!option_cache_allocate (&oc, MDL)) { log_error ("No memory for DHCPNAK message type."); @@ -1929,7 +1953,7 @@ void nak_lease (packet, cip, network_group) if (raw.giaddr.s_addr) { to.sin_addr = raw.giaddr; if (raw.giaddr.s_addr != htonl (INADDR_LOOPBACK)) - to.sin_port = local_port; + to.sin_port = relay_port ? relay_port : local_port; else to.sin_port = remote_port; /* for testing. */ @@ -3752,6 +3776,7 @@ void dhcp_reply (lease) int result; struct lease_state *state = lease -> state; int nulltp, bootpp, unicastp = 1; + u_int16_t relay_port = 0; struct data_string d1; const char *s; @@ -3921,11 +3946,15 @@ void dhcp_reply (lease) #endif memset (to.sin_zero, 0, sizeof to.sin_zero); +#if defined(RELAY_PORT) + relay_port = dhcp_check_relayport(state->packet); +#endif + /* If this was gatewayed, send it back to the gateway... */ if (raw.giaddr.s_addr) { to.sin_addr = raw.giaddr; if (raw.giaddr.s_addr != htonl (INADDR_LOOPBACK)) - to.sin_port = local_port; + to.sin_port = relay_port ? relay_port : local_port; else to.sin_port = remote_port; /* For debugging. */ diff --git a/server/dhcpleasequery.c b/server/dhcpleasequery.c index e16de0fe..40de910d 100644 --- a/server/dhcpleasequery.c +++ b/server/dhcpleasequery.c @@ -152,6 +152,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) { u_int32_t time_rebinding; u_int32_t time_expiry; u_int32_t client_last_transaction_time; + u_int16_t relay_port = 0; struct sockaddr_in to; struct in_addr siaddr; struct data_string prl; @@ -660,12 +661,16 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) { #endif memset(to.sin_zero, 0, sizeof(to.sin_zero)); +#if defined(RELAY_PORT) + relay_port = dhcp_check_relayport(packet); +#endif + /* * Leasequery packets are be sent to the gateway address. */ to.sin_addr = packet->raw->giaddr; if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) { - to.sin_port = local_port; + to.sin_port = relay_port ? relay_port : local_port; } else { to.sin_port = remote_port; /* XXXSK: For debugging. */ } diff --git a/server/dhcpv6.c b/server/dhcpv6.c index f4bdbf81..a7110f98 100644 --- a/server/dhcpv6.c +++ b/server/dhcpv6.c @@ -27,6 +27,14 @@ static void send_dhcpv4_response(struct data_string *raw); static void recv_dhcpv4_query(struct data_string *raw); static void dhcp4o6_dhcpv4_query(struct data_string *reply_ret, struct packet *packet); + +struct udp_data4o6 { + u_int16_t src_port; + u_int8_t rsp_opt_exist; + u_int8_t reserved; +}; + +static int offset_data4o6 = 36; /* 16+16+4 */ #endif /* @@ -211,7 +219,7 @@ isc_result_t dhcpv4o6_handler(omapi_object_t *h) { cc = recv(dhcp4o6_fd, buf, sizeof(buf), 0); - if (cc < DHCP_FIXED_NON_UDP + 32) + if (cc < DHCP_FIXED_NON_UDP + offset_data4o6) return ISC_R_UNEXPECTED; memset(&raw, 0, sizeof(raw)); if (!buffer_allocate(&raw.buffer, cc, MDL)) { @@ -237,7 +245,7 @@ isc_result_t dhcpv4o6_handler(omapi_object_t *h) { * \brief Send the DHCPv4-response back to the DHCPv6 side * (DHCPv6 server function) * - * Format: interface:16 + address:16 + DHCPv6 DHCPv4-response message + * Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-response message * * \param raw the IPC message content */ @@ -246,6 +254,7 @@ static void send_dhcpv4_response(struct data_string *raw) { char name[16 + 1]; struct sockaddr_in6 to_addr; char pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + struct udp_data4o6 udp_data; int send_ret; memset(name, 0, sizeof(name)); @@ -263,26 +272,32 @@ static void send_dhcpv4_response(struct data_string *raw) { memset(&to_addr, 0, sizeof(to_addr)); to_addr.sin6_family = AF_INET6; memcpy(&to_addr.sin6_addr, raw->data + 16, 16); - if ((raw->data[32] == DHCPV6_RELAY_FORW) || - (raw->data[32] == DHCPV6_RELAY_REPL)) { - to_addr.sin6_port = local_port; + memset(&udp_data, 0, sizeof(udp_data)); + memcpy(&udp_data, raw->data + 32, 4); + if ((raw->data[36] == DHCPV6_RELAY_FORW) || + (raw->data[36] == DHCPV6_RELAY_REPL)) { + if (udp_data.rsp_opt_exist) { + to_addr.sin6_port = udp_data.src_port; + } else { + to_addr.sin6_port = local_port; + } } else { to_addr.sin6_port = remote_port; } log_info("send_dhcpv4_response(): sending %s on %s to %s port %d", - dhcpv6_type_names[raw->data[32]], + dhcpv6_type_names[raw->data[36]], name, inet_ntop(AF_INET6, raw->data + 16, pbuf, sizeof(pbuf)), ntohs(to_addr.sin6_port)); - send_ret = send_packet6(ip, raw->data + 32, raw->len - 32, &to_addr); + send_ret = send_packet6(ip, raw->data + 36, raw->len - 36, &to_addr); if (send_ret < 0) { log_error("send_dhcpv4_response: send_packet6(): %m"); - } else if (send_ret != raw->len - 32) { + } else if (send_ret != raw->len - 36) { log_error("send_dhcpv4_response: send_packet6() " "sent %d of %d bytes", - send_ret, raw->len - 32); + send_ret, raw->len - 36); } } #endif /* DHCP4o6 */ @@ -857,6 +872,9 @@ static const int required_opts_solicit[] = { }; static const int required_opts_agent[] = { D6O_INTERFACE_ID, +#if defined(RELAY_PORT) + D6O_RELAY_SOURCE_PORT, +#endif D6O_RELAY_MSG, 0 }; @@ -6854,6 +6872,35 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) { data_string_forget(&a_opt, MDL); } +#if defined(RELAY_PORT) + /* + * Append the relay_source_port option if present. + */ + oc = lookup_option(&dhcpv6_universe, packet->options, + D6O_RELAY_SOURCE_PORT); + if (oc != NULL) { + if (!evaluate_option_cache(&a_opt, packet, + NULL, NULL, + packet->options, NULL, + &global_scope, oc, MDL)) { + log_error("dhcpv6_relay_forw: error evaluating " + "Relay Source Port."); + goto exit; + } + if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, + (unsigned char *)a_opt.data, + a_opt.len, + D6O_RELAY_SOURCE_PORT, 0)) { + log_error("dhcpv6_relay_forw: error saving " + "Relay Source Port."); + goto exit; + } + data_string_forget(&a_opt, MDL); + + packet->relay_source_port = ISC_TRUE; + } +#endif + /* * Append our encapsulated stuff for caller. */ @@ -7147,6 +7194,35 @@ dhcp4o6_relay_forw(struct data_string *reply_ret, struct packet *packet) { data_string_forget(&a_opt, MDL); } +#if defined(RELAY_PORT) + /* + * Append the relay_source_port option if present. + */ + oc = lookup_option(&dhcpv6_universe, packet->options, + D6O_RELAY_SOURCE_PORT); + if (oc != NULL) { + if (!evaluate_option_cache(&a_opt, packet, + NULL, NULL, + packet->options, NULL, + &global_scope, oc, MDL)) { + log_error("dhcpv4o6_relay_forw: error evaluating " + "Relay Source Port."); + goto exit; + } + if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL, + (unsigned char *)a_opt.data, + a_opt.len, + D6O_RELAY_SOURCE_PORT, 0)) { + log_error("dhcpv4o6_relay_forw: error saving " + "Relay Source Port."); + goto exit; + } + data_string_forget(&a_opt, MDL); + + packet->relay_source_port = ISC_TRUE; + } +#endif + /* * Append our encapsulated stuff for caller. */ @@ -7436,12 +7512,13 @@ exit: * \brief Forward a DHCPv4-query message to the DHCPv4 side * (DHCPv6 server function) * - * Format: interface:16 + address:16 + DHCPv6 DHCPv4-query message + * Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-query message * * \brief packet the DHCPv6 DHCPv4-query message */ static void forw_dhcpv4_query(struct packet *packet) { struct data_string ds; + struct udp_data4o6 udp_data; unsigned len; int cc; @@ -7458,7 +7535,7 @@ static void forw_dhcpv4_query(struct packet *packet) { } /* Get a buffer. */ - len = packet->packet_length + 32; + len = packet->packet_length + 36; memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, len, MDL)) { log_error("forw_dhcpv4_query: " @@ -7472,7 +7549,10 @@ static void forw_dhcpv4_query(struct packet *packet) { strncpy((char *)ds.buffer->data, packet->interface->name, 16); memcpy(ds.buffer->data + 16, packet->client_addr.iabuf, 16); - memcpy(ds.buffer->data + 32, + memset(&udp_data, 0, sizeof(udp_data)); + udp_data.src_port = packet->client_port; + memcpy(ds.buffer->data + 32, &udp_data, 4); + memcpy(ds.buffer->data + 36, (unsigned char *)packet->raw, packet->packet_length); @@ -7690,6 +7770,15 @@ dhcpv6(struct packet *packet) { to_addr.sin6_port = packet->client_port; #endif +#if defined(RELAY_PORT) + /* + * Check relay source port. + */ + if (packet->relay_source_port) { + to_addr.sin6_port = packet->client_port; + } +#endif + memcpy(&to_addr.sin6_addr, packet->client_addr.iabuf, sizeof(to_addr.sin6_addr)); @@ -7716,7 +7805,7 @@ dhcpv6(struct packet *packet) { * Receive a message with a DHCPv4-query inside from the DHCPv6 server. * (code copied from \ref do_packet6() \ref and dhcpv6()) * - * Format: interface:16 + address:16 + DHCPv6 DHCPv4-query message + * Format: interface:16 + address:16 + udp:4 + DHCPv6 DHCPv4-query message * * \param raw the DHCPv6 DHCPv4-query message raw content */ @@ -7730,6 +7819,7 @@ static void recv_dhcpv4_query(struct data_string *raw) { const struct dhcpv4_over_dhcpv6_packet *msg; struct data_string reply; struct data_string ds; + struct udp_data4o6 udp_data; unsigned len; int cc; @@ -7748,14 +7838,17 @@ static void recv_dhcpv4_query(struct data_string *raw) { iaddr.len = 16; memcpy(iaddr.iabuf, raw->data + 16, 16); + memset(&udp_data, 0, sizeof(udp_data)); + memcpy(&udp_data, raw->data + 32, 4); + /* * From do_packet6(). */ - if (!packet6_len_okay((char *)raw->data + 32, raw->len - 32)) { + if (!packet6_len_okay((char *)raw->data + 36, raw->len - 36)) { log_error("recv_dhcpv4_query: " "short packet from %s, len %d, dropped", - piaddr(iaddr), raw->len - 32); + piaddr(iaddr), raw->len - 36); return; } @@ -7774,18 +7867,18 @@ static void recv_dhcpv4_query(struct data_string *raw) { return; } - packet->raw = (struct dhcp_packet *)(raw->data + 32); - packet->packet_length = raw->len - 32; - packet->client_port = remote_port; + packet->raw = (struct dhcp_packet *)(raw->data + 36); + packet->packet_length = raw->len - 36; + packet->client_port = udp_data.src_port; packet->client_addr = iaddr; interface_reference(&packet->interface, ip, MDL); - msg_type = raw->data[32]; + msg_type = raw->data[36]; if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options)); - relay = (const struct dhcpv6_relay_packet *)(raw->data + 32); + relay = (const struct dhcpv6_relay_packet *)(raw->data + 36); packet->dhcpv6_msg_type = relay->msg_type; /* relay-specific data */ @@ -7797,7 +7890,7 @@ static void recv_dhcpv4_query(struct data_string *raw) { if (!parse_option_buffer(packet->options, relay->options, - raw->len - 32 - relaylen, + raw->len - 36 - relaylen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ @@ -7808,7 +7901,7 @@ static void recv_dhcpv4_query(struct data_string *raw) { (msg_type == DHCPV6_DHCPV4_RESPONSE)) { int msglen = (int)(offsetof(struct dhcpv4_over_dhcpv6_packet, options)); - msg = (struct dhcpv4_over_dhcpv6_packet *)(raw->data + 32); + msg = (struct dhcpv4_over_dhcpv6_packet *)(raw->data + 36); packet->dhcpv6_msg_type = msg->msg_type; /* message-specific data */ @@ -7817,7 +7910,7 @@ static void recv_dhcpv4_query(struct data_string *raw) { if (!parse_option_buffer(packet->options, msg->options, - raw->len - 32 - msglen, + raw->len - 36 - msglen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ @@ -7878,18 +7971,19 @@ static void recv_dhcpv4_query(struct data_string *raw) { */ build_dhcpv6_reply(&reply, packet); - packet_dereference(&packet, MDL); - - if (reply.data == NULL) + if (reply.data == NULL) { + packet_dereference(&packet, MDL); return; + } /* * Forward the response. */ - len = reply.len + 32; + len = reply.len + 36; memset(&ds, 0, sizeof(ds)); if (!buffer_allocate(&ds.buffer, len, MDL)) { log_error("recv_dhcpv4_query: no memory."); + packet_dereference(&packet, MDL); return; } ds.data = ds.buffer->data; @@ -7897,7 +7991,15 @@ static void recv_dhcpv4_query(struct data_string *raw) { memcpy(ds.buffer->data, name, 16); memcpy(ds.buffer->data + 16, iaddr.iabuf, 16); - memcpy(ds.buffer->data + 32, reply.data, reply.len); + udp_data.rsp_opt_exist = packet->relay_source_port ? 1 : 0; + memcpy(ds.buffer->data + 32, &udp_data, 4); + memcpy(ds.buffer->data + 36, reply.data, reply.len); + + /* + * Now we can release the packet. + */ + packet_dereference(&packet, MDL); + cc = send(dhcp4o6_fd, ds.data, ds.len, 0); if (cc < 0) log_error("recv_dhcpv4_query: send(): %m"); diff --git a/server/stables.c b/server/stables.c index f3424c92..170f6da3 100644 --- a/server/stables.c +++ b/server/stables.c @@ -169,6 +169,7 @@ static struct option agent_options[] = { { "agent-id", "I", &agent_universe, 3, 1 }, { "DOCSIS-device-class", "L", &agent_universe, 4, 1 }, { "link-selection", "I", &agent_universe, 5, 1 }, + { "relay-port", "Z", &agent_universe, 19, 1 }, { NULL, NULL, NULL, 0, 0 } }; |