1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
22 #include <netinet/ether.h>
25 #include "link-config.h"
27 #include "ethtool-util.h"
29 #include "libudev-private.h"
34 #include "path-util.h"
35 #include "conf-parser.h"
36 #include "conf-files.h"
39 struct link_config_ctx
{
40 LIST_HEAD(link_config
, links
);
47 usec_t
*link_dirs_ts_usec
;
50 int link_config_ctx_new(link_config_ctx
**ret
) {
57 ctx
= new0(link_config_ctx
, 1);
61 r
= ethtool_connect(&ctx
->ethtool_fd
);
63 link_config_ctx_free(ctx
);
67 r
= sd_rtnl_open(0, &ctx
->rtnl
);
69 link_config_ctx_free(ctx
);
73 LIST_HEAD_INIT(ctx
->links
);
75 ctx
->link_dirs
= strv_new("/etc/net/links",
79 if (!ctx
->link_dirs
) {
80 log_error("failed to build link config directory array");
81 link_config_ctx_free(ctx
);
84 if (!path_strv_canonicalize_uniq(ctx
->link_dirs
)) {
85 log_error("failed to canonicalize link config directories\n");
86 link_config_ctx_free(ctx
);
90 ctx
->link_dirs_ts_usec
= calloc(strv_length(ctx
->link_dirs
), sizeof(usec_t
));
91 if(!ctx
->link_dirs_ts_usec
) {
92 link_config_ctx_free(ctx
);
100 static void link_configs_free(link_config_ctx
*ctx
) {
101 link_config
*link
, *link_next
;
106 LIST_FOREACH_SAFE(links
, link
, link_next
, ctx
->links
) {
107 free(link
->filename
);
108 free(link
->match_path
);
109 free(link
->match_driver
);
110 free(link
->match_type
);
111 free(link
->description
);
117 void link_config_ctx_free(link_config_ctx
*ctx
) {
121 if (ctx
->ethtool_fd
>= 0)
122 close_nointr_nofail(ctx
->ethtool_fd
);
124 sd_rtnl_unref(ctx
->rtnl
);
126 strv_free(ctx
->link_dirs
);
127 free(ctx
->link_dirs_ts_usec
);
128 link_configs_free(ctx
);
135 static int load_link(link_config_ctx
*ctx
, const char *filename
) {
140 file
= fopen(filename
, "re");
148 link
= new0(link_config
, 1);
154 r
= config_parse(NULL
, filename
, file
, "Match\0Link\0Ethernet\0", config_item_perf_lookup
,
155 (void*) link_config_gperf_lookup
, false, false, link
);
157 log_warning("Colud not parse config file %s: %s", filename
, strerror(-r
));
160 log_info("Parsed configuration file %s", filename
);
162 link
->filename
= strdup(filename
);
164 LIST_PREPEND(links
, ctx
->links
, link
);
173 int link_config_load(link_config_ctx
*ctx
) {
177 link_configs_free(ctx
);
179 /* update timestamps */
180 paths_check_timestamp(ctx
->link_dirs
, ctx
->link_dirs_ts_usec
, true);
182 r
= conf_files_list_strv(&files
, ".link", NULL
, (const char **)ctx
->link_dirs
);
184 log_error("failed to enumerate link files: %s", strerror(-r
));
188 STRV_FOREACH_BACKWARDS(f
, files
) {
189 r
= load_link(ctx
, *f
);
197 bool link_config_should_reload(link_config_ctx
*ctx
) {
198 return paths_check_timestamp(ctx
->link_dirs
, ctx
->link_dirs_ts_usec
, false);
201 static bool match_config(link_config
*match
, struct udev_device
*device
) {
202 const char *property
;
204 if (match
->match_mac
) {
205 property
= udev_device_get_sysattr_value(device
, "address");
206 if (!property
|| !streq(match
->match_mac
, property
)) {
207 log_debug("Device MAC address (%s) did not match MACAddress=%s", property
, match
->match_mac
);
212 if (match
->match_path
) {
213 property
= udev_device_get_property_value(device
, "ID_PATH");
214 if (!property
|| !streq(match
->match_path
, property
)) {
215 log_debug("Device's persistent path (%s) did not match Path=%s", property
, match
->match_path
);
220 if (match
->match_driver
) {
221 property
= udev_device_get_driver(device
);
222 if (!property
|| !streq(match
->match_driver
, property
)) {
223 log_debug("Device driver (%s) did not match Driver=%s", property
, match
->match_driver
);
228 if (match
->match_type
) {
229 property
= udev_device_get_devtype(device
);
230 if (!property
|| !streq(match
->match_type
, property
)) {
231 log_debug("Device type (%s) did not match Type=%s", property
, match
->match_type
);
239 int link_config_get(link_config_ctx
*ctx
, struct udev_device
*device
, link_config
**ret
) {
242 LIST_FOREACH(links
, link
, ctx
->links
) {
243 if (!match_config(link
, device
)) {
244 log_info("Config file %s does not apply to device %s", link
->filename
, udev_device_get_sysname(device
));
246 log_info("Config file %s applies to device %s", link
->filename
, udev_device_get_sysname(device
));
255 static int rtnl_set_properties(sd_rtnl
*rtnl
, int ifindex
, const char *name
, const char *mac
, unsigned int mtu
) {
256 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message
*message
;
257 char new_name
[IFNAMSIZ
];
258 struct ether_addr new_mac
;
265 r
= sd_rtnl_message_link_new(RTM_NEWLINK
, ifindex
, 0, 0, &message
);
270 strscpy(new_name
, IFNAMSIZ
, name
);
271 r
= sd_rtnl_message_append(message
, IFLA_IFNAME
, new_name
);
279 r
= sscanf(mac
, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
280 &new_mac
.ether_addr_octet
[0],
281 &new_mac
.ether_addr_octet
[1],
282 &new_mac
.ether_addr_octet
[2],
283 &new_mac
.ether_addr_octet
[3],
284 &new_mac
.ether_addr_octet
[4],
285 &new_mac
.ether_addr_octet
[5]);
288 r
= sd_rtnl_message_append(message
, IFLA_ADDRESS
, &new_mac
);
296 r
= sd_rtnl_message_append(message
, IFLA_MTU
, &mtu
);
304 r
= sd_rtnl_send_with_reply_and_block(rtnl
, message
, 250 * USEC_PER_MSEC
, NULL
);
312 static bool enable_name_policy(void) {
313 _cleanup_free_
char *line
;
318 r
= read_one_line_file("/proc/cmdline", &line
);
320 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r
));
321 return true; /* something is very wrong, let's not make it worse */
324 FOREACH_WORD_QUOTED(w
, l
, line
, state
)
325 if (strneq(w
, "net.ifnames=0", l
))
331 int link_config_apply(link_config_ctx
*ctx
, link_config
*config
, struct udev_device
*device
) {
332 const char *name
, *new_name
= NULL
;
335 name
= udev_device_get_sysname(device
);
339 log_info("Configuring %s", name
);
341 if (config
->description
) {
342 r
= udev_device_set_sysattr_value(device
, "ifalias",
343 config
->description
);
345 log_warning("Could not set description of %s to '%s': %s",
346 name
, config
->description
, strerror(-r
));
348 log_info("Set link description of %s to '%s'", name
,
349 config
->description
);
352 if (config
->speed
|| config
->duplex
) {
353 r
= ethtool_set_speed(ctx
->ethtool_fd
, name
,
354 config
->speed
, config
->duplex
);
356 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
357 name
, config
->speed
, config
->duplex
, strerror(-r
));
359 log_info("Set speed or duplex of %s to %u Mbytes (%s)", name
,
360 config
->speed
, config
->duplex
);
364 r
= ethtool_set_wol(ctx
->ethtool_fd
, name
, config
->wol
);
366 log_warning("Could not set WakeOnLan of %s to %s: %s",
367 name
, config
->wol
, strerror(-r
));
369 log_info("Set WakeOnLan of %s to %s", name
, config
->wol
);
372 ifindex
= udev_device_get_ifindex(device
);
374 log_warning("Could not find ifindex");
378 if (config
->name_policy
&& enable_name_policy()) {
381 STRV_FOREACH(policy
, config
->name_policy
) {
382 if (streq(*policy
, "onboard")) {
383 new_name
= udev_device_get_property_value(device
, "ID_NET_NAME_ONBOARD");
386 } else if (streq(*policy
, "slot")) {
387 new_name
= udev_device_get_property_value(device
, "ID_NET_NAME_SLOT");
390 } else if (streq(*policy
, "path")) {
391 new_name
= udev_device_get_property_value(device
, "ID_NET_NAME_PATH");
394 } else if (streq(*policy
, "mac")) {
395 new_name
= udev_device_get_property_value(device
, "ID_NET_NAME_MAC");
399 log_warning("Invalid link naming policy '%s', ignoring.", *policy
);
403 if (!new_name
&& config
->name
)
404 new_name
= config
->name
;
406 r
= rtnl_set_properties(ctx
->rtnl
, ifindex
, new_name
, config
->mac
, config
->mtu
);
408 log_warning("Could not set Name, MACAddress or MTU on %s", name
);