IBR-DTNSuite  0.10
NetLinkManager.cpp
Go to the documentation of this file.
1 /*
2  * NetLinkManager.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Johannes Morgenroth <morgenroth@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
22 #include "ibrcommon/config.h"
25 #include "ibrcommon/Logger.h"
26 
27 #include <netlink/netlink.h>
28 #include <netlink/route/addr.h>
29 #include <netlink/route/link.h>
30 #include <netlink/route/rtnl.h>
31 #include <netlink/socket.h>
32 #include <netlink/msg.h>
33 #include <netlink/object-api.h>
34 
35 #include <netdb.h>
36 #include <sys/socket.h>
37 #include <sys/types.h>
38 #include <netinet/tcp.h>
39 #include <sys/un.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <fcntl.h>
43 #include <signal.h>
44 #include <arpa/inet.h>
45 
46 #ifndef IFF_UP
47 #include <net/if.h>
48 #endif
49 
50 #include <iostream>
51 #include <sstream>
52 #include <typeinfo>
53 
54 namespace ibrcommon
55 {
56 #ifdef HAVE_LIBNL3
57 typedef nl_object nl_object_header;
58 #else
59  struct nl_object_header {
60  NLHDR_COMMON
61  };
62 #endif
63 
64  vaddress rtnl_addr_get_local_vaddress(struct rtnl_addr *obj) {
65  struct nl_addr *naddr = rtnl_addr_get_local(obj);
66  if (!naddr) return vaddress();
67 
68  int scope = rtnl_addr_get_scope((struct rtnl_addr *) obj);
69 
70  char scope_buf[256];
71  rtnl_scope2str(scope, scope_buf, sizeof( scope_buf ));
72  std::string scopename(scope_buf);
73 
74  struct sockaddr_storage sa_addr;
75  socklen_t sa_len = sizeof(sockaddr_storage);
76  ::memset(&sa_addr, 0, sa_len);
77 
78  if (nl_addr_fill_sockaddr(naddr, (struct sockaddr*)&sa_addr, &sa_len) < 0) {
79  return vaddress();
80  }
81 
82  char addr_buf[256];
83  if (::getnameinfo((struct sockaddr *) &sa_addr, sa_len, addr_buf, sizeof addr_buf, NULL, 0, NI_NUMERICHOST) != 0) {
84  // error
85  return vaddress();
86  }
87 
88  std::string addrname(addr_buf);
89 
90  /*
91  * nl_addr_fill_sockaddr is does not copy the right scope id for ipv6 sockets
92  * which is necessary for link local-local addresses. Thus we add the interface suffix to
93  * all link-local IPv6 addresses.
94  */
95  if ((sa_addr.ss_family == AF_INET6) && (scopename == vaddress::SCOPE_LINKLOCAL)) {
96  int ifindex = rtnl_addr_get_ifindex((struct rtnl_addr *) obj);
98  addrname += "%" + iface.toString();
99  }
100 
101  return vaddress(addrname, "", scopename, sa_addr.ss_family);
102  }
103 
104 #ifdef HAVE_LIBNL3
105  static void nl_cache_callback(struct nl_cache*, struct nl_object *obj, int action, void*)
106 #else
107  static void nl_cache_callback(struct nl_cache*, struct nl_object *obj, int action)
108 #endif
109  {
110  if (obj == NULL) return;
111 
112  // get the header of the nl_object first
113  nl_object_header *header = static_cast<nl_object_header*>(nl_object_priv(obj));
114 
115  switch (header->ce_msgtype) {
116  case RTM_NEWLINK: {
118 
119  struct rtnl_link *link = (struct rtnl_link *) obj;
120 
121  int ifindex = rtnl_link_get_ifindex(link);
122  vinterface iface = LinkManager::getInstance().getInterface(ifindex);
123 
124  struct nl_addr *naddr = rtnl_link_get_addr(link);
125 
126  // default empty address
127  vaddress addr;
128 
129  if (naddr) {
130  char addr_buf[256];
131  nl_addr2str( naddr, addr_buf, sizeof( addr_buf ));
132  std::string addrname(addr_buf);
133 
134  addr = vaddress(addrname, "", static_cast<sa_family_t>(nl_addr_guess_family(naddr)));
135  }
136 
137  unsigned int flags = rtnl_link_get_flags(link);
138 
139  if (flags & IFF_RUNNING) {
140  evt_action = LinkEvent::ACTION_LINK_RUNNING;
141  } else if (flags & IFF_UP) {
142  evt_action = LinkEvent::ACTION_LINK_UP;
143  } else {
144  evt_action = LinkEvent::ACTION_LINK_DOWN;
145  }
146 
147  LinkEvent lme(evt_action, iface, addr);;
149  break;
150  }
151 
152  case RTM_NEWADDR: {
153 #ifdef HAVE_LIBNL3
155 
156  if (action == NL_ACT_NEW)
157  evt_action = LinkEvent::ACTION_ADDRESS_ADDED;
158  else if (action == NL_ACT_DEL)
160 
161  int ifindex = rtnl_addr_get_ifindex((struct rtnl_addr *) obj);
162  vinterface iface = LinkManager::getInstance().getInterface(ifindex);
163 
164  vaddress addr = rtnl_addr_get_local_vaddress((struct rtnl_addr *) obj);
165 
166  LinkEvent lme(evt_action, iface, addr);;
168  break;
169 #endif
170  }
171 
172  default:
173  if (IBRCOMMON_LOGGER_LEVEL > 90) {
174  struct nl_dump_params dp;
175  memset(&dp, 0, sizeof(struct nl_dump_params));
176 #ifdef HAVE_LIBNL3
177  dp.dp_type = NL_DUMP_LINE;
178 #else
179  dp.dp_type = NL_DUMP_BRIEF;
180 #endif
181  dp.dp_fd = stdout;
182  nl_object_dump(obj, &dp);
183  }
184  break;
185  }
186  }
187 
188  void add_addr_to_list(struct nl_object *obj, void *data)
189  {
190  std::list<vaddress> *list = static_cast<std::list<vaddress>*>(data);
191  vaddress addr = rtnl_addr_get_local_vaddress((struct rtnl_addr *) obj);
192 
193  try {
194  addr.address();
195  list->push_back( addr );
196  } catch (const vaddress::address_not_set&) {
197  }
198  }
199 
200  NetLinkManager::netlinkcache::netlinkcache(int protocol)
201  : _protocol(protocol), _nl_handle(NULL), _mngr(NULL)
202  {
203  }
204 
205  NetLinkManager::netlinkcache::~netlinkcache()
206  {
207  }
208 
209  void NetLinkManager::netlinkcache::up() throw (socket_exception)
210  {
211  if (_state != SOCKET_DOWN)
212  throw socket_exception("socket is already up");
213 
214  // create netlink handle
215 #ifdef HAVE_LIBNL3
216  int ret = 0;
217  _nl_handle = nl_socket_alloc();
218 #else
219  _nl_handle = nl_handle_alloc();
220 #endif
221 
222  // allocate a cache manager for ROUTE
223 #ifdef HAVE_LIBNL3
224  ret = nl_cache_mngr_alloc((struct nl_sock*)_nl_handle, _protocol, NL_AUTO_PROVIDE, &_mngr);
225 
226  if (ret != 0)
227  throw socket_exception("can not allocate netlink cache manager");
228 #else
229  _mngr = nl_cache_mngr_alloc((struct nl_handle*)_nl_handle, _protocol, NL_AUTO_PROVIDE);
230 #endif
231 
232  if (_mngr == NULL)
233  throw socket_exception("can not allocate netlink cache manager");
234 
235  for (std::map<std::string, struct nl_cache*>::iterator iter = _caches.begin(); iter != _caches.end(); ++iter)
236  {
237  const std::string &cachename = (*iter).first;
238 #ifdef HAVE_LIBNL3
239  struct nl_cache *c;
240  ret = nl_cache_mngr_add(_mngr, cachename.c_str(), &nl_cache_callback, this, &c);
241 
242  if (ret != 0)
243  throw socket_exception(std::string("can not allocate netlink cache ") + cachename);
244 #else
245  struct nl_cache *c = nl_cache_mngr_add(_mngr, cachename.c_str(), &nl_cache_callback);
246 #endif
247 
248  if (c == NULL)
249  throw socket_exception(std::string("can not allocate netlink cache ") + cachename);
250 
251  (*iter).second = c;
252  }
253 
254  // mark this socket as up
255  _state = SOCKET_UP;
256  }
257 
258  void NetLinkManager::netlinkcache::down() throw (socket_exception)
259  {
260  if ((_state == SOCKET_DOWN) || (_state == SOCKET_DESTROYED))
261  throw socket_exception("socket is not up");
262 
263  // delete the cache manager
264  nl_cache_mngr_free(_mngr);
265 
266 #ifdef HAVE_LIBNL3
267  // delete the socket
268  nl_socket_free((struct nl_sock*)_nl_handle);
269 #endif
270 
271  // mark this socket as down
272  if (_state == SOCKET_UNMANAGED)
273  _state = SOCKET_DESTROYED;
274  else
275  _state = SOCKET_DOWN;
276  }
277 
278  int NetLinkManager::netlinkcache::fd() const throw (socket_exception)
279  {
280  if (_state == SOCKET_DOWN) throw socket_exception("fd not available");
281  return nl_cache_mngr_get_fd(_mngr);
282  }
283 
284  void NetLinkManager::netlinkcache::receive() throw (socket_exception)
285  {
286  if (_state == SOCKET_DOWN) throw socket_exception("socket not connected");
287  nl_cache_mngr_data_ready(_mngr);
288 #if 0
289  int ret = nl_cache_mngr_data_ready(_mngr);
290 
291  /*
292  * Some implementations always return an error code, because they reached
293  * the last message. In that cases we should not throw an exception. Since
294  * error checking here is not so important we disable this feature.
295  */
296  if (ret < 0)
297  throw socket_exception("can not receive data from netlink manager");
298 #endif
299  }
300 
301  void NetLinkManager::netlinkcache::add(const std::string &cachename) throw (socket_exception)
302  {
303  if (_state != SOCKET_DOWN)
304  throw socket_exception("socket is already up; adding not possible");
305 
306  std::map<std::string, struct nl_cache*>::const_iterator iter = _caches.find(cachename);
307  if (iter != _caches.end()) throw socket_exception("cache already added");
308 
309  // add placeholder to the map
310  _caches[cachename] = NULL;
311  }
312 
313  struct nl_cache* NetLinkManager::netlinkcache::get(const std::string &cachename) const throw (socket_exception)
314  {
315  if (_state == SOCKET_DOWN) throw socket_exception("socket not available");
316 
317  std::map<std::string, struct nl_cache*>::const_iterator iter = _caches.find(cachename);
318  if (iter == _caches.end()) throw socket_exception("cache not available");
319 
320  return (*iter).second;
321  }
322 
323  NetLinkManager::NetLinkManager()
324  : _route_cache(NETLINK_ROUTE), _running(true)
325  {
326  // add cache types to the route cache
327  _route_cache.add("route/link");
328  _route_cache.add("route/addr");
329 
330  // initialize the netlink cache
331  _route_cache.up();
332  }
333 
334  NetLinkManager::~NetLinkManager()
335  {
336  stop();
337  join();
338 
339  try {
340  _route_cache.down();
341  } catch (const socket_exception&) {
342  // catch socket exception caused by double down()
343  // __cancellation() + destructor
344  }
345  }
346 
347  void NetLinkManager::up() throw ()
348  {
349  // add netlink fd to vsocket
350  _sock.add(&_route_cache);
351  _sock.up();
352 
353  this->start();
354  }
355 
356  void NetLinkManager::down() throw ()
357  {
358  _sock.down();
359  _sock.clear();
360 
361  this->stop();
362  this->join();
363  }
364 
365  void NetLinkManager::__cancellation() throw ()
366  {
367  _running = false;
368  _sock.down();
369  }
370 
371  void NetLinkManager::run() throw ()
372  {
373  try {
374  while (_running)
375  {
376  socketset socks;
377  _sock.select(&socks, NULL, NULL, NULL);
378 
379  for (socketset::iterator iter = socks.begin(); iter != socks.end(); ++iter) {
380  try {
381  netlinkcache &cache = dynamic_cast<netlinkcache&>(**iter);
382  cache.receive();
383  } catch (const bad_cast&) { };
384  }
385  }
386  } catch (const socket_exception&) {
387  // stopped / interrupted
388  IBRCOMMON_LOGGER_TAG("NetLinkManager", error) << "NetLink connection stopped" << IBRCOMMON_LOGGER_ENDL;
389  }
390  }
391 
392  const std::list<vaddress> NetLinkManager::getAddressList(const vinterface &iface, const std::string &scope)
393  {
394  ibrcommon::MutexLock l(_cache_mutex);
395 
396  std::list<vaddress> addresses;
397 
398  struct rtnl_addr *filter = rtnl_addr_alloc();
399  const std::string i = iface.toString();
400  int ifindex = rtnl_link_name2i(_route_cache.get("route/link"), i.c_str());
401 
402  rtnl_addr_set_ifindex(filter, ifindex);
403 
404  // set scope if requested
405  if (scope.length() > 0) {
406  rtnl_addr_set_scope(filter, rtnl_str2scope(scope.c_str()));
407  }
408 
409  // query the netlink cache for all known addresses
410  nl_cache_foreach_filter(_route_cache.get("route/addr"), (struct nl_object *) filter, add_addr_to_list, &addresses);
411 
412  // free the filter address
413  rtnl_addr_put(filter);
414 
415  return addresses;
416  }
417 
418  const vinterface NetLinkManager::getInterface(const int ifindex) const
419  {
420  char buf[256];
421  rtnl_link_i2name(_route_cache.get("route/link"), ifindex, (char*)&buf, sizeof buf);
422  return std::string((char*)&buf);
423  }
424 }