162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2007-2012 Nicira, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/if_arp.h>
962306a36Sopenharmony_ci#include <linux/if_bridge.h>
1062306a36Sopenharmony_ci#include <linux/if_vlan.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/llc.h>
1362306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1462306a36Sopenharmony_ci#include <linux/skbuff.h>
1562306a36Sopenharmony_ci#include <linux/openvswitch.h>
1662306a36Sopenharmony_ci#include <linux/export.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <net/ip_tunnels.h>
1962306a36Sopenharmony_ci#include <net/rtnetlink.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "datapath.h"
2262306a36Sopenharmony_ci#include "vport.h"
2362306a36Sopenharmony_ci#include "vport-internal_dev.h"
2462306a36Sopenharmony_ci#include "vport-netdev.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct vport_ops ovs_netdev_vport_ops;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Must be called with rcu_read_lock. */
2962306a36Sopenharmony_cistatic void netdev_port_receive(struct sk_buff *skb)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct vport *vport;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	vport = ovs_netdev_get_vport(skb->dev);
3462306a36Sopenharmony_ci	if (unlikely(!vport))
3562306a36Sopenharmony_ci		goto error;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (unlikely(skb_warn_if_lro(skb)))
3862306a36Sopenharmony_ci		goto error;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/* Make our own copy of the packet.  Otherwise we will mangle the
4162306a36Sopenharmony_ci	 * packet for anyone who came before us (e.g. tcpdump via AF_PACKET).
4262306a36Sopenharmony_ci	 */
4362306a36Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
4462306a36Sopenharmony_ci	if (unlikely(!skb))
4562306a36Sopenharmony_ci		return;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (skb->dev->type == ARPHRD_ETHER)
4862306a36Sopenharmony_ci		skb_push_rcsum(skb, ETH_HLEN);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	ovs_vport_receive(vport, skb, skb_tunnel_info(skb));
5162306a36Sopenharmony_ci	return;
5262306a36Sopenharmony_cierror:
5362306a36Sopenharmony_ci	kfree_skb(skb);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Called with rcu_read_lock and bottom-halves disabled. */
5762306a36Sopenharmony_cistatic rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct sk_buff *skb = *pskb;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
6262306a36Sopenharmony_ci		return RX_HANDLER_PASS;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	netdev_port_receive(skb);
6562306a36Sopenharmony_ci	return RX_HANDLER_CONSUMED;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic struct net_device *get_dpdev(const struct datapath *dp)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct vport *local;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	local = ovs_vport_ovsl(dp, OVSP_LOCAL);
7362306a36Sopenharmony_ci	return local->dev;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct vport *ovs_netdev_link(struct vport *vport, const char *name)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int err;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name);
8162306a36Sopenharmony_ci	if (!vport->dev) {
8262306a36Sopenharmony_ci		err = -ENODEV;
8362306a36Sopenharmony_ci		goto error_free_vport;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	netdev_tracker_alloc(vport->dev, &vport->dev_tracker, GFP_KERNEL);
8662306a36Sopenharmony_ci	if (vport->dev->flags & IFF_LOOPBACK ||
8762306a36Sopenharmony_ci	    (vport->dev->type != ARPHRD_ETHER &&
8862306a36Sopenharmony_ci	     vport->dev->type != ARPHRD_NONE) ||
8962306a36Sopenharmony_ci	    ovs_is_internal_dev(vport->dev)) {
9062306a36Sopenharmony_ci		err = -EINVAL;
9162306a36Sopenharmony_ci		goto error_put;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	rtnl_lock();
9562306a36Sopenharmony_ci	err = netdev_master_upper_dev_link(vport->dev,
9662306a36Sopenharmony_ci					   get_dpdev(vport->dp),
9762306a36Sopenharmony_ci					   NULL, NULL, NULL);
9862306a36Sopenharmony_ci	if (err)
9962306a36Sopenharmony_ci		goto error_unlock;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	err = netdev_rx_handler_register(vport->dev, netdev_frame_hook,
10262306a36Sopenharmony_ci					 vport);
10362306a36Sopenharmony_ci	if (err)
10462306a36Sopenharmony_ci		goto error_master_upper_dev_unlink;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	dev_disable_lro(vport->dev);
10762306a36Sopenharmony_ci	dev_set_promiscuity(vport->dev, 1);
10862306a36Sopenharmony_ci	vport->dev->priv_flags |= IFF_OVS_DATAPATH;
10962306a36Sopenharmony_ci	rtnl_unlock();
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return vport;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cierror_master_upper_dev_unlink:
11462306a36Sopenharmony_ci	netdev_upper_dev_unlink(vport->dev, get_dpdev(vport->dp));
11562306a36Sopenharmony_cierror_unlock:
11662306a36Sopenharmony_ci	rtnl_unlock();
11762306a36Sopenharmony_cierror_put:
11862306a36Sopenharmony_ci	netdev_put(vport->dev, &vport->dev_tracker);
11962306a36Sopenharmony_cierror_free_vport:
12062306a36Sopenharmony_ci	ovs_vport_free(vport);
12162306a36Sopenharmony_ci	return ERR_PTR(err);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ovs_netdev_link);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic struct vport *netdev_create(const struct vport_parms *parms)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct vport *vport;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	vport = ovs_vport_alloc(0, &ovs_netdev_vport_ops, parms);
13062306a36Sopenharmony_ci	if (IS_ERR(vport))
13162306a36Sopenharmony_ci		return vport;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return ovs_netdev_link(vport, parms->name);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void vport_netdev_free(struct rcu_head *rcu)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct vport *vport = container_of(rcu, struct vport, rcu);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	netdev_put(vport->dev, &vport->dev_tracker);
14162306a36Sopenharmony_ci	ovs_vport_free(vport);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_civoid ovs_netdev_detach_dev(struct vport *vport)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	ASSERT_RTNL();
14762306a36Sopenharmony_ci	vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
14862306a36Sopenharmony_ci	netdev_rx_handler_unregister(vport->dev);
14962306a36Sopenharmony_ci	netdev_upper_dev_unlink(vport->dev,
15062306a36Sopenharmony_ci				netdev_master_upper_dev_get(vport->dev));
15162306a36Sopenharmony_ci	dev_set_promiscuity(vport->dev, -1);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void netdev_destroy(struct vport *vport)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	rtnl_lock();
15762306a36Sopenharmony_ci	if (netif_is_ovs_port(vport->dev))
15862306a36Sopenharmony_ci		ovs_netdev_detach_dev(vport);
15962306a36Sopenharmony_ci	rtnl_unlock();
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	call_rcu(&vport->rcu, vport_netdev_free);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_civoid ovs_netdev_tunnel_destroy(struct vport *vport)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	rtnl_lock();
16762306a36Sopenharmony_ci	if (netif_is_ovs_port(vport->dev))
16862306a36Sopenharmony_ci		ovs_netdev_detach_dev(vport);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* We can be invoked by both explicit vport deletion and
17162306a36Sopenharmony_ci	 * underlying netdev deregistration; delete the link only
17262306a36Sopenharmony_ci	 * if it's not already shutting down.
17362306a36Sopenharmony_ci	 */
17462306a36Sopenharmony_ci	if (vport->dev->reg_state == NETREG_REGISTERED)
17562306a36Sopenharmony_ci		rtnl_delete_link(vport->dev, 0, NULL);
17662306a36Sopenharmony_ci	netdev_put(vport->dev, &vport->dev_tracker);
17762306a36Sopenharmony_ci	vport->dev = NULL;
17862306a36Sopenharmony_ci	rtnl_unlock();
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	call_rcu(&vport->rcu, vport_netdev_free);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/* Returns null if this device is not attached to a datapath. */
18562306a36Sopenharmony_cistruct vport *ovs_netdev_get_vport(struct net_device *dev)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	if (likely(netif_is_ovs_port(dev)))
18862306a36Sopenharmony_ci		return (struct vport *)
18962306a36Sopenharmony_ci			rcu_dereference_rtnl(dev->rx_handler_data);
19062306a36Sopenharmony_ci	else
19162306a36Sopenharmony_ci		return NULL;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic struct vport_ops ovs_netdev_vport_ops = {
19562306a36Sopenharmony_ci	.type		= OVS_VPORT_TYPE_NETDEV,
19662306a36Sopenharmony_ci	.create		= netdev_create,
19762306a36Sopenharmony_ci	.destroy	= netdev_destroy,
19862306a36Sopenharmony_ci	.send		= dev_queue_xmit,
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciint __init ovs_netdev_init(void)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	return ovs_vport_ops_register(&ovs_netdev_vport_ops);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_civoid ovs_netdev_exit(void)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	ovs_vport_ops_unregister(&ovs_netdev_vport_ops);
20962306a36Sopenharmony_ci}
210