]> rtime.felk.cvut.cz Git - can-utils.git/commitdiff
cegw improved - can filter and getaddrinfo added
authorRadek Matějka <radek.matejka@gmail.com>
Tue, 29 Jan 2013 21:13:15 +0000 (15:13 -0600)
committerRadek Matějka <radek.matejka@gmail.com>
Tue, 29 Jan 2013 21:13:15 +0000 (15:13 -0600)
Can frame filtering feature was added to cegw. Implementation and
usage follows candump utility.

The udp address is now translated using getaddrinfo. Cegw uses the
first sockaddr structure which getaddrinfo returns. The lookup is
restricted to SOCK_DGRAM and IPv4.

cegw.c

diff --git a/cegw.c b/cegw.c
index 3e0e9d5fac09335fb0fe67bde0f49a52d192a359..feb083af9ee61cb38d9edc95878d7cf40e9698b5 100644 (file)
--- a/cegw.c
+++ b/cegw.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
+#include <netdb.h>
 #include <net/if.h>
 #include <arpa/inet.h>
 #include <linux/can.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/can/raw.h>
 #include <linux/can/canethgw.h>
 
 unsigned int cegw_errno = 0;
 
-enum
-{
+enum {
        CEGW_ERR_UNKNOWN,
        CEGW_ERR_COLON,
-       CEGW_ERR_UNEXLEN,
-       CEGW_ERR_ATON,
-       CEGW_ERR_PORT
+       CEGW_ERR_GETADDRI,
+       CEGW_ERR_FLTALCK,
+       CEGW_ERR_FLTPARSE
 };
 
-char* cegw_errlist[] =
-{
-       [CEGW_ERR_UNKNOWN] = "",
-       [CEGW_ERR_COLON  ] = "expected ':' (<ip>:<port>)",
-       [CEGW_ERR_UNEXLEN] = "unexpected ip address length, please use dot notation"
-                            " (eg. 127.0.0.1)",
-       [CEGW_ERR_ATON   ] = "ip address mismatch",
-       [CEGW_ERR_PORT   ] = "port number"
+char *cegw_errlist[] = {
+
+       [CEGW_ERR_UNKNOWN ] = "",
+       [CEGW_ERR_COLON   ] = "expected ':' (<hostname>:<port>)",
+       [CEGW_ERR_GETADDRI] = "getaddrinfo failed",
+       [CEGW_ERR_FLTALCK ] = "filter alloc failed",
+       [CEGW_ERR_FLTPARSE] = "filter parsing failed"
 };
 
 static const char help_msg[] = "usage:\n"
-                              "        %s <can_if> <udp_listen_addr>:<port> <udp_dest_addr>:<port>\n"
-                              "                [list of additional udp recipients <addr:port>]\n"
+                              "        %s <can_if>[,filter]* <udp_listen_addr>:<port> <udp_dest_addr>:<port>\n"
+                              "                [list of additional udp recipients <addr>:<port>]\n"
                               "example:\n"
                               "        %s can0 192.168.0.1:10501 192.168.0.4:980 192.168.0.7:1160\n\n"
                               "        Executing this command will set the gateway so that it will\n"
                               "        listen for udp messages on 192.168.0.1:10501 and send them\n"
                               "        to can0. Simultaneously, it will send all messages from can0\n"
                               "        to 192.168.0.4:980 and 192.168.0.7:1160 via udp. The message is\n"
-                              "        therefore cloned. Notice that there can be any number\n"
-                              "        of udp recipients.\n";
+                              "        therefore cloned. Notice that there can be more udp recipients.\n"
+                              "        The can filter is specified in the same way as in candump utility.\n";
+
+const struct addrinfo hints = {
+       .ai_family = AF_INET,
+       .ai_socktype = SOCK_DGRAM
+};
 
-static void perr(chars)
+static void perr(char *s)
 {
        if (s) {
                if (cegw_errno == 0) {
@@ -76,66 +82,115 @@ static void perr(char* s)
 }
 
 /**
- * read_addrport - parses @in for eth address.
+ * readsockaddr - parses @in for eth address.
  * Valid input is e.g. 127.0.0.1:10502. If parsing fails
- * the cause is stored in cegw_errno.
+ * the cause is stored in cegw_errno. Please note that
+ * the function modifies content of arg.
  *
- * @param[in]  in   string to search in
- * @param[out] addr ip address
- * @param[out] port transport layer port
+ * @param[in]  arg   hostname:port string
+ * @param[out] addr  filled sockaddr_in structure
  * @return 0 on success, -1 otherwise
  */
-int read_addrport(char* in, struct in_addr* addr, unsigned short* port)
+int readsockaddr(char *arg, struct sockaddr_in *addr)
 {
-       char* delim = NULL;
-       const int addrstr_len = 16;
-       char addrstr[addrstr_len];
-       int addrlen;
+       int ret;
+       char *delim;
+       struct addrinfo *res;
+
+       delim = strchr(arg, ':');
 
-       if ((delim = strchr(in, ':')) == NULL) {
+       if (delim == NULL) {
                cegw_errno = CEGW_ERR_COLON;
                return -1;
        }
 
-       /* get address */
-       addrlen = delim - in;
-       if (addrlen > addrstr_len) {
-               cegw_errno = CEGW_ERR_UNEXLEN;
+       *delim = '\0';
+       delim++;
+
+       ret = getaddrinfo(arg, delim, &hints, &res);
+       if (ret != 0) {
+               fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
+               cegw_errno = CEGW_ERR_GETADDRI;
                return -1;
        }
 
-       memcpy(addrstr, in, addrlen);
-       addrstr[addrlen] = '\0';
-       if (inet_aton(addrstr, addr) == 0) {
-               cegw_errno = CEGW_ERR_ATON;
-               return -1;
+       memcpy(addr, res->ai_addr, sizeof(*addr));
+
+       freeaddrinfo(res);
+       return 0;
+}
+
+/**
+ * readfilter - reads can filter definition from nptr
+ */
+int readfilter(char *nptr, struct can_filter **filter, int *out_numfilter, can_err_mask_t *err_mask)
+{
+       char *ptr;
+       int numfilter;
+       struct can_filter *rfilter;
+
+       numfilter = 0;
+       ptr = nptr;
+       while (ptr) {
+               numfilter++;
+               ptr++; /* hop behind the ',' */
+               ptr = strchr(ptr, ','); /* exit condition */
        }
 
-       /* get port */
-       /* ToDo: handle overflow */
-       if (sscanf(delim, ":%hu", port) != 1) {
-               cegw_errno = CEGW_ERR_PORT;
+       rfilter = malloc(numfilter * sizeof(*rfilter));
+       if (!rfilter) {
+               cegw_errno = CEGW_ERR_FLTALCK;
                return -1;
        }
 
+       numfilter = 0;
+       *err_mask = 0;
+
+       while (nptr) {
+
+               ptr = nptr+1; /* hop behind the ',' */
+               nptr = strchr(ptr, ','); /* update exit condition */
+
+               if (sscanf(ptr, "%x:%x",
+                                       &rfilter[numfilter].can_id,
+                                       &rfilter[numfilter].can_mask) == 2) {
+                       rfilter[numfilter].can_mask &= ~CAN_ERR_FLAG;
+                       numfilter++;
+               } else if (sscanf(ptr, "%x~%x",
+                                       &rfilter[numfilter].can_id,
+                                       &rfilter[numfilter].can_mask) == 2) {
+                       rfilter[numfilter].can_id |= CAN_INV_FILTER;
+                       rfilter[numfilter].can_mask &= ~CAN_ERR_FLAG;
+                       numfilter++;
+               } else if (sscanf(ptr, "#%x", err_mask) != 1) {
+                       cegw_errno = CEGW_ERR_FLTPARSE;
+                       free(rfilter);
+                       return -1;
+               }
+       }
+
+       *filter = rfilter;
+       *out_numfilter = numfilter;
        return 0;
 }
 
-int main(int argc, charargv[])
+int main(int argc, char *argv[])
 {
        int i;
        int fd;
        int tmpi;
        int dstcnt;
-       unsigned short port;
+       char *nptr;
+       int numfilter = 0;
        int udp_sock, can_sock;
+       can_err_mask_t err_mask = 0;
        struct sockaddr_in udp_addr;
        struct sockaddr_can can_addr;
-       struct sockaddr_in* dst = NULL;
-       struct cegw_ioctl* gwctl = NULL;
+       struct sockaddr_in *dst = NULL;
+       struct cegw_ioctl *gwctl = NULL;
+       struct can_filter *filter = NULL;
 
-       if (argc == 1)
-       {
+       if (argc == 1) {
                printf(help_msg, argv[0], argv[0]);
                return 0;
        }
@@ -153,6 +208,11 @@ int main(int argc, char* argv[])
        for (i=1; i<argc; i++) {
                switch (i) {
                        case 1: /* can ifindex */
+                               nptr = strchr(argv[i], ',');
+                               if (nptr) {
+                                       *nptr = '\0';
+                               }
+
                                can_addr.can_family = AF_CAN;
                                tmpi = if_nametoindex(argv[i]);
                                if (tmpi == 0) {
@@ -163,23 +223,19 @@ int main(int argc, char* argv[])
                                can_addr.can_ifindex = tmpi;
                                break;
                        case 2: /* listen addr */
-                               udp_addr.sin_family = AF_INET;
-                               if (read_addrport(argv[i], &udp_addr.sin_addr, &port) != 0) {
-                                       perr("listening address mismatch");
+                               if (readsockaddr(argv[i], &udp_addr) != 0) {
+                                       perr("reading listening address failed");
                                        free(gwctl);
                                        return 1;
                                }
-                               udp_addr.sin_port = htons(port);
                                break;
                        default: /* udp destination */
                                dst = &gwctl->udp_dst[i-3];
-                               dst->sin_family = AF_INET;
-                               if (read_addrport(argv[i], &dst->sin_addr, &port) != 0) {
-                                       perr("udp destination mismatch");
+                               if (readsockaddr(argv[i], dst) != 0) {
+                                       perr("reading udp destination failed");
                                        free(gwctl);
                                        return 1;
                                }
-                               dst->sin_port = htons(port);
                                break;
                }
        }
@@ -212,6 +268,22 @@ int main(int argc, char* argv[])
                return 1;
        }
 
+       /* can filter */
+       if (nptr && (readfilter(nptr, &filter, &numfilter, &err_mask) != 0)) {
+               perr("can filter");
+               free(gwctl);
+               return 1;
+       }
+
+       if (err_mask)
+               setsockopt(can_sock, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
+                               &err_mask, sizeof(err_mask));
+
+       if (numfilter)
+               setsockopt(can_sock, SOL_CAN_RAW, CAN_RAW_FILTER,
+                               filter, numfilter * sizeof(struct can_filter));
+       free(filter);
+
        /* send it to kernel gateway */
        fd = open("/dev/canethgw", O_RDONLY);
        if (fd == -1) {
@@ -238,3 +310,4 @@ int main(int argc, char* argv[])
 
        return 0;
 }
+