1 /* $NetBSD: ah_output.c,v 1.32 2009/03/14 14:46:10 dsl Exp $ */
2 /* $KAME: ah_output.c,v 1.31 2001/07/26 06:53:15 jinmei Exp $ */
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
34 * RFC1826/2402 authentication header.
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: ah_output.c,v 1.32 2009/03/14 14:46:10 dsl Exp $");
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/malloc.h>
46 #include <sys/domain.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
49 #include <sys/socketvar.h>
50 #include <sys/errno.h>
52 #include <sys/kernel.h>
53 #include <sys/syslog.h>
56 #include <net/route.h>
58 #include <netinet/in.h>
60 #include <netinet/in_systm.h>
61 #include <netinet/ip.h>
62 #include <netinet/in_var.h>
65 #include <netinet/ip6.h>
66 #include <netinet6/ip6_var.h>
67 #include <netinet/icmp6.h>
70 #include <netinet6/ipsec.h>
71 #include <netinet6/ipsec_private.h>
72 #include <netinet6/ah.h>
73 #include <netkey/key.h>
74 #include <netkey/keydb.h>
76 #include <net/net_osdep.h>
79 static struct in_addr
*ah4_finaldst(struct mbuf
*);
83 * compute AH header size.
84 * transport mode only. for tunnel mode, we should implement
85 * virtual interface, and control MTU/MSS by the interface MTU.
88 ah_hdrsiz(struct ipsecrequest
*isr
)
90 const struct ah_algorithm
*algo
;
95 panic("ah_hdrsiz: NULL was passed.");
97 if (isr
->saidx
.proto
!= IPPROTO_AH
)
98 panic("unsupported mode passed to ah_hdrsiz");
100 if (isr
->sav
== NULL
)
102 if (isr
->sav
->state
!= SADB_SASTATE_MATURE
103 && isr
->sav
->state
!= SADB_SASTATE_DYING
)
106 /* we need transport mode AH. */
107 algo
= ah_algorithm_lookup(isr
->sav
->alg_auth
);
113 * right now we don't calcurate the padding size. simply
114 * treat the padding size as constant, for simplicity.
116 * XXX variable size padding support
118 hdrsiz
= (((*algo
->sumsiz
)(isr
->sav
) + 3) & ~(4 - 1));
119 if (isr
->sav
->flags
& SADB_X_EXT_OLD
)
120 hdrsiz
+= sizeof(struct ah
);
122 hdrsiz
+= sizeof(struct newah
);
128 * sizeof(struct newah) > sizeof(struct ah).
129 * AH_MAXSUMSIZE is multiple of 4.
131 return sizeof(struct newah
) + AH_MAXSUMSIZE
;
136 * Modify the packet so that it includes the authentication data.
137 * The mbuf passed must start with IPv4 header.
139 * assumes that the first mbuf contains IPv4 header + option only.
140 * the function does not modify m.
143 ah4_output(struct mbuf
*m
, struct ipsecrequest
*isr
)
145 struct secasvar
*sav
= isr
->sav
;
146 const struct ah_algorithm
*algo
;
149 u_int8_t
*ahsumpos
= NULL
;
150 size_t hlen
= 0; /* IP header+option in bytes */
151 size_t plen
= 0; /* AH payload size in bytes */
152 size_t ahlen
= 0; /* plen + sizeof(ah) */
155 struct in_addr
*finaldst
;
157 dst
.s_addr
= 0; /* XXX: GCC */
160 if ((sav
->flags
& SADB_X_EXT_OLD
) == 0 && !sav
->replay
) {
161 ip
= mtod(m
, struct ip
*);
162 ipseclog((LOG_DEBUG
, "ah4_output: internal error: "
163 "sav->replay is null: %x->%x, SPI=%u\n",
164 (u_int32_t
)ntohl(ip
->ip_src
.s_addr
),
165 (u_int32_t
)ntohl(ip
->ip_dst
.s_addr
),
166 (u_int32_t
)ntohl(sav
->spi
)));
167 IPSEC_STATINC(IPSEC_STAT_OUT_INVAL
);
172 algo
= ah_algorithm_lookup(sav
->alg_auth
);
174 ipseclog((LOG_ERR
, "ah4_output: unsupported algorithm: "
175 "SPI=%u\n", (u_int32_t
)ntohl(sav
->spi
)));
176 IPSEC_STATINC(IPSEC_STAT_OUT_INVAL
);
183 * determine the size to grow.
185 if (sav
->flags
& SADB_X_EXT_OLD
) {
187 plen
= ((*algo
->sumsiz
)(sav
) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
188 ahlen
= plen
+ sizeof(struct ah
);
191 plen
= ((*algo
->sumsiz
)(sav
) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
192 ahlen
= plen
+ sizeof(struct newah
);
196 * grow the mbuf to accommodate AH.
198 ip
= mtod(m
, struct ip
*);
199 hlen
= ip
->ip_hl
<< 2;
201 if (m
->m_len
!= hlen
)
202 panic("ah4_output: assumption failed (first mbuf length)");
203 if (M_LEADINGSPACE(m
->m_next
) < ahlen
) {
205 MGET(n
, M_DONTWAIT
, MT_DATA
);
207 ipseclog((LOG_DEBUG
, "ENOBUFS in ah4_output %d\n",
213 n
->m_next
= m
->m_next
;
215 m
->m_pkthdr
.len
+= ahlen
;
216 ahdrpos
= mtod(n
, u_char
*);
218 m
->m_next
->m_len
+= ahlen
;
219 m
->m_next
->m_data
-= ahlen
;
220 m
->m_pkthdr
.len
+= ahlen
;
221 ahdrpos
= mtod(m
->m_next
, u_char
*);
224 ip
= mtod(m
, struct ip
*); /* just to be sure */
229 if (sav
->flags
& SADB_X_EXT_OLD
) {
232 ahdr
= (struct ah
*)ahdrpos
;
233 ahsumpos
= (u_char
*)(ahdr
+ 1);
234 ahdr
->ah_len
= plen
>> 2;
235 ahdr
->ah_nxt
= ip
->ip_p
;
236 ahdr
->ah_reserve
= htons(0);
238 memset(ahdr
+ 1, 0, plen
);
242 ahdr
= (struct newah
*)ahdrpos
;
243 ahsumpos
= (u_char
*)(ahdr
+ 1);
244 ahdr
->ah_len
= (plen
>> 2) + 1; /* plus one for seq# */
245 ahdr
->ah_nxt
= ip
->ip_p
;
246 ahdr
->ah_reserve
= htons(0);
248 if (sav
->replay
->count
== ~0) {
249 if ((sav
->flags
& SADB_X_EXT_CYCSEQ
) == 0) {
250 /* XXX Is it noisy ? */
251 ipseclog((LOG_WARNING
,
252 "replay counter overflowed. %s\n",
253 ipsec_logsastr(sav
)));
254 IPSEC_STATINC(IPSEC_STAT_OUT_INVAL
);
259 sav
->replay
->count
++;
261 * XXX sequence number must not be cycled, if the SA is
262 * installed by IKE daemon.
264 ahdr
->ah_seq
= htonl(sav
->replay
->count
& 0xffffffff);
265 memset(ahdr
+ 1, 0, plen
);
269 * modify IPv4 header.
271 ip
->ip_p
= IPPROTO_AH
;
272 if (ahlen
< (IP_MAXPACKET
- ntohs(ip
->ip_len
)))
273 ip
->ip_len
= htons(ntohs(ip
->ip_len
) + ahlen
);
275 ipseclog((LOG_ERR
, "IPv4 AH output: size exceeds limit\n"));
276 IPSEC_STATINC(IPSEC_STAT_OUT_INVAL
);
282 * If there is source routing option, update destination field in
283 * the IPv4 header to the final destination.
284 * Note that we do not need to update source routing option itself
285 * (as done in IPv4 AH processing -- see ip6_output()), since
286 * source routing option is not part of the ICV computation.
288 finaldst
= ah4_finaldst(m
);
290 dst
.s_addr
= ip
->ip_dst
.s_addr
;
291 ip
->ip_dst
.s_addr
= finaldst
->s_addr
;
295 * calcurate the checksum, based on security association
296 * and the algorithm specified.
298 error
= ah4_calccksum(m
, ahsumpos
, plen
, algo
, sav
);
301 "error after ah4_calccksum, called from ah4_output"));
302 IPSEC_STATINC(IPSEC_STAT_OUT_INVAL
);
307 ip
= mtod(m
, struct ip
*); /* just to make sure */
308 ip
->ip_dst
.s_addr
= dst
.s_addr
;
311 uint64_t *ipss
= IPSEC_STAT_GETREF();
312 ipss
[IPSEC_STAT_OUT_SUCCESS
]++;
313 ipss
[IPSEC_STAT_OUT_AHHIST
+ sav
->alg_auth
]++;
316 key_sa_recordxfer(sav
, m
);
326 /* Calculate AH length */
328 ah_hdrlen(struct secasvar
*sav
)
330 const struct ah_algorithm
*algo
;
333 algo
= ah_algorithm_lookup(sav
->alg_auth
);
336 if (sav
->flags
& SADB_X_EXT_OLD
) {
338 plen
= ((*algo
->sumsiz
)(sav
) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
339 ahlen
= plen
+ sizeof(struct ah
);
342 plen
= ((*algo
->sumsiz
)(sav
) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
343 ahlen
= plen
+ sizeof(struct newah
);
351 * Fill in the Authentication Header and calculate checksum.
354 ah6_output(struct mbuf
*m
, u_char
*nexthdrp
, struct mbuf
*md
,
355 struct ipsecrequest
*isr
)
359 struct secasvar
*sav
= isr
->sav
;
360 const struct ah_algorithm
*algo
;
362 u_int8_t
*ahsumpos
= NULL
;
363 size_t plen
; /* AH payload size in bytes */
368 if (m
->m_len
< sizeof(struct ip6_hdr
)) {
369 ipseclog((LOG_DEBUG
, "ah6_output: first mbuf too short\n"));
374 ahlen
= ah_hdrlen(sav
);
378 for (mprev
= m
; mprev
&& mprev
->m_next
!= md
; mprev
= mprev
->m_next
)
380 if (!mprev
|| mprev
->m_next
!= md
) {
381 ipseclog((LOG_DEBUG
, "ah6_output: md is not in chain\n"));
386 MGET(mah
, M_DONTWAIT
, MT_DATA
);
392 MCLGET(mah
, M_DONTWAIT
);
393 if ((mah
->m_flags
& M_EXT
) == 0) {
402 m
->m_pkthdr
.len
+= ahlen
;
405 if (m
->m_pkthdr
.len
- sizeof(struct ip6_hdr
) > IPV6_MAXPACKET
) {
407 "ah6_output: AH with IPv6 jumbogram is not supported\n"));
411 ip6
= mtod(m
, struct ip6_hdr
*);
412 ip6
->ip6_plen
= htons(m
->m_pkthdr
.len
- sizeof(struct ip6_hdr
));
414 if ((sav
->flags
& SADB_X_EXT_OLD
) == 0 && !sav
->replay
) {
415 ipseclog((LOG_DEBUG
, "ah6_output: internal error: "
416 "sav->replay is null: SPI=%u\n",
417 (u_int32_t
)ntohl(sav
->spi
)));
418 IPSEC6_STATINC(IPSEC_STAT_OUT_INVAL
);
423 algo
= ah_algorithm_lookup(sav
->alg_auth
);
425 ipseclog((LOG_ERR
, "ah6_output: unsupported algorithm: "
426 "SPI=%u\n", (u_int32_t
)ntohl(sav
->spi
)));
427 IPSEC6_STATINC(IPSEC_STAT_OUT_INVAL
);
436 if (sav
->flags
& SADB_X_EXT_OLD
) {
437 struct ah
*ahdr
= mtod(mah
, struct ah
*);
439 plen
= mah
->m_len
- sizeof(struct ah
);
440 ahsumpos
= (u_char
*)(ahdr
+ 1);
441 ahdr
->ah_nxt
= *nexthdrp
;
442 *nexthdrp
= IPPROTO_AH
;
443 ahdr
->ah_len
= plen
>> 2;
444 ahdr
->ah_reserve
= htons(0);
446 memset(ahdr
+ 1, 0, plen
);
448 struct newah
*ahdr
= mtod(mah
, struct newah
*);
450 plen
= mah
->m_len
- sizeof(struct newah
);
451 ahsumpos
= (u_char
*)(ahdr
+ 1);
452 ahdr
->ah_nxt
= *nexthdrp
;
453 *nexthdrp
= IPPROTO_AH
;
454 ahdr
->ah_len
= (plen
>> 2) + 1; /* plus one for seq# */
455 ahdr
->ah_reserve
= htons(0);
457 if (sav
->replay
->count
== ~0) {
458 if ((sav
->flags
& SADB_X_EXT_CYCSEQ
) == 0) {
459 /* XXX Is it noisy ? */
460 ipseclog((LOG_WARNING
,
461 "replay counter overflowed. %s\n",
462 ipsec_logsastr(sav
)));
463 IPSEC6_STATINC(IPSEC_STAT_OUT_INVAL
);
468 sav
->replay
->count
++;
470 * XXX sequence number must not be cycled, if the SA is
471 * installed by IKE daemon.
473 ahdr
->ah_seq
= htonl(sav
->replay
->count
);
474 memset(ahdr
+ 1, 0, plen
);
478 * calcurate the checksum, based on security association
479 * and the algorithm specified.
481 error
= ah6_calccksum(m
, ahsumpos
, plen
, algo
, sav
);
483 IPSEC6_STATINC(IPSEC_STAT_OUT_INVAL
);
486 IPSEC6_STATINC(IPSEC_STAT_OUT_SUCCESS
);
487 key_sa_recordxfer(sav
, m
);
489 IPSEC6_STATINC(IPSEC_STAT_OUT_AHHIST
+ sav
->alg_auth
);
502 * Find the final destination if there is loose/strict source routing option.
503 * Returns NULL if there's no source routing options.
504 * Returns NULL on errors too.
505 * Note that this function will return a pointer INTO the given parameter,
507 * The mbuf must be pulled up toward, at least, ip option part.
509 static struct in_addr
*
510 ah4_finaldst(struct mbuf
*m
)
519 panic("ah4_finaldst: m == NULL");
520 ip
= mtod(m
, struct ip
*);
521 hlen
= (ip
->ip_hl
<< 2);
523 if (m
->m_len
< hlen
) {
525 "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
529 if (hlen
== sizeof(struct ip
))
532 optlen
= hlen
- sizeof(struct ip
);
534 ipseclog((LOG_DEBUG
, "ah4_finaldst: wrong optlen %d\n",
539 q
= (u_char
*)(ip
+ 1);
542 if (i
+ IPOPT_OPTVAL
>= optlen
)
544 if (q
[i
+ IPOPT_OPTVAL
] == IPOPT_EOL
||
545 q
[i
+ IPOPT_OPTVAL
] == IPOPT_NOP
||
546 i
+ IPOPT_OLEN
< optlen
)
551 switch (q
[i
+ IPOPT_OPTVAL
]) {
553 i
= optlen
; /* bye */
560 if (q
[i
+ IPOPT_OLEN
] < 2 + sizeof(struct in_addr
) ||
561 optlen
- i
< q
[i
+ IPOPT_OLEN
]) {
563 "ip_finaldst: invalid IP option "
564 "(code=%02x len=%02x)\n",
565 q
[i
+ IPOPT_OPTVAL
], q
[i
+ IPOPT_OLEN
]));
568 i
+= q
[i
+ IPOPT_OLEN
] - sizeof(struct in_addr
);
569 return (struct in_addr
*)(q
+ i
);
571 if (q
[i
+ IPOPT_OLEN
] < 2 ||
572 optlen
- i
< q
[i
+ IPOPT_OLEN
]) {
574 "ip_finaldst: invalid IP option "
575 "(code=%02x len=%02x)\n",
576 q
[i
+ IPOPT_OPTVAL
], q
[i
+ IPOPT_OLEN
]));
579 i
+= q
[i
+ IPOPT_OLEN
];