Re: [E1000-eedc] [PATCH 4/9] implementation of IEEE 802.1Qbg in lldpad, part 2

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 9/28/2010 8:10 AM, Jens Osterkamp wrote:
> This is the implementation of the edge control protocol (ECP) and VSI
> discovery protocol (VDP) currently being specified in IEEE 802.1Qbg.
> 
> This implementation extends the infrastructure defined lldpad to send and
> receive ECP frames with a new (yet to be defined) ethertype.
> Received frames are validated and analyzed before the content is handed to the
> upper layer protocol (ULP, VDP in this case) for further processing. Frames
> to be transmitted are compiled from VSI (guest interface) profiles registered
> on a interface.
> Reception and transmission of ECP frames is controlled by RX and TX state
> machines, timeouts are handled timeout functions.
> The patch still contains a lot of debug code to allow low-level protocol
> analysis.
> 
> VDP serves as the upper layer protocol (ULP) for TLVs communicated via the
> ECP protocol.
> For this it registers as a new module in lldpad. The VDP module supports a
> station and a bridge role. As a station, new VSI (virtual station interface)
> profiles can be registered to the VDP module using lldptool or libvirt.
> These profiles are then announced to an adjacent switch. Transmitted profiles
> are processed to the desired state by the VDP station state machine.
> As a bridge, the VDP module waits for new profiles received in TLVs by ECP.
> The received profiles are processed to the desired state by a VDP bridge
> state machine.
> 
> VDP module parameters are stored in the "vdp" section under the appropriate
> interface.
> 
> The patch still contains a lot of debug code to allow analysis of VDP
> protocol behavior.
> 
> Signed-off-by: Jens Osterkamp <jens@xxxxxxxxxxxxxxxxxx>
> ---
>  Makefile.am        |   10 +-
>  ecp/ecp.c          |   79 ++++
>  ecp/ecp.h          |  101 +++++
>  ecp/ecp_rx.c       |  599 ++++++++++++++++++++++++++
>  ecp/ecp_tx.c       |  494 ++++++++++++++++++++++
>  include/lldp.h     |    1 +
>  include/lldp_evb.h |    6 +
>  include/lldp_vdp.h |  159 +++++++
>  lldp/l2_packet.h   |    2 +
>  lldp/ports.h       |   11 +-
>  lldp_vdp.c         | 1185 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  lldpad.c           |    2 +
>  12 files changed, 2642 insertions(+), 7 deletions(-)
>  create mode 100644 ecp/ecp.c
>  create mode 100644 ecp/ecp.h
>  create mode 100644 ecp/ecp_rx.c
>  create mode 100644 ecp/ecp_tx.c
>  create mode 100644 include/lldp_vdp.h
>  create mode 100644 lldp_vdp.c
> 
> diff --git a/Makefile.am b/Makefile.am
> index d59a6fa..4b69389 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -56,6 +56,8 @@ $(lldpad_include_HEADERS) $(noinst_HEADERS) \
>  lldp/ports.c lldp/agent.c lldp/l2_packet_linux.c lldp/tx.c \
>  lldp/rx.c lldp/agent.h lldp/l2_packet.h lldp/mibdata.h lldp/ports.h \
>  lldp/states.h \
> +ecp/ecp.c ecp/ecp_tx.c \
> +ecp/ecp_rx.c \
>  include/lldp.h include/lldp_mod.h \
>  lldp_dcbx.c include/lldp_dcbx.h tlv_dcbx.c include/tlv_dcbx.h \
>  lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \
> @@ -67,16 +69,16 @@ lldp_tlv.c include/lldp_tlv.h \
>  lldp_basman.c include/lldp_basman.h    \
>  lldp_med.c include/lldp_med.h \
>  lldp_8023.c include/lldp_8023.h \
> -lldp_evb.c include/lldp_evb.h
> -
> -
> +lldp_evb.c include/lldp_evb.h \
> +lldp_vdp.c include/lldp_vdp.h
> 
>  dcbtool_SOURCES = dcbtool.c clif.c dcbtool_cmds.c parse_cli.l \
>  $(lldpad_include_HEADERS) $(noinst_HEADERS)
> 
>  lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \
>  lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \
> -lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) $(noinst_HEADERS)
> +lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) \
> +$(noinst_HEADERS)
> 
>  nltest_SOURCES = nltest.c nltest.h
> 
> diff --git a/ecp/ecp.c b/ecp/ecp.c
> new file mode 100644
> index 0000000..20a8e66
> --- /dev/null
> +++ b/ecp/ecp.c
> @@ -0,0 +1,79 @@
> +/*******************************************************************************
> +
> +  implementation of ECP according to 802.1Qbg
> +  (c) Copyright IBM Corp. 2010
> +
> +  Author(s): Jens Osterkamp <jens@xxxxxxxxxxxxxxxxxx>
> +
> +  This program is free software; you can redistribute it and/or modify it
> +  under the terms and conditions of the GNU General Public License,
> +  version 2, as published by the Free Software Foundation.
> +
> +  This program is distributed in the hope it will be useful, but WITHOUT
> +  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> +  more details.
> +
> +  You should have received a copy of the GNU General Public License along with
> +  this program; if not, write to the Free Software Foundation, Inc.,
> +  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> +
> +  The full GNU General Public License is included in this distribution in
> +  the file called "COPYING".
> +
> +*******************************************************************************/
> +
> +#include <net/if.h>
> +#include <sys/queue.h>
> +#include <sys/socket.h>
> +#include <sys/ioctl.h>
> +#include <sys/utsname.h>
> +#include <linux/if_bridge.h>
> +#include "lldp.h"
> +#include "lldp_evb.h"
> +#include "lldp_vdp.h"
> +#include "messages.h"
> +#include "config.h"
> +#include "common.h"
> +#include "lldp/l2_packet.h"
> +#include "ecp/ecp.h"
> +
> +/* ecp_init - initialize ecp module
> + * @ifname: interface for which the module is initialized
> + *
> + * returns 0 on success, -1 on error
> + *
> + * finds the port to the interface name, sets up the receive handle for
> + * incoming ecp frames and initializes the ecp rx and tx state machines.
> + * should usually be called when a successful exchange of EVB TLVs has been
> + * made and ECP and VDP protocols are supported by both sides.
> + */
> +int ecp_init(char *ifname)
> +{
> +       struct vdp_data *vd;
> +
> +       printf("%s(%i): starting ECP for if %s !\n", __func__, __LINE__, ifname);
> +
> +       vd = vdp_data(ifname);
> +
> +       if (!vd) {
> +               printf("%s(%i): unable to find vd %s ! \n", __func__, __LINE__, ifname);
> +               goto fail;
> +       }
> +
> +       vd->ecp.l2 = l2_packet_init(vd->ifname, NULL, ETH_P_ECP,
> +               ecp_rx_ReceiveFrame, vd, 1);
> +       if (!vd->ecp.l2) {
> +               printf("ERROR: Failed to open register layer 2 access to "
> +                       "ETH_P_ECP\n");
> +               goto fail;
> +       }
> +
> +       ecp_tx_run_sm(vd);
> +       ecp_rx_run_sm(vd);
> +
> +       return 0;
> +
> +fail:
> +       return -1;
> +}
> diff --git a/ecp/ecp.h b/ecp/ecp.h
> new file mode 100644
> index 0000000..cc9ca2b
> --- /dev/null
> +++ b/ecp/ecp.h
> @@ -0,0 +1,101 @@
> +/*******************************************************************************
> +
> +  implementation of ECP according to 802.1Qbg
> +  (c) Copyright IBM Corp. 2010
> +
> +  Author(s): Jens Osterkamp <jens@xxxxxxxxxxxxxxxxxx>
> +
> +  This program is free software; you can redistribute it and/or modify it
> +  under the terms and conditions of the GNU General Public License,
> +  version 2, as published by the Free Software Foundation.
> +
> +  This program is distributed in the hope it will be useful, but WITHOUT
> +  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> +  more details.
> +
> +  You should have received a copy of the GNU General Public License along with
> +  this program; if not, write to the Free Software Foundation, Inc.,
> +  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> +
> +  The full GNU General Public License is included in this distribution in
> +  the file called "COPYING".
> +
> +*******************************************************************************/
> +
> +#ifndef _ECP_H
> +#define _ECP_H
> +
> +#include "lldp_mod.h"
> +#include "include/lldp_vdp.h"
> +
> +#define ECP_SUBTYPE                    0x0
> +
> +#define ECP_MAX_RETRIES                        3
> +#define ECP_SEQUENCE_NR_START          0x0
> +
> +#define ECP_TRANSMISSION_TIMER         EVB_RTM(RTE)*EVB_RTG
> +#define ECP_TRANSMISSION_DIVIDER       10000
> +
> +typedef enum {
> +       ECP_REQUEST = 0,
> +       ECP_ACK
> +} ecp_mode;
> +
> +struct ecp {
> +       struct l2_packet_data *l2;
> +       int sequence;
> +       int retries;
> +       int ackReceived;
> +       int ackTimerExpired;
> +       u16 lastSequence;
> +       u16 seqECPDU;
> +       struct portrx rx;
> +       struct porttx tx;
> +       struct portstats stats;
> +};
> +
> +struct ecp_hdr {
> +       u8 oui[3];
> +       u8 pad1;
> +       u16 subtype;
> +       u8 mode;
> +       u16 seqnr;
> +} __attribute__ ((__packed__));
> +
> +enum {
> +       ECP_TX_INIT_TRANSMIT,
> +       ECP_TX_TRANSMIT_ECPDU,
> +       ECP_TX_WAIT_FOR_ACK,
> +       ECP_TX_REQUEST_PDU
> +};
> +
> +static const char *ecp_tx_states[] = {
> +       "ECP_TX_IDLE",
> +       "ECP_TX_INIT_TRANSMIT",
> +       "ECP_TX_TRANSMIT_ECPDU",
> +       "ECP_TX_WAIT_FOR_ACK",
> +       "ECP_TX_REQUEST_PDU"
> +};
> +
> +enum {
> +       ECP_RX_IDLE,
> +       ECP_RX_INIT_RECEIVE,
> +       ECP_RX_RECEIVE_WAIT,
> +       ECP_RX_RECEIVE_ECPDU,
> +       ECP_RX_SEND_ACK,
> +       ECP_RX_RESEND_ACK,
> +};
> +
> +static const char *ecp_rx_states[] = {
> +       "ECP_RX_IDLE",
> +       "ECP_RX_INIT_RECEIVE",
> +       "ECP_RX_RECEIVE_WAIT",
> +       "ECP_RX_RECEIVE_ECPDU",
> +       "ECP_RX_SEND_ACK",
> +       "ECP_RX_RESEND_ACK",
> +};
> +
> +void ecp_rx_ReceiveFrame(void *, unsigned int, const u8 *, size_t );
> +
> +#endif /* _ECP_H */
> diff --git a/ecp/ecp_rx.c b/ecp/ecp_rx.c
> new file mode 100644
> index 0000000..3a5dff7
> --- /dev/null
> +++ b/ecp/ecp_rx.c
> @@ -0,0 +1,599 @@
> +/*******************************************************************************
> +
> +  implementation of ECP according to 802.1Qbg
> +  (c) Copyright IBM Corp. 2010
> +
> +  Author(s): Jens Osterkamp <jens@xxxxxxxxxxxxxxxxxx>
> +
> +  This program is free software; you can redistribute it and/or modify it
> +  under the terms and conditions of the GNU General Public License,
> +  version 2, as published by the Free Software Foundation.
> +
> +  This program is distributed in the hope it will be useful, but WITHOUT
> +  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> +  more details.
> +
> +  You should have received a copy of the GNU General Public License along with
> +  this program; if not, write to the Free Software Foundation, Inc.,
> +  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> +
> +  The full GNU General Public License is included in this distribution in
> +  the file called "COPYING".
> +
> +*******************************************************************************/
> +
> +#include "dcb_osdep.h"
> +#include "lldp/ports.h"
> +#include "lldp/l2_packet.h"
> +#include "messages.h"
> +#include "lldp.h"
> +#include "lldpad.h"
> +#include "lldp_mod.h"
> +#include "clif_msgs.h"
> +#include "lldp_mand.h"
> +#include "lldp_vdp.h"
> +
> +/* ecp_rx_Initialize - initializes the ecp rx state machine
> + * @vd: vd for the state machine
> + *
> + * no return value
> + *
> + * initialize some variables, get rid of old frame if necessary
> + */
> +void ecp_rx_Initialize(struct vdp_data *vd)
> +{
> +       vd->ecp.rx.rcvFrame = false;
> +       vd->ecp.rx.badFrame = false;
> +
> +       vd->ecp.ackReceived = 0;
> +
> +       if (vd->ecp.rx.framein) {
> +               free(vd->ecp.rx.framein);
> +               vd->ecp.rx.framein = NULL;
> +       }
> +       vd->ecp.rx.sizein = 0;
> +
> +       return;
> +}
> +
> +/* ecp_rx_freeFrame - free up received frame
> + * @vd: vd for the state machine
> + *
> + * no return value
> + *
> + * frees up an old received frame, set pointer to NULL and size to 0.
> + */
> +void ecp_rx_freeFrame(struct vdp_data *vd)
> +{
> +       free(vd->ecp.rx.framein);
> +       vd->ecp.rx.framein = NULL;
> +       vd->ecp.rx.sizein = 0;
> +}
> +
> +/* ecp_print_framein - print raw received frame
> + * @vd: vd for the state machine
> + *
> + * no return value
> + *
> + * prints out a raw version of a received frame. useful for low-level protocol
> + * debugging.
> + */
> +void ecp_print_framein(struct vdp_data *vd)
> +{
> +       int i;
> +
> +       for (i=0; i < vd->ecp.rx.sizein; i++) {
> +               printf("%02x ", vd->ecp.rx.framein[i]);
> +               if (!((i+1) % 16))
> +                       printf("\n");
> +       }
> +
> +       printf("\n");
> +}
> +
> +/* ecp_rx_SendAckFrame - send ack frame
> + * @vd: port used by ecp
> + *
> + * currently always returns 0
> + *
> + * copies current received frame over to frame out, fills in address of this
> + * port and set mode field to ACK. used by ecp_rx_send_ack_frame.
> + */
> +int ecp_rx_SendAckFrame(struct vdp_data *vd)
> +{
> +       u16 tlv_offset = 0;
> +       struct ecp_hdr *ecp_hdr;
> +       struct l2_ethhdr *hdr;
> +       u8 own_addr[ETH_ALEN];
> +
> +       printf("%s(%i)-%s: acking frame \n", __func__, __LINE__, vd->ifname);
> +
> +       assert(vd->ecp.rx.framein && vd->ecp.rx.sizein);
> +
> +       /* copy over to frameout */
> +       vd->ecp.tx.frameout = (u8 *)malloc(ETH_FRAME_LEN);
> +       memcpy(vd->ecp.tx.frameout, vd->ecp.rx.framein, vd->ecp.rx.sizein);
> +       vd->ecp.tx.sizeout = vd->ecp.rx.sizein;
> +
> +       /* use my own addr to send ACK */
> +       hdr = (struct l2_ethhdr *)vd->ecp.tx.frameout;
> +       l2_packet_get_own_src_addr(vd->ecp.l2,(u8 *)&own_addr);
> +       memcpy(hdr->h_source, &own_addr, ETH_ALEN);
> +
> +       tlv_offset = sizeof(struct l2_ethhdr);
> +       ecp_hdr = (struct ecp_hdr *)&vd->ecp.tx.frameout[tlv_offset];
> +       ecp_hdr->mode = ECP_ACK;
> +
> +       return 0;
> +}
> +
> +/* ecp_rx_send_ack_frame - send out ack frame for received frame
> + * @vd: vd for the state machine
> + *
> + * no return value
> + *
> + * creates an ack frame for a just received frame, prints the about to be
> + * sent frame and finally transmits it.
> + */
> +void ecp_rx_send_ack_frame(struct vdp_data *vd)
> +{
> +       ecp_rx_SendAckFrame(vd);
> +
> +       ecp_print_frameout(vd);
> +
> +       ecp_txFrame(vd);
> +
> +       return;
> +}
> +
> +/* ecp_rx_ReceiveFrame - receive ecp frame
> + * @ctx: rx callback context, struct vd * in this case
> + * @ifindex: index of interface
> + * @buf: buffer which contains the frame just received
> + * @len: size of buffer (frame)
> + *
> + * no return value
> + *
> + * creates a local copy of the buffer and checks the header. keeps some
> + * statistics about ecp frames. Checks if it is a request or an ack frame and branches
> + * to ecp rx or ecp tx state machine.
> + */
> +void ecp_rx_ReceiveFrame(void *ctx, unsigned int ifindex, const u8 *buf, size_t len)
> +{
> +
> +       struct vdp_data *vd = NULL;
> +       struct port *port;
> +       u8  frame_error = 0;
> +       u16 tlv_offset;
> +       struct l2_ethhdr *hdr;
> +       struct l2_ethhdr example_hdr,*ex;
> +       struct ecp_hdr *ecp_hdr;
> +
> +       if (ctx)
> +               vd = (struct vdp_data *)ctx;
> +
> +       port = port_find_by_name(vd->ifname);
> +
> +       printf("%s(%i)-%s: received packet with size %i\n", __func__, __LINE__,
> +              vd->ifname, (int) len);
> +
> +       if (port->adminStatus == disabled || port->adminStatus == enabledTxOnly)
> +               return;
> +
> +       if (vd->ecp.rx.framein &&
> +           vd->ecp.rx.sizein == len &&
> +           (memcmp(buf, vd->ecp.rx.framein, len) == 0)) {
> +               vd->ecp.stats.statsFramesInTotal++;
> +               return;
> +       }
> +
> +       if (vd->ecp.rx.framein)
> +               free(vd->ecp.rx.framein);
> +
> +       vd->ecp.rx.framein = (u8 *)malloc(len);
> +       if (vd->ecp.rx.framein == NULL) {
> +               printf("ERROR - could not allocate memory for rx'ed frame\n");
> +               return;
> +       }
> +       memcpy(vd->ecp.rx.framein, buf, len);
> +
> +       vd->ecp.rx.sizein = (u16)len;
> +       ex = &example_hdr;
> +       memcpy(ex->h_dest, multi_cast_source, ETH_ALEN);
> +       ex->h_proto = htons(ETH_P_ECP);
> +       hdr = (struct l2_ethhdr *)vd->ecp.rx.framein;
> +
> +       if ((memcmp(hdr->h_dest,ex->h_dest, ETH_ALEN) != 0)) {
> +               printf("ERROR multicast address error in incoming frame. "
> +                       "Dropping frame.\n");
> +               frame_error++;
> +               free(vd->ecp.rx.framein);
> +               vd->ecp.rx.framein = NULL;
> +               vd->ecp.rx.sizein = 0;
> +               return;
> +       }
> +
> +       if (hdr->h_proto != example_hdr.h_proto) {
> +               printf("ERROR Ethertype not ECP ethertype but ethertype "
> +                       "'%x' in incoming frame.\n", htons(hdr->h_proto));
> +               frame_error++;
> +               free(vd->ecp.rx.framein);
> +               vd->ecp.rx.framein = NULL;
> +               vd->ecp.rx.sizein = 0;
> +               return;
> +       }
> +
> +       if (!frame_error) {
> +               vd->ecp.stats.statsFramesInTotal++;
> +               vd->ecp.rx.rcvFrame = 1;
> +       }
> +
> +       tlv_offset = sizeof(struct l2_ethhdr);
> +
> +       ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset];
> +
> +       vd->ecp.seqECPDU = ntohs(ecp_hdr->seqnr);
> +
> +       switch(ecp_hdr->mode) {
> +       case ECP_REQUEST:
> +               vd->ecp.ackReceived = false;
> +               ecp_print_framein(vd);
> +               ecp_rx_run_sm(vd);
> +               break;
> +       case ECP_ACK:
> +               vd->ecp.ackReceived = true;
> +               printf("%s(%i)-%s: received ack frame \n", __func__, __LINE__, vd->ifname);
> +               ecp_print_framein(vd);
> +               ecp_tx_run_sm(vd);
> +               vd->ecp.ackReceived = false;
> +               break;
> +       default:
> +               printf("ERROR: unknown mode %i!\n", ecp_hdr->mode);
> +               return;
> +       }
> +
> +       ecp_rx_freeFrame(vd);
> +}
> +
> +/* ecp_rx_validateFrame - validates received frame
> + * @vd: vdp_data used by ecp
> + *
> + * no return value
> + *
> + * checks wether received frame has correct subtype and mode
> + */
> +
> +void ecp_rx_validateFrame(struct vdp_data *vd)
> +{
> +       u16 tlv_offset = 0;
> +       struct ecp_hdr *ecp_hdr;
> +
> +       printf("%s(%i)-%s: validating frame \n", __func__, __LINE__, vd->ifname);
> +
> +       assert(vd->ecp.rx.framein && vd->ecp.rx.sizein);
> +
> +       tlv_offset = sizeof(struct l2_ethhdr);
> +
> +       ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset];
> +
> +       printf("%s(%i)-%s: ecp packet with subtype %04x, mode %02x, sequence nr %04x\n",
> +              __func__, __LINE__, vd->ifname, ecp_hdr->subtype, ecp_hdr->mode, ntohs(ecp_hdr->seqnr));
> +
> +       if (ecp_hdr->subtype != ECP_SUBTYPE) {
> +               printf("ERROR: unknown subtype !\n");
> +               return;
> +       }
> +
> +       if ((ecp_hdr->oui[0] != 0x0) || (ecp_hdr->oui[1] != 0x1b) ||
> +               (ecp_hdr->oui[2] != 0x3f)) {
> +               printf("ERROR: incorrect OUI 0x%02x%02x%02x !\n",
> +                      ecp_hdr->oui[0], ecp_hdr->oui[1], ecp_hdr->oui[2]);
> +               return;
> +       }
> +
> +       switch(ecp_hdr->mode) {
> +       case ECP_REQUEST:
> +               break;
> +       case ECP_ACK:
> +               break;
> +       default:
> +               printf("ERROR: unknown mode %i!\n", ecp_hdr->mode);
> +               return;
> +       }
> +
> +       /* FIXME: also done in ecp_rx_ReceiveFrame,
> +        * are both necessary ? */
> +       vd->ecp.seqECPDU = ntohs(ecp_hdr->seqnr);
> +}
> +
> +/* ecp_rx_validate_frame - wrapper around ecp_rx_validateFrame
> + * @vd: currently used port
> + *
> + * no return value
> + *
> + * sets rcvFrame to false and validates frame. used in ECP_RX_RECEIVE_ECPDU
> + * state of ecp_rx_run_sm
> + */
> +void ecp_rx_validate_frame(struct vdp_data *vd)
> +{
> +       vd->ecp.rx.rcvFrame = false;
> +       ecp_rx_validateFrame(vd);
> +       return;
> +}
> +
> +/* ecp_rx_ProcessFrame - process received ecp frames
> + * @vd: currently used port
> + *
> + * no return value
> + *
> + * walks through the packed vsi tlvs in an ecp frame, extracts them
> + * and passes them to the VDP ULP with vdp_indicate.
> + */
> +void ecp_rx_ProcessFrame(struct vdp_data *vd)
> +{
> +       u16 tlv_cnt = 0;
> +       u8  tlv_type = 0;
> +       u16 tlv_length = 0;
> +       u16 tlv_offset = 0;
> +       u16 *tlv_head_ptr = NULL;
> +       u8  frame_error = 0;
> +       bool tlv_stored     = false;
> +       struct ecp_hdr *ecp_hdr;
> +
> +       printf("%s(%i)-%s: processing frame \n", __func__, __LINE__, vd->ifname);
> +
> +       assert(vd->ecp.rx.framein && vd->ecp.rx.sizein);
> +
> +       tlv_offset = sizeof(struct l2_ethhdr);
> +
> +       ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.framein[tlv_offset];
> +
> +       printf("%s(%i)-%s: ecp packet with subtype %04x, mode %02x, sequence nr %04x\n",
> +              __func__, __LINE__, vd->ifname, ecp_hdr->subtype, ecp_hdr->mode, ntohs(ecp_hdr->seqnr));
> +
> +       if (ecp_hdr->mode == ECP_ACK)
> +               return;
> +
> +       /* processing of VSI_TLVs starts here */
> +
> +       tlv_offset += sizeof(struct ecp_hdr);
> +
> +       do {
> +               tlv_cnt++;
> +               if (tlv_offset > vd->ecp.rx.sizein) {
> +                       printf("%s(%i)-%s: ERROR: Frame overrun! tlv_offset %i, sizein %i cnt %i\n",
> +                              __func__, __LINE__, vd->ifname, tlv_offset, vd->ecp.rx.sizein, tlv_cnt);
> +                       frame_error++;
> +                       goto out;
> +               }
> +
> +               tlv_head_ptr = (u16 *)&vd->ecp.rx.framein[tlv_offset];
> +               tlv_length = htons(*tlv_head_ptr) & 0x01FF;
> +               tlv_type = (u8)(htons(*tlv_head_ptr) >> 9);
> +
> +               u16 tmp_offset = tlv_offset + tlv_length;
> +               if (tmp_offset > vd->ecp.rx.sizein) {
> +                       printf("ERROR: Frame overflow error: offset=%d, "
> +                               "rx.size=%d \n", tmp_offset, vd->ecp.rx.sizein);
> +                       frame_error++;
> +                       goto out;
> +               }
> +
> +               u8 *info = (u8 *)&vd->ecp.rx.framein[tlv_offset +
> +                                       sizeof(*tlv_head_ptr)];
> +
> +               struct unpacked_tlv *tlv = create_tlv();
> +
> +               if (!tlv) {
> +                       printf("ERROR: Failed to malloc space for "
> +                               "incoming TLV. \n");
> +                       goto out;
> +               }
> +
> +               if ((tlv_length == 0) && (tlv->type != TYPE_0)) {
> +                               printf("ERROR: tlv_length == 0\n");
> +                               free_unpkd_tlv(tlv);
> +                               goto out;
> +               }
> +
> +               tlv->type = tlv_type;
> +               tlv->length = tlv_length;
> +               tlv->info = (u8 *)malloc(tlv_length);
> +               if (tlv->info) {
> +                       memset(tlv->info,0, tlv_length);
> +                       memcpy(tlv->info, info, tlv_length);
> +               } else {
> +                       printf("ERROR: Failed to malloc space for incoming "
> +                               "TLV info \n");
> +                       free_unpkd_tlv(tlv);
> +                       goto out;
> +               }
> +
> +               /* Validate the TLV */
> +               tlv_offset += sizeof(*tlv_head_ptr) + tlv_length;
> +
> +               if (tlv->type == TYPE_127) { /* private TLV */
> +                       /* give VSI TLV to VDP */
> +                       if (!vdp_indicate(vd, tlv, ecp_hdr->mode))
> +                               tlv_stored = true;
> +                       else {
> +                               /* TODO: put it in a list and try again later until
> +                                * timer and retries have expired */
> +                               tlv_stored = false;
> +                       }
> +               }
> +
> +               if ((tlv->type != TYPE_0) && !tlv_stored) {
> +                       printf("\n%s: allocated TLV (%u) "
> +                                  " was not stored! (%p)\n", __func__, tlv->type,
> +                                  tlv);
> +                       tlv = free_unpkd_tlv(tlv);
> +                       vd->ecp.stats.statsTLVsUnrecognizedTotal++;
> +               }
> +
> +               tlv = NULL;
> +               tlv_stored = false;
> +       } while(tlv_type != 0);
> +
> +out:
> +       if (frame_error) {
> +               vd->ecp.stats.statsFramesDiscardedTotal++;
> +               vd->ecp.stats.statsFramesInErrorsTotal++;
> +               vd->ecp.rx.badFrame = true;
> +       }
> +
> +       return;
> +}
> +
> +/* ecp_rx_change_state - changes the ecp rx sm state
> + * @vd: currently used port
> + * @newstate: new state for the sm
> + *
> + * no return value
> + *
> + * checks state transistion for consistency and finally changes the state of
> + * the profile.
> + */
> +void ecp_rx_change_state(struct vdp_data *vd, u8 newstate)
> +{
> +       switch(newstate) {
> +       case ECP_RX_IDLE:
> +               break;
> +       case ECP_RX_INIT_RECEIVE:
> +               break;
> +       case ECP_RX_RECEIVE_WAIT:
> +               assert((vd->ecp.rx.state == ECP_RX_INIT_RECEIVE) ||
> +                      (vd->ecp.rx.state == ECP_RX_IDLE) ||
> +                      (vd->ecp.rx.state == ECP_RX_SEND_ACK) ||
> +                      (vd->ecp.rx.state == ECP_RX_RESEND_ACK));
> +               break;
> +       case ECP_RX_RECEIVE_ECPDU:
> +               assert(vd->ecp.rx.state == ECP_RX_RECEIVE_WAIT);
> +               break;
> +       case ECP_RX_SEND_ACK:
> +               assert(vd->ecp.rx.state == ECP_RX_RECEIVE_ECPDU);
> +               break;
> +       case ECP_RX_RESEND_ACK:
> +               assert(vd->ecp.rx.state == ECP_RX_RECEIVE_ECPDU);
> +               break;
> +       default:
> +               printf("ERROR: The ECP_RX State Machine is broken!\n");
> +               log_message(MSG_ERR_RX_SM_INVALID, "%s", vd->ifname);
> +       }
> +
> +       printf("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
> +              vd->ifname, ecp_rx_states[vd->ecp.rx.state], ecp_rx_states[newstate]);
> +
> +       vd->ecp.rx.state = newstate;
> +}
> +
> +/* ecp_set_rx_state - sets the ecp rx sm state
> + * @vd: currently used port
> + *
> + * returns true or false
> + *
> + * switches the state machine to the next state depending on the input
> + * variables. returns true or false depending on wether the state machine
> + * can be run again with the new state or can stop at the current state.
> + */
> +bool ecp_set_rx_state(struct vdp_data *vd)
> +{
> +       struct port *port = port_find_by_name(vd->ifname);
> +
> +       if (port->portEnabled == false) {
> +               ecp_rx_change_state(vd, ECP_RX_IDLE);
> +       }
> +
> +       switch(vd->ecp.rx.state) {
> +       case ECP_RX_IDLE:
> +               if (port->portEnabled == true) {
> +                       ecp_rx_change_state(vd, ECP_RX_INIT_RECEIVE);
> +                       return true;
> +               }
> +               return false;
> +       case ECP_RX_INIT_RECEIVE:
> +               if ((port->adminStatus == enabledRxTx) ||
> +                       (port->adminStatus == enabledRxOnly)) {
> +                       ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT);
> +                       return true;
> +               }
> +               return false;
> +       case ECP_RX_RECEIVE_WAIT:
> +               if ((port->adminStatus == disabled) ||
> +                       (port->adminStatus == enabledTxOnly)) {
> +                       ecp_rx_change_state(vd, ECP_RX_IDLE);
> +                       return true;
> +               }
> +               if (vd->ecp.rx.rcvFrame == true) {
> +                       ecp_rx_change_state(vd, ECP_RX_RECEIVE_ECPDU);
> +                       return true;
> +               }
> +               return false;
> +       case ECP_RX_RECEIVE_ECPDU:
> +               if (vd->ecp.seqECPDU == vd->ecp.lastSequence) {
> +                       printf("%s(%i):-(%s) seqECPDU %x, lastSequence %x\n", __func__, __LINE__,
> +                              vd->ifname, vd->ecp.seqECPDU, vd->ecp.lastSequence);
> +                       ecp_rx_change_state(vd, ECP_RX_RESEND_ACK);
> +                       return true;
> +               }
> +               if (vd->ecp.seqECPDU != vd->ecp.lastSequence) {
> +                       ecp_rx_change_state(vd, ECP_RX_RESEND_ACK);
> +                       return true;
> +               }
> +               return false;
> +       case ECP_RX_SEND_ACK:

How can we get to this state? Did I miss something or could the ECP_RX_SEND_ACK and ECP_RX_RESEND_ACK states be combined or ECP_RX_SEND_ACK removed outright?

> +               ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT);
> +               return false;
> +       case ECP_RX_RESEND_ACK:
> +               ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT);
> +               return false;
> +       default:
> +               printf("ERROR: The ECP_RX State Machine is broken!\n");
> +               log_message(MSG_ERR_RX_SM_INVALID, "%s", vd->ifname);
> +               return false;
> +       }
> +}
> +
> +/* ecp_rx_run_sm - state machine for ecp rx
> + * @vd: currently used port
> + *
> + * no return value
> + *
> + * runs the state machine for ecp rx.
> + */
> +void ecp_rx_run_sm(struct vdp_data *vd)
> +{
> +       ecp_set_rx_state(vd);
> +       do {
> +               printf("%s(%i)-%s: ecp_rx - %s\n", __func__, __LINE__,
> +                      vd->ifname, ecp_rx_states[vd->ecp.tx.state]);
> +
> +               switch(vd->ecp.rx.state) {
> +               case ECP_RX_IDLE:
> +                       break;
> +               case ECP_RX_INIT_RECEIVE:
> +                       ecp_rx_Initialize(vd);
> +                       break;
> +               case ECP_RX_RECEIVE_WAIT:
> +                       break;
> +               case ECP_RX_RECEIVE_ECPDU:
> +                       ecp_rx_validate_frame(vd);
> +                       break;
> +               case ECP_RX_SEND_ACK:
> +                       ecp_rx_ProcessFrame(vd);
> +                       break;
> +               case ECP_RX_RESEND_ACK:
> +                       ecp_rx_ProcessFrame(vd);
> +                       if (!vd->ecp.ackReceived) {
> +                               ecp_rx_send_ack_frame(vd);
> +                               ecp_rx_freeFrame(vd);
> +                       }
> +                       break;
> +               default:
> +                       printf("ERROR: The ECP_RX State Machine is broken!\n");
> +                       log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname);
> +               }
> +       } while (ecp_set_rx_state(vd) == true);
> +
> +}
> diff --git a/ecp/ecp_tx.c b/ecp/ecp_tx.c
> new file mode 100644
> index 0000000..9ddf821
> --- /dev/null
> +++ b/ecp/ecp_tx.c
> @@ -0,0 +1,494 @@
> +/*******************************************************************************
> +
> +  implementation of ECP according to 802.1Qbg
> +  (c) Copyright IBM Corp. 2010
> +
> +  Author(s): Jens Osterkamp <jens@xxxxxxxxxxxxxxxxxx>
> +
> +  This program is free software; you can redistribute it and/or modify it
> +  under the terms and conditions of the GNU General Public License,
> +  version 2, as published by the Free Software Foundation.
> +
> +  This program is distributed in the hope it will be useful, but WITHOUT
> +  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> +  more details.
> +
> +  You should have received a copy of the GNU General Public License along with
> +  this program; if not, write to the Free Software Foundation, Inc.,
> +  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> +
> +  The full GNU General Public License is included in this distribution in
> +  the file called "COPYING".
> +
> +*******************************************************************************/
> +
> +#include "dcb_osdep.h"
> +#include "lldp/ports.h"
> +#include "lldp/l2_packet.h"
> +#include "eloop.h"
> +#include "messages.h"
> +#include "lldpad.h"
> +#include "lldp_tlv.h"
> +#include "lldp_mod.h"
> +#include "lldp_mand.h"
> +#include "lldp_evb.h"
> +#include "include/lldp_vdp.h"
> +
> +void ecp_tx_run_sm(struct vdp_data *);
> +
> +/* ecp_somethingChangedLocal - set flag if port has changed
> + * @vd: port to set the flag for
> + * @mode: mode to set the flag to
> + *
> + * no return value
> + *
> + * set the localChange flag with a mode to indicate a port has changed.
> + * used  to signal an ecpdu needs to be sent out.
> + */
> +
> +void ecp_somethingChangedLocal(struct vdp_data *vd, int mode)
> +{
> +       if (!vd)
> +               return;
> +
> +       vd->ecp.tx.localChange |= mode;
> +
> +       return;
> +}
> +
> +/* ecp_print_frameout - print outbound frame
> + * @vd: currently used port
> + *
> + * no return value
> + *
> + * prints a raw dump of an outbound ecp frame. useful for low-level protocol
> + * debugging.
> + */
> +void ecp_print_frameout(struct vdp_data *vd)
> +{
> +       int i;
> +
> +       for (i=0; i < vd->ecp.tx.sizeout; i++) {
> +               printf("%02x ", vd->ecp.tx.frameout[i]);
> +               if (!((i+1) % 16))
> +                       printf("\n");
> +       }
> +
> +       printf("\n");
> +}
> +
> +/* ecp_build_ECPDU - create an ecp protocol data unit
> + * @vd: currently used port
> + * @mode: mode to create pdu with (REQ or ACK)
> + *
> + * returns true on success, false on failure
> + *
> + * creates the frame header with the ports mac address, the ecp header with REQ
> + * or ACK mode, plus a list of packed TLVs created from the profiles on this
> + * port.
> + */
> +bool ecp_build_ECPDU(struct vdp_data *vd, int mode)
> +{
> +       struct l2_ethhdr eth;
> +       struct ecp_hdr ecp_hdr;
> +       u8  own_addr[ETH_ALEN];
> +       u32 fb_offset = 0;
> +       u32 datasize = 0;
> +       struct packed_tlv *ptlv =  NULL;
> +       struct vsi_profile *p;
> +
> +       if (vd->ecp.tx.frameout) {
> +               free(vd->ecp.tx.frameout);
> +               vd->ecp.tx.frameout = NULL;
> +       }
> +
> +       /* TODO: different multicast address for sending ECP over S-channel (multi_cast_source_s)
> +        * S-channels to implement later */
> +       memcpy(eth.h_dest, multi_cast_source, ETH_ALEN);
> +       l2_packet_get_own_src_addr(vd->ecp.l2,(u8 *)&own_addr);
> +       memcpy(eth.h_source, &own_addr, ETH_ALEN);
> +       eth.h_proto = htons(ETH_P_ECP);
> +       vd->ecp.tx.frameout = (u8 *)malloc(ETH_FRAME_LEN);
> +       if (vd->ecp.tx.frameout == NULL) {
> +               printf("InfoECPDU: Failed to malloc frame buffer \n");
> +               return false;
> +       }
> +       memset(vd->ecp.tx.frameout,0,ETH_FRAME_LEN);
> +       memcpy(vd->ecp.tx.frameout, (void *)&eth, sizeof(struct l2_ethhdr));
> +       fb_offset += sizeof(struct l2_ethhdr);
> +
> +       ecp_hdr.oui[0] = 0x0;
> +       ecp_hdr.oui[1] = 0x1b;
> +       ecp_hdr.oui[2] = 0x3f;
> +
> +       ecp_hdr.pad1 = 0x0;
> +
> +       ecp_hdr.subtype = ECP_SUBTYPE;
> +       switch(mode) {
> +       case VDP_PROFILE_REQ:
> +               ecp_hdr.mode = ECP_REQUEST;
> +               break;
> +       case VDP_PROFILE_ACK:
> +               ecp_hdr.mode = ECP_ACK;
> +               break;
> +       default:
> +               printf("%s(%i): unknown mode for %s !\n", __func__, __LINE__,
> +                      vd->ifname);
> +       }
> +
> +       vd->ecp.lastSequence++;
> +       ecp_hdr.seqnr = htons(vd->ecp.lastSequence);
> +
> +       if ((sizeof(struct ecp_hdr)+fb_offset) > ETH_MAX_DATA_LEN)
> +                               goto error;
> +       memcpy(vd->ecp.tx.frameout+fb_offset, (void *)&ecp_hdr, sizeof(struct ecp_hdr));
> +       datasize += sizeof(struct ecp_hdr);
> +       fb_offset += sizeof(struct ecp_hdr);
> +
> +       /* create packed_tlvs for all profiles on this interface */
> +       LIST_FOREACH(p, &vd->profile_head, profile) {
> +               if(!p) {
> +                       printf("%s(%i): list vd->profile_head empty !\n", __func__, __LINE__);
> +                       continue;
> +               }
> +
> +               if (p->localChange != mode) {
> +                       printf("%s(%i): not sending out profile !\n", __func__, __LINE__);
> +                       continue;
> +               }
> +
> +               ptlv = vdp_gettlv(vd, p);
> +
> +               if(!ptlv) {
> +                       printf("%s(%i): ptlv not created !\n", __func__, __LINE__);
> +                       continue;
> +               }
> +
> +               if (ptlv) {
> +                       if ((ptlv->size+fb_offset) > ETH_MAX_DATA_LEN)
> +                               goto error;
> +                       memcpy(vd->ecp.tx.frameout+fb_offset,
> +                              ptlv->tlv, ptlv->size);
> +                       datasize += ptlv->size;
> +                       fb_offset += ptlv->size;

I think the ptlv needs to be free'd here? Else where is it getting free'd.

> +               }
> +       }
> +
> +       /* The End TLV marks the end of the LLDP PDU */
> +       ptlv = pack_end_tlv();
> +       if (!ptlv || ((ptlv->size + fb_offset) > ETH_MAX_DATA_LEN))
> +               goto error;
> +       memcpy(vd->ecp.tx.frameout + fb_offset, ptlv->tlv, ptlv->size);
> +       datasize += ptlv->size;
> +       fb_offset += ptlv->size;
> +       ptlv =  free_pkd_tlv(ptlv);
> +
> +       if (datasize > ETH_MAX_DATA_LEN)
> +               goto error;
> +
> +       if (datasize < ETH_MIN_DATA_LEN)
> +               vd->ecp.tx.sizeout = ETH_MIN_PKT_LEN;
> +       else
> +               vd->ecp.tx.sizeout = fb_offset;
> +
> +       return true;
> +
> +error:
> +       ptlv = free_pkd_tlv(ptlv);
> +       if (vd->ecp.tx.frameout)
> +               free(vd->ecp.tx.frameout);
> +       vd->ecp.tx.frameout = NULL;
> +       printf("InfoECPDU: packed TLV too large for tx frame\n");
> +       return false;
> +}
> +
> +/* ecp_tx_Initialize - initializes the ecp tx state machine
> + * @vd: currently used port
> + *
> + * no return value
> + *
> + * initializes some variables for the ecp tx state machine.
> + */
> +void ecp_tx_Initialize(struct vdp_data *vd)
> +{
> +       if (vd->ecp.tx.frameout) {
> +               free(vd->ecp.tx.frameout);
> +               vd->ecp.tx.frameout = NULL;
> +       }
> +       vd->ecp.tx.localChange = VDP_PROFILE_REQ;
> +       vd->ecp.lastSequence = ECP_SEQUENCE_NR_START;
> +       vd->ecp.stats.statsFramesOutTotal = 0;
> +       vd->ecp.ackTimerExpired = false;
> +       vd->ecp.retries = 0;
> +
> +       struct port *port = port_find_by_name(vd->ifname);
> +       l2_packet_get_port_state(vd->ecp.l2, (u8 *)&(port->portEnabled));
> +
> +       return;
> +}
> +
> +/* ecp_txFrame - transmit ecp frame
> + * @vd: currently used port
> + *
> + * returns the number of characters sent on success, -1 on failure
> + *
> + * sends out the frame stored in the frameout structure using l2_packet_send.
> + */
> +u8 ecp_txFrame(struct vdp_data *vd)
> +{
> +       int status = 0;
> +
> +       status = l2_packet_send(vd->ecp.l2, (u8 *)&multi_cast_source,
> +               htons(ETH_P_ECP),vd->ecp.tx.frameout,vd->ecp.tx.sizeout);
> +       vd->ecp.stats.statsFramesOutTotal++;
> +
> +       return status;
> +}
> +
> +/* ecp_tx_create_frame - create ecp frame
> + * @vd: currently used port
> + *
> + * no return value
> + *
> + *
> + */
> +void ecp_tx_create_frame(struct vdp_data *vd)
> +{
> +       /* send REQs */
> +       if (vd->ecp.tx.localChange & VDP_PROFILE_REQ) {
> +               printf("%s(%i)-%s: sending REQs\n", __func__, __LINE__, vd->ifname);
> +               ecp_build_ECPDU(vd, VDP_PROFILE_REQ);
> +               ecp_print_frameout(vd);
> +               ecp_txFrame(vd);
> +       }
> +
> +       /* send ACKs */
> +       if (vd->ecp.tx.localChange & VDP_PROFILE_ACK) {
> +               printf("%s(%i)-%s: sending ACKs\n", __func__, __LINE__, vd->ifname);
> +               ecp_build_ECPDU(vd, VDP_PROFILE_ACK);
> +               ecp_print_frameout(vd);
> +               ecp_txFrame(vd);
> +       }
> +
> +       vd->ecp.tx.localChange = 0;
> +       return;
> +}
> +
> +/* ecp_timeout_handler - handles the ack timer expiry
> + * @eloop_data: data structure of event loop
> + * @user_ctx: user context, port here
> + *
> + * no return value
> + *
> + * called when the ECP ack timer has expired. sets a flag and calls the ECP
> + * state machine.
> + */
> +static void ecp_tx_timeout_handler(void *eloop_data, void *user_ctx)
> +{
> +       struct vdp_data *vd;
> +
> +       vd = (struct vdp_data *) user_ctx;
> +
> +       vd->ecp.ackTimerExpired = true;
> +
> +       printf("%s(%i)-%s: timer expired\n", __func__, __LINE__,
> +              vd->ifname);
> +
> +       ecp_tx_run_sm(vd);
> +}
> +
> +/* ecp_tx_stop_ackTimer - stop the ECP ack timer
> + * @vd: currently used port
> + *
> + * returns the number of removed handlers
> + *
> + * stops the ECP ack timer. used when a ack frame for the port has been
> + * received.
> + */
> +static int ecp_tx_stop_ackTimer(struct vdp_data *vd)
> +{
> +       printf("%s(%i)-%s: stopping timer\n", __func__, __LINE__,
> +              vd->ifname);
> +
> +       return eloop_cancel_timeout(ecp_tx_timeout_handler, NULL, (void *) vd);
> +}
> +
> +/* ecp_tx_start_ackTimer - starts the ECP ack timer
> + * @profile: profile to process
> + *
> + * returns 0 on success, -1 on error
> + *
> + * starts the ack timer when a frame has been sent out.
> + */
> +static void ecp_tx_start_ackTimer(struct vdp_data *vd)
> +{
> +       unsigned int secs, usecs;
> +
> +       vd->ecp.ackTimerExpired = false;
> +
> +       secs = ECP_TRANSMISSION_TIMER / ECP_TRANSMISSION_DIVIDER;
> +       usecs = ECP_TRANSMISSION_TIMER % ECP_TRANSMISSION_DIVIDER;
> +
> +       printf("%s(%i)-%s: starting timer\n", __func__, __LINE__,
> +              vd->ifname);
> +
> +       eloop_register_timeout(secs, usecs, ecp_tx_timeout_handler, NULL, (void *) vd);
> +}
> +
> +/* ecp_tx_change_state - changes the ecp tx sm state
> + * @vd: currently used port
> + * @newstate: new state for the sm
> + *
> + * no return value
> + *
> + * checks state transistion for consistency and finally changes the state of
> + * the profile.
> + */
> +static void ecp_tx_change_state(struct vdp_data *vd, u8 newstate)
> +{
> +       switch(newstate) {
> +       case ECP_TX_INIT_TRANSMIT:
> +               break;
> +       case ECP_TX_TRANSMIT_ECPDU:
> +               assert((vd->ecp.tx.state == ECP_TX_INIT_TRANSMIT) ||
> +                      (vd->ecp.tx.state == ECP_TX_WAIT_FOR_ACK) ||
> +                      (vd->ecp.tx.state == ECP_TX_REQUEST_PDU));
> +               break;
> +       case ECP_TX_WAIT_FOR_ACK:
> +               assert(vd->ecp.tx.state == ECP_TX_TRANSMIT_ECPDU);
> +               break;
> +       case ECP_TX_REQUEST_PDU:
> +               assert(vd->ecp.tx.state == ECP_TX_WAIT_FOR_ACK);
> +               break;
> +       default:
> +               printf("ERROR: The ECP_TX State Machine is broken!\n");
> +               log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname);
> +       }
> +
> +       printf("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
> +              vd->ifname, ecp_tx_states[vd->ecp.tx.state], ecp_tx_states[newstate]);
> +
> +       vd->ecp.tx.state = newstate;
> +       return;
> +}
> +
> +/* ecp_set_tx_state - sets the ecp tx sm state
> + * @vd: currently used port
> + *
> + * returns true or false
> + *
> + * switches the state machine to the next state depending on the input
> + * variables. returns true or false depending on wether the state machine
> + * can be run again with the new state or can stop at the current state.
> + */
> +static bool ecp_set_tx_state(struct vdp_data *vd)
> +{
> +       struct port *port = port_find_by_name(vd->ifname);
> +
> +       if (!port) {
> +               printf("%s(%i): port not found !\n", __func__, __LINE__);
> +               return 0;
> +       }
> +
> +       if ((port->portEnabled == false) && (port->prevPortEnabled == true)) {
> +               printf("set_tx_state: port was disabled\n");
> +               ecp_tx_change_state(vd, ECP_TX_INIT_TRANSMIT);
> +       }
> +       port->prevPortEnabled = port->portEnabled;
> +
> +       switch (vd->ecp.tx.state) {
> +       case ECP_TX_INIT_TRANSMIT:
> +               if (port->portEnabled && ((port->adminStatus == enabledRxTx) ||
> +                       (port->adminStatus == enabledTxOnly)) && vd->ecp.tx.localChange) {
> +                       ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU);
> +                       return true;
> +               }
> +               return false;
> +       case ECP_TX_TRANSMIT_ECPDU:
> +               if ((port->adminStatus == disabled) ||
> +                       (port->adminStatus == enabledRxOnly)) {
> +                       ecp_tx_change_state(vd, ECP_TX_INIT_TRANSMIT);
> +                       return true;
> +               }
> +               ecp_tx_change_state(vd, ECP_TX_WAIT_FOR_ACK);
> +               return true;
> +       case ECP_TX_WAIT_FOR_ACK:
> +               if (vd->ecp.ackTimerExpired) {
> +                       vd->ecp.retries++;
> +                       if (vd->ecp.retries < ECP_MAX_RETRIES) {
> +                               ecp_somethingChangedLocal(vd, VDP_PROFILE_REQ);
> +                               ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU);
> +                               return true;
> +                       }
> +                       if (vd->ecp.retries == ECP_MAX_RETRIES) {
> +                               printf("%s(%i)-%s: 1 \n", __func__, __LINE__,
> +                                      vd->ifname);
> +                               ecp_tx_change_state(vd, ECP_TX_REQUEST_PDU);
> +                               return true;
> +                       }
> +               }
> +               if (vd->ecp.ackReceived && vd->ecp.seqECPDU == vd->ecp.lastSequence) {
> +                       vd->ecp.ackReceived = false;
> +                       ecp_tx_change_state(vd, ECP_TX_REQUEST_PDU);
> +                       return true;
> +               }
> +               return false;
> +       case ECP_TX_REQUEST_PDU:
> +               if (vd->ecp.tx.localChange & VDP_PROFILE_REQ) {
> +                       ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU);
> +                       return true;
> +               }
> +               return false;
> +       default:
> +               printf("ERROR: The TX State Machine is broken!\n");
> +               log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname);
> +               return false;
> +       }
> +}
> +
> +/* ecp_tx_run_sm - state machine for ecp tx
> + * @vd: currently used vdp_data
> + *
> + * no return value
> + *
> + * runs the state machine for ecp tx.
> + */
> +void ecp_tx_run_sm(struct vdp_data *vd)
> +{
> +       do {
> +               printf("%s(%i)-%s: ecp_tx - %s\n", __func__, __LINE__,
> +                      vd->ifname, ecp_tx_states[vd->ecp.tx.state]);
> +
> +               switch(vd->ecp.tx.state) {
> +               case ECP_TX_INIT_TRANSMIT:
> +                       ecp_tx_Initialize(vd);
> +                       break;
> +               case ECP_TX_TRANSMIT_ECPDU:
> +                       ecp_tx_create_frame(vd);
> +                       ecp_tx_start_ackTimer(vd);
> +                       break;
> +               case ECP_TX_WAIT_FOR_ACK:
> +                       if (vd->ecp.ackReceived) {
> +                               printf("%s(%i)-%s: ECP_TX_WAIT_FOR_ACK ackReceived\n", __func__, __LINE__,
> +                                      vd->ifname);
> +                               printf("%s(%i)-%s: 2: seqECPDU %x lastSequence %x \n", __func__, __LINE__,
> +                                      vd->ifname, vd->ecp.seqECPDU, vd->ecp.lastSequence);
> +                               vd->ecp.tx.localChange = 0;
> +                               ecp_tx_stop_ackTimer(vd);
> +                       }
> +                       break;
> +               case ECP_TX_REQUEST_PDU:
> +                       vd->ecp.retries = 0;
> +                       printf("%s(%i)-%s: ECP_TX_REQUEST_PDU lastSequence %x\n", __func__, __LINE__,
> +                              vd->ifname, vd->ecp.lastSequence);
> +                       break;
> +               default:
> +                       printf("%s(%i): ERROR The TX State Machine is broken!\n", __func__,
> +                              __LINE__);
> +                       log_message(MSG_ERR_TX_SM_INVALID, "%s", vd->ifname);
> +               }
> +       } while (ecp_set_tx_state(vd) == true);
> +
> +       return;
> +}
> diff --git a/include/lldp.h b/include/lldp.h
> index e00ba7a..fd515cd 100644
> --- a/include/lldp.h
> +++ b/include/lldp.h
> @@ -190,6 +190,7 @@ enum {
> 
>  /* IEEE 802.1Qbg subtype */
>  #define LLDP_EVB_SUBTYPE               0
> +#define LLDP_VDP_SUBTYPE               0x2
> 
>  /* forwarding mode */
>  #define LLDP_EVB_CAPABILITY_FORWARD_STANDARD           (1 << 7)
> diff --git a/include/lldp_evb.h b/include/lldp_evb.h
> index 667f9ad..d028c21 100644
> --- a/include/lldp_evb.h
> +++ b/include/lldp_evb.h
> @@ -37,6 +37,12 @@ typedef enum {
>         EVB_CONFIRMATION
>  } evb_state;
> 
> +#define        RTE             13
> +/* retransmission granularity (RTG) in microseconds */
> +#define EVB_RTG                10
> +/* retransmission multiplier (RTM) */
> +#define EVB_RTM(rte)   (2<<(RTE-1))
> +
>  struct tlv_info_evb {
>         u8 oui[3];
>         u8 sub;
> diff --git a/include/lldp_vdp.h b/include/lldp_vdp.h
> new file mode 100644
> index 0000000..b97d8c0
> --- /dev/null
> +++ b/include/lldp_vdp.h
> @@ -0,0 +1,159 @@
> +/*******************************************************************************
> +
> +  implementation of according to IEEE 802.1Qbg
> +  (c) Copyright IBM Corp. 2010
> +
> +  Author(s): Jens Osterkamp <jens@xxxxxxxxxxxxxxxxxx>
> +
> +  This program is free software; you can redistribute it and/or modify it
> +  under the terms and conditions of the GNU General Public License,
> +  version 2, as published by the Free Software Foundation.
> +
> +  This program is distributed in the hope it will be useful, but WITHOUT
> +  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> +  more details.
> +
> +  You should have received a copy of the GNU General Public License along with
> +  this program; if not, write to the Free Software Foundation, Inc.,
> +  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> +
> +  The full GNU General Public License is included in this distribution in
> +  the file called "COPYING".
> +
> +*******************************************************************************/
> +
> +#ifndef _LLDP_VDP_H
> +#define _LLDP_VDP_H
> +
> +#include "lldp_mod.h"
> +#include "ecp/ecp.h"
> +
> +#define LLDP_MOD_VDP           OUI_IEEE_8021Qbg+1
> +
> +#define VDP_MODE_PREASSOCIATE          0x0
> +#define VDP_MODE_PREASSOCIATE_WITH_RR  0x1
> +#define VDP_MODE_ASSOCIATE             0x2
> +#define VDP_MODE_DEASSOCIATE           0x3
> +
> +#define VDP_RESPONSE_SUCCESS           0x0
> +#define VDP_RESPONSE_INVALID_FORMAT    0x1
> +#define VDP_RESPONSE_INSUFF_RESOURCES  0x2
> +#define VDP_RESPONSE_UNUSED_VTID       0x3
> +#define VDP_RESPONSE_VTID_VIOLATION    0x4
> +#define VDP_RESPONSE_VTID_VER_VIOLATION        0x5
> +#define VDP_RESPONSE_OUT_OF_SYNC       0x6
> +
> +enum {
> +       VDP_PROFILE_NOCHANGE = 0,
> +       VDP_PROFILE_REQ,
> +       VDP_PROFILE_ACK,
> +       VDP_PROFILE_NACK,
> +};
> +
> +#define VDP_MACVLAN_FORMAT_1   1
> +
> +#define VDP_TRANSMISSION_TIMER         3*EVB_RTM(RTE)*EVB_RTG
> +#define VDP_TRANSMISSION_DIVIDER       10000
> +
> +#define VDP_ROLE_STATION               0
> +#define VDP_ROLE_BRIDGE                        1
> +
> +enum {
> +       VSI_UNASSOCIATED = 0,
> +       VSI_ASSOC_PROCESSING,
> +       VSI_ASSOCIATED,
> +       VSI_PREASSOC_PROCESSING,
> +       VSI_PREASSOCIATED,
> +       VSI_DEASSOC_PROCESSING,
> +       VSI_EXIT,
> +};
> +
> +static char *vsi_states[] = {
> +       "VSI_UNASSOCIATED",
> +       "VSI_ASSOC_PROCESSING",
> +       "VSI_ASSOCIATED",
> +       "VSI_PREASSOC_PROCESSING",
> +       "VSI_PREASSOCIATED",
> +       "VSI_DEASSOC_PROCESSING",
> +       "VSI_EXIT"
> +};
> +
> +struct mac_vlan {
> +       u8 mac[6];
> +       u16 vlan;
> +} __attribute__ ((__packed__));
> +
> +struct tlv_info_vdp {
> +       u8 oui[3];
> +       u8 sub;
> +       u8 mode;
> +       u8 response;
> +       u8 mgrid;
> +       u8 id[3];
> +       u8 version;
> +       u8 instance[16];
> +       u8 format;
> +       u16 entries;
> +       struct mac_vlan mac_vlan;
> +} __attribute__ ((__packed__));
> +
> +struct vsi_profile {
> +       int mode;
> +       int response;
> +       u8 mgrid;
> +       int id;
> +       u8 version;
> +       u8 instance[16];
> +       u8 mac[6]; /* TODO: currently only one MAC/VLAN pair supported, more later */
> +       u16 vlan;
> +       struct port *port;
> +       int ackTimerExpired;
> +       int ackReceived;
> +       int state;
> +       int localChange;
> +       LIST_ENTRY(vsi_profile) profile;
> +};
> +
> +struct vdp_data {
> +       char ifname[IFNAMSIZ];
> +       struct ecp ecp;
> +       struct unpacked_tlv *vdp;
> +       int role;
> +       LIST_HEAD(profile_head, vsi_profile) profile_head;
> +       LIST_ENTRY(vdp_data) entry;
> +};
> +
> +struct vdp_user_data {
> +       LIST_HEAD(vdp_head, vdp_data) head;
> +};
> +
> +struct lldp_module *vdp_register(void);
> +void vdp_unregister(struct lldp_module *mod);
> +struct vdp_data *vdp_data(char *ifname);
> +struct packed_tlv *vdp_gettlv(struct vdp_data *vd, struct vsi_profile *profile);
> +void vdp_vsi_sm_station(struct vsi_profile *profile);
> +struct vsi_profile *vdp_add_profile(struct vsi_profile *profile);
> +
> +#define MAC_ADDR_STRLEN                18
> +#define INSTANCE_STRLEN                32
> +
> +#define PRINT_PROFILE(s, p)    \
> +{ int c; \
> +  c = sprintf(s, "\nmode: %i\n", p->mode); s += c; \
> +  c = sprintf(s, "response: %i\n", p->response); s += c; \
> +  c = sprintf(s, "state: %i\n", p->state); s += c; \
> +  c = sprintf(s, "mgrid: %i\n", p->mgrid); s += c; \
> +  c = sprintf(s, "id: %x\n", p->id); \
> +  s += c; \
> +  c = sprintf(s, "version: %i\n", p->version); s += c; \
> +  char instance[INSTANCE_STRLEN+2]; \
> +  instance2str(p->instance, instance, sizeof(instance)); \
> +  c = sprintf(s, "instance: %s\n", &instance[0]); s += c; \
> +  char macbuf[MAC_ADDR_STRLEN+1]; \
> +  mac2str(p->mac, macbuf, MAC_ADDR_STRLEN); \
> +  c = sprintf(s, "mac: %s\n", macbuf); s += c; \
> +  c = sprintf(s, "vlan: %i\n\n", p->vlan); s += c; \
> +}
> +
> +#endif /* _LLDP_VDP_H */
> diff --git a/lldp/l2_packet.h b/lldp/l2_packet.h
> index 16f3683..0962429 100644
> --- a/lldp/l2_packet.h
> +++ b/lldp/l2_packet.h
> @@ -36,6 +36,8 @@
> 
>  #define ETH_P_LLDP 0x88cc
> 
> +/* TODO: use extended ethertype until final ethertype is available */
> +#define ETH_P_ECP 0x88b7
> 
>  #define ETH_FRAME_LEN   1514
> 
> diff --git a/lldp/ports.h b/lldp/ports.h
> index 0138efe..44dc5f1 100644
> --- a/lldp/ports.h
> +++ b/lldp/ports.h
> @@ -142,15 +142,20 @@ struct port {
>         u8 portEnabled;
>         u8 prevPortEnabled;
>         u8 adminStatus;
> -       u8 rxChanges;
> -       u16   lldpdu;
> +
> +       /* protocol specific */
>         struct l2_packet_data *l2;
>         struct portrx rx;
>         struct porttx tx;
> -       struct porttlvs tlvs;
>         struct portstats stats;
>         struct porttimers timers;
> +       u8 rxChanges;
> +       u16   lldpdu;
>         struct msap msap;
> +
> +       /* not sure */
> +       struct porttlvs tlvs;
> +
>         struct port *next;
>  };
> 
> diff --git a/lldp_vdp.c b/lldp_vdp.c
> new file mode 100644
> index 0000000..c2b21bb
> --- /dev/null
> +++ b/lldp_vdp.c
> @@ -0,0 +1,1185 @@
> +/*******************************************************************************
> +
> +  implementation of VDP according to IEEE 802.1Qbg
> +  (c) Copyright IBM Corp. 2010
> +
> +  Author(s): Jens Osterkamp <jens@xxxxxxxxxxxxxxxxxx>
> +
> +  This program is free software; you can redistribute it and/or modify it
> +  under the terms and conditions of the GNU General Public License,
> +  version 2, as published by the Free Software Foundation.
> +
> +  This program is distributed in the hope it will be useful, but WITHOUT
> +  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> +  more details.
> +
> +  You should have received a copy of the GNU General Public License along with
> +  this program; if not, write to the Free Software Foundation, Inc.,
> +  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> +
> +  The full GNU General Public License is included in this distribution in
> +  the file called "COPYING".
> +
> +*******************************************************************************/
> +
> +#include <net/if.h>
> +#include <sys/queue.h>
> +#include <sys/socket.h>
> +#include <sys/ioctl.h>
> +#include <sys/utsname.h>
> +#include <linux/if_bridge.h>
> +#include "lldp.h"
> +#include "lldp_vdp.h"
> +#include "ecp/ecp.h"
> +#include "eloop.h"
> +#include "lldp_evb.h"
> +#include "messages.h"
> +#include "config.h"
> +#include "common.h"
> +#include "lldp_vdp_clif.h"
> +#include "lldp_vdp_cmds.h"
> +
> +/* vdp_data - searches vdp_data in the list of modules for this port
> + * @ifname: interface name to search for
> + *
> + * returns vdp_data on success, NULL on error
> + *
> + * searches the list of user_data for the VDP module user_data.
> + */
> +struct vdp_data *vdp_data(char *ifname)
> +{
> +       struct vdp_user_data *ud;
> +       struct vdp_data *vd = NULL;
> +
> +       ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_VDP);
> +       if (ud) {
> +               LIST_FOREACH(vd, &ud->head, entry) {
> +                       if (!strncmp(ifname, vd->ifname, IFNAMSIZ))
> +                               return vd;
> +               }
> +       }
> +       return NULL;
> +}
> +
> +/* vdp_free_tlv - free tlv in vdp_data
> + * @vd: vdp_data
> + *
> + * no return value
> + *
> + * frees up tlv in vdp_data. used in vdp_free_data.
> + */
> +static void vdp_free_tlv(struct vdp_data *vd)
> +{
> +       if (vd) {
> +               FREE_UNPKD_TLV(vd, vdp);
> +       }
> +}
> +
> +/* vdp_free_data - frees up vdp data
> + * @ud: user data structure
> + *
> + * no return value
> + *
> + * removes vd_structure from the user_data list. frees up tlv in vdp_data.
> + * used in vdp_unregister.
> + */
> +static void vdp_free_data(struct vdp_user_data *ud)
> +{
> +       struct vdp_data *vd;
> +       if (ud) {
> +               while (!LIST_EMPTY(&ud->head)) {
> +                       vd = LIST_FIRST(&ud->head);
> +                       LIST_REMOVE(vd, entry);
> +                       vdp_free_tlv(vd);
> +                       free(vd);
> +               }
> +       }
> +}
> +
> +/* vdp_print_profile - print a vsi profile
> + * @profile: profile to print
> + *
> + * no return value
> + *
> + * prints the contents of a profile first to a string using the PRINT_PROFILE
> + * macro, and then to the screen. Used for debug purposes.
> + */
> +static inline void vdp_print_profile(struct vsi_profile *profile)
> +{
> +       char *s, *t;
> +
> +       s = t = malloc(VDP_BUF_SIZE);
> +
> +        if (!s) {
> +               printf("%s(%i): unable to allocate string !\n", __func__, __LINE__);
> +        }
> +
> +       PRINT_PROFILE(t, profile);
> +
> +       printf("profile: %s\n", s);
> +
> +       free(s);
> +}
> +
> +/* vdp_somethingChangedLocal - set flag if profile has changed
> + * @profile: profile to set the flag for
> + * @mode: mode to set the flag to
> + *
> + * no return value
> + *
> + * set the localChange flag with a mode to indicate a profile has changed.
> + * used next time when a ecpdu with profiles is sent out.
> + */
> +void vdp_somethingChangedLocal(struct vsi_profile *profile, int mode)
> +{
> +       profile->localChange = mode;
> +}
> +
> +/* vdp_ackTimer_expired - checks for expired ack timer
> + * @profile: profile to be checked
> + *
> + * returns true or false
> + *
> + * returns value of profile->ackTimerExpired, true if ack timer has expired,
> + * false otherwise.
> + */
> +static bool vdp_ackTimer_expired(struct vsi_profile *profile)
> +{
> +       return profile->ackTimerExpired;
> +}
> +
> +/* vdp_timeout_handler - handles the ack timer expiry
> + * @eloop_data: data structure of event loop
> + * @user_ctx: user context, profile here
> + *
> + * no return value
> + *
> + * called when the VDP ack timer has expired. sets a flag and calls the VDP
> + * state machine.
> + */
> +void vdp_timeout_handler(void *eloop_data, void *user_ctx)
> +{
> +       struct vsi_profile *profile;
> +
> +       profile = (struct vsi_profile *) user_ctx;
> +
> +       profile->ackTimerExpired = true;
> +
> +       printf("%s(%i)-%s: timer expired\n", __func__, __LINE__,
> +              profile->port->ifname);
> +
> +       vdp_vsi_sm_station(profile);
> +}
> +
> +/* vdp_stop_ackTimer - stop the VDP ack timer
> + * @profile: profile to process
> + *
> + * returns the number of removed handlers
> + *
> + * stops the VDP ack timer. used when a ack frame for the profile has been
> + * received.
> + */
> +static int vdp_stop_ackTimer(struct vsi_profile *profile)
> +{
> +       printf("%s(%i)-%s: stopping timer\n", __func__, __LINE__,
> +              profile->port->ifname);
> +
> +       return eloop_cancel_timeout(vdp_timeout_handler, NULL, (void *) profile);
> +}
> +
> +/* vdp_start_ackTimer - starts the VDP ack timer
> + * @profile: profile to process
> + *
> + * returns 0 on success, -1 on error
> + *
> + * starts the ack timer when a frame has been sent out.
> + */
> +static int vdp_start_ackTimer(struct vsi_profile *profile)
> +{
> +       unsigned int secs, usecs;
> +
> +       profile->ackTimerExpired = false;
> +
> +       secs = VDP_TRANSMISSION_TIMER / VDP_TRANSMISSION_DIVIDER;
> +       usecs = VDP_TRANSMISSION_TIMER % VDP_TRANSMISSION_DIVIDER;
> +
> +       printf("%s(%i)-%s: starting timer\n", __func__, __LINE__,
> +              profile->port->ifname);
> +
> +       return eloop_register_timeout(secs, usecs, vdp_timeout_handler, NULL, (void *) profile);
> +}
> +
> +/* vdp_vsi_change_station_state - changes the VDP station sm state
> + * @profile: profile to process
> + * @newstate: new state for the sm
> + *
> + * no return value
> + *
> + * actually changes the state of the profile
> + */
> +void vdp_vsi_change_station_state(struct vsi_profile *profile, u8 newstate)
> +{
> +       switch(newstate) {
> +       case VSI_UNASSOCIATED:
> +               break;
> +       case VSI_ASSOC_PROCESSING:
> +               assert((profile->state == VSI_PREASSOCIATED) ||
> +                      (profile->state == VSI_UNASSOCIATED));
> +               break;
> +       case VSI_ASSOCIATED:
> +               assert(profile->state == VSI_ASSOC_PROCESSING);
> +               break;
> +       case VSI_PREASSOC_PROCESSING:
> +               assert(profile->state == VSI_UNASSOCIATED);
> +               break;
> +       case VSI_PREASSOCIATED:
> +               assert(profile->state == VSI_PREASSOC_PROCESSING);
> +               break;
> +       case VSI_DEASSOC_PROCESSING:
> +               assert((profile->state == VSI_PREASSOCIATED) ||
> +                      (profile->state == VSI_ASSOCIATED));
> +               break;
> +       case VSI_EXIT:
> +               assert((profile->state == VSI_ASSOC_PROCESSING) ||
> +                      (profile->state == VSI_PREASSOC_PROCESSING) ||
> +                      (profile->state == VSI_DEASSOC_PROCESSING) ||
> +                      (profile->state == VSI_PREASSOCIATED) ||
> +                      (profile->state == VSI_ASSOCIATED));
> +               break;
> +       default:
> +               printf("ERROR: The VDP station State Machine is broken!\n");
> +               break;
> +       }
> +
> +       printf("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
> +              profile->port->ifname, vsi_states[profile->state], vsi_states[newstate]);
> +
> +       profile->state = newstate;
> +}
> +
> +/* vdp_vsi_set_station_state - sets the vdp sm station state
> + * @profile: profile to process
> + *
> + * returns true or false
> + *
> + * switches the state machine to the next state depending on the input
> + * variables. returns true or false depending on wether the state machine
> + * can be run again with the new state or can stop at the current state.
> + */
> +static bool vdp_vsi_set_station_state(struct vsi_profile *profile)
> +{
> +       switch(profile->state) {
> +       case VSI_UNASSOCIATED:
> +               if ((profile->mode == VDP_MODE_PREASSOCIATE) ||
> +                   (profile->mode == VDP_MODE_PREASSOCIATE_WITH_RR)) {
> +                       vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING);
> +                       return true;
> +               } else if (profile->mode == VDP_MODE_ASSOCIATE) {
> +                       vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_ASSOC_PROCESSING:
> +               if (profile->ackReceived) {
> +                       vdp_vsi_change_station_state(profile, VSI_ASSOCIATED);
> +                       return true;
> +               } else if (!profile->ackReceived && vdp_ackTimer_expired(profile)) {
> +                       vdp_vsi_change_station_state(profile, VSI_EXIT);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_ASSOCIATED:
> +               if (profile->mode == VDP_MODE_PREASSOCIATE) {
> +                       vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING);
> +                       return true;
> +               } else if (profile->mode == VDP_MODE_DEASSOCIATE) {
> +                       vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_PREASSOC_PROCESSING:
> +               if (profile->ackReceived) {
> +                       vdp_vsi_change_station_state(profile, VSI_PREASSOCIATED);
> +                       return true;
> +               } else if (vdp_ackTimer_expired(profile)) {
> +                       vdp_vsi_change_station_state(profile, VSI_EXIT);
> +                       return true;
> +               }
> +       case VSI_PREASSOCIATED:
> +               if (profile->mode == VDP_MODE_DEASSOCIATE) {
> +                       vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING);
> +                       return true;
> +               }
> +               if (profile->mode == VDP_MODE_ASSOCIATE) {
> +                       vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_DEASSOC_PROCESSING:
> +               if ((profile->ackReceived) || vdp_ackTimer_expired(profile)) {
> +                       vdp_vsi_change_station_state(profile, VSI_EXIT);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_EXIT:
> +               return false;
> +       default:
> +               printf("ERROR: The VSI RX State Machine is broken!\n");
> +               log_message(MSG_ERR_RX_SM_INVALID, "");
> +               return false;
> +       }
> +}
> +
> +/* vdp_vsi_sm_station - state machine for vdp station role
> + * @profile: profile for which the state is processed
> + *
> + * no return value
> + *
> + * runs the state machine for the station role of VDP.
> + */
> +void vdp_vsi_sm_station(struct vsi_profile *profile)
> +{
> +       struct vdp_data *vd = vdp_data(profile->port->ifname);
> +
> +       vdp_vsi_set_station_state(profile);
> +       do {
> +               printf("%s(%i)-%s: station - %s\n", __func__, __LINE__,
> +                      profile->port->ifname, vsi_states[profile->state]);
> +
> +               switch(profile->state) {
> +               case VSI_UNASSOCIATED:
> +                       break;
> +               case VSI_ASSOC_PROCESSING:
> +                       vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
> +                       ecp_somethingChangedLocal(vd, VDP_PROFILE_REQ);
> +                       ecp_tx_run_sm(vd);
> +                       vdp_start_ackTimer(profile);
> +                       break;
> +               case VSI_ASSOCIATED:
> +                        vdp_stop_ackTimer(profile);
> +                        /* TODO:
> +                         * vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
> +                        *       if (!vsiError) vsistate=ASSOCIATED */
> +                       break;
> +               case VSI_PREASSOC_PROCESSING:
> +                       /* send out profile */
> +                       vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
> +                       ecp_somethingChangedLocal(vd, VDP_PROFILE_REQ);
> +                       ecp_tx_run_sm(vd);
> +                       vdp_start_ackTimer(profile);
> +                       break;
> +               case VSI_PREASSOCIATED:
> +                       profile->ackReceived = false;
> +                       vdp_somethingChangedLocal(profile, VDP_PROFILE_NOCHANGE);
> +                       vdp_stop_ackTimer(profile);
> +                       /* TODO  vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
> +                        *       if (!vsiError) vsistate=PREASSOCIATED */
> +                       break;
> +               case VSI_DEASSOC_PROCESSING:
> +                       vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
> +                       ecp_somethingChangedLocal(vd, VDP_PROFILE_REQ);
> +                       ecp_tx_run_sm(vd);
> +                       vdp_start_ackTimer(profile);
> +                       break;
> +               case VSI_EXIT:
> +                       /* TODO: send DEASSOC here ? */
> +                       vdp_stop_ackTimer(profile);
> +                       vdp_remove_profile(profile);
> +                       break;
> +               default:
> +                       printf("ERROR: The VSI RX station State Machine is broken!\n");
> +                       log_message(MSG_ERR_TX_SM_INVALID, "");
> +               }
> +       } while (vdp_vsi_set_station_state(profile) == true);
> +
> +}
> +
> +/* vdp_vsi_change_bridge_state - changes the VDP bridge sm state
> + * @profile: profile to process
> + * @newstate: new state for the sm
> + *
> + * no return value
> + *
> + * actually changes the state of the profile
> + */
> +static void vdp_vsi_change_bridge_state(struct vsi_profile *profile, u8 newstate)
> +{
> +       switch(newstate) {
> +       case VSI_UNASSOCIATED:
> +               break;
> +       case VSI_ASSOC_PROCESSING:
> +               assert((profile->state == VSI_UNASSOCIATED) ||
> +                     (profile->state == VSI_PREASSOCIATED) ||
> +                     (profile->state == VSI_ASSOCIATED));
> +               break;
> +       case VSI_ASSOCIATED:
> +               assert(profile->state == VSI_ASSOC_PROCESSING);
> +               break;
> +       case VSI_PREASSOC_PROCESSING:
> +               assert((profile->state == VSI_UNASSOCIATED) ||
> +                     (profile->state == VSI_PREASSOCIATED) ||
> +                     (profile->state == VSI_ASSOCIATED));
> +               break;
> +       case VSI_PREASSOCIATED:
> +               assert(profile->state == VSI_PREASSOC_PROCESSING);
> +               break;
> +       case VSI_DEASSOC_PROCESSING:
> +               assert((profile->state == VSI_UNASSOCIATED) ||
> +                     (profile->state == VSI_PREASSOCIATED) ||
> +                     (profile->state == VSI_ASSOCIATED));
> +               break;
> +       case VSI_EXIT:
> +               assert((profile->state == VSI_DEASSOC_PROCESSING) ||
> +                     (profile->state == VSI_PREASSOC_PROCESSING) ||
> +                     (profile->state == VSI_ASSOC_PROCESSING));
> +               break;
> +       default:
> +               printf("ERROR: The VDP bridge State Machine is broken!\n");
> +               break;
> +       }
> +       profile->state = newstate;
> +}
> +
> +/* vdp_vsi_set_bridge_state - sets the vdp sm bridge state
> + * @profile: profile to process
> + *
> + * returns true or false
> + *
> + * switches the state machine to the next state depending on the input
> + * variables. returns true or false depending on wether the state machine
> + * can be run again with the new state or can stop at the current state.
> + */
> +static bool vdp_vsi_set_bridge_state(struct vsi_profile *profile)
> +{
> +       switch(profile->state) {
> +       case VSI_UNASSOCIATED:
> +               if ((profile->mode == VDP_MODE_DEASSOCIATE)) /* || (INACTIVE)) */ {
> +                       vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
> +                       return true;
> +               } else if (profile->mode == VDP_MODE_ASSOCIATE) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
> +                       return true;
> +               } else if (profile->mode == VDP_MODE_PREASSOCIATE) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_ASSOC_PROCESSING:
> +               /* TODO: handle error case
> +               if (!vsiError) ||
> +                  (vsiError && vsiState == Assoc) {
> +                  */
> +               if (profile->mode == VDP_MODE_ASSOCIATE) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_ASSOCIATED);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_ASSOCIATED:
> +               if (profile->mode == VDP_MODE_ASSOCIATE) /* || ( INACTIVE )*/ {
> +                       vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
> +                       return true;
> +               } else if (profile->mode == VDP_MODE_PREASSOCIATE) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
> +                       return true;
> +               }  else if (profile->mode == VDP_MODE_ASSOCIATE) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_PREASSOC_PROCESSING:
> +                if (profile->response != VDP_RESPONSE_SUCCESS) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_EXIT);
> +                       return true;
> +                }
> +               vdp_vsi_change_bridge_state(profile, VSI_PREASSOCIATED);
> +               return false;
> +       case VSI_PREASSOCIATED:
> +               if (profile->mode == VDP_MODE_ASSOCIATE) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
> +                       return true;
> +               } else if (profile->mode == VDP_MODE_DEASSOCIATE ) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
> +                       return true;
> +               }  else if (profile->mode == VDP_MODE_PREASSOCIATE ) {
> +                       vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
> +                       return true;
> +               }
> +               return false;
> +       case VSI_DEASSOC_PROCESSING:
> +               vdp_vsi_change_bridge_state(profile, VSI_EXIT);
> +               return false;
> +       case VSI_EXIT:
> +               return false;
> +       default:
> +               printf("ERROR: The VSI RX State Machine (bridge) is broken!\n");
> +               log_message(MSG_ERR_RX_SM_INVALID, "");
> +               return false;
> +       }
> +}
> +
> +/* vdp_vsi_sm_bridge - state machine for vdp bridge role
> + * @profile: profile for which the state is processed
> + *
> + * no return value
> + *
> + * runs the state machine for the bridge role of VDP.
> + */
> +static void vdp_vsi_sm_bridge(struct vsi_profile *profile)
> +{
> +       struct vdp_data *vd = vdp_data(profile->port->ifname);
> +
> +       vdp_vsi_set_bridge_state(profile);
> +       do {
> +               printf("%s(%i)-%s: bridge - %s\n", __func__, __LINE__,
> +                      profile->port->ifname, vsi_states[profile->state]);
> +               switch(profile->state) {
> +               case VSI_UNASSOCIATED:
> +                       break;
> +               case VSI_ASSOC_PROCESSING:
> +                       /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
> +                        *       if (vsiError)
> +                        *              txTLV(Assoc NACK)
> +                        *       else
> +                        *              txTLV(Assoc ACK)
> +                        */
> +                       break;
> +               case VSI_ASSOCIATED:
> +                       break;
> +               case VSI_PREASSOC_PROCESSING:
> +                       /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
> +                        *       if (vsiError)
> +                        *              txTLV(PreAssoc NACK)
> +                        *       else
> +                        *              txTLV(PreAssoc ACK)
> +                        */
> +                       /* for now, we always succeed */
> +                       profile->response = VDP_RESPONSE_SUCCESS;
> +                       printf("%s(%i)-%s: framein %p, sizein %i\n", __func__, __LINE__,
> +                              profile->port->ifname, vd->ecp.rx.framein,
> +                              vd->ecp.rx.sizein);
> +                       ecp_rx_send_ack_frame(profile->port);
> +                       break;
> +               case VSI_PREASSOCIATED:
> +                       printf("%s(%i)-%s: \n", __func__, __LINE__, profile->port->ifname);
> +                       break;
> +               case VSI_DEASSOC_PROCESSING:
> +                       /* TODO: txTLV(DeAssoc ACK) */
> +                       break;
> +               case VSI_EXIT:
> +                       vdp_remove_profile(profile);
> +                       break;
> +               default:
> +                       printf("ERROR: The VSI RX bridge State Machine is broken!\n");
> +                       log_message(MSG_ERR_TX_SM_INVALID, "");
> +               }
> +       } while (vdp_vsi_set_bridge_state(profile) == true);
> +
> +}
> +
> +/*
> + * vdp_print_vsi_tlv - print the raw contents of a VSI TLV
> + * @tlv: the unpacked tlv which gets printed
> + *
> + * No return value
> + *
> + * used for protocol debug purposes
> + */
> +static void vdp_print_vsi_tlv(struct unpacked_tlv *tlv)
> +{
> +       int i;
> +
> +       printf("### %s:type %i, length %i, info:\n", __func__, tlv->type, tlv->length);
> +
> +       for (i=0; i < tlv->length; i++) {
> +               printf("%02x ", tlv->info[i]);
> +               if (!((i+1) % 16))
> +                       printf("\n");
> +       }
> +
> +       printf("\n");
> +}
> +
> +/*
> + * vdp_validate_tlv - validates vsi tlvs
> + * @vdp: decoded vsi tlv
> + *
> + * Returns 0 on success, 1 on error
> + *
> + * checks the contents of an already decoded vsi tlv for inconsistencies
> + */
> +static int vdp_validate_tlv(struct tlv_info_vdp *vdp)
> +{
> +       if (ntoh24(vdp->oui) != OUI_IEEE_8021Qbg) {
> +               printf("vdp->oui %06x \n", ntoh24(vdp->oui));
> +               goto out_err;
> +       }
> +
> +       if (vdp->sub != LLDP_VDP_SUBTYPE) {
> +               printf("vdp->sub %02x \n", vdp->sub);
> +               goto out_err;
> +       }
> +
> +       if ((vdp->mode < VDP_MODE_PREASSOCIATE) ||
> +               (vdp->mode > VDP_MODE_DEASSOCIATE)) {
> +               printf("Unknown mode %02x in vsi tlv !\n", vdp->mode);
> +               goto out_err;
> +       }
> +
> +       if ((vdp->response < VDP_RESPONSE_SUCCESS) ||
> +               (vdp->response > VDP_RESPONSE_OUT_OF_SYNC)) {
> +               printf("Unknown response %02x \n", vdp->response);
> +               goto out_err;
> +       }
> +
> +       if (vdp->format != VDP_MACVLAN_FORMAT_1) {
> +               printf("Unknown format %02x in vsi tlv !\n", vdp->format);
> +               goto out_err;
> +       }
> +
> +       if (ntohs(vdp->entries) != 1) {
> +               printf("Multiple entries %02x in vsi tlv !\n", vdp->entries);
> +               goto out_err;
> +       }
> +
> +       return 0;
> +
> +out_err:
> +       return 1;
> +}
> +
> +/*
> + * vdp_indicate - receive VSI TLVs from ECP
> + * @port: the port on which the tlv was received
> + * @tlv: the unpacked tlv to receive
> + * @ecp_mode: the mode under which the tlv was received (ACK or REQ)
> + *
> + * Returns 0 on success
> + *
> + * receives a vsi tlv and creates a profile. Take appropriate action
> + * depending on the role of the (receive) port
> + */
> +int vdp_indicate(struct vdp_data *vd, struct unpacked_tlv *tlv, int ecp_mode)
> +{
> +       struct tlv_info_vdp *vdp;
> +       struct vsi_profile *p, *profile;
> +       struct port *port = port_find_by_name(vd->ifname);
> +
> +       printf("%s(%i): indicating vdp for for %s !\n", __func__, __LINE__, vd->ifname);
> +
> +       if (!port) {
> +               printf("%s(%i): port not found for %s !\n", __func__, __LINE__, vd->ifname);
> +               goto out_err;
> +       }
> +
> +       vdp = malloc(sizeof(struct tlv_info_vdp));
> +
> +       if (!vdp) {
> +               printf("%s(%i): unable to allocate vdp !\n", __func__, __LINE__);
> +               goto out_err;
> +       }
> +
> +       memset(vdp, 0, sizeof(struct tlv_info_vdp));
> +       memcpy(vdp, tlv->info, tlv->length);
> +
> +       if (vdp_validate_tlv(vdp)) {
> +               printf("%s(%i): Invalid TLV received !\n", __func__, __LINE__);
> +               goto out_err;

Should be goto out_vdp

> +       }
> +
> +       profile = malloc(sizeof(struct vsi_profile));
> +
> +        if (!profile) {
> +               printf("%s(%i): unable to allocate profile !\n", __func__, __LINE__);
> +               goto out_vdp;
> +        }
> +
> +       memset(profile, 0, sizeof(struct vsi_profile));
> +
> +       profile->mode = vdp->mode;
> +       profile->response = vdp->response;
> +
> +       profile->mgrid = vdp->mgrid;
> +       profile->id = ntoh24(vdp->id);
> +       profile->version = vdp->version;
> +       memcpy(&profile->instance, &vdp->instance, 16);
> +       memcpy(&profile->mac, &vdp->mac_vlan.mac, MAC_ADDR_LEN);
> +       profile->vlan = ntohs(vdp->mac_vlan.vlan);
> +
> +       profile->port = port;
> +
> +       if (vd->role == VDP_ROLE_STATION) {
> +               /* do we have the profile already ? */
> +               LIST_FOREACH(p, &vd->profile_head, profile) {
> +                       if (vdp_profile_equal(p, profile)) {
> +                               printf("%s(%i): station: profile found, localChange %i ackReceived %i!\n",
> +                                      __func__, __LINE__, p->localChange, p->ackReceived);
> +
> +                               p->ackReceived = true;
> +
> +                               vdp_vsi_sm_station(p);
> +                       } else {
> +                               printf("%s(%i): station: profile not found !\n", __func__, __LINE__);
> +                               /* ignore profile */
> +                       }
> +               }
> +       }
> +
> +       if (vd->role == VDP_ROLE_BRIDGE) {
> +               /* do we have the profile already ? */
> +               LIST_FOREACH(p, &vd->profile_head, profile) {
> +                       if (vdp_profile_equal(p, profile)) {
> +                               break;
> +                       }
> +               }
> +
> +               if (p) {
> +                       printf("%s(%i): bridge: profile found !\n", __func__, __LINE__);
> +               } else {
> +                       printf("%s(%i): bridge: profile not found !\n", __func__, __LINE__);
> +                       /* put it in the list  */
> +                       profile->state = VSI_UNASSOCIATED;
> +                       LIST_INSERT_HEAD(&vd->profile_head, profile, profile );
> +               }
> +
> +               vdp_vsi_sm_bridge(profile);
> +       }
> +


Here the profile is added to a list if the port is in the VDP_ROLE_BRIDGE mode. Otherwise the profile is only used to lookup an existing profile? Looks like there might be a memory leak if the profile already exists. Does the profile need to be cleaned up the somewhere?

> +       return 0;
> +
> +out_vdp:
> +       free(vdp);
> +out_err:
> +       printf("%s(%i): error !\n", __func__, __LINE__);
> +       return 1;
> +
> +}


The rest looks good.

Thanks,
John
_______________________________________________
Virtualization mailing list
Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/virtualization


[Index of Archives]     [KVM Development]     [Libvirt Development]     [Libvirt Users]     [CentOS Virtualization]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux