]> git.ipfire.org Git - people/stevee/ipfire-3.x.git/blob - dhcp/patches/0012-RFC-3442-Classless-Static-Route-Option-for-DHCPv4-51.patch
dhcp: Update to 4.4.1
[people/stevee/ipfire-3.x.git] / dhcp / patches / 0012-RFC-3442-Classless-Static-Route-Option-for-DHCPv4-51.patch
1 From 2756fcc3f88c27d0e12e72dbdd2906fbf45f2362 Mon Sep 17 00:00:00 2001
2 From: Pavel Zhukov <pzhukov@redhat.com>
3 Date: Thu, 21 Feb 2019 10:32:35 +0100
4 Subject: [PATCH 12/21] RFC 3442 - Classless Static Route Option for DHCPv4
5 (#516325)
6 Cc: pzhukov@redhat.com
7
8 (Submitted to dhcp-bugs@isc.org - [ISC-Bugs #24572])
9 ---
10 client/clparse.c | 13 ++++++++++--
11 common/dhcp-options.5 | 43 +++++++++++++++++++++++++++++++++++++++
12 common/inet.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
13 common/options.c | 49 +++++++++++++++++++++++++++++++++++++++++++-
14 common/parse.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++-
15 common/tables.c | 2 ++
16 includes/dhcp.h | 1 +
17 includes/dhcpd.h | 2 ++
18 includes/dhctoken.h | 5 +++--
19 9 files changed, 219 insertions(+), 6 deletions(-)
20
21 diff --git a/client/clparse.c b/client/clparse.c
22 index 44387ed..862e4f9 100644
23 --- a/client/clparse.c
24 +++ b/client/clparse.c
25 @@ -31,7 +31,7 @@
26
27 struct client_config top_level_config;
28
29 -#define NUM_DEFAULT_REQUESTED_OPTS 14
30 +#define NUM_DEFAULT_REQUESTED_OPTS 15
31 /* There can be 2 extra requested options for DHCPv4-over-DHCPv6. */
32 struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 2 + 1];
33
34 @@ -87,7 +87,11 @@ isc_result_t read_client_conf ()
35 dhcp_universe.code_hash, &code, 0, MDL);
36
37 /* 4 */
38 - code = DHO_ROUTERS;
39 + /* The Classless Static Routes option code MUST appear in the parameter
40 + * request list prior to both the Router option code and the Static
41 + * Routes option code, if present. (RFC3442)
42 + */
43 + code = DHO_CLASSLESS_STATIC_ROUTES;
44 option_code_hash_lookup(&default_requested_options[3],
45 dhcp_universe.code_hash, &code, 0, MDL);
46
47 @@ -141,6 +145,11 @@ isc_result_t read_client_conf ()
48 option_code_hash_lookup(&default_requested_options[13],
49 dhcp_universe.code_hash, &code, 0, MDL);
50
51 + /* 15 */
52 + code = DHO_ROUTERS;
53 + option_code_hash_lookup(&default_requested_options[14],
54 + dhcp_universe.code_hash, &code, 0, MDL);
55 +
56 for (code = 0 ; code < NUM_DEFAULT_REQUESTED_OPTS ; code++) {
57 if (default_requested_options[code] == NULL)
58 log_fatal("Unable to find option definition for "
59 diff --git a/common/dhcp-options.5 b/common/dhcp-options.5
60 index d9e1197..2343b19 100644
61 --- a/common/dhcp-options.5
62 +++ b/common/dhcp-options.5
63 @@ -110,6 +110,26 @@ hexadecimal, separated by colons. For example:
64 or
65 option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f;
66 .fi
67 +.PP
68 +The
69 +.B destination-descriptor
70 +describe the IP subnet number and subnet mask
71 +of a particular destination using a compact encoding. This encoding
72 +consists of one octet describing the width of the subnet mask,
73 +followed by all the significant octets of the subnet number.
74 +The following table contains some examples of how various subnet
75 +number/mask combinations can be encoded:
76 +.nf
77 +.sp 1
78 +Subnet number Subnet mask Destination descriptor
79 +0 0 0
80 +10.0.0.0 255.0.0.0 8.10
81 +10.0.0.0 255.255.255.0 24.10.0.0
82 +10.17.0.0 255.255.0.0 16.10.17
83 +10.27.129.0 255.255.255.0 24.10.27.129
84 +10.229.0.128 255.255.255.128 25.10.229.0.128
85 +10.198.122.47 255.255.255.255 32.10.198.122.47
86 +.fi
87 .SH SETTING OPTION VALUES USING EXPRESSIONS
88 Sometimes it's helpful to be able to set the value of a DHCP option
89 based on some value that the client has sent. To do this, you can
90 @@ -1086,6 +1106,29 @@ dhclient-script will create routes:
91 .RE
92 .PP
93 .nf
94 +.B option \fBclassless-static-routes\fR \fIdestination-descriptor ip-address\fR
95 + [\fB,\fR \fIdestination-descriptor ip-address\fR...]\fB;\fR
96 +.fi
97 +.RS 0.25i
98 +.PP
99 +This option (see RFC3442) specifies a list of classless static routes
100 +that the client should install in its routing cache.
101 +.PP
102 +This option can contain one or more static routes, each of which
103 +consists of a destination descriptor and the IP address of the router
104 +that should be used to reach that destination.
105 +.PP
106 +Many clients may not implement the Classless Static Routes option.
107 +DHCP server administrators should therefore configure their DHCP
108 +servers to send both a Router option and a Classless Static Routes
109 +option, and should specify the default router(s) both in the Router
110 +option and in the Classless Static Routes option.
111 +.PP
112 +If the DHCP server returns both a Classless Static Routes option and
113 +a Router option, the DHCP client ignores the Router option.
114 +.RE
115 +.PP
116 +.nf
117 .B option \fBstreettalk-directory-assistance-server\fR \fIip-address\fR
118 [\fB,\fR \fIip-address\fR...]\fB;\fR
119 .fi
120 diff --git a/common/inet.c b/common/inet.c
121 index c4da73c..981fb92 100644
122 --- a/common/inet.c
123 +++ b/common/inet.c
124 @@ -519,6 +519,60 @@ free_iaddrcidrnetlist(struct iaddrcidrnetlist **result) {
125 return ISC_R_SUCCESS;
126 }
127
128 +static const char *
129 +inet_ntopdd(const unsigned char *src, unsigned srclen, char *dst, size_t size)
130 +{
131 + char tmp[sizeof("32.255.255.255.255")];
132 + int len;
133 +
134 + switch (srclen) {
135 + case 2:
136 + len = sprintf (tmp, "%u.%u", src[0], src[1]);
137 + break;
138 + case 3:
139 + len = sprintf (tmp, "%u.%u.%u", src[0], src[1], src[2]);
140 + break;
141 + case 4:
142 + len = sprintf (tmp, "%u.%u.%u.%u", src[0], src[1], src[2], src[3]);
143 + break;
144 + case 5:
145 + len = sprintf (tmp, "%u.%u.%u.%u.%u", src[0], src[1], src[2], src[3], src[4]);
146 + break;
147 + default:
148 + return NULL;
149 + }
150 + if (len < 0)
151 + return NULL;
152 +
153 + if (len > size) {
154 + errno = ENOSPC;
155 + return NULL;
156 + }
157 +
158 + return strcpy (dst, tmp);
159 +}
160 +
161 +/* pdestdesc() turns an iaddr structure into a printable dest. descriptor */
162 +const char *
163 +pdestdesc(const struct iaddr addr) {
164 + static char pbuf[sizeof("255.255.255.255.255")];
165 +
166 + if (addr.len == 0) {
167 + return "<null destination descriptor>";
168 + }
169 + if (addr.len == 1) {
170 + return "0";
171 + }
172 + if ((addr.len >= 2) && (addr.len <= 5)) {
173 + return inet_ntopdd(addr.iabuf, addr.len, pbuf, sizeof(pbuf));
174 + }
175 +
176 + log_fatal("pdestdesc():%s:%d: Invalid destination descriptor length %d.",
177 + MDL, addr.len);
178 + /* quell compiler warnings */
179 + return NULL;
180 +}
181 +
182 /* piaddr() turns an iaddr structure into a printable address. */
183 /* XXX: should use a const pointer rather than passing the structure */
184 const char *
185 diff --git a/common/options.c b/common/options.c
186 index fc0e088..3034cf0 100644
187 --- a/common/options.c
188 +++ b/common/options.c
189 @@ -729,7 +729,11 @@ cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
190 * packet.
191 */
192 priority_list[priority_len++] = DHO_SUBNET_MASK;
193 - priority_list[priority_len++] = DHO_ROUTERS;
194 + if (lookup_option(&dhcp_universe, cfg_options,
195 + DHO_CLASSLESS_STATIC_ROUTES))
196 + priority_list[priority_len++] = DHO_CLASSLESS_STATIC_ROUTES;
197 + else
198 + priority_list[priority_len++] = DHO_ROUTERS;
199 priority_list[priority_len++] = DHO_DOMAIN_NAME_SERVERS;
200 priority_list[priority_len++] = DHO_HOST_NAME;
201 priority_list[priority_len++] = DHO_FQDN;
202 @@ -1804,6 +1808,7 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
203 unsigned long tval;
204 isc_boolean_t a_array = ISC_FALSE;
205 int len_used;
206 + unsigned int octets = 0;
207
208 if (emit_commas)
209 comma = ',';
210 @@ -1812,6 +1817,7 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
211
212 memset (enumbuf, 0, sizeof enumbuf);
213
214 + if (option->format[0] != 'R') { /* see explanation lower */
215 /* Figure out the size of the data. */
216 for (l = i = 0; option -> format [i]; i++, l++) {
217 if (l >= sizeof(fmtbuf) - 1)
218 @@ -2004,6 +2010,33 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
219 if (numhunk < 0)
220 numhunk = 1;
221
222 + } else { /* option->format[i] == 'R') */
223 + /* R (destination descriptor) has variable length.
224 + * We can find it only in classless static route option,
225 + * so we are for sure parsing classless static route option now.
226 + * We go through whole the option to check whether there are no
227 + * missing/extra bytes.
228 + * I didn't find out how to improve the existing code and that's the
229 + * reason for this separate 'else' where I do my own checkings.
230 + * I know it's little bit unsystematic, but it works.
231 + */
232 + numhunk = 0;
233 + numelem = 2; /* RI */
234 + fmtbuf[0]='R'; fmtbuf[1]='I'; fmtbuf[2]=0;
235 + for (i =0; i < len; i = i + octets + 5) {
236 + if (data[i] > 32) { /* subnet mask width */
237 + log_error ("wrong subnet mask width in destination descriptor");
238 + break;
239 + }
240 + numhunk++;
241 + octets = ((data[i]+7) / 8);
242 + }
243 + if (i != len) {
244 + log_error ("classless static routes option has wrong size or "
245 + "there's some garbage in format");
246 + }
247 + }
248 +
249 /* Cycle through the array (or hunk) printing the data. */
250 for (i = 0; i < numhunk; i++) {
251 if ((a_array == ISC_TRUE) && (i != 0) && (numelem > 0)) {
252 @@ -2159,6 +2192,20 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
253 strcpy(op, piaddr(iaddr));
254 dp += 4;
255 break;
256 +
257 + case 'R':
258 + if (dp[0] <= 32)
259 + iaddr.len = (((dp[0]+7)/8)+1);
260 + else {
261 + log_error ("wrong subnet mask width in destination descriptor");
262 + return "<error>";
263 + }
264 +
265 + memcpy(iaddr.iabuf, dp, iaddr.len);
266 + strcpy(op, pdestdesc(iaddr));
267 + dp += iaddr.len;
268 + break;
269 +
270 case '6':
271 iaddr.len = 16;
272 memcpy(iaddr.iabuf, dp, 16);
273 diff --git a/common/parse.c b/common/parse.c
274 index 3ac4ebf..f17bc0b 100644
275 --- a/common/parse.c
276 +++ b/common/parse.c
277 @@ -344,6 +344,39 @@ int parse_ip_addr (cfile, addr)
278 return 0;
279 }
280
281 +/*
282 + * destination-descriptor :== NUMBER DOT NUMBER |
283 + * NUMBER DOT NUMBER DOT NUMBER |
284 + * NUMBER DOT NUMBER DOT NUMBER DOT NUMBER |
285 + * NUMBER DOT NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
286 + */
287 +
288 +int parse_destination_descriptor (cfile, addr)
289 + struct parse *cfile;
290 + struct iaddr *addr;
291 +{
292 + unsigned int mask_width, dest_dest_len;
293 + addr -> len = 0;
294 + if (parse_numeric_aggregate (cfile, addr -> iabuf,
295 + &addr -> len, DOT, 10, 8)) {
296 + mask_width = (unsigned int)addr->iabuf[0];
297 + dest_dest_len = (((mask_width+7)/8)+1);
298 + if (mask_width > 32) {
299 + parse_warn (cfile,
300 + "subnet mask width (%u) greater than 32.", mask_width);
301 + }
302 + else if (dest_dest_len != addr->len) {
303 + parse_warn (cfile,
304 + "destination descriptor with subnet mask width %u "
305 + "should have %u octets, but has %u octets.",
306 + mask_width, dest_dest_len, addr->len);
307 + }
308 +
309 + return 1;
310 + }
311 + return 0;
312 +}
313 +
314 /*
315 * Return true if every character in the string is hexadecimal.
316 */
317 @@ -724,8 +757,10 @@ unsigned char *parse_numeric_aggregate (cfile, buf,
318 if (count) {
319 token = peek_token (&val, (unsigned *)0, cfile);
320 if (token != separator) {
321 - if (!*max)
322 + if (!*max) {
323 + *max = count;
324 break;
325 + }
326 if (token != RBRACE && token != LBRACE)
327 token = next_token (&val,
328 (unsigned *)0,
329 @@ -1672,6 +1707,9 @@ int parse_option_code_definition (cfile, option)
330 case IP_ADDRESS:
331 type = 'I';
332 break;
333 + case DESTINATION_DESCRIPTOR:
334 + type = 'R';
335 + break;
336 case IP6_ADDRESS:
337 type = '6';
338 break;
339 @@ -5101,6 +5139,15 @@ int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
340 }
341 break;
342
343 + case 'R': /* destination descriptor */
344 + if (!parse_destination_descriptor (cfile, &addr)) {
345 + return 0;
346 + }
347 + if (!make_const_data (&t, addr.iabuf, addr.len, 0, 1, MDL)) {
348 + return 0;
349 + }
350 + break;
351 +
352 case '6': /* IPv6 address. */
353 if (!parse_ip6_addr(cfile, &addr)) {
354 return 0;
355 @@ -5378,6 +5425,13 @@ int parse_option_decl (oc, cfile)
356 goto exit;
357 len = ip_addr.len;
358 dp = ip_addr.iabuf;
359 + goto alloc;
360 +
361 + case 'R': /* destination descriptor */
362 + if (!parse_destination_descriptor (cfile, &ip_addr))
363 + goto exit;
364 + len = ip_addr.len;
365 + dp = ip_addr.iabuf;
366
367 alloc:
368 if (hunkix + len > sizeof hunkbuf) {
369 diff --git a/common/tables.c b/common/tables.c
370 index d2294c0..f1be07d 100644
371 --- a/common/tables.c
372 +++ b/common/tables.c
373 @@ -45,6 +45,7 @@ HASH_FUNCTIONS (option_code, const unsigned *, struct option,
374 Format codes:
375
376 I - IPv4 address
377 + R - destination descriptor (RFC3442)
378 6 - IPv6 address
379 l - 32-bit signed integer
380 L - 32-bit unsigned integer
381 @@ -216,6 +217,7 @@ static struct option dhcp_options[] = {
382 #endif
383 { "subnet-selection", "I", &dhcp_universe, 118, 1 },
384 { "domain-search", "D", &dhcp_universe, 119, 1 },
385 + { "classless-static-routes", "RIA", &dhcp_universe, 121, 1 },
386 { "vivco", "Evendor-class.", &dhcp_universe, 124, 1 },
387 { "vivso", "Evendor.", &dhcp_universe, 125, 1 },
388 #if 0
389 diff --git a/includes/dhcp.h b/includes/dhcp.h
390 index 0a74137..95bf539 100644
391 --- a/includes/dhcp.h
392 +++ b/includes/dhcp.h
393 @@ -158,6 +158,7 @@ struct dhcp_packet {
394 #define DHO_ASSOCIATED_IP 92
395 #define DHO_SUBNET_SELECTION 118 /* RFC3011! */
396 #define DHO_DOMAIN_SEARCH 119 /* RFC3397 */
397 +#define DHO_CLASSLESS_STATIC_ROUTES 121 /* RFC3442 */
398 #define DHO_VIVCO_SUBOPTIONS 124
399 #define DHO_VIVSO_SUBOPTIONS 125
400
401 diff --git a/includes/dhcpd.h b/includes/dhcpd.h
402 index 3632a6b..2ac39ae 100644
403 --- a/includes/dhcpd.h
404 +++ b/includes/dhcpd.h
405 @@ -2951,6 +2951,7 @@ isc_result_t range2cidr(struct iaddrcidrnetlist **result,
406 const struct iaddr *lo, const struct iaddr *hi);
407 isc_result_t free_iaddrcidrnetlist(struct iaddrcidrnetlist **result);
408 const char *piaddr (struct iaddr);
409 +const char *pdestdesc (struct iaddr);
410 char *piaddrmask(struct iaddr *, struct iaddr *);
411 char *piaddrcidr(const struct iaddr *, unsigned int);
412 u_int16_t validate_port(char *);
413 @@ -3169,6 +3170,7 @@ void parse_client_lease_declaration (struct parse *,
414 int parse_option_decl (struct option_cache **, struct parse *);
415 void parse_string_list (struct parse *, struct string_list **, int);
416 int parse_ip_addr (struct parse *, struct iaddr *);
417 +int parse_destination_descriptor (struct parse *, struct iaddr *);
418 int parse_ip_addr_with_subnet(struct parse *, struct iaddrmatch *);
419 void parse_reject_statement (struct parse *, struct client_config *);
420
421 diff --git a/includes/dhctoken.h b/includes/dhctoken.h
422 index 7e7215a..b4d93ba 100644
423 --- a/includes/dhctoken.h
424 +++ b/includes/dhctoken.h
425 @@ -376,8 +376,9 @@ enum dhcp_token {
426 LEASE_ID_FORMAT = 676,
427 TOKEN_HEX = 677,
428 TOKEN_OCTAL = 678,
429 - KEY_ALGORITHM = 679
430 - BOOTP_BROADCAST_ALWAYS = 680
431 + KEY_ALGORITHM = 679,
432 + BOOTP_BROADCAST_ALWAYS = 680,
433 + DESTINATION_DESCRIPTOR = 681
434 };
435
436 #define is_identifier(x) ((x) >= FIRST_TOKEN && \
437 --
438 2.14.5