]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Merged rt44535 (relay port)
authorFrancis Dupont <fdupont@isc.org>
Sat, 23 Dec 2017 00:18:23 +0000 (01:18 +0100)
committerFrancis Dupont <fdupont@isc.org>
Sat, 23 Dec 2017 00:18:23 +0000 (01:18 +0100)
27 files changed:
RELNOTES
common/bpf.c
common/discover.c
common/dlpi.c
common/lpf.c
common/nit.c
common/packet.c
common/raw.c
common/socket.c
common/tables.c
common/upf.c
configure
configure.ac
configure.ac+lt
configure.ac-base
configure.ac-lt
doc/DHCPv4-over-DHCPv6
includes/config.h.in
includes/dhcp.h
includes/dhcp6.h
includes/dhcpd.h
relay/dhcrelay.8
relay/dhcrelay.c
server/dhcp.c
server/dhcpleasequery.c
server/dhcpv6.c
server/stables.c

index 7d2d4927be1e8ce683d65710e99f11d6f7476aa3..6b11b787fbc7accf5df257a7a6472f2852a50d01 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -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
index dc41f80985e17cfc68c4e0d3463986d739bfb8fa..16076fea4f81e3cef7f3d5c1d0a6b978e8f61251 100644 (file)
@@ -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");
index f81d63f76ea054dc1d98d22130bf00a13abe701a..6ef8852941320e610c44b31f6651f14e11c923af 100644 (file)
@@ -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; ... */
index b459dc6d14139eec2d3293ac05f68fd250a92f10..3990bf12a60f7aae5e3bfd2201f24e537656ec79 100644 (file)
@@ -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;
 {
index 6e6ba014898a11d5d0c74904a003e8e3511d9838..82a279bba5f2dcd8723dd2eddb8ea24c711d5996 100644 (file)
@@ -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) {
index 1e14ebb761350b35e0c8f14b7d294321f8bd726a..d822c151f1ec8a69ab838343db370fbd03e64110 100644 (file)
@@ -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;
 {
index d6907fe072634034078a48f2a9fe20077222aa69..f3f62c198715bbf2303d0bfef401146c2e5e1364 100644 (file)
@@ -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 */
 
index 3ad0bfd267b37f8483c58c2690ca47205f235f40..5c88b25ea2c301cb2c8f3811bffcaa218419a64a 100644 (file)
@@ -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");
 
index 8879bc20ff2cc4b93f4ce2ad764ef47beb2f1afc..483eb9c3bd53260a5c27f0849f8bc1c148e65777 100644 (file)
 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 */
index 2350e803821262ee98e08abe2006cf36028585b4..b3d5ae660711dbc7309eb65b511604ab3e534274 100644 (file)
@@ -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 }
 };
 
index 1b49301e71ec3c608c710fa5c6251bf3a9c2a7b9..9785879a0a7331c68b350961e069a88f28d316f7 100644 (file)
@@ -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;
index 5d12223e9cf9a25f7d21b066ea8ee17678ced5cb..e76404a804e861748910cb511d56c97464409589 100755 (executable)
--- a/configure
+++ b/configure
@@ -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
index 52e7bb4ea97be130d2274da0f6f442589d04bd9b..62593d3f4855446a29aced8158095be27c453220 100644 (file)
@@ -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
index e8c0b16af2052c09e0a902aed674abf2482896c8..ede7ec046cd69998cf4cedbdf931a8b8f9505b50 100644 (file)
@@ -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
index 89d3fbffd7cd552074d21001e9161d75dc2f2945..c6136ca924aee2e57c319bc55b42396a03c3d078 100644 (file)
@@ -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
index 52e7bb4ea97be130d2274da0f6f442589d04bd9b..62593d3f4855446a29aced8158095be27c453220 100644 (file)
@@ -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
index 2d82a0573be95d2191c70e6ad6de1f3eb509ff38..415ea041107721fdf4c6cb0ea16bc61203034a02 100644 (file)
@@ -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.
index 28a4cfa96617fbf8a9b618f09ea0ec545f42eef0..1608f67190498ca9a6ce7ac53c913cb16a2cf0c0 100644 (file)
 /* 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
 
index c9c482d07df39264aaedab59f687c0262df52c59..0a74137e055c0c0a0d28ba6623892c72b533c28f 100644 (file)
@@ -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
index b51bac26611c92b1606713e443b25d8a49c99173..c78501c60a4b3ead9ff62593396f8a6430bbfedb 100644 (file)
 #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.
index 044aca93570cf208c0aac470ef245d76f8b5342f..90456b76747cdc00dddaf1b3c12cc06ae3a6419a 100644 (file)
@@ -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 *);
index 22567ee5b5b6ed3623be078ddb2d1093a4e2373b..53cd28bfc996be13c04f1aed813d48cc59efe9d0 100644 (file)
@@ -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.
index 897a776656f5d4c74d5410f2bc45fe7d87af0e5f..aaedd8ff83b0ee311479df01f423919839bc9625 100644 (file)
@@ -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:
index f16d001d4965304f94836108ab6c716f69edd39c..a0080e52340b66e9f596e5b6ee545f93f26c0287 100644 (file)
@@ -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. */
 
index e16de0fed7525e1a4fc61da434ead14d0f9415a8..40de910d08688073c1dbf4ed82c8b9c308750ea8 100644 (file)
@@ -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. */
        }
index f4bdbf81ff52a048c546c85496bcdaccceb54290..a7110f982f878fbf357ddfe5afd11a2a5a388b60 100644 (file)
@@ -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");
index f3424c927cb3a5dd4f1535c9970d2559906dc343..170f6da31bdd36f69761a427a5c6b97187b8889e 100644 (file)
@@ -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 }
 };