dhclient - Implement RFC 3442
authorMatthew Dillon <dillon@backplane.com>
Fri, 2 Jun 2017 17:57:44 +0000 (10:57 -0700)
committerMatthew Dillon <dillon@backplane.com>
Fri, 2 Jun 2017 17:57:44 +0000 (10:57 -0700)
* Implement RFC 3442

Taken-from: FreeBSD

sbin/dhclient/clparse.c
sbin/dhclient/dhclient-script
sbin/dhclient/dhclient.c
sbin/dhclient/dhclient.conf.5
sbin/dhclient/dhcp.h
sbin/dhclient/tables.c

index 8b67635..e5c6c95 100644 (file)
@@ -73,6 +73,8 @@ read_client_conf(void)
            [config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
        config->requested_options
            [config->requested_option_count++] = DHO_TIME_OFFSET;
+       config->requested_options
+           [config->requested_option_count++] = DHO_CLASSLESS_ROUTES;
        config->requested_options
            [config->requested_option_count++] = DHO_ROUTERS;
        config->requested_options
index bf4b2a9..5d6c3a4 100644 (file)
@@ -26,6 +26,14 @@ delete_old_address() {
        if [ -n "$old_ip_address" ]; then
                ifconfig $interface inet $old_ip_address delete
                #route delete "$old_ip_address" 127.0.0.1 >/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
 }
 
@@ -39,11 +47,60 @@ add_new_address() {
        #route add $new_ip_address 127.0.0.1 >/dev/null 2>&1
 }
 
+fill_classless_routes() {
+       set $1
+       while [ $# -ge 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() {
        arp -dan
 }
 
 add_new_routes() {
+       # 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
                route -q delete default
                if [ "$new_ip_address" = "$router" ]; then
index eab6682..8145d9e 100644 (file)
@@ -92,6 +92,7 @@ int           findproto(char *, int);
 struct sockaddr        *get_ifa(char *, int);
 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);
 char           *option_as_string(unsigned int code, unsigned char *data, int len);
@@ -2021,6 +2022,9 @@ check_option(struct client_lease *l, int option)
        case DHO_TFTP_SERVER:
        case DHO_END:
                return (1);
+       case DHO_CLASSLESS_ROUTES:
+               return (check_classless_option(l->options[option].data,
+                                              l->options[option].len));
        default:
                if (!unknown_ok)
                        warning("unknown dhcp option value 0x%x", option);
@@ -2028,6 +2032,70 @@ check_option(struct client_lease *l, int option)
        }
 }
 
+/* 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 *name)
 {
index cd40968..7c43b48 100644 (file)
@@ -440,7 +440,8 @@ interface "ep0" {
     supersede domain-name "fugue.com rc.vix.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;
+           classless-routes, domain-name, domain-name-servers,
+           host-name;
     require subnet-mask, domain-name-servers;
     script "/etc/dhclient-script";
 }
index fa6f53f..ff3930a 100644 (file)
@@ -155,6 +155,7 @@ struct dhcp_packet {
 #define DHO_DHCP_CLIENT_IDENTIFIER     61
 #define DHO_TFTP_SERVER                        66
 #define DHO_DHCP_USER_CLASS_ID         77
+#define DHO_CLASSLESS_ROUTES           121
 #define DHO_END                                255
 
 /* DHCP message types. */
index 8e87caa..480710c 100644 (file)
@@ -183,7 +183,7 @@ const struct option dhcp_options[256] = {
        /* 118 */ { "option-118", "X" },
        /* 119 */ { "option-119", "X" },
        /* 120 */ { "option-120", "X" },
-       /* 121 */ { "option-121", "X" },
+       /* 121 */ { "classless-routes", "BA" },
        /* 122 */ { "option-122", "X" },
        /* 123 */ { "option-123", "X" },
        /* 124 */ { "option-124", "X" },