diff -up dhcp-4.3.4/client/clparse.c.rfc3442 dhcp-4.3.4/client/clparse.c --- dhcp-4.3.4/client/clparse.c.rfc3442 2016-04-29 12:23:34.192032714 +0200 +++ dhcp-4.3.4/client/clparse.c 2016-04-29 12:24:37.531016317 +0200 @@ -31,7 +31,7 @@ struct client_config top_level_config; -#define NUM_DEFAULT_REQUESTED_OPTS 14 +#define NUM_DEFAULT_REQUESTED_OPTS 15 /* There can be 2 extra requested options for DHCPv4-over-DHCPv6. */ struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 2 + 1]; @@ -87,7 +87,11 @@ isc_result_t read_client_conf () dhcp_universe.code_hash, &code, 0, MDL); /* 4 */ - code = DHO_ROUTERS; + /* The Classless Static Routes option code MUST appear in the parameter + * request list prior to both the Router option code and the Static + * Routes option code, if present. (RFC3442) + */ + code = DHO_CLASSLESS_STATIC_ROUTES; option_code_hash_lookup(&default_requested_options[3], dhcp_universe.code_hash, &code, 0, MDL); @@ -141,6 +145,11 @@ isc_result_t read_client_conf () option_code_hash_lookup(&default_requested_options[13], dhcp_universe.code_hash, &code, 0, MDL); + /* 15 */ + code = DHO_ROUTERS; + option_code_hash_lookup(&default_requested_options[14], + dhcp_universe.code_hash, &code, 0, MDL); + for (code = 0 ; code < NUM_DEFAULT_REQUESTED_OPTS ; code++) { if (default_requested_options[code] == NULL) log_fatal("Unable to find option definition for " diff -up dhcp-4.3.4/common/dhcp-options.5.rfc3442 dhcp-4.3.4/common/dhcp-options.5 --- dhcp-4.3.4/common/dhcp-options.5.rfc3442 2016-04-29 12:23:34.183032716 +0200 +++ dhcp-4.3.4/common/dhcp-options.5 2016-04-29 12:23:34.237032703 +0200 @@ -111,6 +111,26 @@ hexadecimal, separated by colons. For e or option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f; .fi +.PP +The +.B destination-descriptor +describe the IP subnet number and subnet mask +of a particular destination using a compact encoding. This encoding +consists of one octet describing the width of the subnet mask, +followed by all the significant octets of the subnet number. +The following table contains some examples of how various subnet +number/mask combinations can be encoded: +.nf +.sp 1 +Subnet number Subnet mask Destination descriptor +0 0 0 +10.0.0.0 255.0.0.0 8.10 +10.0.0.0 255.255.255.0 24.10.0.0 +10.17.0.0 255.255.0.0 16.10.17 +10.27.129.0 255.255.255.0 24.10.27.129 +10.229.0.128 255.255.255.128 25.10.229.0.128 +10.198.122.47 255.255.255.255 32.10.198.122.47 +.fi .SH SETTING OPTION VALUES USING EXPRESSIONS Sometimes it's helpful to be able to set the value of a DHCP option based on some value that the client has sent. To do this, you can @@ -1031,6 +1051,29 @@ dhclient-script will create routes: .RE .PP .nf +.B option \fBclassless-static-routes\fR \fIdestination-descriptor ip-address\fR + [\fB,\fR \fIdestination-descriptor ip-address\fR...]\fB;\fR +.fi +.RS 0.25i +.PP +This option (see RFC3442) specifies a list of classless static routes +that the client should install in its routing cache. +.PP +This option can contain one or more static routes, each of which +consists of a destination descriptor and the IP address of the router +that should be used to reach that destination. +.PP +Many clients may not implement the Classless Static Routes option. +DHCP server administrators should therefore configure their DHCP +servers to send both a Router option and a Classless Static Routes +option, and should specify the default router(s) both in the Router +option and in the Classless Static Routes option. +.PP +If the DHCP server returns both a Classless Static Routes option and +a Router option, the DHCP client ignores the Router option. +.RE +.PP +.nf .B option \fBstreettalk-directory-assistance-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...]\fB;\fR .fi diff -up dhcp-4.3.4/common/inet.c.rfc3442 dhcp-4.3.4/common/inet.c --- dhcp-4.3.4/common/inet.c.rfc3442 2016-03-22 14:16:51.000000000 +0100 +++ dhcp-4.3.4/common/inet.c 2016-04-29 12:23:34.237032703 +0200 @@ -519,6 +519,60 @@ free_iaddrcidrnetlist(struct iaddrcidrne return ISC_R_SUCCESS; } +static const char * +inet_ntopdd(const unsigned char *src, unsigned srclen, char *dst, size_t size) +{ + char tmp[sizeof("32.255.255.255.255")]; + int len; + + switch (srclen) { + case 2: + len = sprintf (tmp, "%u.%u", src[0], src[1]); + break; + case 3: + len = sprintf (tmp, "%u.%u.%u", src[0], src[1], src[2]); + break; + case 4: + len = sprintf (tmp, "%u.%u.%u.%u", src[0], src[1], src[2], src[3]); + break; + case 5: + len = sprintf (tmp, "%u.%u.%u.%u.%u", src[0], src[1], src[2], src[3], src[4]); + break; + default: + return NULL; + } + if (len < 0) + return NULL; + + if (len > size) { + errno = ENOSPC; + return NULL; + } + + return strcpy (dst, tmp); +} + +/* pdestdesc() turns an iaddr structure into a printable dest. descriptor */ +const char * +pdestdesc(const struct iaddr addr) { + static char pbuf[sizeof("255.255.255.255.255")]; + + if (addr.len == 0) { + return ""; + } + if (addr.len == 1) { + return "0"; + } + if ((addr.len >= 2) && (addr.len <= 5)) { + return inet_ntopdd(addr.iabuf, addr.len, pbuf, sizeof(pbuf)); + } + + log_fatal("pdestdesc():%s:%d: Invalid destination descriptor length %d.", + MDL, addr.len); + /* quell compiler warnings */ + return NULL; +} + /* piaddr() turns an iaddr structure into a printable address. */ /* XXX: should use a const pointer rather than passing the structure */ const char * diff -up dhcp-4.3.4/common/options.c.rfc3442 dhcp-4.3.4/common/options.c --- dhcp-4.3.4/common/options.c.rfc3442 2016-03-22 14:16:51.000000000 +0100 +++ dhcp-4.3.4/common/options.c 2016-04-29 12:23:34.237032703 +0200 @@ -713,7 +713,11 @@ cons_options(struct packet *inpacket, st * packet. */ priority_list[priority_len++] = DHO_SUBNET_MASK; - priority_list[priority_len++] = DHO_ROUTERS; + if (lookup_option(&dhcp_universe, cfg_options, + DHO_CLASSLESS_STATIC_ROUTES)) + priority_list[priority_len++] = DHO_CLASSLESS_STATIC_ROUTES; + else + priority_list[priority_len++] = DHO_ROUTERS; priority_list[priority_len++] = DHO_DOMAIN_NAME_SERVERS; priority_list[priority_len++] = DHO_HOST_NAME; priority_list[priority_len++] = DHO_FQDN; @@ -1694,6 +1698,7 @@ const char *pretty_print_option (option, unsigned long tval; isc_boolean_t a_array = ISC_FALSE; int len_used; + unsigned int octets = 0; if (emit_commas) comma = ','; @@ -1702,6 +1707,7 @@ const char *pretty_print_option (option, memset (enumbuf, 0, sizeof enumbuf); + if (option->format[0] != 'R') { /* see explanation lower */ /* Figure out the size of the data. */ for (l = i = 0; option -> format [i]; i++, l++) { if (l >= sizeof(fmtbuf) - 1) @@ -1894,6 +1900,33 @@ const char *pretty_print_option (option, if (numhunk < 0) numhunk = 1; + } else { /* option->format[i] == 'R') */ + /* R (destination descriptor) has variable length. + * We can find it only in classless static route option, + * so we are for sure parsing classless static route option now. + * We go through whole the option to check whether there are no + * missing/extra bytes. + * I didn't find out how to improve the existing code and that's the + * reason for this separate 'else' where I do my own checkings. + * I know it's little bit unsystematic, but it works. + */ + numhunk = 0; + numelem = 2; /* RI */ + fmtbuf[0]='R'; fmtbuf[1]='I'; fmtbuf[2]=0; + for (i =0; i < len; i = i + octets + 5) { + if (data[i] > 32) { /* subnet mask width */ + log_error ("wrong subnet mask width in destination descriptor"); + break; + } + numhunk++; + octets = ((data[i]+7) / 8); + } + if (i != len) { + log_error ("classless static routes option has wrong size or " + "there's some garbage in format"); + } + } + /* Cycle through the array (or hunk) printing the data. */ for (i = 0; i < numhunk; i++) { if ((a_array == ISC_TRUE) && (i != 0) && (numelem > 0)) { @@ -2049,6 +2082,20 @@ const char *pretty_print_option (option, strcpy(op, piaddr(iaddr)); dp += 4; break; + + case 'R': + if (dp[0] <= 32) + iaddr.len = (((dp[0]+7)/8)+1); + else { + log_error ("wrong subnet mask width in destination descriptor"); + return ""; + } + + memcpy(iaddr.iabuf, dp, iaddr.len); + strcpy(op, pdestdesc(iaddr)); + dp += iaddr.len; + break; + case '6': iaddr.len = 16; memcpy(iaddr.iabuf, dp, 16); diff -up dhcp-4.3.4/common/parse.c.rfc3442 dhcp-4.3.4/common/parse.c --- dhcp-4.3.4/common/parse.c.rfc3442 2016-04-29 12:23:34.220032707 +0200 +++ dhcp-4.3.4/common/parse.c 2016-04-29 12:23:34.238032702 +0200 @@ -341,6 +341,39 @@ int parse_ip_addr (cfile, addr) } /* + * destination-descriptor :== NUMBER DOT NUMBER | + * NUMBER DOT NUMBER DOT NUMBER | + * NUMBER DOT NUMBER DOT NUMBER DOT NUMBER | + * NUMBER DOT NUMBER DOT NUMBER DOT NUMBER DOT NUMBER + */ + +int parse_destination_descriptor (cfile, addr) + struct parse *cfile; + struct iaddr *addr; +{ + unsigned int mask_width, dest_dest_len; + addr -> len = 0; + if (parse_numeric_aggregate (cfile, addr -> iabuf, + &addr -> len, DOT, 10, 8)) { + mask_width = (unsigned int)addr->iabuf[0]; + dest_dest_len = (((mask_width+7)/8)+1); + if (mask_width > 32) { + parse_warn (cfile, + "subnet mask width (%u) greater than 32.", mask_width); + } + else if (dest_dest_len != addr->len) { + parse_warn (cfile, + "destination descriptor with subnet mask width %u " + "should have %u octets, but has %u octets.", + mask_width, dest_dest_len, addr->len); + } + + return 1; + } + return 0; +} + +/* * Return true if every character in the string is hexadecimal. */ static int @@ -720,8 +753,10 @@ unsigned char *parse_numeric_aggregate ( if (count) { token = peek_token (&val, (unsigned *)0, cfile); if (token != separator) { - if (!*max) + if (!*max) { + *max = count; break; + } if (token != RBRACE && token != LBRACE) token = next_token (&val, (unsigned *)0, @@ -1668,6 +1703,9 @@ int parse_option_code_definition (cfile, case IP_ADDRESS: type = 'I'; break; + case DESTINATION_DESCRIPTOR: + type = 'R'; + break; case IP6_ADDRESS: type = '6'; break; @@ -5097,6 +5135,15 @@ int parse_option_token (rv, cfile, fmt, } break; + case 'R': /* destination descriptor */ + if (!parse_destination_descriptor (cfile, &addr)) { + return 0; + } + if (!make_const_data (&t, addr.iabuf, addr.len, 0, 1, MDL)) { + return 0; + } + break; + case '6': /* IPv6 address. */ if (!parse_ip6_addr(cfile, &addr)) { return 0; @@ -5374,6 +5421,13 @@ int parse_option_decl (oc, cfile) goto exit; len = ip_addr.len; dp = ip_addr.iabuf; + goto alloc; + + case 'R': /* destination descriptor */ + if (!parse_destination_descriptor (cfile, &ip_addr)) + goto exit; + len = ip_addr.len; + dp = ip_addr.iabuf; alloc: if (hunkix + len > sizeof hunkbuf) { diff -up dhcp-4.3.4/common/tables.c.rfc3442 dhcp-4.3.4/common/tables.c --- dhcp-4.3.4/common/tables.c.rfc3442 2016-04-29 12:23:34.209032710 +0200 +++ dhcp-4.3.4/common/tables.c 2016-04-29 12:23:34.238032702 +0200 @@ -45,6 +45,7 @@ HASH_FUNCTIONS (option_code, const unsig Format codes: I - IPv4 address + R - destination descriptor (RFC3442) 6 - IPv6 address l - 32-bit signed integer L - 32-bit unsigned integer @@ -216,6 +217,7 @@ static struct option dhcp_options[] = { #endif { "subnet-selection", "I", &dhcp_universe, 118, 1 }, { "domain-search", "D", &dhcp_universe, 119, 1 }, + { "classless-static-routes", "RIA", &dhcp_universe, 121, 1 }, { "vivco", "Evendor-class.", &dhcp_universe, 124, 1 }, { "vivso", "Evendor.", &dhcp_universe, 125, 1 }, #if 0 diff -up dhcp-4.3.4/includes/dhcpd.h.rfc3442 dhcp-4.3.4/includes/dhcpd.h --- dhcp-4.3.4/includes/dhcpd.h.rfc3442 2016-04-29 12:23:34.186032716 +0200 +++ dhcp-4.3.4/includes/dhcpd.h 2016-04-29 12:23:34.239032702 +0200 @@ -2894,6 +2894,7 @@ isc_result_t range2cidr(struct iaddrcidr const struct iaddr *lo, const struct iaddr *hi); isc_result_t free_iaddrcidrnetlist(struct iaddrcidrnetlist **result); const char *piaddr (struct iaddr); +const char *pdestdesc (struct iaddr); char *piaddrmask(struct iaddr *, struct iaddr *); char *piaddrcidr(const struct iaddr *, unsigned int); u_int16_t validate_port(char *); @@ -3108,6 +3109,7 @@ void parse_client_lease_declaration (str int parse_option_decl (struct option_cache **, struct parse *); void parse_string_list (struct parse *, struct string_list **, int); int parse_ip_addr (struct parse *, struct iaddr *); +int parse_destination_descriptor (struct parse *, struct iaddr *); int parse_ip_addr_with_subnet(struct parse *, struct iaddrmatch *); void parse_reject_statement (struct parse *, struct client_config *); diff -up dhcp-4.3.4/includes/dhcp.h.rfc3442 dhcp-4.3.4/includes/dhcp.h --- dhcp-4.3.4/includes/dhcp.h.rfc3442 2016-03-22 14:16:51.000000000 +0100 +++ dhcp-4.3.4/includes/dhcp.h 2016-04-29 12:23:34.239032702 +0200 @@ -159,6 +159,7 @@ struct dhcp_packet { #define DHO_ASSOCIATED_IP 92 #define DHO_SUBNET_SELECTION 118 /* RFC3011! */ #define DHO_DOMAIN_SEARCH 119 /* RFC3397 */ +#define DHO_CLASSLESS_STATIC_ROUTES 121 /* RFC3442 */ #define DHO_VIVCO_SUBOPTIONS 124 #define DHO_VIVSO_SUBOPTIONS 125 diff -up dhcp-4.3.4/includes/dhctoken.h.rfc3442 dhcp-4.3.4/includes/dhctoken.h --- dhcp-4.3.4/includes/dhctoken.h.rfc3442 2016-04-29 12:23:34.239032702 +0200 +++ dhcp-4.3.4/includes/dhctoken.h 2016-04-29 12:25:07.236008628 +0200 @@ -374,7 +374,8 @@ enum dhcp_token { LEASE_ID_FORMAT = 676, TOKEN_HEX = 677, TOKEN_OCTAL = 678, - BOOTP_BROADCAST_ALWAYS = 679 + BOOTP_BROADCAST_ALWAYS = 679, + DESTINATION_DESCRIPTOR = 680 }; #define is_identifier(x) ((x) >= FIRST_TOKEN && \