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