mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
![Paolo Abeni](/assets/img/avatar_default.png)
This changeset moves ipvlan address under RCU protection, using
a per ipvlan device spinlock to protect list mutation and RCU
read access to protect list traversal.
Also explicitly use RCU read lock to traverse the per port
ipvlans list, so that we can now perform a full address lookup
without asserting the RTNL lock.
Overall this allows the ipvlan driver to check fully for duplicate
addresses - before this commit ipv6 addresses assigned by autoconf
via prefix delegation where accepted without any check - and avoid
the following rntl assertion failure still in the same code path:
RTNL: assertion failed at drivers/net/ipvlan/ipvlan_core.c (124)
WARNING: CPU: 15 PID: 0 at drivers/net/ipvlan/ipvlan_core.c:124 ipvlan_addr_busy+0x97/0xa0 [ipvlan]
Modules linked in: ipvlan(E) ixgbe
CPU: 15 PID: 0 Comm: swapper/15 Tainted: G E 4.16.0-rc2.ipvlan+ #1782
Hardware name: Dell Inc. PowerEdge R730/072T6D, BIOS 2.1.7 06/16/2016
RIP: 0010:ipvlan_addr_busy+0x97/0xa0 [ipvlan]
RSP: 0018:ffff881ff9e03768 EFLAGS: 00010286
RAX: 0000000000000000 RBX: ffff881fdf2a9000 RCX: 0000000000000000
RDX: 0000000000000001 RSI: 00000000000000f6 RDI: 0000000000000300
RBP: ffff881fdf2a8000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000001 R11: ffff881ff9e034c0 R12: ffff881fe07bcc00
R13: 0000000000000001 R14: ffffffffa02002b0 R15: 0000000000000001
FS: 0000000000000000(0000) GS:ffff881ff9e00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fc5c1a4f248 CR3: 000000207e012005 CR4: 00000000001606e0
Call Trace:
<IRQ>
ipvlan_addr6_event+0x6c/0xd0 [ipvlan]
notifier_call_chain+0x49/0x90
atomic_notifier_call_chain+0x6a/0x100
ipv6_add_addr+0x5f9/0x720
addrconf_prefix_rcv_add_addr+0x244/0x3c0
addrconf_prefix_rcv+0x2f3/0x790
ndisc_router_discovery+0x633/0xb70
ndisc_rcv+0x155/0x180
icmpv6_rcv+0x4ac/0x5f0
ip6_input_finish+0x138/0x6a0
ip6_input+0x41/0x1f0
ipv6_rcv+0x4db/0x8d0
__netif_receive_skb_core+0x3d5/0xe40
netif_receive_skb_internal+0x89/0x370
napi_gro_receive+0x14f/0x1e0
ixgbe_clean_rx_irq+0x4ce/0x1020 [ixgbe]
ixgbe_poll+0x31a/0x7a0 [ixgbe]
net_rx_action+0x296/0x4f0
__do_softirq+0xcf/0x4f5
irq_exit+0xf5/0x110
do_IRQ+0x62/0x110
common_interrupt+0x91/0x91
</IRQ>
v1 -> v2: drop unneeded in_softirq check in ipvlan_addr6_validator_event()
Fixes: e9997c2938
("ipvlan: fix check for IP addresses in control path")
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
180 lines
4.8 KiB
C
180 lines
4.8 KiB
C
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
#ifndef __IPVLAN_H
|
|
#define __IPVLAN_H
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/rculist.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/if_link.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/inetdevice.h>
|
|
#include <linux/netfilter.h>
|
|
#include <net/ip.h>
|
|
#include <net/ip6_route.h>
|
|
#include <net/netns/generic.h>
|
|
#include <net/rtnetlink.h>
|
|
#include <net/route.h>
|
|
#include <net/addrconf.h>
|
|
#include <net/l3mdev.h>
|
|
|
|
#define IPVLAN_DRV "ipvlan"
|
|
#define IPV_DRV_VER "0.1"
|
|
|
|
#define IPVLAN_HASH_SIZE (1 << BITS_PER_BYTE)
|
|
#define IPVLAN_HASH_MASK (IPVLAN_HASH_SIZE - 1)
|
|
|
|
#define IPVLAN_MAC_FILTER_BITS 8
|
|
#define IPVLAN_MAC_FILTER_SIZE (1 << IPVLAN_MAC_FILTER_BITS)
|
|
#define IPVLAN_MAC_FILTER_MASK (IPVLAN_MAC_FILTER_SIZE - 1)
|
|
|
|
#define IPVLAN_QBACKLOG_LIMIT 1000
|
|
|
|
typedef enum {
|
|
IPVL_IPV6 = 0,
|
|
IPVL_ICMPV6,
|
|
IPVL_IPV4,
|
|
IPVL_ARP,
|
|
} ipvl_hdr_type;
|
|
|
|
struct ipvl_pcpu_stats {
|
|
u64 rx_pkts;
|
|
u64 rx_bytes;
|
|
u64 rx_mcast;
|
|
u64 tx_pkts;
|
|
u64 tx_bytes;
|
|
struct u64_stats_sync syncp;
|
|
u32 rx_errs;
|
|
u32 tx_drps;
|
|
};
|
|
|
|
struct ipvl_port;
|
|
|
|
struct ipvl_dev {
|
|
struct net_device *dev;
|
|
struct list_head pnode;
|
|
struct ipvl_port *port;
|
|
struct net_device *phy_dev;
|
|
struct list_head addrs;
|
|
struct ipvl_pcpu_stats __percpu *pcpu_stats;
|
|
DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
|
|
netdev_features_t sfeatures;
|
|
u32 msg_enable;
|
|
spinlock_t addrs_lock;
|
|
};
|
|
|
|
struct ipvl_addr {
|
|
struct ipvl_dev *master; /* Back pointer to master */
|
|
union {
|
|
struct in6_addr ip6; /* IPv6 address on logical interface */
|
|
struct in_addr ip4; /* IPv4 address on logical interface */
|
|
} ipu;
|
|
#define ip6addr ipu.ip6
|
|
#define ip4addr ipu.ip4
|
|
struct hlist_node hlnode; /* Hash-table linkage */
|
|
struct list_head anode; /* logical-interface linkage */
|
|
ipvl_hdr_type atype;
|
|
struct rcu_head rcu;
|
|
};
|
|
|
|
struct ipvl_port {
|
|
struct net_device *dev;
|
|
possible_net_t pnet;
|
|
struct hlist_head hlhead[IPVLAN_HASH_SIZE];
|
|
struct list_head ipvlans;
|
|
u16 mode;
|
|
u16 flags;
|
|
u16 dev_id_start;
|
|
struct work_struct wq;
|
|
struct sk_buff_head backlog;
|
|
int count;
|
|
struct ida ida;
|
|
};
|
|
|
|
struct ipvl_skb_cb {
|
|
bool tx_pkt;
|
|
};
|
|
#define IPVL_SKB_CB(_skb) ((struct ipvl_skb_cb *)&((_skb)->cb[0]))
|
|
|
|
static inline struct ipvl_port *ipvlan_port_get_rcu(const struct net_device *d)
|
|
{
|
|
return rcu_dereference(d->rx_handler_data);
|
|
}
|
|
|
|
static inline struct ipvl_port *ipvlan_port_get_rcu_bh(const struct net_device *d)
|
|
{
|
|
return rcu_dereference_bh(d->rx_handler_data);
|
|
}
|
|
|
|
static inline struct ipvl_port *ipvlan_port_get_rtnl(const struct net_device *d)
|
|
{
|
|
return rtnl_dereference(d->rx_handler_data);
|
|
}
|
|
|
|
static inline bool ipvlan_is_private(const struct ipvl_port *port)
|
|
{
|
|
return !!(port->flags & IPVLAN_F_PRIVATE);
|
|
}
|
|
|
|
static inline void ipvlan_mark_private(struct ipvl_port *port)
|
|
{
|
|
port->flags |= IPVLAN_F_PRIVATE;
|
|
}
|
|
|
|
static inline void ipvlan_clear_private(struct ipvl_port *port)
|
|
{
|
|
port->flags &= ~IPVLAN_F_PRIVATE;
|
|
}
|
|
|
|
static inline bool ipvlan_is_vepa(const struct ipvl_port *port)
|
|
{
|
|
return !!(port->flags & IPVLAN_F_VEPA);
|
|
}
|
|
|
|
static inline void ipvlan_mark_vepa(struct ipvl_port *port)
|
|
{
|
|
port->flags |= IPVLAN_F_VEPA;
|
|
}
|
|
|
|
static inline void ipvlan_clear_vepa(struct ipvl_port *port)
|
|
{
|
|
port->flags &= ~IPVLAN_F_VEPA;
|
|
}
|
|
|
|
void ipvlan_init_secret(void);
|
|
unsigned int ipvlan_mac_hash(const unsigned char *addr);
|
|
rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb);
|
|
void ipvlan_process_multicast(struct work_struct *work);
|
|
int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev);
|
|
void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr);
|
|
struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
|
|
const 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);
|
|
struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, struct sk_buff *skb,
|
|
u16 proto);
|
|
unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
|
|
const struct nf_hook_state *state);
|
|
void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
|
|
unsigned int len, bool success, bool mcast);
|
|
int ipvlan_link_new(struct net *src_net, struct net_device *dev,
|
|
struct nlattr *tb[], struct nlattr *data[],
|
|
struct netlink_ext_ack *extack);
|
|
void ipvlan_link_delete(struct net_device *dev, struct list_head *head);
|
|
void ipvlan_link_setup(struct net_device *dev);
|
|
int ipvlan_link_register(struct rtnl_link_ops *ops);
|
|
#endif /* __IPVLAN_H */
|