IBR-DTNSuite  0.12
dtntunnel.cpp
Go to the documentation of this file.
1 /*
2  * dtntunnel.cpp
3  *
4  * Copyright (C) 2013 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 "config.h"
23 
24 #include <iostream>
25 #include <iomanip>
26 #include <stdlib.h>
27 
28 #ifdef HAVE_LIBDAEMON
29 #include <libdaemon/daemon.h>
30 #endif
31 
32 // Base for send and receive bundle to/from the IBR-DTN daemon.
33 #include "ibrdtn/api/Client.h"
34 
35 // TCP client implemented as a stream.
36 #include <ibrcommon/net/socket.h>
37 
38 // Some classes to be thread-safe.
39 #include "ibrcommon/thread/Mutex.h"
42 #include <ibrcommon/Logger.h>
43 
44 // Basic functionalities for streaming.
45 #include <iostream>
46 
47 // A queue for bundles.
48 #include <queue>
49 
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <string.h>
55 #include <errno.h>
56 
57 #include <sys/ioctl.h>
58 #include <sys/socket.h>
59 #include <linux/if.h>
60 #include <linux/if_tun.h>
61 
62 int tun_alloc(char *dev, short int flags) {
63 
64  struct ifreq ifr;
65  int fd, err;
66  char clonedev[] = "/dev/net/tun";
67 
68  /* Arguments taken by the function:
69  *
70  * char *dev: the name of an interface (or '\0'). MUST have enough
71  * space to hold the interface name if '\0' is passed
72  * int flags: interface flags (eg, IFF_TUN etc.)
73  */
74 
75  /* open the clone device */
76  if( (fd = open(clonedev, O_RDWR)) < 0 ) {
77  return fd;
78  }
79 
80  /* preparation of the struct ifr, of type "struct ifreq" */
81  memset(&ifr, 0, sizeof(ifr));
82 
83  ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
84 
85  if (*dev) {
86  /* if a device name was specified, put it in the structure; otherwise,
87  * the kernel will try to allocate the "next" device of the
88  * specified type */
89  strncpy(ifr.ifr_name, dev, IFNAMSIZ);
90  }
91 
92  /* try to create the device */
93  if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
94  close(fd);
95  return err;
96  }
97 
98  /* if the operation was successful, write back the name of the
99  * interface to the variable "dev", so the caller can know
100  * it. Note that the caller MUST reserve space in *dev (see calling
101  * code below) */
102  strcpy(dev, ifr.ifr_name);
103 
104  /* this is the special file descriptor that the caller will use to talk
105  * with the virtual interface */
106  return fd;
107 }
108 
109 size_t throughput_data_up[5] = { 0, 0, 0, 0, 0 };
110 size_t throughput_data_down[5] = { 0, 0, 0, 0, 0 };
112 
113 void add_throughput_data(ssize_t amount, int updown) {
114  if (updown == 0) {
116  } else {
118  }
119 }
120 
122  float throughput_sum_up = 0;
123  float throughput_sum_down = 0;
124 
125  for (int i = 0; i < 5; ++i) {
126  throughput_sum_up += static_cast<float>(throughput_data_up[i]);
127  throughput_sum_down += static_cast<float>(throughput_data_down[i]);
128  }
129 
130  std::cout << " up: " << setiosflags(ios::right) << setw(12) << setiosflags(ios::fixed) << setprecision(2) << (throughput_sum_up/1024) << " kB/s ";
131  std::cout << " down: " << setiosflags(ios::right) << setw(12) << setiosflags(ios::fixed) << setprecision(2) << (throughput_sum_down/1024) << " kB/s\r" << std::flush;
132 
133  throughput_pos++;
134  if (throughput_pos > 4) throughput_pos = 0;
135 
138 }
139 
140 class TUN2BundleGateway : public dtn::api::Client
141 {
142  public:
143  TUN2BundleGateway(const std::string &app, ibrcommon::socketstream &stream, const std::string &ptp_dev)
144  : dtn::api::Client(app, stream), _stream(stream), _fd(-1)
145  {
146  char tun_name[IFNAMSIZ];
147 
148  strcpy(tun_name, ptp_dev.c_str());
149  _fd = tun_alloc(tun_name, IFF_TUN); /* tun interface */
150 
151  tun_device = tun_name;
152 
153  if (_fd == -1)
154  throw ibrcommon::Exception("Error: failed to open tun device");
155 
156  // connect the API
157  this->connect();
158  }
159 
163  virtual ~TUN2BundleGateway()
164  {
165  // Close the tcp connection.
166  _stream.close();
167  };
168 
169  void shutdown() {
170  if (_fd < 0) return;
171 
172  // close client connection
173  this->close();
174 
175  // close socket
176  ::close(_fd);
177  _fd = -1;
178  }
179 
180  void process(const dtn::data::EID &endpoint, unsigned int lifetime = 60) {
181  if (_fd == -1) throw ibrcommon::Exception("Tunnel closed.");
182 
183  char data[65536];
184  ssize_t ret = ::read(_fd, data, sizeof(data));
185 
186  if (ret == -1) {
187  throw ibrcommon::Exception("Error: failed to read from tun device");
188  }
189 
190  add_throughput_data(ret, 1);
191 
192  // create a blob
194 
195  // add the data
196  blob.iostream()->write(data, ret);
197 
198  // create a new bundle
200 
201  b.destination = endpoint;
202  b.push_back(blob);
203  b.lifetime = lifetime;
204 
205  // transmit the packet
206  (*this) << b;
207  flush();
208  }
209 
210  const std::string& getDeviceName() const
211  {
212  return tun_device;
213  }
214 
215  private:
216  ibrcommon::socketstream &_stream;
217 
218  // file descriptor for the tun device
219  int _fd;
220 
221  std::string tun_device;
222 
228  void received(const dtn::data::Bundle &b)
229  {
231  ibrcommon::BLOB::iostream stream = ref.iostream();
232  char data[65536];
233  stream->read(data, sizeof(data));
234  size_t ret = stream->gcount();
235 
236  if (::write(_fd, data, ret) < 0)
237  {
238  IBRCOMMON_LOGGER_TAG("Core", error) << "Error while writing" << IBRCOMMON_LOGGER_ENDL;
239  }
240 
241  add_throughput_data(ret, 0);
242  }
243 };
244 
245 bool m_running = true;
246 TUN2BundleGateway *_gateway = NULL;
247 
248 void term(int signal)
249 {
250  if (signal >= 1)
251  {
252  m_running = false;
253  if (_gateway != NULL)
254  _gateway->shutdown();
255  }
256 }
257 
258 void print_help(const char *argv0)
259 {
260  std::cout << "-- dtntunnel (IBR-DTN) --" << std::endl;
261  std::cout << "Syntax: " << argv0 << " [options] <endpoint>" << std::endl;
262  std::cout << " <endpoint> Destination of outgoing bundles" << std::endl << std::endl;
263  std::cout << "* optional parameters *" << std::endl;
264  std::cout << " -h Display help message" << std::endl;
265  std::cout << " -d <dev> Virtual network device to create (default: tun0)" << std::endl;
266  std::cout << " -s <name> Application suffix of the local endpoint (default: tunnel)" << std::endl;
267  std::cout << " -l <seconds> Lifetime of each packet (default: 60)" << std::endl;
268  std::cout << " -t Show throughput" << std::endl;
269 #ifdef HAVE_LIBDAEMON
270  std::cout << " -D Daemonize the process" << std::endl;
271  std::cout << " -k Stop the running daemon" << std::endl;
272  std::cout << " -p <file> Store the pid in this pidfile" << std::endl;
273 #endif
274 }
275 
276 #ifdef HAVE_LIBDAEMON
277 static char* __daemon_pidfile__ = NULL;
278 
279 static const char* __daemon_pid_file_proc__(void) {
280  return __daemon_pidfile__;
281 }
282 #endif
283 
284 int main(int argc, char *argv[])
285 {
286  int index;
287  int c;
288 
289  std::string ptp_dev("tun0");
290  std::string app_name("tunnel");
291  std::string endpoint("dtn:none");
292  unsigned int lifetime = 60;
293  bool daemonize = false;
294  bool stop_daemon = false;
295  std::string pidfile;
296  bool throughput = false;
297 
298  // catch process signals
299  ibrcommon::SignalHandler sighandler(term);
300  sighandler.handle(SIGINT);
301  sighandler.handle(SIGTERM);
302  sighandler.handle(SIGQUIT);
303 
304 #ifdef HAVE_LIBDAEMON
305  while ((c = getopt (argc, argv, "td:s:l:hDkp:")) != -1)
306 #else
307  while ((c = getopt (argc, argv, "td:s:l:h")) != -1)
308 #endif
309  switch (c)
310  {
311 #ifdef HAVE_LIBDAEMON
312  case 'D':
313  daemonize = true;
314  break;
315 
316  case 'k':
317  daemonize = true;
318  stop_daemon = true;
319  break;
320 
321  case 'p':
322  pidfile = optarg;
323  break;
324 #endif
325  case 'd':
326  ptp_dev = optarg;
327  break;
328 
329  case 't':
330  throughput = true;
331  break;
332 
333  case 's':
334  app_name = optarg;
335  break;
336 
337  case 'l':
338  lifetime = atoi(optarg);
339  break;
340 
341  default:
342  print_help(argv[0]);
343  return 1;
344  }
345 
346  int optindex = 0;
347  for (index = optind; index < argc; ++index)
348  {
349  switch (optindex)
350  {
351  case 0:
352  endpoint = std::string(argv[index]);
353  break;
354  }
355 
356  optindex++;
357  }
358 
359  // print help if not enough parameters are set
360  if (!stop_daemon && (optindex < 1)) { print_help(argv[0]); exit(0); }
361 
362  //initialize sighandler after possible exit call
363  sighandler.initialize();
364 
365  // logging options
366  //const unsigned char logopts = ibrcommon::Logger::LOG_DATETIME | ibrcommon::Logger::LOG_LEVEL;
367  const unsigned char logopts = 0;
368 
369  // error filter
371 
372  // logging filter, everything but debug, err and crit
374 
375  // syslog filter, everything but DEBUG and NOTICE
377 
378 #ifdef HAVE_LIBDAEMON
379  if (daemonize) {
380  // enable syslog logging
381  ibrcommon::Logger::enableSyslog(argv[0], LOG_PID, LOG_DAEMON, logsys);
382  } else
383 #endif
384  {
385  // add logging to the cout
386  ibrcommon::Logger::addStream(std::cout, logstd, logopts);
387 
388  // add logging to the cerr
389  ibrcommon::Logger::addStream(std::cerr, logerr, logopts);
390  }
391 
392 #ifdef HAVE_LIBDAEMON
393  if (daemonize)
394  {
395 #ifdef HAVE_DAEMON_RESET_SIGS
396  /* Reset signal handlers */
397  if (daemon_reset_sigs(-1) < 0) {
398  IBRCOMMON_LOGGER_TAG("Core", error) << "Failed to reset all signal handlers: " << strerror(errno) << IBRCOMMON_LOGGER_ENDL;
399  return 1;
400  }
401 
402  /* Unblock signals */
403  if (daemon_unblock_sigs(-1) < 0) {
404  IBRCOMMON_LOGGER_TAG("Core", error) << "Failed to unblock all signals: " << strerror(errno) << IBRCOMMON_LOGGER_ENDL;
405  return 1;
406  }
407 #endif
408  pid_t pid;
409 
410  /* Set identification string for the daemon for both syslog and PID file */
411  daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
412 
413  /* set the pid file path */
414  if (pidfile.length() > 0) {
415  __daemon_pidfile__ = new char[pidfile.length() + 1];
416  ::strcpy(__daemon_pidfile__, pidfile.c_str());
417  daemon_pid_file_proc = __daemon_pid_file_proc__;
418  }
419 
420  /* Check if we are called with -k parameter */
421  if (stop_daemon)
422  {
423  int ret;
424 
425  /* Kill daemon with SIGTERM */
426 
427  /* Check if the new function daemon_pid_file_kill_wait() is available, if it is, use it. */
428  if ((ret = daemon_pid_file_kill_wait(SIGTERM, 5)) < 0)
429  IBRCOMMON_LOGGER_TAG("Core", warning) << "Failed to kill daemon: " << strerror(errno) << IBRCOMMON_LOGGER_ENDL;
430 
431  return ret < 0 ? 1 : 0;
432  }
433 
434  /* Check that the daemon is not rung twice a the same time */
435  if ((pid = daemon_pid_file_is_running()) >= 0) {
436  IBRCOMMON_LOGGER_TAG("Core", error) << "Daemon already running on PID file " << pid << IBRCOMMON_LOGGER_ENDL;
437  return 1;
438  }
439 
440  /* Prepare for return value passing from the initialization procedure of the daemon process */
441  if (daemon_retval_init() < 0) {
442  IBRCOMMON_LOGGER_TAG("Core", error) << "Failed to create pipe." << IBRCOMMON_LOGGER_ENDL;
443  return 1;
444  }
445 
446  /* Do the fork */
447  if ((pid = daemon_fork()) < 0) {
448 
449  /* Exit on error */
450  daemon_retval_done();
451  return 1;
452 
453  } else if (pid) { /* The parent */
454  int ret;
455 
456  /* Wait for 20 seconds for the return value passed from the daemon process */
457  if ((ret = daemon_retval_wait(20)) < 0) {
458  IBRCOMMON_LOGGER_TAG("Core", error) << "Could not recieve return value from daemon process: " << strerror(errno) << IBRCOMMON_LOGGER_ENDL;
459  return 255;
460  }
461 
462  return ret;
463 
464  } else { /* The daemon */
465  /* Close FDs */
466  if (daemon_close_all(-1) < 0) {
467  IBRCOMMON_LOGGER_TAG("Core", error) << "Failed to close all file descriptors: " << strerror(errno) << IBRCOMMON_LOGGER_ENDL;
468 
469  /* Send the error condition to the parent process */
470  daemon_retval_send(1);
471 
472  /* Do a cleanup */
473  daemon_retval_send(255);
474  daemon_signal_done();
475  daemon_pid_file_remove();
476 
477  return -1;
478  }
479 
480  /* Create the PID file */
481  if (daemon_pid_file_create() < 0) {
482  IBRCOMMON_LOGGER_TAG("Core", error) << "Could not create PID file ( " << strerror(errno) << ")." << IBRCOMMON_LOGGER_ENDL;
483  daemon_retval_send(2);
484 
485  /* Do a cleanup */
486  daemon_retval_send(255);
487  daemon_signal_done();
488  daemon_pid_file_remove();
489 
490  return -1;
491  }
492 
493  /* Send OK to parent process */
494  daemon_retval_send(0);
495  }
496  }
497 #endif
498 
499  IBRCOMMON_LOGGER_TAG("Core", info) << "IBR-DTN IP <-> Bundle Tunnel" << IBRCOMMON_LOGGER_ENDL;
500 
501  // create a connection to the dtn daemon
502  ibrcommon::vaddress addr("localhost", 4550);
504 
505  try {
506  // set-up tun2bundle gateway
507  TUN2BundleGateway gateway(app_name, conn, ptp_dev);
508  _gateway = &gateway;
509 
510  IBRCOMMON_LOGGER_TAG("Core", info) << "Local: " << app_name << IBRCOMMON_LOGGER_ENDL;
511  IBRCOMMON_LOGGER_TAG("Core", info) << "Peer: " << endpoint << IBRCOMMON_LOGGER_ENDL;
512  IBRCOMMON_LOGGER_TAG("Core", info) << "Device: " << gateway.getDeviceName() << IBRCOMMON_LOGGER_ENDL;
514  IBRCOMMON_LOGGER_TAG("Core", notice) << "Now you need to set-up the ip tunnel. You can use commands like this:" << IBRCOMMON_LOGGER_ENDL;
515  IBRCOMMON_LOGGER_TAG("Core", notice) << "# sudo ip link set " << gateway.getDeviceName() << " up mtu 65535" << IBRCOMMON_LOGGER_ENDL;
516  IBRCOMMON_LOGGER_TAG("Core", notice) << "# sudo ip addr add 10.0.0.1/24 dev " << gateway.getDeviceName() << IBRCOMMON_LOGGER_ENDL;
518 
519  timer_t timerid;
520  struct sigevent sev;
521 
522  if (!daemonize && throughput) {
523  // enable throughput timer
524  signal(SIGRTMIN, timer_display_throughput);
525 
526  sev.sigev_notify = SIGEV_SIGNAL;
527  sev.sigev_signo = SIGRTMIN;
528  sev.sigev_value.sival_ptr = &timerid;
529 
530  // create a timer
531  timer_create(CLOCK_MONOTONIC, &sev, &timerid);
532 
533  // arm the timer
534  struct itimerspec its;
535  size_t freq_nanosecs = 200000000;
536  its.it_value.tv_sec = freq_nanosecs / 1000000000;;
537  its.it_value.tv_nsec = freq_nanosecs % 1000000000;
538  its.it_interval.tv_sec = its.it_value.tv_sec;
539  its.it_interval.tv_nsec = its.it_value.tv_nsec;
540 
541  if (timer_settime(timerid, 0, &its, NULL) == -1) {
542  IBRCOMMON_LOGGER_TAG("Core", error) << "Timer set failed." << IBRCOMMON_LOGGER_ENDL;
543  }
544  }
545 
546  // destination
547  dtn::data::EID eid(endpoint);
548 
549  while (m_running)
550  {
551  gateway.process(eid, lifetime);
552  }
553 
554  gateway.shutdown();
555  } catch (const ibrcommon::Exception &ex) {
556  if (m_running) {
558  return -1;
559  }
560  }
561 
562 #ifdef HAVE_LIBDAEMON
563  if (daemonize) {
564  /* Do a cleanup */
565  IBRCOMMON_LOGGER_TAG("Core", info) << "Stopped" << app_name << IBRCOMMON_LOGGER_ENDL;
566  daemon_retval_send(255);
567  daemon_signal_done();
568  daemon_pid_file_remove();
569  } else
570 #endif
571  {
572  std::cout << std::endl;
573  }
574 
575  return 0;
576 }