[lacp] Add simple LACP implementation
authorMichael Brown <mcb30@ipxe.org>
Mon, 10 May 2010 15:30:10 +0000 (16:30 +0100)
committerStefan Hajnoczi <stefanha@gmail.com>
Wed, 7 Jul 2010 19:14:37 +0000 (20:14 +0100)
Some switch configurations will refuse to enable our port unless we
can speak LACP to inform the switch that we are alive.  Add a very
simple passive LACP implementation that is sufficient to convince at
least Linux's bonding driver (when tested using qemu attached to a tap
device enslaved to a bond device configured as "mode=802.3ad").

Signed-off-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
src/core/proto_eth_slow.c [deleted file]
src/include/gpxe/errfile.h
src/include/gpxe/eth_slow.h [new file with mode: 0644]
src/net/eth_slow.c [new file with mode: 0644]
src/net/ethernet.c

diff --git a/src/core/proto_eth_slow.c b/src/core/proto_eth_slow.c
deleted file mode 100644 (file)
index b759a71..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-/* Copyright 2004 Linux Networx */
-#ifdef PROTO_LACP
-#if 0
-#include "nic.h"
-#include "timer.h"
-#endif
-
-#define LACP_DEBUG 0
-
-/* Structure definitions originally taken from the linux bond_3ad driver */
-
-#define SLOW_DST_MAC "\x01\x80\xc2\x00\x00\x02"
-static const char slow_dest[] = SLOW_DST_MAC;
-
-
-#define SLOW_SUBTYPE_LACP 1
-#define SLOW_SUBTYPE_MARKER 2
-
-struct slow_header {
-       uint8_t subtype;
-};
-
-struct lacp_info {
-       uint16_t system_priority;
-       uint8_t  system[ETH_ALEN];
-       uint16_t key;
-       uint16_t port_priority;
-       uint16_t port;
-       uint8_t  state;
-       uint8_t  reserved[3];
-} PACKED;
-
-#define LACP_CMP_LEN (2 + 6 + 2 + 2 + 2)
-#define LACP_CP_LEN  (2 + 6 + 2 + 2 + 2 + 1)
-
-/* Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard) */
-struct slow_lacp {
-       uint8_t  subtype;                      /* = LACP(= 0x01) */
-       uint8_t  version_number;
-       uint8_t  tlv_type_actor_info;          /* = actor information(type/length/value) */
-#define LACP_TLV_TERMINATOR 0
-#define LACP_TLV_ACTOR      1
-#define LACP_TLV_PARTNER    2
-#define LACP_TLV_COLLECTOR  3
-       uint8_t  actor_information_length;     /* = 20 */
-       struct lacp_info actor;
-       uint8_t  tlv_type_partner_info;        /* = partner information */
-       uint8_t  partner_information_length;   /* = 20 */
-       struct lacp_info partner;
-       uint8_t  tlv_type_collector_info;      /* = collector information */
-       uint8_t  collector_information_length; /* = 16 */
-       uint16_t collector_max_delay;
-       uint8_t  reserved_12[12];
-       uint8_t  tlv_type_terminator;          /* = terminator */
-       uint8_t  terminator_length;            /* = 0 */ 
-       uint8_t  reserved_50[50];              /* = 0 */
-} PACKED;
-
-/* Marker Protocol Data Unit(PDU) structure(43.5.3.2 in the 802.3ad standard) */
-struct slow_marker {
-       uint8_t  subtype;                      /* = 0x02  (marker PDU) */
-       uint8_t  version_number;               /* = 0x01 */
-       uint8_t  tlv_type;
-#define MARKER_TLV_TERMINATOR 0                /* marker terminator */
-#define MARKER_TLV_INFO       1                       /* marker information */
-#define MARKER_TLV_RESPONSE   2                       /* marker response information */
-       uint8_t  marker_length;                /* = 0x16 */
-       uint16_t requester_port;               /* The number assigned to the port by the requester */
-       uint8_t  requester_system[ETH_ALEN];   /* The requester's system id */
-       uint32_t requester_transaction_id;     /* The transaction id allocated by the requester, */
-       uint16_t pad;                          /* = 0 */
-       uint8_t  tlv_type_terminator;          /* = 0x00 */
-       uint8_t  terminator_length;            /* = 0x00 */
-       uint8_t  reserved_90[90];              /* = 0 */
-} PACKED;
-
-union slow_union {
-       struct slow_header header;
-       struct slow_lacp lacp;
-       struct slow_marker marker;
-};
-
-#define FAST_PERIODIC_TIME   (1*TICKS_PER_SEC)
-#define SLOW_PERIODIC_TIME   (30*TICKS_PER_SEC)
-#define SHORT_TIMEOUT_TIME   (3*FAST_PERIODIC_TIME)
-#define LONG_TIMEOUT_TIME    (3*SLOW_PERIODIC_TIME)
-#define CHURN_DETECTION_TIME (60*TICKS_PER_SEC)
-#define AGGREGATE_WAIT_TIME  (2*TICKS_PER_SEC)
-
-#define LACP_ACTIVITY        (1 << 0)
-#define LACP_TIMEOUT         (1 << 1)
-#define LACP_AGGREGATION     (1 << 2)
-#define LACP_SYNCHRONIZATION (1 << 3)
-#define LACP_COLLECTING      (1 << 4)
-#define LACP_DISTRIBUTING    (1 << 5)
-#define LACP_DEFAULTED       (1 << 6)
-#define LACP_EXPIRED         (1 << 7)
-
-#define UNSELECTED 0
-#define STANDBY    1
-#define SELECTED   2
-
-
-struct lacp_state {
-       struct slow_lacp pkt;
-       unsigned long current_while_timer; /* Time when the LACP information expires */
-       unsigned long periodic_timer; /* Time when I need to send my partner an update */
-};
-
-static struct lacp_state lacp;
-
-
-#if LACP_DEBUG > 0
-static void print_lacp_state(uint8_t state)
-{
-       printf("%hhx", state);
-       if (state & LACP_ACTIVITY) {
-               printf(" Activity");
-       }
-       if (state & LACP_TIMEOUT) {
-               printf(" Timeout");
-       }
-       if (state & LACP_AGGREGATION) {
-               printf(" Aggregation");
-       }
-       if (state & LACP_SYNCHRONIZATION) {
-               printf(" Syncronization");
-       }
-       if (state & LACP_COLLECTING) {
-               printf(" Collecting");
-       }
-       if (state & LACP_DISTRIBUTING) {
-               printf(" Distributing");
-       }
-       if (state & LACP_DEFAULTED) {
-               printf(" Defaulted");
-       }
-       if (state & LACP_EXPIRED) {
-               printf(" Expired");
-       }
-       printf("\n");
-}
-
-static inline void print_lacpdu(struct slow_lacp *pkt)
-{
-       printf("subtype version:  %hhx %hhx\n", 
-               pkt->subtype, pkt->version_number);
-       printf("actor_tlv %hhx", pkt->tlv_type_actor_info);
-       printf(" len: %hhx (\n", pkt->actor_information_length);
-       printf(" sys_pri: %hx", ntohs(pkt->actor.system_priority));
-       printf(" mac: %!", pkt->actor.system);
-       printf(" key: %hx", ntohs(pkt->actor.key));
-       printf(" port_pri: %hx", ntohs(pkt->actor.port_priority));
-       printf(" port: %hx\n", ntohs(pkt->actor.port));
-       printf(" state: ");
-       print_lacp_state(pkt->actor.state);
-#if LACP_DEBUG > 1
-       printf(" reserved:     %hhx %hhx %hhx\n",
-               pkt->actor.reserved[0], pkt->actor.reserved[1], pkt->actor.reserved[2]);
-#endif
-       printf(")\n");
-       printf("partner_tlv: %hhx", pkt->tlv_type_partner_info);
-       printf(" len: %hhx (\n", pkt->partner_information_length);
-       printf(" sys_pri: %hx", ntohs(pkt->partner.system_priority));
-       printf(" mac: %!", pkt->partner.system);
-       printf(" key: %hx", ntohs(pkt->partner.key));
-       printf(" port_pri: %hx", ntohs(pkt->partner.port_priority));
-       printf(" port: %hx\n", ntohs(pkt->partner.port));
-       printf(" state: ");
-       print_lacp_state(pkt->partner.state);
-#if LACP_DEBUG > 1
-       printf(" reserved:     %hhx %hhx %hhx\n",
-               pkt->partner.reserved[0], pkt->partner.reserved[1], pkt->partner.reserved[2]);
-#endif
-       printf(")\n");
-       printf("collector_tlv: %hhx ", pkt->tlv_type_collector_info);
-       printf(" len: %hhx (", pkt->collector_information_length);
-       printf(" max_delay: %hx", ntohs(pkt->collector_max_delay));
-#if LACP_DEBUG > 1
-       printf("reserved_12:      %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n",
-               pkt->reserved_12[0], pkt->reserved_12[1], pkt->reserved_12[2], 
-               pkt->reserved_12[3], pkt->reserved_12[4], pkt->reserved_12[5], 
-               pkt->reserved_12[6], pkt->reserved_12[7], pkt->reserved_12[8], 
-               pkt->reserved_12[9], pkt->reserved_12[10], pkt->reserved_12[11]);
-#endif
-       printf(" )\n");
-       printf("terminator_tlv: %hhx", pkt->tlv_type_terminator);
-       printf(" len: %hhx ()\n", pkt->terminator_length);
-}
-
-static inline unsigned long lacp_timer_val(unsigned long now, unsigned long when)
-{
-       return when?(when - now)/TICKS_PER_SEC : 0;
-}
-static void print_lacp(const char *which, struct slow_lacp *pkt, unsigned long now)
-{
-       printf("%s\n", which);
-       print_lacpdu(pkt);
-       printf("timers: c %ds p %ds\n",
-               lacp_timer_val(now, lacp.current_while_timer),
-               lacp_timer_val(now, lacp.periodic_timer)
-               );
-       printf("\n");
-}
-#else /* LACP_DEBUG */
-#define print_lacp(which, pkt, now) do {} while(0)
-#endif /* LACP_DEBUG */
-
-static void lacp_init_state(const uint8_t *mac)
-{
-       memset(&lacp, 0, sizeof(lacp));
-
-       /* Initialize the packet constants */
-       lacp.pkt.subtype               = 1;
-       lacp.pkt.version_number        = 1;
-
-
-       /* The default state of my interface */
-       lacp.pkt.tlv_type_actor_info      = LACP_TLV_ACTOR;
-       lacp.pkt.actor_information_length = 0x14;
-       lacp.pkt.actor.system_priority    = htons(1);
-       memcpy(lacp.pkt.actor.system, mac, ETH_ALEN);
-       lacp.pkt.actor.key                = htons(1);
-       lacp.pkt.actor.port               = htons(1);
-       lacp.pkt.actor.port_priority      = htons(1);
-       lacp.pkt.actor.state = 
-               LACP_SYNCHRONIZATION |
-               LACP_COLLECTING      |
-               LACP_DISTRIBUTING    |
-               LACP_DEFAULTED;
-
-       /* Set my partner defaults */
-       lacp.pkt.tlv_type_partner_info      = LACP_TLV_PARTNER;
-       lacp.pkt.partner_information_length = 0x14;
-       lacp.pkt.partner.system_priority    = htons(1);
-       /* memset(lacp.pkt.parnter_system, 0, ETH_ALEN); */
-       lacp.pkt.partner.key                = htons(1);
-       lacp.pkt.partner.port               = htons(1);
-       lacp.pkt.partner.port_priority      = htons(1);
-       lacp.pkt.partner.state =
-               LACP_ACTIVITY        |
-               LACP_SYNCHRONIZATION |
-               LACP_COLLECTING      |
-               LACP_DISTRIBUTING    |
-               LACP_DEFAULTED;
-
-       lacp.pkt.tlv_type_collector_info      = LACP_TLV_COLLECTOR;
-       lacp.pkt.collector_information_length = 0x10;
-       lacp.pkt.collector_max_delay          = htons(0x8000); /* ???? */
-
-       lacp.pkt.tlv_type_terminator          = LACP_TLV_TERMINATOR;
-       lacp.pkt.terminator_length            = 0;
-}
-
-#define LACP_NTT_MASK (LACP_ACTIVITY | LACP_TIMEOUT | \
-       LACP_SYNCHRONIZATION | LACP_AGGREGATION)
-
-static inline int lacp_update_ntt(struct slow_lacp *pkt)
-{
-       int ntt = 0;
-       if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) != 0) ||
-               ((pkt->partner.state & LACP_NTT_MASK) != 
-                       (lacp.pkt.actor.state & LACP_NTT_MASK)))
-       {
-               ntt = 1;
-       }
-       return ntt;
-}
-
-static inline void lacp_record_pdu(struct slow_lacp *pkt)
-{
-       memcpy(&lacp.pkt.partner, &pkt->actor, LACP_CP_LEN);
-
-       lacp.pkt.actor.state &= ~LACP_DEFAULTED;
-       lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
-       if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) == 0) &&
-               ((pkt->partner.state & LACP_AGGREGATION) ==
-                       (lacp.pkt.actor.state & LACP_AGGREGATION)))
-       {
-               lacp.pkt.partner.state  |= LACP_SYNCHRONIZATION;
-       }
-       if (!(pkt->actor.state & LACP_AGGREGATION)) {
-               lacp.pkt.partner.state |= LACP_SYNCHRONIZATION;
-       }
-
-       /* ACTIVITY? */
-}
-
-static inline int lacp_timer_expired(unsigned long now, unsigned long when)
-{
-       return when && (now > when);
-}
-
-static inline void lacp_start_periodic_timer(unsigned long now)
-{
-       if ((lacp.pkt.partner.state & LACP_ACTIVITY) ||
-               (lacp.pkt.actor.state & LACP_ACTIVITY)) {
-               lacp.periodic_timer = now +
-                       (((lacp.pkt.partner.state & LACP_TIMEOUT)?
-                               FAST_PERIODIC_TIME : SLOW_PERIODIC_TIME));
-       }
-}
-
-static inline void lacp_start_current_while_timer(unsigned long now)
-{
-       lacp.current_while_timer = now +
-               ((lacp.pkt.actor.state & LACP_TIMEOUT) ?
-               SHORT_TIMEOUT_TIME : LONG_TIMEOUT_TIME);
-
-       lacp.pkt.actor.state &= ~LACP_EXPIRED;
-}
-
-static void send_lacp_reports(unsigned long now, int ntt)
-{
-       if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
-               lacp_init_state(nic.node_addr);
-       }
-       /* If the remote information has expired I need to take action */
-       if (lacp_timer_expired(now, lacp.current_while_timer)) {
-               if (!(lacp.pkt.actor.state & LACP_EXPIRED)) {
-                       lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
-                       lacp.pkt.partner.state |= LACP_TIMEOUT;
-                       lacp.pkt.actor.state |= LACP_EXPIRED;
-                       lacp.current_while_timer = now + SHORT_TIMEOUT_TIME;
-                       ntt = 1;
-               }
-               else {
-                       lacp_init_state(nic.node_addr);
-               }
-       }
-       /* If the periodic timer has expired I need to transmit */
-       if (lacp_timer_expired(now, lacp.periodic_timer)) {
-               ntt = 1;
-               /* Reset by lacp_start_periodic_timer */
-       }
-       if (ntt) {
-               eth_transmit(slow_dest, ETH_P_SLOW, sizeof(lacp.pkt), &lacp.pkt);
-
-               /* Restart the periodic timer */
-               lacp_start_periodic_timer(now);
-
-               print_lacp("Trasmitted", &lacp.pkt, now);
-       }
-}
-
-static inline void send_eth_slow_reports(unsigned long now)
-{
-       send_lacp_reports(now, 0);
-}
-
-static inline void process_eth_slow(unsigned short ptype, unsigned long now)
-{
-       union slow_union *pkt;
-       if ((ptype != ETH_P_SLOW) || 
-               (nic.packetlen < (ETH_HLEN + sizeof(pkt->header)))) {
-               return;
-       }
-       pkt = (union slow_union *)&nic.packet[ETH_HLEN];
-       if ((pkt->header.subtype == SLOW_SUBTYPE_LACP) &&
-               (nic.packetlen >= ETH_HLEN + sizeof(pkt->lacp))) {
-               int ntt;
-               if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
-                       lacp_init_state(nic.node_addr);
-               }
-               /* As long as nic.packet is 2 byte aligned all is good */
-               print_lacp("Received", &pkt->lacp, now);
-               /* I don't actually implement the MUX or SELECT
-                * machines.  
-                *
-                * What logically happens when the client and I
-                * disagree about an aggregator is the current
-                * aggregtator is unselected.  The MUX machine places
-                * me in DETACHED.  The SELECT machine runs and
-                * reslects the same aggregator.  If I go through
-                * these steps fast enough an outside observer can not
-                * notice this.  
-                *
-                * Since the process will not generate any noticeable
-                * effect it does not need an implmenetation.  This
-                * keeps the code simple and the code and binary
-                * size down.
-                */
-               /* lacp_update_selected(&pkt->lacp); */
-               ntt = lacp_update_ntt(&pkt->lacp);
-               lacp_record_pdu(&pkt->lacp);
-               lacp_start_current_while_timer(now);
-               send_lacp_reports(now, ntt);
-       }
-       /* If we receive a marker information packet return it */
-       else if ((pkt->header.subtype == SLOW_SUBTYPE_MARKER) &&
-               (nic.packetlen >= ETH_HLEN + sizeof(pkt->marker)) &&
-               (pkt->marker.tlv_type == MARKER_TLV_INFO) &&
-               (pkt->marker.marker_length == 0x16)) 
-       {
-               pkt->marker.tlv_type = MARKER_TLV_RESPONSE;
-               eth_transmit(slow_dest, ETH_P_SLOW, 
-                       sizeof(pkt->marker), &pkt->marker);
-       }
-
- }
-#else
-
-#define send_eth_slow_reports(now)    do {} while(0)
-#define process_eth_slow(ptype, now)  do {} while(0)
-
-#endif 
index a17da90..45d9d07 100644 (file)
@@ -176,6 +176,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ERRFILE_wpa_psk                        ( ERRFILE_NET | 0x00270000 )
 #define ERRFILE_wpa_tkip               ( ERRFILE_NET | 0x00280000 )
 #define ERRFILE_wpa_ccmp               ( ERRFILE_NET | 0x00290000 )
+#define ERRFILE_eth_slow               ( ERRFILE_NET | 0x002a0000 )
 
 #define ERRFILE_image                ( ERRFILE_IMAGE | 0x00000000 )
 #define ERRFILE_elf                  ( ERRFILE_IMAGE | 0x00010000 )
diff --git a/src/include/gpxe/eth_slow.h b/src/include/gpxe/eth_slow.h
new file mode 100644 (file)
index 0000000..0cbc575
--- /dev/null
@@ -0,0 +1,255 @@
+#ifndef _GPXE_ETH_SLOW_H
+#define _GPXE_ETH_SLOW_H
+
+/** @file
+ *
+ * Ethernet slow protocols
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** Slow protocols header */
+struct eth_slow_header {
+       /** Slow protocols subtype */
+       uint8_t subtype;
+       /** Subtype version number */
+       uint8_t version;
+} __attribute__ (( packed ));
+
+/** LACP subtype */
+#define ETH_SLOW_SUBTYPE_LACP 1
+
+/** LACP version number */
+#define ETH_SLOW_LACP_VERSION 1
+
+/** Marker subtype */
+#define ETH_SLOW_SUBTYPE_MARKER 2
+
+/** Marker version number */
+#define ETH_SLOW_MARKER_VERSION 1
+
+/** TLV (type, length, value) header */
+struct eth_slow_tlv_header {
+       /** Type
+        *
+        * This is an ETH_SLOW_TLV_XXX constant.
+        */
+       uint8_t type;
+       /** Length
+        *
+        * The length includes the TLV header (except for a TLV
+        * terminator, which has a length of zero).
+        */
+       uint8_t length;
+} __attribute__ (( packed ));
+
+/** Terminator type */
+#define ETH_SLOW_TLV_TERMINATOR 0
+
+/** Terminator length */
+#define ETH_SLOW_TLV_TERMINATOR_LEN 0
+
+/** LACP actor type */
+#define ETH_SLOW_TLV_LACP_ACTOR 1
+
+/** LACP actor length */
+#define ETH_SLOW_TLV_LACP_ACTOR_LEN \
+       ( sizeof ( struct eth_slow_lacp_entity_tlv ) )
+
+/** LACP partner type */
+#define ETH_SLOW_TLV_LACP_PARTNER 2
+
+/** LACP partner length */
+#define ETH_SLOW_TLV_LACP_PARTNER_LEN \
+       ( sizeof ( struct eth_slow_lacp_entity_tlv ) )
+
+/** LACP collector type */
+#define ETH_SLOW_TLV_LACP_COLLECTOR 3
+
+/** LACP collector length */
+#define ETH_SLOW_TLV_LACP_COLLECTOR_LEN \
+       ( sizeof ( struct eth_slow_lacp_collector_tlv ) )
+
+/** Marker request type */
+#define ETH_SLOW_TLV_MARKER_REQUEST 1
+
+/** Marker request length */
+#define ETH_SLOW_TLV_MARKER_REQUEST_LEN \
+       ( sizeof ( struct eth_slow_marker_tlv ) )
+
+/** Marker response type */
+#define ETH_SLOW_TLV_MARKER_RESPONSE 2
+
+/** Marker response length */
+#define ETH_SLOW_TLV_MARKER_RESPONSE_LEN \
+       ( sizeof ( struct eth_slow_marker_tlv ) )
+
+/** Terminator TLV */
+struct eth_slow_terminator_tlv {
+       /** TLV header */
+       struct eth_slow_tlv_header tlv;
+} __attribute__ (( packed ));
+
+/** LACP entity (actor or partner) TLV */
+struct eth_slow_lacp_entity_tlv {
+       /** TLV header */
+       struct eth_slow_tlv_header tlv;
+       /** System priority
+        *
+        * Used to determine the order in which ports are selected for
+        * aggregation.
+        */
+       uint16_t system_priority;
+       /** System identifier
+        *
+        * Used to uniquely identify the system (i.e. the entity with
+        * potentially multiple ports).
+        */
+       uint8_t system[ETH_ALEN];
+       /** Key
+        *
+        * Used to uniquely identify a group of aggregatable ports
+        * within a system.
+        */
+       uint16_t key;
+       /** Port priority
+        *
+        * Used to determine the order in which ports are selected for
+        * aggregation.
+        */
+       uint16_t port_priority;
+       /** Port identifier
+        *
+        * Used to uniquely identify a port within a system.
+        */
+       uint16_t port;
+       /** State
+        *
+        * This is the bitwise OR of zero or more LACP_STATE_XXX
+        * constants.
+        */
+       uint8_t state;
+       /** Reserved */
+       uint8_t reserved[3];
+} __attribute__ (( packed ));
+
+/** Maximum system priority */
+#define LACP_SYSTEM_PRIORITY_MAX 0xffff
+
+/** Maximum port priority */
+#define LACP_PORT_PRIORITY_MAX 0xff
+
+/** LACP entity is active
+ *
+ * Represented by the state character "A"/"a"
+ */
+#define LACP_STATE_ACTIVE 0x01
+
+/** LACP timeout is short
+ *
+ * Short timeout is one second, long timeout is 30s
+ *
+ * Represented by the state character "F"/"f"
+ */
+#define LACP_STATE_FAST 0x02
+
+/** LACP link is aggregateable
+ *
+ * Represented by the state characters "G"/"g"
+ */
+#define LACP_STATE_AGGREGATABLE 0x04
+
+/** LACP link is in synchronisation
+ *
+ * Represented by the state characters "S"/"s"
+ */
+#define LACP_STATE_IN_SYNC 0x08
+
+/** LACP link is collecting (receiving)
+ *
+ * Represented by the state characters "C"/"c"
+ */
+#define LACP_STATE_COLLECTING 0x10
+
+/** LACP link is distributing (transmitting)
+ *
+ * Represented by the state characters "D"/"d"
+ */
+#define LACP_STATE_DISTRIBUTING 0x20
+
+/** LACP entity is using defaulted partner information
+ *
+ * Represented by the state characters "L"/"l"
+ */
+#define LACP_STATE_DEFAULTED 0x40
+
+/** LACP entity receive state machine is in EXPIRED
+ *
+ * Represented by the state characters "X"/"x"
+ */
+#define LACP_STATE_EXPIRED 0x80
+
+/** LACP collector TLV */
+struct eth_slow_lacp_collector_tlv {
+       /** TLV header */
+       struct eth_slow_tlv_header tlv;
+       /** Maximum delay (in 10us increments) */
+       uint16_t max_delay;
+       /** Reserved */
+       uint8_t reserved[12];
+} __attribute__ (( packed ));
+
+/** Marker TLV */
+struct eth_slow_marker_tlv {
+       /** TLV header */
+       struct eth_slow_tlv_header tlv;
+       /** Requester port */
+       uint16_t port;
+       /** Requester system */
+       uint8_t system[ETH_ALEN];
+       /** Requester transaction ID */
+       uint32_t xact;
+       /** Padding */
+       uint16_t pad;
+} __attribute__ (( packed ));
+
+/** LACP packet */
+struct eth_slow_lacp {
+       /** Slow protocols header */
+       struct eth_slow_header header;
+       /** Actor information */
+       struct eth_slow_lacp_entity_tlv actor;
+       /** Partner information */
+       struct eth_slow_lacp_entity_tlv partner;
+       /** Collector information */
+       struct eth_slow_lacp_collector_tlv collector;
+       /** Terminator */
+       struct eth_slow_terminator_tlv terminator;
+       /** Reserved */
+       uint8_t reserved[50];
+} __attribute__ (( packed ));
+
+/** Marker packet */
+struct eth_slow_marker {
+       /** Slow protocols header */
+       struct eth_slow_header header;
+       /** Marker information */
+       struct eth_slow_marker_tlv marker;
+       /** Terminator */
+       struct eth_slow_terminator_tlv terminator;
+       /** Reserved */
+       uint8_t reserved[90];
+} __attribute__ (( packed ));
+
+/** Slow protocols packet */
+union eth_slow_packet {
+       /** Slow protocols header */
+       struct eth_slow_header header;
+       /** LACP packet */
+       struct eth_slow_lacp lacp;
+       /** Marker packet */
+       struct eth_slow_marker marker;
+} __attribute__ (( packed ));
+
+#endif /* _GPXE_ETH_SLOW_H */
diff --git a/src/net/eth_slow.c b/src/net/eth_slow.c
new file mode 100644 (file)
index 0000000..c108bd1
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/eth_slow.h>
+
+/** @file
+ *
+ * Ethernet slow protocols
+ *
+ * We implement a very simple passive LACP entity, that pretends that
+ * each port is the only port on an individual system.  We avoid the
+ * need for timeout logic (and retaining local state about our
+ * partner) by requesting the same timeout period (1s or 30s) as our
+ * partner requests, and then simply responding to every packet the
+ * partner sends us.
+ */
+
+struct net_protocol eth_slow_protocol;
+
+/** Slow protocols multicast address */
+static const uint8_t eth_slow_address[ETH_ALEN] =
+       { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
+
+/**
+ * Name LACP TLV type
+ *
+ * @v type             LACP TLV type
+ * @ret name           Name of LACP TLV type
+ */
+static inline __attribute__ (( always_inline )) const char *
+eth_slow_lacp_tlv_name ( uint8_t type ) {
+       switch ( type ) {
+       case ETH_SLOW_TLV_TERMINATOR:           return "terminator";
+       case ETH_SLOW_TLV_LACP_ACTOR:           return "actor";
+       case ETH_SLOW_TLV_LACP_PARTNER:         return "partner";
+       case ETH_SLOW_TLV_LACP_COLLECTOR:       return "collector";
+       default:                                return "<invalid>";
+       }
+}
+
+/**
+ * Name marker TLV type
+ *
+ * @v type             Marker TLV type
+ * @ret name           Name of marker TLV type
+ */
+static inline __attribute__ (( always_inline )) const char *
+eth_slow_marker_tlv_name ( uint8_t type ) {
+       switch ( type ) {
+       case ETH_SLOW_TLV_TERMINATOR:           return "terminator";
+       case ETH_SLOW_TLV_MARKER_REQUEST:       return "request";
+       case ETH_SLOW_TLV_MARKER_RESPONSE:      return "response";
+       default:                                return "<invalid>";
+       }
+}
+
+/**
+ * Name LACP state
+ *
+ * @v state            LACP state
+ * @ret name           LACP state name
+ */
+static const char * eth_slow_lacp_state_name ( uint8_t state ) {
+       static char state_chars[] = "AFGSRTLX";
+       unsigned int i;
+
+       for ( i = 0 ; i < 8 ; i++ ) {
+               state_chars[i] |= 0x20;
+               if ( state & ( 1 << i ) )
+                       state_chars[i] &= ~0x20;
+       }
+       return state_chars;
+}
+
+/**
+ * Dump LACP packet
+ *
+ * @v iobuf            I/O buffer
+ * @v netdev           Network device
+ * @v label            "RX" or "TX"
+ */
+static void eth_slow_lacp_dump ( struct io_buffer *iobuf,
+                                struct net_device *netdev,
+                                const char *label ) {
+       union eth_slow_packet *eth_slow = iobuf->data;
+       struct eth_slow_lacp *lacp = &eth_slow->lacp;
+
+       DBGC ( netdev,
+              "SLOW %p %s LACP actor (%04x,%s,%04x,%02x,%04x) [%s]\n",
+              netdev, label, ntohs ( lacp->actor.system_priority ),
+              eth_ntoa ( lacp->actor.system ),
+              ntohs ( lacp->actor.key ),
+              ntohs ( lacp->actor.port_priority ),
+              ntohs ( lacp->actor.port ),
+              eth_slow_lacp_state_name ( lacp->actor.state ) );
+       DBGC ( netdev,
+              "SLOW %p %s LACP partner (%04x,%s,%04x,%02x,%04x) [%s]\n",
+              netdev, label, ntohs ( lacp->partner.system_priority ),
+              eth_ntoa ( lacp->partner.system ),
+              ntohs ( lacp->partner.key ),
+              ntohs ( lacp->partner.port_priority ),
+              ntohs ( lacp->partner.port ),
+              eth_slow_lacp_state_name ( lacp->partner.state ) );
+       DBGC ( netdev, "SLOW %p %s LACP collector %04x (%d us)\n",
+              netdev, label, ntohs ( lacp->collector.max_delay ),
+              ( ntohs ( lacp->collector.max_delay ) * 10 ) );
+       DBGC2_HDA ( netdev, 0, iobuf, iob_len ( iobuf ) );
+}
+
+/**
+ * Process incoming LACP packet
+ *
+ * @v iobuf            I/O buffer
+ * @v netdev           Network device
+ * @ret rc             Return status code
+ */
+static int eth_slow_lacp_rx ( struct io_buffer *iobuf,
+                             struct net_device *netdev ) {
+       union eth_slow_packet *eth_slow = iobuf->data;
+       struct eth_slow_lacp *lacp = &eth_slow->lacp;
+
+       eth_slow_lacp_dump ( iobuf, netdev, "RX" );
+
+       /* Build response */
+       memset ( lacp->reserved, 0, sizeof ( lacp->reserved ) );
+       memset ( &lacp->terminator, 0, sizeof ( lacp->terminator ) );
+       memset ( &lacp->collector, 0, sizeof ( lacp->collector ) );
+       lacp->collector.tlv.type = ETH_SLOW_TLV_LACP_COLLECTOR;
+       lacp->collector.tlv.length = ETH_SLOW_TLV_LACP_COLLECTOR_LEN;
+       memcpy ( &lacp->partner, &lacp->actor, sizeof ( lacp->partner ) );
+       lacp->partner.tlv.type = ETH_SLOW_TLV_LACP_PARTNER;
+       lacp->partner.tlv.length = ETH_SLOW_TLV_LACP_PARTNER_LEN;
+       memset ( &lacp->partner.reserved, 0,
+                sizeof ( lacp->partner.reserved ) );
+       memset ( &lacp->actor, 0, sizeof ( lacp->actor ) );
+       lacp->actor.tlv.type = ETH_SLOW_TLV_LACP_ACTOR;
+       lacp->actor.tlv.length = ETH_SLOW_TLV_LACP_ACTOR_LEN;
+       lacp->actor.system_priority = htons ( LACP_SYSTEM_PRIORITY_MAX );
+       memcpy ( lacp->actor.system, netdev->ll_addr,
+                sizeof ( lacp->actor.system ) );
+       lacp->actor.key = htons ( 1 );
+       lacp->actor.port_priority = htons ( LACP_PORT_PRIORITY_MAX );
+       lacp->actor.port = htons ( 1 );
+       lacp->actor.state = ( LACP_STATE_IN_SYNC |
+                             LACP_STATE_COLLECTING |
+                             LACP_STATE_DISTRIBUTING |
+                             ( lacp->partner.state & LACP_STATE_FAST ) );
+       lacp->header.version = ETH_SLOW_LACP_VERSION;
+
+       /* Send response */
+       eth_slow_lacp_dump ( iobuf, netdev, "TX" );
+       return net_tx ( iobuf, netdev, &eth_slow_protocol, eth_slow_address );
+}
+
+/**
+ * Dump marker packet
+ *
+ * @v iobuf            I/O buffer
+ * @v netdev           Network device
+ * @v label            "RX" or "TX"
+ */
+static void eth_slow_marker_dump ( struct io_buffer *iobuf,
+                                  struct net_device *netdev,
+                                  const char *label ) {
+       union eth_slow_packet *eth_slow = iobuf->data;
+       struct eth_slow_marker *marker = &eth_slow->marker;
+
+       DBGC ( netdev, "SLOW %p %s marker %s port %04x system %s xact %08x\n",
+              netdev, label,
+              eth_slow_marker_tlv_name ( marker->marker.tlv.type ),
+              ntohs ( marker->marker.port ),
+              eth_ntoa ( marker->marker.system ),
+              ntohl ( marker->marker.xact ) );
+       DBGC2_HDA ( netdev, 0, iobuf, iob_len ( iobuf ) );
+}
+
+/**
+ * Process incoming marker packet
+ *
+ * @v iobuf            I/O buffer
+ * @v netdev           Network device
+ * @ret rc             Return status code
+ */
+static int eth_slow_marker_rx ( struct io_buffer *iobuf,
+                               struct net_device *netdev ) {
+       union eth_slow_packet *eth_slow = iobuf->data;
+       struct eth_slow_marker *marker = &eth_slow->marker;
+
+       eth_slow_marker_dump ( iobuf, netdev, "RX" );
+
+       if ( marker->marker.tlv.type == ETH_SLOW_TLV_MARKER_REQUEST ) {
+               /* Send marker response */
+               marker->marker.tlv.type = ETH_SLOW_TLV_MARKER_RESPONSE;
+               eth_slow_marker_dump ( iobuf, netdev, "TX" );
+               return net_tx ( iobuf, netdev, &eth_slow_protocol,
+                               eth_slow_address );
+       } else {
+               /* Discard all other marker packets */
+               free_iob ( iobuf );
+               return -EINVAL;
+       }
+}
+
+/**
+ * Process incoming slow packet
+ *
+ * @v iobuf            I/O buffer
+ * @v netdev           Network device
+ * @v ll_source                Link-layer source address
+ * @ret rc             Return status code
+ */
+static int eth_slow_rx ( struct io_buffer *iobuf,
+                        struct net_device *netdev,
+                        const void *ll_source __unused ) {
+       union eth_slow_packet *eth_slow = iobuf->data;
+
+       /* Sanity checks */
+       if ( iob_len ( iobuf ) < sizeof ( *eth_slow ) ) {
+               free_iob ( iobuf );
+               return -EINVAL;
+       }
+
+       /* Handle according to subtype */
+       switch ( eth_slow->header.subtype ) {
+       case ETH_SLOW_SUBTYPE_LACP:
+               return eth_slow_lacp_rx ( iobuf, netdev );
+       case ETH_SLOW_SUBTYPE_MARKER:
+               return eth_slow_marker_rx ( iobuf, netdev );
+       default:
+               DBGC ( netdev, "SLOW %p RX unknown subtype %02x\n",
+                      netdev, eth_slow->header.subtype );
+               free_iob ( iobuf );
+               return -EINVAL;
+       }
+}
+
+/** Slow protocol */
+struct net_protocol eth_slow_protocol __net_protocol = {
+       .name = "Slow",
+       .net_proto = htons ( ETH_P_SLOW ),
+       .rx = eth_slow_rx,
+};
index 79ed1dc..0056db9 100644 (file)
@@ -191,3 +191,6 @@ struct net_device * alloc_etherdev ( size_t priv_size ) {
        }
        return netdev;
 }
+
+/* Drag in Ethernet slow protocols */
+REQUIRE_OBJECT ( eth_slow );