IPComp Tunnel Mode Patch

Karim Fodil-Lemelin kfl at xiphos.ca
Mon May 3 09:45:17 PDT 2004


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


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


More information about the freebsd-net mailing list