]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[master] Add support for relayed client-linklayer-address option (RFC 6939)
authorThomas Markwalder <tmark@isc.org>
Thu, 28 Jan 2016 21:30:01 +0000 (16:30 -0500)
committerThomas Markwalder <tmark@isc.org>
Thu, 28 Jan 2016 21:30:01 +0000 (16:30 -0500)
    Merges in rt40334.

RELNOTES
includes/dhcp6.h
includes/dhcpd.h
server/dhcpv6.c
server/mdb6.c

index d3228acc414bec2dba6a38e0b39ffc8a624d74cd..68f32659f3bf456f5911b809396f8cc616176598 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -181,6 +181,12 @@ by Eric Young (eay@cryptsoft.com).
   to be set at configure time via the environment variable 'AR'.
   [ISC-Bugs #41536]
 
+- The server will now match DHCPv6 relayed clients to host declarations
+  which include the "hardware" statement, if the relay connected to the
+  client supplies the client's hardware address via client-linklayer-address
+  option as per RFC 6939.
+  [ISC-Bugs #40334]
+
                        Changes since 4.3.3b1
 
 - None
index 94ddc5927ed34c1be0f28fec224a922b451beb02..03fedfa8f7b34cab06f09b878da3d118d7299fc8 100644 (file)
@@ -3,7 +3,7 @@
    DHCPv6 Protocol structures... */
 
 /*
- * Copyright (c) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2013,2016 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 2006-2009 by Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -76,6 +76,7 @@
 #define D6O_CLT_TIME                           46 /* RFC5007 */
 #define D6O_LQ_RELAY_DATA                      47 /* RFC5007 */
 #define D6O_LQ_CLIENT_LINK                     48 /* RFC5007 */
+#define D6O_CLIENT_LINKLAYER_ADDR              79 /* RFC6939 */
 
 /* 
  * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
index 4270edcace4bac152ad96ce88ba0032287ab0959..cef878585f95a853e8eb65f8b5c7a79f1bff74c4 100644 (file)
@@ -3429,6 +3429,7 @@ int find_hosts_by_option(struct host_decl **, struct packet *,
                         struct option_state *, const char *, int);
 int find_host_for_network (struct subnet **, struct host_decl **,
                           struct iaddr *, struct shared_network *);
+
 void new_address_range (struct parse *, struct iaddr, struct iaddr,
                        struct subnet *, struct pool *,
                        struct lease **);
@@ -3750,6 +3751,11 @@ void mark_phosts_unavailable(void);
 void mark_interfaces_unavailable(void);
 void report_jumbo_ranges();
 
+#if defined(DHCPv6)
+int find_hosts6(struct host_decl** host, struct packet* packet,
+                const struct data_string* client_id, char* file, int line);
+#endif
+
 #if defined (BINARY_LEASES)
 /* leasechain.c */
 int lc_not_empty(struct leasechain *lc);
index 335e7296f53cc5f98785c00039e4cc0a53fe9152..f307c9b8295c90536d71805a8fe03fb56ea57691 100644 (file)
@@ -147,8 +147,6 @@ static isc_result_t reply_process_send_prefix(struct reply_state *reply,
 static struct iasubopt *prefix_compare(struct reply_state *reply,
                                       struct iasubopt *alpha,
                                       struct iasubopt *beta);
-static int find_hosts_by_duid_chaddr(struct host_decl **host,
-                                    const struct data_string *client_id);
 static void schedule_lease_timeout_reply(struct reply_state *reply);
 
 static int eval_prefix_mode(int thislen, int preflen, int prefix_mode);
@@ -1643,27 +1641,10 @@ lease_to_client(struct data_string *reply_ret,
        }
 
        /*
-        * Find a host record that matches from the packet, if any, and is
+        * Find a host record that matches the packet, if any, and is
         * valid for the shared network the client is on.
         */
-       if (find_hosts_by_uid(&reply.host, client_id->data, client_id->len,
-                             MDL)) {
-               packet->known = 1;
-               seek_shared_host(&reply.host, reply.shared);
-       }
-
-       if ((reply.host == NULL) &&
-           find_hosts_by_option(&reply.host, packet, packet->options, MDL)) {
-               packet->known = 1;
-               seek_shared_host(&reply.host, reply.shared);
-       }
-
-       /*
-        * Check for 'hardware' matches last, as some of the synthesis methods
-        * are not considered to be as reliable.
-        */
-       if ((reply.host == NULL) &&
-           find_hosts_by_duid_chaddr(&reply.host, client_id)) {
+       if (find_hosts6(&reply.host, packet, client_id, MDL)) {
                packet->known = 1;
                seek_shared_host(&reply.host, reply.shared);
        }
@@ -5552,24 +5533,7 @@ iterate_over_ia_na(struct data_string *reply_ret,
         * Find the host record that matches from the packet, if any.
         */
        packet_host = NULL;
-       if (!find_hosts_by_uid(&packet_host,
-                              client_id->data, client_id->len, MDL)) {
-               packet_host = NULL;
-               /*
-                * Note: In general, we don't expect a client to provide
-                *       enough information to match by option for these
-                *       types of messages, but if we don't have a UID
-                *       match we can check anyway.
-                */
-               if (!find_hosts_by_option(&packet_host,
-                                         packet, packet->options, MDL)) {
-                       packet_host = NULL;
-
-                       if (!find_hosts_by_duid_chaddr(&packet_host,
-                                                      client_id))
-                               packet_host = NULL;
-               }
-       }
+       find_hosts6(&packet_host, packet, client_id, MDL);
 
        /*
         * Set our reply information.
@@ -6081,24 +6045,7 @@ iterate_over_ia_pd(struct data_string *reply_ret,
         * Find the host record that matches from the packet, if any.
         */
        packet_host = NULL;
-       if (!find_hosts_by_uid(&packet_host,
-                              client_id->data, client_id->len, MDL)) {
-               packet_host = NULL;
-               /*
-                * Note: In general, we don't expect a client to provide
-                *       enough information to match by option for these
-                *       types of messages, but if we don't have a UID
-                *       match we can check anyway.
-                */
-               if (!find_hosts_by_option(&packet_host,
-                                         packet, packet->options, MDL)) {
-                       packet_host = NULL;
-
-                       if (!find_hosts_by_duid_chaddr(&packet_host,
-                                                      client_id))
-                               packet_host = NULL;
-               }
-       }
+       find_hosts6(&packet_host, packet, client_id, MDL);
 
        /*
         * Build our option state for reply.
@@ -6922,79 +6869,6 @@ fixed_matches_shared(struct host_decl *host, struct shared_network *shared) {
        return matched;
 }
 
-/*
- * find_host_by_duid_chaddr() synthesizes a DHCPv4-like 'hardware'
- * parameter from a DHCPv6 supplied DUID (client-identifier option),
- * and may seek to use client or relay supplied hardware addresses.
- */
-static int
-find_hosts_by_duid_chaddr(struct host_decl **host,
-                         const struct data_string *client_id) {
-       static int once_htype;
-       int htype, hlen;
-       const unsigned char *chaddr;
-
-       /*
-        * The DUID-LL and DUID-LLT must have a 2-byte DUID type and 2-byte
-        * htype.
-        */
-       if (client_id->len < 4)
-               return 0;
-
-       /*
-        * The third and fourth octets of the DUID-LL and DUID-LLT
-        * is the hardware type, but in 16 bits.
-        */
-       htype = getUShort(client_id->data + 2);
-       hlen = 0;
-       chaddr = NULL;
-
-       /* The first two octets of the DUID identify the type. */
-       switch(getUShort(client_id->data)) {
-             case DUID_LLT:
-               if (client_id->len > 8) {
-                       hlen = client_id->len - 8;
-                       chaddr = client_id->data + 8;
-               }
-               break;
-
-             case DUID_LL:
-               /*
-                * Note that client_id->len must be greater than or equal
-                * to four to get to this point in the function.
-                */
-               hlen = client_id->len - 4;
-               chaddr = client_id->data + 4;
-               break;
-
-             default:
-               break;
-       }
-
-       if ((hlen == 0) || (hlen > HARDWARE_ADDR_LEN))
-               return 0;
-
-       /*
-        * XXX: DHCPv6 gives a 16-bit field for the htype.  DHCPv4 gives an
-        * 8-bit field.  To change the semantics of the generic 'hardware'
-        * structure, we would have to adjust many DHCPv4 sources (from
-        * interface to DHCPv4 lease code), and we would have to update the
-        * 'hardware' config directive (probably being reverse compatible and
-        * providing a new upgrade/replacement primitive).  This is a little
-        * too much to change for now.  Hopefully we will revisit this before
-        * hardware types exceeding 8 bits are assigned.
-        */
-       if ((htype & 0xFF00) && !once_htype) {
-               once_htype = 1;
-               log_error("Attention: At least one client advertises a "
-                         "hardware type of %d, which exceeds the software "
-                         "limitation of 255.", htype);
-       }
-
-       return find_hosts_by_haddr(host, htype, chaddr, hlen, MDL);
-}
-
-
 /*!
  *
  * \brief Constructs a REPLY with status of UseMulticast to a given packet
@@ -7305,4 +7179,3 @@ get_first_ia_addr_val (struct packet* packet, int addr_type,
 }
 
 #endif /* DHCPv6 */
-
index 3633cd58a5a651951cb34a79c5807024610014d9..984988ad82125a038b43f11f917bcaac5503d3fc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2015 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2007-2016 by Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -2560,4 +2560,221 @@ report_jumbo_ranges() {
        }
 }
 
+
+/*
+ * \brief Tests that 16-bit hardware type is less than 256
+ *
+ * XXX: DHCPv6 gives a 16-bit field for the htype.  DHCPv4 gives an
+ * 8-bit field.  To change the semantics of the generic 'hardware'
+ * structure, we would have to adjust many DHCPv4 sources (from
+ * interface to DHCPv4 lease code), and we would have to update the
+ * 'hardware' config directive (probably being reverse compatible and
+ * providing a new upgrade/replacement primitive).  This is a little
+ * too much to change for now.  Hopefully we will revisit this before
+ * hardware types exceeding 8 bits are assigned.
+ *
+ * Uses a static variable to limit log occurence to once per startup
+ *
+ * \param htype hardware type value to test
+ *
+ * \return returns 0 if the value is too large
+ *
+*/
+int htype_bounds_check(uint16_t htype) {
+       static int log_once = 0;
+
+       if (htype & 0xFF00) {
+               if (!log_once) {
+                       log_error("Attention: At least one client advertises a "
+                         "hardware type of %d, which exceeds the software "
+                         "limitation of 255.", htype);
+                       log_once = 1;
+               }
+
+               return(0);
+       }
+
+       return(1);
+}
+
+/*!
+ * \brief Look for hosts by MAC address if it's available
+ *
+ * Checks the inbound packet against host declarations which specified:
+ *
+ *      "hardware ethernet <MAC>;"
+ *
+ * For directly connected clients, the function will use the MAC address
+ * contained in packet:haddr if it's populated.  \TODO - While the logic is in
+ * place for this search, the socket layer does not yet populate packet:haddr,
+ * this is to be done under rt41523.
+ *
+ * For relayed clients, the function will use the MAC address from the
+ * client-linklayer-address option if it has been supplied by the relay
+ * directly connected to the client.
+ *
+ * \param hp[out] - pointer to storage for the host delcaration if found
+ * \param packet - received packet
+ * \param opt_state - option state to search
+ * \param file - source file
+ * \param line - line number
+ *
+ * \return non-zero if a matching host was found, zero otherwise
+*/
+int find_hosts_by_haddr6(struct host_decl **hp,
+                        struct packet *packet,
+                        struct option_state *opt_state,
+                        const char *file, int line) {
+       int found = 0;
+       int htype;
+       int hlen;
+
+       /* For directly connected clients, use packet:haddr if populated */
+       if (packet->dhcpv6_container_packet == NULL) {
+               if (packet->haddr) {
+                       htype = packet->haddr->hbuf[0];
+                       hlen = packet->haddr->hlen - 1,
+                       log_debug("find_hosts_by_haddr6: using packet->haddr,"
+                                 " type: %d, len: %d", htype, hlen);
+                       found = find_hosts_by_haddr (hp, htype,
+                                                    &packet->haddr->hbuf[1],
+                                                    hlen, MDL);
+               }
+       } else {
+               /* The first container packet is the from the relay directly
+                * connected to the client. Per RFC 6939, that is only relay
+                * that may supply the client linklayer address option. */
+               struct packet *relay_packet = packet->dhcpv6_container_packet;
+               struct option_state *relay_state = relay_packet->options;
+               struct data_string rel_addr;
+               struct option_cache *oc;
+
+               /* Look for the option in the first relay packet */
+               oc = lookup_option(&dhcpv6_universe, relay_state,
+                                  D6O_CLIENT_LINKLAYER_ADDR);
+               if (!oc) {
+                       /* Not there, so bail */
+                       return (0);
+               }
+
+               /* The option is present, fetch the address data */
+               memset(&rel_addr, 0, sizeof(rel_addr));
+               if (!evaluate_option_cache(&rel_addr, relay_packet, NULL, NULL,
+                                          relay_state, NULL, &global_scope,
+                                          oc, MDL)) {
+                       log_error("find_hosts_by_add6:"
+                                 "Error evaluating option cache");
+                       return (0);
+               }
+
+               /* The relay address data should be:
+                *   byte 0 - 1 = hardware type
+                *   bytes 2 - hlen = hardware address
+                 * where  hlen ( hardware address len) is option data len - 2 */
+               hlen = rel_addr.len - 2;
+               if (hlen > 0 && hlen <= HARDWARE_ADDR_LEN) {
+                       htype = getUShort(rel_addr.data);
+                       if (htype_bounds_check(htype)) {
+                               /* Looks valid, let's search with it */
+                               log_debug("find_hosts_by_haddr6:"
+                                         "using relayed haddr"
+                                         " type: %d, len: %d", htype, hlen);
+                               found = find_hosts_by_haddr (hp, htype,
+                                                            &rel_addr.data[2],
+                                                            hlen, MDL);
+                       }
+               }
+
+               data_string_forget(&rel_addr, MDL);
+        }
+
+       return (found);
+}
+
+/*
+ * find_host_by_duid_chaddr() synthesizes a DHCPv4-like 'hardware'
+ * parameter from a DHCPv6 supplied DUID (client-identifier option),
+ * and may seek to use client or relay supplied hardware addresses.
+ */
+int
+find_hosts_by_duid_chaddr(struct host_decl **host,
+                         const struct data_string *client_id) {
+       int htype, hlen;
+       const unsigned char *chaddr;
+
+       /*
+        * The DUID-LL and DUID-LLT must have a 2-byte DUID type and 2-byte
+        * htype.
+        */
+       if (client_id->len < 4)
+               return 0;
+
+       /*
+        * The third and fourth octets of the DUID-LL and DUID-LLT
+        * is the hardware type, but in 16 bits.
+        */
+       htype = getUShort(client_id->data + 2);
+       hlen = 0;
+       chaddr = NULL;
+
+       /* The first two octets of the DUID identify the type. */
+       switch(getUShort(client_id->data)) {
+             case DUID_LLT:
+               if (client_id->len > 8) {
+                       hlen = client_id->len - 8;
+                       chaddr = client_id->data + 8;
+               }
+               break;
+
+             case DUID_LL:
+               /*
+                * Note that client_id->len must be greater than or equal
+                * to four to get to this point in the function.
+                */
+               hlen = client_id->len - 4;
+               chaddr = client_id->data + 4;
+               break;
+
+             default:
+               break;
+       }
+
+       if ((hlen == 0) || (hlen > HARDWARE_ADDR_LEN) ||
+           !htype_bounds_check(htype)) {
+               return (0);
+       }
+
+       return find_hosts_by_haddr(host, htype, chaddr, hlen, MDL);
+}
+
+/*
+ * \brief Finds a host record that matches the packet, if any
+ *
+ * This function centralizes the logic for matching v6 client
+ * packets to host declarations.  We check in the following order
+ * for matches with:
+ *
+ * 1. client_id if specified
+ * 2. MAC address when explicitly available
+ * 3. packet option
+ * 4. synthesized hardware address - this is done last as some
+ * synthesis methods are not consided to be reliable
+ *
+ * \param[out] host - pointer to storage for the located host
+ * \param packet - inbound client packet
+ * \param client_id - client identifier (if one)
+ * \param file - source file
+ * \param line - source file line number
+ * \return non-zero if a host is found, zero otherwise
+*/
+int
+find_hosts6(struct host_decl** host, struct packet* packet,
+            const struct data_string* client_id, char* file, int line) {
+        return (find_hosts_by_uid(host, client_id->data, client_id->len, MDL)
+                || find_hosts_by_haddr6(host, packet, packet->options, MDL)
+                || find_hosts_by_option(host, packet, packet->options, MDL)
+                || find_hosts_by_duid_chaddr(host, client_id));
+}
+
+
 /* unittest moved to server/tests/mdb6_unittest.c */