manage tun connection
authorMazet Laurent <laurent.mazet@thalesgroup.com>
Fri, 23 May 2025 15:16:44 +0000 (17:16 +0200)
committerMazet Laurent <laurent.mazet@thalesgroup.com>
Fri, 23 May 2025 15:16:44 +0000 (17:16 +0200)
makefile
mapec.c
mapec.h
mapec_test.c

index 84a07fc08b3dafe7fb33751b304364805d610963..6e112547db4343a263a1367f192fc2d0b1d53bce 100644 (file)
--- a/makefile
+++ b/makefile
@@ -6,6 +6,7 @@ CC ?= gcc
 RANLIB ?= ranlib
 
 #INCLUDES = -I../debug -D__MEMORY_ALLOCATION__
+INCLUDES = -D_DEFAULT_SOURCE=1
 INCLUDES += -DVERBOSE_COLOR=1
 #INCLUDES += -DVERBOSE_LOCATION=1
 OFLAGS  = -O4 -Os
diff --git a/mapec.c b/mapec.c
index 8f1a420ec34cd78af0ac3ea411cc73bff1bb0cb6..3aec8a2286a07ba351704e1ff68d713cc4b0bc13 100644 (file)
--- a/mapec.c
+++ b/mapec.c
   - initial version
 */
 
+#include <arpa/inet.h>
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/if_tun.h>
+#include <linux/sockios.h>
+#include <net/if.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <sys/ioctl.h>
 #include <sys/select.h>
 #include <sys/socket.h>
+#include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -37,7 +43,8 @@ DECLARE_VERBOSE_LEVEL (mapec, INFO);
 */
 typedef enum {
     MAPEC_error_e = 0,
-    MAPEC_udp_e
+    MAPEC_udp_e,
+    MAPEC_tun_e
 } MAPEC_type_t;
 
 /**
@@ -83,20 +90,23 @@ void __attribute__ ((constructor)) _init_morep_ (void)
     atexit (free_all_mapecs);
 }
 
-int parse_protocol (MAPEC_addr_t *addr, char *url)
+int parse_protocol (MAPEC_type_t *type, char *url)
 {
 
     VERBOSE (mapec, TRACE, PRINTF ("parse_protocol\n"));
 
     int rc = -1;
-    MAPEC_type_t type = MAPEC_error_e;
+    MAPEC_type_t newtype = MAPEC_error_e;
     if (strncmp (url, "udp://", 6) == 0) {
-        type = MAPEC_udp_e;
+        newtype = MAPEC_udp_e;
+        rc = 6;
+    } else if (strncmp (url, "tun://", 6) == 0) {
+        newtype = MAPEC_tun_e;
         rc = 6;
     }
-    if (addr->type == MAPEC_error_e) {
-        addr->type = type;
-    } else if (addr->type != type) {
+    if (*type == MAPEC_error_e) {
+        *type = newtype;
+    } else if (*type != newtype) {
         rc = -1;
     }
 
@@ -126,7 +136,7 @@ int parse_address (struct sockaddr_in *addr, char *url)
 
     addr->sin_family = AF_INET;
     addr->sin_port = htons (atoi (port));
-    
+
     VERBOSE (mapec, INFO, PRINTF ("parse URL -> %s %s\n", hostname, port));
 
     if (hostname[0] != '*') {
@@ -146,33 +156,28 @@ int parse_address (struct sockaddr_in *addr, char *url)
     return 0;
 }
 
-int MAPEC_Connect (char *local_address, char * remote_address)
+int connect_udp (MAPEC_addr_t *mapec, char *local_address, char *remote_address)
 {
 
-    VERBOSE (mapec, TRACE, PRINTF ("MAPEC_Connect\n"));
-
-    MAPEC_addr_t mapec = { 0 };
-    int rc;
+    VERBOSE (mapec, TRACE, PRINTF ("connect_udp\n"));
 
     /* parse local url */
     VERBOSE (mapec, DEBUG, PRINTF ("MAPEC local addr %s\n", local_address));
-    rc = parse_protocol (&mapec, local_address);
-    if ((rc <= 0) || (parse_address (&mapec.addrin, local_address + rc) < 0)) {
+    if (parse_address (&mapec->addrin, local_address) < 0) {
         VERBOSE (mapec, ERROR, PRINTF ("can't parse local url\n"));
         errno = EINVAL;
         return -1;
     }
-    VERBOSE (mapec, DEBUG, PRINTF ("local port -> %d\n", ntohs (mapec.addrin.sin_port)));
+    VERBOSE (mapec, DEBUG, PRINTF ("local port -> %d\n", ntohs (mapec->addrin.sin_port)));
 
     /* parse remote url */
     VERBOSE (mapec, DEBUG, PRINTF ("MAPEC remote addr %s\n", remote_address));
-    rc = parse_protocol (&mapec, remote_address);
-    if ((rc <= 0) || (parse_address (&mapec.addrout, remote_address + rc) < 0)) {
+    if (parse_address (&mapec->addrout, remote_address) < 0) {
         VERBOSE (mapec, ERROR, PRINTF ("can't parse remote url\n"));
         errno = EINVAL;
         return -1;
     }
-    VERBOSE (mapec, DEBUG, PRINTF ("remote port -> %d\n", ntohs (mapec.addrout.sin_port)));
+    VERBOSE (mapec, DEBUG, PRINTF ("remote port -> %d\n", ntohs (mapec->addrout.sin_port)));
 
     /* create socket */
     int fid = socket (PF_INET, SOCK_DGRAM, 0);
@@ -185,18 +190,110 @@ int MAPEC_Connect (char *local_address, char * remote_address)
     /* bind on local */
     int val = 1;
     setsockopt (fid, SOL_SOCKET, SO_REUSEADDR, (int *)&val, sizeof (val));
-    if (bind (fid, (struct sockaddr *)&mapec.addrin, sizeof (mapec.addrin)) < 0) {
+    if (bind (fid, (struct sockaddr *)&mapec->addrin, sizeof (mapec->addrin)) < 0) {
         VERBOSE (mapec, ERROR, PRINTF ("can't bind socket %d\n", fid));
         return -1;
     }
 
     /* connect on remote */
-    if (connect (fid, (struct sockaddr *)&mapec.addrout, sizeof (mapec.addrout)) < 0) {
+    if (connect (fid, (struct sockaddr *)&mapec->addrout, sizeof (mapec->addrout)) < 0) {
         VERBOSE (mapec, ERROR, PRINTF ("can't connect socket %d\n", fid));
         return -1;
     }
-    MAPEC_list[fid] = mapecdup (&mapec);
+
+    return fid;
+}
+
+#define ifreq_offsetof(x) offsetof(struct ifreq, x)
+
+int connect_tun (MAPEC_addr_t *mapec, char *device_name, char *ip_address)
+{
+
+    VERBOSE (mapec, TRACE, PRINTF ("connect_tun\n"));
+
+    /* request a TUN device */
+    int fid = open ("/dev/net/tun", O_RDWR);
+    if (fid < 0) {
+        VERBOSE (mapec, ERROR, PRINTF ("can't open a tun device '%s'\n", device_name));
+        return -1;
+    }
+
+    /* link to named device */
+    struct ifreq ifr = { 0 };
+    ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+    if (ioctl (fid, TUNSETIFF, &ifr) < 0) {
+        VERBOSE (mapec, ERROR, PRINTF ("can't link to named device '%s'\n", device_name));
+        return -1;
+    }
+
+    /* Create a channel to the NET kernel. */
+    int sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock < 0) {
+        VERBOSE (mapec, ERROR, PRINTF ("can't accept socket %d\n", sock));
+        errno = ENOMEM;
+        return -1;
+    }
+
+    /* get interface name */
+    ifr.ifr_flags = 0;
+    strncpy(ifr.ifr_name, device_name, IFNAMSIZ);
+
+    /* set ip address */
+    mapec->addrin.sin_family = AF_INET;
+    mapec->addrin.sin_port   = 0;
+    mapec->addrin.sin_addr.s_addr = inet_addr (ip_address);
+    memcpy (&ifr.ifr_addr, &mapec->addrin, sizeof (struct sockaddr));
+
+    if ((ioctl (sock, SIOCSIFADDR, &ifr) < 0) || (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)) {
+        VERBOSE (mapec, ERROR, PRINTF ("can't set ip address '%s' on tun device '%s'\n", ip_address, device_name));
+        return -1;
+    }
+
+    ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+    if (ioctl (sock, SIOCSIFFLAGS, &ifr) < 0) {
+        VERBOSE (mapec, ERROR, PRINTF ("can't activate tun interface '%s'\n", device_name));
+        return -1;
+    }
+    close (sock);
+
+    return fid;
+}
+
+int MAPEC_Connect (char *arg1, char *arg2)
+{
+
+    VERBOSE (mapec, TRACE, PRINTF ("MAPEC_Connect\n"));
+
+    MAPEC_addr_t mapec = { 0 };
+
+    /* check type */
+    int rc = parse_protocol (&mapec.type, arg1);
+    if (arg2) {
+        rc = parse_protocol (&mapec.type, arg2);
+    }
+    if (rc < 0) {
+        VERBOSE (mapec, ERROR, PRINTF ("non coherent protocol '%s' <> '%s'\n", arg1, (arg2) ? arg2 : ""));
+        errno = EINVAL;
+        return -1;
+    }
+
+    int fid;
+    switch (mapec.type) {
+    case MAPEC_udp_e:
+        fid = connect_udp (&mapec, arg1 + rc, arg2 + rc);
+        break;
+
+    case MAPEC_tun_e:
+        fid = connect_tun (&mapec, arg1 + rc, arg2 + rc);
+        break;
+
+    default:
+        fid = -1;
+    }
+
+    if (fid >= 0) {
+        MAPEC_list[fid] = mapecdup (&mapec);
+    }
 
     return fid;
 }
diff --git a/mapec.h b/mapec.h
index b2fc202eb243bfd8f5ecc1959fb0f36fc63e5ce8..af382839c50aac49753aeab31f0a867535bdc41f 100644 (file)
--- a/mapec.h
+++ b/mapec.h
@@ -35,10 +35,13 @@ __BEGIN_DECLS
    Establish a connection between a local URL and a remote URL. Block
    until connection is established or refused.
 
-   @param local_address local address of the MAPEC with the following
-   syntax: 'udp://hostname:port' or 'tun://device'.
-   @param remote_address same format as for local_address. For TUN,
-   remote address is not usesfull.
+   @param arg1 first argument
+   @param arg2 second argrument
+
+   For UDP connection, arguements are local and remote addresses. The
+   syntax is 'udp://hostname:port'
+   For TUN connection, first argument is an url, second argument is the
+   IP address of the tun device. The syntax is tun://device and tun://ip
 
    @return a new file descriptor associated with this connection
 
@@ -46,7 +49,7 @@ __BEGIN_DECLS
    @see MAPEC_Send()
    @see MAPEC_Receive()
 */
-int MAPEC_Connect (char *local_address, char *remote_address);
+int MAPEC_Connect (char *arg1, char *arg2);
 
 /**
    @ingroup MAPEC
index b19757338ce2b3e18cbb7f1688af1fcb51501ebd..1848e6ddf43e4f8dd114de7d9c94c5d7cdc896a2 100644 (file)
 
 #include "mapec.h"
 
-int MAPEC_Send_UDP_OK = 0;
-int MAPEC_Echo_UDP_OK = 0;
+char *progname = NULL;
+char *client_local_url = "udp://localhost:13501";
+char *client_remote_url = "udp://localhost:12501";
+char *server_local_url = "udp://localhost:12501";
+char *server_remote_url = "udp://localhost:13501";
+
+int MAPEC_Send_OK = 0;
+int MAPEC_Echo_OK = 0;
 
 #define UNUSED __attribute__ ((unused))
 
-void *udp_client (UNUSED void *dummy)
+void *client (UNUSED void *dummy)
 {
 
-    VERBOSE (mapec, TRACE, PRINTF ("test_client\n"));
+    VERBOSE (mapec, TRACE, PRINTF ("client\n"));
 
     int count;
     unsigned char tx_data[10][1500];
     int tx_len[10];
 
-    int fid = MAPEC_Connect ("udp://localhost:13501", "udp://localhost:12501");
+    int fid = MAPEC_Connect (client_local_url, client_remote_url);
     if (fid < 0) {
         VERBOSE (mapec, WARNING, PRINTF ("client MAPEC_Connect error\n"));
-        MAPEC_Echo_UDP_OK = 0;
+        MAPEC_Echo_OK = 0;
         return NULL;
     }
 
@@ -53,7 +59,7 @@ void *udp_client (UNUSED void *dummy)
     VERBOSE (mapec, INFO, PRINTF ("client connected to server, trying to exchange %d messages\n", nb_messages));
 
     for (count = 0; count < nb_messages; count++) {
-            
+
         tx_len[count] = rand () % sizeof (tx_data[count]);
         for (int i = 0; i < tx_len[count]; i++)
             tx_data[count][i] = rand () % 256;
@@ -61,13 +67,13 @@ void *udp_client (UNUSED void *dummy)
         int rc = MAPEC_Send (fid, tx_data[count], tx_len[count]);
         if (rc < 0) {
             VERBOSE (mapec, WARNING, PRINTF ("client MAPEC_Send tx error (%d/%d-%d)\n", rc, tx_len[count], errno));
-            MAPEC_Echo_UDP_OK = 0;
+            MAPEC_Echo_OK = 0;
             return NULL;
         }
 
         if (tx_len[count] != rc) {
             VERBOSE (mapec, WARNING, PRINTF ("client MAPEC_Send tx len error tx=%d rc=%d\n", tx_len[count], rc));
-        MAPEC_Echo_UDP_OK = 0;
+        MAPEC_Echo_OK = 0;
             return NULL;
         }
         VERBOSE (mapec, DEBUG, PRINTF ("client MAPEC_Send ok (%d)\n", tx_len[count]));
@@ -89,36 +95,36 @@ void *udp_client (UNUSED void *dummy)
 
         if (tx_len[count] != rx_len) {
             VERBOSE (mapec, WARNING, PRINTF ("client MAPEC_Receive rx len error : tx=%d / rx=%d\n", tx_len[count], rx_len));
-            MAPEC_Echo_UDP_OK = 0;
+            MAPEC_Echo_OK = 0;
             return NULL;
         }
 
         if (memcmp (rx_data, tx_data[count], tx_len[count]) != 0 ) {
             VERBOSE (mapec, WARNING, PRINTF ("client MAPEC_Receive rx corrupted data\n"));
-            MAPEC_Echo_UDP_OK = 0;
+            MAPEC_Echo_OK = 0;
             return NULL;
         }
     }
 
     MAPEC_Close (fid);
     VERBOSE (mapec, INFO, PRINTF ("client end of test, %d echo processed\n", count));
-    MAPEC_Echo_UDP_OK = 1;
+    MAPEC_Echo_OK = 1;
 
     return NULL;
 }
 
-void *udp_server (UNUSED void *dummy)
+void *server (UNUSED void *dummy)
 {
 
-    VERBOSE (mapec, TRACE, PRINTF ("udp_server\n"));
+    VERBOSE (mapec, TRACE, PRINTF ("server\n"));
 
-    int fid = MAPEC_Connect ("udp://localhost:12501", "udp://localhost:13501");
+    int fid = MAPEC_Connect (server_local_url, server_remote_url);
     if (fid < 0) {
-        VERBOSE (mapec, ERROR, PRINTF ("MAPEC_Connect (%d)", fid));
+        VERBOSE (mapec, ERROR, PRINTF ("MAPEC_Connect (%d)\n", fid));
         exit (1);
     }
 
-    VERBOSE (mapec, INFO, PRINTF ("udp_server waiting for data\n"));
+    VERBOSE (mapec, INFO, PRINTF ("server waiting for data\n"));
 
     while (1) {
         unsigned char data[8192];
@@ -135,12 +141,12 @@ void *udp_server (UNUSED void *dummy)
             break;
         }
 
-        VERBOSE (mapec, INFO, PRINTF ("udp_server receive data len=%d, sending echo\n", rx_len));
+        VERBOSE (mapec, INFO, PRINTF ("server receive data len=%d, sending echo\n", rx_len));
         usleep (1e4);
 
         int tx_len = MAPEC_Send (fid, data, rx_len);
         if (tx_len == rx_len) {
-            MAPEC_Send_UDP_OK = 1;
+            MAPEC_Send_OK = 1;
         }
     }
 
@@ -160,43 +166,86 @@ void *udp_server (UNUSED void *dummy)
 int main (int argc, char **argv)
 {
 
-    /* process arguments */
-    if (argc > 1) {
-        CHANGE_VERBOSE_LEVEL (mapec, atoi (argv[1]));
-        argc--;
+    /* get basename */
+    char *pt = progname = argv[0];
+    while (*pt) {
+        if ((*pt == '/') || (*pt == '\\')) {
+           progname = pt + 1;
+        }
+        pt++;
     }
-    if (argc > 1) {
-        printf ("usage: %s [verbose level]\n", argv[0]);
-        exit (1);
+
+    /* process argument */
+    while (argc-- > 1) {
+        char *arg = *(++argv);
+        if (arg[0] != '-') {
+            VERBOSE (mapec, ERROR, PRINTF ("%s: unknown command '%s'\n", progname, arg));
+            return 1;
+        }
+        char c = arg[1];
+        switch (c) {
+        case 'c':
+            arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+            if (arg == NULL) {
+                VERBOSE (mapec, ERROR, PRINTF ("%s: client url not specified\n", progname));
+                return 1;
+            }
+            client_local_url = arg;
+            client_remote_url = ((argc > 1) && (**(argv + 1) != '-') && (argc--)) ?  *(++argv) : NULL;
+            break;
+        case 's':
+            arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+            if (arg == NULL) {
+                VERBOSE (mapec, ERROR, PRINTF ("%s: server url not specified\n", progname));
+                return 1;
+            }
+            server_local_url = arg;
+            server_remote_url = ((argc > 1) && (**(argv + 1) != '-') && (argc--)) ?  *(++argv) : NULL;
+            break;
+        case 'v':
+            arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+            if (arg == NULL) {
+                VERBOSE (mapec, ERROR, PRINTF ("%s: verbose level not specified\n", progname));
+                return 1;
+            }
+            CHANGE_VERBOSE_LEVEL (mapec, atoi (arg));
+            break;
+        case 'h':
+        default:
+            printf ("usage: %s [-c url [url]] [-h] [-s url [url]] [-v int]\n", progname);
+            return (c != 'h');
+        }
     }
 
     printf ("*** STARTING SERVERS ***\n");
 
-    pthread_t udp_thread;
+    pthread_t server_thread;
 
-    pthread_create (&udp_thread, NULL, udp_server, NULL);
+    pthread_create (&server_thread, NULL, server, NULL);
 
     /* Give some delay to server to setup there MAPEC */
     usleep (1e6);
-    
+
     printf ("*** STARTING TEST SEQUENCE ***\n");
 
-    pthread_t udp_client_thread;
+    pthread_t client_thread;
+
+    pthread_create (&client_thread, NULL, client, NULL);
 
-    pthread_create (&udp_client_thread, NULL, udp_client, NULL);
+    pthread_join (client_thread, NULL);
 
-    pthread_join (udp_client_thread, NULL);
-    
     printf ("*** END OF TEST SEQUENCE ***\n");
 
     int failed = 0;
-    DUMP_STATUS (MAPEC_Send_UDP_OK);
-    DUMP_STATUS (MAPEC_Echo_UDP_OK);
+    DUMP_STATUS (MAPEC_Send_OK);
+    DUMP_STATUS (MAPEC_Echo_OK);
 
     return failed ? EXIT_FAILURE : EXIT_SUCCESS;
 }
 
+/* test: mapec_test.exe -h */
 /* test: mapec_test.exe */
-/* test: mapec_test.exe 4 */
+/* test: mapec_test.exe -v 4 */
+/* test: mapec_test.exe -v 3 -c udp://localhost:1234 udp://localhost:1235 -s udp://localhost:1235 udp://localhost:1234 */
 
 /* vim: set ts=4 sw=4 si et: */