]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Major rewrite for packet builder and parser.
authorVincent Bernat <bernat@luffy.cx>
Sun, 8 Mar 2009 15:01:22 +0000 (16:01 +0100)
committerVincent Bernat <bernat@luffy.cx>
Sun, 8 Mar 2009 15:01:22 +0000 (16:01 +0100)
Packet builder now uses POKE_* macro that push bytes one by one using
memcpy to avoid any alignment issue that may appear on architectures
like ARM. The use of those macros instead of IOV renders the code
usually smaller except for very simple protocols.

Packet parser does not use structure casting any more. This is not
safe on architectures that do not handle unaligned read. We now read
packets sequentially using memcpy when reading more than one byte.
This makes the parser harder to read and somewhat longer.

15 files changed:
CHANGELOG
src/Makefile.am
src/cdp.c
src/cdp.h
src/edp.c
src/edp.h
src/frame.h [new file with mode: 0644]
src/iov.c [deleted file]
src/llc.h [deleted file]
src/lldp.c
src/lldp.h
src/lldpd.c
src/lldpd.h
src/sonmp.c
src/sonmp.h

index 4e6945863f3daba8355898e5293a6d4f9093786f..b3b61b9d800e054aa21c780e5dfe986beb9f86ea 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,13 @@
+lldpd (0.4)
+
+  * Rewrite of packet builder and parser to be able to cope with
+    architecture that cannot do unaligned read. For decoder, we don't
+    cast structures any more since they can be unaligned. For encoder,
+    we use memcpy through the use of macro that build packets step by
+    step.
+
+ -- Vincent Bernat <bernat@luffy.cx>
+
 lldpd (0.3.2)
 
   * Fix LLDP-MED support
index de012328a688570f0034eb7d059fff65b340ef35..2829b54f698dd427747c31fd6d1526d86d35d69c 100644 (file)
@@ -1,7 +1,7 @@
 sbin_PROGRAMS = lldpd lldpctl
 
-COMMON = log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h llc.h edp.h
-lldpd_SOURCES = lldpd.c lldp.c cdp.c sonmp.c edp.c iov.c features.c client.c priv.c privsep_fdpass.c $(COMMON)
+COMMON = log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h edp.h
+lldpd_SOURCES = frame.h frame.c lldpd.c lldp.c cdp.c sonmp.c edp.c features.c client.c priv.c privsep_fdpass.c $(COMMON)
 lldpctl_SOURCES = lldpctl.c $(COMMON)
 
 lldpd_LDADD = @LIBOBJS@
index 3581e39852efae9c0cb7b476d73d48ffe7aff953..94e83c2a86fed4dd7be068070d4e0c1b95cfaefe 100644 (file)
--- a/src/cdp.c
+++ b/src/cdp.c
 
 /* We also supports FDP which is very similar to CDPv1 */
 #include "lldpd.h"
+#include "frame.h"
 
 #if defined (ENABLE_CDP) || defined (ENABLE_FDP)
 
+#include <unistd.h>
 #include <errno.h>
 #include <arpa/inet.h>
 
@@ -26,31 +28,20 @@ static int
 cdp_send(struct lldpd *global, struct lldpd_chassis *chassis,
         struct lldpd_hardware *hardware, int version)
 {
-       struct cdp_header ch;
-       struct ethllc llc;
        u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
        u_int8_t llcorg[] = LLC_ORG_CISCO;
-       struct iovec *iov = NULL;
-       struct cdp_tlv_head device;
-       struct cdp_tlv_head port;
-       struct cdp_tlv_head soft;
-       struct cdp_tlv_head platform;
-       struct cdp_tlv_address_head ah;
-       struct cdp_tlv_address_one ao;
-       struct cdp_tlv_capabilities cap;
 #ifdef ENABLE_FDP
        char *capstr;
 #endif
-       unsigned int c = -1, i, len;
+       u_int16_t checksum;
+       int length;
+       u_int32_t cap;
+       u_int8_t *packet;
+       u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end;
 
-#define IOV_NEW                                                        \
-       if ((iov = (struct iovec*)realloc(iov, (++c + 1) *      \
-                   sizeof(struct iovec))) == NULL)             \
-               fatal(NULL);
-
-       /* Handle FDP */
 #ifdef ENABLE_FDP
        if (version == 0) {
+               /* With FDP, change multicast address and LLC PID */
                const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR;
                const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY;
                memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr));
@@ -58,89 +49,81 @@ cdp_send(struct lldpd *global, struct lldpd_chassis *chassis,
        }
 #endif
 
-       /* Ether + LLC */
-       memset(&llc, 0, sizeof(llc));
-       memcpy(&llc.ether.shost, &hardware->h_lladdr,
-           sizeof(llc.ether.shost));
-       memcpy(&llc.ether.dhost, &mcastaddr,
-           sizeof(llc.ether.dhost));
-       llc.dsap = llc.ssap = 0xaa;
-       llc.control = 0x03;
-       memcpy(llc.org, llcorg, sizeof(llc.org));
-       llc.protoid = htons(LLC_PID_CDP);
-       IOV_NEW;
-       iov[c].iov_base = &llc;
-       iov[c].iov_len = sizeof(llc);
+       length = hardware->h_mtu;
+       if ((packet = (u_int8_t*)malloc(length)) == NULL)
+               return ENOMEM;
+       memset(packet, 0, length);
+       pos = packet;
+
+       /* Ethernet header */
+       if (!(
+             POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
+             POKE_BYTES(&hardware->h_lladdr, sizeof(hardware->h_lladdr)) &&
+             POKE_SAVE(pos_len_eh) && /* We compute the len later */
+             POKE_UINT16(0)))
+               goto toobig;
+
+       /* LLC */
+       if (!(
+             POKE_SAVE(pos_llc) &&
+             POKE_UINT8(0xaa) && /* SSAP */
+             POKE_UINT8(0xaa) && /* DSAP */
+             POKE_UINT8(0x03) && /* Control field */
+             POKE_BYTES(llcorg, sizeof(llcorg)) &&
+             POKE_UINT16(LLC_PID_CDP)))
+               goto toobig;
 
        /* CDP header */
-       memset(&ch, 0, sizeof(ch));
-       if (version == 0)
-               ch.version = 1;
-       else
-               ch.version = version;
-       ch.ttl = chassis->c_ttl;
-       IOV_NEW;
-       iov[c].iov_base = &ch;
-       iov[c].iov_len = sizeof(struct cdp_header);
+       if (!(
+             POKE_SAVE(pos_cdp) &&
+             POKE_UINT8(((version == 0) && 1) || version) &&
+             POKE_UINT8(chassis->c_ttl) &&
+             POKE_SAVE(pos_checksum) && /* Save checksum position */
+             POKE_UINT16(0)))
+               goto toobig;
 
        /* Chassis ID */
-       memset(&device, 0, sizeof(device));
-       device.tlv_type = htons(CDP_TLV_CHASSIS);
-       device.tlv_len = htons(sizeof(device) + strlen(chassis->c_name));
-       IOV_NEW;
-       iov[c].iov_base = &device;
-       iov[c].iov_len = sizeof(device);
-       IOV_NEW;
-       iov[c].iov_base = chassis->c_name;
-       iov[c].iov_len = strlen(chassis->c_name);
+       if (!(
+             POKE_START_CDP_TLV(CDP_TLV_CHASSIS) &&
+             POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
+             POKE_END_CDP_TLV))
+               goto toobig;
 
        /* Adresses */
-       memset(&ah, 0, sizeof(ah));
-       ah.head.tlv_type = htons(CDP_TLV_ADDRESSES);
-       ah.head.tlv_len = htons(sizeof(ah) + sizeof(ao));
-       ah.nb = htonl(1);
-       IOV_NEW;
-       iov[c].iov_base = &ah;
-       iov[c].iov_len = sizeof(ah);
-       memset(&ao, 0, sizeof(ao));
-       ao.ptype = 1;
-       ao.plen = 1;
-       ao.proto = CDP_ADDRESS_PROTO_IP;
-       ao.alen = htons(sizeof(struct in_addr));
-       memcpy(&ao.addr, &chassis->c_mgmt, sizeof(struct in_addr));
-       IOV_NEW;
-       iov[c].iov_base = &ao;
-       iov[c].iov_len = sizeof(ao);
+       if (!(
+             POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) &&
+             POKE_UINT32(1) && /* We ship only one address */
+             POKE_UINT8(1) &&  /* Type: NLPID */
+             POKE_UINT8(1) &&  /* Length: 1 */
+             POKE_UINT8(CDP_ADDRESS_PROTO_IP) && /* IP */
+             POKE_UINT16(sizeof(struct in_addr)) && /* Address length */
+             POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)) &&
+             POKE_END_CDP_TLV))
+               goto toobig;
 
        /* Port ID */
-       memset(&port, 0, sizeof(port));
-       port.tlv_type = htons(CDP_TLV_PORT);
-       port.tlv_len = htons(sizeof(port) + strlen(hardware->h_lport.p_descr));
-       IOV_NEW;
-       iov[c].iov_base = &port;
-       iov[c].iov_len = sizeof(port);
-       IOV_NEW;
-       iov[c].iov_base = hardware->h_lport.p_descr;
-       iov[c].iov_len = strlen(hardware->h_lport.p_descr);
+       if (!(
+             POKE_START_CDP_TLV(CDP_TLV_PORT) &&
+             POKE_BYTES(hardware->h_lport.p_descr,
+                        strlen(hardware->h_lport.p_descr)) &&
+             POKE_END_CDP_TLV))
+               goto toobig;
 
        /* Capabilities */
        if (version != 0) {
-               memset(&cap, 0, sizeof(cap));
-               cap.head.tlv_type = htons(CDP_TLV_CAPABILITIES);
-               cap.head.tlv_len = htons(sizeof(cap));
-               cap.cap = 0;
+               cap = 0;
                if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
-                       cap.cap |= CDP_CAP_ROUTER;
+                       cap |= CDP_CAP_ROUTER;
                if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
-                       cap.cap |= CDP_CAP_BRIDGE;
-               cap.cap = htonl(cap.cap);
-               IOV_NEW;
-               iov[c].iov_base = &cap;
-               iov[c].iov_len = sizeof(cap);
+                       cap |= CDP_CAP_BRIDGE;
+               if (!(
+                     POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
+                     POKE_UINT32(cap) &&
+                     POKE_END_CDP_TLV))
+                       goto toobig;
 #ifdef ENABLE_FDP
        } else {
                /* With FDP, it seems that a string is used in place of an int */
-               memset(&cap, 0, sizeof(cap));
                if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
                        capstr = "Router";
                else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
@@ -149,65 +132,59 @@ cdp_send(struct lldpd *global, struct lldpd_chassis *chassis,
                        capstr = "Bridge";
                else
                        capstr = "Host";
-               cap.head.tlv_type = htons(CDP_TLV_CAPABILITIES);
-               cap.head.tlv_len = htons(sizeof(struct cdp_tlv_head) +
-                   strlen(capstr));
-               IOV_NEW;
-               iov[c].iov_base = &cap;
-               iov[c].iov_len = sizeof(struct cdp_tlv_head);
-               IOV_NEW;
-               iov[c].iov_base = capstr;
-               iov[c].iov_len = strlen(capstr);
+               if (!(
+                     POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
+                     POKE_BYTES(capstr, strlen(capstr)) &&
+                     POKE_END_CDP_TLV))
+                       goto toobig;
 #endif
        }
                
        /* Software version */
-       memset(&soft, 0, sizeof(soft));
-       soft.tlv_type = htons(CDP_TLV_SOFTWARE);
-       soft.tlv_len = htons(sizeof(soft) + strlen(chassis->c_descr));
-       IOV_NEW;
-       iov[c].iov_base = &soft;
-       iov[c].iov_len = sizeof(soft);
-       IOV_NEW;
-       iov[c].iov_base = chassis->c_descr;
-       iov[c].iov_len = strlen(chassis->c_descr);
+       if (!(
+             POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) &&
+             POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) &&
+             POKE_END_CDP_TLV))
+               goto toobig;
 
        /* Platform */
-       memset(&platform, 0, sizeof(platform));
-       platform.tlv_type = htons(CDP_TLV_PLATFORM);
-       platform.tlv_len = htons(sizeof(platform) + strlen("Linux"));
-       IOV_NEW;
-       iov[c].iov_base = &platform;
-       iov[c].iov_len = sizeof(platform);
-       IOV_NEW;
-       iov[c].iov_base = "Linux";
-       iov[c].iov_len = strlen("Linux");
-
-       c++;
+       if (!(
+             POKE_START_CDP_TLV(CDP_TLV_PLATFORM) &&
+             POKE_BYTES("Linux", strlen("Linux")) &&
+             POKE_END_CDP_TLV))
+               goto toobig;
+       POKE_SAVE(end);
 
        /* Compute len and checksum */
-       len = 0;
-       for (i = 0; i < c; i++) {
-               len += iov[i].iov_len;
-       }
-       len -= sizeof(struct ieee8023);
-       llc.ether.size = htons(len);
-       ch.checksum = iov_checksum(&iov[1], c - 1, (version != 0) ? 1 : 0);
+       POKE_RESTORE(pos_len_eh);
+       if (!(POKE_UINT16(end - pos_llc))) goto toobig;
+       checksum = frame_checksum(pos_cdp, end - pos_cdp, (version != 0) ? 1 : 0);
+       POKE_RESTORE(pos_checksum);
+       if (!(POKE_UINT16(ntohs(checksum)))) goto toobig;
 
-       if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real :
-                  hardware->h_raw, iov, c) == -1) {
+       if (write((hardware->h_raw_real > 0) ? hardware->h_raw_real :
+                  hardware->h_raw, packet, end - packet) == -1) {
                LLOG_WARN("unable to send packet on real device for %s",
                           hardware->h_ifname);
-               free(iov);
+               free(packet);
                return ENETDOWN;
        }
 
        hardware->h_tx_cnt++;
 
-       free(iov);
+       free(packet);
        return 0;
+ toobig:
+       free(packet);
+       return -1;
 }
 
+#define CHECK_TLV_SIZE(x, name)                                   \
+       do { if (tlv_len < (x)) {                          \
+          LLOG_WARNX(name " CDP/FDP TLV too short received on %s",\
+              hardware->h_ifname);                        \
+          goto malformed;                                 \
+       } } while (0)
 /* cdp_decode also decodes FDP */
 int
 cdp_decode(struct lldpd *cfg, char *frame, int s,
@@ -216,21 +193,16 @@ cdp_decode(struct lldpd *cfg, char *frame, int s,
 {
        struct lldpd_chassis *chassis;
        struct lldpd_port *port;
-       struct ethllc *llc;
-       struct cdp_header *ch;
-       struct cdp_tlv_head *tlv;
-       struct cdp_tlv_address_head *ah;
-       struct cdp_tlv_address_one *ao;
-       struct iovec iov;
        u_int16_t cksum;
-       char *software = NULL, *platform = NULL;
-       int software_len = 0, platform_len = 0;
+       u_int8_t *software = NULL, *platform = NULL;
+       int software_len = 0, platform_len = 0, proto, version, nb, caps;
        const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR;
 #ifdef ENABLE_FDP
        const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR;
        int fdp = 0;
 #endif
-       int i, f, len, rlen;
+       u_int8_t *pos, *tlv, *pos_address, *pos_next_address;
+       int length, len_eth, tlv_type, tlv_len, addresses_len, address_len;
 
        if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
                LLOG_WARN("failed to allocate remote chassis");
@@ -245,15 +217,19 @@ cdp_decode(struct lldpd *cfg, char *frame, int s,
        TAILQ_INIT(&port->p_vlans);
 #endif
 
-       if (s < sizeof(struct ethllc) + sizeof(struct cdp_header)) {
-               LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
+       length = s;
+       pos = (u_int8_t*)frame;
+
+       if (length < 2*ETH_ALEN + sizeof(u_int16_t) /* Ethernet */ +
+           8 /* LLC */ + 4 /* CDP header */) {
+               LLOG_WARNX("too short CDP/FDP frame received on %s", hardware->h_ifname);
                goto malformed;
        }
 
-       llc = (struct ethllc *)frame;
-       if (memcmp(&llc->ether.dhost, cdpaddr, sizeof(cdpaddr)) != 0) {
+       if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) {
 #ifdef ENABLE_FDP
-               if (memcmp(&llc->ether.dhost, fdpaddr, sizeof(fdpaddr)) != 0)
+               PEEK_RESTORE((u_int8_t*)frame);
+               if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0)
                        fdp = 1;
                else {
 #endif
@@ -264,34 +240,30 @@ cdp_decode(struct lldpd *cfg, char *frame, int s,
                }
 #endif
        }
-       if (ntohs(llc->ether.size) > s - sizeof(struct ieee8023)) {
+       PEEK_DISCARD(ETH_ALEN); /* Don't care of source address */
+       len_eth = PEEK_UINT16;
+       if (len_eth > length) {
                LLOG_WARNX("incorrect 802.3 frame size reported on %s",
                    hardware->h_ifname);
                goto malformed;
        }
-       if (llc->protoid != htons(LLC_PID_CDP)) {
-               if ((llc->protoid != htons(LLC_PID_DRIP)) &&
-                   (llc->protoid != htons(LLC_PID_PAGP)) &&
-                   (llc->protoid != htons(LLC_PID_PVSTP)) &&
-                   (llc->protoid != htons(LLC_PID_UDLD)) &&
-                   (llc->protoid != htons(LLC_PID_VTP)) &&
-                   (llc->protoid != htons(LLC_PID_DTP)) &&
-                   (llc->protoid != htons(LLC_PID_STP)))
+       PEEK_DISCARD(6);        /* Skip beginning of LLC */
+       proto = PEEK_UINT16;
+       if (proto != LLC_PID_CDP) {
+               if ((proto != LLC_PID_DRIP) &&
+                   (proto != LLC_PID_PAGP) &&
+                   (proto != LLC_PID_PVSTP) &&
+                   (proto != LLC_PID_UDLD) &&
+                   (proto != LLC_PID_VTP) &&
+                   (proto != LLC_PID_DTP) &&
+                   (proto != LLC_PID_STP))
                        LLOG_DEBUG("incorrect LLC protocol ID received on %s",
                            hardware->h_ifname);
                goto malformed;
        }
-       f = sizeof(struct ethllc);
-       ch = (struct cdp_header *)(frame + f);
-       if ((ch->version != 1) && (ch->version != 2)) {
-               LLOG_WARNX("incorrect CDP/FDP version (%d) for frame received on %s",
-                   ch->version, hardware->h_ifname);
-               goto malformed;
-       }
-       chassis->c_ttl = ntohs(ch->ttl);
-       iov.iov_len = s - f;
-       iov.iov_base = frame + f;
-       cksum = iov_checksum(&iov, 1,
+
+       /* Check checksum */
+       cksum = frame_checksum(pos, len_eth - 8,
 #ifdef ENABLE_FDP
            !fdp                /* fdp = 0 -> cisco checksum */
 #else
@@ -305,147 +277,145 @@ cdp_decode(struct lldpd *cfg, char *frame, int s,
                goto malformed;
        }
 
-       f += sizeof(struct cdp_header);
-       while (f < s) {
-               if (f + sizeof(struct cdp_tlv_head) > s) {
+       /* Check version */
+       version = PEEK_UINT8;
+       if ((version != 1) && (version != 2)) {
+               LLOG_WARNX("incorrect CDP/FDP version (%d) for frame received on %s",
+                   version, hardware->h_ifname);
+               goto malformed;
+       }
+       chassis->c_ttl = PEEK_UINT8; /* TTL */
+       PEEK_DISCARD_UINT16;         /* Checksum, already checked */
+
+       while (length) {
+               if (length < 4) {
                        LLOG_WARNX("CDP/FDP TLV header is too large for "
                            "frame received on %s",
                            hardware->h_ifname);
                        goto malformed;
                }
-               tlv = (struct cdp_tlv_head *)(frame + f);
-               len = ntohs(tlv->tlv_len) - sizeof(struct cdp_tlv_head);
-               if ((len < 0) || (f + sizeof(struct cdp_tlv_head) + len > s)) {
+               tlv_type = PEEK_UINT16;
+               tlv_len = PEEK_UINT16 - 4;
+               PEEK_SAVE(tlv);
+               if ((tlv_len < 0) || (length < tlv_len)) {
                        LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame "
                            "received on %s",
                            hardware->h_ifname);
                        goto malformed;
                }
-               switch (ntohs(tlv->tlv_type)) {
+               switch (tlv_type) {
                case CDP_TLV_CHASSIS:
-                       f += sizeof(struct cdp_tlv_head);
-                       if ((chassis->c_name = (char *)calloc(1, len + 1)) == NULL) {
+                       if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == NULL) {
                                LLOG_WARN("unable to allocate memory for chassis name");
                                goto malformed;
                        }
-                       memcpy(chassis->c_name, frame + f, len);
+                       PEEK_BYTES(chassis->c_name, tlv_len);
                        chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
-                       if ((chassis->c_id =  (char *)malloc(len)) == NULL) {
+                       if ((chassis->c_id =  (char *)malloc(tlv_len)) == NULL) {
                                LLOG_WARN("unable to allocate memory for chassis ID");
                                goto malformed;
                        }
-                       memcpy(chassis->c_id, frame + f, len);
-                       chassis->c_id_len = len;
-                       f += len;
+                       memcpy(chassis->c_id, chassis->c_name, tlv_len);
+                       chassis->c_id_len = tlv_len;
                        break;
                case CDP_TLV_ADDRESSES:
-                       if (len < 4) {
-                               LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame "
-                                   "received on %s",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
-                       ah = (struct cdp_tlv_address_head *)(frame + f);
-                       f += sizeof(struct cdp_tlv_address_head);
-                       len -= 4;
-                       for (i = 0; i < ntohl(ah->nb); i++) {
-                               if (len < sizeof(struct cdp_tlv_address_one) -
-                                   sizeof(struct in_addr)) {
-                                       LLOG_WARNX("incorrect size for address TLV in "
-                                           "frame received from %s",
-                                           hardware->h_ifname);
+                       CHECK_TLV_SIZE(4, "Address");
+                       addresses_len = tlv_len - 4;
+                       for (nb = PEEK_UINT32; nb > 0; nb--) {
+                               PEEK_SAVE(pos_address);
+                               /* We first try to get the real length of the packet */
+                               if (addresses_len < 2) {
+                                       LLOG_WARN("too short address subframe "
+                                                 "received on %s",
+                                                 hardware->h_ifname);
                                        goto malformed;
                                }
-                               ao = (struct cdp_tlv_address_one *)(frame + f);
-                               rlen = 2 + ao->plen + 2 + ntohs(ao->alen);
-                               if (len < rlen) {
-                                       LLOG_WARNX("incorrect address size in TLV "
-                                           "received from %s",
-                                           hardware->h_ifname);
+                               PEEK_DISCARD_UINT8; addresses_len--;
+                               address_len = PEEK_UINT8; addresses_len--;
+                               if (addresses_len < address_len + 2) {
+                                       LLOG_WARN("too short address subframe "
+                                                 "received on %s",
+                                                 hardware->h_ifname);
                                        goto malformed;
                                }
-                               if ((ao->ptype == 1) && (ao->plen == 1) &&
-                                   (ao->proto == CDP_ADDRESS_PROTO_IP) &&
-                                   (ntohs(ao->alen) == sizeof(struct in_addr)) &&
+                               PEEK_DISCARD(address_len);
+                               addresses_len -= address_len;
+                               address_len = PEEK_UINT16; addresses_len -= 2;
+                               if (addresses_len < address_len) {
+                                       LLOG_WARN("too short address subframe "
+                                                 "received on %s",
+                                                 hardware->h_ifname);
+                                       goto malformed;
+                               }
+                               PEEK_DISCARD(address_len);
+                               PEEK_SAVE(pos_next_address);
+                               /* Next, we go back and try to extract
+                                  IPv4 address */
+                               PEEK_RESTORE(pos_address);
+                               if ((PEEK_UINT8 == 1) && (PEEK_UINT8 == 1) &&
+                                   (PEEK_UINT8 == CDP_ADDRESS_PROTO_IP) &&
+                                   (PEEK_UINT16 == sizeof(struct in_addr)) &&
                                    (chassis->c_mgmt.s_addr == INADDR_ANY))
-                                       chassis->c_mgmt.s_addr = ao->addr.s_addr;
-                               f += rlen;
-                               len -= rlen;
-                       }
-                       if (len != 0) {
-                               LLOG_WARNX("not enough addresses found in TLV "
-                                   "received from %s",
-                                   hardware->h_ifname);
-                               goto malformed;
+                                       PEEK_BYTES(&chassis->c_mgmt,
+                                                  sizeof(struct in_addr));
+                               /* Go to the end of the address */
+                               PEEK_RESTORE(pos_next_address);
                        }
                        break;
                case CDP_TLV_PORT:
-                       f += sizeof(struct cdp_tlv_head);
-                       if ((port->p_descr = (char *)calloc(1, len + 1)) == NULL) {
+                       if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) {
                                LLOG_WARN("unable to allocate memory for port description");
                                goto malformed;
                        }
-                       memcpy(port->p_descr, frame + f, len);
-                       port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
-                       if ((port->p_id =  (char *)malloc(ETH_ALEN)) == NULL) {
+                       PEEK_BYTES(port->p_descr, tlv_len);
+                       port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+                       if ((port->p_id =  (char *)calloc(1, tlv_len)) == NULL) {
                                LLOG_WARN("unable to allocate memory for port ID");
                                goto malformed;
                        }
-                       memcpy(port->p_id, llc->ether.shost, ETH_ALEN);
-                       port->p_id_len = ETH_ALEN;
-                       f += len;
+                       memcpy(port->p_id, port->p_descr, tlv_len);
+                       port->p_id_len = tlv_len;
                        break;
                case CDP_TLV_CAPABILITIES:
-                       f += sizeof(struct cdp_tlv_head);
 #ifdef ENABLE_FDP
                        if (fdp) {
                                /* Capabilities are string with FDP */
-                               if (!strncmp("Router", frame + f, len))
+                               if (!strncmp("Router", (char*)pos, tlv_len))
                                        chassis->c_cap_enabled = LLDP_CAP_ROUTER;
-                               else if (!strncmp("Switch", frame + f, len))
+                               else if (!strncmp("Switch", (char*)pos, tlv_len))
                                        chassis->c_cap_enabled = LLDP_CAP_BRIDGE;
-                               else if (!strncmp("Bridge", frame + f, len))
+                               else if (!strncmp("Bridge", (char*)pos, tlv_len))
                                        chassis->c_cap_enabled = LLDP_CAP_REPEATER;
                                else
                                        chassis->c_cap_enabled = LLDP_CAP_STATION;
                                chassis->c_cap_available = chassis->c_cap_enabled;
-                               f += len;
                                break;
                        }
 #endif
-                       if (len != 4) {
-                               LLOG_WARNX("incorrect size for capabilities TLV "
-                                   "on frame received from %s",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
-                       if (ntohl(*(u_int32_t*)(frame + f)) & CDP_CAP_ROUTER)
+                       CHECK_TLV_SIZE(4, "Capabilities");
+                       caps = PEEK_UINT32;
+                       if (caps & CDP_CAP_ROUTER)
                                chassis->c_cap_enabled |= LLDP_CAP_ROUTER;
-                       if (ntohl(*(u_int32_t*)(frame + f)) & 0x0e)
+                       if (caps & 0x0e)
                                chassis->c_cap_enabled |= LLDP_CAP_BRIDGE;
                        if (chassis->c_cap_enabled == 0)
                                chassis->c_cap_enabled = LLDP_CAP_STATION;
                        chassis->c_cap_available = chassis->c_cap_enabled;
-                       f += 4;
                        break;
                case CDP_TLV_SOFTWARE:
-                       f += sizeof(struct cdp_tlv_head);
-                       software_len = len;
-                       software = (char *)(frame + f);
-                       f += len;
+                       software_len = tlv_len;
+                       PEEK_SAVE(software);
                        break;
                case CDP_TLV_PLATFORM:
-                       f += sizeof(struct cdp_tlv_head);
-                       platform_len = len;
-                       platform = (char *)(frame + f);
-                       f += len;
+                       platform_len = tlv_len;
+                       PEEK_SAVE(platform);
                        break;
                default:
                        LLOG_DEBUG("unknown CDP/FDP TLV type (%d) received on %s",
-                           ntohs(tlv->tlv_type), hardware->h_ifname);
-                       f += sizeof(struct cdp_tlv_head) + len;
+                           ntohs(tlv_type), hardware->h_ifname);
                        hardware->h_rx_unrecognized_cnt++;
                }
+               PEEK_DISCARD(tlv + tlv_len - pos);
        }
        if (!software && platform) {
                if ((chassis->c_descr = (char *)calloc(1,
@@ -524,16 +494,17 @@ fdp_send(struct lldpd *global, struct lldpd_chassis *chassis,
 
 #ifdef ENABLE_CDP
 static int
-cdp_guess(char *frame, int len, int version)
+cdp_guess(char *pos, int length, int version)
 {
        const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
-       struct cdp_header *ch;
-       if (len < sizeof(struct ethllc) + sizeof(struct cdp_header))
+       if (length < 2*ETH_ALEN + sizeof(u_int16_t) /* Ethernet */ +
+           8 /* LLC */ + 4 /* CDP header */)
                return 0;
-       if (memcmp(frame, mcastaddr, ETH_ALEN) != 0)
+       if (PEEK_CMP(mcastaddr, ETH_ALEN) != 0)
                return 0;
-       ch = (struct cdp_header *)(frame + sizeof(struct ethllc));
-       return (ch->version == version);
+       PEEK_DISCARD(ETH_ALEN); PEEK_DISCARD_UINT16; /* Ethernet */
+       PEEK_DISCARD(8);                             /* LLC */
+       return (PEEK_UINT8 == version);
 }
 
 int
index 8694e679f67935ba013a0e49097cf8ff0041500d..8f2e087fbf9bd997697d3c40d008b30ab83b8b33 100644 (file)
--- a/src/cdp.h
+++ b/src/cdp.h
 #define LLC_PID_DTP 0x2004
 #define LLC_PID_STP 0x200a
 
-struct cdp_header {
-       u_int8_t        version;
-       u_int8_t          ttl;
-       u_int16_t         checksum;
-} __attribute__ ((__packed__));
-
-struct cdp_tlv_head {
-       u_int16_t        tlv_type;
-       u_int16_t        tlv_len;
-} __attribute__ ((__packed__));
-
 enum {
        CDP_TLV_CHASSIS                 = 1,
        CDP_TLV_ADDRESSES               = 2,
@@ -55,24 +44,7 @@ enum {
        CDP_TLV_PLATFORM                = 6
 };
 
-struct cdp_tlv_address_head {
-       struct cdp_tlv_head head;
-       u_int32_t nb;
-} __attribute__ ((__packed__));
-
-struct cdp_tlv_address_one {
-       u_int8_t  ptype;        /* Should be 1 */
-       u_int8_t  plen;         /* Should be 1 */
 #define CDP_ADDRESS_PROTO_IP 0xcc
-       u_int8_t  proto;        /* 0xcc for IP */
-       u_int16_t alen;         /* Should be 4 */
-       struct in_addr addr;
-} __attribute__ ((__packed__));
-
-struct cdp_tlv_capabilities {
-       struct cdp_tlv_head head;
-       u_int32_t cap;
-} __attribute__ ((__packed__));
 
 #define CDP_CAP_ROUTER 1
 #define CDP_CAP_BRIDGE 8
index 0e2e525742a3033e91021a911322940cab31e0b7..be422f606a2e7ec50bc5f61f96fa21136610c58d 100644 (file)
--- a/src/edp.c
+++ b/src/edp.c
  */
 
 #include "lldpd.h"
+#include "frame.h"
 
 #ifdef ENABLE_EDP
 
 #include <stdio.h>
+#include <unistd.h>
 #include <errno.h>
 #include <arpa/inet.h>
 #include <fnmatch.h>
@@ -29,136 +31,142 @@ int
 edp_send(struct lldpd *global, struct lldpd_chassis *chassis,
     struct lldpd_hardware *hardware)
 {
-       struct edp_header eh;
-       struct ethllc llc;
        const u_int8_t mcastaddr[] = EDP_MULTICAST_ADDR;
        const u_int8_t llcorg[] = LLC_ORG_EXTREME;
-       struct iovec *iov = NULL;
+       int length, i, v;
+       u_int8_t *packet, *pos, *pos_llc, *pos_len_eh, *pos_len_edp, *pos_edp, *tlv, *end;
+       u_int16_t checksum;
 #ifdef ENABLE_DOT1
-       struct edp_tlv_vlan *ovlan = NULL;
        struct lldpd_vlan *vlan;
        unsigned int state = 0;
 #endif
-       struct edp_tlv_head device;
-       struct edp_tlv_head null;
-       struct edp_tlv_info info;
        u_int8_t edp_fakeversion[] = {7, 6, 4, 99};
-       unsigned int i, c, v, len;
        /* Subsequent XXX can be replaced by other values. We place
           them here to ensure the position of "" to be a bit
           invariant with version changes. */
        char *deviceslot[] = { "eth", "veth", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "", NULL };
 
-#define IOV_NEW                                                        \
-       if ((iov = (struct iovec*)realloc(iov, (++c + 1) *      \
-                   sizeof(struct iovec))) == NULL)             \
-               fatal(NULL);
-
 #ifdef ENABLE_DOT1
        while (state != 2) {
-               free(iov); iov = NULL;
-               free(ovlan); ovlan = NULL;
 #endif
-               c = v = -1;
+               length = hardware->h_mtu;
+               if ((packet = (u_int8_t*)malloc(length)) == NULL)
+                       return ENOMEM;
+               memset(packet, 0, length);
+               pos = packet;
+               v = 0;
+
+               /* Ethernet header */
+               if (!(
+                     POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
+                     POKE_BYTES(&hardware->h_lladdr, sizeof(hardware->h_lladdr)) &&
+                     POKE_SAVE(pos_len_eh) && /* We compute the len later */
+                     POKE_UINT16(0)))
+                       goto toobig;
 
-               /* Ether + LLC */
-               memset(&llc, 0, sizeof(llc));
-               memcpy(&llc.ether.shost, &hardware->h_lladdr,
-                   sizeof(llc.ether.shost));
-               memcpy(&llc.ether.dhost, &mcastaddr,
-                   sizeof(llc.ether.dhost));
-               llc.dsap = llc.ssap = 0xaa;
-               llc.control = 0x03;
-               memcpy(llc.org, llcorg, sizeof(llc.org));
-               llc.protoid = htons(LLC_PID_EDP);
-               IOV_NEW;
-               iov[c].iov_base = &llc;
-               iov[c].iov_len = sizeof(llc);
+               /* LLC */
+               if (!(
+                     POKE_SAVE(pos_llc) && /* We need to save our
+                                              current position to
+                                              compute ethernet len */
+                     /* SSAP and DSAP */
+                     POKE_UINT8(0xaa) && POKE_UINT8(0xaa) &&
+                     /* Control field */
+                     POKE_UINT8(0x03) &&
+                     /* ORG */
+                     POKE_BYTES(llcorg, sizeof(llcorg)) &&
+                     POKE_UINT16(LLC_PID_EDP)))
+                       goto toobig;
 
                /* EDP header */
-               memset(&eh, 0, sizeof(eh));
-               eh.version = 1;
-               eh.sequence = htons(seq++);
                if ((chassis->c_id_len != ETH_ALEN) ||
                    (chassis->c_id_subtype != LLDP_CHASSISID_SUBTYPE_LLADDR)) {
                        LLOG_WARNX("local chassis does not use MAC address as chassis ID!?");
+                       free(packet);
                        return EINVAL;
                }
-               memcpy(&eh.mac, chassis->c_id, ETH_ALEN);
-               IOV_NEW;
-               iov[c].iov_base = &eh;
-               iov[c].iov_len = sizeof(eh);
+               if (!(
+                     POKE_SAVE(pos_edp) && /* Save the start of EDP frame */
+                     POKE_UINT8(1) && POKE_UINT8(0) &&
+                     POKE_SAVE(pos_len_edp) && /* We compute the len
+                                                  and the checksum
+                                                  later */
+                     POKE_UINT32(0) && /* Len + Checksum */
+                     POKE_UINT16(seq) &&
+                     POKE_UINT16(0) &&
+                     POKE_BYTES(&hardware->h_lladdr, sizeof(hardware->h_lladdr))))
+                       goto toobig;
+               seq++;
 
 #ifdef ENABLE_DOT1
                switch (state) {
                case 0:
 #endif
                        /* Display TLV */
-                       memset(&device, 0, sizeof(device));
-                       device.tlv_marker = EDP_TLV_MARKER;
-                       device.tlv_type = EDP_TLV_DISPLAY;
-                       device.tlv_len = htons(sizeof(device) + strlen(chassis->c_name) + 1);
-                       IOV_NEW;
-                       iov[c].iov_base = &device;
-                       iov[c].iov_len = sizeof(device);
-                       IOV_NEW;
-                       iov[c].iov_base = chassis->c_name;
-                       iov[c].iov_len = strlen(chassis->c_name) + 1;
+                       if (!(
+                             POKE_START_EDP_TLV(EDP_TLV_DISPLAY) &&
+                             POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
+                             POKE_UINT8(0) && /* Add a NULL character
+                                                 for better
+                                                 compatibility */
+                             POKE_END_EDP_TLV))
+                               goto toobig;
 
                        /* Info TLV */
-                       memset(&info, 0, sizeof(info));
-                       info.head.tlv_marker = EDP_TLV_MARKER;
-                       info.head.tlv_type = EDP_TLV_INFO;
-                       info.head.tlv_len = htons(sizeof(info));
+                       if (!(
+                             POKE_START_EDP_TLV(EDP_TLV_INFO)))
+                               goto toobig;
+                       /* We try to emulate the slot thing */
                        for (i=0; deviceslot[i] != NULL; i++) {
                                if (strncmp(hardware->h_ifname, deviceslot[i],
                                        strlen(deviceslot[i])) == 0) {
-                                       info.slot = htons(i);
-                                       info.port = htons(atoi(hardware->h_ifname +
-                                               strlen(deviceslot[i])));
+                                       if (!(
+                                             POKE_UINT16(i) &&
+                                             POKE_UINT16(atoi(hardware->h_ifname +
+                                                              strlen(deviceslot[i])))))
+                                               goto toobig;
                                        break;
                                }
                        }
+                       /* If we don't find a "slot", we say that the
+                          interface is in slot 8 */
                        if (deviceslot[i] == NULL) {
-                               info.slot = htons(8);
-                               info.port = htons(if_nametoindex(hardware->h_ifname));
+                               if (!(
+                                     POKE_UINT16(8) &&
+                                     POKE_UINT16(if_nametoindex(hardware->h_ifname))))
+                                       goto toobig;
                        }
-                       memcpy(info.version, edp_fakeversion, sizeof(info.version));
-                       info.connections[0] = info.connections[1] = 0xff;
-                       IOV_NEW;
-                       iov[c].iov_base = &info;
-                       iov[c].iov_len = sizeof(info);
+                       if (!(
+                             POKE_UINT16(0) && /* vchassis */
+                             POKE_UINT32(0) && POKE_UINT16(0) && /* Reserved */
+                             /* Version */
+                             POKE_BYTES(edp_fakeversion, sizeof(edp_fakeversion)) &&
+                             /* Connections, we say that we won't
+                                have more interfaces than this
+                                mask. */
+                             POKE_UINT32(0xffffffff) &&
+                             POKE_UINT32(0) && POKE_UINT32(0) && POKE_UINT32(0) &&
+                             POKE_END_EDP_TLV))
+                               goto toobig;
+
 #ifdef ENABLE_DOT1
                        break;
                case 1:
-                       v = 0;
-                       TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans,
-                           v_entries)
-                           v++;
-                       if (v == 0) {
-                               v = -1;
-                               break;
-                       }
-                       if ((ovlan = (struct edp_tlv_vlan*)malloc(
-                                       v*sizeof(struct edp_tlv_vlan))) == NULL) {
-                               LLOG_WARN("no room for vlans");
-                               v = -1;
-                       }
                        TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans,
                            v_entries) {
-                               v--;
-                               memset(&ovlan[v], 0, sizeof(ovlan[v]));
-                               ovlan[v].head.tlv_marker = EDP_TLV_MARKER;
-                               ovlan[v].head.tlv_type = EDP_TLV_VLAN;
-                               ovlan[v].head.tlv_len = htons(sizeof(ovlan[v]) +
-                                   strlen(vlan->v_name) + 1);
-                               ovlan[v].vid = htons(vlan->v_vid);
-                               IOV_NEW;
-                               iov[c].iov_base = &ovlan[v];
-                               iov[c].iov_len = sizeof(ovlan[v]);
-                               IOV_NEW;
-                               iov[c].iov_base = vlan->v_name;
-                               iov[c].iov_len = strlen(vlan->v_name) + 1;
+                               v++;
+                               if (!(
+                                     POKE_START_EDP_TLV(EDP_TLV_VLAN) &&
+                                     POKE_UINT8(0) && /* Flags: no IP address */
+                                     POKE_UINT8(0) && /* Reserved */
+                                     POKE_UINT16(vlan->v_vid) &&
+                                     POKE_UINT32(0) && /* Reserved */
+                                     POKE_UINT32(0) && /* IP address */
+                                     /* VLAN name */
+                                     POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) &&
+                                     POKE_UINT8(0) &&
+                                     POKE_END_EDP_TLV))
+                                       goto toobig;
                        }
                        break;
                }
@@ -168,37 +176,30 @@ edp_send(struct lldpd *global, struct lldpd_chassis *chassis,
 #endif
                        
                /* Null TLV */
-               memset(&null, 0, sizeof(null));
-               null.tlv_marker = EDP_TLV_MARKER;
-               null.tlv_type = EDP_TLV_NULL;
-               null.tlv_len = htons(sizeof(null));
-               IOV_NEW;
-               iov[c].iov_base = &null;
-               iov[c].iov_len = sizeof(null);
-
-               c++;
+               if (!(
+                     POKE_START_EDP_TLV(EDP_TLV_NULL) &&
+                     POKE_END_EDP_TLV &&
+                     POKE_SAVE(end)))
+                       goto toobig;
 
                /* Compute len and checksum */
-               len = 0;
-               for (i = 0; i < c; i++) {
-                       len += iov[i].iov_len;
-               }
-               len -= sizeof(struct ieee8023);
-               llc.ether.size = htons(len);
-               len = len + sizeof(struct ieee8023) - sizeof(struct ethllc);
-               eh.len = htons(len);
-               eh.checksum = iov_checksum(&iov[1], c - 1, 0);
+               i = end - pos_llc; /* Ethernet length */
+               v = end - pos_edp; /* EDP length */
+               POKE_RESTORE(pos_len_eh);
+               if (!(POKE_UINT16(i))) goto toobig;
+               POKE_RESTORE(pos_len_edp);
+               if (!(POKE_UINT16(v))) goto toobig;
+               checksum = frame_checksum(pos_edp, v, 0);
+               if (!(POKE_UINT16(ntohs(checksum)))) goto toobig;
 
-               if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real :
-                       hardware->h_raw, iov, c) == -1) {
+               if (write((hardware->h_raw_real > 0) ? hardware->h_raw_real :
+                       hardware->h_raw, packet, end - packet) == -1) {
                        LLOG_WARN("unable to send packet on real device for %s",
                            hardware->h_ifname);
-#ifdef ENABLE_DOT1
-                       free(ovlan);
-#endif
-                       free(iov);
+                       free(packet);
                        return ENETDOWN;
                }
+               free(packet);
 
 #ifdef ENABLE_DOT1             
                state++;
@@ -206,14 +207,19 @@ edp_send(struct lldpd *global, struct lldpd_chassis *chassis,
 #endif
 
        hardware->h_tx_cnt++;
-#ifdef ENABLE_DOT1
-       free(ovlan);
-#endif
-       free(iov);
-
        return 0;
+ toobig:
+       free(packet);
+       return E2BIG;
 }
 
+#define CHECK_TLV_SIZE(x, name)                                   \
+       do { if (tlv_len < (x)) {                          \
+          LLOG_WARNX(name " EDP TLV too short received on %s",\
+              hardware->h_ifname);                        \
+          goto malformed;                                 \
+       } } while (0)
+
 int
 edp_decode(struct lldpd *cfg, char *frame, int s,
     struct lldpd_hardware *hardware,
@@ -221,17 +227,15 @@ edp_decode(struct lldpd *cfg, char *frame, int s,
 {
        struct lldpd_chassis *chassis;
        struct lldpd_port *port;
-       struct ethllc *llc;
-       struct edp_header *eh;
-       struct edp_tlv_head *tlv;
-       struct edp_tlv_info *info;
 #ifdef ENABLE_DOT1
-       struct edp_tlv_vlan *vlan;
-       struct lldpd_vlan *lvlan, *lvlan_next;
+       struct lldpd_vlan *lvlan = NULL, *lvlan_next;
 #endif
        const unsigned char edpaddr[] = EDP_MULTICAST_ADDR;
-       struct iovec iov;
-       int f, len, gotend = 0, gotvlans = 0;
+       int length, gotend = 0, gotvlans = 0, edp_len, tlv_len, tlv_type;
+       int edp_port, edp_slot;
+       u_int8_t *pos, *pos_edp, *tlv;
+       u_int8_t version[4];
+       struct in_addr address;
 
        if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
                LLOG_WARN("failed to allocate remote chassis");
@@ -246,41 +250,44 @@ edp_decode(struct lldpd *cfg, char *frame, int s,
        TAILQ_INIT(&port->p_vlans);
 #endif
 
-       if (s < sizeof(struct ethllc) + sizeof(struct edp_header)) {
-               LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
+       length = s;
+       pos = (u_int8_t*)frame;
+
+       if (length < 2*ETH_ALEN + sizeof(u_int16_t) + 8 /* LLC */ +
+           10 + ETH_ALEN /* EDP header */) {
+               LLOG_WARNX("too short EDP frame received on %s", hardware->h_ifname);
                goto malformed;
        }
 
-       llc = (struct ethllc *)frame;
-       if (memcmp(&llc->ether.dhost, edpaddr, sizeof(edpaddr)) != 0) {
+       if (PEEK_CMP(edpaddr, sizeof(edpaddr)) != 0) {
                LLOG_INFO("frame not targeted at EDP multicast address received on %s",
                    hardware->h_ifname);
                goto malformed;
        }
-       if (ntohs(llc->ether.size) > s - sizeof(struct ieee8023)) {
-               LLOG_WARNX("incorrect 802.3 frame size reported on %s",
-                   hardware->h_ifname);
-               goto malformed;
-       }
-       if (llc->protoid != htons(LLC_PID_EDP)) {
+       PEEK_DISCARD(ETH_ALEN); PEEK_DISCARD_UINT16;
+       PEEK_DISCARD(6);        /* LLC: DSAP + SSAP + control + org */
+       if (PEEK_UINT16 != LLC_PID_EDP) {
                LLOG_DEBUG("incorrect LLC protocol ID received on %s",
                    hardware->h_ifname);
                goto malformed;
        }
 
-       f = sizeof(struct ethllc);
-       eh = (struct edp_header *)(frame + f);
-       if (eh->version != 1) {
-               LLOG_WARNX("incorrect EDP version (%d) for frame received on %s",
-                   eh->version, hardware->h_ifname);
+       PEEK_SAVE(pos_edp);     /* Save the start of EDP packet */
+       if (PEEK_UINT8 != 1) {
+               LLOG_WARNX("incorrect EDP version for frame received on %s",
+                   hardware->h_ifname);
                goto malformed;
        }
-       if (eh->idtype != htons(0)) {
+       PEEK_DISCARD_UINT8;     /* Reserved */
+       edp_len = PEEK_UINT16;
+       PEEK_DISCARD_UINT16;    /* Checksum */
+       PEEK_DISCARD_UINT16;    /* Sequence */
+       if (PEEK_UINT16 != 0) { /* ID Type = 0 = MAC */
                LLOG_WARNX("incorrect device id type for frame received on %s",
                    hardware->h_ifname);
                goto malformed;
        }
-       if (ntohs(eh->len) > s - f) {
+       if (edp_len > length + 10) {
                LLOG_WARNX("incorrect size for EDP frame received on %s",
                    hardware->h_ifname);
                goto malformed;
@@ -292,27 +299,32 @@ edp_decode(struct lldpd *cfg, char *frame, int s,
                LLOG_WARN("unable to allocate memory for chassis ID");
                goto malformed;
        }
-       memcpy(chassis->c_id, eh->mac, ETH_ALEN);
-       /* We ignore reserved bytes and sequence number */
-       iov.iov_len = ntohs(eh->len);
-       iov.iov_base = frame + f;
-       if (iov_checksum(&iov, 1, 0) != 0) {
+       PEEK_BYTES(chassis->c_id, ETH_ALEN);
+
+       /* Let's check checksum */
+       if (frame_checksum(pos_edp, edp_len, 0) != 0) {
                LLOG_WARNX("incorrect EDP checksum for frame received on %s",
                    hardware->h_ifname);
                goto malformed;
        }
 
-       f += sizeof(struct edp_header);
-       while ((f < s) && !gotend) {
-               if (f + sizeof(struct edp_tlv_head) > s) {
+       while (length && !gotend) {
+               if (length < 4) {
                        LLOG_WARNX("EDP TLV header is too large for "
                            "frame received on %s",
                            hardware->h_ifname);
                        goto malformed;
                }
-               tlv = (struct edp_tlv_head *)(frame + f);
-               len = ntohs(tlv->tlv_len) - sizeof(struct edp_tlv_head);
-               if ((len < 0) || (f + sizeof(struct edp_tlv_head) + len > s)) {
+               if (PEEK_UINT8 != EDP_TLV_MARKER) {
+                       LLOG_WARNX("incorrect marker starting EDP TLV header for frame "
+                           "received on %s",
+                           hardware->h_ifname);
+                       goto malformed;
+               }
+               tlv_type = PEEK_UINT8;
+               tlv_len = PEEK_UINT16 - 4;
+               PEEK_SAVE(tlv);
+               if ((tlv_len < 0) || (tlv_len > length)) {
                        LLOG_DEBUG("incorrect size in EDP TLV header for frame "
                            "received on %s",
                            hardware->h_ifname);
@@ -320,107 +332,90 @@ edp_decode(struct lldpd *cfg, char *frame, int s,
                        gotend = 1;
                        break;
                }
-               f += sizeof(struct edp_tlv_head);
-               if (tlv->tlv_marker != EDP_TLV_MARKER) {
-                       LLOG_WARNX("incorrect marker starting EDP TLV header for frame "
-                           "received on %s",
-                           hardware->h_ifname);
-                       goto malformed;
-               }
-               switch (tlv->tlv_type) {
+               switch (tlv_type) {
                case EDP_TLV_INFO:
-                       if (len != sizeof(struct edp_tlv_info) -
-                           sizeof(struct edp_tlv_head)) {
-                               LLOG_WARNX("wrong size for EDP TLV info for frame "
-                                   "received on %s (%d vs %d)",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
-                       info = (struct edp_tlv_info *)(frame + f -
-                           sizeof(struct edp_tlv_head));
+                       CHECK_TLV_SIZE(32, "Info");
                        port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+                       edp_slot = PEEK_UINT16; edp_port = PEEK_UINT16;
                        if (asprintf(&port->p_id, "%d/%d",
-                               ntohs(info->slot) + 1, ntohs(info->port) + 1) == -1) {
+                               edp_slot + 1, edp_port + 1) == -1) {
                                LLOG_WARN("unable to allocate memory for "
                                    "port ID");
                                goto malformed;
                        }
                        port->p_id_len = strlen(port->p_id);
                        if (asprintf(&port->p_descr, "Slot %d / Port %d",
-                               ntohs(info->slot) + 1, ntohs(info->port) + 1) == -1) {
+                               edp_slot + 1, edp_port + 1) == -1) {
                                LLOG_WARN("unable to allocate memory for "
                                    "port description");
                                goto malformed;
                        }
+                       PEEK_DISCARD_UINT16; /* vchassis */
+                       PEEK_DISCARD(6);     /* Reserved */
+                       PEEK_BYTES(version, 4);
                        if (asprintf(&chassis->c_descr,
                                "EDP enabled device, version %d.%d.%d.%d",
-                               info->version[0], info->version[1],
-                               info->version[2], info->version[3]) == -1) {
+                               version[0], version[1],
+                               version[2], version[3]) == -1) {
                                LLOG_WARN("unable to allocate memory for "
                                    "chassis description");
                                goto malformed;
                        }
                        break;
                case EDP_TLV_DISPLAY:
-                       if ((chassis->c_name = (char *)calloc(1, len + 1)) == NULL) {
+                       if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == NULL) {
                                LLOG_WARN("unable to allocate memory for chassis "
                                    "name");
                                goto malformed;
                        }
                        /* TLV display contains a lot of garbage */
-                       strlcpy(chassis->c_name, frame + f, len);
+                       PEEK_BYTES(chassis->c_name, tlv_len);
                        break;
                case EDP_TLV_NULL:
-                       if (len != 0) {
+                       if (tlv_len != 0) {
                                LLOG_WARNX("null tlv with incorrect size in frame "
                                    "received on %s",
                                    hardware->h_ifname);
                                goto malformed;
                        }
-                       if (f != s)
+                       if (length)
                                LLOG_DEBUG("extra data after edp frame on %s",
                                    hardware->h_ifname);
                        gotend = 1;
                        break;
                case EDP_TLV_VLAN:
 #ifdef ENABLE_DOT1
-                       if (len < sizeof(struct edp_tlv_vlan) -
-                           sizeof(struct edp_tlv_head)) {
-                               LLOG_WARNX("wrong size for EDP TLV vlan for frame "
-                                   "received on %s (%d vs %d)",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
-                       vlan = (struct edp_tlv_vlan *)(frame + f -
-                           sizeof(struct edp_tlv_head));
+                       CHECK_TLV_SIZE(12, "VLAN");
                        if ((lvlan = (struct lldpd_vlan *)calloc(1,
                                    sizeof(struct lldpd_vlan))) == NULL) {
                                LLOG_WARN("unable to allocate vlan");
                                goto malformed;
                        }
-                       lvlan->v_vid = ntohs(vlan->vid);
-                       if ((lvlan->v_name = (char *)calloc(1, len + 1 -
-                                   sizeof(struct edp_tlv_vlan) +
-                                   sizeof(struct edp_tlv_head))) == NULL) {
+                       PEEK_DISCARD_UINT16; /* Flags + reserved */
+                       lvlan->v_vid = PEEK_UINT16; /* VID */
+                       PEEK_DISCARD(4);            /* Reserved */
+                       PEEK_BYTES(&address, sizeof(address));
+
+                       if ((lvlan->v_name = (char *)calloc(1,
+                                   tlv_len + 1 - 12)) == NULL) {
                                LLOG_WARN("unable to allocate vlan name");
+                               free(lvlan);
                                goto malformed;
                        }
-                       strlcpy(lvlan->v_name, frame + f + sizeof(struct edp_tlv_vlan) -
-                           sizeof(struct edp_tlv_head), len -
-                           sizeof(struct edp_tlv_vlan) +
-                           sizeof(struct edp_tlv_head));
-                       if (vlan->ip.s_addr != INADDR_ANY) {
+                       PEEK_BYTES(lvlan->v_name, tlv_len - 12);
+
+                       if (address.s_addr != INADDR_ANY) {
                                if (chassis->c_mgmt.s_addr == INADDR_ANY)
-                                       chassis->c_mgmt.s_addr = vlan->ip.s_addr;
+                                       chassis->c_mgmt.s_addr = address.s_addr;
                                else
                                        /* We need to guess the good one */
                                        if (cfg->g_mgmt_pattern != NULL) {
                                                /* We can try to use this to prefer an address */
                                                char *ip;
-                                               ip = inet_ntoa(vlan->ip);
+                                               ip = inet_ntoa(address);
                                                if (fnmatch(cfg->g_mgmt_pattern,
                                                        ip, 0) == 0)
-                                                       chassis->c_mgmt.s_addr = vlan->ip.s_addr;
+                                                       chassis->c_mgmt.s_addr = address.s_addr;
                                        }
                        }
                        TAILQ_INSERT_TAIL(&port->p_vlans,
@@ -430,10 +425,10 @@ edp_decode(struct lldpd *cfg, char *frame, int s,
                        break;
                default:
                        LLOG_DEBUG("unknown EDP TLV type (%d) received on %s",
-                           tlv->tlv_type, hardware->h_ifname);
+                           tlv_type, hardware->h_ifname);
                        hardware->h_rx_unrecognized_cnt++;
                }
-               f += len;
+               PEEK_DISCARD(tlv + tlv_len - pos);
        }
        if ((chassis->c_id == NULL) ||
            (port->p_id == NULL) ||
index 5e7aec2872b0018ac02d38d94cad9537b021dc72..9a4f6c28a0cfefcc388105d008ead49a894e0286 100644 (file)
--- a/src/edp.h
+++ b/src/edp.h
 
 #define EDP_TLV_MARKER  0x99
 
-#include "llc.h"
-
-struct edp_header {
-       u_int8_t        version;
-       u_int8_t        reserved;
-       u_int16_t       len;
-       u_int16_t       checksum;
-       u_int16_t       sequence;
-       u_int16_t       idtype; /* Should be 0 for MAC */
-       u_int8_t        mac[ETH_ALEN];
-} __attribute__ ((__packed__));
-
-struct edp_tlv_head {
-       u_int8_t         tlv_marker; /* 0x99 */
-       u_int8_t         tlv_type;
-       u_int16_t        tlv_len;
-} __attribute__ ((__packed__));
-
 enum {
        EDP_TLV_NULL                    = 0,
        EDP_TLV_DISPLAY                 = 1,
@@ -51,24 +33,4 @@ enum {
        EDP_TLV_ESRP                    = 8,
 };
 
-struct edp_tlv_info {
-       struct edp_tlv_head head;
-       u_int16_t       slot;
-       u_int16_t       port;
-       u_int16_t       vchassis;
-       u_int8_t        reserved[6];
-       u_int8_t        version[4];
-       u_int8_t        connections[16];
-} __attribute__ ((__packed__));
-
-#define EDP_VLAN_HAS_IP (1 << 8)
-struct edp_tlv_vlan {
-       struct edp_tlv_head head;
-       u_int8_t        flags;
-       u_int8_t        reserved1[1];
-       u_int16_t       vid;
-       u_int8_t        reserved2[4];
-       struct in_addr  ip;
-} __attribute__ ((__packed__));
-
 #endif /* _EDP_H */
diff --git a/src/frame.h b/src/frame.h
new file mode 100644 (file)
index 0000000..7bde063
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2009 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRAME_H
+#define _FRAME_H
+
+union {
+       uint8_t uint8;
+       uint16_t uint16;
+       uint32_t uint32;
+} types;
+
+/* This set of macro are used to build packets. The current position in buffer
+ * is `pos'. The length of the remaining space in buffer is `length'. `type'
+ * should be a member of `types'. This was stolen from ladvd which was adapted
+ * from Net::CDP. */
+
+#define POKE(value, type, func)                          \
+        ((length >= sizeof(type)) &&             \
+            (                                    \
+               type = func(value),               \
+                memcpy(pos, &type, sizeof(type)), \
+               length -= sizeof(type),           \
+               pos += sizeof(type),              \
+               1                                 \
+           ))
+#define POKE_UINT8(value) POKE(value, types.uint8, )
+#define POKE_UINT16(value) POKE(value, types.uint16, htons)
+#define POKE_UINT32(value) POKE(value, types.uint32, htonl)
+#define POKE_BYTES(value, bytes)                              \
+        ((length >= (bytes)) &&                                       \
+            (                                                 \
+               memcpy(pos, value, bytes),                     \
+               length -= (bytes),                             \
+               pos += (bytes),                                \
+               1                                              \
+            ))
+#define POKE_SAVE(where)                       \
+       (where = pos, 1)
+#define POKE_RESTORE(where)                    \
+       do {                                    \
+       if ((where) > pos)                      \
+               length -= ((where) - pos);      \
+       else                                    \
+               length += (pos - (where));      \
+       pos = (where);                          \
+       } while(0)
+
+/* This set of macro are used to parse packets. The same variable as for POKE_*
+ * are used. There is no check on boundaries. */
+
+#define PEEK(type, func)                               \
+       (                                               \
+               memcpy(&type, pos, sizeof(type)),       \
+               length -= sizeof(type),                 \
+               pos += sizeof(type),                    \
+               func(type)                              \
+       )
+#define PEEK_UINT8 PEEK(types.uint8, )
+#define PEEK_UINT16 PEEK(types.uint16, ntohs)
+#define PEEK_UINT32 PEEK(types.uint32, ntohl)
+#define PEEK_BYTES(value, bytes)                              \
+        do {                                                  \
+               memcpy(value, pos, bytes);                     \
+               length -= (bytes);                             \
+               pos += (bytes);                                \
+       } while (0)
+#define PEEK_DISCARD(bytes)                    \
+       do {                                    \
+               length -= (bytes);              \
+               pos += (bytes);                 \
+       } while (0)
+#define PEEK_DISCARD_UINT8 PEEK_DISCARD(1)
+#define PEEK_DISCARD_UINT16 PEEK_DISCARD(2)
+#define PEEK_DISCARD_UINT32 PEEK_DISCARD(3)
+#define PEEK_CMP(value, bytes)                 \
+       (length -= (bytes),                     \
+        pos += (bytes),                        \
+        memcmp(pos-bytes, value, bytes))
+#define PEEK_SAVE POKE_SAVE
+#define PEEK_RESTORE POKE_RESTORE
+
+/* LLDP specific. We need a `tlv' pointer. */
+#define POKE_START_LLDP_TLV(type)  \
+        (                         \
+        tlv = pos,                \
+        POKE_UINT16(type << 9)    \
+       )
+#define POKE_END_LLDP_TLV                                     \
+        (                                                     \
+        memcpy(&types.uint16, tlv, sizeof(uint16_t)),         \
+        types.uint16 |= htons((pos - (tlv + 2)) & 0x01ff),    \
+        memcpy(tlv, &types.uint16, sizeof(uint16_t)),         \
+        1                                                     \
+       )
+
+/* Same for CDP */
+#define POKE_START_CDP_TLV(type)  \
+        (                         \
+        POKE_UINT16(type),        \
+        tlv = pos,                \
+        POKE_UINT16(0)            \
+       )
+#define POKE_END_CDP_TLV                                      \
+        (                                                     \
+        types.uint16 = htons(pos - tlv + 2),                  \
+        memcpy(tlv, &types.uint16, sizeof(uint16_t)),         \
+        1                                                     \
+       )
+
+/* Same for EDP */
+#define POKE_START_EDP_TLV(type)          \
+        (                                 \
+        POKE_UINT8(EDP_TLV_MARKER),       \
+        POKE_UINT8(type),                 \
+        tlv = pos,                        \
+        POKE_UINT16(0)                    \
+       )
+#define POKE_END_EDP_TLV POKE_END_CDP_TLV
+
+u_int16_t frame_checksum(const u_int8_t *, int, int);
+
+#endif /* _FRAME_H */
diff --git a/src/iov.c b/src/iov.c
deleted file mode 100644 (file)
index c78b6f9..0000000
--- a/src/iov.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "lldpd.h"
-
-void
-iov_dump(struct lldpd_frame **buffer, struct iovec *iov, int count)
-{
-       int i;
-       int len = 0;
-       void *p;
-       for (i = 0; i < count; i++)
-               len += iov[i].iov_len;
-       if ((*buffer = (struct lldpd_frame *)malloc(len +
-                   sizeof(int))) == NULL) {
-               LLOG_WARN("unable to allocate buffer");
-               return;
-       }
-       memcpy(*buffer, &len, sizeof(int));
-       p = (*buffer)->frame;
-       for (i = 0; i < count; i++) {
-               memcpy(p, iov[i].iov_base, iov[i].iov_len);
-               p += iov[i].iov_len;
-       }
-}
-
-u_int16_t
-iov_checksum(struct iovec *iov, int count, int cisco)
-{
-       unsigned int sum = 0, v = 0;
-       int len, oddbyte = 0, i;
-       u_char *cp;
-
-       /* We compute in network byte order */
-       for (i = 0; i < count; i++) {
-               len = iov[i].iov_len;
-               cp = iov[i].iov_base;
-               if (oddbyte) {
-                       sum += (v << 8) + *cp++;
-                       len--;
-               }
-               while ((len -= 2) >= 0) {
-                       sum += *cp++ << 8;
-                       sum += *cp++;
-               }
-               if ((oddbyte = len & 1) != 0)
-                       v = *cp;
-       }
-       /* The remaining byte seems to be handled oddly by Cisco. Any hint about
-        * this is welcome. */
-       if (oddbyte) {
-               if (cisco)
-                       sum += v;
-               else
-                       sum += v << 8;
-       }
-       sum = (sum >> 16) + (sum & 0xffff);
-       sum += sum >> 16;
-       sum = ntohs(sum);
-       return (0xffff & ~sum);
-}
diff --git a/src/llc.h b/src/llc.h
deleted file mode 100644 (file)
index 002ccf1..0000000
--- a/src/llc.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _LLC_H
-#define _LLC_H
-
-struct ieee8023 {
-       u_int8_t  dhost[ETH_ALEN];      /* destination eth addr */
-       u_int8_t  shost[ETH_ALEN];      /* source ether addr    */
-       u_int16_t size;         /* packet type ID field */
-} __attribute__ ((__packed__));
-
-struct ethllc {
-       struct ieee8023 ether;
-       u_int8_t  dsap;         /* destination SAP */
-       u_int8_t  ssap;         /* source SAP */
-       u_int8_t  control;              /* LLC control field */
-       u_int8_t  org[3];
-       u_int16_t protoid;
-} __attribute__ ((__packed__));
-
-#endif
index 172d9b4c75da43398a10afff2ebcf1091e456fa3..02d94d9d074382825ac2a56753d509a7627db6c0 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "lldpd.h"
+#include "frame.h"
 
 #include <unistd.h>
 #include <errno.h>
 
 int
 lldp_send(struct lldpd *global, struct lldpd_chassis *chassis,
-               struct lldpd_hardware *hardware)
+         struct lldpd_hardware *hardware)
 {
-       struct ether_header eh;
-       const u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR;
-       struct iovec *iov = NULL;
-       struct lldp_id chid, pid;
-       struct lldp_ttl ttl;
-       struct lldp_end end;
-       struct lldp_string name;
-       struct lldp_string descr;
-       struct lldp_string str;
-       struct lldp_cap cap;
-       struct lldp_mgmt mgmt;
+       struct lldpd_port *port;
+       struct lldpd_frame *frame;
+       int length;
+       u_int8_t *packet, *pos, *tlv;
+
+       u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR;
 #ifdef ENABLE_DOT1
        const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1;
-       struct lldp_vlan *ovlan = NULL;
-       int v;
        struct lldpd_vlan *vlan;
 #endif
 #ifdef ENABLE_DOT3
        const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3;
-       struct lldp_aggreg aggreg;
-       struct lldp_macphy macphy;
-       struct lldp_mfs mfs;
 #endif
 #ifdef ENABLE_LLDPMED
        int i;
        const u_int8_t med[] = LLDP_TLV_ORG_MED;
-       struct lldpmed_cap medcap;
-       struct lldp_org medhw, medfw, medsw, medsn,
-           medmodel, medasset, medmanuf, medloc[3];
 #endif
-       struct lldpd_port *port = &hardware->h_lport;
-       u_int c = -1, len = 0;
-       struct lldpd_frame *buffer;
 
-#define IOV_NEW                                                        \
-       if ((iov = (struct iovec*)realloc(iov, (++c + 1) *      \
-                   sizeof(struct iovec))) == NULL)             \
-               fatal(NULL);
+       port = &hardware->h_lport;
+       length = hardware->h_mtu;
+       if ((packet = (u_int8_t*)malloc(length)) == NULL)
+               return ENOMEM;
+       memset(packet, 0, length);
+       pos = packet;
 
        /* Ethernet header */
-       memset(&eh, 0, sizeof(eh));
-       memcpy(&eh.ether_shost, &hardware->h_lladdr,
-           sizeof(eh.ether_shost));
-       memcpy(&eh.ether_dhost, &mcastaddr,
-           sizeof(eh.ether_dhost));
-       eh.ether_type = htons(ETHERTYPE_LLDP);
-       IOV_NEW;
-       iov[c].iov_base = &eh;
-       iov[c].iov_len = sizeof(struct ether_header);
+       if (!(
+             /* LLDP multicast address */
+             POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
+             /* Source MAC address */
+             POKE_BYTES(&hardware->h_lladdr, sizeof(hardware->h_lladdr)) &&
+             /* LLDP frame */
+             POKE_UINT16(ETHERTYPE_LLDP)))
+               goto toobig;
 
        /* Chassis ID */
-       memset(&chid, 0, sizeof(chid));
-       len = chassis->c_id_len + sizeof(chid);
-       chid.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_CHASSIS_ID,
-           len - sizeof(struct lldp_tlv_head));
-       chid.tlv_id_subtype = chassis->c_id_subtype;
-       IOV_NEW;
-       iov[c].iov_base = &chid;
-       iov[c].iov_len = sizeof(chid);
-       IOV_NEW;
-       iov[c].iov_base = chassis->c_id;
-       iov[c].iov_len = chassis->c_id_len;
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_CHASSIS_ID) &&
+             POKE_UINT8(chassis->c_id_subtype) &&
+             POKE_BYTES(chassis->c_id, chassis->c_id_len) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
        /* Port ID */
-       memset(&pid, 0, sizeof(pid));
-       len = port->p_id_len + sizeof(pid);
-       pid.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_PORT_ID,
-           len - sizeof(struct lldp_tlv_head));
-       pid.tlv_id_subtype = port->p_id_subtype;
-       IOV_NEW;
-       iov[c].iov_base = &pid;
-       iov[c].iov_len = sizeof(pid);
-       IOV_NEW;
-       iov[c].iov_base = port->p_id;
-       iov[c].iov_len = port->p_id_len;
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_PORT_ID) &&
+             POKE_UINT8(port->p_id_subtype) &&
+             POKE_BYTES(port->p_id, port->p_id_len) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
        /* Time to live */
-       memset(&ttl, 0, sizeof(ttl));
-       len = sizeof(ttl);
-       ttl.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_TTL,
-           len - sizeof(struct lldp_tlv_head));
-       ttl.tlv_ttl = htons(chassis->c_ttl);
-       IOV_NEW;
-       iov[c].iov_base = &ttl;
-       iov[c].iov_len = sizeof(ttl);
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_TTL) &&
+             POKE_UINT16(chassis->c_ttl) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
        /* System name */
-       memset(&name, 0, sizeof(name));
-       len = sizeof(name) + strlen(chassis->c_name);
-       name.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_NAME,
-           len - sizeof(struct lldp_tlv_head));
-       IOV_NEW;
-       iov[c].iov_base = &name;
-       iov[c].iov_len = sizeof(name);
-       IOV_NEW;
-       iov[c].iov_base = chassis->c_name;
-       iov[c].iov_len = strlen(chassis->c_name);
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_NAME) &&
+             POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
        /* System description */
-       memset(&descr, 0, sizeof(descr));
-       len = sizeof(descr) + strlen(chassis->c_descr);
-       descr.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_DESCR,
-           len - sizeof(struct lldp_tlv_head));
-       IOV_NEW;
-       iov[c].iov_base = &descr;
-       iov[c].iov_len = sizeof(descr);
-       IOV_NEW;
-       iov[c].iov_base = chassis->c_descr;
-       iov[c].iov_len = strlen(chassis->c_descr);
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_DESCR) &&
+             POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
        /* System capabilities */
-       memset(&cap, 0, sizeof(cap));
-       len = sizeof(cap);
-       cap.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_CAP,
-           len - sizeof(struct lldp_tlv_head));
-       cap.tlv_cap_available = htons(chassis->c_cap_available);
-       cap.tlv_cap_enabled = htons(chassis->c_cap_enabled);
-       IOV_NEW;
-       iov[c].iov_base = &cap;
-       iov[c].iov_len = len;
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_CAP) &&
+             POKE_UINT16(chassis->c_cap_available) &&
+             POKE_UINT16(chassis->c_cap_enabled) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
        if (chassis->c_mgmt.s_addr != INADDR_ANY) {
                /* Management address */
-               memset(&mgmt, 0, sizeof(mgmt));
-               len = sizeof(mgmt);
-               mgmt.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_MGMT_ADDR,
-                   len - sizeof(struct lldp_tlv_head));
-               mgmt.mgmt_len = sizeof(struct in_addr) + sizeof(u_int8_t);
-               mgmt.mgmt_subtype = LLDP_MGMT_ADDR_IP4;
-               memcpy(&mgmt.mgmt_addr, &chassis->c_mgmt,
-                   sizeof(struct in_addr));
+               if (!(
+                     POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) &&
+                     /* Size of the address, including its type */
+                     POKE_UINT8(sizeof(struct in_addr) + 1) &&
+                     /* Address is IPv4 */
+                     POKE_UINT8(LLDP_MGMT_ADDR_IP4) &&
+                     POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr))))
+                       goto toobig;
 
                /* Interface port type, OID */
-               if (chassis->c_mgmt_if == 0)
-                       mgmt.mgmt_iface_subtype =
-                           LLDP_MGMT_IFACE_UNKNOWN;
-               else {
-                       mgmt.mgmt_iface_subtype =
-                           LLDP_MGMT_IFACE_IFINDEX;
-                       mgmt.mgmt_iface_id =
-                           htonl(chassis->c_mgmt_if);
+               if (chassis->c_mgmt_if == 0) {
+                       if (!(
+                             /* We don't know the management interface */
+                             POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) &&
+                             POKE_UINT32(0)))
+                               goto toobig;
+               } else {
+                       if (!(
+                             /* We have the index of the management interface */
+                             POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) &&
+                             POKE_UINT32(chassis->c_mgmt_if)))
+                               goto toobig;
                }
-               IOV_NEW;
-               iov[c].iov_base = &mgmt;
-               iov[c].iov_len = len;
+               if (!(
+                     /* We don't provide an OID for management */
+                     POKE_UINT8(0) &&
+                     POKE_END_LLDP_TLV))
+                       goto toobig;
        }
 
        /* Port description */
-       memset(&str, 0, sizeof(str));
-       len = sizeof(str) + strlen(port->p_descr);
-       str.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_PORT_DESCR,
-           len - sizeof(struct lldp_tlv_head));
-       IOV_NEW;
-       iov[c].iov_base = &str;
-       iov[c].iov_len = sizeof(str);
-       IOV_NEW;
-       iov[c].iov_base = port->p_descr;
-       iov[c].iov_len = strlen(port->p_descr);
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_PORT_DESCR) &&
+             POKE_BYTES(port->p_descr, strlen(port->p_descr)) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
 #ifdef ENABLE_DOT1
        /* VLANs */
-       v = 0;
-       TAILQ_FOREACH(vlan, &port->p_vlans, v_entries)
-           v++;
-       if ((v > 0) &&
-           ((ovlan = (struct lldp_vlan*)malloc(v*sizeof(struct lldp_vlan))) == NULL))
-               LLOG_WARN("no room for vlans");
-       else {
-               TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
-                       v--;
-                       memset(&ovlan[v], 0, sizeof(ovlan[v]));
-                       ovlan[v].tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
-                           sizeof(ovlan[v].tlv_org_id) +
-                           sizeof(ovlan[v].tlv_org_subtype) + sizeof(ovlan[v].vid) +
-                           sizeof(ovlan[v].len) + strlen(vlan->v_name));
-                       memcpy(ovlan[v].tlv_org_id, dot1, sizeof(ovlan[v].tlv_org_id));
-                       ovlan[v].tlv_org_subtype = LLDP_TLV_DOT1_VLANNAME;
-                       ovlan[v].vid = htons(vlan->v_vid);
-                       ovlan[v].len = strlen(vlan->v_name);
-                       IOV_NEW;
-                       iov[c].iov_base = &ovlan[v];
-                       iov[c].iov_len = sizeof(ovlan[v]);
-                       IOV_NEW;
-                       iov[c].iov_base = vlan->v_name;
-                       iov[c].iov_len = strlen(vlan->v_name);
-               }
+       TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
+               if (!(
+                     POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+                     POKE_BYTES(dot1, sizeof(dot1)) &&
+                     POKE_UINT8(LLDP_TLV_DOT1_VLANNAME) &&
+                     POKE_UINT16(vlan->v_vid) &&
+                     POKE_UINT8(strlen(vlan->v_name)) &&
+                     POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) &&
+                     POKE_END_LLDP_TLV))
+                       goto toobig;
        }
 #endif
 
 #ifdef ENABLE_DOT3
        /* Aggregation status */
-       memset(&aggreg, 0, sizeof(aggreg));
-       aggreg.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
-           sizeof(aggreg.tlv_org_id) +
-           sizeof(aggreg.tlv_org_subtype) +
-           sizeof(aggreg.status) + sizeof(aggreg.id));
-       memcpy(aggreg.tlv_org_id, dot3, sizeof(aggreg.tlv_org_id));
-       aggreg.tlv_org_subtype = LLDP_TLV_DOT3_LA;
-       aggreg.status = (port->p_aggregid) ? 3:1; /* Bit 0 = capability ; Bit 1 = status */
-       aggreg.id = htonl(port->p_aggregid);
-       IOV_NEW;
-       iov[c].iov_base = &aggreg;
-       iov[c].iov_len = sizeof(aggreg);
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+             POKE_BYTES(dot3, sizeof(dot3)) &&
+             POKE_UINT8(LLDP_TLV_DOT3_LA) &&
+             /* Bit 0 = capability ; Bit 1 = status */
+             POKE_UINT8((port->p_aggregid) ? 3:1) &&
+             POKE_UINT32(port->p_aggregid) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
        /* MAC/PHY */
-       memset(&macphy, 0, sizeof(macphy));
-       macphy.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
-           sizeof(macphy.tlv_org_id) +
-           sizeof(macphy.tlv_org_subtype) +
-           sizeof(macphy.autoneg) + sizeof(macphy.advertised) +
-           sizeof(macphy.mau));
-       memcpy(macphy.tlv_org_id, dot3, sizeof(macphy.tlv_org_id));
-       macphy.tlv_org_subtype = LLDP_TLV_DOT3_MAC;
-       macphy.autoneg = port->p_autoneg_support |
-           (port->p_autoneg_enabled << 1);
-       macphy.advertised = htons(port->p_autoneg_advertised);
-       macphy.mau = htons(port->p_mau_type);
-       IOV_NEW;
-       iov[c].iov_base = &macphy;
-       iov[c].iov_len = sizeof(macphy);
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+             POKE_BYTES(dot3, sizeof(dot3)) &&
+             POKE_UINT8(LLDP_TLV_DOT3_MAC) &&
+             POKE_UINT8(port->p_autoneg_support |
+                        (port->p_autoneg_enabled << 1)) &&
+             POKE_UINT16(port->p_autoneg_advertised) &&
+             POKE_UINT16(port->p_mau_type) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
        /* MFS */
-       memset(&mfs, 0, sizeof(mfs));
-       mfs.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
-           sizeof(mfs.tlv_org_id) +
-           sizeof(mfs.tlv_org_subtype) +
-           sizeof(mfs.mfs));
-       memcpy(mfs.tlv_org_id, dot3, sizeof(mfs.tlv_org_id));
-       mfs.tlv_org_subtype = LLDP_TLV_DOT3_MFS;
-       mfs.mfs = htons(port->p_mfs);
-       IOV_NEW;
-       iov[c].iov_base = &mfs;
-       iov[c].iov_len = sizeof(mfs);
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+             POKE_BYTES(dot3, sizeof(dot3)) &&
+             POKE_UINT8(LLDP_TLV_DOT3_MFS) &&
+             POKE_UINT16(port->p_mfs) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 #endif
 
 #ifdef ENABLE_LLDPMED
        if (port->p_med_cap_enabled) {
                /* LLDP-MED cap */
-               memset(&medcap, 0, sizeof(medcap));
-               medcap.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
-                   sizeof(medcap.tlv_org_id) +
-                   sizeof(medcap.tlv_org_subtype) + 
-                   sizeof(medcap.tlv_cap) + sizeof(medcap.tlv_type));
-               memcpy(medcap.tlv_org_id, med, sizeof(medcap.tlv_org_id));
-               medcap.tlv_org_subtype = LLDP_TLV_MED_CAP;
-               medcap.tlv_cap = htons(global->g_lchassis.c_med_cap_available);
-               medcap.tlv_type = global->g_lchassis.c_med_type;
-               IOV_NEW;
-               iov[c].iov_base = &medcap;
-               iov[c].iov_len = sizeof(medcap);
+               if (!(
+                     POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+                     POKE_BYTES(med, sizeof(med)) &&
+                     POKE_UINT8(LLDP_TLV_MED_CAP) &&
+                     POKE_UINT16(chassis->c_med_cap_available) &&
+                     POKE_UINT16(chassis->c_med_type) &&
+                     POKE_END_LLDP_TLV))
+                       goto toobig;
 
                /* LLDP-MED inventory */
-#define LLDP_INVENTORY(value, target, subtype)                         \
+#define LLDP_INVENTORY(value, subtype)                                 \
                if (value) {                                            \
-                   memset(&target, 0, sizeof(target));                 \
-                   len = (strlen(value)>32)?32:strlen(value);          \
-                   target.tlv_head.type_len =                          \
-                       LLDP_TLV_HEAD(LLDP_TLV_ORG,                     \
-                           sizeof(target.tlv_org_id) +                 \
-                           sizeof(target.tlv_org_subtype) +            \
-                           len);                                       \
-                   memcpy(target.tlv_org_id, med,                      \
-                       sizeof(target.tlv_org_id));                     \
-                   target.tlv_org_subtype = subtype;                   \
-                   IOV_NEW;                                            \
-                   iov[c].iov_base = &target;                          \
-                   iov[c].iov_len = sizeof(target);                    \
-                   IOV_NEW;                                            \
-                   iov[c].iov_base = value;                            \
-                   iov[c].iov_len = len;                               \
+                   if (!(                                              \
+                         POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&          \
+                         POKE_BYTES(med, sizeof(med)) &&               \
+                         POKE_UINT8(subtype) &&                        \
+                         POKE_BYTES(value,                             \
+                               (strlen(value)>32)?32:strlen(value)) && \
+                         POKE_END_LLDP_TLV))                           \
+                           goto toobig;                                \
                }
 
                if (port->p_med_cap_enabled & LLDPMED_CAP_IV) {
-                       LLDP_INVENTORY(global->g_lchassis.c_med_hw,
-                           medhw, LLDP_TLV_MED_IV_HW);
-                       LLDP_INVENTORY(global->g_lchassis.c_med_fw,
-                           medfw, LLDP_TLV_MED_IV_FW);
-                       LLDP_INVENTORY(global->g_lchassis.c_med_sw,
-                           medsw, LLDP_TLV_MED_IV_SW);
-                       LLDP_INVENTORY(global->g_lchassis.c_med_sn,
-                           medsn, LLDP_TLV_MED_IV_SN);
-                       LLDP_INVENTORY(global->g_lchassis.c_med_manuf,
-                           medmanuf, LLDP_TLV_MED_IV_MANUF);
-                       LLDP_INVENTORY(global->g_lchassis.c_med_model,
-                           medmodel, LLDP_TLV_MED_IV_MODEL);
-                       LLDP_INVENTORY(global->g_lchassis.c_med_asset,
-                           medasset, LLDP_TLV_MED_IV_ASSET);
+                       LLDP_INVENTORY(chassis->c_med_hw,
+                           LLDP_TLV_MED_IV_HW);
+                       LLDP_INVENTORY(chassis->c_med_fw,
+                           LLDP_TLV_MED_IV_FW);
+                       LLDP_INVENTORY(chassis->c_med_sw,
+                           LLDP_TLV_MED_IV_SW);
+                       LLDP_INVENTORY(chassis->c_med_sn,
+                           LLDP_TLV_MED_IV_SN);
+                       LLDP_INVENTORY(chassis->c_med_manuf,
+                           LLDP_TLV_MED_IV_MANUF);
+                       LLDP_INVENTORY(chassis->c_med_model,
+                           LLDP_TLV_MED_IV_MODEL);
+                       LLDP_INVENTORY(chassis->c_med_asset,
+                           LLDP_TLV_MED_IV_ASSET);
                }
 
                /* LLDP-MED location */
                for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) {
                        if (port->p_med_location[i].format == i + 1) {
-                               memset(&medloc[i], 0, sizeof(struct lldp_org));
-                               medloc[i].tlv_head.type_len =
-                                   LLDP_TLV_HEAD(LLDP_TLV_ORG,
-                                       sizeof(medloc[i].tlv_org_id) +
-                                       sizeof(medloc[i].tlv_org_subtype) + 1 +
-                                       port->p_med_location[i].data_len);
-                               memcpy(medloc[i].tlv_org_id, med,
-                                   sizeof(medloc[i].tlv_org_id));
-                               medloc[i].tlv_org_subtype = LLDP_TLV_MED_LOCATION;
-                               IOV_NEW;
-                               iov[c].iov_base = &medloc[i];
-                               iov[c].iov_len = sizeof(medloc[i]);
-                               IOV_NEW;
-                               iov[c].iov_base =
-                                   &port->p_med_location[i].format;
-                               iov[c].iov_len = 1;
-                               IOV_NEW;
-                               iov[c].iov_base =
-                                   port->p_med_location[i].data;
-                               iov[c].iov_len =
-                                   port->p_med_location[i].data_len;
+                               if (!(
+                                     POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+                                     POKE_BYTES(med, sizeof(med)) &&
+                                     POKE_UINT8(LLDP_TLV_MED_LOCATION) &&
+                                     POKE_UINT8(port->p_med_location[i].format) &&
+                                     POKE_BYTES(port->p_med_location[i].data,
+                                         port->p_med_location[i].data_len) &&
+                                     POKE_END_LLDP_TLV))
+                                       goto toobig;
                        }
                }
        }
 #endif
 
        /* END */
-       memset(&end, 0, sizeof(end));
-       IOV_NEW;
-       iov[c].iov_base = &end;
-       iov[c].iov_len = sizeof(end);
+       if (!(
+             POKE_START_LLDP_TLV(LLDP_TLV_END) &&
+             POKE_END_LLDP_TLV))
+               goto toobig;
 
-       c++;
        if (!global->g_multi ||
            (hardware->h_mode == LLDPD_MODE_ANY) ||
            (hardware->h_mode == LLDPD_MODE_LLDP)) {
                
-               if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real :
-                       hardware->h_raw, iov, c) == -1) {
+               if (write((hardware->h_raw_real > 0) ? hardware->h_raw_real :
+                       hardware->h_raw, packet, 
+                       pos - packet) == -1) {
                        LLOG_WARN("unable to send packet on real device for %s",
                            hardware->h_ifname);
-                       free(iov);
-#ifdef ENABLE_DOT1
-                       free(ovlan);
-#endif
+                       free(packet);
                        return ENETDOWN;
                }
 
                hardware->h_tx_cnt++;
        }
 
-       iov_dump(&buffer, iov, c);
-       free(iov);
-#ifdef ENABLE_DOT1
-       free(ovlan);
-#endif
-       if (buffer != NULL) {
-
-               /* We assume that LLDP frame is the reference */
+       /* We assume that LLDP frame is the reference */
+       if ((frame = (struct lldpd_frame*)malloc(
+                       sizeof(int) + pos - packet)) != NULL) {
+               frame->size = pos - packet;
+               memcpy(&frame->frame, packet, frame->size);
                if ((hardware->h_llastframe == NULL) ||
-                   (hardware->h_llastframe->size != buffer->size) ||
-                   (memcmp(hardware->h_llastframe->frame, buffer->frame,
-                       buffer->size) != 0)) {
+                   (hardware->h_llastframe->size != frame->size) ||
+                   (memcmp(hardware->h_llastframe->frame, frame->frame,
+                       frame->size) != 0)) {
                        free(hardware->h_llastframe);
-                       hardware->h_llastframe = buffer;
-                       hardware->h_llastchange = time(NULL);
+               hardware->h_llastframe = frame;
+               hardware->h_llastchange = time(NULL);
                } else
-                       free(buffer);
+                       free(frame);
        }
 
+       free(packet);
        return 0;
+
+toobig:
+       free(packet);
+       return E2BIG;
 }
 
+#define CHECK_TLV_SIZE(x, name)                                   \
+       do { if (tlv_size < (x)) {                         \
+          LLOG_WARNX(name " TLV too short received on %s",\
+              hardware->h_ifname);                        \
+          goto malformed;                                 \
+       } } while (0)
+
 int
 lldp_decode(struct lldpd *cfg, char *frame, int s,
     struct lldpd_hardware *hardware,
@@ -401,17 +317,19 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
 {
        struct lldpd_chassis *chassis;
        struct lldpd_port *port;
-       struct ether_header *ether;
        const char lldpaddr[] = LLDP_MULTICAST_ADDR;
        const char dot1[] = LLDP_TLV_ORG_DOT1;
        const char dot3[] = LLDP_TLV_ORG_DOT3;
        const char med[] = LLDP_TLV_ORG_MED;
-       int f;                   /* Current position in frame */
-       int size, type, subtype; /* TLV header */
+       char orgid[3];
+       int length, gotend = 0;
+       int tlv_size, tlv_type, tlv_subtype;
+       u_int8_t *pos, *tlv;
        char *b;
-       int gotend = 0;
+#ifdef ENABLE_DOT1
        struct lldpd_vlan *vlan;
        int vlan_len;
+#endif
 
        if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
                LLOG_WARN("failed to allocate remote chassis");
@@ -426,164 +344,133 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
        TAILQ_INIT(&port->p_vlans);
 #endif
 
-       if (s < sizeof(struct ether_header)) {
+       length = s;
+       pos = (u_int8_t*)frame;
+
+       if (length < 2*ETH_ALEN + sizeof(u_int16_t)) {
                LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
                goto malformed;
        }
-       ether = (struct ether_header *)frame;
-       if (memcmp(ether->ether_dhost, lldpaddr, sizeof(lldpaddr)) != 0) {
+       if (PEEK_CMP(lldpaddr, ETH_ALEN) != 0) {
                LLOG_INFO("frame not targeted at LLDP multicast address received on %s",
                    hardware->h_ifname);
                goto malformed;
        }
-       if (ETHERTYPE_LLDP != ntohs(ether->ether_type)) {
+       PEEK_DISCARD(ETH_ALEN); /* Skip source address */
+       if (PEEK_UINT16 != ETHERTYPE_LLDP) {
                LLOG_INFO("non LLDP frame received on %s",
                    hardware->h_ifname);
                goto malformed;
        }
 
-       f = sizeof(struct ether_header);
-       while ((f < s) && (!gotend)) {
-               if (f + 2 > s) {
+       while (length && (!gotend)) {
+               if (length < 2) {
                        LLOG_WARNX("tlv header too short received on %s",
                            hardware->h_ifname);
                        goto malformed;
                }
-               size = ntohs(*(u_int16_t*)(frame + f)) & 0x1ff;
-               type = ntohs(*(u_int16_t*)(frame + f)) >> 9;
-               f += 2;
-               if (f + size > s) {
-                       LLOG_WARNX("tlv header too short received on %s",
+               tlv_size = PEEK_UINT16;
+               tlv_type = tlv_size >> 9;
+               tlv_size = tlv_size & 0x1ff;
+               PEEK_SAVE(tlv);
+               if (length < tlv_size) {
+                       LLOG_WARNX("frame too short for tlv received on %s",
                            hardware->h_ifname);
                        goto malformed;
                }
-               switch (type) {
+               switch (tlv_type) {
                case LLDP_TLV_END:
-                       if (size != 0) {
+                       if (tlv_size != 0) {
                                LLOG_WARNX("lldp end received with size not null on %s",
                                    hardware->h_ifname);
                                goto malformed;
                        }
-                       if (f != s)
+                       if (length)
                                LLOG_DEBUG("extra data after lldp end on %s",
                                    hardware->h_ifname);
                        gotend = 1;
                        break;
                case LLDP_TLV_CHASSIS_ID:
                case LLDP_TLV_PORT_ID:
-                       if (size < 2) {
-                               LLOG_WARNX("tlv id too small received on %s",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
-                       subtype = *(u_int8_t*)(frame + f);
-                       f++;
-                       if ((subtype == 0) || (subtype > 7)) {
+                       CHECK_TLV_SIZE(2, "Port Id");
+                       tlv_subtype = PEEK_UINT8;
+                       if ((tlv_subtype == 0) || (tlv_subtype > 7)) {
                                LLOG_WARNX("unknown subtype for tlv id received on %s",
                                    hardware->h_ifname);
                                goto malformed;
                        }
-                       if ((b = (char *)calloc(1, size - 1)) == NULL) {
+                       if ((b = (char *)calloc(1, tlv_size - 1)) == NULL) {
                                LLOG_WARN("unable to allocate memory for id tlv "
                                    "received on %s",
                                    hardware->h_ifname);
                                goto malformed;
                        }
-                       memcpy(b, frame + f, size - 1);
-                       if (type == LLDP_TLV_PORT_ID) {
-                               port->p_id_subtype = subtype;
+                       PEEK_BYTES(b, tlv_size - 1);
+                       if (tlv_type == LLDP_TLV_PORT_ID) {
+                               port->p_id_subtype = tlv_subtype;
                                port->p_id = b;
-                               port->p_id_len = size - 1;
+                               port->p_id_len = tlv_size - 1;
                        } else {
-                               chassis->c_id_subtype = subtype;
+                               chassis->c_id_subtype = tlv_subtype;
                                chassis->c_id = b;
-                               chassis->c_id_len = size - 1;
+                               chassis->c_id_len = tlv_size - 1;
                        }
-                       f += size - 1;
                        break;
                case LLDP_TLV_TTL:
-                       if (size < 2) {
-                               LLOG_WARNX("too short frame for ttl tlv received on %s",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
-                       chassis->c_ttl = ntohs(*(u_int16_t*)(frame + f));
-                       f += size;
+                       CHECK_TLV_SIZE(2, "TTL");
+                       chassis->c_ttl = PEEK_UINT16;
                        break;
                case LLDP_TLV_PORT_DESCR:
                case LLDP_TLV_SYSTEM_NAME:
                case LLDP_TLV_SYSTEM_DESCR:
-                       if (size < 1) {
+                       if (tlv_size < 1) {
                                LLOG_DEBUG("empty tlv received on %s",
                                    hardware->h_ifname);
                                break;
                        }
-                       if ((b = (char *)calloc(1, size + 1)) == NULL) {
+                       if ((b = (char *)calloc(1, tlv_size + 1)) == NULL) {
                                LLOG_WARN("unable to allocate memory for string tlv "
                                    "received on %s",
                                    hardware->h_ifname);
                                goto malformed;
                        }
-                       memcpy(b, frame + f, size);
-                       f += size;
-                       if (type == LLDP_TLV_PORT_DESCR)
+                       PEEK_BYTES(b, tlv_size);
+                       if (tlv_type == LLDP_TLV_PORT_DESCR)
                                port->p_descr = b;
-                       else if (type == LLDP_TLV_SYSTEM_NAME)
+                       else if (tlv_type == LLDP_TLV_SYSTEM_NAME)
                                chassis->c_name = b;
                        else chassis->c_descr = b;
                        break;
                case LLDP_TLV_SYSTEM_CAP:
-                       if (size < 4) {
-                               LLOG_WARNX("too short system cap tlv "
-                                   "received on %s",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
-                       chassis->c_cap_available = ntohs(*(u_int16_t*)(frame + f));
-                       f += 2;
-                       chassis->c_cap_enabled = ntohs(*(u_int16_t*)(frame + f));
-                       f += size - 2;
+                       CHECK_TLV_SIZE(4, "System capabilities");
+                       chassis->c_cap_available = PEEK_UINT16;
+                       chassis->c_cap_enabled = PEEK_UINT16;
                        break;
                case LLDP_TLV_MGMT_ADDR:
-                       if (size < 11) {
-                               LLOG_WARNX("too short management tlv received on %s",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
+                       CHECK_TLV_SIZE(11, "Management address");
                        if ((chassis->c_mgmt.s_addr == INADDR_ANY) &&
-                           (*(u_int8_t*)(frame + f) == 5) &&
-                           (*(u_int8_t*)(frame + f + 1) == 1)) {
+                           (PEEK_UINT8 == 1+sizeof(struct in_addr)) &&
+                           (PEEK_UINT8 == LLDP_MGMT_ADDR_IP4)) {
                                /* We have an IPv4 address, we ignore anything else */
-                               memcpy(&chassis->c_mgmt, frame + f + 2, sizeof(struct in_addr));
+                               PEEK_BYTES(&chassis->c_mgmt, sizeof(struct in_addr));
                                chassis->c_mgmt_if = 0;
                                /* We only handle ifIndex subtype */
-                               if (*(u_int8_t*)(frame + f + 6) == LLDP_MGMT_IFACE_IFINDEX)
-                                       chassis->c_mgmt_if = ntohl(*(u_int32_t*)(frame + f + 7));
+                               if (PEEK_UINT8 == LLDP_MGMT_IFACE_IFINDEX)
+                                       chassis->c_mgmt_if = PEEK_UINT32;
                        }
-                       f += size;
                        break;
                case LLDP_TLV_ORG:
-                       if (size < 4) {
-                               LLOG_WARNX("too short org tlv received on %s",
-                                   hardware->h_ifname);
-                               goto malformed;
-                       }
-                       if (memcmp(dot1, frame + f, 3) == 0) {
+                       CHECK_TLV_SIZE(4, "Organisational");
+                       PEEK_BYTES(orgid, sizeof(orgid));
+                       tlv_subtype = PEEK_UINT8;
+                       if (memcmp(dot1, orgid, sizeof(orgid)) == 0) {
 #ifndef ENABLE_DOT1
-                               f += size;
                                hardware->h_rx_unrecognized_cnt++;
 #else
                                /* Dot1 */
-                               switch (*(u_int8_t*)(frame + f + 3)) {
+                               switch (tlv_subtype) {
                                case LLDP_TLV_DOT1_VLANNAME:
-                                       if ((size < 7) ||
-                                           (size < 7 + *(u_int8_t*)(frame + f + 6))) {
-                                               LLOG_WARNX("too short vlan tlv "
-                                                   "received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       f += 4;
+                                       CHECK_TLV_SIZE(7, "VLAN");
                                        if ((vlan = (struct lldpd_vlan *)calloc(1,
                                                    sizeof(struct lldpd_vlan))) == NULL) {
                                                LLOG_WARN("unable to alloc vlan "
@@ -592,10 +479,9 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                                    hardware->h_ifname);
                                                goto malformed;
                                        }
-                                       vlan->v_vid = ntohs(*(u_int16_t*)(frame + f));
-                                       f += 2;
-                                       vlan_len = *(u_int8_t*)(frame + f);
-                                       f += 1;
+                                       vlan->v_vid = PEEK_UINT16;
+                                       vlan_len = PEEK_UINT8;
+                                       CHECK_TLV_SIZE(7 + vlan_len, "VLAN");
                                        if ((vlan->v_name =
                                                (char *)calloc(1, vlan_len + 1)) == NULL) {
                                                LLOG_WARN("unable to alloc vlan name for "
@@ -603,128 +489,74 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                                    hardware->h_ifname);
                                                goto malformed;
                                        }
-                                       memcpy(vlan->v_name, frame + f,
-                                           vlan_len);
+                                       PEEK_BYTES(vlan->v_name, vlan_len);
                                        TAILQ_INSERT_TAIL(&port->p_vlans,
                                            vlan, v_entries);
-                                       f += size - 7;
                                        break;
                                case LLDP_TLV_DOT1_PVID:
-                                       if (size < 6) {
-                                               LLOG_WARNX("too short pvid tlv "
-                                                   "received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       port->p_pvid =
-                                           ntohs(*(u_int16_t*)(frame + f + 4));
-                                       f += size;
+                                       CHECK_TLV_SIZE(6, "PVID");
+                                       port->p_pvid = PEEK_UINT16;
                                        break;
                                default:
                                        /* Unknown Dot1 TLV, ignore it */
-                                       f += size;
                                        hardware->h_rx_unrecognized_cnt++;
                                }
 #endif
-                       } else if (memcmp(dot3, frame + f, 3) == 0) {
+                       } else if (memcmp(dot3, orgid, sizeof(orgid)) == 0) {
 #ifndef ENABLE_DOT3
-                               f += size;
                                hardware->h_rx_unrecognized_cnt++;
 #else
                                /* Dot3 */
-                               subtype = *(u_int8_t*)(frame + f + 3);
-                               switch (subtype) {
+                               switch (tlv_subtype) {
                                case LLDP_TLV_DOT3_MAC:
-                                       f += 4;
-                                       if (size < 9) {
-                                               LLOG_WARNX("too short mac/phy tlv "
-                                                   "received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       port->p_autoneg_support =
-                                           *(u_int8_t*)(frame + f) && 0x1;
+                                       CHECK_TLV_SIZE(9, "MAC/PHY");
+                                       port->p_autoneg_support = PEEK_UINT8;
                                        port->p_autoneg_enabled =
-                                           *(u_int8_t*)(frame + f) && 0x2;
-                                       f += 1;
+                                           port->p_autoneg_support && 0x2;
+                                       port->p_autoneg_support =
+                                           port->p_autoneg_support && 0x1;
                                        port->p_autoneg_advertised =
-                                           ntohs(*(u_int16_t*)(frame + f));
-                                       f += 2;
-                                       port->p_mau_type =
-                                           ntohs(*(u_int16_t*)(frame + f));
-                                       f += size - 7;
+                                           PEEK_UINT16;
+                                       port->p_mau_type = PEEK_UINT16;
                                        break;
                                case LLDP_TLV_DOT3_LA:
-                                       if (size < 9) {
-                                               LLOG_WARNX("too short aggreg tlv "
-                                                   "received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       port->p_aggregid =
-                                           ntohl(*(u_int32_t*)(frame + f + 5));
-                                       f += size;
+                                       CHECK_TLV_SIZE(9, "Link aggregation");
+                                       port->p_aggregid = PEEK_UINT32;
                                        break;
                                case LLDP_TLV_DOT3_MFS:
-                                       if (size < 6) {
-                                               LLOG_WARNX("too short mfs tlv "
-                                                   "received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       port->p_mfs =
-                                           ntohs(*(u_int16_t*)(frame + f + 4));
-                                       f += size;
+                                       CHECK_TLV_SIZE(6, "MFS");
+                                       port->p_mfs = PEEK_UINT16;
                                        break;
                                default:
                                        /* Unknown Dot3 TLV, ignore it */
-                                       f += size;
                                        hardware->h_rx_unrecognized_cnt++;
                                }
 #endif
-                       } else if (memcmp(med, frame + f, 3) == 0) {
+                       } else if (memcmp(med, orgid, sizeof(orgid)) == 0) {
                                /* LLDP-MED */
 #ifndef ENABLE_LLDPMED
-                               f += size;
                                hardware->h_rx_unrecognized_cnt++;
 #else
                                u_int32_t policy;
                                int loctype;
+                               int power;
 
-                               subtype = *(u_int8_t*)(frame + f + 3);
-                               switch (subtype) {
+                               switch (tlv_subtype) {
                                case LLDP_TLV_MED_CAP:
-                                       f += 4;
-                                       if (size < 7) {
-                                               LLOG_WARNX("too short LLDP-MED cap "
-                                                   "tlv received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       chassis->c_med_cap_available =
-                                           ntohs(*(u_int16_t*)(frame + f));
-                                       f += 2;
-                                       chassis->c_med_type =
-                                           *(u_int8_t*)(frame + f);
-                                       f += size - 6;
+                                       CHECK_TLV_SIZE(7, "LLDP-MED capabilities");
+                                       chassis->c_med_cap_available = PEEK_UINT16;
+                                       chassis->c_med_type = PEEK_UINT8;
                                        port->p_med_cap_enabled |=
                                            LLDPMED_CAP_CAP;
                                        break;
                                case LLDP_TLV_MED_POLICY:
-                                       f += 4;
-                                       if (size < 8) {
-                                               LLOG_WARNX("too short LLDP-MED policy "
-                                                   "tlv received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       policy = ntohl(*((u_int32_t *)(frame + f)));
+                                       CHECK_TLV_SIZE(8, "LLDP-MED policy");
+                                       policy = PEEK_UINT32;
                                        if (((policy >> 24) < 1) ||
                                            ((policy >> 24) > LLDPMED_APPTYPE_LAST)) {
                                                LLOG_INFO("unknown policy field %d "
                                                    "received on %s",
                                                    hardware->h_ifname);
-                                               f += 4;
                                                break;
                                        }
                                        port->p_med_policy[(policy >> 24) - 1].type =
@@ -739,59 +571,44 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                            (policy & 0x1C0) >> 6;
                                        port->p_med_policy[(policy >> 24) - 1].dscp =
                                            policy & 0x3F;
-                                       f += size - 4;
                                        port->p_med_cap_enabled |=
                                            LLDPMED_CAP_POLICY;
                                        break;
                                case LLDP_TLV_MED_LOCATION:
-                                       f += 4;
-                                       if (size <= 5) {
-                                               LLOG_WARNX("too short LLDP-MED location "
-                                                   "tlv received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       loctype = *(u_int8_t*)(frame + f);
-                                       f += 1;
-                                       if ((loctype < 1) || (loctype > LLDPMED_LOCFORMAT_LAST)) {
+                                       CHECK_TLV_SIZE(5, "LLDP-MED Location");
+                                       loctype = PEEK_UINT8;
+                                       if ((loctype < 1) ||
+                                           (loctype > LLDPMED_LOCFORMAT_LAST)) {
                                                LLOG_INFO("unknown location type "
                                                    "received on %s",
                                                    hardware->h_ifname);
-                                               f += size - 5;
                                                break;
                                        }
                                        if ((port->p_med_location[loctype - 1].data =
-                                               (char*)malloc(size - 5)) == NULL) {
+                                               (char*)malloc(tlv_size - 5)) == NULL) {
                                                LLOG_WARN("unable to allocate memory "
                                                    "for LLDP-MED location for "
                                                    "frame received on %s",
                                                    hardware->h_ifname);
                                                goto malformed;
                                        }
-                                       memcpy(port->p_med_location[loctype - 1].data,
-                                           (char*)(frame + f),
-                                           size - 5);
+                                       PEEK_BYTES(port->p_med_location[loctype - 1].data,
+                                           tlv_size - 5);
                                        port->p_med_location[loctype - 1].data_len =
-                                           size - 5;
+                                           tlv_size - 5;
                                        port->p_med_location[loctype - 1].format = loctype;
-                                       f += size - 5;
                                        port->p_med_cap_enabled |=
                                            LLDPMED_CAP_LOCATION;
                                        break;
                                case LLDP_TLV_MED_MDI:
-                                       f += 4;
-                                       if (size < 7) {
-                                               LLOG_WARNX("too short LLDP-MED PoE-MDI "
-                                                   "tlv received on %s",
-                                                   hardware->h_ifname);
-                                               goto malformed;
-                                       }
-                                       switch (*(u_int8_t*)(frame + f) & 0xC0) {
+                                       CHECK_TLV_SIZE(7, "LLDP-MED PoE-MDI");
+                                       power = PEEK_UINT8;
+                                       switch (power & 0xC0) {
                                        case 0x0:
                                                port->p_med_pow_devicetype = LLDPMED_POW_TYPE_PSE;
                                                port->p_med_cap_enabled |=
                                                    LLDPMED_CAP_MDI_PSE;
-                                               switch (*(u_int8_t*)(frame + f) & 0x30) {
+                                               switch (power & 0x30) {
                                                case 0x0:
                                                        port->p_med_pow_source =
                                                            LLDPMED_POW_SOURCE_UNKNOWN;
@@ -813,7 +630,7 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                                port->p_med_pow_devicetype = LLDPMED_POW_TYPE_PD;
                                                port->p_med_cap_enabled |=
                                                    LLDPMED_CAP_MDI_PD;
-                                               switch (*(u_int8_t*)(frame + f) & 0x30) {
+                                               switch (power & 0x30) {
                                                case 0x0:
                                                        port->p_med_pow_source =
                                                            LLDPMED_POW_SOURCE_UNKNOWN;
@@ -835,7 +652,7 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                                port->p_med_pow_devicetype =
                                                    LLDPMED_POW_TYPE_RESERVED;
                                        }
-                                       switch (*(u_int8_t*)(frame + f) & 0x0F) {
+                                       switch (power & 0x0F) {
                                        case 0x0:
                                                port->p_med_pow_priority =
                                                    LLDPMED_POW_PRIO_UNKNOWN;
@@ -856,10 +673,7 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                                port->p_med_pow_priority =
                                                    LLDPMED_POW_PRIO_UNKNOWN;
                                        }
-                                       f += 1;
-                                       port->p_med_pow_val =
-                                           ntohs(*(u_int16_t*)(frame + f));
-                                       f += size - 5;
+                                       port->p_med_pow_val = PEEK_UINT16;
                                        break;
                                case LLDP_TLV_MED_IV_HW:
                                case LLDP_TLV_MED_IV_SW:
@@ -868,11 +682,10 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                case LLDP_TLV_MED_IV_MANUF:
                                case LLDP_TLV_MED_IV_MODEL:
                                case LLDP_TLV_MED_IV_ASSET:
-                                       f += 4;
-                                       if (size <= 4)
+                                       if (tlv_size <= 4)
                                                b = NULL;
                                        else {
-                                               if ((b = (char*)malloc(size - 3)) ==
+                                               if ((b = (char*)malloc(tlv_size - 3)) ==
                                                    NULL) {
                                                        LLOG_WARN("unable to allocate "
                                                            "memory for LLDP-MED "
@@ -881,11 +694,10 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                                            hardware->h_ifname);
                                                        goto malformed;
                                                }
-                                               strlcpy(b,
-                                                   (char*)(frame + f),
-                                                   size - 3);
+                                               PEEK_BYTES(b, tlv_size - 4);
+                                               b[tlv_size - 4] = '\0';
                                        }
-                                       switch (subtype) {
+                                       switch (tlv_subtype) {
                                        case LLDP_TLV_MED_IV_HW:
                                                chassis->c_med_hw = b;
                                                break;
@@ -912,13 +724,11 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                                free(b);
                                                break;
                                        }
-                                       f += size - 4;
                                        port->p_med_cap_enabled |=
                                            LLDPMED_CAP_IV;
                                        break;
                                default:
                                        /* Unknown LLDP MED, ignore it */
-                                       f += size;
                                        hardware->h_rx_unrecognized_cnt++;
                                }
 #endif /* ENABLE_LLDPMED */
@@ -926,14 +736,18 @@ lldp_decode(struct lldpd *cfg, char *frame, int s,
                                LLOG_INFO("unknown org tlv received on %s",
                                    hardware->h_ifname);
                                hardware->h_rx_unrecognized_cnt++;
-                               f += size;
                        }
                        break;
                default:
                        LLOG_WARNX("unknown tlv (%d) received on %s",
-                           type, hardware->h_ifname);
+                           tlv_type, hardware->h_ifname);
+                       goto malformed;
+               }
+               if (pos > tlv + tlv_size) {
+                       LLOG_WARNX("BUG: already past TLV!");
                        goto malformed;
                }
+               PEEK_DISCARD(tlv + tlv_size - pos);
        }
 
        /* Some random check */
index 10a28523342a0158e9a04844e59127cbf3cb9b2e..76f091b37502c6fa58d25bbe02258a977a0e3ce5 100644 (file)
        0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e                                      \
 }
 
-#define LLDP_TLV_HEAD(type, len) htons(((type) << 9) | (len))
-struct lldp_tlv_head {
-       u_int16_t       type_len;
-} __attribute__ ((__packed__));
-
 enum {
        LLDP_TLV_END                    = 0,
        LLDP_TLV_CHASSIS_ID             = 1,
@@ -62,12 +57,6 @@ enum {
        LLDP_TLV_DOT3_MFS               = 4
 };
 
-/* Chassis ID or Port ID */
-struct lldp_id {
-       struct lldp_tlv_head     tlv_head;      
-       u_int8_t                 tlv_id_subtype;
-} __attribute__ ((__packed__));
-
 enum {
        LLDP_CHASSISID_SUBTYPE_CHASSIS  = 1,
        LLDP_CHASSISID_SUBTYPE_IFALIAS  = 2,
@@ -88,21 +77,6 @@ enum {
        LLDP_PORTID_SUBTYPE_LOCAL       = 7
 };
 
-struct lldp_ttl {
-       struct lldp_tlv_head     tlv_head;      
-       u_int16_t                tlv_ttl;
-} __attribute__ ((__packed__));
-
-struct lldp_string {
-       struct lldp_tlv_head     tlv_head;
-} __attribute__ ((__packed__));
-
-struct lldp_cap {
-       struct lldp_tlv_head     tlv_head;
-       u_int16_t                tlv_cap_available;
-       u_int16_t                tlv_cap_enabled;
-} __attribute__ ((__packed__));
-
 /* Operational MAU Type field, from RFC 3636 */
 #define LLDP_DOT3_MAU_AUI 1
 #define LLDP_DOT3_MAU_10BASE5 2
@@ -184,60 +158,6 @@ enum {
        LLDP_MGMT_IFACE_SYSPORT = 3
 };
 
-/* Supports only IPv4 */
-struct lldp_mgmt {
-       struct lldp_tlv_head     tlv_head;
-       u_int8_t                 mgmt_len;
-       u_int8_t                 mgmt_subtype; /* Should be 1 */
-       struct in_addr           mgmt_addr;
-       u_int8_t                 mgmt_iface_subtype;
-       u_int32_t                mgmt_iface_id;
-       u_int8_t                 mgmt_oid_len;
-       u_int8_t                 mgmt_oid[0];
-} __attribute__ ((__packed__));
-
-struct lldp_org {
-       struct lldp_tlv_head     tlv_head;
-       u_int8_t                 tlv_org_id[3];
-       u_int8_t                 tlv_org_subtype;
-} __attribute__ ((__packed__));
-
-struct lldp_vlan {
-       struct lldp_tlv_head     tlv_head;
-       u_int8_t                 tlv_org_id[3];
-       u_int8_t                 tlv_org_subtype;
-       u_int16_t                vid;
-       u_int8_t                 len;
-} __attribute__ ((__packed__));
-
-struct lldp_aggreg {
-       struct lldp_tlv_head     tlv_head;
-       u_int8_t                 tlv_org_id[3];
-       u_int8_t                 tlv_org_subtype;
-       u_int8_t                 status;
-       u_int32_t                id;
-} __attribute__ ((__packed__));
-
-struct lldp_macphy {
-       struct lldp_tlv_head     tlv_head;
-       u_int8_t                 tlv_org_id[3];
-       u_int8_t                 tlv_org_subtype;
-       u_int8_t                 autoneg;
-       u_int16_t                advertised;
-       u_int16_t                mau;
-} __attribute__ ((__packed__));
-
-struct lldp_mfs {
-       struct lldp_tlv_head     tlv_head;
-       u_int8_t                 tlv_org_id[3];
-       u_int8_t                 tlv_org_subtype;
-       u_int16_t                mfs;
-} __attribute__ ((__packed__));
-
-struct lldp_end {
-       struct lldp_tlv_head     tlv_head;
-} __attribute__ ((__packed__));
-
 #ifdef ENABLE_LLDPMED
 enum {
        LLDP_TLV_MED_CAP        = 1,
@@ -297,14 +217,6 @@ enum {
 #define LLDPMED_CAP_MDI_PD 0x10
 #define LLDPMED_CAP_IV 0x20
 
-struct lldpmed_cap {
-       struct lldp_tlv_head     tlv_head;
-       u_int8_t                 tlv_org_id[3];
-       u_int8_t                 tlv_org_subtype;
-       u_int16_t                tlv_cap;
-       u_int8_t                 tlv_type;
-} __attribute__ ((__packed__));
 #endif /* ENABLE_LLDPMED */
 
-
 #endif /* _LLDP_H */
index 4ef4526399479279bb64504df17beba2cdca5bfc..5f3b7dece62faaaddf9e6c43acc11975ef858db0 100644 (file)
@@ -744,8 +744,8 @@ lldpd_decode(struct lldpd *cfg, char *frame, int s,
        int guess = LLDPD_MODE_LLDP;
 
        /* Discard VLAN frames */
-       if ((s >= sizeof(struct ieee8023)) &&
-           (((struct ieee8023*)frame)->size == htons(ETHERTYPE_VLAN)))
+       if ((s >= sizeof(struct ethhdr)) &&
+           (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
                return;
 
        if ((hardware->h_rlastframe != NULL) &&
index 203da1352ac496718dfd034eeff861b2fc081202..84ad3f525254293ed0e2ded34c745dcbddf50f36 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/ethtool.h>
 
 #include "compat.h"
-#include "llc.h"
 #include "lldp.h"
 #if defined (ENABLE_CDP) || defined (ENABLE_FDP)
 #include "cdp.h"
@@ -423,10 +422,6 @@ void                agent_priv_register_domain();
 /* strlcpy.c */
 size_t strlcpy(char *, const char *, size_t);
 
-/* iov.c */
-void            iov_dump(struct lldpd_frame **, struct iovec *, int);
-u_int16_t       iov_checksum(struct iovec *, int, int);
-
 /* client.c */
 struct client_handle {
        enum hmsg_type type;
index ed8ba737bcd9b230d33a5ad0ac4203caecc053b1..ff0e2729faa1ee7fabc71e9fc306d713e1291c06 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "lldpd.h"
+#include "frame.h"
 
 #ifdef ENABLE_SONMP
 
@@ -184,44 +185,81 @@ sonmp_send(struct lldpd *global, struct lldpd_chassis *chassis,
 {
        const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR;
        const u_int8_t llcorg[] = LLC_ORG_NORTEL;
-       struct sonmp frame;
-       memset(&frame, 0, sizeof(frame));
-       memcpy(&frame.llc.ether.shost, &hardware->h_lladdr,
-           sizeof(frame.llc.ether.shost));
-       memcpy(&frame.llc.ether.dhost, &mcastaddr,
-           sizeof(frame.llc.ether.dhost));
-       frame.llc.ether.size = htons(sizeof(struct sonmp) -
-           sizeof(struct ieee8023));
-       frame.llc.dsap = frame.llc.ssap = 0xaa;
-       frame.llc.control = 0x03;
-       memcpy(frame.llc.org, llcorg, sizeof(frame.llc.org));
-       frame.llc.protoid = htons(LLC_PID_SONMP_HELLO);
-       memcpy(&frame.addr, &chassis->c_mgmt, sizeof(struct in_addr));
-       frame.seg[2] = if_nametoindex(hardware->h_ifname);
-       frame.chassis = 1;      /* Other */
-       frame.backplane = 12;   /* Ethernet, Fast Ethernet and Gigabit */
-       frame.links = 1;        /* Dunno what it is */
-       frame.state = SONMP_TOPOLOGY_NEW; /* Should work. We have no state */
+       u_int8_t *packet, *pos, *pos_pid, *end;
+       int length;
+
+       length = hardware->h_mtu;
+       if ((packet = (u_int8_t*)malloc(length)) == NULL)
+               return ENOMEM;
+       memset(packet, 0, length);
+       pos = packet;
+
+       /* Ethernet header */
+       if (!(
+             /* SONMP multicast address as target */
+             POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
+             /* Source MAC addresss */
+             POKE_BYTES(&hardware->h_lladdr, sizeof(hardware->h_lladdr)) &&
+             /* SONMP frame is of fixed size */
+             POKE_UINT16(SONMP_SIZE)))
+               goto toobig;
+
+       /* LLC header */
+       if (!(
+             /* DSAP and SSAP */
+             POKE_UINT8(0xaa) && POKE_UINT8(0xaa) &&
+             /* Control field */
+             POKE_UINT8(0x03) &&
+             /* ORG */
+             POKE_BYTES(llcorg, sizeof(llcorg)) &&
+             POKE_SAVE(pos_pid) && /* We will modify PID later to
+                                      create a new frame */
+             POKE_UINT16(LLC_PID_SONMP_HELLO)))
+               goto toobig;
+
+       /* SONMP */
+       if (!(
+             /* Our IP address */
+             POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)) &&
+             /* Segment on three bytes, we don't have slots, so we
+                skip the first two bytes */
+             POKE_UINT16(0) &&
+             POKE_UINT8(if_nametoindex(hardware->h_ifname)) &&
+             POKE_UINT8(1) &&  /* Chassis: Other */
+             POKE_UINT8(12) && /* Back: Ethernet, Fast Ethernet and Gigabit */
+             POKE_UINT8(SONMP_TOPOLOGY_NEW) && /* Should work. We have no state */
+             POKE_UINT8(1) &&  /* Links: Dunno what it is */
+             POKE_SAVE(end)))
+               goto toobig;
 
        if (write((hardware->h_raw_real > 0) ? hardware->h_raw_real :
-               hardware->h_raw, &frame, sizeof(struct sonmp)) == -1) {
+               hardware->h_raw, packet, end - packet) == -1) {
                LLOG_WARN("unable to send packet on real device for %s",
                           hardware->h_ifname);
+               free(packet);
                return ENETDOWN;
        }
 
-       frame.llc.protoid = htons(LLC_PID_SONMP_FLATNET);
-       frame.llc.ether.dhost[ETH_ALEN-1] = 1;
+       POKE_RESTORE(pos_pid);  /* Modify LLC PID */
+       POKE_UINT16(LLC_PID_SONMP_FLATNET);
+       POKE_RESTORE(packet);   /* Go to the beginning */
+       PEEK_DISCARD(ETH_ALEN - 1); /* Modify the last byte of the MAC address */
+       POKE_UINT8(1);
 
        if (write((hardware->h_raw_real > 0) ? hardware->h_raw_real :
-               hardware->h_raw, &frame, sizeof(struct sonmp)) == -1) {
+               hardware->h_raw, packet, end - packet) == -1) {
                LLOG_WARN("unable to send second SONMP packet on real device for %s",
                           hardware->h_ifname);
+               free(packet);
                return ENETDOWN;
        }
 
+       free(packet);
        hardware->h_tx_cnt++;
        return 0;
+ toobig:
+       free(packet);
+       return -1;
 }
 
 int
@@ -229,11 +267,13 @@ sonmp_decode(struct lldpd *cfg, char *frame, int s,
     struct lldpd_hardware *hardware,
     struct lldpd_chassis **newchassis, struct lldpd_port **newport)
 {
-       struct sonmp *f;
        const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR;
        struct lldpd_chassis *chassis;
        struct lldpd_port *port;
-       int i;
+       int length, i;
+       u_int8_t *pos;
+       u_int8_t seg[3], rchassis;
+       struct in_addr address;
 
        if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
                LLOG_WARN("failed to allocate remote chassis");
@@ -248,19 +288,21 @@ sonmp_decode(struct lldpd *cfg, char *frame, int s,
        TAILQ_INIT(&port->p_vlans);
 #endif
 
-       if (s < sizeof(struct sonmp)) {
-               LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
+       length = s;
+       pos = (u_int8_t*)frame;
+       if (length < SONMP_SIZE) {
+               LLOG_WARNX("too short SONMP frame received on %s", hardware->h_ifname);
                goto malformed;
        }
-       f = (struct sonmp *)frame;
-       if (memcmp(f->llc.ether.dhost, mcastaddr,
-               sizeof(mcastaddr)) != 0) {
+       if (PEEK_CMP(mcastaddr, sizeof(mcastaddr)) != 0)
                /* There is two multicast address. We just handle only one of
                 * them. */
                goto malformed;
-       }
-       if (f->llc.protoid != htons(LLC_PID_SONMP_HELLO)) {
-               LLOG_DEBUG("incorrect LLC protocol ID received on %s",
+       /* We skip to LLC PID */
+       PEEK_DISCARD(ETH_ALEN); PEEK_DISCARD_UINT16;
+       PEEK_DISCARD(6);
+       if (PEEK_UINT16 != LLC_PID_SONMP_HELLO) {
+               LLOG_DEBUG("incorrect LLC protocol ID received for SONMP on %s",
                    hardware->h_ifname);
                goto malformed;
        }
@@ -273,14 +315,17 @@ sonmp_decode(struct lldpd *cfg, char *frame, int s,
        }
        chassis->c_id_len = sizeof(struct in_addr) + 1;
        chassis->c_id[0] = 1;
-       memcpy(chassis->c_id + 1, &f->addr, sizeof(struct in_addr));
-       if (asprintf(&chassis->c_name, "%s", inet_ntoa(f->addr)) == -1) {
+       PEEK_BYTES(&address, sizeof(struct in_addr));
+       memcpy(chassis->c_id + 1, &address, sizeof(struct in_addr));
+       if (asprintf(&chassis->c_name, "%s", inet_ntoa(address)) == -1) {
                LLOG_WARNX("unable to write chassis name for %s",
                    hardware->h_ifname);
                goto malformed;
        }
+       PEEK_BYTES(seg, sizeof(seg));
+       rchassis = PEEK_UINT8;
        for (i=0; sonmp_chassis_types[i].type != 0; i++) {
-               if (sonmp_chassis_types[i].type == f->chassis)
+               if (sonmp_chassis_types[i].type == rchassis)
                        break;
        }
        if (asprintf(&chassis->c_descr, "%s",
@@ -289,36 +334,36 @@ sonmp_decode(struct lldpd *cfg, char *frame, int s,
                    hardware->h_ifname);
                goto malformed;
        }
-       memcpy(&chassis->c_mgmt, &f->addr, sizeof(struct in_addr));
+       memcpy(&chassis->c_mgmt, &address, sizeof(struct in_addr));
        chassis->c_ttl = LLDPD_TTL;
 
        port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL;
        if (asprintf(&port->p_id, "%02x-%02x-%02x",
-               f->seg[0], f->seg[1], f->seg[2]) == -1) {
+               seg[0], seg[1], seg[2]) == -1) {
                LLOG_WARN("unable to allocate memory for port id on %s",
                    hardware->h_ifname);
                goto malformed;
        }
        port->p_id_len = strlen(port->p_id);
 
-       if ((f->seg[0] == 0) && (f->seg[1] == 0)) {
+       /* Port description depend on the number of segments */
+       if ((seg[0] == 0) && (seg[1] == 0)) {
                if (asprintf(&port->p_descr, "port %d",
-                       *(u_int8_t *)(&f->seg[2])) == -1) {
+                       seg[2]) == -1) {
                        LLOG_WARNX("unable to write port description for %s",
                            hardware->h_ifname);
                        goto malformed;
                }
-       } else if (f->seg[0] == 0) {
+       } else if (seg[0] == 0) {
                if (asprintf(&port->p_descr, "port %d/%d",
-                       *(u_int8_t *)(&f->seg[1]),
-                       *(u_int8_t *)(&f->seg[2])) == -1) {
+                       seg[1], seg[2]) == -1) {
                        LLOG_WARNX("unable to write port description for %s",
                            hardware->h_ifname);
                        goto malformed;
                }
        } else {
                if (asprintf(&port->p_descr, "port %x:%x:%x",
-                       f->seg[0], f->seg[1], f->seg[2]) == -1) {
+                       seg[0], seg[1], seg[2]) == -1) {
                        LLOG_WARNX("unable to write port description for %s",
                            hardware->h_ifname);
                        goto malformed;
index 0b173bc28efe45a5a11404a60bb449203827c20f..f2051ac439b2f3cc05c8e996f2d3183cfc094919 100644 (file)
 #define LLC_ORG_NORTEL { 0x00, 0x00, 0x81 }
 #define LLC_PID_SONMP_HELLO 0x01a2
 #define LLC_PID_SONMP_FLATNET 0x01a1
-
-#include "llc.h"
-
-struct sonmp {
-       struct ethllc llc;
-       struct in_addr addr;
-       u_int8_t seg[3];
-       u_int8_t chassis;
-       u_int8_t backplane;
-       u_int8_t state;
-       u_int8_t links;
-} __attribute__ ((__packed__));
+#define SONMP_SIZE (2*ETH_ALEN + sizeof(u_int16_t) + 8)
 
 struct sonmp_chassis {
        int type;