Lely core libraries  2.2.5
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 
47 struct rtnl_handle {
48  int fd;
49  __u32 pid;
50  __u32 seq;
51 };
52 
53 #ifdef __cplusplus
54 extern "C" {
55 #endif
56 
57 typedef int rtnl_recv_func_t(struct nlmsghdr *nlh, size_t len, void *arg);
58 
59 static int rtnl_open(struct rtnl_handle *rth);
60 static int rtnl_close(struct rtnl_handle *rth);
61 
62 static ssize_t rtnl_send(const struct rtnl_handle *rth, struct nlmsghdr *nlh,
63  void *data, __u32 len);
64 static ssize_t rtnl_recv(const struct rtnl_handle *rth, void **pbuf);
65 
66 static int rtnl_recv_ack(const struct rtnl_handle *rth);
67 
68 static int rtnl_recv_type(const struct rtnl_handle *rth, __u16 type,
69  rtnl_recv_func_t *func, void *arg);
70 
71 static 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 
75 static int rtnl_send_getlink_request(struct rtnl_handle *rth,
76  unsigned char ifi_family, unsigned short ifi_type,
77  int ifi_index);
78 
79 static struct rtattr *rta_find(
80  struct rtattr *rta, unsigned int len, unsigned short type);
81 
82 static inline int
83 rtnl_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 
113 error_getsockname:
114 error_bind:
115  close(rth->fd);
116  rth->fd = -1;
117 error_socket:
118  errno = errsv;
119  return -1;
120 }
121 
122 static inline int
123 rtnl_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 
132 static inline ssize_t
133 rtnl_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 
160 static inline ssize_t
161 rtnl_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 
214 static inline int
215 rtnl_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 
246 static inline int
247 rtnl_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 
284 static inline int
285 rtnl_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 
309 static inline int
310 rtnl_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 
332 static inline struct rtattr *
333 rta_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_
string.h
rtnl_handle
Definition: rtnl.h:47
io.h
unistd.h
stdlib.h