• 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(char* option) {
224         return (config_parse_opt(option) == 0);
225 }
226 
227 bool config_set_rtx_delay_max(enum config_dhcp_msg msg, unsigned int value)
228 {
229         if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX) {
230                 error("Invalid retransmission Maximum Delay value");
231                 return false;
232         }
233         config_dhcp.message_rtx[msg].delay_max = value;
234         return true;
235 }
236 
237 bool config_set_rtx_timeout_init(enum config_dhcp_msg msg, unsigned int value)
238 {
239         if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX || value == 0) {
240                 error("Invalid retransmission Initial Timeout value");
241                 return false;
242         }
243         config_dhcp.message_rtx[msg].timeout_init = value;
244         return true;
245 }
246 
247 bool config_set_rtx_timeout_max(enum config_dhcp_msg msg, unsigned int value)
248 {
249         if (msg >= CONFIG_DHCP_MAX || value > UINT16_MAX) {
250                 error("Invalid retransmission Maximum Timeout value");
251                 return false;
252         }
253         config_dhcp.message_rtx[msg].timeout_max = value;
254         return true;
255 }
256 
257 bool config_set_rtx_rc_max(enum config_dhcp_msg msg, unsigned int value)
258 {
259         if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX) {
260                 error("Invalid retransmission Retry Attempt value");
261                 return false;
262         }
263         config_dhcp.message_rtx[msg].rc_max = value;
264         return true;
265 }
266 
267 bool config_set_irt_default(unsigned int value)
268 {
269         if (value == 0) {
270                 error("Invalid Default Information Refresh Time value");
271                 return false;
272         }
273         config_dhcp.irt_default = value;
274         return true;
275 }
276 
277 bool config_set_irt_min(unsigned int value)
278 {
279         if (value == 0) {
280                 error("Invalid Minimum Information Refresh Time value");
281                 return false;
282         }
283         config_dhcp.irt_min = value;
284         return true;
285 }
286 
287 bool config_set_rand_factor(unsigned int value)
288 {
289         if (value > 999 || value < 10) {
290                 error("Invalid Random Factor value");
291                 return false;
292         }
293         config_dhcp.rand_factor = value;
294         return true;
295 }
296 
297 bool config_set_auth_protocol(const char* protocol)
298 {
299         if (!strcmp(protocol, "None")) {
300                 config_dhcp.auth_protocol = AUTH_PROT_NONE;
301         } else if (!strcmp(protocol, "ConfigurationToken")) {
302                 config_dhcp.auth_protocol = AUTH_PROT_TOKEN;
303         } else if (!strcmp(protocol, "ReconfigureKeyAuthentication")) {
304                 config_dhcp.auth_protocol = AUTH_PROT_RKAP;
305         } else {
306                 error("Invalid Authentication protocol");
307                 return false;
308         }
309 
310         return true;
311 }
312 
313 bool config_set_auth_token(const char* token)
314 {
315         free(config_dhcp.auth_token);
316 
317         config_dhcp.auth_token = strdup(token);
318         return config_dhcp.auth_token != NULL;
319 }
320 
321 void config_set_client_opt_cfg(struct odhcp6c_opt_cfg *opt_cfg)
322 {
323         config_dhcp.strict_rfc7550 = opt_cfg->strict_rfc7550 != 0;
324 }
325 
326 static int config_parse_opt_u8(const char *src, uint8_t **dst)
327 {
328         int len = strlen(src);
329 
330         uint8_t *tmp = realloc(*dst, len/2);
331         if (!tmp)
332                 return -1;
333         *dst = tmp;
334 
335         return script_unhexlify(*dst, len, src);
336 }
337 
338 static int config_parse_opt_string(const char *src, uint8_t **dst, const bool array)
339 {
340         int o_len = 0;
341         char *sep = strpbrk(src, ARRAY_SEP);
342 
343         if (sep && !array)
344                 return -1;
345 
346         do {
347                 if (sep) {
348                         *sep = 0;
349                         sep++;
350                 }
351 
352                 int len = strlen(src);
353 
354                 uint8_t *tmp = realloc(*dst, o_len + len);
355                 if (!tmp)
356                         return -1;
357                 *dst = tmp;
358 
359                 memcpy(&((*dst)[o_len]), src, len);
360 
361                 o_len += len;
362                 src = sep;
363 
364                 if (sep)
365                         sep = strpbrk(src, ARRAY_SEP);
366         } while (src);
367 
368         return o_len;
369 }
370 
371 static int config_parse_opt_dns_string(const char *src, uint8_t **dst, const bool array)
372 {
373         int o_len = 0;
374         char *sep = strpbrk(src, ARRAY_SEP);
375 
376         if (sep && !array)
377                 return -1;
378 
379         do {
380                 uint8_t tmp[256];
381 
382                 if (sep) {
383                         *sep = 0;
384                         sep++;
385                 }
386 
387                 int len = dn_comp(src, tmp, sizeof(tmp), NULL, NULL);
388                 if (len < 0)
389                         return -1;
390 
391                 uint8_t *dst_tmp = realloc(*dst, o_len + len);
392                 if (!dst_tmp)
393                         return -1;
394                 *dst = dst_tmp;
395 
396                 memcpy(&((*dst)[o_len]), tmp, len);
397 
398                 o_len += len;
399                 src = sep;
400 
401                 if (sep)
402                         sep = strpbrk(src, ARRAY_SEP);
403         } while (src);
404 
405         return o_len;
406 }
407 
408 static int config_parse_opt_ip6(const char *src, uint8_t **dst, const bool array)
409 {
410         int o_len = 0;
411         char *sep = strpbrk(src, ARRAY_SEP);
412 
413         if (sep && !array)
414                 return -1;
415 
416         do {
417                 int len = sizeof(struct in6_addr);
418 
419                 if (sep) {
420                         *sep = 0;
421                         sep++;
422                 }
423 
424                 uint8_t *tmp = realloc(*dst, o_len + len);
425                 if (!tmp)
426                         return -1;
427                 *dst = tmp;
428 
429                 if (inet_pton(AF_INET6, src, &((*dst)[o_len])) < 1)
430                         return -1;
431 
432                 o_len += len;
433                 src = sep;
434 
435                 if (sep)
436                         sep = strpbrk(src, ARRAY_SEP);
437         } while (src);
438 
439         return o_len;
440 }
441 
442 static int config_parse_opt_user_class(const char *src, uint8_t **dst, const bool array)
443 {
444         int o_len = 0;
445         char *sep = strpbrk(src, ARRAY_SEP);
446 
447         if (sep && !array)
448                 return -1;
449 
450         do {
451                 if (sep) {
452                         *sep = 0;
453                         sep++;
454                 }
455                 uint16_t str_len = strlen(src);
456 
457                 uint8_t *tmp = realloc(*dst, o_len + str_len + 2);
458                 if (!tmp)
459                         return -1;
460                 *dst = tmp;
461 
462                 struct user_class {
463                         uint16_t len;
464                         uint8_t data[];
465                 } *e = (struct user_class *)&((*dst)[o_len]);
466 
467                 e->len = ntohs(str_len);
468                 memcpy(e->data, src, str_len);
469 
470                 o_len += str_len + 2;
471                 src = sep;
472 
473                 if (sep)
474                         sep = strpbrk(src, ARRAY_SEP);
475         } while (src);
476 
477         return o_len;
478 }
479 
480 static uint8_t *config_state_find_opt(const uint16_t code)
481 {
482         size_t opts_len;
483         uint8_t *odata, *opts = odhcp6c_get_state(STATE_OPTS, &opts_len);
484         uint16_t otype, olen;
485 
486         dhcpv6_for_each_option(opts, &opts[opts_len], otype, olen, odata) {
487                 if (otype == code)
488                         return &odata[-4];
489         }
490 
491         return NULL;
492 }
493 
494 int config_add_opt(const uint16_t code, const uint8_t *data, const uint16_t len)
495 {
496         struct {
497                 uint16_t code;
498                 uint16_t len;
499         } opt_hdr = { htons(code), htons(len) };
500 
501         if (config_state_find_opt(code))
502                 return -1;
503 
504         if (odhcp6c_add_state(STATE_OPTS, &opt_hdr, sizeof(opt_hdr)) ||
505                         odhcp6c_add_state(STATE_OPTS, data, len)) {
506                 error("Failed to add option %hu", code);
507                 return 1;
508         }
509 
510         return 0;
511 }
512 
513 int config_parse_opt_data(const char *data, uint8_t **dst, const unsigned int type,
514                 const bool array)
515 {
516         int ret = 0;
517 
518         switch (type) {
519         case OPT_U8:
520                 ret = config_parse_opt_u8(data, dst);
521                 break;
522 
523         case OPT_STR:
524                 ret = config_parse_opt_string(data, dst, array);
525                 break;
526 
527         case OPT_DNS_STR:
528                 ret = config_parse_opt_dns_string(data, dst, array);
529                 break;
530 
531         case OPT_IP6:
532                 ret = config_parse_opt_ip6(data, dst, array);
533                 break;
534 
535         case OPT_USER_CLASS:
536                 ret = config_parse_opt_user_class(data, dst, array);
537                 break;
538 
539         default:
540                 ret = -1;
541                 break;
542         }
543 
544         return ret;
545 }
546 
547 int config_parse_opt(const char *opt)
548 {
549         uint32_t optn;
550         char *data;
551         uint8_t *payload = NULL;
552         int payload_len;
553         unsigned int type = OPT_U8;
554         bool array = false;
555         struct odhcp6c_opt *dopt = NULL;
556         int ret = -1;
557 
558         data = strpbrk(opt, ":");
559         if (!data)
560                 return -1;
561 
562         *data = '\0';
563         data++;
564 
565         if (strlen(opt) == 0 || strlen(data) == 0)
566                 return -1;
567 
568         dopt = odhcp6c_find_opt_by_name(opt);
569         if (!dopt) {
570                 char *e;
571                 optn = strtoul(opt, &e, 0);
572                 if (*e || e == opt || optn > USHRT_MAX)
573                         return -1;
574 
575                 dopt = odhcp6c_find_opt(optn);
576         } else {
577                 optn = dopt->code;
578         }
579 
580         /* Check if the type for the content is well-known */
581         if (dopt) {
582                 /* Refuse internal options */
583                 if (dopt->flags & OPT_INTERNAL)
584                         return -1;
585 
586                 type = dopt->flags & OPT_MASK_SIZE;
587                 array = ((dopt->flags & OPT_ARRAY) == OPT_ARRAY) ? true : false;
588         } else if (data[0] == '"' || data[0] == '\'') {
589                 char *end = strrchr(data + 1, data[0]);
590 
591                 if (end && (end == (data + strlen(data) - 1))) {
592                         /* Raw option is specified as a string */
593                         type = OPT_STR;
594                         data++;
595                         *end = '\0';
596                 }
597         }
598 
599         payload_len = config_parse_opt_data(data, &payload, type, array);
600         if (payload_len > 0)
601                 ret = config_add_opt(optn, payload, payload_len);
602 
603         free(payload);
604 
605         return ret;
606 }
607 
608 void config_apply_dhcp_rtx(struct dhcpv6_retx* dhcpv6_retx)
609 {
610         dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_delay = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].delay_max;
611         dhcpv6_retx[DHCPV6_MSG_SOLICIT].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_init;
612         dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_max;
613         dhcpv6_retx[DHCPV6_MSG_REQUEST].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_init;
614         dhcpv6_retx[DHCPV6_MSG_REQUEST].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_max;
615         dhcpv6_retx[DHCPV6_MSG_REQUEST].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].rc_max;
616         dhcpv6_retx[DHCPV6_MSG_RENEW].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_init;
617         dhcpv6_retx[DHCPV6_MSG_RENEW].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_max;
618         dhcpv6_retx[DHCPV6_MSG_REBIND].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_init;
619         dhcpv6_retx[DHCPV6_MSG_REBIND].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_max;
620         dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_delay = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].delay_max;
621         dhcpv6_retx[DHCPV6_MSG_INFO_REQ].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_init;
622         dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_max;
623         dhcpv6_retx[DHCPV6_MSG_RELEASE].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].timeout_init;
624         dhcpv6_retx[DHCPV6_MSG_RELEASE].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].rc_max;
625         dhcpv6_retx[DHCPV6_MSG_DECLINE].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].timeout_init;
626         dhcpv6_retx[DHCPV6_MSG_DECLINE].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].rc_max;
627 }
628 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt