2011-10-26 06:26:31 +04:00
/*
2012-05-04 05:55:23 +04:00
* Copyright ( c ) 2007 - 2012 Nicira , Inc .
2011-10-26 06:26:31 +04:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/if_arp.h>
# include <linux/if_bridge.h>
# include <linux/if_vlan.h>
# include <linux/kernel.h>
# include <linux/llc.h>
# include <linux/rtnetlink.h>
# include <linux/skbuff.h>
2013-07-26 16:01:54 +04:00
# include <linux/openvswitch.h>
2015-07-29 14:52:06 +03:00
# include <linux/export.h>
2011-10-26 06:26:31 +04:00
2015-07-21 11:44:06 +03:00
# include <net/ip_tunnels.h>
# include <net/rtnetlink.h>
2011-10-26 06:26:31 +04:00
# include "datapath.h"
2015-07-21 11:44:06 +03:00
# include "vport.h"
2011-10-26 06:26:31 +04:00
# include "vport-internal_dev.h"
# include "vport-netdev.h"
2014-10-22 19:29:06 +04:00
static struct vport_ops ovs_netdev_vport_ops ;
2011-10-26 06:26:31 +04:00
/* Must be called with rcu_read_lock. */
2015-08-30 03:44:07 +03:00
static void netdev_port_receive ( struct sk_buff * skb )
2011-10-26 06:26:31 +04:00
{
2015-08-30 03:44:07 +03:00
struct vport * vport ;
vport = ovs_netdev_get_vport ( skb - > dev ) ;
2013-01-22 11:57:26 +04:00
if ( unlikely ( ! vport ) )
goto error ;
if ( unlikely ( skb_warn_if_lro ( skb ) ) )
goto error ;
2011-10-26 06:26:31 +04:00
/* Make our own copy of the packet. Otherwise we will mangle the
* packet for anyone who came before us ( e . g . tcpdump via AF_PACKET ) .
2013-02-22 15:41:26 +04:00
*/
2011-10-26 06:26:31 +04:00
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( unlikely ( ! skb ) )
return ;
2016-11-10 18:28:24 +03:00
if ( skb - > dev - > type = = ARPHRD_ETHER ) {
skb_push ( skb , ETH_HLEN ) ;
skb_postpush_rcsum ( skb , skb - > data , ETH_HLEN ) ;
}
2015-08-20 14:56:25 +03:00
ovs_vport_receive ( vport , skb , skb_tunnel_info ( skb ) ) ;
2013-01-22 11:57:26 +04:00
return ;
error :
kfree_skb ( skb ) ;
2011-10-26 06:26:31 +04:00
}
/* Called with rcu_read_lock and bottom-halves disabled. */
static rx_handler_result_t netdev_frame_hook ( struct sk_buff * * pskb )
{
struct sk_buff * skb = * pskb ;
if ( unlikely ( skb - > pkt_type = = PACKET_LOOPBACK ) )
return RX_HANDLER_PASS ;
2015-08-30 03:44:07 +03:00
netdev_port_receive ( skb ) ;
2011-10-26 06:26:31 +04:00
return RX_HANDLER_CONSUMED ;
}
2014-11-06 17:58:52 +03:00
static struct net_device * get_dpdev ( const struct datapath * dp )
2013-07-26 16:01:54 +04:00
{
struct vport * local ;
local = ovs_vport_ovsl ( dp , OVSP_LOCAL ) ;
BUG_ON ( ! local ) ;
2015-07-21 11:44:04 +03:00
return local - > dev ;
2013-07-26 16:01:54 +04:00
}
2015-07-29 14:52:06 +03:00
struct vport * ovs_netdev_link ( struct vport * vport , const char * name )
2011-10-26 06:26:31 +04:00
{
int err ;
2015-07-21 11:44:04 +03:00
vport - > dev = dev_get_by_name ( ovs_dp_get_net ( vport - > dp ) , name ) ;
if ( ! vport - > dev ) {
2011-10-26 06:26:31 +04:00
err = - ENODEV ;
goto error_free_vport ;
}
2015-07-21 11:44:04 +03:00
if ( vport - > dev - > flags & IFF_LOOPBACK | |
2016-11-10 18:28:24 +03:00
( vport - > dev - > type ! = ARPHRD_ETHER & &
vport - > dev - > type ! = ARPHRD_NONE ) | |
2015-07-21 11:44:04 +03:00
ovs_is_internal_dev ( vport - > dev ) ) {
2011-10-26 06:26:31 +04:00
err = - EINVAL ;
goto error_put ;
}
2013-04-16 00:23:03 +04:00
rtnl_lock ( ) ;
2015-07-21 11:44:04 +03:00
err = netdev_master_upper_dev_link ( vport - > dev ,
2017-10-05 03:48:47 +03:00
get_dpdev ( vport - > dp ) ,
NULL , NULL , NULL ) ;
2013-07-26 16:01:54 +04:00
if ( err )
goto error_unlock ;
2015-07-21 11:44:04 +03:00
err = netdev_rx_handler_register ( vport - > dev , netdev_frame_hook ,
2011-10-26 06:26:31 +04:00
vport ) ;
if ( err )
2013-07-26 16:01:54 +04:00
goto error_master_upper_dev_unlink ;
2011-10-26 06:26:31 +04:00
2015-07-21 11:44:04 +03:00
dev_disable_lro ( vport - > dev ) ;
dev_set_promiscuity ( vport - > dev , 1 ) ;
vport - > dev - > priv_flags | = IFF_OVS_DATAPATH ;
2013-04-16 00:23:03 +04:00
rtnl_unlock ( ) ;
2011-10-26 06:26:31 +04:00
return vport ;
2013-07-26 16:01:54 +04:00
error_master_upper_dev_unlink :
2015-07-21 11:44:04 +03:00
netdev_upper_dev_unlink ( vport - > dev , get_dpdev ( vport - > dp ) ) ;
2013-04-16 00:23:03 +04:00
error_unlock :
rtnl_unlock ( ) ;
2011-10-26 06:26:31 +04:00
error_put :
2015-07-21 11:44:04 +03:00
dev_put ( vport - > dev ) ;
2011-10-26 06:26:31 +04:00
error_free_vport :
ovs_vport_free ( vport ) ;
return ERR_PTR ( err ) ;
}
2015-07-29 14:52:06 +03:00
EXPORT_SYMBOL_GPL ( ovs_netdev_link ) ;
2011-10-26 06:26:31 +04:00
2015-07-21 11:44:04 +03:00
static struct vport * netdev_create ( const struct vport_parms * parms )
{
struct vport * vport ;
vport = ovs_vport_alloc ( 0 , & ovs_netdev_vport_ops , parms ) ;
if ( IS_ERR ( vport ) )
return vport ;
2015-07-29 14:52:06 +03:00
return ovs_netdev_link ( vport , parms - > name ) ;
2015-07-21 11:44:04 +03:00
}
2015-08-08 09:51:33 +03:00
static void vport_netdev_free ( struct rcu_head * rcu )
2012-11-29 02:01:52 +04:00
{
2015-07-21 11:44:04 +03:00
struct vport * vport = container_of ( rcu , struct vport , rcu ) ;
2012-11-29 02:01:52 +04:00
2015-07-21 11:44:06 +03:00
if ( vport - > dev )
dev_put ( vport - > dev ) ;
2015-07-21 11:44:04 +03:00
ovs_vport_free ( vport ) ;
2012-11-29 02:01:52 +04:00
}
2013-10-16 01:54:11 +04:00
void ovs_netdev_detach_dev ( struct vport * vport )
2011-10-26 06:26:31 +04:00
{
2013-10-16 01:54:11 +04:00
ASSERT_RTNL ( ) ;
2015-07-21 11:44:04 +03:00
vport - > dev - > priv_flags & = ~ IFF_OVS_DATAPATH ;
netdev_rx_handler_unregister ( vport - > dev ) ;
netdev_upper_dev_unlink ( vport - > dev ,
netdev_master_upper_dev_get ( vport - > dev ) ) ;
dev_set_promiscuity ( vport - > dev , - 1 ) ;
2013-10-16 01:54:11 +04:00
}
static void netdev_destroy ( struct vport * vport )
{
rtnl_lock ( ) ;
2015-07-21 11:44:04 +03:00
if ( vport - > dev - > priv_flags & IFF_OVS_DATAPATH )
2013-10-16 01:54:11 +04:00
ovs_netdev_detach_dev ( vport ) ;
2013-04-16 00:23:03 +04:00
rtnl_unlock ( ) ;
2011-10-26 06:26:31 +04:00
2015-08-08 09:51:33 +03:00
call_rcu ( & vport - > rcu , vport_netdev_free ) ;
2011-10-26 06:26:31 +04:00
}
2015-08-08 09:51:33 +03:00
void ovs_netdev_tunnel_destroy ( struct vport * vport )
{
rtnl_lock ( ) ;
if ( vport - > dev - > priv_flags & IFF_OVS_DATAPATH )
ovs_netdev_detach_dev ( vport ) ;
2015-12-01 20:33:36 +03:00
/* We can be invoked by both explicit vport deletion and
* underlying netdev deregistration ; delete the link only
* if it ' s not already shutting down .
*/
if ( vport - > dev - > reg_state = = NETREG_REGISTERED )
rtnl_delete_link ( vport - > dev ) ;
2015-08-08 09:51:33 +03:00
dev_put ( vport - > dev ) ;
vport - > dev = NULL ;
rtnl_unlock ( ) ;
call_rcu ( & vport - > rcu , vport_netdev_free ) ;
}
EXPORT_SYMBOL_GPL ( ovs_netdev_tunnel_destroy ) ;
2011-10-26 06:26:31 +04:00
/* Returns null if this device is not attached to a datapath. */
struct vport * ovs_netdev_get_vport ( struct net_device * dev )
{
if ( likely ( dev - > priv_flags & IFF_OVS_DATAPATH ) )
return ( struct vport * )
rcu_dereference_rtnl ( dev - > rx_handler_data ) ;
else
return NULL ;
}
2014-10-22 19:29:06 +04:00
static struct vport_ops ovs_netdev_vport_ops = {
2011-10-26 06:26:31 +04:00
. type = OVS_VPORT_TYPE_NETDEV ,
. create = netdev_create ,
. destroy = netdev_destroy ,
2015-10-21 09:00:10 +03:00
. send = dev_queue_xmit ,
2011-10-26 06:26:31 +04:00
} ;
2014-10-22 19:29:06 +04:00
int __init ovs_netdev_init ( void )
{
2015-07-29 14:52:06 +03:00
return ovs_vport_ops_register ( & ovs_netdev_vport_ops ) ;
2014-10-22 19:29:06 +04:00
}
void ovs_netdev_exit ( void )
{
ovs_vport_ops_unregister ( & ovs_netdev_vport_ops ) ;
}