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 
38 static int io_rtnl_getattr_func(struct ifinfomsg *ifi, struct rtattr *rta,
39  unsigned short rtalen, void *data);
40 
41 static int io_rtnl_recv(int fd, int (*func)(struct nlmsghdr *nlh, void *data),
42  void *data);
43 static int io_rtnl_recv_ack(int fd);
44 static int io_rtnl_recv_newlink(
45  int fd, io_rtnl_newlink_func_t *func, void *data);
46 static int io_rtnl_recv_newlink_func(struct nlmsghdr *nlh, void *data);
47 
48 static ssize_t io_rtnl_send(int fd, struct iovec *iov, int iovlen);
49 static ssize_t io_rtnl_send_getlink(int fd, __u32 seq, __u32 pid);
50 static 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 
54 int
55 io_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 
79 error_bind:
80  close(fd);
81 error_socket:
82  errno = errsv;
83  return -1;
84 }
85 
86 int
87 io_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 
99 int
100 io_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 
108 int
109 io_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 
144 static int
145 io_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 
184 int
185 io_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 
203 static int
204 io_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 
251 static int
252 io_rtnl_recv_ack(int fd)
253 {
254  return io_rtnl_recv(fd, NULL, NULL);
255 }
256 
257 static int
258 io_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 
268 static int
269 io_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 
288 static ssize_t
289 io_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 
307 static ssize_t
308 io_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 
329 static ssize_t
330 io_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
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
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
This header file is part of the C11 and POSIX compatibility library; it includes <string.h> and defines any missing functionality.
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_socket(__u32 pid, __u32 groups)
Opens an rtnetlink socket.
Definition: rtnl.c:55
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
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_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
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
This is the internal header file of the I/O library.
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 fd
The native file descriptor.
Definition: handle.h:56
This is the internal header file of the rtnetlink declarations.