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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt