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>
2013-09-19 20:06:50 +04:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
__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
*
2009-12-26 14:50:59 +03:00
* Set the protocol type . For a packet of type ETH_P_802_3 / 2 we put the length
* in here instead .
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 ,
2012-04-15 09:58:06 +04:00
const void * daddr , const void * saddr , unsigned int 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
2009-12-26 14:50:59 +03:00
if ( type ! = ETH_P_802_3 & & type ! = ETH_P_802_2 )
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 ) ) {
2015-03-03 06:54:56 +03:00
eth_zero_addr ( eth - > h_dest ) ;
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
2014-09-06 03:20:26 +04:00
/**
* eth_get_headlen - determine the the length of header for an ethernet frame
* @ data : pointer to start of frame
* @ len : total length of frame
*
* Make a best effort attempt to pull the length for all of the headers for
* a given frame in a linear buffer .
*/
u32 eth_get_headlen ( void * data , unsigned int len )
{
const struct ethhdr * eth = ( const struct ethhdr * ) data ;
struct flow_keys keys ;
/* this should never happen, but better safe than sorry */
if ( len < sizeof ( * eth ) )
return len ;
/* parse any remaining L2/L3 headers, check for L4 */
if ( ! __skb_flow_dissect ( NULL , & keys , data ,
eth - > h_proto , sizeof ( * eth ) , len ) )
return max_t ( u32 , keys . thoff , sizeof ( * eth ) ) ;
/* parse for any L4 headers */
return min_t ( u32 , __skb_get_poff ( NULL , data , & keys , len ) , len ) ;
}
EXPORT_SYMBOL ( eth_get_headlen ) ;
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
{
2014-01-17 03:03:31 +04:00
unsigned short _service_access_point ;
const unsigned short * sap ;
const struct ethhdr * eth ;
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 ) ;
2015-05-01 00:53:59 +03:00
eth = ( struct ethhdr * ) skb - > data ;
2010-05-02 13:21:44 +04:00
skb_pull_inline ( skb , ETH_HLEN ) ;
2006-08-18 05:20:18 +04:00
2015-05-01 00:53:54 +03:00
if ( unlikely ( is_multicast_ether_addr_64bits ( eth - > h_dest ) ) ) {
net, drivers/net: Convert compare_ether_addr_64bits to ether_addr_equal_64bits
Use the new bool function ether_addr_equal_64bits to add
some clarity and reduce the likelihood for misuse of
compare_ether_addr_64bits for sorting.
Done via cocci script:
$ cat compare_ether_addr_64bits.cocci
@@
expression a,b;
@@
- !compare_ether_addr_64bits(a, b)
+ ether_addr_equal_64bits(a, b)
@@
expression a,b;
@@
- compare_ether_addr_64bits(a, b)
+ !ether_addr_equal_64bits(a, b)
@@
expression a,b;
@@
- !ether_addr_equal_64bits(a, b) == 0
+ ether_addr_equal_64bits(a, b)
@@
expression a,b;
@@
- !ether_addr_equal_64bits(a, b) != 0
+ !ether_addr_equal_64bits(a, b)
@@
expression a,b;
@@
- ether_addr_equal_64bits(a, b) == 0
+ !ether_addr_equal_64bits(a, b)
@@
expression a,b;
@@
- ether_addr_equal_64bits(a, b) != 0
+ ether_addr_equal_64bits(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal_64bits(a, b)
+ ether_addr_equal_64bits(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-09 21:04:04 +04:00
if ( ether_addr_equal_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
}
2013-09-28 04:19:41 +04:00
else if ( unlikely ( ! ether_addr_equal_64bits ( eth - > h_dest ,
dev - > dev_addr ) ) )
skb - > pkt_type = PACKET_OTHERHOST ;
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 .
*/
2014-08-28 04:04:46 +04:00
if ( unlikely ( netdev_uses_dsa ( dev ) ) )
return htons ( ETH_P_XDSA ) ;
2008-10-07 17:45:02 +04:00
2015-05-01 00:53:48 +03:00
if ( likely ( ( eth - > h_proto & htons ( 0xFF00 ) ) > = htons ( ETH_P_802_3_MIN ) ) )
2005-04-17 02:20:36 +04:00
return eth - > h_proto ;
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
*/
2014-01-17 03:03:31 +04:00
sap = skb_header_pointer ( skb , 0 , sizeof ( * sap ) , & _service_access_point ) ;
if ( sap & & * sap = = 0xFFFF )
2005-04-17 02:20:36 +04:00
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
2011-07-26 03:17:01 +04:00
* @ type : Ethernet type field
2012-07-10 14:55:09 +04:00
*
2006-08-18 05:18:53 +04:00
* Create an Ethernet header template from the neighbour .
*/
2011-07-13 10:28:12 +04:00
int eth_header_cache ( const struct neighbour * neigh , struct hh_cache * hh , __be16 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
2013-01-21 05:17:22 +04:00
/**
* eth_prepare_mac_addr_change - prepare for mac change
* @ dev : network device
* @ p : socket address
*/
int eth_prepare_mac_addr_change ( struct net_device * dev , void * p )
{
struct sockaddr * addr = p ;
if ( ! ( dev - > priv_flags & IFF_LIVE_ADDR_CHANGE ) & & netif_running ( dev ) )
return - EBUSY ;
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EADDRNOTAVAIL ;
return 0 ;
}
EXPORT_SYMBOL ( eth_prepare_mac_addr_change ) ;
/**
* eth_commit_mac_addr_change - commit mac change
* @ dev : network device
* @ p : socket address
*/
void eth_commit_mac_addr_change ( struct net_device * dev , void * p )
{
struct sockaddr * addr = p ;
memcpy ( dev - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
}
EXPORT_SYMBOL ( eth_commit_mac_addr_change ) ;
2006-08-18 05:18:53 +04:00
/**
* eth_mac_addr - set new Ethernet hardware address
* @ dev : network device
* @ p : socket address
2012-07-10 14:55:09 +04:00
*
2006-08-18 05:18:53 +04:00
* 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
{
2013-01-21 05:17:22 +04:00
int ret ;
2007-07-12 06:41:18 +04:00
2013-01-21 05:17:22 +04:00
ret = eth_prepare_mac_addr_change ( dev , p ) ;
if ( ret < 0 )
return ret ;
eth_commit_mac_addr_change ( dev , p ) ;
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 ,
. 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
2012-07-10 14:55:09 +04:00
*
2006-08-18 05:18:53 +04:00
* 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 ;
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 ;
2011-09-14 07:05:02 +04:00
dev - > priv_flags | = IFF_TX_SKB_SHARING ;
2007-02-09 17:24:44 +03:00
2015-03-03 06:54:56 +03:00
eth_broadcast_addr ( dev - > broadcast ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( ether_setup ) ;
/**
2011-01-09 22:36:31 +03:00
* alloc_etherdev_mqs - 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
2011-01-09 22:36:31 +03:00
* @ txqs : The number of TX queues this device has .
2011-01-12 17:50:51 +03:00
* @ rxqs : The number of RX 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 .
*/
2011-01-09 22:36:31 +03:00
struct net_device * alloc_etherdev_mqs ( int sizeof_priv , unsigned int txqs ,
unsigned int rxqs )
2005-04-17 02:20:36 +04:00
{
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 18:37:24 +04:00
return alloc_netdev_mqs ( sizeof_priv , " eth%d " , NET_NAME_UNKNOWN ,
ether_setup , txqs , rxqs ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-09 22:36:31 +03:00
EXPORT_SYMBOL ( alloc_etherdev_mqs ) ;
2007-10-04 04:59:30 +04:00
2007-12-25 08:28:09 +03:00
ssize_t sysfs_format_mac ( char * buf , const unsigned char * addr , int len )
{
2013-07-17 04:09:15 +04:00
return scnprintf ( buf , PAGE_SIZE , " %*phC \n " , len , addr ) ;
2007-12-25 08:28:09 +03:00
}
EXPORT_SYMBOL ( sysfs_format_mac ) ;
2014-12-31 06:10:15 +03:00
struct sk_buff * * eth_gro_receive ( struct sk_buff * * head ,
struct sk_buff * skb )
{
struct sk_buff * p , * * pp = NULL ;
struct ethhdr * eh , * eh2 ;
unsigned int hlen , off_eth ;
const struct packet_offload * ptype ;
__be16 type ;
int flush = 1 ;
off_eth = skb_gro_offset ( skb ) ;
hlen = off_eth + sizeof ( * eh ) ;
eh = skb_gro_header_fast ( skb , off_eth ) ;
if ( skb_gro_header_hard ( skb , hlen ) ) {
eh = skb_gro_header_slow ( skb , hlen , off_eth ) ;
if ( unlikely ( ! eh ) )
goto out ;
}
flush = 0 ;
for ( p = * head ; p ; p = p - > next ) {
if ( ! NAPI_GRO_CB ( p ) - > same_flow )
continue ;
eh2 = ( struct ethhdr * ) ( p - > data + off_eth ) ;
if ( compare_ether_header ( eh , eh2 ) ) {
NAPI_GRO_CB ( p ) - > same_flow = 0 ;
continue ;
}
}
type = eh - > h_proto ;
rcu_read_lock ( ) ;
ptype = gro_find_receive_by_type ( type ) ;
if ( ptype = = NULL ) {
flush = 1 ;
goto out_unlock ;
}
skb_gro_pull ( skb , sizeof ( * eh ) ) ;
skb_gro_postpull_rcsum ( skb , eh , sizeof ( * eh ) ) ;
pp = ptype - > callbacks . gro_receive ( head , skb ) ;
out_unlock :
rcu_read_unlock ( ) ;
out :
NAPI_GRO_CB ( skb ) - > flush | = flush ;
return pp ;
}
EXPORT_SYMBOL ( eth_gro_receive ) ;
int eth_gro_complete ( struct sk_buff * skb , int nhoff )
{
struct ethhdr * eh = ( struct ethhdr * ) ( skb - > data + nhoff ) ;
__be16 type = eh - > h_proto ;
struct packet_offload * ptype ;
int err = - ENOSYS ;
if ( skb - > encapsulation )
skb_set_inner_mac_header ( skb , nhoff ) ;
rcu_read_lock ( ) ;
ptype = gro_find_complete_by_type ( type ) ;
if ( ptype ! = NULL )
err = ptype - > callbacks . gro_complete ( skb , nhoff +
sizeof ( struct ethhdr ) ) ;
rcu_read_unlock ( ) ;
return err ;
}
EXPORT_SYMBOL ( eth_gro_complete ) ;
static struct packet_offload eth_packet_offload __read_mostly = {
. type = cpu_to_be16 ( ETH_P_TEB ) ,
. callbacks = {
. gro_receive = eth_gro_receive ,
. gro_complete = eth_gro_complete ,
} ,
} ;
static int __init eth_offload_init ( void )
{
dev_add_offload ( & eth_packet_offload ) ;
return 0 ;
}
fs_initcall ( eth_offload_init ) ;