iPXE
eth_slow.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <byteswap.h>
00029 #include <errno.h>
00030 #include <ipxe/timer.h>
00031 #include <ipxe/iobuf.h>
00032 #include <ipxe/netdevice.h>
00033 #include <ipxe/if_ether.h>
00034 #include <ipxe/ethernet.h>
00035 #include <ipxe/eth_slow.h>
00036 
00037 /** @file
00038  *
00039  * Ethernet slow protocols
00040  *
00041  * We implement a very simple passive LACP entity, that pretends that
00042  * each port is the only port on an individual system.  We avoid the
00043  * need for timeout logic (and retaining local state about our
00044  * partner) by requesting the same timeout period (1s or 30s) as our
00045  * partner requests, and then simply responding to every packet the
00046  * partner sends us.
00047  */
00048 
00049 struct net_protocol eth_slow_protocol __net_protocol;
00050 
00051 /** Slow protocols multicast address */
00052 static const uint8_t eth_slow_address[ETH_ALEN] =
00053         { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
00054 
00055 /**
00056  * Name LACP TLV type
00057  *
00058  * @v type              LACP TLV type
00059  * @ret name            Name of LACP TLV type
00060  */
00061 static inline __attribute__ (( always_inline )) const char *
00062 eth_slow_lacp_tlv_name ( uint8_t type ) {
00063         switch ( type ) {
00064         case ETH_SLOW_TLV_TERMINATOR:           return "terminator";
00065         case ETH_SLOW_TLV_LACP_ACTOR:           return "actor";
00066         case ETH_SLOW_TLV_LACP_PARTNER:         return "partner";
00067         case ETH_SLOW_TLV_LACP_COLLECTOR:       return "collector";
00068         default:                                return "<invalid>";
00069         }
00070 }
00071 
00072 /**
00073  * Name marker TLV type
00074  *
00075  * @v type              Marker TLV type
00076  * @ret name            Name of marker TLV type
00077  */
00078 static inline __attribute__ (( always_inline )) const char *
00079 eth_slow_marker_tlv_name ( uint8_t type ) {
00080         switch ( type ) {
00081         case ETH_SLOW_TLV_TERMINATOR:           return "terminator";
00082         case ETH_SLOW_TLV_MARKER_REQUEST:       return "request";
00083         case ETH_SLOW_TLV_MARKER_RESPONSE:      return "response";
00084         default:                                return "<invalid>";
00085         }
00086 }
00087 
00088 /**
00089  * Name LACP state
00090  *
00091  * @v state             LACP state
00092  * @ret name            LACP state name
00093  */
00094 static const char * eth_slow_lacp_state_name ( uint8_t state ) {
00095         static char state_chars[] = "AFGSCDLX";
00096         unsigned int i;
00097 
00098         for ( i = 0 ; i < 8 ; i++ ) {
00099                 state_chars[i] |= 0x20;
00100                 if ( state & ( 1 << i ) )
00101                         state_chars[i] &= ~0x20;
00102         }
00103         return state_chars;
00104 }
00105 
00106 /**
00107  * Dump LACP packet
00108  *
00109  * @v iobuf             I/O buffer
00110  * @v netdev            Network device
00111  * @v label             "RX" or "TX"
00112  */
00113 static void eth_slow_lacp_dump ( struct io_buffer *iobuf,
00114                                  struct net_device *netdev,
00115                                  const char *label ) {
00116         union eth_slow_packet *eth_slow = iobuf->data;
00117         struct eth_slow_lacp *lacp = &eth_slow->lacp;
00118 
00119         DBGC ( netdev,
00120                "SLOW %s %s LACP actor (%04x,%s,%04x,%02x,%04x) [%s]\n",
00121                netdev->name, label, ntohs ( lacp->actor.system_priority ),
00122                eth_ntoa ( lacp->actor.system ),
00123                ntohs ( lacp->actor.key ),
00124                ntohs ( lacp->actor.port_priority ),
00125                ntohs ( lacp->actor.port ),
00126                eth_slow_lacp_state_name ( lacp->actor.state ) );
00127         DBGC ( netdev,
00128                "SLOW %s %s LACP partner (%04x,%s,%04x,%02x,%04x) [%s]\n",
00129                netdev->name, label, ntohs ( lacp->partner.system_priority ),
00130                eth_ntoa ( lacp->partner.system ),
00131                ntohs ( lacp->partner.key ),
00132                ntohs ( lacp->partner.port_priority ),
00133                ntohs ( lacp->partner.port ),
00134                eth_slow_lacp_state_name ( lacp->partner.state ) );
00135         DBGC ( netdev, "SLOW %s %s LACP collector %04x (%d us)\n",
00136                netdev->name, label, ntohs ( lacp->collector.max_delay ),
00137                ( ntohs ( lacp->collector.max_delay ) * 10 ) );
00138         DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
00139 }
00140 
00141 /**
00142  * Process incoming LACP packet
00143  *
00144  * @v iobuf             I/O buffer
00145  * @v netdev            Network device
00146  * @ret rc              Return status code
00147  */
00148 static int eth_slow_lacp_rx ( struct io_buffer *iobuf,
00149                               struct net_device *netdev ) {
00150         union eth_slow_packet *eth_slow = iobuf->data;
00151         struct eth_slow_lacp *lacp = &eth_slow->lacp;
00152         unsigned int interval;
00153 
00154         eth_slow_lacp_dump ( iobuf, netdev, "RX" );
00155 
00156         /* If partner is not in sync, collecting, and distributing,
00157          * then block the link until after the next expected LACP
00158          * packet.
00159          */
00160         if ( ~lacp->actor.state & ( LACP_STATE_IN_SYNC |
00161                                     LACP_STATE_COLLECTING |
00162                                     LACP_STATE_DISTRIBUTING ) ) {
00163                 DBGC ( netdev, "SLOW %s LACP partner is down\n", netdev->name );
00164                 interval = ( ( lacp->actor.state & LACP_STATE_FAST ) ?
00165                              ( ( LACP_INTERVAL_FAST + 1 ) * TICKS_PER_SEC ) :
00166                              ( ( LACP_INTERVAL_SLOW + 1 ) * TICKS_PER_SEC ) );
00167                 netdev_link_block ( netdev, interval );
00168         } else {
00169                 if ( netdev_link_blocked ( netdev ) ) {
00170                         DBGC ( netdev, "SLOW %s LACP partner is up\n",
00171                                netdev->name );
00172                 }
00173                 netdev_link_unblock ( netdev );
00174         }
00175 
00176         /* Build response */
00177         memset ( lacp->reserved, 0, sizeof ( lacp->reserved ) );
00178         memset ( &lacp->terminator, 0, sizeof ( lacp->terminator ) );
00179         memset ( &lacp->collector, 0, sizeof ( lacp->collector ) );
00180         lacp->collector.tlv.type = ETH_SLOW_TLV_LACP_COLLECTOR;
00181         lacp->collector.tlv.length = ETH_SLOW_TLV_LACP_COLLECTOR_LEN;
00182         memcpy ( &lacp->partner, &lacp->actor, sizeof ( lacp->partner ) );
00183         lacp->partner.tlv.type = ETH_SLOW_TLV_LACP_PARTNER;
00184         lacp->partner.tlv.length = ETH_SLOW_TLV_LACP_PARTNER_LEN;
00185         memset ( &lacp->partner.reserved, 0,
00186                  sizeof ( lacp->partner.reserved ) );
00187         memset ( &lacp->actor, 0, sizeof ( lacp->actor ) );
00188         lacp->actor.tlv.type = ETH_SLOW_TLV_LACP_ACTOR;
00189         lacp->actor.tlv.length = ETH_SLOW_TLV_LACP_ACTOR_LEN;
00190         lacp->actor.system_priority = htons ( LACP_SYSTEM_PRIORITY_MAX );
00191         memcpy ( lacp->actor.system, netdev->ll_addr,
00192                  sizeof ( lacp->actor.system ) );
00193         lacp->actor.key = htons ( 1 );
00194         lacp->actor.port_priority = htons ( LACP_PORT_PRIORITY_MAX );
00195         lacp->actor.port = htons ( 1 );
00196         lacp->actor.state = ( LACP_STATE_AGGREGATABLE |
00197                               LACP_STATE_IN_SYNC |
00198                               LACP_STATE_COLLECTING |
00199                               LACP_STATE_DISTRIBUTING |
00200                               ( lacp->partner.state & LACP_STATE_FAST ) );
00201         lacp->header.version = ETH_SLOW_LACP_VERSION;
00202 
00203         /* Send response */
00204         eth_slow_lacp_dump ( iobuf, netdev, "TX" );
00205         return net_tx ( iobuf, netdev, &eth_slow_protocol, eth_slow_address,
00206                         netdev->ll_addr );
00207 }
00208 
00209 /**
00210  * Dump marker packet
00211  *
00212  * @v iobuf             I/O buffer
00213  * @v netdev            Network device
00214  * @v label             "RX" or "TX"
00215  */
00216 static void eth_slow_marker_dump ( struct io_buffer *iobuf,
00217                                    struct net_device *netdev,
00218                                    const char *label ) {
00219         union eth_slow_packet *eth_slow = iobuf->data;
00220         struct eth_slow_marker *marker = &eth_slow->marker;
00221 
00222         DBGC ( netdev, "SLOW %s %s marker %s port %04x system %s xact %08x\n",
00223                netdev->name, label,
00224                eth_slow_marker_tlv_name ( marker->marker.tlv.type ),
00225                ntohs ( marker->marker.port ),
00226                eth_ntoa ( marker->marker.system ),
00227                ntohl ( marker->marker.xact ) );
00228         DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
00229 }
00230 
00231 /**
00232  * Process incoming marker packet
00233  *
00234  * @v iobuf             I/O buffer
00235  * @v netdev            Network device
00236  * @ret rc              Return status code
00237  */
00238 static int eth_slow_marker_rx ( struct io_buffer *iobuf,
00239                                 struct net_device *netdev ) {
00240         union eth_slow_packet *eth_slow = iobuf->data;
00241         struct eth_slow_marker *marker = &eth_slow->marker;
00242 
00243         eth_slow_marker_dump ( iobuf, netdev, "RX" );
00244 
00245         if ( marker->marker.tlv.type == ETH_SLOW_TLV_MARKER_REQUEST ) {
00246                 /* Send marker response */
00247                 marker->marker.tlv.type = ETH_SLOW_TLV_MARKER_RESPONSE;
00248                 eth_slow_marker_dump ( iobuf, netdev, "TX" );
00249                 return net_tx ( iobuf, netdev, &eth_slow_protocol,
00250                                 eth_slow_address, netdev->ll_addr );
00251         } else {
00252                 /* Discard all other marker packets */
00253                 free_iob ( iobuf );
00254                 return -EINVAL;
00255         }
00256 }
00257 
00258 /**
00259  * Process incoming slow packet
00260  *
00261  * @v iobuf             I/O buffer
00262  * @v netdev            Network device
00263  * @v ll_dest           Link-layer destination address
00264  * @v ll_source         Link-layer source address
00265  * @v flags             Packet flags
00266  * @ret rc              Return status code
00267  */
00268 static int eth_slow_rx ( struct io_buffer *iobuf,
00269                          struct net_device *netdev,
00270                          const void *ll_dest __unused,
00271                          const void *ll_source __unused,
00272                          unsigned int flags __unused ) {
00273         union eth_slow_packet *eth_slow = iobuf->data;
00274 
00275         /* Sanity checks */
00276         if ( iob_len ( iobuf ) < sizeof ( *eth_slow ) ) {
00277                 free_iob ( iobuf );
00278                 return -EINVAL;
00279         }
00280 
00281         /* Handle according to subtype */
00282         switch ( eth_slow->header.subtype ) {
00283         case ETH_SLOW_SUBTYPE_LACP:
00284                 return eth_slow_lacp_rx ( iobuf, netdev );
00285         case ETH_SLOW_SUBTYPE_MARKER:
00286                 return eth_slow_marker_rx ( iobuf, netdev );
00287         default:
00288                 DBGC ( netdev, "SLOW %s RX unknown subtype %02x\n",
00289                        netdev->name, eth_slow->header.subtype );
00290                 free_iob ( iobuf );
00291                 return -EINVAL;
00292         }
00293 }
00294 
00295 /** Slow protocol */
00296 struct net_protocol eth_slow_protocol __net_protocol = {
00297         .name = "Slow",
00298         .net_proto = htons ( ETH_P_SLOW ),
00299         .rx = eth_slow_rx,
00300 };