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