chiark / gitweb /
networkd: add network_get_by_name
[elogind.git] / src / network / networkd-network.c
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
25 #include "path-util.h"
26 #include "conf-files.h"
27 #include "conf-parser.h"
28 #include "util.h"
29 #include "networkd.h"
30 #include "networkd-netdev.h"
31 #include "networkd-link.h"
32 #include "network-internal.h"
33
34 static int network_load_one(Manager *manager, const char *filename) {
35         _cleanup_network_free_ Network *network = NULL;
36         _cleanup_fclose_ FILE *file = NULL;
37         char *d;
38         Route *route;
39         Address *address;
40         int r;
41
42         assert(manager);
43         assert(filename);
44
45         file = fopen(filename, "re");
46         if (!file) {
47                 if (errno == ENOENT)
48                         return 0;
49                 else
50                         return -errno;
51         }
52
53         if (null_or_empty_fd(fileno(file))) {
54                 log_debug("Skipping empty file: %s", filename);
55                 return 0;
56         }
57
58         network = new0(Network, 1);
59         if (!network)
60                 return log_oom();
61
62         network->manager = manager;
63
64         LIST_HEAD_INIT(network->static_addresses);
65         LIST_HEAD_INIT(network->static_routes);
66         LIST_HEAD_INIT(network->static_fdb_entries);
67
68         network->stacked_netdevs = hashmap_new(&string_hash_ops);
69         if (!network->stacked_netdevs)
70                 return log_oom();
71
72         network->addresses_by_section = hashmap_new(NULL);
73         if (!network->addresses_by_section)
74                 return log_oom();
75
76         network->routes_by_section = hashmap_new(NULL);
77         if (!network->routes_by_section)
78                 return log_oom();
79
80         network->fdb_entries_by_section = hashmap_new(NULL);
81         if (!network->fdb_entries_by_section)
82                 return log_oom();
83
84         network->filename = strdup(filename);
85         if (!network->filename)
86                 return log_oom();
87
88         network->name = strdup(basename(filename));
89         if (!network->name)
90                 return log_oom();
91
92         d = strrchr(network->name, '.');
93         if (!d)
94                 return -EINVAL;
95
96         assert(streq(d, ".network"));
97
98         *d = '\0';
99
100         network->dhcp = ADDRESS_FAMILY_NO;
101         network->dhcp_ntp = true;
102         network->dhcp_dns = true;
103         network->dhcp_hostname = true;
104         network->dhcp_routes = true;
105         network->dhcp_sendhost = true;
106         network->dhcp_route_metric = DHCP_ROUTE_METRIC;
107
108         network->llmnr = LLMNR_SUPPORT_YES;
109
110         r = config_parse(NULL, filename, file,
111                          "Match\0"
112                          "Link\0"
113                          "Network\0"
114                          "Address\0"
115                          "Route\0"
116                          "DHCP\0"
117                          "DHCPv4\0"
118                          "Bridge\0"
119                          "BridgeFDB\0",
120                          config_item_perf_lookup, network_network_gperf_lookup,
121                          false, false, true, network);
122         if (r < 0)
123                 return r;
124
125         /* IPMasquerade=yes implies IPForward=yes */
126         if (network->ip_masquerade)
127                 network->ip_forward |= ADDRESS_FAMILY_IPV4;
128
129         LIST_PREPEND(networks, manager->networks, network);
130
131         r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
132         if (r < 0)
133                 return r;
134
135         r = hashmap_put(manager->networks_by_name, network->name, network);
136         if (r < 0)
137                 return r;
138
139         LIST_FOREACH(routes, route, network->static_routes) {
140                 if (!route->family) {
141                         log_warning("Route section without Gateway field configured in %s. "
142                                     "Ignoring", filename);
143                         return 0;
144                 }
145         }
146
147         LIST_FOREACH(addresses, address, network->static_addresses) {
148                 if (!address->family) {
149                         log_warning("Address section without Address field configured in %s. "
150                                     "Ignoring", filename);
151                         return 0;
152                 }
153         }
154
155         network = NULL;
156
157         return 0;
158 }
159
160 int network_load(Manager *manager) {
161         Network *network;
162         _cleanup_strv_free_ char **files = NULL;
163         char **f;
164         int r;
165
166         assert(manager);
167
168         while ((network = manager->networks))
169                 network_free(network);
170
171         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
172         if (r < 0)
173                 return log_error_errno(r, "Failed to enumerate network files: %m");
174
175         STRV_FOREACH_BACKWARDS(f, files) {
176                 r = network_load_one(manager, *f);
177                 if (r < 0)
178                         return r;
179         }
180
181         return 0;
182 }
183
184 void network_free(Network *network) {
185         NetDev *netdev;
186         Route *route;
187         Address *address;
188         FdbEntry *fdb_entry;
189         Iterator i;
190
191         if (!network)
192                 return;
193
194         free(network->filename);
195
196         free(network->match_mac);
197         free(network->match_path);
198         free(network->match_driver);
199         free(network->match_type);
200         free(network->match_name);
201
202         free(network->description);
203         free(network->dhcp_vendor_class_identifier);
204
205         free(network->mac);
206
207         strv_free(network->ntp);
208         strv_free(network->dns);
209         strv_free(network->domains);
210
211         netdev_unref(network->bridge);
212
213         netdev_unref(network->bond);
214
215         HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
216                 hashmap_remove(network->stacked_netdevs, netdev->ifname);
217                 netdev_unref(netdev);
218         }
219         hashmap_free(network->stacked_netdevs);
220
221         while ((route = network->static_routes))
222                 route_free(route);
223
224         while ((address = network->static_addresses))
225                 address_free(address);
226
227         while ((fdb_entry = network->static_fdb_entries))
228                 fdb_entry_free(fdb_entry);
229
230         hashmap_free(network->addresses_by_section);
231         hashmap_free(network->routes_by_section);
232         hashmap_free(network->fdb_entries_by_section);
233
234         if (network->manager) {
235                 if (network->manager->networks)
236                         LIST_REMOVE(networks, network->manager->networks, network);
237
238                 if (network->manager->networks_by_name)
239                         hashmap_remove(network->manager->networks_by_name, network->name);
240         }
241
242         free(network->name);
243
244         condition_free_list(network->match_host);
245         condition_free_list(network->match_virt);
246         condition_free_list(network->match_kernel);
247         condition_free_list(network->match_arch);
248
249         free(network);
250 }
251
252 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
253         Network *network;
254
255         assert(manager);
256         assert(name);
257         assert(ret);
258
259         network = hashmap_get(manager->networks_by_name, name);
260         if (!network)
261                 return -ENOENT;
262
263         *ret = network;
264
265         return 0;
266 }
267
268 int network_get(Manager *manager, struct udev_device *device,
269                 const char *ifname, const struct ether_addr *address,
270                 Network **ret) {
271         Network *network;
272
273         assert(manager);
274         assert(ret);
275
276         LIST_FOREACH(networks, network, manager->networks) {
277                 if (net_match_config(network->match_mac, network->match_path,
278                                      network->match_driver, network->match_type,
279                                      network->match_name, network->match_host,
280                                      network->match_virt, network->match_kernel,
281                                      network->match_arch,
282                                      address,
283                                      udev_device_get_property_value(device, "ID_PATH"),
284                                      udev_device_get_driver(udev_device_get_parent(device)),
285                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
286                                      udev_device_get_devtype(device),
287                                      ifname)) {
288                         if (network->match_name) {
289                                 const char *attr;
290                                 uint8_t name_assign_type = NET_NAME_UNKNOWN;
291
292                                 attr = udev_device_get_sysattr_value(device, "name_assign_type");
293                                 if (attr)
294                                         (void)safe_atou8(attr, &name_assign_type);
295
296                                 if (name_assign_type == NET_NAME_ENUM)
297                                         log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
298                                                     IFNAMSIZ, ifname, network->filename);
299                                 else
300                                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
301                         } else
302                                 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
303
304                         *ret = network;
305                         return 0;
306                 }
307         }
308
309         *ret = NULL;
310
311         return -ENOENT;
312 }
313
314 int network_apply(Manager *manager, Network *network, Link *link) {
315         int r;
316
317         link->network = network;
318
319         if (network->ipv4ll_route) {
320                 Route *route;
321
322                 r = route_new_static(network, 0, &route);
323                 if (r < 0)
324                         return r;
325
326                 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
327                 if (r == 0)
328                         return -EINVAL;
329                 if (r < 0)
330                         return -errno;
331
332                 route->family = AF_INET;
333                 route->dst_prefixlen = 16;
334                 route->scope = RT_SCOPE_LINK;
335                 route->metrics = IPV4LL_ROUTE_METRIC;
336                 route->protocol = RTPROT_STATIC;
337         }
338
339         if (network->dns || network->ntp) {
340                 r = link_save(link);
341                 if (r < 0)
342                         return r;
343         }
344
345         return 0;
346 }
347
348 int config_parse_netdev(const char *unit,
349                 const char *filename,
350                 unsigned line,
351                 const char *section,
352                 unsigned section_line,
353                 const char *lvalue,
354                 int ltype,
355                 const char *rvalue,
356                 void *data,
357                 void *userdata) {
358         Network *network = userdata;
359         _cleanup_free_ char *kind_string = NULL;
360         char *p;
361         NetDev *netdev;
362         NetDevKind kind;
363         int r;
364
365         assert(filename);
366         assert(lvalue);
367         assert(rvalue);
368         assert(data);
369
370         kind_string = strdup(lvalue);
371         if (!kind_string)
372                 return log_oom();
373
374         /* the keys are CamelCase versions of the kind */
375         for (p = kind_string; *p; p++)
376                 *p = tolower(*p);
377
378         kind = netdev_kind_from_string(kind_string);
379         if (kind == _NETDEV_KIND_INVALID) {
380                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
381                            "Invalid NetDev kind: %s", lvalue);
382                 return 0;
383         }
384
385         r = netdev_get(network->manager, rvalue, &netdev);
386         if (r < 0) {
387                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
388                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
389                 return 0;
390         }
391
392         if (netdev->kind != kind) {
393                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
394                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
395                 return 0;
396         }
397
398         switch (kind) {
399         case NETDEV_KIND_BRIDGE:
400                 network->bridge = netdev;
401
402                 break;
403         case NETDEV_KIND_BOND:
404                 network->bond = netdev;
405
406                 break;
407         case NETDEV_KIND_VLAN:
408         case NETDEV_KIND_MACVLAN:
409         case NETDEV_KIND_IPVLAN:
410         case NETDEV_KIND_VXLAN:
411                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
412                 if (r < 0) {
413                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
414                                    "Can not add VLAN '%s' to network: %s",
415                                    rvalue, strerror(-r));
416                         return 0;
417                 }
418
419                 break;
420         default:
421                 assert_not_reached("Can not parse NetDev");
422         }
423
424         netdev_ref(netdev);
425
426         return 0;
427 }
428
429 int config_parse_domains(const char *unit,
430                          const char *filename,
431                          unsigned line,
432                          const char *section,
433                          unsigned section_line,
434                          const char *lvalue,
435                          int ltype,
436                          const char *rvalue,
437                          void *data,
438                          void *userdata) {
439         Network *network = userdata;
440         char ***domains = data;
441         char **domain;
442         int r;
443
444         r = config_parse_strv(unit, filename, line, section, section_line,
445                               lvalue, ltype, rvalue, domains, userdata);
446         if (r < 0)
447                 return r;
448
449         strv_uniq(*domains);
450         network->wildcard_domain = !!strv_find(*domains, "*");
451
452         STRV_FOREACH(domain, *domains) {
453                 if (is_localhost(*domain))
454                         log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
455                 else if (!hostname_is_valid(*domain)) {
456                         if (!streq(*domain, "*"))
457                                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
458                 } else
459                         continue;
460
461                 strv_remove(*domains, *domain);
462
463                 /* We removed one entry, make sure we don't skip the next one */
464                 domain--;
465         }
466
467         return 0;
468 }
469
470 int config_parse_tunnel(const char *unit,
471                         const char *filename,
472                         unsigned line,
473                         const char *section,
474                         unsigned section_line,
475                         const char *lvalue,
476                         int ltype,
477                         const char *rvalue,
478                         void *data,
479                         void *userdata) {
480         Network *network = userdata;
481         NetDev *netdev;
482         int r;
483
484         assert(filename);
485         assert(lvalue);
486         assert(rvalue);
487         assert(data);
488
489         r = netdev_get(network->manager, rvalue, &netdev);
490         if (r < 0) {
491                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
492                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
493                 return 0;
494         }
495
496         if (netdev->kind != NETDEV_KIND_IPIP &&
497             netdev->kind != NETDEV_KIND_SIT &&
498             netdev->kind != NETDEV_KIND_GRE &&
499             netdev->kind != NETDEV_KIND_GRETAP &&
500             netdev->kind != NETDEV_KIND_IP6GRE &&
501             netdev->kind != NETDEV_KIND_IP6GRETAP &&
502             netdev->kind != NETDEV_KIND_VTI &&
503             netdev->kind != NETDEV_KIND_IP6TNL
504             ) {
505                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
506                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
507                 return 0;
508         }
509
510         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
511         if (r < 0) {
512                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
513                            "Can not add VLAN '%s' to network: %s",
514                            rvalue, strerror(-r));
515                 return 0;
516         }
517
518         netdev_ref(netdev);
519
520         return 0;
521 }
522
523 int config_parse_dhcp(
524                 const char* unit,
525                 const char *filename,
526                 unsigned line,
527                 const char *section,
528                 unsigned section_line,
529                 const char *lvalue,
530                 int ltype,
531                 const char *rvalue,
532                 void *data,
533                 void *userdata) {
534
535         AddressFamilyBoolean *dhcp = data, s;
536
537         assert(filename);
538         assert(lvalue);
539         assert(rvalue);
540         assert(data);
541
542         /* Note that this is mostly like
543          * config_parse_address_family_boolean(), except that it
544          * understands some old names for the enum values */
545
546         s = address_family_boolean_from_string(rvalue);
547         if (s < 0) {
548
549                 /* Previously, we had a slightly different enum here,
550                  * support its values for compatbility. */
551
552                 if (streq(rvalue, "none"))
553                         s = ADDRESS_FAMILY_NO;
554                 else if (streq(rvalue, "v4"))
555                         s = ADDRESS_FAMILY_IPV4;
556                 else if (streq(rvalue, "v6"))
557                         s = ADDRESS_FAMILY_IPV6;
558                 else if (streq(rvalue, "both"))
559                         s = ADDRESS_FAMILY_YES;
560                 else {
561                         log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
562                         return 0;
563                 }
564         }
565
566         *dhcp = s;
567         return 0;
568 }
569
570 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
571         [LLMNR_SUPPORT_NO] = "no",
572         [LLMNR_SUPPORT_YES] = "yes",
573         [LLMNR_SUPPORT_RESOLVE] = "resolve",
574 };
575
576 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
577
578 int config_parse_llmnr(
579                 const char* unit,
580                 const char *filename,
581                 unsigned line,
582                 const char *section,
583                 unsigned section_line,
584                 const char *lvalue,
585                 int ltype,
586                 const char *rvalue,
587                 void *data,
588                 void *userdata) {
589
590         LLMNRSupport *llmnr = data;
591         int k;
592
593         assert(filename);
594         assert(lvalue);
595         assert(rvalue);
596         assert(data);
597
598         /* Our enum shall be a superset of booleans, hence first try
599          * to parse as boolean, and then as enum */
600
601         k = parse_boolean(rvalue);
602         if (k > 0)
603                 *llmnr = LLMNR_SUPPORT_YES;
604         else if (k == 0)
605                 *llmnr = LLMNR_SUPPORT_NO;
606         else {
607                 LLMNRSupport s;
608
609                 s = llmnr_support_from_string(rvalue);
610                 if (s < 0){
611                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
612                         return 0;
613                 }
614
615                 *llmnr = s;
616         }
617
618         return 0;
619 }