ipv4: send arp replies to the correct tunnel

When using ip lwtunnels, the additional data for xmit (basically, the actual
tunnel to use) are carried in ip_tunnel_info either in dst->lwtstate or in
metadata dst. When replying to ARP requests, we need to send the reply to
the same tunnel the request came from. This means we need to construct
proper metadata dst for ARP replies.

We could perform another route lookup to get a dst entry with the correct
lwtstate. However, this won't always ensure that the outgoing tunnel is the
same as the incoming one, and it won't work anyway for IPv4 duplicate
address detection.

The only thing to do is to "reverse" the ip_tunnel_info.

Signed-off-by: Jiri Benc <jbenc@redhat.com>
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Jiri Benc 2015-09-22 18:12:11 +02:00 committed by David S. Miller
parent d5b8d64043
commit 63d008a4e9
3 changed files with 55 additions and 14 deletions

View File

@ -276,6 +276,8 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto);
int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
__be32 src, __be32 dst, u8 proto, __be32 src, __be32 dst, u8 proto,
u8 tos, u8 ttl, __be16 df, bool xnet); u8 tos, u8 ttl, __be16 df, bool xnet);
struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
gfp_t flags);
struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum, struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum,
int gso_type_mask); int gso_type_mask);

View File

@ -113,6 +113,8 @@
#include <net/arp.h> #include <net/arp.h>
#include <net/ax25.h> #include <net/ax25.h>
#include <net/netrom.h> #include <net/netrom.h>
#include <net/dst_metadata.h>
#include <net/ip_tunnels.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
@ -296,7 +298,8 @@ static void arp_send_dst(int type, int ptype, __be32 dest_ip,
struct net_device *dev, __be32 src_ip, struct net_device *dev, __be32 src_ip,
const unsigned char *dest_hw, const unsigned char *dest_hw,
const unsigned char *src_hw, const unsigned char *src_hw,
const unsigned char *target_hw, struct sk_buff *oskb) const unsigned char *target_hw,
struct dst_entry *dst)
{ {
struct sk_buff *skb; struct sk_buff *skb;
@ -309,9 +312,7 @@ static void arp_send_dst(int type, int ptype, __be32 dest_ip,
if (!skb) if (!skb)
return; return;
if (oskb) skb_dst_set(skb, dst);
skb_dst_copy(skb, oskb);
arp_xmit(skb); arp_xmit(skb);
} }
@ -333,6 +334,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
__be32 target = *(__be32 *)neigh->primary_key; __be32 target = *(__be32 *)neigh->primary_key;
int probes = atomic_read(&neigh->probes); int probes = atomic_read(&neigh->probes);
struct in_device *in_dev; struct in_device *in_dev;
struct dst_entry *dst = NULL;
rcu_read_lock(); rcu_read_lock();
in_dev = __in_dev_get_rcu(dev); in_dev = __in_dev_get_rcu(dev);
@ -381,9 +383,10 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
} }
} }
if (skb && !(dev->priv_flags & IFF_XMIT_DST_RELEASE))
dst = dst_clone(skb_dst(skb));
arp_send_dst(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr, arp_send_dst(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
dst_hw, dev->dev_addr, NULL, dst_hw, dev->dev_addr, NULL, dst);
dev->priv_flags & IFF_XMIT_DST_RELEASE ? NULL : skb);
} }
static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip) static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
@ -649,6 +652,7 @@ static int arp_process(struct sock *sk, struct sk_buff *skb)
int addr_type; int addr_type;
struct neighbour *n; struct neighbour *n;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
struct dst_entry *reply_dst = NULL;
bool is_garp = false; bool is_garp = false;
/* arp_rcv below verifies the ARP header and verifies the device /* arp_rcv below verifies the ARP header and verifies the device
@ -749,13 +753,18 @@ static int arp_process(struct sock *sk, struct sk_buff *skb)
* cache. * cache.
*/ */
if (arp->ar_op == htons(ARPOP_REQUEST) && skb_metadata_dst(skb))
reply_dst = (struct dst_entry *)
iptunnel_metadata_reply(skb_metadata_dst(skb),
GFP_ATOMIC);
/* Special case: IPv4 duplicate address detection packet (RFC2131) */ /* Special case: IPv4 duplicate address detection packet (RFC2131) */
if (sip == 0) { if (sip == 0) {
if (arp->ar_op == htons(ARPOP_REQUEST) && if (arp->ar_op == htons(ARPOP_REQUEST) &&
inet_addr_type_dev_table(net, dev, tip) == RTN_LOCAL && inet_addr_type_dev_table(net, dev, tip) == RTN_LOCAL &&
!arp_ignore(in_dev, sip, tip)) !arp_ignore(in_dev, sip, tip))
arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha, arp_send_dst(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip,
dev->dev_addr, sha); sha, dev->dev_addr, sha, reply_dst);
goto out; goto out;
} }
@ -774,9 +783,10 @@ static int arp_process(struct sock *sk, struct sk_buff *skb)
if (!dont_send) { if (!dont_send) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev); n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n) { if (n) {
arp_send(ARPOP_REPLY, ETH_P_ARP, sip, arp_send_dst(ARPOP_REPLY, ETH_P_ARP,
dev, tip, sha, dev->dev_addr, sip, dev, tip, sha,
sha); dev->dev_addr, sha,
reply_dst);
neigh_release(n); neigh_release(n);
} }
} }
@ -794,9 +804,10 @@ static int arp_process(struct sock *sk, struct sk_buff *skb)
if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED || if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED ||
skb->pkt_type == PACKET_HOST || skb->pkt_type == PACKET_HOST ||
NEIGH_VAR(in_dev->arp_parms, PROXY_DELAY) == 0) { NEIGH_VAR(in_dev->arp_parms, PROXY_DELAY) == 0) {
arp_send(ARPOP_REPLY, ETH_P_ARP, sip, arp_send_dst(ARPOP_REPLY, ETH_P_ARP,
dev, tip, sha, dev->dev_addr, sip, dev, tip, sha,
sha); dev->dev_addr, sha,
reply_dst);
} else { } else {
pneigh_enqueue(&arp_tbl, pneigh_enqueue(&arp_tbl,
in_dev->arp_parms, skb); in_dev->arp_parms, skb);

View File

@ -46,6 +46,7 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include <net/dst_metadata.h>
int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
__be32 src, __be32 dst, __u8 proto, __be32 src, __be32 dst, __u8 proto,
@ -119,6 +120,33 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
} }
EXPORT_SYMBOL_GPL(iptunnel_pull_header); EXPORT_SYMBOL_GPL(iptunnel_pull_header);
struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
gfp_t flags)
{
struct metadata_dst *res;
struct ip_tunnel_info *dst, *src;
if (!md || md->u.tun_info.mode & IP_TUNNEL_INFO_TX)
return NULL;
res = metadata_dst_alloc(0, flags);
if (!res)
return NULL;
dst = &res->u.tun_info;
src = &md->u.tun_info;
dst->key.tun_id = src->key.tun_id;
if (src->mode & IP_TUNNEL_INFO_IPV6)
memcpy(&dst->key.u.ipv6.dst, &src->key.u.ipv6.src,
sizeof(struct in6_addr));
else
dst->key.u.ipv4.dst = src->key.u.ipv4.src;
dst->mode = src->mode | IP_TUNNEL_INFO_TX;
return res;
}
EXPORT_SYMBOL_GPL(iptunnel_metadata_reply);
struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
bool csum_help, bool csum_help,
int gso_type_mask) int gso_type_mask)