From d444858b1dd29740860d259fc3e80eb25883bd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Wed, 21 Nov 2018 17:31:45 +0100 Subject: [PATCH 1/4] Rename ether_crc32() to ether_fcs(). From the caller's perspective, what's important is that it computes the Ethernet Frame Control Sequence (FCS), not the specific algorithm being used. --- include/ft/ethernet.h | 4 ++-- lib/libft/ft_ether.c | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/ft/ethernet.h b/include/ft/ethernet.h index e16d61d..dc6dab6 100644 --- a/include/ft/ethernet.h +++ b/include/ft/ethernet.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016-2017 The University of Oslo + * Copyright (c) 2016-2018 The University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,6 +49,6 @@ typedef struct ether_ftr { uint32_t fcs; } __attribute__((__packed__)) ether_ftr; -uint32_t ether_crc32(const uint8_t *, size_t); +uint32_t ether_fcs(const uint8_t *, size_t); #endif diff --git a/lib/libft/ft_ether.c b/lib/libft/ft_ether.c index 630522e..0d1ecad 100644 --- a/lib/libft/ft_ether.c +++ b/lib/libft/ft_ether.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016 The University of Oslo + * Copyright (c) 2016-2018 The University of Oslo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -99,8 +99,11 @@ static const uint32_t crc32tab[256] = { 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, }; +/* + * Compute the 32-bit Frame Check Sequence for an Ethernet frame. + */ uint32_t -ether_crc32(const uint8_t *data, size_t len) +ether_fcs(const uint8_t *data, size_t len) { uint32_t crc32; From 90b1508d989e5abc213696bf48b9cfe2e5dc637e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Wed, 21 Nov 2018 17:33:00 +0100 Subject: [PATCH 2/4] Add an Ethernet address parser. --- include/ft/ethernet.h | 1 + lib/libft/ft_ether.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/include/ft/ethernet.h b/include/ft/ethernet.h index dc6dab6..62a4dfe 100644 --- a/include/ft/ethernet.h +++ b/include/ft/ethernet.h @@ -50,5 +50,6 @@ typedef struct ether_ftr { } __attribute__((__packed__)) ether_ftr; uint32_t ether_fcs(const uint8_t *, size_t); +const char *ether_parse(const char *, ether_addr *); #endif diff --git a/lib/libft/ft_ether.c b/lib/libft/ft_ether.c index 0d1ecad..11983f9 100644 --- a/lib/libft/ft_ether.c +++ b/lib/libft/ft_ether.c @@ -30,6 +30,7 @@ #include #include +#include #include static const uint32_t crc32tab[256] = { @@ -112,3 +113,38 @@ ether_fcs(const uint8_t *data, size_t len) crc32 = (crc32 >> 8) ^ crc32tab[(crc32 ^ *data++) & 0xff]; return (crc32); } + +/* + * Parse the textual representation of an Ethernet address. We accept + * both the traditional colon-separated syntax and the less common + * hyphen-separated syntax. + */ +static const uint8_t x2b[256] = { + ['0'] = 0x00, ['1'] = 0x01, ['2'] = 0x02, ['3'] = 0x03, + ['4'] = 0x04, ['5'] = 0x05, ['6'] = 0x06, ['7'] = 0x07, + ['8'] = 0x08, ['9'] = 0x09, ['A'] = 0x0a, ['B'] = 0x0b, + ['C'] = 0x0c, ['D'] = 0x0d, ['E'] = 0x0e, ['F'] = 0x0f, + ['a'] = 0x0a, ['b'] = 0x0b, ['c'] = 0x0c, ['d'] = 0x0d, + ['e'] = 0x0e, ['f'] = 0x0f, +}; +#define is_sep(ch) ((ch) == ':' || (ch) == '-') +const char * +ether_parse(const char *str, ether_addr *addr) +{ + const unsigned char *us; + char sep; + int i; + + us = (const unsigned char *)str; + if (is_xdigit(us[0]) && is_xdigit(us[1]) && is_sep(us[2])) { + addr->o[0] = x2b[us[0]] << 4 | x2b[us[1]]; + sep = us[2]; + us += 2; + for (i = 1; i < 6; ++i, us += 3) { + if (us[0] != sep || !is_xdigit(us[1]) || !is_xdigit(us[2])) + return (NULL); + addr->o[i] = x2b[us[1]] << 4 | x2b[us[2]]; + } + } + return ((const char *)us); +} From f804eae6e351fe012ae1df413de22a0389ace4f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Wed, 21 Nov 2018 18:48:52 +0100 Subject: [PATCH 3/4] Add a few unit tests for ether_parse(). --- t/.gitignore | 1 + t/Makefile.am | 5 +- t/t_ether.h | 52 +++++++++++++ t/t_ether_addr.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 t/t_ether.h create mode 100644 t/t_ether_addr.c diff --git a/t/.gitignore b/t/.gitignore index eebbc8f..4e26306 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -1,5 +1,6 @@ *.log *.trs +/t_ether_addr /t_ip4_addr /t_ip4_range /t_ip4_set diff --git a/t/Makefile.am b/t/Makefile.am index b2f3348..94fb97f 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -1,11 +1,14 @@ AM_CPPFLAGS = -I$(top_srcdir)/include $(CRYB_TEST_CFLAGS) LIBFT = $(top_builddir)/lib/libft/libft.a -noinst_HEADERS = t_ip4.h +noinst_HEADERS = t_ether.h t_ip4.h if HAVE_CRYB_TEST check_PROGRAMS = +check_PROGRAMS += t_ether_addr +t_ether_addr_LDADD = $(LIBFT) $(CRYB_TEST_LIBS) + check_PROGRAMS += t_ip4_addr t_ip4_addr_LDADD = $(LIBFT) $(CRYB_TEST_LIBS) diff --git a/t/t_ether.h b/t/t_ether.h new file mode 100644 index 0000000..570f4c0 --- /dev/null +++ b/t/t_ether.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2016-2018 The University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef T_ETHER_H_INCLUDED +#define T_ETHER_H_INCLUDED + +#include + +#include +#include + +static inline int +t_compare_ether_addr(const ether_addr *e, const ether_addr *r) +{ + + if (memcmp(e, r, sizeof(ether_addr)) != 0) { + t_printv("expected %02x:%02x:%02x:%02x:%02x:%02x\n" + "received %02x:%02x:%02x:%02x:%02x:%02x\n", + e->o[0], e->o[1], e->o[2], e->o[3], e->o[4], e->o[5], + r->o[0], r->o[1], r->o[2], r->o[3], r->o[4], r->o[5]); + return (0); + } + return (1); +} + +#endif diff --git a/t/t_ether_addr.c b/t/t_ether_addr.c new file mode 100644 index 0000000..38851ef --- /dev/null +++ b/t/t_ether_addr.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2016-2018 The University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "t_ether.h" + +static struct t_ether_case { + const char *desc; + const char *str; + size_t len; + ether_addr addr; +} t_ether_cases[] = { + { + .desc = "empty", + .str = "", + .len = 0, + .addr = { .o = { 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "comma", + .str = ",", + .len = 0, + .addr = { .o = { 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "zero", + .str = "00:00:00:00:00:00", + .len = 17, + .addr = { .o = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, + }, + { + .desc = "zero comma", + .str = "00:00:00:00:00:00,", + .len = 17, + .addr = { .o = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, + }, + { + .desc = "valid", + .str = "02:00:18:11:09:02", + .len = 17, + .addr = { .o = { 0x02, 0x00, 0x18, 0x11, 0x09, 0x02 } }, + }, + { + .desc = "dashes", + .str = "02-00-18-11-09-02", + .len = 17, + .addr = { .o = { 0x02, 0x00, 0x18, 0x11, 0x09, 0x02 } }, + }, + { + .desc = "mixed", + .str = "02-00:18-11:09-02", + .len = ~0U, + .addr = { .o = { 0x02, 0x00, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "broadcast", + .str = "ff:ff:ff:ff:ff:ff", + .len = 17, + .addr = { .o = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }, + }, + { + .desc = "BROADCAST", + .str = "FF:FF:FF:FF:FF:FF", + .len = 17, + .addr = { .o = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }, + }, + { + .desc = "BrOaDcAsT", + .str = "Ff:fF:Ff:fF:Ff:fF", + .len = 17, + .addr = { .o = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }, + }, + { + .desc = "garbage 1", + .str = "xyzzyx", + .len = 0, + .addr = { .o = { 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "garbage 2", + .str = "0yzzyx", + .len = 0, + .addr = { .o = { 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "garbage 3", + .str = "00zzyx", + .len = 0, + .addr = { .o = { 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "garbage 4", + .str = "00:zyx", + .len = ~0U, + .addr = { .o = { 0x00, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "garbage 5", + .str = "00:0yx", + .len = ~0U, + .addr = { .o = { 0x00, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "garbage 6", + .str = "00:00x", + .len = ~0U, + .addr = { .o = { 0x00, 0x00, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, + { + .desc = "garbage 7", + .str = "00:00-", + .len = ~0U, + .addr = { .o = { 0x00, 0x00, 0xa5, 0xa5, 0xa5, 0xa5 } }, + }, +}; + +static int +t_ether_addr(char **desc CRYB_UNUSED, void *arg) +{ + struct t_ether_case *t = arg; + ether_addr addr; + const char *e; + int ret; + + addr = t_ether_cases[0].addr; + e = ether_parse(t->str, &addr); + if (t->len == ~0U) { + ret = t_is_null(e); + } else { + ret = t_is_not_null(e); + if (t->len > 0) + ret &= t_compare_ptr(t->str + t->len, e); + } + ret &= t_compare_ether_addr(&t->addr, &addr); + return (ret); +} + +static int +t_prepare(int argc CRYB_UNUSED, char *argv[] CRYB_UNUSED) +{ + unsigned int i; + + for (i = 0; i < sizeof t_ether_cases / sizeof t_ether_cases[0]; ++i) + t_add_test(t_ether_addr, &t_ether_cases[i], + "%s", t_ether_cases[i].desc); + return (0); +} + +int +main(int argc, char *argv[]) +{ + + t_main(t_prepare, NULL, argc, argv); +} From fcd6acda34ac447b09b89147e8979415461b5d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Wed, 21 Nov 2018 21:31:18 +0100 Subject: [PATCH 4/4] Make the Ethernet address configurable. --- sbin/flytrap/flytrap.8.in | 5 ++++- sbin/flytrap/main.c | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/sbin/flytrap/flytrap.8.in b/sbin/flytrap/flytrap.8.in index 9af7581..6fd309f 100644 --- a/sbin/flytrap/flytrap.8.in +++ b/sbin/flytrap/flytrap.8.in @@ -26,7 +26,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd November 14, 2018 +.Dd November 21, 2018 .Dt FLYTRAP 8 .Os .Sh NAME @@ -35,6 +35,7 @@ .Sh SYNOPSIS .Nm .Op Fl dfnov +.Op Fl e Ar addr .Op Fl I Ar addr Ns | Ns Ar range Ns | Ns Ar subnet .Op Fl i Ar addr Ns | Ns Ar range Ns | Ns Ar subnet .Op Fl p Ar pidfile @@ -55,6 +56,8 @@ The following options are available: .Bl -tag -width Fl .It Fl d Enable log messages at debug level or higher. +.It Fl e Ar addr +Use the specified Ethernet address instead of the hardcoded default. .It Fl f Foreground mode: do not daemonize and do not create a pidfile. .It Fl I Ar a.b.c.d Ns | Ns Ar a.b.c.d-e.f.g.h Ns | Ns Ar a.b.c.d/p diff --git a/sbin/flytrap/main.c b/sbin/flytrap/main.c index 66b7989..8fed36b 100644 --- a/sbin/flytrap/main.c +++ b/sbin/flytrap/main.c @@ -92,6 +92,20 @@ exclude_range(ip4s_node **set, const char *range) return (0); } +static int +set_ether_addr(const char *addr) +{ + ether_addr ea; + const char *e; + + if ((e = ether_parse(addr, &ea)) == NULL || *e != '\0') + return (-1); + ft_verbose("ethernet address %02x:%02x:%02x:%02x:%02x:%02x", + ea.o[0], ea.o[1], ea.o[2], ea.o[3], ea.o[4], ea.o[5]); + flytrap_ether_addr = ea; + return (0); +} + static void daemonize(void) { @@ -117,7 +131,7 @@ usage(void) { fprintf(stderr, "usage: " - "flytrap [-dfnov] [-p pidfile] [-t csvfile] " + "flytrap [-dfnov] [-p pidfile] [-t csvfile] [-e addr] " "[-Ii addr|range|subnet] [-Xx addr|range|subnet] " "iface\n"); exit(1); @@ -132,7 +146,7 @@ main(int argc, char *argv[]) ifname = NULL; ft_log_level = FT_LOG_LEVEL_NOTICE; ft_log_init("flytrap", NULL); - while ((opt = getopt(argc, argv, "dfhI:i:nop:t:vX:x:")) != -1) { + while ((opt = getopt(argc, argv, "de:fhI:i:nop:t:vX:x:")) != -1) { switch (opt) { case 'd': if (ft_log_level > FT_LOG_LEVEL_DEBUG) @@ -145,6 +159,10 @@ main(int argc, char *argv[]) if (include_range(&src_set, optarg) != 0) usage(); break; + case 'e': + if (set_ether_addr(optarg) != 0) + usage(); + break; case 'i': if (include_range(&dst_set, optarg) != 0) usage();