1 //==========================================================================
3 // src/sys/netinet6/ah_output.c
5 //==========================================================================
6 //####BSDCOPYRIGHTBEGIN####
8 // -------------------------------------------
10 // Portions of this software may have been derived from OpenBSD,
11 // FreeBSD or other sources, and are covered by the appropriate
12 // copyright disclaimers included herein.
14 // Portions created by Red Hat are
15 // Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
17 // -------------------------------------------
19 //####BSDCOPYRIGHTEND####
20 //==========================================================================
22 /* $KAME: ah_output.c,v 1.31 2001/07/26 06:53:15 jinmei Exp $ */
25 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
26 * All rights reserved.
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. Neither the name of the project nor the names of its contributors
37 * may be used to endorse or promote products derived from this software
38 * without specific prior written permission.
40 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * RFC1826/2402 authentication header.
57 #include <sys/param.h>
59 #include <sys/domain.h>
60 #include <sys/protosw.h>
61 #include <sys/socket.h>
62 #include <sys/socketvar.h>
63 #include <sys/errno.h>
67 #include <net/route.h>
69 #include <netinet/in.h>
71 #include <netinet/in_systm.h>
72 #include <netinet/ip.h>
73 #include <netinet/in_var.h>
76 #include <netinet/ip6.h>
77 #include <netinet6/ip6_var.h>
78 #include <netinet/icmp6.h>
81 #include <netinet6/ipsec.h>
82 #include <netinet6/ah.h>
83 #include <netkey/key.h>
84 #include <netkey/keydb.h>
87 static struct in_addr *ah4_finaldst __P((struct mbuf *));
91 * compute AH header size.
92 * transport mode only. for tunnel mode, we should implement
93 * virtual interface, and control MTU/MSS by the interface MTU.
97 struct ipsecrequest *isr;
99 const struct ah_algorithm *algo;
104 panic("ah_hdrsiz: NULL was passed.\n");
106 if (isr->saidx.proto != IPPROTO_AH)
107 panic("unsupported mode passed to ah_hdrsiz");
109 if (isr->sav == NULL)
111 if (isr->sav->state != SADB_SASTATE_MATURE
112 && isr->sav->state != SADB_SASTATE_DYING)
115 /* we need transport mode AH. */
116 algo = ah_algorithm_lookup(isr->sav->alg_auth);
122 * right now we don't calcurate the padding size. simply
123 * treat the padding size as constant, for simplicity.
125 * XXX variable size padding support
127 hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
128 if (isr->sav->flags & SADB_X_EXT_OLD)
129 hdrsiz += sizeof(struct ah);
131 hdrsiz += sizeof(struct newah);
137 * sizeof(struct newah) > sizeof(struct ah).
138 * 16 = (16 + 3) & ~(4 - 1).
140 return sizeof(struct newah) + 16;
145 * Modify the packet so that it includes the authentication data.
146 * The mbuf passed must start with IPv4 header.
148 * assumes that the first mbuf contains IPv4 header + option only.
149 * the function does not modify m.
154 struct ipsecrequest *isr;
156 struct secasvar *sav = isr->sav;
157 const struct ah_algorithm *algo;
160 u_char *ahsumpos = NULL;
161 size_t hlen = 0; /* IP header+option in bytes */
162 size_t plen = 0; /* AH payload size in bytes */
163 size_t ahlen = 0; /* plen + sizeof(ah) */
166 struct in_addr *finaldst;
170 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
173 ip = mtod(m, struct ip *);
174 ipseclog((LOG_DEBUG, "ah4_output: internal error: "
175 "sav->replay is null: %x->%x, SPI=%u\n",
176 (u_int32_t)ntohl(ip->ip_src.s_addr),
177 (u_int32_t)ntohl(ip->ip_dst.s_addr),
178 (u_int32_t)ntohl(sav->spi)));
179 ipsecstat.out_inval++;
184 algo = ah_algorithm_lookup(sav->alg_auth);
186 ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: "
187 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
188 ipsecstat.out_inval++;
195 * determine the size to grow.
197 if (sav->flags & SADB_X_EXT_OLD) {
199 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
200 ahlen = plen + sizeof(struct ah);
203 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
204 ahlen = plen + sizeof(struct newah);
208 * grow the mbuf to accomodate AH.
210 ip = mtod(m, struct ip *);
212 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
214 hlen = ip->ip_hl << 2;
217 if (m->m_len != hlen)
218 panic("ah4_output: assumption failed (first mbuf length)");
219 if (M_LEADINGSPACE(m->m_next) < ahlen) {
221 MGET(n, M_DONTWAIT, MT_DATA);
223 ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n",
229 n->m_next = m->m_next;
231 m->m_pkthdr.len += ahlen;
232 ahdrpos = mtod(n, u_char *);
234 m->m_next->m_len += ahlen;
235 m->m_next->m_data -= ahlen;
236 m->m_pkthdr.len += ahlen;
237 ahdrpos = mtod(m->m_next, u_char *);
240 ip = mtod(m, struct ip *); /* just to be sure */
245 if (sav->flags & SADB_X_EXT_OLD) {
248 ahdr = (struct ah *)ahdrpos;
249 ahsumpos = (u_char *)(ahdr + 1);
250 ahdr->ah_len = plen >> 2;
251 ahdr->ah_nxt = ip->ip_p;
252 ahdr->ah_reserve = htons(0);
254 bzero(ahdr + 1, plen);
258 ahdr = (struct newah *)ahdrpos;
259 ahsumpos = (u_char *)(ahdr + 1);
260 ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
261 ahdr->ah_nxt = ip->ip_p;
262 ahdr->ah_reserve = htons(0);
264 if (sav->replay->count == ~0) {
265 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
266 /* XXX Is it noisy ? */
267 ipseclog((LOG_WARNING,
268 "replay counter overflowed. %s\n",
269 ipsec_logsastr(sav)));
270 ipsecstat.out_inval++;
275 sav->replay->count++;
277 * XXX sequence number must not be cycled, if the SA is
278 * installed by IKE daemon.
280 ahdr->ah_seq = htonl(sav->replay->count);
281 bzero(ahdr + 1, plen);
285 * modify IPv4 header.
287 ip->ip_p = IPPROTO_AH;
288 if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
289 ip->ip_len = htons(ntohs(ip->ip_len) + ahlen);
291 ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
292 ipsecstat.out_inval++;
298 * If there is source routing option, update destination field in
299 * the IPv4 header to the final destination.
300 * Note that we do not need to update source routing option itself
301 * (as done in IPv4 AH processing -- see ip6_output()), since
302 * source routing option is not part of the ICV computation.
304 finaldst = ah4_finaldst(m);
306 dst.s_addr = ip->ip_dst.s_addr;
307 ip->ip_dst.s_addr = finaldst->s_addr;
311 * calcurate the checksum, based on security association
312 * and the algorithm specified.
314 error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
317 "error after ah4_calccksum, called from ah4_output"));
320 ipsecstat.out_inval++;
325 ip = mtod(m, struct ip *); /* just to make sure */
326 ip->ip_dst.s_addr = dst.s_addr;
328 ipsecstat.out_success++;
329 ipsecstat.out_ahhist[sav->alg_auth]++;
330 key_sa_recordxfer(sav, m);
336 /* Calculate AH length */
339 struct secasvar *sav;
341 const struct ah_algorithm *algo;
344 algo = ah_algorithm_lookup(sav->alg_auth);
347 if (sav->flags & SADB_X_EXT_OLD) {
349 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
350 ahlen = plen + sizeof(struct ah);
353 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
354 ahlen = plen + sizeof(struct newah);
362 * Fill in the Authentication Header and calculate checksum.
365 ah6_output(m, nexthdrp, md, isr)
369 struct ipsecrequest *isr;
373 struct secasvar *sav = isr->sav;
374 const struct ah_algorithm *algo;
376 u_char *ahsumpos = NULL;
377 size_t plen; /* AH payload size in bytes */
382 if (m->m_len < sizeof(struct ip6_hdr)) {
383 ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n"));
388 ahlen = ah_hdrlen(sav);
392 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
394 if (!mprev || mprev->m_next != md) {
395 ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n"));
400 MGET(mah, M_DONTWAIT, MT_DATA);
406 MCLGET(mah, M_DONTWAIT);
407 if ((mah->m_flags & M_EXT) == 0) {
416 m->m_pkthdr.len += ahlen;
419 if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
421 "ip6_output: AH with IPv6 jumbogram is not supported\n"));
425 ip6 = mtod(m, struct ip6_hdr *);
426 ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
428 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
429 ipseclog((LOG_DEBUG, "ah6_output: internal error: "
430 "sav->replay is null: SPI=%u\n",
431 (u_int32_t)ntohl(sav->spi)));
432 ipsec6stat.out_inval++;
437 algo = ah_algorithm_lookup(sav->alg_auth);
439 ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: "
440 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
441 ipsec6stat.out_inval++;
450 if (sav->flags & SADB_X_EXT_OLD) {
451 struct ah *ahdr = mtod(mah, struct ah *);
453 plen = mah->m_len - sizeof(struct ah);
454 ahsumpos = (u_char *)(ahdr + 1);
455 ahdr->ah_nxt = *nexthdrp;
456 *nexthdrp = IPPROTO_AH;
457 ahdr->ah_len = plen >> 2;
458 ahdr->ah_reserve = htons(0);
460 bzero(ahdr + 1, plen);
462 struct newah *ahdr = mtod(mah, struct newah *);
464 plen = mah->m_len - sizeof(struct newah);
465 ahsumpos = (u_char *)(ahdr + 1);
466 ahdr->ah_nxt = *nexthdrp;
467 *nexthdrp = IPPROTO_AH;
468 ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
469 ahdr->ah_reserve = htons(0);
471 if (sav->replay->count == ~0) {
472 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
473 /* XXX Is it noisy ? */
474 ipseclog((LOG_WARNING,
475 "replay counter overflowed. %s\n",
476 ipsec_logsastr(sav)));
477 ipsec6stat.out_inval++;
482 sav->replay->count++;
484 * XXX sequence number must not be cycled, if the SA is
485 * installed by IKE daemon.
487 ahdr->ah_seq = htonl(sav->replay->count);
488 bzero(ahdr + 1, plen);
492 * calcurate the checksum, based on security association
493 * and the algorithm specified.
495 error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
497 ipsec6stat.out_inval++;
500 ipsec6stat.out_success++;
501 key_sa_recordxfer(sav, m);
503 ipsec6stat.out_ahhist[sav->alg_auth]++;
511 * Find the final destination if there is loose/strict source routing option.
512 * Returns NULL if there's no source routing options.
513 * Returns NULL on errors too.
514 * Note that this function will return a pointer INTO the given parameter,
516 * The mbuf must be pulled up toward, at least, ip option part.
518 static struct in_addr *
529 panic("ah4_finaldst: m == NULL");
530 ip = mtod(m, struct ip *);
531 hlen = (ip->ip_hl << 2);
533 if (m->m_len < hlen) {
535 "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
539 if (hlen == sizeof(struct ip))
542 optlen = hlen - sizeof(struct ip);
544 ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n",
549 q = (u_char *)(ip + 1);
552 if (i + IPOPT_OPTVAL >= optlen)
554 if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
555 q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
556 i + IPOPT_OLEN < optlen)
561 switch (q[i + IPOPT_OPTVAL]) {
563 i = optlen; /* bye */
570 if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
571 optlen - i < q[i + IPOPT_OLEN]) {
573 "ip_finaldst: invalid IP option "
574 "(code=%02x len=%02x)\n",
575 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
578 i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
579 return (struct in_addr *)(q + i);
581 if (q[i + IPOPT_OLEN] < 2 ||
582 optlen - i < q[i + IPOPT_OLEN]) {
584 "ip_finaldst: invalid IP option "
585 "(code=%02x len=%02x)\n",
586 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
589 i += q[i + IPOPT_OLEN];