]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/net/link-config.c
5a45c53cc639c631e899c45014de9f517d3216d4
[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 if (null_or_empty_path(filename)) {
157 log_debug("skipping empty file: %s", filename);
158 return 0;
159 }
160
161 file = fopen(filename, "re");
162 if (!file) {
163 if (errno == ENOENT)
164 return 0;
165 else
166 return -errno;
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, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
178 (void*) link_config_gperf_lookup, false, false, link);
179 if (r < 0) {
180 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
181 return r;
182 } else
183 log_debug("Parsed configuration file %s", filename);
184
185 link->filename = strdup(filename);
186
187 LIST_PREPEND(links, ctx->links, link);
188 link = NULL;
189
190 return 0;
191 }
192
193 static bool enable_name_policy(void) {
194 _cleanup_free_ char *line = NULL;
195 char *w, *state;
196 int r;
197 size_t l;
198
199 r = proc_cmdline(&line);
200 if (r < 0)
201 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
202 if (r <= 0)
203 return true;
204
205 FOREACH_WORD_QUOTED(w, l, line, state)
206 if (strneq(w, "net.ifnames=0", l))
207 return false;
208
209 return true;
210 }
211
212 int link_config_load(link_config_ctx *ctx) {
213 int r;
214 _cleanup_strv_free_ char **files;
215 char **f;
216
217 link_configs_free(ctx);
218
219 if (!enable_name_policy()) {
220 ctx->enable_name_policy = false;
221 log_info("Network interface NamePolicy= disabled on kernel commandline, ignoring.");
222 }
223
224 /* update timestamp */
225 paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true);
226
227 r = conf_files_list_strv(&files, ".link", NULL, link_dirs);
228 if (r < 0) {
229 log_error("failed to enumerate link files: %s", strerror(-r));
230 return r;
231 }
232
233 STRV_FOREACH_BACKWARDS(f, files) {
234 r = load_link(ctx, *f);
235 if (r < 0)
236 return r;
237 }
238
239 return 0;
240 }
241
242 bool link_config_should_reload(link_config_ctx *ctx) {
243 return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false);
244 }
245
246 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
247 link_config *link;
248
249 LIST_FOREACH(links, link, ctx->links) {
250
251 if (net_match_config(link->match_mac, link->match_path, link->match_driver,
252 link->match_type, NULL, link->match_host,
253 link->match_virt, link->match_kernel, link->match_arch,
254 ether_aton(udev_device_get_sysattr_value(device, "address")),
255 udev_device_get_property_value(device, "ID_PATH"),
256 udev_device_get_driver(udev_device_get_parent(device)),
257 udev_device_get_property_value(device, "ID_NET_DRIVER"),
258 udev_device_get_devtype(device),
259 NULL)) {
260 log_debug("Config file %s applies to device %s",
261 link->filename,
262 udev_device_get_sysname(device));
263 *ret = link;
264 return 0;
265 }
266 }
267
268 *ret = NULL;
269
270 return -ENOENT;
271 }
272
273 static bool mac_is_random(struct udev_device *device) {
274 const char *s;
275 unsigned type;
276 int r;
277
278 s = udev_device_get_sysattr_value(device, "addr_assign_type");
279 if (!s)
280 return false; /* if we don't know, assume it is not random */
281 r = safe_atou(s, &type);
282 if (r < 0)
283 return false;
284
285 /* check for NET_ADDR_RANDOM */
286 return type == 1;
287 }
288
289 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
290 int r;
291
292 if (want_random)
293 random_bytes(mac->ether_addr_octet, ETH_ALEN);
294 else {
295 uint8_t result[8];
296
297 r = net_get_unique_predictable_data(device, result);
298 if (r < 0)
299 return r;
300
301 assert_cc(ETH_ALEN <= sizeof(result));
302 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
303 }
304
305 /* see eth_random_addr in the kernel */
306 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
307 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
308
309 return 0;
310 }
311
312 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) {
313 const char *old_name;
314 const char *new_name = NULL;
315 struct ether_addr generated_mac;
316 struct ether_addr *mac = NULL;
317 int r, ifindex;
318
319 assert(ctx);
320 assert(config);
321 assert(device);
322 assert(name);
323
324 r = link_config_ctx_connect(ctx);
325 if (r < 0)
326 return r;
327
328 old_name = udev_device_get_sysname(device);
329 if (!old_name)
330 return -EINVAL;
331
332 r = ethtool_set_speed(ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
333 if (r < 0)
334 log_warning("Could not set speed or duplex of %s to %u Mbps (%s): %s",
335 old_name, config->speed / 1024, duplex_to_string(config->duplex),
336 strerror(-r));
337
338 r = ethtool_set_wol(ctx->ethtool_fd, old_name, config->wol);
339 if (r < 0)
340 log_warning("Could not set WakeOnLan of %s to %s: %s",
341 old_name, wol_to_string(config->wol), strerror(-r));
342
343 ifindex = udev_device_get_ifindex(device);
344 if (ifindex <= 0) {
345 log_warning("Could not find ifindex");
346 return -ENODEV;
347 }
348
349 if (ctx->enable_name_policy && config->name_policy) {
350 NamePolicy *policy;
351
352 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
353 switch (*policy) {
354 case NAMEPOLICY_DATABASE:
355 new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE");
356 break;
357 case NAMEPOLICY_ONBOARD:
358 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
359 break;
360 case NAMEPOLICY_SLOT:
361 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
362 break;
363 case NAMEPOLICY_PATH:
364 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
365 break;
366 case NAMEPOLICY_MAC:
367 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
368 break;
369 default:
370 break;
371 }
372 }
373 }
374
375 if (new_name)
376 *name = new_name; /* a name was set by a policy */
377 else if (config->name)
378 *name = config->name; /* a name was set manually in the config */
379 else
380 *name = NULL;
381
382 switch (config->mac_policy) {
383 case MACPOLICY_PERSISTENT:
384 if (mac_is_random(device)) {
385 r = get_mac(device, false, &generated_mac);
386 if (r == -ENOENT)
387 break;
388 else if (r < 0)
389 return r;
390 mac = &generated_mac;
391 }
392 break;
393 case MACPOLICY_RANDOM:
394 if (!mac_is_random(device)) {
395 r = get_mac(device, true, &generated_mac);
396 if (r == -ENOENT)
397 break;
398 else if (r < 0)
399 return r;
400 mac = &generated_mac;
401 }
402 break;
403 default:
404 mac = config->mac;
405 }
406
407 r = rtnl_set_link_properties(ctx->rtnl, ifindex, config->alias, mac, config->mtu);
408 if (r < 0) {
409 log_warning("Could not set Alias, MACAddress or MTU on %s: %s", old_name, strerror(-r));
410 return r;
411 }
412
413 return 0;
414 }
415
416 int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
417 const char *name;
418 char *driver;
419 int r;
420
421 r = link_config_ctx_connect(ctx);
422 if (r < 0)
423 return r;
424
425 name = udev_device_get_sysname(device);
426 if (!name)
427 return -EINVAL;
428
429 r = ethtool_get_driver(ctx->ethtool_fd, name, &driver);
430 if (r < 0)
431 return r;
432
433 *ret = driver;
434 return 0;
435 }
436
437 static const char* const mac_policy_table[_MACPOLICY_MAX] = {
438 [MACPOLICY_PERSISTENT] = "persistent",
439 [MACPOLICY_RANDOM] = "random"
440 };
441
442 DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy);
443 DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, "Failed to parse MAC address policy");
444
445 static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
446 [NAMEPOLICY_DATABASE] = "database",
447 [NAMEPOLICY_ONBOARD] = "onboard",
448 [NAMEPOLICY_SLOT] = "slot",
449 [NAMEPOLICY_PATH] = "path",
450 [NAMEPOLICY_MAC] = "mac"
451 };
452
453 DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
454 DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, _NAMEPOLICY_INVALID, "Failed to parse interface name policy");