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