softwarecontainer  0.18.0-739e8d7 2017-05-04
netlink.cpp
1 /*
2  * Copyright (C) 2016-2017 Pelagicore AB
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
9  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
11  * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
12  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
13  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
14  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15  * SOFTWARE.
16  *
17  * For further information see LICENSE
18  */
19 
20 #include "netlink.h"
21 
22 #include <linux/if_arp.h> // ARPHRD_ defines
23 
24 #include <unistd.h> // getpid(), getpagesize()
25 #include <string.h> // memcpy, memset etc
26 #include <iostream> // cout
27 
28 namespace softwarecontainer {
29 
31 {
32  m_sequenceNumber = 1;
33  m_hasKernelDump = false;
34  m_netlinkInitialized = false;
35 
36  if (!setupNetlink()) {
37  fprintf(stderr, "Failed to setup netlink\n");
38  }
39 
40  if (!getKernelDump()) {
41  fprintf(stderr, "Failed to initialize cache\n");
42  }
43 }
44 
46 {
47  if (m_netlinkInitialized) {
48  shutdown(m_fd, SHUT_RDWR);
49  close(m_fd);
50  }
51 
52  if (m_hasKernelDump) {
53  clearCache();
54  }
55 }
56 
57 bool Netlink::setupNetlink()
58 {
59  if (m_netlinkInitialized) {
60  return true;
61  }
62 
63  if ((m_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
64  fprintf(stderr, "Socket error: %s", strerror(errno));
65  return false;
66  }
67 
68  // Setup local sockaddr
69  memset(&m_local, 0, sizeof(m_local));
70  m_local.nl_family = AF_NETLINK;
71  m_local.nl_groups = 0;
72 
73  if (bind(m_fd, (struct sockaddr *) &m_local, sizeof(m_local)) < 0) {
74  fprintf(stderr, "Failed to bind socket: %s \n", strerror(errno));
75  return false;
76  } else {
77  m_pid = m_local.nl_pid;
78  }
79 
80  // Setup kernel sockaddr
81  memset(&m_kernel, 0, sizeof(m_kernel));
82  m_kernel.nl_family = AF_NETLINK;
83 
84  m_netlinkInitialized = true;
85  return true;
86 }
87 
88 template<typename payload>
89 Netlink::netlink_request<payload> Netlink::createMessage(const int type, const int flags)
90 {
91  netlink_request<payload> request;
92  // Initialize length to be header + payload (no attributes)
93  request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(payload));
94  request.hdr.nlmsg_type = type;
95  // We want ACK
96  request.hdr.nlmsg_flags = flags | NLM_F_ACK | NLM_F_REQUEST;
97  // Sequence numbers are monotonically increasing, pid is just an identifier
98  request.hdr.nlmsg_seq = m_sequenceNumber++;
99  request.hdr.nlmsg_pid = m_pid;
100  // Set both payload and attribute buffer to 0
101  memset(&request.pay, 0, sizeof(payload));
102  memset(request.attr, 0, sizeof(request.attr));
103 
104  return request;
105 }
106 
107 template<typename payload>
108 bool Netlink::addAttribute(netlink_request<payload> &request, const int type, const size_t length, const void *data)
109 {
110  long unsigned int MAXSIZE = sizeof(request.attr);
111  int usedAttributeLength = request.hdr.nlmsg_len - sizeof(request.hdr) - sizeof(request.pay);
112  if (usedAttributeLength + RTA_LENGTH(length) > MAXSIZE) {
113  return false;
114  }
115 
116  // First, get a pointer to memory at the end of the payload (pointed out by nlmsg_len)
117  struct rtattr *rta = (struct rtattr *)(((char *) &request) + NLMSG_ALIGN(request.hdr.nlmsg_len));
118 
119  // Set type of attribute and attribute length (calculated with macro to include header)
120  rta->rta_type = type;
121  rta->rta_len = RTA_LENGTH(length);
122 
123  // Copy the data to the data location in the attribute
124  memcpy(RTA_DATA(rta), data, length);
125 
126  // Update nlmsg_len to reflect that end of msg now includes an attribute
127  request.hdr.nlmsg_len = NLMSG_ALIGN(request.hdr.nlmsg_len) + RTA_LENGTH(length);
128 
129  return true;
130 }
131 
132 template<typename payload>
133 bool Netlink::sendMessage(netlink_request<payload> &request)
134 {
135  if (!m_netlinkInitialized && !setupNetlink()) {
136  fprintf(stderr, "Could not setup netlink communication with kernel\n");
137  return false;
138  }
139 
140  // Create iovector, which holds pointer to, and size of,
141  // the netlink message payload
142  struct iovec io;
143  memset(&io, 0, sizeof(io));
144  io.iov_base = &request;
145  io.iov_len = request.hdr.nlmsg_len;
146 
147  // Create the message header, to wrap everything in
148  struct msghdr msghdr;
149  memset(&msghdr, 0, sizeof(msghdr));
150 
151  // We only send one message here, and it's the one we set in the vector above
152  msghdr.msg_iov = &io;
153  msghdr.msg_iovlen = 1;
154  // We send it to the kernel
155  msghdr.msg_name = &m_kernel;
156  msghdr.msg_namelen = sizeof(m_kernel);
157 
158  if ((sendmsg(m_fd, &msghdr, 0)) == -1) {
159  fprintf(stderr, "Unable to send msg to kernel: %s\n", strerror(errno));
160  return false;
161  }
162 
163  // TODO: Distinguish from kernel errors (negative) and other errors (positive)
164  int status = readMessage();
165  if (status < 0) {
166  fprintf(stderr, "Got an error from the kernel: %s\n", strerror(-status));
167  return false;
168  } else if (status > 0) {
169  fprintf(stderr, "Error in the netlink code: %i\n", status);
170  }
171 
172  return true;
173 }
174 
175 bool Netlink::setDefaultGateway(const char *gatewayAddress)
176 {
177  netlink_request<rtmsg> set_gw = createMessage<rtmsg>(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE);
178  set_gw.pay.rtm_family = AF_INET;
179  set_gw.pay.rtm_table = RT_TABLE_MAIN;
180  set_gw.pay.rtm_protocol = RTPROT_STATIC;
181  set_gw.pay.rtm_scope = RT_SCOPE_UNIVERSE;
182  set_gw.pay.rtm_type = RTN_UNICAST;
183 
184  struct in_addr gw_addr;
185  if (inet_aton(gatewayAddress, &gw_addr) == 0) {
186  return false;
187  }
188  addAttribute(set_gw, RTA_GATEWAY, sizeof(gw_addr), &gw_addr);
189 
190  return sendMessage(set_gw);
191 }
192 
193 // Bring up by index
194 bool Netlink::linkUp(const int ifaceIndex)
195 {
196  if (!checkKernelDump()) {
197  return false;
198  }
199 
200  for (LinkInfo link : m_links) {
201  ifinfomsg ifinfo = link.first;
202  if (ifinfo.ifi_index != ifaceIndex) {
203  continue;
204  }
205 
206  // First, bring the link up
207  netlink_request<ifinfomsg> msg_up = createMessage<ifinfomsg>(RTM_NEWLINK, NLM_F_CREATE);
208  msg_up.pay.ifi_family = AF_UNSPEC;
209  msg_up.pay.ifi_flags = ifinfo.ifi_flags | IFF_UP;
210  msg_up.pay.ifi_change |= IFF_UP;
211  msg_up.pay.ifi_index = ifinfo.ifi_index;
212  if (!sendMessage(msg_up)) {
213  fprintf(stderr, "Failed to bring device %i up\n", ifinfo.ifi_index);
214  return false;
215  } else {
216  return true;
217  }
218  }
219 
220  return false;
221 }
222 
223 bool Netlink::setIP(const int ifaceIndex, const in_addr ip, const unsigned char netmask)
224 {
225  for (LinkInfo link : m_links) {
226  ifinfomsg ifinfo = link.first;
227  if (ifinfo.ifi_index != ifaceIndex) {
228  continue;
229  }
230 
231  // TODO: Support for ipv6
232  netlink_request<ifaddrmsg> msg_setip = createMessage<ifaddrmsg>(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE);
233  msg_setip.pay.ifa_family = AF_INET; // ipv4
234  msg_setip.pay.ifa_prefixlen = netmask;
235  msg_setip.pay.ifa_scope = RT_SCOPE_UNIVERSE;
236  msg_setip.pay.ifa_index = ifinfo.ifi_index; // interface (link)
237 
238  // Calculate the broadcast address.
239  // Broadcast = IP | (~Netmask)
240  in_addr_t inet_netmask = (1 << netmask) -1;
241  in_addr_t inet_bcast = ip.s_addr | (~inet_netmask);
242  in_addr bcast_addr = { inet_bcast };
243 
244  addAttribute(msg_setip, IFA_LOCAL, sizeof(ip), &ip);
245  addAttribute(msg_setip, IFA_BROADCAST, sizeof(bcast_addr), &bcast_addr);
246 
247  // No matter what happens, we return since we found the link we've been looking for.
248  if (!sendMessage(msg_setip)) {
249  // TODO: pton to print ip number also.
250  fprintf(stderr, "Failed to set ip on link %i\n", ifinfo.ifi_index);
251  return false;
252  } else {
253  return true;
254  }
255  }
256 
257  // No link found, that's a failure.
258  return false;
259 }
260 
261 bool Netlink::linkDown(const int ifaceIndex)
262 {
263  for (LinkInfo link : m_links) {
264  ifinfomsg ifinfo = link.first;
265  if (ifinfo.ifi_type == ARPHRD_LOOPBACK) {
266  continue; // This is the loopback device
267  }
268 
269  if (ifinfo.ifi_index != ifaceIndex) {
270  continue; // Only bring down the requested iface
271  }
272 
273  netlink_request<ifinfomsg> down_msg = createMessage<ifinfomsg>(RTM_NEWLINK, 0);
274  down_msg.pay.ifi_family = AF_UNSPEC;
275  down_msg.pay.ifi_index = ifinfo.ifi_index;
276  down_msg.pay.ifi_flags = ~IFF_UP;
277  down_msg.pay.ifi_change = IFF_UP;
278 
279  if (!sendMessage(down_msg)) {
280  return false;
281  } else {
282  return true;
283  }
284  }
285 
286  return false;
287 }
288 
289 bool Netlink::findLink(const char *ifaceName, LinkInfo &linkInfo)
290 {
291  if (!checkKernelDump()) {
292  return false;
293  }
294 
295  for (LinkInfo link : m_links) {
296  AttributeList attributes = link.second;
297  for (AttributeInfo attrinfo : attributes) {
298  rtattr attr = attrinfo.first;
299  void *data = attrinfo.second;
300 
301  if (attr.rta_type == IFLA_IFNAME) {
302  char *ifname = (char *) data;
303  if (strcmp(ifname, ifaceName) == 0) {
304  linkInfo = link;
305  return true;
306  }
307  }
308  }
309  }
310 
311  return false;
312 }
313 
314 bool Netlink::findAddresses(const unsigned int interfaceIndex, std::vector<AddressInfo> &result)
315 {
316  if (!checkKernelDump()) {
317  return false;
318  }
319 
320  for (AddressInfo addressInfo : m_addresses) {
321  ifaddrmsg addrmsg = addressInfo.first;
322  if (addrmsg.ifa_index == interfaceIndex) {
323  result.push_back(addressInfo);
324  }
325  }
326 
327  return true;
328 
329 }
330 
331 bool Netlink::hasAddress(const std::vector<AddressInfo> &haystack,
332  const int addressFamily,
333  const char *needle)
334 {
335  for (AddressInfo addressInfo : haystack) {
336  AttributeList attributes = addressInfo.second;
337  for (AttributeInfo attrPair : attributes) {
338 
339  // Skip if this attribute is not an address attribute
340  rtattr attr = attrPair.first;
341  if (attr.rta_type != IFA_ADDRESS && attr.rta_type != IFA_LOCAL) {
342  continue;
343  }
344 
345  void *data = attrPair.second;
346  char out[INET6_ADDRSTRLEN];
347 
348  // It is ok here for inet_ntop to fail (data could be bad)
349  if (inet_ntop(addressFamily, data, out, sizeof(out)) && strcmp(out, needle) == 0) {
350  return true;
351  }
352  }
353  }
354 
355  std::cout << "Netlink does not have address" << std::endl;
356  return false;
357 }
358 
359 int Netlink::readMessage()
360 {
361  if (!m_netlinkInitialized && !setupNetlink()) {
362  fprintf(stderr, "Could not setup netlink communication with kernel\n");
363  return 1;
364  }
365 
366  size_t PAGE_SIZE = getpagesize();
367  char *buf= new char[PAGE_SIZE];
368 
369  bool end = false;
370  while (!end) {
371  int len;
372  struct nlmsghdr *msg;
373  struct msghdr reply;
374  memset(&reply, 0, sizeof(reply));
375 
376  struct iovec io = { buf, sizeof(char) * PAGE_SIZE };
377  reply.msg_iov = &io;
378  reply.msg_iovlen = 1;
379  reply.msg_name = &m_kernel;
380  reply.msg_namelen = sizeof(m_kernel);
381 
382  len = recvmsg(m_fd, &reply, 0); /* read lots of data */
383  if (len == 0) {
384  end = true;
385  }
386 
387  for (msg = (struct nlmsghdr *) buf; NLMSG_OK(msg, len); msg = NLMSG_NEXT(msg, len)) {
388  switch(msg->nlmsg_type)
389  {
390  case NLMSG_ERROR: {
391  struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(msg);
392  if (err->error != 0) {
393  return err->error; // Actually an error
394  } else {
395  end = true; // This was an ack
396  }
397 
398  break;
399  }
400  case NLMSG_DONE: // End of multipart message
401  end = true;
402  break;
403  case RTM_GETLINK:
404  case RTM_NEWLINK:
405  saveMessage<ifinfomsg, LinkInfo>(*msg, m_links);
406  break;
407  case RTM_NEWADDR:
408  case RTM_GETADDR:
409  saveMessage<ifaddrmsg, AddressInfo>(*msg, m_addresses);
410  break;
411  case RTM_NEWROUTE:
412  case RTM_GETROUTE:
413  saveMessage<rtmsg, RouteInfo>(*msg, m_routes);
414  break;
415  case RTM_NEWNEIGH:
416  case RTM_GETNEIGH:
417  break;
418  case RTM_NEWRULE:
419  case RTM_GETRULE:
420  break;
421  case RTM_NEWQDISC:
422  case RTM_GETQDISC:
423  break;
424  case RTM_NEWTCLASS:
425  case RTM_GETTCLASS:
426  break;
427  case RTM_NEWTFILTER:
428  case RTM_GETTFILTER:
429  break;
430  // Group all delete cases together, since we don't want to
431  // save anything here. TODO: Delete if present in cache
432  case RTM_DELLINK:
433  printf("Got dellink\n");
434  break;
435  case RTM_DELROUTE:
436  case RTM_DELADDR:
437  case RTM_DELTFILTER:
438  case RTM_DELTCLASS:
439  case RTM_DELQDISC:
440  case RTM_DELRULE:
441  case RTM_DELNEIGH:
442  break;
443 
444  default: /* for education only, should not happen here */
445  printf("message type %d, length %d\n", msg->nlmsg_type, msg->nlmsg_len);
446  break;
447  }
448  }
449  }
450  delete[] buf;
451  return 0;
452 }
453 
459 {
460  m_hasKernelDump = false;
461 
462  netlink_request<rtgenmsg> link_msg = createMessage<rtgenmsg>(RTM_GETLINK, NLM_F_DUMP);
463  link_msg.pay.rtgen_family = AF_PACKET;
464  if (!sendMessage(link_msg)) {
465  std::cerr << "Could not send link message" << std::endl;
466  return false;
467  }
468 
469  netlink_request<rtgenmsg> addr_msg = createMessage<rtgenmsg>(RTM_GETADDR, NLM_F_DUMP);
470  addr_msg.pay.rtgen_family = AF_PACKET;
471  if (!sendMessage(addr_msg)) {
472  std::cerr << "Could not send address message" << std::endl;
473  return false;
474  }
475 
476  netlink_request<rtgenmsg> route_msg = createMessage<rtgenmsg>(RTM_GETROUTE, NLM_F_DUMP);
477  route_msg.pay.rtgen_family = AF_PACKET;
478  if (!sendMessage(route_msg)) {
479  std::cerr << "Could not send route message" << std::endl;
480  return false;
481  }
482 
483  m_hasKernelDump = true;
484  return true;
485 }
486 
488 {
489  if (!m_hasKernelDump && !getKernelDump()) {
490  std::cerr << "Could not get cache dump from kernel" << std::endl;
491  return false;
492  }
493 
494  return true;
495 }
496 
497 template<typename msgtype>
498 bool Netlink::getAttributes(const struct nlmsghdr &header, AttributeList &result)
499 {
500  // Get the pointer to the data section of the message
501  char *dataptr = (char *)NLMSG_DATA(&header);
502  // Add on the size of the message type header = go to the data section
503  // where attributes are stored = first attribute
504  rtattr *attribute = (struct rtattr *)(dataptr + NLMSG_ALIGN(sizeof(msgtype)));
505 
506  // Total length of msg minus header = all attributes
507  int len = header.nlmsg_len - NLMSG_LENGTH(sizeof(msgtype));
508  while (RTA_OK(attribute, len)) {
509  // Allocate enough data, and copy the attribute data to it
510  void *data = malloc(RTA_PAYLOAD(attribute));
511  if (data == nullptr) {
512  fprintf(stderr, "Could not malloc enough to store attribute\n");
513  return false;
514  }
515 
516  memcpy(data, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
517  // Then save the attribute and its data
518  std::pair<rtattr, void *> pair(*attribute, data);
519  result.push_back(pair);
520 
521  // Update attribute pointer
522  attribute = RTA_NEXT(attribute, len);
523  }
524 
525  return true;
526 }
527 
528 template<typename msgtype, typename InfoType>
529 bool Netlink::saveMessage(const struct nlmsghdr &header, std::vector<InfoType> &result)
530 {
531  // Bring out the msgtype struct
532  msgtype *msg = (msgtype *) NLMSG_DATA(&header);
533  // Get a vector of attributes for this message
534  AttributeList attributes;
535  if (!getAttributes<msgtype>(header, attributes)) {
536  fprintf(stderr, "Could not get attributes for message\n");
537  return false;
538  }
539  // Save them both as a pair to the given result vector
540  result.push_back(std::pair<msgtype, AttributeList>(*msg, attributes));
541  return true;
542 }
543 
544 void Netlink::freeAttributes(AttributeList &attrList)
545 {
546  for (AttributeInfo attrInfo : attrList) {
547  void *data = attrInfo.second;
548  free(data);
549  }
550 }
551 
552 void Netlink::clearCache()
553 {
554  for (LinkInfo link : m_links) {
555  freeAttributes(link.second);
556  }
557  m_links.clear();
558 
559  for (AddressInfo addr : m_addresses) {
560  freeAttributes(addr.second);
561  }
562  m_addresses.clear();
563 
564  for (RouteInfo route : m_routes) {
565  freeAttributes(route.second);
566  }
567  m_routes.clear();
568 
569  m_hasKernelDump = false;
570 }
571 
572 } // namespace softwarecontainer
Developers guide to adding a config item: