rohc team mailing list archive
-
rohc team
-
Mailing list archive
-
Message #00708
Ethernet Transport app for ROHC pkts
Hello,
I have built an app which is an Ethernet based transport application for
ROHC pkts very similar to rohctunnel app on the trunk. The major highlights
are:-
1) It directly uses Ethernet as transport.
2) Because of Ethernet it is pont-to-point and cannot traverse
routers/gateways.
3) Since Ethernet's minimum payload is 46 bytes and kernel adds padding for
any payload less than that size, thus I have added a length byte after
sequence bytes before sending it on Ethernet. Other end will read this byte
as the length of ROHC packet instead of length returned by Ethernet
interface.
I am attaching the patch for review and submission.
regards,
Raman
=== modified file 'app/Makefile.am'
--- app/Makefile.am 2012-11-17 09:43:22 +0000
+++ app/Makefile.am 2012-12-29 08:30:11 +0000
@@ -5,6 +5,7 @@
################################################################################
SUBDIRS = \
+ ethTunnel \
performance \
tunnel \
sniffer
=== added directory 'app/ethTunnel'
=== added file 'app/ethTunnel/Makefile.am'
--- app/ethTunnel/Makefile.am 1970-01-01 00:00:00 +0000
+++ app/ethTunnel/Makefile.am 2012-12-19 10:26:33 +0000
@@ -0,0 +1,28 @@
+################################################################################
+# Name : Makefile
+# Author : Didier Barvaux <didier.barvaux@xxxxxxxxxxxxxxxxxxxx>
+# Didier Barvaux <didier@xxxxxxxxxxx>
+# Description: create the Ethernt ROHC tunnel application
+################################################################################
+
+sbin_PROGRAMS = ethTunnel
+
+ethTunnel_CFLAGS = \
+ -g -Wall -Wstrict-prototypes \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/comp \
+ -I$(top_srcdir)/src/decomp
+
+ethTunnel_LDFLAGS = \
+ -L$(top_builddir)/src/common/.libs \
+ -L$(top_builddir)/src/comp/.libs \
+ -L$(top_builddir)/src/decomp/.libs
+
+ethTunnel_SOURCES = \
+ ethTunnel.c
+
+ethTunnel_LDADD = \
+ -lrohc_comp \
+ -lrohc_decomp\
+ -lrohc_common
+
=== added file 'app/ethTunnel/ethTunnel.c'
--- app/ethTunnel/ethTunnel.c 1970-01-01 00:00:00 +0000
+++ app/ethTunnel/ethTunnel.c 2013-01-07 13:47:39 +0000
@@ -0,0 +1,1701 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file ethTunnel.c
+ * @brief ROHC Ethernet tunnel
+ *
+ * Description
+ * -----------
+ *
+ * The program creates a ROHC tunnel over Ethernet between two systems.
+ * Both the systems need to be on same Ethernet network connected via
+ * L2 hub/switch. This is needed so that packets can be forwarded using
+ * MAC Address without the need of network layer routing.
+ *
+ * A ROHC tunnel compresses the IP packets it receives from a virtual
+ * network interface and decompresses the ROHC packets it receives over Ethernet.
+ *
+ * +-----------+ +----------+
+ * IP packets | Virtual | +--------------+ | |
+ * sent by --> | interface | --> | Compressor | --> | ROHC |
+ * the host | (TUN) | +--------------+ | packets |
+ * | | | over |
+ * IP packets | | +--------------+ | Ethernet |
+ * received <-- | | <-- | Decompressor | <-- | |
+ * from the | | +--------------+ | |
+ * tunnel +-----------+ +----------+
+ *
+ * The program outputs messages from the tunnel application on stderr and
+ * messages from the ROHC library on stdout. It outputs compression statistics
+ * on file descriptor 3 and decompression statistics on file descriptor 4.
+ *
+ * The tunnel can emulate a lossy medium with a given error rate. Unidirectional
+ * mode can be forced (no feedback channel).
+ *
+ * Usage
+ * -----
+ *
+ * Run the ethTunnel without any argument to see what arguments the application
+ * accepts.
+ *
+ * Basic example
+ * -------------
+ *
+ * Type as root on machine A:
+ *
+ * # ethTunnel rohc0 remote 08:00:27:E1:1E:E6 local-interface eth0 tun-ip-address 10.0.0.1
+ *
+ * Type as root on machine B:
+ *
+ * # ethTunnel rohc0 remote 08:00:27:0F:D9:8D local-interface eth0 tun-ip-address 10.0.0.2
+ *
+ * Notes:-
+ * 1) The 'tun-ip-address' currently only supports IPv4 address.
+ * 2) The 'tun-ip-address' defaults to /24 netmask.
+ * 3) The 'tun-ip-address' should not clash with IP routes on any of the machines.
+ * Use command 'route -n' to check routes defined on the system.
+ * 4) To know the MAC address of remote machines use command 'arp' OR get it
+ * directly from remote machine
+ * 5) Use command 'route -n' in a different shell to check new route defined on the
+ * system after executing above 'ethTunnel' binary. An entry similar to:
+ * Destination Gateway Genmask Flags Metric Ref Use Iface
+ * 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 rohc0
+ * should be observed.
+ *
+ * Then, on machine B:
+ * $ ping 10.0.0.1
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <signal.h>
+#include <errno.h>
+#include <math.h> /* for HUGE_VAL */
+#include <time.h> /* for time(2) */
+#include <sys/time.h> /* for gettimeofday(2) */
+#include <assert.h>
+
+/* TUN includes */
+#include <net/if.h> /* for IFNAMSIZ */
+#include <linux/if_tun.h>
+#include <net/ethernet.h> //ETHER_ADDR_LEN=6, ETH_P_*
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+/* struct sockaddr_ll */
+#include <netpacket/packet.h>
+
+/* UDP includes */
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+/* ROHC includes */
+#include "rohc.h"
+#include "rohc_comp.h"
+#include "rohc_decomp.h"
+
+
+
+/*
+ * Macros & definitions:
+ */
+
+/// Return the greater value from the two
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+
+/// The maximal size of data that can be received on the virtual interface
+#define TUNTAP_BUFSIZE 1518
+
+/// The maximal size of a ROHC packet
+#define MAX_ROHC_SIZE (5 * 1024)
+
+/// Enable debug ?
+#define DEBUG 0
+
+/// Stop on compression/decompression failure
+#define STOP_ON_FAILURE 0
+
+#define MACFMT "%02X:%02X:%02X:%02X:%02X:%02X"
+#define MACBYTES(p) (uint8_t)p[0],(uint8_t)p[1], \
+ (uint8_t)p[2],(uint8_t)p[3], \
+ (uint8_t)p[4],(uint8_t)p[5]
+
+
+/*
+ * Function prototypes:
+ */
+
+int tun_create(char *name, const char* tun_ip_address);
+int read_from_tun(int fd, unsigned char *packet, unsigned int *length);
+int write_to_tun(int fd, unsigned char *packet, unsigned int length);
+
+int ethernet_create(const char* laddr, int* localInterfaceIndex);
+int read_from_ethernet(int sock, unsigned char *buffer, unsigned int *length);
+int write_to_ethernet(int sock, unsigned char destMacOctets[ETHER_ADDR_LEN],
+ int localInterfaceIndex,
+ unsigned char *packet, unsigned int length);
+
+int tun2ethernet(struct rohc_comp *comp,
+ int from, int to,
+ unsigned char destMacOctets[ETHER_ADDR_LEN],
+ int localInterfaceIndex,
+ int error, double ber, double pe2, double p2);
+int ethernet2tun(struct rohc_decomp *decomp, int from,
+ int to, int localInterfaceIndex);
+int flush_feedback(struct rohc_comp *comp,
+ unsigned char destMacOctets[ETHER_ADDR_LEN],
+ int localInterfaceIndex, int to);
+
+void dump_packet(char *descr, unsigned char *packet, unsigned int length);
+double get_probability(char *arg, int *error);
+int is_timeout(struct timeval first,
+ struct timeval second,
+ unsigned int max);
+int convertMacStringToArray(const char* macStr,
+ unsigned char macOctets[ETHER_ADDR_LEN]);
+static int gen_random_num(const struct rohc_comp *const comp,
+ void *const user_context)
+ __attribute__((nonnull(1)));
+
+/*
+ * Main functions:
+ */
+
+
+/// Whether the application should continue to live or not
+int alive;
+
+
+/**
+ * @brief Catch the INT, TERM and KILL signals to properly shutdown the tunnel
+ *
+ * @param sig The signal catched: SIGINT, SIGTERM or SIGKILL
+ */
+void sighandler(int sig)
+{
+ fprintf(stderr, "signal %d received, terminate the process\n", sig);
+ alive = 0;
+}
+
+
+/**
+ * @brief Display the application usage
+ */
+void usage(void)
+{
+ printf("Ethernet ROHC tunnel: make a ROHC over Ethernet\n\
+\n\
+usage:\n\
+ ethTunnel [ version | help ]\n\
+ ethTunnel TUNNEL [ ERROR ] [ DIR ]\n\
+\n\
+Tunnel parameters:\n\
+ TUNNEL := NAME remote MAC_ADDR local-interface LOCAL tun-ip-address IPv4_ADDR\n\
+ NAME := STRING The name of the tunnel\n\
+ RADDR := MAC-ADDR The MAC address of remote host in standard notation\n\
+ LOCAL := Local Interface The local ethernet interface string of the host\n\
+ IPv4_ADDR := IP Address The local ROHC Tunnel IPv4 Address to create\n\
+\n\
+Error model (none if not specified):\n\
+ ERROR := error { none | uniform RATE | burst PE2 P2 }\n\
+ RATE := FLOAT The BER (binary error rate) to emulate\n\
+ PE2 := FLOAT The probability to be in error state\n\
+ P2 := FLOAT The probability to stay in error state\n\
+\n\
+Direction (bidirectional if not specified):\n\
+ DIR := dir { bidirectional | unidirectional }\n\
+\n\
+Examples:\n\
+ # ethTunnel rohc0 remote 08:00:27:E1:1E:E6 local-interface eth0 tun-ip-address 10.0.0.1\n\
+ # ethTunnel rohc0 remote 08:00:27:E1:1E:E6 local-interface eth0 tun-ip-address 10.0.0.1\\\n\
+ dir unidirectional\n\
+ # ethTunnel rohc0 remote 08:00:27:E1:1E:E6 local-interface eth0 tun-ip-address 10.0.0.1\\\n\
+ error uniform 1e-5 dir bidirectional\n\
+ # ethTunnel rohc0 remote 08:00:27:E1:1E:E6 local-interface eth0 tun-ip-address 10.0.0.1\\\n\
+ error burst 1e-5 2e-5\n\
+");
+}
+
+
+/**
+ * @brief Display the application version
+ */
+void version(void)
+{
+ printf("ROHC Ethernet tunnel version %s\n", rohc_version());
+}
+
+
+/// The file descriptor where to write the compression statistics
+FILE *stats_comp;
+/// The file descriptor where to write the decompression statistics
+FILE *stats_decomp;
+/// The sequence number for the Ethernet tunnel (used to discover lost packets)
+unsigned int seq;
+
+
+/**
+ * @brief Setup a ROHC over Ethernet tunnel
+ *
+ * @param argc The number of arguments given on the command line
+ * @param argv The arguments given on the command line
+ * @return 0 in case of success, > 0 otherwise
+ */
+int main(int argc, char *argv[])
+{
+ int failure = 0;
+
+ char *tun_name;
+ unsigned char raddr[ETHER_ADDR_LEN]; //Remote MAC Address
+ char* laddr; //Local inerface e.g eth0
+ int localInterfaceIndex; //Local Interface Index
+ static const char* tun_ip_address; // Local tun IP Address e.g "10.0.0.1";
+ int error_model;
+ int conv_error;
+ double ber = 0;
+ double pe2 = 0;
+ double p2 = 0;
+ int arg_count;
+ int is_umode;
+
+ unsigned long seed;
+ int ret;
+
+ int tun, ethernet;
+
+ fd_set readfds;
+ struct timespec timeout;
+ sigset_t sigmask;
+
+ struct timeval last;
+ struct timeval now;
+
+ struct rohc_comp *comp;
+ struct rohc_decomp *decomp;
+
+
+ /*
+ * Parse arguments:
+ */
+
+ /* check the number of arguments:
+ * ethTunnel version -> 2 arguments
+ * ethTunnel help -> 2 arguments
+ * ethTunnel TUNNEL -> 6 arguments
+ * ethTunnel TUNNEL ERROR -> 8-10 arguments
+ * ethTunnel TUNNEL DIR -> 8 arguments
+ * ethTunnel TUNNEL ERROR DIR -> 10-12 arguments
+ */
+ if(argc != 2 && argc != 8 && (argc < 10 || argc > 14))
+ {
+ usage();
+ goto quit;
+ }
+
+ /* is the first argument 'version' or 'help' ? */
+ if(strcmp(argv[1], "version") == 0)
+ {
+ version();
+ goto quit;
+ }
+ else if(strcmp(argv[1], "help") == 0)
+ {
+ usage();
+ goto quit;
+ }
+
+ /* first argument is not 'version' or 'help', so we have a tunnel request */
+ if(argc < 6)
+ {
+ usage();
+ goto quit;
+ }
+
+ /* get the tunnel name */
+ tun_name = argv[1];
+
+ /* get the remote MAC address */
+ if(strcmp(argv[2], "remote") != 0)
+ {
+ usage();
+ goto quit;
+ }
+ if(convertMacStringToArray(argv[3], raddr) != ETHER_ADDR_LEN)
+ {
+ fprintf(stderr, "bad remote MAC Address:- %s\n", argv[3]);
+ goto quit;
+ }
+ printf("Remote MAC Address: " MACFMT "\n", MACBYTES(raddr));
+
+ /* get the local Interface address */
+ if(strcmp(argv[4], "local-interface") != 0)
+ {
+ usage();
+ goto quit;
+ }
+ laddr = argv[5];
+
+ /* get the local Interface address */
+ if(strcmp(argv[6], "tun-ip-address") != 0)
+ {
+ usage();
+ goto quit;
+ }
+ tun_ip_address = argv[7];
+
+ /* get the error model and its parameters if present */
+ if(argc >= 9 && strcmp(argv[8], "error") == 0)
+ {
+ arg_count = 9;
+
+ if(argc < arg_count + 1)
+ {
+ fprintf(stderr, "the error keyword requires an argument: "
+ "none, uniform or burst\n");
+ goto quit;
+ }
+
+ if(strcmp(argv[arg_count], "none") == 0)
+ {
+ /* no error model */
+ fprintf(stderr, "do not emulate lossy medium\n");
+ error_model = 0;
+ arg_count++;
+ }
+ else if(strcmp(argv[arg_count], "uniform") == 0)
+ {
+ /* uniform error model */
+ error_model = 1;
+ arg_count++;
+
+ /* check if parameters are present */
+ if(argc < arg_count + 1)
+ {
+ usage();
+ goto quit;
+ }
+
+ /* get the RATE value */
+ ber = get_probability(argv[arg_count], &conv_error);
+ if(conv_error != 0)
+ {
+ fprintf(stderr, "cannot read the RATE parameter\n");
+ goto quit;
+ }
+ arg_count++;
+
+ fprintf(stderr, "emulate lossy medium with %e errors/bit "
+ "= 1 error every %lu bytes\n",
+ ber, (unsigned long) (1 / (ber * 8)));
+ }
+ else if(strcmp(argv[arg_count], "burst") == 0)
+ {
+ /* non-uniform/burst error model */
+ error_model = 2;
+ arg_count++;
+
+ /* check if parameters are present */
+ if(argc < arg_count + 2)
+ {
+ usage();
+ goto quit;
+ }
+
+ /* get the PE2 probability */
+ pe2 = get_probability(argv[arg_count], &conv_error);
+ if(conv_error != 0)
+ {
+ fprintf(stderr, "cannot read the PE2 parameter\n");
+ goto quit;
+ }
+ arg_count++;
+
+ /* get the P2 probability */
+ p2 = get_probability(argv[arg_count], &conv_error);
+ if(conv_error != 0)
+ {
+ fprintf(stderr, "cannot read the P2 parameter\n");
+ goto quit;
+ }
+ arg_count++;
+
+ fprintf(stderr, "emulate lossy medium with PE2 = %e and P2 = %e\n",
+ pe2, p2);
+ }
+ else
+ {
+ fprintf(stderr, "bad error model: %s\n", argv[arg_count]);
+ goto quit;
+ }
+ }
+ else
+ {
+ /* no error model */
+ fprintf(stderr, "do not emulate lossy medium (default)\n");
+ error_model = 0;
+ arg_count = 8;
+ }
+
+ /* get the direction mode if present */
+ if(argc >= arg_count + 1 && strcmp(argv[arg_count], "dir") == 0)
+ {
+ arg_count++;
+
+ if(argc < arg_count + 1)
+ {
+ fprintf(stderr, "the dir keyword requires an argument: "
+ "unidirectional or bidirectional\n");
+ goto quit;
+ }
+
+ if(strcmp(argv[arg_count], "unidirectional") == 0)
+ {
+ fprintf(stderr, "force unidirectional mode\n");
+ is_umode = 1;
+ }
+ else if(strcmp(argv[arg_count], "bidirectional") == 0)
+ {
+ fprintf(stderr, "force bidirectional mode\n");
+ is_umode = 0;
+ }
+ else
+ {
+ fprintf(stderr, "bad direction mode: %s\n", argv[arg_count]);
+ goto quit;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "force bidirectional mode (default)\n");
+ is_umode = 0;
+ }
+
+
+ /*
+ * Network interface part:
+ */
+
+ /* create virtual network interface */
+ tun = tun_create(tun_name, tun_ip_address);
+ if(tun < 0)
+ {
+ fprintf(stderr, "%s creation failed, be sure to start ethTunnel "
+ "as root\n", tun_name);
+ failure = 1;
+ goto quit;
+ }
+ fprintf(stderr, "%s created with IP Address %s, fd %d\n", tun_name,
+ tun_ip_address, tun);
+
+ /* create an Ethernet socket */
+ ethernet = ethernet_create(laddr, &localInterfaceIndex);
+ if(ethernet < 0)
+ {
+ fprintf(stderr, "Ethernet socket creation failed\n");
+ failure = 1;
+ goto close_tun;
+ }
+ fprintf(stderr, "Ethenet socket created, fd %d\n", ethernet);
+
+
+ /*
+ * ROHC part:
+ */
+
+ /* create the compressor and activate profiles */
+ comp = rohc_alloc_compressor(15, 0, 0, 0);
+ if(comp == NULL)
+ {
+ fprintf(stderr, "cannot create the ROHC compressor\n");
+ goto close_ethernet;
+ }
+ rohc_activate_profile(comp, ROHC_PROFILE_UNCOMPRESSED);
+ rohc_activate_profile(comp, ROHC_PROFILE_UDP);
+ rohc_activate_profile(comp, ROHC_PROFILE_IP);
+ rohc_activate_profile(comp, ROHC_PROFILE_UDPLITE);
+ rohc_activate_profile(comp, ROHC_PROFILE_RTP);
+ rohc_activate_profile(comp, ROHC_PROFILE_ESP);
+
+ /* initialize the random generator */
+ seed = time(NULL);
+ srand(seed);
+
+ /* set the callback for random numbers */
+ if(!rohc_comp_set_random_cb(comp, gen_random_num, NULL))
+ {
+ fprintf(stderr, "failed to set the callback for random numbers\n");
+ goto destroy_comp;
+ }
+
+ /* create the decompressor (associate it with the compressor) */
+ decomp = rohc_alloc_decompressor(is_umode ? NULL : comp);
+ if(decomp == NULL)
+ {
+ fprintf(stderr, "cannot create the ROHC decompressor\n");
+ goto destroy_comp;
+ }
+
+
+ /*
+ * Main program:
+ */
+
+ /* write the compression stats to fd 3 */
+ stats_comp = fdopen(3, "a");
+ if(stats_comp == NULL)
+ {
+ fprintf(stderr, "cannot open fd 3 for compression stats: %s (%d)\n",
+ strerror(errno), errno);
+ goto destroy_decomp;
+ }
+
+ /* write the decompression stats to fd 4 */
+ stats_decomp = fdopen(4, "a");
+ if(stats_decomp == NULL)
+ {
+ fprintf(stderr, "cannot open fd 4 for decompresion stats: %s (%d)\n",
+ strerror(errno), errno);
+ goto close_stats_comp;
+ }
+
+ /* init the tunnel sequence number */
+ seq = 0;
+
+ /* catch signals to properly shutdown the bridge */
+ alive = 1;
+ signal(SIGKILL, sighandler);
+ signal(SIGTERM, sighandler);
+ signal(SIGINT, sighandler);
+
+ /* poll network interfaces each second */
+ timeout.tv_sec = 1;
+ timeout.tv_nsec = 0;
+
+ /* mask signals during interface polling */
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGKILL);
+ sigaddset(&sigmask, SIGTERM);
+ sigaddset(&sigmask, SIGINT);
+
+ /* initialize the last time we sent a packet */
+ gettimeofday(&last, NULL);
+
+ /* tunnel each packet from the Ethernet socket to the virtual interface
+ * and from the virtual interface to the Ethernet socket */
+ do
+ {
+ /* poll the read sockets/file descriptors */
+ FD_ZERO(&readfds);
+ FD_SET(tun, &readfds);
+ FD_SET(ethernet, &readfds);
+
+ ret = pselect(max(tun, ethernet) + 1, &readfds, NULL, NULL,
+ &timeout, &sigmask);
+ if(ret < 0)
+ {
+ fprintf(stderr, "pselect failed: %s (%d)\n", strerror(errno), errno);
+ failure = 1;
+ alive = 0;
+ }
+ else if(ret > 0)
+ {
+ /* bridge from TUN to Ethernet */
+ if(FD_ISSET(tun, &readfds))
+ {
+ failure = tun2ethernet(comp, tun, ethernet, raddr, localInterfaceIndex,
+ error_model, ber, pe2, p2);
+ gettimeofday(&last, NULL);
+#if STOP_ON_FAILURE
+ if(failure)
+ alive = 0;
+#endif
+ }
+
+ /* bridge from Ethernet to TUN */
+ if(
+#if STOP_ON_FAILURE
+ !failure &&
+#endif
+ FD_ISSET(ethernet, &readfds))
+ {
+ failure = ethernet2tun(decomp, ethernet, tun, localInterfaceIndex);
+#if STOP_ON_FAILURE
+ if(failure)
+ alive = 0;
+#endif
+ }
+ }
+
+ /* flush feedback data if nothing is sent in the tunnel for a moment */
+ gettimeofday(&now, NULL);
+ if(now.tv_sec > last.tv_sec + 1)
+ {
+ failure = flush_feedback(comp, raddr, localInterfaceIndex, ethernet);
+ last = now;
+#if STOP_ON_FAILURE
+ if(failure)
+ alive = 0;
+#endif
+ }
+ }
+ while(alive);
+
+
+ /*
+ * Cleaning:
+ */
+
+ fclose(stats_decomp);
+close_stats_comp:
+ fclose(stats_comp);
+destroy_decomp:
+ rohc_free_decompressor(decomp);
+destroy_comp:
+ rohc_free_compressor(comp);
+close_ethernet:
+ close(ethernet);
+close_tun:
+ close(tun);
+quit:
+ return failure;
+}
+
+
+
+/*
+ * TUN interface:
+ */
+
+
+/**
+ * @brief Create a virtual network interface of type TUN and assign IP config
+ *
+ * @param name The name of the TUN interface to create
+ * @return An opened file descriptor on the TUN interface in case of
+ * success, a negative value otherwise
+ */
+int tun_create(char *name, const char* tun_ip_address)
+{
+ struct ifreq ifr;
+ int fd, fd1, err;
+
+ if((fd = open("/dev/net/tun", O_RDWR)) < 0)
+ {
+ fprintf(stderr, "failed to open /dev/net/tun: %s (%d)\n",
+ strerror(errno), errno);
+ return fd;
+ }
+
+ /* flags: IFF_TUN - TUN device (no Ethernet headers)
+ * IFF_TAP - TAP device
+ * IFF_NO_PI - Do not provide packet information */
+ bzero(&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ ifr.ifr_name[IFNAMSIZ - 1] = '\0';
+ ifr.ifr_flags = IFF_TUN;
+
+ /* create the TUN interface */
+ if((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0)
+ {
+ fprintf(stderr, "failed to ioctl(TUNSETIFF) on /dev/net/tun: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return err;
+ }
+
+ /*Create socket to assign IPv4 configuration to tun device*/
+ if((fd1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
+ {
+ fprintf(stderr, "failed to open /dev/net/tun: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return fd1;
+ }
+
+ /*Assign IPv4 Address to tun device*/
+ ifr.ifr_flags = 0;
+ struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
+ addr->sin_family = AF_INET;
+ inet_aton(tun_ip_address, &addr->sin_addr);
+ if((err = ioctl(fd1, SIOCSIFADDR, &ifr)) < 0)
+ {
+ fprintf(stderr, "failed to SET IP ADDRESS tun: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd1);
+ close(fd);
+ return err;
+ }
+
+ /*Assign Net mask to tun device*/
+ inet_aton("255.255.255.0", &addr->sin_addr);
+ if((err = ioctl(fd1, SIOCSIFNETMASK, &ifr)) < 0)
+ {
+ fprintf(stderr, "failed to SET NetMask 255.255.255.0: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd1);
+ close(fd);
+ return err;
+ }
+
+ /*Assign Flags for tun device*/
+ ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
+ if((err = ioctl(fd1, SIOCSIFFLAGS, &ifr)) < 0)
+ {
+ fprintf(stderr, "failed to SET Flags IFF_UP | IFF_RUNNING: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd1);
+ close(fd);
+ return err;
+ }
+
+ close(fd1);
+ return fd;
+}
+
+
+/**
+ * @brief Read data from the TUN interface
+ *
+ * Data read by this function contains a 4-byte header that gives the protocol
+ * of the data.
+ *
+ * +-----+-----+-----+-----+
+ * | 0 | 0 | Protocol |
+ * +-----+-----+-----+-----+
+ *
+ * Protocol = 0x0800 for IPv4
+ * 0x86dd for IPv6
+ *
+ * @param fd The TUN file descriptor to read data from
+ * @param packet The buffer where to store the data
+ * @param length OUT: the length of the data
+ * @return 0 in case of success, a non-null value otherwise
+ */
+int read_from_tun(int fd, unsigned char *packet, unsigned int *length)
+{
+ int ret;
+
+ ret = read(fd, packet, *length);
+
+ if(ret < 0 || ret > *length)
+ {
+ fprintf(stderr, "read failed: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+
+ *length = ret;
+
+#if DEBUG
+ fprintf(stderr, "read %u bytes on fd %d\n", ret, fd);
+#endif
+
+ return 0;
+
+error:
+ *length = 0;
+ return 1;
+}
+
+
+/**
+ * @brief Write data to the TUN interface
+ *
+ * Data written to the TUN interface must contain a 4-byte header that gives
+ * the protocol of the data. See the read_from_tun function for details.
+ *
+ * @param fd The TUN file descriptor to write data to
+ * @param packet The packet to write to the TUN interface (header included)
+ * @param length The length of the packet (header included)
+ * @return 0 in case of success, a non-null value otherwise
+ */
+int write_to_tun(int fd, unsigned char *packet, unsigned int length)
+{
+ int ret;
+
+ ret = write(fd, packet, length);
+ if(ret < 0)
+ {
+ fprintf(stderr, "write failed: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+
+#if DEBUG
+ fprintf(stderr, "%u bytes written on fd %d\n", length, fd);
+#endif
+
+ return 0;
+
+error:
+ return 1;
+}
+
+
+
+/*
+ * Raw socket:
+ */
+
+
+/**
+ * @brief Create an Ethernet socket
+ *
+ * @param laddr The local interface name to bind the socket to
+ * @return An opened socket descriptor on the TUN interface in case of
+ * success, a negative value otherwise
+ */
+int ethernet_create(const char* laddr, int* localInterfaceIndex)
+{
+ int sock;
+ int ret;
+ struct sockaddr_ll addr = {0};
+
+ /* create an Ethernet ROHC socket */
+ sock = socket(AF_PACKET, SOCK_DGRAM, htons(ROHC_ETHERTYPE));
+
+ if(sock < 0)
+ {
+ fprintf(stderr, "cannot create Ethernet socket:%s\n", strerror(errno));
+ goto quit;
+ }
+
+ /* Use ifreq structure to get interface information */
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(struct ifreq));
+ size_t if_name_len = strlen(laddr);
+ if (if_name_len < sizeof(ifr.ifr_name))
+ {
+ memcpy(ifr.ifr_name,laddr,if_name_len);
+ ifr.ifr_name[if_name_len] = 0;
+ }
+ else
+ {
+ fprintf(stderr, "Interface name is too long\n");
+ goto quit;
+ }
+
+ // Get interface index
+ if(ioctl(sock, SIOCGIFINDEX, &ifr) == -1)
+ {
+ fprintf(stderr, "Failed to get interface. Error:%s", strerror(errno));
+ goto close;
+ }
+
+ *localInterfaceIndex = ifr.ifr_ifindex;
+ printf("Interface %s index %d ", laddr, *localInterfaceIndex);
+
+ /* Bind the socket on given interface for ROHC */
+ bzero(&addr, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_protocol = htons(ROHC_ETHERTYPE); //ROHC
+ addr.sll_ifindex = *localInterfaceIndex;
+
+ ret = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if(ret < 0)
+ {
+ fprintf(stderr, "cannot bind to Ethernrt socket: %s (%d)\n",
+ strerror(errno), errno);
+ goto close;
+ }
+
+ return sock;
+
+close:
+ close(sock);
+quit:
+ return -1;
+}
+
+
+/**
+ * @brief Read data from the Ethernet socket
+ *
+ * @param sock The Ethernet socket descriptor to read data from
+ * @param buffer The buffer where to store the data
+ * @param length OUT: the length of the data
+ * @return 0 in case of success, a non-null value otherwise
+ */
+int read_from_ethernet(int sock, unsigned char *buffer, unsigned int *length)
+{
+ struct sockaddr_ll fromAddr;
+ socklen_t fromAddrLen = sizeof(struct sockaddr_ll);
+ int ret;
+
+ bzero(&fromAddr, fromAddrLen);
+
+ /* read data from the Etherent socket */
+ ret = recvfrom(sock, buffer, *length, 0, (struct sockaddr *) &fromAddr,
+ &fromAddrLen);
+
+ if(ret < 0 || ret > *length)
+ {
+ fprintf(stderr, "recvfrom failed: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+
+ if(ret == 0)
+ goto quit;
+
+ *length = ret;
+
+#if DEBUG
+ fprintf(stderr, "read one %u-byte ROHC packet from Ethernet \n", *length - 3);
+#endif
+
+quit:
+ return 0;
+
+error:
+ *length = 0;
+ return 1;
+}
+
+
+/**
+ * @brief Write data to Ethernet socket
+ *
+ * All UDP packets contain a sequence number that identify the UDP packet. It
+ * helps discovering lost packets (for statistical purposes). The buffer that
+ * contains the ROHC packet must have 2 bytes of free space at the beginning.
+ * This allows the write_to_ethernet function to add the 2-bytes sequence number in
+ * the UDP packet without allocating new memory.
+ *
+ * @param sock The UDP socket descriptor to write data to
+ * @param destMacOctetes The remote MAC address
+ * @param localInterfaceIndex The local interface index
+ * @param buffer The packet to write to the UDP socket
+ * @param length The length of the packet
+ * @return 0 in case of success, a non-null value otherwise
+ */
+int write_to_ethernet(int sock, unsigned char destMacOctets[ETHER_ADDR_LEN],
+ int localInterfaceIndex,
+ unsigned char *packet, unsigned int length)
+{
+ struct sockaddr_ll addr;
+ int ret;
+
+ bzero(&addr, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_protocol = htons(ROHC_ETHERTYPE);
+ addr.sll_ifindex = localInterfaceIndex;
+ addr.sll_halen = ETHER_ADDR_LEN;
+
+ // Destination MAC Address
+ memcpy(addr.sll_addr,destMacOctets,ETHER_ADDR_LEN);
+
+ /* write the tunnel sequence number at the beginning of packet */
+ packet[0] = (htons(seq) >> 8) & 0xff;
+ packet[1] = htons(seq) & 0xff;
+
+ /* Actual ROHC Packet Length. Since for Ethernet min payload is 46
+ and any excess bytes after payload are padded. It is very much
+ possible that actual ROHC packet length is less than 46 bytes
+ So the actual ROHC packet length is conveyed in third byte */
+ packet[2] = length - 3;
+
+ /* send the data on Ethernet socket */
+ ret = sendto(sock, packet, length, 0, (struct sockaddr *) &addr,
+ sizeof(struct sockaddr_ll));
+ if(ret < 0)
+ {
+ fprintf(stderr, "sendto failed: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+
+#if DEBUG
+ fprintf(stderr, "%u bytes written on Ethernet \n", length);
+#endif
+
+ return 0;
+
+error:
+ return 1;
+}
+
+
+
+/*
+ * Forwarding between the TUN interface and the UDP socket
+ */
+
+
+/**
+ * @brief Forward IP packets received on the TUN interface to Ethernet socket
+ *
+ * The function compresses the IP packets thanks to the ROHC library before
+ * sending them on the Ethernet socket.
+ *
+ * @param comp The ROHC compressor
+ * @param from The TUN file descriptor to read from
+ * @param to The Ethernet socket descriptor to write to
+ * @param destMacOctetes The remote MAC address
+ * @param localInterfaceIndex The local interface index
+ * @param error Type of error emulation (0 = none, 1 = uniform,
+ * 2 = non-uniform/burst)
+ * @param ber The BER (Binary Error Rate) to emulate (value used on first
+ * call only if error model is uniform)
+ * @param pe2 The probability to be in error state (value used on first
+ * call only if error model is non-uniform)
+ * @param p2 The probability to stay in error state (value used on first
+ * call only if error model is non-uniform)
+ * @return 0 in case of success, a non-null value otherwise
+ */
+int tun2ethernet(struct rohc_comp *comp,
+ int from, int to,
+ unsigned char destMacOctets[ETHER_ADDR_LEN],
+ int localInterfaceIndex,
+ int error, double ber, double pe2, double p2)
+{
+ static unsigned char buffer[TUNTAP_BUFSIZE];
+ static unsigned char rohc_packet[3 + MAX_ROHC_SIZE];
+ unsigned int buffer_len = TUNTAP_BUFSIZE;
+ unsigned char *packet;
+ unsigned int packet_len;
+ bool is_segment = false;
+ size_t rohc_size;
+ int ret;
+
+ /* error emulation */
+ static unsigned int dropped = 0;
+ int to_drop = 0;
+
+ /* uniform model */
+ static unsigned long nb_bytes = 0;
+ static unsigned long bytes_without_error = 0;
+
+ /* non-uniform error model */
+ static int is_state_drop = 0;
+ static float p1 = 0;
+ static struct timeval last;
+ struct timeval now;
+
+ /* statistics output */
+ rohc_comp_last_packet_info2_t last_packet_info;
+ const char *modes[] = { "error", "U-mode", "O-mode", "R-mode" };
+ const char *states[] = { "error", "IR", "FO", "SO" };
+
+ /* init the error model variables */
+ if(error > 0)
+ {
+ /* init uniform error model variables */
+ if(error == 1 && bytes_without_error == 0)
+ {
+ // find out the number of bytes without an error
+ bytes_without_error = (unsigned long) (1 / (ber * 8));
+ }
+
+ /* init non-uniform error model variables */
+ if(error == 2 && p1 == 0)
+ {
+ /* init of the random generator */
+ gettimeofday(&last, NULL);
+ srand(last.tv_sec);
+
+ /* init the probability to stay in non-error state */
+ p1 = (p2 - 1) / (1 - pe2) + 2 - p2;
+ }
+ }
+
+#if DEBUG
+ fprintf(stderr, "\n");
+#endif
+
+ /* read the IP packet from the virtual interface */
+ ret = read_from_tun(from, buffer, &buffer_len);
+ if(ret != 0)
+ {
+ fprintf(stderr, "read_from_tun failed\n");
+ goto error;
+ }
+
+ if(buffer_len == 0)
+ goto quit;
+
+ packet = &buffer[4];
+ packet_len = buffer_len - 4;
+
+ /* increment the tunnel sequence number */
+ seq++;
+
+ /* compress the IP packet */
+#if DEBUG
+ fprintf(stderr, "compress packet #%u (%u bytes)\n", seq, packet_len);
+#endif
+ ret = rohc_compress2(comp, packet, packet_len,
+ rohc_packet + 3, MAX_ROHC_SIZE,
+ &rohc_size);
+ if(ret == ROHC_NEED_SEGMENT)
+ {
+ is_segment = true;
+ }
+ else if(ret != ROHC_OK)
+ {
+ fprintf(stderr, "compression of packet #%u failed\n", seq);
+ dump_packet("IP packet", packet, packet_len);
+ goto error;
+ }
+
+ /* emulate lossy medium if asked to do so */
+ if(error == 1) /* uniform error model */
+ {
+ if(nb_bytes + rohc_size >= bytes_without_error)
+ {
+ to_drop = 1;
+ dropped++;
+ fprintf(stderr, "error inserted, ROHC packet #%u dropped\n", seq);
+ nb_bytes = rohc_size - (bytes_without_error - nb_bytes);
+ }
+
+ nb_bytes += rohc_size;
+ }
+ else if(error == 2) /* non-uniform/burst error model */
+ {
+ /* reset to normal state if too much time between two packets */
+ gettimeofday(&now, NULL);
+ if(is_state_drop && is_timeout(last, now, 2))
+ {
+ fprintf(stderr, "go back to normal state (too much time between "
+ "packets #%u and #%u)\n", seq - 1, seq);
+ is_state_drop = 0;
+ }
+ last = now;
+
+ /* do we change state ? */
+ int r = rand() % 1000;
+ if(!is_state_drop)
+ is_state_drop = (r > (int) (p1 * 1000));
+ else
+ is_state_drop = (r <= (int) (p2 * 1000));
+
+ if(is_state_drop)
+ {
+ to_drop = 1;
+ dropped++;
+ fprintf(stderr, "error inserted, ROHC packet #%u dropped\n", seq);
+ }
+ }
+
+ /* write the ROHC packet to Ethernet if not dropped */
+ if(!to_drop)
+ {
+ if(is_segment)
+ {
+ /* retrieve and transmit all remaining ROHC segments */
+ while((ret = rohc_comp_get_segment(comp, rohc_packet + 3, MAX_ROHC_SIZE,
+ &rohc_size)) != ROHC_NEED_SEGMENT)
+ {
+ /* write the ROHC segment in the UDP tunnel */
+ ret = write_to_ethernet(to, destMacOctets, localInterfaceIndex,
+ rohc_packet, 3 + rohc_size);
+ if(ret != 0)
+ {
+ fprintf(stderr, "write_to_ethernet(segment) failed\n");
+ goto error;
+ }
+ }
+ }
+ else
+ {
+ /* write the ROHC packet in the UDP tunnel */
+ ret = write_to_ethernet(to, destMacOctets, localInterfaceIndex,
+ rohc_packet, 3 + rohc_size);
+ if(ret != 0)
+ {
+ fprintf(stderr, "write_to_ethernet(packet) failed\n");
+ goto error;
+ }
+ }
+ }
+
+ /* print packet statistics */
+ last_packet_info.version_major = 0;
+ last_packet_info.version_minor = 0;
+ ret = rohc_comp_get_last_packet_info2(comp, &last_packet_info);
+ if(ret != ROHC_OK)
+ {
+ fprintf(stderr, "cannot display stats about the last compressed packet\n");
+ goto error;
+ }
+ fprintf(stats_comp, "%d\t%s\t%s\t%lu\t%lu\t%lu\t%lu\t%u\n",
+ seq,
+ modes[last_packet_info.context_mode],
+ states[last_packet_info.context_state],
+ last_packet_info.total_last_uncomp_size,
+ last_packet_info.header_last_uncomp_size,
+ last_packet_info.total_last_comp_size,
+ last_packet_info.header_last_comp_size,
+ dropped);
+ fflush(stats_comp);
+
+quit:
+ return 0;
+
+error:
+ return 1;
+}
+
+
+/*
+ * @brief Print packet statistics for decompressor
+ *
+ * @param decomp The ROHC decompressor
+ * @param seq The tunnel sequence number
+ * @param lost_packets The number of lost packets
+ * @return 0 in case of success, 1 otherwise
+ */
+int print_decomp_stats(struct rohc_decomp *decomp,
+ unsigned int seq,
+ unsigned int lost_packets)
+{
+ if(decomp->last_context == NULL)
+ {
+ fprintf(stderr, "cannot display stats (last context == NULL)\n");
+ goto error;
+ }
+
+ fprintf(stats_decomp, "%u\t%d\t%u\t%d\n", seq,
+ lost_packets + decomp->last_context->num_decomp_failures,
+ lost_packets, decomp->last_context->num_decomp_failures);
+ fflush(stats_decomp);
+
+ return 0;
+
+error:
+ return 1;
+}
+
+
+/**
+ * @brief Forward ROHC packets received on the Ethernet to the TUN interface
+ *
+ * The function decompresses the ROHC packets thanks to the ROHC library before
+ * sending them on the TUN interface.
+ *
+ * @param decomp The ROHC decompressor
+ * @param from The Ethernet socket descriptor to read from
+ * @param to The TUN file descriptor to write to
+ * @param localInterfaceIndex The local interface index
+ * @return 0 in case of success, a non-null value otherwise
+ */
+int ethernet2tun(struct rohc_decomp *decomp, int from,
+ int to, int localInterfaceIndex)
+{
+ static unsigned char packet[3 + MAX_ROHC_SIZE];
+ static unsigned char decomp_packet[MAX_ROHC_SIZE + 4];
+ unsigned int packet_len = TUNTAP_BUFSIZE;
+ int decomp_size;
+ int ret;
+ static unsigned int max_seq = 0;
+ unsigned int new_seq;
+ static unsigned long lost_packets = 0;
+ unsigned int rohc_pkt_len = 0;
+
+#if DEBUG
+ fprintf(stderr, "\n");
+#endif
+
+ /* read the sequence number + ROHC packet from the UDP tunnel */
+ ret = read_from_ethernet(from, packet, &packet_len);
+ if(ret != 0)
+ {
+ fprintf(stderr, "read_from_ethernet failed\n");
+ goto error;
+ }
+
+ if(packet_len <= 2)
+ goto quit;
+
+ /* find out if some ROHC packets were lost between compressor and
+ * decompressor (use the tunnel sequence number) */
+ new_seq = ntohs((packet[0] << 8) + packet[1]);
+
+ /* Actual ROHC Packet Length. Since for Ethernet min payload is 46
+ bytes and it is possible that actual ROHC packet length is not
+ that much. So the actual length is extracted from third byte */
+ rohc_pkt_len = packet[2];
+
+ if(new_seq < max_seq)
+ {
+ /* some packets were reordered, the packet was wrongly
+ * considered as lost */
+ fprintf(stderr, "ROHC packet with seq = %u received after seq = %u\n",
+ new_seq, max_seq);
+ lost_packets--;
+ }
+ else if(new_seq > max_seq + 1)
+ {
+ /* there is a gap between sequence numbers, some packets were lost */
+ fprintf(stderr, "ROHC packet(s) probably lost between "
+ "seq = %u and seq = %u\n", max_seq, new_seq);
+ lost_packets += new_seq - (max_seq + 1);
+ }
+ else if(new_seq == max_seq)
+ {
+ /* should not happen */
+ fprintf(stderr, "ROHC packet #%u duplicated\n", new_seq);
+ }
+
+ if(new_seq > max_seq)
+ {
+ /* update max sequence numbers */
+ max_seq = new_seq;
+ }
+
+ /* decompress the ROHC packet */
+#if DEBUG
+ fprintf(stderr, "decompress ROHC packet #%u (%u bytes)\n",
+ new_seq, rohc_pkt_len);
+#endif
+ decomp_size = rohc_decompress(decomp, packet + 3, rohc_pkt_len,
+ &decomp_packet[4], MAX_ROHC_SIZE);
+ if(decomp_size <= 0)
+ {
+ if(decomp_size == ROHC_FEEDBACK_ONLY)
+ {
+ /* no stats for feedback-only packets */
+ goto quit;
+ }
+ else
+ {
+ fprintf(stderr, "decompression of packet #%u failed\n", new_seq);
+ dump_packet("ROHC packet", packet + 3, packet_len - 3);
+ goto drop;
+ }
+ }
+
+ /* build the TUN header */
+ decomp_packet[0] = 0;
+ decomp_packet[1] = 0;
+ switch((decomp_packet[4] >> 4) & 0x0f)
+ {
+ case 4: /* IPv4 */
+ decomp_packet[2] = 0x08;
+ decomp_packet[3] = 0x00;
+ break;
+ case 6: /* IPv6 */
+ decomp_packet[2] = 0x86;
+ decomp_packet[3] = 0xdd;
+ break;
+ default:
+ fprintf(stderr, "bad IP version (%d)\n",
+ (decomp_packet[4] >> 4) & 0x0f);
+ dump_packet("ROHC packet", packet, packet_len);
+ dump_packet("Decompressed packet", &decomp_packet[4], decomp_size);
+ goto drop;
+ }
+
+ /* write the IP packet on the virtual interface */
+ ret = write_to_tun(to, decomp_packet, decomp_size + 4);
+ if(ret != 0)
+ {
+ fprintf(stderr, "write_to_tun failed\n");
+ goto drop;
+ }
+
+ /* print packet statistics */
+ ret = print_decomp_stats(decomp, new_seq, lost_packets);
+ if(ret != 0)
+ {
+ fprintf(stderr, "cannot display stats (print_decomp_stats failed)\n");
+ goto drop;
+ }
+
+quit:
+ return 0;
+
+drop:
+ /* print packet statistics */
+ ret = print_decomp_stats(decomp, new_seq, lost_packets);
+ if(ret != 0)
+ fprintf(stderr, "cannot display stats (print_decomp_stats failed)\n");
+
+error:
+ return 1;
+}
+
+
+
+/*
+ * Feedback flushing to the UDP socket
+ */
+
+
+/**
+ * @brief Flush feedback packets stored at the compressor to the UDP socket
+ *
+ * @param comp The ROHC compressor
+ * @param to The UDP socket descriptor to write to
+ * @param raddr The remote address of the tunnel
+ * @param port The remote port of the tunnel
+ * @return 0 in case of success, a non-null value otherwise
+ */
+int flush_feedback(struct rohc_comp *comp,
+ unsigned char destMacOctets[ETHER_ADDR_LEN],
+ int localInterfaceIndex, int to)
+{
+ static unsigned char rohc_packet[3 + MAX_ROHC_SIZE];
+ int rohc_size;
+ int ret;
+
+#if DEBUG
+ fprintf(stderr, "\n");
+#endif
+
+ /* flush feedback data as many times as necessary */
+ do
+ {
+ /* flush feedback data */
+ rohc_size = rohc_feedback_flush(comp, rohc_packet + 3, MAX_ROHC_SIZE);
+
+#if DEBUG
+ fprintf(stderr, "flush %d bytes of feedback data\n", rohc_size);
+#endif
+
+ if(rohc_size > 0)
+ {
+ /* increment the tunnel sequence number */
+ seq++;
+
+ /* write the ROHC packet in the Ethernet tunnel */
+ ret = write_to_ethernet(to, destMacOctets, localInterfaceIndex,
+ rohc_packet, 3 + rohc_size);
+ if(ret != 0)
+ {
+ fprintf(stderr, "write_to_ethernet failed\n");
+ goto error;
+ }
+ }
+ }
+ while(rohc_size > 0);
+
+ return 0;
+
+error:
+ return 1;
+}
+
+
+
+/*
+ * Miscellaneous functions:
+ */
+
+
+/**
+ * @brief Display the content of a IP or ROHC packet
+ *
+ * This function is used for debugging purposes.
+ *
+ * @param descr A string that describes the packet
+ * @param packet The packet to display
+ * @param length The length of the packet to display
+ */
+void dump_packet(char *descr, unsigned char *packet, unsigned int length)
+{
+ unsigned int i;
+
+ fprintf(stderr, "-------------------------------\n");
+ fprintf(stderr, "%s (%u bytes):\n", descr, length);
+ for(i = 0; i < length; i++)
+ {
+ if(i > 0 && (i % 16) == 0)
+ fprintf(stderr, "\n");
+ else if(i > 0 && (i % 8) == 0)
+ fprintf(stderr, "\t");
+
+ fprintf(stderr, "%.2x ", packet[i]);
+ }
+ fprintf(stderr, "\n");
+ fprintf(stderr, "-------------------------------\n");
+}
+
+
+/**
+ * @brief Get a probability number from the command line
+ *
+ * If error = 1, the return value is undetermined.
+ *
+ * @param arg The argument from the command line
+ * @param error OUT: whether the conversion failed or not
+ * @return The probability
+ */
+double get_probability(char *arg, int *error)
+{
+ double proba;
+ char *endptr;
+
+ /* set error by default */
+ *error = 1;
+
+ /* convert from string to double */
+ proba = strtod(arg, &endptr);
+
+ /* check for conversion error */
+ if(proba == 0 && endptr == arg)
+ {
+ if(errno == ERANGE)
+ fprintf(stderr, "probability out of range (underflow): %s (%d)\n",
+ strerror(errno), errno);
+ else
+ fprintf(stderr, "bad probability value\n");
+ goto quit;
+ }
+
+ /* check for overflow */
+ if(proba == HUGE_VAL)
+ {
+ fprintf(stderr, "probability out of range (overflow): %s (%d)\n",
+ strerror(errno), errno);
+ goto quit;
+ }
+
+ /* check probability value */
+ if(proba < 0 || proba > 1)
+ {
+ fprintf(stderr, "probability must not be negative nor greater than 1\n");
+ goto quit;
+ }
+
+ /* everything is fine */
+ *error = 0;
+
+quit:
+ return proba;
+}
+
+/**
+ * @brief Whether timeout is reached or not ?
+ *
+ * Timeout is reached if the differences between the two dates
+ * is greater than the amount of time given as third parameter.
+ *
+ * @param first The first date
+ * @param second The second date
+ * @param max The maximal amount of time between the two dates
+ * in seconds
+ * @return Whether timeout is reached or not ?
+ */
+int is_timeout(struct timeval first,
+ struct timeval second,
+ unsigned int max)
+{
+ unsigned int delta_sec;
+ int is_timeout;
+
+ delta_sec = second.tv_sec - first.tv_sec;
+
+ if(delta_sec > max)
+ is_timeout = 1;
+ else if(delta_sec == max)
+ {
+ if(second.tv_usec > first.tv_usec)
+ is_timeout = 1;
+ else
+ is_timeout = 0;
+ }
+ else
+ is_timeout = 0;
+
+ return is_timeout;
+}
+
+int convertMacStringToArray(const char * macStr,
+ unsigned char macOctets[ETHER_ADDR_LEN])
+{
+ const char* ch = macStr;
+ char first = 0;
+ char second = 0;
+ char p = 0;
+ int i = 0;
+ int sep_count = 0;
+ int mac_byte_count = 0;
+
+ while(*ch)
+ {
+ if(*ch == ':')
+ {
+ macOctets[i] = (first << 4) | second;
+ ++i;
+ ++sep_count;
+ }
+ else
+ {
+ p = toupper(*ch);
+ if(isalpha(p) && (p >= 'A' && p <= 'F'))
+ {
+ first = p - 'A' + 10;
+ }
+ else if(isdigit(p) && (p >= '0' || p <= '9'))
+ {
+ first = p - '0';
+ }
+ else
+ {
+ fprintf(stderr, "Wrong char %c at octet number %d\n", p, i+1);
+ return 0;
+ }
+
+ ++ch;
+ p = toupper(*ch);
+ if(isalpha(p) && (p >= 'A' && p <= 'F'))
+ {
+ second = p - 'A' + 10;
+ }
+ else if(isdigit(p) && (p >= '0' || p <= '9'))
+ {
+ second = p - '0';
+ }
+ else
+ {
+ fprintf(stderr, "Wrong char %c at octet number %d\n", p, i+1);
+ return 0;
+ }
+ ++mac_byte_count;
+ }
+ ++ch;
+ }
+ macOctets[i] = (first << 4) | second;
+
+ if(sep_count != 5 || mac_byte_count != 6)
+ {
+ return 0;
+ }
+
+ return i + 1;
+}
+
+/**
+ * @brief Generate a random number
+ *
+ * @param comp The ROHC compressor
+ * @param user_context Should always be NULL
+ * @return A random number
+ */
+static int gen_random_num(const struct rohc_comp *const comp,
+ void *const user_context)
+{
+ assert(comp != NULL);
+ assert(user_context == NULL);
+ return rand();
+}
+
=== modified file 'configure.ac'
--- configure.ac 2013-01-06 13:30:27 +0000
+++ configure.ac 2013-01-07 10:35:12 +0000
@@ -629,6 +629,7 @@
statistics/Makefile \
examples/Makefile \
app/Makefile \
+ app/ethTunnel/Makefile \
app/performance/Makefile \
app/tunnel/Makefile \
app/sniffer/Makefile \
Follow ups