Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <arpa/inet.h>
4 : #include <linux/if.h>
5 : #include <netinet/ether.h>
6 :
7 : #include "sd-id128.h"
8 : #include "sd-ndisc.h"
9 :
10 : #include "alloc-util.h"
11 : #include "condition.h"
12 : #include "conf-parser.h"
13 : #include "device-util.h"
14 : #include "dhcp-lease-internal.h"
15 : #include "env-util.h"
16 : #include "ether-addr-util.h"
17 : #include "hexdecoct.h"
18 : #include "log.h"
19 : #include "network-internal.h"
20 : #include "parse-util.h"
21 : #include "siphash24.h"
22 : #include "socket-util.h"
23 : #include "string-util.h"
24 : #include "strv.h"
25 : #include "utf8.h"
26 : #include "util.h"
27 :
28 0 : const char *net_get_name_persistent(sd_device *device) {
29 : const char *name, *field;
30 :
31 0 : assert(device);
32 :
33 : /* fetch some persistent data unique (on this machine) to this device */
34 0 : FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC")
35 0 : if (sd_device_get_property_value(device, field, &name) >= 0)
36 0 : return name;
37 :
38 0 : return NULL;
39 : }
40 :
41 : #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
42 :
43 0 : int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result) {
44 0 : size_t l, sz = 0;
45 : const char *name;
46 : int r;
47 : uint8_t *v;
48 :
49 0 : assert(device);
50 :
51 : /* net_get_name_persistent() will return one of the device names based on stable information about
52 : * the device. If this is not available, we fall back to using the actual device name. */
53 0 : name = net_get_name_persistent(device);
54 0 : if (!name && use_sysname)
55 0 : (void) sd_device_get_sysname(device, &name);
56 0 : if (!name)
57 0 : return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
58 : "No stable identifying information found");
59 :
60 0 : log_device_debug(device, "Using \"%s\" as stable identifying information", name);
61 0 : l = strlen(name);
62 0 : sz = sizeof(sd_id128_t) + l;
63 0 : v = newa(uint8_t, sz);
64 :
65 : /* Fetch some persistent data unique to this machine */
66 0 : r = sd_id128_get_machine((sd_id128_t*) v);
67 0 : if (r < 0)
68 0 : return r;
69 0 : memcpy(v + sizeof(sd_id128_t), name, l);
70 :
71 : /* Let's hash the machine ID plus the device name. We use
72 : * a fixed, but originally randomly created hash key here. */
73 0 : *result = htole64(siphash24(v, sz, HASH_KEY.bytes));
74 0 : return 0;
75 : }
76 :
77 4 : static bool net_condition_test_strv(char * const *patterns, const char *string) {
78 : char * const *p;
79 4 : bool match = false, has_positive_rule = false;
80 :
81 4 : if (strv_isempty(patterns))
82 2 : return true;
83 :
84 4 : STRV_FOREACH(p, patterns) {
85 2 : const char *q = *p;
86 : bool invert;
87 :
88 2 : invert = *q == '!';
89 2 : q += invert;
90 :
91 2 : if (!invert)
92 2 : has_positive_rule = true;
93 :
94 2 : if (string && fnmatch(q, string, 0) == 0) {
95 0 : if (invert)
96 0 : return false;
97 : else
98 0 : match = true;
99 : }
100 : }
101 :
102 2 : return has_positive_rule ? match : true;
103 : }
104 :
105 0 : static int net_condition_test_property(char * const *match_property, sd_device *device) {
106 : char * const *p;
107 :
108 0 : if (strv_isempty(match_property))
109 0 : return true;
110 :
111 0 : STRV_FOREACH(p, match_property) {
112 0 : _cleanup_free_ char *key = NULL;
113 : const char *val, *dev_val;
114 : bool invert, v;
115 :
116 0 : invert = **p == '!';
117 :
118 0 : val = strchr(*p + invert, '=');
119 0 : if (!val)
120 0 : return -EINVAL;
121 :
122 0 : key = strndup(*p + invert, val - *p - invert);
123 0 : if (!key)
124 0 : return -ENOMEM;
125 :
126 0 : val++;
127 :
128 0 : v = device &&
129 0 : sd_device_get_property_value(device, key, &dev_val) >= 0 &&
130 0 : fnmatch(val, dev_val, 0) == 0;
131 :
132 0 : if (invert ? v : !v)
133 0 : return false;
134 : }
135 :
136 0 : return true;
137 : }
138 :
139 2 : bool net_match_config(Set *match_mac,
140 : char * const *match_paths,
141 : char * const *match_drivers,
142 : char * const *match_types,
143 : char * const *match_names,
144 : char * const *match_property,
145 : sd_device *device,
146 : const struct ether_addr *dev_mac,
147 : const char *dev_name) {
148 :
149 2 : const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
150 :
151 2 : if (device) {
152 2 : (void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
153 2 : (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
154 2 : (void) sd_device_get_devtype(device, &dev_type);
155 :
156 2 : if (!dev_name)
157 0 : (void) sd_device_get_sysname(device, &dev_name);
158 2 : if (!dev_mac &&
159 0 : sd_device_get_sysattr_value(device, "address", &mac_str) >= 0)
160 0 : dev_mac = ether_aton(mac_str);
161 : }
162 :
163 2 : if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac)))
164 0 : return false;
165 :
166 2 : if (!net_condition_test_strv(match_paths, dev_path))
167 0 : return false;
168 :
169 2 : if (!net_condition_test_strv(match_drivers, dev_driver))
170 2 : return false;
171 :
172 0 : if (!net_condition_test_strv(match_types, dev_type))
173 0 : return false;
174 :
175 0 : if (!net_condition_test_strv(match_names, dev_name))
176 0 : return false;
177 :
178 0 : if (!net_condition_test_property(match_property, device))
179 0 : return false;
180 :
181 0 : return true;
182 : }
183 :
184 1 : int config_parse_net_condition(const char *unit,
185 : const char *filename,
186 : unsigned line,
187 : const char *section,
188 : unsigned section_line,
189 : const char *lvalue,
190 : int ltype,
191 : const char *rvalue,
192 : void *data,
193 : void *userdata) {
194 :
195 1 : ConditionType cond = ltype;
196 1 : Condition **list = data, *c;
197 : bool negate;
198 :
199 1 : assert(filename);
200 1 : assert(lvalue);
201 1 : assert(rvalue);
202 1 : assert(data);
203 :
204 1 : if (isempty(rvalue)) {
205 0 : *list = condition_free_list_type(*list, cond);
206 0 : return 0;
207 : }
208 :
209 1 : negate = rvalue[0] == '!';
210 1 : if (negate)
211 0 : rvalue++;
212 :
213 1 : c = condition_new(cond, rvalue, false, negate);
214 1 : if (!c)
215 0 : return log_oom();
216 :
217 : /* Drop previous assignment. */
218 1 : *list = condition_free_list_type(*list, cond);
219 :
220 1 : LIST_PREPEND(conditions, *list, c);
221 1 : return 0;
222 : }
223 :
224 5 : int config_parse_match_strv(
225 : const char *unit,
226 : const char *filename,
227 : unsigned line,
228 : const char *section,
229 : unsigned section_line,
230 : const char *lvalue,
231 : int ltype,
232 : const char *rvalue,
233 : void *data,
234 : void *userdata) {
235 :
236 5 : const char *p = rvalue;
237 5 : char ***sv = data;
238 : bool invert;
239 : int r;
240 :
241 5 : assert(filename);
242 5 : assert(lvalue);
243 5 : assert(rvalue);
244 5 : assert(data);
245 :
246 5 : if (isempty(rvalue)) {
247 0 : *sv = strv_free(*sv);
248 0 : return 0;
249 : }
250 :
251 5 : invert = *p == '!';
252 5 : p += invert;
253 :
254 9 : for (;;) {
255 19 : _cleanup_free_ char *word = NULL, *k = NULL;
256 :
257 14 : r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
258 14 : if (r == 0)
259 5 : return 0;
260 9 : if (r == -ENOMEM)
261 0 : return log_oom();
262 9 : if (r < 0) {
263 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
264 0 : return 0;
265 : }
266 :
267 9 : if (invert) {
268 4 : k = strjoin("!", word);
269 4 : if (!k)
270 0 : return log_oom();
271 : } else
272 5 : k = TAKE_PTR(word);
273 :
274 9 : r = strv_consume(sv, TAKE_PTR(k));
275 9 : if (r < 0)
276 0 : return log_oom();
277 : }
278 : }
279 :
280 29 : int config_parse_match_ifnames(
281 : const char *unit,
282 : const char *filename,
283 : unsigned line,
284 : const char *section,
285 : unsigned section_line,
286 : const char *lvalue,
287 : int ltype,
288 : const char *rvalue,
289 : void *data,
290 : void *userdata) {
291 :
292 29 : const char *p = rvalue;
293 29 : char ***sv = data;
294 : bool invert;
295 : int r;
296 :
297 29 : assert(filename);
298 29 : assert(lvalue);
299 29 : assert(rvalue);
300 29 : assert(data);
301 :
302 29 : invert = *p == '!';
303 29 : p += invert;
304 :
305 33 : for (;;) {
306 91 : _cleanup_free_ char *word = NULL, *k = NULL;
307 :
308 62 : r = extract_first_word(&p, &word, NULL, 0);
309 62 : if (r == 0)
310 29 : return 0;
311 33 : if (r == -ENOMEM)
312 0 : return log_oom();
313 33 : if (r < 0) {
314 0 : log_syntax(unit, LOG_ERR, filename, line, 0,
315 : "Failed to parse interface name list: %s", rvalue);
316 0 : return 0;
317 : }
318 :
319 33 : if (!ifname_valid(word)) {
320 0 : log_syntax(unit, LOG_ERR, filename, line, 0,
321 : "Interface name is not valid or too long, ignoring assignment: %s", word);
322 0 : continue;
323 : }
324 :
325 33 : if (invert) {
326 4 : k = strjoin("!", word);
327 4 : if (!k)
328 0 : return log_oom();
329 : } else
330 29 : k = TAKE_PTR(word);
331 :
332 33 : r = strv_consume(sv, TAKE_PTR(k));
333 33 : if (r < 0)
334 0 : return log_oom();
335 : }
336 : }
337 :
338 0 : int config_parse_match_property(
339 : const char *unit,
340 : const char *filename,
341 : unsigned line,
342 : const char *section,
343 : unsigned section_line,
344 : const char *lvalue,
345 : int ltype,
346 : const char *rvalue,
347 : void *data,
348 : void *userdata) {
349 :
350 0 : const char *p = rvalue;
351 0 : char ***sv = data;
352 : bool invert;
353 : int r;
354 :
355 0 : assert(filename);
356 0 : assert(lvalue);
357 0 : assert(rvalue);
358 0 : assert(data);
359 :
360 0 : invert = *p == '!';
361 0 : p += invert;
362 :
363 0 : for (;;) {
364 0 : _cleanup_free_ char *word = NULL, *k = NULL;
365 :
366 0 : r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
367 0 : if (r == 0)
368 0 : return 0;
369 0 : if (r == -ENOMEM)
370 0 : return log_oom();
371 0 : if (r < 0) {
372 0 : log_syntax(unit, LOG_ERR, filename, line, 0,
373 : "Invalid syntax, ignoring: %s", rvalue);
374 0 : return 0;
375 : }
376 :
377 0 : if (!env_assignment_is_valid(word)) {
378 0 : log_syntax(unit, LOG_ERR, filename, line, 0,
379 : "Invalid property or value, ignoring assignment: %s", word);
380 0 : continue;
381 : }
382 :
383 0 : if (invert) {
384 0 : k = strjoin("!", word);
385 0 : if (!k)
386 0 : return log_oom();
387 : } else
388 0 : k = TAKE_PTR(word);
389 :
390 0 : r = strv_consume(sv, TAKE_PTR(k));
391 0 : if (r < 0)
392 0 : return log_oom();
393 : }
394 : }
395 :
396 0 : int config_parse_ifalias(const char *unit,
397 : const char *filename,
398 : unsigned line,
399 : const char *section,
400 : unsigned section_line,
401 : const char *lvalue,
402 : int ltype,
403 : const char *rvalue,
404 : void *data,
405 : void *userdata) {
406 :
407 0 : char **s = data;
408 0 : _cleanup_free_ char *n = NULL;
409 :
410 0 : assert(filename);
411 0 : assert(lvalue);
412 0 : assert(rvalue);
413 0 : assert(data);
414 :
415 0 : n = strdup(rvalue);
416 0 : if (!n)
417 0 : return log_oom();
418 :
419 0 : if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
420 0 : log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
421 0 : return 0;
422 : }
423 :
424 0 : if (isempty(n))
425 0 : *s = mfree(*s);
426 : else
427 0 : free_and_replace(*s, n);
428 :
429 0 : return 0;
430 : }
431 :
432 26 : int config_parse_hwaddr(const char *unit,
433 : const char *filename,
434 : unsigned line,
435 : const char *section,
436 : unsigned section_line,
437 : const char *lvalue,
438 : int ltype,
439 : const char *rvalue,
440 : void *data,
441 : void *userdata) {
442 :
443 26 : _cleanup_free_ struct ether_addr *n = NULL;
444 26 : struct ether_addr **hwaddr = data;
445 : int r;
446 :
447 26 : assert(filename);
448 26 : assert(lvalue);
449 26 : assert(rvalue);
450 26 : assert(data);
451 :
452 26 : n = new0(struct ether_addr, 1);
453 26 : if (!n)
454 0 : return log_oom();
455 :
456 26 : r = ether_addr_from_string(rvalue, n);
457 26 : if (r < 0) {
458 15 : log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
459 15 : return 0;
460 : }
461 :
462 11 : free_and_replace(*hwaddr, n);
463 :
464 11 : return 0;
465 : }
466 :
467 28 : int config_parse_hwaddrs(const char *unit,
468 : const char *filename,
469 : unsigned line,
470 : const char *section,
471 : unsigned section_line,
472 : const char *lvalue,
473 : int ltype,
474 : const char *rvalue,
475 : void *data,
476 : void *userdata) {
477 :
478 28 : _cleanup_set_free_free_ Set *s = NULL;
479 28 : const char *p = rvalue;
480 28 : Set **hwaddrs = data;
481 : int r;
482 :
483 28 : assert(filename);
484 28 : assert(lvalue);
485 28 : assert(rvalue);
486 28 : assert(data);
487 :
488 28 : if (isempty(rvalue)) {
489 : /* Empty assignment resets the list */
490 1 : *hwaddrs = set_free_free(*hwaddrs);
491 1 : return 0;
492 : }
493 :
494 27 : s = set_new(ðer_addr_hash_ops);
495 27 : if (!s)
496 0 : return log_oom();
497 :
498 38 : for (;;) {
499 65 : _cleanup_free_ char *word = NULL;
500 65 : _cleanup_free_ struct ether_addr *n = NULL;
501 :
502 65 : r = extract_first_word(&p, &word, NULL, 0);
503 65 : if (r == 0)
504 27 : break;
505 38 : if (r == -ENOMEM)
506 0 : return log_oom();
507 38 : if (r < 0) {
508 0 : log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
509 0 : return 0;
510 : }
511 :
512 38 : n = new(struct ether_addr, 1);
513 38 : if (!n)
514 0 : return log_oom();
515 :
516 38 : r = ether_addr_from_string(word, n);
517 38 : if (r < 0) {
518 18 : log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring: %s", word);
519 18 : continue;
520 : }
521 :
522 20 : r = set_put(s, n);
523 20 : if (r < 0)
524 0 : return log_oom();
525 20 : if (r > 0)
526 17 : n = NULL; /* avoid cleanup */
527 : }
528 :
529 27 : r = set_ensure_allocated(hwaddrs, ðer_addr_hash_ops);
530 27 : if (r < 0)
531 0 : return log_oom();
532 :
533 27 : r = set_move(*hwaddrs, s);
534 27 : if (r < 0)
535 0 : return log_oom();
536 :
537 27 : return 0;
538 : }
539 :
540 0 : int config_parse_bridge_port_priority(
541 : const char *unit,
542 : const char *filename,
543 : unsigned line,
544 : const char *section,
545 : unsigned section_line,
546 : const char *lvalue,
547 : int ltype,
548 : const char *rvalue,
549 : void *data,
550 : void *userdata) {
551 :
552 : uint16_t i;
553 : int r;
554 :
555 0 : assert(filename);
556 0 : assert(lvalue);
557 0 : assert(rvalue);
558 0 : assert(data);
559 :
560 0 : r = safe_atou16(rvalue, &i);
561 0 : if (r < 0) {
562 0 : log_syntax(unit, LOG_ERR, filename, line, r,
563 : "Failed to parse bridge port priority, ignoring: %s", rvalue);
564 0 : return 0;
565 : }
566 :
567 0 : if (i > LINK_BRIDGE_PORT_PRIORITY_MAX) {
568 0 : log_syntax(unit, LOG_ERR, filename, line, r,
569 : "Bridge port priority is larger than maximum %u, ignoring: %s", LINK_BRIDGE_PORT_PRIORITY_MAX, rvalue);
570 0 : return 0;
571 : }
572 :
573 0 : *((uint16_t *)data) = i;
574 :
575 0 : return 0;
576 : }
577 :
578 0 : size_t serialize_in_addrs(FILE *f,
579 : const struct in_addr *addresses,
580 : size_t size,
581 : bool with_leading_space,
582 : bool (*predicate)(const struct in_addr *addr)) {
583 : size_t count;
584 : size_t i;
585 :
586 0 : assert(f);
587 0 : assert(addresses);
588 :
589 0 : count = 0;
590 :
591 0 : for (i = 0; i < size; i++) {
592 : char sbuf[INET_ADDRSTRLEN];
593 :
594 0 : if (predicate && !predicate(&addresses[i]))
595 0 : continue;
596 0 : if (with_leading_space)
597 0 : fputc(' ', f);
598 : else
599 0 : with_leading_space = true;
600 0 : fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f);
601 0 : count++;
602 : }
603 :
604 0 : return count;
605 : }
606 :
607 1 : int deserialize_in_addrs(struct in_addr **ret, const char *string) {
608 1 : _cleanup_free_ struct in_addr *addresses = NULL;
609 1 : int size = 0;
610 :
611 1 : assert(ret);
612 1 : assert(string);
613 :
614 6 : for (;;) {
615 7 : _cleanup_free_ char *word = NULL;
616 : struct in_addr *new_addresses;
617 : int r;
618 :
619 7 : r = extract_first_word(&string, &word, NULL, 0);
620 7 : if (r < 0)
621 0 : return r;
622 7 : if (r == 0)
623 1 : break;
624 :
625 6 : new_addresses = reallocarray(addresses, size + 1, sizeof(struct in_addr));
626 6 : if (!new_addresses)
627 0 : return -ENOMEM;
628 : else
629 6 : addresses = new_addresses;
630 :
631 6 : r = inet_pton(AF_INET, word, &(addresses[size]));
632 6 : if (r <= 0)
633 3 : continue;
634 :
635 3 : size++;
636 : }
637 :
638 1 : *ret = size > 0 ? TAKE_PTR(addresses) : NULL;
639 :
640 1 : return size;
641 : }
642 :
643 0 : void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) {
644 : unsigned i;
645 :
646 0 : assert(f);
647 0 : assert(addresses);
648 0 : assert(size);
649 :
650 0 : for (i = 0; i < size; i++) {
651 : char buffer[INET6_ADDRSTRLEN];
652 :
653 0 : fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
654 :
655 0 : if (i < size - 1)
656 0 : fputc(' ', f);
657 : }
658 0 : }
659 :
660 1 : int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
661 1 : _cleanup_free_ struct in6_addr *addresses = NULL;
662 1 : int size = 0;
663 :
664 1 : assert(ret);
665 1 : assert(string);
666 :
667 6 : for (;;) {
668 7 : _cleanup_free_ char *word = NULL;
669 : struct in6_addr *new_addresses;
670 : int r;
671 :
672 7 : r = extract_first_word(&string, &word, NULL, 0);
673 7 : if (r < 0)
674 0 : return r;
675 7 : if (r == 0)
676 1 : break;
677 :
678 6 : new_addresses = reallocarray(addresses, size + 1, sizeof(struct in6_addr));
679 6 : if (!new_addresses)
680 0 : return -ENOMEM;
681 : else
682 6 : addresses = new_addresses;
683 :
684 6 : r = inet_pton(AF_INET6, word, &(addresses[size]));
685 6 : if (r <= 0)
686 3 : continue;
687 :
688 3 : size++;
689 : }
690 :
691 1 : *ret = TAKE_PTR(addresses);
692 :
693 1 : return size;
694 : }
695 :
696 0 : void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
697 : unsigned i;
698 :
699 0 : assert(f);
700 0 : assert(key);
701 0 : assert(routes);
702 0 : assert(size);
703 :
704 0 : fprintf(f, "%s=", key);
705 :
706 0 : for (i = 0; i < size; i++) {
707 : char sbuf[INET_ADDRSTRLEN];
708 : struct in_addr dest, gw;
709 : uint8_t length;
710 :
711 0 : assert_se(sd_dhcp_route_get_destination(routes[i], &dest) >= 0);
712 0 : assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
713 0 : assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
714 :
715 0 : fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length);
716 0 : fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": "");
717 : }
718 :
719 0 : fputs("\n", f);
720 0 : }
721 :
722 4 : int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
723 4 : _cleanup_free_ struct sd_dhcp_route *routes = NULL;
724 4 : size_t size = 0, allocated = 0;
725 :
726 4 : assert(ret);
727 4 : assert(ret_size);
728 4 : assert(ret_allocated);
729 4 : assert(string);
730 :
731 : /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
732 9 : for (;;) {
733 13 : _cleanup_free_ char *word = NULL;
734 : char *tok, *tok_end;
735 : unsigned n;
736 : int r;
737 :
738 13 : r = extract_first_word(&string, &word, NULL, 0);
739 13 : if (r < 0)
740 0 : return r;
741 13 : if (r == 0)
742 4 : break;
743 :
744 9 : if (!GREEDY_REALLOC(routes, allocated, size + 1))
745 0 : return -ENOMEM;
746 :
747 9 : tok = word;
748 :
749 : /* get the subnet */
750 9 : tok_end = strchr(tok, '/');
751 9 : if (!tok_end)
752 2 : continue;
753 7 : *tok_end = '\0';
754 :
755 7 : r = inet_aton(tok, &routes[size].dst_addr);
756 7 : if (r == 0)
757 0 : continue;
758 :
759 7 : tok = tok_end + 1;
760 :
761 : /* get the prefixlen */
762 7 : tok_end = strchr(tok, ',');
763 7 : if (!tok_end)
764 0 : continue;
765 :
766 7 : *tok_end = '\0';
767 :
768 7 : r = safe_atou(tok, &n);
769 7 : if (r < 0 || n > 32)
770 1 : continue;
771 :
772 6 : routes[size].dst_prefixlen = (uint8_t) n;
773 6 : tok = tok_end + 1;
774 :
775 : /* get the gateway */
776 6 : r = inet_aton(tok, &routes[size].gw_addr);
777 6 : if (r == 0)
778 1 : continue;
779 :
780 5 : size++;
781 : }
782 :
783 4 : *ret_size = size;
784 4 : *ret_allocated = allocated;
785 4 : *ret = TAKE_PTR(routes);
786 :
787 4 : return 0;
788 : }
789 :
790 0 : int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) {
791 0 : _cleanup_free_ char *hex_buf = NULL;
792 :
793 0 : assert(f);
794 0 : assert(key);
795 0 : assert(data);
796 :
797 0 : hex_buf = hexmem(data, size);
798 0 : if (!hex_buf)
799 0 : return -ENOMEM;
800 :
801 0 : fprintf(f, "%s=%s\n", key, hex_buf);
802 :
803 0 : return 0;
804 : }
|