| diff -up dhcp-4.2.5b1/client/clparse.c.rfc3442 dhcp-4.2.5b1/client/clparse.c |
| |
| |
| @@ -37,7 +37,7 @@ |
| |
| struct client_config top_level_config; |
| |
| -#define NUM_DEFAULT_REQUESTED_OPTS 14 |
| +#define NUM_DEFAULT_REQUESTED_OPTS 15 |
| struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 1]; |
| |
| static void parse_client_default_duid(struct parse *cfile); |
| @@ -90,7 +90,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); |
| |
| @@ -144,6 +148,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.2.5b1/common/dhcp-options.5.rfc3442 dhcp-4.2.5b1/common/dhcp-options.5 |
| |
| |
| @@ -116,6 +116,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 |
| @@ -932,6 +952,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.2.5b1/common/inet.c.rfc3442 dhcp-4.2.5b1/common/inet.c |
| |
| |
| @@ -528,6 +528,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 "<null destination descriptor>"; |
| + } |
| + 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.2.5b1/common/options.c.rfc3442 dhcp-4.2.5b1/common/options.c |
| |
| |
| @@ -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) |
| @@ -1876,6 +1882,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)) { |
| @@ -2031,6 +2064,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 "<error>"; |
| + } |
| + |
| + 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.2.5b1/common/parse.c.rfc3442 dhcp-4.2.5b1/common/parse.c |
| |
| |
| @@ -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 |
| @@ -719,8 +752,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, |
| @@ -1660,6 +1695,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; |
| @@ -5418,6 +5456,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; |
| @@ -5695,6 +5742,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.2.5b1/common/tables.c.rfc3442 dhcp-4.2.5b1/common/tables.c |
| |
| |
| @@ -52,6 +52,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 |
| @@ -210,6 +211,7 @@ static struct option dhcp_options[] = { |
| { "default-url", "t", &dhcp_universe, 114, 1 }, |
| { "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.2.5b1/includes/dhcpd.h.rfc3442 dhcp-4.2.5b1/includes/dhcpd.h |
| |
| |
| @@ -2678,6 +2678,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 *); |
| @@ -2887,6 +2888,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.2.5b1/includes/dhcp.h.rfc3442 dhcp-4.2.5b1/includes/dhcp.h |
| |
| |
| @@ -163,6 +163,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.2.5b1/includes/dhctoken.h.rfc3442 dhcp-4.2.5b1/includes/dhctoken.h |
| |
| |
| @@ -365,7 +365,8 @@ enum dhcp_token { |
| PRIMARY6 = 666, |
| SECONDARY6 = 667, |
| TOKEN_INFINIBAND = 668, |
| - BOOTP_BROADCAST_ALWAYS = 669 |
| + BOOTP_BROADCAST_ALWAYS = 669, |
| + DESTINATION_DESCRIPTOR = 670 |
| }; |
| |
| #define is_identifier(x) ((x) >= FIRST_TOKEN && \ |