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