From 4d18a6391e0641145ae2bba5e54e5bf9e763eb57 Mon Sep 17 00:00:00 2001 From: emaste Date: Fri, 9 Feb 2007 17:50:26 +0000 Subject: [PATCH] Implement RFC3442, the Classless Static Route option. The original DHCP specification includes a route option but it supports only class-based routes. RFC3442 adds support for specifying the netmask width for each static route. A variable length encoding is used to minimize the size of this option. PR: bin/99534 Submitted by: Andrey V. Elsukov Reviewed by: brooks --- sbin/dhclient/clparse.c | 2 ++ sbin/dhclient/dhclient-script | 60 +++++++++++++++++++++++++++++++ sbin/dhclient/dhclient.c | 68 +++++++++++++++++++++++++++++++++++ sbin/dhclient/dhclient.conf | 5 +-- sbin/dhclient/dhcp.h | 1 + sbin/dhclient/tables.c | 5 +-- 6 files changed, 137 insertions(+), 4 deletions(-) diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c index e43a876e0310..5d7084e74c79 100644 --- a/sbin/dhclient/clparse.c +++ b/sbin/dhclient/clparse.c @@ -89,6 +89,8 @@ read_client_conf(void) [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS; top_level_config.requested_options [top_level_config.requested_option_count++] = DHO_TIME_OFFSET; + top_level_config.requested_options + [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES; top_level_config.requested_options [top_level_config.requested_option_count++] = DHO_ROUTERS; top_level_config.requested_options diff --git a/sbin/dhclient/dhclient-script b/sbin/dhclient/dhclient-script index ced527d39428..899cb6de6a4b 100644 --- a/sbin/dhclient/dhclient-script +++ b/sbin/dhclient/dhclient-script @@ -86,8 +86,43 @@ add_new_alias() { fi } +fill_classless_routes() { + set $1 + while [ $# -gt 5 ]; do + if [ $1 -eq 0 ]; then + route="default" + elif [ $1 -le 8 ]; then + route="$2.0.0.0/$1" + shift + elif [ $1 -le 16 ]; then + route="$2.$3.0.0/$1" + shift; shift + elif [ $1 -le 24 ]; then + route="$2.$3.$4.0/$1" + shift; shift; shift + else + route="$2.$3.$4.$5/$1" + shift; shift; shift; shift + fi + shift + router="$1.$2.$3.$4" + classless_routes="$classless_routes $route $router" + shift; shift; shift; shift + done +} + delete_old_routes() { #route delete "$old_ip_address" $LOCALHOST >/dev/null 2>&1 + if [ -n "$old_classless_routes" ]; then + fill_classless_routes "$old_classless_routes" + set $classless_routes + while [ $# -gt 1 ]; do + route delete "$1" "$2" + shift; shift + done + return 0; + fi + for router in $old_routers; do if [ $if_defaultroute = x -o $if_defaultroute = $interface ]; then route delete default $route >/dev/null 2>&1 @@ -107,6 +142,31 @@ delete_old_routes() { add_new_routes() { #route add $new_ip_address $LOCALHOST >/dev/null 2>&1 + + # RFC 3442: If the DHCP server returns both a Classless Static + # Routes option and a Router option, the DHCP client MUST ignore + # the Router option. + # + # DHCP clients that support this option (Classless Static Routes) + # MUST NOT install the routes specified in the Static Routes + # option (option code 33) if both a Static Routes option and the + # Classless Static Routes option are provided. + + if [ -n "$new_classless_routes" ]; then + fill_classless_routes "$new_classless_routes" + $LOGGER "New Classless Static Routes ($interface): $classless_routes" + set $classless_routes + while [ $# -gt 1 ]; do + if [ "0.0.0.0" = "$2" ]; then + route add "$1" -iface "$interface" + else + route add "$1" "$2" + fi + shift; shift + done + return + fi + for router in $new_routers; do if [ "$new_ip_address" = "$router" ]; then route add default -iface $router >/dev/null 2>&1 diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c index 4fa55edd958f..c2d3635b492c 100644 --- a/sbin/dhclient/dhclient.c +++ b/sbin/dhclient/dhclient.c @@ -115,6 +115,7 @@ struct sockaddr *get_ifa(char *, int); void routehandler(struct protocol *); void usage(void); int check_option(struct client_lease *l, int option); +int check_classless_option(unsigned char *data, int len); int ipv4addrs(char * buf); int res_hnok(const char *dn); int check_search(const char *srch); @@ -2374,12 +2375,79 @@ check_option(struct client_lease *l, int option) case DHO_DHCP_USER_CLASS_ID: case DHO_END: return (1); + case DHO_CLASSLESS_ROUTES: + return (check_classless_option(l->options[option].data, + l->options[option].len)); default: warning("unknown dhcp option value 0x%x", option); return (unknown_ok); } } +/* RFC 3442 The Classless Static Routes option checks */ +int +check_classless_option(unsigned char *data, int len) +{ + int i = 0; + unsigned char width; + in_addr_t addr, mask; + + if (len < 5) { + warning("Too small length: %d", len); + return (0); + } + while(i < len) { + width = data[i++]; + if (width == 0) { + i += 4; + continue; + } else if (width < 9) { + addr = (in_addr_t)(data[i] << 24); + i += 1; + } else if (width < 17) { + addr = (in_addr_t)(data[i] << 24) + + (in_addr_t)(data[i + 1] << 16); + i += 2; + } else if (width < 25) { + addr = (in_addr_t)(data[i] << 24) + + (in_addr_t)(data[i + 1] << 16) + + (in_addr_t)(data[i + 2] << 8); + i += 3; + } else if (width < 33) { + addr = (in_addr_t)(data[i] << 24) + + (in_addr_t)(data[i + 1] << 16) + + (in_addr_t)(data[i + 2] << 8) + + data[i + 3]; + i += 4; + } else { + warning("Incorrect subnet width: %d", width); + return (0); + } + mask = (in_addr_t)(~0) << (32 - width); + addr = ntohl(addr); + mask = ntohl(mask); + + /* + * From RFC 3442: + * ... After deriving a subnet number and subnet mask + * from each destination descriptor, the DHCP client + * MUST zero any bits in the subnet number where the + * corresponding bit in the mask is zero... + */ + if ((addr & mask) != addr) { + addr &= mask; + data[i - 1] = (unsigned char)( + (addr >> (((32 - width)/8)*8)) & 0xFF); + } + i += 4; + } + if (i > len) { + warning("Incorrect data length: %d (must be %d)", len, i); + return (0); + } + return (1); +} + int res_hnok(const char *dn) { diff --git a/sbin/dhclient/dhclient.conf b/sbin/dhclient/dhclient.conf index 4f280668b8e8..7eaeeb8eb40a 100644 --- a/sbin/dhclient/dhclient.conf +++ b/sbin/dhclient/dhclient.conf @@ -5,8 +5,9 @@ send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; send dhcp-lease-time 3600; supersede domain-name "fugue.com home.vix.com"; prepend domain-name-servers 127.0.0.1; -request subnet-mask, broadcast-address, time-offset, routers, - domain-name, domain-name-servers, host-name; +request subnet-mask, broadcast-address, time-offset, + classless-routes, routers, domain-name, + domain-name-servers, host-name; require subnet-mask, domain-name-servers; timeout 60; retry 60; diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h index db1c05c4fddc..3f0eb81bb65c 100644 --- a/sbin/dhclient/dhcp.h +++ b/sbin/dhclient/dhcp.h @@ -162,6 +162,7 @@ struct dhcp_packet { #define DHO_FINGER_SERVER 73 #define DHO_IRC_SERVER 74 #define DHO_DHCP_USER_CLASS_ID 77 +#define DHO_CLASSLESS_ROUTES 121 #define DHO_END 255 /* DHCP message types. */ diff --git a/sbin/dhclient/tables.c b/sbin/dhclient/tables.c index 2c3add2a27c8..f7ee057bf797 100644 --- a/sbin/dhclient/tables.c +++ b/sbin/dhclient/tables.c @@ -186,7 +186,7 @@ struct option dhcp_options[256] = { { "option-118", "X", &dhcp_universe, 118 }, { "option-119", "X", &dhcp_universe, 119 }, { "option-120", "X", &dhcp_universe, 120 }, - { "option-121", "X", &dhcp_universe, 121 }, + { "classless-routes", "BA", &dhcp_universe, 121 }, { "option-122", "X", &dhcp_universe, 122 }, { "option-123", "X", &dhcp_universe, 123 }, { "option-124", "X", &dhcp_universe, 124 }, @@ -337,6 +337,7 @@ unsigned char dhcp_option_default_priority_list[] = { DHO_DHCP_CLIENT_IDENTIFIER, DHO_SUBNET_MASK, DHO_TIME_OFFSET, + DHO_CLASSLESS_ROUTES, DHO_ROUTERS, DHO_TIME_SERVERS, DHO_NAME_SERVERS, @@ -392,7 +393,7 @@ unsigned char dhcp_option_default_priority_list[] = { 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, - 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 119, 120, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,