[PATCH 2/2] Implement IPv6 support for TCP MD5 Signature Option (RFC 2385)

BERARD David contact at davidberard.fr
Fri Sep 3 11:55:21 PDT 2010


Import of FreeBSD commit r183001
Tested with a Cisco 2611XM running IOS 12.3(24), and Quagga 0.99.17
---
 sys/netinet/tcp_output.c   |   48 +++++++++++------------
 sys/netinet/tcp_subr.c     |   88 ++++++++++++++++++++++++++++++++++++--------
 sys/netinet/tcp_syncache.c |    2 +-
 sys/netinet/tcp_var.h      |    2 +-
 4 files changed, 97 insertions(+), 43 deletions(-)

diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index dd8292b..8f0fe27 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -602,27 +602,26 @@ send:
 		tcp_sack_fill_report(tp, opt, &optlen);
 
 #ifdef TCP_SIGNATURE
-	if (!isipv6)
-		if (tp->t_flags & TF_SIGNATURE) {
-			int i;
-			u_char *bp;
-			/*
-			 * Initialize TCP-MD5 option (RFC2385)
-			 */
-			bp = (u_char *)opt + optlen;
-			*bp++ = TCPOLEN_SIGNATURE;
-			sigoff = optlen + 2;
-			for (i = 0; i < TCP_SIGLEN; i++)
-				*bp++ = 0;
-			optlen += TCPOLEN_SIGNATURE;
-			/*
-			 * Terminate options list and maintain 32-bit alignment.
-			 */
-			*bp++ = TCPOPT_NOP;
-			*bp++ = TCPOPT_EOL;
-			optlen += 2;
-		}
+	if (tp->t_flags & TF_SIGNATURE) {
+		int i;
+		u_char *bp;
+		/*
+		 * Initialize TCP-MD5 option (RFC2385)
+		 */
+		bp = (u_char *)opt + optlen;
+		*bp++ = TCPOPT_SIGNATURE;
+		*bp++ = TCPOLEN_SIGNATURE;
+		sigoff = optlen + 2;
+		for (i = 0; i < TCP_SIGLEN; i++)
+			*bp++ = 0;
+		optlen += TCPOLEN_SIGNATURE;
+		/*
+		 * Terminate options list and maintain 32-bit alignment.
+		 */
+		*bp++ = TCPOPT_NOP;
+		*bp++ = TCPOPT_EOL;
+		optlen += 2;
+	}
 #endif /* TCP_SIGNATURE */
 	KASSERT(optlen <= TCP_MAXOLEN, ("too many TCP options"));
 	hdrlen += optlen;
@@ -844,10 +843,9 @@ send:
 	}
 
 #ifdef TCP_SIGNATURE
-	if (!isipv6)
-		if (tp->t_flags & TF_SIGNATURE)
-			tcpsignature_compute(m, sizeof(struct ip), len, optlen,
-					(u_char *)(th + 1) + sigoff, IPSEC_DIR_OUTBOUND);
+	if (tp->t_flags & TF_SIGNATURE)
+		tcpsignature_compute(m, len, optlen,
+				(u_char *)(th + 1) + sigoff, IPSEC_DIR_OUTBOUND);
 #endif /* TCP_SIGNATURE */
 
 	/*
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index fafda1e..f64b97c 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -1984,15 +1984,12 @@ tcp_xmit_bandwidth_limit(struct tcpcb *tp, tcp_seq ack_seq)
 
 #ifdef TCP_SIGNATURE
 /*
- * Compute TCP-MD5 hash of a TCPv4 segment. (RFC2385)
+ * Compute TCP-MD5 hash of a TCP segment. (RFC2385)
  *
  * We do this over ip, tcphdr, segment data, and the key in the SADB.
  * When called from tcp_input(), we can be sure that th_sum has been
  * zeroed out and verified already.
  *
- * This function is for IPv4 use only. Calling this function with an
- * IPv6 packet in the mbuf chain will yield undefined results.
- *
  * Return 0 if successful, otherwise return -1.
  *
  * XXX The key is retrieved from the system's PF_KEY SADB, by keying a
@@ -2004,7 +2001,6 @@ tcp_xmit_bandwidth_limit(struct tcpcb *tp, tcp_seq ack_seq)
 int
 tcpsignature_compute(
 	struct mbuf *m,		/* mbuf chain */
-	int off0,		/* offset to TCP header */
 	int len,		/* length of TCP data */
 	int optlen,		/* length of TCP options */
 	u_char *buf,		/* storage for MD5 digest */
@@ -2017,6 +2013,12 @@ tcpsignature_compute(
 	struct ipovly *ipovly;
 	struct secasvar *sav;
 	struct tcphdr *th;
+#ifdef INET6
+	struct ip6_hdr *ip6;
+	struct in6_addr in6;
+	uint32_t plen;
+	uint16_t nhdr;
+#endif /* INET6 */
 	u_short savecsum;
 
 	KASSERT(m != NULL, ("passed NULL mbuf. Game over."));
@@ -2025,21 +2027,36 @@ tcpsignature_compute(
 	 * Extract the destination from the IP header in the mbuf.
 	 */
 	ip = mtod(m, struct ip *);
+#ifdef INET6
+	ip6 = NULL;     /* Make the compiler happy. */
+#endif /* INET6 */
 	/*
 	 * Look up an SADB entry which matches the address found in
 	 * the segment.
 	 */
-	sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
-			IPPROTO_TCP, htonl(TCP_SIG_SPI));
+	switch (IP_VHL_V(ip->ip_vhl)) {
+	case IPVERSION:
+		sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
+				IPPROTO_TCP, htonl(TCP_SIG_SPI));
+		break;
+#ifdef INET6
+	case (IPV6_VERSION >> 4):
+		ip6 = mtod(m, struct ip6_hdr *);
+		sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
+				IPPROTO_TCP, htonl(TCP_SIG_SPI));
+		break;
+#endif /* INET6 */
+	default:
+		return (EINVAL);
+		/* NOTREACHED */
+		break;
+	}
 	if (sav == NULL) {
 		kprintf("%s: SADB lookup failed\n", __func__);
 		return (EINVAL);
 	}
 	MD5Init(&ctx);
 
-	ipovly = (struct ipovly *)ip;
-	th = (struct tcphdr *)((u_char *)ip + off0);
-	doff = off0 + sizeof(struct tcphdr) + optlen;
 	/*
 	 * Step 1: Update MD5 hash with IP pseudo-header.
 	 *
@@ -2049,12 +2066,51 @@ tcpsignature_compute(
 	 * XXX One cannot depend on ipovly->ih_len here. When called from
 	 * tcp_output(), the underlying ip_len member has not yet been set.
 	 */
-	ippseudo.ippseudo_src = ipovly->ih_src;
-	ippseudo.ippseudo_dst = ipovly->ih_dst;
-	ippseudo.ippseudo_pad = 0;
-	ippseudo.ippseudo_p = IPPROTO_TCP;
-	ippseudo.ippseudo_len = htons(len + sizeof(struct tcphdr) + optlen);
-	MD5Update(&ctx, (char *)&ippseudo, sizeof(struct ippseudo));
+	switch (IP_VHL_V(ip->ip_vhl)) {
+	case IPVERSION:
+		ipovly = (struct ipovly *)ip;
+		ippseudo.ippseudo_src = ipovly->ih_src;
+		ippseudo.ippseudo_dst = ipovly->ih_dst;
+		ippseudo.ippseudo_pad = 0;
+		ippseudo.ippseudo_p = IPPROTO_TCP;
+		ippseudo.ippseudo_len = htons(len + sizeof(struct tcphdr) + optlen);
+		MD5Update(&ctx, (char *)&ippseudo, sizeof(struct ippseudo));
+		th = (struct tcphdr *)((u_char *)ip + sizeof(struct ip));
+		doff = sizeof(struct ip) + sizeof(struct tcphdr) + optlen;
+		break;
+#ifdef INET6
+	/*
+	 * RFC 2385, 2.0  Proposal
+	 * For IPv6, the pseudo-header is as described in RFC 2460, namely the
+	 * 128-bit source IPv6 address, 128-bit destination IPv6 address, zero-
+	 * extended next header value (to form 32 bits), and 32-bit segment
+	 * length.
+	 * Note: Upper-Layer Packet Length comes before Next Header.
+	 */
+	case (IPV6_VERSION >> 4):
+		in6 = ip6->ip6_src;
+		in6_clearscope(&in6);
+		MD5Update(&ctx, (char *)&in6, sizeof(struct in6_addr));
+		in6 = ip6->ip6_dst;
+		in6_clearscope(&in6);
+		MD5Update(&ctx, (char *)&in6, sizeof(struct in6_addr));
+		plen = htonl(len + sizeof(struct tcphdr) + optlen);
+		MD5Update(&ctx, (char *)&plen, sizeof(uint32_t));
+		nhdr = 0;
+		MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
+		MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
+		MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
+		nhdr = IPPROTO_TCP;
+		MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
+		th = (struct tcphdr *)((u_char *)ip6 + sizeof(struct ip6_hdr));
+		doff = sizeof(struct ip6_hdr) + sizeof(struct tcphdr) + optlen;
+		break;
+#endif /* INET6 */
+	default:
+		return (EINVAL);
+		/* NOTREACHED */
+		break;
+	}
 	/*
 	 * Step 2: Update MD5 hash with TCP header, excluding options.
 	 * The TCP checksum must be set to zero.
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
index ce53940..bc5fc02 100644
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -1271,7 +1271,7 @@ syncache_respond(struct syncache *sc, struct mbuf *m)
 		*bp++ = TCPOLEN_SIGNATURE;
 		for (i = 0; i < TCP_SIGLEN; i++)
 			*bp++ = 0;
-		tcpsignature_compute(m, sizeof(struct ip), 0, optlen,
+		tcpsignature_compute(m, 0, optlen,
 				optp + 2, IPSEC_DIR_OUTBOUND);
 		*bp++ = TCPOPT_NOP;
 		*bp++ = TCPOPT_EOL;
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 3549565..5b10d87 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -647,7 +647,7 @@ void	 syncache_destroy(struct tcpcb *tp);
 
 #ifdef TCP_SIGNATURE
 int tcpsignature_apply(void *fstate, void *data, unsigned int len);
-int tcpsignature_compute(struct mbuf *m, int off0, int len, int tcpoptlen,
+int tcpsignature_compute(struct mbuf *m, int len, int tcpoptlen,
 		u_char *buf, u_int direction);
 #endif /* TCP_SIGNATURE */
 
-- 
1.6.6.2


--XsQoSWH+UP9D9v3l--





More information about the Submit mailing list