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