chiark / gitweb /
ec67126b21b1995ce01bddb453260afe9b076a30
[elogind.git] / src / udev / net / ethtool-util.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 <sys/ioctl.h>
23 #include <net/if.h>
24 #include <linux/ethtool.h>
25 #include <linux/sockios.h>
26
27 #include "ethtool-util.h"
28
29 #include "strxcpyx.h"
30 #include "util.h"
31 #include "log.h"
32 #include "conf-parser.h"
33
34 static const char* const duplex_table[_DUP_MAX] = {
35         [DUP_FULL] = "full",
36         [DUP_HALF] = "half"
37 };
38
39 DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
40 DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
41
42 static const char* const wol_table[_WOL_MAX] = {
43         [WOL_PHY] = "phy",
44         [WOL_MAGIC] = "magic",
45         [WOL_OFF] = "off"
46 };
47
48 DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
49 DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
50
51 int ethtool_connect(int *ret) {
52         int fd;
53
54         assert_return(ret, -EINVAL);
55
56         fd = socket(PF_INET, SOCK_DGRAM, 0);
57         if (fd < 0) {
58                 return -errno;
59         }
60
61         *ret = fd;
62
63         return 0;
64 }
65
66 int ethtool_get_driver(int *fd, const char *ifname, char **ret) {
67         struct ethtool_drvinfo ecmd = {
68                 .cmd = ETHTOOL_GDRVINFO
69         };
70         struct ifreq ifr = {
71                 .ifr_data = (void*) &ecmd
72         };
73         char *d;
74         int r;
75
76         if (*fd < 0) {
77                 r = ethtool_connect(fd);
78                 if (r < 0)
79                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
80         }
81
82         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
83
84         r = ioctl(*fd, SIOCETHTOOL, &ifr);
85         if (r < 0)
86                 return -errno;
87
88         d = strdup(ecmd.driver);
89         if (!d)
90                 return -ENOMEM;
91
92         *ret = d;
93         return 0;
94 }
95
96 int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex)
97 {
98         struct ethtool_cmd ecmd = {
99                 .cmd = ETHTOOL_GSET
100         };
101         struct ifreq ifr = {
102                 .ifr_data = (void*) &ecmd
103         };
104         bool need_update = false;
105         int r;
106
107         if (speed == 0 && duplex == _DUP_INVALID)
108                 return 0;
109
110         if (*fd < 0) {
111                 r = ethtool_connect(fd);
112                 if (r < 0)
113                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
114         }
115
116         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
117
118         r = ioctl(*fd, SIOCETHTOOL, &ifr);
119         if (r < 0)
120                 return -errno;
121
122         if (ethtool_cmd_speed(&ecmd) != speed) {
123                 ethtool_cmd_speed_set(&ecmd, speed);
124                 need_update = true;
125         }
126
127         switch (duplex) {
128                 case DUP_HALF:
129                         if (ecmd.duplex != DUPLEX_HALF) {
130                                 ecmd.duplex = DUPLEX_HALF;
131                                 need_update = true;
132                         }
133                         break;
134                 case DUP_FULL:
135                         if (ecmd.duplex != DUPLEX_FULL) {
136                                 ecmd.duplex = DUPLEX_FULL;
137                                 need_update = true;
138                         }
139                         break;
140                 default:
141                         break;
142         }
143
144         if (need_update) {
145                 ecmd.cmd = ETHTOOL_SSET;
146
147                 r = ioctl(*fd, SIOCETHTOOL, &ifr);
148                 if (r < 0)
149                         return -errno;
150         }
151
152         return 0;
153 }
154
155 int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
156         struct ethtool_wolinfo ecmd = {
157                 .cmd = ETHTOOL_GWOL
158         };
159         struct ifreq ifr = {
160                 .ifr_data = (void*) &ecmd
161         };
162         bool need_update = false;
163         int r;
164
165         if (wol == _WOL_INVALID)
166                 return 0;
167
168         if (*fd < 0) {
169                 r = ethtool_connect(fd);
170                 if (r < 0)
171                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
172         }
173
174         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
175
176         r = ioctl(*fd, SIOCETHTOOL, &ifr);
177         if (r < 0)
178                 return -errno;
179
180         switch (wol) {
181                 case WOL_PHY:
182                         if (ecmd.wolopts != WAKE_PHY) {
183                                 ecmd.wolopts = WAKE_PHY;
184                                 need_update = true;
185                         }
186                         break;
187                 case WOL_MAGIC:
188                         if (ecmd.wolopts != WAKE_MAGIC) {
189                                 ecmd.wolopts = WAKE_MAGIC;
190                                 need_update = true;
191                         }
192                         break;
193                 case WOL_OFF:
194                         if (ecmd.wolopts != 0) {
195                                 ecmd.wolopts = 0;
196                                 need_update = true;
197                         }
198                         break;
199                 default:
200                         break;
201         }
202
203         if (need_update) {
204                 ecmd.cmd = ETHTOOL_SWOL;
205
206                 r = ioctl(*fd, SIOCETHTOOL, &ifr);
207                 if (r < 0)
208                         return -errno;
209         }
210
211         return 0;
212 }