diff options
author | Thomas Markwalder <tmark@isc.org> | 2016-01-28 16:30:01 -0500 |
---|---|---|
committer | Thomas Markwalder <tmark@isc.org> | 2016-01-28 16:30:01 -0500 |
commit | 62a9eb918c7a12bc97fc72960d9c57f35e08d72c (patch) | |
tree | bbc108746102269b1eaa379e28afc1f18569d572 | |
parent | cab61f43770ff1785362adab09f885bc50a9ec5f (diff) | |
download | isc-dhcp-62a9eb918c7a12bc97fc72960d9c57f35e08d72c.tar.gz |
[master] Add support for relayed client-linklayer-address option (RFC 6939)
Merges in rt40334.
-rw-r--r-- | RELNOTES | 6 | ||||
-rw-r--r-- | includes/dhcp6.h | 3 | ||||
-rw-r--r-- | includes/dhcpd.h | 6 | ||||
-rw-r--r-- | server/dhcpv6.c | 135 | ||||
-rw-r--r-- | server/mdb6.c | 219 |
5 files changed, 236 insertions, 133 deletions
@@ -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 diff --git a/includes/dhcp6.h b/includes/dhcp6.h index 94ddc592..03fedfa8 100644 --- a/includes/dhcp6.h +++ b/includes/dhcp6.h @@ -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. diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 4270edca..cef87858 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -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); diff --git a/server/dhcpv6.c b/server/dhcpv6.c index 335e7296..f307c9b8 100644 --- a/server/dhcpv6.c +++ b/server/dhcpv6.c @@ -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 */ - diff --git a/server/mdb6.c b/server/mdb6.c index 3633cd58..984988ad 100644 --- a/server/mdb6.c +++ b/server/mdb6.c @@ -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 */ |