Subject: checksum handling
To: None <tech-net@netbsd.org>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: tech-net
Date: 12/18/2004 21:21:14
--NextPart-20041218211932-0831101
Content-Type: Text/Plain; charset=us-ascii

hi,

if no one objects,
i'll factor out input checksum handling code as the attached diffs
so that packet filters can use it.

YAMAMOTO Takashi

--NextPart-20041218211932-0831101
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="csum.diff"

Index: netinet/udp_var.h
===================================================================
--- netinet/udp_var.h	(revision 990)
+++ netinet/udp_var.h	(working copy)
@@ -100,6 +100,8 @@ int	 udp_output(struct mbuf *, ...);
 int	 udp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 int	 udp_usrreq(struct socket *,
 	    int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *);
+
+int	 udp4_input_checksum(struct mbuf *, const struct udphdr *, int, int);
 #endif
 
 #endif /* _NETINET_UDP_VAR_H_ */
Index: netinet/tcp_input.c
===================================================================
--- netinet/tcp_input.c	(revision 991)
+++ netinet/tcp_input.c	(working copy)
@@ -779,6 +779,87 @@ tcp6_log_refused(ip6, th)
 #endif
 
 /*
+ * Checksum extended TCP header and data.
+ */
+int
+tcp_input_checksum(int af, struct mbuf *m, const struct tcphdr *th, int toff,
+    int off, int tlen)
+{
+
+	/*
+	 * XXX it's better to record and check if this mbuf is
+	 * already checked.
+	 */
+
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		switch (m->m_pkthdr.csum_flags &
+			((m->m_pkthdr.rcvif->if_csum_flags_rx & M_CSUM_TCPv4) |
+			 M_CSUM_TCP_UDP_BAD | M_CSUM_DATA)) {
+		case M_CSUM_TCPv4|M_CSUM_TCP_UDP_BAD:
+			TCP_CSUM_COUNTER_INCR(&tcp_hwcsum_bad);
+			goto badcsum;
+
+		case M_CSUM_TCPv4|M_CSUM_DATA: {
+			u_int32_t hw_csum = m->m_pkthdr.csum_data;
+
+			TCP_CSUM_COUNTER_INCR(&tcp_hwcsum_data);
+			if (m->m_pkthdr.csum_flags & M_CSUM_NO_PSEUDOHDR) {
+				const struct ip *ip =
+				    mtod(m, const struct ip *);
+
+				hw_csum = in_cksum_phdr(ip->ip_src.s_addr,
+				    ip->ip_dst.s_addr,
+				    htons(hw_csum + tlen + off + IPPROTO_TCP));
+			}
+			if ((hw_csum ^ 0xffff) != 0)
+				goto badcsum;
+			break;
+		}
+
+		case M_CSUM_TCPv4:
+			/* Checksum was okay. */
+			TCP_CSUM_COUNTER_INCR(&tcp_hwcsum_ok);
+			break;
+
+		default:
+			/*
+			 * Must compute it ourselves.  Maybe skip checksum
+			 * on loopback interfaces.
+			 */
+			if (__predict_true(!(m->m_pkthdr.rcvif->if_flags &
+					     IFF_LOOPBACK) ||
+					   tcp_do_loopback_cksum)) {
+				TCP_CSUM_COUNTER_INCR(&tcp_swcsum);
+				if (in4_cksum(m, IPPROTO_TCP, toff,
+					      tlen + off) != 0)
+					goto badcsum;
+			}
+			break;
+		}
+		break;
+#endif /* INET4 */
+
+#ifdef INET6
+	case AF_INET6:
+		if (__predict_true((m->m_flags & M_LOOP) == 0 ||
+		    tcp_do_loopback_cksum)) {
+			if (in6_cksum(m, IPPROTO_TCP, toff, tlen + off) != 0)
+				goto badcsum;
+		}
+		break;
+#endif /* INET6 */
+	}
+
+	return 0;
+
+badcsum:
+	tcpstat.tcps_rcvbadsum++;
+	return -1;
+}
+
+/*
  * TCP input routine, follows pages 65-76 of the
  * protocol specification dated September, 1981 very closely.
  */
@@ -1106,63 +1187,9 @@ findpcb:
 	/*
 	 * Checksum extended TCP header and data.
 	 */
-	switch (af) {
-#ifdef INET
-	case AF_INET:
-		switch (m->m_pkthdr.csum_flags &
-			((m->m_pkthdr.rcvif->if_csum_flags_rx & M_CSUM_TCPv4) |
-			 M_CSUM_TCP_UDP_BAD | M_CSUM_DATA)) {
-		case M_CSUM_TCPv4|M_CSUM_TCP_UDP_BAD:
-			TCP_CSUM_COUNTER_INCR(&tcp_hwcsum_bad);
-			goto badcsum;
+	if (tcp_input_checksum(af, m, th, toff, off, tlen))
+		goto badcsum;
 
-		case M_CSUM_TCPv4|M_CSUM_DATA: {
-			u_int32_t hw_csum = m->m_pkthdr.csum_data;
-			TCP_CSUM_COUNTER_INCR(&tcp_hwcsum_data);
-			if (m->m_pkthdr.csum_flags & M_CSUM_NO_PSEUDOHDR) {
-				hw_csum = in_cksum_phdr(ip->ip_src.s_addr,
-				    ip->ip_dst.s_addr,
-				    htons(hw_csum + tlen + off + IPPROTO_TCP));
-			}
-			if ((hw_csum ^ 0xffff) != 0)
-				goto badcsum;
-			break;
-		}
-
-		case M_CSUM_TCPv4:
-			/* Checksum was okay. */
-			TCP_CSUM_COUNTER_INCR(&tcp_hwcsum_ok);
-			break;
-
-		default:
-			/*
-			 * Must compute it ourselves.  Maybe skip checksum
-			 * on loopback interfaces.
-			 */
-			if (__predict_true(!(m->m_pkthdr.rcvif->if_flags &
-					     IFF_LOOPBACK) ||
-					   tcp_do_loopback_cksum)) {
-				TCP_CSUM_COUNTER_INCR(&tcp_swcsum);
-				if (in4_cksum(m, IPPROTO_TCP, toff,
-					      tlen + off) != 0)
-					goto badcsum;
-			}
-			break;
-		}
-		break;
-#endif /* INET4 */
-
-#ifdef INET6
-	case AF_INET6:
-		if (__predict_true((m->m_flags & M_LOOP) == 0 ||
-		    tcp_do_loopback_cksum)) {
-			if (in6_cksum(m, IPPROTO_TCP, toff, tlen + off) != 0)
-				goto badcsum;
-		}
-		break;
-#endif /* INET6 */
-	}
-
 	TCP_FIELDS_TO_HOST(th);
 
 	/* Unscale the window into a 32-bit value. */
@@ -2549,7 +2576,6 @@ dropwithreset:
 	return;
 
 badcsum:
-	tcpstat.tcps_rcvbadsum++;
 drop:
 	/*
 	 * Drop space held by incoming segment and return.
Index: netinet/tcp_var.h
===================================================================
--- netinet/tcp_var.h	(revision 990)
+++ netinet/tcp_var.h	(working copy)
@@ -822,7 +822,10 @@ int	 syn_cache_respond(struct syn_cache 
 void	 syn_cache_timer(void *);
 void	 syn_cache_cleanup(struct tcpcb *);
 
-int	tcp_newreno(struct tcpcb *, struct tcphdr *);
+int	 tcp_newreno(struct tcpcb *, struct tcphdr *);
+
+int	 tcp_input_checksum(int, struct mbuf *, const struct tcphdr *, int, int,
+    int);
 #endif
 
 #endif /* _NETINET_TCP_VAR_H_ */
Index: netinet/udp_usrreq.c
===================================================================
--- netinet/udp_usrreq.c	(revision 990)
+++ netinet/udp_usrreq.c	(working copy)
@@ -210,6 +210,75 @@ udp_init(void)
 }
 
 #ifdef INET
+
+/*
+ * Checksum extended UDP header and data.
+ */
+
+int
+udp4_input_checksum(struct mbuf *m, const struct udphdr *uh,
+    int iphlen, int len)
+{
+
+	/*
+	 * XXX it's better to record and check if this mbuf is
+	 * already checked.
+	 */
+
+	if (uh->uh_sum == 0)
+		return 0;
+
+	switch (m->m_pkthdr.csum_flags &
+	    ((m->m_pkthdr.rcvif->if_csum_flags_rx & M_CSUM_UDPv4) |
+	    M_CSUM_TCP_UDP_BAD | M_CSUM_DATA)) {
+	case M_CSUM_UDPv4|M_CSUM_TCP_UDP_BAD:
+		UDP_CSUM_COUNTER_INCR(&udp_hwcsum_bad);
+		goto badcsum;
+
+	case M_CSUM_UDPv4|M_CSUM_DATA: {
+		u_int32_t hw_csum = m->m_pkthdr.csum_data;
+
+		UDP_CSUM_COUNTER_INCR(&udp_hwcsum_data);
+		if (m->m_pkthdr.csum_flags & M_CSUM_NO_PSEUDOHDR) {
+			const struct ip *ip =
+			    mtod(m, const struct ip *);
+
+			hw_csum = in_cksum_phdr(ip->ip_src.s_addr,
+			    ip->ip_dst.s_addr,
+			    htons(hw_csum + len + IPPROTO_UDP));
+		}
+		if ((hw_csum ^ 0xffff) != 0)
+			goto badcsum;
+		break;
+	}
+
+	case M_CSUM_UDPv4:
+		/* Checksum was okay. */
+		UDP_CSUM_COUNTER_INCR(&udp_hwcsum_ok);
+		break;
+
+	default:
+		/*
+		 * Need to compute it ourselves.  Maybe skip checksum
+		 * on loopback interfaces.
+		 */
+		if (__predict_true(!(m->m_pkthdr.rcvif->if_flags &
+				     IFF_LOOPBACK) ||
+				   udp_do_loopback_cksum)) {
+			UDP_CSUM_COUNTER_INCR(&udp_swcsum);
+			if (in4_cksum(m, IPPROTO_UDP, iphlen, len) != 0)
+				goto badcsum;
+		}
+		break;
+	}
+
+	return 0;
+
+badcsum:
+	udpstat.udps_badsum++;
+	return -1;
+}
+
 void
 udp_input(struct mbuf *m, ...)
 {
@@ -262,47 +331,9 @@ udp_input(struct mbuf *m, ...)
 	/*
 	 * Checksum extended UDP header and data.
 	 */
-	if (uh->uh_sum) {
-		switch (m->m_pkthdr.csum_flags &
-		    ((m->m_pkthdr.rcvif->if_csum_flags_rx & M_CSUM_UDPv4) |
-		    M_CSUM_TCP_UDP_BAD | M_CSUM_DATA)) {
-		case M_CSUM_UDPv4|M_CSUM_TCP_UDP_BAD:
-			UDP_CSUM_COUNTER_INCR(&udp_hwcsum_bad);
-			goto badcsum;
+	if (udp4_input_checksum(m, uh, iphlen, len))
+		goto badcsum;
 
-		case M_CSUM_UDPv4|M_CSUM_DATA: {
-			u_int32_t hw_csum = m->m_pkthdr.csum_data;
-			UDP_CSUM_COUNTER_INCR(&udp_hwcsum_data);
-			if (m->m_pkthdr.csum_flags & M_CSUM_NO_PSEUDOHDR)
-				hw_csum = in_cksum_phdr(ip->ip_src.s_addr,
-				    ip->ip_dst.s_addr,
-				    htons(hw_csum + len + IPPROTO_UDP));
-			if ((hw_csum ^ 0xffff) != 0)
-				goto badcsum;
-			break;
-		}
-
-		case M_CSUM_UDPv4:
-			/* Checksum was okay. */
-			UDP_CSUM_COUNTER_INCR(&udp_hwcsum_ok);
-			break;
-
-		default:
-			/*
-			 * Need to compute it ourselves.  Maybe skip checksum
-			 * on loopback interfaces.
-			 */
-			if (__predict_true(!(m->m_pkthdr.rcvif->if_flags &
-					     IFF_LOOPBACK) ||
-					   udp_do_loopback_cksum)) {
-				UDP_CSUM_COUNTER_INCR(&udp_swcsum);
-				if (in4_cksum(m, IPPROTO_UDP, iphlen, len) != 0)
-					goto badcsum;
-			}
-			break;
-		}
-	}
-
 	/* construct source and dst sockaddrs. */
 	bzero(&src, sizeof(src));
 	src.sin_family = AF_INET;
@@ -367,7 +398,6 @@ bad:
 
 badcsum:
 	m_freem(m);
-	udpstat.udps_badsum++;
 }
 #endif
 

--NextPart-20041218211932-0831101
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="csum.pf.diff"

Index: dist/pf/net/pf.c
===================================================================
--- dist/pf/net/pf.c	(revision 976)
+++ dist/pf/net/pf.c	(working copy)
@@ -5423,7 +5423,31 @@ pf_check_proto_cksum(struct mbuf *m, int
 		return (1);
 	if (m->m_pkthdr.len < off + len)
 		return (1);
-		switch (af) {
+#ifdef __NetBSD__
+	switch (p) {
+	case IPPROTO_TCP: {
+			struct tcphdr th; /* XXX */
+			int thlen;
+
+			m_copydata(m, off, sizeof(th), &th); /* XXX */
+			thlen = th.th_off << 2;
+			return tcp_input_checksum(af, m, &th, off,
+			    thlen, len - thlen) != 0;
+		}
+
+	case IPPROTO_UDP:
+#ifdef INET
+		if (af == AF_INET) {
+			struct udphdr uh; /* XXX */
+
+			m_copydata(m, off, sizeof(uh), &uh); /* XXX */
+			return udp4_input_checksum(m, &uh, off, len) != 0;
+		}
+#endif
+		break;
+	}
+#endif /* __NetBSD__ */
+	switch (af) {
 #ifdef INET
 	case AF_INET:
 		if (p == IPPROTO_ICMP) {

--NextPart-20041218211932-0831101--