[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240529231847.16719-2-ekinzie@labn.net>
Date: Wed, 29 May 2024 19:18:44 -0400
From: Eric Kinzie <ekinzie@...n.net>
To: "David S . Miller" <davem@...emloft.net>,
David Ahern <dsahern@...nel.org>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>
Cc: Eric H Kinzie <ekinzie@...n.net>,
netdev@...r.kernel.org
Subject: [RFC net-next 1/2] net: do not interpret MPLS shim as start of IP header
From: Eric H Kinzie <ekinzie@...n.net>
When one or more MPLS labels are present, the struct sk_buff
"network_header" offset points to the outermost label, instead of the
IP header. This is used by mpls_hdr() to find the outer label. ip_hdr()
also uses the network_header offset and unconditionally expects to find
an IP header there.
When forwarding an MPLS-encapsulated packet, the data interpreted
by arp_solicit() as an IP header is offset by at least four bytes.
For example, with one MPLS label, the IP TTL, protocol and header
checksum fields are used as the source IP address. With a TTL of 127,
the source address is within the prefix assigned to the loopback interface
(127.0.0.0/8). This results in ARP requests such as:
10:40:32.131061 ARP, Request who-has 10.0.1.3 tell 127.1.197.239, length 28
10:40:33.144226 ARP, Request who-has 10.0.1.3 tell 127.1.197.239, length 28
10:40:34.168224 ARP, Request who-has 10.0.1.3 tell 127.1.197.56, length 28
Examine the inner network header for the source address if the network
header is not IP, but the inner header is. Also fix a similar situation
in IPv6 neighbor discovery.
Signed-off-by: Eric H Kinzie <ekinzie@...n.net>
---
net/ipv4/arp.c | 15 ++++++++++++---
net/ipv6/ndisc.c | 13 +++++++++++--
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 11c1519b3699..653394362c80 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -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 */
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 254b192c5705..16a93798cb00 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -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)) {
--
2.43.2
Powered by blists - more mailing lists