Lely core libraries  2.2.5
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 (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 
76 error_bind:
77  close(fd);
78 error_socket:
79  errno = errsv;
80  return -1;
81 }
82 
83 int
84 io_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 
96 int
97 io_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 
105 int
106 io_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 
138 static int
139 io_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 
178 int
179 io_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 
197 static int
198 io_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 
245 static int
246 io_rtnl_recv_ack(int fd)
247 {
248  return io_rtnl_recv(fd, NULL, NULL);
249 }
250 
251 static int
252 io_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 
262 static int
263 io_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 
282 static ssize_t
283 io_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 
301 static ssize_t
302 io_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 
323 static ssize_t
324 io_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 // #HAVE_LINUX_RTNETLINK_H
string.h
MIN
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
rtnl.h
io_rtnl_setattr
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
io.h
io_rtnl_getattr
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
io_rtnl_newlink
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
io_rta_find
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
io_handle::fd
int fd
The native file descriptor.
Definition: handle.h:56
io_rtnl_getlink
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
io_rtnl_socket
int io_rtnl_socket(__u32 pid, __u32 groups)
Opens an rtnetlink socket.
Definition: rtnl.c:55
io_rtnl_newlink_func_t
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