]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-network.c
networkd/sd-network: expose statically configured NTP servers
[thirdparty/systemd.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 "network-internal.h"
27 #include "path-util.h"
28 #include "conf-files.h"
29 #include "conf-parser.h"
30 #include "util.h"
31
32 static int network_load_one(Manager *manager, const char *filename) {
33 _cleanup_network_free_ Network *network = NULL;
34 _cleanup_fclose_ FILE *file = NULL;
35 Route *route;
36 Address *address;
37 int r;
38
39 assert(manager);
40 assert(filename);
41
42 file = fopen(filename, "re");
43 if (!file) {
44 if (errno == ENOENT)
45 return 0;
46 else
47 return -errno;
48 }
49
50 if (null_or_empty_path(filename)) {
51 log_debug("skipping empty file: %s", filename);
52 return 0;
53 }
54
55 network = new0(Network, 1);
56 if (!network)
57 return log_oom();
58
59 network->manager = manager;
60
61 LIST_HEAD_INIT(network->static_addresses);
62 LIST_HEAD_INIT(network->static_routes);
63
64 network->vlans = hashmap_new(string_hash_func, string_compare_func);
65 if (!network->vlans)
66 return log_oom();
67
68 network->macvlans = hashmap_new(uint64_hash_func, uint64_compare_func);
69 if (!network->macvlans)
70 return log_oom();
71
72 network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
73 if (!network->addresses_by_section)
74 return log_oom();
75
76 network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
77 if (!network->routes_by_section)
78 return log_oom();
79
80 network->filename = strdup(filename);
81 if (!network->filename)
82 return log_oom();
83
84 network->dhcp_ntp = true;
85 network->dhcp_dns = true;
86 network->dhcp_hostname = true;
87 network->dhcp_domainname = true;
88
89 r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
90 (void*) network_network_gperf_lookup, false, false, network);
91 if (r < 0) {
92 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
93 return r;
94 }
95
96 LIST_PREPEND(networks, manager->networks, network);
97
98 LIST_FOREACH(routes, route, network->static_routes) {
99 if (!route->family) {
100 log_warning("Route section without Gateway field configured in %s. "
101 "Ignoring", filename);
102 return 0;
103 }
104 }
105
106 LIST_FOREACH(addresses, address, network->static_addresses) {
107 if (!address->family) {
108 log_warning("Address section without Address field configured in %s. "
109 "Ignoring", filename);
110 return 0;
111 }
112 }
113
114 network = NULL;
115
116 return 0;
117 }
118
119 int network_load(Manager *manager) {
120 Network *network;
121 _cleanup_strv_free_ char **files = NULL;
122 char **f;
123 int r;
124
125 assert(manager);
126
127 while ((network = manager->networks))
128 network_free(network);
129
130 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
131 if (r < 0) {
132 log_error("Failed to enumerate network files: %s", strerror(-r));
133 return r;
134 }
135
136 STRV_FOREACH_BACKWARDS(f, files) {
137 r = network_load_one(manager, *f);
138 if (r < 0)
139 return r;
140 }
141
142 return 0;
143 }
144
145 void network_free(Network *network) {
146 NetDev *netdev;
147 Route *route;
148 Address *address;
149 Iterator i;
150
151 if (!network)
152 return;
153
154 free(network->filename);
155
156 free(network->match_mac);
157 free(network->match_path);
158 free(network->match_driver);
159 free(network->match_type);
160 free(network->match_name);
161
162 free(network->description);
163
164 while ((address = network->ntp)) {
165 LIST_REMOVE(addresses, network->ntp, address);
166 address_free(address);
167 }
168
169 while ((address = network->dns)) {
170 LIST_REMOVE(addresses, network->dns, address);
171 address_free(address);
172 }
173
174 netdev_unref(network->bridge);
175
176 netdev_unref(network->bond);
177
178 HASHMAP_FOREACH(netdev, network->vlans, i)
179 netdev_unref(netdev);
180 hashmap_free(network->vlans);
181
182 HASHMAP_FOREACH(netdev, network->macvlans, i)
183 netdev_unref(netdev);
184 hashmap_free(network->macvlans);
185
186 while ((route = network->static_routes))
187 route_free(route);
188
189 while ((address = network->static_addresses))
190 address_free(address);
191
192 hashmap_free(network->addresses_by_section);
193 hashmap_free(network->routes_by_section);
194
195 if (network->manager && network->manager->networks)
196 LIST_REMOVE(networks, network->manager->networks, network);
197
198 condition_free_list(network->match_host);
199 condition_free_list(network->match_virt);
200 condition_free_list(network->match_kernel);
201 condition_free_list(network->match_arch);
202
203 free(network);
204 }
205
206 int network_get(Manager *manager, struct udev_device *device,
207 const char *ifname, const struct ether_addr *address,
208 Network **ret) {
209 Network *network;
210
211 assert(manager);
212 assert(ret);
213
214 LIST_FOREACH(networks, network, manager->networks) {
215 if (net_match_config(network->match_mac, network->match_path,
216 network->match_driver, network->match_type,
217 network->match_name, network->match_host,
218 network->match_virt, network->match_kernel,
219 network->match_arch,
220 address,
221 udev_device_get_property_value(device, "ID_PATH"),
222 udev_device_get_driver(udev_device_get_parent(device)),
223 udev_device_get_property_value(device, "ID_NET_DRIVER"),
224 udev_device_get_devtype(device),
225 ifname)) {
226 log_debug("%*s: found matching network '%s'", IFNAMSIZ, ifname,
227 network->filename);
228 *ret = network;
229 return 0;
230 }
231 }
232
233 *ret = NULL;
234
235 return -ENOENT;
236 }
237
238 int network_apply(Manager *manager, Network *network, Link *link) {
239 int r;
240
241 link->network = network;
242
243 if (network->dns || network->ntp) {
244 r = link_save(link);
245 if (r < 0)
246 return r;
247 }
248
249 return 0;
250 }
251
252 int config_parse_netdev(const char *unit,
253 const char *filename,
254 unsigned line,
255 const char *section,
256 unsigned section_line,
257 const char *lvalue,
258 int ltype,
259 const char *rvalue,
260 void *data,
261 void *userdata) {
262 Network *network = userdata;
263 _cleanup_free_ char *kind_string = NULL;
264 char *p;
265 NetDev *netdev;
266 NetDevKind kind;
267 int r;
268
269 assert(filename);
270 assert(lvalue);
271 assert(rvalue);
272 assert(data);
273
274 kind_string = strdup(lvalue);
275 if (!kind_string)
276 return log_oom();
277
278 /* the keys are CamelCase versions of the kind */
279 for (p = kind_string; *p; p++)
280 *p = tolower(*p);
281
282 kind = netdev_kind_from_string(kind_string);
283 if (kind == _NETDEV_KIND_INVALID) {
284 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
285 "Invalid NetDev kind: %s", lvalue);
286 return 0;
287 }
288
289 r = netdev_get(network->manager, rvalue, &netdev);
290 if (r < 0) {
291 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
292 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
293 return 0;
294 }
295
296 if (netdev->kind != kind) {
297 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
298 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
299 return 0;
300 }
301
302 switch (kind) {
303 case NETDEV_KIND_BRIDGE:
304 network->bridge = netdev;
305
306 break;
307 case NETDEV_KIND_BOND:
308 network->bond = netdev;
309
310 break;
311 case NETDEV_KIND_VLAN:
312 r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
313 if (r < 0) {
314 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
315 "Can not add VLAN to network: %s", rvalue);
316 return 0;
317 }
318
319 break;
320 case NETDEV_KIND_MACVLAN:
321 r = hashmap_put(network->macvlans, netdev->ifname, netdev);
322 if (r < 0) {
323 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
324 "Can not add MACVLAN to network: %s", rvalue);
325 return 0;
326 }
327
328 break;
329 default:
330 assert_not_reached("Can not parse NetDev");
331 }
332
333 netdev_ref(netdev);
334
335 return 0;
336 }
337
338 int config_parse_tunnel(const char *unit,
339 const char *filename,
340 unsigned line,
341 const char *section,
342 unsigned section_line,
343 const char *lvalue,
344 int ltype,
345 const char *rvalue,
346 void *data,
347 void *userdata) {
348 Network *network = userdata;
349 NetDev *netdev;
350 int r;
351
352 assert(filename);
353 assert(lvalue);
354 assert(rvalue);
355 assert(data);
356
357 r = netdev_get(network->manager, rvalue, &netdev);
358 if (r < 0) {
359 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
360 "Tunnel is invalid, ignoring assignment: %s", rvalue);
361 return 0;
362 }
363
364 if (netdev->kind != NETDEV_KIND_IPIP &&
365 netdev->kind != NETDEV_KIND_SIT &&
366 netdev->kind != NETDEV_KIND_GRE) {
367 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
368 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
369 return 0;
370 }
371
372 network->tunnel = netdev;
373
374 return 0;
375 }