1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "device-private.h"
4 #include "hexdecoct.h"
5 #include "netlink-util.h"
6 #include "strv.h"
7 #include "udev-netlink.h"
8
link_info_clear(LinkInfo * info)9 void link_info_clear(LinkInfo *info) {
10 if (!info)
11 return;
12
13 info->ifname = mfree(info->ifname);
14 info->ifalias = mfree(info->ifalias);
15 info->phys_port_id = mfree(info->phys_port_id);
16 info->phys_switch_id = mfree(info->phys_switch_id);
17 info->phys_port_name = mfree(info->phys_port_name);
18 }
19
link_info_get(sd_netlink ** rtnl,int ifindex,LinkInfo * ret)20 int link_info_get(sd_netlink **rtnl, int ifindex, LinkInfo *ret) {
21 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
22 _cleanup_(link_info_clear) LinkInfo info = LINK_INFO_NULL;
23 uint16_t nlmsg_type, max_attr;
24 int r;
25
26 assert(rtnl);
27 assert(ifindex > 0);
28 assert(ret);
29
30 if (!*rtnl) {
31 r = sd_netlink_open(rtnl);
32 if (r < 0)
33 return r;
34 }
35
36 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
37 if (r < 0)
38 return r;
39
40 r = sd_netlink_call(*rtnl, message, 0, &reply);
41 if (r == -EINVAL)
42 return -ENODEV; /* The device does not exist */
43 if (r < 0)
44 return r;
45
46 r = sd_netlink_message_get_type(reply, &nlmsg_type);
47 if (r < 0)
48 return r;
49 if (nlmsg_type != RTM_NEWLINK)
50 return -ENXIO;
51
52 r = sd_rtnl_message_link_get_ifindex(reply, &info.ifindex);
53 if (r < 0)
54 return r;
55 if (info.ifindex != ifindex)
56 return -ENXIO;
57
58 r = sd_rtnl_message_link_get_type(reply, &info.iftype);
59 if (r < 0)
60 return r;
61
62 r = netlink_message_read_hw_addr(reply, IFLA_ADDRESS, &info.hw_addr);
63 if (r < 0 && r != -ENODATA)
64 return r;
65
66 r = netlink_message_read_hw_addr(reply, IFLA_BROADCAST, &info.broadcast);
67 if (r < 0 && r != -ENODATA)
68 return r;
69
70 r = sd_netlink_message_read_string_strdup(reply, IFLA_IFNAME, &info.ifname);
71 if (r < 0)
72 return r;
73
74 r = sd_netlink_message_read_u32(reply, IFLA_MTU, &info.mtu);
75 if (r < 0)
76 return r;
77
78 r = sd_netlink_message_read_u32(reply, IFLA_LINK, &info.iflink);
79 if (r == -ENODATA)
80 info.iflink = info.ifindex;
81 else if (r < 0)
82 return r;
83
84 r = sd_netlink_message_read_u8(reply, IFLA_LINKMODE, &info.link_mode);
85 if (r < 0)
86 return r;
87
88 r = sd_netlink_message_read_string_strdup(reply, IFLA_IFALIAS, &info.ifalias);
89 if (r < 0 && r != -ENODATA)
90 return r;
91
92 r = sd_netlink_message_read_u32(reply, IFLA_GROUP, &info.group);
93 if (r < 0)
94 return r;
95
96 r = sd_netlink_message_read_data(reply, IFLA_PHYS_PORT_ID,
97 &info.phys_port_id_len, (void**) &info.phys_port_id);
98 if (r < 0 && r != -ENODATA)
99 return r;
100
101 r = sd_netlink_message_read_data(reply, IFLA_PHYS_SWITCH_ID,
102 &info.phys_switch_id_len, (void**) &info.phys_switch_id);
103 if (r < 0 && r != -ENODATA)
104 return r;
105
106 r = sd_netlink_message_read_string_strdup(reply, IFLA_PHYS_PORT_NAME, &info.phys_port_name);
107 if (r < 0 && r != -ENODATA)
108 return r;
109
110 r = sd_netlink_message_get_max_attribute(reply, &max_attr);
111 if (r < 0)
112 return r;
113
114 info.phys_port_id_supported = max_attr >= IFLA_PHYS_PORT_ID;
115 info.phys_switch_id_supported = max_attr >= IFLA_PHYS_SWITCH_ID;
116 info.phys_port_name_supported = max_attr >= IFLA_PHYS_PORT_NAME;
117
118 *ret = info;
119 info = LINK_INFO_NULL;
120 return 0;
121 }
122
cache_unsigned(sd_device * device,const char * attr,uint64_t val)123 static int cache_unsigned(sd_device *device, const char *attr, uint64_t val) {
124 _cleanup_free_ char *str = NULL;
125 int r;
126
127 assert(device);
128 assert(attr);
129
130 if (device_get_cached_sysattr_value(device, attr, NULL) != -ESTALE)
131 return 0;
132
133 if (asprintf(&str, "%"PRIu64, val) < 0)
134 return -ENOMEM;
135
136 r = device_cache_sysattr_value(device, attr, str);
137 if (r < 0)
138 return r;
139
140 TAKE_PTR(str);
141 return 0;
142 }
143
cache_hw_addr(sd_device * device,const char * attr,const struct hw_addr_data * hw_addr)144 static int cache_hw_addr(sd_device *device, const char *attr, const struct hw_addr_data *hw_addr) {
145 _cleanup_free_ char *str = NULL;
146 int r;
147
148 assert(device);
149 assert(attr);
150 assert(hw_addr);
151
152 if (device_get_cached_sysattr_value(device, attr, NULL) != -ESTALE)
153 return 0;
154
155 str = new(char, HW_ADDR_TO_STRING_MAX);
156 if (!str)
157 return -ENOMEM;
158
159 r = device_cache_sysattr_value(device, attr, hw_addr_to_string(hw_addr, str));
160 if (r < 0)
161 return r;
162
163 TAKE_PTR(str);
164 return 0;
165 }
166
cache_binary(sd_device * device,const char * attr,size_t len,const uint8_t * data)167 static int cache_binary(sd_device *device, const char *attr, size_t len, const uint8_t *data) {
168 _cleanup_free_ char *str = NULL;
169 int r;
170
171 assert(device);
172 assert(attr);
173
174 if (device_get_cached_sysattr_value(device, attr, NULL) != -ESTALE)
175 return 0;
176
177 if (data) {
178 size_t j = 0;
179
180 str = new(char, len * 2 + 1);
181 if (!str)
182 return -ENOMEM;
183
184 for (size_t i = 0; i < len; i++) {
185 str[j++] = hexchar(data[i] >> 4);
186 str[j++] = hexchar(data[i] & 0x0f);
187 }
188
189 str[j] = '\0';
190 }
191
192 r = device_cache_sysattr_value(device, attr, str);
193 if (r < 0)
194 return r;
195
196 TAKE_PTR(str);
197 return 0;
198 }
199
cache_string(sd_device * device,const char * attr,const char * val)200 static int cache_string(sd_device *device, const char *attr, const char *val) {
201 _cleanup_free_ char *str = NULL;
202 int r;
203
204 assert(device);
205 assert(attr);
206
207 if (device_get_cached_sysattr_value(device, attr, NULL) != -ESTALE)
208 return 0;
209
210 if (val) {
211 str = strdup(val);
212 if (!str)
213 return -ENOMEM;
214 }
215
216 r = device_cache_sysattr_value(device, attr, str);
217 if (r < 0)
218 return r;
219
220 TAKE_PTR(str);
221 return 0;
222 }
223
device_cache_sysattr_from_link_info(sd_device * device,LinkInfo * info)224 int device_cache_sysattr_from_link_info(sd_device *device, LinkInfo *info) {
225 int ifindex, r;
226
227 assert(device);
228 assert(info);
229
230 r = sd_device_get_ifindex(device, &ifindex);
231 if (r < 0)
232 return r;
233
234 if (ifindex != info->ifindex)
235 return -EINVAL;
236
237 r = cache_unsigned(device, "type", info->iftype);
238 if (r < 0)
239 return r;
240
241 r = cache_unsigned(device, "addr_len", info->hw_addr.length);
242 if (r < 0)
243 return r;
244
245 r = cache_hw_addr(device, "address", &info->hw_addr);
246 if (r < 0)
247 return r;
248
249 r = cache_hw_addr(device, "broadcast", &info->broadcast);
250 if (r < 0)
251 return r;
252
253 r = cache_unsigned(device, "mtu", info->mtu);
254 if (r < 0)
255 return r;
256
257 r = cache_unsigned(device, "iflink", info->iflink);
258 if (r < 0)
259 return r;
260
261 r = cache_unsigned(device, "link_mode", info->link_mode);
262 if (r < 0)
263 return r;
264
265 r = cache_string(device, "ifalias", strempty(info->ifalias));
266 if (r < 0)
267 return r;
268
269 r = cache_unsigned(device, "netdev_group", info->group);
270 if (r < 0)
271 return r;
272
273 if (info->phys_port_id_supported) {
274 r = cache_binary(device, "phys_port_id", info->phys_port_id_len, info->phys_port_id);
275 if (r < 0)
276 return r;
277 }
278
279 if (info->phys_switch_id_supported) {
280 r = cache_binary(device, "phys_switch_id", info->phys_switch_id_len, info->phys_switch_id);
281 if (r < 0)
282 return r;
283 }
284
285 if (info->phys_port_name_supported) {
286 r = cache_string(device, "phys_port_name", info->phys_port_name);
287 if (r < 0)
288 return r;
289 }
290
291 return 0;
292 }
293
device_get_sysattr_value_maybe_from_netlink(sd_device * device,sd_netlink ** rtnl,const char * sysattr,const char ** ret_value)294 int device_get_sysattr_value_maybe_from_netlink(
295 sd_device *device,
296 sd_netlink **rtnl,
297 const char *sysattr,
298 const char **ret_value) {
299
300 _cleanup_(link_info_clear) LinkInfo info = LINK_INFO_NULL;
301 int ifindex, r;
302
303 assert(device);
304 assert(rtnl);
305 assert(sysattr);
306
307 if (sd_device_get_ifindex(device, &ifindex) < 0)
308 return sd_device_get_sysattr_value(device, sysattr, ret_value);
309
310 if (!STR_IN_SET(sysattr,
311 "type", "addr_len", "address", "broadcast", "mtu", "iflink", "linkmode",
312 "ifalias", "group", "phys_port_id", "phys_switch_id", "phys_port_name"))
313 return sd_device_get_sysattr_value(device, sysattr, ret_value);
314
315 r = device_get_cached_sysattr_value(device, sysattr, ret_value);
316 if (r != -ESTALE)
317 return r;
318
319 r = link_info_get(rtnl, ifindex, &info);
320 if (r < 0)
321 return r;
322
323 r = device_cache_sysattr_from_link_info(device, &info);
324 if (r < 0)
325 return r;
326
327 /* Do not use device_get_cached_sysattr_value() here, as kernel may not support
328 * IFLA_PHYS_PORT_NAME, and in that case we need to read the value from sysfs. */
329 return sd_device_get_sysattr_value(device, sysattr, ret_value);
330 }
331