mirror of
https://github.com/xcat2/xNBA.git
synced 2024-11-25 02:50:17 +00:00
[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 <mcb30@ipxe.org>
This commit is contained in:
parent
2d9a303248
commit
84996b7b09
@ -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
|
@ -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 )
|
||||
|
255
src/include/ipxe/eth_slow.h
Normal file
255
src/include/ipxe/eth_slow.h
Normal file
@ -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 */
|
267
src/net/eth_slow.c
Normal file
267
src/net/eth_slow.c
Normal file
@ -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 <ipxe/iobuf.h>
|
||||
#include <ipxe/netdevice.h>
|
||||
#include <ipxe/if_ether.h>
|
||||
#include <ipxe/ethernet.h>
|
||||
#include <ipxe/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 = ð_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,
|
||||
};
|
@ -191,3 +191,6 @@ struct net_device * alloc_etherdev ( size_t priv_size ) {
|
||||
}
|
||||
return netdev;
|
||||
}
|
||||
|
||||
/* Drag in Ethernet slow protocols */
|
||||
REQUIRE_OBJECT ( eth_slow );
|
||||
|
Loading…
Reference in New Issue
Block a user