OpenSDN source code
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
linux/metadata_ipv6_netlink.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023 Matvey Kraposhin
3  */
4 extern "C" {
5  #include <errno.h>
6  #include <net/if.h>
7  #include <sys/socket.h>
8  #include <unistd.h>
9  #include <linux/netlink.h>
10  #include <linux/rtnetlink.h>
11  #include <linux/neighbour.h>
12  #include <linux/if_addr.h>
13  #include <arpa/inet.h>
14 }
15 
16 #ifndef NLM_F_DUMP_FILTERED
17 #define NLM_F_DUMP_FILTERED 0x20
18 #endif
19 
20 #include <cstring>
21 #include <cmn/agent_cmn.h>
22 #include <oper/interface_common.h>
24 #include "services/services_init.h"
25 
26 namespace aux {
27 
30 void insert_attr(nlmsghdr* nl_msg,
31  unsigned short attr_type, int attr_len, const void* attr_data) // T-L-V
32 {
33  if (nl_msg == NULL) {
34  LOG(ERROR, "NULL pointer in nl_msg in" // INFO | DEBUG
35  " void insert_attr, metadata_ipv6_netlink.cc");
36  return;
37  }
38  if (attr_data == NULL) {
39  LOG(ERROR, "NULL pointer in attr_data in"
40  " void insert_attr, metadata_ipv6_netlink.cc");
41  return;
42  }
43 
44  struct rtattr *payload =
45  (struct rtattr*)(((char*)nl_msg) + NLMSG_ALIGN(nl_msg->nlmsg_len));
46  payload->rta_type = attr_type;
47  payload->rta_len = RTA_LENGTH(attr_len);
48  std::memcpy
49  (
50  RTA_DATA(payload),
51  attr_data, attr_len);
52  nl_msg->nlmsg_len = NLMSG_ALIGN(nl_msg->nlmsg_len) + payload->rta_len;
53 }
54 
55 
57 template <class T>
59 {
61  nlmsghdr nl_message_hdr;
62 
65 
68  char payload[16384];
69 
71  unsigned int& msg_len;
72 
75  msghdr* message (sockaddr_nl& s_nl) {
76  if (this->msg_len < NLMSG_LENGTH(sizeof(T))) {
77  LOG(ERROR, " too short msg_len, "
78  "NetlinkRequest::message, metadata_ipv6_netlink.cc, "<<
79  "this->msg_len = " << this->msg_len <<
80  ", NLMSG_LENGTH(sizeof(T)) = " << NLMSG_LENGTH(sizeof(T)));
81  return NULL;
82  }
83 
84  iov_.iov_len = this->msg_len;
85  msg_ptr_.reset(new msghdr);
86  std::memset(msg_ptr_.get(), 0, sizeof(msghdr));
87  msg_ptr_->msg_name = &s_nl;
88  msg_ptr_->msg_namelen = sizeof(s_nl);
89  msg_ptr_->msg_iov = &iov_;
90  msg_ptr_->msg_iovlen = 1; // 1 iov entry
91 
92  return msg_ptr_.get();
93  }
94 
97  void insert_attr(unsigned short attr_type,
98  int attr_len, const void* attr_data) {
99  aux::insert_attr(&nl_message_hdr, attr_type, attr_len, attr_data);
100  }
101 
104  std::memset(&nl_message_hdr, 0, sizeof(nl_message_hdr));
105  std::memset(&nl_req_hdr, 0, sizeof(nl_req_hdr));
106  std::memset(&payload, 0, sizeof(payload));
107 
108  iov_.iov_base = &nl_message_hdr;
109  }
110 
113 
114 private:
115 
118 
121 
123  iovec iov_;
124 
126  std::unique_ptr<msghdr> msg_ptr_;
127 };
128 
129 
133 
134 private:
135 
137  NetlinkSocket(const NetlinkSocket& nl);
138 
140  const NetlinkSocket& operator = (const NetlinkSocket& nl);
141 
142 private:
143 
146 
148  sockaddr_nl local_sock_addr;
149 
150 public:
151 
155  socket_fd = -1;
156  // open a socket
157  socket_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
158  if (socket_fd < 0) {
159  LOG(ERROR, "An error has occured during opening the socket"
160  "NetlinkSocket::NetlinkSocket, metadata_ipv6_netlink.cc, "
161  "errno = " << errno << std::endl);
162  return;
163  }
164 
165  // bind the socket to an address
166  local_sock_addr.nl_family = AF_NETLINK;
167  local_sock_addr.nl_groups = 0; // no multicast
168  local_sock_addr.nl_pad = 0; // not used here
169  local_sock_addr.nl_pid = 0;
170  if (bind(socket_fd,
171  (sockaddr*)(&local_sock_addr), sizeof(local_sock_addr)) < 0) {
172  LOG(ERROR, "An error has occured during binding the socket"
173  "NetlinkSocket::NetlinkSocket, metadata_ipv6_netlink.cc, "
174  "errno = " << errno << std::endl);
175  close(socket_fd);
176  }
177  }
178 
181  int close_res = close(socket_fd);
182  if (close_res) {
183  LOG(ERROR, "An error has occured during closing the socket"
184  "NetlinkSocket::NetlinkSocket, metadata_ipv6_netlink.cc, "
185  "errno = " << errno << std::endl);
186  }
187  }
188 
190  const int& fd() const {
191  return socket_fd;
192  }
193 
195  sockaddr_nl& netlink_socket() {
196  return local_sock_addr;
197  }
198 };
199 
201 
203  struct nlmsghdr *resp_nh_;
204 
206  int n_recv_;
207 
209  struct msghdr msg_;
210 
212  struct iovec iov_;
213 
215  char buffer_[16384];
216 
217 private:
218 
220  NetlinkResponse();
221 
222 public:
223 
226  NetlinkResponse(msghdr* req_msg, NetlinkSocket& nl_sock) {
227  resp_nh_ = NULL;
228  memset(buffer_, 0, sizeof(buffer_));
229  memset(&iov_, 0, sizeof(iov_));
230 
231  msg_.msg_name = req_msg->msg_name;
232  msg_.msg_namelen = req_msg->msg_namelen;
233  msg_.msg_iov = &iov_;
234  msg_.msg_iovlen = 1;
235  iov_.iov_base = buffer_;
236  iov_.iov_len = sizeof(buffer_);
237  n_recv_ = recvmsg(nl_sock.fd(), &msg_, 0);
238  if (n_recv_ < 0) {
239  LOG(ERROR, "Error receiving message from netlink: "
240  << strerror(errno) << std::endl);
241  } else {
242  resp_nh_ = reinterpret_cast<struct nlmsghdr *>(&buffer_[0]);
243  }
244  }
245 
248 
250  template <class T>
251  int response_iterate(const T& handler_func) {
252  for (;
253  NLMSG_OK(resp_nh_, n_recv_);
254  resp_nh_ = NLMSG_NEXT(resp_nh_, n_recv_)) {
255  int handle_res = handler_func(resp_nh_);
256  if (handle_res >= 0) {
257  return handle_res;
258  }
259  }
260  return -1;
261  }
262 };
263 
264 struct IPv6Reader {
265 
266  explicit IPv6Reader(const unsigned int itf_idx)
267  :
268  vhost_idx_(itf_idx) {
269  }
270 
271  unsigned int vhost_idx_;
272 
274 
275  int read_ipv6_address(const struct nlmsghdr *resp_nh) const {
276  struct rtattr *resp_attr = NULL;
277  struct ifaddrmsg *ifa_resp = NULL;
278  int attr_len = 0;
279  if (resp_nh->nlmsg_type != RTM_NEWADDR) {
280  return -1;
281  }
282  ifa_resp = static_cast<struct ifaddrmsg *>(NLMSG_DATA(resp_nh));
283  if (ifa_resp == NULL) {
284  return -1;
285  }
286  if (ifa_resp->ifa_index != vhost_idx_) {
287  return -1;
288  }
289  attr_len = resp_nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa_resp));
290  for (resp_attr = IFA_RTA(ifa_resp);
291  RTA_OK(resp_attr, attr_len);
292  resp_attr = RTA_NEXT(resp_attr, attr_len)) {
293  if (resp_attr->rta_type == IFA_ADDRESS) {
294  char addr_str[256];
295  memset(addr_str, 0, 256);
296  inet_ntop(AF_INET6,
297  RTA_DATA(resp_attr), addr_str, 256);
298  if (addr_str[0] != 0) {
299  vhost_addr_ = Ip6Address::from_string(addr_str);
300  if (!vhost_addr_.is_unspecified() &&
301  vhost_addr_.is_link_local()) {
302  return 1;
303  }
304  }
305  }
306  }
307  return -1;
308  }
309 
310  int operator() (const struct nlmsghdr *resp_nh) const {
311  return read_ipv6_address(resp_nh);
312  }
313 
314 private:
315 
316  IPv6Reader();
317 
318  IPv6Reader(const IPv6Reader&);
319 
320 };
321 
323 static bool read_ack_response(
324  const msghdr *msg,
325  int send_n,
326  NetlinkSocket& nl_sock,
327  const char* func_name) {
328 
330  nl_ack.msg_len = NLMSG_LENGTH(sizeof(nl_ack.nl_req_hdr));
331  msghdr *ack = nl_ack.message(nl_sock.netlink_socket());
332  int recv_n = -1;
333  if (msg && send_n > 0 && ack) {
334  recv_n = recvmsg(nl_sock.fd(), ack, MSG_WAITALL);
335  if (recv_n <= 0) {
336  LOG(ERROR, "Recvmsg failed,"<<
337  func_name << ", metadata_ipv6_netlink.cc, "
338  "errno = " << errno << std::endl);
339  return false;
340  }
341  if (nl_ack.nl_message_hdr.nlmsg_type == NLMSG_ERROR) {
342  const int ack_err = -nl_ack.nl_req_hdr.error;
343  if (ack_err == 0
344  || ack_err == EEXIST || ack_err == EADDRNOTAVAIL) {
345  // EEXIST -- address is already present, not an error
346  // EADDRNOTAVAIL -- address is not present, not an error
347  return true;
348  }
349  LOG(ERROR, "Error in the Netlink message, "<<
350  func_name << ", metadata_ipv6_netlink.cc, "
351  "error code = " << ack_err);
352  }
353  }
354  return false;
355 }
356 
357 } //namespace aux
358 
360  // set ip address
361  //
362  // set device index
363  const int dev_idx =
365  if (dev_idx == 0) {
366  std::cout<< "Device " << services_->agent()->vhost_interface_name()
367  << " has not been found "
368  << std::endl;
369  return false;
370  } else {
371 
372  std::cout<< "dev_idx = " << dev_idx
373  << ", dev_name = " << services_->agent()->vhost_interface_name()
374  << std::endl;
375  }
376 
377  // open a socket
378  aux::NetlinkSocket nl_sock;
379  int send_n = -1;
380  {
381  aux::NetlinkRequest<ifaddrmsg> addr_nl_req;
382  ifaddrmsg &nl_req_param = addr_nl_req.nl_req_hdr;
383  nl_req_param.ifa_family = AF_INET6;
384  nl_req_param.ifa_prefixlen = 128;
385  nl_req_param.ifa_index = dev_idx;
386 
387  nlmsghdr &msg_hdr = addr_nl_req.nl_message_hdr;
388  msg_hdr.nlmsg_type = RTM_GETADDR;
389  msg_hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; // | NLM_F_DUMP_FILTERED;
390  addr_nl_req.msg_len = NLMSG_LENGTH(sizeof(nl_req_param));
391 
392  msghdr *msg = addr_nl_req.message(nl_sock.netlink_socket());
393  send_n = -1;
394  if (msg == NULL) {
395  return false;
396  }
397 
398  // send a request to the kernel
399  send_n = sendmsg(nl_sock.fd(), msg, 0);
400  if (send_n <= 0) {
401  LOG(ERROR, "sendmsg failed (address request), "
402  "MetadataProxy::NetlinkAddVhostIp, metadata_ipv6_netlink.cc, "
403  "errno = " << errno << std::endl);
404  return false;
405  }
406 
407  // read the response
408  aux::NetlinkResponse addr_resp(msg, nl_sock);
409  aux::IPv6Reader ipv6_reader(dev_idx);
410  int ret_value = addr_resp.response_iterate(ipv6_reader);
411  if (ret_value > 0) {
412  vhost_ll_ip = ipv6_reader.vhost_addr_;
413  return true;
414  }
415  }
416 
417  return false;
418 }
419 
420 void MetadataProxy::
421 NetlinkAddVhostNb(const IpAddress& nb_ip, const MacAddress& via_mac) {
422  if (!nb_ip.is_v6()) {
423  return;
424  }
425 
426  // set ip address
427  in6_addr addr6;
428  int addr_res = inet_pton(AF_INET6, nb_ip.to_string().c_str(), &addr6);
429  if (addr_res < 0) {
430  LOG(ERROR, "An error has occured during address initialization,"
431  "MetadataProxy::NetlinkAddVhostNb, metadata_ipv6_netlink.cc, "
432  "errno = " << errno << std::endl);
433  return;
434  }
435  if (addr_res == 0) {
436  LOG(ERROR, "A wrong address has been specified,"
437  "MetadataProxy::NetlinkAddVhostNb, metadata_ipv6_netlink.cc, "
438  "address = " << nb_ip.to_string().c_str() << std::endl);
439  return;
440  }
441 
442  // set mac address
443  unsigned char nb_mac_addr[] = {0,0,0,0,0,0};
444  via_mac.ToArray(nb_mac_addr, sizeof(nb_mac_addr));
445 
446  // set device index
447  const int dev_idx =
449  if (dev_idx <= 0) {
450  LOG(ERROR, "Error while retreiving device index,"
451  "MetadataProxy::NetlinkAddVhostNb, metadata_ipv6_netlink.cc, "
452  "dev_idx = " << dev_idx << std::endl);
453  }
454 
455  // open a socket
456  aux::NetlinkSocket nl_sock;
457 
459 
460  ndmsg &nd_message_hdr = nd_req.nl_req_hdr;
461  nd_message_hdr.ndm_family = AF_INET6; // AF_INET/AF_INET6/AF_UNSPEC
462  nd_message_hdr.ndm_ifindex = dev_idx;
463  nd_message_hdr.ndm_state = NUD_PERMANENT;
464 
465  nlmsghdr &nl_message_hdr = nd_req.nl_message_hdr;
466  nl_message_hdr.nlmsg_type = RTM_NEWNEIGH;
467  nl_message_hdr.nlmsg_flags = // NLM_F_REPLACE is neccessary, since a
468  NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE // route might be already
469  | NLM_F_ACK; // in the table
470 
471  nd_req.msg_len = NLMSG_LENGTH(sizeof(nd_message_hdr));
472 
473  // insert lladdr (MAC)
474  nd_req.insert_attr(NDA_LLADDR, sizeof(nb_mac_addr), &nb_mac_addr[0]);
475  // insert IP (IPv6)
476  nd_req.insert_attr(NDA_DST, sizeof(addr6), &addr6);
477 
478  msghdr* msg = nd_req.message(nl_sock.netlink_socket());
479  int send_n = -1;
480  if (msg) {
481  // sending of a request to the kernel
482  send_n = sendmsg(nl_sock.fd(), msg, 0);
483  if (send_n <= 0) {
484  LOG(ERROR, "Sendmsg failed,"
485  "MetadataProxy::NetlinkAddVhostNb, metadata_ipv6_netlink.cc, "
486  "errno = " << errno << std::endl);
487  }
488  }
489 
490  aux::read_ack_response(msg, send_n, nl_sock, "NetlinkAddVhostNb");
491 }
492 
494  if (!intf_addr.is_v6()) {
495  return;
496  }
497 
498  // set ip address
499  in6_addr addr6;
500  int addr_res = inet_pton(AF_INET6, intf_addr.to_string().c_str(), &addr6);
501  if (addr_res < 0) {
502  LOG(ERROR, "An error has occured during address initialization,"
503  "MetadataProxy::NetlinkAddInterfaceRoute, metadata_ipv6_netlink.cc, "
504  "errno = " << errno << std::endl);
505  return;
506  }
507  if (addr_res == 0) {
508  LOG(ERROR, "A wrong address has been specified,"
509  "MetadataProxy::NetlinkAddInterfaceRoute, metadata_ipv6_netlink.cc, "
510  "address = " << intf_addr.to_string().c_str() << std::endl);
511  return;
512  }
513 
514  // set device index
515  const int dev_idx =
517  if (dev_idx <= 0) {
518  LOG(ERROR, "Error while retreiving device index,"
519  "MetadataProxy::NetlinkAddInterfaceRoute, metadata_ipv6_netlink.cc, "
520  "dev_idx = " << dev_idx << std::endl);
521  }
522 
523  // open a socket
524  aux::NetlinkSocket nl_sock;
525 
527 
528  rtmsg &rt_message_hdr = rt_req.nl_req_hdr;
529  std::memset(&rt_message_hdr, 0, sizeof(rt_message_hdr));
530  rt_message_hdr.rtm_family = AF_INET6; // AF_INET/AF_INET6/AF_UNSPEC
531  rt_message_hdr.rtm_table = RT_TABLE_MAIN;
532  rt_message_hdr.rtm_protocol = RTPROT_STATIC;
533  rt_message_hdr.rtm_scope = RT_SCOPE_UNIVERSE;
534  rt_message_hdr.rtm_type = RTN_UNICAST;
535  rt_message_hdr.rtm_dst_len = 128;
536 
537 
538  nlmsghdr &nl_message_hdr = rt_req.nl_message_hdr;
539  nl_message_hdr.nlmsg_type = RTM_NEWROUTE;
540  nl_message_hdr.nlmsg_flags = // NLM_F_REPLACE is neccessary, since a
541  NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE // route might be already
542  | NLM_F_ACK; // in the table
543 
544  rt_req.msg_len = NLMSG_LENGTH(sizeof(rt_message_hdr));
545 
546  rt_req.insert_attr(RTA_DST, sizeof(addr6), &addr6);
547  rt_req.insert_attr(RTA_OIF, sizeof(dev_idx), &dev_idx);
548 
549  msghdr* msg = rt_req.message(nl_sock.netlink_socket());
550  int send_n = -1;
551  if (msg) {
552  // sending of a request to the kernel
553  send_n = sendmsg(nl_sock.fd(), msg, 0);
554  if (send_n <= 0) {
555  LOG(ERROR, "Sendmsg failed,"
556  "MetadataProxy::NetlinkAddInterfaceRoute,"
557  " metadata_ipv6_netlink.cc, "
558  "errno = " << errno << std::endl);
559  }
560  }
561 
562  aux::read_ack_response(msg, send_n, nl_sock, "NetlinkAddVhostNb");
563 }
564 
565 //
566 // END-OF-FILE
567 //
568 
struct nlmsghdr * resp_nh_
A pointer to the begin of the response.
void insert_attr(unsigned short attr_type, int attr_len, const void *attr_data)
Inserts an attribute stored in attr_data into this Netlink reequest.
unsigned int & msg_len
A reference to the total length of this Netlink request.
NetlinkResponse(msghdr *req_msg, NetlinkSocket &nl_sock)
Construct a NetlinkResponse object from the request message and the Netlink socket.
int n_recv_
The number of received bytes in the response.
~NetlinkResponse()
NetlinkResponse dtor.
bool ToArray(u_int8_t *p, size_t s) const
Definition: mac_address.cc:93
IPv6Reader(const unsigned int itf_idx)
int response_iterate(const T &handler_func)
Iterates over all response parts.
boost::asio::ip::address IpAddress
Definition: address.h:13
void insert_attr(nlmsghdr *nl_msg, unsigned short attr_type, int attr_len, const void *attr_data)
A wrapper function to insert an attribute into a netlink message.
char buffer_[16384]
A buffer for response data.
nlmsghdr nl_message_hdr
A structure carrying the main netlink header.
iovec iov_
An iovec struct to keep headers with data.
const std::string & vhost_interface_name() const
Definition: agent.cc:104
char payload[16384]
A block of memory of memory carrying attributes of this request.
int socket_fd
A Netlink socket ID (descriptor)
std::unique_ptr< msghdr > msg_ptr_
A pointer to the Netlink message header.
struct iovec iov_
Contains response data.
bool NetlinkGetVhostIp(Ip6Address &vhost_ll_ip)
Adds an IP address specified in vhost_ll_ip to vhost0 interface inet6 addresses.
const NetlinkRequest & operator=(const NetlinkRequest< T > &)
Disallow assignment operator.
A class template to store Netlink request data.
const NetlinkSocket & operator=(const NetlinkSocket &nl)
Forbid the copy assignment.
boost::asio::ip::address_v6 Ip6Address
Definition: address.h:15
Agent * agent()
Definition: services_init.h:31
sockaddr_nl & netlink_socket()
Returns a reference to the Netlink socket sockaddr struct.
T nl_req_hdr
A structure carrying the request-specific header.
int VhostIndex(const std::string &vhost_name)
Returns index of a network device that is specified by vhost_name.
void NetlinkAddVhostNb(const IpAddress &nb_ip, const MacAddress &via_mac)
Adds a new neighbour (an arp entry) with given IP and MAC addresses.
struct msghdr msg_
contains message with response
ServicesModule * services_
sockaddr_nl local_sock_addr
A Netlink socket sockaddr structure.
NetlinkResponse()
Forbid default ctor for NetlinkResponse.
void NetlinkAddInterfaceRoute(const IpAddress &intf_addr)
Adds routes to internal addresses of VM&#39;s interfaces.
NetlinkRequest()
Creates new blank request.
int operator()(const struct nlmsghdr *resp_nh) const
const int & fd() const
Returns a reference to the Netlink socket descriptor.
#define LOG(_Level, _Msg)
Definition: logging.h:33
int read_ipv6_address(const struct nlmsghdr *resp_nh) const
NetlinkSocket()
Creates a socket and binds it with AF_NETLINK family.
A storage for the Netlink socket descriptor.
msghdr * message(sockaddr_nl &s_nl)
Returns a pointer to the Netlink message, generated using this class.
~NetlinkRequest()
NetlinkRequest dtor.
static bool read_ack_response(const msghdr *msg, int send_n, NetlinkSocket &nl_sock, const char *func_name)
Reads a response (ack/nock) after a Netlink request.
~NetlinkSocket()
Closes the Netlink connection.