File: | build-scan/../src/udev/net/ethtool-util.c |
Warning: | line 560, column 19 Potential leak of memory pointed to by 'u' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include <net/if.h> | |||
4 | #include <sys/ioctl.h> | |||
5 | #include <linux1/ethtool.h> | |||
6 | #include <linux1/sockios.h> | |||
7 | ||||
8 | #include "conf-parser.h" | |||
9 | #include "ethtool-util.h" | |||
10 | #include "link-config.h" | |||
11 | #include "log.h" | |||
12 | #include "missing.h" | |||
13 | #include "socket-util.h" | |||
14 | #include "string-table.h" | |||
15 | #include "strxcpyx.h" | |||
16 | #include "util.h" | |||
17 | ||||
18 | static const char* const duplex_table[_DUP_MAX] = { | |||
19 | [DUP_FULL] = "full", | |||
20 | [DUP_HALF] = "half" | |||
21 | }; | |||
22 | ||||
23 | DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex)const char *duplex_to_string(Duplex i) { if (i < 0 || i >= (Duplex) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(duplex_table), typeof(&*(duplex_table))), sizeof( duplex_table)/sizeof((duplex_table)[0]), ((void)0)))) return ( (void*)0); return duplex_table[i]; } Duplex duplex_from_string (const char *s) { return (Duplex) string_table_lookup(duplex_table , __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(duplex_table), typeof(&*(duplex_table))), sizeof( duplex_table)/sizeof((duplex_table)[0]), ((void)0))), s); }; | |||
24 | DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting")int config_parse_duplex(const char *unit, const char *filename , unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Duplex *i = data, x; do { if ((__builtin_expect (!!(!(filename)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("filename"), "../src/udev/net/ethtool-util.c", 24, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(lvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("lvalue"), "../src/udev/net/ethtool-util.c" , 24, __PRETTY_FUNCTION__); } while (0); do { if ((__builtin_expect (!!(!(rvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ( "rvalue"), "../src/udev/net/ethtool-util.c", 24, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(data)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("data"), "../src/udev/net/ethtool-util.c", 24 , __PRETTY_FUNCTION__); } while (0); x = duplex_from_string(rvalue ); if (x < 0) { ({ int _level = (3), _e = (0); (log_get_max_level_realm (LOG_REALM_UDEV) >= ((_level) & 0x07)) ? log_syntax_internal (unit, _level, filename, line, _e, "../src/udev/net/ethtool-util.c" , 24, __func__, "Failed to parse duplex setting" ", ignoring: %s" , rvalue) : -abs(_e); }); return 0; } *i = x; return 0; }; | |||
25 | ||||
26 | static const char* const wol_table[_WOL_MAX] = { | |||
27 | [WOL_PHY] = "phy", | |||
28 | [WOL_UCAST] = "unicast", | |||
29 | [WOL_MCAST] = "multicast", | |||
30 | [WOL_BCAST] = "broadcast", | |||
31 | [WOL_ARP] = "arp", | |||
32 | [WOL_MAGIC] = "magic", | |||
33 | [WOL_MAGICSECURE] = "secureon", | |||
34 | [WOL_OFF] = "off" | |||
35 | }; | |||
36 | ||||
37 | DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan)const char *wol_to_string(WakeOnLan i) { if (i < 0 || i >= (WakeOnLan) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(wol_table), typeof(&*(wol_table))), sizeof(wol_table )/sizeof((wol_table)[0]), ((void)0)))) return ((void*)0); return wol_table[i]; } WakeOnLan wol_from_string(const char *s) { return (WakeOnLan) string_table_lookup(wol_table, __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(wol_table), typeof(& *(wol_table))), sizeof(wol_table)/sizeof((wol_table)[0]), ((void )0))), s); }; | |||
38 | DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting")int config_parse_wol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata ) { WakeOnLan *i = data, x; do { if ((__builtin_expect(!!(!(filename )),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("filename"), "../src/udev/net/ethtool-util.c", 38, __PRETTY_FUNCTION__); } while (0); do { if ((__builtin_expect(!!(!(lvalue)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("lvalue"), "../src/udev/net/ethtool-util.c" , 38, __PRETTY_FUNCTION__); } while (0); do { if ((__builtin_expect (!!(!(rvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ( "rvalue"), "../src/udev/net/ethtool-util.c", 38, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(data)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("data"), "../src/udev/net/ethtool-util.c", 38 , __PRETTY_FUNCTION__); } while (0); x = wol_from_string(rvalue ); if (x < 0) { ({ int _level = (3), _e = (0); (log_get_max_level_realm (LOG_REALM_UDEV) >= ((_level) & 0x07)) ? log_syntax_internal (unit, _level, filename, line, _e, "../src/udev/net/ethtool-util.c" , 38, __func__, "Failed to parse WakeOnLan setting" ", ignoring: %s" , rvalue) : -abs(_e); }); return 0; } *i = x; return 0; }; | |||
39 | ||||
40 | static const char* const port_table[_NET_DEV_PORT_MAX] = { | |||
41 | [NET_DEV_PORT_TP] = "tp", | |||
42 | [NET_DEV_PORT_AUI] = "aui", | |||
43 | [NET_DEV_PORT_MII] = "mii", | |||
44 | [NET_DEV_PORT_FIBRE] = "fibre", | |||
45 | [NET_DEV_PORT_BNC] = "bnc" | |||
46 | }; | |||
47 | ||||
48 | DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort)const char *port_to_string(NetDevPort i) { if (i < 0 || i >= (NetDevPort) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(port_table), typeof(&*(port_table))), sizeof(port_table )/sizeof((port_table)[0]), ((void)0)))) return ((void*)0); return port_table[i]; } NetDevPort port_from_string(const char *s) { return (NetDevPort) string_table_lookup(port_table, __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (port_table), typeof(&*(port_table))), sizeof(port_table) /sizeof((port_table)[0]), ((void)0))), s); }; | |||
49 | DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting")int config_parse_port(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { NetDevPort *i = data, x; do { if ((__builtin_expect (!!(!(filename)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("filename"), "../src/udev/net/ethtool-util.c", 49, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(lvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("lvalue"), "../src/udev/net/ethtool-util.c" , 49, __PRETTY_FUNCTION__); } while (0); do { if ((__builtin_expect (!!(!(rvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ( "rvalue"), "../src/udev/net/ethtool-util.c", 49, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(data)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("data"), "../src/udev/net/ethtool-util.c", 49 , __PRETTY_FUNCTION__); } while (0); x = port_from_string(rvalue ); if (x < 0) { ({ int _level = (3), _e = (0); (log_get_max_level_realm (LOG_REALM_UDEV) >= ((_level) & 0x07)) ? log_syntax_internal (unit, _level, filename, line, _e, "../src/udev/net/ethtool-util.c" , 49, __func__, "Failed to parse Port setting" ", ignoring: %s" , rvalue) : -abs(_e); }); return 0; } *i = x; return 0; }; | |||
50 | ||||
51 | static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { | |||
52 | [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", | |||
53 | [NET_DEV_FEAT_GRO] = "rx-gro", | |||
54 | [NET_DEV_FEAT_LRO] = "rx-lro", | |||
55 | [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation", | |||
56 | [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation", | |||
57 | }; | |||
58 | ||||
59 | int ethtool_connect(int *ret) { | |||
60 | int fd; | |||
61 | ||||
62 | assert_return(ret, -EINVAL)do { if (!(((__builtin_expect(!!(ret),1))) ? (1) : (log_assert_failed_return_realm (LOG_REALM_UDEV, ("ret"), "../src/udev/net/ethtool-util.c", 62 , __PRETTY_FUNCTION__), 0))) return (-22); } while (0); | |||
63 | ||||
64 | fd = socket_ioctl_fd(); | |||
65 | if (fd < 0) | |||
66 | return fd; | |||
67 | ||||
68 | *ret = fd; | |||
69 | ||||
70 | return 0; | |||
71 | } | |||
72 | ||||
73 | int ethtool_get_driver(int *fd, const char *ifname, char **ret) { | |||
74 | struct ethtool_drvinfo ecmd = { | |||
75 | .cmd = ETHTOOL_GDRVINFO0x00000003 | |||
76 | }; | |||
77 | struct ifreq ifr = { | |||
78 | .ifr_dataifr_ifru.ifru_data = (void*) &ecmd | |||
79 | }; | |||
80 | char *d; | |||
81 | int r; | |||
82 | ||||
83 | if (*fd < 0) { | |||
84 | r = ethtool_connect(fd); | |||
85 | if (r < 0) | |||
86 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 86, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); }); | |||
87 | } | |||
88 | ||||
89 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
90 | ||||
91 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
92 | if (r < 0) | |||
93 | return -errno(*__errno_location ()); | |||
94 | ||||
95 | d = strdup(ecmd.driver); | |||
96 | if (!d) | |||
97 | return -ENOMEM12; | |||
98 | ||||
99 | *ret = d; | |||
100 | return 0; | |||
101 | } | |||
102 | ||||
103 | int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) { | |||
104 | struct ethtool_cmd ecmd = { | |||
105 | .cmd = ETHTOOL_GSET0x00000001 | |||
106 | }; | |||
107 | struct ifreq ifr = { | |||
108 | .ifr_dataifr_ifru.ifru_data = (void*) &ecmd | |||
109 | }; | |||
110 | bool_Bool need_update = false0; | |||
111 | int r; | |||
112 | ||||
113 | if (speed == 0 && duplex == _DUP_INVALID) | |||
114 | return 0; | |||
115 | ||||
116 | if (*fd < 0) { | |||
117 | r = ethtool_connect(fd); | |||
118 | if (r < 0) | |||
119 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 119, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); }); | |||
120 | } | |||
121 | ||||
122 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
123 | ||||
124 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
125 | if (r < 0) | |||
126 | return -errno(*__errno_location ()); | |||
127 | ||||
128 | if (ethtool_cmd_speed(&ecmd) != speed) { | |||
129 | ethtool_cmd_speed_set(&ecmd, speed); | |||
130 | need_update = true1; | |||
131 | } | |||
132 | ||||
133 | switch (duplex) { | |||
134 | case DUP_HALF: | |||
135 | if (ecmd.duplex != DUPLEX_HALF0x00) { | |||
136 | ecmd.duplex = DUPLEX_HALF0x00; | |||
137 | need_update = true1; | |||
138 | } | |||
139 | break; | |||
140 | case DUP_FULL: | |||
141 | if (ecmd.duplex != DUPLEX_FULL0x01) { | |||
142 | ecmd.duplex = DUPLEX_FULL0x01; | |||
143 | need_update = true1; | |||
144 | } | |||
145 | break; | |||
146 | default: | |||
147 | break; | |||
148 | } | |||
149 | ||||
150 | if (need_update) { | |||
151 | ecmd.cmd = ETHTOOL_SSET0x00000002; | |||
152 | ||||
153 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
154 | if (r < 0) | |||
155 | return -errno(*__errno_location ()); | |||
156 | } | |||
157 | ||||
158 | return 0; | |||
159 | } | |||
160 | ||||
161 | int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { | |||
162 | struct ethtool_wolinfo ecmd = { | |||
163 | .cmd = ETHTOOL_GWOL0x00000005 | |||
164 | }; | |||
165 | struct ifreq ifr = { | |||
166 | .ifr_dataifr_ifru.ifru_data = (void*) &ecmd | |||
167 | }; | |||
168 | bool_Bool need_update = false0; | |||
169 | int r; | |||
170 | ||||
171 | if (wol == _WOL_INVALID) | |||
172 | return 0; | |||
173 | ||||
174 | if (*fd < 0) { | |||
175 | r = ethtool_connect(fd); | |||
176 | if (r < 0) | |||
177 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 177, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); }); | |||
178 | } | |||
179 | ||||
180 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
181 | ||||
182 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
183 | if (r < 0) | |||
184 | return -errno(*__errno_location ()); | |||
185 | ||||
186 | switch (wol) { | |||
187 | case WOL_PHY: | |||
188 | if (ecmd.wolopts != WAKE_PHY(1 << 0)) { | |||
189 | ecmd.wolopts = WAKE_PHY(1 << 0); | |||
190 | need_update = true1; | |||
191 | } | |||
192 | break; | |||
193 | case WOL_UCAST: | |||
194 | if (ecmd.wolopts != WAKE_UCAST(1 << 1)) { | |||
195 | ecmd.wolopts = WAKE_UCAST(1 << 1); | |||
196 | need_update = true1; | |||
197 | } | |||
198 | break; | |||
199 | case WOL_MCAST: | |||
200 | if (ecmd.wolopts != WAKE_MCAST(1 << 2)) { | |||
201 | ecmd.wolopts = WAKE_MCAST(1 << 2); | |||
202 | need_update = true1; | |||
203 | } | |||
204 | break; | |||
205 | case WOL_BCAST: | |||
206 | if (ecmd.wolopts != WAKE_BCAST(1 << 3)) { | |||
207 | ecmd.wolopts = WAKE_BCAST(1 << 3); | |||
208 | need_update = true1; | |||
209 | } | |||
210 | break; | |||
211 | case WOL_ARP: | |||
212 | if (ecmd.wolopts != WAKE_ARP(1 << 4)) { | |||
213 | ecmd.wolopts = WAKE_ARP(1 << 4); | |||
214 | need_update = true1; | |||
215 | } | |||
216 | break; | |||
217 | case WOL_MAGIC: | |||
218 | if (ecmd.wolopts != WAKE_MAGIC(1 << 5)) { | |||
219 | ecmd.wolopts = WAKE_MAGIC(1 << 5); | |||
220 | need_update = true1; | |||
221 | } | |||
222 | break; | |||
223 | case WOL_MAGICSECURE: | |||
224 | if (ecmd.wolopts != WAKE_MAGICSECURE(1 << 6)) { | |||
225 | ecmd.wolopts = WAKE_MAGICSECURE(1 << 6); | |||
226 | need_update = true1; | |||
227 | } | |||
228 | break; | |||
229 | case WOL_OFF: | |||
230 | if (ecmd.wolopts != 0) { | |||
231 | ecmd.wolopts = 0; | |||
232 | need_update = true1; | |||
233 | } | |||
234 | break; | |||
235 | default: | |||
236 | break; | |||
237 | } | |||
238 | ||||
239 | if (need_update) { | |||
240 | ecmd.cmd = ETHTOOL_SWOL0x00000006; | |||
241 | ||||
242 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
243 | if (r < 0) | |||
244 | return -errno(*__errno_location ()); | |||
245 | } | |||
246 | ||||
247 | return 0; | |||
248 | } | |||
249 | ||||
250 | static int get_stringset(int fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) { | |||
251 | _cleanup_free___attribute__((cleanup(freep))) struct ethtool_gstrings *strings = NULL((void*)0); | |||
252 | struct { | |||
253 | struct ethtool_sset_info info; | |||
254 | uint32_t space; | |||
255 | } buffer = { | |||
256 | .info = { | |||
257 | .cmd = ETHTOOL_GSSET_INFO0x00000037, | |||
258 | .sset_mask = UINT64_C(1)1UL << stringset_id, | |||
259 | }, | |||
260 | }; | |||
261 | unsigned len; | |||
262 | int r; | |||
263 | ||||
264 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &buffer.info; | |||
265 | ||||
266 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
267 | if (r < 0) | |||
268 | return -errno(*__errno_location ()); | |||
269 | ||||
270 | if (!buffer.info.sset_mask) | |||
271 | return -EINVAL22; | |||
272 | ||||
273 | len = buffer.info.data[0]; | |||
274 | ||||
275 | strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN)(calloc(1, (sizeof(struct ethtool_gstrings) + len * 32))); | |||
276 | if (!strings) | |||
277 | return -ENOMEM12; | |||
278 | ||||
279 | strings->cmd = ETHTOOL_GSTRINGS0x0000001b; | |||
280 | strings->string_set = stringset_id; | |||
281 | strings->len = len; | |||
282 | ||||
283 | ifr->ifr_dataifr_ifru.ifru_data = (void *) strings; | |||
284 | ||||
285 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
286 | if (r < 0) | |||
287 | return -errno(*__errno_location ()); | |||
288 | ||||
289 | *gstrings = TAKE_PTR(strings)({ typeof(strings) _ptr_ = (strings); (strings) = ((void*)0); _ptr_; }); | |||
290 | ||||
291 | return 0; | |||
292 | } | |||
293 | ||||
294 | static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) { | |||
295 | unsigned i; | |||
296 | ||||
297 | for (i = 0; i < strings->len; i++) { | |||
298 | if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature)(strcmp(((char *) &strings->data[i * 32]),(feature)) == 0)) | |||
299 | return i; | |||
300 | } | |||
301 | ||||
302 | return -1; | |||
303 | } | |||
304 | ||||
305 | int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { | |||
306 | _cleanup_free___attribute__((cleanup(freep))) struct ethtool_gstrings *strings = NULL((void*)0); | |||
307 | struct ethtool_sfeatures *sfeatures; | |||
308 | int block, bit, i, r; | |||
309 | struct ifreq ifr = {}; | |||
310 | ||||
311 | if (*fd < 0) { | |||
312 | r = ethtool_connect(fd); | |||
313 | if (r < 0) | |||
314 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 314, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); }); | |||
315 | } | |||
316 | ||||
317 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
318 | ||||
319 | r = get_stringset(*fd, &ifr, ETH_SS_FEATURES, &strings); | |||
320 | if (r < 0) | |||
321 | return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 321, __func__, "link_config: could not get ethtool features for %s" , ifname) : -abs(_e); }); | |||
322 | ||||
323 | sfeatures = alloca0(sizeof(struct ethtool_gstrings) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]))({ char *_new_; size_t _len_ = sizeof(struct ethtool_gstrings ) + ({ const typeof((strings->len)) __unique_prefix_X50 = ( (strings->len)); const typeof((32U)) __unique_prefix_Y51 = ((32U)); (__unique_prefix_X50 / __unique_prefix_Y51 + !!(__unique_prefix_X50 % __unique_prefix_Y51)); }) * sizeof(sfeatures->features[ 0]); _new_ = __builtin_alloca (_len_); (void *) memset(_new_, 0, _len_); }); | |||
324 | sfeatures->cmd = ETHTOOL_SFEATURES0x0000003b; | |||
325 | sfeatures->size = DIV_ROUND_UP(strings->len, 32U)({ const typeof((strings->len)) __unique_prefix_X52 = ((strings ->len)); const typeof((32U)) __unique_prefix_Y53 = ((32U)) ; (__unique_prefix_X52 / __unique_prefix_Y53 + !!(__unique_prefix_X52 % __unique_prefix_Y53)); }); | |||
326 | ||||
327 | for (i = 0; i < _NET_DEV_FEAT_MAX; i++) { | |||
328 | ||||
329 | if (features[i] != -1) { | |||
330 | ||||
331 | r = find_feature_index(strings, netdev_feature_table[i]); | |||
332 | if (r < 0) { | |||
333 | log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i])({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 333, __func__, "link_config: could not find feature: %s" , netdev_feature_table[i]) : -abs(_e); }); | |||
334 | continue; | |||
335 | } | |||
336 | ||||
337 | block = r / 32; | |||
338 | bit = r % 32; | |||
339 | ||||
340 | sfeatures->features[block].valid |= 1 << bit; | |||
341 | ||||
342 | if (features[i]) | |||
343 | sfeatures->features[block].requested |= 1 << bit; | |||
344 | else | |||
345 | sfeatures->features[block].requested &= ~(1 << bit); | |||
346 | } | |||
347 | } | |||
348 | ||||
349 | ifr.ifr_dataifr_ifru.ifru_data = (void *) sfeatures; | |||
350 | ||||
351 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
352 | if (r < 0) | |||
353 | return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 353, __func__, "link_config: could not set ethtool features for %s" , ifname) : -abs(_e); }); | |||
354 | ||||
355 | return 0; | |||
356 | } | |||
357 | ||||
358 | static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **g) { | |||
359 | struct ecmd { | |||
360 | struct ethtool_link_settings req; | |||
361 | __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32(127)]; | |||
362 | } ecmd = { | |||
363 | .req.cmd = ETHTOOL_GLINKSETTINGS0x0000004c, | |||
364 | }; | |||
365 | struct ethtool_link_usettings *u; | |||
366 | unsigned offset; | |||
367 | int r; | |||
368 | ||||
369 | /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS | |||
370 | handshake first to agree on the length of the link mode bitmaps. If kernel doesn't | |||
371 | agree with user, it returns the bitmap length it is expecting from user as a negative | |||
372 | length (and cmd field is 0). When kernel and user agree, kernel returns valid info in | |||
373 | all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on | |||
374 | https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf | |||
375 | */ | |||
376 | ||||
377 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
378 | ||||
379 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
380 | if (r < 0) | |||
381 | return -errno(*__errno_location ()); | |||
382 | ||||
383 | if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS0x0000004c) | |||
384 | return -EOPNOTSUPP95; | |||
385 | ||||
386 | ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; | |||
387 | ||||
388 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
389 | ||||
390 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
391 | if (r < 0) | |||
392 | return -errno(*__errno_location ()); | |||
393 | ||||
394 | if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS0x0000004c) | |||
395 | return -EOPNOTSUPP95; | |||
396 | ||||
397 | u = new0(struct ethtool_link_usettings , 1)((struct ethtool_link_usettings*) calloc((1), sizeof(struct ethtool_link_usettings ))); | |||
398 | if (!u) | |||
399 | return -ENOMEM12; | |||
400 | ||||
401 | u->base = ecmd.req; | |||
402 | ||||
403 | offset = 0; | |||
404 | memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |||
405 | ||||
406 | offset += ecmd.req.link_mode_masks_nwords; | |||
407 | memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |||
408 | ||||
409 | offset += ecmd.req.link_mode_masks_nwords; | |||
410 | memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |||
411 | ||||
412 | *g = u; | |||
413 | ||||
414 | return 0; | |||
415 | } | |||
416 | ||||
417 | static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **u) { | |||
418 | struct ethtool_link_usettings *e; | |||
419 | struct ethtool_cmd ecmd = { | |||
420 | .cmd = ETHTOOL_GSET0x00000001, | |||
421 | }; | |||
422 | int r; | |||
423 | ||||
424 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
425 | ||||
426 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
427 | if (r < 0) | |||
428 | return -errno(*__errno_location ()); | |||
429 | ||||
430 | e = new0(struct ethtool_link_usettings, 1)((struct ethtool_link_usettings*) calloc((1), sizeof(struct ethtool_link_usettings ))); | |||
431 | if (!e) | |||
432 | return -ENOMEM12; | |||
433 | ||||
434 | e->base.cmd = ETHTOOL_GSET0x00000001; | |||
435 | ||||
436 | e->base.link_mode_masks_nwords = 1; | |||
437 | e->base.speed = ethtool_cmd_speed(&ecmd); | |||
438 | e->base.duplex = ecmd.duplex; | |||
439 | e->base.port = ecmd.port; | |||
440 | e->base.phy_address = ecmd.phy_address; | |||
441 | e->base.autoneg = ecmd.autoneg; | |||
442 | e->base.mdio_support = ecmd.mdio_support; | |||
443 | ||||
444 | e->link_modes.supported[0] = ecmd.supported; | |||
445 | e->link_modes.advertising[0] = ecmd.advertising; | |||
446 | e->link_modes.lp_advertising[0] = ecmd.lp_advertising; | |||
447 | ||||
448 | *u = e; | |||
449 | ||||
450 | return 0; | |||
451 | } | |||
452 | ||||
453 | static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { | |||
454 | struct { | |||
455 | struct ethtool_link_settings req; | |||
456 | __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32(127)]; | |||
457 | } ecmd = {}; | |||
458 | unsigned int offset; | |||
459 | int r; | |||
460 | ||||
461 | if (u->base.cmd != ETHTOOL_GLINKSETTINGS0x0000004c || u->base.link_mode_masks_nwords <= 0) | |||
462 | return -EINVAL22; | |||
463 | ||||
464 | ecmd.req = u->base; | |||
465 | ecmd.req.cmd = ETHTOOL_SLINKSETTINGS0x0000004d; | |||
466 | offset = 0; | |||
467 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords); | |||
468 | ||||
469 | offset += ecmd.req.link_mode_masks_nwords; | |||
470 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords); | |||
471 | ||||
472 | offset += ecmd.req.link_mode_masks_nwords; | |||
473 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords); | |||
474 | ||||
475 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
476 | ||||
477 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
478 | if (r < 0) | |||
479 | return -errno(*__errno_location ()); | |||
480 | ||||
481 | return 0; | |||
482 | } | |||
483 | ||||
484 | static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { | |||
485 | struct ethtool_cmd ecmd = { | |||
486 | .cmd = ETHTOOL_SSET0x00000002, | |||
487 | }; | |||
488 | int r; | |||
489 | ||||
490 | if (u->base.cmd != ETHTOOL_GSET0x00000001 || u->base.link_mode_masks_nwords <= 0) | |||
491 | return -EINVAL22; | |||
492 | ||||
493 | ecmd.supported = u->link_modes.supported[0]; | |||
494 | ecmd.advertising = u->link_modes.advertising[0]; | |||
495 | ecmd.lp_advertising = u->link_modes.lp_advertising[0]; | |||
496 | ||||
497 | ethtool_cmd_speed_set(&ecmd, u->base.speed); | |||
498 | ||||
499 | ecmd.duplex = u->base.duplex; | |||
500 | ecmd.port = u->base.port; | |||
501 | ecmd.phy_address = u->base.phy_address; | |||
502 | ecmd.autoneg = u->base.autoneg; | |||
503 | ecmd.mdio_support = u->base.mdio_support; | |||
504 | ||||
505 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
506 | ||||
507 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
508 | if (r < 0) | |||
509 | return -errno(*__errno_location ()); | |||
510 | ||||
511 | return 0; | |||
512 | } | |||
513 | ||||
514 | /* If autonegotiation is disabled, the speed and duplex represent the fixed link | |||
515 | * mode and are writable if the driver supports multiple link modes. If it is | |||
516 | * enabled then they are read-only. If the link is up they represent the negotiated | |||
517 | * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest | |||
518 | * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode. | |||
519 | */ | |||
520 | ||||
521 | int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link) { | |||
522 | _cleanup_free___attribute__((cleanup(freep))) struct ethtool_link_usettings *u = NULL((void*)0); | |||
523 | struct ifreq ifr = {}; | |||
524 | int r; | |||
525 | ||||
526 | if (link->autonegotiation != 0) { | |||
| ||||
527 | log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable.")({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/net/ethtool-util.c", 527, __func__, "link_config: autonegotiation is unset or enabled, the speed and duplex are not writable." ) : -abs(_e); }); | |||
528 | return 0; | |||
529 | } | |||
530 | ||||
531 | if (*fd < 0) { | |||
532 | r = ethtool_connect(fd); | |||
533 | if (r < 0) | |||
534 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 534, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); }); | |||
535 | } | |||
536 | ||||
537 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
538 | ||||
539 | r = get_glinksettings(*fd, &ifr, &u); | |||
540 | if (r < 0) { | |||
541 | r = get_gset(*fd, &ifr, &u); | |||
542 | if (r
| |||
543 | return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 543, __func__, "link_config: Cannot get device settings for %s : %m" , ifname) : -abs(_e); }); | |||
544 | } | |||
545 | ||||
546 | if (link->speed) | |||
547 | u->base.speed = DIV_ROUND_UP(link->speed, 1000000)({ const typeof((link->speed)) __unique_prefix_X54 = ((link ->speed)); const typeof((1000000)) __unique_prefix_Y55 = ( (1000000)); (__unique_prefix_X54 / __unique_prefix_Y55 + !!(__unique_prefix_X54 % __unique_prefix_Y55)); }); | |||
548 | ||||
549 | if (link->duplex != _DUP_INVALID) | |||
550 | u->base.duplex = link->duplex; | |||
551 | ||||
552 | if (link->port != _NET_DEV_PORT_INVALID) | |||
553 | u->base.port = link->port; | |||
554 | ||||
555 | u->base.autoneg = link->autonegotiation; | |||
556 | ||||
557 | if (u->base.cmd
| |||
558 | r = set_slinksettings(*fd, &ifr, u); | |||
559 | else | |||
560 | r = set_sset(*fd, &ifr, u); | |||
| ||||
561 | if (r < 0) | |||
562 | return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 562, __func__, "link_config: Cannot set device settings for %s : %m" , ifname) : -abs(_e); }); | |||
563 | ||||
564 | return r; | |||
565 | } | |||
566 | ||||
567 | int config_parse_channel(const char *unit, | |||
568 | const char *filename, | |||
569 | unsigned line, | |||
570 | const char *section, | |||
571 | unsigned section_line, | |||
572 | const char *lvalue, | |||
573 | int ltype, | |||
574 | const char *rvalue, | |||
575 | void *data, | |||
576 | void *userdata) { | |||
577 | link_config *config = data; | |||
578 | uint32_t k; | |||
579 | int r; | |||
580 | ||||
581 | assert(filename)do { if ((__builtin_expect(!!(!(filename)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("filename"), "../src/udev/net/ethtool-util.c" , 581, __PRETTY_FUNCTION__); } while (0); | |||
582 | assert(section)do { if ((__builtin_expect(!!(!(section)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("section"), "../src/udev/net/ethtool-util.c" , 582, __PRETTY_FUNCTION__); } while (0); | |||
583 | assert(lvalue)do { if ((__builtin_expect(!!(!(lvalue)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("lvalue"), "../src/udev/net/ethtool-util.c" , 583, __PRETTY_FUNCTION__); } while (0); | |||
584 | assert(rvalue)do { if ((__builtin_expect(!!(!(rvalue)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("rvalue"), "../src/udev/net/ethtool-util.c" , 584, __PRETTY_FUNCTION__); } while (0); | |||
585 | assert(data)do { if ((__builtin_expect(!!(!(data)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("data"), "../src/udev/net/ethtool-util.c", 585 , __PRETTY_FUNCTION__); } while (0); | |||
586 | ||||
587 | r = safe_atou32(rvalue, &k); | |||
588 | if (r < 0) { | |||
589 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse channel value, ignoring: %s", rvalue)({ int _level = (3), _e = (r); (log_get_max_level_realm(LOG_REALM_UDEV ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/udev/net/ethtool-util.c", 589, __func__ , "Failed to parse channel value, ignoring: %s", rvalue) : -abs (_e); }); | |||
590 | return 0; | |||
591 | } | |||
592 | ||||
593 | if (k < 1) { | |||
594 | log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid %s value, ignoring: %s", lvalue, rvalue)({ int _level = (3), _e = (-22); (log_get_max_level_realm(LOG_REALM_UDEV ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/udev/net/ethtool-util.c", 594, __func__ , "Invalid %s value, ignoring: %s", lvalue, rvalue) : -abs(_e ); }); | |||
595 | return 0; | |||
596 | } | |||
597 | ||||
598 | if (streq(lvalue, "RxChannels")(strcmp((lvalue),("RxChannels")) == 0)) { | |||
599 | config->channels.rx_count = k; | |||
600 | config->channels.rx_count_set = true1; | |||
601 | } else if (streq(lvalue, "TxChannels")(strcmp((lvalue),("TxChannels")) == 0)) { | |||
602 | config->channels.tx_count = k; | |||
603 | config->channels.tx_count_set = true1; | |||
604 | } else if (streq(lvalue, "OtherChannels")(strcmp((lvalue),("OtherChannels")) == 0)) { | |||
605 | config->channels.other_count = k; | |||
606 | config->channels.other_count_set = true1; | |||
607 | } else if (streq(lvalue, "CombinedChannels")(strcmp((lvalue),("CombinedChannels")) == 0)) { | |||
608 | config->channels.combined_count = k; | |||
609 | config->channels.combined_count_set = true1; | |||
610 | } | |||
611 | ||||
612 | return 0; | |||
613 | } | |||
614 | ||||
615 | int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) { | |||
616 | struct ethtool_channels ecmd = { | |||
617 | .cmd = ETHTOOL_GCHANNELS0x0000003c | |||
618 | }; | |||
619 | struct ifreq ifr = { | |||
620 | .ifr_dataifr_ifru.ifru_data = (void*) &ecmd | |||
621 | }; | |||
622 | ||||
623 | bool_Bool need_update = false0; | |||
624 | int r; | |||
625 | ||||
626 | if (*fd < 0) { | |||
627 | r = ethtool_connect(fd); | |||
628 | if (r < 0) | |||
629 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 629, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); }); | |||
630 | } | |||
631 | ||||
632 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
633 | ||||
634 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
635 | if (r < 0) | |||
636 | return -errno(*__errno_location ()); | |||
637 | ||||
638 | if (channels->rx_count_set && ecmd.rx_count != channels->rx_count) { | |||
639 | ecmd.rx_count = channels->rx_count; | |||
640 | need_update = true1; | |||
641 | } | |||
642 | ||||
643 | if (channels->tx_count_set && ecmd.tx_count != channels->tx_count) { | |||
644 | ecmd.tx_count = channels->tx_count; | |||
645 | need_update = true1; | |||
646 | } | |||
647 | ||||
648 | if (channels->other_count_set && ecmd.other_count != channels->other_count) { | |||
649 | ecmd.other_count = channels->other_count; | |||
650 | need_update = true1; | |||
651 | } | |||
652 | ||||
653 | if (channels->combined_count_set && ecmd.combined_count != channels->combined_count) { | |||
654 | ecmd.combined_count = channels->combined_count; | |||
655 | need_update = true1; | |||
656 | } | |||
657 | ||||
658 | if (need_update) { | |||
659 | ecmd.cmd = ETHTOOL_SCHANNELS0x0000003d; | |||
660 | ||||
661 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
662 | if (r < 0) | |||
663 | return -errno(*__errno_location ()); | |||
664 | } | |||
665 | ||||
666 | return 0; | |||
667 | } |