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