1/* $FreeBSD: src/sys/netinet6/ipcomp_input.c,v 1.1.2.2 2001/07/03 11:01:54 ume Exp $ */ 2/* $KAME: ipcomp_input.c,v 1.25 2001/03/01 09:12:09 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/mcache.h> 43#include <sys/domain.h> 44#include <sys/protosw.h> 45#include <sys/socket.h> 46#include <sys/errno.h> 47#include <sys/time.h> 48#include <sys/kernel.h> 49#include <sys/syslog.h> 50 51#include <net/if.h> 52#include <net/route.h> 53#include <libkern/zlib.h> 54#include <kern/cpu_number.h> 55#include <kern/locks.h> 56 57#include <netinet/in.h> 58#include <netinet/in_systm.h> 59#include <netinet/in_var.h> 60#include <netinet/ip.h> 61#include <netinet/ip_var.h> 62#include <netinet/ip_ecn.h> 63#include <netinet/kpi_ipfilter_var.h> 64 65#if INET6 66#include <netinet/ip6.h> 67#include <netinet6/ip6_var.h> 68#endif 69#include <netinet6/ipcomp.h> 70#if INET6 71#include <netinet6/ipcomp6.h> 72#endif 73 74#include <netinet6/ipsec.h> 75#if INET6 76#include <netinet6/ipsec6.h> 77#endif 78#include <netkey/key.h> 79#include <netkey/keydb.h> 80 81#include <net/net_osdep.h> 82#include <mach/sdt.h> 83 84#define IPLEN_FLIPPED 85 86void 87ipcomp4_input(struct mbuf *m, int off) 88{ 89 struct mbuf *md; 90 struct ip *ip; 91 struct ipcomp *ipcomp; 92 const struct ipcomp_algorithm *algo; 93 u_int16_t cpi; /* host order */ 94 u_int16_t nxt; 95 size_t hlen; 96 int error; 97 size_t newlen, olen; 98 struct secasvar *sav = NULL; 99 100 if (m->m_pkthdr.len < off + sizeof(struct ipcomp)) { 101 ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " 102 "(packet too short)\n")); 103 IPSEC_STAT_INCREMENT(ipsecstat.in_inval); 104 goto fail; 105 } 106 107 md = m_pulldown(m, off, sizeof(*ipcomp), NULL); 108 if (!md) { 109 m = NULL; /*already freed*/ 110 ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " 111 "(pulldown failure)\n")); 112 IPSEC_STAT_INCREMENT(ipsecstat.in_inval); 113 goto fail; 114 } 115 ipcomp = mtod(md, struct ipcomp *); 116 117 /* Expect 32-bit aligned data pointer on strict-align platforms */ 118 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); 119 120 ip = mtod(m, struct ip *); 121 nxt = ipcomp->comp_nxt; 122#ifdef _IP_VHL 123 hlen = IP_VHL_HL(ip->ip_vhl) << 2; 124#else 125 hlen = ip->ip_hl << 2; 126#endif 127 128 cpi = ntohs(ipcomp->comp_cpi); 129 130 if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { 131 sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, 132 (caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi)); 133 if (sav != NULL 134 && (sav->state == SADB_SASTATE_MATURE 135 || sav->state == SADB_SASTATE_DYING)) { 136 cpi = sav->alg_enc; /*XXX*/ 137 /* other parameters to look at? */ 138 } 139 } 140 algo = ipcomp_algorithm_lookup(cpi); 141 if (!algo) { 142 ipseclog((LOG_WARNING, "IPv4 IPComp input: unknown cpi %u\n", 143 cpi)); 144 IPSEC_STAT_INCREMENT(ipsecstat.in_nosa); 145 goto fail; 146 } 147 148 /* chop ipcomp header */ 149 ipcomp = NULL; 150 md->m_data += sizeof(struct ipcomp); 151 md->m_len -= sizeof(struct ipcomp); 152 m->m_pkthdr.len -= sizeof(struct ipcomp); 153#ifdef IPLEN_FLIPPED 154 ip->ip_len -= sizeof(struct ipcomp); 155#else 156 ip->ip_len = htons(ntohs(ip->ip_len) - sizeof(struct ipcomp)); 157#endif 158 159 olen = m->m_pkthdr.len; 160 newlen = m->m_pkthdr.len - off; 161 error = (*algo->decompress)(m, m->m_next, &newlen); 162 if (error != 0) { 163 if (error == EINVAL) { 164 IPSEC_STAT_INCREMENT(ipsecstat.in_inval); 165 } else if (error == ENOBUFS) 166 IPSEC_STAT_INCREMENT(ipsecstat.in_nomem); 167 m = NULL; 168 goto fail; 169 } 170 IPSEC_STAT_INCREMENT(ipsecstat.in_comphist[cpi]); 171 172 /* 173 * returning decompressed packet onto icmp is meaningless. 174 * mark it decrypted to prevent icmp from attaching original packet. 175 */ 176 m->m_flags |= M_DECRYPTED; 177 178 m->m_pkthdr.len = off + newlen; 179 ip = mtod(m, struct ip *); 180 { 181 size_t len; 182#ifdef IPLEN_FLIPPED 183 len = ip->ip_len; 184#else 185 len = ntohs(ip->ip_len); 186#endif 187 /* 188 * be careful about underflow. also, do not assign exact value 189 * as ip_len is manipulated differently on *BSDs. 190 */ 191 len += m->m_pkthdr.len; 192 len -= olen; 193 if (len & ~0xffff) { 194 /* packet too big after decompress */ 195 IPSEC_STAT_INCREMENT(ipsecstat.in_inval); 196 goto fail; 197 } 198#ifdef IPLEN_FLIPPED 199 ip->ip_len = len & 0xffff; 200#else 201 ip->ip_len = htons(len & 0xffff); 202#endif 203 ip->ip_p = nxt; 204 } 205 206 if (sav) { 207 key_sa_recordxfer(sav, m); 208 if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) { 209 IPSEC_STAT_INCREMENT(ipsecstat.in_nomem); 210 goto fail; 211 } 212 key_freesav(sav, KEY_SADB_UNLOCKED); 213 sav = NULL; 214 } 215 216 if (nxt != IPPROTO_DONE) { 217 if ((ip_protox[nxt]->pr_flags & PR_LASTHDR) != 0 && 218 ipsec4_in_reject(m, NULL)) { 219 IPSEC_STAT_INCREMENT(ipsecstat.in_polvio); 220 goto fail; 221 } 222 223 DTRACE_IP6(receive, struct mbuf *, m, struct inpcb *, NULL, 224 struct ip *, ip, struct ifnet *, m->m_pkthdr.rcvif, 225 struct ip *, ip, struct ip6_hdr *, NULL); 226 227 ip_proto_dispatch_in(m, off, nxt, 0); 228 } else 229 m_freem(m); 230 m = NULL; 231 232 IPSEC_STAT_INCREMENT(ipsecstat.in_success); 233 return; 234 235fail: 236 if (sav) 237 key_freesav(sav, KEY_SADB_UNLOCKED); 238 239 if (m) 240 m_freem(m); 241 return; 242} 243 244#if INET6 245int 246ipcomp6_input(struct mbuf **mp, int *offp, int proto) 247{ 248#pragma unused(proto) 249 struct mbuf *m, *md; 250 int off; 251 struct ip6_hdr *ip6; 252 struct ipcomp *ipcomp; 253 const struct ipcomp_algorithm *algo; 254 u_int16_t cpi; /* host order */ 255 u_int16_t nxt; 256 int error; 257 size_t newlen; 258 struct secasvar *sav = NULL; 259 char *prvnxtp; 260 261 m = *mp; 262 off = *offp; 263 264 md = m_pulldown(m, off, sizeof(*ipcomp), NULL); 265 if (!md) { 266 m = NULL; /*already freed*/ 267 ipseclog((LOG_DEBUG, "IPv6 IPComp input: assumption failed " 268 "(pulldown failure)\n")); 269 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); 270 goto fail; 271 } 272 ipcomp = mtod(md, struct ipcomp *); 273 274 /* Expect 32-bit aligned data pointer on strict-align platforms */ 275 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); 276 277 ip6 = mtod(m, struct ip6_hdr *); 278 nxt = ipcomp->comp_nxt; 279 280 cpi = ntohs(ipcomp->comp_cpi); 281 282 if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { 283 sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, 284 (caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi)); 285 if (sav != NULL 286 && (sav->state == SADB_SASTATE_MATURE 287 || sav->state == SADB_SASTATE_DYING)) { 288 cpi = sav->alg_enc; /*XXX*/ 289 /* other parameters to look at? */ 290 } 291 } 292 algo = ipcomp_algorithm_lookup(cpi); 293 if (!algo) { 294 ipseclog((LOG_WARNING, "IPv6 IPComp input: unknown cpi %u; " 295 "dropping the packet for simplicity\n", cpi)); 296 IPSEC_STAT_INCREMENT(ipsec6stat.in_nosa); 297 goto fail; 298 } 299 300 /* chop ipcomp header */ 301 ipcomp = NULL; 302 md->m_data += sizeof(struct ipcomp); 303 md->m_len -= sizeof(struct ipcomp); 304 m->m_pkthdr.len -= sizeof(struct ipcomp); 305 306 newlen = m->m_pkthdr.len - off; 307 error = (*algo->decompress)(m, md, &newlen); 308 if (error != 0) { 309 if (error == EINVAL) { 310 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); 311 } else if (error == ENOBUFS) 312 IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem); 313 m = NULL; 314 goto fail; 315 } 316 IPSEC_STAT_INCREMENT(ipsec6stat.in_comphist[cpi]); 317 m->m_pkthdr.len = off + newlen; 318 319 /* 320 * returning decompressed packet onto icmp is meaningless. 321 * mark it decrypted to prevent icmp from attaching original packet. 322 */ 323 m->m_flags |= M_DECRYPTED; 324 325 /* update next header field */ 326 prvnxtp = ip6_get_prevhdr(m, off); 327 *prvnxtp = nxt; 328 329 /* 330 * no need to adjust payload length, as all the IPv6 protocols 331 * look at m->m_pkthdr.len 332 */ 333 334 if (sav) { 335 key_sa_recordxfer(sav, m); 336 if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) { 337 IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem); 338 goto fail; 339 } 340 key_freesav(sav, KEY_SADB_UNLOCKED); 341 sav = NULL; 342 } 343 *offp = off; 344 *mp = m; 345 IPSEC_STAT_INCREMENT(ipsec6stat.in_success); 346 return nxt; 347 348fail: 349 if (m) 350 m_freem(m); 351 if (sav) 352 key_freesav(sav, KEY_SADB_UNLOCKED); 353 return IPPROTO_DONE; 354} 355#endif /* INET6 */ 356