Date: Mon, 03 May 2004 11:50:31 -0400 From: Karim Fodil-Lemelin <kfl@xiphos.ca> To: Marco Berizzi <pupilla@hotmail.com>, freebsd-net@freebsd.org, chris@e-easy.com.au Subject: IPComp Tunnel Mode Patch Message-ID: <40966A47.3040708@xiphos.ca>
next in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
Hi,
Here is the patch for getting IPComp to work in tunnel mode. This
patch is incomplete but It is working enough (for me) to be usefull.
Here is some notes I made about it:
IPComp works now in tunnel mode with ipv4 only (I wanna fix the m_pulldown issue before IPv6 support).
In ipcomp_input.c check before and after m_pulldown, somehting is not right (change #if 0 to #if 1 to convice you) since I get a total len (sums of m_len from the chain) != m_pkthdr.len. The kludge does it for now but should be looked into.
Tested with ESP over IPcomp and IPcomp alone in tunnel mode (needs more testing).
Did not try with FAST_IPSEC yet.
IPv6 Should be more or less the same thing. Hopefully ipcomp_input() is already done :)
To Install: stand in /usr/src/sys/netinet6/ and do "patch < <PATH_TO_PATCH>/ipcomp.patch"
Or use the provided text output.
Kernel opt:
options IPSEC #IP security
options IPSEC_ESP #IP security (crypto; define w/ IPSEC)
key file used:
ipcomp alone:
#
# Here, A.B.C.D is our public ip.
# W.X.Y.Z is the other host public ip.
#spdadd A.B.C.D/32 W.X.Y.Z/32 ipencap -P out ipsec esp/tunnel/A.B.C.D-W.X.Y.Z/require;
#spdadd W.X.Y.Z/32 A.B.C.D/32 ipencap -P in ipsec esp/tunnel/W.X.Y.Z-A.B.C.D/require;
spdadd 192.168.16.2/32 192.168.15.2/32 any -P out ipsec ipcomp/tunnel/192.168.16.2-192.168.15.2/use;
spdadd 192.168.15.2/32 192.168.16.2/32 any -P in ipsec ipcomp/tunnel/192.168.15.2-192.168.16.2/use;
ipcomp and esp:
#
# Here, A.B.C.D is our public ip.
# W.X.Y.Z is the other host public ip.
#spdadd A.B.C.D/32 W.X.Y.Z/32 ipencap -P out ipsec esp/tunnel/A.B.C.D-W.X.Y.Z/require;
#spdadd W.X.Y.Z/32 A.B.C.D/32 ipencap -P in ipsec esp/tunnel/W.X.Y.Z-A.B.C.D/require;
spdadd 192.168.16.2/24 192.168.15.2/24 any -P out ipsec ipcomp/tunnel/192.168.16.2-192.168.15.2/require esp/tunnel/192.168.16.2-192.168.15.2/require;
spdadd 192.168.15.2/24 192.168.16.2/24 any -P in ipsec ipcomp/tunnel/192.168.15.2-192.168.16.2/require esp/tunnel/192.168.15.2-192.168.16.2/require;
You can ignore the rest of this email if you use the attachment.
Let me know how it goes.
Regards,
Karim Fodil-Lemelin
Network Eng.
Xiphos Technologies Inc.
The Patch code:
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipcomp.h ./ipcomp.h
*** /usr/src/sys/netinet6/ipcomp.h Sun Apr 28 01:40:27 2002
--- ./ipcomp.h Sat May 1 17:33:11 2004
***************
*** 56,61 ****
--- 56,63 ----
#define IPCOMP_CPI_NEGOTIATE_MIN 256
#ifdef _KERNEL
+ #include <netinet6/ipsec.h>
+
struct ipcomp_algorithm {
int (*compress) __P((struct mbuf *, struct mbuf *, size_t *));
int (*decompress) __P((struct mbuf *, struct mbuf *, size_t *));
***************
*** 65,71 ****
struct ipsecrequest;
extern const struct ipcomp_algorithm *ipcomp_algorithm_lookup __P((int));
extern void ipcomp4_input __P((struct mbuf *, ...));
! extern int ipcomp4_output __P((struct mbuf *, struct ipsecrequest *));
#endif /* KERNEL */
#endif /* _NETINET6_IPCOMP_H_ */
--- 67,73 ----
struct ipsecrequest;
extern const struct ipcomp_algorithm *ipcomp_algorithm_lookup __P((int));
extern void ipcomp4_input __P((struct mbuf *, ...));
! extern int ipcomp4_output __P((struct ipsec_output_state *, struct ipsecrequest *));
#endif /* KERNEL */
#endif /* _NETINET6_IPCOMP_H_ */
Only in .: ipcomp.patch
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipcomp6.h ./ipcomp6.h
*** /usr/src/sys/netinet6/ipcomp6.h Tue Jul 3 07:01:54 2001
--- ./ipcomp6.h Sat May 1 17:32:36 2004
***************
*** 36,45 ****
#ifndef _NETINET6_IPCOMP6_H_
#define _NETINET6_IPCOMP6_H_
-
#ifdef _KERNEL
extern int ipcomp6_input __P((struct mbuf **, int *, int));
! extern int ipcomp6_output __P((struct mbuf *, u_char *, struct mbuf *,
struct ipsecrequest *));
#endif /*KERNEL*/
--- 36,47 ----
#ifndef _NETINET6_IPCOMP6_H_
#define _NETINET6_IPCOMP6_H_
#ifdef _KERNEL
+
+ #include <netinet6/ipsec.h>
+
extern int ipcomp6_input __P((struct mbuf **, int *, int));
! extern int ipcomp6_output __P((struct ipsec_output_state *, u_char *, struct mbuf *,
struct ipsecrequest *));
#endif /*KERNEL*/
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipcomp_input.c ./ipcomp_input.c
*** /usr/src/sys/netinet6/ipcomp_input.c Sun Apr 28 01:40:27 2002
--- ./ipcomp_input.c Sun May 2 21:00:08 2004
***************
*** 107,112 ****
--- 107,113 ----
struct secasvar *sav = NULL;
int off, proto;
va_list ap;
+ int s;
va_start(ap, m);
off = va_arg(ap, int);
***************
*** 120,125 ****
--- 121,162 ----
goto fail;
}
+ /* kfl:
+ * If we are dealing with a mbuf chain
+ * (happens when doing ESP over IPComp over a tunnel) we try to pullup
+ * all in one mbuf before pulling down. Since each protocol
+ * pulls up we end up here with a mbuf chain made of multiple
+ * small mbufs (about 20 to 60 bytes each). Although there is
+ * nothing wrong with it, m_pulldown return a chain where
+ * m_pkthdr.len is not equal to the sum of all m_len. By doing this
+ * pullup and the other hack (valid for 2 mbufs only) we get things
+ * working.
+ *
+ * XXX Its probably a good idea to rewrite this kludge into something
+ * more general. Turn the #if 0 to #if 1 and see that m_pulldown does
+ * something funky with the chain.
+ */
+ if (m->m_next) {
+ m = m_pullup(m, m->m_pkthdr.len);
+ if (!m) {
+ ipseclog((LOG_DEBUG, "IPv4 IPComp input: too ambitious "
+ "(pullup failure)\n"));
+ goto fail;
+ }
+ }
+ #if 0
+ {
+ struct mbuf *n;
+ int total_len = 0;
+ for (n=m; n; n=n->m_next)
+ total_len += n->m_len;
+ if (m->m_pkthdr.len != total_len)
+ /* Should never happen */
+ printf("before pulldown, len mismatch! m_pkthdr.len:%d total_len:%d\n",
+ m->m_pkthdr.len, total_len);
+ }
+ #endif
+
md = m_pulldown(m, off, sizeof(*ipcomp), NULL);
if (!m) {
m = NULL; /* already freed */
***************
*** 128,133 ****
--- 165,192 ----
ipsecstat.in_inval++;
goto fail;
}
+ #if 0
+ {
+ struct mbuf *n;
+ int total_len = 0;
+ for (n=m; n; n=n->m_next)
+ total_len += n->m_len;
+ if (m->m_pkthdr.len != total_len)
+ /* Should never happen */
+ printf("after pulldown, len mismatch! m_pkthdr.len:%d total_len:%d\n",
+ m->m_pkthdr.len, total_len);
+ }
+ #endif
+ /*
+ * kfl: I think m_pulldown has some problems with m_len, I should look
+ * into it but this hack (along with the previous one) does the trick for me now.
+ * XXX WARNING this assumes there is only two mbufs in the
+ * chain (which is ok for most packets because of the m_pullup and m_pulldown
+ * being carefull not to split mbufs easily).
+ */
+ if (m->m_next->m_next == 0 && (m->m_pkthdr.len > m->m_len + md->m_len))
+ md->m_len = m->m_pkthdr.len - m->m_len;
+
ipcomp = mtod(md, struct ipcomp *);
ip = mtod(m, struct ip *);
nxt = ipcomp->comp_nxt;
***************
*** 167,173 ****
#else
ip->ip_len = htons(ntohs(ip->ip_len) - sizeof(struct ipcomp));
#endif
-
olen = m->m_pkthdr.len;
newlen = m->m_pkthdr.len - off;
error = (*algo->decompress)(m, m->m_next, &newlen);
--- 226,231 ----
***************
*** 181,186 ****
--- 239,249 ----
}
ipsecstat.in_comphist[cpi]++;
+ if (newlen < olen) {
+ ipseclog((LOG_DEBUG, "IPv4 IPComp input: olen > newlen something may be wrong"
+ " with the mbuf chain\n"));
+ goto fail;
+ }
/*
* returning decompressed packet onto icmp is meaningless.
* mark it decrypted to prevent icmp from attaching original packet.
***************
*** 215,240 ****
ip->ip_p = nxt;
}
! if (sav) {
! key_sa_recordxfer(sav, m);
! if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) {
! ipsecstat.in_nomem++;
! goto fail;
! }
! key_freesav(sav);
! sav = NULL;
}
! if (nxt != IPPROTO_DONE) {
! if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
! ipsec4_in_reject(m, NULL)) {
! ipsecstat.in_polvio++;
! goto fail;
! }
! (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
! } else
! m_freem(m);
! m = NULL;
ipsecstat.in_success++;
return;
--- 278,356 ----
ip->ip_p = nxt;
}
! /* was it transmitted over the IPsec tunnel */
! if (ipsec4_tunnel_validate(m, off, nxt, sav)) {
! u_int8_t tos;
!
! tos = ip->ip_tos;
!
! m_adj(m, off);
! if (m->m_len < sizeof(*ip)) {
! m = m_pullup(m, sizeof(*ip));
! if (!m) {
! ipsecstat.in_inval++;
! goto fail;
}
+ }
! ip = mtod(m, struct ip *);
! /*
! * ECN consideration.
! * XXX Should we do this here?
! */
! ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
! if (!key_checktunnelsanity(sav, AF_INET,
! (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
! ipseclog((LOG_ERR, "ipsec tunnel address mismatch "
! "in IPv4 IPComp input: %u %s\n",
! cpi, ipsec_logsastr(sav)));
! ipsecstat.in_inval++;
! goto fail;
! }
!
! key_sa_recordxfer(sav, m);
! if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0 ||
! ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
! ipsecstat.in_nomem++;
! goto fail;
! }
! s = splimp();
! if (IF_QFULL(&ipintrq)) {
! ipsecstat.in_inval++;
! splx(s);
! goto fail;
! }
! IF_ENQUEUE(&ipintrq, m);
! m = NULL;
! schednetisr(NETISR_IP); /* can be skipped but to make sure */
! splx(s);
! nxt = IPPROTO_DONE;
! } else {
! if (sav) {
! key_sa_recordxfer(sav, m);
! if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) {
! ipsecstat.in_nomem++;
! goto fail;
! }
! key_freesav(sav);
! sav = NULL;
! }
!
! if (nxt != IPPROTO_DONE) {
! if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
! ipsec4_in_reject(m, NULL)) {
! ipsecstat.in_polvio++;
! goto fail;
! }
! printf("nxt :%d\n", nxt);
! (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
! } else
! m_freem(m);
! m = NULL;
! }
!
! if (sav)
! key_freesav(sav);
ipsecstat.in_success++;
return;
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipcomp_output.c ./ipcomp_output.c
*** /usr/src/sys/netinet6/ipcomp_output.c Sun Apr 28 01:40:27 2002
--- ./ipcomp_output.c Sun May 2 17:40:45 2004
***************
*** 81,87 ****
#include <net/net_osdep.h>
! static int ipcomp_output __P((struct mbuf *, u_char *, struct mbuf *,
struct ipsecrequest *, int));
/*
--- 81,87 ----
#include <net/net_osdep.h>
! static int ipcomp_output __P((struct ipsec_output_state *, u_char *, struct mbuf *,
struct ipsecrequest *, int));
/*
***************
*** 103,118 ****
* <-----------------> compoff
*/
static int
! ipcomp_output(m, nexthdrp, md, isr, af)
! struct mbuf *m;
u_char *nexthdrp;
struct mbuf *md;
struct ipsecrequest *isr;
int af;
{
struct mbuf *n;
! struct mbuf *md0;
! struct mbuf *mcopy;
struct mbuf *mprev;
struct ipcomp *ipcomp;
struct secasvar *sav = isr->sav;
--- 103,119 ----
* <-----------------> compoff
*/
static int
! ipcomp_output(state, nexthdrp, md, isr, af)
! struct ipsec_output_state *state;
u_char *nexthdrp;
struct mbuf *md;
struct ipsecrequest *isr;
int af;
{
struct mbuf *n;
! struct mbuf *m = state->m;
! struct mbuf *md0 = 0;
! struct mbuf *mcopy = 0;
struct mbuf *mprev;
struct ipcomp *ipcomp;
struct secasvar *sav = isr->sav;
***************
*** 171,186 ****
* compromise two m_copym(). we will be going through every byte of
* the payload during compression process anyways.
*/
! mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
! if (mcopy == NULL) {
! error = ENOBUFS;
! return 0;
! }
! md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT);
! if (md0 == NULL) {
! m_freem(mcopy);
! error = ENOBUFS;
! return 0;
}
plen0 = plen;
--- 172,194 ----
* compromise two m_copym(). we will be going through every byte of
* the payload during compression process anyways.
*/
! /* kfl:
! * In tunnel mode, we already have a copy.
! * XXX We save one m_copym in tunnel mode and I
! * beleive we should be able in transport mode as well.
! */
! if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
! mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
! if (mcopy == NULL) {
! error = ENOBUFS;
! return 0;
! }
! md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT);
! if (md0 == NULL) {
! m_freem(mcopy);
! error = ENOBUFS;
! return 0;
! }
}
plen0 = plen;
***************
*** 296,302 ****
m->m_pkthdr.len += complen;
ipcomp = mtod(md, struct ipcomp *);
}
-
bzero(ipcomp, sizeof(*ipcomp));
ipcomp->comp_nxt = *nexthdrp;
*nexthdrp = IPPROTO_IPCOMP;
--- 304,309 ----
***************
*** 304,310 ****
switch (af) {
#ifdef INET
case AF_INET:
! if (compoff + complen + plen < IP_MAXPACKET)
ip->ip_len = htons(compoff + complen + plen);
else {
ipseclog((LOG_ERR,
--- 311,317 ----
switch (af) {
#ifdef INET
case AF_INET:
! if (compoff + complen + plen < IP_MAXPACKET)
ip->ip_len = htons(compoff + complen + plen);
else {
ipseclog((LOG_ERR,
***************
*** 333,341 ****
stat->out_success++;
/* compute byte lifetime against original packet */
! key_sa_recordxfer(sav, mcopy);
! m_freem(mcopy);
!
return 0;
fail:
--- 340,354 ----
stat->out_success++;
/* compute byte lifetime against original packet */
! if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
! key_sa_recordxfer(sav, state->mcopy);
! m_freem(state->mcopy);
! state->mcopy = NULL;
! }
! else {
! key_sa_recordxfer(sav, mcopy);
! m_freem(mcopy);
! }
return 0;
fail:
***************
*** 348,357 ****
#ifdef INET
int
! ipcomp4_output(m, isr)
! struct mbuf *m;
struct ipsecrequest *isr;
{
struct ip *ip;
if (m->m_len < sizeof(struct ip)) {
ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n"));
--- 361,371 ----
#ifdef INET
int
! ipcomp4_output(state, isr)
! struct ipsec_output_state *state;
struct ipsecrequest *isr;
{
+ struct mbuf *m = state->m;
struct ip *ip;
if (m->m_len < sizeof(struct ip)) {
ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n"));
***************
*** 361,384 ****
}
ip = mtod(m, struct ip *);
/* XXX assumes that m->m_next points to payload */
! return ipcomp_output(m, &ip->ip_p, m->m_next, isr, AF_INET);
}
#endif /* INET */
#ifdef INET6
int
! ipcomp6_output(m, nexthdrp, md, isr)
! struct mbuf *m;
u_char *nexthdrp;
struct mbuf *md;
struct ipsecrequest *isr;
{
if (m->m_len < sizeof(struct ip6_hdr)) {
ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n"));
ipsec6stat.out_inval++;
m_freem(m);
return 0;
}
! return ipcomp_output(m, nexthdrp, md, isr, AF_INET6);
}
#endif /* INET6 */
--- 375,399 ----
}
ip = mtod(m, struct ip *);
/* XXX assumes that m->m_next points to payload */
! return ipcomp_output(state, &ip->ip_p, m->m_next, isr, AF_INET);
}
#endif /* INET */
#ifdef INET6
int
! ipcomp6_output(state, nexthdrp, md, isr)
! struct ipsec_output_state *state;
u_char *nexthdrp;
struct mbuf *md;
struct ipsecrequest *isr;
{
+ struct mbuf *m = state->m;
if (m->m_len < sizeof(struct ip6_hdr)) {
ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n"));
ipsec6stat.out_inval++;
m_freem(m);
return 0;
}
! return ipcomp_output(state, nexthdrp, md, isr, AF_INET6);
}
#endif /* INET6 */
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipsec.c ./ipsec.c
*** /usr/src/sys/netinet6/ipsec.c Thu Jan 23 16:06:47 2003
--- ./ipsec.c Sat May 1 17:30:28 2004
***************
*** 2575,2580 ****
--- 2575,2590 ----
s = splnet();
if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+ /*
+ * Make a copy in case we cannot compress the packet in IPComp.
+ */
+ if (isr->saidx.proto == IPPROTO_IPCOMP) {
+ state->mcopy = m_copym(state->m, 0, M_COPYALL, M_NOWAIT);
+ if (state->mcopy == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ }
/*
* build IPsec tunnel.
*/
***************
*** 2657,2665 ****
}
break;
case IPPROTO_IPCOMP:
! if ((error = ipcomp4_output(state->m, isr)) != 0) {
state->m = NULL;
! goto bad;
}
break;
default:
--- 2667,2681 ----
}
break;
case IPPROTO_IPCOMP:
! if ((error = ipcomp4_output(state, isr)) != 0) {
! m_freem(state->mcopy);
state->m = NULL;
! goto bad;
! }
! /* If we still have a copy, use it. */
! else if (state->mcopy) {
! m_freem(state->m);
! state->m = state->mcopy;
}
break;
default:
***************
*** 2824,2830 ****
error = ah6_output(state->m, nexthdrp, mprev->m_next, isr);
break;
case IPPROTO_IPCOMP:
! error = ipcomp6_output(state->m, nexthdrp, mprev->m_next, isr);
break;
default:
ipseclog((LOG_ERR, "ipsec6_output_trans: "
--- 2840,2846 ----
error = ah6_output(state->m, nexthdrp, mprev->m_next, isr);
break;
case IPPROTO_IPCOMP:
! error = ipcomp6_output(state, nexthdrp, mprev->m_next, isr);
break;
default:
ipseclog((LOG_ERR, "ipsec6_output_trans: "
***************
*** 2986,2991 ****
--- 3002,3017 ----
/*
* build IPsec tunnel.
*/
+ /*
+ * Make a copy in case we cannot compress the packet in IPComp.
+ */
+ if (isr->saidx.proto == IPPROTO_IPCOMP) {
+ state->mcopy = m_copym(state->m, 0, M_COPYALL, M_NOWAIT);
+ if (state->mcopy == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ }
/* XXX should be processed with other familiy */
if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET6) {
ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipsec.h ./ipsec.h
*** /usr/src/sys/netinet6/ipsec.h Thu Jan 23 16:06:47 2003
--- ./ipsec.h Sat May 1 17:19:12 2004
***************
*** 272,277 ****
--- 272,278 ----
#ifdef _KERNEL
struct ipsec_output_state {
struct mbuf *m;
+ struct mbuf *mcopy;
struct route *ro;
struct sockaddr *dst;
};
[-- Attachment #2 --]
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipcomp.h ./ipcomp.h
*** /usr/src/sys/netinet6/ipcomp.h Sun Apr 28 01:40:27 2002
--- ./ipcomp.h Sat May 1 17:33:11 2004
***************
*** 56,61 ****
--- 56,63 ----
#define IPCOMP_CPI_NEGOTIATE_MIN 256
#ifdef _KERNEL
+ #include <netinet6/ipsec.h>
+
struct ipcomp_algorithm {
int (*compress) __P((struct mbuf *, struct mbuf *, size_t *));
int (*decompress) __P((struct mbuf *, struct mbuf *, size_t *));
***************
*** 65,71 ****
struct ipsecrequest;
extern const struct ipcomp_algorithm *ipcomp_algorithm_lookup __P((int));
extern void ipcomp4_input __P((struct mbuf *, ...));
! extern int ipcomp4_output __P((struct mbuf *, struct ipsecrequest *));
#endif /* KERNEL */
#endif /* _NETINET6_IPCOMP_H_ */
--- 67,73 ----
struct ipsecrequest;
extern const struct ipcomp_algorithm *ipcomp_algorithm_lookup __P((int));
extern void ipcomp4_input __P((struct mbuf *, ...));
! extern int ipcomp4_output __P((struct ipsec_output_state *, struct ipsecrequest *));
#endif /* KERNEL */
#endif /* _NETINET6_IPCOMP_H_ */
Only in .: ipcomp.patch
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipcomp6.h ./ipcomp6.h
*** /usr/src/sys/netinet6/ipcomp6.h Tue Jul 3 07:01:54 2001
--- ./ipcomp6.h Sat May 1 17:32:36 2004
***************
*** 36,45 ****
#ifndef _NETINET6_IPCOMP6_H_
#define _NETINET6_IPCOMP6_H_
-
#ifdef _KERNEL
extern int ipcomp6_input __P((struct mbuf **, int *, int));
! extern int ipcomp6_output __P((struct mbuf *, u_char *, struct mbuf *,
struct ipsecrequest *));
#endif /*KERNEL*/
--- 36,47 ----
#ifndef _NETINET6_IPCOMP6_H_
#define _NETINET6_IPCOMP6_H_
#ifdef _KERNEL
+
+ #include <netinet6/ipsec.h>
+
extern int ipcomp6_input __P((struct mbuf **, int *, int));
! extern int ipcomp6_output __P((struct ipsec_output_state *, u_char *, struct mbuf *,
struct ipsecrequest *));
#endif /*KERNEL*/
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipcomp_input.c ./ipcomp_input.c
*** /usr/src/sys/netinet6/ipcomp_input.c Sun Apr 28 01:40:27 2002
--- ./ipcomp_input.c Sun May 2 21:00:08 2004
***************
*** 107,112 ****
--- 107,113 ----
struct secasvar *sav = NULL;
int off, proto;
va_list ap;
+ int s;
va_start(ap, m);
off = va_arg(ap, int);
***************
*** 120,125 ****
--- 121,162 ----
goto fail;
}
+ /* kfl:
+ * If we are dealing with a mbuf chain
+ * (happens when doing ESP over IPComp over a tunnel) we try to pullup
+ * all in one mbuf before pulling down. Since each protocol
+ * pulls up we end up here with a mbuf chain made of multiple
+ * small mbufs (about 20 to 60 bytes each). Although there is
+ * nothing wrong with it, m_pulldown return a chain where
+ * m_pkthdr.len is not equal to the sum of all m_len. By doing this
+ * pullup and the other hack (valid for 2 mbufs only) we get things
+ * working.
+ *
+ * XXX Its probably a good idea to rewrite this kludge into something
+ * more general. Turn the #if 0 to #if 1 and see that m_pulldown does
+ * something funky with the chain.
+ */
+ if (m->m_next) {
+ m = m_pullup(m, m->m_pkthdr.len);
+ if (!m) {
+ ipseclog((LOG_DEBUG, "IPv4 IPComp input: too ambitious "
+ "(pullup failure)\n"));
+ goto fail;
+ }
+ }
+ #if 0
+ {
+ struct mbuf *n;
+ int total_len = 0;
+ for (n=m; n; n=n->m_next)
+ total_len += n->m_len;
+ if (m->m_pkthdr.len != total_len)
+ /* Should never happen */
+ printf("before pulldown, len mismatch! m_pkthdr.len:%d total_len:%d\n",
+ m->m_pkthdr.len, total_len);
+ }
+ #endif
+
md = m_pulldown(m, off, sizeof(*ipcomp), NULL);
if (!m) {
m = NULL; /* already freed */
***************
*** 128,133 ****
--- 165,192 ----
ipsecstat.in_inval++;
goto fail;
}
+ #if 0
+ {
+ struct mbuf *n;
+ int total_len = 0;
+ for (n=m; n; n=n->m_next)
+ total_len += n->m_len;
+ if (m->m_pkthdr.len != total_len)
+ /* Should never happen */
+ printf("after pulldown, len mismatch! m_pkthdr.len:%d total_len:%d\n",
+ m->m_pkthdr.len, total_len);
+ }
+ #endif
+ /*
+ * kfl: I think m_pulldown has some problems with m_len, I should look
+ * into it but this hack (along with the previous one) does the trick for me now.
+ * XXX WARNING this assumes there is only two mbufs in the
+ * chain (which is ok for most packets because of the m_pullup and m_pulldown
+ * being carefull not to split mbufs easily).
+ */
+ if (m->m_next->m_next == 0 && (m->m_pkthdr.len > m->m_len + md->m_len))
+ md->m_len = m->m_pkthdr.len - m->m_len;
+
ipcomp = mtod(md, struct ipcomp *);
ip = mtod(m, struct ip *);
nxt = ipcomp->comp_nxt;
***************
*** 167,173 ****
#else
ip->ip_len = htons(ntohs(ip->ip_len) - sizeof(struct ipcomp));
#endif
-
olen = m->m_pkthdr.len;
newlen = m->m_pkthdr.len - off;
error = (*algo->decompress)(m, m->m_next, &newlen);
--- 226,231 ----
***************
*** 181,186 ****
--- 239,249 ----
}
ipsecstat.in_comphist[cpi]++;
+ if (newlen < olen) {
+ ipseclog((LOG_DEBUG, "IPv4 IPComp input: olen > newlen something may be wrong"
+ " with the mbuf chain\n"));
+ goto fail;
+ }
/*
* returning decompressed packet onto icmp is meaningless.
* mark it decrypted to prevent icmp from attaching original packet.
***************
*** 215,240 ****
ip->ip_p = nxt;
}
! if (sav) {
! key_sa_recordxfer(sav, m);
! if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) {
! ipsecstat.in_nomem++;
! goto fail;
! }
! key_freesav(sav);
! sav = NULL;
}
! if (nxt != IPPROTO_DONE) {
! if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
! ipsec4_in_reject(m, NULL)) {
! ipsecstat.in_polvio++;
! goto fail;
! }
! (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
! } else
! m_freem(m);
! m = NULL;
ipsecstat.in_success++;
return;
--- 278,356 ----
ip->ip_p = nxt;
}
! /* was it transmitted over the IPsec tunnel */
! if (ipsec4_tunnel_validate(m, off, nxt, sav)) {
! u_int8_t tos;
!
! tos = ip->ip_tos;
!
! m_adj(m, off);
! if (m->m_len < sizeof(*ip)) {
! m = m_pullup(m, sizeof(*ip));
! if (!m) {
! ipsecstat.in_inval++;
! goto fail;
}
+ }
! ip = mtod(m, struct ip *);
! /*
! * ECN consideration.
! * XXX Should we do this here?
! */
! ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
! if (!key_checktunnelsanity(sav, AF_INET,
! (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
! ipseclog((LOG_ERR, "ipsec tunnel address mismatch "
! "in IPv4 IPComp input: %u %s\n",
! cpi, ipsec_logsastr(sav)));
! ipsecstat.in_inval++;
! goto fail;
! }
!
! key_sa_recordxfer(sav, m);
! if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0 ||
! ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
! ipsecstat.in_nomem++;
! goto fail;
! }
! s = splimp();
! if (IF_QFULL(&ipintrq)) {
! ipsecstat.in_inval++;
! splx(s);
! goto fail;
! }
! IF_ENQUEUE(&ipintrq, m);
! m = NULL;
! schednetisr(NETISR_IP); /* can be skipped but to make sure */
! splx(s);
! nxt = IPPROTO_DONE;
! } else {
! if (sav) {
! key_sa_recordxfer(sav, m);
! if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) {
! ipsecstat.in_nomem++;
! goto fail;
! }
! key_freesav(sav);
! sav = NULL;
! }
!
! if (nxt != IPPROTO_DONE) {
! if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
! ipsec4_in_reject(m, NULL)) {
! ipsecstat.in_polvio++;
! goto fail;
! }
! printf("nxt :%d\n", nxt);
! (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
! } else
! m_freem(m);
! m = NULL;
! }
!
! if (sav)
! key_freesav(sav);
ipsecstat.in_success++;
return;
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipcomp_output.c ./ipcomp_output.c
*** /usr/src/sys/netinet6/ipcomp_output.c Sun Apr 28 01:40:27 2002
--- ./ipcomp_output.c Sun May 2 17:40:45 2004
***************
*** 81,87 ****
#include <net/net_osdep.h>
! static int ipcomp_output __P((struct mbuf *, u_char *, struct mbuf *,
struct ipsecrequest *, int));
/*
--- 81,87 ----
#include <net/net_osdep.h>
! static int ipcomp_output __P((struct ipsec_output_state *, u_char *, struct mbuf *,
struct ipsecrequest *, int));
/*
***************
*** 103,118 ****
* <-----------------> compoff
*/
static int
! ipcomp_output(m, nexthdrp, md, isr, af)
! struct mbuf *m;
u_char *nexthdrp;
struct mbuf *md;
struct ipsecrequest *isr;
int af;
{
struct mbuf *n;
! struct mbuf *md0;
! struct mbuf *mcopy;
struct mbuf *mprev;
struct ipcomp *ipcomp;
struct secasvar *sav = isr->sav;
--- 103,119 ----
* <-----------------> compoff
*/
static int
! ipcomp_output(state, nexthdrp, md, isr, af)
! struct ipsec_output_state *state;
u_char *nexthdrp;
struct mbuf *md;
struct ipsecrequest *isr;
int af;
{
struct mbuf *n;
! struct mbuf *m = state->m;
! struct mbuf *md0 = 0;
! struct mbuf *mcopy = 0;
struct mbuf *mprev;
struct ipcomp *ipcomp;
struct secasvar *sav = isr->sav;
***************
*** 171,186 ****
* compromise two m_copym(). we will be going through every byte of
* the payload during compression process anyways.
*/
! mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
! if (mcopy == NULL) {
! error = ENOBUFS;
! return 0;
! }
! md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT);
! if (md0 == NULL) {
! m_freem(mcopy);
! error = ENOBUFS;
! return 0;
}
plen0 = plen;
--- 172,194 ----
* compromise two m_copym(). we will be going through every byte of
* the payload during compression process anyways.
*/
! /* kfl:
! * In tunnel mode, we already have a copy.
! * XXX We save one m_copym in tunnel mode and I
! * beleive we should be able in transport mode as well.
! */
! if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
! mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
! if (mcopy == NULL) {
! error = ENOBUFS;
! return 0;
! }
! md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT);
! if (md0 == NULL) {
! m_freem(mcopy);
! error = ENOBUFS;
! return 0;
! }
}
plen0 = plen;
***************
*** 296,302 ****
m->m_pkthdr.len += complen;
ipcomp = mtod(md, struct ipcomp *);
}
-
bzero(ipcomp, sizeof(*ipcomp));
ipcomp->comp_nxt = *nexthdrp;
*nexthdrp = IPPROTO_IPCOMP;
--- 304,309 ----
***************
*** 304,310 ****
switch (af) {
#ifdef INET
case AF_INET:
! if (compoff + complen + plen < IP_MAXPACKET)
ip->ip_len = htons(compoff + complen + plen);
else {
ipseclog((LOG_ERR,
--- 311,317 ----
switch (af) {
#ifdef INET
case AF_INET:
! if (compoff + complen + plen < IP_MAXPACKET)
ip->ip_len = htons(compoff + complen + plen);
else {
ipseclog((LOG_ERR,
***************
*** 333,341 ****
stat->out_success++;
/* compute byte lifetime against original packet */
! key_sa_recordxfer(sav, mcopy);
! m_freem(mcopy);
!
return 0;
fail:
--- 340,354 ----
stat->out_success++;
/* compute byte lifetime against original packet */
! if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
! key_sa_recordxfer(sav, state->mcopy);
! m_freem(state->mcopy);
! state->mcopy = NULL;
! }
! else {
! key_sa_recordxfer(sav, mcopy);
! m_freem(mcopy);
! }
return 0;
fail:
***************
*** 348,357 ****
#ifdef INET
int
! ipcomp4_output(m, isr)
! struct mbuf *m;
struct ipsecrequest *isr;
{
struct ip *ip;
if (m->m_len < sizeof(struct ip)) {
ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n"));
--- 361,371 ----
#ifdef INET
int
! ipcomp4_output(state, isr)
! struct ipsec_output_state *state;
struct ipsecrequest *isr;
{
+ struct mbuf *m = state->m;
struct ip *ip;
if (m->m_len < sizeof(struct ip)) {
ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n"));
***************
*** 361,384 ****
}
ip = mtod(m, struct ip *);
/* XXX assumes that m->m_next points to payload */
! return ipcomp_output(m, &ip->ip_p, m->m_next, isr, AF_INET);
}
#endif /* INET */
#ifdef INET6
int
! ipcomp6_output(m, nexthdrp, md, isr)
! struct mbuf *m;
u_char *nexthdrp;
struct mbuf *md;
struct ipsecrequest *isr;
{
if (m->m_len < sizeof(struct ip6_hdr)) {
ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n"));
ipsec6stat.out_inval++;
m_freem(m);
return 0;
}
! return ipcomp_output(m, nexthdrp, md, isr, AF_INET6);
}
#endif /* INET6 */
--- 375,399 ----
}
ip = mtod(m, struct ip *);
/* XXX assumes that m->m_next points to payload */
! return ipcomp_output(state, &ip->ip_p, m->m_next, isr, AF_INET);
}
#endif /* INET */
#ifdef INET6
int
! ipcomp6_output(state, nexthdrp, md, isr)
! struct ipsec_output_state *state;
u_char *nexthdrp;
struct mbuf *md;
struct ipsecrequest *isr;
{
+ struct mbuf *m = state->m;
if (m->m_len < sizeof(struct ip6_hdr)) {
ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n"));
ipsec6stat.out_inval++;
m_freem(m);
return 0;
}
! return ipcomp_output(state, nexthdrp, md, isr, AF_INET6);
}
#endif /* INET6 */
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipsec.c ./ipsec.c
*** /usr/src/sys/netinet6/ipsec.c Thu Jan 23 16:06:47 2003
--- ./ipsec.c Sat May 1 17:30:28 2004
***************
*** 2575,2580 ****
--- 2575,2590 ----
s = splnet();
if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+ /*
+ * Make a copy in case we cannot compress the packet in IPComp.
+ */
+ if (isr->saidx.proto == IPPROTO_IPCOMP) {
+ state->mcopy = m_copym(state->m, 0, M_COPYALL, M_NOWAIT);
+ if (state->mcopy == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ }
/*
* build IPsec tunnel.
*/
***************
*** 2657,2665 ****
}
break;
case IPPROTO_IPCOMP:
! if ((error = ipcomp4_output(state->m, isr)) != 0) {
state->m = NULL;
! goto bad;
}
break;
default:
--- 2667,2681 ----
}
break;
case IPPROTO_IPCOMP:
! if ((error = ipcomp4_output(state, isr)) != 0) {
! m_freem(state->mcopy);
state->m = NULL;
! goto bad;
! }
! /* If we still have a copy, use it. */
! else if (state->mcopy) {
! m_freem(state->m);
! state->m = state->mcopy;
}
break;
default:
***************
*** 2824,2830 ****
error = ah6_output(state->m, nexthdrp, mprev->m_next, isr);
break;
case IPPROTO_IPCOMP:
! error = ipcomp6_output(state->m, nexthdrp, mprev->m_next, isr);
break;
default:
ipseclog((LOG_ERR, "ipsec6_output_trans: "
--- 2840,2846 ----
error = ah6_output(state->m, nexthdrp, mprev->m_next, isr);
break;
case IPPROTO_IPCOMP:
! error = ipcomp6_output(state, nexthdrp, mprev->m_next, isr);
break;
default:
ipseclog((LOG_ERR, "ipsec6_output_trans: "
***************
*** 2986,2991 ****
--- 3002,3017 ----
/*
* build IPsec tunnel.
*/
+ /*
+ * Make a copy in case we cannot compress the packet in IPComp.
+ */
+ if (isr->saidx.proto == IPPROTO_IPCOMP) {
+ state->mcopy = m_copym(state->m, 0, M_COPYALL, M_NOWAIT);
+ if (state->mcopy == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ }
/* XXX should be processed with other familiy */
if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET6) {
ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
diff --exclude CVS -C 3 /usr/src/sys/netinet6/ipsec.h ./ipsec.h
*** /usr/src/sys/netinet6/ipsec.h Thu Jan 23 16:06:47 2003
--- ./ipsec.h Sat May 1 17:19:12 2004
***************
*** 272,277 ****
--- 272,278 ----
#ifdef _KERNEL
struct ipsec_output_state {
struct mbuf *m;
+ struct mbuf *mcopy;
struct route *ro;
struct sockaddr *dst;
};
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?40966A47.3040708>
