1/* $NetBSD: ipcomp_output.c,v 1.29 2009/03/18 16:00:23 cegger Exp $ */ 2/* $KAME: ipcomp_output.c,v 1.24 2001/07/26 06:53:18 jinmei Exp $ */ 3 4/* 5 * Copyright (C) 1999 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33/* 34 * RFC2393 IP payload compression protocol (IPComp). 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD: ipcomp_output.c,v 1.29 2009/03/18 16:00:23 cegger Exp $"); 39 40#include "opt_inet.h" 41 42#include <sys/param.h> 43#include <sys/systm.h> 44#include <sys/malloc.h> 45#include <sys/mbuf.h> 46#include <sys/domain.h> 47#include <sys/protosw.h> 48#include <sys/socket.h> 49#include <sys/errno.h> 50#include <sys/time.h> 51#include <sys/kernel.h> 52#include <sys/syslog.h> 53 54#include <net/if.h> 55#include <net/route.h> 56#include <net/netisr.h> 57#include <net/zlib.h> 58#include <sys/cpu.h> 59 60#include <netinet/in.h> 61#include <netinet/in_systm.h> 62#include <netinet/in_var.h> 63#include <netinet/ip.h> 64#include <netinet/ip_var.h> 65#include <netinet/ip_ecn.h> 66 67#ifdef INET6 68#include <netinet/ip6.h> 69#include <netinet6/ip6_var.h> 70#endif 71#include <netinet6/ipcomp.h> 72 73#include <netinet6/ipsec.h> 74#include <netinet6/ipsec_private.h> 75#include <netkey/key.h> 76#include <netkey/keydb.h> 77 78#include <net/net_osdep.h> 79 80static int ipcomp_output(struct mbuf *, u_char *, struct mbuf *, 81 struct ipsecrequest *, int); 82 83/* 84 * Modify the packet so that the payload is compressed. 85 * The mbuf (m) must start with IPv4 or IPv6 header. 86 * On failure, free the given mbuf and return non-zero. 87 * 88 * on invocation: 89 * m nexthdrp md 90 * v v v 91 * IP ......... payload 92 * during the encryption: 93 * m nexthdrp mprev md 94 * v v v v 95 * IP ............... ipcomp payload 96 * <-----><-----> 97 * complen plen 98 * <-> hlen 99 * <-----------------> compoff 100 */ 101static int 102ipcomp_output(struct mbuf *m, u_char *nexthdrp, struct mbuf *md, 103 struct ipsecrequest *isr, int af) 104{ 105 struct mbuf *n; 106 struct mbuf *md0; 107 struct mbuf *mcopy; 108 struct mbuf *mprev; 109 struct ipcomp *ipcomp; 110 struct secasvar *sav = isr->sav; 111 const struct ipcomp_algorithm *algo; 112 u_int16_t cpi; /* host order */ 113 size_t plen0, plen; /* payload length to be compressed */ 114 size_t compoff; 115 int afnumber; 116 int error = 0; 117 percpu_t *stat; 118 119 switch (af) { 120#ifdef INET 121 case AF_INET: 122 afnumber = 4; 123 stat = ipsecstat_percpu; 124 break; 125#endif 126#ifdef INET6 127 case AF_INET6: 128 afnumber = 6; 129 stat = ipsec6stat_percpu; 130 break; 131#endif 132 default: 133 ipseclog((LOG_ERR, "ipcomp_output: unsupported af %d\n", af)); 134 return 0; /* no change at all */ 135 } 136 137 /* grab parameters */ 138 algo = ipcomp_algorithm_lookup(sav->alg_enc); 139 if ((ntohl(sav->spi) & ~0xffff) != 0 || !algo) { 140 _NET_STATINC(stat, IPSEC_STAT_OUT_INVAL); 141 error = EINVAL; 142 goto fail1; 143 } 144 if ((sav->flags & SADB_X_EXT_RAWCPI) == 0) 145 cpi = sav->alg_enc; 146 else 147 cpi = ntohl(sav->spi) & 0xffff; 148 149 /* compute original payload length */ 150 plen = 0; 151 for (n = md; n; n = n->m_next) 152 plen += n->m_len; 153 154 /* if the payload is short enough, we don't need to compress */ 155 if (plen < algo->minplen) 156 return 0; 157 158 /* 159 * retain the original packet for two purposes: 160 * (1) we need to backout our changes when compression is not necessary. 161 * (2) byte lifetime computation should use the original packet. 162 * see RFC2401 page 23. 163 * compromise two m_copym(). we will be going through every byte of 164 * the payload during compression process anyways. 165 */ 166 mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT); 167 if (mcopy == NULL) { 168 error = ENOBUFS; 169 goto fail1; 170 } 171 md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT); 172 if (md0 == NULL) { 173 error = ENOBUFS; 174 goto fail2; 175 } 176 plen0 = plen; 177 178 /* make the packet over-writable */ 179 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) 180 ; 181 if (mprev == NULL || mprev->m_next != md) { 182 ipseclog((LOG_DEBUG, "ipcomp%d_output: md is not in chain\n", 183 afnumber)); 184 _NET_STATINC(stat, IPSEC_STAT_OUT_INVAL); 185 error = EINVAL; 186 goto fail3; 187 } 188 mprev->m_next = NULL; 189 if ((md = ipsec_copypkt(md)) == NULL) { 190 error = ENOBUFS; 191 goto fail3; 192 } 193 mprev->m_next = md; 194 195 /* compress data part */ 196 if ((*algo->compress)(m, md, &plen) || mprev->m_next == NULL) { 197 ipseclog((LOG_ERR, "packet compression failure\n")); 198 m = NULL; 199 _NET_STATINC(stat, IPSEC_STAT_OUT_INVAL); 200 error = EINVAL; 201 goto fail3; 202 } 203 _NET_STATINC(stat, IPSEC_STAT_OUT_COMPHIST + sav->alg_enc); 204 md = mprev->m_next; 205 206 /* 207 * if the packet became bigger, meaningless to use IPComp. 208 * we've only wasted our CPU time. 209 */ 210 if (plen0 < plen) { 211 m_freem(md); 212 m_freem(mcopy); 213 mprev->m_next = md0; 214 return 0; 215 } 216 217 /* 218 * no need to backout change beyond here. 219 */ 220 m_freem(md0); 221 md0 = NULL; 222 223 m->m_pkthdr.len -= plen0; 224 m->m_pkthdr.len += plen; 225 226 { 227 /* 228 * insert IPComp header. 229 */ 230#ifdef INET 231 struct ip *ip = NULL; 232#endif 233#ifdef INET6 234 struct ip6_hdr *ip6 = NULL; 235#endif 236 size_t hlen = 0; /* ip header len */ 237 size_t complen = sizeof(struct ipcomp); 238 239 switch (af) { 240#ifdef INET 241 case AF_INET: 242 ip = mtod(m, struct ip *); 243 hlen = ip->ip_hl << 2; 244 break; 245#endif 246#ifdef INET6 247 case AF_INET6: 248 ip6 = mtod(m, struct ip6_hdr *); 249 hlen = sizeof(*ip6); 250 break; 251#endif 252 } 253 254 compoff = m->m_pkthdr.len - plen; 255 256 /* 257 * grow the mbuf to accommodate ipcomp header. 258 * before: IP ... payload 259 * after: IP ... ipcomp payload 260 */ 261 if (M_LEADINGSPACE(md) < complen) { 262 MGET(n, M_DONTWAIT, MT_DATA); 263 if (!n) { 264 error = ENOBUFS; 265 goto fail2; 266 } 267 n->m_len = complen; 268 mprev->m_next = n; 269 n->m_next = md; 270 m->m_pkthdr.len += complen; 271 ipcomp = mtod(n, struct ipcomp *); 272 } else { 273 md->m_len += complen; 274 md->m_data -= complen; 275 m->m_pkthdr.len += complen; 276 ipcomp = mtod(md, struct ipcomp *); 277 } 278 279 memset(ipcomp, 0, sizeof(*ipcomp)); 280 ipcomp->comp_nxt = *nexthdrp; 281 *nexthdrp = IPPROTO_IPCOMP; 282 ipcomp->comp_cpi = htons(cpi); 283 switch (af) { 284#ifdef INET 285 case AF_INET: 286 if (compoff + complen + plen < IP_MAXPACKET) 287 ip->ip_len = htons(compoff + complen + plen); 288 else { 289 ipseclog((LOG_ERR, 290 "IPv4 ESP output: size exceeds limit\n")); 291 IPSEC_STATINC(IPSEC_STAT_OUT_INVAL); 292 error = EMSGSIZE; 293 goto fail2; 294 } 295 break; 296#endif 297#ifdef INET6 298 case AF_INET6: 299 /* total packet length will be computed in ip6_output() */ 300 break; 301#endif 302 } 303 } 304 305 if (!m) { 306 ipseclog((LOG_DEBUG, 307 "NULL mbuf after compression in ipcomp%d_output", 308 afnumber)); 309 _NET_STATINC(stat, IPSEC_STAT_OUT_INVAL); 310 } 311 _NET_STATINC(stat, IPSEC_STAT_OUT_SUCCESS); 312 313 /* compute byte lifetime against original packet */ 314 key_sa_recordxfer(sav, mcopy); 315 m_freem(mcopy); 316 317 return 0; 318 319fail3: 320 m_freem(md0); 321fail2: 322 m_freem(mcopy); 323fail1: 324 m_freem(m); 325#if 1 326 return error; 327#else 328 panic("something bad in ipcomp_output"); 329#endif 330} 331 332#ifdef INET 333int 334ipcomp4_output(struct mbuf *m, struct ipsecrequest *isr) 335{ 336 struct ip *ip; 337 if (m->m_len < sizeof(struct ip)) { 338 ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n")); 339 IPSEC_STATINC(IPSEC_STAT_OUT_INVAL); 340 m_freem(m); 341 return EINVAL; 342 } 343 ip = mtod(m, struct ip *); 344 /* XXX assumes that m->m_next points to payload */ 345 return ipcomp_output(m, &ip->ip_p, m->m_next, isr, AF_INET); 346} 347#endif /* INET */ 348 349#ifdef INET6 350int 351ipcomp6_output(struct mbuf *m, u_char *nexthdrp, struct mbuf *md, 352 struct ipsecrequest *isr) 353{ 354 if (m->m_len < sizeof(struct ip6_hdr)) { 355 ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n")); 356 IPSEC6_STATINC(IPSEC_STAT_OUT_INVAL); 357 m_freem(m); 358 return EINVAL; 359 } 360 return ipcomp_output(m, nexthdrp, md, isr, AF_INET6); 361} 362#endif /* INET6 */ 363