]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/network-internal.c
various tools: be more explicit when a glob is passed when not supported
[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,
8d968fdd 145 char * const *match_ssid,
277ba8d1 146 Set *match_bssid,
b38de0e9 147 sd_device *device,
505f8da7 148 const struct ether_addr *dev_mac,
8d968fdd 149 const char *dev_name,
277ba8d1
YW
150 const char *ssid,
151 const struct ether_addr *bssid) {
be32eb9b 152
b38de0e9
YW
153 const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
154
155 if (device) {
156 (void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
157 (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
158 (void) sd_device_get_devtype(device, &dev_type);
159
160 if (!dev_name)
161 (void) sd_device_get_sysname(device, &dev_name);
162 if (!dev_mac &&
163 sd_device_get_sysattr_value(device, "address", &mac_str) >= 0)
164 dev_mac = ether_aton(mac_str);
165 }
166
25ea58d3 167 if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac)))
7eb08da4 168 return false;
be32eb9b 169
618b196e 170 if (!net_condition_test_strv(match_paths, dev_path))
ee5de57b 171 return false;
5256e00e 172
618b196e 173 if (!net_condition_test_strv(match_drivers, dev_driver))
ee5de57b 174 return false;
5256e00e 175
618b196e 176 if (!net_condition_test_strv(match_types, dev_type))
ee5de57b 177 return false;
5256e00e 178
618b196e 179 if (!net_condition_test_strv(match_names, dev_name))
ee5de57b 180 return false;
5256e00e 181
44005bfb
YW
182 if (!net_condition_test_property(match_property, device))
183 return false;
184
8d968fdd
YW
185 if (!net_condition_test_strv(match_ssid, ssid))
186 return false;
187
277ba8d1
YW
188 if (match_bssid && (!bssid || !set_contains(match_bssid, bssid)))
189 return false;
190
7eb08da4 191 return true;
be32eb9b 192}
5fde13d7 193
2cc412b5
TG
194int config_parse_net_condition(const char *unit,
195 const char *filename,
196 unsigned line,
197 const char *section,
198 unsigned section_line,
199 const char *lvalue,
200 int ltype,
201 const char *rvalue,
202 void *data,
203 void *userdata) {
204
205 ConditionType cond = ltype;
c4f58dea 206 Condition **list = data, *c;
2cc412b5 207 bool negate;
2cc412b5
TG
208
209 assert(filename);
210 assert(lvalue);
211 assert(rvalue);
212 assert(data);
213
c4f58dea
YW
214 if (isempty(rvalue)) {
215 *list = condition_free_list_type(*list, cond);
216 return 0;
217 }
218
2cc412b5
TG
219 negate = rvalue[0] == '!';
220 if (negate)
221 rvalue++;
222
2bd0da7a 223 c = condition_new(cond, rvalue, false, negate);
2cc412b5
TG
224 if (!c)
225 return log_oom();
226
c4f58dea
YW
227 /* Drop previous assignment. */
228 *list = condition_free_list_type(*list, cond);
2cc412b5 229
c4f58dea 230 LIST_PREPEND(conditions, *list, c);
2cc412b5
TG
231 return 0;
232}
233
54a84237 234int config_parse_match_strv(
d31645ad
LP
235 const char *unit,
236 const char *filename,
237 unsigned line,
238 const char *section,
239 unsigned section_line,
240 const char *lvalue,
241 int ltype,
242 const char *rvalue,
243 void *data,
244 void *userdata) {
5256e00e 245
54a84237 246 const char *p = rvalue;
5256e00e 247 char ***sv = data;
54a84237 248 bool invert;
5256e00e
TG
249 int r;
250
251 assert(filename);
252 assert(lvalue);
253 assert(rvalue);
254 assert(data);
255
54a84237
YW
256 if (isempty(rvalue)) {
257 *sv = strv_free(*sv);
258 return 0;
259 }
260
261 invert = *p == '!';
262 p += invert;
263
93e28226 264 for (;;) {
54a84237 265 _cleanup_free_ char *word = NULL, *k = NULL;
5256e00e 266
adfafd88 267 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
54a84237
YW
268 if (r == 0)
269 return 0;
270 if (r == -ENOMEM)
271 return log_oom();
a9dd908d 272 if (r < 0) {
54a84237 273 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
a9dd908d
LP
274 return 0;
275 }
54a84237
YW
276
277 if (invert) {
278 k = strjoin("!", word);
279 if (!k)
280 return log_oom();
281 } else
282 k = TAKE_PTR(word);
283
284 r = strv_consume(sv, TAKE_PTR(k));
285 if (r < 0)
286 return log_oom();
287 }
288}
289
290int config_parse_match_ifnames(
291 const char *unit,
292 const char *filename,
293 unsigned line,
294 const char *section,
295 unsigned section_line,
296 const char *lvalue,
297 int ltype,
298 const char *rvalue,
299 void *data,
300 void *userdata) {
301
302 const char *p = rvalue;
303 char ***sv = data;
304 bool invert;
305 int r;
306
307 assert(filename);
308 assert(lvalue);
309 assert(rvalue);
310 assert(data);
311
312 invert = *p == '!';
313 p += invert;
314
315 for (;;) {
316 _cleanup_free_ char *word = NULL, *k = NULL;
317
318 r = extract_first_word(&p, &word, NULL, 0);
93e28226 319 if (r == 0)
54a84237
YW
320 return 0;
321 if (r == -ENOMEM)
322 return log_oom();
323 if (r < 0) {
324 log_syntax(unit, LOG_ERR, filename, line, 0,
325 "Failed to parse interface name list: %s", rvalue);
326 return 0;
327 }
5256e00e 328
d31645ad 329 if (!ifname_valid(word)) {
54a84237
YW
330 log_syntax(unit, LOG_ERR, filename, line, 0,
331 "Interface name is not valid or too long, ignoring assignment: %s", word);
332 continue;
5256e00e
TG
333 }
334
54a84237
YW
335 if (invert) {
336 k = strjoin("!", word);
337 if (!k)
338 return log_oom();
339 } else
340 k = TAKE_PTR(word);
341
342 r = strv_consume(sv, TAKE_PTR(k));
5256e00e
TG
343 if (r < 0)
344 return log_oom();
345 }
5256e00e
TG
346}
347
44005bfb
YW
348int config_parse_match_property(
349 const char *unit,
350 const char *filename,
351 unsigned line,
352 const char *section,
353 unsigned section_line,
354 const char *lvalue,
355 int ltype,
356 const char *rvalue,
357 void *data,
358 void *userdata) {
359
360 const char *p = rvalue;
361 char ***sv = data;
362 bool invert;
363 int r;
364
365 assert(filename);
366 assert(lvalue);
367 assert(rvalue);
368 assert(data);
369
370 invert = *p == '!';
371 p += invert;
372
373 for (;;) {
374 _cleanup_free_ char *word = NULL, *k = NULL;
375
376 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
377 if (r == 0)
378 return 0;
379 if (r == -ENOMEM)
380 return log_oom();
381 if (r < 0) {
382 log_syntax(unit, LOG_ERR, filename, line, 0,
383 "Invalid syntax, ignoring: %s", rvalue);
384 return 0;
385 }
386
387 if (!env_assignment_is_valid(word)) {
388 log_syntax(unit, LOG_ERR, filename, line, 0,
389 "Invalid property or value, ignoring assignment: %s", word);
390 continue;
391 }
392
393 if (invert) {
394 k = strjoin("!", word);
395 if (!k)
396 return log_oom();
397 } else
398 k = TAKE_PTR(word);
399
400 r = strv_consume(sv, TAKE_PTR(k));
401 if (r < 0)
402 return log_oom();
403 }
404}
405
d2df0d0e
TG
406int config_parse_ifalias(const char *unit,
407 const char *filename,
408 unsigned line,
409 const char *section,
71a61510 410 unsigned section_line,
d2df0d0e
TG
411 const char *lvalue,
412 int ltype,
413 const char *rvalue,
414 void *data,
415 void *userdata) {
416
417 char **s = data;
9c39eb5c 418 _cleanup_free_ char *n = NULL;
d2df0d0e
TG
419
420 assert(filename);
421 assert(lvalue);
422 assert(rvalue);
423 assert(data);
424
425 n = strdup(rvalue);
426 if (!n)
427 return log_oom();
428
429 if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
12ca818f 430 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
d2df0d0e
TG
431 return 0;
432 }
433
44386b44
YW
434 if (isempty(n))
435 *s = mfree(*s);
ae2a15bc 436 else
44386b44 437 free_and_replace(*s, n);
d2df0d0e
TG
438
439 return 0;
440}
441
5fde13d7
TG
442int config_parse_hwaddr(const char *unit,
443 const char *filename,
444 unsigned line,
445 const char *section,
71a61510 446 unsigned section_line,
5fde13d7
TG
447 const char *lvalue,
448 int ltype,
449 const char *rvalue,
450 void *data,
451 void *userdata) {
e5c1be89
YW
452
453 _cleanup_free_ struct ether_addr *n = NULL;
5fde13d7 454 struct ether_addr **hwaddr = data;
5fde13d7
TG
455 int r;
456
457 assert(filename);
458 assert(lvalue);
459 assert(rvalue);
460 assert(data);
461
a12fa420 462 n = new0(struct ether_addr, 1);
5fde13d7
TG
463 if (!n)
464 return log_oom();
465
e5c1be89
YW
466 r = ether_addr_from_string(rvalue, n);
467 if (r < 0) {
468 log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
5fde13d7
TG
469 return 0;
470 }
471
899f0d25 472 free_and_replace(*hwaddr, n);
5fde13d7
TG
473
474 return 0;
475}
f5284182 476
206b63ee
YW
477int config_parse_hwaddrs(const char *unit,
478 const char *filename,
479 unsigned line,
480 const char *section,
481 unsigned section_line,
482 const char *lvalue,
483 int ltype,
484 const char *rvalue,
485 void *data,
486 void *userdata) {
487
488 _cleanup_set_free_free_ Set *s = NULL;
489 const char *p = rvalue;
490 Set **hwaddrs = data;
491 int r;
492
493 assert(filename);
494 assert(lvalue);
495 assert(rvalue);
496 assert(data);
497
498 if (isempty(rvalue)) {
499 /* Empty assignment resets the list */
e90d0374 500 *hwaddrs = set_free_free(*hwaddrs);
206b63ee
YW
501 return 0;
502 }
503
504 s = set_new(&ether_addr_hash_ops);
505 if (!s)
506 return log_oom();
507
508 for (;;) {
509 _cleanup_free_ char *word = NULL;
510 _cleanup_free_ struct ether_addr *n = NULL;
511
512 r = extract_first_word(&p, &word, NULL, 0);
513 if (r == 0)
514 break;
515 if (r == -ENOMEM)
516 return log_oom();
517 if (r < 0) {
518 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
519 return 0;
520 }
521
e90d0374 522 n = new(struct ether_addr, 1);
206b63ee
YW
523 if (!n)
524 return log_oom();
525
526 r = ether_addr_from_string(word, n);
527 if (r < 0) {
528 log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring: %s", word);
529 continue;
530 }
531
532 r = set_put(s, n);
533 if (r < 0)
534 return log_oom();
535 if (r > 0)
536 n = NULL; /* avoid cleanup */
537 }
538
539 r = set_ensure_allocated(hwaddrs, &ether_addr_hash_ops);
540 if (r < 0)
541 return log_oom();
542
543 r = set_move(*hwaddrs, s);
544 if (r < 0)
545 return log_oom();
546
547 return 0;
548}
549
f00ff0de
DJL
550int config_parse_bridge_port_priority(
551 const char *unit,
552 const char *filename,
553 unsigned line,
554 const char *section,
555 unsigned section_line,
556 const char *lvalue,
557 int ltype,
558 const char *rvalue,
559 void *data,
560 void *userdata) {
561
562 uint16_t i;
563 int r;
564
565 assert(filename);
566 assert(lvalue);
567 assert(rvalue);
568 assert(data);
569
570 r = safe_atou16(rvalue, &i);
571 if (r < 0) {
572 log_syntax(unit, LOG_ERR, filename, line, r,
573 "Failed to parse bridge port priority, ignoring: %s", rvalue);
574 return 0;
575 }
576
577 if (i > LINK_BRIDGE_PORT_PRIORITY_MAX) {
578 log_syntax(unit, LOG_ERR, filename, line, r,
579 "Bridge port priority is larger than maximum %u, ignoring: %s", LINK_BRIDGE_PORT_PRIORITY_MAX, rvalue);
580 return 0;
581 }
582
583 *((uint16_t *)data) = i;
584
585 return 0;
586}
587
072320ea
TH
588size_t serialize_in_addrs(FILE *f,
589 const struct in_addr *addresses,
590 size_t size,
591 bool with_leading_space,
592 bool (*predicate)(const struct in_addr *addr)) {
593 size_t count;
594 size_t i;
09bee74d
TG
595
596 assert(f);
09bee74d 597 assert(addresses);
09bee74d 598
072320ea
TH
599 count = 0;
600
601 for (i = 0; i < size; i++) {
189255d2
TH
602 char sbuf[INET_ADDRSTRLEN];
603
072320ea
TH
604 if (predicate && !predicate(&addresses[i]))
605 continue;
606 if (with_leading_space)
607 fputc(' ', f);
608 else
609 with_leading_space = true;
189255d2 610 fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f);
072320ea
TH
611 count++;
612 }
613
614 return count;
09bee74d
TG
615}
616
a2ba62c7 617int deserialize_in_addrs(struct in_addr **ret, const char *string) {
09bee74d 618 _cleanup_free_ struct in_addr *addresses = NULL;
a2ba62c7 619 int size = 0;
09bee74d
TG
620
621 assert(ret);
09bee74d
TG
622 assert(string);
623
93e28226
SS
624 for (;;) {
625 _cleanup_free_ char *word = NULL;
09bee74d
TG
626 struct in_addr *new_addresses;
627 int r;
628
93e28226
SS
629 r = extract_first_word(&string, &word, NULL, 0);
630 if (r < 0)
631 return r;
632 if (r == 0)
633 break;
634
62d74c78 635 new_addresses = reallocarray(addresses, size + 1, sizeof(struct in_addr));
09bee74d
TG
636 if (!new_addresses)
637 return -ENOMEM;
638 else
639 addresses = new_addresses;
640
93e28226 641 r = inet_pton(AF_INET, word, &(addresses[size]));
09bee74d
TG
642 if (r <= 0)
643 continue;
644
313cefa1 645 size++;
09bee74d
TG
646 }
647
c24b6821 648 *ret = size > 0 ? TAKE_PTR(addresses) : NULL;
09bee74d 649
a2ba62c7 650 return size;
09bee74d
TG
651}
652
1f152e4b 653void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) {
b729fa14
PF
654 unsigned i;
655
656 assert(f);
657 assert(addresses);
658 assert(size);
659
1f152e4b
LP
660 for (i = 0; i < size; i++) {
661 char buffer[INET6_ADDRSTRLEN];
662
663 fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
664
665 if (i < size - 1)
666 fputc(' ', f);
667 }
b729fa14
PF
668}
669
a2ba62c7 670int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
09bee74d 671 _cleanup_free_ struct in6_addr *addresses = NULL;
a2ba62c7 672 int size = 0;
09bee74d
TG
673
674 assert(ret);
09bee74d
TG
675 assert(string);
676
93e28226
SS
677 for (;;) {
678 _cleanup_free_ char *word = NULL;
09bee74d
TG
679 struct in6_addr *new_addresses;
680 int r;
681
93e28226
SS
682 r = extract_first_word(&string, &word, NULL, 0);
683 if (r < 0)
684 return r;
685 if (r == 0)
686 break;
687
62d74c78 688 new_addresses = reallocarray(addresses, size + 1, sizeof(struct in6_addr));
09bee74d
TG
689 if (!new_addresses)
690 return -ENOMEM;
691 else
692 addresses = new_addresses;
693
93e28226 694 r = inet_pton(AF_INET6, word, &(addresses[size]));
09bee74d
TG
695 if (r <= 0)
696 continue;
697
698 size++;
699 }
700
ae2a15bc 701 *ret = TAKE_PTR(addresses);
09bee74d 702
a2ba62c7 703 return size;
09bee74d 704}
e1ea665e 705
f8693fc7 706void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
e1ea665e
EY
707 unsigned i;
708
709 assert(f);
710 assert(key);
711 assert(routes);
712 assert(size);
713
714 fprintf(f, "%s=", key);
715
fbf7dcb5 716 for (i = 0; i < size; i++) {
189255d2 717 char sbuf[INET_ADDRSTRLEN];
f8693fc7
BG
718 struct in_addr dest, gw;
719 uint8_t length;
720
721 assert_se(sd_dhcp_route_get_destination(routes[i], &dest) >= 0);
722 assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
723 assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
724
189255d2
TH
725 fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length);
726 fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": "");
fbf7dcb5 727 }
e1ea665e
EY
728
729 fputs("\n", f);
730}
731
732int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
733 _cleanup_free_ struct sd_dhcp_route *routes = NULL;
734 size_t size = 0, allocated = 0;
e1ea665e
EY
735
736 assert(ret);
737 assert(ret_size);
738 assert(ret_allocated);
739 assert(string);
740
93e28226
SS
741 /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
742 for (;;) {
743 _cleanup_free_ char *word = NULL;
e1ea665e
EY
744 char *tok, *tok_end;
745 unsigned n;
746 int r;
747
93e28226
SS
748 r = extract_first_word(&string, &word, NULL, 0);
749 if (r < 0)
750 return r;
751 if (r == 0)
752 break;
e1ea665e 753
93e28226 754 if (!GREEDY_REALLOC(routes, allocated, size + 1))
31db0120 755 return -ENOMEM;
e1ea665e 756
93e28226 757 tok = word;
e1ea665e
EY
758
759 /* get the subnet */
760 tok_end = strchr(tok, '/');
761 if (!tok_end)
762 continue;
763 *tok_end = '\0';
764
765 r = inet_aton(tok, &routes[size].dst_addr);
766 if (r == 0)
767 continue;
768
769 tok = tok_end + 1;
770
771 /* get the prefixlen */
772 tok_end = strchr(tok, ',');
773 if (!tok_end)
774 continue;
775
776 *tok_end = '\0';
777
778 r = safe_atou(tok, &n);
779 if (r < 0 || n > 32)
780 continue;
781
782 routes[size].dst_prefixlen = (uint8_t) n;
783 tok = tok_end + 1;
784
785 /* get the gateway */
786 r = inet_aton(tok, &routes[size].gw_addr);
787 if (r == 0)
788 continue;
789
790 size++;
791 }
792
793 *ret_size = size;
794 *ret_allocated = allocated;
ae2a15bc 795 *ret = TAKE_PTR(routes);
e1ea665e
EY
796
797 return 0;
798}
a073309f 799
e4735228 800int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) {
a073309f
AC
801 _cleanup_free_ char *hex_buf = NULL;
802
803 assert(f);
804 assert(key);
805 assert(data);
806
807 hex_buf = hexmem(data, size);
4e361acc 808 if (!hex_buf)
a073309f
AC
809 return -ENOMEM;
810
811 fprintf(f, "%s=%s\n", key, hex_buf);
812
813 return 0;
814}