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