@@ -330,6 +330,15 @@ void arp_send(int type, int ptype, __be32 dest_ip,
}
EXPORT_SYMBOL(arp_send);
+static __be32 __ip_srcaddr(const struct sk_buff *skb)
+{
+ /* Handle cases like MPLS where IP is the inner header */
+ if (skb->protocol != cpu_to_be16(ETH_P_IP) &&
+ skb->inner_protocol == cpu_to_be16(ETH_P_IP))
+ return inner_ip_hdr(skb)->saddr;
+ return ip_hdr(skb)->saddr;
+}
+
static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
{
__be32 saddr = 0;
@@ -350,13 +359,13 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
default:
case 0: /* By default announce any local IP */
if (skb && inet_addr_type_dev_table(dev_net(dev), dev,
- ip_hdr(skb)->saddr) == RTN_LOCAL)
- saddr = ip_hdr(skb)->saddr;
+ __ip_srcaddr(skb)) == RTN_LOCAL)
+ saddr = __ip_srcaddr(skb);
break;
case 1: /* Restrict announcements of saddr in same subnet */
if (!skb)
break;
- saddr = ip_hdr(skb)->saddr;
+ saddr = __ip_srcaddr(skb);
if (inet_addr_type_dev_table(dev_net(dev), dev,
saddr) == RTN_LOCAL) {
/* saddr should be known to target */
@@ -730,6 +730,15 @@ static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
kfree_skb(skb);
}
+static struct in6_addr *__ip6_srcaddr(const struct sk_buff *skb)
+{
+ /* Handle cases like MPLS where IPv6 is the inner header */
+ if (skb->protocol != cpu_to_be16(ETH_P_IPV6) &&
+ skb->inner_protocol == cpu_to_be16(ETH_P_IPV6))
+ return &inner_ipv6_hdr(skb)->saddr;
+ return &ipv6_hdr(skb)->saddr;
+}
+
/* Called with locked neigh: either read or both */
static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
@@ -740,10 +749,10 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
int probes = atomic_read(&neigh->probes);
- if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr,
+ if (skb && ipv6_chk_addr_and_flags(dev_net(dev), __ip6_srcaddr(skb),
dev, false, 1,
IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))
- saddr = &ipv6_hdr(skb)->saddr;
+ saddr = __ip6_srcaddr(skb);
probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);
if (probes < 0) {
if (!(READ_ONCE(neigh->nud_state) & NUD_VALID)) {