2005-04-17 02:20:36 +04:00
/*
* INET An implementation of the TCP / IP protocol suite for the LINUX
* operating system . INET is implemented using the BSD Socket
* interface as the means of communication with the user level .
*
* Ethernet - type device handling .
*
* Version : @ ( # ) eth . c 1.0 .7 05 / 25 / 93
*
2005-05-06 03:16:16 +04:00
* Authors : Ross Biro
2005-04-17 02:20:36 +04:00
* Fred N . van Kempen , < waltje @ uWalt . NL . Mugnet . ORG >
* Mark Evans , < evansmp @ uhura . aston . ac . uk >
* Florian La Roche , < rzsfl @ rz . uni - sb . de >
* Alan Cox , < gw4pts @ gw4pts . ampr . org >
2007-02-09 17:24:44 +03:00
*
2005-04-17 02:20:36 +04:00
* Fixes :
* Mr Linux : Arp problems
* Alan Cox : Generic queue tidyup ( very tiny here )
* Alan Cox : eth_header ntohs should be htons
* Alan Cox : eth_rebuild_header missing an htons and
* minor other things .
2007-02-09 17:24:44 +03:00
* Tegge : Arp bug fixes .
2005-04-17 02:20:36 +04:00
* Florian : Removed many unnecessary functions , code cleanup
* and changes for new arp and skbuff .
* Alan Cox : Redid header building to reflect new format .
* Alan Cox : ARP only when compiled with CONFIG_INET
* Greg Page : 802.2 and SNAP stuff .
* Alan Cox : MAC layer pointers / new format .
* Paul Gortmaker : eth_copy_and_sum shouldn ' t csum padding .
* Alan Cox : Protect against forwarding explosions with
* older network drivers and IFF_ALLMULTI .
* Christer Weinigel : Better rebuild header message .
* Andrew Morton : 26F eb01 : kill ether_setup ( ) - use netdev_boot_setup ( ) .
*
* 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 ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/socket.h>
# include <linux/in.h>
# include <linux/inet.h>
# include <linux/ip.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/errno.h>
# include <linux/init.h>
2006-01-06 03:35:42 +03:00
# include <linux/if_ether.h>
2005-04-17 02:20:36 +04:00
# include <net/dst.h>
# include <net/arp.h>
# include <net/sock.h>
# include <net/ipv6.h>
# include <net/ip.h>
2008-10-07 17:45:02 +04:00
# include <net/dsa.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/system.h>
__setup ( " ether= " , netdev_boot_setup ) ;
2006-08-18 05:18:53 +04:00
/**
* eth_header - create the Ethernet header
* @ skb : buffer to alter
* @ dev : source device
* @ type : Ethernet type field
* @ daddr : destination address ( NULL leave destination address )
* @ saddr : source address ( NULL use device source address )
* @ len : packet length ( < = skb - > len )
2005-04-17 02:20:36 +04:00
*
2006-08-18 05:18:53 +04:00
*
* Set the protocol type . For a packet of type ETH_P_802_3 we put the length
* in here instead . It is up to the 802.2 layer to carry protocol information .
2005-04-17 02:20:36 +04:00
*/
2007-10-09 12:40:57 +04:00
int eth_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type ,
const void * daddr , const void * saddr , unsigned len )
2005-04-17 02:20:36 +04:00
{
2006-08-18 05:20:18 +04:00
struct ethhdr * eth = ( struct ethhdr * ) skb_push ( skb , ETH_HLEN ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:20:18 +04:00
if ( type ! = ETH_P_802_3 )
2005-04-17 02:20:36 +04:00
eth - > h_proto = htons ( type ) ;
else
eth - > h_proto = htons ( len ) ;
/*
2006-08-18 05:20:18 +04:00
* Set the source hardware address .
2005-04-17 02:20:36 +04:00
*/
2006-08-18 05:20:18 +04:00
if ( ! saddr )
2005-06-29 02:49:06 +04:00
saddr = dev - > dev_addr ;
2007-08-25 09:35:44 +04:00
memcpy ( eth - > h_source , saddr , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:20:18 +04:00
if ( daddr ) {
2007-08-25 09:35:44 +04:00
memcpy ( eth - > h_dest , daddr , ETH_ALEN ) ;
2006-02-24 03:18:01 +03:00
return ETH_HLEN ;
}
2006-08-18 05:20:18 +04:00
2005-04-17 02:20:36 +04:00
/*
2006-08-18 05:20:18 +04:00
* Anyway , the loopback - device should never use this function . . .
2005-04-17 02:20:36 +04:00
*/
2006-08-18 05:20:18 +04:00
if ( dev - > flags & ( IFF_LOOPBACK | IFF_NOARP ) ) {
2007-08-25 09:35:44 +04:00
memset ( eth - > h_dest , 0 , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
return ETH_HLEN ;
}
2006-08-18 05:20:18 +04:00
2005-04-17 02:20:36 +04:00
return - ETH_HLEN ;
}
2007-10-09 12:40:57 +04:00
EXPORT_SYMBOL ( eth_header ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:18:53 +04:00
/**
* eth_rebuild_header - rebuild the Ethernet MAC header .
* @ skb : socket buffer to update
*
* This is called after an ARP or IPV6 ndisc it ' s resolution on this
* sk_buff . We now let protocol ( ARP ) fill in the other fields .
2005-04-17 02:20:36 +04:00
*
2006-08-18 05:18:53 +04:00
* This routine CANNOT use cached dst - > neigh !
* Really , it is used only when dst - > neigh is wrong .
2005-04-17 02:20:36 +04:00
*/
int eth_rebuild_header ( struct sk_buff * skb )
{
struct ethhdr * eth = ( struct ethhdr * ) skb - > data ;
struct net_device * dev = skb - > dev ;
2006-08-18 05:20:18 +04:00
switch ( eth - > h_proto ) {
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_INET
2008-09-21 09:20:49 +04:00
case htons ( ETH_P_IP ) :
2006-08-18 05:20:18 +04:00
return arp_find ( eth - > h_dest , skb ) ;
# endif
2005-04-17 02:20:36 +04:00
default :
printk ( KERN_DEBUG
2006-08-18 05:20:18 +04:00
" %s: unable to resolve type %X addresses. \n " ,
2005-04-17 02:20:36 +04:00
dev - > name , ( int ) eth - > h_proto ) ;
2006-08-18 05:20:18 +04:00
2007-08-25 09:35:44 +04:00
memcpy ( eth - > h_source , dev - > dev_addr , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
break ;
}
return 0 ;
}
2007-10-09 12:40:57 +04:00
EXPORT_SYMBOL ( eth_rebuild_header ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:18:53 +04:00
/**
* eth_type_trans - determine the packet ' s protocol ID .
* @ skb : received socket data
* @ dev : receiving network device
*
* The rule here is that we
* assume 802.3 if the type field is short enough to be a length .
* This is normal practice and works for any ' now in use ' protocol .
2005-04-17 02:20:36 +04:00
*/
2005-07-12 23:08:43 +04:00
__be16 eth_type_trans ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
struct ethhdr * eth ;
unsigned char * rawp ;
2006-08-18 05:20:18 +04:00
2007-04-26 04:40:23 +04:00
skb - > dev = dev ;
2007-03-20 01:30:44 +03:00
skb_reset_mac_header ( skb ) ;
2006-08-18 05:20:18 +04:00
skb_pull ( skb , ETH_HLEN ) ;
2005-04-17 02:20:36 +04:00
eth = eth_hdr ( skb ) ;
2006-08-18 05:20:18 +04:00
eth: Declare an optimized compare_ether_addr_64bits() function
Linus mentioned we could try to perform long word operations, even
on potentially unaligned addresses, on x86 at least. David mentioned
the HAVE_EFFICIENT_UNALIGNED_ACCESS test to handle this on all
arches that have efficient unailgned accesses.
I tried this idea and got nice assembly on 32 bits:
158: 33 82 38 01 00 00 xor 0x138(%edx),%eax
15e: 33 8a 34 01 00 00 xor 0x134(%edx),%ecx
164: c1 e0 10 shl $0x10,%eax
167: 09 c1 or %eax,%ecx
169: 74 0b je 176 <eth_type_trans+0x87>
And very nice assembly on 64 bits of course (one xor, one shl)
Nice oprofile improvement in eth_type_trans(), 0.17 % instead of 0.41 %,
expected since we remove 8 instructions on a fast path.
This patch implements a compare_ether_addr_64bits() function, that
uses the CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ifdef to efficiently
perform the 6 bytes comparison on all capable arches.
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-11-24 10:24:32 +03:00
if ( unlikely ( is_multicast_ether_addr ( eth - > h_dest ) ) ) {
if ( ! compare_ether_addr_64bits ( eth - > h_dest , dev - > broadcast ) )
2005-09-28 03:03:05 +04:00
skb - > pkt_type = PACKET_BROADCAST ;
2005-04-17 02:20:36 +04:00
else
2005-09-28 03:03:05 +04:00
skb - > pkt_type = PACKET_MULTICAST ;
2005-04-17 02:20:36 +04:00
}
2006-08-18 05:20:18 +04:00
2005-04-17 02:20:36 +04:00
/*
2006-08-18 05:20:18 +04:00
* This ALLMULTI check should be redundant by 1.4
* so don ' t forget to remove it .
2005-04-17 02:20:36 +04:00
*
2006-08-18 05:20:18 +04:00
* Seems , you forgot to remove it . All silly devices
* seems to set IFF_PROMISC .
2005-04-17 02:20:36 +04:00
*/
2006-08-18 05:20:18 +04:00
else if ( 1 /*dev->flags&IFF_PROMISC */ ) {
eth: Declare an optimized compare_ether_addr_64bits() function
Linus mentioned we could try to perform long word operations, even
on potentially unaligned addresses, on x86 at least. David mentioned
the HAVE_EFFICIENT_UNALIGNED_ACCESS test to handle this on all
arches that have efficient unailgned accesses.
I tried this idea and got nice assembly on 32 bits:
158: 33 82 38 01 00 00 xor 0x138(%edx),%eax
15e: 33 8a 34 01 00 00 xor 0x134(%edx),%ecx
164: c1 e0 10 shl $0x10,%eax
167: 09 c1 or %eax,%ecx
169: 74 0b je 176 <eth_type_trans+0x87>
And very nice assembly on 64 bits of course (one xor, one shl)
Nice oprofile improvement in eth_type_trans(), 0.17 % instead of 0.41 %,
expected since we remove 8 instructions on a fast path.
This patch implements a compare_ether_addr_64bits() function, that
uses the CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ifdef to efficiently
perform the 6 bytes comparison on all capable arches.
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-11-24 10:24:32 +03:00
if ( unlikely ( compare_ether_addr_64bits ( eth - > h_dest , dev - > dev_addr ) ) )
2005-09-28 03:03:05 +04:00
skb - > pkt_type = PACKET_OTHERHOST ;
2005-04-17 02:20:36 +04:00
}
2006-08-18 05:20:18 +04:00
2008-10-07 17:45:02 +04:00
/*
* Some variants of DSA tagging don ' t have an ethertype field
* at all , so we check here whether one of those tagging
* variants has been configured on the receiving interface ,
* and if so , set skb - > protocol without looking at the packet .
*/
if ( netdev_uses_dsa_tags ( dev ) )
return htons ( ETH_P_DSA ) ;
2008-10-07 17:46:07 +04:00
if ( netdev_uses_trailer_tags ( dev ) )
return htons ( ETH_P_TRAILER ) ;
2008-10-07 17:45:02 +04:00
2005-04-17 02:20:36 +04:00
if ( ntohs ( eth - > h_proto ) > = 1536 )
return eth - > h_proto ;
2006-08-18 05:20:18 +04:00
2005-04-17 02:20:36 +04:00
rawp = skb - > data ;
2006-08-18 05:20:18 +04:00
2005-04-17 02:20:36 +04:00
/*
2006-08-18 05:20:18 +04:00
* This is a magic hack to spot IPX packets . Older Novell breaks
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
* layer . We look for FFFF which isn ' t a used 802.2 SSAP / DSAP . This
* won ' t work for fault tolerant netware but does for the rest .
2005-04-17 02:20:36 +04:00
*/
if ( * ( unsigned short * ) rawp = = 0xFFFF )
return htons ( ETH_P_802_3 ) ;
2006-08-18 05:20:18 +04:00
2005-04-17 02:20:36 +04:00
/*
2006-08-18 05:20:18 +04:00
* Real 802.2 LLC
2005-04-17 02:20:36 +04:00
*/
return htons ( ETH_P_802_2 ) ;
}
2006-08-18 05:20:18 +04:00
EXPORT_SYMBOL ( eth_type_trans ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:18:53 +04:00
/**
* eth_header_parse - extract hardware address from packet
* @ skb : packet to extract header from
* @ haddr : destination buffer
*/
2007-10-09 12:40:57 +04:00
int eth_header_parse ( const struct sk_buff * skb , unsigned char * haddr )
2005-04-17 02:20:36 +04:00
{
2007-09-27 09:13:38 +04:00
const struct ethhdr * eth = eth_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
memcpy ( haddr , eth - > h_source , ETH_ALEN ) ;
return ETH_ALEN ;
}
2007-10-09 12:40:57 +04:00
EXPORT_SYMBOL ( eth_header_parse ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:18:53 +04:00
/**
* eth_header_cache - fill cache entry from neighbour
* @ neigh : source neighbour
* @ hh : destination cache entry
* Create an Ethernet header template from the neighbour .
*/
2007-10-09 12:40:57 +04:00
int eth_header_cache ( const struct neighbour * neigh , struct hh_cache * hh )
2005-04-17 02:20:36 +04:00
{
2006-09-29 01:20:34 +04:00
__be16 type = hh - > hh_type ;
2005-04-17 02:20:36 +04:00
struct ethhdr * eth ;
2007-10-09 12:40:57 +04:00
const struct net_device * dev = neigh - > dev ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:20:18 +04:00
eth = ( struct ethhdr * )
( ( ( u8 * ) hh - > hh_data ) + ( HH_DATA_OFF ( sizeof ( * eth ) ) ) ) ;
2005-04-17 02:20:36 +04:00
2007-03-07 08:19:03 +03:00
if ( type = = htons ( ETH_P_802_3 ) )
2005-04-17 02:20:36 +04:00
return - 1 ;
eth - > h_proto = type ;
2007-08-25 09:35:44 +04:00
memcpy ( eth - > h_source , dev - > dev_addr , ETH_ALEN ) ;
memcpy ( eth - > h_dest , neigh - > ha , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
hh - > hh_len = ETH_HLEN ;
return 0 ;
}
2007-10-09 12:40:57 +04:00
EXPORT_SYMBOL ( eth_header_cache ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:18:53 +04:00
/**
* eth_header_cache_update - update cache entry
* @ hh : destination cache entry
* @ dev : network device
* @ haddr : new hardware address
*
2005-04-17 02:20:36 +04:00
* Called by Address Resolution module to notify changes in address .
*/
2007-10-09 12:40:57 +04:00
void eth_header_cache_update ( struct hh_cache * hh ,
const struct net_device * dev ,
const unsigned char * haddr )
2005-04-17 02:20:36 +04:00
{
2006-08-18 05:20:18 +04:00
memcpy ( ( ( u8 * ) hh - > hh_data ) + HH_DATA_OFF ( sizeof ( struct ethhdr ) ) ,
2007-08-25 09:35:44 +04:00
haddr , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-09 12:40:57 +04:00
EXPORT_SYMBOL ( eth_header_cache_update ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:18:53 +04:00
/**
* eth_mac_addr - set new Ethernet hardware address
* @ dev : network device
* @ p : socket address
* Change hardware address of device .
*
* This doesn ' t change hardware matching , so needs to be overridden
* for most real devices .
*/
2008-11-20 09:42:31 +03:00
int eth_mac_addr ( struct net_device * dev , void * p )
2005-04-17 02:20:36 +04:00
{
2006-08-18 05:20:18 +04:00
struct sockaddr * addr = p ;
2007-07-12 06:41:18 +04:00
2005-04-17 02:20:36 +04:00
if ( netif_running ( dev ) )
return - EBUSY ;
2007-07-12 06:41:18 +04:00
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EADDRNOTAVAIL ;
2007-08-25 09:35:44 +04:00
memcpy ( dev - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-20 09:42:31 +03:00
EXPORT_SYMBOL ( eth_mac_addr ) ;
2005-04-17 02:20:36 +04:00
2006-08-18 05:18:53 +04:00
/**
* eth_change_mtu - set new MTU size
* @ dev : network device
* @ new_mtu : new Maximum Transfer Unit
*
* Allow changing MTU size . Needs to be overridden for devices
* supporting jumbo frames .
*/
2008-11-20 09:42:31 +03:00
int eth_change_mtu ( struct net_device * dev , int new_mtu )
2005-04-17 02:20:36 +04:00
{
2006-01-06 03:35:42 +03:00
if ( new_mtu < 68 | | new_mtu > ETH_DATA_LEN )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
dev - > mtu = new_mtu ;
return 0 ;
}
2008-11-20 09:42:31 +03:00
EXPORT_SYMBOL ( eth_change_mtu ) ;
2005-04-17 02:20:36 +04:00
2008-11-20 09:42:31 +03:00
int eth_validate_addr ( struct net_device * dev )
2007-10-24 07:19:37 +04:00
{
if ( ! is_valid_ether_addr ( dev - > dev_addr ) )
2008-04-14 09:45:40 +04:00
return - EADDRNOTAVAIL ;
2007-10-24 07:19:37 +04:00
return 0 ;
}
2008-11-20 09:42:31 +03:00
EXPORT_SYMBOL ( eth_validate_addr ) ;
2007-10-24 07:19:37 +04:00
2007-10-09 12:40:57 +04:00
const struct header_ops eth_header_ops ____cacheline_aligned = {
. create = eth_header ,
. parse = eth_header_parse ,
. rebuild = eth_rebuild_header ,
. cache = eth_header_cache ,
. cache_update = eth_header_cache_update ,
} ;
2006-08-18 05:18:53 +04:00
/**
* ether_setup - setup Ethernet network device
* @ dev : network device
* Fill in the fields of the device structure with Ethernet - generic values .
2005-04-17 02:20:36 +04:00
*/
void ether_setup ( struct net_device * dev )
{
2007-10-09 12:40:57 +04:00
dev - > header_ops = & eth_header_ops ;
2008-11-20 09:42:31 +03:00
# ifdef CONFIG_COMPAT_NET_DEV_OPS
2005-04-17 02:20:36 +04:00
dev - > change_mtu = eth_change_mtu ;
dev - > set_mac_address = eth_mac_addr ;
2007-10-24 07:19:37 +04:00
dev - > validate_addr = eth_validate_addr ;
2008-11-20 09:42:31 +03:00
# endif
2005-04-17 02:20:36 +04:00
dev - > type = ARPHRD_ETHER ;
dev - > hard_header_len = ETH_HLEN ;
2006-01-06 03:35:42 +03:00
dev - > mtu = ETH_DATA_LEN ;
2005-04-17 02:20:36 +04:00
dev - > addr_len = ETH_ALEN ;
2007-02-09 17:24:44 +03:00
dev - > tx_queue_len = 1000 ; /* Ethernet wants good queues */
2005-04-17 02:20:36 +04:00
dev - > flags = IFF_BROADCAST | IFF_MULTICAST ;
2007-02-09 17:24:44 +03:00
2006-08-18 05:20:18 +04:00
memset ( dev - > broadcast , 0xFF , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( ether_setup ) ;
/**
2007-07-07 00:36:20 +04:00
* alloc_etherdev_mq - Allocates and sets up an Ethernet device
2005-04-17 02:20:36 +04:00
* @ sizeof_priv : Size of additional driver - private structure to be allocated
2006-08-18 05:18:53 +04:00
* for this Ethernet device
2007-07-07 00:36:20 +04:00
* @ queue_count : The number of queues this device has .
2005-04-17 02:20:36 +04:00
*
2006-08-18 05:18:53 +04:00
* Fill in the fields of the device structure with Ethernet - generic
2005-04-17 02:20:36 +04:00
* values . Basically does everything except registering the device .
*
* Constructs a new net device , complete with a private data area of
2006-08-18 05:18:53 +04:00
* size ( sizeof_priv ) . A 32 - byte ( not bit ) alignment is enforced for
2005-04-17 02:20:36 +04:00
* this private data area .
*/
2007-07-07 00:36:20 +04:00
struct net_device * alloc_etherdev_mq ( int sizeof_priv , unsigned int queue_count )
2005-04-17 02:20:36 +04:00
{
2007-07-07 00:36:20 +04:00
return alloc_netdev_mq ( sizeof_priv , " eth%d " , ether_setup , queue_count ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-07 00:36:20 +04:00
EXPORT_SYMBOL ( alloc_etherdev_mq ) ;
2007-10-04 04:59:30 +04:00
2007-12-25 08:28:09 +03:00
static size_t _format_mac_addr ( char * buf , int buflen ,
const unsigned char * addr , int len )
2007-10-04 04:59:30 +04:00
{
2007-12-25 08:28:09 +03:00
int i ;
char * cp = buf ;
for ( i = 0 ; i < len ; i + + ) {
cp + = scnprintf ( cp , buflen - ( cp - buf ) , " %02x " , addr [ i ] ) ;
if ( i = = len - 1 )
break ;
cp + = strlcpy ( cp , " : " , buflen - ( cp - buf ) ) ;
}
return cp - buf ;
}
ssize_t sysfs_format_mac ( char * buf , const unsigned char * addr , int len )
{
size_t l ;
l = _format_mac_addr ( buf , PAGE_SIZE , addr , len ) ;
l + = strlcpy ( buf + l , " \n " , PAGE_SIZE - l ) ;
return ( ( ssize_t ) l ) ;
}
EXPORT_SYMBOL ( sysfs_format_mac ) ;
char * print_mac ( char * buf , const unsigned char * addr )
{
_format_mac_addr ( buf , MAC_BUF_SIZE , addr , ETH_ALEN ) ;
2007-10-04 04:59:30 +04:00
return buf ;
}
EXPORT_SYMBOL ( print_mac ) ;