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