NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: bin/47894: racoon w/NAT-T - pfkey update: wrong ports
Hi Gergely,
I found a problem about checksum update of transport mode NAT-T IPsec.
A NAT box can rewrite a address in IP header, but it cannot rewrite a
checksum field in TCP/UDP header. The header is protected by ESP.
So IPsec stack must update the checksum instead of NAT BOX,
but there is no implementation about it.
IKE Protocol has NAT-OA(Origianl Address) payloads to do this. We must
handle the payload and update the checksum. There is some difficulity
around it. There is two NAT-OA payload for initiator side and responder
side. But our kernel doesn't know its side of nagotiation. racoon knows it,
but there is no API to send the side information to kernel.
Tunnel mode IPsec doesn't affected, because inner IP header is not
rewritten by NAT box.
I wrote a simple patch that recalculate TCP/UDP checksum instead of
differential update. This patch is not a goal, but we can confirm that
your problem really comes from checksum calculation.
Please try this patch.
Thank you,
Index: ipsec_input.c
===================================================================
RCS file: /cvsroot/src/sys/netipsec/ipsec_input.c,v
retrieving revision 1.32
diff -u -w -p -r1.32 ipsec_input.c
--- ipsec_input.c 8 Mar 2014 12:18:04 -0000 1.32
+++ ipsec_input.c 20 Jun 2014 07:07:43 -0000
@@ -70,6 +70,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
@@ -118,6 +120,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
@@ -324,6 +383,20 @@ ipsec4_common_input_cb(struct mbuf *m, s
ip->ip_len = htons(m->m_pkthdr.len);
prot = ip->ip_p;
+ /* Update TCP/UDP checksum */
+ m = ipsec4_fixup_checksum(m);
+ if (m == NULL) {
+ DPRINTF(("ipsec4_common_input_cb: processing failed "
+ "for SA %s/%08lx\n",
+ ipsec_address(&sav->sah->saidx.dst),
+ (u_long) ntohl(sav->spi)));
+ IPSEC_ISTAT(sproto, ESP_STAT_HDROPS, AH_STAT_HDROPS,
+ IPCOMP_STAT_HDROPS);
+ error = ENOBUFS;
+ goto bad;
+ }
+
+
/* IP-in-IP encapsulation */
if (prot == IPPROTO_IPIP) {
struct ip ipn;
--
Internet Initiative Japan Inc.
Device Engineering Section,
Core Product Development Department,
Product Division,
Technology Unit
SUENAGA Hiroki <hsuenaga%iij.ad.jp@localhost>
PGP: 66B3 8939 6758 20BA F243 89EC 557A 8CFB ABA9 5E92
Home |
Main Index |
Thread Index |
Old Index