]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
5fde13d7 TG |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright (C) 2013 Tom Gundersen <teg@jklm.no> | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
f5284182 | 21 | #include <arpa/inet.h> |
07630cea LP |
22 | #include <linux/if.h> |
23 | #include <netinet/ether.h> | |
5fde13d7 | 24 | |
dccca82b | 25 | #include "sd-id128.h" |
07630cea LP |
26 | #include "sd-ndisc.h" |
27 | ||
b5efdb8a | 28 | #include "alloc-util.h" |
07630cea LP |
29 | #include "condition.h" |
30 | #include "conf-parser.h" | |
e1ea665e | 31 | #include "dhcp-lease-internal.h" |
aa31ce18 | 32 | #include "ether-addr-util.h" |
cf0fbc49 | 33 | #include "hexdecoct.h" |
be32eb9b | 34 | #include "log.h" |
6bedfcbb LP |
35 | #include "network-internal.h" |
36 | #include "parse-util.h" | |
07630cea | 37 | #include "siphash24.h" |
d31645ad | 38 | #include "socket-util.h" |
07630cea LP |
39 | #include "string-util.h" |
40 | #include "strv.h" | |
5fde13d7 | 41 | #include "utf8.h" |
a12fa420 | 42 | #include "util.h" |
5fde13d7 | 43 | |
fc541430 | 44 | const char *net_get_name(struct udev_device *device) { |
44e7b949 | 45 | const char *name, *field; |
fc541430 TG |
46 | |
47 | assert(device); | |
b5db00e5 UTL |
48 | |
49 | /* fetch some persistent data unique (on this machine) to this device */ | |
44e7b949 | 50 | FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") { |
b5db00e5 UTL |
51 | name = udev_device_get_property_value(device, field); |
52 | if (name) | |
44e7b949 | 53 | return name; |
b5db00e5 UTL |
54 | } |
55 | ||
44e7b949 | 56 | return NULL; |
fc541430 TG |
57 | } |
58 | ||
59 | #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a) | |
60 | ||
dbe81cbd | 61 | int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result) { |
fc541430 TG |
62 | size_t l, sz = 0; |
63 | const char *name = NULL; | |
64 | int r; | |
65 | uint8_t *v; | |
66 | ||
67 | assert(device); | |
68 | ||
69 | name = net_get_name(device); | |
b5db00e5 UTL |
70 | if (!name) |
71 | return -ENOENT; | |
72 | ||
73 | l = strlen(name); | |
74 | sz = sizeof(sd_id128_t) + l; | |
75 | v = alloca(sz); | |
76 | ||
77 | /* fetch some persistent data unique to this machine */ | |
78 | r = sd_id128_get_machine((sd_id128_t*) v); | |
79 | if (r < 0) | |
80 | return r; | |
81 | memcpy(v + sizeof(sd_id128_t), name, l); | |
82 | ||
83 | /* Let's hash the machine ID plus the device name. We | |
84 | * use a fixed, but originally randomly created hash | |
85 | * key here. */ | |
933f9cae | 86 | *result = htole64(siphash24(v, sz, HASH_KEY.bytes)); |
b5db00e5 UTL |
87 | |
88 | return 0; | |
89 | } | |
90 | ||
1aa68db1 DM |
91 | static bool net_condition_test_strv(char * const *raw_patterns, |
92 | const char *string) { | |
618b196e DM |
93 | if (strv_isempty(raw_patterns)) |
94 | return true; | |
95 | ||
96 | /* If the patterns begin with "!", edit it out and negate the test. */ | |
97 | if (raw_patterns[0][0] == '!') { | |
98 | char **patterns; | |
99 | unsigned i, length; | |
100 | ||
101 | length = strv_length(raw_patterns) + 1; /* Include the NULL. */ | |
102 | patterns = newa(char*, length); | |
103 | patterns[0] = raw_patterns[0] + 1; /* Skip the "!". */ | |
104 | for (i = 1; i < length; i++) | |
105 | patterns[i] = raw_patterns[i]; | |
106 | ||
107 | return !string || !strv_fnmatch(patterns, string, 0); | |
108 | } | |
109 | ||
110 | return string && strv_fnmatch(raw_patterns, string, 0); | |
111 | } | |
112 | ||
be32eb9b | 113 | bool net_match_config(const struct ether_addr *match_mac, |
5256e00e TG |
114 | char * const *match_paths, |
115 | char * const *match_drivers, | |
116 | char * const *match_types, | |
117 | char * const *match_names, | |
2cc412b5 TG |
118 | Condition *match_host, |
119 | Condition *match_virt, | |
5022f08a LP |
120 | Condition *match_kernel_cmdline, |
121 | Condition *match_kernel_version, | |
edbb03e9 | 122 | Condition *match_arch, |
505f8da7 | 123 | const struct ether_addr *dev_mac, |
b3e01314 | 124 | const char *dev_path, |
bf175aaf | 125 | const char *dev_parent_driver, |
b3e01314 TG |
126 | const char *dev_driver, |
127 | const char *dev_type, | |
32bc8adc | 128 | const char *dev_name) { |
be32eb9b | 129 | |
2cb62395 | 130 | if (match_host && condition_test(match_host) <= 0) |
7eb08da4 | 131 | return false; |
2cc412b5 | 132 | |
2cb62395 | 133 | if (match_virt && condition_test(match_virt) <= 0) |
7eb08da4 | 134 | return false; |
2cc412b5 | 135 | |
5022f08a LP |
136 | if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0) |
137 | return false; | |
138 | ||
139 | if (match_kernel_version && condition_test(match_kernel_version) <= 0) | |
7eb08da4 | 140 | return false; |
2cc412b5 | 141 | |
2cb62395 | 142 | if (match_arch && condition_test(match_arch) <= 0) |
7eb08da4 | 143 | return false; |
edbb03e9 | 144 | |
505f8da7 | 145 | if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) |
7eb08da4 | 146 | return false; |
be32eb9b | 147 | |
618b196e | 148 | if (!net_condition_test_strv(match_paths, dev_path)) |
ee5de57b | 149 | return false; |
5256e00e | 150 | |
618b196e | 151 | if (!net_condition_test_strv(match_drivers, dev_driver)) |
ee5de57b | 152 | return false; |
5256e00e | 153 | |
618b196e | 154 | if (!net_condition_test_strv(match_types, dev_type)) |
ee5de57b | 155 | return false; |
5256e00e | 156 | |
618b196e | 157 | if (!net_condition_test_strv(match_names, dev_name)) |
ee5de57b | 158 | return false; |
5256e00e | 159 | |
7eb08da4 | 160 | return true; |
be32eb9b | 161 | } |
5fde13d7 | 162 | |
2cc412b5 TG |
163 | int config_parse_net_condition(const char *unit, |
164 | const char *filename, | |
165 | unsigned line, | |
166 | const char *section, | |
167 | unsigned section_line, | |
168 | const char *lvalue, | |
169 | int ltype, | |
170 | const char *rvalue, | |
171 | void *data, | |
172 | void *userdata) { | |
173 | ||
174 | ConditionType cond = ltype; | |
175 | Condition **ret = data; | |
176 | bool negate; | |
177 | Condition *c; | |
178 | _cleanup_free_ char *s = NULL; | |
179 | ||
180 | assert(filename); | |
181 | assert(lvalue); | |
182 | assert(rvalue); | |
183 | assert(data); | |
184 | ||
185 | negate = rvalue[0] == '!'; | |
186 | if (negate) | |
187 | rvalue++; | |
188 | ||
189 | s = strdup(rvalue); | |
190 | if (!s) | |
191 | return log_oom(); | |
192 | ||
193 | c = condition_new(cond, s, false, negate); | |
194 | if (!c) | |
195 | return log_oom(); | |
196 | ||
197 | if (*ret) | |
198 | condition_free(*ret); | |
199 | ||
200 | *ret = c; | |
201 | return 0; | |
202 | } | |
203 | ||
d31645ad LP |
204 | int config_parse_ifnames( |
205 | const char *unit, | |
206 | const char *filename, | |
207 | unsigned line, | |
208 | const char *section, | |
209 | unsigned section_line, | |
210 | const char *lvalue, | |
211 | int ltype, | |
212 | const char *rvalue, | |
213 | void *data, | |
214 | void *userdata) { | |
5256e00e TG |
215 | |
216 | char ***sv = data; | |
5256e00e TG |
217 | int r; |
218 | ||
219 | assert(filename); | |
220 | assert(lvalue); | |
221 | assert(rvalue); | |
222 | assert(data); | |
223 | ||
93e28226 SS |
224 | for (;;) { |
225 | _cleanup_free_ char *word = NULL; | |
5256e00e | 226 | |
93e28226 | 227 | r = extract_first_word(&rvalue, &word, NULL, 0); |
a9dd908d LP |
228 | if (r < 0) { |
229 | log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue); | |
230 | return 0; | |
231 | } | |
93e28226 SS |
232 | if (r == 0) |
233 | break; | |
5256e00e | 234 | |
d31645ad LP |
235 | if (!ifname_valid(word)) { |
236 | log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); | |
5256e00e TG |
237 | return 0; |
238 | } | |
239 | ||
93e28226 | 240 | r = strv_push(sv, word); |
5256e00e TG |
241 | if (r < 0) |
242 | return log_oom(); | |
93e28226 SS |
243 | |
244 | word = NULL; | |
5256e00e TG |
245 | } |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
d2df0d0e TG |
250 | int config_parse_ifalias(const char *unit, |
251 | const char *filename, | |
252 | unsigned line, | |
253 | const char *section, | |
71a61510 | 254 | unsigned section_line, |
d2df0d0e TG |
255 | const char *lvalue, |
256 | int ltype, | |
257 | const char *rvalue, | |
258 | void *data, | |
259 | void *userdata) { | |
260 | ||
261 | char **s = data; | |
9c39eb5c | 262 | _cleanup_free_ char *n = NULL; |
d2df0d0e TG |
263 | |
264 | assert(filename); | |
265 | assert(lvalue); | |
266 | assert(rvalue); | |
267 | assert(data); | |
268 | ||
269 | n = strdup(rvalue); | |
270 | if (!n) | |
271 | return log_oom(); | |
272 | ||
273 | if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) { | |
12ca818f | 274 | log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue); |
d2df0d0e TG |
275 | return 0; |
276 | } | |
277 | ||
278 | free(*s); | |
9c39eb5c | 279 | if (*n) { |
d2df0d0e | 280 | *s = n; |
9c39eb5c TG |
281 | n = NULL; |
282 | } else | |
d2df0d0e | 283 | *s = NULL; |
d2df0d0e TG |
284 | |
285 | return 0; | |
286 | } | |
287 | ||
5fde13d7 TG |
288 | int config_parse_hwaddr(const char *unit, |
289 | const char *filename, | |
290 | unsigned line, | |
291 | const char *section, | |
71a61510 | 292 | unsigned section_line, |
5fde13d7 TG |
293 | const char *lvalue, |
294 | int ltype, | |
295 | const char *rvalue, | |
296 | void *data, | |
297 | void *userdata) { | |
298 | struct ether_addr **hwaddr = data; | |
299 | struct ether_addr *n; | |
9ed8b06c DKG |
300 | const char *start; |
301 | size_t offset; | |
5fde13d7 TG |
302 | int r; |
303 | ||
304 | assert(filename); | |
305 | assert(lvalue); | |
306 | assert(rvalue); | |
307 | assert(data); | |
308 | ||
a12fa420 | 309 | n = new0(struct ether_addr, 1); |
5fde13d7 TG |
310 | if (!n) |
311 | return log_oom(); | |
312 | ||
9ed8b06c DKG |
313 | start = rvalue + strspn(rvalue, WHITESPACE); |
314 | r = ether_addr_from_string(start, n, &offset); | |
315 | ||
316 | if (r || (start[offset + strspn(start + offset, WHITESPACE)] != '\0')) { | |
12ca818f | 317 | log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue); |
5fde13d7 TG |
318 | free(n); |
319 | return 0; | |
320 | } | |
321 | ||
322 | free(*hwaddr); | |
323 | *hwaddr = n; | |
324 | ||
325 | return 0; | |
326 | } | |
f5284182 | 327 | |
413708d1 VK |
328 | int config_parse_iaid(const char *unit, |
329 | const char *filename, | |
330 | unsigned line, | |
331 | const char *section, | |
332 | unsigned section_line, | |
333 | const char *lvalue, | |
334 | int ltype, | |
335 | const char *rvalue, | |
336 | void *data, | |
337 | void *userdata) { | |
338 | uint32_t iaid; | |
339 | int r; | |
340 | ||
341 | assert(filename); | |
342 | assert(lvalue); | |
343 | assert(rvalue); | |
344 | assert(data); | |
345 | ||
346 | r = safe_atou32(rvalue, &iaid); | |
347 | if (r < 0) { | |
b7f71444 VK |
348 | log_syntax(unit, LOG_ERR, filename, line, r, |
349 | "Unable to read IAID, ignoring assignment: %s", rvalue); | |
350 | return 0; | |
413708d1 VK |
351 | } |
352 | ||
353 | *((uint32_t *)data) = iaid; | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
f00ff0de DJL |
358 | int config_parse_bridge_port_priority( |
359 | const char *unit, | |
360 | const char *filename, | |
361 | unsigned line, | |
362 | const char *section, | |
363 | unsigned section_line, | |
364 | const char *lvalue, | |
365 | int ltype, | |
366 | const char *rvalue, | |
367 | void *data, | |
368 | void *userdata) { | |
369 | ||
370 | uint16_t i; | |
371 | int r; | |
372 | ||
373 | assert(filename); | |
374 | assert(lvalue); | |
375 | assert(rvalue); | |
376 | assert(data); | |
377 | ||
378 | r = safe_atou16(rvalue, &i); | |
379 | if (r < 0) { | |
380 | log_syntax(unit, LOG_ERR, filename, line, r, | |
381 | "Failed to parse bridge port priority, ignoring: %s", rvalue); | |
382 | return 0; | |
383 | } | |
384 | ||
385 | if (i > LINK_BRIDGE_PORT_PRIORITY_MAX) { | |
386 | log_syntax(unit, LOG_ERR, filename, line, r, | |
387 | "Bridge port priority is larger than maximum %u, ignoring: %s", LINK_BRIDGE_PORT_PRIORITY_MAX, rvalue); | |
388 | return 0; | |
389 | } | |
390 | ||
391 | *((uint16_t *)data) = i; | |
392 | ||
393 | return 0; | |
394 | } | |
395 | ||
396 | ||
b0e39c82 | 397 | void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { |
09bee74d TG |
398 | unsigned i; |
399 | ||
400 | assert(f); | |
09bee74d TG |
401 | assert(addresses); |
402 | assert(size); | |
403 | ||
09bee74d TG |
404 | for (i = 0; i < size; i++) |
405 | fprintf(f, "%s%s", inet_ntoa(addresses[i]), | |
406 | (i < (size - 1)) ? " ": ""); | |
09bee74d TG |
407 | } |
408 | ||
a2ba62c7 | 409 | int deserialize_in_addrs(struct in_addr **ret, const char *string) { |
09bee74d | 410 | _cleanup_free_ struct in_addr *addresses = NULL; |
a2ba62c7 | 411 | int size = 0; |
09bee74d TG |
412 | |
413 | assert(ret); | |
09bee74d TG |
414 | assert(string); |
415 | ||
93e28226 SS |
416 | for (;;) { |
417 | _cleanup_free_ char *word = NULL; | |
09bee74d TG |
418 | struct in_addr *new_addresses; |
419 | int r; | |
420 | ||
93e28226 SS |
421 | r = extract_first_word(&string, &word, NULL, 0); |
422 | if (r < 0) | |
423 | return r; | |
424 | if (r == 0) | |
425 | break; | |
426 | ||
62d74c78 | 427 | new_addresses = reallocarray(addresses, size + 1, sizeof(struct in_addr)); |
09bee74d TG |
428 | if (!new_addresses) |
429 | return -ENOMEM; | |
430 | else | |
431 | addresses = new_addresses; | |
432 | ||
93e28226 | 433 | r = inet_pton(AF_INET, word, &(addresses[size])); |
09bee74d TG |
434 | if (r <= 0) |
435 | continue; | |
436 | ||
313cefa1 | 437 | size++; |
09bee74d TG |
438 | } |
439 | ||
09bee74d TG |
440 | *ret = addresses; |
441 | addresses = NULL; | |
442 | ||
a2ba62c7 | 443 | return size; |
09bee74d TG |
444 | } |
445 | ||
1f152e4b | 446 | void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) { |
b729fa14 PF |
447 | unsigned i; |
448 | ||
449 | assert(f); | |
450 | assert(addresses); | |
451 | assert(size); | |
452 | ||
1f152e4b LP |
453 | for (i = 0; i < size; i++) { |
454 | char buffer[INET6_ADDRSTRLEN]; | |
455 | ||
456 | fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f); | |
457 | ||
458 | if (i < size - 1) | |
459 | fputc(' ', f); | |
460 | } | |
b729fa14 PF |
461 | } |
462 | ||
a2ba62c7 | 463 | int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { |
09bee74d | 464 | _cleanup_free_ struct in6_addr *addresses = NULL; |
a2ba62c7 | 465 | int size = 0; |
09bee74d TG |
466 | |
467 | assert(ret); | |
09bee74d TG |
468 | assert(string); |
469 | ||
93e28226 SS |
470 | for (;;) { |
471 | _cleanup_free_ char *word = NULL; | |
09bee74d TG |
472 | struct in6_addr *new_addresses; |
473 | int r; | |
474 | ||
93e28226 SS |
475 | r = extract_first_word(&string, &word, NULL, 0); |
476 | if (r < 0) | |
477 | return r; | |
478 | if (r == 0) | |
479 | break; | |
480 | ||
62d74c78 | 481 | new_addresses = reallocarray(addresses, size + 1, sizeof(struct in6_addr)); |
09bee74d TG |
482 | if (!new_addresses) |
483 | return -ENOMEM; | |
484 | else | |
485 | addresses = new_addresses; | |
486 | ||
93e28226 | 487 | r = inet_pton(AF_INET6, word, &(addresses[size])); |
09bee74d TG |
488 | if (r <= 0) |
489 | continue; | |
490 | ||
491 | size++; | |
492 | } | |
493 | ||
09bee74d TG |
494 | *ret = addresses; |
495 | addresses = NULL; | |
496 | ||
a2ba62c7 | 497 | return size; |
09bee74d | 498 | } |
e1ea665e | 499 | |
f8693fc7 | 500 | void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) { |
e1ea665e EY |
501 | unsigned i; |
502 | ||
503 | assert(f); | |
504 | assert(key); | |
505 | assert(routes); | |
506 | assert(size); | |
507 | ||
508 | fprintf(f, "%s=", key); | |
509 | ||
fbf7dcb5 | 510 | for (i = 0; i < size; i++) { |
f8693fc7 BG |
511 | struct in_addr dest, gw; |
512 | uint8_t length; | |
513 | ||
514 | assert_se(sd_dhcp_route_get_destination(routes[i], &dest) >= 0); | |
515 | assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0); | |
516 | assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0); | |
517 | ||
518 | fprintf(f, "%s/%" PRIu8, inet_ntoa(dest), length); | |
519 | fprintf(f, ",%s%s", inet_ntoa(gw), (i < (size - 1)) ? " ": ""); | |
fbf7dcb5 | 520 | } |
e1ea665e EY |
521 | |
522 | fputs("\n", f); | |
523 | } | |
524 | ||
525 | int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) { | |
526 | _cleanup_free_ struct sd_dhcp_route *routes = NULL; | |
527 | size_t size = 0, allocated = 0; | |
e1ea665e EY |
528 | |
529 | assert(ret); | |
530 | assert(ret_size); | |
531 | assert(ret_allocated); | |
532 | assert(string); | |
533 | ||
93e28226 SS |
534 | /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ |
535 | for (;;) { | |
536 | _cleanup_free_ char *word = NULL; | |
e1ea665e EY |
537 | char *tok, *tok_end; |
538 | unsigned n; | |
539 | int r; | |
540 | ||
93e28226 SS |
541 | r = extract_first_word(&string, &word, NULL, 0); |
542 | if (r < 0) | |
543 | return r; | |
544 | if (r == 0) | |
545 | break; | |
e1ea665e | 546 | |
93e28226 | 547 | if (!GREEDY_REALLOC(routes, allocated, size + 1)) |
31db0120 | 548 | return -ENOMEM; |
e1ea665e | 549 | |
93e28226 | 550 | tok = word; |
e1ea665e EY |
551 | |
552 | /* get the subnet */ | |
553 | tok_end = strchr(tok, '/'); | |
554 | if (!tok_end) | |
555 | continue; | |
556 | *tok_end = '\0'; | |
557 | ||
558 | r = inet_aton(tok, &routes[size].dst_addr); | |
559 | if (r == 0) | |
560 | continue; | |
561 | ||
562 | tok = tok_end + 1; | |
563 | ||
564 | /* get the prefixlen */ | |
565 | tok_end = strchr(tok, ','); | |
566 | if (!tok_end) | |
567 | continue; | |
568 | ||
569 | *tok_end = '\0'; | |
570 | ||
571 | r = safe_atou(tok, &n); | |
572 | if (r < 0 || n > 32) | |
573 | continue; | |
574 | ||
575 | routes[size].dst_prefixlen = (uint8_t) n; | |
576 | tok = tok_end + 1; | |
577 | ||
578 | /* get the gateway */ | |
579 | r = inet_aton(tok, &routes[size].gw_addr); | |
580 | if (r == 0) | |
581 | continue; | |
582 | ||
583 | size++; | |
584 | } | |
585 | ||
586 | *ret_size = size; | |
587 | *ret_allocated = allocated; | |
588 | *ret = routes; | |
589 | routes = NULL; | |
590 | ||
591 | return 0; | |
592 | } | |
a073309f | 593 | |
e4735228 | 594 | int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) { |
a073309f AC |
595 | _cleanup_free_ char *hex_buf = NULL; |
596 | ||
597 | assert(f); | |
598 | assert(key); | |
599 | assert(data); | |
600 | ||
601 | hex_buf = hexmem(data, size); | |
602 | if (hex_buf == NULL) | |
603 | return -ENOMEM; | |
604 | ||
605 | fprintf(f, "%s=%s\n", key, hex_buf); | |
606 | ||
607 | return 0; | |
608 | } | |
609 | ||
e4735228 | 610 | int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) { |
a073309f AC |
611 | assert(data); |
612 | assert(data_len); | |
613 | assert(string); | |
614 | ||
615 | if (strlen(string) % 2) | |
616 | return -EINVAL; | |
617 | ||
618 | return unhexmem(string, strlen(string), (void **)data, data_len); | |
619 | } |