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