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