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
110static 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 */
131static int
132ipcomp_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
361fail:
362#if 1
363 return error;
364#else
365 panic("something bad in ipcomp_output");
366#endif
367}
368
369#if INET
370int
371ipcomp4_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
387int
388ipcomp6_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*/
403