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;
 };


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;
  };
_______________________________________________
[EMAIL PROTECTED] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to