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