2005-04-17 02:20:36 +04:00
/* -*- linux-c -*-
* INET 802.1 Q VLAN
* Ethernet - type device handling .
*
* Authors : Ben Greear < greearb @ candelatech . com >
2008-01-21 11:27:00 +03:00
* Please send support related email to : netdev @ vger . kernel . org
2005-04-17 02:20:36 +04:00
* VLAN Home Page : http : //www.candelatech.com/~greear/vlan.html
2007-02-09 17:24:25 +03:00
*
2005-04-17 02:20:36 +04:00
* Fixes : Mar 22 2001 : Martin Bokaemper < mbokaemper @ unispherenetworks . com >
* - reset skb - > pkt_type on incoming packets when MAC was changed
* - see that changed MAC is saddr for outgoing packets
* Oct 20 , 2001 : Ard van Breeman :
* - Fix MC - list , finally .
* - Flush MC - list on VLAN destroy .
2007-02-09 17:24:25 +03:00
*
2005-04-17 02:20:36 +04:00
*
* 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/skbuff.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
2008-07-08 14:22:42 +04:00
# include <linux/ethtool.h>
2005-04-17 02:20:36 +04:00
# include <net/arp.h>
# include "vlan.h"
# include "vlanproc.h"
# include <linux/if_vlan.h>
/*
* Rebuild the Ethernet MAC header . This is called after an ARP
* ( or in future other address resolution ) has completed on this
* sk_buff . We now let ARP fill in the other fields .
*
* This routine CANNOT use cached dst - > neigh !
* Really , it is used only when dst - > neigh is wrong .
*
* TODO : This needs a checkup , I ' m ignorant here . - - BLG
*/
2008-01-21 11:22:11 +03:00
static int vlan_dev_rebuild_header ( struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev = skb - > dev ;
struct vlan_ethhdr * veth = ( struct vlan_ethhdr * ) ( skb - > data ) ;
switch ( veth - > h_vlan_encapsulated_proto ) {
# ifdef CONFIG_INET
2008-09-21 09:20:49 +04:00
case htons ( ETH_P_IP ) :
2005-04-17 02:20:36 +04:00
/* TODO: Confirm this will work with VLAN headers... */
return arp_find ( veth - > h_dest , skb ) ;
2007-02-09 17:24:25 +03:00
# endif
2005-04-17 02:20:36 +04:00
default :
2008-01-21 11:24:30 +03:00
pr_debug ( " %s: unable to resolve type %X addresses. \n " ,
dev - > name , ntohs ( veth - > h_vlan_encapsulated_proto ) ) ;
2007-02-09 17:24:25 +03:00
2005-04-17 02:20:36 +04:00
memcpy ( veth - > h_source , dev - > dev_addr , ETH_ALEN ) ;
break ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
static inline struct sk_buff * vlan_check_reorder_header ( struct sk_buff * skb )
{
2008-01-21 11:26:07 +03:00
if ( vlan_dev_info ( skb - > dev ) - > flags & VLAN_FLAG_REORDER_HDR ) {
2008-07-09 02:36:57 +04:00
if ( skb_cow ( skb , skb_headroom ( skb ) ) < 0 )
skb = NULL ;
2005-04-17 02:20:36 +04:00
if ( skb ) {
/* Lifted from Gleb's VLAN code... */
memmove ( skb - > data - ETH_HLEN ,
skb - > data - VLAN_ETH_HLEN , 12 ) ;
2007-04-11 08:21:55 +04:00
skb - > mac_header + = VLAN_HLEN ;
2005-04-17 02:20:36 +04:00
}
}
return skb ;
}
2008-01-21 11:28:03 +03:00
static inline void vlan_set_encap_proto ( struct sk_buff * skb ,
struct vlan_hdr * vhdr )
{
__be16 proto ;
unsigned char * rawp ;
/*
* Was a VLAN packet , grab the encapsulated protocol , which the layer
* three protocols care about .
*/
proto = vhdr - > h_vlan_encapsulated_proto ;
if ( ntohs ( proto ) > = 1536 ) {
skb - > protocol = proto ;
return ;
}
rawp = skb - > data ;
if ( * ( unsigned short * ) rawp = = 0xFFFF )
/*
* 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 .
*/
skb - > protocol = htons ( ETH_P_802_3 ) ;
else
/*
* Real 802.2 LLC
*/
skb - > protocol = htons ( ETH_P_802_2 ) ;
}
2005-04-17 02:20:36 +04:00
/*
2007-02-09 17:24:25 +03:00
* Determine the packet ' s protocol ID . The rule here is that we
2005-04-17 02:20:36 +04:00
* 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 .
*
* Also , at this point we assume that we ARE dealing exclusively with
* VLAN packets , or packets that should be made into VLAN packets based
* on a default VLAN ID .
*
* NOTE : Should be similar to ethernet / eth . c .
*
* SANITY NOTE : This method is called when a packet is moving up the stack
* towards userland . To get here , it would have already passed
* through the ethernet / eth . c eth_type_trans ( ) method .
* SANITY NOTE 2 : We are referencing to the VLAN_HDR frields , which MAY be
* stored UNALIGNED in the memory . RISC systems don ' t like
* such cases very much . . .
2008-01-21 11:26:41 +03:00
* SANITY NOTE 2 a : According to Dave Miller & Alexey , it will always be
* aligned , so there doesn ' t need to be any of the unaligned
* stuff . It has been commented out now . . . - - Ben
2005-04-17 02:20:36 +04:00
*
*/
int vlan_skb_recv ( struct sk_buff * skb , struct net_device * dev ,
2008-01-21 11:26:41 +03:00
struct packet_type * ptype , struct net_device * orig_dev )
2005-04-17 02:20:36 +04:00
{
2007-08-25 10:36:29 +04:00
struct vlan_hdr * vhdr ;
2005-04-17 02:20:36 +04:00
struct net_device_stats * stats ;
2008-07-08 14:24:44 +04:00
u16 vlan_id ;
u16 vlan_tci ;
2005-04-17 02:20:36 +04:00
2008-01-21 11:26:41 +03:00
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( skb = = NULL )
2008-01-21 11:27:18 +03:00
goto err_free ;
2007-08-25 10:36:29 +04:00
2008-01-21 11:27:18 +03:00
if ( unlikely ( ! pskb_may_pull ( skb , VLAN_HLEN ) ) )
goto err_free ;
2007-08-25 10:36:29 +04:00
2008-01-21 11:27:18 +03:00
vhdr = ( struct vlan_hdr * ) skb - > data ;
2008-07-08 14:24:44 +04:00
vlan_tci = ntohs ( vhdr - > h_vlan_TCI ) ;
vlan_id = vlan_tci & VLAN_VID_MASK ;
2005-04-17 02:20:36 +04:00
rcu_read_lock ( ) ;
2008-07-08 14:24:44 +04:00
skb - > dev = __find_vlan_dev ( dev , vlan_id ) ;
2005-04-17 02:20:36 +04:00
if ( ! skb - > dev ) {
2008-01-21 11:26:41 +03:00
pr_debug ( " %s: ERROR: No net_device for VID: %u on dev: %s \n " ,
2008-07-08 14:24:44 +04:00
__func__ , vlan_id , dev - > name ) ;
2008-01-21 11:27:18 +03:00
goto err_unlock ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:19:31 +03:00
stats = & skb - > dev - > stats ;
2005-04-17 02:20:36 +04:00
stats - > rx_packets + + ;
stats - > rx_bytes + = skb - > len ;
2006-03-21 09:43:56 +03:00
skb_pull_rcsum ( skb , VLAN_HLEN ) ;
2005-12-15 03:23:16 +03:00
2008-07-08 14:24:44 +04:00
skb - > priority = vlan_get_ingress_priority ( skb - > dev , vlan_tci ) ;
2005-04-17 02:20:36 +04:00
2008-01-21 11:24:30 +03:00
pr_debug ( " %s: priority: %u for TCI: %hu \n " ,
2008-07-08 14:24:44 +04:00
__func__ , skb - > priority , vlan_tci ) ;
2005-04-17 02:20:36 +04:00
switch ( skb - > pkt_type ) {
case PACKET_BROADCAST : /* Yeah, stats collect these together.. */
2008-01-21 11:26:41 +03:00
/* stats->broadcast ++; // no such counter :-( */
2005-04-17 02:20:36 +04:00
break ;
case PACKET_MULTICAST :
stats - > multicast + + ;
break ;
2007-02-09 17:24:25 +03:00
case PACKET_OTHERHOST :
2005-04-17 02:20:36 +04:00
/* Our lower layer thinks this is not local, let's make sure.
2008-01-21 11:26:41 +03:00
* This allows the VLAN to have a different MAC than the
* underlying device , and still route correctly .
2005-04-17 02:20:36 +04:00
*/
2008-01-21 11:26:41 +03:00
if ( ! compare_ether_addr ( eth_hdr ( skb ) - > h_dest ,
skb - > dev - > dev_addr ) )
2005-04-17 02:20:36 +04:00
skb - > pkt_type = PACKET_HOST ;
break ;
default :
break ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
2008-01-21 11:28:03 +03:00
vlan_set_encap_proto ( skb , vhdr ) ;
2005-04-17 02:20:36 +04:00
skb = vlan_check_reorder_header ( skb ) ;
2008-01-21 11:27:18 +03:00
if ( ! skb ) {
2005-04-17 02:20:36 +04:00
stats - > rx_errors + + ;
2008-01-21 11:27:18 +03:00
goto err_unlock ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:27:18 +03:00
netif_rx ( skb ) ;
2005-04-17 02:20:36 +04:00
rcu_read_unlock ( ) ;
2008-01-21 11:27:18 +03:00
return NET_RX_SUCCESS ;
err_unlock :
rcu_read_unlock ( ) ;
err_free :
kfree_skb ( skb ) ;
return NET_RX_DROP ;
2005-04-17 02:20:36 +04:00
}
2008-07-08 14:24:44 +04:00
static inline u16
2008-01-21 11:26:41 +03:00
vlan_dev_get_egress_qos_mask ( struct net_device * dev , struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:26:41 +03:00
struct vlan_priority_tci_mapping * mp ;
2005-04-17 02:20:36 +04:00
2008-01-21 11:26:41 +03:00
mp = vlan_dev_info ( dev ) - > egress_priority_map [ ( skb - > priority & 0xF ) ] ;
2005-04-17 02:20:36 +04:00
while ( mp ) {
if ( mp - > priority = = skb - > priority ) {
2008-01-21 11:26:41 +03:00
return mp - > vlan_qos ; /* This should already be shifted
* to mask correctly with the
* VLAN ' s TCI */
2005-04-17 02:20:36 +04:00
}
mp = mp - > next ;
}
return 0 ;
}
/*
2007-02-09 17:24:25 +03:00
* Create the VLAN header for an arbitrary protocol layer
2005-04-17 02:20:36 +04:00
*
* saddr = NULL means use device source address
* daddr = NULL means leave destination address ( eg unresolved arp )
*
* This is called when the SKB is moving down the stack towards the
* physical devices .
*/
2008-01-21 11:22:11 +03:00
static int vlan_dev_hard_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type ,
const void * daddr , const void * saddr ,
unsigned int len )
2005-04-17 02:20:36 +04:00
{
struct vlan_hdr * vhdr ;
2008-07-15 09:51:19 +04:00
unsigned int vhdrlen = 0 ;
2008-07-08 14:24:44 +04:00
u16 vlan_tci = 0 ;
2008-07-15 09:51:19 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
2008-07-09 02:36:57 +04:00
if ( WARN_ON ( skb_headroom ( skb ) < dev - > hard_header_len ) )
return - ENOSPC ;
2008-07-15 09:51:19 +04:00
if ( ! ( vlan_dev_info ( dev ) - > flags & VLAN_FLAG_REORDER_HDR ) ) {
2005-04-17 02:20:36 +04:00
vhdr = ( struct vlan_hdr * ) skb_push ( skb , VLAN_HLEN ) ;
2008-07-08 14:24:44 +04:00
vlan_tci = vlan_dev_info ( dev ) - > vlan_id ;
vlan_tci | = vlan_dev_get_egress_qos_mask ( dev , skb ) ;
vhdr - > h_vlan_TCI = htons ( vlan_tci ) ;
2005-04-17 02:20:36 +04:00
/*
2008-01-21 11:26:41 +03: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
*/
2008-01-21 11:26:41 +03:00
if ( type ! = ETH_P_802_3 )
2005-04-17 02:20:36 +04:00
vhdr - > h_vlan_encapsulated_proto = htons ( type ) ;
2008-01-21 11:26:41 +03:00
else
2005-04-17 02:20:36 +04:00
vhdr - > h_vlan_encapsulated_proto = htons ( len ) ;
2007-04-14 03:12:47 +04:00
skb - > protocol = htons ( ETH_P_8021Q ) ;
2008-07-15 09:51:19 +04:00
type = ETH_P_8021Q ;
vhdrlen = VLAN_HLEN ;
2005-04-17 02:20:36 +04:00
}
/* Before delegating work to the lower layer, enter our MAC-address */
if ( saddr = = NULL )
saddr = dev - > dev_addr ;
2008-07-15 09:51:19 +04:00
/* Now make the underlying real hard header */
2008-01-21 11:26:07 +03:00
dev = vlan_dev_info ( dev ) - > real_dev ;
2008-07-15 09:51:19 +04:00
rc = dev_hard_header ( skb , dev , type , daddr , saddr , len + vhdrlen ) ;
if ( rc > 0 )
rc + = vhdrlen ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2008-01-21 11:22:11 +03:00
static int vlan_dev_hard_start_xmit ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:19:31 +03:00
struct net_device_stats * stats = & dev - > stats ;
2005-04-17 02:20:36 +04:00
struct vlan_ethhdr * veth = ( struct vlan_ethhdr * ) ( skb - > data ) ;
2008-02-24 07:09:11 +03:00
2005-04-17 02:20:36 +04:00
/* Handle non-VLAN frames if they are sent to us, for example by DHCP.
*
* NOTE : THIS ASSUMES DIX ETHERNET , SPECIFICALLY NOT SUPPORTING
* OTHER THINGS LIKE FDDI / TokenRing / 802.3 SNAPs . . .
*/
2007-11-29 14:16:41 +03:00
if ( veth - > h_vlan_proto ! = htons ( ETH_P_8021Q ) | |
2008-07-15 09:51:39 +04:00
vlan_dev_info ( dev ) - > flags & VLAN_FLAG_REORDER_HDR ) {
unsigned int orig_headroom = skb_headroom ( skb ) ;
2008-07-08 14:24:44 +04:00
u16 vlan_tci ;
2005-04-17 02:20:36 +04:00
2008-01-21 11:26:07 +03:00
vlan_dev_info ( dev ) - > cnt_encap_on_xmit + + ;
2005-04-17 02:20:36 +04:00
2008-07-08 14:24:44 +04:00
vlan_tci = vlan_dev_info ( dev ) - > vlan_id ;
vlan_tci | = vlan_dev_get_egress_qos_mask ( dev , skb ) ;
skb = __vlan_put_tag ( skb , vlan_tci ) ;
2005-04-17 02:20:36 +04:00
if ( ! skb ) {
stats - > tx_dropped + + ;
2008-07-15 09:51:39 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:26:41 +03:00
if ( orig_headroom < VLAN_HLEN )
2008-01-21 11:26:07 +03:00
vlan_dev_info ( dev ) - > cnt_inc_headroom_on_tx + + ;
2005-04-17 02:20:36 +04:00
}
2008-07-15 09:51:39 +04:00
stats - > tx_packets + + ;
2005-04-17 02:20:36 +04:00
stats - > tx_bytes + = skb - > len ;
2008-01-21 11:26:07 +03:00
skb - > dev = vlan_dev_info ( dev ) - > real_dev ;
2005-04-17 02:20:36 +04:00
dev_queue_xmit ( skb ) ;
2008-07-15 09:51:39 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:22:11 +03:00
static int vlan_dev_hwaccel_hard_start_xmit ( struct sk_buff * skb ,
struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:19:31 +03:00
struct net_device_stats * stats = & dev - > stats ;
2008-07-08 14:24:44 +04:00
u16 vlan_tci ;
2005-04-17 02:20:36 +04:00
2008-07-08 14:24:44 +04:00
vlan_tci = vlan_dev_info ( dev ) - > vlan_id ;
vlan_tci | = vlan_dev_get_egress_qos_mask ( dev , skb ) ;
skb = __vlan_hwaccel_put_tag ( skb , vlan_tci ) ;
2005-04-17 02:20:36 +04:00
stats - > tx_packets + + ;
stats - > tx_bytes + = skb - > len ;
2008-01-21 11:26:07 +03:00
skb - > dev = vlan_dev_info ( dev ) - > real_dev ;
2005-04-17 02:20:36 +04:00
dev_queue_xmit ( skb ) ;
2008-07-15 09:51:39 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:22:11 +03:00
static int vlan_dev_change_mtu ( struct net_device * dev , int new_mtu )
2005-04-17 02:20:36 +04:00
{
/* TODO: gotta make sure the underlying layer can handle it,
* maybe an IFF_VLAN_CAPABLE flag for devices ?
*/
2008-01-21 11:26:07 +03:00
if ( vlan_dev_info ( dev ) - > real_dev - > mtu < new_mtu )
2005-04-17 02:20:36 +04:00
return - ERANGE ;
dev - > mtu = new_mtu ;
return 0 ;
}
2007-06-13 23:05:22 +04:00
void vlan_dev_set_ingress_priority ( const struct net_device * dev ,
2008-07-08 14:24:44 +04:00
u32 skb_prio , u16 vlan_prio )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:26:07 +03:00
struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
2007-06-13 23:07:22 +04:00
if ( vlan - > ingress_priority_map [ vlan_prio & 0x7 ] & & ! skb_prio )
vlan - > nr_ingress_mappings - - ;
else if ( ! vlan - > ingress_priority_map [ vlan_prio & 0x7 ] & & skb_prio )
vlan - > nr_ingress_mappings + + ;
vlan - > ingress_priority_map [ vlan_prio & 0x7 ] = skb_prio ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:05:22 +04:00
int vlan_dev_set_egress_priority ( const struct net_device * dev ,
2008-07-08 14:24:44 +04:00
u32 skb_prio , u16 vlan_prio )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:26:07 +03:00
struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
2005-04-17 02:20:36 +04:00
struct vlan_priority_tci_mapping * mp = NULL ;
struct vlan_priority_tci_mapping * np ;
2007-06-13 23:07:22 +04:00
u32 vlan_qos = ( vlan_prio < < 13 ) & 0xE000 ;
2007-02-09 17:24:25 +03:00
2007-06-13 23:05:22 +04:00
/* See if a priority mapping exists.. */
2007-06-13 23:07:22 +04:00
mp = vlan - > egress_priority_map [ skb_prio & 0xF ] ;
2007-06-13 23:05:22 +04:00
while ( mp ) {
if ( mp - > priority = = skb_prio ) {
2007-06-13 23:07:22 +04:00
if ( mp - > vlan_qos & & ! vlan_qos )
vlan - > nr_egress_mappings - - ;
else if ( ! mp - > vlan_qos & & vlan_qos )
vlan - > nr_egress_mappings + + ;
mp - > vlan_qos = vlan_qos ;
2007-06-13 23:05:22 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:05:22 +04:00
mp = mp - > next ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:05:22 +04:00
/* Create a new mapping then. */
2007-06-13 23:07:22 +04:00
mp = vlan - > egress_priority_map [ skb_prio & 0xF ] ;
2007-06-13 23:05:22 +04:00
np = kmalloc ( sizeof ( struct vlan_priority_tci_mapping ) , GFP_KERNEL ) ;
if ( ! np )
return - ENOBUFS ;
np - > next = mp ;
np - > priority = skb_prio ;
2007-06-13 23:07:22 +04:00
np - > vlan_qos = vlan_qos ;
vlan - > egress_priority_map [ skb_prio & 0xF ] = np ;
if ( vlan_qos )
vlan - > nr_egress_mappings + + ;
2007-06-13 23:05:22 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:07:37 +04:00
/* Flags are defined in the vlan_flags enum in include/linux/if_vlan.h file. */
2008-07-06 08:26:27 +04:00
int vlan_dev_change_flags ( const struct net_device * dev , u32 flags , u32 mask )
2005-04-17 02:20:36 +04:00
{
2008-07-06 08:26:27 +04:00
struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
u32 old_flags = vlan - > flags ;
2008-07-06 08:26:57 +04:00
if ( mask & ~ ( VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP ) )
2008-07-06 08:26:27 +04:00
return - EINVAL ;
vlan - > flags = ( old_flags & ~ mask ) | ( flags & mask ) ;
2008-07-06 08:26:57 +04:00
if ( netif_running ( dev ) & & ( vlan - > flags ^ old_flags ) & VLAN_FLAG_GVRP ) {
if ( vlan - > flags & VLAN_FLAG_GVRP )
vlan_gvrp_request_join ( dev ) ;
else
vlan_gvrp_request_leave ( dev ) ;
}
2008-07-06 08:26:27 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:05:22 +04:00
void vlan_dev_get_realdev_name ( const struct net_device * dev , char * result )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:26:07 +03:00
strncpy ( result , vlan_dev_info ( dev ) - > real_dev - > name , 23 ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:22:11 +03:00
static int vlan_dev_open ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:26:07 +03:00
struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
2007-07-12 06:45:24 +04:00
struct net_device * real_dev = vlan - > real_dev ;
int err ;
if ( ! ( real_dev - > flags & IFF_UP ) )
2005-04-17 02:20:36 +04:00
return - ENETDOWN ;
2007-07-12 06:45:24 +04:00
if ( compare_ether_addr ( dev - > dev_addr , real_dev - > dev_addr ) ) {
err = dev_unicast_add ( real_dev , dev - > dev_addr , ETH_ALEN ) ;
if ( err < 0 )
2008-07-15 07:59:03 +04:00
goto out ;
2007-07-12 06:45:24 +04:00
}
2008-07-15 07:59:03 +04:00
if ( dev - > flags & IFF_ALLMULTI ) {
err = dev_set_allmulti ( real_dev , 1 ) ;
if ( err < 0 )
goto del_unicast ;
}
if ( dev - > flags & IFF_PROMISC ) {
err = dev_set_promiscuity ( real_dev , 1 ) ;
if ( err < 0 )
goto clear_allmulti ;
}
memcpy ( vlan - > real_dev_addr , real_dev - > dev_addr , ETH_ALEN ) ;
2007-07-15 05:52:56 +04:00
2008-07-06 08:26:57 +04:00
if ( vlan - > flags & VLAN_FLAG_GVRP )
vlan_gvrp_request_join ( dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2008-07-15 07:59:03 +04:00
clear_allmulti :
if ( dev - > flags & IFF_ALLMULTI )
dev_set_allmulti ( real_dev , - 1 ) ;
del_unicast :
if ( compare_ether_addr ( dev - > dev_addr , real_dev - > dev_addr ) )
dev_unicast_delete ( real_dev , dev - > dev_addr , ETH_ALEN ) ;
out :
return err ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:22:11 +03:00
static int vlan_dev_stop ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-07-06 08:26:57 +04:00
struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
struct net_device * real_dev = vlan - > real_dev ;
if ( vlan - > flags & VLAN_FLAG_GVRP )
vlan_gvrp_request_leave ( dev ) ;
2007-07-12 06:45:24 +04:00
2007-07-15 05:53:28 +04:00
dev_mc_unsync ( real_dev , dev ) ;
2008-02-01 03:53:23 +03:00
dev_unicast_unsync ( real_dev , dev ) ;
2007-07-15 05:52:56 +04:00
if ( dev - > flags & IFF_ALLMULTI )
dev_set_allmulti ( real_dev , - 1 ) ;
if ( dev - > flags & IFF_PROMISC )
dev_set_promiscuity ( real_dev , - 1 ) ;
2007-07-12 06:45:24 +04:00
if ( compare_ether_addr ( dev - > dev_addr , real_dev - > dev_addr ) )
dev_unicast_delete ( real_dev , dev - > dev_addr , dev - > addr_len ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-01-21 11:22:11 +03:00
static int vlan_dev_set_mac_address ( struct net_device * dev , void * p )
2007-11-11 08:52:35 +03:00
{
2008-01-21 11:26:07 +03:00
struct net_device * real_dev = vlan_dev_info ( dev ) - > real_dev ;
2007-11-11 08:52:35 +03:00
struct sockaddr * addr = p ;
int err ;
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EADDRNOTAVAIL ;
if ( ! ( dev - > flags & IFF_UP ) )
goto out ;
if ( compare_ether_addr ( addr - > sa_data , real_dev - > dev_addr ) ) {
err = dev_unicast_add ( real_dev , addr - > sa_data , ETH_ALEN ) ;
if ( err < 0 )
return err ;
}
if ( compare_ether_addr ( dev - > dev_addr , real_dev - > dev_addr ) )
dev_unicast_delete ( real_dev , dev - > dev_addr , ETH_ALEN ) ;
out :
memcpy ( dev - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
return 0 ;
}
2008-01-21 11:22:11 +03:00
static int vlan_dev_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:26:07 +03:00
struct net_device * real_dev = vlan_dev_info ( dev ) - > real_dev ;
2008-11-20 08:53:47 +03:00
const struct net_device_ops * ops = real_dev - > netdev_ops ;
2005-04-17 02:20:36 +04:00
struct ifreq ifrr ;
int err = - EOPNOTSUPP ;
strncpy ( ifrr . ifr_name , real_dev - > name , IFNAMSIZ ) ;
ifrr . ifr_ifru = ifr - > ifr_ifru ;
2008-01-21 11:26:41 +03:00
switch ( cmd ) {
2005-04-17 02:20:36 +04:00
case SIOCGMIIPHY :
case SIOCGMIIREG :
case SIOCSMIIREG :
2008-11-20 08:53:47 +03:00
if ( netif_device_present ( real_dev ) & & ops - > ndo_do_ioctl )
err = ops - > ndo_do_ioctl ( real_dev , & ifrr , cmd ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2007-02-09 17:24:25 +03:00
if ( ! err )
2005-04-17 02:20:36 +04:00
ifr - > ifr_ifru = ifrr . ifr_ifru ;
return err ;
}
2009-01-08 21:50:20 +03:00
static int vlan_dev_neigh_setup ( struct net_device * dev , struct neigh_parms * pa )
{
struct net_device * real_dev = vlan_dev_info ( dev ) - > real_dev ;
const struct net_device_ops * ops = real_dev - > netdev_ops ;
int err = 0 ;
if ( netif_device_present ( real_dev ) & & ops - > ndo_neigh_setup )
2009-03-05 10:46:25 +03:00
err = ops - > ndo_neigh_setup ( real_dev , pa ) ;
2009-01-08 21:50:20 +03:00
return err ;
}
2008-01-21 11:22:11 +03:00
static void vlan_dev_change_rx_flags ( struct net_device * dev , int change )
2007-07-15 05:52:56 +04:00
{
2008-01-21 11:26:07 +03:00
struct net_device * real_dev = vlan_dev_info ( dev ) - > real_dev ;
2007-07-15 05:52:56 +04:00
if ( change & IFF_ALLMULTI )
dev_set_allmulti ( real_dev , dev - > flags & IFF_ALLMULTI ? 1 : - 1 ) ;
if ( change & IFF_PROMISC )
dev_set_promiscuity ( real_dev , dev - > flags & IFF_PROMISC ? 1 : - 1 ) ;
}
2008-02-01 03:53:23 +03:00
static void vlan_dev_set_rx_mode ( struct net_device * vlan_dev )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:26:07 +03:00
dev_mc_sync ( vlan_dev_info ( vlan_dev ) - > real_dev , vlan_dev ) ;
2008-02-01 03:53:23 +03:00
dev_unicast_sync ( vlan_dev_info ( vlan_dev ) - > real_dev , vlan_dev ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:22:11 +03:00
/*
* vlan network devices have devices nesting below it , and are a special
* " super class " of normal network devices ; split their locks off into a
* separate class since they always nest .
*/
static struct lock_class_key vlan_netdev_xmit_lock_key ;
2008-07-23 01:16:42 +04:00
static struct lock_class_key vlan_netdev_addr_lock_key ;
2008-01-21 11:22:11 +03:00
2008-07-17 11:34:19 +04:00
static void vlan_dev_set_lockdep_one ( struct net_device * dev ,
struct netdev_queue * txq ,
void * _subclass )
2008-07-09 10:13:53 +04:00
{
lockdep_set_class_and_subclass ( & txq - > _xmit_lock ,
2008-07-17 11:34:19 +04:00
& vlan_netdev_xmit_lock_key ,
* ( int * ) _subclass ) ;
2008-07-09 10:13:53 +04:00
}
static void vlan_dev_set_lockdep_class ( struct net_device * dev , int subclass )
{
2008-07-23 01:16:42 +04:00
lockdep_set_class_and_subclass ( & dev - > addr_list_lock ,
& vlan_netdev_addr_lock_key ,
subclass ) ;
2008-07-17 11:34:19 +04:00
netdev_for_each_tx_queue ( dev , vlan_dev_set_lockdep_one , & subclass ) ;
2008-07-09 10:13:53 +04:00
}
2008-01-21 11:22:11 +03:00
static const struct header_ops vlan_header_ops = {
. create = vlan_dev_hard_header ,
. rebuild = vlan_dev_rebuild_header ,
. parse = eth_header_parse ,
} ;
2008-12-26 03:45:19 +03:00
static const struct net_device_ops vlan_netdev_ops , vlan_netdev_accel_ops ;
2008-01-21 11:22:11 +03:00
static int vlan_dev_init ( struct net_device * dev )
{
2008-01-21 11:26:07 +03:00
struct net_device * real_dev = vlan_dev_info ( dev ) - > real_dev ;
2008-01-21 11:22:11 +03:00
int subclass = 0 ;
/* IFF_BROADCAST|IFF_MULTICAST; ??? */
2008-03-26 10:15:17 +03:00
dev - > flags = real_dev - > flags & ~ ( IFF_UP | IFF_PROMISC | IFF_ALLMULTI ) ;
2008-01-21 11:22:11 +03:00
dev - > iflink = real_dev - > ifindex ;
dev - > state = ( real_dev - > state & ( ( 1 < < __LINK_STATE_NOCARRIER ) |
( 1 < < __LINK_STATE_DORMANT ) ) ) |
( 1 < < __LINK_STATE_PRESENT ) ;
2008-05-23 11:22:04 +04:00
dev - > features | = real_dev - > features & real_dev - > vlan_features ;
2008-09-12 07:17:05 +04:00
dev - > gso_max_size = real_dev - > gso_max_size ;
2008-05-21 01:54:50 +04:00
2008-01-21 11:22:11 +03:00
/* ipv6 shared card related stuff */
dev - > dev_id = real_dev - > dev_id ;
if ( is_zero_ether_addr ( dev - > dev_addr ) )
memcpy ( dev - > dev_addr , real_dev - > dev_addr , dev - > addr_len ) ;
if ( is_zero_ether_addr ( dev - > broadcast ) )
memcpy ( dev - > broadcast , real_dev - > broadcast , dev - > addr_len ) ;
if ( real_dev - > features & NETIF_F_HW_VLAN_TX ) {
dev - > header_ops = real_dev - > header_ops ;
dev - > hard_header_len = real_dev - > hard_header_len ;
2008-12-26 03:45:19 +03:00
dev - > netdev_ops = & vlan_netdev_accel_ops ;
2008-01-21 11:22:11 +03:00
} else {
dev - > header_ops = & vlan_header_ops ;
dev - > hard_header_len = real_dev - > hard_header_len + VLAN_HLEN ;
2008-12-26 03:45:19 +03:00
dev - > netdev_ops = & vlan_netdev_ops ;
2008-01-21 11:22:11 +03:00
}
2009-03-05 10:46:25 +03:00
netdev_resync_ops ( dev ) ;
2008-01-21 11:22:11 +03:00
2008-07-08 14:22:16 +04:00
if ( is_vlan_dev ( real_dev ) )
2008-01-21 11:22:11 +03:00
subclass = 1 ;
2008-07-09 10:13:53 +04:00
vlan_dev_set_lockdep_class ( dev , subclass ) ;
2008-01-21 11:22:11 +03:00
return 0 ;
}
2008-04-04 23:45:12 +04:00
static void vlan_dev_uninit ( struct net_device * dev )
{
struct vlan_priority_tci_mapping * pm ;
struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( vlan - > egress_priority_map ) ; i + + ) {
while ( ( pm = vlan - > egress_priority_map [ i ] ) ! = NULL ) {
vlan - > egress_priority_map [ i ] = pm - > next ;
kfree ( pm ) ;
}
}
}
2008-10-29 08:12:36 +03:00
static int vlan_ethtool_get_settings ( struct net_device * dev ,
struct ethtool_cmd * cmd )
{
const struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
struct net_device * real_dev = vlan - > real_dev ;
2009-04-18 02:59:23 +04:00
if ( ! real_dev - > ethtool_ops | |
! real_dev - > ethtool_ops - > get_settings )
2008-10-29 08:12:36 +03:00
return - EOPNOTSUPP ;
return real_dev - > ethtool_ops - > get_settings ( real_dev , cmd ) ;
}
static void vlan_ethtool_get_drvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * info )
{
strcpy ( info - > driver , vlan_fullname ) ;
strcpy ( info - > version , vlan_version ) ;
strcpy ( info - > fw_version , " N/A " ) ;
}
2008-07-08 14:22:42 +04:00
static u32 vlan_ethtool_get_rx_csum ( struct net_device * dev )
{
const struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
struct net_device * real_dev = vlan - > real_dev ;
if ( real_dev - > ethtool_ops = = NULL | |
real_dev - > ethtool_ops - > get_rx_csum = = NULL )
return 0 ;
return real_dev - > ethtool_ops - > get_rx_csum ( real_dev ) ;
}
2008-07-15 09:51:01 +04:00
static u32 vlan_ethtool_get_flags ( struct net_device * dev )
{
const struct vlan_dev_info * vlan = vlan_dev_info ( dev ) ;
struct net_device * real_dev = vlan - > real_dev ;
if ( ! ( real_dev - > features & NETIF_F_HW_VLAN_RX ) | |
real_dev - > ethtool_ops = = NULL | |
real_dev - > ethtool_ops - > get_flags = = NULL )
return 0 ;
return real_dev - > ethtool_ops - > get_flags ( real_dev ) ;
}
2008-07-08 14:22:42 +04:00
static const struct ethtool_ops vlan_ethtool_ops = {
2008-10-29 08:12:36 +03:00
. get_settings = vlan_ethtool_get_settings ,
. get_drvinfo = vlan_ethtool_get_drvinfo ,
2008-07-08 14:22:42 +04:00
. get_link = ethtool_op_get_link ,
. get_rx_csum = vlan_ethtool_get_rx_csum ,
2008-07-15 09:51:01 +04:00
. get_flags = vlan_ethtool_get_flags ,
2008-07-08 14:22:42 +04:00
} ;
2008-11-20 08:53:47 +03:00
static const struct net_device_ops vlan_netdev_ops = {
. ndo_change_mtu = vlan_dev_change_mtu ,
. ndo_init = vlan_dev_init ,
. ndo_uninit = vlan_dev_uninit ,
. ndo_open = vlan_dev_open ,
. ndo_stop = vlan_dev_stop ,
2008-12-26 03:45:19 +03:00
. ndo_start_xmit = vlan_dev_hard_start_xmit ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_set_mac_address = vlan_dev_set_mac_address ,
. ndo_set_rx_mode = vlan_dev_set_rx_mode ,
. ndo_set_multicast_list = vlan_dev_set_rx_mode ,
. ndo_change_rx_flags = vlan_dev_change_rx_flags ,
. ndo_do_ioctl = vlan_dev_ioctl ,
2009-01-08 21:50:20 +03:00
. ndo_neigh_setup = vlan_dev_neigh_setup ,
2008-12-26 03:45:19 +03:00
} ;
static const struct net_device_ops vlan_netdev_accel_ops = {
. ndo_change_mtu = vlan_dev_change_mtu ,
. ndo_init = vlan_dev_init ,
. ndo_uninit = vlan_dev_uninit ,
. ndo_open = vlan_dev_open ,
. ndo_stop = vlan_dev_stop ,
. ndo_start_xmit = vlan_dev_hwaccel_hard_start_xmit ,
2008-11-20 08:53:47 +03:00
. ndo_validate_addr = eth_validate_addr ,
. ndo_set_mac_address = vlan_dev_set_mac_address ,
. ndo_set_rx_mode = vlan_dev_set_rx_mode ,
. ndo_set_multicast_list = vlan_dev_set_rx_mode ,
. ndo_change_rx_flags = vlan_dev_change_rx_flags ,
. ndo_do_ioctl = vlan_dev_ioctl ,
2009-01-08 21:50:20 +03:00
. ndo_neigh_setup = vlan_dev_neigh_setup ,
2008-11-20 08:53:47 +03:00
} ;
2008-01-21 11:22:11 +03:00
void vlan_setup ( struct net_device * dev )
{
ether_setup ( dev ) ;
dev - > priv_flags | = IFF_802_1Q_VLAN ;
dev - > tx_queue_len = 0 ;
2008-11-20 08:53:47 +03:00
dev - > netdev_ops = & vlan_netdev_ops ;
2008-01-21 11:22:11 +03:00
dev - > destructor = free_netdev ;
2008-07-08 14:22:42 +04:00
dev - > ethtool_ops = & vlan_ethtool_ops ;
2008-01-21 11:22:11 +03:00
memset ( dev - > broadcast , 0 , ETH_ALEN ) ;
}