]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/net/link-config.c
network: move configuration to /etc/systemd/network
[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
41 struct link_config_ctx {
42 LIST_HEAD(link_config, links);
43
44 int ethtool_fd;
45
46 sd_rtnl *rtnl;
47
48 char **link_dirs;
49 usec_t *link_dirs_ts_usec;
50 };
51
52 int link_config_ctx_new(link_config_ctx **ret) {
53 link_config_ctx *ctx;
54 int r;
55
56 if (!ret)
57 return -EINVAL;
58
59 ctx = new0(link_config_ctx, 1);
60 if (!ctx)
61 return -ENOMEM;
62
63 r = ethtool_connect(&ctx->ethtool_fd);
64 if (r < 0) {
65 link_config_ctx_free(ctx);
66 return r;
67 }
68
69 r = sd_rtnl_open(0, &ctx->rtnl);
70 if (r < 0) {
71 link_config_ctx_free(ctx);
72 return r;
73 }
74
75 LIST_HEAD_INIT(ctx->links);
76
77 ctx->link_dirs = strv_new("/etc/systemd/network",
78 "/run/systemd/network",
79 "/usr/lib/systemd/network",
80 NULL);
81 if (!ctx->link_dirs) {
82 log_error("failed to build link config directory array");
83 link_config_ctx_free(ctx);
84 return -ENOMEM;
85 }
86 if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
87 log_error("failed to canonicalize link config directories\n");
88 link_config_ctx_free(ctx);
89 return -ENOMEM;
90 }
91
92 ctx->link_dirs_ts_usec = calloc(strv_length(ctx->link_dirs), sizeof(usec_t));
93 if(!ctx->link_dirs_ts_usec) {
94 link_config_ctx_free(ctx);
95 return -ENOMEM;
96 }
97
98 *ret = ctx;
99 return 0;
100 }
101
102 static void link_configs_free(link_config_ctx *ctx) {
103 link_config *link, *link_next;
104
105 if (!ctx)
106 return;
107
108 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
109 free(link->filename);
110 free(link->match_path);
111 free(link->match_driver);
112 free(link->match_type);
113 free(link->description);
114
115 free(link);
116 }
117 }
118
119 void link_config_ctx_free(link_config_ctx *ctx) {
120 if (!ctx)
121 return;
122
123 if (ctx->ethtool_fd >= 0)
124 close_nointr_nofail(ctx->ethtool_fd);
125
126 sd_rtnl_unref(ctx->rtnl);
127
128 strv_free(ctx->link_dirs);
129 free(ctx->link_dirs_ts_usec);
130 link_configs_free(ctx);
131
132 free(ctx);
133
134 return;
135 }
136
137 static int load_link(link_config_ctx *ctx, const char *filename) {
138 link_config *link;
139 FILE *file;
140 int r;
141
142 file = fopen(filename, "re");
143 if (!file) {
144 if (errno == ENOENT)
145 return 0;
146 else
147 return errno;
148 }
149
150 link = new0(link_config, 1);
151 if (!link) {
152 r = log_oom();
153 goto failure;
154 }
155
156 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
157 (void*) link_config_gperf_lookup, false, false, link);
158 if (r < 0) {
159 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
160 goto failure;
161 } else
162 log_info("Parsed configuration file %s", filename);
163
164 link->filename = strdup(filename);
165
166 LIST_PREPEND(links, ctx->links, link);
167
168 return 0;
169
170 failure:
171 free(link);
172 return r;
173 }
174
175 int link_config_load(link_config_ctx *ctx) {
176 int r;
177 char **files, **f;
178
179 link_configs_free(ctx);
180
181 /* update timestamps */
182 paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, true);
183
184 r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
185 if (r < 0) {
186 log_error("failed to enumerate link files: %s", strerror(-r));
187 return r;
188 }
189
190 STRV_FOREACH_BACKWARDS(f, files) {
191 r = load_link(ctx, *f);
192 if (r < 0)
193 return r;
194 }
195
196 return 0;
197 }
198
199 bool link_config_should_reload(link_config_ctx *ctx) {
200 return paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, false);
201 }
202
203 static bool match_config(link_config *match, struct udev_device *device) {
204 const char *property;
205
206 if (match->match_mac) {
207 property = udev_device_get_sysattr_value(device, "address");
208 if (!property || !streq(match->match_mac, property)) {
209 log_debug("Device MAC address (%s) did not match MACAddress=%s", property, match->match_mac);
210 return 0;
211 }
212 }
213
214 if (match->match_path) {
215 property = udev_device_get_property_value(device, "ID_PATH");
216 if (!property || !streq(match->match_path, property)) {
217 log_debug("Device's persistent path (%s) did not match Path=%s", property, match->match_path);
218 return 0;
219 }
220 }
221
222 if (match->match_driver) {
223 property = udev_device_get_driver(device);
224 if (!property || !streq(match->match_driver, property)) {
225 log_debug("Device driver (%s) did not match Driver=%s", property, match->match_driver);
226 return 0;
227 }
228 }
229
230 if (match->match_type) {
231 property = udev_device_get_devtype(device);
232 if (!property || !streq(match->match_type, property)) {
233 log_debug("Device type (%s) did not match Type=%s", property, match->match_type);
234 return 0;
235 }
236 }
237
238 return 1;
239 }
240
241 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
242 link_config *link;
243
244 LIST_FOREACH(links, link, ctx->links) {
245 if (!match_config(link, device)) {
246 log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
247 } else {
248 log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
249 *ret = link;
250 return 0;
251 }
252 }
253
254 return -ENOENT;
255 }
256
257 static int rtnl_set_properties(sd_rtnl *rtnl, int ifindex, const char *name, const struct ether_addr *mac, unsigned int mtu) {
258 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message;
259 bool need_update = false;
260 int r;
261
262 assert(rtnl);
263 assert(ifindex > 0);
264
265 r = sd_rtnl_message_link_new(RTM_NEWLINK, ifindex, 0, 0, &message);
266 if (r < 0)
267 return r;
268
269 if (name) {
270 r = sd_rtnl_message_append(message, IFLA_IFNAME, name);
271 if (r < 0)
272 return r;
273
274 need_update = true;
275 }
276
277 if (mac) {
278 r = sd_rtnl_message_append(message, IFLA_ADDRESS, mac);
279 if (r < 0)
280 return r;
281
282 need_update = true;
283 }
284
285 if (mtu > 0) {
286 r = sd_rtnl_message_append(message, IFLA_MTU, &mtu);
287 if (r < 0)
288 return r;
289
290 need_update = true;
291 }
292
293 if (need_update) {
294 r = sd_rtnl_send_with_reply_and_block(rtnl, message, 5 * USEC_PER_SEC, NULL);
295 if (r < 0)
296 return r;
297 }
298
299 return 0;
300 }
301
302 static bool enable_name_policy(void) {
303 _cleanup_free_ char *line;
304 char *w, *state;
305 int r;
306 size_t l;
307
308 r = read_one_line_file("/proc/cmdline", &line);
309 if (r < 0) {
310 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
311 return true; /* something is very wrong, let's not make it worse */
312 }
313
314 FOREACH_WORD_QUOTED(w, l, line, state)
315 if (strneq(w, "net.ifnames=0", l))
316 return false;
317
318 return true;
319 }
320
321 static bool mac_is_random(struct udev_device *device) {
322 const char *s;
323 int type;
324
325 s = udev_device_get_sysattr_value(device, "addr_assign_type");
326 if (!s)
327 return -EINVAL;
328 type = strtoul(s, NULL, 0);
329
330 /* check for NET_ADDR_RANDOM */
331 return type == 1;
332 }
333
334 static bool mac_is_permanent(struct udev_device *device) {
335 const char *s;
336 int type;
337
338 s = udev_device_get_sysattr_value(device, "addr_assign_type");
339 if (!s)
340 return -EINVAL;
341 type = strtoul(s, NULL, 0);
342
343 /* check for NET_ADDR_PERM */
344 return type == 0;
345 }
346
347 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr **ret) {
348 struct ether_addr *mac;
349 unsigned int seed;
350 int r, i;
351
352 mac = calloc(1, sizeof(struct ether_addr));
353 if (!mac)
354 return -ENOMEM;
355
356 if (want_random)
357 seed = random_u();
358 else {
359 const char *name;
360 sd_id128_t machine;
361 char machineid_buf[33];
362 const char *seed_str;
363
364 /* fetch some persistent data unique (on this machine) to this device */
365 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
366 if (!name) {
367 name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
368 if (!name) {
369 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
370 if (!name)
371 return -1;
372 }
373 }
374 /* fetch some persistent data unique to this machine */
375 r = sd_id128_get_machine(&machine);
376 if (r < 0)
377 return -1;
378
379 /* combine the data */
380 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
381
382 /* hash to get seed */
383 seed = string_hash_func(seed_str);
384 }
385
386 srandom(seed);
387
388 for(i = 0; i < ETH_ALEN; i++) {
389 mac->ether_addr_octet[i] = random();
390 }
391
392 /* see eth_random_addr in the kernel */
393 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
394 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
395
396 *ret = mac;
397
398 return 0;
399 }
400
401 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
402 const char *name;
403 char *new_name = NULL;
404 struct ether_addr *mac = NULL;
405 int r, ifindex;
406
407 name = udev_device_get_sysname(device);
408 if (!name)
409 return -EINVAL;
410
411 log_info("Configuring %s", name);
412
413 if (config->description) {
414 r = udev_device_set_sysattr_value(device, "ifalias",
415 config->description);
416 if (r < 0)
417 log_warning("Could not set description of %s to '%s': %s",
418 name, config->description, strerror(-r));
419 else
420 log_info("Set link description of %s to '%s'", name,
421 config->description);
422 }
423
424 if (config->speed || config->duplex) {
425 r = ethtool_set_speed(ctx->ethtool_fd, name,
426 config->speed, config->duplex);
427 if (r < 0)
428 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
429 name, config->speed, config->duplex, strerror(-r));
430 else
431 log_info("Set speed or duplex of %s to %u Mbytes (%s)", name,
432 config->speed, config->duplex);
433 }
434
435 if (config->wol) {
436 r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol);
437 if (r < 0)
438 log_warning("Could not set WakeOnLan of %s to %s: %s",
439 name, config->wol, strerror(-r));
440 else
441 log_info("Set WakeOnLan of %s to %s", name, config->wol);
442 }
443
444 ifindex = udev_device_get_ifindex(device);
445 if (ifindex <= 0) {
446 log_warning("Could not find ifindex");
447 return -ENODEV;
448 }
449
450 if (config->name_policy && enable_name_policy()) {
451 char **policy;
452
453 STRV_FOREACH(policy, config->name_policy) {
454 if (streq(*policy, "onboard")) {
455 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"), &new_name);
456 if (r < 0)
457 return r;
458 if (new_name)
459 break;
460 } else if (streq(*policy, "slot")) {
461 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_SLOT"), &new_name);
462 if (r < 0)
463 return r;
464 if (new_name)
465 break;
466 } else if (streq(*policy, "path")) {
467 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_PATH"), &new_name);
468 if (r < 0)
469 return r;
470 if (new_name)
471 break;
472 } else if (streq(*policy, "mac")) {
473 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_MAC"), &new_name);
474 if (r < 0)
475 return r;
476 if (new_name)
477 break;
478 } else
479 log_warning("Invalid link naming policy '%s', ignoring.", *policy);
480 }
481 }
482
483 if (!new_name && config->name) {
484 new_name = calloc(1, IFNAMSIZ);
485 strscpy(new_name, IFNAMSIZ, config->name);
486 }
487
488 if (config->mac_policy) {
489 if (streq(config->mac_policy, "persistent")) {
490 if (!mac_is_permanent(device)) {
491 r = get_mac(device, false, &mac);
492 if (r < 0)
493 return r;
494 }
495 } else if (streq(config->mac_policy, "random")) {
496 if (!mac_is_random(device)) {
497 r = get_mac(device, true, &mac);
498 if (r < 0)
499 return r;
500 }
501 } else
502 log_warning("Invalid MACAddress policy '%s', ignoring.", config->mac_policy);
503 }
504
505 if (!mac && config->mac) {
506 mac = calloc(1, sizeof(struct ether_addr));
507 r = sscanf(config->mac, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
508 &mac->ether_addr_octet[0],
509 &mac->ether_addr_octet[1],
510 &mac->ether_addr_octet[2],
511 &mac->ether_addr_octet[3],
512 &mac->ether_addr_octet[4],
513 &mac->ether_addr_octet[5]);
514 if (r != 6) {
515 r = -EINVAL;
516 goto out;
517 }
518 }
519
520 r = rtnl_set_properties(ctx->rtnl, ifindex, new_name, mac, config->mtu);
521 if (r < 0) {
522 log_warning("Could not set Name, MACAddress or MTU on %s: %s", name, strerror(-r));
523 goto out;
524 }
525
526 return 0;
527 out:
528 free(new_name);
529 free(mac);
530 return r;
531 }