• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/odhcp6c/src/config.c

  1 /**
  2  * SPDX-License-Identifier: BSD-2-Clause-Patent
  3  *
  4  * SPDX-FileCopyrightText: Copyright (c) 2024 SoftAtHome
  5  *
  6  * Redistribution and use in source and binary forms, with or
  7  * without modification, are permitted provided that the following
  8  * conditions are met:
  9  *
 10  * 1. Redistributions of source code must retain the above copyright
 11  * notice, this list of conditions and the following disclaimer.
 12  *
 13  * 2. Redistributions in binary form must reproduce the above
 14  * copyright notice, this list of conditions and the following
 15  * disclaimer in the documentation and/or other materials provided
 16  * with the distribution.
 17  *
 18  * Subject to the terms and conditions of this license, each
 19  * copyright holder and contributor hereby grants to those receiving
 20  * rights under this license a perpetual, worldwide, non-exclusive,
 21  * no-charge, royalty-free, irrevocable (except for failure to
 22  * satisfy the conditions of this license) patent license to make,
 23  * have made, use, offer to sell, sell, import, and otherwise
 24  * transfer this software, where such license applies only to those
 25  * patent claims, already acquired or hereafter acquired, licensable
 26  * by such copyright holder or contributor that are necessarily
 27  * infringed by:
 28  *
 29  * (a) their Contribution(s) (the licensed copyrights of copyright
 30  * holders and non-copyrightable additions of contributors, in
 31  * source or binary form) alone; or
 32  *
 33  * (b) combination of their Contribution(s) with the work of
 34  * authorship to which such Contribution(s) was added by such
 35  * copyright holder or contributor, if, at the time the Contribution
 36  * is added, such addition causes such combination to be necessarily
 37  * infringed. The patent license shall not apply to any other
 38  * combinations which include the Contribution.
 39  *
 40  * Except as expressly stated above, no rights or licenses from any
 41  * copyright holder or contributor is granted under this license,
 42  * whether expressly, by implication, estoppel or otherwise.
 43  *
 44  * DISCLAIMER
 45  *
 46  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 47  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 48  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 49  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 50  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 51  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 52  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 53  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 54  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 55  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 56  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 57  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 58  * POSSIBILITY OF SUCH DAMAGE.
 59  *
 60  */
 61 
 62 #include <arpa/inet.h>
 63 #include <limits.h>
 64 #include <resolv.h>
 65 #include <stdlib.h>
 66 #include <string.h>
 67 
 68 #include "config.h"
 69 #include "odhcp6c.h"
 70 
 71 #define ARRAY_SEP " ,\t"
 72 
 73 static struct config_dhcp config_dhcp;
 74 
 75 struct config_dhcp *config_dhcp_get(void) {
 76         return &config_dhcp;
 77 }
 78 
 79 void config_dhcp_reset(void) {
 80         config_dhcp.log_level = LOG_WARNING;
 81         config_dhcp.release = true;
 82         config_dhcp.dscp = 0;
 83         config_dhcp.sk_prio = 0;
 84         config_dhcp.stateful_only_mode = false;
 85         config_dhcp.ia_na_mode = IA_MODE_TRY;
 86         config_dhcp.ia_pd_mode = IA_MODE_NONE;
 87         config_dhcp.client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE;
 88         config_dhcp.allow_slaac_only = true;
 89         config_dhcp.oro_user_cnt = 0;
 90         memset(config_dhcp.message_rtx, 0, sizeof(config_dhcp.message_rtx));
 91         config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].delay_max = DHCPV6_MAX_DELAY;
 92         config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_init = DHCPV6_SOL_INIT_RT;
 93         config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_max = DHCPV6_SOL_MAX_RT;
 94         config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_init = DHCPV6_REQ_INIT_RT;
 95         config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_max = DHCPV6_REQ_MAX_RT;
 96         config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].rc_max = DHCPV6_REQ_MAX_RC;
 97         config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_init = DHCPV6_REN_INIT_RT;
 98         config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_max = DHCPV6_REN_MAX_RT;
 99         config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_init = DHCPV6_REB_INIT_RT;
100         config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_max = DHCPV6_REB_MAX_RT;
101         config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].delay_max = DHCPV6_MAX_DELAY;
102         config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_init = DHCPV6_INF_INIT_RT;
103         config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_max = DHCPV6_INF_MAX_RT;
104         config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].timeout_init = DHCPV6_REL_INIT_RT;
105         config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].rc_max = DHCPV6_REL_MAX_RC;
106         config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].timeout_init = DHCPV6_DEC_INIT_RT;
107         config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].rc_max = DHCPV6_DEC_MAX_RC;
108         config_dhcp.irt_default = DHCPV6_IRT_DEFAULT;
109         config_dhcp.irt_min = DHCPV6_IRT_MIN;
110         config_dhcp.rand_factor = DHCPV6_RAND_FACTOR;
111         config_dhcp.auth_protocol = AUTH_PROT_RKAP;
112         free(config_dhcp.auth_token);
113         config_dhcp.auth_token = NULL;
114 }
115 
116 void config_set_release(bool enable) {
117         config_dhcp.release = enable;
118 }
119 
120 bool config_set_dscp(unsigned int value) {
121         if (value > 63) {
122                 error("Invalid DSCP value");
123                 return false;
124         }
125         config_dhcp.dscp = value;
126         return true;
127 }
128 
129 bool config_set_sk_priority(unsigned int priority) {
130         if (priority > 6) {
131                 error("Invalid SK priority value");
132                 return false;
133         }
134         config_dhcp.sk_prio = priority;
135         return true;
136 }
137 
138 void config_set_client_options(enum dhcpv6_config option, bool enable) {
139         if (enable) {
140                 config_dhcp.client_options |= option;
141         } else {
142                 config_dhcp.client_options &= ~option;
143         }
144 }
145 
146 bool config_set_request_addresses(char* mode) {
147         if (!strcmp(mode, "force")) {
148                 config_dhcp.ia_na_mode = IA_MODE_FORCE;
149                 config_dhcp.allow_slaac_only = false;
150         } else if (!strcmp(mode, "none")) {
151                 config_dhcp.ia_na_mode = IA_MODE_NONE;
152         } else if (!strcmp(mode, "try")) {
153                 config_dhcp.ia_na_mode = IA_MODE_TRY;
154         } else {
155                 error("Invalid IA_NA Request Addresses mode");
156                 return false;
157         }
158 
159         return true;
160 }
161 
162 bool config_set_request_prefix(unsigned int length, unsigned int id) {
163         struct odhcp6c_request_prefix prefix = {0};
164 
165         odhcp6c_clear_state(STATE_IA_PD_INIT);
166 
167         if (config_dhcp.ia_pd_mode != IA_MODE_FORCE)
168                 config_dhcp.ia_pd_mode = length > 128 ? IA_MODE_NONE : IA_MODE_TRY;
169 
170         if (length <= 128) {
171                 prefix.length = length;
172                 prefix.iaid = htonl(id);
173 
174                 if (odhcp6c_add_state(STATE_IA_PD_INIT, &prefix, sizeof(prefix))) {
175                         error("Failed to set request IPv6-Prefix");
176                         return false;
177                 }
178         }
179 
180         return true;
181 }
182 
183 void config_set_force_prefix(bool enable) {
184         if (enable) {
185                 config_dhcp.allow_slaac_only = false;
186                 config_dhcp.ia_pd_mode = IA_MODE_FORCE;
187         } else {
188                 config_dhcp.ia_pd_mode = IA_MODE_NONE;
189         }
190 }
191 
192 void config_set_stateful_only(bool enable) {
193         config_dhcp.stateful_only_mode = enable;
194 }
195 
196 void config_set_allow_slaac_only(bool value) {
197         config_dhcp.allow_slaac_only = value;
198 }
199 
200 void config_clear_requested_options(void) {
201         config_dhcp.oro_user_cnt = 0;
202 }
203 
204 bool config_add_requested_options(unsigned int option) {
205         if (option > UINT16_MAX) {
206                 error("Invalid requested option");
207                 return false;
208         }
209 
210         option = htons(option);
211         if (odhcp6c_insert_state(STATE_ORO, 0, &option, 2)) {
212                 error("Failed to set requested option");
213                 return false;
214         }
215         config_dhcp.oro_user_cnt++;
216         return true;
217 }
218 
219 void config_clear_send_options(void) {
220         odhcp6c_clear_state(STATE_OPTS);
221 }
222 
223 bool config_add_send_options(const char *option) {
224         char *dup = strdup(option);
225         bool ok;
226 
227         if (!dup)
228                 return false;
229 
230         ok = (config_parse_opt(dup) == 0);
231         free(dup);
232 
233         return ok;
234 }
235 
236 bool config_set_rtx_delay_max(enum config_dhcp_msg msg, unsigned int value)
237 {
238         if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX) {
239                 error("Invalid retransmission Maximum Delay value");
240                 return false;
241         }
242         config_dhcp.message_rtx[msg].delay_max = value;
243         return true;
244 }
245 
246 bool config_set_rtx_timeout_init(enum config_dhcp_msg msg, unsigned int value)
247 {
248         if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX || value == 0) {
249                 error("Invalid retransmission Initial Timeout value");
250                 return false;
251         }
252         config_dhcp.message_rtx[msg].timeout_init = value;
253         return true;
254 }
255 
256 bool config_set_rtx_timeout_max(enum config_dhcp_msg msg, unsigned int value)
257 {
258         if (msg >= CONFIG_DHCP_MAX || value > UINT16_MAX) {
259                 error("Invalid retransmission Maximum Timeout value");
260                 return false;
261         }
262         config_dhcp.message_rtx[msg].timeout_max = value;
263         return true;
264 }
265 
266 bool config_set_rtx_rc_max(enum config_dhcp_msg msg, unsigned int value)
267 {
268         if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX) {
269                 error("Invalid retransmission Retry Attempt value");
270                 return false;
271         }
272         config_dhcp.message_rtx[msg].rc_max = value;
273         return true;
274 }
275 
276 bool config_set_irt_default(unsigned int value)
277 {
278         if (value == 0) {
279                 error("Invalid Default Information Refresh Time value");
280                 return false;
281         }
282         config_dhcp.irt_default = value;
283         return true;
284 }
285 
286 bool config_set_irt_min(unsigned int value)
287 {
288         if (value == 0) {
289                 error("Invalid Minimum Information Refresh Time value");
290                 return false;
291         }
292         config_dhcp.irt_min = value;
293         return true;
294 }
295 
296 bool config_set_rand_factor(unsigned int value)
297 {
298         if (value > 999 || value < 10) {
299                 error("Invalid Random Factor value");
300                 return false;
301         }
302         config_dhcp.rand_factor = value;
303         return true;
304 }
305 
306 bool config_set_auth_protocol(const char* protocol)
307 {
308         if (!strcmp(protocol, "None")) {
309                 config_dhcp.auth_protocol = AUTH_PROT_NONE;
310         } else if (!strcmp(protocol, "ConfigurationToken")) {
311                 config_dhcp.auth_protocol = AUTH_PROT_TOKEN;
312         } else if (!strcmp(protocol, "ReconfigureKeyAuthentication")) {
313                 config_dhcp.auth_protocol = AUTH_PROT_RKAP;
314         } else {
315                 error("Invalid Authentication protocol");
316                 return false;
317         }
318 
319         return true;
320 }
321 
322 bool config_set_auth_token(const char* token)
323 {
324         free(config_dhcp.auth_token);
325 
326         config_dhcp.auth_token = strdup(token);
327         return config_dhcp.auth_token != NULL;
328 }
329 
330 void config_set_client_opt_cfg(struct odhcp6c_opt_cfg *opt_cfg)
331 {
332         config_dhcp.strict_rfc7550 = opt_cfg->strict_rfc7550 != 0;
333 }
334 
335 static int config_parse_opt_u8(const char *src, uint8_t **dst)
336 {
337         int len = strlen(src);
338 
339         uint8_t *tmp = realloc(*dst, len/2);
340         if (!tmp)
341                 return -1;
342         *dst = tmp;
343 
344         return script_unhexlify(*dst, len, src);
345 }
346 
347 static int config_parse_opt_string(char *src, uint8_t **dst, const bool array)
348 {
349         int o_len = 0;
350         char *sep = strpbrk(src, ARRAY_SEP);
351 
352         if (sep && !array)
353                 return -1;
354 
355         do {
356                 if (sep) {
357                         *sep = 0;
358                         sep++;
359                 }
360 
361                 int len = strlen(src);
362 
363                 uint8_t *tmp = realloc(*dst, o_len + len);
364                 if (!tmp)
365                         return -1;
366                 *dst = tmp;
367 
368                 memcpy(&((*dst)[o_len]), src, len);
369 
370                 o_len += len;
371                 src = sep;
372 
373                 if (sep)
374                         sep = strpbrk(src, ARRAY_SEP);
375         } while (src);
376 
377         return o_len;
378 }
379 
380 static int config_parse_opt_dns_string(char *src, uint8_t **dst, const bool array)
381 {
382         int o_len = 0;
383         char *sep = strpbrk(src, ARRAY_SEP);
384 
385         if (sep && !array)
386                 return -1;
387 
388         do {
389                 uint8_t tmp[256];
390 
391                 if (sep) {
392                         *sep = 0;
393                         sep++;
394                 }
395 
396                 int len = dn_comp(src, tmp, sizeof(tmp), NULL, NULL);
397                 if (len < 0)
398                         return -1;
399 
400                 uint8_t *dst_tmp = realloc(*dst, o_len + len);
401                 if (!dst_tmp)
402                         return -1;
403                 *dst = dst_tmp;
404 
405                 memcpy(&((*dst)[o_len]), tmp, len);
406 
407                 o_len += len;
408                 src = sep;
409 
410                 if (sep)
411                         sep = strpbrk(src, ARRAY_SEP);
412         } while (src);
413 
414         return o_len;
415 }
416 
417 static int config_parse_opt_ip6(char *src, uint8_t **dst, const bool array)
418 {
419         int o_len = 0;
420         char *sep = strpbrk(src, ARRAY_SEP);
421 
422         if (sep && !array)
423                 return -1;
424 
425         do {
426                 int len = sizeof(struct in6_addr);
427 
428                 if (sep) {
429                         *sep = 0;
430                         sep++;
431                 }
432 
433                 uint8_t *tmp = realloc(*dst, o_len + len);
434                 if (!tmp)
435                         return -1;
436                 *dst = tmp;
437 
438                 if (inet_pton(AF_INET6, src, &((*dst)[o_len])) < 1)
439                         return -1;
440 
441                 o_len += len;
442                 src = sep;
443 
444                 if (sep)
445                         sep = strpbrk(src, ARRAY_SEP);
446         } while (src);
447 
448         return o_len;
449 }
450 
451 static int config_parse_opt_user_class(char *src, uint8_t **dst, const bool array)
452 {
453         int o_len = 0;
454         char *sep = strpbrk(src, ARRAY_SEP);
455 
456         if (sep && !array)
457                 return -1;
458 
459         do {
460                 if (sep) {
461                         *sep = 0;
462                         sep++;
463                 }
464                 uint16_t str_len = strlen(src);
465 
466                 uint8_t *tmp = realloc(*dst, o_len + str_len + 2);
467                 if (!tmp)
468                         return -1;
469                 *dst = tmp;
470 
471                 struct user_class {
472                         uint16_t len;
473                         uint8_t data[];
474                 } *e = (struct user_class *)&((*dst)[o_len]);
475 
476                 e->len = ntohs(str_len);
477                 memcpy(e->data, src, str_len);
478 
479                 o_len += str_len + 2;
480                 src = sep;
481 
482                 if (sep)
483                         sep = strpbrk(src, ARRAY_SEP);
484         } while (src);
485 
486         return o_len;
487 }
488 
489 static uint8_t *config_state_find_opt(const uint16_t code)
490 {
491         size_t opts_len;
492         uint8_t *odata, *opts = odhcp6c_get_state(STATE_OPTS, &opts_len);
493         uint16_t otype, olen;
494 
495         dhcpv6_for_each_option(opts, &opts[opts_len], otype, olen, odata) {
496                 if (otype == code)
497                         return &odata[-4];
498         }
499 
500         return NULL;
501 }
502 
503 int config_add_opt(const uint16_t code, const uint8_t *data, const uint16_t len)
504 {
505         struct {
506                 uint16_t code;
507                 uint16_t len;
508         } opt_hdr = { htons(code), htons(len) };
509 
510         if (config_state_find_opt(code))
511                 return -1;
512 
513         if (odhcp6c_add_state(STATE_OPTS, &opt_hdr, sizeof(opt_hdr)) ||
514                         odhcp6c_add_state(STATE_OPTS, data, len)) {
515                 error("Failed to add option %hu", code);
516                 return 1;
517         }
518 
519         return 0;
520 }
521 
522 int config_parse_opt_data(char *data, uint8_t **dst, const unsigned int type,
523                 const bool array)
524 {
525         int ret = 0;
526 
527         switch (type) {
528         case OPT_U8:
529                 ret = config_parse_opt_u8(data, dst);
530                 break;
531 
532         case OPT_STR:
533                 ret = config_parse_opt_string(data, dst, array);
534                 break;
535 
536         case OPT_DNS_STR:
537                 ret = config_parse_opt_dns_string(data, dst, array);
538                 break;
539 
540         case OPT_IP6:
541                 ret = config_parse_opt_ip6(data, dst, array);
542                 break;
543 
544         case OPT_USER_CLASS:
545                 ret = config_parse_opt_user_class(data, dst, array);
546                 break;
547 
548         default:
549                 ret = -1;
550                 break;
551         }
552 
553         return ret;
554 }
555 
556 int config_parse_opt(char *opt)
557 {
558         uint32_t optn;
559         char *data;
560         uint8_t *payload = NULL;
561         int payload_len;
562         unsigned int type = OPT_U8;
563         bool array = false;
564         struct odhcp6c_opt *dopt = NULL;
565         int ret = -1;
566 
567         data = strpbrk(opt, ":");
568         if (!data)
569                 return -1;
570 
571         *data = '\0';
572         data++;
573 
574         if (strlen(opt) == 0 || strlen(data) == 0)
575                 return -1;
576 
577         dopt = odhcp6c_find_opt_by_name(opt);
578         if (!dopt) {
579                 char *e;
580                 optn = strtoul(opt, &e, 0);
581                 if (*e || e == opt || optn > USHRT_MAX)
582                         return -1;
583 
584                 dopt = odhcp6c_find_opt(optn);
585         } else {
586                 optn = dopt->code;
587         }
588 
589         /* Check if the type for the content is well-known */
590         if (dopt) {
591                 /* Refuse internal options */
592                 if (dopt->flags & OPT_INTERNAL)
593                         return -1;
594 
595                 type = dopt->flags & OPT_MASK_SIZE;
596                 array = ((dopt->flags & OPT_ARRAY) == OPT_ARRAY) ? true : false;
597         } else if (data[0] == '"' || data[0] == '\'') {
598                 char *end = strrchr(data + 1, data[0]);
599 
600                 if (end && (end == (data + strlen(data) - 1))) {
601                         /* Raw option is specified as a string */
602                         type = OPT_STR;
603                         data++;
604                         *end = '\0';
605                 }
606         }
607 
608         payload_len = config_parse_opt_data(data, &payload, type, array);
609         if (payload_len > 0)
610                 ret = config_add_opt(optn, payload, payload_len);
611 
612         free(payload);
613 
614         return ret;
615 }
616 
617 void config_apply_dhcp_rtx(struct dhcpv6_retx* dhcpv6_retx)
618 {
619         dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_delay = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].delay_max;
620         dhcpv6_retx[DHCPV6_MSG_SOLICIT].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_init;
621         dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_max;
622         dhcpv6_retx[DHCPV6_MSG_REQUEST].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_init;
623         dhcpv6_retx[DHCPV6_MSG_REQUEST].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_max;
624         dhcpv6_retx[DHCPV6_MSG_REQUEST].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].rc_max;
625         dhcpv6_retx[DHCPV6_MSG_RENEW].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_init;
626         dhcpv6_retx[DHCPV6_MSG_RENEW].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_max;
627         dhcpv6_retx[DHCPV6_MSG_REBIND].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_init;
628         dhcpv6_retx[DHCPV6_MSG_REBIND].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_max;
629         dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_delay = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].delay_max;
630         dhcpv6_retx[DHCPV6_MSG_INFO_REQ].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_init;
631         dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_max;
632         dhcpv6_retx[DHCPV6_MSG_RELEASE].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].timeout_init;
633         dhcpv6_retx[DHCPV6_MSG_RELEASE].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].rc_max;
634         dhcpv6_retx[DHCPV6_MSG_DECLINE].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].timeout_init;
635         dhcpv6_retx[DHCPV6_MSG_DECLINE].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].rc_max;
636 }
637 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt