iPXE
|
00001 /* 00002 * Copyright (C) 2006 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 <stdint.h> 00027 #include <stdlib.h> 00028 #include <stdio.h> 00029 #include <string.h> 00030 #include <byteswap.h> 00031 #include <errno.h> 00032 #include <assert.h> 00033 #include <ipxe/if_arp.h> 00034 #include <ipxe/if_ether.h> 00035 #include <ipxe/in.h> 00036 #include <ipxe/netdevice.h> 00037 #include <ipxe/iobuf.h> 00038 #include <ipxe/ethernet.h> 00039 00040 /** @file 00041 * 00042 * Ethernet protocol 00043 * 00044 */ 00045 00046 /** Ethernet broadcast MAC address */ 00047 uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 00048 00049 /** 00050 * Check if Ethernet packet has an 802.3 LLC header 00051 * 00052 * @v ethhdr Ethernet header 00053 * @ret is_llc Packet has 802.3 LLC header 00054 */ 00055 static inline int eth_is_llc_packet ( struct ethhdr *ethhdr ) { 00056 uint8_t len_msb; 00057 00058 /* Check if the protocol field contains a value short enough 00059 * to be a frame length. The slightly convoluted form of the 00060 * comparison is designed to reduce to a single x86 00061 * instruction. 00062 */ 00063 len_msb = *( ( uint8_t * ) ðhdr->h_protocol ); 00064 return ( len_msb < 0x06 ); 00065 } 00066 00067 /** 00068 * Add Ethernet link-layer header 00069 * 00070 * @v netdev Network device 00071 * @v iobuf I/O buffer 00072 * @v ll_dest Link-layer destination address 00073 * @v ll_source Source link-layer address 00074 * @v net_proto Network-layer protocol, in network-byte order 00075 * @ret rc Return status code 00076 */ 00077 int eth_push ( struct net_device *netdev __unused, struct io_buffer *iobuf, 00078 const void *ll_dest, const void *ll_source, 00079 uint16_t net_proto ) { 00080 struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) ); 00081 00082 /* Build Ethernet header */ 00083 memcpy ( ethhdr->h_dest, ll_dest, ETH_ALEN ); 00084 memcpy ( ethhdr->h_source, ll_source, ETH_ALEN ); 00085 ethhdr->h_protocol = net_proto; 00086 00087 return 0; 00088 } 00089 00090 /** 00091 * Remove Ethernet link-layer header 00092 * 00093 * @v netdev Network device 00094 * @v iobuf I/O buffer 00095 * @ret ll_dest Link-layer destination address 00096 * @ret ll_source Source link-layer address 00097 * @ret net_proto Network-layer protocol, in network-byte order 00098 * @ret flags Packet flags 00099 * @ret rc Return status code 00100 */ 00101 int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf, 00102 const void **ll_dest, const void **ll_source, 00103 uint16_t *net_proto, unsigned int *flags ) { 00104 struct ethhdr *ethhdr = iobuf->data; 00105 uint16_t *llc_proto; 00106 00107 /* Sanity check. While in theory we could receive a one-byte 00108 * packet, this will never happen in practice and performing 00109 * the combined length check here avoids the need for an 00110 * additional comparison if we detect an LLC frame. 00111 */ 00112 if ( iob_len ( iobuf ) < ( sizeof ( *ethhdr ) + sizeof ( *llc_proto ))){ 00113 DBG ( "Ethernet packet too short (%zd bytes)\n", 00114 iob_len ( iobuf ) ); 00115 return -EINVAL; 00116 } 00117 00118 /* Strip off Ethernet header */ 00119 iob_pull ( iobuf, sizeof ( *ethhdr ) ); 00120 00121 /* Fill in required fields */ 00122 *ll_dest = ethhdr->h_dest; 00123 *ll_source = ethhdr->h_source; 00124 *net_proto = ethhdr->h_protocol; 00125 *flags = ( ( is_multicast_ether_addr ( ethhdr->h_dest ) ? 00126 LL_MULTICAST : 0 ) | 00127 ( is_broadcast_ether_addr ( ethhdr->h_dest ) ? 00128 LL_BROADCAST : 0 ) ); 00129 00130 /* If this is an LLC frame (with a length in place of the 00131 * protocol field), then use the next two bytes (which happen 00132 * to be the LLC DSAP and SSAP) as the protocol. This allows 00133 * for minimal-overhead support for receiving (rare) LLC 00134 * frames, without requiring a full LLC protocol layer. 00135 */ 00136 if ( eth_is_llc_packet ( ethhdr ) ) { 00137 llc_proto = iobuf->data; 00138 *net_proto = *llc_proto; 00139 } 00140 00141 return 0; 00142 } 00143 00144 /** 00145 * Initialise Ethernet address 00146 * 00147 * @v hw_addr Hardware address 00148 * @v ll_addr Link-layer address 00149 */ 00150 void eth_init_addr ( const void *hw_addr, void *ll_addr ) { 00151 memcpy ( ll_addr, hw_addr, ETH_ALEN ); 00152 } 00153 00154 /** 00155 * Generate random Ethernet address 00156 * 00157 * @v hw_addr Generated hardware address 00158 */ 00159 void eth_random_addr ( void *hw_addr ) { 00160 uint8_t *addr = hw_addr; 00161 unsigned int i; 00162 00163 for ( i = 0 ; i < ETH_ALEN ; i++ ) 00164 addr[i] = random(); 00165 addr[0] &= ~0x01; /* Clear multicast bit */ 00166 addr[0] |= 0x02; /* Set locally-assigned bit */ 00167 } 00168 00169 /** 00170 * Transcribe Ethernet address 00171 * 00172 * @v ll_addr Link-layer address 00173 * @ret string Link-layer address in human-readable format 00174 */ 00175 const char * eth_ntoa ( const void *ll_addr ) { 00176 static char buf[18]; /* "00:00:00:00:00:00" */ 00177 const uint8_t *eth_addr = ll_addr; 00178 00179 sprintf ( buf, "%02x:%02x:%02x:%02x:%02x:%02x", 00180 eth_addr[0], eth_addr[1], eth_addr[2], 00181 eth_addr[3], eth_addr[4], eth_addr[5] ); 00182 return buf; 00183 } 00184 00185 /** 00186 * Hash multicast address 00187 * 00188 * @v af Address family 00189 * @v net_addr Network-layer address 00190 * @v ll_addr Link-layer address to fill in 00191 * @ret rc Return status code 00192 */ 00193 int eth_mc_hash ( unsigned int af, const void *net_addr, void *ll_addr ) { 00194 const uint8_t *net_addr_bytes = net_addr; 00195 uint8_t *ll_addr_bytes = ll_addr; 00196 00197 switch ( af ) { 00198 case AF_INET: 00199 ll_addr_bytes[0] = 0x01; 00200 ll_addr_bytes[1] = 0x00; 00201 ll_addr_bytes[2] = 0x5e; 00202 ll_addr_bytes[3] = net_addr_bytes[1] & 0x7f; 00203 ll_addr_bytes[4] = net_addr_bytes[2]; 00204 ll_addr_bytes[5] = net_addr_bytes[3]; 00205 return 0; 00206 case AF_INET6: 00207 ll_addr_bytes[0] = 0x33; 00208 ll_addr_bytes[1] = 0x33; 00209 memcpy ( &ll_addr_bytes[2], &net_addr_bytes[12], 4 ); 00210 return 0; 00211 default: 00212 return -ENOTSUP; 00213 } 00214 } 00215 00216 /** 00217 * Generate Ethernet-compatible compressed link-layer address 00218 * 00219 * @v ll_addr Link-layer address 00220 * @v eth_addr Ethernet-compatible address to fill in 00221 */ 00222 int eth_eth_addr ( const void *ll_addr, void *eth_addr ) { 00223 memcpy ( eth_addr, ll_addr, ETH_ALEN ); 00224 return 0; 00225 } 00226 00227 /** 00228 * Generate EUI-64 address 00229 * 00230 * @v ll_addr Link-layer address 00231 * @v eui64 EUI-64 address to fill in 00232 * @ret rc Return status code 00233 */ 00234 int eth_eui64 ( const void *ll_addr, void *eui64 ) { 00235 00236 memcpy ( ( eui64 + 0 ), ( ll_addr + 0 ), 3 ); 00237 memcpy ( ( eui64 + 5 ), ( ll_addr + 3 ), 3 ); 00238 *( ( uint16_t * ) ( eui64 + 3 ) ) = htons ( 0xfffe ); 00239 return 0; 00240 } 00241 00242 /** Ethernet protocol */ 00243 struct ll_protocol ethernet_protocol __ll_protocol = { 00244 .name = "Ethernet", 00245 .ll_proto = htons ( ARPHRD_ETHER ), 00246 .hw_addr_len = ETH_ALEN, 00247 .ll_addr_len = ETH_ALEN, 00248 .ll_header_len = ETH_HLEN, 00249 .push = eth_push, 00250 .pull = eth_pull, 00251 .init_addr = eth_init_addr, 00252 .ntoa = eth_ntoa, 00253 .mc_hash = eth_mc_hash, 00254 .eth_addr = eth_eth_addr, 00255 .eui64 = eth_eui64, 00256 }; 00257 00258 /** 00259 * Allocate Ethernet device 00260 * 00261 * @v priv_size Size of driver private data 00262 * @ret netdev Network device, or NULL 00263 */ 00264 struct net_device * alloc_etherdev ( size_t priv_size ) { 00265 struct net_device *netdev; 00266 00267 netdev = alloc_netdev ( priv_size ); 00268 if ( netdev ) { 00269 netdev->ll_protocol = ðernet_protocol; 00270 netdev->ll_broadcast = eth_broadcast; 00271 netdev->max_pkt_len = ETH_FRAME_LEN; 00272 netdev->mtu = ETH_MAX_MTU; 00273 } 00274 return netdev; 00275 } 00276 00277 /* Drag in objects via ethernet_protocol */ 00278 REQUIRING_SYMBOL ( ethernet_protocol ); 00279 00280 /* Drag in Ethernet configuration */ 00281 REQUIRE_OBJECT ( config_ethernet );