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