From 84996b7b09341b3cb4b93fdae4bdb2cb217045c7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 May 2010 16:30:10 +0100 Subject: [PATCH] [lacp] Add simple LACP implementation 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 --- src/core/proto_eth_slow.c | 406 ------------------------------------ src/include/ipxe/errfile.h | 1 + src/include/ipxe/eth_slow.h | 255 ++++++++++++++++++++++ src/net/eth_slow.c | 267 ++++++++++++++++++++++++ src/net/ethernet.c | 3 + 5 files changed, 526 insertions(+), 406 deletions(-) delete mode 100644 src/core/proto_eth_slow.c create mode 100644 src/include/ipxe/eth_slow.h create mode 100644 src/net/eth_slow.c diff --git a/src/core/proto_eth_slow.c b/src/core/proto_eth_slow.c deleted file mode 100644 index b759a713..00000000 --- a/src/core/proto_eth_slow.c +++ /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 diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 736ce464..54e54619 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -171,6 +171,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/ipxe/eth_slow.h b/src/include/ipxe/eth_slow.h new file mode 100644 index 00000000..00509197 --- /dev/null +++ b/src/include/ipxe/eth_slow.h @@ -0,0 +1,255 @@ +#ifndef _IPXE_ETH_SLOW_H +#define _IPXE_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 /* _IPXE_ETH_SLOW_H */ diff --git a/src/net/eth_slow.c b/src/net/eth_slow.c new file mode 100644 index 00000000..86fe62c3 --- /dev/null +++ b/src/net/eth_slow.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2010 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/** @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 ""; + } +} + +/** + * 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 ""; + } +} + +/** + * 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 = ð_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 = ð_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, ð_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 = ð_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 = ð_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, ð_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, +}; diff --git a/src/net/ethernet.c b/src/net/ethernet.c index 5c5936f4..d14cfefc 100644 --- a/src/net/ethernet.c +++ b/src/net/ethernet.c @@ -191,3 +191,6 @@ struct net_device * alloc_etherdev ( size_t priv_size ) { } return netdev; } + +/* Drag in Ethernet slow protocols */ +REQUIRE_OBJECT ( eth_slow );