chiark / gitweb /
networkd: monopolize in_addr utility functions in shared/in-addr-util.h
[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 "networkd.h"
26 #include "networkd-netdev.h"
27 #include "network-internal.h"
28 #include "path-util.h"
29 #include "conf-files.h"
30 #include "conf-parser.h"
31 #include "util.h"
32
33 static int network_load_one(Manager *manager, const char *filename) {
34         _cleanup_network_free_ Network *network = NULL;
35         _cleanup_fclose_ FILE *file = NULL;
36         Route *route;
37         Address *address;
38         int r;
39
40         assert(manager);
41         assert(filename);
42
43         file = fopen(filename, "re");
44         if (!file) {
45                 if (errno == ENOENT)
46                         return 0;
47                 else
48                         return -errno;
49         }
50
51         if (null_or_empty_fd(fileno(file))) {
52                 log_debug("Skipping empty file: %s", filename);
53                 return 0;
54         }
55
56         network = new0(Network, 1);
57         if (!network)
58                 return log_oom();
59
60         network->manager = manager;
61
62         LIST_HEAD_INIT(network->static_addresses);
63         LIST_HEAD_INIT(network->static_routes);
64
65         network->stacked_netdevs = hashmap_new(string_hash_func, string_compare_func);
66         if (!network->stacked_netdevs)
67                 return log_oom();
68
69         network->addresses_by_section = hashmap_new(NULL, NULL);
70         if (!network->addresses_by_section)
71                 return log_oom();
72
73         network->routes_by_section = hashmap_new(NULL, NULL);
74         if (!network->routes_by_section)
75                 return log_oom();
76
77         network->filename = strdup(filename);
78         if (!network->filename)
79                 return log_oom();
80
81         network->ipv4ll_route = true;
82
83         network->dhcp = DHCP_SUPPORT_NONE;
84         network->dhcp_ntp = true;
85         network->dhcp_dns = true;
86         network->dhcp_hostname = true;
87         network->dhcp_domainname = true;
88         network->dhcp_routes = true;
89         network->dhcp_sendhost = true;
90
91         network->llmnr = LLMNR_SUPPORT_YES;
92
93         r = config_parse(NULL, filename, file,
94                          "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
95                          config_item_perf_lookup, network_network_gperf_lookup,
96                          false, false, true, network);
97         if (r < 0)
98                 return r;
99
100         LIST_PREPEND(networks, manager->networks, network);
101
102         LIST_FOREACH(routes, route, network->static_routes) {
103                 if (!route->family) {
104                         log_warning("Route section without Gateway field configured in %s. "
105                                     "Ignoring", filename);
106                         return 0;
107                 }
108         }
109
110         LIST_FOREACH(addresses, address, network->static_addresses) {
111                 if (!address->family) {
112                         log_warning("Address section without Address field configured in %s. "
113                                     "Ignoring", filename);
114                         return 0;
115                 }
116         }
117
118         network = NULL;
119
120         return 0;
121 }
122
123 int network_load(Manager *manager) {
124         Network *network;
125         _cleanup_strv_free_ char **files = NULL;
126         char **f;
127         int r;
128
129         assert(manager);
130
131         while ((network = manager->networks))
132                 network_free(network);
133
134         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
135         if (r < 0) {
136                 log_error("Failed to enumerate network files: %s", strerror(-r));
137                 return r;
138         }
139
140         STRV_FOREACH_BACKWARDS(f, files) {
141                 r = network_load_one(manager, *f);
142                 if (r < 0)
143                         return r;
144         }
145
146         return 0;
147 }
148
149 void network_free(Network *network) {
150         NetDev *netdev;
151         Route *route;
152         Address *address;
153         Iterator i;
154
155         if (!network)
156                 return;
157
158         free(network->filename);
159
160         free(network->match_mac);
161         free(network->match_path);
162         free(network->match_driver);
163         free(network->match_type);
164         free(network->match_name);
165
166         free(network->description);
167         free(network->dhcp_vendor_class_identifier);
168
169         strv_free(network->ntp);
170         strv_free(network->dns);
171
172         netdev_unref(network->bridge);
173
174         netdev_unref(network->bond);
175
176         HASHMAP_FOREACH(netdev, network->stacked_netdevs, i)
177                 netdev_unref(netdev);
178         hashmap_free(network->stacked_netdevs);
179
180         while ((route = network->static_routes))
181                 route_free(route);
182
183         while ((address = network->static_addresses))
184                 address_free(address);
185
186         hashmap_free(network->addresses_by_section);
187         hashmap_free(network->routes_by_section);
188
189         if (network->manager && network->manager->networks)
190                 LIST_REMOVE(networks, network->manager->networks, network);
191
192         condition_free_list(network->match_host);
193         condition_free_list(network->match_virt);
194         condition_free_list(network->match_kernel);
195         condition_free_list(network->match_arch);
196
197         free(network);
198 }
199
200 int network_get(Manager *manager, struct udev_device *device,
201                 const char *ifname, const struct ether_addr *address,
202                 Network **ret) {
203         Network *network;
204
205         assert(manager);
206         assert(ret);
207
208         LIST_FOREACH(networks, network, manager->networks) {
209                 if (net_match_config(network->match_mac, network->match_path,
210                                      network->match_driver, network->match_type,
211                                      network->match_name, network->match_host,
212                                      network->match_virt, network->match_kernel,
213                                      network->match_arch,
214                                      address,
215                                      udev_device_get_property_value(device, "ID_PATH"),
216                                      udev_device_get_driver(udev_device_get_parent(device)),
217                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
218                                      udev_device_get_devtype(device),
219                                      ifname)) {
220                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
221                                   network->filename);
222                         *ret = network;
223                         return 0;
224                 }
225         }
226
227         *ret = NULL;
228
229         return -ENOENT;
230 }
231
232 int network_apply(Manager *manager, Network *network, Link *link) {
233         int r;
234
235         link->network = network;
236
237         if (network->ipv4ll_route) {
238                 Route *route;
239
240                 r = route_new_static(network, 0, &route);
241                 if (r < 0)
242                         return r;
243
244                 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
245                 if (r == 0)
246                         return -EINVAL;
247                 if (r < 0)
248                         return -errno;
249
250                 route->family = AF_INET;
251                 route->dst_prefixlen = 16;
252                 route->scope = RT_SCOPE_LINK;
253                 route->metrics = IPV4LL_ROUTE_METRIC;
254                 route->protocol = RTPROT_STATIC;
255         }
256
257         if (network->dns || network->ntp) {
258                 r = link_save(link);
259                 if (r < 0)
260                         return r;
261         }
262
263         return 0;
264 }
265
266 int config_parse_netdev(const char *unit,
267                 const char *filename,
268                 unsigned line,
269                 const char *section,
270                 unsigned section_line,
271                 const char *lvalue,
272                 int ltype,
273                 const char *rvalue,
274                 void *data,
275                 void *userdata) {
276         Network *network = userdata;
277         _cleanup_free_ char *kind_string = NULL;
278         char *p;
279         NetDev *netdev;
280         NetDevKind kind;
281         int r;
282
283         assert(filename);
284         assert(lvalue);
285         assert(rvalue);
286         assert(data);
287
288         kind_string = strdup(lvalue);
289         if (!kind_string)
290                 return log_oom();
291
292         /* the keys are CamelCase versions of the kind */
293         for (p = kind_string; *p; p++)
294                 *p = tolower(*p);
295
296         kind = netdev_kind_from_string(kind_string);
297         if (kind == _NETDEV_KIND_INVALID) {
298                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
299                            "Invalid NetDev kind: %s", lvalue);
300                 return 0;
301         }
302
303         r = netdev_get(network->manager, rvalue, &netdev);
304         if (r < 0) {
305                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
306                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
307                 return 0;
308         }
309
310         if (netdev->kind != kind) {
311                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
312                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
313                 return 0;
314         }
315
316         switch (kind) {
317         case NETDEV_KIND_BRIDGE:
318                 network->bridge = netdev;
319
320                 break;
321         case NETDEV_KIND_BOND:
322                 network->bond = netdev;
323
324                 break;
325         case NETDEV_KIND_VLAN:
326         case NETDEV_KIND_MACVLAN:
327         case NETDEV_KIND_VXLAN:
328                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
329                 if (r < 0) {
330                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
331                                    "Can not add VLAN '%s' to network: %s",
332                                    rvalue, strerror(-r));
333                         return 0;
334                 }
335
336                 break;
337         default:
338                 assert_not_reached("Can not parse NetDev");
339         }
340
341         netdev_ref(netdev);
342
343         return 0;
344 }
345
346 int config_parse_tunnel(const char *unit,
347                         const char *filename,
348                         unsigned line,
349                         const char *section,
350                         unsigned section_line,
351                         const char *lvalue,
352                         int ltype,
353                         const char *rvalue,
354                         void *data,
355                         void *userdata) {
356         Network *network = userdata;
357         NetDev *netdev;
358         int r;
359
360         assert(filename);
361         assert(lvalue);
362         assert(rvalue);
363         assert(data);
364
365         r = netdev_get(network->manager, rvalue, &netdev);
366         if (r < 0) {
367                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
368                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
369                 return 0;
370         }
371
372         if (netdev->kind != NETDEV_KIND_IPIP &&
373             netdev->kind != NETDEV_KIND_SIT &&
374             netdev->kind != NETDEV_KIND_GRE &&
375             netdev->kind != NETDEV_KIND_VTI) {
376                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
377                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
378                 return 0;
379         }
380
381         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
382         if (r < 0) {
383                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
384                            "Can not add VLAN '%s' to network: %s",
385                            rvalue, strerror(-r));
386                 return 0;
387         }
388
389         netdev_ref(netdev);
390
391         return 0;
392 }
393
394 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
395         [DHCP_SUPPORT_NONE] = "none",
396         [DHCP_SUPPORT_BOTH] = "both",
397         [DHCP_SUPPORT_V4] = "v4",
398         [DHCP_SUPPORT_V6] = "v6",
399 };
400
401 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
402
403 int config_parse_dhcp(
404                 const char* unit,
405                 const char *filename,
406                 unsigned line,
407                 const char *section,
408                 unsigned section_line,
409                 const char *lvalue,
410                 int ltype,
411                 const char *rvalue,
412                 void *data,
413                 void *userdata) {
414
415         DHCPSupport *dhcp = data;
416         int k;
417
418         assert(filename);
419         assert(lvalue);
420         assert(rvalue);
421         assert(data);
422
423         /* Our enum shall be a superset of booleans, hence first try
424          * to parse as boolean, and then as enum */
425
426         k = parse_boolean(rvalue);
427         if (k > 0)
428                 *dhcp = DHCP_SUPPORT_BOTH;
429         else if (k == 0)
430                 *dhcp = DHCP_SUPPORT_NONE;
431         else {
432                 DHCPSupport s;
433
434                 s = dhcp_support_from_string(rvalue);
435                 if (s < 0){
436                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
437                         return 0;
438                 }
439
440                 *dhcp = s;
441         }
442
443         return 0;
444 }
445
446 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
447         [LLMNR_SUPPORT_NO] = "no",
448         [LLMNR_SUPPORT_YES] = "yes",
449         [LLMNR_SUPPORT_RESOLVE] = "resolve",
450 };
451
452 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
453
454 int config_parse_llmnr(
455                 const char* unit,
456                 const char *filename,
457                 unsigned line,
458                 const char *section,
459                 unsigned section_line,
460                 const char *lvalue,
461                 int ltype,
462                 const char *rvalue,
463                 void *data,
464                 void *userdata) {
465
466         LLMNRSupport *llmnr = data;
467         int k;
468
469         assert(filename);
470         assert(lvalue);
471         assert(rvalue);
472         assert(data);
473
474         /* Our enum shall be a superset of booleans, hence first try
475          * to parse as boolean, and then as enum */
476
477         k = parse_boolean(rvalue);
478         if (k > 0)
479                 *llmnr = LLMNR_SUPPORT_YES;
480         else if (k == 0)
481                 *llmnr = LLMNR_SUPPORT_NO;
482         else {
483                 LLMNRSupport s;
484
485                 s = llmnr_support_from_string(rvalue);
486                 if (s < 0){
487                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
488                         return 0;
489                 }
490
491                 *llmnr = s;
492         }
493
494         return 0;
495 }