Lely core libraries 1.9.2
rtnl.c
Go to the documentation of this file.
1
24#include "io.h"
25
26#ifdef HAVE_LINUX_RTNETLINK_H
27
28#include "rtnl.h"
29
30#include <assert.h>
31#include <errno.h>
32#include <string.h>
33
34#ifndef RTNL_BUFSIZE
35#define RTNL_BUFSIZE 8192
36#endif
37
38static int io_rtnl_getattr_func(struct ifinfomsg *ifi, struct rtattr *rta,
39 unsigned short rtalen, void *data);
40
41static int io_rtnl_recv(int fd, int (*func)(struct nlmsghdr *nlh, void *data),
42 void *data);
43static int io_rtnl_recv_ack(int fd);
44static int io_rtnl_recv_newlink(
45 int fd, io_rtnl_newlink_func_t *func, void *data);
46static int io_rtnl_recv_newlink_func(struct nlmsghdr *nlh, void *data);
47
48static ssize_t io_rtnl_send(int fd, struct iovec *iov, int iovlen);
49static ssize_t io_rtnl_send_getlink(int fd, __u32 seq, __u32 pid);
50static ssize_t io_rtnl_send_newlink(int fd, __u32 seq, __u32 pid, int ifi_index,
51 unsigned int ifi_flags, struct rtattr *rta,
52 unsigned int rtalen);
53
54int
55io_rtnl_socket(__u32 pid, __u32 groups)
56{
57 int errsv = 0;
58
59 int fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
60 if (__unlikely(fd == -1)) {
61 errsv = errno;
62 goto error_socket;
63 }
64
65 struct sockaddr_nl addr = {
66 .nl_family = AF_NETLINK, .nl_pid = pid, .nl_groups = groups
67 };
68
69 // clang-format off
70 if (__unlikely(bind(fd, (struct sockaddr *)&addr, sizeof(addr))
71 == -1)) {
72 // clang-format on
73 errsv = errno;
74 goto error_bind;
75 }
76
77 return fd;
78
79error_bind:
80 close(fd);
81error_socket:
82 errno = errsv;
83 return -1;
84}
85
86int
87io_rtnl_newlink(int fd, __u32 seq, __u32 pid, int ifi_index,
88 unsigned int ifi_flags, struct rtattr *rta,
89 unsigned short rtalen)
90{
91 // clang-format off
92 if (__unlikely(io_rtnl_send_newlink(fd, seq, pid, ifi_index, ifi_flags,
93 rta, rtalen) == -1))
94 // clang-format on
95 return -1;
96 return io_rtnl_recv_ack(fd);
97}
98
99int
100io_rtnl_getlink(int fd, __u32 seq, __u32 pid, io_rtnl_newlink_func_t *func,
101 void *data)
102{
103 if (__unlikely(io_rtnl_send_getlink(fd, seq, pid) == -1))
104 return -1;
105 return io_rtnl_recv_newlink(fd, func, data);
106}
107
108int
109io_rtnl_getattr(int fd, __u32 seq, __u32 pid, int ifi_index,
110 unsigned int *pifi_flags, unsigned short type, void *data,
111 unsigned short payload)
112{
113 assert(data || !payload);
114
115 if (__unlikely(ifi_index <= 0)) {
116 errno = ENODEV;
117 return -1;
118 }
119
120 struct {
121 int ifi_index;
122 unsigned int *pifi_flags;
123 unsigned short type;
124 void *data;
125 unsigned short payload;
126 } args = { ifi_index, pifi_flags, type, data, payload };
127
128 // clang-format off
129 if (__unlikely(io_rtnl_getlink(fd, seq, pid, &io_rtnl_getattr_func,
130 &args) == -1))
131 // clang-format on
132 return -1;
133
134 // On success, rtnl_getattr_func() sets ifi_index to 0.
135 if (__unlikely(args.ifi_index)) {
136 errno = ENODEV;
137 return -1;
138 }
139
140 // Return the actual size of the attribute payload.
141 return args.payload;
142}
143
144static int
145io_rtnl_getattr_func(struct ifinfomsg *ifi, struct rtattr *rta,
146 unsigned short rtalen, void *data)
147{
148 assert(ifi);
149 assert(rta);
150 struct {
151 int ifi_index;
152 unsigned int *pifi_flags;
153 unsigned short type;
154 void *data;
155 unsigned short payload;
156 } *pargs = data;
157 assert(pargs);
158
159 // Set ifi_index to 0 once we've found the interface. This ensures we
160 // only copy the attribute once, even if the interface occurs multiple
161 // times.
162 if (!pargs->ifi_index || pargs->ifi_index != ifi->ifi_index)
163 return 0;
164 pargs->ifi_index = 0;
165
166 if (pargs->pifi_flags)
167 *pargs->pifi_flags = ifi->ifi_flags;
168
169 rta = io_rta_find(rta, rtalen, pargs->type);
170 if (__unlikely(!rta)) {
171 errno = EOPNOTSUPP;
172 return -1;
173 }
174
175 unsigned short payload = RTA_PAYLOAD(rta);
176 if (pargs->data)
177 memcpy(pargs->data, RTA_DATA(rta),
178 MIN(pargs->payload, payload));
179 pargs->payload = payload;
180
181 return 0;
182}
183
184int
185io_rtnl_setattr(int fd, __u32 seq, __u32 pid, int ifi_index,
186 unsigned int ifi_flags, unsigned short type, const void *data,
187 unsigned short payload)
188{
189 assert(data || !payload);
190
191 char buf[RTA_SPACE(payload)];
192
193 struct rtattr *rta = (struct rtattr *)buf;
194 *rta = (struct rtattr){ .rta_len = RTA_LENGTH(payload),
195 .rta_type = type };
196 if (data)
197 memcpy(RTA_DATA(rta), data, payload);
198
199 return io_rtnl_newlink(
200 fd, seq, pid, ifi_index, ifi_flags, rta, rta->rta_len);
201}
202
203static int
204io_rtnl_recv(int fd, int (*func)(struct nlmsghdr *nlh, void *data), void *data)
205{
206 char buf[RTNL_BUFSIZE] = { 0 };
207
208 int result = 0;
209 int errsv = errno;
210
211 int done = 0;
212 while (!done) {
213 ssize_t len;
214 do {
215 errno = errsv;
216 len = recv(fd, buf, sizeof(buf), 0);
217 } while (__unlikely(len == -1 && errno == EINTR));
218 if (__unlikely(len <= 0)) {
219 if (len < 0) {
220 result = -1;
221 errsv = errno;
222 }
223 break;
224 }
225
226 struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
227 for (; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
228 if (nlh->nlmsg_type == NLMSG_DONE
229 || !(nlh->nlmsg_flags & NLM_F_MULTI))
230 done = 1;
231 if (nlh->nlmsg_type == NLMSG_ERROR) {
232 struct nlmsgerr *err = NLMSG_DATA(nlh);
233 if (__unlikely(err->error < 0 && !result)) {
234 result = -1;
235 errsv = -err->error;
236 }
237 }
238 if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
239 continue;
240 if (__unlikely(func && func(nlh, data) && !result)) {
241 result = -1;
242 errsv = errno;
243 }
244 }
245 }
246
247 errno = errsv;
248 return result;
249}
250
251static int
252io_rtnl_recv_ack(int fd)
253{
254 return io_rtnl_recv(fd, NULL, NULL);
255}
256
257static int
258io_rtnl_recv_newlink(int fd, io_rtnl_newlink_func_t *func, void *data)
259{
260 struct {
262 void *data;
263 } args = { func, data };
264
265 return io_rtnl_recv(fd, &io_rtnl_recv_newlink_func, &args);
266}
267
268static int
269io_rtnl_recv_newlink_func(struct nlmsghdr *nlh, void *data)
270{
271 assert(nlh);
272 struct {
274 void *data;
275 } *pargs = data;
276 assert(pargs);
277
278 if (nlh->nlmsg_type != RTM_NEWLINK)
279 return 0;
280
281 if (!pargs->func)
282 return 0;
283
284 return pargs->func(NLMSG_DATA(nlh), IFLA_RTA(NLMSG_DATA(nlh)),
285 IFLA_PAYLOAD(nlh), pargs->data);
286}
287
288static ssize_t
289io_rtnl_send(int fd, struct iovec *iov, int iovlen)
290{
291 struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
292
293 struct msghdr msg = { .msg_name = &addr,
294 .msg_namelen = sizeof(addr),
295 .msg_iov = iov,
296 .msg_iovlen = iovlen };
297
298 ssize_t result;
299 int errsv = errno;
300 do {
301 errno = errsv;
302 result = sendmsg(fd, &msg, 0);
303 } while (__unlikely(result == -1 && errno == EINTR));
304 return result;
305}
306
307static ssize_t
308io_rtnl_send_getlink(int fd, __u32 seq, __u32 pid)
309{
310 char buf[NLMSG_SPACE(sizeof(struct rtgenmsg))] = { 0 };
311
312 struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
313 // clang-format off
314 *nlh = (struct nlmsghdr){
315 .nlmsg_len = NLMSG_SPACE(sizeof(struct rtgenmsg)),
316 .nlmsg_type = RTM_GETLINK,
317 .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
318 .nlmsg_seq = seq,
319 .nlmsg_pid = pid };
320 // clang-format on
321
322 struct rtgenmsg *rtgen = NLMSG_DATA(nlh);
323 *rtgen = (struct rtgenmsg){ .rtgen_family = AF_UNSPEC };
324
325 struct iovec iov[] = { { buf, sizeof(buf) } };
326 return io_rtnl_send(fd, iov, sizeof(iov) / sizeof(*iov));
327}
328
329static ssize_t
330io_rtnl_send_newlink(int fd, __u32 seq, __u32 pid, int ifi_index,
331 unsigned int ifi_flags, struct rtattr *rta, unsigned int rtalen)
332{
333 assert(rta || !rtalen);
334
335 char buf[NLMSG_SPACE(sizeof(struct ifinfomsg))] = { 0 };
336
337 struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
338 *nlh = (struct nlmsghdr){
339 .nlmsg_len = NLMSG_SPACE(sizeof(struct ifinfomsg)) + rtalen,
340 .nlmsg_type = RTM_NEWLINK,
341 .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
342 .nlmsg_seq = seq,
343 .nlmsg_pid = pid
344 };
345
346 struct ifinfomsg *ifi = NLMSG_DATA(nlh);
347 *ifi = (struct ifinfomsg){ .ifi_family = AF_UNSPEC,
348 .ifi_index = ifi_index,
349 .ifi_flags = ifi_flags,
350 .ifi_change = 0xffffffffu };
351
352 struct iovec iov[] = { { buf, sizeof(buf) }, { rta, rtalen } };
353 return io_rtnl_send(fd, iov, sizeof(iov) / sizeof(*iov));
354}
355
356#endif // #HAVE_LINUX_RTNETLINK_H
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
int io_rtnl_getattr(int fd, __u32 seq, __u32 pid, int ifi_index, unsigned int *pifi_flags, unsigned short type, void *data, unsigned short payload)
Invokes io_rtnl_getlink() and retrieves a single attribute of the specified network interface.
Definition: rtnl.c:109
int io_rtnl_socket(__u32 pid, __u32 groups)
Opens an rtnetlink socket.
Definition: rtnl.c:55
int io_rtnl_getlink(int fd, __u32 seq, __u32 pid, io_rtnl_newlink_func_t *func, void *data)
Sends an RTM_GETLINK request and invokes the specified callback function for each received network in...
Definition: rtnl.c:100
int io_rtnl_newlink(int fd, __u32 seq, __u32 pid, int ifi_index, unsigned int ifi_flags, struct rtattr *rta, unsigned short rtalen)
Sends an RTM_NEWLINK request and waits until the acknowledgment is received.
Definition: rtnl.c:87
int io_rtnl_setattr(int fd, __u32 seq, __u32 pid, int ifi_index, unsigned int ifi_flags, unsigned short type, const void *data, unsigned short payload)
Invokes io_rtnl_newlink() to set at most one attribute of the specified network interface.
Definition: rtnl.c:185
This is the internal header file of the rtnetlink declarations.
static struct rtattr * io_rta_find(struct rtattr *rta, unsigned short len, unsigned short type)
Finds an attribute in a list of attributes.
Definition: rtnl.h:169
int io_rtnl_newlink_func_t(struct ifinfomsg *ifi, struct rtattr *rta, unsigned short rtalen, void *data)
The type of a callback function invoked when an RTM_NEWLINK response is received.
Definition: rtnl.h:54
This is the internal header file of the I/O library.
This header file is part of the C11 and POSIX compatibility library; it includes <string....
ptrdiff_t ssize_t
Used for a count of bytes or an error indication.
Definition: types.h:43