Module Name: src Committed By: christos Date: Wed Jun 28 13:12:37 UTC 2017
Modified Files: src/sys/netipsec: ipsec_input.c Log Message: PR/52346: Frank Kardel: Fix checksumming for NAT-T See XXX for improvements. To generate a diff of this commit: cvs rdiff -u -r1.43 -r1.44 src/sys/netipsec/ipsec_input.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netipsec/ipsec_input.c diff -u src/sys/netipsec/ipsec_input.c:1.43 src/sys/netipsec/ipsec_input.c:1.44 --- src/sys/netipsec/ipsec_input.c:1.43 Fri May 19 00:34:09 2017 +++ src/sys/netipsec/ipsec_input.c Wed Jun 28 09:12:37 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: ipsec_input.c,v 1.43 2017/05/19 04:34:09 ozaki-r Exp $ */ +/* $NetBSD: ipsec_input.c,v 1.44 2017/06/28 13:12:37 christos Exp $ */ /* $FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/netipsec/ipsec_input.c,v 1.2.4.2 2003/03/28 20:32:53 sam Exp $ */ /* $OpenBSD: ipsec_input.c,v 1.63 2003/02/20 18:35:43 deraadt Exp $ */ @@ -39,7 +39,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ipsec_input.c,v 1.43 2017/05/19 04:34:09 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ipsec_input.c,v 1.44 2017/06/28 13:12:37 christos Exp $"); /* * IPsec input processing. @@ -68,6 +68,8 @@ __KERNEL_RCSID(0, "$NetBSD: ipsec_input. #include <netinet/ip_var.h> #include <netinet/in_var.h> #include <netinet/in_proto.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> #include <netinet/ip6.h> #ifdef INET6 @@ -114,6 +116,63 @@ do { \ } while (/*CONSTCOND*/0) /* + * fixup TCP/UDP checksum + * + * XXX: if we have NAT-OA payload from IKE server, + * we must do the differential update of checksum. + * + * XXX: NAT-OAi/NAT-OAr drived from IKE initiator/responder. + * how to know the IKE side from kernel? + */ +static struct mbuf * +ipsec4_fixup_checksum(struct mbuf *m) +{ + struct ip *ip; + struct tcphdr *th; + struct udphdr *uh; + int poff, off; + int plen; + + if (m->m_len < sizeof(*ip)) + m = m_pullup(m, sizeof(*ip)); + ip = mtod(m, struct ip *); + poff = ip->ip_hl << 2; + plen = ntohs(ip->ip_len) - poff; + + switch (ip->ip_p) { + case IPPROTO_TCP: + IP6_EXTHDR_GET(th, struct tcphdr *, m, poff, sizeof(*th)); + if (th == NULL) + return NULL; + off = th->th_off << 2; + if (off < sizeof(*th) || off > plen) { + m_freem(m); + return NULL; + } + th->th_sum = 0; + th->th_sum = in4_cksum(m, IPPROTO_TCP, poff, plen); + break; + case IPPROTO_UDP: + IP6_EXTHDR_GET(uh, struct udphdr *, m, poff, sizeof(*uh)); + if (uh == NULL) + return NULL; + off = sizeof(*uh); + if (off > plen) { + m_freem(m); + return NULL; + } + uh->uh_sum = 0; + uh->uh_sum = in4_cksum(m, IPPROTO_UDP, poff, plen); + break; + default: + /* no checksum */ + return m; + } + + return m; +} + +/* * ipsec_common_input gets called when an IPsec-protected packet * is received by IPv4 or IPv6. It's job is to find the right SA # and call the appropriate transform. The transform callback @@ -304,19 +363,37 @@ ipsec4_common_input_cb(struct mbuf *m, s } /* Fix IPv4 header */ - if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) { - char buf[IPSEC_ADDRSTRLEN]; - IPSECLOG(LOG_DEBUG, "processing failed for SA %s/%08lx\n", - ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)), - (u_long) ntohl(sav->spi)); - IPSEC_ISTAT(sproto, ESP_STAT_HDROPS, AH_STAT_HDROPS, - IPCOMP_STAT_HDROPS); - error = ENOBUFS; - goto bad; + if (skip != 0) { + if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) { + char buf[IPSEC_ADDRSTRLEN]; +cantpull: + IPSECLOG(LOG_DEBUG, + "processing failed for SA %s/%08lx\n", + ipsec_address(&sav->sah->saidx.dst, buf, + sizeof(buf)), (u_long) ntohl(sav->spi)); + IPSEC_ISTAT(sproto, ESP_STAT_HDROPS, AH_STAT_HDROPS, + IPCOMP_STAT_HDROPS); + error = ENOBUFS; + goto bad; + } + + ip = mtod(m, struct ip *); + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); + } else { + ip = mtod(m, struct ip *); } - ip = mtod(m, struct ip *); - ip->ip_len = htons(m->m_pkthdr.len); + /* + * Update TCP/UDP checksum + * XXX: should only do it in NAT-T case + * XXX: should do it incrementally, see FreeBSD code. + */ + m = ipsec4_fixup_checksum(m); + if (m == NULL) + goto cantpull; + prot = ip->ip_p; /* IP-in-IP encapsulation */