1/* $FreeBSD: src/sys/netinet6/ipcomp_output.c,v 1.1.2.2 2001/07/03 11:01:54 ume Exp $ */ 2/* $KAME: ipcomp_output.c,v 1.23 2001/01/23 08:59:37 itojun 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 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/malloc.h> 41#include <sys/mbuf.h> 42#include <sys/domain.h> 43#include <sys/protosw.h> 44#include <sys/socket.h> 45#include <sys/errno.h> 46#include <sys/time.h> 47#include <sys/kernel.h> 48#include <sys/syslog.h> 49 50#include <net/if.h> 51#include <net/route.h> 52#include <libkern/zlib.h> 53#include <kern/cpu_number.h> 54#include <kern/locks.h> 55 56#include <netinet/in.h> 57#include <netinet/in_systm.h> 58#include <netinet/in_var.h> 59#include <netinet/ip.h> 60#include <netinet/ip_var.h> 61#include <netinet/ip_ecn.h> 62 63#if INET6 64#include <netinet/ip6.h> 65#include <netinet6/ip6_var.h> 66#endif 67#include <netinet6/ipcomp.h> 68#if INET6 69#include <netinet6/ipcomp6.h> 70#endif 71 72#include <netinet6/ipsec.h> 73#if INET6 74#include <netinet6/ipsec6.h> 75#endif 76#include <netkey/key.h> 77#include <netkey/keydb.h> 78 79#include <net/net_osdep.h> 80 81 82static int ipcomp_output(struct mbuf *, u_char *, struct mbuf *, 83 int, struct secasvar *sav); 84 85/* 86 * Modify the packet so that the payload is compressed. 87 * The mbuf (m) must start with IPv4 or IPv6 header. 88 * On failure, free the given mbuf and return non-zero. 89 * 90 * on invocation: 91 * m nexthdrp md 92 * v v v 93 * IP ......... payload 94 * during the encryption: 95 * m nexthdrp mprev md 96 * v v v v 97 * IP ............... ipcomp payload 98 * <-----><-----> 99 * complen plen 100 * <-> hlen 101 * <-----------------> compoff 102 */ 103static int 104ipcomp_output(m, nexthdrp, md, af, sav) 105 struct mbuf *m; 106 u_char *nexthdrp; 107 struct mbuf *md; 108 int af; 109 struct secasvar *sav; 110{ 111 struct mbuf *n; 112 struct mbuf *md0; 113 struct mbuf *mcopy; 114 struct mbuf *mprev; 115 struct ipcomp *ipcomp; 116 const struct ipcomp_algorithm *algo; 117 u_int16_t cpi; /* host order */ 118 size_t plen0, plen; /*payload length to be compressed*/ 119 size_t compoff; 120 int afnumber; 121 int error = 0; 122 struct ipsecstat *stat; 123 124 switch (af) { 125#if INET 126 case AF_INET: 127 afnumber = 4; 128 stat = &ipsecstat; 129 break; 130#endif 131#if INET6 132 case AF_INET6: 133 afnumber = 6; 134 stat = &ipsec6stat; 135 break; 136#endif 137 default: 138 ipseclog((LOG_ERR, "ipcomp_output: unsupported af %d\n", af)); 139 return 0; /* no change at all */ 140 } 141 142 /* grab parameters */ 143 algo = ipcomp_algorithm_lookup(sav->alg_enc); 144 if ((ntohl(sav->spi) & ~0xffff) != 0 || !algo) { 145 IPSEC_STAT_INCREMENT(stat->out_inval); 146 m_freem(m); 147 return EINVAL; 148 } 149 if ((sav->flags & SADB_X_EXT_RAWCPI) == 0) 150 cpi = sav->alg_enc; 151 else 152 cpi = ntohl(sav->spi) & 0xffff; 153 154 /* compute original payload length */ 155 plen = 0; 156 for (n = md; n; n = n->m_next) 157 plen += n->m_len; 158 159 /* if the payload is short enough, we don't need to compress */ 160 if (plen < algo->minplen) 161 return 0; 162 163 /* 164 * retain the original packet for two purposes: 165 * (1) we need to backout our changes when compression is not necessary. 166 * (2) byte lifetime computation should use the original packet. 167 * see RFC2401 page 23. 168 * compromise two m_copym(). we will be going through every byte of 169 * the payload during compression process anyways. 170 */ 171 mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT); 172 if (mcopy == NULL) { 173 error = ENOBUFS; 174 return 0; 175 } 176 md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT); 177 if (md0 == NULL) { 178 m_freem(mcopy); 179 error = ENOBUFS; 180 return 0; 181 } 182 plen0 = plen; 183 184 /* make the packet over-writable */ 185 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) 186 ; 187 if (mprev == NULL || mprev->m_next != md) { 188 ipseclog((LOG_DEBUG, "ipcomp%d_output: md is not in chain\n", 189 afnumber)); 190 IPSEC_STAT_INCREMENT(stat->out_inval); 191 m_freem(m); 192 m_freem(md0); 193 m_freem(mcopy); 194 return EINVAL; 195 } 196 mprev->m_next = NULL; 197 if ((md = ipsec_copypkt(md)) == NULL) { 198 m_freem(m); 199 m_freem(md0); 200 m_freem(mcopy); 201 error = ENOBUFS; 202 goto fail; 203 } 204 mprev->m_next = md; 205 206 /* compress data part */ 207 if ((*algo->compress)(m, md, &plen) || mprev->m_next == NULL) { 208 ipseclog((LOG_ERR, "packet compression failure\n")); 209 m = NULL; 210 m_freem(md0); 211 m_freem(mcopy); 212 IPSEC_STAT_INCREMENT(stat->out_inval); 213 error = EINVAL; 214 goto fail; 215 } 216 IPSEC_STAT_INCREMENT(stat->out_comphist[sav->alg_enc]); 217 md = mprev->m_next; 218 219 /* 220 * if the packet became bigger, meaningless to use IPComp. 221 * we've only wasted our cpu time. 222 */ 223 if (plen0 < plen) { 224 m_freem(md); 225 m_freem(mcopy); 226 mprev->m_next = md0; 227 return 0; 228 } 229 230 /* 231 * no need to backout change beyond here. 232 */ 233 m_freem(md0); 234 md0 = NULL; 235 236 m->m_pkthdr.len -= plen0; 237 m->m_pkthdr.len += plen; 238 239 { 240 /* 241 * insert IPComp header. 242 */ 243#if INET 244 struct ip *ip = NULL; 245#endif 246#if INET6 247 struct ip6_hdr *ip6 = NULL; 248#endif 249 size_t hlen = 0; /*ip header len*/ 250 size_t complen = sizeof(struct ipcomp); 251 252 switch (af) { 253#if INET 254 case AF_INET: 255 ip = mtod(m, struct ip *); 256#ifdef _IP_VHL 257 hlen = IP_VHL_HL(ip->ip_vhl) << 2; 258#else 259 hlen = ip->ip_hl << 2; 260#endif 261 break; 262#endif 263#if INET6 264 case AF_INET6: 265 ip6 = mtod(m, struct ip6_hdr *); 266 hlen = sizeof(*ip6); 267 break; 268#endif 269 } 270 271 compoff = m->m_pkthdr.len - plen; 272 273 /* 274 * grow the mbuf to accomodate ipcomp header. 275 * before: IP ... payload 276 * after: IP ... ipcomp payload 277 */ 278 if (M_LEADINGSPACE(md) < complen) { 279 MGET(n, M_DONTWAIT, MT_DATA); 280 if (!n) { 281 m_freem(m); 282 error = ENOBUFS; 283 goto fail; 284 } 285 n->m_len = complen; 286 mprev->m_next = n; 287 n->m_next = md; 288 m->m_pkthdr.len += complen; 289 ipcomp = mtod(n, struct ipcomp *); 290 } else { 291 md->m_len += complen; 292 md->m_data -= complen; 293 m->m_pkthdr.len += complen; 294 ipcomp = mtod(md, struct ipcomp *); 295 } 296 297 bzero(ipcomp, sizeof(*ipcomp)); 298 ipcomp->comp_nxt = *nexthdrp; 299 *nexthdrp = IPPROTO_IPCOMP; 300 ipcomp->comp_cpi = htons(cpi); 301 switch (af) { 302#if INET 303 case AF_INET: 304 if (compoff + complen + plen < IP_MAXPACKET) 305 ip->ip_len = htons(compoff + complen + plen); 306 else { 307 ipseclog((LOG_ERR, 308 "IPv4 ESP output: size exceeds limit\n")); 309 IPSEC_STAT_INCREMENT(ipsecstat.out_inval); 310 m_freem(m); 311 error = EMSGSIZE; 312 goto fail; 313 } 314 break; 315#endif 316#if INET6 317 case AF_INET6: 318 /* total packet length will be computed in ip6_output() */ 319 break; 320#endif 321 } 322 } 323 324 if (!m) { 325 ipseclog((LOG_DEBUG, 326 "NULL mbuf after compression in ipcomp%d_output", 327 afnumber)); 328 IPSEC_STAT_INCREMENT(stat->out_inval); 329 } 330 IPSEC_STAT_INCREMENT(stat->out_success); 331 332 /* compute byte lifetime against original packet */ 333 key_sa_recordxfer(sav, mcopy); 334 m_freem(mcopy); 335 336 return 0; 337 338fail: 339#if 1 340 return error; 341#else 342 panic("something bad in ipcomp_output"); 343#endif 344} 345 346#if INET 347int 348ipcomp4_output(m, sav) 349 struct mbuf *m; 350 struct secasvar *sav; 351{ 352 struct ip *ip; 353 if (m->m_len < sizeof(struct ip)) { 354 ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n")); 355 IPSEC_STAT_INCREMENT(ipsecstat.out_inval); 356 m_freem(m); 357 return 0; 358 } 359 ip = mtod(m, struct ip *); 360 /* XXX assumes that m->m_next points to payload */ 361 return ipcomp_output(m, &ip->ip_p, m->m_next, AF_INET, sav); 362} 363#endif /*INET*/ 364 365#if INET6 366int 367ipcomp6_output(m, nexthdrp, md, sav) 368 struct mbuf *m; 369 u_char *nexthdrp; 370 struct mbuf *md; 371 struct secasvar *sav; 372{ 373 if (m->m_len < sizeof(struct ip6_hdr)) { 374 ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n")); 375 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); 376 m_freem(m); 377 return 0; 378 } 379 return ipcomp_output(m, nexthdrp, md, AF_INET6, sav); 380} 381#endif /*INET6*/ 382