Hi,

inspired by the canlogserver, I wrote a small quickhack utility to
link the can interfaces of our testbench with virtual interfaces on
my workstation. The program simply passes the can data from
a socketcan interface over a tcp connection to the counterpart.
It's just a small utility without any kind of configuration management,
bandwidth optimization etc., but feel free to add.

Maybe someone here similar requirements.

I don't know whether it's ready to commit to the repository, so feel
free to annotate and comment.

Here's the description from the patch:

canlink links raw SocketCAN interfaces over a tcp connection.
The utility is single binary for client and server. Each received
can frame is transmitted in a single tcp frame. There's no
intelligence like filters implemented. Error frames are masked.
Several clients can connect to one server.

To start the server part, append the interfaces, you'd like to provide
as command line arguments e.g:

canlink can0 can1 can3

The client takes the servers hostname as argument with the -h switch:

canlink -h serverhost vcan0 vcan1 vcan2 van4

The used port can be adjusted with the -p switch (default 29500).

Every frame received on the server can0 gets sent on the clients vcan0
interface and vice versa. Same for can1 and vcan1 and so on. Canlink
numbers the channels after their appearance on the command line.
Thus vcan4 will not receive frames, but transmit.

Thanks!

cheers,

Felix
From d8344a73396faa7a1edb0be77c2591d29e295b02 Mon Sep 17 00:00:00 2001
From: Felix Obenhuber <[email protected]>
Date: Thu, 28 Oct 2010 18:46:23 +0200
Subject: [PATCH] can-utils: add canlink utility

canlink links raw SocketCAN interfaces over a tcp connection.
The utility is single binary for client and server. Each received
can frame is transmitted in a single tcp frame. There's no
intelligence like filters implemented. Error frames are masked.
Several clients can connect to one server.

To start the server part, append the interfaces, you'd like to provide
as command line arguemnts e.g:

canlink can0 can1 can3

The client takes the servers hostname as argument with the -h switch:

canlink -h serverhost vcan0 vcan1 vcan2 van4

The used port can be adjusted with the -p switch (default 29500).

Every frame received on the server can0 gets sent on the clients vcan0
interface and vice versa. Same for can1 and vcan1 and so on. Canlink
numbers the channels after their appearance on the command line.
Thus vcan4 will not receive frames, but transmit.
---
 can-utils/GNUmakefile.am |    1 +
 can-utils/canlink.c      |  365 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 366 insertions(+), 0 deletions(-)
 create mode 100644 can-utils/canlink.c

diff --git a/can-utils/GNUmakefile.am b/can-utils/GNUmakefile.am
index d507a76..719411c 100644
--- a/can-utils/GNUmakefile.am
+++ b/can-utils/GNUmakefile.am
@@ -27,6 +27,7 @@ bin_PROGRAMS = \
 	candump \
 	canfdtest \
 	cangen \
+	canlink \
 	canlogserver \
 	canplayer \
 	cansend \
diff --git a/can-utils/canlink.c b/can-utils/canlink.c
new file mode 100644
index 0000000..037c75f
--- /dev/null
+++ b/can-utils/canlink.c
@@ -0,0 +1,365 @@
+/*
+ * canlink.c - link socketcan interfaces over a tcp connection
+ *
+ * Copyright (c) 2010 Felix Obenhuber <[email protected]>
+ *
+ * This code is partly derived from calogserver.c:
+ *   Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ *
+ * 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
+ *
+ * Send feedback to <[email protected]>
+ *
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libgen.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <linux/can.h>
+#include <linux/can/raw.h>
+
+#define DEFAULT_PORT 29500
+#define MAXDEV 256 
+
+extern int optind, opterr, optopt;
+
+struct canlink_frame
+{
+    uint8_t index;
+    struct can_frame frame;
+};
+
+void usage(char* prg)
+{
+    fprintf(stderr, "\nUsage: %s [options] <CAN interface>+\n", prg);
+    fprintf(stderr, "  (use CTRL-C to terminate %s)\n\n", prg);
+    fprintf(stderr, "Options: -p <port>   listen on port <port>. Default: %d\n", DEFAULT_PORT);
+    fprintf(stderr, "         -h <host>   connect to canlink server on host\n");
+    fprintf(stderr, "                     without the host option act as server\n");
+}
+
+
+void childexit(int i)
+{
+    (void)i;
+    wait(NULL);
+}
+
+
+void clean_exit(int i)
+{
+    (void)i;
+    exit(0);
+}
+
+
+int main(int argc, char** argv)
+{
+    unsigned i, j;
+    int nbytes;
+    int opt;
+    int port = DEFAULT_PORT;
+    char host[1024];
+    int verbose = 0;
+    int s[MAXDEV];
+    unsigned numcandev = 1;
+    struct sockaddr_can addr;
+    struct ifreq ifr;
+    fd_set rdfs;
+    struct can_frame frame;
+    int socki, accsocket;
+    struct canlink_frame tcp_frame;
+    int max_fd = 0;
+    struct sockaddr_in inaddr;
+    struct sockaddr_in peer_addr;
+    socklen_t sin_size = sizeof(peer_addr);
+    int client_mode = 0;
+    struct hostent *peer_ent;
+    static int running = 1;
+
+    struct sigaction signalaction;
+
+    sigset_t sigset;
+    sigemptyset(&sigset);
+    signalaction.sa_handler = &childexit;
+    signalaction.sa_mask = sigset;
+    signalaction.sa_flags = 0;
+    sigaction(SIGCHLD, &signalaction, NULL);
+
+    signalaction.sa_handler = &clean_exit;
+    signalaction.sa_mask = sigset;
+    signalaction.sa_flags = 0;
+    sigaction(SIGTERM, &signalaction, NULL);
+    sigaction(SIGINT, &signalaction, NULL);
+
+    while ((opt = getopt(argc, argv, "h:v:p:?")) != -1)
+    {
+        switch (opt)
+        {
+        case 'p':
+            port = atoi(optarg);
+            break;
+        case 'v':
+            verbose = 1;
+            break;
+        case 'h':
+            client_mode = 1;
+            strcpy(host, optarg);
+            break;
+        default:
+            usage(basename(argv[0]));
+            exit(1);
+            break;
+        }
+
+        if (optind == argc)
+        {
+            usage(basename(argv[0]));
+            exit(0);
+        }
+    }
+
+    if (! client_mode)
+    {
+        socki = socket(AF_INET, SOCK_STREAM, 0);
+        if (socki < 0)
+        {
+            perror("socket");
+            exit(1);
+        }
+
+        max_fd = socki;
+
+        inaddr.sin_family = AF_INET;
+        inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+        inaddr.sin_port = htons(port);
+
+        if (bind(socki, (struct sockaddr*)&inaddr, sizeof(inaddr)) < 0)
+        {
+            perror("bind");
+            exit(1);
+        }
+
+        if (listen(socki, 3) != 0)
+        {
+            perror("listen");
+            exit(1);
+        }
+
+        while (1)
+        {
+            accsocket = accept(socki, (struct sockaddr*) & peer_addr,
+                &sin_size);
+            max_fd = ( max_fd < accsocket ) ? accsocket : max_fd;
+            if (accsocket > 0)
+            {
+                if (!fork())
+                    break;
+                else
+                    close(accsocket);
+            }
+            else if (errno != EINTR)
+            {
+                perror("accept");
+                exit(1);
+            }
+        }
+    }
+    else
+    {
+        accsocket = socket(AF_INET, SOCK_STREAM, 0);
+        if (accsocket < 0)
+        {
+            perror("socket");
+            exit(1);
+        }
+        max_fd = ( max_fd < accsocket ) ? accsocket : max_fd;
+
+        bzero(&peer_addr, sizeof(peer_addr));
+        peer_addr.sin_family = AF_INET;
+        peer_addr.sin_port = htons(port);
+
+        peer_ent = gethostbyname(host);
+        if (peer_ent == 0)
+        {
+            perror(host);
+            exit(1);
+        }
+
+        bcopy(peer_ent->h_addr, &(peer_addr.sin_addr.s_addr),
+            peer_ent->h_length);
+
+        if (connect(accsocket, (struct sockaddr*)&peer_addr,
+              sizeof(peer_addr)) != 0)
+        {
+            perror("connect");
+            exit(1);
+        }
+    }
+
+
+    numcandev = argc - optind;
+    if (numcandev > MAXDEV)
+    {
+        fprintf(stderr, "too many arguments\n");
+        exit(1);
+    }
+
+    for (i = 0; i < numcandev; i++)
+    {
+        if ((s[i] = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
+        {
+            perror("socket");
+            exit(1);
+        }
+
+        max_fd = ( max_fd < s[i] ) ? s[i] : max_fd;
+
+        j = strlen(argv[optind+i]);
+
+        if (!(j < IFNAMSIZ))
+        {
+            printf("name of CAN device '%s' is too long!\n", argv[optind+i]);
+            return 1;
+        }
+
+        addr.can_family = AF_CAN;
+        strcpy(ifr.ifr_name, argv[optind+i]);
+        if (ioctl(s[i], SIOCGIFINDEX, &ifr) < 0)
+        {
+            perror("SIOCGIFINDEX");
+            exit(1);
+        }
+        addr.can_ifindex = ifr.ifr_ifindex;
+
+        if (bind(s[i], (struct sockaddr *)&addr, sizeof(addr)) < 0)
+        {
+            perror("bind");
+            return 1;
+        }
+    }
+
+
+    while (running)
+    {
+        FD_ZERO(&rdfs);
+        for (i = 0; i < numcandev; i++)
+            FD_SET(s[i], &rdfs);
+        FD_SET(accsocket, &rdfs);
+
+        if (select(max_fd + 1, &rdfs, NULL, NULL, NULL) < 0)
+        {
+            perror("select");
+            running = 0;
+            continue;
+        }
+
+        for (i = 0; i < numcandev; i++)
+        {
+            if (FD_ISSET(s[i], &rdfs))
+            {
+                socklen_t len = sizeof(addr);
+                if ((nbytes = recvfrom(s[i], &frame,
+                                       sizeof(struct can_frame), 0,
+                                       (struct sockaddr*) & addr, &len)) < 0)
+                {
+                    perror("read");
+                    running = 0;
+                    continue;
+                }
+
+                if (nbytes == 0)
+                {
+                    running = 0;
+                    continue;
+                }
+
+                if ((unsigned)nbytes < sizeof(struct can_frame))
+                {
+                    perror("incomplete CAN frame");
+                    running = 0;
+                    continue;
+                }
+
+                tcp_frame.index = i;
+                tcp_frame.frame = frame;
+                if (write(accsocket, &tcp_frame,
+                      sizeof(struct canlink_frame)) < 0)
+                {
+                    perror("write");
+                    running = 0;
+                    continue;
+                }
+            }
+        }
+
+        /* handle incomming data */
+        if (FD_ISSET(accsocket, &rdfs))
+        {
+            socklen_t len = sizeof(peer_addr);
+            if ((nbytes = recvfrom(accsocket, &tcp_frame,
+                                   sizeof(struct canlink_frame), 0,
+                                   (struct sockaddr*) & addr, &len)) < 0)
+            {
+                perror("read");
+                exit(1);
+            }
+
+            if (nbytes == 0)
+            {
+                running = 0;
+                continue;
+            }
+
+            if ((unsigned)nbytes < sizeof(struct canlink_frame))
+            {
+                perror("incomplete data\n");
+                running = 0;
+                continue;
+            }
+
+            if (tcp_frame.index < numcandev)
+            {
+                if (write(s[ tcp_frame.index ], &tcp_frame.frame,
+                          sizeof(struct can_frame)) < 0)
+                {
+                    perror("write peer");
+                    running = 0;
+                    continue;
+                }
+            }
+        }
+
+    }
+
+    close(accsocket);
+    for (i = 0; i < numcandev; i++)
+        close(s[i]);
+
+    return 0;
+}
+/* eof */
-- 
1.6.3.3

_______________________________________________
Socketcan-users mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-users

Reply via email to