Lely core libraries 2.3.4
rtnl.h
Go to the documentation of this file.
1
21#ifndef LELY_IO2_INTERN_LINUX_RTNL_H_
22#define LELY_IO2_INTERN_LINUX_RTNL_H_
23
24#include "io.h"
25
26#ifdef __linux__
27
28#include <assert.h>
29#include <errno.h>
30#include <stdlib.h>
31#include <string.h>
32#include <time.h>
33
34#include <sys/socket.h>
35#include <unistd.h>
36
37#include <linux/rtnetlink.h>
38
44#define RTA_TAIL(rta) \
45 (struct rtattr *)((char *)(rta) + RTA_ALIGN((rta)->rta_len))
46
48 int fd;
49 __u32 pid;
50 __u32 seq;
51};
52
53#ifdef __cplusplus
54extern "C" {
55#endif
56
57typedef int rtnl_recv_func_t(struct nlmsghdr *nlh, size_t len, void *arg);
58
59static int rtnl_open(struct rtnl_handle *rth);
60static int rtnl_close(struct rtnl_handle *rth);
61
62static ssize_t rtnl_send(const struct rtnl_handle *rth, struct nlmsghdr *nlh,
63 void *data, __u32 len);
64static ssize_t rtnl_recv(const struct rtnl_handle *rth, void **pbuf);
65
66static int rtnl_recv_ack(const struct rtnl_handle *rth);
67
68static int rtnl_recv_type(const struct rtnl_handle *rth, __u16 type,
69 rtnl_recv_func_t *func, void *arg);
70
71static int rtnl_send_newlink_request(struct rtnl_handle *rth,
72 unsigned char ifi_family, unsigned short ifi_type,
73 int ifi_index, unsigned int ifi_flags, void *data, __u32 len);
74
75static int rtnl_send_getlink_request(struct rtnl_handle *rth,
76 unsigned char ifi_family, unsigned short ifi_type,
77 int ifi_index);
78
79static struct rtattr *rta_find(
80 struct rtattr *rta, unsigned int len, unsigned short type);
81
82static inline int
83rtnl_open(struct rtnl_handle *rth)
84{
85 assert(rth);
86
87 int errsv;
88
89 rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
90 if (rth->fd == -1) {
91 errsv = errno;
92 goto error_socket;
93 }
94
95 struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
96 socklen_t addrlen = sizeof(addr);
97 if (bind(rth->fd, (struct sockaddr *)&addr, addrlen) == -1) {
98 errsv = errno;
99 goto error_bind;
100 }
101 if (getsockname(rth->fd, (struct sockaddr *)&addr, &addrlen) == -1) {
102 errsv = errno;
103 goto error_getsockname;
104 }
105 assert(addrlen == sizeof(addr));
106 assert(addr.nl_family == AF_NETLINK);
107 rth->pid = addr.nl_pid;
108
109 rth->seq = time(NULL);
110
111 return 0;
112
113error_getsockname:
114error_bind:
115 close(rth->fd);
116 rth->fd = -1;
117error_socket:
118 errno = errsv;
119 return -1;
120}
121
122static inline int
123rtnl_close(struct rtnl_handle *rth)
124{
125 assert(rth);
126
127 int fd = rth->fd;
128 rth->fd = -1;
129 return close(fd);
130}
131
132static inline ssize_t
133rtnl_send(const struct rtnl_handle *rth, struct nlmsghdr *nlh, void *data,
134 __u32 len)
135{
136 assert(rth);
137 assert(nlh);
138
139 nlh->nlmsg_pid = rth->pid;
140
141 struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
142 struct iovec iov[2] = {
143 { .iov_base = nlh, .iov_len = nlh->nlmsg_len - len },
144 { .iov_base = data, .iov_len = len }
145 };
146 struct msghdr msg = { .msg_name = &addr,
147 .msg_namelen = sizeof(addr),
148 .msg_iov = iov,
149 .msg_iovlen = data ? 2 : 1 };
150
151 ssize_t result;
152 int errsv = errno;
153 do {
154 errno = errsv;
155 result = sendmsg(rth->fd, &msg, 0);
156 } while (result == -1 && errno == EINTR);
157 return result;
158}
159
160static inline ssize_t
161rtnl_recv(const struct rtnl_handle *rth, void **pbuf)
162{
163 assert(rth);
164
165 void *buf = NULL;
166 for (;; free(buf), buf = NULL) {
167 ssize_t result;
168 int errsv = errno;
169
170 do {
171 errno = errsv;
172 result = recv(rth->fd, NULL, 0, MSG_PEEK | MSG_TRUNC);
173 } while (result == -1 && errno == EINTR);
174 if (result <= 0)
175 break;
176 size_t len = result;
177
178 buf = malloc(len);
179 if (!buf)
180 break;
181
182 struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
183 socklen_t addrlen = sizeof(addr);
184 do {
185 errno = errsv;
186 result = recvfrom(rth->fd, buf, len, 0,
187 (struct sockaddr *)&addr, &addrlen);
188 } while (result == -1 && errno == EINTR);
189 if (result < 0)
190 break;
191 assert(addrlen == sizeof(addr));
192 assert(addr.nl_family == AF_NETLINK);
193
194 struct nlmsghdr *nlh = buf;
195 if (result != (ssize_t)len || !NLMSG_OK(nlh, len)) {
196 errno = ENOBUFS;
197 break;
198 }
199
200 if (addr.nl_pid || nlh->nlmsg_pid != rth->pid)
201 continue;
202
203 if (pbuf)
204 *pbuf = buf;
205 else
206 free(buf);
207 return result;
208 }
209 free(buf);
210
211 return -1;
212}
213
214static inline int
215rtnl_recv_ack(const struct rtnl_handle *rth)
216{
217 void *buf = NULL;
218 for (;; free(buf), buf = NULL) {
219 ssize_t len = rtnl_recv(rth, &buf);
220 if (len <= 0)
221 break;
222
223 for (struct nlmsghdr *nlh = buf; NLMSG_OK(nlh, len);
224 NLMSG_NEXT(nlh, len)) {
225 if (nlh->nlmsg_seq != rth->seq)
226 continue;
227
228 int error = 0;
229 if (nlh->nlmsg_type == NLMSG_ERROR) {
230 struct nlmsgerr *err = NLMSG_DATA(nlh);
231 if ((error = -err->error))
232 errno = error;
233 } else {
234 errno = error = EPROTO;
235 }
236
237 free(buf);
238 return error ? -1 : 0;
239 }
240 }
241 free(buf);
242
243 return -1;
244}
245
246static inline int
247rtnl_recv_type(const struct rtnl_handle *rth, __u16 type,
248 rtnl_recv_func_t *func, void *arg)
249{
250 void *buf = NULL;
251 for (;; free(buf), buf = NULL) {
252 ssize_t len = rtnl_recv(rth, &buf);
253 if (len <= 0)
254 break;
255
256 for (struct nlmsghdr *nlh = buf; NLMSG_OK(nlh, len);
257 NLMSG_NEXT(nlh, len)) {
258 if (nlh->nlmsg_seq != rth->seq)
259 continue;
260
261 int error = 0;
262 if (nlh->nlmsg_type == type) {
263 if (func)
264 error = func(nlh, len, arg);
265 } else if (nlh->nlmsg_type == NLMSG_ERROR) {
266 struct nlmsgerr *err = NLMSG_DATA(nlh);
267 if ((error = -err->error))
268 errno = error;
269 else
270 errno = error = EPROTO;
271 } else {
272 errno = error = EPROTO;
273 }
274
275 free(buf);
276 return error ? -1 : 0;
277 }
278 }
279 free(buf);
280
281 return -1;
282}
283
284static inline int
285rtnl_send_newlink_request(struct rtnl_handle *rth, unsigned char ifi_family,
286 unsigned short ifi_type, int ifi_index, unsigned int ifi_flags,
287 void *data, __u32 len)
288{
289 assert(rth);
290
291 char buf[NLMSG_SPACE(sizeof(struct ifinfomsg))] = { 0 };
292
293 struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
294 *nlh = (struct nlmsghdr){ .nlmsg_len = sizeof(buf) + len,
295 .nlmsg_type = RTM_NEWLINK,
296 .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
297 .nlmsg_seq = ++rth->seq };
298
299 struct ifinfomsg *ifi = NLMSG_DATA(nlh);
300 *ifi = (struct ifinfomsg){ .ifi_family = ifi_family,
301 .ifi_type = ifi_type,
302 .ifi_index = ifi_index,
303 .ifi_flags = ifi_flags,
304 .ifi_change = 0xffffffffu };
305
306 return rtnl_send(rth, nlh, data, len) == -1 ? -1 : 0;
307}
308
309static inline int
310rtnl_send_getlink_request(struct rtnl_handle *rth, unsigned char ifi_family,
311 unsigned short ifi_type, int ifi_index)
312{
313 assert(rth);
314
315 char buf[NLMSG_SPACE(sizeof(struct ifinfomsg))] = { 0 };
316
317 struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
318 *nlh = (struct nlmsghdr){ .nlmsg_len = sizeof(buf),
319 .nlmsg_type = RTM_GETLINK,
320 .nlmsg_flags = NLM_F_REQUEST,
321 .nlmsg_seq = ++rth->seq };
322
323 struct ifinfomsg *ifi = NLMSG_DATA(nlh);
324 *ifi = (struct ifinfomsg){ .ifi_family = ifi_family,
325 .ifi_type = ifi_type,
326 .ifi_index = ifi_index,
327 .ifi_change = 0xffffffffu };
328
329 return rtnl_send(rth, nlh, NULL, 0) == -1 ? -1 : 0;
330}
331
332static inline struct rtattr *
333rta_find(struct rtattr *rta, unsigned int len, unsigned short type)
334{
335 assert(rta);
336
337 for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
338 if (rta->rta_type == type)
339 return rta;
340 }
341 return NULL;
342}
343
344#ifdef __cplusplus
345}
346#endif
347
348#endif // __linux__
349
350#endif // !LELY_IO2_INTERN_LINUX_RTNL_H_
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 <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <string....
This header file is part of the C11 and POSIX compatibility library; it includes <unistd....