Bug Summary

File:build-scan/../src/udev/net/ethtool-util.c
Warning:line 558, column 19
Potential leak of memory pointed to by 'u'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ethtool-util.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/udev/libudev-core.a.p -I src/udev -I ../src/udev -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I ../src/udev/net -I /usr/include/blkid -D _FILE_OFFSET_BITS=64 -D LOG_REALM=LOG_REALM_UDEV -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/udev/net/ethtool-util.c
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
18static const char* const duplex_table[_DUP_MAX] = {
19 [DUP_FULL] = "full",
20 [DUP_HALF] = "half"
21};
22
23DEFINE_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); }
;
24DEFINE_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
26static 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
37DEFINE_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); }
;
38DEFINE_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
40static 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
48DEFINE_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); }
;
49DEFINE_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
51static 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
59int 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
73int 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
103int 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
161int 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
250static 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
294static 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
305int 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_X44 = (
(strings->len)); const typeof((32U)) __unique_prefix_Y45 =
((32U)); (__unique_prefix_X44 / __unique_prefix_Y45 + !!(__unique_prefix_X44
% __unique_prefix_Y45)); }) * 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_X46 = ((strings
->len)); const typeof((32U)) __unique_prefix_Y47 = ((32U))
; (__unique_prefix_X46 / __unique_prefix_Y47 + !!(__unique_prefix_X46
% __unique_prefix_Y47)); })
;
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
358static 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)
6
Assuming 'r' is >= 0
7
Taking false branch
381 return -errno(*__errno_location ());
382
383 if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS0x0000004c)
8
Assuming field 'link_mode_masks_nwords' is < 0
9
Assuming field 'cmd' is equal to ETHTOOL_GLINKSETTINGS
10
Taking false branch
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)
11
Assuming 'r' is >= 0
12
Taking false branch
392 return -errno(*__errno_location ());
393
394 if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS0x0000004c)
13
Assuming field 'link_mode_masks_nwords' is > 0
14
Assuming field 'cmd' is equal to ETHTOOL_GLINKSETTINGS
15
Taking false branch
395 return -EOPNOTSUPP95;
396
397 u = new0(struct ethtool_link_usettings , 1)((struct ethtool_link_usettings*) calloc((1), sizeof(struct ethtool_link_usettings
)))
;
16
Memory is allocated
398 if (!u)
17
Assuming 'u' is non-null
18
Taking false branch
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
417static 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
453static 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
484static 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
521int 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) {
1
Assuming field 'autonegotiation' is equal to 0
2
Taking false branch
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) {
3
Assuming the condition is false
4
Taking false branch
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);
5
Calling 'get_glinksettings'
19
Returned allocated memory via 3rd parameter
540 if (r
19.1
'r' is >= 0
< 0) {
20
Taking false branch
541 r = get_gset(*fd, &ifr, &u);
542 if (r < 0)
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)
21
Assuming field 'speed' is 0
22
Taking false branch
547 u->base.speed = DIV_ROUND_UP(link->speed, 1000000)({ const typeof((link->speed)) __unique_prefix_X48 = ((link
->speed)); const typeof((1000000)) __unique_prefix_Y49 = (
(1000000)); (__unique_prefix_X48 / __unique_prefix_Y49 + !!(__unique_prefix_X48
% __unique_prefix_Y49)); })
;
548
549 if (link->duplex != _DUP_INVALID)
23
Assuming field 'duplex' is equal to _DUP_INVALID
24
Taking false branch
550 u->base.duplex = link->duplex;
551
552 if (link->port != _NET_DEV_PORT_INVALID)
25
Assuming field 'port' is equal to _NET_DEV_PORT_INVALID
26
Taking false branch
553 u->base.port = link->port;
554
555 u->base.autoneg = link->autonegotiation;
556
557 if (u->base.cmd
26.1
Field 'cmd' is equal to ETHTOOL_GLINKSETTINGS
== ETHTOOL_GLINKSETTINGS0x0000004c)
27
Taking true branch
558 r = set_slinksettings(*fd, &ifr, u);
28
Potential leak of memory pointed to by '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
567int 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
615int 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}