ipvlan: decouple l3s mode dependencies from other modes
Right now ipvlan has a hard dependency on CONFIG_NETFILTER and otherwise it cannot be built. However, the only ipvlan operation mode that actually depends on netfilter is l3s, everything else is independent of it. Break this hard dependency such that users are able to use ipvlan l3 mode on systems where netfilter is not compiled in. Therefore, this adds a hidden CONFIG_IPVLAN_L3S bool which is defaulting to y when CONFIG_NETFILTER is set in order to retain existing behavior for l3s. All l3s related code is refactored into ipvlan_l3s.c that is compiled in when enabled. Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Cc: Mahesh Bandewar <maheshb@google.com> Cc: Florian Westphal <fw@strlen.de> Cc: Martynas Pumputis <m@lambda.lt> Acked-by: Florian Westphal <fw@strlen.de> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
998a8a8387
commit
c675e06a98
@ -145,13 +145,15 @@ config MACVTAP
|
|||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called macvtap.
|
will be called macvtap.
|
||||||
|
|
||||||
|
config IPVLAN_L3S
|
||||||
|
depends on NETFILTER
|
||||||
|
def_bool y
|
||||||
|
select NET_L3_MASTER_DEV
|
||||||
|
|
||||||
config IPVLAN
|
config IPVLAN
|
||||||
tristate "IP-VLAN support"
|
tristate "IP-VLAN support"
|
||||||
depends on INET
|
depends on INET
|
||||||
depends on IPV6 || !IPV6
|
depends on IPV6 || !IPV6
|
||||||
depends on NETFILTER
|
|
||||||
select NET_L3_MASTER_DEV
|
|
||||||
---help---
|
---help---
|
||||||
This allows one to create virtual devices off of a main interface
|
This allows one to create virtual devices off of a main interface
|
||||||
and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
|
and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
|
||||||
|
@ -5,4 +5,5 @@
|
|||||||
obj-$(CONFIG_IPVLAN) += ipvlan.o
|
obj-$(CONFIG_IPVLAN) += ipvlan.o
|
||||||
obj-$(CONFIG_IPVTAP) += ipvtap.o
|
obj-$(CONFIG_IPVTAP) += ipvtap.o
|
||||||
|
|
||||||
ipvlan-objs := ipvlan_core.o ipvlan_main.o
|
ipvlan-objs-$(CONFIG_IPVLAN_L3S) += ipvlan_l3s.o
|
||||||
|
ipvlan-objs := ipvlan_core.o ipvlan_main.o $(ipvlan-objs-y)
|
||||||
|
@ -165,10 +165,9 @@ struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
|
|||||||
const void *iaddr, bool is_v6);
|
const void *iaddr, bool is_v6);
|
||||||
bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6);
|
bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6);
|
||||||
void ipvlan_ht_addr_del(struct ipvl_addr *addr);
|
void ipvlan_ht_addr_del(struct ipvl_addr *addr);
|
||||||
struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, struct sk_buff *skb,
|
struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port, void *lyr3h,
|
||||||
u16 proto);
|
int addr_type, bool use_dest);
|
||||||
unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
|
void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type);
|
||||||
const struct nf_hook_state *state);
|
|
||||||
void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
|
void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
|
||||||
unsigned int len, bool success, bool mcast);
|
unsigned int len, bool success, bool mcast);
|
||||||
int ipvlan_link_new(struct net *src_net, struct net_device *dev,
|
int ipvlan_link_new(struct net *src_net, struct net_device *dev,
|
||||||
@ -177,6 +176,36 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
|
|||||||
void ipvlan_link_delete(struct net_device *dev, struct list_head *head);
|
void ipvlan_link_delete(struct net_device *dev, struct list_head *head);
|
||||||
void ipvlan_link_setup(struct net_device *dev);
|
void ipvlan_link_setup(struct net_device *dev);
|
||||||
int ipvlan_link_register(struct rtnl_link_ops *ops);
|
int ipvlan_link_register(struct rtnl_link_ops *ops);
|
||||||
|
#ifdef CONFIG_IPVLAN_L3S
|
||||||
|
int ipvlan_l3s_register(struct ipvl_port *port);
|
||||||
|
void ipvlan_l3s_unregister(struct ipvl_port *port);
|
||||||
|
void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet);
|
||||||
|
int ipvlan_l3s_init(void);
|
||||||
|
void ipvlan_l3s_cleanup(void);
|
||||||
|
#else
|
||||||
|
static inline int ipvlan_l3s_register(struct ipvl_port *port)
|
||||||
|
{
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ipvlan_l3s_unregister(struct ipvl_port *port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ipvlan_migrate_l3s_hook(struct net *oldnet,
|
||||||
|
struct net *newnet)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int ipvlan_l3s_init(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ipvlan_l3s_cleanup(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IPVLAN_L3S */
|
||||||
|
|
||||||
static inline bool netif_is_ipvlan_port(const struct net_device *dev)
|
static inline bool netif_is_ipvlan_port(const struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
@ -138,7 +138,7 @@ bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type)
|
void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type)
|
||||||
{
|
{
|
||||||
void *lyr3h = NULL;
|
void *lyr3h = NULL;
|
||||||
|
|
||||||
@ -355,9 +355,8 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port,
|
struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port, void *lyr3h,
|
||||||
void *lyr3h, int addr_type,
|
int addr_type, bool use_dest)
|
||||||
bool use_dest)
|
|
||||||
{
|
{
|
||||||
struct ipvl_addr *addr = NULL;
|
struct ipvl_addr *addr = NULL;
|
||||||
|
|
||||||
@ -647,7 +646,9 @@ int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
case IPVLAN_MODE_L2:
|
case IPVLAN_MODE_L2:
|
||||||
return ipvlan_xmit_mode_l2(skb, dev);
|
return ipvlan_xmit_mode_l2(skb, dev);
|
||||||
case IPVLAN_MODE_L3:
|
case IPVLAN_MODE_L3:
|
||||||
|
#ifdef CONFIG_IPVLAN_L3S
|
||||||
case IPVLAN_MODE_L3S:
|
case IPVLAN_MODE_L3S:
|
||||||
|
#endif
|
||||||
return ipvlan_xmit_mode_l3(skb, dev);
|
return ipvlan_xmit_mode_l3(skb, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,8 +744,10 @@ rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
|
|||||||
return ipvlan_handle_mode_l2(pskb, port);
|
return ipvlan_handle_mode_l2(pskb, port);
|
||||||
case IPVLAN_MODE_L3:
|
case IPVLAN_MODE_L3:
|
||||||
return ipvlan_handle_mode_l3(pskb, port);
|
return ipvlan_handle_mode_l3(pskb, port);
|
||||||
|
#ifdef CONFIG_IPVLAN_L3S
|
||||||
case IPVLAN_MODE_L3S:
|
case IPVLAN_MODE_L3S:
|
||||||
return RX_HANDLER_PASS;
|
return RX_HANDLER_PASS;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Should not reach here */
|
/* Should not reach here */
|
||||||
@ -753,97 +756,3 @@ rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
|
|||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return RX_HANDLER_CONSUMED;
|
return RX_HANDLER_CONSUMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ipvl_addr *ipvlan_skb_to_addr(struct sk_buff *skb,
|
|
||||||
struct net_device *dev)
|
|
||||||
{
|
|
||||||
struct ipvl_addr *addr = NULL;
|
|
||||||
struct ipvl_port *port;
|
|
||||||
void *lyr3h;
|
|
||||||
int addr_type;
|
|
||||||
|
|
||||||
if (!dev || !netif_is_ipvlan_port(dev))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
port = ipvlan_port_get_rcu(dev);
|
|
||||||
if (!port || port->mode != IPVLAN_MODE_L3S)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
|
|
||||||
if (!lyr3h)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
|
|
||||||
out:
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, struct sk_buff *skb,
|
|
||||||
u16 proto)
|
|
||||||
{
|
|
||||||
struct ipvl_addr *addr;
|
|
||||||
struct net_device *sdev;
|
|
||||||
|
|
||||||
addr = ipvlan_skb_to_addr(skb, dev);
|
|
||||||
if (!addr)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
sdev = addr->master->dev;
|
|
||||||
switch (proto) {
|
|
||||||
case AF_INET:
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct iphdr *ip4h = ip_hdr(skb);
|
|
||||||
|
|
||||||
err = ip_route_input_noref(skb, ip4h->daddr, ip4h->saddr,
|
|
||||||
ip4h->tos, sdev);
|
|
||||||
if (unlikely(err))
|
|
||||||
goto out;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
|
||||||
case AF_INET6:
|
|
||||||
{
|
|
||||||
struct dst_entry *dst;
|
|
||||||
struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
|
||||||
int flags = RT6_LOOKUP_F_HAS_SADDR;
|
|
||||||
struct flowi6 fl6 = {
|
|
||||||
.flowi6_iif = sdev->ifindex,
|
|
||||||
.daddr = ip6h->daddr,
|
|
||||||
.saddr = ip6h->saddr,
|
|
||||||
.flowlabel = ip6_flowinfo(ip6h),
|
|
||||||
.flowi6_mark = skb->mark,
|
|
||||||
.flowi6_proto = ip6h->nexthdr,
|
|
||||||
};
|
|
||||||
|
|
||||||
skb_dst_drop(skb);
|
|
||||||
dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6,
|
|
||||||
skb, flags);
|
|
||||||
skb_dst_set(skb, dst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
return skb;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
|
|
||||||
const struct nf_hook_state *state)
|
|
||||||
{
|
|
||||||
struct ipvl_addr *addr;
|
|
||||||
unsigned int len;
|
|
||||||
|
|
||||||
addr = ipvlan_skb_to_addr(skb, skb->dev);
|
|
||||||
if (!addr)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
skb->dev = addr->master->dev;
|
|
||||||
len = skb->len + ETH_HLEN;
|
|
||||||
ipvlan_count_rx(addr->master, len, true, false);
|
|
||||||
out:
|
|
||||||
return NF_ACCEPT;
|
|
||||||
}
|
|
||||||
|
227
drivers/net/ipvlan/ipvlan_l3s.c
Normal file
227
drivers/net/ipvlan/ipvlan_l3s.c
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
|
||||||
|
*
|
||||||
|
* 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 "ipvlan.h"
|
||||||
|
|
||||||
|
static unsigned int ipvlan_netid __read_mostly;
|
||||||
|
|
||||||
|
struct ipvlan_netns {
|
||||||
|
unsigned int ipvl_nf_hook_refcnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ipvl_addr *ipvlan_skb_to_addr(struct sk_buff *skb,
|
||||||
|
struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct ipvl_addr *addr = NULL;
|
||||||
|
struct ipvl_port *port;
|
||||||
|
int addr_type;
|
||||||
|
void *lyr3h;
|
||||||
|
|
||||||
|
if (!dev || !netif_is_ipvlan_port(dev))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
port = ipvlan_port_get_rcu(dev);
|
||||||
|
if (!port || port->mode != IPVLAN_MODE_L3S)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
|
||||||
|
if (!lyr3h)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
|
||||||
|
out:
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *ipvlan_l3_rcv(struct net_device *dev,
|
||||||
|
struct sk_buff *skb, u16 proto)
|
||||||
|
{
|
||||||
|
struct ipvl_addr *addr;
|
||||||
|
struct net_device *sdev;
|
||||||
|
|
||||||
|
addr = ipvlan_skb_to_addr(skb, dev);
|
||||||
|
if (!addr)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
sdev = addr->master->dev;
|
||||||
|
switch (proto) {
|
||||||
|
case AF_INET:
|
||||||
|
{
|
||||||
|
struct iphdr *ip4h = ip_hdr(skb);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = ip_route_input_noref(skb, ip4h->daddr, ip4h->saddr,
|
||||||
|
ip4h->tos, sdev);
|
||||||
|
if (unlikely(err))
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
case AF_INET6:
|
||||||
|
{
|
||||||
|
struct dst_entry *dst;
|
||||||
|
struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
||||||
|
int flags = RT6_LOOKUP_F_HAS_SADDR;
|
||||||
|
struct flowi6 fl6 = {
|
||||||
|
.flowi6_iif = sdev->ifindex,
|
||||||
|
.daddr = ip6h->daddr,
|
||||||
|
.saddr = ip6h->saddr,
|
||||||
|
.flowlabel = ip6_flowinfo(ip6h),
|
||||||
|
.flowi6_mark = skb->mark,
|
||||||
|
.flowi6_proto = ip6h->nexthdr,
|
||||||
|
};
|
||||||
|
|
||||||
|
skb_dst_drop(skb);
|
||||||
|
dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6,
|
||||||
|
skb, flags);
|
||||||
|
skb_dst_set(skb, dst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return skb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct l3mdev_ops ipvl_l3mdev_ops = {
|
||||||
|
.l3mdev_l3_rcv = ipvlan_l3_rcv,
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
|
||||||
|
const struct nf_hook_state *state)
|
||||||
|
{
|
||||||
|
struct ipvl_addr *addr;
|
||||||
|
unsigned int len;
|
||||||
|
|
||||||
|
addr = ipvlan_skb_to_addr(skb, skb->dev);
|
||||||
|
if (!addr)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
skb->dev = addr->master->dev;
|
||||||
|
len = skb->len + ETH_HLEN;
|
||||||
|
ipvlan_count_rx(addr->master, len, true, false);
|
||||||
|
out:
|
||||||
|
return NF_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct nf_hook_ops ipvl_nfops[] = {
|
||||||
|
{
|
||||||
|
.hook = ipvlan_nf_input,
|
||||||
|
.pf = NFPROTO_IPV4,
|
||||||
|
.hooknum = NF_INET_LOCAL_IN,
|
||||||
|
.priority = INT_MAX,
|
||||||
|
},
|
||||||
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
{
|
||||||
|
.hook = ipvlan_nf_input,
|
||||||
|
.pf = NFPROTO_IPV6,
|
||||||
|
.hooknum = NF_INET_LOCAL_IN,
|
||||||
|
.priority = INT_MAX,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ipvlan_register_nf_hook(struct net *net)
|
||||||
|
{
|
||||||
|
struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (!vnet->ipvl_nf_hook_refcnt) {
|
||||||
|
err = nf_register_net_hooks(net, ipvl_nfops,
|
||||||
|
ARRAY_SIZE(ipvl_nfops));
|
||||||
|
if (!err)
|
||||||
|
vnet->ipvl_nf_hook_refcnt = 1;
|
||||||
|
} else {
|
||||||
|
vnet->ipvl_nf_hook_refcnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ipvlan_unregister_nf_hook(struct net *net)
|
||||||
|
{
|
||||||
|
struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
|
||||||
|
|
||||||
|
if (WARN_ON(!vnet->ipvl_nf_hook_refcnt))
|
||||||
|
return;
|
||||||
|
|
||||||
|
vnet->ipvl_nf_hook_refcnt--;
|
||||||
|
if (!vnet->ipvl_nf_hook_refcnt)
|
||||||
|
nf_unregister_net_hooks(net, ipvl_nfops,
|
||||||
|
ARRAY_SIZE(ipvl_nfops));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet)
|
||||||
|
{
|
||||||
|
struct ipvlan_netns *old_vnet;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
|
old_vnet = net_generic(oldnet, ipvlan_netid);
|
||||||
|
if (!old_vnet->ipvl_nf_hook_refcnt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ipvlan_register_nf_hook(newnet);
|
||||||
|
ipvlan_unregister_nf_hook(oldnet);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ipvlan_ns_exit(struct net *net)
|
||||||
|
{
|
||||||
|
struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) {
|
||||||
|
vnet->ipvl_nf_hook_refcnt = 0;
|
||||||
|
nf_unregister_net_hooks(net, ipvl_nfops,
|
||||||
|
ARRAY_SIZE(ipvl_nfops));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pernet_operations ipvlan_net_ops = {
|
||||||
|
.id = &ipvlan_netid,
|
||||||
|
.size = sizeof(struct ipvlan_netns),
|
||||||
|
.exit = ipvlan_ns_exit,
|
||||||
|
};
|
||||||
|
|
||||||
|
int ipvlan_l3s_init(void)
|
||||||
|
{
|
||||||
|
return register_pernet_subsys(&ipvlan_net_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipvlan_l3s_cleanup(void)
|
||||||
|
{
|
||||||
|
unregister_pernet_subsys(&ipvlan_net_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipvlan_l3s_register(struct ipvl_port *port)
|
||||||
|
{
|
||||||
|
struct net_device *dev = port->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
|
ret = ipvlan_register_nf_hook(read_pnet(&port->pnet));
|
||||||
|
if (!ret) {
|
||||||
|
dev->l3mdev_ops = &ipvl_l3mdev_ops;
|
||||||
|
dev->priv_flags |= IFF_L3MDEV_MASTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipvlan_l3s_unregister(struct ipvl_port *port)
|
||||||
|
{
|
||||||
|
struct net_device *dev = port->dev;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
|
dev->priv_flags &= ~IFF_L3MDEV_MASTER;
|
||||||
|
ipvlan_unregister_nf_hook(read_pnet(&port->pnet));
|
||||||
|
dev->l3mdev_ops = NULL;
|
||||||
|
}
|
@ -9,73 +9,10 @@
|
|||||||
|
|
||||||
#include "ipvlan.h"
|
#include "ipvlan.h"
|
||||||
|
|
||||||
static unsigned int ipvlan_netid __read_mostly;
|
|
||||||
|
|
||||||
struct ipvlan_netns {
|
|
||||||
unsigned int ipvl_nf_hook_refcnt;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct nf_hook_ops ipvl_nfops[] = {
|
|
||||||
{
|
|
||||||
.hook = ipvlan_nf_input,
|
|
||||||
.pf = NFPROTO_IPV4,
|
|
||||||
.hooknum = NF_INET_LOCAL_IN,
|
|
||||||
.priority = INT_MAX,
|
|
||||||
},
|
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
|
||||||
{
|
|
||||||
.hook = ipvlan_nf_input,
|
|
||||||
.pf = NFPROTO_IPV6,
|
|
||||||
.hooknum = NF_INET_LOCAL_IN,
|
|
||||||
.priority = INT_MAX,
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct l3mdev_ops ipvl_l3mdev_ops = {
|
|
||||||
.l3mdev_l3_rcv = ipvlan_l3_rcv,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev)
|
|
||||||
{
|
|
||||||
ipvlan->dev->mtu = dev->mtu;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ipvlan_register_nf_hook(struct net *net)
|
|
||||||
{
|
|
||||||
struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if (!vnet->ipvl_nf_hook_refcnt) {
|
|
||||||
err = nf_register_net_hooks(net, ipvl_nfops,
|
|
||||||
ARRAY_SIZE(ipvl_nfops));
|
|
||||||
if (!err)
|
|
||||||
vnet->ipvl_nf_hook_refcnt = 1;
|
|
||||||
} else {
|
|
||||||
vnet->ipvl_nf_hook_refcnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ipvlan_unregister_nf_hook(struct net *net)
|
|
||||||
{
|
|
||||||
struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
|
|
||||||
|
|
||||||
if (WARN_ON(!vnet->ipvl_nf_hook_refcnt))
|
|
||||||
return;
|
|
||||||
|
|
||||||
vnet->ipvl_nf_hook_refcnt--;
|
|
||||||
if (!vnet->ipvl_nf_hook_refcnt)
|
|
||||||
nf_unregister_net_hooks(net, ipvl_nfops,
|
|
||||||
ARRAY_SIZE(ipvl_nfops));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
|
static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
|
||||||
struct netlink_ext_ack *extack)
|
struct netlink_ext_ack *extack)
|
||||||
{
|
{
|
||||||
struct ipvl_dev *ipvlan;
|
struct ipvl_dev *ipvlan;
|
||||||
struct net_device *mdev = port->dev;
|
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -97,17 +34,12 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
|
|||||||
}
|
}
|
||||||
if (nval == IPVLAN_MODE_L3S) {
|
if (nval == IPVLAN_MODE_L3S) {
|
||||||
/* New mode is L3S */
|
/* New mode is L3S */
|
||||||
err = ipvlan_register_nf_hook(read_pnet(&port->pnet));
|
err = ipvlan_l3s_register(port);
|
||||||
if (!err) {
|
if (err)
|
||||||
mdev->l3mdev_ops = &ipvl_l3mdev_ops;
|
|
||||||
mdev->priv_flags |= IFF_L3MDEV_MASTER;
|
|
||||||
} else
|
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (port->mode == IPVLAN_MODE_L3S) {
|
} else if (port->mode == IPVLAN_MODE_L3S) {
|
||||||
/* Old mode was L3S */
|
/* Old mode was L3S */
|
||||||
mdev->priv_flags &= ~IFF_L3MDEV_MASTER;
|
ipvlan_l3s_unregister(port);
|
||||||
ipvlan_unregister_nf_hook(read_pnet(&port->pnet));
|
|
||||||
mdev->l3mdev_ops = NULL;
|
|
||||||
}
|
}
|
||||||
port->mode = nval;
|
port->mode = nval;
|
||||||
}
|
}
|
||||||
@ -166,11 +98,8 @@ static void ipvlan_port_destroy(struct net_device *dev)
|
|||||||
struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
|
struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
|
||||||
if (port->mode == IPVLAN_MODE_L3S) {
|
if (port->mode == IPVLAN_MODE_L3S)
|
||||||
dev->priv_flags &= ~IFF_L3MDEV_MASTER;
|
ipvlan_l3s_unregister(port);
|
||||||
ipvlan_unregister_nf_hook(dev_net(dev));
|
|
||||||
dev->l3mdev_ops = NULL;
|
|
||||||
}
|
|
||||||
netdev_rx_handler_unregister(dev);
|
netdev_rx_handler_unregister(dev);
|
||||||
cancel_work_sync(&port->wq);
|
cancel_work_sync(&port->wq);
|
||||||
while ((skb = __skb_dequeue(&port->backlog)) != NULL) {
|
while ((skb = __skb_dequeue(&port->backlog)) != NULL) {
|
||||||
@ -446,6 +375,11 @@ static const struct header_ops ipvlan_header_ops = {
|
|||||||
.cache_update = eth_header_cache_update,
|
.cache_update = eth_header_cache_update,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev)
|
||||||
|
{
|
||||||
|
ipvlan->dev->mtu = dev->mtu;
|
||||||
|
}
|
||||||
|
|
||||||
static bool netif_is_ipvlan(const struct net_device *dev)
|
static bool netif_is_ipvlan(const struct net_device *dev)
|
||||||
{
|
{
|
||||||
/* both ipvlan and ipvtap devices use the same netdev_ops */
|
/* both ipvlan and ipvtap devices use the same netdev_ops */
|
||||||
@ -781,7 +715,6 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
|||||||
|
|
||||||
case NETDEV_REGISTER: {
|
case NETDEV_REGISTER: {
|
||||||
struct net *oldnet, *newnet = dev_net(dev);
|
struct net *oldnet, *newnet = dev_net(dev);
|
||||||
struct ipvlan_netns *old_vnet;
|
|
||||||
|
|
||||||
oldnet = read_pnet(&port->pnet);
|
oldnet = read_pnet(&port->pnet);
|
||||||
if (net_eq(newnet, oldnet))
|
if (net_eq(newnet, oldnet))
|
||||||
@ -789,12 +722,7 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
|||||||
|
|
||||||
write_pnet(&port->pnet, newnet);
|
write_pnet(&port->pnet, newnet);
|
||||||
|
|
||||||
old_vnet = net_generic(oldnet, ipvlan_netid);
|
ipvlan_migrate_l3s_hook(oldnet, newnet);
|
||||||
if (!old_vnet->ipvl_nf_hook_refcnt)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ipvlan_register_nf_hook(newnet);
|
|
||||||
ipvlan_unregister_nf_hook(oldnet);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NETDEV_UNREGISTER:
|
case NETDEV_UNREGISTER:
|
||||||
@ -1068,23 +996,6 @@ static struct notifier_block ipvlan_addr6_vtor_notifier_block __read_mostly = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void ipvlan_ns_exit(struct net *net)
|
|
||||||
{
|
|
||||||
struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
|
|
||||||
|
|
||||||
if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) {
|
|
||||||
vnet->ipvl_nf_hook_refcnt = 0;
|
|
||||||
nf_unregister_net_hooks(net, ipvl_nfops,
|
|
||||||
ARRAY_SIZE(ipvl_nfops));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct pernet_operations ipvlan_net_ops = {
|
|
||||||
.id = &ipvlan_netid,
|
|
||||||
.size = sizeof(struct ipvlan_netns),
|
|
||||||
.exit = ipvlan_ns_exit,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init ipvlan_init_module(void)
|
static int __init ipvlan_init_module(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -1099,13 +1010,13 @@ static int __init ipvlan_init_module(void)
|
|||||||
register_inetaddr_notifier(&ipvlan_addr4_notifier_block);
|
register_inetaddr_notifier(&ipvlan_addr4_notifier_block);
|
||||||
register_inetaddr_validator_notifier(&ipvlan_addr4_vtor_notifier_block);
|
register_inetaddr_validator_notifier(&ipvlan_addr4_vtor_notifier_block);
|
||||||
|
|
||||||
err = register_pernet_subsys(&ipvlan_net_ops);
|
err = ipvlan_l3s_init();
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
err = ipvlan_link_register(&ipvlan_link_ops);
|
err = ipvlan_link_register(&ipvlan_link_ops);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
unregister_pernet_subsys(&ipvlan_net_ops);
|
ipvlan_l3s_cleanup();
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1126,7 +1037,7 @@ error:
|
|||||||
static void __exit ipvlan_cleanup_module(void)
|
static void __exit ipvlan_cleanup_module(void)
|
||||||
{
|
{
|
||||||
rtnl_link_unregister(&ipvlan_link_ops);
|
rtnl_link_unregister(&ipvlan_link_ops);
|
||||||
unregister_pernet_subsys(&ipvlan_net_ops);
|
ipvlan_l3s_cleanup();
|
||||||
unregister_netdevice_notifier(&ipvlan_notifier_block);
|
unregister_netdevice_notifier(&ipvlan_notifier_block);
|
||||||
unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
|
unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
|
||||||
unregister_inetaddr_validator_notifier(
|
unregister_inetaddr_validator_notifier(
|
||||||
|
Loading…
Reference in New Issue
Block a user