]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/ipcomp_output.c
xnu-792.21.3.tar.gz
[apple/xnu.git] / bsd / netinet6 / ipcomp_output.c
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 $ */
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 <net/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
63 #if INET6
64 #include <netinet/ip6.h>
65 #include <netinet6/ip6_var.h>
66 #endif
67 #include <netinet6/ipcomp.h>
68 #if INET6
69 #include <netinet6/ipcomp6.h>
70 #endif
71
72 #include <netinet6/ipsec.h>
73 #if INET6
74 #include <netinet6/ipsec6.h>
75 #endif
76 #include <netkey/key.h>
77 #include <netkey/keydb.h>
78
79 #include <net/net_osdep.h>
80
81 extern lck_mtx_t *sadb_mutex;
82
83 static int ipcomp_output(struct mbuf *, u_char *, struct mbuf *,
84 struct ipsecrequest *, int);
85
86 /*
87 * Modify the packet so that the payload is compressed.
88 * The mbuf (m) must start with IPv4 or IPv6 header.
89 * On failure, free the given mbuf and return non-zero.
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 */
104 static int
105 ipcomp_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;
114 struct mbuf *mcopy;
115 struct mbuf *mprev;
116 struct ipcomp *ipcomp;
117 struct secasvar *sav = isr->sav;
118 const struct ipcomp_algorithm *algo;
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;
124 struct ipsecstat *stat;
125
126 switch (af) {
127 #if INET
128 case AF_INET:
129 afnumber = 4;
130 stat = &ipsecstat;
131 break;
132 #endif
133 #if INET6
134 case AF_INET6:
135 afnumber = 6;
136 stat = &ipsec6stat;
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 */
145 algo = ipcomp_algorithm_lookup(sav->alg_enc);
146 if ((ntohl(sav->spi) & ~0xffff) != 0 || !algo) {
147 stat->out_inval++;
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;
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 /*
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.
172 */
173 mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
174 if (mcopy == NULL) {
175 error = ENOBUFS;
176 return 0;
177 }
178 md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT);
179 if (md0 == NULL) {
180 m_freem(mcopy);
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));
192 stat->out_inval++;
193 m_freem(m);
194 m_freem(md0);
195 m_freem(mcopy);
196 return EINVAL;
197 }
198 mprev->m_next = NULL;
199 if ((md = ipsec_copypkt(md)) == NULL) {
200 m_freem(m);
201 m_freem(md0);
202 m_freem(mcopy);
203 error = ENOBUFS;
204 goto fail;
205 }
206 mprev->m_next = md;
207
208 /* compress data part */
209 lck_mtx_unlock(sadb_mutex);
210 if ((*algo->compress)(m, md, &plen) || mprev->m_next == NULL) {
211 lck_mtx_lock(sadb_mutex);
212 ipseclog((LOG_ERR, "packet compression failure\n"));
213 m = NULL;
214 m_freem(md0);
215 m_freem(mcopy);
216 stat->out_inval++;
217 error = EINVAL;
218 goto fail;
219 }
220 lck_mtx_lock(sadb_mutex);
221 stat->out_comphist[sav->alg_enc]++;
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);
230 m_freem(mcopy);
231 mprev->m_next = md0;
232 return 0;
233 }
234
235 /*
236 * no need to backout change beyond here.
237 */
238 m_freem(md0);
239 md0 = NULL;
240
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));
333 stat->out_inval++;
334 }
335 stat->out_success++;
336
337 /* compute byte lifetime against original packet */
338 key_sa_recordxfer(sav, mcopy);
339 m_freem(mcopy);
340
341 return 0;
342
343 fail:
344 #if 1
345 return error;
346 #else
347 panic("something bad in ipcomp_output");
348 #endif
349 }
350
351 #if INET
352 int
353 ipcomp4_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);
362 return 0;
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
370 #ifdef INET6
371 int
372 ipcomp6_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);
382 return 0;
383 }
384 return ipcomp_output(m, nexthdrp, md, isr, AF_INET6);
385 }
386 #endif /*INET6*/