Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

IPComp Tunnel Mode Patch

1 view
Skip to first unread message

Karim Fodil-Lemelin

unread,
May 3, 2004, 11:50:31 AM5/3/04
to Marco Berizzi, freeb...@freebsd.org, ch...@e-easy.com.au
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;
};


ipcomp.patch

Sam Leffler

unread,
May 3, 2004, 12:59:14 PM5/3/04
to freeb...@freebsd.org, Marco Berizzi, ch...@e-easy.com.au
On Monday 03 May 2004 08:50 am, Karim Fodil-Lemelin wrote:
> 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.

FAST_IPSEC uses the crypto subsystem for ipcomp and that code has an issue
with the kernel zlib code. It's been a long time since I looked at the issue
but beware that any problems you hit are likely to be over in the crypto
stuff and not the protocol support.

If anyone is interested in working on this it'd also be good to bring over the
openbsd hifn changes to support h/w ipcomp.

Sam

0 new messages