diff mbox series

[RFC,net-next,RESEND,1/2] net: do not interpret MPLS shim as start of IP header

Message ID 20240613113529.238-2-ekinzie@labn.net (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series MPLS point-to-multipoint | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 848 this patch: 848
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 5 of 5 maintainers
netdev/build_clang success Errors and warnings before: 849 this patch: 849
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 851 this patch: 851
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 58 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Eric Kinzie June 13, 2024, 11:35 a.m. UTC
From: Eric H Kinzie <ekinzie@labn.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@labn.net>
---
 net/ipv4/arp.c   | 15 ++++++++++++---
 net/ipv6/ndisc.c | 13 +++++++++++--
 2 files changed, 23 insertions(+), 5 deletions(-)
diff mbox series

Patch

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)) {