OpenSDN source code
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
vnswif_listener.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3  */
4 
5 #include <assert.h>
6 
7 #include <bits/sockaddr.h>
8 #include <linux/netlink.h>
9 #include <linux/rtnetlink.h>
10 #include <ifaddrs.h>
11 
12 #include <base/logging.h>
13 #include <base/util.h>
14 #include <cmn/agent_cmn.h>
15 #include <init/agent_param.h>
16 #include <cfg/cfg_init.h>
17 #include <oper/route_common.h>
18 #include <oper/mirror_table.h>
19 #include <ksync/ksync_index.h>
21 #include "vnswif_listener.h"
22 
25 }
26 
28 }
30  int s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
31 
32  if (s < 0) {
33  LOG(ERROR, "Error <" << errno << ": " << strerror(errno) <<
34  "> creating socket. BackTrace: " << AgentBackTrace(1));
35  _Exit(0);
36  }
37 
38  /* Bind to netlink socket */
39  struct sockaddr_nl addr;
40  memset (&addr, 0, sizeof(addr));
41  addr.nl_family = AF_NETLINK;
42  addr.nl_groups = (RTMGRP_IPV4_ROUTE | RTMGRP_LINK | RTMGRP_IPV4_IFADDR);
43  if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
44  LOG(ERROR, "Error <" << errno << ": " << strerror(errno) <<
45  "> binding to netlink address family. BackTrace: "
46  << AgentBackTrace(1));
47  _Exit(0);
48 
49  }
50 
51  return s;
52 }
53 
55  /* Fetch Links from kernel syncronously, to allow dump request for routes
56  * to go through fine
57  */
58  InitNetlinkScan(RTM_GETLINK, ++seqno_);
59 
60  /* Fetch routes from kernel asyncronously and update the gateway-id */
61  InitNetlinkScan(RTM_GETADDR, ++seqno_);
62 
63  /* Fetch routes from kernel asyncronously and update the gateway-id */
64  InitNetlinkScan(RTM_GETROUTE, ++seqno_);
65 }
66 
67 // Initiate netlink scan based on type and flags
68 void
70  struct nlmsghdr *nlh;
71  const uint32_t buf_size = VnswInterfaceListenerLinux::kMaxBufferSize;
72 
73  memset(tx_buf_, 0, buf_size);
74  nlh = (struct nlmsghdr *)tx_buf_;
75 
76  /* Fill in the nlmsg header */
77  nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
78  nlh->nlmsg_type = type;
79  // The message is a request for dump.
80  nlh->nlmsg_flags = (NLM_F_DUMP | NLM_F_REQUEST);
81  nlh->nlmsg_seq = seqno;
82 
83  struct rtgenmsg *rt_gen = (struct rtgenmsg *) NLMSG_DATA (nlh);
84  rt_gen->rtgen_family = AF_PACKET;
85 
86  boost::system::error_code ec;
87  sock_.send(boost::asio::buffer(nlh,nlh->nlmsg_len), 0, ec);
88  assert(ec.value() == 0);
89 
90  uint8_t read_buf[buf_size];
91 
92  /*
93  * Wait/Read the response for dump request, linux kernel doesn't handle
94  * dump request if response for previous dump request is not complete.
95  */
96  int end = 0;
97  while (end == 0) {
98  memset(read_buf, 0, buf_size);
99  std::size_t len = sock_.receive(boost::asio::buffer(read_buf,
100  buf_size), 0, ec);
101  assert(ec.value() == 0);
102  struct nlmsghdr *nl = (struct nlmsghdr *)read_buf;
103  end = NlMsgDecode(nl, len, seqno);
104  }
105 }
106 
107 
109  read_buf_ = new uint8_t[kMaxBufferSize];
110  sock_.async_receive(boost::asio::buffer(read_buf_, kMaxBufferSize),
111  boost::bind(&VnswInterfaceListenerLinux::ReadHandler, this,
112  boost::asio::placeholders::error,
113  boost::asio::placeholders::bytes_transferred));
114 }
115 
116 
118  const boost::system::error_code &error, std::size_t len) {
119  struct nlmsghdr *nlh;
120 
121  if (!error) {
122  nlh = (struct nlmsghdr *)read_buf_;
123  NlMsgDecode(nlh, len, -1);
124  } else {
125  LOG(ERROR, "Error < : " << error.message() <<
126  "> reading packet on netlink sock");
127  }
128 
129  if (read_buf_) {
130  delete [] read_buf_;
131  read_buf_ = NULL;
132  }
134 }
135 
136 
137 /****************************************************************************
138  * Link Local route event handler
139  ****************************************************************************/
140 int VnswInterfaceListenerLinux::AddAttr(uint8_t *buff, int type, void *data, int alen) {
141  struct nlmsghdr *n = (struct nlmsghdr *)buff;
142  int len = RTA_LENGTH(alen);
143 
144  if (NLMSG_ALIGN(n->nlmsg_len) + len > VnswInterfaceListenerLinux::kMaxBufferSize)
145  return -1;
146 
147  struct rtattr *rta = (struct rtattr*)(((char*)n)+NLMSG_ALIGN(n->nlmsg_len));
148  rta->rta_type = type;
149  rta->rta_len = len;
150  memcpy(RTA_DATA(rta), data, alen);
151  n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
152  return 0;
153 }
154 
156  uint8_t plen,
157  bool del_rt) {
158  struct nlmsghdr *nlh;
159  struct rtmsg *rtm;
160  uint32_t ipaddr;
161 
162  memset(tx_buf_, 0, kMaxBufferSize);
163 
164  nlh = (struct nlmsghdr *) tx_buf_;
165  rtm = (struct rtmsg *) NLMSG_DATA (nlh);
166 
167  /* Fill in the nlmsg header*/
168  nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
169  if (del_rt) {
170  nlh->nlmsg_type = RTM_DELROUTE;
171  nlh->nlmsg_flags = NLM_F_REQUEST;
172  } else {
173  nlh->nlmsg_type = RTM_NEWROUTE;
174  nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
175  }
176  nlh->nlmsg_seq = ++seqno_;
177  rtm = (struct rtmsg *) NLMSG_DATA (nlh);
178  rtm->rtm_table = RT_TABLE_MAIN;
179  rtm->rtm_family = AF_INET;
180  rtm->rtm_type = RTN_UNICAST;
181  rtm->rtm_protocol = kVnswRtmProto;
182  rtm->rtm_scope = RT_SCOPE_LINK;
183  rtm->rtm_dst_len = plen;
184  ipaddr = RT_TABLE_MAIN;
185  AddAttr(tx_buf_, RTA_TABLE, (void *) &ipaddr, 4);
186  ipaddr = htonl(addr.to_ulong());
187  AddAttr(tx_buf_, RTA_DST, (void *) &ipaddr, 4);
188  int if_index = if_nametoindex(agent_->vhost_interface_name().c_str());
189  AddAttr(tx_buf_, RTA_OIF, (void *) &if_index, 4);
190 
191  boost::system::error_code ec;
192  sock_.send(boost::asio::buffer(nlh,nlh->nlmsg_len), 0, ec);
193  assert(ec.value() == 0);
194 }
195 
196 /****************************************************************************
197  * Netlink message handlers
198  * Decodes netlink messages and enqueues events to revent_queue_
199  ****************************************************************************/
201  switch (type) {
202  case NLMSG_DONE:
203  return "NLMSG_DONE";
204  case RTM_NEWADDR:
205  return "RTM_NEWADDR";
206  case RTM_DELADDR:
207  return "RTM_DELADDR";
208  case RTM_NEWROUTE:
209  return "RTM_NEWROUTE";
210  case RTM_DELROUTE:
211  return "RTM_DELROUTE";
212  case RTM_NEWLINK:
213  return "RTM_NEWLINK";
214  case RTM_DELLINK:
215  return "RTM_DELLINK";
216  default:
217  break;
218  }
219  std::stringstream str;
220  str << "UNHANDLED <" << type << ">";
221  return str.str();
222 }
223 
226  struct rtmsg *rtm = (struct rtmsg *) NLMSG_DATA (nlh);
227 
228  if (rtm->rtm_family != AF_INET || rtm->rtm_table != RT_TABLE_MAIN
229  || rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK) {
230  LOG(DEBUG, "Ignoring Netlink route with family "
231  << (uint32_t)rtm->rtm_family
232  << " table " << (uint32_t)rtm->rtm_table
233  << " type " << (uint32_t)rtm->rtm_type
234  << " scope " << (uint32_t)rtm->rtm_scope);
235  return NULL;
236  }
237 
238  int oif = -1;
239  uint32_t dst_ip = 0;
240  uint32_t gw_ip = 0;
241 
242  /* Get the route atttibutes len */
243  int rtl = RTM_PAYLOAD(nlh);
244 
245  /* Loop through all attributes */
246  for (struct rtattr *rth = (struct rtattr *) RTM_RTA(rtm);
247  RTA_OK(rth, rtl); rth = RTA_NEXT(rth, rtl)) {
248  /* Get the gateway (Next hop) */
249  if (rth->rta_type == RTA_DST) {
250  dst_ip = *((int *)RTA_DATA(rth));
251  }
252  if (rth->rta_type == RTA_GATEWAY) {
253  gw_ip = *((int *)RTA_DATA(rth));
254  }
255  if (rth->rta_type == RTA_OIF) {
256  oif = *((int *)RTA_DATA(rth));
257  }
258  }
259 
260  if (oif == -1) {
261  return NULL;
262  }
263 
264  char name[IFNAMSIZ];
265  if_indextoname(oif, name);
266  Ip4Address dst_addr((unsigned long)ntohl(dst_ip));
267  Ip4Address gw_addr((unsigned long)ntohl(gw_ip));
268  LOG(DEBUG, "Handle netlink route message "
269  << NetlinkTypeToString(nlh->nlmsg_type)
270  << " : " << dst_addr.to_string() << "/"
271  << (unsigned short)rtm->rtm_dst_len
272  << " Interface " << name << " GW " << gw_addr.to_string());
273 
275  if (nlh->nlmsg_type == RTM_DELROUTE) {
276  type = Event::DEL_ROUTE;
277  } else {
278  type = Event::ADD_ROUTE;
279  }
280 
281  return new Event(type, dst_addr, rtm->rtm_dst_len, name, gw_addr,
282  rtm->rtm_protocol, rtm->rtm_flags);
283 }
284 
287  /* Get the atttibutes len */
288  int rtl = RTM_PAYLOAD(nlh);
289 
290  const char *port_name = NULL;
291  struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA (nlh);
292  /* Loop through all attributes */
293  for (struct rtattr *rth = IFLA_RTA(ifi); RTA_OK(rth, rtl);
294  rth = RTA_NEXT(rth, rtl)) {
295  /* Get the interface name */
296  if (rth->rta_type == IFLA_IFNAME) {
297  port_name = (char *) RTA_DATA(rth);
298  }
299  }
300 
301  assert(port_name != NULL);
302  LOG(DEBUG, "Handle netlink interface message "
303  << NetlinkTypeToString(nlh->nlmsg_type)
304  << " for interface " << port_name << " flags " << ifi->ifi_flags);
305 
307  if (nlh->nlmsg_type == RTM_DELLINK) {
308  type = Event::DEL_INTERFACE;
309  } else {
310  type = Event::ADD_INTERFACE;
311  }
312  return new Event(type, port_name, ifi->ifi_flags, ifi->ifi_type);
313 }
314 
317  struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA (nlh);
318 
319  // Get interface name from os-index
320  char name[IFNAMSIZ];
321  if_indextoname(ifa->ifa_index, name);
322 
323  LOG(DEBUG, "Handle netlink address message "
324  << NetlinkTypeToString(nlh->nlmsg_type) << " for interface " << name);
325 
326  uint32_t ipaddr = 0;
327  int rtl = IFA_PAYLOAD(nlh);
328  for (struct rtattr *rth = IFA_RTA(ifa); rtl && RTA_OK(rth, rtl);
329  rth = RTA_NEXT(rth,rtl)) {
330  if (rth->rta_type != IFA_LOCAL) {
331  continue;
332  }
333 
334  ipaddr = ntohl(* ((uint32_t *)RTA_DATA(rth)));
335  }
336 
337  if (ipaddr == 0)
338  return NULL;
339 
340  assert(ipaddr != 0);
342  if (nlh->nlmsg_type == RTM_DELADDR) {
343  type = Event::DEL_ADDR;
344  } else {
345  type = Event::ADD_ADDR;
346  }
347  return new Event(type, name, Ip4Address(ipaddr), ifa->ifa_prefixlen,
348  ifa->ifa_flags, false);
349 }
350 
352  std::size_t len, uint32_t seq_no) {
353  Event *event = NULL;
354  struct nlmsghdr *nlh = nl;
355  for (; (NLMSG_OK(nlh, len)); nlh = NLMSG_NEXT(nlh, len)) {
356 
357  switch (nlh->nlmsg_type) {
358  case NLMSG_DONE:
359  if (nlh->nlmsg_seq == seq_no) {
360  return 1;
361  }
362  return 0;
363  case RTM_NEWADDR:
364  case RTM_DELADDR:
365  event = HandleNetlinkAddrMsg(nlh);
366  break;
367  case RTM_NEWROUTE:
368  case RTM_DELROUTE:
369  event = HandleNetlinkRouteMsg(nlh);
370  break;
371  case RTM_NEWLINK:
372  case RTM_DELLINK:
373  event = HandleNetlinkIntfMsg(nlh);
374  break;
375  default:
376  LOG(DEBUG, "VnswInterfaceListenerLinux got message : "
377  << NetlinkTypeToString(nlh->nlmsg_type));
378  break;
379  }
380 
381  if (event) {
382  revent_queue_->Enqueue(event);
383  }
384  }
385  return 0;
386 }
int NlMsgDecode(struct nlmsghdr *nl, std::size_t len, uint32_t seq_no)
WorkQueue< Event * > * revent_queue_
Event * HandleNetlinkAddrMsg(struct nlmsghdr *)
const std::string & vhost_interface_name() const
Definition: agent.cc:104
uint8_t tx_buf_[kMaxBufferSize]
int AddAttr(uint8_t *, int, void *, int)
void ReadHandler(const boost::system::error_code &, std::size_t length)
void InitNetlinkScan(uint32_t type, uint32_t seqno)
local::datagram_protocol::socket sock_
uint8_t type
Definition: load_balance.h:109
Definition: agent.h:358
Event * HandleNetlinkIntfMsg(struct nlmsghdr *)
Event
Definition: http_client.h:27
VnswInterfaceListenerLinux(Agent *agent)
void UpdateLinkLocalRoute(const Ip4Address &addr, uint8_t plen, bool del_rt)
boost::asio::ip::address_v4 Ip4Address
Definition: address.h:14
std::string AgentBackTrace(int skip=1)
Definition: agent.cc:1187
static const uint32_t kMaxBufferSize
#define LOG(_Level, _Msg)
Definition: logging.h:33
string NetlinkTypeToString(uint32_t)
virtual void RegisterAsyncReadHandler()
Event * HandleNetlinkRouteMsg(struct nlmsghdr *)