Add gflldp, a minimal LLDP implementation
This might be used as part of an auto-activation service
for non-GPON networks, serving a similar role to OMCI and
Ranging in GPON.
Change-Id: Ic3cf9b2cefb3ff1a731145ceb7ece8c8746d4481
diff --git a/cmds/Makefile b/cmds/Makefile
index 4778036..45580b2 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -33,6 +33,7 @@
dnsck \
freemegs \
gfhd254_reboot \
+ gflldpd \
gstatic \
http_bouncer \
ionice \
@@ -52,6 +53,7 @@
LIB_TARGETS=\
stdoutline.so
HOST_TEST_TARGETS=\
+ host-gflldpd_test \
host-netusage_test \
host-utils_test \
host-isoping_test
@@ -272,6 +274,10 @@
anonid: anonid.o
host-anonid: host-anonid.o
anonid host-anonid: LIBS += -lcrypto
+host-gflldpd_test.o: CXXFLAGS += -D WVTEST_CONFIGURED -I ../wvtest/cpp
+host-gflldpd_test.o: gflldpd.c
+host-gflldpd_test: LIBS+=$(HOST_LIBS) -lm -lstdc++
+host-gflldpd_test: host-gflldpd_test.o host-wvtestmain.o host-wvtest.o
TESTS = $(wildcard test-*.sh) $(wildcard test-*.py) $(wildcard *_test.py) $(TEST_TARGETS)
diff --git a/cmds/gflldpd.c b/cmds/gflldpd.c
new file mode 100644
index 0000000..44c2dd2
--- /dev/null
+++ b/cmds/gflldpd.c
@@ -0,0 +1,246 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013 Keichi Takahashi keichi.t@me.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* Substantially derived from * https://github.com/keichi/tiny-lldpd
+ * also under the MIT license */
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <netpacket/packet.h>
+#include <sys/socket.h>
+
+#define MAXINTERFACES 8
+const char *ifnames[MAXINTERFACES] = {0};
+int ninterfaces = 0;
+
+uint8_t sendbuf[1024];
+
+const uint8_t lldpaddr[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e};
+#define ETH_P_LLDP 0x88cc
+#define TLV_END 0
+#define TLV_CHASSIS_ID 1
+#define TLV_PORT_ID 2
+#define TLV_TTL 3
+#define TLV_PORT_DESCRIPTION 4
+#define TLV_SYSTEM_NAME 5
+
+#define CHASSIS_ID_MAC_ADDRESS 4
+#define PORT_ID_MAC_ADDRESS 3
+
+static int write_lldp_tlv_header(void *p, int type, int length)
+{
+ *((uint16_t *)p) = htons((type & 0x7f) << 9 | (length & 0x1ff));
+ return 2;
+}
+
+
+static int write_lldp_type_subtype_tlv(size_t offset,
+ uint8_t type, uint8_t subtype, int length, const void *data)
+{
+ uint8_t *p = sendbuf + offset;
+
+ if ((offset + 2 + 1 + length) > sizeof(sendbuf)) {
+ fprintf(stderr, "LLDP frame too large %zd > %zd\n",
+ (offset + 2 + 1 + length), sizeof(sendbuf));
+ exit(1);
+ }
+
+ p += write_lldp_tlv_header(p, type, length + 1);
+ *p++ = subtype;
+ memcpy(p, data, length);
+ p += length;
+
+ return (p - sendbuf);
+}
+
+
+static int write_lldp_type_tlv(size_t offset, uint8_t type,
+ int length, const void *data)
+{
+ uint8_t *p = sendbuf + offset;
+
+ if ((offset + 2 + length) > sizeof(sendbuf)) {
+ fprintf(stderr, "LLDP frame too large %zd > %zd\n",
+ (offset + 2 + length), sizeof(sendbuf));
+ exit(1);
+ }
+
+ p += write_lldp_tlv_header(p, type, length);
+ memcpy(p, data, length);
+ p += length;
+
+ return (p - sendbuf);
+}
+
+
+static int write_lldp_end_tlv(size_t offset)
+{
+ uint8_t *p = sendbuf + offset;
+
+ if ((offset + 2) > sizeof(sendbuf)) {
+ fprintf(stderr, "LLDP frame too large %zd > %zd\n",
+ (offset + 2), sizeof(sendbuf));
+ exit(1);
+ }
+
+ offset += write_lldp_tlv_header(p, TLV_END, 0);
+ return offset;
+}
+
+
+static void mac_str_to_bytes(const char *macstr, uint8_t *mac)
+{
+ if (sscanf(macstr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) {
+ fprintf(stderr, "Invalid MAC address: %s\n", macstr);
+ exit(1);
+ }
+}
+
+
+static size_t format_lldp_packet(const char *macaddr, const char *ifname,
+ const char *serial)
+{
+ uint8_t saddr[ETH_ALEN];
+ size_t offset = 0;
+ struct ether_header *eh = (struct ether_header *)sendbuf;
+ uint16_t ttl;
+
+ mac_str_to_bytes(macaddr, saddr);
+ memset(sendbuf, 0, sizeof(sendbuf));
+
+ eh = (struct ether_header *)sendbuf;
+ memcpy(eh->ether_shost, saddr, sizeof(eh->ether_shost));
+ memcpy(eh->ether_dhost, lldpaddr, sizeof(eh->ether_dhost));
+ eh->ether_type = htons(ETH_P_LLDP);
+ offset = sizeof(*eh);
+
+ offset = write_lldp_type_subtype_tlv(offset,
+ TLV_CHASSIS_ID, CHASSIS_ID_MAC_ADDRESS, ETH_ALEN, saddr);
+ offset = write_lldp_type_subtype_tlv(offset,
+ TLV_PORT_ID, PORT_ID_MAC_ADDRESS, ETH_ALEN, saddr);
+
+ ttl = htons(120);
+ offset = write_lldp_type_tlv(offset, TLV_TTL, sizeof(ttl), &ttl);
+
+ offset = write_lldp_type_tlv(offset,
+ TLV_PORT_DESCRIPTION, strlen(ifname), ifname);
+ offset = write_lldp_type_tlv(offset,
+ TLV_SYSTEM_NAME, strlen(serial), serial);
+ offset = write_lldp_end_tlv(offset);
+
+ return offset;
+}
+
+
+#ifndef UNIT_TESTS
+static void send_lldp_packet(int s, size_t len, const char *ifname)
+{
+ struct sockaddr_ll sll;
+
+ memset(&sll, 0, sizeof(sll));
+ sll.sll_family = PF_PACKET;
+ sll.sll_ifindex = if_nametoindex(ifname);
+ sll.sll_hatype = ARPHRD_ETHER;
+ sll.sll_halen = ETH_ALEN;
+ sll.sll_pkttype = PACKET_OTHERHOST;
+ memcpy(sll.sll_addr, lldpaddr, ETH_ALEN);
+ if (sendto(s, sendbuf, len, 0, (struct sockaddr*)&sll, sizeof(sll)) < 0) {
+ fprintf(stderr, "LLDP sendto failed\n");
+ exit(1);
+ }
+}
+
+
+static void usage(const char *progname)
+{
+ fprintf(stderr, "usage: %s -i eth# -m 00:11:22:33:44:55 -s G0123456789\n",
+ progname);
+ exit(1);
+}
+
+
+int main(int argc, char *argv[])
+{
+ const char *macaddr = NULL;
+ const char *serial = NULL;
+ int c;
+ int s;
+
+ while ((c = getopt(argc, argv, "i:m:s:")) != -1) {
+ switch (c) {
+ case 'i':
+ if (ninterfaces == (MAXINTERFACES - 1)) {
+ usage(argv[0]);
+ }
+ ifnames[ninterfaces++] = optarg;
+ break;
+ case 'm':
+ macaddr = optarg;
+ break;
+ case 's':
+ serial = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (ninterfaces == 0 || macaddr == NULL || serial == NULL) {
+ usage(argv[0]);
+ }
+
+ if ((s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
+ fprintf(stderr, "socket(PF_PACKET) failed\n");
+ exit(1);
+ }
+
+ while (1) {
+ int i;
+
+ for (i = 0; i < ninterfaces; ++i) {
+ if (ifnames[i] != NULL) {
+ size_t len = format_lldp_packet(macaddr, ifnames[i], serial);
+ send_lldp_packet(s, len, ifnames[i]);
+ }
+ usleep(10000 + (rand() % 80000));
+ }
+
+ usleep(500000 + (rand() % 1000000));
+ }
+
+ return 0;
+}
+#endif /* UNIT_TESTS */
diff --git a/cmds/gflldpd_test.cc b/cmds/gflldpd_test.cc
new file mode 100644
index 0000000..21f33df
--- /dev/null
+++ b/cmds/gflldpd_test.cc
@@ -0,0 +1,61 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013 Keichi Takahashi keichi.t@me.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <netinet/if_ether.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <wvtest.h>
+
+
+#define UNIT_TESTS
+#include "gflldpd.c"
+
+
+WVTEST_MAIN("mac_str_to_bytes") {
+ uint8_t expected_mac[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
+ uint8_t mac[ETH_ALEN];
+
+ mac_str_to_bytes("00:11:22:33:44:55", mac);
+ WVPASSEQ(memcmp(mac, expected_mac, ETH_ALEN), 0);
+}
+
+
+WVTEST_MAIN("format_lldp_packet") {
+ size_t siz;
+ uint8_t expected[] = {
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x00, 0x11,
+ 0x22, 0x33, 0x44, 0x55, 0x88, 0xcc, 0x02, 0x07,
+ 0x04, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x04,
+ 0x07, 0x03, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x06, 0x02, 0x00, 0x78, 0x08, 0x04, 0x65, 0x74,
+ 0x68, 0x30, 0x0a, 0x0b, 0x47, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00,
+ 0x00
+ };
+
+ siz = format_lldp_packet("00:11:22:33:44:55", "eth0", "G0123456789");
+ WVPASSEQ(siz, sizeof(expected));
+ WVPASSEQ(memcmp(sendbuf, expected, siz), 0);
+}