]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/net/link-config.c
Be more careful when checking for empty files
[thirdparty/systemd.git] / src / udev / net / link-config.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright (C) 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 <netinet/ether.h>
23 #include <net/if.h>
24
25 #include "sd-id128.h"
26
27 #include "link-config.h"
28 #include "ethtool-util.h"
29
30 #include "libudev-private.h"
31 #include "sd-rtnl.h"
32 #include "util.h"
33 #include "log.h"
34 #include "strv.h"
35 #include "path-util.h"
36 #include "conf-parser.h"
37 #include "conf-files.h"
38 #include "fileio.h"
39 #include "hashmap.h"
40 #include "rtnl-util.h"
41 #include "network-internal.h"
42 #include "siphash24.h"
43
44 struct link_config_ctx {
45 LIST_HEAD(link_config, links);
46
47 int ethtool_fd;
48
49 bool enable_name_policy;
50
51 sd_rtnl *rtnl;
52
53 usec_t link_dirs_ts_usec;
54 };
55
56 static const char* const link_dirs[] = {
57 "/etc/systemd/network",
58 "/run/systemd/network",
59 "/usr/lib/systemd/network",
60 #ifdef HAVE_SPLIT_USR
61 "/lib/systemd/network",
62 #endif
63 NULL};
64
65 DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free);
66 #define _cleanup_link_config_ctx_free_ _cleanup_(link_config_ctx_freep)
67
68 int link_config_ctx_new(link_config_ctx **ret) {
69 _cleanup_link_config_ctx_free_ link_config_ctx *ctx = NULL;
70
71 if (!ret)
72 return -EINVAL;
73
74 ctx = new0(link_config_ctx, 1);
75 if (!ctx)
76 return -ENOMEM;
77
78 LIST_HEAD_INIT(ctx->links);
79
80 ctx->ethtool_fd = -1;
81
82 ctx->enable_name_policy = true;
83
84 *ret = ctx;
85 ctx = NULL;
86
87 return 0;
88 }
89
90 static int link_config_ctx_connect(link_config_ctx *ctx) {
91 int r;
92
93 if (ctx->ethtool_fd == -1) {
94 r = ethtool_connect(&ctx->ethtool_fd);
95 if (r < 0) {
96 log_warning("link_config: could not connect to ethtool: %s",
97 strerror(-r));
98 return r;
99 }
100 }
101
102 if (!ctx->rtnl) {
103 r = sd_rtnl_open(&ctx->rtnl, 0);
104 if (r < 0) {
105 log_warning("link_config: could not connect to rtnl: %s",
106 strerror(-r));
107 return r;
108 }
109 }
110
111 return 0;
112 }
113
114 static void link_configs_free(link_config_ctx *ctx) {
115 link_config *link, *link_next;
116
117 if (!ctx)
118 return;
119
120 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
121 free(link->filename);
122 free(link->match_path);
123 free(link->match_driver);
124 free(link->match_type);
125 free(link->description);
126 free(link->alias);
127 free(link->name_policy);
128
129 free(link);
130 }
131 }
132
133 void link_config_ctx_free(link_config_ctx *ctx) {
134 if (!ctx)
135 return;
136
137 safe_close(ctx->ethtool_fd);
138
139 sd_rtnl_unref(ctx->rtnl);
140
141 link_configs_free(ctx);
142
143 free(ctx);
144
145 return;
146 }
147
148 static int load_link(link_config_ctx *ctx, const char *filename) {
149 _cleanup_free_ link_config *link = NULL;
150 _cleanup_fclose_ FILE *file = NULL;
151 int r;
152
153 assert(ctx);
154 assert(filename);
155
156 file = fopen(filename, "re");
157 if (!file) {
158 if (errno == ENOENT)
159 return 0;
160 else
161 return -errno;
162 }
163
164 if (null_or_empty_fd(fileno(file))) {
165 log_debug("Skipping empty file: %s", filename);
166 return 0;
167 }
168
169 link = new0(link_config, 1);
170 if (!link)
171 return log_oom();
172
173 link->mac_policy = _MACPOLICY_INVALID;
174 link->wol = _WOL_INVALID;
175 link->duplex = _DUP_INVALID;
176
177 r = config_parse(NULL, filename, file,
178 "Match\0Link\0Ethernet\0",
179 config_item_perf_lookup, link_config_gperf_lookup,
180 false, false, true, link);
181 if (r < 0)
182 return r;
183 else
184 log_debug("Parsed configuration file %s", filename);
185
186 link->filename = strdup(filename);
187
188 LIST_PREPEND(links, ctx->links, link);
189 link = NULL;
190
191 return 0;
192 }
193
194 static bool enable_name_policy(void) {
195 _cleanup_free_ char *line = NULL;
196 char *w, *state;
197 int r;
198 size_t l;
199
200 r = proc_cmdline(&line);
201 if (r < 0)
202 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
203 if (r <= 0)
204 return true;
205
206 FOREACH_WORD_QUOTED(w, l, line, state)
207 if (strneq(w, "net.ifnames=0", l))
208 return false;
209
210 return true;
211 }
212
213 int link_config_load(link_config_ctx *ctx) {
214 int r;
215 _cleanup_strv_free_ char **files;
216 char **f;
217
218 link_configs_free(ctx);
219
220 if (!enable_name_policy()) {
221 ctx->enable_name_policy = false;
222 log_info("Network interface NamePolicy= disabled on kernel commandline, ignoring.");
223 }
224
225 /* update timestamp */
226 paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true);
227
228 r = conf_files_list_strv(&files, ".link", NULL, link_dirs);
229 if (r < 0) {
230 log_error("failed to enumerate link files: %s", strerror(-r));
231 return r;
232 }
233
234 STRV_FOREACH_BACKWARDS(f, files) {
235 r = load_link(ctx, *f);
236 if (r < 0)
237 return r;
238 }
239
240 return 0;
241 }
242
243 bool link_config_should_reload(link_config_ctx *ctx) {
244 return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false);
245 }
246
247 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
248 link_config *link;
249
250 LIST_FOREACH(links, link, ctx->links) {
251
252 if (net_match_config(link->match_mac, link->match_path, link->match_driver,
253 link->match_type, NULL, link->match_host,
254 link->match_virt, link->match_kernel, link->match_arch,
255 ether_aton(udev_device_get_sysattr_value(device, "address")),
256 udev_device_get_property_value(device, "ID_PATH"),
257 udev_device_get_driver(udev_device_get_parent(device)),
258 udev_device_get_property_value(device, "ID_NET_DRIVER"),
259 udev_device_get_devtype(device),
260 NULL)) {
261 log_debug("Config file %s applies to device %s",
262 link->filename,
263 udev_device_get_sysname(device));
264 *ret = link;
265 return 0;
266 }
267 }
268
269 *ret = NULL;
270
271 return -ENOENT;
272 }
273
274 static bool mac_is_random(struct udev_device *device) {
275 const char *s;
276 unsigned type;
277 int r;
278
279 s = udev_device_get_sysattr_value(device, "addr_assign_type");
280 if (!s)
281 return false; /* if we don't know, assume it is not random */
282 r = safe_atou(s, &type);
283 if (r < 0)
284 return false;
285
286 /* check for NET_ADDR_RANDOM */
287 return type == 1;
288 }
289
290 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
291 int r;
292
293 if (want_random)
294 random_bytes(mac->ether_addr_octet, ETH_ALEN);
295 else {
296 uint8_t result[8];
297
298 r = net_get_unique_predictable_data(device, result);
299 if (r < 0)
300 return r;
301
302 assert_cc(ETH_ALEN <= sizeof(result));
303 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
304 }
305
306 /* see eth_random_addr in the kernel */
307 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
308 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
309
310 return 0;
311 }
312
313 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) {
314 const char *old_name;
315 const char *new_name = NULL;
316 struct ether_addr generated_mac;
317 struct ether_addr *mac = NULL;
318 int r, ifindex;
319
320 assert(ctx);
321 assert(config);
322 assert(device);
323 assert(name);
324
325 r = link_config_ctx_connect(ctx);
326 if (r < 0)
327 return r;
328
329 old_name = udev_device_get_sysname(device);
330 if (!old_name)
331 return -EINVAL;
332
333 r = ethtool_set_speed(ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
334 if (r < 0)
335 log_warning("Could not set speed or duplex of %s to %u Mbps (%s): %s",
336 old_name, config->speed / 1024, duplex_to_string(config->duplex),
337 strerror(-r));
338
339 r = ethtool_set_wol(ctx->ethtool_fd, old_name, config->wol);
340 if (r < 0)
341 log_warning("Could not set WakeOnLan of %s to %s: %s",
342 old_name, wol_to_string(config->wol), strerror(-r));
343
344 ifindex = udev_device_get_ifindex(device);
345 if (ifindex <= 0) {
346 log_warning("Could not find ifindex");
347 return -ENODEV;
348 }
349
350 if (ctx->enable_name_policy && config->name_policy) {
351 NamePolicy *policy;
352
353 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
354 switch (*policy) {
355 case NAMEPOLICY_DATABASE:
356 new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE");
357 break;
358 case NAMEPOLICY_ONBOARD:
359 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
360 break;
361 case NAMEPOLICY_SLOT:
362 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
363 break;
364 case NAMEPOLICY_PATH:
365 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
366 break;
367 case NAMEPOLICY_MAC:
368 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
369 break;
370 default:
371 break;
372 }
373 }
374 }
375
376 if (new_name)
377 *name = new_name; /* a name was set by a policy */
378 else if (config->name)
379 *name = config->name; /* a name was set manually in the config */
380 else
381 *name = NULL;
382
383 switch (config->mac_policy) {
384 case MACPOLICY_PERSISTENT:
385 if (mac_is_random(device)) {
386 r = get_mac(device, false, &generated_mac);
387 if (r == -ENOENT)
388 break;
389 else if (r < 0)
390 return r;
391 mac = &generated_mac;
392 }
393 break;
394 case MACPOLICY_RANDOM:
395 if (!mac_is_random(device)) {
396 r = get_mac(device, true, &generated_mac);
397 if (r == -ENOENT)
398 break;
399 else if (r < 0)
400 return r;
401 mac = &generated_mac;
402 }
403 break;
404 default:
405 mac = config->mac;
406 }
407
408 r = rtnl_set_link_properties(ctx->rtnl, ifindex, config->alias, mac, config->mtu);
409 if (r < 0) {
410 log_warning("Could not set Alias, MACAddress or MTU on %s: %s", old_name, strerror(-r));
411 return r;
412 }
413
414 return 0;
415 }
416
417 int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
418 const char *name;
419 char *driver;
420 int r;
421
422 r = link_config_ctx_connect(ctx);
423 if (r < 0)
424 return r;
425
426 name = udev_device_get_sysname(device);
427 if (!name)
428 return -EINVAL;
429
430 r = ethtool_get_driver(ctx->ethtool_fd, name, &driver);
431 if (r < 0)
432 return r;
433
434 *ret = driver;
435 return 0;
436 }
437
438 static const char* const mac_policy_table[_MACPOLICY_MAX] = {
439 [MACPOLICY_PERSISTENT] = "persistent",
440 [MACPOLICY_RANDOM] = "random"
441 };
442
443 DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy);
444 DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, "Failed to parse MAC address policy");
445
446 static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
447 [NAMEPOLICY_DATABASE] = "database",
448 [NAMEPOLICY_ONBOARD] = "onboard",
449 [NAMEPOLICY_SLOT] = "slot",
450 [NAMEPOLICY_PATH] = "path",
451 [NAMEPOLICY_MAC] = "mac"
452 };
453
454 DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
455 DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, _NAMEPOLICY_INVALID, "Failed to parse interface name policy");