]>
Commit | Line | Data |
---|---|---|
60f067b4 JS |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2013 Tom Gundersen <teg@jklm.no> | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <ctype.h> | |
23 | #include <net/if.h> | |
24 | ||
60f067b4 JS |
25 | #include "conf-files.h" |
26 | #include "conf-parser.h" | |
27 | #include "util.h" | |
e3bff60a | 28 | #include "hostname-util.h" |
e735f4d4 MP |
29 | #include "networkd.h" |
30 | #include "networkd-netdev.h" | |
31 | #include "networkd-link.h" | |
32 | #include "network-internal.h" | |
86f210e9 | 33 | #include "dns-domain.h" |
60f067b4 JS |
34 | |
35 | static int network_load_one(Manager *manager, const char *filename) { | |
36 | _cleanup_network_free_ Network *network = NULL; | |
37 | _cleanup_fclose_ FILE *file = NULL; | |
e735f4d4 | 38 | char *d; |
60f067b4 JS |
39 | Route *route; |
40 | Address *address; | |
41 | int r; | |
42 | ||
43 | assert(manager); | |
44 | assert(filename); | |
45 | ||
46 | file = fopen(filename, "re"); | |
47 | if (!file) { | |
48 | if (errno == ENOENT) | |
49 | return 0; | |
50 | else | |
51 | return -errno; | |
52 | } | |
53 | ||
5eef597e MP |
54 | if (null_or_empty_fd(fileno(file))) { |
55 | log_debug("Skipping empty file: %s", filename); | |
60f067b4 JS |
56 | return 0; |
57 | } | |
58 | ||
59 | network = new0(Network, 1); | |
60 | if (!network) | |
61 | return log_oom(); | |
62 | ||
63 | network->manager = manager; | |
64 | ||
65 | LIST_HEAD_INIT(network->static_addresses); | |
66 | LIST_HEAD_INIT(network->static_routes); | |
e735f4d4 | 67 | LIST_HEAD_INIT(network->static_fdb_entries); |
60f067b4 | 68 | |
5eef597e MP |
69 | network->stacked_netdevs = hashmap_new(&string_hash_ops); |
70 | if (!network->stacked_netdevs) | |
60f067b4 JS |
71 | return log_oom(); |
72 | ||
5eef597e | 73 | network->addresses_by_section = hashmap_new(NULL); |
60f067b4 JS |
74 | if (!network->addresses_by_section) |
75 | return log_oom(); | |
76 | ||
5eef597e | 77 | network->routes_by_section = hashmap_new(NULL); |
60f067b4 JS |
78 | if (!network->routes_by_section) |
79 | return log_oom(); | |
80 | ||
e735f4d4 MP |
81 | network->fdb_entries_by_section = hashmap_new(NULL); |
82 | if (!network->fdb_entries_by_section) | |
83 | return log_oom(); | |
84 | ||
60f067b4 JS |
85 | network->filename = strdup(filename); |
86 | if (!network->filename) | |
87 | return log_oom(); | |
88 | ||
e735f4d4 MP |
89 | network->name = strdup(basename(filename)); |
90 | if (!network->name) | |
91 | return log_oom(); | |
92 | ||
93 | d = strrchr(network->name, '.'); | |
94 | if (!d) | |
95 | return -EINVAL; | |
96 | ||
97 | assert(streq(d, ".network")); | |
98 | ||
99 | *d = '\0'; | |
100 | ||
101 | network->dhcp = ADDRESS_FAMILY_NO; | |
60f067b4 JS |
102 | network->dhcp_ntp = true; |
103 | network->dhcp_dns = true; | |
104 | network->dhcp_hostname = true; | |
e842803a MB |
105 | network->dhcp_routes = true; |
106 | network->dhcp_sendhost = true; | |
5eef597e | 107 | network->dhcp_route_metric = DHCP_ROUTE_METRIC; |
e3bff60a | 108 | network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID; |
60f067b4 | 109 | |
5eef597e MP |
110 | network->llmnr = LLMNR_SUPPORT_YES; |
111 | ||
e735f4d4 MP |
112 | network->link_local = ADDRESS_FAMILY_IPV6; |
113 | ||
5eef597e | 114 | r = config_parse(NULL, filename, file, |
f47781d8 MP |
115 | "Match\0" |
116 | "Link\0" | |
117 | "Network\0" | |
118 | "Address\0" | |
119 | "Route\0" | |
120 | "DHCP\0" | |
121 | "DHCPv4\0" | |
e735f4d4 MP |
122 | "Bridge\0" |
123 | "BridgeFDB\0", | |
5eef597e MP |
124 | config_item_perf_lookup, network_network_gperf_lookup, |
125 | false, false, true, network); | |
126 | if (r < 0) | |
60f067b4 | 127 | return r; |
60f067b4 | 128 | |
e735f4d4 MP |
129 | /* IPMasquerade=yes implies IPForward=yes */ |
130 | if (network->ip_masquerade) | |
131 | network->ip_forward |= ADDRESS_FAMILY_IPV4; | |
132 | ||
60f067b4 JS |
133 | LIST_PREPEND(networks, manager->networks, network); |
134 | ||
e735f4d4 MP |
135 | r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops); |
136 | if (r < 0) | |
137 | return r; | |
138 | ||
139 | r = hashmap_put(manager->networks_by_name, network->name, network); | |
140 | if (r < 0) | |
141 | return r; | |
142 | ||
60f067b4 JS |
143 | LIST_FOREACH(routes, route, network->static_routes) { |
144 | if (!route->family) { | |
145 | log_warning("Route section without Gateway field configured in %s. " | |
146 | "Ignoring", filename); | |
147 | return 0; | |
148 | } | |
149 | } | |
150 | ||
151 | LIST_FOREACH(addresses, address, network->static_addresses) { | |
152 | if (!address->family) { | |
153 | log_warning("Address section without Address field configured in %s. " | |
154 | "Ignoring", filename); | |
155 | return 0; | |
156 | } | |
157 | } | |
158 | ||
159 | network = NULL; | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | int network_load(Manager *manager) { | |
165 | Network *network; | |
166 | _cleanup_strv_free_ char **files = NULL; | |
167 | char **f; | |
168 | int r; | |
169 | ||
170 | assert(manager); | |
171 | ||
172 | while ((network = manager->networks)) | |
173 | network_free(network); | |
174 | ||
175 | r = conf_files_list_strv(&files, ".network", NULL, network_dirs); | |
f47781d8 MP |
176 | if (r < 0) |
177 | return log_error_errno(r, "Failed to enumerate network files: %m"); | |
60f067b4 JS |
178 | |
179 | STRV_FOREACH_BACKWARDS(f, files) { | |
180 | r = network_load_one(manager, *f); | |
181 | if (r < 0) | |
182 | return r; | |
183 | } | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | void network_free(Network *network) { | |
189 | NetDev *netdev; | |
190 | Route *route; | |
191 | Address *address; | |
e735f4d4 | 192 | FdbEntry *fdb_entry; |
60f067b4 JS |
193 | Iterator i; |
194 | ||
195 | if (!network) | |
196 | return; | |
197 | ||
198 | free(network->filename); | |
199 | ||
200 | free(network->match_mac); | |
e735f4d4 MP |
201 | strv_free(network->match_path); |
202 | strv_free(network->match_driver); | |
203 | strv_free(network->match_type); | |
204 | strv_free(network->match_name); | |
60f067b4 JS |
205 | |
206 | free(network->description); | |
5eef597e | 207 | free(network->dhcp_vendor_class_identifier); |
60f067b4 | 208 | |
f47781d8 MP |
209 | free(network->mac); |
210 | ||
5eef597e MP |
211 | strv_free(network->ntp); |
212 | strv_free(network->dns); | |
213 | strv_free(network->domains); | |
e3bff60a | 214 | strv_free(network->bind_carrier); |
60f067b4 JS |
215 | |
216 | netdev_unref(network->bridge); | |
217 | ||
218 | netdev_unref(network->bond); | |
219 | ||
5eef597e MP |
220 | HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) { |
221 | hashmap_remove(network->stacked_netdevs, netdev->ifname); | |
60f067b4 | 222 | netdev_unref(netdev); |
5eef597e MP |
223 | } |
224 | hashmap_free(network->stacked_netdevs); | |
e842803a | 225 | |
60f067b4 JS |
226 | while ((route = network->static_routes)) |
227 | route_free(route); | |
228 | ||
229 | while ((address = network->static_addresses)) | |
230 | address_free(address); | |
231 | ||
e735f4d4 MP |
232 | while ((fdb_entry = network->static_fdb_entries)) |
233 | fdb_entry_free(fdb_entry); | |
234 | ||
60f067b4 JS |
235 | hashmap_free(network->addresses_by_section); |
236 | hashmap_free(network->routes_by_section); | |
e735f4d4 MP |
237 | hashmap_free(network->fdb_entries_by_section); |
238 | ||
239 | if (network->manager) { | |
240 | if (network->manager->networks) | |
241 | LIST_REMOVE(networks, network->manager->networks, network); | |
242 | ||
243 | if (network->manager->networks_by_name) | |
244 | hashmap_remove(network->manager->networks_by_name, network->name); | |
245 | } | |
60f067b4 | 246 | |
e735f4d4 | 247 | free(network->name); |
60f067b4 JS |
248 | |
249 | condition_free_list(network->match_host); | |
250 | condition_free_list(network->match_virt); | |
251 | condition_free_list(network->match_kernel); | |
252 | condition_free_list(network->match_arch); | |
253 | ||
254 | free(network); | |
255 | } | |
256 | ||
e735f4d4 MP |
257 | int network_get_by_name(Manager *manager, const char *name, Network **ret) { |
258 | Network *network; | |
259 | ||
260 | assert(manager); | |
261 | assert(name); | |
262 | assert(ret); | |
263 | ||
264 | network = hashmap_get(manager->networks_by_name, name); | |
265 | if (!network) | |
266 | return -ENOENT; | |
267 | ||
268 | *ret = network; | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
60f067b4 JS |
273 | int network_get(Manager *manager, struct udev_device *device, |
274 | const char *ifname, const struct ether_addr *address, | |
275 | Network **ret) { | |
276 | Network *network; | |
e3bff60a MP |
277 | struct udev_device *parent; |
278 | const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL; | |
60f067b4 JS |
279 | |
280 | assert(manager); | |
281 | assert(ret); | |
282 | ||
e3bff60a MP |
283 | if (device) { |
284 | path = udev_device_get_property_value(device, "ID_PATH"); | |
285 | ||
286 | parent = udev_device_get_parent(device); | |
287 | if (parent) | |
288 | parent_driver = udev_device_get_driver(parent); | |
289 | ||
290 | driver = udev_device_get_property_value(device, "ID_NET_DRIVER"); | |
291 | ||
292 | devtype = udev_device_get_devtype(device); | |
293 | } | |
294 | ||
60f067b4 JS |
295 | LIST_FOREACH(networks, network, manager->networks) { |
296 | if (net_match_config(network->match_mac, network->match_path, | |
297 | network->match_driver, network->match_type, | |
298 | network->match_name, network->match_host, | |
299 | network->match_virt, network->match_kernel, | |
300 | network->match_arch, | |
e3bff60a MP |
301 | address, path, parent_driver, driver, |
302 | devtype, ifname)) { | |
303 | if (network->match_name && device) { | |
f47781d8 MP |
304 | const char *attr; |
305 | uint8_t name_assign_type = NET_NAME_UNKNOWN; | |
306 | ||
307 | attr = udev_device_get_sysattr_value(device, "name_assign_type"); | |
308 | if (attr) | |
e3bff60a | 309 | (void) safe_atou8(attr, &name_assign_type); |
f47781d8 MP |
310 | |
311 | if (name_assign_type == NET_NAME_ENUM) | |
312 | log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname", | |
313 | IFNAMSIZ, ifname, network->filename); | |
314 | else | |
315 | log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename); | |
316 | } else | |
317 | log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename); | |
318 | ||
60f067b4 JS |
319 | *ret = network; |
320 | return 0; | |
321 | } | |
322 | } | |
323 | ||
324 | *ret = NULL; | |
325 | ||
326 | return -ENOENT; | |
327 | } | |
328 | ||
329 | int network_apply(Manager *manager, Network *network, Link *link) { | |
330 | int r; | |
331 | ||
332 | link->network = network; | |
333 | ||
5eef597e MP |
334 | if (network->ipv4ll_route) { |
335 | Route *route; | |
336 | ||
337 | r = route_new_static(network, 0, &route); | |
338 | if (r < 0) | |
339 | return r; | |
340 | ||
341 | r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in); | |
342 | if (r == 0) | |
343 | return -EINVAL; | |
344 | if (r < 0) | |
345 | return -errno; | |
346 | ||
347 | route->family = AF_INET; | |
348 | route->dst_prefixlen = 16; | |
349 | route->scope = RT_SCOPE_LINK; | |
350 | route->metrics = IPV4LL_ROUTE_METRIC; | |
351 | route->protocol = RTPROT_STATIC; | |
352 | } | |
353 | ||
60f067b4 JS |
354 | if (network->dns || network->ntp) { |
355 | r = link_save(link); | |
356 | if (r < 0) | |
357 | return r; | |
358 | } | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | int config_parse_netdev(const char *unit, | |
364 | const char *filename, | |
365 | unsigned line, | |
366 | const char *section, | |
367 | unsigned section_line, | |
368 | const char *lvalue, | |
369 | int ltype, | |
370 | const char *rvalue, | |
371 | void *data, | |
372 | void *userdata) { | |
373 | Network *network = userdata; | |
374 | _cleanup_free_ char *kind_string = NULL; | |
375 | char *p; | |
376 | NetDev *netdev; | |
377 | NetDevKind kind; | |
378 | int r; | |
379 | ||
380 | assert(filename); | |
381 | assert(lvalue); | |
382 | assert(rvalue); | |
383 | assert(data); | |
384 | ||
385 | kind_string = strdup(lvalue); | |
386 | if (!kind_string) | |
387 | return log_oom(); | |
388 | ||
389 | /* the keys are CamelCase versions of the kind */ | |
390 | for (p = kind_string; *p; p++) | |
391 | *p = tolower(*p); | |
392 | ||
393 | kind = netdev_kind_from_string(kind_string); | |
394 | if (kind == _NETDEV_KIND_INVALID) { | |
395 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
396 | "Invalid NetDev kind: %s", lvalue); | |
397 | return 0; | |
398 | } | |
399 | ||
400 | r = netdev_get(network->manager, rvalue, &netdev); | |
401 | if (r < 0) { | |
402 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
403 | "%s could not be found, ignoring assignment: %s", lvalue, rvalue); | |
404 | return 0; | |
405 | } | |
406 | ||
407 | if (netdev->kind != kind) { | |
408 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
409 | "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue); | |
410 | return 0; | |
411 | } | |
412 | ||
413 | switch (kind) { | |
414 | case NETDEV_KIND_BRIDGE: | |
415 | network->bridge = netdev; | |
416 | ||
417 | break; | |
418 | case NETDEV_KIND_BOND: | |
419 | network->bond = netdev; | |
420 | ||
421 | break; | |
422 | case NETDEV_KIND_VLAN: | |
60f067b4 | 423 | case NETDEV_KIND_MACVLAN: |
e735f4d4 | 424 | case NETDEV_KIND_IPVLAN: |
e842803a | 425 | case NETDEV_KIND_VXLAN: |
5eef597e | 426 | r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); |
e842803a MB |
427 | if (r < 0) { |
428 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
e3bff60a MP |
429 | "Can not add VLAN '%s' to network: %m", |
430 | rvalue); | |
e842803a MB |
431 | return 0; |
432 | } | |
433 | ||
60f067b4 JS |
434 | break; |
435 | default: | |
436 | assert_not_reached("Can not parse NetDev"); | |
437 | } | |
438 | ||
439 | netdev_ref(netdev); | |
440 | ||
441 | return 0; | |
442 | } | |
443 | ||
5eef597e MP |
444 | int config_parse_domains(const char *unit, |
445 | const char *filename, | |
446 | unsigned line, | |
447 | const char *section, | |
448 | unsigned section_line, | |
449 | const char *lvalue, | |
450 | int ltype, | |
451 | const char *rvalue, | |
452 | void *data, | |
453 | void *userdata) { | |
454 | Network *network = userdata; | |
455 | char ***domains = data; | |
456 | char **domain; | |
457 | int r; | |
458 | ||
459 | r = config_parse_strv(unit, filename, line, section, section_line, | |
460 | lvalue, ltype, rvalue, domains, userdata); | |
461 | if (r < 0) | |
462 | return r; | |
463 | ||
464 | strv_uniq(*domains); | |
465 | network->wildcard_domain = !!strv_find(*domains, "*"); | |
466 | ||
467 | STRV_FOREACH(domain, *domains) { | |
468 | if (is_localhost(*domain)) | |
469 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain); | |
86f210e9 MP |
470 | else { |
471 | r = dns_name_is_valid(*domain); | |
472 | if (r <= 0 && !streq(*domain, "*")) { | |
473 | if (r < 0) | |
474 | log_error_errno(r, "Failed to validate domain name: %s: %m", *domain); | |
475 | if (r == 0) | |
476 | log_warning("Domain name is not valid, ignoring assignment: %s", *domain); | |
477 | } else | |
478 | continue; | |
479 | } | |
5eef597e MP |
480 | |
481 | strv_remove(*domains, *domain); | |
482 | ||
483 | /* We removed one entry, make sure we don't skip the next one */ | |
484 | domain--; | |
485 | } | |
486 | ||
487 | return 0; | |
488 | } | |
489 | ||
60f067b4 JS |
490 | int config_parse_tunnel(const char *unit, |
491 | const char *filename, | |
492 | unsigned line, | |
493 | const char *section, | |
494 | unsigned section_line, | |
495 | const char *lvalue, | |
496 | int ltype, | |
497 | const char *rvalue, | |
498 | void *data, | |
499 | void *userdata) { | |
500 | Network *network = userdata; | |
501 | NetDev *netdev; | |
502 | int r; | |
503 | ||
504 | assert(filename); | |
505 | assert(lvalue); | |
506 | assert(rvalue); | |
507 | assert(data); | |
508 | ||
509 | r = netdev_get(network->manager, rvalue, &netdev); | |
510 | if (r < 0) { | |
e3bff60a | 511 | log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue); |
60f067b4 JS |
512 | return 0; |
513 | } | |
514 | ||
515 | if (netdev->kind != NETDEV_KIND_IPIP && | |
516 | netdev->kind != NETDEV_KIND_SIT && | |
517 | netdev->kind != NETDEV_KIND_GRE && | |
e735f4d4 MP |
518 | netdev->kind != NETDEV_KIND_GRETAP && |
519 | netdev->kind != NETDEV_KIND_IP6GRE && | |
520 | netdev->kind != NETDEV_KIND_IP6GRETAP && | |
521 | netdev->kind != NETDEV_KIND_VTI && | |
e3bff60a | 522 | netdev->kind != NETDEV_KIND_VTI6 && |
e735f4d4 MP |
523 | netdev->kind != NETDEV_KIND_IP6TNL |
524 | ) { | |
60f067b4 JS |
525 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
526 | "NetDev is not a tunnel, ignoring assignment: %s", rvalue); | |
527 | return 0; | |
528 | } | |
529 | ||
5eef597e MP |
530 | r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); |
531 | if (r < 0) { | |
e3bff60a | 532 | log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue); |
5eef597e MP |
533 | return 0; |
534 | } | |
535 | ||
536 | netdev_ref(netdev); | |
537 | ||
538 | return 0; | |
539 | } | |
540 | ||
e735f4d4 MP |
541 | int config_parse_ipv4ll( |
542 | const char* unit, | |
543 | const char *filename, | |
544 | unsigned line, | |
545 | const char *section, | |
546 | unsigned section_line, | |
547 | const char *lvalue, | |
548 | int ltype, | |
549 | const char *rvalue, | |
550 | void *data, | |
551 | void *userdata) { | |
5eef597e | 552 | |
e735f4d4 MP |
553 | AddressFamilyBoolean *link_local = data; |
554 | ||
555 | assert(filename); | |
556 | assert(lvalue); | |
557 | assert(rvalue); | |
558 | assert(data); | |
559 | ||
560 | /* Note that this is mostly like | |
561 | * config_parse_address_family_boolean(), except that it | |
562 | * applies only to IPv4 */ | |
563 | ||
564 | if (parse_boolean(rvalue)) | |
565 | *link_local |= ADDRESS_FAMILY_IPV4; | |
566 | else | |
567 | *link_local &= ~ADDRESS_FAMILY_IPV4; | |
568 | ||
569 | return 0; | |
570 | } | |
5eef597e MP |
571 | |
572 | int config_parse_dhcp( | |
573 | const char* unit, | |
574 | const char *filename, | |
575 | unsigned line, | |
576 | const char *section, | |
577 | unsigned section_line, | |
578 | const char *lvalue, | |
579 | int ltype, | |
580 | const char *rvalue, | |
581 | void *data, | |
582 | void *userdata) { | |
583 | ||
e735f4d4 | 584 | AddressFamilyBoolean *dhcp = data, s; |
5eef597e MP |
585 | |
586 | assert(filename); | |
587 | assert(lvalue); | |
588 | assert(rvalue); | |
589 | assert(data); | |
590 | ||
e735f4d4 MP |
591 | /* Note that this is mostly like |
592 | * config_parse_address_family_boolean(), except that it | |
593 | * understands some old names for the enum values */ | |
594 | ||
595 | s = address_family_boolean_from_string(rvalue); | |
596 | if (s < 0) { | |
597 | ||
598 | /* Previously, we had a slightly different enum here, | |
599 | * support its values for compatbility. */ | |
600 | ||
601 | if (streq(rvalue, "none")) | |
602 | s = ADDRESS_FAMILY_NO; | |
603 | else if (streq(rvalue, "v4")) | |
604 | s = ADDRESS_FAMILY_IPV4; | |
605 | else if (streq(rvalue, "v6")) | |
606 | s = ADDRESS_FAMILY_IPV6; | |
607 | else if (streq(rvalue, "both")) | |
608 | s = ADDRESS_FAMILY_YES; | |
609 | else { | |
610 | log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue); | |
5eef597e MP |
611 | return 0; |
612 | } | |
5eef597e MP |
613 | } |
614 | ||
e735f4d4 | 615 | *dhcp = s; |
5eef597e MP |
616 | return 0; |
617 | } | |
618 | ||
e3bff60a MP |
619 | static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { |
620 | [DHCP_CLIENT_ID_MAC] = "mac", | |
621 | [DHCP_CLIENT_ID_DUID] = "duid" | |
622 | }; | |
623 | ||
624 | DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier); | |
625 | DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type"); | |
626 | ||
5eef597e MP |
627 | static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = { |
628 | [LLMNR_SUPPORT_NO] = "no", | |
629 | [LLMNR_SUPPORT_YES] = "yes", | |
630 | [LLMNR_SUPPORT_RESOLVE] = "resolve", | |
631 | }; | |
632 | ||
633 | DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport); | |
634 | ||
635 | int config_parse_llmnr( | |
636 | const char* unit, | |
637 | const char *filename, | |
638 | unsigned line, | |
639 | const char *section, | |
640 | unsigned section_line, | |
641 | const char *lvalue, | |
642 | int ltype, | |
643 | const char *rvalue, | |
644 | void *data, | |
645 | void *userdata) { | |
646 | ||
647 | LLMNRSupport *llmnr = data; | |
648 | int k; | |
649 | ||
650 | assert(filename); | |
651 | assert(lvalue); | |
652 | assert(rvalue); | |
e735f4d4 | 653 | assert(llmnr); |
5eef597e MP |
654 | |
655 | /* Our enum shall be a superset of booleans, hence first try | |
656 | * to parse as boolean, and then as enum */ | |
657 | ||
658 | k = parse_boolean(rvalue); | |
659 | if (k > 0) | |
660 | *llmnr = LLMNR_SUPPORT_YES; | |
661 | else if (k == 0) | |
662 | *llmnr = LLMNR_SUPPORT_NO; | |
663 | else { | |
664 | LLMNRSupport s; | |
665 | ||
666 | s = llmnr_support_from_string(rvalue); | |
667 | if (s < 0){ | |
668 | log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue); | |
669 | return 0; | |
670 | } | |
671 | ||
672 | *llmnr = s; | |
673 | } | |
60f067b4 JS |
674 | |
675 | return 0; | |
676 | } | |
e735f4d4 MP |
677 | |
678 | int config_parse_ipv6token( | |
679 | const char* unit, | |
680 | const char *filename, | |
681 | unsigned line, | |
682 | const char *section, | |
683 | unsigned section_line, | |
684 | const char *lvalue, | |
685 | int ltype, | |
686 | const char *rvalue, | |
687 | void *data, | |
688 | void *userdata) { | |
689 | ||
690 | union in_addr_union buffer; | |
691 | struct in6_addr *token = data; | |
692 | int r; | |
693 | ||
694 | assert(filename); | |
695 | assert(lvalue); | |
696 | assert(rvalue); | |
697 | assert(token); | |
698 | ||
699 | r = in_addr_from_string(AF_INET6, rvalue, &buffer); | |
700 | if (r < 0) { | |
e3bff60a | 701 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue); |
e735f4d4 MP |
702 | return 0; |
703 | } | |
704 | ||
705 | r = in_addr_is_null(AF_INET6, &buffer); | |
706 | if (r < 0) { | |
e3bff60a | 707 | log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue); |
e735f4d4 MP |
708 | return 0; |
709 | } | |
710 | ||
711 | if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) { | |
712 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue); | |
713 | return 0; | |
714 | } | |
715 | ||
716 | *token = buffer.in6; | |
717 | ||
718 | return 0; | |
719 | } | |
86f210e9 MP |
720 | |
721 | int config_parse_address_family_boolean_with_kernel( | |
722 | const char* unit, | |
723 | const char *filename, | |
724 | unsigned line, | |
725 | const char *section, | |
726 | unsigned section_line, | |
727 | const char *lvalue, | |
728 | int ltype, | |
729 | const char *rvalue, | |
730 | void *data, | |
731 | void *userdata) { | |
732 | ||
733 | AddressFamilyBoolean *fwd = data, s; | |
734 | ||
735 | assert(filename); | |
736 | assert(lvalue); | |
737 | assert(rvalue); | |
738 | assert(data); | |
739 | ||
740 | s = address_family_boolean_from_string(rvalue); | |
741 | if (s < 0) { | |
742 | if (streq(rvalue, "kernel")) | |
743 | s = _ADDRESS_FAMILY_BOOLEAN_INVALID; | |
744 | else { | |
745 | log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding option, ignoring: %s", rvalue); | |
746 | return 0; | |
747 | } | |
748 | } | |
749 | ||
750 | *fwd = s; | |
751 | ||
752 | return 0; | |
753 | } |