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