mirror of
https://github.com/openvswitch/ovs
synced 2025-10-29 15:28:56 +00:00
The variable tun_dst in struct ovs_gso_cb isn't necessarily all-zeros which came from the Netlink layer. When delete a netdev port and immediately add a vxlan port, they maybe use the same port_no. So the variable tun_dst of struct ovs_gso_cb hasn't be set, when the skb sent to the vxlan port. And the panic will be triggered. BUG: unable to handle kernel NULL pointer dereference at 0000000000000052 IP: [<ffffffffa07954f4>] rpl_vxlan_xmit+0x34/0x60 [openvswitch] PGD 1f9f374067 PUD 1f9f375067 PMD 0 Oops: 0000 [#1] SMP RIP: 0010:[<ffffffffa07954f4>] [<ffffffffa07954f4>] rpl_vxlan_xmit+0x34/0x60 [openvswitch] RSP: 0018:ffff881fff483898 EFLAGS: 00010202 RAX: 0000000000000040 RBX: ffff881ff2d59f00 RCX: ffff881f742016b0 RDX: 0000000000000001 RSI: ffff881f9f5f0000 RDI: ffff881ff2d59f00 RBP: ffff881fff483898 R08: 000000000000002e R09: 0000000000000000 R10: 0000000000000000 R11: ffff881fff483a50 R12: ffff881f74201680 R13: 000000000000ffbe R14: 0000000000000000 R15: ffff881ff2d59f00 FS: 00007f8b6f7fe700(0000) GS:ffff881fff480000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000052 CR3: 0000001f9f373000 CR4: 00000000000027e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Call Trace: <IRQ> [<ffffffffa0786480>] ovs_vport_send+0xa0/0x180 [openvswitch] [<ffffffffa077414e>] do_output+0x4e/0xf0 [openvswitch] [<ffffffffa07758ae>] do_execute_actions+0xa6e/0xa90 [openvswitch] [<ffffffff815b654f>] ? netlink_unicast+0x16f/0x1b0 [<ffffffff815732bb>] ? skb_zerocopy+0x1fb/0x380 [<ffffffffa07847ca>] ? flow_lookup.isra.8+0x4a/0xc0 [openvswitch] [<ffffffffa0775b2d>] ovs_execute_actions+0x4d/0x140 [openvswitch] [<ffffffffa077c604>] ovs_dp_process_packet+0x94/0x140 [openvswitch] [<ffffffffa07762c4>] ? ovs_ct_update_key+0xc4/0x150 [openvswitch] [<ffffffffa078637b>] ovs_vport_receive+0x7b/0xe0 [openvswitch] [<ffffffffa077c604>] ? ovs_dp_process_packet+0x94/0x140 [openvswitch] [<ffffffff816062d6>] ? __fib_validate_source.isra.13+0x2b6/0x400 [<ffffffff8158da15>] ? dst_init+0xe5/0xf0 [<ffffffffa021a2af>] ? generic_packet+0x1f/0x30 [nf_conntrack] [<ffffffffa02160d0>] ? nf_conntrack_in+0x350/0x5f0 [nf_conntrack] [<ffffffffa0787047>] netdev_port_receive+0xa7/0x100 [openvswitch] [<ffffffffa07870be>] netdev_frame_hook+0x1e/0x30 [openvswitch] [<ffffffff81581a52>] __netif_receive_skb_core+0x1e2/0x800 [<ffffffff81582088>] __netif_receive_skb+0x18/0x60 [<ffffffff81582110>] netif_receive_skb_internal+0x40/0xc0 [<ffffffff81583228>] napi_gro_receive+0xd8/0x130 [<ffffffffa04ef634>] ixgbe_clean_rx_irq+0x7c4/0xa60 [ixgbe] [<ffffffffa04f0930>] ixgbe_poll+0x2e0/0x6c0 [ixgbe] [<ffffffff815828b0>] net_rx_action+0x170/0x380 [<ffffffff81090b0f>] __do_softirq+0xef/0x280 [<ffffffff816ac15c>] call_softirq+0x1c/0x30 [<ffffffff8102e47d>] do_softirq+0x5d/0xb0 [<ffffffff81090ebd>] irq_exit+0x12d/0x140 [<ffffffff816accf8>] do_IRQ+0x58/0xf0 [<ffffffff816a1ced>] common_interrupt+0x6d/0x6d <EOI> Signed-off-by: Yunjian Wang <wangyunjian@huawei.com> Signed-off-by: Ben Pfaff <blp@ovn.org> Tested-by: Greg Rose <gvrose8192@gmail.com> Reviewed-by: Greg Rose <gvrose8192@gmail.com>
215 lines
5.3 KiB
C
215 lines
5.3 KiB
C
#ifndef __LINUX_GSO_WRAPPER_H
|
|
#define __LINUX_GSO_WRAPPER_H
|
|
|
|
#include <linux/version.h>
|
|
#include "datapath.h"
|
|
|
|
typedef void (*gso_fix_segment_t)(struct sk_buff *);
|
|
|
|
struct ovs_gso_cb {
|
|
struct ovs_skb_cb dp_cb;
|
|
#ifndef USE_UPSTREAM_TUNNEL
|
|
struct metadata_dst *tun_dst;
|
|
#endif
|
|
#ifndef USE_UPSTREAM_TUNNEL_GSO
|
|
gso_fix_segment_t fix_segment;
|
|
bool ipv6;
|
|
#endif
|
|
#ifndef HAVE_INNER_PROTOCOL
|
|
__be16 inner_protocol;
|
|
#endif
|
|
#ifndef USE_UPSTREAM_TUNNEL
|
|
/* Keep original tunnel info during userspace action execution. */
|
|
struct metadata_dst *fill_md_dst;
|
|
#endif
|
|
};
|
|
#define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb)
|
|
|
|
|
|
#ifndef USE_UPSTREAM_TUNNEL_GSO
|
|
#include <linux/netdevice.h>
|
|
#include <linux/skbuff.h>
|
|
#include <net/protocol.h>
|
|
|
|
static inline void skb_clear_ovs_gso_cb(struct sk_buff *skb)
|
|
{
|
|
OVS_GSO_CB(skb)->fix_segment = NULL;
|
|
#ifndef USE_UPSTREAM_TUNNEL
|
|
OVS_GSO_CB(skb)->tun_dst = NULL;
|
|
#endif
|
|
}
|
|
#else
|
|
static inline void skb_clear_ovs_gso_cb(struct sk_buff *skb)
|
|
{
|
|
#ifndef USE_UPSTREAM_TUNNEL
|
|
OVS_GSO_CB(skb)->tun_dst = NULL;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_INNER_PROTOCOL
|
|
static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb)
|
|
{
|
|
OVS_GSO_CB(skb)->inner_protocol = htons(0);
|
|
}
|
|
|
|
static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
|
|
__be16 ethertype)
|
|
{
|
|
OVS_GSO_CB(skb)->inner_protocol = ethertype;
|
|
}
|
|
|
|
static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
|
|
{
|
|
return OVS_GSO_CB(skb)->inner_protocol;
|
|
}
|
|
|
|
#else
|
|
|
|
static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb)
|
|
{
|
|
/* Nothing to do. The inner_protocol is either zero or
|
|
* has been set to a value by another user.
|
|
* Either way it may be considered initialised.
|
|
*/
|
|
}
|
|
|
|
static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
|
|
{
|
|
return skb->inner_protocol;
|
|
}
|
|
|
|
#ifdef ENCAP_TYPE_ETHER
|
|
#define ovs_skb_set_inner_protocol skb_set_inner_protocol
|
|
#else
|
|
static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
|
|
__be16 ethertype)
|
|
{
|
|
skb->inner_protocol = ethertype;
|
|
}
|
|
#endif /* ENCAP_TYPE_ETHER */
|
|
#endif /* HAVE_INNER_PROTOCOL */
|
|
|
|
#define skb_inner_mac_offset rpl_skb_inner_mac_offset
|
|
static inline int skb_inner_mac_offset(const struct sk_buff *skb)
|
|
{
|
|
return skb_inner_mac_header(skb) - skb->data;
|
|
}
|
|
|
|
#ifndef USE_UPSTREAM_TUNNEL_GSO
|
|
#define ip_local_out rpl_ip_local_out
|
|
int rpl_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
|
|
|
|
#define ip6_local_out rpl_ip6_local_out
|
|
int rpl_ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
|
|
#else
|
|
|
|
static inline int rpl_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
|
|
#ifdef HAVE_IP_LOCAL_OUT_TAKES_NET
|
|
/* net and sk parameters are added at same time. */
|
|
return ip_local_out(net, sk, skb);
|
|
#else
|
|
return ip_local_out(skb);
|
|
#endif
|
|
}
|
|
#define ip_local_out rpl_ip_local_out
|
|
|
|
static inline int rpl_ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
memset(IP6CB(skb), 0, sizeof (*IP6CB(skb)));
|
|
#ifdef HAVE_IP_LOCAL_OUT_TAKES_NET
|
|
return ip6_local_out(net, sk, skb);
|
|
#else
|
|
return ip6_local_out(skb);
|
|
#endif
|
|
}
|
|
#define ip6_local_out rpl_ip6_local_out
|
|
|
|
#endif /* USE_UPSTREAM_TUNNEL_GSO */
|
|
|
|
#ifndef USE_UPSTREAM_TUNNEL
|
|
/* We need two separate functions to manage different dst in this case.
|
|
* First is dst_entry and second is tunnel-dst.
|
|
* So define ovs_* separate functions for tun_dst.
|
|
*/
|
|
static inline void ovs_skb_dst_set(struct sk_buff *skb, void *dst)
|
|
{
|
|
OVS_GSO_CB(skb)->tun_dst = (void *)dst;
|
|
}
|
|
|
|
static inline struct ip_tunnel_info *ovs_skb_tunnel_info(struct sk_buff *skb)
|
|
{
|
|
if (likely(OVS_GSO_CB(skb)->tun_dst))
|
|
return &OVS_GSO_CB(skb)->tun_dst->u.tun_info;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static inline void ovs_skb_dst_drop(struct sk_buff *skb)
|
|
{
|
|
OVS_GSO_CB(skb)->tun_dst = NULL;
|
|
}
|
|
|
|
static inline void ovs_dst_hold(void *dst)
|
|
{
|
|
}
|
|
|
|
static inline void ovs_dst_release(struct dst_entry *dst)
|
|
{
|
|
struct metadata_dst *tun_dst = (struct metadata_dst *) dst;
|
|
|
|
dst_cache_destroy(&tun_dst->u.tun_info.dst_cache);
|
|
kfree(dst);
|
|
}
|
|
|
|
#else
|
|
#define ovs_skb_dst_set skb_dst_set
|
|
#define ovs_skb_dst_drop skb_dst_drop
|
|
#define ovs_dst_hold dst_hold
|
|
#define ovs_dst_release dst_release
|
|
#endif
|
|
|
|
#ifndef USE_UPSTREAM_TUNNEL
|
|
#define SKB_INIT_FILL_METADATA_DST(skb) OVS_GSO_CB(skb)->fill_md_dst = NULL;
|
|
|
|
#define SKB_RESTORE_FILL_METADATA_DST(skb) do { \
|
|
if (OVS_GSO_CB(skb)->fill_md_dst) { \
|
|
kfree(OVS_GSO_CB(skb)->tun_dst); \
|
|
OVS_GSO_CB(skb)->tun_dst = OVS_GSO_CB(skb)->fill_md_dst; \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
#define SKB_SETUP_FILL_METADATA_DST(skb) ({ \
|
|
struct metadata_dst *new_md_dst; \
|
|
struct metadata_dst *md_dst; \
|
|
int md_size; \
|
|
int ret = 1; \
|
|
\
|
|
SKB_RESTORE_FILL_METADATA_DST(skb); \
|
|
new_md_dst = kmalloc(sizeof(struct metadata_dst) + 256, GFP_ATOMIC); \
|
|
if (new_md_dst) { \
|
|
md_dst = OVS_GSO_CB(skb)->tun_dst; \
|
|
md_size = new_md_dst->u.tun_info.options_len; \
|
|
memcpy(&new_md_dst->u.tun_info, &md_dst->u.tun_info, \
|
|
sizeof(struct ip_tunnel_info) + md_size); \
|
|
\
|
|
OVS_GSO_CB(skb)->fill_md_dst = md_dst; \
|
|
OVS_GSO_CB(skb)->tun_dst = new_md_dst; \
|
|
ret = 1; \
|
|
} else { \
|
|
ret = 0; \
|
|
} \
|
|
ret; \
|
|
})
|
|
|
|
#else
|
|
#define SKB_INIT_FILL_METADATA_DST(skb) do {} while(0)
|
|
#define SKB_SETUP_FILL_METADATA_DST(skb) (true)
|
|
#define SKB_RESTORE_FILL_METADATA_DST(skb) do {} while(0)
|
|
#endif
|
|
|
|
#endif
|