diff options
author | Hajimu UMEMOTO <ume@FreeBSD.org> | 2001-06-11 12:39:29 +0000 |
---|---|---|
committer | Hajimu UMEMOTO <ume@FreeBSD.org> | 2001-06-11 12:39:29 +0000 |
commit | 33841545909f4a4ee94aa148b3a9cbcdc1abb02a (patch) | |
tree | a79fc7ad2b97862c4a404f352f0211ad93a7b5f1 /sys | |
parent | 52ebde4fbaab8a8b79de6b17892943783abec7be (diff) |
Sync with recent KAME.
This work was based on kame-20010528-freebsd43-snap.tgz and some
critical problem after the snap was out were fixed.
There are many many changes since last KAME merge.
TODO:
- The definitions of SADB_* in sys/net/pfkeyv2.h are still different
from RFC2407/IANA assignment because of binary compatibility
issue. It should be fixed under 5-CURRENT.
- ip6po_m member of struct ip6_pktopts is no longer used. But, it
is still there because of binary compatibility issue. It should
be removed under 5-CURRENT.
Reviewed by: itojun
Obtained from: KAME
MFC after: 3 weeks
Notes
Notes:
svn path=/head/; revision=78064
Diffstat (limited to 'sys')
137 files changed, 13173 insertions, 8649 deletions
diff --git a/sys/conf/files b/sys/conf/files index cb6a4a228ae3..d39e8292f6d6 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -163,19 +163,15 @@ contrib/ipfilter/netinet/ip_nat.c optional ipfilter inet contrib/ipfilter/netinet/ip_proxy.c optional ipfilter inet contrib/ipfilter/netinet/ip_state.c optional ipfilter inet contrib/ipfilter/netinet/mlfk_ipl.c optional ipfilter inet -crypto/blowfish/bf_cbc.c optional ipsec ipsec_esp -crypto/blowfish/bf_cbc_m.c optional ipsec ipsec_esp crypto/blowfish/bf_enc.c optional ipsec ipsec_esp crypto/blowfish/bf_skey.c optional ipsec ipsec_esp crypto/cast128/cast128.c optional ipsec ipsec_esp -crypto/cast128/cast128_cbc.c optional ipsec ipsec_esp -crypto/des/des_3cbc.c optional ipsec ipsec_esp -crypto/des/des_cbc.c optional ipsec ipsec_esp crypto/des/des_ecb.c optional ipsec ipsec_esp crypto/des/des_setkey.c optional ipsec ipsec_esp -crypto/rc5/rc5.c optional ipsec ipsec_esp -crypto/rc5/rc5_cbc.c optional ipsec ipsec_esp +crypto/rijndael/rijndael-alg-fst.c optional ipsec ipsec_esp +crypto/rijndael/rijndael-api-fst.c optional ipsec ipsec_esp crypto/sha1.c optional ipsec +crypto/sha2/sha2.c optional ipsec ddb/db_access.c optional ddb ddb/db_break.c optional ddb ddb/db_command.c optional ddb @@ -1083,6 +1079,7 @@ netinet6/dest6.c optional inet6 netinet6/esp_core.c optional ipsec ipsec_esp netinet6/esp_input.c optional ipsec ipsec_esp netinet6/esp_output.c optional ipsec ipsec_esp +netinet6/esp_rijndael.c optional ipsec ipsec_esp netinet6/frag6.c optional inet6 netinet6/icmp6.c optional inet6 netinet6/in6.c optional inet6 @@ -1124,8 +1121,8 @@ netipx/ipx_usrreq.c optional ipx netipx/spx_debug.c optional ipx netipx/spx_usrreq.c optional ipx netkey/key.c optional ipsec -netkey/keydb.c optional ipsec netkey/key_debug.c optional ipsec +netkey/keydb.c optional ipsec netkey/keysock.c optional ipsec netnatm/natm.c optional natm netnatm/natm_pcb.c optional natm diff --git a/sys/crypto/blowfish/bf_cbc.c b/sys/crypto/blowfish/bf_cbc.c deleted file mode 100644 index 6eb6d3b0c0d2..000000000000 --- a/sys/crypto/blowfish/bf_cbc.c +++ /dev/null @@ -1,151 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: bf_cbc.c,v 1.3 2000/03/27 04:36:25 sumikawa Exp $ */ - -/* crypto/bf/bf_cbc.c */ -/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@mincom.oz.au). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@mincom.oz.au). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@mincom.oz.au)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ - -#include <crypto/blowfish/blowfish.h> -#include <crypto/blowfish/bf_locl.h> - -void BF_cbc_encrypt(in, out, length, ks, iv, encrypt) -unsigned char *in; -unsigned char *out; -long length; -BF_KEY *ks; -unsigned char *iv; -int encrypt; - { - register BF_LONG tin0,tin1; - register BF_LONG tout0,tout1,xor0,xor1; - register long l=length; - BF_LONG tin[2]; - - if (encrypt) - { - n2l(iv,tout0); - n2l(iv,tout1); - iv-=8; - for (l-=8; l>=0; l-=8) - { - n2l(in,tin0); - n2l(in,tin1); - tin0^=tout0; - tin1^=tout1; - tin[0]=tin0; - tin[1]=tin1; - BF_encrypt(tin,ks,BF_ENCRYPT); - tout0=tin[0]; - tout1=tin[1]; - l2n(tout0,out); - l2n(tout1,out); - } - if (l != -8) - { - n2ln(in,tin0,tin1,l+8); - tin0^=tout0; - tin1^=tout1; - tin[0]=tin0; - tin[1]=tin1; - BF_encrypt(tin,ks,BF_ENCRYPT); - tout0=tin[0]; - tout1=tin[1]; - l2n(tout0,out); - l2n(tout1,out); - } - l2n(tout0,iv); - l2n(tout1,iv); - } - else - { - n2l(iv,xor0); - n2l(iv,xor1); - iv-=8; - for (l-=8; l>=0; l-=8) - { - n2l(in,tin0); - n2l(in,tin1); - tin[0]=tin0; - tin[1]=tin1; - BF_encrypt(tin,ks,BF_DECRYPT); - tout0=tin[0]^xor0; - tout1=tin[1]^xor1; - l2n(tout0,out); - l2n(tout1,out); - xor0=tin0; - xor1=tin1; - } - if (l != -8) - { - n2l(in,tin0); - n2l(in,tin1); - tin[0]=tin0; - tin[1]=tin1; - BF_encrypt(tin,ks,BF_DECRYPT); - tout0=tin[0]^xor0; - tout1=tin[1]^xor1; - l2nn(tout0,tout1,out,l+8); - xor0=tin0; - xor1=tin1; - } - l2n(xor0,iv); - l2n(xor1,iv); - } - tin0=tin1=tout0=tout1=xor0=xor1=0; - tin[0]=tin[1]=0; - } - diff --git a/sys/crypto/blowfish/bf_cbc_m.c b/sys/crypto/blowfish/bf_cbc_m.c deleted file mode 100644 index 088adad1441c..000000000000 --- a/sys/crypto/blowfish/bf_cbc_m.c +++ /dev/null @@ -1,343 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: bf_cbc_m.c,v 1.4 2000/06/14 10:41:16 itojun Exp $ */ - -/* - * heavily modified to accept mbuf, by Jun-ichiro itojun Itoh - * <itojun@itojun.org>, 1997. - */ -/* crypto/bf/bf_cbc.c */ -/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@mincom.oz.au). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@mincom.oz.au). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@mincom.oz.au)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ - -#include <sys/param.h> -#include <sys/mbuf.h> -#include <sys/systm.h> - -#include <crypto/blowfish/blowfish.h> -#include <crypto/blowfish/bf_locl.h> - -#define panic(x) do { printf(x); return EINVAL; } while (0) - -int BF_cbc_encrypt_m(m0, skip, length, key, iv, mode) - struct mbuf *m0; - int skip; - int length; - BF_KEY *key; - unsigned char *iv; - int mode; -{ - u_int8_t inbuf[8], outbuf[8]; - struct mbuf *m; - size_t off; - register BF_LONG tin0, tin1; - register BF_LONG tout0, tout1; - BF_LONG tin[2]; - - /* sanity checks */ - if (m0->m_pkthdr.len < skip) { - printf("mbuf length < skip\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < length) { - printf("mbuf length < encrypt length\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < skip + length) { - printf("mbuf length < skip + encrypt length\n"); - return EINVAL; - } - if (length % 8) { - printf("length is not multiple of 8\n"); - return EINVAL; - } - - m = m0; - off = 0; - - /* skip over the header */ - while (skip) { - if (!m) - panic("mbuf chain?\n"); - if (m->m_len <= skip) { - skip -= m->m_len; - m = m->m_next; - off = 0; - } else { - off = skip; - skip = 0; - } - } - - /* initialize */ - tin0 = tin1 = tout0 = tout1 = 0; - tin[0] = tin[1] = 0; - - if (mode == BF_ENCRYPT) { - u_int8_t *in, *out; - - n2l(iv, tout0); - n2l(iv, tout1); - - while (0 < length) { - if (!m) - panic("mbuf chain?\n"); - - /* - * copy the source into input buffer. - * don't update off or m, since we need to use them * later. - */ - if (off + 8 <= m->m_len) - bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); - else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *in; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - in = &inbuf[0]; - while (in - &inbuf[0] < 8) { - if (!p) - panic("mbuf chain?\n"); - - *in++ = *p++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && ! n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - } - - in = &inbuf[0]; - out = &outbuf[0]; - n2l(in, tin0); - n2l(in, tin1); - - tin0 ^= tout0; tin[0] = tin0; - tin1 ^= tout1; tin[1] = tin1; - BF_encrypt(tin, key, BF_ENCRYPT); - tout0 = tin[0]; l2n(tout0, out); - tout1 = tin[1]; l2n(tout1, out); - - /* - * copy the output buffer into the result. - * need to update off and m. - */ - if (off + 8 < m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - off += 8; - } else if (off + 8 == m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - do { - m = m->m_next; - } while (m && ! m->m_len); - off = 0; - } else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *out; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - out = &outbuf[0]; - while (out - &outbuf[0] < 8) { - if (!p) - panic("mbuf chain?"); - *p++ = *out++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && ! n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - - m = n; - off = noff; - } - - length -= 8; - } - } else if (mode == BF_DECRYPT) { - register BF_LONG xor0, xor1; - u_int8_t *in, *out; - - xor0 = xor1 = 0; - n2l(iv, xor0); - n2l(iv, xor1); - - while (0 < length) { - if (!m) - panic("mbuf chain?\n"); - - /* - * copy the source into input buffer. - * don't update off or m, since we need to use them * later. - */ - if (off + 8 <= m->m_len) - bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); - else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *in; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - in = &inbuf[0]; - while (in - &inbuf[0] < 8) { - if (!p) - panic("mbuf chain?\n"); - *in++ = *p++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && ! n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - } - - in = &inbuf[0]; - out = &outbuf[0]; - n2l(in, tin0); tin[0] = tin0; - n2l(in, tin1); tin[1] = tin1; - BF_encrypt(tin, key, BF_DECRYPT); - tout0 = tin[0] ^ xor0; - tout1 = tin[1] ^ xor1; - l2n(tout0, out); - l2n(tout1, out); - xor0 = tin0; - xor1 = tin1; - - - /* - * copy the output buffer into the result. - * need to update off and m. - */ - if (off + 8 < m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - off += 8; - } else if (off + 8 == m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - do { - m = m->m_next; - } while (m && ! m->m_len); - off = 0; - } else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *out; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - out = &outbuf[0]; - while (out - &outbuf[0] < 8) { - if (!p) - panic("mbuf chain?\n"); - *p++ = *out++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && ! n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - - m = n; - off = noff; - } - - length -= 8; - } - } - - return 0; -} diff --git a/sys/crypto/blowfish/bf_enc.c b/sys/crypto/blowfish/bf_enc.c index 6a3bef672295..5edd6db9048a 100644 --- a/sys/crypto/blowfish/bf_enc.c +++ b/sys/crypto/blowfish/bf_enc.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: bf_enc.c,v 1.3 2000/03/27 04:36:26 sumikawa Exp $ */ +/* $KAME: bf_enc.c,v 1.5 2000/09/18 21:21:19 itojun Exp $ */ /* crypto/bf/bf_enc.c */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) @@ -59,6 +59,7 @@ * [including the GNU Public Licence.] */ +#include <sys/types.h> #include <crypto/blowfish/blowfish.h> #include <crypto/blowfish/bf_locl.h> @@ -72,72 +73,71 @@ If you set BF_ROUNDS to some value other than 16 or 20, you will have to modify the code. #endif -void BF_encrypt(data,key,encrypt) -BF_LONG *data; -BF_KEY *key; -int encrypt; - { - register BF_LONG l,r,*p,*s; +/* XXX "data" is host endian */ +void +BF_encrypt(data, key, encrypt) + BF_LONG *data; + BF_KEY *key; + int encrypt; +{ + register BF_LONG l, r, *p, *s; - p=key->P; - s= &(key->S[0]); - l=data[0]; - r=data[1]; + p = key->P; + s= &key->S[0]; + l = data[0]; + r = data[1]; - if (encrypt) - { + if (encrypt) { l^=p[0]; - BF_ENC(r,l,s,p[ 1]); - BF_ENC(l,r,s,p[ 2]); - BF_ENC(r,l,s,p[ 3]); - BF_ENC(l,r,s,p[ 4]); - BF_ENC(r,l,s,p[ 5]); - BF_ENC(l,r,s,p[ 6]); - BF_ENC(r,l,s,p[ 7]); - BF_ENC(l,r,s,p[ 8]); - BF_ENC(r,l,s,p[ 9]); - BF_ENC(l,r,s,p[10]); - BF_ENC(r,l,s,p[11]); - BF_ENC(l,r,s,p[12]); - BF_ENC(r,l,s,p[13]); - BF_ENC(l,r,s,p[14]); - BF_ENC(r,l,s,p[15]); - BF_ENC(l,r,s,p[16]); + BF_ENC(r, l, s, p[ 1]); + BF_ENC(l, r, s, p[ 2]); + BF_ENC(r, l, s, p[ 3]); + BF_ENC(l, r, s, p[ 4]); + BF_ENC(r, l, s, p[ 5]); + BF_ENC(l, r, s, p[ 6]); + BF_ENC(r, l, s, p[ 7]); + BF_ENC(l, r, s, p[ 8]); + BF_ENC(r, l, s, p[ 9]); + BF_ENC(l, r, s, p[10]); + BF_ENC(r, l, s, p[11]); + BF_ENC(l, r, s, p[12]); + BF_ENC(r, l, s, p[13]); + BF_ENC(l, r, s, p[14]); + BF_ENC(r, l, s, p[15]); + BF_ENC(l, r, s, p[16]); #if BF_ROUNDS == 20 - BF_ENC(r,l,s,p[17]); - BF_ENC(l,r,s,p[18]); - BF_ENC(r,l,s,p[19]); - BF_ENC(l,r,s,p[20]); + BF_ENC(r, l, s, p[17]); + BF_ENC(l, r, s, p[18]); + BF_ENC(r, l, s, p[19]); + BF_ENC(l, r, s, p[20]); #endif - r^=p[BF_ROUNDS+1]; - } - else - { - l^=p[BF_ROUNDS+1]; + r ^= p[BF_ROUNDS + 1]; + } else { + l ^= p[BF_ROUNDS + 1]; #if BF_ROUNDS == 20 - BF_ENC(r,l,s,p[20]); - BF_ENC(l,r,s,p[19]); - BF_ENC(r,l,s,p[18]); - BF_ENC(l,r,s,p[17]); + BF_ENC(r, l, s, p[20]); + BF_ENC(l, r, s, p[19]); + BF_ENC(r, l, s, p[18]); + BF_ENC(l, r, s, p[17]); #endif - BF_ENC(r,l,s,p[16]); - BF_ENC(l,r,s,p[15]); - BF_ENC(r,l,s,p[14]); - BF_ENC(l,r,s,p[13]); - BF_ENC(r,l,s,p[12]); - BF_ENC(l,r,s,p[11]); - BF_ENC(r,l,s,p[10]); - BF_ENC(l,r,s,p[ 9]); - BF_ENC(r,l,s,p[ 8]); - BF_ENC(l,r,s,p[ 7]); - BF_ENC(r,l,s,p[ 6]); - BF_ENC(l,r,s,p[ 5]); - BF_ENC(r,l,s,p[ 4]); - BF_ENC(l,r,s,p[ 3]); - BF_ENC(r,l,s,p[ 2]); - BF_ENC(l,r,s,p[ 1]); - r^=p[0]; - } - data[1]=l&0xffffffff; - data[0]=r&0xffffffff; + BF_ENC(r, l, s, p[16]); + BF_ENC(l, r, s, p[15]); + BF_ENC(r, l, s, p[14]); + BF_ENC(l, r, s, p[13]); + BF_ENC(r, l, s, p[12]); + BF_ENC(l, r, s, p[11]); + BF_ENC(r, l, s, p[10]); + BF_ENC(l, r, s, p[ 9]); + BF_ENC(r, l, s, p[ 8]); + BF_ENC(l, r, s, p[ 7]); + BF_ENC(r, l, s, p[ 6]); + BF_ENC(l, r, s, p[ 5]); + BF_ENC(r, l, s, p[ 4]); + BF_ENC(l, r, s, p[ 3]); + BF_ENC(r, l, s, p[ 2]); + BF_ENC(l, r, s, p[ 1]); + r ^= p[0]; } + data[1] = l & 0xffffffff; + data[0] = r & 0xffffffff; +} diff --git a/sys/crypto/blowfish/bf_locl.h b/sys/crypto/blowfish/bf_locl.h index 07598d2ed255..52585bb1ee4b 100644 --- a/sys/crypto/blowfish/bf_locl.h +++ b/sys/crypto/blowfish/bf_locl.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: bf_locl.h,v 1.3 2000/03/27 04:36:26 sumikawa Exp $ */ +/* $KAME: bf_locl.h,v 1.5 2000/08/31 06:03:48 itojun Exp $ */ /* crypto/bf/bf_local.h */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) @@ -67,10 +67,10 @@ */ #undef c2l -#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ - l|=((unsigned long)(*((c)++)))<< 8L, \ - l|=((unsigned long)(*((c)++)))<<16L, \ - l|=((unsigned long)(*((c)++)))<<24L) +#define c2l(c,l) (l =((BF_LONG)(*((c)++))) , \ + l|=((BF_LONG)(*((c)++)))<< 8L, \ + l|=((BF_LONG)(*((c)++)))<<16L, \ + l|=((BF_LONG)(*((c)++)))<<24L) /* NOTE - c is not incremented as per c2l */ #undef c2ln @@ -78,14 +78,14 @@ c+=n; \ l1=l2=0; \ switch (n) { \ - case 8: l2 =((unsigned long)(*(--(c))))<<24L; \ - case 7: l2|=((unsigned long)(*(--(c))))<<16L; \ - case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \ - case 5: l2|=((unsigned long)(*(--(c)))); \ - case 4: l1 =((unsigned long)(*(--(c))))<<24L; \ - case 3: l1|=((unsigned long)(*(--(c))))<<16L; \ - case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \ - case 1: l1|=((unsigned long)(*(--(c)))); \ + case 8: l2 =((BF_LONG)(*(--(c))))<<24L; \ + case 7: l2|=((BF_LONG)(*(--(c))))<<16L; \ + case 6: l2|=((BF_LONG)(*(--(c))))<< 8L; \ + case 5: l2|=((BF_LONG)(*(--(c)))); \ + case 4: l1 =((BF_LONG)(*(--(c))))<<24L; \ + case 3: l1|=((BF_LONG)(*(--(c))))<<16L; \ + case 2: l1|=((BF_LONG)(*(--(c))))<< 8L; \ + case 1: l1|=((BF_LONG)(*(--(c)))); \ } \ } @@ -116,14 +116,14 @@ c+=n; \ l1=l2=0; \ switch (n) { \ - case 8: l2 =((unsigned long)(*(--(c)))) ; \ - case 7: l2|=((unsigned long)(*(--(c))))<< 8; \ - case 6: l2|=((unsigned long)(*(--(c))))<<16; \ - case 5: l2|=((unsigned long)(*(--(c))))<<24; \ - case 4: l1 =((unsigned long)(*(--(c)))) ; \ - case 3: l1|=((unsigned long)(*(--(c))))<< 8; \ - case 2: l1|=((unsigned long)(*(--(c))))<<16; \ - case 1: l1|=((unsigned long)(*(--(c))))<<24; \ + case 8: l2 =((BF_LONG)(*(--(c)))) ; \ + case 7: l2|=((BF_LONG)(*(--(c))))<< 8; \ + case 6: l2|=((BF_LONG)(*(--(c))))<<16; \ + case 5: l2|=((BF_LONG)(*(--(c))))<<24; \ + case 4: l1 =((BF_LONG)(*(--(c)))) ; \ + case 3: l1|=((BF_LONG)(*(--(c))))<< 8; \ + case 2: l1|=((BF_LONG)(*(--(c))))<<16; \ + case 1: l1|=((BF_LONG)(*(--(c))))<<24; \ } \ } @@ -143,10 +143,10 @@ } #undef n2l -#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \ - l|=((unsigned long)(*((c)++)))<<16L, \ - l|=((unsigned long)(*((c)++)))<< 8L, \ - l|=((unsigned long)(*((c)++)))) +#define n2l(c,l) (l =((BF_LONG)(*((c)++)))<<24L, \ + l|=((BF_LONG)(*((c)++)))<<16L, \ + l|=((BF_LONG)(*((c)++)))<< 8L, \ + l|=((BF_LONG)(*((c)++)))) #undef l2n #define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ @@ -161,9 +161,17 @@ * BF_PTR for sparc and MIPS/SGI * use nothing for Alpha and HP. */ -#if !defined(BF_PTR) && !defined(BF_PTR2) -#undef BF_PTR +#undef BF_PTR +#undef BF_PTR2 +#ifdef __NetBSD__ +#ifdef __i386__ +#define BF_PTR2 +#else +#ifdef __mips__ +#define BF_PTR +#endif #endif +#endif /*NetBSD*/ #define BF_M 0x3fc #define BF_0 22L diff --git a/sys/crypto/blowfish/bf_skey.c b/sys/crypto/blowfish/bf_skey.c index 5717c3f59547..4bbe036b78c9 100644 --- a/sys/crypto/blowfish/bf_skey.c +++ b/sys/crypto/blowfish/bf_skey.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: bf_skey.c,v 1.3 2000/03/27 04:36:27 sumikawa Exp $ */ +/* $KAME: bf_skey.c,v 1.5 2000/11/06 13:58:08 itojun Exp $ */ /* crypto/bf/bf_skey.c */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) @@ -66,58 +66,55 @@ #include <crypto/blowfish/bf_locl.h> #include <crypto/blowfish/bf_pi.h> -void BF_set_key(key,len,data) -BF_KEY *key; -int len; -unsigned char *data; - { +void +BF_set_key(key, len, data) + BF_KEY *key; + int len; + unsigned char *data; +{ int i; - BF_LONG *p,ri,in[2]; - unsigned char *d,*end; + BF_LONG *p, ri, in[2]; + unsigned char *d, *end; + memcpy((char *)key, (char *)&bf_init, sizeof(BF_KEY)); + p = key->P; - memcpy((char *)key,(char *)&bf_init,sizeof(BF_KEY)); - p=key->P; + if (len > ((BF_ROUNDS + 2) * 4)) + len = (BF_ROUNDS + 2) * 4; - if (len > ((BF_ROUNDS+2)*4)) len=(BF_ROUNDS+2)*4; - - d=data; + d = data; end= &(data[len]); - for (i=0; i<(BF_ROUNDS+2); i++) - { - ri= *(d++); - if (d >= end) d=data; - - ri<<=8; - ri|= *(d++); - if (d >= end) d=data; + for (i = 0; i < BF_ROUNDS + 2; i++) { + ri = *(d++); + if (d >= end) d = data; - ri<<=8; - ri|= *(d++); - if (d >= end) d=data; + ri <<= 8; + ri |= *(d++); + if (d >= end) d = data; - ri<<=8; - ri|= *(d++); - if (d >= end) d=data; + ri <<= 8; + ri |= *(d++); + if (d >= end) d = data; - p[i]^=ri; - } + ri <<= 8; + ri |= *(d++); + if (d >= end) d = data; - in[0]=0L; - in[1]=0L; - for (i=0; i<(BF_ROUNDS+2); i+=2) - { - BF_encrypt(in,key,BF_ENCRYPT); - p[i ]=in[0]; - p[i+1]=in[1]; - } + p[i] ^= ri; + } - p=key->S; - for (i=0; i<4*256; i+=2) - { - BF_encrypt(in,key,BF_ENCRYPT); - p[i ]=in[0]; - p[i+1]=in[1]; - } + in[0] = 0L; + in[1] = 0L; + for (i = 0; i < BF_ROUNDS + 2; i += 2) { + BF_encrypt(in, key, BF_ENCRYPT); + p[i ] = in[0]; + p[i+1] = in[1]; } + p = key->S; + for (i = 0; i < 4 * 256; i += 2) { + BF_encrypt(in, key, BF_ENCRYPT); + p[i ] = in[0]; + p[i+1] = in[1]; + } +} diff --git a/sys/crypto/blowfish/blowfish.h b/sys/crypto/blowfish/blowfish.h index c96b4ec08bb0..76605f8d0e6f 100644 --- a/sys/crypto/blowfish/blowfish.h +++ b/sys/crypto/blowfish/blowfish.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: blowfish.h,v 1.4 2000/06/14 10:41:16 itojun Exp $ */ +/* $KAME: blowfish.h,v 1.10 2000/09/18 21:21:20 itojun Exp $ */ /* crypto/bf/blowfish.h */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) @@ -69,54 +69,19 @@ extern "C" { #define BF_ENCRYPT 1 #define BF_DECRYPT 0 -/* If you make this 'unsigned int' the pointer variants will work on - * the Alpha, otherwise they will not. Strangly using the '8 byte' - * BF_LONG and the default 'non-pointer' inner loop is the best configuration - * for the Alpha */ -#define BF_LONG unsigned long +/* must be 32bit quantity */ +#define BF_LONG u_int32_t #define BF_ROUNDS 16 #define BF_BLOCK 8 -typedef struct bf_key_st - { +typedef struct bf_key_st { BF_LONG P[BF_ROUNDS+2]; BF_LONG S[4*256]; - } BF_KEY; - -#ifndef NOPROTO - -void BF_set_key(BF_KEY *key, int len, unsigned char *data); -void BF_ecb_encrypt(unsigned char *in,unsigned char *out,BF_KEY *key, - int encrypt); -void BF_encrypt(BF_LONG *data,BF_KEY *key,int encrypt); -void BF_cbc_encrypt(unsigned char *in, unsigned char *out, long length, - BF_KEY *ks, unsigned char *iv, int encrypt); -void BF_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, - BF_KEY *schedule, unsigned char *ivec, int *num, int encrypt); -void BF_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, - BF_KEY *schedule, unsigned char *ivec, int *num); -char *BF_options(void); - -/* added by itojun */ -struct mbuf; -int BF_cbc_encrypt_m(struct mbuf *, int, int, BF_KEY *, unsigned char *, int); - -#else - -void BF_set_key(); -void BF_ecb_encrypt(); -void BF_encrypt(); -void BF_cbc_encrypt(); -void BF_cfb64_encrypt(); -void BF_ofb64_encrypt(); -char *BF_options(); - -/* added by itojun */ -void BF_cbc_encrypt_m(); - -#endif +} BF_KEY; +void BF_set_key __P((BF_KEY *, int, unsigned char *)); +void BF_encrypt __P((BF_LONG *, BF_KEY *, int)); #ifdef __cplusplus } #endif diff --git a/sys/crypto/cast128/cast128.c b/sys/crypto/cast128/cast128.c index 4df1be900f62..88873f2e5f71 100644 --- a/sys/crypto/cast128/cast128.c +++ b/sys/crypto/cast128/cast128.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: cast128.c,v 1.3 2000/03/27 04:36:29 sumikawa Exp $ */ +/* $KAME: cast128.c,v 1.4 2000/11/06 13:58:08 itojun Exp $ */ /* * heavily modified by Tomomi Suzuki <suzuki@grelot.elec.ryukoku.ac.jp> diff --git a/sys/crypto/cast128/cast128.h b/sys/crypto/cast128/cast128.h index 019c2de1306d..4057a1f4673a 100644 --- a/sys/crypto/cast128/cast128.h +++ b/sys/crypto/cast128/cast128.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: cast128.h,v 1.4 2000/06/14 10:41:16 itojun Exp $ */ +/* $KAME: cast128.h,v 1.6 2000/09/18 20:59:20 itojun Exp $ */ /* * heavily modified by Tomomi Suzuki <suzuki@grelot.elec.ryukoku.ac.jp> @@ -40,7 +40,6 @@ #define RFC2144_CAST_128_H #include <sys/param.h> -#include <sys/mbuf.h> #define CAST128_ENCRYPT 1 @@ -56,8 +55,5 @@ extern void cast128_encrypt_round12 __P((u_int8_t *, const u_int8_t *, u_int32_t *)); extern void cast128_decrypt_round12 __P((u_int8_t *, const u_int8_t *, u_int32_t *)); -extern int cast128_cbc_process __P((struct mbuf *, size_t, size_t, - u_int32_t *, u_int8_t *, size_t, int)); - #endif diff --git a/sys/crypto/cast128/cast128_cbc.c b/sys/crypto/cast128/cast128_cbc.c deleted file mode 100644 index e4725a945400..000000000000 --- a/sys/crypto/cast128/cast128_cbc.c +++ /dev/null @@ -1,222 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: cast128_cbc.c,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ - -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* - * based on sys/crypto/des/des_cbc.c, rewrote by Tomomi Suzuki - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/mbuf.h> -#include <crypto/cast128/cast128.h> - -#define panic(x) do { printf(x); return EINVAL; } while (0) - -int -cast128_cbc_process(m0, skip, length, subkey, iv, keylen, mode) - struct mbuf *m0; - size_t skip; - size_t length; - u_int32_t *subkey; - u_int8_t *iv; - size_t keylen; - int mode; -{ - struct mbuf *m; - u_int8_t inbuf[8], outbuf[8]; - size_t off; - - /* sanity check */ - if (m0->m_pkthdr.len < skip) { - printf("cast128_cbc_process: mbuf length < skip\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < length) { - printf("cast128_cbc_process: mbuf length < encrypt length\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < skip + length) { - printf("cast128_cbc_process: " - "mbuf length < skip + encrypt length\n"); - return EINVAL; - } - if (length % 8) { - printf("cast128_cbc_process: length is not multiple of 8\n"); - return EINVAL; - } - - m = m0; - off = 0; - - /* skip over the header */ - while (skip) { - if (!m) - panic("cast128_cbc_process: mbuf chain?\n"); - if (m->m_len <= skip) { - skip -= m->m_len; - m = m->m_next; - off = 0; - } else { - off = skip; - skip = 0; - } - } - - /* copy iv into outbuf for XOR (encrypt) */ - bcopy(iv, outbuf, 8); - - /* - * encrypt/decrypt packet - */ - while (length > 0) { - int i; - - if (!m) - panic("cast128_cbc_process: mbuf chain?\n"); - - /* - * copy the source into input buffer. - * don't update off or m, since we need to use them - * later. - */ - if (off + 8 <= m->m_len) - bcopy(mtod(m, u_int8_t *)+off, inbuf, 8); - else { - struct mbuf *n; - size_t noff; - u_int8_t *p, *in; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - in = inbuf; - while (in - inbuf < 8) { - if (!p) { - panic("cast128_cbc_process: " - "mbuf chain?\n"); - } - *in++ = *p++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && !n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *); - else - p = NULL; - } - } - - /* encrypt/decrypt */ - switch (mode) { - case CAST128_ENCRYPT: - /* XOR */ - for (i = 0; i < 8; i++) - inbuf[i] ^= outbuf[i]; - - /* encrypt */ - if (keylen <= 80/8) - cast128_encrypt_round12(outbuf, inbuf, subkey); - else - cast128_encrypt_round16(outbuf, inbuf, subkey); - break; - - case CAST128_DECRYPT: - /* decrypt */ - if (keylen <= 80/8) - cast128_decrypt_round12(outbuf, inbuf, subkey); - else - cast128_decrypt_round16(outbuf, inbuf, subkey); - - /* XOR */ - for (i = 0; i < 8; i++) - outbuf[i] ^= iv[i]; - - /* copy inbuf into iv for next XOR */ - bcopy(inbuf, iv, 8); - break; - } - - /* - * copy the output buffer into the result. - * need to update off and m. - */ - if (off + 8 < m->m_len) { - bcopy(outbuf, mtod(m, u_int8_t *) + off, 8); - off += 8; - } else if (off + 8 == m->m_len) { - bcopy(outbuf, mtod(m, u_int8_t *) + off, 8); - do { - m = m->m_next; - } while (m && !m->m_len); - off = 0; - } else { - struct mbuf *n; - size_t noff; - u_int8_t *p, *out; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - out = outbuf; - while (out - outbuf < 8) { - if (!p) { - panic("cast128_cbc_process: " - "mbuf chain?\n"); - } - *p++ = *out++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && !n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *); - else - p = NULL; - } - - m = n; - off = noff; - } - - length -= 8; - } - - return 0; -} diff --git a/sys/crypto/des/des.h b/sys/crypto/des/des.h index 536f0c992f7c..c21f972fb348 100644 --- a/sys/crypto/des/des.h +++ b/sys/crypto/des/des.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: des.h,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ +/* $KAME: des.h,v 1.7 2000/09/18 20:59:21 itojun Exp $ */ /* lib/des/des.h */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) @@ -55,11 +55,8 @@ extern "C" { #endif -/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a - * %20 speed up (longs are 8 bytes, int's are 4). */ -#ifndef DES_LONG -#define DES_LONG unsigned long -#endif +/* must be 32bit quantity */ +#define DES_LONG u_int32_t typedef unsigned char des_cblock[8]; typedef struct des_ks_struct @@ -83,196 +80,18 @@ typedef struct des_ks_struct #define DES_CBC_MODE 0 #define DES_PCBC_MODE 1 -#define des_ecb2_encrypt(i,o,k1,k2,e) \ - des_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e)) - -#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ - des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e)) - -#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \ - des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e)) - -#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \ - des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n)) - -#define C_Block des_cblock -#define Key_schedule des_key_schedule -#ifdef KERBEROS -#define ENCRYPT DES_ENCRYPT -#define DECRYPT DES_DECRYPT -#endif -#define KEY_SZ DES_KEY_SZ -#define string_to_key des_string_to_key -#define read_pw_string des_read_pw_string -#define random_key des_random_key -#define pcbc_encrypt des_pcbc_encrypt -#define set_key des_set_key -#define key_sched des_key_sched -#define ecb_encrypt des_ecb_encrypt -#define cbc_encrypt des_cbc_encrypt -#define ncbc_encrypt des_ncbc_encrypt -#define xcbc_encrypt des_xcbc_encrypt -#define cbc_cksum des_cbc_cksum -#define quad_cksum des_quad_cksum - -/* For compatibility with the MIT lib - eay 20/05/92 */ -typedef des_key_schedule bit_64; -#define des_fixup_key_parity des_set_odd_parity -#define des_check_key_parity check_parity - extern int des_check_key; /* defaults to false */ -extern int des_rw_mode; /* defaults to DES_PCBC_MODE */ -/* The next line is used to disable full ANSI prototypes, if your - * compiler has problems with the prototypes, make sure this line always - * evaluates to true :-) */ -#if defined(MSDOS) || defined(__STDC__) -#undef NOPROTO -#endif -#ifndef NOPROTO -char *des_options(void); -void des_ecb3_encrypt(des_cblock *input,des_cblock *output, - des_key_schedule ks1,des_key_schedule ks2, - des_key_schedule ks3, int enc); -DES_LONG des_cbc_cksum(des_cblock *input,des_cblock *output, - long length,des_key_schedule schedule,des_cblock *ivec); -/* -void des_cbc_encrypt(des_cblock *input,des_cblock *output,long length, - des_key_schedule schedule,des_cblock *ivec,int enc); -*/ -int des_cbc_encrypt(struct mbuf *, size_t, size_t, - des_key_schedule schedule,des_cblock *ivec, int enc); -void des_ncbc_encrypt(des_cblock *input,des_cblock *output,long length, - des_key_schedule schedule,des_cblock *ivec,int enc); -void des_xcbc_encrypt(des_cblock *input,des_cblock *output,long length, - des_key_schedule schedule,des_cblock *ivec, - des_cblock *inw,des_cblock *outw,int enc); -void des_3cbc_encrypt(des_cblock *input,des_cblock *output,long length, - des_key_schedule sk1,des_key_schedule sk2, - des_cblock *ivec1,des_cblock *ivec2,int enc); -extern int des_3cbc_process(struct mbuf *, size_t, size_t, - des_key_schedule *schedule, des_cblock *ivec, int mode); -void des_cfb_encrypt(unsigned char *in,unsigned char *out,int numbits, - long length,des_key_schedule schedule,des_cblock *ivec,int enc); -void des_ecb_encrypt(des_cblock *input,des_cblock *output, - des_key_schedule ks,int enc); -void des_encrypt(DES_LONG *data,des_key_schedule ks, int enc); -void des_encrypt2(DES_LONG *data,des_key_schedule ks, int enc); -void des_ede3_cbc_encrypt(des_cblock *input, des_cblock *output, - long length, des_key_schedule ks1, des_key_schedule ks2, - des_key_schedule ks3, des_cblock *ivec, int enc); -void des_ede3_cfb64_encrypt(unsigned char *in, unsigned char *out, - long length, des_key_schedule ks1, des_key_schedule ks2, - des_key_schedule ks3, des_cblock *ivec, int *num, int encrypt); -void des_ede3_ofb64_encrypt(unsigned char *in, unsigned char *out, - long length, des_key_schedule ks1, des_key_schedule ks2, - des_key_schedule ks3, des_cblock *ivec, int *num); +char *des_options __P((void)); +void des_ecb_encrypt __P((des_cblock *, des_cblock *, + des_key_schedule, int)); +void des_encrypt __P((DES_LONG *, des_key_schedule, int)); +void des_encrypt2 __P((DES_LONG *, des_key_schedule, int)); -int des_enc_read(int fd,char *buf,int len,des_key_schedule sched, - des_cblock *iv); -int des_enc_write(int fd,char *buf,int len,des_key_schedule sched, - des_cblock *iv); -#ifdef PERL5 -char *des_crypt(const char *buf,const char *salt); -#else -/* some stupid compilers complain because I have declared char instead - * of const char */ -#if 1 -char *crypt(const char *buf,const char *salt); -#else -char *crypt(); -#endif -#endif -void des_ofb_encrypt(unsigned char *in,unsigned char *out, - int numbits,long length,des_key_schedule schedule,des_cblock *ivec); -void des_pcbc_encrypt(des_cblock *input,des_cblock *output,long length, - des_key_schedule schedule,des_cblock *ivec,int enc); -DES_LONG des_quad_cksum(des_cblock *input,des_cblock *output, - long length,int out_count,des_cblock *seed); -void des_random_seed(des_cblock key); -void des_random_key(des_cblock ret); -int des_read_password(des_cblock *key,char *prompt,int verify); -int des_read_2passwords(des_cblock *key1,des_cblock *key2, - char *prompt,int verify); -int des_read_pw_string(char *buf,int length,char *prompt,int verify); -void des_set_odd_parity(des_cblock *key); -int des_is_weak_key(des_cblock *key); -int des_set_key(des_cblock *key,des_key_schedule schedule); -int des_key_sched(des_cblock *key,des_key_schedule schedule); -void des_string_to_key(char *str,des_cblock *key); -void des_string_to_2keys(char *str,des_cblock *key1,des_cblock *key2); -void des_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, - des_key_schedule schedule, des_cblock *ivec, int *num, int enc); -void des_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, - des_key_schedule schedule, des_cblock *ivec, int *num); - -/* Extra functions from Mark Murray <mark@grondar.za> */ -/* -void des_cblock_print_file(des_cblock *cb, FILE *fp); -*/ -/* The following functions are not in the normal unix build or the - * SSLeay build. When using the SSLeay build, use RAND_seed() - * and RAND_bytes() instead. */ -int des_new_random_key(des_cblock *key); -void des_init_random_number_generator(des_cblock *key); -void des_set_random_generator_seed(des_cblock *key); -void des_set_sequence_number(des_cblock new_sequence_number); -void des_generate_random_block(des_cblock *block); - -#else - -char *des_options(); -void des_ecb3_encrypt(); -DES_LONG des_cbc_cksum(); -void des_cbc_encrypt(); -void des_ncbc_encrypt(); -void des_xcbc_encrypt(); -void des_3cbc_encrypt(); -void des_cfb_encrypt(); -void des_ede3_cfb64_encrypt(); -void des_ede3_ofb64_encrypt(); -void des_ecb_encrypt(); -void des_encrypt(); -void des_encrypt2(); -void des_ede3_cbc_encrypt(); -int des_enc_read(); -int des_enc_write(); -#ifdef PERL5 -char *des_crypt(); -#else -char *crypt(); -#endif -void des_ofb_encrypt(); -void des_pcbc_encrypt(); -DES_LONG des_quad_cksum(); -void des_random_seed(); -void des_random_key(); -int des_read_password(); -int des_read_2passwords(); -int des_read_pw_string(); -void des_set_odd_parity(); -int des_is_weak_key(); -int des_set_key(); -int des_key_sched(); -void des_string_to_key(); -void des_string_to_2keys(); -void des_cfb64_encrypt(); -void des_ofb64_encrypt(); - -/* Extra functions from Mark Murray <mark@grondar.za> */ -void des_cblock_print_file(); -/* The following functions are not in the normal unix build or the - * SSLeay build. When using the SSLeay build, use RAND_seed() - * and RAND_bytes() instead. */ -#ifdef FreeBSD -int des_new_random_key(); -void des_init_random_number_generator(); -void des_set_random_generator_seed(); -void des_set_sequence_number(); -void des_generate_random_block(); -#endif - -#endif +void des_set_odd_parity __P((des_cblock *)); +int des_is_weak_key __P((des_cblock *)); +int des_set_key __P((des_cblock *, des_key_schedule)); +int des_key_sched __P((des_cblock *, des_key_schedule)); #ifdef __cplusplus } diff --git a/sys/crypto/des/des_3cbc.c b/sys/crypto/des/des_3cbc.c deleted file mode 100644 index e6758718963f..000000000000 --- a/sys/crypto/des/des_3cbc.c +++ /dev/null @@ -1,250 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: des_3cbc.c,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ - -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* - * based on sys/crypto/des/des_cbc.c, rewrote by Tomomi Suzuki - */ -#include <crypto/des/des_locl.h> - -#define panic(x) do { printf(x); return EINVAL; } while (0) - -int des_3cbc_process(m0, skip, length, schedule, ivec, mode) - struct mbuf *m0; - size_t skip; - size_t length; - des_key_schedule *schedule; - des_cblock (*ivec); - int mode; -{ - u_int8_t inbuf[8], outbuf[8]; - struct mbuf *m; - size_t off; - DES_LONG tin0, tin1; - DES_LONG tout0, tout1; - DES_LONG tin[2]; - DES_LONG xor0 = 0, xor1 = 0; - u_int8_t *iv; - u_int8_t *in, *out; - - /* sanity check */ - if (m0->m_pkthdr.len < skip) { - printf("des_3cbc_process: mbuf length < skip\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < length) { - printf("des_3cbc_process: mbuf length < encrypt length\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < skip + length) { - printf("des_3cbc_process: mbuf length < " - "skip + encrypt length\n"); - return EINVAL; - } - if (length % 8) { - printf("des_3cbc_process: length(%lu) is not multiple of 8\n", - (u_long)length); - return EINVAL; - } - - m = m0; - off = 0; - - /* skip over the header */ - while (skip) { - if (!m) - panic("des_3cbc_process: mbuf chain?\n"); - if (m->m_len <= skip) { - skip -= m->m_len; - m = m->m_next; - off = 0; - } else { - off = skip; - skip = 0; - } - } - - /* initialize */ - tin0 = tin1 = tout0 = tout1 = 0; - tin[0] = tin[1] = 0; - - switch (mode) { - case DES_ENCRYPT: - iv = (u_int8_t *)ivec; - c2l(iv, tout0); - c2l(iv, tout1); - break; - case DES_DECRYPT: - xor0 = xor1 = 0; - iv = (u_int8_t *)ivec; - c2l(iv, xor0); - c2l(iv, xor1); - break; - } - - /* - * encrypt/decrypt packet - */ - while (length > 0) { - if (!m) - panic("des_3cbc_process: mbuf chain?\n"); - - /* - * copy the source into input buffer. - * don't update off or m, since we need to use them - * later. - */ - if (off + 8 <= m->m_len) - bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); - else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *in; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - in = &inbuf[0]; - while (in - &inbuf[0] < 8) { - if (!p) { - panic("des_3cbc_process: " - "mbuf chain?\n"); - } - *in++ = *p++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && !n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - } - - /* encrypt/decrypt */ - switch (mode) { - case DES_ENCRYPT: - in = &inbuf[0]; - out = &outbuf[0]; - c2l(in, tin0); - c2l(in, tin1); - - /* XOR */ - tin0 ^= tout0; tin[0] = tin0; - tin1 ^= tout1; tin[1] = tin1; - - des_encrypt((DES_LONG *)tin, schedule[0], DES_ENCRYPT); - des_encrypt((DES_LONG *)tin, schedule[1], DES_DECRYPT); - des_encrypt((DES_LONG *)tin, schedule[2], DES_ENCRYPT); - - tout0 = tin[0]; l2c(tout0, out); - tout1 = tin[1]; l2c(tout1, out); - break; - case DES_DECRYPT: - in = &inbuf[0]; - out = &outbuf[0]; - c2l(in, tin0); tin[0] = tin0; - c2l(in, tin1); tin[1] = tin1; - - des_encrypt((DES_LONG *)tin, schedule[2], DES_DECRYPT); - des_encrypt((DES_LONG *)tin, schedule[1], DES_ENCRYPT); - des_encrypt((DES_LONG *)tin, schedule[0], DES_DECRYPT); - - /* XOR */ - tout0 = tin[0] ^ xor0; - tout1 = tin[1] ^ xor1; - l2c(tout0, out); - l2c(tout1, out); - - /* for next iv */ - xor0 = tin0; - xor1 = tin1; - break; - } - - /* - * copy the output buffer int the result. - * need to update off and m. - */ - if (off + 8 < m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - off += 8; - } else if (off + 8 == m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - do { - m = m->m_next; - } while (m && !m->m_len); - off = 0; - } else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *out; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - out = &outbuf[0]; - while (out - &outbuf[0] < 8) { - if (!p) { - panic("des_3cbc_process: " - "mbuf chain?\n"); - } - *p++ = *out++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && !n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - - m = n; - off = noff; - } - - length -= 8; - } - - return 0; -} - diff --git a/sys/crypto/des/des_cbc.c b/sys/crypto/des/des_cbc.c deleted file mode 100644 index 92de8f89fd91..000000000000 --- a/sys/crypto/des/des_cbc.c +++ /dev/null @@ -1,331 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: des_cbc.c,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ - -/* - * heavily modified by Yoshifumi Nishida <nishida@sfc.wide.ad.jp>. - * then, completely rewrote by Jun-ichiro itojun Itoh <itojun@itojun.org>, - * 1997. - */ -/* crypto/des/cbc_enc.c */ -/* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) - * All rights reserved. - * - * This file is part of an SSL implementation written - * by Eric Young (eay@mincom.oz.au). - * The implementation was written so as to conform with Netscapes SSL - * specification. This library and applications are - * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE - * as long as the following conditions are aheared to. - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. If this code is used in a product, - * Eric Young should be given attribution as the author of the parts used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Eric Young (eay@mincom.oz.au) - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ - -#include <crypto/des/des_locl.h> - -#define panic(x) do {printf(x); return EINVAL;} while (0) - -int des_cbc_encrypt(m0, skip, length, schedule, ivec, mode) - struct mbuf *m0; - size_t skip; - size_t length; - des_key_schedule schedule; - des_cblock (*ivec); - int mode; -{ - u_int8_t inbuf[8], outbuf[8]; - struct mbuf *m; - size_t off; - register DES_LONG tin0, tin1; - register DES_LONG tout0, tout1; - DES_LONG tin[2]; - u_int8_t *iv; - - /* sanity checks */ - if (m0->m_pkthdr.len < skip) { - printf("mbuf length < skip\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < length) { - printf("mbuf length < encrypt length\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < skip + length) { - printf("mbuf length < skip + encrypt length\n"); - return EINVAL; - } - if (length % 8) { - printf("length is not multiple of 8\n"); - return EINVAL; - } - - m = m0; - off = 0; - - /* skip over the header */ - while (skip) { - if (!m) - panic("mbuf chain?\n"); - if (m->m_len <= skip) { - skip -= m->m_len; - m = m->m_next; - off = 0; - } else { - off = skip; - skip = 0; - } - } - - /* initialize */ - tin0 = tin1 = tout0 = tout1 = 0; - tin[0] = tin[1] = 0; - - if (mode == DES_ENCRYPT) { - u_int8_t *in, *out; - - iv = (u_int8_t *)ivec; - c2l(iv, tout0); - c2l(iv, tout1); - - while (0 < length) { - if (!m) - panic("mbuf chain?\n"); - - /* - * copy the source into input buffer. - * don't update off or m, since we need to use them * later. - */ - if (off + 8 <= m->m_len) - bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); - else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *in; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - in = &inbuf[0]; - while (in - &inbuf[0] < 8) { - if (!p) - panic("mbuf chain?\n"); - - *in++ = *p++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && ! n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - } - - in = &inbuf[0]; - out = &outbuf[0]; - c2l(in, tin0); - c2l(in, tin1); - - tin0 ^= tout0; tin[0] = tin0; - tin1 ^= tout1; tin[1] = tin1; - des_encrypt((DES_LONG *)tin, schedule, DES_ENCRYPT); - tout0 = tin[0]; l2c(tout0, out); - tout1 = tin[1]; l2c(tout1, out); - - /* - * copy the output buffer into the result. - * need to update off and m. - */ - if (off + 8 < m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - off += 8; - } else if (off + 8 == m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - do { - m = m->m_next; - } while (m && ! m->m_len); - off = 0; - } else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *out; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - out = &outbuf[0]; - while (out - &outbuf[0] < 8) { - if (!p) - panic("mbuf chain?"); - *p++ = *out++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && ! n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - - m = n; - off = noff; - } - - length -= 8; - } - } else if (mode == DES_DECRYPT) { - register DES_LONG xor0, xor1; - u_int8_t *in, *out; - - xor0 = xor1 = 0; - iv = (u_int8_t *)ivec; - c2l(iv, xor0); - c2l(iv, xor1); - - while (0 < length) { - if (!m) - panic("mbuf chain?\n"); - - /* - * copy the source into input buffer. - * don't update off or m, since we need to use them * later. - */ - if (off + 8 <= m->m_len) - bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); - else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *in; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - in = &inbuf[0]; - while (in - &inbuf[0] < 8) { - if (!p) - panic("mbuf chain?\n"); - *in++ = *p++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && ! n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - } - - in = &inbuf[0]; - out = &outbuf[0]; - c2l(in, tin0); tin[0] = tin0; - c2l(in, tin1); tin[1] = tin1; - des_encrypt((DES_LONG *)tin, schedule, DES_DECRYPT); - tout0 = tin[0] ^ xor0; - tout1 = tin[1] ^ xor1; - l2c(tout0, out); - l2c(tout1, out); - xor0 = tin0; - xor1 = tin1; - - - /* - * copy the output buffer into the result. - * need to update off and m. - */ - if (off + 8 < m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - off += 8; - } else if (off + 8 == m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - do { - m = m->m_next; - } while (m && ! m->m_len); - off = 0; - } else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *out; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - out = &outbuf[0]; - while (out - &outbuf[0] < 8) { - if (!p) - panic("mbuf chain?\n"); - *p++ = *out++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && ! n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - - m = n; - off = noff; - } - - length -= 8; - } - } - - return 0; -} diff --git a/sys/crypto/des/des_ecb.c b/sys/crypto/des/des_ecb.c index d828b915afbd..aa1b22b5ddb8 100644 --- a/sys/crypto/des/des_ecb.c +++ b/sys/crypto/des/des_ecb.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: des_ecb.c,v 1.3 2000/03/27 04:36:33 sumikawa Exp $ */ +/* $KAME: des_ecb.c,v 1.5 2000/11/06 13:58:08 itojun Exp $ */ /* crypto/des/ecb_enc.c */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) @@ -48,6 +48,8 @@ * [including the GNU Public Licence.] */ +#include <sys/param.h> +#include <sys/systm.h> #include <crypto/des/des_locl.h> #include <crypto/des/spr.h> diff --git a/sys/crypto/des/des_locl.h b/sys/crypto/des/des_locl.h index ae6e828dbdec..82486dc7ad84 100644 --- a/sys/crypto/des/des_locl.h +++ b/sys/crypto/des/des_locl.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: des_locl.h,v 1.4 2000/03/27 04:43:46 sumikawa Exp $ */ +/* $KAME: des_locl.h,v 1.6 2000/11/06 13:58:09 itojun Exp $ */ /* lib/des/des_locl.h */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) @@ -55,83 +55,17 @@ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */ -#include <sys/param.h> -#include <sys/malloc.h> -#include <sys/mbuf.h> -#include <sys/systm.h> - #ifndef HEADER_DES_LOCL_H #define HEADER_DES_LOCL_H -#if defined(WIN32) || defined(WIN16) -#ifndef MSDOS -#define MSDOS -#endif -#endif - -/* -#include <stdio.h> -#include <stdlib.h> -#ifndef MSDOS -#include <unistd.h> -#endif -*/ #include <crypto/des/des.h> -/* the following is tweaked from a config script, that is why it is a - * protected undef/define */ -#ifndef DES_PTR #undef DES_PTR -#endif - -#ifdef MSDOS /* Visual C++ 2.1 (Windows NT/95) */ -#include <stdlib.h> -#include <errno.h> -#include <time.h> -#include <io.h> -#ifndef RAND -#define RAND -#endif -#undef NOPROTO -#endif - -#if !defined(_KERNEL) && (defined(__STDC__) || defined(VMS) || defined(M_XENIX) || defined(MSDOS)) -#ifndef __NetBSD__ -#include <string.h> -#endif -#endif - -#ifdef __NetBSD__ -#include <sys/systm.h> -#endif - -#ifndef RAND -#define RAND -#endif - -#ifdef linux -#undef RAND -#endif - -#ifdef MSDOS -#define getpid() 2 -#define RAND -#undef NOPROTO -#endif - -#if defined(NOCONST) -#define const -#endif #ifdef __STDC__ #undef NOPROTO #endif -#ifdef RAND -#define srandom(s) srand(s) -#define random rand -#endif - #define ITERATIONS 16 #define HALF_ITERATIONS 8 @@ -194,11 +128,7 @@ } \ } -#if defined(WIN32) -#define ROTATE(a,n) (_lrotr(a,n)) -#else #define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n)))) -#endif /* The changes to this macro may help or hinder, depending on the * compiler and the achitecture. gcc2 always seems to do well :-). @@ -313,36 +243,3 @@ PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \ } #endif - - -/* -#define mbuf2char(i_mbuf, i_index, in) \ - { \ - register int i; \ - struct mbuf *m; \ - char *buf; \ - m = i_mbuf; \ - for (i = 0; i < 8; i ++){ \ - if (i_index + i == m->m_len){ \ - m = m->m_next; \ - } \ - buf = mtod(m, char *); \ - in[i] = *(buf + i); \ - } - - -#define char2mbuf(o_mbuf, o_index, out) \ - { \ - register int i; \ - struct mbuf *m; \ - char *buf; \ - m = o_mbuf; \ - for (i = 0; i < 8; i ++){ \ - if (i_index + i == m->m_len){ \ - m = m->m_next; \ - } \ - buf = mtod(m, char *); \ - *(buf + i) = out[i]; \ - } -*/ - diff --git a/sys/crypto/des/des_setkey.c b/sys/crypto/des/des_setkey.c index 48d13fcecc36..2ddf8bd0f279 100644 --- a/sys/crypto/des/des_setkey.c +++ b/sys/crypto/des/des_setkey.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: des_setkey.c,v 1.3 2000/03/27 04:36:33 sumikawa Exp $ */ +/* $KAME: des_setkey.c,v 1.5 2000/11/06 13:58:09 itojun Exp $ */ /* crypto/des/set_key.c */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) @@ -55,15 +55,13 @@ * 1.1 added norm_expand_bits * 1.0 First working version */ +#include <sys/param.h> +#include <sys/systm.h> #include <crypto/des/des_locl.h> #include <crypto/des/podd.h> #include <crypto/des/sk.h> -#ifndef NOPROTO -static int check_parity(des_cblock (*key)); -#else -static int check_parity(); -#endif +static int check_parity __P((des_cblock (*))); int des_check_key=0; diff --git a/sys/crypto/md5.c b/sys/crypto/md5.c index e82770089174..3351d419d521 100644 --- a/sys/crypto/md5.c +++ b/sys/crypto/md5.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: md5.c,v 1.4 2000/03/27 04:36:22 sumikawa Exp $ */ +/* $KAME: md5.c,v 1.5 2000/11/08 06:13:08 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. diff --git a/sys/crypto/rc5/rc5.c b/sys/crypto/rc5/rc5.c deleted file mode 100644 index 99a8ac6faf91..000000000000 --- a/sys/crypto/rc5/rc5.c +++ /dev/null @@ -1,219 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: rc5.c,v 1.3 2000/03/27 04:36:36 sumikawa Exp $ */ - -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#include <crypto/rc5/rc5.h> - - -void -set_rc5_expandkey(e_key, key, keylen, rounds) - RC5_WORD *e_key; - u_int8_t *key; - size_t keylen; - int rounds; -{ - int i, j, k, LL, t, T; - RC5_WORD L[256/WW]; - RC5_WORD A, B; - - LL = (keylen + WW - 1) / WW; - - bzero(L, sizeof(RC5_WORD)*LL); - - for (i = 0; i < keylen; i++) { - t = (key[i] & 0xff) << (8*(i%4)); - L[i/WW] = L[i/WW] + t; - } - - T = 2 * (rounds + 1); - e_key[0] = Pw; - for (i = 1; i < T; i++) - e_key[i] = e_key[i-1] + Qw; - - i = j = 0; - A = B = 0; - if (LL > T) - k = 3 * LL; - else - k = 3 * T; - - for (; k > 0; k--) { - A = ROTL(e_key[i]+A+B, 3, W); - e_key[i] = A; - B = ROTL(L[j]+A+B, A+B, W); - L[j] = B; - - i = (i + 1) % T; - j = (j + 1) % LL; - } -} - - -/* - * - */ -void -rc5_encrypt_round16(out, in, e_key) - u_int8_t *out; - const u_int8_t *in; - const RC5_WORD *e_key; -{ - RC5_WORD A, B; - const RC5_WORD *e_keyA, *e_keyB; - - A = in[0] & 0xff; - A += (in[1] & 0xff) << 8; - A += (in[2] & 0xff) << 16; - A += (in[3] & 0xff) << 24; - B = in[4] & 0xff; - B += (in[5] & 0xff) << 8; - B += (in[6] & 0xff) << 16; - B += (in[7] & 0xff) << 24; - - e_keyA = e_key; - e_keyB = e_key + 1; - - A += *e_keyA; e_keyA += 2; - B += *e_keyB; e_keyB += 2; - - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; /* round 4 */ - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; /* round 8 */ - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; /* round 12 */ - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; - A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; - B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; /* round 16 */ - - out[0] = A & 0xff; - out[1] = (A >> 8) & 0xff; - out[2] = (A >> 16) & 0xff; - out[3] = (A >> 24) & 0xff; - out[4] = B & 0xff; - out[5] = (B >> 8) & 0xff; - out[6] = (B >> 16) & 0xff; - out[7] = (B >> 24) & 0xff; -} - - -/* - * - */ -void -rc5_decrypt_round16(out, in, e_key) - u_int8_t *out; - const u_int8_t *in; - const RC5_WORD *e_key; -{ - RC5_WORD A, B; - const RC5_WORD *e_keyA, *e_keyB; - - A = in[0] & 0xff; - A += (in[1] & 0xff) << 8; - A += (in[2] & 0xff) << 16; - A += (in[3] & 0xff) << 24; - B = in[4] & 0xff; - B += (in[5] & 0xff) << 8; - B += (in[6] & 0xff) << 16; - B += (in[7] & 0xff) << 24; - - e_keyA = e_key + 2*16; - e_keyB = e_key + 2*16 + 1; - - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; /* round 4 */ - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; /* round 8 */ - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; /* round 12 */ - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; - B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; - A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; /* round 16 */ - - B = B - *e_keyB; - A = A - *e_keyA; - - out[0] = A & 0xff; - out[1] = (A >> 8) & 0xff; - out[2] = (A >> 16) & 0xff; - out[3] = (A >> 24) & 0xff; - out[4] = B & 0xff; - out[5] = (B >> 8) & 0xff; - out[6] = (B >> 16) & 0xff; - out[7] = (B >> 24) & 0xff; -} - diff --git a/sys/crypto/rc5/rc5_cbc.c b/sys/crypto/rc5/rc5_cbc.c deleted file mode 100644 index 5972cc61220b..000000000000 --- a/sys/crypto/rc5/rc5_cbc.c +++ /dev/null @@ -1,215 +0,0 @@ -/* $FreeBSD$ */ -/* $KAME: rc5_cbc.c,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ - -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* - * based on sys/crypto/des/des_cbc.c, rewrote by Tomomi Suzuki - */ -#include <crypto/rc5/rc5.h> - -#define panic(x) do { printf(x); return EINVAL; } while (0) - -int -rc5_cbc_process(m0, skip, length, e_key, iv, mode) - struct mbuf *m0; - size_t skip; - size_t length; - RC5_WORD *e_key; - u_int8_t *iv; - int mode; -{ - u_int8_t inbuf[8], outbuf[8]; - struct mbuf *m; - size_t off; - - /* sanity check */ - if (m0->m_pkthdr.len < skip) { - printf("rc5_cbc_process: mbuf length < skip\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < length) { - printf("rc5_cbc_process: mbuf length < encrypt length\n"); - return EINVAL; - } - if (m0->m_pkthdr.len < skip + length) { - printf("rc5_cbc_process: mbuf length < " - "skip + encrypt length\n"); - return EINVAL; - } - if (length % 8) { - printf("rc5_cbc_process: length(%lu)is not multipleof 8\n", - (u_long)length); - return EINVAL; - } - - m = m0; - off = 0; - - /* skip over the header */ - while (skip) { - if (!m) - panic("rc5_cbc_process: mbuf chain?\n"); - if (m->m_len <= skip) { - skip -= m->m_len; - m = m->m_next; - off = 0; - } else { - off = skip; - skip = 0; - } - } - - /* copy iv into outbuf for XOR (encrypt) */ - bcopy(iv, outbuf, 8); - - /* - * encrypt/decrypt packet - */ - while (length > 0) { - int i; - - if (!m) - panic("rc5_cbc_process: mbuf chain?\n"); - - /* - * copy the source into input buffer. - * don't update off or m, since we need to use them - * later. - */ - if (off + 8 <= m->m_len) - bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); - else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *in; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - in = &inbuf[0]; - while (in - &inbuf[0] < 8) { - if (!p) { - panic("rc5_cbc_process: " - "mbuf chain?\n"); - } - *in++ = *p++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && !n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - } - - /* encrypt/decrypt */ - switch (mode) { - case RC5_ENCRYPT: - /* XOR */ - for (i = 0; i < 8; i++) - inbuf[i] ^= outbuf[i]; - - /* encrypt */ - rc5_encrypt_round16(outbuf, inbuf, e_key); - break; - - case RC5_DECRYPT: - /* decrypt */ - rc5_decrypt_round16(outbuf, inbuf, e_key); - - /* XOR */ - for (i = 0; i < 8; i++) - outbuf[i] ^= iv[i]; - - /* copy inbuf into iv for next XOR */ - bcopy(inbuf, iv, 8); - break; - } - - /* - * copy the output buffer into the result. - * need to update off and m. - */ - if (off + 8 < m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - off += 8; - } else if (off + 8 == m->m_len) { - bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); - do { - m = m->m_next; - } while (m && !m->m_len); - off = 0; - } else { - struct mbuf *n; - size_t noff; - u_int8_t *p; - u_int8_t *out; - - n = m; - noff = off; - p = mtod(n, u_int8_t *) + noff; - - out = &outbuf[0]; - while (out - &outbuf[0] < 8) { - if (!p) { - panic("rc5_cbc_process: " - "mbuf chain?\n"); - } - *p++ = *out++; - noff++; - if (noff < n->m_len) - continue; - do { - n = n->m_next; - } while (n && !n->m_len); - noff = 0; - if (n) - p = mtod(n, u_int8_t *) + noff; - else - p = NULL; - } - - m = n; - off = noff; - } - - length -= 8; - } - - return 0; -} - diff --git a/sys/crypto/rijndael/boxes-fst.dat b/sys/crypto/rijndael/boxes-fst.dat index 6315523bcdbc..3fed9c025ea3 100644 --- a/sys/crypto/rijndael/boxes-fst.dat +++ b/sys/crypto/rijndael/boxes-fst.dat @@ -1,6 +1,7 @@ -/* $KAME$ */ +/* $FreeBSD$ */ +/* $KAME: boxes-fst.dat,v 1.6 2001/05/27 00:23:22 itojun Exp $ */ -word8 S[256] = { +const word8 S[256] = { 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, @@ -20,7 +21,7 @@ word8 S[256] = { }; #ifdef INTERMEDIATE_VALUE_KAT -static word8 Si[256] = { +static const word8 Si[256] = { 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78, @@ -40,7 +41,13 @@ static word8 Si[256] = { }; #endif /* INTERMEDIATE_VALUE_KAT */ -static word8 T1[256][4] = { +union xtab { + word32 xt32[256]; + word8 xt8[256][4]; +}; + +static const union xtab xT1 = { + .xt8 = { {0xc6,0x63,0x63,0xa5}, {0xf8,0x7c,0x7c,0x84}, {0xee,0x77,0x77,0x99}, {0xf6,0x7b,0x7b,0x8d}, {0xff,0xf2,0xf2,0x0d}, {0xd6,0x6b,0x6b,0xbd}, {0xde,0x6f,0x6f,0xb1}, {0x91,0xc5,0xc5,0x54}, {0x60,0x30,0x30,0x50}, {0x02,0x01,0x01,0x03}, {0xce,0x67,0x67,0xa9}, {0x56,0x2b,0x2b,0x7d}, @@ -105,9 +112,12 @@ static word8 T1[256][4] = { {0x65,0xbf,0xbf,0xda}, {0xd7,0xe6,0xe6,0x31}, {0x84,0x42,0x42,0xc6}, {0xd0,0x68,0x68,0xb8}, {0x82,0x41,0x41,0xc3}, {0x29,0x99,0x99,0xb0}, {0x5a,0x2d,0x2d,0x77}, {0x1e,0x0f,0x0f,0x11}, {0x7b,0xb0,0xb0,0xcb}, {0xa8,0x54,0x54,0xfc}, {0x6d,0xbb,0xbb,0xd6}, {0x2c,0x16,0x16,0x3a} + } }; +#define T1 xT1.xt8 -static word8 T2[256][4] = { +static const union xtab xT2 = { + .xt8 = { {0xa5,0xc6,0x63,0x63}, {0x84,0xf8,0x7c,0x7c}, {0x99,0xee,0x77,0x77}, {0x8d,0xf6,0x7b,0x7b}, {0x0d,0xff,0xf2,0xf2}, {0xbd,0xd6,0x6b,0x6b}, {0xb1,0xde,0x6f,0x6f}, {0x54,0x91,0xc5,0xc5}, {0x50,0x60,0x30,0x30}, {0x03,0x02,0x01,0x01}, {0xa9,0xce,0x67,0x67}, {0x7d,0x56,0x2b,0x2b}, @@ -172,9 +182,12 @@ static word8 T2[256][4] = { {0xda,0x65,0xbf,0xbf}, {0x31,0xd7,0xe6,0xe6}, {0xc6,0x84,0x42,0x42}, {0xb8,0xd0,0x68,0x68}, {0xc3,0x82,0x41,0x41}, {0xb0,0x29,0x99,0x99}, {0x77,0x5a,0x2d,0x2d}, {0x11,0x1e,0x0f,0x0f}, {0xcb,0x7b,0xb0,0xb0}, {0xfc,0xa8,0x54,0x54}, {0xd6,0x6d,0xbb,0xbb}, {0x3a,0x2c,0x16,0x16} + } }; +#define T2 xT2.xt8 -static word8 T3[256][4] = { +static const union xtab xT3 = { + .xt8 = { {0x63,0xa5,0xc6,0x63}, {0x7c,0x84,0xf8,0x7c}, {0x77,0x99,0xee,0x77}, {0x7b,0x8d,0xf6,0x7b}, {0xf2,0x0d,0xff,0xf2}, {0x6b,0xbd,0xd6,0x6b}, {0x6f,0xb1,0xde,0x6f}, {0xc5,0x54,0x91,0xc5}, {0x30,0x50,0x60,0x30}, {0x01,0x03,0x02,0x01}, {0x67,0xa9,0xce,0x67}, {0x2b,0x7d,0x56,0x2b}, @@ -239,9 +252,12 @@ static word8 T3[256][4] = { {0xbf,0xda,0x65,0xbf}, {0xe6,0x31,0xd7,0xe6}, {0x42,0xc6,0x84,0x42}, {0x68,0xb8,0xd0,0x68}, {0x41,0xc3,0x82,0x41}, {0x99,0xb0,0x29,0x99}, {0x2d,0x77,0x5a,0x2d}, {0x0f,0x11,0x1e,0x0f}, {0xb0,0xcb,0x7b,0xb0}, {0x54,0xfc,0xa8,0x54}, {0xbb,0xd6,0x6d,0xbb}, {0x16,0x3a,0x2c,0x16} + } }; +#define T3 xT3.xt8 -static word8 T4[256][4] = { +static const union xtab xT4 = { + .xt8 = { {0x63,0x63,0xa5,0xc6}, {0x7c,0x7c,0x84,0xf8}, {0x77,0x77,0x99,0xee}, {0x7b,0x7b,0x8d,0xf6}, {0xf2,0xf2,0x0d,0xff}, {0x6b,0x6b,0xbd,0xd6}, {0x6f,0x6f,0xb1,0xde}, {0xc5,0xc5,0x54,0x91}, {0x30,0x30,0x50,0x60}, {0x01,0x01,0x03,0x02}, {0x67,0x67,0xa9,0xce}, {0x2b,0x2b,0x7d,0x56}, @@ -306,9 +322,12 @@ static word8 T4[256][4] = { {0xbf,0xbf,0xda,0x65}, {0xe6,0xe6,0x31,0xd7}, {0x42,0x42,0xc6,0x84}, {0x68,0x68,0xb8,0xd0}, {0x41,0x41,0xc3,0x82}, {0x99,0x99,0xb0,0x29}, {0x2d,0x2d,0x77,0x5a}, {0x0f,0x0f,0x11,0x1e}, {0xb0,0xb0,0xcb,0x7b}, {0x54,0x54,0xfc,0xa8}, {0xbb,0xbb,0xd6,0x6d}, {0x16,0x16,0x3a,0x2c} + } }; +#define T4 xT4.xt8 -static word8 T5[256][4] = { +static const union xtab xT5 = { + .xt8 = { {0x51,0xf4,0xa7,0x50}, {0x7e,0x41,0x65,0x53}, {0x1a,0x17,0xa4,0xc3}, {0x3a,0x27,0x5e,0x96}, {0x3b,0xab,0x6b,0xcb}, {0x1f,0x9d,0x45,0xf1}, {0xac,0xfa,0x58,0xab}, {0x4b,0xe3,0x03,0x93}, {0x20,0x30,0xfa,0x55}, {0xad,0x76,0x6d,0xf6}, {0x88,0xcc,0x76,0x91}, {0xf5,0x02,0x4c,0x25}, @@ -373,9 +392,12 @@ static word8 T5[256][4] = { {0x16,0x1d,0xc3,0x72}, {0xbc,0xe2,0x25,0x0c}, {0x28,0x3c,0x49,0x8b}, {0xff,0x0d,0x95,0x41}, {0x39,0xa8,0x01,0x71}, {0x08,0x0c,0xb3,0xde}, {0xd8,0xb4,0xe4,0x9c}, {0x64,0x56,0xc1,0x90}, {0x7b,0xcb,0x84,0x61}, {0xd5,0x32,0xb6,0x70}, {0x48,0x6c,0x5c,0x74}, {0xd0,0xb8,0x57,0x42} + } }; +#define T5 xT5.xt8 -static word8 T6[256][4] = { +static const union xtab xT6 = { + .xt8 = { {0x50,0x51,0xf4,0xa7}, {0x53,0x7e,0x41,0x65}, {0xc3,0x1a,0x17,0xa4}, {0x96,0x3a,0x27,0x5e}, {0xcb,0x3b,0xab,0x6b}, {0xf1,0x1f,0x9d,0x45}, {0xab,0xac,0xfa,0x58}, {0x93,0x4b,0xe3,0x03}, {0x55,0x20,0x30,0xfa}, {0xf6,0xad,0x76,0x6d}, {0x91,0x88,0xcc,0x76}, {0x25,0xf5,0x02,0x4c}, @@ -440,9 +462,12 @@ static word8 T6[256][4] = { {0x72,0x16,0x1d,0xc3}, {0x0c,0xbc,0xe2,0x25}, {0x8b,0x28,0x3c,0x49}, {0x41,0xff,0x0d,0x95}, {0x71,0x39,0xa8,0x01}, {0xde,0x08,0x0c,0xb3}, {0x9c,0xd8,0xb4,0xe4}, {0x90,0x64,0x56,0xc1}, {0x61,0x7b,0xcb,0x84}, {0x70,0xd5,0x32,0xb6}, {0x74,0x48,0x6c,0x5c}, {0x42,0xd0,0xb8,0x57} + } }; +#define T6 xT6.xt8 -static word8 T7[256][4] = { +static const union xtab xT7 = { + .xt8 = { {0xa7,0x50,0x51,0xf4}, {0x65,0x53,0x7e,0x41}, {0xa4,0xc3,0x1a,0x17}, {0x5e,0x96,0x3a,0x27}, {0x6b,0xcb,0x3b,0xab}, {0x45,0xf1,0x1f,0x9d}, {0x58,0xab,0xac,0xfa}, {0x03,0x93,0x4b,0xe3}, {0xfa,0x55,0x20,0x30}, {0x6d,0xf6,0xad,0x76}, {0x76,0x91,0x88,0xcc}, {0x4c,0x25,0xf5,0x02}, @@ -507,9 +532,12 @@ static word8 T7[256][4] = { {0xc3,0x72,0x16,0x1d}, {0x25,0x0c,0xbc,0xe2}, {0x49,0x8b,0x28,0x3c}, {0x95,0x41,0xff,0x0d}, {0x01,0x71,0x39,0xa8}, {0xb3,0xde,0x08,0x0c}, {0xe4,0x9c,0xd8,0xb4}, {0xc1,0x90,0x64,0x56}, {0x84,0x61,0x7b,0xcb}, {0xb6,0x70,0xd5,0x32}, {0x5c,0x74,0x48,0x6c}, {0x57,0x42,0xd0,0xb8} + } }; +#define T7 xT7.xt8 -static word8 T8[256][4] = { +static const union xtab xT8 = { + .xt8 = { {0xf4,0xa7,0x50,0x51}, {0x41,0x65,0x53,0x7e}, {0x17,0xa4,0xc3,0x1a}, {0x27,0x5e,0x96,0x3a}, {0xab,0x6b,0xcb,0x3b}, {0x9d,0x45,0xf1,0x1f}, {0xfa,0x58,0xab,0xac}, {0xe3,0x03,0x93,0x4b}, {0x30,0xfa,0x55,0x20}, {0x76,0x6d,0xf6,0xad}, {0xcc,0x76,0x91,0x88}, {0x02,0x4c,0x25,0xf5}, @@ -574,9 +602,11 @@ static word8 T8[256][4] = { {0x1d,0xc3,0x72,0x16}, {0xe2,0x25,0x0c,0xbc}, {0x3c,0x49,0x8b,0x28}, {0x0d,0x95,0x41,0xff}, {0xa8,0x01,0x71,0x39}, {0x0c,0xb3,0xde,0x08}, {0xb4,0xe4,0x9c,0xd8}, {0x56,0xc1,0x90,0x64}, {0xcb,0x84,0x61,0x7b}, {0x32,0xb6,0x70,0xd5}, {0x6c,0x5c,0x74,0x48}, {0xb8,0x57,0x42,0xd0} + } }; +#define T8 xT8.xt8 -static word8 S5[256] = { +static const word8 S5[256] = { 0x52,0x09,0x6a,0xd5, 0x30,0x36,0xa5,0x38, 0xbf,0x40,0xa3,0x9e, @@ -643,7 +673,8 @@ static word8 S5[256] = { 0x55,0x21,0x0c,0x7d }; -static word8 U1[256][4] = { +static const union xtab xU1 = { + .xt8 = { {0x00,0x00,0x00,0x00}, {0x0e,0x09,0x0d,0x0b}, {0x1c,0x12,0x1a,0x16}, {0x12,0x1b,0x17,0x1d}, {0x38,0x24,0x34,0x2c}, {0x36,0x2d,0x39,0x27}, {0x24,0x36,0x2e,0x3a}, {0x2a,0x3f,0x23,0x31}, {0x70,0x48,0x68,0x58}, {0x7e,0x41,0x65,0x53}, {0x6c,0x5a,0x72,0x4e}, {0x62,0x53,0x7f,0x45}, @@ -708,9 +739,12 @@ static word8 U1[256][4] = { {0xef,0x15,0xe8,0xe6}, {0xe1,0x1c,0xe5,0xed}, {0xf3,0x07,0xf2,0xf0}, {0xfd,0x0e,0xff,0xfb}, {0xa7,0x79,0xb4,0x92}, {0xa9,0x70,0xb9,0x99}, {0xbb,0x6b,0xae,0x84}, {0xb5,0x62,0xa3,0x8f}, {0x9f,0x5d,0x80,0xbe}, {0x91,0x54,0x8d,0xb5}, {0x83,0x4f,0x9a,0xa8}, {0x8d,0x46,0x97,0xa3} + } }; +#define U1 xU1.xt8 -static word8 U2[256][4] = { +static const union xtab xU2 = { + .xt8 = { {0x00,0x00,0x00,0x00}, {0x0b,0x0e,0x09,0x0d}, {0x16,0x1c,0x12,0x1a}, {0x1d,0x12,0x1b,0x17}, {0x2c,0x38,0x24,0x34}, {0x27,0x36,0x2d,0x39}, {0x3a,0x24,0x36,0x2e}, {0x31,0x2a,0x3f,0x23}, {0x58,0x70,0x48,0x68}, {0x53,0x7e,0x41,0x65}, {0x4e,0x6c,0x5a,0x72}, {0x45,0x62,0x53,0x7f}, @@ -775,9 +809,12 @@ static word8 U2[256][4] = { {0xe6,0xef,0x15,0xe8}, {0xed,0xe1,0x1c,0xe5}, {0xf0,0xf3,0x07,0xf2}, {0xfb,0xfd,0x0e,0xff}, {0x92,0xa7,0x79,0xb4}, {0x99,0xa9,0x70,0xb9}, {0x84,0xbb,0x6b,0xae}, {0x8f,0xb5,0x62,0xa3}, {0xbe,0x9f,0x5d,0x80}, {0xb5,0x91,0x54,0x8d}, {0xa8,0x83,0x4f,0x9a}, {0xa3,0x8d,0x46,0x97} + } }; +#define U2 xU2.xt8 -static word8 U3[256][4] = { +static const union xtab xU3 = { + .xt8 = { {0x00,0x00,0x00,0x00}, {0x0d,0x0b,0x0e,0x09}, {0x1a,0x16,0x1c,0x12}, {0x17,0x1d,0x12,0x1b}, {0x34,0x2c,0x38,0x24}, {0x39,0x27,0x36,0x2d}, {0x2e,0x3a,0x24,0x36}, {0x23,0x31,0x2a,0x3f}, {0x68,0x58,0x70,0x48}, {0x65,0x53,0x7e,0x41}, {0x72,0x4e,0x6c,0x5a}, {0x7f,0x45,0x62,0x53}, @@ -842,9 +879,12 @@ static word8 U3[256][4] = { {0xe8,0xe6,0xef,0x15}, {0xe5,0xed,0xe1,0x1c}, {0xf2,0xf0,0xf3,0x07}, {0xff,0xfb,0xfd,0x0e}, {0xb4,0x92,0xa7,0x79}, {0xb9,0x99,0xa9,0x70}, {0xae,0x84,0xbb,0x6b}, {0xa3,0x8f,0xb5,0x62}, {0x80,0xbe,0x9f,0x5d}, {0x8d,0xb5,0x91,0x54}, {0x9a,0xa8,0x83,0x4f}, {0x97,0xa3,0x8d,0x46} + } }; +#define U3 xU3.xt8 -static word8 U4[256][4] = { +static const union xtab xU4 = { + .xt8 = { {0x00,0x00,0x00,0x00}, {0x09,0x0d,0x0b,0x0e}, {0x12,0x1a,0x16,0x1c}, {0x1b,0x17,0x1d,0x12}, {0x24,0x34,0x2c,0x38}, {0x2d,0x39,0x27,0x36}, {0x36,0x2e,0x3a,0x24}, {0x3f,0x23,0x31,0x2a}, {0x48,0x68,0x58,0x70}, {0x41,0x65,0x53,0x7e}, {0x5a,0x72,0x4e,0x6c}, {0x53,0x7f,0x45,0x62}, @@ -909,8 +949,10 @@ static word8 U4[256][4] = { {0x15,0xe8,0xe6,0xef}, {0x1c,0xe5,0xed,0xe1}, {0x07,0xf2,0xf0,0xf3}, {0x0e,0xff,0xfb,0xfd}, {0x79,0xb4,0x92,0xa7}, {0x70,0xb9,0x99,0xa9}, {0x6b,0xae,0x84,0xbb}, {0x62,0xa3,0x8f,0xb5}, {0x5d,0x80,0xbe,0x9f}, {0x54,0x8d,0xb5,0x91}, {0x4f,0x9a,0xa8,0x83}, {0x46,0x97,0xa3,0x8d} + } }; +#define U4 xU4.xt8 -static word32 rcon[30] = { +static const word32 rcon[30] = { 0x01,0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; diff --git a/sys/crypto/rijndael/rijndael-alg-fst.c b/sys/crypto/rijndael/rijndael-alg-fst.c index 33d0d8a0c498..ac9668e9a93b 100644 --- a/sys/crypto/rijndael/rijndael-alg-fst.c +++ b/sys/crypto/rijndael/rijndael-alg-fst.c @@ -1,4 +1,5 @@ -/* $KAME$ */ +/* $FreeBSD$ */ +/* $KAME: rijndael-alg-fst.c,v 1.7 2001/05/27 00:23:23 itojun Exp $ */ /* * rijndael-alg-fst.c v2.3 April '2000 @@ -14,6 +15,11 @@ #include <sys/cdefs.h> #include <sys/types.h> +#ifdef _KERNEL +#include <sys/systm.h> +#else +#include <string.h> +#endif #include <crypto/rijndael/rijndael-alg-fst.h> #include <crypto/rijndael/rijndael_local.h> @@ -24,7 +30,11 @@ int rijndaelKeySched(word8 k[MAXKC][4], word8 W[MAXROUNDS+1][4][4], int ROUNDS) * The number of calculations depends on keyBits and blockBits */ int j, r, t, rconpointer = 0; - word8 tk[MAXKC][4]; + union { + word8 x8[MAXKC][4]; + word32 x32[MAXKC]; + } xtk; +#define tk xtk.x8 int KC = ROUNDS - 6; for (j = KC-1; j >= 0; j--) { @@ -79,6 +89,7 @@ int rijndaelKeySched(word8 k[MAXKC][4], word8 W[MAXROUNDS+1][4][4], int ROUNDS) } } return 0; +#undef tk } int rijndaelKeyEncToDec(word8 W[MAXROUNDS+1][4][4], int ROUNDS) { @@ -120,9 +131,21 @@ int rijndaelKeyEncToDec(word8 W[MAXROUNDS+1][4][4], int ROUNDS) { /** * Encrypt a single block. */ -int rijndaelEncrypt(word8 a[16], word8 b[16], word8 rk[MAXROUNDS+1][4][4], int ROUNDS) { +int rijndaelEncrypt(word8 in[16], word8 out[16], word8 rk[MAXROUNDS+1][4][4], int ROUNDS) { int r; - word8 temp[4][4]; + union { + word8 x8[16]; + word32 x32[4]; + } xa, xb; +#define a xa.x8 +#define b xb.x8 + union { + word8 x8[4][4]; + word32 x32[4]; + } xtemp; +#define temp xtemp.x8 + + memcpy(a, in, sizeof a); *((word32*)temp[0]) = *((word32*)(a )) ^ *((word32*)rk[0][0]); *((word32*)temp[1]) = *((word32*)(a+ 4)) ^ *((word32*)rk[0][1]); @@ -193,7 +216,12 @@ int rijndaelEncrypt(word8 a[16], word8 b[16], word8 rk[MAXROUNDS+1][4][4], int R *((word32*)(b+ 8)) ^= *((word32*)rk[ROUNDS][2]); *((word32*)(b+12)) ^= *((word32*)rk[ROUNDS][3]); + memcpy(out, b, sizeof b /* XXX out */); + return 0; +#undef a +#undef b +#undef temp } #ifdef INTERMEDIATE_VALUE_KAT @@ -268,10 +296,22 @@ int rijndaelEncryptRound(word8 a[4][4], word8 rk[MAXROUNDS+1][4][4], int ROUNDS, /** * Decrypt a single block. */ -int rijndaelDecrypt(word8 a[16], word8 b[16], word8 rk[MAXROUNDS+1][4][4], int ROUNDS) { +int rijndaelDecrypt(word8 in[16], word8 out[16], word8 rk[MAXROUNDS+1][4][4], int ROUNDS) { int r; - word8 temp[4][4]; + union { + word8 x8[16]; + word32 x32[4]; + } xa, xb; +#define a xa.x8 +#define b xb.x8 + union { + word8 x8[4][4]; + word32 x32[4]; + } xtemp; +#define temp xtemp.x8 + memcpy(a, in, sizeof a); + *((word32*)temp[0]) = *((word32*)(a )) ^ *((word32*)rk[ROUNDS][0]); *((word32*)temp[1]) = *((word32*)(a+ 4)) ^ *((word32*)rk[ROUNDS][1]); *((word32*)temp[2]) = *((word32*)(a+ 8)) ^ *((word32*)rk[ROUNDS][2]); @@ -341,7 +381,12 @@ int rijndaelDecrypt(word8 a[16], word8 b[16], word8 rk[MAXROUNDS+1][4][4], int R *((word32*)(b+ 8)) ^= *((word32*)rk[0][2]); *((word32*)(b+12)) ^= *((word32*)rk[0][3]); + memcpy(out, b, sizeof b /* XXX out */); + return 0; +#undef a +#undef b +#undef temp } diff --git a/sys/crypto/rijndael/rijndael-alg-fst.h b/sys/crypto/rijndael/rijndael-alg-fst.h index 6061bf430854..5b22ef4c7cba 100644 --- a/sys/crypto/rijndael/rijndael-alg-fst.h +++ b/sys/crypto/rijndael/rijndael-alg-fst.h @@ -1,4 +1,5 @@ -/* $KAME$ */ +/* $FreeBSD$ */ +/* $KAME: rijndael-alg-fst.h,v 1.4 2000/10/02 17:14:26 itojun Exp $ */ /* * rijndael-alg-fst.h v2.3 April '2000 diff --git a/sys/crypto/rijndael/rijndael-api-fst.c b/sys/crypto/rijndael/rijndael-api-fst.c index 1a2de505eaf6..1eec694ff6a7 100644 --- a/sys/crypto/rijndael/rijndael-api-fst.c +++ b/sys/crypto/rijndael/rijndael-api-fst.c @@ -1,4 +1,5 @@ -/* $KAME: $ */ +/* $FreeBSD$ */ +/* $KAME: rijndael-api-fst.c,v 1.10 2001/05/27 09:34:18 itojun Exp $ */ /* * rijndael-api-fst.c v2.3 April '2000 @@ -16,8 +17,12 @@ */ #include <sys/param.h> -#include <sys/systm.h> #include <sys/types.h> +#ifdef _KERNEL +#include <sys/systm.h> +#else +#include <string.h> +#endif #include <crypto/rijndael/rijndael-alg-fst.h> #include <crypto/rijndael/rijndael-api-fst.h> #include <crypto/rijndael/rijndael_local.h> @@ -44,36 +49,16 @@ int rijndael_makeKey(keyInstance *key, BYTE direction, int keyLen, char *keyMate } if (keyMaterial != NULL) { - strncpy(key->keyMaterial, keyMaterial, keyLen/4); + bcopy(keyMaterial, key->keyMaterial, keyLen/8); } key->ROUNDS = keyLen/32 + 6; /* initialize key schedule: */ keyMat = key->keyMaterial; -#ifndef BINARY_KEY_MATERIAL - for (i = 0; i < key->keyLen/8; i++) { - int t, j; - - t = *keyMat++; - if ((t >= '0') && (t <= '9')) j = (t - '0') << 4; - else if ((t >= 'a') && (t <= 'f')) j = (t - 'a' + 10) << 4; - else if ((t >= 'A') && (t <= 'F')) j = (t - 'A' + 10) << 4; - else return BAD_KEY_MAT; - - t = *keyMat++; - if ((t >= '0') && (t <= '9')) j ^= (t - '0'); - else if ((t >= 'a') && (t <= 'f')) j ^= (t - 'a' + 10); - else if ((t >= 'A') && (t <= 'F')) j ^= (t - 'A' + 10); - else return BAD_KEY_MAT; - - k[i >> 2][i & 3] = (word8)j; - } -#else for (i = 0; i < key->keyLen/8; i++) { k[i >> 2][i & 3] = (word8)keyMat[i]; } -#endif /* ?BINARY_KEY_MATERIAL */ rijndaelKeySched(k, key->keySched, key->ROUNDS); if (direction == DIR_DECRYPT) { rijndaelKeyEncToDec(key->keySched, key->ROUNDS); @@ -89,28 +74,7 @@ int rijndael_cipherInit(cipherInstance *cipher, BYTE mode, char *IV) { return BAD_CIPHER_MODE; } if (IV != NULL) { -#ifndef BINARY_KEY_MATERIAL - int i; - for (i = 0; i < MAX_IV_SIZE; i++) { - int t, j; - - t = IV[2*i]; - if ((t >= '0') && (t <= '9')) j = (t - '0') << 4; - else if ((t >= 'a') && (t <= 'f')) j = (t - 'a' + 10) << 4; - else if ((t >= 'A') && (t <= 'F')) j = (t - 'A' + 10) << 4; - else return BAD_CIPHER_INSTANCE; - - t = IV[2*i+1]; - if ((t >= '0') && (t <= '9')) j ^= (t - '0'); - else if ((t >= 'a') && (t <= 'f')) j ^= (t - 'a' + 10); - else if ((t >= 'A') && (t <= 'F')) j ^= (t - 'A' + 10); - else return BAD_CIPHER_INSTANCE; - - cipher->IV[i] = (word8)j; - } -#else bcopy(IV, cipher->IV, MAX_IV_SIZE); -#endif /* ?BINARY_KEY_MATERIAL */ } else { bzero(cipher->IV, MAX_IV_SIZE); } diff --git a/sys/crypto/rijndael/rijndael-api-fst.h b/sys/crypto/rijndael/rijndael-api-fst.h index c98f3f4d1cb4..a4ab920413bc 100644 --- a/sys/crypto/rijndael/rijndael-api-fst.h +++ b/sys/crypto/rijndael/rijndael-api-fst.h @@ -1,4 +1,5 @@ -/* $KAME$ */ +/* $FreeBSD$ */ +/* $KAME: rijndael-api-fst.h,v 1.6 2001/05/27 00:23:23 itojun Exp $ */ /* * rijndael-api-fst.h v2.3 April '2000 @@ -55,7 +56,11 @@ typedef struct { /* The following parameters are algorithm dependent, replace or add as necessary */ int ROUNDS; /* key-length-dependent number of rounds */ int blockLen; /* block length */ - u_int8_t keySched[RIJNDAEL_MAXROUNDS+1][4][4]; /* key schedule */ + union { + u_int8_t xkS8[RIJNDAEL_MAXROUNDS+1][4][4]; /* key schedule */ + u_int32_t xkS32[RIJNDAEL_MAXROUNDS+1][4]; /* key schedule */ + } xKeySched; +#define keySched xKeySched.xkS8 } keyInstance; /* The structure for cipher information */ diff --git a/sys/crypto/rijndael/rijndael_local.h b/sys/crypto/rijndael/rijndael_local.h index 23e909c56633..a959b1b9ed0e 100644 --- a/sys/crypto/rijndael/rijndael_local.h +++ b/sys/crypto/rijndael/rijndael_local.h @@ -9,5 +9,3 @@ typedef u_int32_t word32; #define MAXKC RIJNDAEL_MAXKC #define MAXROUNDS RIJNDAEL_MAXROUNDS - -#define BINARY_KEY_MATERIAL 1 diff --git a/sys/crypto/sha1.c b/sys/crypto/sha1.c index bbf20b8f0ce8..b210b52ae89e 100644 --- a/sys/crypto/sha1.c +++ b/sys/crypto/sha1.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: sha1.c,v 1.4 2000/03/27 04:36:23 sumikawa Exp $ */ +/* $KAME: sha1.c,v 1.5 2000/11/08 06:13:08 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. diff --git a/sys/crypto/sha2/sha2.c b/sys/crypto/sha2/sha2.c new file mode 100644 index 000000000000..9b3a5c6b9459 --- /dev/null +++ b/sys/crypto/sha2/sha2.c @@ -0,0 +1,1048 @@ +/* $FreeBSD$ */ +/* $KAME: sha2.c,v 1.6 2001/03/12 11:31:04 itojun Exp $ */ + +/* + * sha2.c + * + * Version 1.0.0beta1 + * + * Written by Aaron D. Gifford <me@aarongifford.com> + * + * Copyright 2000 Aaron D. Gifford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/systm.h> +#include <machine/endian.h> +#include <crypto/sha2/sha2.h> + +/* + * ASSERT NOTE: + * Some sanity checking code is included using assert(). On my FreeBSD + * system, this additional code can be removed by compiling with NDEBUG + * defined. Check your own systems manpage on assert() to see how to + * compile WITHOUT the sanity checking code on your system. + * + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ + +#if defined(__bsdi__) || defined(__FreeBSD__) +#define assert(x) +#endif + + +/*** SHA-256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivilent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including <sys/types.h> (which in turn includes + * <machine/endian.h> where the appropriate definitions are actually + * made). + */ +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif + +/* + * Define the followingsha2_* types to types of the correct length on + * the native archtecture. Most BSD systems and Linux define u_intXX_t + * types. Machines with very recent ANSI C headers, can use the + * uintXX_t definintions from inttypes.h by defining SHA2_USE_INTTYPES_H + * during compile or in the sha.h header file. + * + * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t + * will need to define these three typedefs below (and the appropriate + * ones in sha.h too) by hand according to their system architecture. + * + * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t + * types and pointing out recent ANSI C support for uintXX_t in inttypes.h. + */ +#if 0 /*def SHA2_USE_INTTYPES_H*/ + +typedef uint8_t sha2_byte; /* Exactly 1 byte */ +typedef uint32_t sha2_word32; /* Exactly 4 bytes */ +typedef uint64_t sha2_word64; /* Exactly 8 bytes */ + +#else /* SHA2_USE_INTTYPES_H */ + +typedef u_int8_t sha2_byte; /* Exactly 1 byte */ +typedef u_int32_t sha2_word32; /* Exactly 4 bytes */ +typedef u_int64_t sha2_word64; /* Exactly 8 bytes */ + +#endif /* SHA2_USE_INTTYPES_H */ + + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + + +/*** ENDIAN REVERSAL MACROS *******************************************/ +#if BYTE_ORDER == LITTLE_ENDIAN +#define REVERSE32(w,x) { \ + sha2_word32 tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ +} +#define REVERSE64(w,x) { \ + sha2_word64 tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ + ((tmp & 0x0000ffff0000ffffULL) << 16); \ +} +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w,n) { \ + (w)[0] += (sha2_word64)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ +} + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define R(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) + +/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) +#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) +#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) +#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) +#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) +#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) +#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +void SHA512_Last(SHA512_CTX*); +void SHA256_Transform(SHA256_CTX*, const sha2_word32*); +void SHA512_Transform(SHA512_CTX*, const sha2_word64*); + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ +/* Hash constant words K for SHA-256: */ +const static sha2_word32 K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-256: */ +const static sha2_word32 sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +/* Hash constant words K for SHA-384 and SHA-512: */ +const static sha2_word64 K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* Initial hash value H for SHA-384 */ +const static sha2_word64 sha384_initial_hash_value[8] = { + 0xcbbb9d5dc1059ed8ULL, + 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, + 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, + 0x47b5481dbefa4fa4ULL +}; + +/* Initial hash value H for SHA-512 */ +const static sha2_word64 sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL +}; + +/* + * Constant used by SHA256/384/512_End() functions for converting the + * digest to a readable hexadecimal character string: + */ +static const char *sha2_hex_digits = "0123456789abcdef"; + + +/*** SHA-256: *********************************************************/ +void SHA256_Init(SHA256_CTX* context) { + if (context == (SHA256_CTX*)0) { + return; + } + bcopy(sha256_initial_hash_value, context->state, SHA256_DIGEST_LENGTH); + bzero(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#if BYTE_ORDER == LITTLE_ENDIAN + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + REVERSE32(*data++, W256[j]); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + + +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + (W256[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND256(a,b,c,d,e,f,g,h) \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { + sha2_word32 a, b, c, d, e, f, g, h, s0, s1; + sha2_word32 T1, *W256; + int j; + + W256 = (sha2_word32*)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { + sha2_word32 a, b, c, d, e, f, g, h, s0, s1; + sha2_word32 T1, T2, *W256; + int j; + + W256 = (sha2_word32*)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { +#if BYTE_ORDER == LITTLE_ENDIAN + /* Copy data while converting to host byte order */ + REVERSE32(*data++,W256[j]); + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + /* Apply the SHA-256 compression function to update a..h with copy */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) { + unsigned int freespace, usedspace; + + if (len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0); + + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + bcopy(data, &context->buffer[usedspace], freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; + SHA256_Transform(context, (sha2_word32*)context->buffer); + } else { + /* The buffer is not yet full */ + bcopy(data, &context->buffer[usedspace], len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA256_Transform(context, (sha2_word32*)data); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + bcopy(data, context->buffer, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) { + sha2_word32 *d = (sha2_word32*)digest; + unsigned int usedspace; + + /* Sanity check: */ + assert(context != (SHA256_CTX*)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount,context->bitcount); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace < SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + bzero(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA256_BLOCK_LENGTH) { + bzero(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA256_Transform(context, (sha2_word32*)context->buffer); + + /* And set-up for the last transform: */ + bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + } + } else { + /* Set-up for the last transform: */ + bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Set the bit count: */ + *(sha2_word64*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount; + + /* Final transform: */ + SHA256_Transform(context, (sha2_word32*)context->buffer); + +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE32(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + bcopy(context->state, d, SHA256_DIGEST_LENGTH); +#endif + } + + /* Clean up state data: */ + bzero(context, sizeof(context)); + usedspace = 0; +} + +char *SHA256_End(SHA256_CTX* context, char buffer[]) { + sha2_byte digest[SHA256_DIGEST_LENGTH], *d = digest; + int i; + + /* Sanity check: */ + assert(context != (SHA256_CTX*)0); + + if (buffer != (char*)0) { + SHA256_Final(digest, context); + + for (i = 0; i < SHA256_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + bzero(context, sizeof(context)); + } + bzero(digest, SHA256_DIGEST_LENGTH); + return buffer; +} + +char* SHA256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) { + SHA256_CTX context; + + SHA256_Init(&context); + SHA256_Update(&context, data, len); + return SHA256_End(&context, digest); +} + + +/*** SHA-512: *********************************************************/ +void SHA512_Init(SHA512_CTX* context) { + if (context == (SHA512_CTX*)0) { + return; + } + bcopy(sha512_initial_hash_value, context->state, SHA512_DIGEST_LENGTH); + bzero(context->buffer, SHA512_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ +#if BYTE_ORDER == LITTLE_ENDIAN + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ + REVERSE64(*data++, W512[j]); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ + K512[j] + W512[j]; \ + (d) += T1, \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \ + j++ + + +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ + K512[j] + (W512[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND512(a,b,c,d,e,f,g,h) \ + s0 = W512[(j+1)&0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j+14)&0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) { + sha2_word64 a, b, c, d, e, f, g, h, s0, s1; + sha2_word64 T1, *W512 = (sha2_word64*)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + ROUND512_0_TO_15(a,b,c,d,e,f,g,h); + ROUND512_0_TO_15(h,a,b,c,d,e,f,g); + ROUND512_0_TO_15(g,h,a,b,c,d,e,f); + ROUND512_0_TO_15(f,g,h,a,b,c,d,e); + ROUND512_0_TO_15(e,f,g,h,a,b,c,d); + ROUND512_0_TO_15(d,e,f,g,h,a,b,c); + ROUND512_0_TO_15(c,d,e,f,g,h,a,b); + ROUND512_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a,b,c,d,e,f,g,h); + ROUND512(h,a,b,c,d,e,f,g); + ROUND512(g,h,a,b,c,d,e,f); + ROUND512(f,g,h,a,b,c,d,e); + ROUND512(e,f,g,h,a,b,c,d); + ROUND512(d,e,f,g,h,a,b,c); + ROUND512(c,d,e,f,g,h,a,b); + ROUND512(b,c,d,e,f,g,h,a); + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) { + sha2_word64 a, b, c, d, e, f, g, h, s0, s1; + sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + REVERSE64(*data++, W512[j]); + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + /* Apply the SHA-512 compression function to update a..h with copy */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j+1)&0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j+14)&0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) { + unsigned int freespace, usedspace; + + if (len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0); + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + bcopy(data, &context->buffer[usedspace], freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; + SHA512_Transform(context, (sha2_word64*)context->buffer); + } else { + /* The buffer is not yet full */ + bcopy(data, &context->buffer[usedspace], len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA512_Transform(context, (sha2_word64*)data); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + bcopy(data, context->buffer, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void SHA512_Last(SHA512_CTX* context) { + unsigned int usedspace; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount[0],context->bitcount[0]); + REVERSE64(context->bitcount[1],context->bitcount[1]); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace < SHA512_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + bzero(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA512_BLOCK_LENGTH) { + bzero(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA512_Transform(context, (sha2_word64*)context->buffer); + + /* And set-up for the last transform: */ + bzero(context->buffer, SHA512_BLOCK_LENGTH - 2); + } + } else { + /* Prepare for final transform: */ + bzero(context->buffer, SHA512_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits): */ + *(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; + *(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0]; + + /* Final transform: */ + SHA512_Transform(context, (sha2_word64*)context->buffer); +} + +void SHA512_Final(sha2_byte digest[], SHA512_CTX* context) { + sha2_word64 *d = (sha2_word64*)digest; + + /* Sanity check: */ + assert(context != (SHA512_CTX*)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + SHA512_Last(context); + + /* Save the hash data for output: */ +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE64(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + bcopy(context->state, d, SHA512_DIGEST_LENGTH); +#endif + } + + /* Zero out state data */ + bzero(context, sizeof(context)); +} + +char *SHA512_End(SHA512_CTX* context, char buffer[]) { + sha2_byte digest[SHA512_DIGEST_LENGTH], *d = digest; + int i; + + /* Sanity check: */ + assert(context != (SHA512_CTX*)0); + + if (buffer != (char*)0) { + SHA512_Final(digest, context); + + for (i = 0; i < SHA512_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + bzero(context, sizeof(context)); + } + bzero(digest, SHA512_DIGEST_LENGTH); + return buffer; +} + +char* SHA512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) { + SHA512_CTX context; + + SHA512_Init(&context); + SHA512_Update(&context, data, len); + return SHA512_End(&context, digest); +} + + +/*** SHA-384: *********************************************************/ +void SHA384_Init(SHA384_CTX* context) { + if (context == (SHA384_CTX*)0) { + return; + } + bcopy(sha384_initial_hash_value, context->state, SHA512_DIGEST_LENGTH); + bzero(context->buffer, SHA384_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +void SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) { + SHA512_Update((SHA512_CTX*)context, data, len); +} + +void SHA384_Final(sha2_byte digest[], SHA384_CTX* context) { + sha2_word64 *d = (sha2_word64*)digest; + + /* Sanity check: */ + assert(context != (SHA384_CTX*)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + SHA512_Last((SHA512_CTX*)context); + + /* Save the hash data for output: */ +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 6; j++) { + REVERSE64(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + bcopy(context->state, d, SHA384_DIGEST_LENGTH); +#endif + } + + /* Zero out state data */ + bzero(context, sizeof(context)); +} + +char *SHA384_End(SHA384_CTX* context, char buffer[]) { + sha2_byte digest[SHA384_DIGEST_LENGTH], *d = digest; + int i; + + /* Sanity check: */ + assert(context != (SHA384_CTX*)0); + + if (buffer != (char*)0) { + SHA384_Final(digest, context); + + for (i = 0; i < SHA384_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + bzero(context, sizeof(context)); + } + bzero(digest, SHA384_DIGEST_LENGTH); + return buffer; +} + +char* SHA384_Data(const sha2_byte* data, size_t len, char digest[SHA384_DIGEST_STRING_LENGTH]) { + SHA384_CTX context; + + SHA384_Init(&context); + SHA384_Update(&context, data, len); + return SHA384_End(&context, digest); +} + diff --git a/sys/crypto/sha2/sha2.h b/sys/crypto/sha2/sha2.h new file mode 100644 index 000000000000..084faa71f13d --- /dev/null +++ b/sys/crypto/sha2/sha2.h @@ -0,0 +1,141 @@ +/* $FreeBSD$ */ +/* $KAME: sha2.h,v 1.3 2001/03/12 08:27:48 itojun Exp $ */ + +/* + * sha2.h + * + * Version 1.0.0beta1 + * + * Written by Aaron D. Gifford <me@aarongifford.com> + * + * Copyright 2000 Aaron D. Gifford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef __SHA2_H__ +#define __SHA2_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) +#define SHA384_BLOCK_LENGTH 128 +#define SHA384_DIGEST_LENGTH 48 +#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) +#define SHA512_BLOCK_LENGTH 128 +#define SHA512_DIGEST_LENGTH 64 +#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) + + +/*** SHA-256/384/512 Context Structures *******************************/ +/* NOTE: If your architecture does not define either u_intXX_t types or + * uintXX_t (from inttypes.h), you may need to define things by hand + * for your system: + */ +#if 0 +typedef unsigned char u_int8_t; /* 1-byte (8-bits) */ +typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */ +typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */ +#endif +/* + * Most BSD systems already define u_intXX_t types, as does Linux. + * Some systems, however, like Compaq's Tru64 Unix instead can use + * uintXX_t types defined by very recent ANSI C standards and included + * in the file: + * + * #include <inttypes.h> + * + * If you choose to use <inttypes.h> then please define: + * + * #define SHA2_USE_INTTYPES_H + * + * Or on the command line during compile: + * + * cc -DSHA2_USE_INTTYPES_H ... + */ +#if 0 /*def SHA2_USE_INTTYPES_H*/ + +typedef struct _SHA256_CTX { + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[SHA256_BLOCK_LENGTH]; +} SHA256_CTX; +typedef struct _SHA512_CTX { + uint64_t state[8]; + uint64_t bitcount[2]; + uint8_t buffer[SHA512_BLOCK_LENGTH]; +} SHA512_CTX; + +#else /* SHA2_USE_INTTYPES_H */ + +typedef struct _SHA256_CTX { + u_int32_t state[8]; + u_int64_t bitcount; + u_int8_t buffer[SHA256_BLOCK_LENGTH]; +} SHA256_CTX; +typedef struct _SHA512_CTX { + u_int64_t state[8]; + u_int64_t bitcount[2]; + u_int8_t buffer[SHA512_BLOCK_LENGTH]; +} SHA512_CTX; + +#endif /* SHA2_USE_INTTYPES_H */ + +typedef SHA512_CTX SHA384_CTX; + + +/*** SHA-256/384/512 Function Prototypes ******************************/ + +void SHA256_Init __P((SHA256_CTX *)); +void SHA256_Update __P((SHA256_CTX*, const u_int8_t*, size_t)); +void SHA256_Final __P((u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*)); +char* SHA256_End __P((SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH])); +char* SHA256_Data __P((const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH])); + +void SHA384_Init __P((SHA384_CTX*)); +void SHA384_Update __P((SHA384_CTX*, const u_int8_t*, size_t)); +void SHA384_Final __P((u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*)); +char* SHA384_End __P((SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH])); +char* SHA384_Data __P((const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH])); + +void SHA512_Init __P((SHA512_CTX*)); +void SHA512_Update __P((SHA512_CTX*, const u_int8_t*, size_t)); +void SHA512_Final __P((u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*)); +char* SHA512_End __P((SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH])); +char* SHA512_Data __P((const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH])); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __SHA2_H__ */ + diff --git a/sys/kern/uipc_domain.c b/sys/kern/uipc_domain.c index 0e3b6a3639fd..6223bad07093 100644 --- a/sys/kern/uipc_domain.c +++ b/sys/kern/uipc_domain.c @@ -209,6 +209,32 @@ pfctlinput(cmd, sa) (*pr->pr_ctlinput)(cmd, sa, (void *)0); } +void +pfctlinput2(cmd, sa, ctlparam) + int cmd; + struct sockaddr *sa; + void *ctlparam; +{ + struct domain *dp; + struct protosw *pr; + + if (!sa) + return; + for (dp = domains; dp; dp = dp->dom_next) { + /* + * the check must be made by xx_ctlinput() anyways, to + * make sure we use data item pointed to by ctlparam in + * correct way. the following check is made just for safety. + */ + if (dp->dom_family != sa->sa_family) + continue; + + for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) + if (pr->pr_ctlinput) + (*pr->pr_ctlinput)(cmd, sa, ctlparam); + } +} + static void pfslowtimo(arg) void *arg; diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c index df9db286c016..b30975b6e938 100644 --- a/sys/kern/uipc_mbuf.c +++ b/sys/kern/uipc_mbuf.c @@ -570,15 +570,6 @@ m_freem(struct mbuf *m) if (m == NULL) return; do { - /* - * we do need to check non-first mbuf, since some of existing - * code does not call M_PREPEND properly. - * (example: call to bpf_mtap from drivers) - */ - if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.aux) { - m_freem(m->m_pkthdr.aux); - m->m_pkthdr.aux = NULL; - } MFREE(m, n); m = n; } while (m); diff --git a/sys/kern/uipc_mbuf2.c b/sys/kern/uipc_mbuf2.c index b00bf7f39f89..f2ce41fe56b5 100644 --- a/sys/kern/uipc_mbuf2.c +++ b/sys/kern/uipc_mbuf2.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: uipc_mbuf2.c,v 1.15 2000/02/22 14:01:37 itojun Exp $ */ +/* $KAME: uipc_mbuf2.c,v 1.29 2001/02/14 13:42:10 itojun Exp $ */ /* $NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej Exp $ */ /* @@ -75,6 +75,9 @@ #include <sys/mbuf.h> #include <sys/mutex.h> +/* can't call it m_dup(), as freebsd[34] uses m_dup() with different arg */ +static struct mbuf *m_dup1 __P((struct mbuf *, int, int, int)); + /* * ensure that [off, off + len) is contiguous on the mbuf chain "m". * packet chain before "off" is kept untouched. @@ -125,20 +128,47 @@ m_pulldown(struct mbuf *m, int off, int len, int *offp) } /* + * XXX: This code is flawed because it considers a "writable" mbuf + * data region to require all of the following: + * (i) mbuf _has_ to have M_EXT set; if it is just a regular + * mbuf, it is still not considered "writable." + * (ii) since mbuf has M_EXT, the ext_type _has_ to be + * EXT_CLUSTER. Anything else makes it non-writable. + * (iii) M_WRITABLE() must evaluate true. + * Ideally, the requirement should only be (iii). + * + * If we're writable, we're sure we're writable, because the ref. count + * cannot increase from 1, as that would require posession of mbuf + * n by someone else (which is impossible). However, if we're _not_ + * writable, we may eventually become writable )if the ref. count drops + * to 1), but we'll fail to notice it unless we re-evaluate + * M_WRITABLE(). For now, we only evaluate once at the beginning and + * live with this. + */ + /* + * XXX: This is dumb. If we're just a regular mbuf with no M_EXT, + * then we're not "writable," according to this code. + */ + writable = 0; + if ((n->m_flags & M_EXT) == 0 || + (n->m_ext.ext_type == EXT_CLUSTER && M_WRITABLE(n))) + writable = 1; + + /* * the target data is on <n, off>. * if we got enough data on the mbuf "n", we're done. */ - if ((off == 0 || offp) && len <= n->m_len - off) + if ((off == 0 || offp) && len <= n->m_len - off && writable) goto ok; /* - * when len < n->m_len - off and off != 0, it is a special case. + * when len <= n->m_len - off and off != 0, it is a special case. * len bytes from <n, off> sits in single mbuf, but the caller does * not like the starting position (off). * chop the current mbuf into two pieces, set off to 0. */ - if (len < n->m_len - off) { - o = m_copym(n, off, n->m_len - off, M_DONTWAIT); + if (len <= n->m_len - off) { + o = m_dup1(n, off, n->m_len - off, M_DONTWAIT); if (o == NULL) { m_freem(m); return NULL; /* ENOBUFS */ @@ -175,33 +205,6 @@ m_pulldown(struct mbuf *m, int off, int len, int *offp) * easy cases first. * we need to use m_copydata() to get data from <n->m_next, 0>. */ - /* - * XXX: This code is flawed because it considers a "writable" mbuf - * data region to require all of the following: - * (i) mbuf _has_ to have M_EXT set; if it is just a regular - * mbuf, it is still not considered "writable." - * (ii) since mbuf has M_EXT, the ext_type _has_ to be - * EXT_CLUSTER. Anything else makes it non-writable. - * (iii) M_WRITABLE() must evaluate true. - * Ideally, the requirement should only be (iii). - * - * If we're writable, we're sure we're writable, because the ref. count - * cannot increase from 1, as that would require posession of mbuf - * n by someone else (which is impossible). However, if we're _not_ - * writable, we may eventually become writable )if the ref. count drops - * to 1), but we'll fail to notice it unless we re-evaluate - * M_WRITABLE(). For now, we only evaluate once at the beginning and - * live with this. - */ - /* - * XXX: This is dumb. If we're just a regular mbuf with no M_EXT, - * then we're not "writable," according to this code. - */ - writable = 0; - if ((n->m_flags & M_EXT) && (n->m_ext.ext_type == EXT_CLUSTER) && - M_WRITABLE(n)) - writable = 1; - if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen && writable) { m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len); @@ -225,18 +228,17 @@ m_pulldown(struct mbuf *m, int off, int len, int *offp) * on both end. */ MGET(o, M_DONTWAIT, m->m_type); - if (o == NULL) { - m_freem(m); - return NULL; /* ENOBUFS */ - } - if (len > MHLEN) { /* use MHLEN just for safety */ + if (o && len > MLEN) { MCLGET(o, M_DONTWAIT); if ((o->m_flags & M_EXT) == 0) { - m_freem(m); m_free(o); - return NULL; /* ENOBUFS */ + o = NULL; } } + if (!o) { + m_freem(m); + return NULL; /* ENOBUFS */ + } /* get hlen from <n, off> into <o, 0> */ o->m_len = hlen; bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen); @@ -265,12 +267,46 @@ ok: return n; } +static struct mbuf * +m_dup1(struct mbuf *m, int off, int len, int wait) +{ + struct mbuf *n; + int l; + int copyhdr; + + if (len > MCLBYTES) + return NULL; + if (off == 0 && (m->m_flags & M_PKTHDR) != 0) { + copyhdr = 1; + MGETHDR(n, wait, m->m_type); + l = MHLEN; + } else { + copyhdr = 0; + MGET(n, wait, m->m_type); + l = MLEN; + } + if (n && len > l) { + MCLGET(n, wait); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } + if (!n) + return NULL; + + if (copyhdr) + M_COPY_PKTHDR(n, m); + m_copydata(m, off, len, mtod(n, caddr_t)); + return n; +} + /* * pkthdr.aux chain manipulation. * we don't allow clusters at this moment. */ struct mbuf * -m_aux_add(struct mbuf *m, int af, int type) +m_aux_add2(struct mbuf *m, int af, int type, void *p) { struct mbuf *n; struct mauxtag *t; @@ -287,8 +323,10 @@ m_aux_add(struct mbuf *m, int af, int type) return NULL; t = mtod(n, struct mauxtag *); + bzero(t, sizeof(*t)); t->af = af; t->type = type; + t->p = p; n->m_data += sizeof(struct mauxtag); n->m_len = 0; n->m_next = m->m_pkthdr.aux; @@ -297,7 +335,7 @@ m_aux_add(struct mbuf *m, int af, int type) } struct mbuf * -m_aux_find(struct mbuf *m, int af, int type) +m_aux_find2(struct mbuf *m, int af, int type, void *p) { struct mbuf *n; struct mauxtag *t; @@ -307,12 +345,30 @@ m_aux_find(struct mbuf *m, int af, int type) for (n = m->m_pkthdr.aux; n; n = n->m_next) { t = (struct mauxtag *)n->m_dat; - if (t->af == af && t->type == type) + if (n->m_data != ((caddr_t)t) + sizeof(struct mauxtag)) { + printf("m_aux_find: invalid m_data for mbuf=%p (%p %p)\n", n, t, n->m_data); + continue; + } + if (t->af == af && t->type == type && t->p == p) return n; } return NULL; } +struct mbuf * +m_aux_find(struct mbuf *m, int af, int type) +{ + + return m_aux_find2(m, af, type, NULL); +} + +struct mbuf * +m_aux_add(struct mbuf *m, int af, int type) +{ + + return m_aux_add2(m, af, type, NULL); +} + void m_aux_delete(struct mbuf *m, struct mbuf *victim) { @@ -327,6 +383,12 @@ m_aux_delete(struct mbuf *m, struct mbuf *victim) while (n) { t = (struct mauxtag *)n->m_dat; next = n->m_next; + if (n->m_data != ((caddr_t)t) + sizeof(struct mauxtag)) { + printf("m_aux_delete: invalid m_data for mbuf=%p (%p %p)\n", n, t, n->m_data); + prev = n; + n = next; + continue; + } if (n == victim) { if (prev) prev->m_next = n->m_next; diff --git a/sys/net/if.c b/sys/net/if.c index f98d85e763a5..f52d74db20c6 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -269,7 +269,7 @@ if_detach(ifp) #endif /* INET */ #ifdef INET6 if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { - in6_purgeaddr(ifa, ifp); + in6_purgeaddr(ifa); /* ifp_addrhead is already updated */ continue; } @@ -278,6 +278,16 @@ if_detach(ifp) IFAFREE(ifa); } +#ifdef INET6 + /* + * Remove all IPv6 kernel structs related to ifp. This should be done + * before removing routing entries below, since IPv6 interface direct + * routes are expected to be removed by the IPv6-specific kernel API. + * Otherwise, the kernel will detect some inconsistency and bark it. + */ + in6_ifdetach(ifp); +#endif + /* * Delete all remaining routes using this interface * Unfortuneatly the only way to do this is to slog through @@ -290,11 +300,6 @@ if_detach(ifp) (void) rnh->rnh_walktree(rnh, if_rtdel, ifp); } -#ifdef INET6 - /* nuke all IPv6 kernel structs related to ifp */ - in6_ifdetach(ifp); -#endif - TAILQ_REMOVE(&ifnet, ifp, if_link); mtx_destroy(&ifp->if_snd.ifq_mtx); splx(s); @@ -897,6 +902,7 @@ ifioctl(so, cmd, data, p) #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif + case SIOCSLIFPHYADDR: case SIOCSIFMEDIA: case SIOCSIFGENERIC: error = suser(p); @@ -913,6 +919,9 @@ ifioctl(so, cmd, data, p) ifs = (struct ifstat *)data; ifs->ascii[0] = '\0'; + case SIOCGIFPSRCADDR: + case SIOCGIFPDSTADDR: + case SIOCGLIFPHYADDR: case SIOCGIFMEDIA: case SIOCGIFGENERIC: if (ifp->if_ioctl == 0) diff --git a/sys/net/if_faith.c b/sys/net/if_faith.c index b28fecd899eb..840b77b4fc85 100644 --- a/sys/net/if_faith.c +++ b/sys/net/if_faith.c @@ -1,3 +1,5 @@ +/* $KAME: if_faith.c,v 1.21 2001/02/20 07:59:26 itojun Exp $ */ + /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. @@ -41,30 +43,58 @@ /* * Loopback interface driver for protocol testing and timing. */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "faith.h" +#if NFAITH > 0 #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/mbuf.h> #include <sys/socket.h> +#include <sys/errno.h> #include <sys/sockio.h> +#include <sys/time.h> +#include <sys/queue.h> #include <net/if.h> #include <net/if_types.h> #include <net/netisr.h> #include <net/route.h> #include <net/bpf.h> +#include <net/if_faith.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif + +#ifdef INET6 +#ifndef INET +#include <netinet/in.h> +#endif +#include <netinet6/in6_var.h> +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#endif + +#include "bpf.h" +#define NBPFILTER NBPF #include <net/net_osdep.h> -extern int loioctl __P((struct ifnet *, u_long, caddr_t)); -extern int looutput __P((struct ifnet *ifp, - struct mbuf *m, struct sockaddr *dst, struct rtentry *rt)); +static int faithioctl __P((struct ifnet *, u_long, caddr_t)); +int faithoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *)); +static void faithrtrequest __P((int, struct rtentry *, struct sockaddr *)); void faithattach __P((void *)); PSEUDO_SET(faithattach, if_faith); + static struct ifnet faithif[NFAITH]; #define FAITHMTU 1500 @@ -74,8 +104,8 @@ void faithattach(faith) void *faith; { - register struct ifnet *ifp; - register int i; + struct ifnet *ifp; + int i; for (i = 0; i < NFAITH; i++) { ifp = &faithif[i]; @@ -85,13 +115,210 @@ faithattach(faith) ifp->if_mtu = FAITHMTU; /* LOOPBACK commented out to announce IPv6 routes to faith */ ifp->if_flags = /* IFF_LOOPBACK | */ IFF_MULTICAST; - ifp->if_ioctl = loioctl; - ifp->if_output = looutput; + ifp->if_ioctl = faithioctl; + ifp->if_output = faithoutput; ifp->if_type = IFT_FAITH; ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_hdrlen = 0; ifp->if_addrlen = 0; if_attach(ifp); +#if NBPFILTER > 0 +#ifdef HAVE_OLD_BPF bpfattach(ifp, DLT_NULL, sizeof(u_int)); +#else + bpfattach(&ifp->if_bpf, ifp, DLT_NULL, sizeof(u_int)); +#endif +#endif + } +} + +int +faithoutput(ifp, m, dst, rt) + struct ifnet *ifp; + struct mbuf *m; + struct sockaddr *dst; + struct rtentry *rt; +{ + int isr; + struct ifqueue *ifq = 0; + + if ((m->m_flags & M_PKTHDR) == 0) + panic("faithoutput no HDR"); +#if NBPFILTER > 0 + /* BPF write needs to be handled specially */ + if (dst->sa_family == AF_UNSPEC) { + dst->sa_family = *(mtod(m, int *)); + m->m_len -= sizeof(int); + m->m_pkthdr.len -= sizeof(int); + m->m_data += sizeof(int); } + + if (ifp->if_bpf) { + /* + * We need to prepend the address family as + * a four byte field. Cons up a faith header + * to pacify bpf. This is safe because bpf + * will only read from the mbuf (i.e., it won't + * try to free it or keep a pointer a to it). + */ + struct mbuf m0; + u_int32_t af = dst->sa_family; + + m0.m_next = m; + m0.m_len = 4; + m0.m_data = (char *)⁡ + +#ifdef HAVE_OLD_BPF + bpf_mtap(ifp, &m0); +#else + bpf_mtap(ifp->if_bpf, &m0); +#endif + } +#endif + + if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { + m_freem(m); + return (rt->rt_flags & RTF_BLACKHOLE ? 0 : + rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); + } + ifp->if_opackets++; + ifp->if_obytes += m->m_pkthdr.len; + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + ifq = &ipintrq; + isr = NETISR_IP; + break; +#endif +#ifdef INET6 + case AF_INET6: + ifq = &ip6intrq; + isr = NETISR_IPV6; + break; +#endif + default: + m_freem(m); + return EAFNOSUPPORT; + } + + /* XXX do we need more sanity checks? */ + + m->m_pkthdr.rcvif = ifp; + ifp->if_ipackets++; + ifp->if_ibytes += m->m_pkthdr.len; + (void) IF_HANDOFF(ifq, m, NULL); + schednetisr(isr); + return (0); +} + +/* ARGSUSED */ +static void +faithrtrequest(cmd, rt, sa) + int cmd; + struct rtentry *rt; + struct sockaddr *sa; +{ + if (rt) { + rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; /* for ISO */ + /* + * For optimal performance, the send and receive buffers + * should be at least twice the MTU plus a little more for + * overhead. + */ + rt->rt_rmx.rmx_recvpipe = + rt->rt_rmx.rmx_sendpipe = 3 * FAITHMTU; + } +} + +/* + * Process an ioctl request. + */ +/* ARGSUSED */ +static int +faithioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct ifaddr *ifa; + struct ifreq *ifr = (struct ifreq *)data; + int error = 0; + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP | IFF_RUNNING; + ifa = (struct ifaddr *)data; + ifa->ifa_rtrequest = faithrtrequest; + /* + * Everything else is done at a higher level. + */ + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifr == 0) { + error = EAFNOSUPPORT; /* XXX */ + break; + } + switch (ifr->ifr_addr.sa_family) { +#ifdef INET + case AF_INET: + break; +#endif +#ifdef INET6 + case AF_INET6: + break; +#endif + + default: + error = EAFNOSUPPORT; + break; + } + break; + +#ifdef SIOCSIFMTU + case SIOCSIFMTU: + ifp->if_mtu = ifr->ifr_mtu; + break; +#endif + + case SIOCSIFFLAGS: + break; + + default: + error = EINVAL; + } + return (error); +} + +/* + * XXX could be slow + * XXX could be layer violation to call sys/net from sys/netinet6 + */ +int +faithprefix(in6) + struct in6_addr *in6; +{ + struct rtentry *rt; + struct sockaddr_in6 sin6; + int ret; + + if (ip6_keepfaith == 0) + return 0; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = *in6; + rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); + if (rt && rt->rt_ifp && rt->rt_ifp->if_type == IFT_FAITH && + (rt->rt_ifp->if_flags & IFF_UP) != 0) + ret = 1; + else + ret = 0; + if (rt) + RTFREE(rt); + return ret; } +#endif /* NFAITH > 0 */ diff --git a/sys/net/if_faith.h b/sys/net/if_faith.h new file mode 100644 index 000000000000..1e30bfbc760a --- /dev/null +++ b/sys/net/if_faith.h @@ -0,0 +1,41 @@ +/* $FreeBSD$ */ +/* $KAME: if_faith.h,v 1.1 2000/07/26 05:49:21 itojun Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NET_IF_FAITH_H_ +#define _NET_IF_FAITH_H_ + +#ifdef _KERNEL +struct in6_addr; +int faithprefix __P((struct in6_addr *)); +#endif + +#endif /* _NET_IF_FAITH_H_ */ diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c index 68031cb2cfe2..465063cd27f0 100644 --- a/sys/net/if_gif.c +++ b/sys/net/if_gif.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: if_gif.c,v 1.28 2000/06/20 12:30:03 jinmei Exp $ */ +/* $KAME: if_gif.c,v 1.47 2001/05/01 05:28:42 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -52,11 +52,11 @@ #include <net/route.h> #include <net/bpf.h> -#ifdef INET #include <netinet/in.h> #include <netinet/in_systm.h> -#include <netinet/in_var.h> #include <netinet/ip.h> +#ifdef INET +#include <netinet/in_var.h> #include <netinet/in_gif.h> #endif /* INET */ @@ -114,11 +114,11 @@ void gifattach(dummy) void *dummy; { - register struct gif_softc *sc; - register int i; + struct gif_softc *sc; + int i; ngif = NGIF; - gif = sc = malloc (ngif * sizeof(struct gif_softc), M_DEVBUF, M_WAITOK); + gif = sc = malloc(ngif * sizeof(struct gif_softc), M_DEVBUF, M_WAITOK); bzero(sc, ngif * sizeof(struct gif_softc)); for (i = 0; i < ngif; sc++, i++) { sc->gif_if.if_name = "gif"; @@ -148,6 +148,10 @@ gifattach(dummy) sc->gif_if.if_mtu = GIF_MTU; sc->gif_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; +#if 0 + /* turn off ingress filter */ + sc->gif_if.if_flags |= IFF_LINK2; +#endif sc->gif_if.if_ioctl = gif_ioctl; sc->gif_if.if_output = gif_output; sc->gif_if.if_type = IFT_GIF; @@ -229,7 +233,7 @@ gif_output(ifp, m, dst, rt) struct sockaddr *dst; struct rtentry *rt; /* added in net2 */ { - register struct gif_softc *sc = (struct gif_softc*)ifp; + struct gif_softc *sc = (struct gif_softc*)ifp; int error = 0; static int called = 0; /* XXX: MUTEX */ @@ -268,7 +272,7 @@ gif_output(ifp, m, dst, rt) * try to free it or keep a pointer a to it). */ struct mbuf m0; - u_int af = dst->sa_family; + u_int32_t af = dst->sa_family; m0.m_next = m; m0.m_len = 4; @@ -284,8 +288,11 @@ gif_output(ifp, m, dst, rt) ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; + /* inner AF-specific encapsulation */ + /* XXX should we check if our outer source is legal? */ + /* dispatch to output logic based on outer AF */ switch (sc->gif_psrc->sa_family) { #ifdef INET case AF_INET: @@ -300,11 +307,13 @@ gif_output(ifp, m, dst, rt) default: m_freem(m); error = ENETDOWN; + goto end; } end: called = 0; /* reset recursion counter */ - if (error) ifp->if_oerrors++; + if (error) + ifp->if_oerrors++; return error; } @@ -315,7 +324,7 @@ gif_input(m, af, gifp) struct ifnet *gifp; { int isr; - register struct ifqueue *ifq = 0; + struct ifqueue *ifq = 0; if (gifp == NULL) { /* just in case */ @@ -335,11 +344,11 @@ gif_input(m, af, gifp) * try to free it or keep a pointer a to it). */ struct mbuf m0; - u_int af = AF_INET6; + u_int32_t af1 = af; m0.m_next = m; m0.m_len = 4; - m0.m_data = (char *)⁡ + m0.m_data = (char *)&af1; #ifdef HAVE_OLD_BPF bpf_mtap(gifp, &m0); @@ -435,13 +444,16 @@ gif_ioctl(ifp, cmd, data) #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif /* INET6 */ + case SIOCSLIFPHYADDR: switch (cmd) { +#ifdef INET case SIOCSIFPHYADDR: src = (struct sockaddr *) &(((struct in_aliasreq *)data)->ifra_addr); dst = (struct sockaddr *) &(((struct in_aliasreq *)data)->ifra_dstaddr); break; +#endif #ifdef INET6 case SIOCSIFPHYADDR_IN6: src = (struct sockaddr *) @@ -450,6 +462,66 @@ gif_ioctl(ifp, cmd, data) &(((struct in6_aliasreq *)data)->ifra_dstaddr); break; #endif + case SIOCSLIFPHYADDR: + src = (struct sockaddr *) + &(((struct if_laddrreq *)data)->addr); + dst = (struct sockaddr *) + &(((struct if_laddrreq *)data)->dstaddr); + } + + /* sa_family must be equal */ + if (src->sa_family != dst->sa_family) + return EINVAL; + + /* validate sa_len */ + switch (src->sa_family) { +#ifdef INET + case AF_INET: + if (src->sa_len != sizeof(struct sockaddr_in)) + return EINVAL; + break; +#endif +#ifdef INET6 + case AF_INET6: + if (src->sa_len != sizeof(struct sockaddr_in6)) + return EINVAL; + break; +#endif + default: + return EAFNOSUPPORT; + } + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + if (dst->sa_len != sizeof(struct sockaddr_in)) + return EINVAL; + break; +#endif +#ifdef INET6 + case AF_INET6: + if (dst->sa_len != sizeof(struct sockaddr_in6)) + return EINVAL; + break; +#endif + default: + return EAFNOSUPPORT; + } + + /* check sa_family looks sane for the cmd */ + switch (cmd) { + case SIOCSIFPHYADDR: + if (src->sa_family == AF_INET) + break; + return EAFNOSUPPORT; +#ifdef INET6 + case SIOCSIFPHYADDR_IN6: + if (src->sa_family == AF_INET6) + break; + return EAFNOSUPPORT; +#endif /* INET6 */ + case SIOCSLIFPHYADDR: + /* checks done in the above */ + break; } for (i = 0; i < ngif; i++) { @@ -493,41 +565,16 @@ gif_ioctl(ifp, cmd, data) #endif } - if (src->sa_family != dst->sa_family || - src->sa_len != dst->sa_len) { - error = EINVAL; - break; - } - switch (src->sa_family) { -#ifdef INET - case AF_INET: - size = sizeof(struct sockaddr_in); - break; -#endif -#ifdef INET6 - case AF_INET6: - size = sizeof(struct sockaddr_in6); - break; -#endif - default: - error = EAFNOSUPPORT; - goto bad; - } - if (src->sa_len != size) { - error = EINVAL; - break; - } - if (sc->gif_psrc) free((caddr_t)sc->gif_psrc, M_IFADDR); - sa = (struct sockaddr *)malloc(size, M_IFADDR, M_WAITOK); - bcopy((caddr_t)src, (caddr_t)sa, size); + sa = (struct sockaddr *)malloc(src->sa_len, M_IFADDR, M_WAITOK); + bcopy((caddr_t)src, (caddr_t)sa, src->sa_len); sc->gif_psrc = sa; if (sc->gif_pdst) free((caddr_t)sc->gif_pdst, M_IFADDR); - sa = (struct sockaddr *)malloc(size, M_IFADDR, M_WAITOK); - bcopy((caddr_t)dst, (caddr_t)sa, size); + sa = (struct sockaddr *)malloc(dst->sa_len, M_IFADDR, M_WAITOK); + bcopy((caddr_t)dst, (caddr_t)sa, dst->sa_len); sc->gif_pdst = sa; ifp->if_flags |= IFF_RUNNING; @@ -548,7 +595,7 @@ gif_ioctl(ifp, cmd, data) free((caddr_t)sc->gif_pdst, M_IFADDR); sc->gif_pdst = NULL; } - /* change the IFF_UP flag as well? */ + /* change the IFF_{UP, RUNNING} flag as well? */ break; #endif @@ -561,25 +608,27 @@ gif_ioctl(ifp, cmd, data) goto bad; } src = sc->gif_psrc; - switch (sc->gif_psrc->sa_family) { + switch (cmd) { #ifdef INET - case AF_INET: + case SIOCGIFPSRCADDR: dst = &ifr->ifr_addr; - size = sizeof(struct sockaddr_in); + size = sizeof(ifr->ifr_addr); break; #endif /* INET */ #ifdef INET6 - case AF_INET6: + case SIOCGIFPSRCADDR_IN6: dst = (struct sockaddr *) &(((struct in6_ifreq *)data)->ifr_addr); - size = sizeof(struct sockaddr_in6); + size = sizeof(((struct in6_ifreq *)data)->ifr_addr); break; #endif /* INET6 */ default: error = EADDRNOTAVAIL; goto bad; } - bcopy((caddr_t)src, (caddr_t)dst, size); + if (src->sa_len > size) + return EINVAL; + bcopy((caddr_t)src, (caddr_t)dst, src->sa_len); break; case SIOCGIFPDSTADDR: @@ -591,25 +640,52 @@ gif_ioctl(ifp, cmd, data) goto bad; } src = sc->gif_pdst; - switch (sc->gif_pdst->sa_family) { + switch (cmd) { #ifdef INET - case AF_INET: + case SIOCGIFPDSTADDR: dst = &ifr->ifr_addr; - size = sizeof(struct sockaddr_in); + size = sizeof(ifr->ifr_addr); break; #endif /* INET */ #ifdef INET6 - case AF_INET6: + case SIOCGIFPDSTADDR_IN6: dst = (struct sockaddr *) &(((struct in6_ifreq *)data)->ifr_addr); - size = sizeof(struct sockaddr_in6); + size = sizeof(((struct in6_ifreq *)data)->ifr_addr); break; #endif /* INET6 */ default: error = EADDRNOTAVAIL; goto bad; } - bcopy((caddr_t)src, (caddr_t)dst, size); + if (src->sa_len > size) + return EINVAL; + bcopy((caddr_t)src, (caddr_t)dst, src->sa_len); + break; + + case SIOCGLIFPHYADDR: + if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) { + error = EADDRNOTAVAIL; + goto bad; + } + + /* copy src */ + src = sc->gif_psrc; + dst = (struct sockaddr *) + &(((struct if_laddrreq *)data)->addr); + size = sizeof(((struct if_laddrreq *)data)->addr); + if (src->sa_len > size) + return EINVAL; + bcopy((caddr_t)src, (caddr_t)dst, src->sa_len); + + /* copy dst */ + src = sc->gif_pdst; + dst = (struct sockaddr *) + &(((struct if_laddrreq *)data)->dstaddr); + size = sizeof(((struct if_laddrreq *)data)->dstaddr); + if (src->sa_len > size) + return EINVAL; + bcopy((caddr_t)src, (caddr_t)dst, src->sa_len); break; case SIOCSIFFLAGS: diff --git a/sys/net/if_gif.h b/sys/net/if_gif.h index 36992861898e..3978e7b13cf4 100644 --- a/sys/net/if_gif.h +++ b/sys/net/if_gif.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: if_gif.h,v 1.13 2000/06/17 20:34:24 itojun Exp $ */ +/* $KAME: if_gif.h,v 1.17 2000/09/11 11:36:41 sumikawa Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -38,11 +38,9 @@ #define _NET_IF_GIF_H_ -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__) #if defined(_KERNEL) && !defined(_LKM) #include "opt_inet.h" #endif -#endif #include <netinet/in.h> /* xxx sigh, why route have struct route instead of pointer? */ diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c index 0e9ac65577a7..f8e11f7a28c7 100644 --- a/sys/net/if_loop.c +++ b/sys/net/if_loop.c @@ -58,6 +58,7 @@ #include <net/netisr.h> #include <net/route.h> #include <net/bpf.h> +#include <net/bpfdesc.h> #ifdef INET #include <netinet/in.h> @@ -236,6 +237,8 @@ looutput(ifp, m, dst, rt) m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t)); n->m_pkthdr = m->m_pkthdr; n->m_len = m->m_pkthdr.len; + n->m_pkthdr.aux = m->m_pkthdr.aux; + m->m_pkthdr.aux = (struct mbuf *)NULL; m_freem(m); m = n; } @@ -277,7 +280,7 @@ contiguousfail: int if_simloop(ifp, m, af, hlen) struct ifnet *ifp; - register struct mbuf *m; + struct mbuf *m; int af; int hlen; { @@ -300,17 +303,19 @@ if_simloop(ifp, m, af, hlen) if (ifp->if_bpf) { struct mbuf m0, *n = m; - /* - * We need to prepend the address family as - * a four byte field. Cons up a dummy header - * to pacify bpf. This is safe because bpf - * will only read from the mbuf (i.e., it won't - * try to free it or keep a pointer a to it). - */ - m0.m_next = m; - m0.m_len = 4; - m0.m_data = (char *)⁡ - n = &m0; + if (ifp->if_bpf->bif_dlt == DLT_NULL) { + /* + * We need to prepend the address family as + * a four byte field. Cons up a dummy header + * to pacify bpf. This is safe because bpf + * will only read from the mbuf (i.e., it won't + * try to free it or keep a pointer a to it). + */ + m0.m_next = m; + m0.m_len = 4; + m0.m_data = (char *)⁡ + n = &m0; + } bpf_mtap(ifp, n); } diff --git a/sys/net/if_sppp.h b/sys/net/if_sppp.h index 84fede39716d..0c2a2011cd16 100644 --- a/sys/net/if_sppp.h +++ b/sys/net/if_sppp.h @@ -39,6 +39,7 @@ struct slcp { }; #define IDX_IPCP 1 /* idx into state table */ +#define IDX_IPV6CP 2 /* idx into state table */ struct sipcp { u_long opts; /* IPCP options to send (bitfield) */ @@ -46,6 +47,10 @@ struct sipcp { #define IPCP_HISADDR_SEEN 1 /* have seen his address already */ #define IPCP_MYADDR_DYN 2 /* my address is dynamically assigned */ #define IPCP_MYADDR_SEEN 4 /* have seen his address already */ +#ifdef notdef +#define IPV6CP_MYIFID_DYN 2 /* my ifid is dynamically assigned */ +#endif +#define IPV6CP_MYIFID_SEEN 4 /* have seen his ifid already */ }; #define AUTHNAMELEN 32 @@ -62,8 +67,8 @@ struct sauth { u_char challenge[AUTHKEYLEN]; /* random challenge */ }; -#define IDX_PAP 2 -#define IDX_CHAP 3 +#define IDX_PAP 3 +#define IDX_CHAP 4 #define IDX_COUNT (IDX_CHAP + 1) /* bump this when adding cp's! */ @@ -87,8 +92,8 @@ struct sppp { u_int pp_flags; /* sub modes */ u_short pp_alivecnt; /* keepalive packets counter */ u_short pp_loopcnt; /* loopback detection counter */ - u_long pp_seq; /* local sequence number */ - u_long pp_rseq; /* remote sequence number */ + u_long pp_seq[IDX_COUNT]; /* local sequence number */ + u_long pp_rseq[IDX_COUNT]; /* remote sequence number */ enum ppp_phase pp_phase; /* phase we're currently in */ int state[IDX_COUNT]; /* state machine */ u_char confid[IDX_COUNT]; /* id of last configuration request */ @@ -98,6 +103,7 @@ struct sppp { struct callout_handle pap_my_to_ch; /* PAP needs one more... */ struct slcp lcp; /* LCP params */ struct sipcp ipcp; /* IPCP params */ + struct sipcp ipv6cp; /* IPv6CP params */ struct sauth myauth; /* auth params, i'm peer */ struct sauth hisauth; /* auth params, i'm authenticator */ /* diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c index 6928c9576837..e489c58ee4e5 100644 --- a/sys/net/if_spppsubr.c +++ b/sys/net/if_spppsubr.c @@ -135,10 +135,12 @@ #define PPP_ISO 0x0023 /* ISO OSI Protocol */ #define PPP_XNS 0x0025 /* Xerox NS Protocol */ #define PPP_IPX 0x002b /* Novell IPX Protocol */ +#define PPP_IPV6 0x0057 /* Internet Protocol Version 6 */ #define PPP_LCP 0xc021 /* Link Control Protocol */ #define PPP_PAP 0xc023 /* Password Authentication Protocol */ #define PPP_CHAP 0xc223 /* Challenge-Handshake Auth Protocol */ #define PPP_IPCP 0x8021 /* Internet Protocol Control Protocol */ +#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */ #define CONF_REQ 1 /* PPP configure request */ #define CONF_ACK 2 /* PPP configure acknowledge */ @@ -165,6 +167,9 @@ #define IPCP_OPT_COMPRESSION 2 /* IP compression protocol (VJ) */ #define IPCP_OPT_ADDRESS 3 /* local IP address */ +#define IPV6CP_OPT_IFID 1 /* interface identifier */ +#define IPV6CP_OPT_COMPRESSION 2 /* IPv6 compression protocol */ + #define PAP_REQ 1 /* PAP name/password request */ #define PAP_ACK 2 /* PAP acknowledge */ #define PAP_NAK 3 /* PAP fail */ @@ -340,6 +345,21 @@ static void sppp_ipcp_tls(struct sppp *sp); static void sppp_ipcp_tlf(struct sppp *sp); static void sppp_ipcp_scr(struct sppp *sp); +static void sppp_ipv6cp_init(struct sppp *sp); +static void sppp_ipv6cp_up(struct sppp *sp); +static void sppp_ipv6cp_down(struct sppp *sp); +static void sppp_ipv6cp_open(struct sppp *sp); +static void sppp_ipv6cp_close(struct sppp *sp); +static void sppp_ipv6cp_TO(void *sp); +static int sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipv6cp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipv6cp_tlu(struct sppp *sp); +static void sppp_ipv6cp_tld(struct sppp *sp); +static void sppp_ipv6cp_tls(struct sppp *sp); +static void sppp_ipv6cp_tlf(struct sppp *sp); +static void sppp_ipv6cp_scr(struct sppp *sp); + static void sppp_pap_input(struct sppp *sp, struct mbuf *m); static void sppp_pap_init(struct sppp *sp); static void sppp_pap_open(struct sppp *sp); @@ -363,6 +383,9 @@ static const char *sppp_auth_type_name(u_short proto, u_char type); static const char *sppp_cp_type_name(u_char type); static const char *sppp_dotted_quad(u_long addr); static const char *sppp_ipcp_opt_name(u_char opt); +#ifdef INET6 +static const char *sppp_ipv6cp_opt_name(u_char opt); +#endif static const char *sppp_lcp_opt_name(u_char opt); static const char *sppp_phase_name(enum ppp_phase phase); static const char *sppp_proto_name(u_short proto); @@ -377,6 +400,15 @@ static void sppp_print_bytes(const u_char *p, u_short len); static void sppp_print_string(const char *p, u_short len); static void sppp_qflush(struct ifqueue *ifq); static void sppp_set_ip_addr(struct sppp *sp, u_long src); +#ifdef INET6 +static void sppp_get_ip6_addrs(struct sppp *sp, struct in6_addr *src, + struct in6_addr *dst, struct in6_addr *srcmask); +#ifdef IPV6CP_MYIFID_DYN +static void sppp_set_ip6_addr(struct sppp *sp, const struct in6_addr *src); +static void sppp_gen_ip6_addr(struct sppp *sp, const struct in6_addr *src); +#endif +static void sppp_suggest_ip6_addr(struct sppp *sp, struct in6_addr *src); +#endif /* our control protocol descriptors */ static const struct cp lcp = { @@ -395,6 +427,20 @@ static const struct cp ipcp = { sppp_ipcp_scr }; +static const struct cp ipv6cp = { + PPP_IPV6CP, IDX_IPV6CP, +#ifdef INET6 /*don't run IPv6CP if there's no IPv6 support*/ + CP_NCP, +#else + 0, +#endif + "ipv6cp", + sppp_ipv6cp_up, sppp_ipv6cp_down, sppp_ipv6cp_open, sppp_ipv6cp_close, + sppp_ipv6cp_TO, sppp_ipv6cp_RCR, sppp_ipv6cp_RCN_rej, sppp_ipv6cp_RCN_nak, + sppp_ipv6cp_tlu, sppp_ipv6cp_tld, sppp_ipv6cp_tls, sppp_ipv6cp_tlf, + sppp_ipv6cp_scr +}; + static const struct cp pap = { PPP_PAP, IDX_PAP, CP_AUTH, "pap", sppp_null, sppp_null, sppp_pap_open, sppp_pap_close, @@ -414,6 +460,7 @@ static const struct cp chap = { static const struct cp *cps[IDX_COUNT] = { &lcp, /* IDX_LCP */ &ipcp, /* IDX_IPCP */ + &ipv6cp, /* IDX_IPV6CP */ &pap, /* IDX_PAP */ &chap, /* IDX_CHAP */ }; @@ -499,7 +546,7 @@ sppp_input(struct ifnet *ifp, struct mbuf *m) h->address, h->control, ntohs(h->protocol)); if (sp->state[IDX_LCP] == STATE_OPENED) sppp_cp_send (sp, PPP_LCP, PROTO_REJ, - ++sp->pp_seq, m->m_pkthdr.len + 2, + ++sp->pp_seq[IDX_LCP], m->m_pkthdr.len + 2, &h->protocol); ++ifp->if_noproto; goto drop; @@ -530,6 +577,20 @@ sppp_input(struct ifnet *ifp, struct mbuf *m) } break; #endif +#ifdef INET6 + case PPP_IPV6CP: + if (sp->pp_phase == PHASE_NETWORK) + sppp_cp_input(&ipv6cp, sp, m); + m_freem (m); + return; + + case PPP_IPV6: + if (sp->state[IDX_IPV6CP] == STATE_OPENED) { + schednetisr (NETISR_IPV6); + inq = &ip6intrq; + } + break; +#endif #ifdef IPX case PPP_IPX: /* IPX IPXCP not implemented yet */ @@ -628,7 +689,7 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, { struct sppp *sp = (struct sppp*) ifp; struct ppp_header *h; - struct ifqueue *ifq; + struct ifqueue *ifq = NULL; int s, rv = 0; int debug = ifp->if_flags & IFF_DEBUG; @@ -652,7 +713,6 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, s = splimp(); } - ifq = &ifp->if_snd; #ifdef INET if (dst->sa_family == AF_INET) { /* XXX Check mbuf length here? */ @@ -699,6 +759,12 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, } #endif +#ifdef INET6 + if (dst->sa_family == AF_INET6) { + /* XXX do something tricky here? */ + } +#endif + /* * Prepend general data packet PPP header. For now, IP only. */ @@ -750,7 +816,18 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, if (sp->pp_mode == IFF_CISCO) h->protocol = htons (ETHERTYPE_IPV6); else { - goto nosupport; + /* + * Don't choke with an ENETDOWN early. It's + * possible that we just started dialing out, + * so don't drop the packet immediately. If + * we notice that we run out of buffer space + * below, we will however remember that we are + * not ready to carry IP packets, and return + * ENETDOWN, as opposed to ENOBUFS. + */ + h->protocol = htons(PPP_IPV6); + if (sp->state[IDX_IPV6CP] != STATE_OPENED) + rv = ENETDOWN; } break; #endif @@ -812,8 +889,8 @@ sppp_attach(struct ifnet *ifp) sp->pp_cpq.ifq_maxlen = 20; sp->pp_loopcnt = 0; sp->pp_alivecnt = 0; - sp->pp_seq = 0; - sp->pp_rseq = 0; + bzero(&sp->pp_seq[0], sizeof(sp->pp_seq)); + bzero(&sp->pp_rseq[0], sizeof(sp->pp_rseq)); sp->pp_phase = PHASE_DEAD; sp->pp_up = lcp.Up; sp->pp_down = lcp.Down; @@ -822,6 +899,7 @@ sppp_attach(struct ifnet *ifp) sppp_lcp_init(sp); sppp_ipcp_init(sp); + sppp_ipv6cp_init(sp); sppp_pap_init(sp); sppp_chap_init(sp); } @@ -1079,8 +1157,8 @@ sppp_cisco_input(struct sppp *sp, struct mbuf *m) break; case CISCO_KEEPALIVE_REQ: sp->pp_alivecnt = 0; - sp->pp_rseq = ntohl (h->par1); - if (sp->pp_seq == sp->pp_rseq) { + sp->pp_rseq[IDX_LCP] = ntohl (h->par1); + if (sp->pp_seq[IDX_LCP] == sp->pp_rseq[IDX_LCP]) { /* Local and remote sequence numbers are equal. * Probably, the line is in loopback mode. */ if (sp->pp_loopcnt >= MAXALIVECNT) { @@ -1096,9 +1174,9 @@ sppp_cisco_input(struct sppp *sp, struct mbuf *m) /* Generate new local sequence number */ #if defined(__FreeBSD__) && __FreeBSD__ >= 3 - sp->pp_seq = random(); + sp->pp_seq[IDX_LCP] = random(); #else - sp->pp_seq ^= time.tv_sec ^ time.tv_usec; + sp->pp_seq[IDX_LCP] ^= time.tv_sec ^ time.tv_usec; #endif break; } @@ -1574,8 +1652,8 @@ sppp_cp_input(const struct cp *cp, struct sppp *sp, struct mbuf *m) if (debug) log(-1, SPP_FMT "%s send code-rej for 0x%x\n", SPP_ARGS(ifp), cp->name, h->type); - sppp_cp_send(sp, cp->proto, CODE_REJ, ++sp->pp_seq, - m->m_pkthdr.len, h); + sppp_cp_send(sp, cp->proto, CODE_REJ, + ++sp->pp_seq[cp->protoidx], m->m_pkthdr.len, h); ++ifp->if_ierrors; } } @@ -1730,7 +1808,8 @@ sppp_close_event(const struct cp *cp, struct sppp *sp) case STATE_ACK_RCVD: case STATE_ACK_SENT: sp->rst_counter[cp->protoidx] = sp->lcp.max_terminate; - sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, 0, 0); + sppp_cp_send(sp, cp->proto, TERM_REQ, + ++sp->pp_seq[cp->protoidx], 0, 0); sppp_cp_change_state(cp, sp, STATE_CLOSING); break; } @@ -1772,8 +1851,8 @@ sppp_to_event(const struct cp *cp, struct sppp *sp) switch (sp->state[cp->protoidx]) { case STATE_CLOSING: case STATE_STOPPING: - sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, - 0, 0); + sppp_cp_send(sp, cp->proto, TERM_REQ, + ++sp->pp_seq[cp->protoidx], 0, 0); TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout, sp->ch[cp->protoidx]); break; @@ -1835,6 +1914,8 @@ sppp_lcp_init(struct sppp *sp) sp->lcp.magic = 0; sp->state[IDX_LCP] = STATE_INITIAL; sp->fail_counter[IDX_LCP] = 0; + sp->pp_seq[IDX_LCP] = 0; + sp->pp_rseq[IDX_LCP] = 0; sp->lcp.protos = 0; sp->lcp.mru = sp->lcp.their_mru = PP_MTU; @@ -2468,7 +2549,7 @@ sppp_lcp_scr(struct sppp *sp) opt[i++] = CHAP_MD5; } - sp->confid[IDX_LCP] = ++sp->pp_seq; + sp->confid[IDX_LCP] = ++sp->pp_seq[IDX_LCP]; sppp_cp_send (sp, PPP_LCP, CONF_REQ, sp->confid[IDX_LCP], i, &opt); } @@ -2519,6 +2600,8 @@ sppp_ipcp_init(struct sppp *sp) sp->ipcp.flags = 0; sp->state[IDX_IPCP] = STATE_INITIAL; sp->fail_counter[IDX_IPCP] = 0; + sp->pp_seq[IDX_IPCP] = 0; + sp->pp_rseq[IDX_IPCP] = 0; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 callout_handle_init(&sp->ch[IDX_IPCP]); #endif @@ -2894,13 +2977,516 @@ sppp_ipcp_scr(struct sppp *sp) opt[i++] = ouraddr; } - sp->confid[IDX_IPCP] = ++sp->pp_seq; + sp->confid[IDX_IPCP] = ++sp->pp_seq[IDX_IPCP]; sppp_cp_send(sp, PPP_IPCP, CONF_REQ, sp->confid[IDX_IPCP], i, &opt); } /* *--------------------------------------------------------------------------* * * + * The IPv6CP implementation. * + * * + *--------------------------------------------------------------------------* + */ + +#ifdef INET6 +static void +sppp_ipv6cp_init(struct sppp *sp) +{ + sp->ipv6cp.opts = 0; + sp->ipv6cp.flags = 0; + sp->state[IDX_IPV6CP] = STATE_INITIAL; + sp->fail_counter[IDX_IPV6CP] = 0; + sp->pp_seq[IDX_IPV6CP] = 0; + sp->pp_rseq[IDX_IPV6CP] = 0; +#if defined(__NetBSD__) + callout_init(&sp->ch[IDX_IPV6CP]); +#endif +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + callout_handle_init(&sp->ch[IDX_IPV6CP]); +#endif +} + +static void +sppp_ipv6cp_up(struct sppp *sp) +{ + sppp_up_event(&ipv6cp, sp); +} + +static void +sppp_ipv6cp_down(struct sppp *sp) +{ + sppp_down_event(&ipv6cp, sp); +} + +static void +sppp_ipv6cp_open(struct sppp *sp) +{ + STDDCL; + struct in6_addr myaddr, hisaddr; + +#ifdef IPV6CP_MYIFID_DYN + sp->ipv6cp.flags &= ~(IPV6CP_MYIFID_SEEN|IPV6CP_MYIFID_DYN); +#else + sp->ipv6cp.flags &= ~IPV6CP_MYIFID_SEEN; +#endif + + sppp_get_ip6_addrs(sp, &myaddr, &hisaddr, 0); + /* + * If we don't have our address, this probably means our + * interface doesn't want to talk IPv6 at all. (This could + * be the case if somebody wants to speak only IPX, for + * example.) Don't open IPv6CP in this case. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&myaddr)) { + /* XXX this message should go away */ + if (debug) + log(LOG_DEBUG, SPP_FMT "ipv6cp_open(): no IPv6 interface\n", + SPP_ARGS(ifp)); + return; + } + + sp->ipv6cp.flags |= IPV6CP_MYIFID_SEEN; + sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID); + sppp_open_event(&ipv6cp, sp); +} + +static void +sppp_ipv6cp_close(struct sppp *sp) +{ + sppp_close_event(&ipv6cp, sp); +} + +static void +sppp_ipv6cp_TO(void *cookie) +{ + sppp_to_event(&ipv6cp, (struct sppp *)cookie); +} + +/* + * Analyze a configure request. Return true if it was agreeable, and + * caused action sca, false if it has been rejected or nak'ed, and + * caused action scn. (The return value is used to make the state + * transition decision in the state automaton.) + */ +static int +sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *r, *p; + struct ifnet *ifp = &sp->pp_if; + int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG; + struct in6_addr myaddr, desiredaddr, suggestaddr; + int ifidcount; + int type; + int collision, nohisaddr; + + len -= 4; + origlen = len; + /* + * Make sure to allocate a buf that can at least hold a + * conf-nak with an `address' option. We might need it below. + */ + buf = r = malloc ((len < 6? 6: len), M_TEMP, M_NOWAIT); + if (! buf) + return (0); + + /* pass 1: see if we can recognize them */ + if (debug) + log(LOG_DEBUG, SPP_FMT "ipv6cp parse opts:", + SPP_ARGS(ifp)); + p = (void*) (h+1); + ifidcount = 0; + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog(" %s", sppp_ipv6cp_opt_name(*p)); + switch (*p) { + case IPV6CP_OPT_IFID: + if (len >= 10 && p[1] == 10 && ifidcount == 0) { + /* correctly formed address option */ + ifidcount++; + continue; + } + if (debug) + addlog(" [invalid]"); + break; +#ifdef notyet + case IPV6CP_OPT_COMPRESSION: + if (len >= 4 && p[1] >= 4) { + /* correctly formed compress option */ + continue; + } + if (debug) + addlog(" [invalid]"); + break; +#endif + default: + /* Others not supported. */ + if (debug) + addlog(" [rej]"); + break; + } + /* Add the option to rejected list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + if (rlen) { + if (debug) + addlog(" send conf-rej\n"); + sppp_cp_send (sp, PPP_IPV6CP, CONF_REJ, h->ident, rlen, buf); + goto end; + } else if (debug) + addlog("\n"); + + /* pass 2: parse option values */ + sppp_get_ip6_addrs(sp, &myaddr, 0, 0); + if (debug) + log(LOG_DEBUG, SPP_FMT "ipv6cp parse opt values: ", + SPP_ARGS(ifp)); + p = (void*) (h+1); + len = origlen; + type = CONF_ACK; + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog(" %s", sppp_ipv6cp_opt_name(*p)); + switch (*p) { +#ifdef notyet + case IPV6CP_OPT_COMPRESSION: + continue; +#endif + case IPV6CP_OPT_IFID: + bzero(&desiredaddr, sizeof(desiredaddr)); + bcopy(&p[2], &desiredaddr.s6_addr[8], 8); + collision = (bcmp(&desiredaddr.s6_addr[8], + &myaddr.s6_addr[8], 8) == 0); + nohisaddr = IN6_IS_ADDR_UNSPECIFIED(&desiredaddr); + + desiredaddr.s6_addr16[0] = htons(0xfe80); + desiredaddr.s6_addr16[1] = htons(sp->pp_if.if_index); + + if (!collision && !nohisaddr) { + /* no collision, hisaddr known - Conf-Ack */ + type = CONF_ACK; + + if (debug) { + addlog(" %s [%s]", + ip6_sprintf(&desiredaddr), + sppp_cp_type_name(type)); + } + continue; + } + + bzero(&suggestaddr, sizeof(&suggestaddr)); + if (collision && nohisaddr) { + /* collision, hisaddr unknown - Conf-Rej */ + type = CONF_REJ; + bzero(&p[2], 8); + } else { + /* + * - no collision, hisaddr unknown, or + * - collision, hisaddr known + * Conf-Nak, suggest hisaddr + */ + type = CONF_NAK; + sppp_suggest_ip6_addr(sp, &suggestaddr); + bcopy(&suggestaddr.s6_addr[8], &p[2], 8); + } + if (debug) + addlog(" %s [%s]", ip6_sprintf(&desiredaddr), + sppp_cp_type_name(type)); + break; + } + /* Add the option to nak'ed list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + + if (rlen == 0 && type == CONF_ACK) { + if (debug) + addlog(" send %s\n", sppp_cp_type_name(type)); + sppp_cp_send (sp, PPP_IPV6CP, type, h->ident, origlen, h+1); + } else { +#ifdef DIAGNOSTIC + if (type == CONF_ACK) + panic("IPv6CP RCR: CONF_ACK with non-zero rlen"); +#endif + + if (debug) { + addlog(" send %s suggest %s\n", + sppp_cp_type_name(type), ip6_sprintf(&suggestaddr)); + } + sppp_cp_send (sp, PPP_IPV6CP, type, h->ident, rlen, buf); + } + + end: + free (buf, M_TEMP); + return (rlen == 0); +} + +/* + * Analyze the IPv6CP Configure-Reject option list, and adjust our + * negotiation. + */ +static void +sppp_ipv6cp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *p; + struct ifnet *ifp = &sp->pp_if; + int debug = ifp->if_flags & IFF_DEBUG; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, SPP_FMT "ipv6cp rej opts:", + SPP_ARGS(ifp)); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { + if (debug) + addlog(" %s", sppp_ipv6cp_opt_name(*p)); + switch (*p) { + case IPV6CP_OPT_IFID: + /* + * Peer doesn't grok address option. This is + * bad. XXX Should we better give up here? + */ + sp->ipv6cp.opts &= ~(1 << IPV6CP_OPT_IFID); + break; +#ifdef notyet + case IPV6CP_OPT_COMPRESS: + sp->ipv6cp.opts &= ~(1 << IPV6CP_OPT_COMPRESS); + break; +#endif + } + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} + +/* + * Analyze the IPv6CP Configure-NAK option list, and adjust our + * negotiation. + */ +static void +sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *p; + struct ifnet *ifp = &sp->pp_if; + int debug = ifp->if_flags & IFF_DEBUG; + struct in6_addr suggestaddr; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, SPP_FMT "ipv6cp nak opts:", + SPP_ARGS(ifp)); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { + if (debug) + addlog(" %s", sppp_ipv6cp_opt_name(*p)); + switch (*p) { + case IPV6CP_OPT_IFID: + /* + * Peer doesn't like our local ifid. See + * if we can do something for him. We'll drop + * him our address then. + */ + if (len < 10 || p[1] != 10) + break; + bzero(&suggestaddr, sizeof(suggestaddr)); + suggestaddr.s6_addr16[0] = htons(0xfe80); + suggestaddr.s6_addr16[1] = htons(sp->pp_if.if_index); + bcopy(&p[2], &suggestaddr.s6_addr[8], 8); + + sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID); + if (debug) + addlog(" [suggestaddr %s]", + ip6_sprintf(&suggestaddr)); +#ifdef IPV6CP_MYIFID_DYN + /* + * When doing dynamic address assignment, + * we accept his offer. + */ + if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN) { + struct in6_addr lastsuggest; + /* + * If <suggested myaddr from peer> equals to + * <hisaddr we have suggested last time>, + * we have a collision. generate new random + * ifid. + */ + sppp_suggest_ip6_addr(&lastsuggest); + if (IN6_ARE_ADDR_EQUAL(&suggestaddr, + lastsuggest)) { + if (debug) + addlog(" [random]"); + sppp_gen_ip6_addr(sp, &suggestaddr); + } + sppp_set_ip6_addr(sp, &suggestaddr, 0); + if (debug) + addlog(" [agree]"); + sp->ipv6cp.flags |= IPV6CP_MYIFID_SEEN; + } +#else + /* + * Since we do not do dynamic address assignment, + * we ignore it and thus continue to negotiate + * our already existing value. This can possibly + * go into infinite request-reject loop. + * + * This is not likely because we normally use + * ifid based on MAC-address. + * If you have no ethernet card on the node, too bad. + * XXX should we use fail_counter? + */ +#endif + break; +#ifdef notyet + case IPV6CP_OPT_COMPRESS: + /* + * Peer wants different compression parameters. + */ + break; +#endif + } + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} +static void +sppp_ipv6cp_tlu(struct sppp *sp) +{ + /* we are up - notify isdn daemon */ + if (sp->pp_con) + sp->pp_con(sp); +} + +static void +sppp_ipv6cp_tld(struct sppp *sp) +{ +} + +static void +sppp_ipv6cp_tls(struct sppp *sp) +{ + /* indicate to LCP that it must stay alive */ + sp->lcp.protos |= (1 << IDX_IPV6CP); +} + +static void +sppp_ipv6cp_tlf(struct sppp *sp) +{ + +#if 0 /* need #if 0 to close IPv6CP properly */ + /* we no longer need LCP */ + sp->lcp.protos &= ~(1 << IDX_IPV6CP); + sppp_lcp_check_and_close(sp); +#endif +} + +static void +sppp_ipv6cp_scr(struct sppp *sp) +{ + char opt[10 /* ifid */ + 4 /* compression, minimum */]; + struct in6_addr ouraddr; + int i = 0; + + if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_IFID)) { + sppp_get_ip6_addrs(sp, &ouraddr, 0, 0); + opt[i++] = IPV6CP_OPT_IFID; + opt[i++] = 10; + bcopy(&ouraddr.s6_addr[8], &opt[i], 8); + i += 8; + } + +#ifdef notyet + if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_COMPRESSION)) { + opt[i++] = IPV6CP_OPT_COMPRESSION; + opt[i++] = 4; + opt[i++] = 0; /* TBD */ + opt[i++] = 0; /* TBD */ + /* variable length data may follow */ + } +#endif + + sp->confid[IDX_IPV6CP] = ++sp->pp_seq[IDX_IPV6CP]; + sppp_cp_send(sp, PPP_IPV6CP, CONF_REQ, sp->confid[IDX_IPV6CP], i, &opt); +} +#else /*INET6*/ +static void sppp_ipv6cp_init(struct sppp *sp) +{ +} + +static void sppp_ipv6cp_up(struct sppp *sp) +{ +} + +static void sppp_ipv6cp_down(struct sppp *sp) +{ +} + + +static void sppp_ipv6cp_open(struct sppp *sp) +{ +} + +static void sppp_ipv6cp_close(struct sppp *sp) +{ +} + +static void sppp_ipv6cp_TO(void *sp) +{ +} + +static int sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len) +{ + return 0; +} + +static void sppp_ipv6cp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) +{ +} + +static void sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) +{ +} + +static void sppp_ipv6cp_tlu(struct sppp *sp) +{ +} + +static void sppp_ipv6cp_tld(struct sppp *sp) +{ +} + +static void sppp_ipv6cp_tls(struct sppp *sp) +{ +} + +static void sppp_ipv6cp_tlf(struct sppp *sp) +{ +} + +static void sppp_ipv6cp_scr(struct sppp *sp) +{ +} +#endif /*INET6*/ + +/* + *--------------------------------------------------------------------------* + * * * The CHAP implementation. * * * *--------------------------------------------------------------------------* @@ -3213,6 +3799,8 @@ sppp_chap_init(struct sppp *sp) /* Chap doesn't have STATE_INITIAL at all. */ sp->state[IDX_CHAP] = STATE_CLOSED; sp->fail_counter[IDX_CHAP] = 0; + sp->pp_seq[IDX_CHAP] = 0; + sp->pp_rseq[IDX_CHAP] = 0; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 callout_handle_init(&sp->ch[IDX_CHAP]); #endif @@ -3371,7 +3959,7 @@ sppp_chap_scr(struct sppp *sp) ch[3] = seed ^ random(); clen = AUTHKEYLEN; - sp->confid[IDX_CHAP] = ++sp->pp_seq; + sp->confid[IDX_CHAP] = ++sp->pp_seq[IDX_CHAP]; sppp_auth_send(&chap, sp, CHAP_CHALLENGE, sp->confid[IDX_CHAP], sizeof clen, (const char *)&clen, @@ -3544,6 +4132,8 @@ sppp_pap_init(struct sppp *sp) /* PAP doesn't have STATE_INITIAL at all. */ sp->state[IDX_PAP] = STATE_CLOSED; sp->fail_counter[IDX_PAP] = 0; + sp->pp_seq[IDX_PAP] = 0; + sp->pp_rseq[IDX_PAP] = 0; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 callout_handle_init(&sp->ch[IDX_PAP]); callout_handle_init(&sp->pap_my_to_ch); @@ -3678,7 +4268,7 @@ sppp_pap_scr(struct sppp *sp) { u_char idlen, pwdlen; - sp->confid[IDX_PAP] = ++sp->pp_seq; + sp->confid[IDX_PAP] = ++sp->pp_seq[IDX_PAP]; pwdlen = sppp_strnlen(sp->myauth.secret, AUTHKEYLEN); idlen = sppp_strnlen(sp->myauth.name, AUTHNAMELEN); @@ -3824,11 +4414,11 @@ sppp_keepalive(void *dummy) if (sp->pp_alivecnt <= MAXALIVECNT) ++sp->pp_alivecnt; if (sp->pp_mode == IFF_CISCO) - sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq, - sp->pp_rseq); + sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, + ++sp->pp_seq[IDX_LCP], sp->pp_rseq[IDX_LCP]); else if (sp->pp_phase >= PHASE_AUTHENTICATE) { long nmagic = htonl (sp->lcp.magic); - sp->lcp.echoid = ++sp->pp_seq; + sp->lcp.echoid = ++sp->pp_seq[IDX_LCP]; sppp_cp_send (sp, PPP_LCP, ECHO_REQ, sp->lcp.echoid, 4, &nmagic); } @@ -3957,8 +4547,154 @@ sppp_set_ip_addr(struct sppp *sp, u_long src) } #endif } +} + +#ifdef INET6 +/* + * Get both IPv6 addresses. + */ +static void +sppp_get_ip6_addrs(struct sppp *sp, struct in6_addr *src, struct in6_addr *dst, + struct in6_addr *srcmask) +{ + struct ifnet *ifp = &sp->pp_if; + struct ifaddr *ifa; + struct sockaddr_in6 *si, *sm; + struct in6_addr ssrc, ddst; + + sm = NULL; + bzero(&ssrc, sizeof(ssrc)); + bzero(&ddst, sizeof(ddst)); + /* + * Pick the first link-local AF_INET6 address from the list, + * aliases don't make any sense on a p2p link anyway. + */ +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + for (ifa = ifp->if_addrhead.tqh_first, si = 0; + ifa; + ifa = ifa->ifa_link.tqe_next) +#elif defined(__NetBSD__) || defined (__OpenBSD__) + for (ifa = ifp->if_addrlist.tqh_first, si = 0; + ifa; + ifa = ifa->ifa_list.tqe_next) +#else + for (ifa = ifp->if_addrlist, si = 0; + ifa; + ifa = ifa->ifa_next) +#endif + if (ifa->ifa_addr->sa_family == AF_INET6) { + si = (struct sockaddr_in6 *)ifa->ifa_addr; + sm = (struct sockaddr_in6 *)ifa->ifa_netmask; + if (si && IN6_IS_ADDR_LINKLOCAL(&si->sin6_addr)) + break; + } + if (ifa) { + if (si && !IN6_IS_ADDR_UNSPECIFIED(&si->sin6_addr)) { + bcopy(&si->sin6_addr, &ssrc, sizeof(ssrc)); + if (srcmask) { + bcopy(&sm->sin6_addr, srcmask, + sizeof(*srcmask)); + } + } + + si = (struct sockaddr_in6 *)ifa->ifa_dstaddr; + if (si && !IN6_IS_ADDR_UNSPECIFIED(&si->sin6_addr)) + bcopy(&si->sin6_addr, &ddst, sizeof(ddst)); + } + + if (dst) + bcopy(&ddst, dst, sizeof(*dst)); + if (src) + bcopy(&ssrc, src, sizeof(*src)); } +#ifdef IPV6CP_MYIFID_DYN +/* + * Generate random ifid. + */ +static void +sppp_gen_ip6_addr(struct sppp *sp, struct in6_addr *addr) +{ + /* TBD */ +} + +/* + * Set my IPv6 address. Must be called at splimp. + */ +static void +sppp_set_ip6_addr(struct sppp *sp, const struct in6_addr *src) +{ + STDDCL; + struct ifaddr *ifa; + struct sockaddr_in6 *sin6; + + /* + * Pick the first link-local AF_INET6 address from the list, + * aliases don't make any sense on a p2p link anyway. + */ + + sin6 = NULL; +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + for (ifa = ifp->if_addrhead.tqh_first; + ifa; + ifa = ifa->ifa_link.tqe_next) +#elif defined(__NetBSD__) || defined (__OpenBSD__) + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) +#else + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#endif + { + if (ifa->ifa_addr->sa_family == AF_INET6) + { + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (sin6 && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + break; + } + } + + if (ifa && sin6) + { + int error; + struct sockaddr_in6 new_sin6 = *sin6; + + bcopy(src, &new_sin6.sin6_addr, sizeof(new_sin6.sin6_addr)); + error = in6_ifinit(ifp, ifatoia6(ifa), &new_sin6, 1); + if (debug && error) + { + log(LOG_DEBUG, SPP_FMT "sppp_set_ip6_addr: in6_ifinit " + " failed, error=%d\n", SPP_ARGS(ifp), error); + } + } +} +#endif + +/* + * Suggest a candidate address to be used by peer. + */ +static void +sppp_suggest_ip6_addr(struct sppp *sp, struct in6_addr *suggest) +{ + struct in6_addr myaddr; + struct timeval tv; + + sppp_get_ip6_addrs(sp, &myaddr, 0, 0); + + myaddr.s6_addr[8] &= ~0x02; /* u bit to "local" */ + microtime(&tv); + if ((tv.tv_usec & 0xff) == 0 && (tv.tv_sec & 0xff) == 0) { + myaddr.s6_addr[14] ^= 0xff; + myaddr.s6_addr[15] ^= 0xff; + } else { + myaddr.s6_addr[14] ^= (tv.tv_usec & 0xff); + myaddr.s6_addr[15] ^= (tv.tv_sec & 0xff); + } + if (suggest) + bcopy(&myaddr, suggest, sizeof(myaddr)); +} +#endif /*INET6*/ + static int sppp_params(struct sppp *sp, u_long cmd, void *data) { @@ -4165,6 +4901,20 @@ sppp_ipcp_opt_name(u_char opt) return buf; } +#ifdef INET6 +static const char * +sppp_ipv6cp_opt_name(u_char opt) +{ + static char buf[12]; + switch (opt) { + case IPV6CP_OPT_IFID: return "ifid"; + case IPV6CP_OPT_COMPRESSION: return "compression"; + } + sprintf (buf, "0x%x", opt); + return buf; +} +#endif + static const char * sppp_state_name(int state) { @@ -4205,6 +4955,7 @@ sppp_proto_name(u_short proto) case PPP_IPCP: return "ipcp"; case PPP_PAP: return "pap"; case PPP_CHAP: return "chap"; + case PPP_IPV6CP: return "ipv6cp"; } snprintf(buf, sizeof(buf), "proto/0x%x", (unsigned)proto); return buf; diff --git a/sys/net/if_stf.c b/sys/net/if_stf.c index d6a64958ee53..979e6c5ea778 100644 --- a/sys/net/if_stf.c +++ b/sys/net/if_stf.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: if_stf.c,v 1.42 2000/08/15 07:24:23 itojun Exp $ */ +/* $KAME: if_stf.c,v 1.60 2001/05/03 14:51:47 itojun Exp $ */ /* * Copyright (C) 2000 WIDE Project. @@ -31,7 +31,7 @@ */ /* - * 6to4 interface, based on draft-ietf-ngtrans-6to4-06.txt. + * 6to4 interface, based on RFC3056. * * 6to4 interface is NOT capable of link-layer (I mean, IPv4) multicasting. * There is no address mapping defined from IPv6 multicast address to IPv4 @@ -60,12 +60,13 @@ * ICMPv6: * - Redirects cannot be used due to the lack of link-local address. * - * Starting from 04 draft, the specification suggests how to construct - * link-local address for 6to4 interface. - * However, it seems to have no real use and does not help the above symptom - * much. Even if we assign link-locals to interface, we cannot really - * use link-local unicast/multicast on top of 6to4 cloud, and the above - * analysis does not change. + * stf interface does not have, and will not need, a link-local address. + * It seems to have no real benefit and does not help the above symptoms much. + * Even if we assign link-locals to interface, we cannot really + * use link-local unicast/multicast on top of 6to4 cloud (since there's no + * encapsulation defined for link-local address), and the above analysis does + * not change. RFC3056 does not mandate the assignment of link-local address + * either. * * 6to4 interface has security issues. Refer to * http://playground.iijlab.net/i-d/draft-itojun-ipv6-transition-abuse-00.txt @@ -159,8 +160,10 @@ static int stf_encapcheck __P((const struct mbuf *, int, int, void *)); static struct in6_ifaddr *stf_getsrcifa6 __P((struct ifnet *)); static int stf_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); -static int stf_checkaddr4 __P((struct in_addr *, struct ifnet *)); -static int stf_checkaddr6 __P((struct in6_addr *, struct ifnet *)); +static int stf_checkaddr4 __P((struct stf_softc *, struct in_addr *, + struct ifnet *)); +static int stf_checkaddr6 __P((struct stf_softc *, struct in6_addr *, + struct ifnet *)); static void stf_rtrequest __P((int, struct rtentry *, struct sockaddr *)); static int stf_ioctl __P((struct ifnet *, u_long, caddr_t)); @@ -197,6 +200,10 @@ stfattach(dummy) sc->sc_if.if_ioctl = stf_ioctl; sc->sc_if.if_output = stf_output; sc->sc_if.if_type = IFT_STF; +#if 0 + /* turn off ingress filter */ + sc->sc_if.if_flags |= IFF_LINK2; +#endif sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; if_attach(&sc->sc_if); #if NBPFILTER > 0 @@ -230,6 +237,10 @@ stf_encapcheck(m, off, proto, arg) if ((sc->sc_if.if_flags & IFF_UP) == 0) return 0; + /* IFF_LINK0 means "no decapsulation" */ + if ((sc->sc_if.if_flags & IFF_LINK0) != 0) + return 0; + if (proto != IPPROTO_IPV6) return 0; @@ -317,6 +328,7 @@ stf_output(ifp, m, dst, rt) { struct stf_softc *sc; struct sockaddr_in6 *dst6; + struct in_addr *in4; struct sockaddr_in *dst4; u_int8_t tos; struct ip *ip; @@ -351,6 +363,19 @@ stf_output(ifp, m, dst, rt) ip6 = mtod(m, struct ip6_hdr *); tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; + /* + * Pickup the right outer dst addr from the list of candidates. + * ip6_dst has priority as it may be able to give us shorter IPv4 hops. + */ + if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst)) + in4 = GET_V4(&ip6->ip6_dst); + else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr)) + in4 = GET_V4(&dst6->sin6_addr); + else { + m_freem(m); + return ENETUNREACH; + } + M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); if (m && m->m_len < sizeof(struct ip)) m = m_pullup(m, sizeof(struct ip)); @@ -362,12 +387,14 @@ stf_output(ifp, m, dst, rt) bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr), &ip->ip_src, sizeof(ip->ip_src)); - bcopy(GET_V4(&dst6->sin6_addr), &ip->ip_dst, sizeof(ip->ip_dst)); + bcopy(in4, &ip->ip_dst, sizeof(ip->ip_dst)); ip->ip_p = IPPROTO_IPV6; ip->ip_ttl = ip_gif_ttl; /*XXX*/ ip->ip_len = m->m_pkthdr.len; /*host order*/ if (ifp->if_flags & IFF_LINK1) ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos); + else + ip_ecn_ingress(ECN_NOCARE, &ip->ip_tos, &tos); dst4 = (struct sockaddr_in *)&sc->sc_ro.ro_dst; if (dst4->sin_family != AF_INET || @@ -394,9 +421,10 @@ stf_output(ifp, m, dst, rt) } static int -stf_checkaddr4(in, ifp) +stf_checkaddr4(sc, in, inifp) + struct stf_softc *sc; struct in_addr *in; - struct ifnet *ifp; /* incoming interface */ + struct ifnet *inifp; /* incoming interface */ { struct in_ifaddr *ia4; @@ -427,7 +455,7 @@ stf_checkaddr4(in, ifp) /* * perform ingress filter */ - if (ifp) { + if (sc && (sc->sc_if.if_flags & IFF_LINK2) == 0 && inifp) { struct sockaddr_in sin; struct rtentry *rt; @@ -436,10 +464,14 @@ stf_checkaddr4(in, ifp) sin.sin_len = sizeof(struct sockaddr_in); sin.sin_addr = *in; rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); - if (!rt) - return -1; - if (rt->rt_ifp != ifp) { - rtfree(rt); + if (!rt || rt->rt_ifp != inifp) { +#if 0 + log(LOG_WARNING, "%s: packet from 0x%x dropped " + "due to ingress filter\n", if_name(&sc->sc_if), + (u_int32_t)ntohl(sin.sin_addr.s_addr)); +#endif + if (rt) + rtfree(rt); return -1; } rtfree(rt); @@ -449,15 +481,16 @@ stf_checkaddr4(in, ifp) } static int -stf_checkaddr6(in6, ifp) +stf_checkaddr6(sc, in6, inifp) + struct stf_softc *sc; struct in6_addr *in6; - struct ifnet *ifp; /* incoming interface */ + struct ifnet *inifp; /* incoming interface */ { /* * check 6to4 addresses */ if (IN6_IS_ADDR_6TO4(in6)) - return stf_checkaddr4(GET_V4(in6), ifp); + return stf_checkaddr4(sc, GET_V4(in6), inifp); /* * reject anything that look suspicious. the test is implemented @@ -476,7 +509,7 @@ void in_stf_input(struct mbuf *m, ...) #else in_stf_input(m, va_alist) - register struct mbuf *m; + struct mbuf *m; #endif { int off, proto; @@ -514,8 +547,8 @@ in_stf_input(m, va_alist) * perform sanity check against outer src/dst. * for source, perform ingress filter as well. */ - if (stf_checkaddr4(&ip->ip_dst, NULL) < 0 || - stf_checkaddr4(&ip->ip_src, m->m_pkthdr.rcvif) < 0) { + if (stf_checkaddr4(sc, &ip->ip_dst, NULL) < 0 || + stf_checkaddr4(sc, &ip->ip_src, m->m_pkthdr.rcvif) < 0) { m_freem(m); return; } @@ -534,8 +567,8 @@ in_stf_input(m, va_alist) * perform sanity check against inner src/dst. * for source, perform ingress filter as well. */ - if (stf_checkaddr6(&ip6->ip6_dst, NULL) < 0 || - stf_checkaddr6(&ip6->ip6_src, m->m_pkthdr.rcvif) < 0) { + if (stf_checkaddr6(sc, &ip6->ip6_dst, NULL) < 0 || + stf_checkaddr6(sc, &ip6->ip6_src, m->m_pkthdr.rcvif) < 0) { m_freem(m); return; } @@ -543,6 +576,8 @@ in_stf_input(m, va_alist) itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; if ((ifp->if_flags & IFF_LINK1) != 0) ip_ecn_egress(ECN_ALLOWED, &otos, &itos); + else + ip_ecn_egress(ECN_NOCARE, &otos, &itos); ip6->ip6_flow &= ~htonl(0xff << 20); ip6->ip6_flow |= htonl((u_int32_t)itos << 20); @@ -558,7 +593,7 @@ in_stf_input(m, va_alist) * try to free it or keep a pointer a to it). */ struct mbuf m0; - u_int af = AF_INET6; + u_int32_t af = AF_INET6; m0.m_next = m; m0.m_len = 4; @@ -594,11 +629,7 @@ static void stf_rtrequest(cmd, rt, sa) int cmd; struct rtentry *rt; -#if defined(__bsdi__) && _BSDI_VERSION >= 199802 - struct rt_addrinfo *sa; -#else struct sockaddr *sa; -#endif { if (rt) diff --git a/sys/net/net_osdep.c b/sys/net/net_osdep.c index 02000f6d4889..ef3bb0160d20 100644 --- a/sys/net/net_osdep.c +++ b/sys/net/net_osdep.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: net_osdep.c,v 1.4 2000/03/25 07:23:34 sumikawa Exp $ */ +/* $KAME: net_osdep.c,v 1.9 2001/04/06 09:22:05 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -32,6 +32,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/kernel.h> #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/sockio.h> @@ -52,8 +53,15 @@ const char * if_name(ifp) struct ifnet *ifp; { - static char nam[IFNAMSIZ + 10]; /*enough?*/ +#define MAXNUMBUF 8 + static char nam[MAXNUMBUF][IFNAMSIZ + 10]; /*enough?*/ + static int ifbufround = 0; + char *cp; - snprintf(nam, sizeof(nam), "%s%d", ifp->if_name, ifp->if_unit); - return nam; + ifbufround = (ifbufround + 1) % MAXNUMBUF; + cp = nam[ifbufround]; + + snprintf(cp, sizeof(nam), "%s%d", ifp->if_name, ifp->if_unit); + return((const char *)cp); +#undef MAXNUMBUF } diff --git a/sys/net/net_osdep.h b/sys/net/net_osdep.h index 3bb3c181d100..399dd3143c26 100644 --- a/sys/net/net_osdep.h +++ b/sys/net/net_osdep.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: net_osdep.h,v 1.22 2000/08/15 07:23:10 itojun Exp $ */ +/* $KAME: net_osdep.h,v 1.44 2001/05/16 03:13:40 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -36,16 +36,40 @@ /* * OS dependencies: * + * - whether the IPv4 input routine convert the byte order of some fileds + * of the IP header (x: convert to the host byte order, s: strip the header + * length for possible reassembly) + * ip_len ip_id ip_off + * bsdi3: xs x x + * bsdi4: xs x + * FreeBSD: xs x + * NetBSD: x x + * OpenBSD: xs x x + * + * - ifa_ifwithaf() + * bsdi[34], netbsd, and openbsd define it in sys/net/if.c + * freebsd (all versions) does not have it. + * * - struct rt_addrinfo - * all *BSDs except bsdi4 only have two members; rti_addrs and rti_info[]. - * bsdi4 has additional members; rti_flags, rti_ifa, rti_ifp, and rti_rtm. + * bsdi4, netbsd 1.5R and beyond: rti_addrs, rti_info[], rti_flags, rti_ifa, + * rti_ifp, and rti_rtm. + * others: rti_addrs and rti_info[] only. + * + * - ifa->ifa_rtrequest + * bsdi4, netbsd 1.5R and beyond: rt_addrinfo * + * others: sockaddr * (note that sys/net/route.c:rtrequest() has an unsafe + * typecast code, from 4.3BSD-reno) * - * - side effects of rtrequest[1](RTM_DELETE) + * - side effects of rtrequest{,1}(RTM_DELETE) * BSDI[34]: delete all cloned routes underneath the route. * FreeBSD[234]: delete all protocol-cloned routes underneath the route. * note that cloned routes from an interface direct route * still remain. - * NetBSD, OpenBSD: no side effects. + * NetBSD: 1.5 have no side effects. KAME/netbsd15, and post-1.5R, have + * the same effects as of BSDI. + * OpenBSD: have no side effects. KAME/openbsd has the same effects as + * of BSDI (the change is not merged - yet). + * * - privileged process * NetBSD, FreeBSD 3 * struct proc *p; @@ -64,11 +88,13 @@ * needs to give struct proc * as argument * OpenBSD, BSDI [34], FreeBSD 2 * do not need struct proc * + * * - bpf: - * OpenBSD, NetBSD, BSDI [34] + * OpenBSD, NetBSD 1.5, BSDI [34] * need caddr_t * (= if_bpf **) and struct ifnet * - * FreeBSD 2, FreeBSD 3 + * FreeBSD 2, FreeBSD 3, NetBSD post-1.5N * need only struct ifnet * as argument + * * - struct ifnet * use queue.h? member names if name * --- --- --- @@ -77,34 +103,57 @@ * OpenBSD yes standard if_xname * NetBSD yes standard if_xname * BSDI [34] no old standard if_name+unit + * * - usrreq * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 * single function with PRU_xx, arguments are mbuf * FreeBSD 3 * separates functions, non-mbuf arguments + * * - {set,get}sockopt * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 * manipulation based on mbuf * FreeBSD 3 * non-mbuf manipulation using sooptcopy{in,out}() + * * - timeout() and untimeout() - * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 + * NetBSD 1.4.x, OpenBSD, BSDI [34], FreeBSD 2 * timeout() is a void function * FreeBSD 3 * timeout() is non-void, must keep returned value for untimeout() + * callout_xx is also available (sys/callout.h) + * NetBSD 1.5 + * timeout() is obsoleted, use callout_xx (sys/callout.h) + * OpenBSD 2.8 + * timeout_{add,set,del} is encouraged (sys/timeout.h) + * * - sysctl * NetBSD, OpenBSD * foo_sysctl() * BSDI [34] - * foo_sysctl() but with different style - * FreeBSD 2, FreeBSD 3 - * linker hack + * foo_sysctl() but with different style. sysctl_int_arr() takes + * care of most of the cases. + * FreeBSD + * linker hack. however, there are freebsd version differences + * (how wonderful!). + * on FreeBSD[23] function arg #define includes paren. + * int foo SYSCTL_HANDLER_ARGS; + * on FreeBSD4, function arg #define does not include paren. + * int foo(SYSCTL_HANDLER_ARGS); + * on some versions, forward reference to the tree is okay. + * on some versions, you need SYSCTL_DECL(). you need things + * like this. + * #ifdef SYSCTL_DECL + * SYSCTL_DECL(net_inet_ip6); + * #endif + * it is hard to share functions between freebsd and non-freebsd. * * - if_ioctl * NetBSD, FreeBSD 3, BSDI [34] * 2nd argument is u_long cmd * FreeBSD 2 * 2nd argument is int cmd + * * - if attach routines * NetBSD * void xxattach(int); @@ -115,6 +164,7 @@ * - ovbcopy() * in NetBSD 1.4 or later, ovbcopy() is not supplied in the kernel. * bcopy() is safe against overwrites. + * * - splnet() * NetBSD 1.4 or later requires splsoftnet(). * other operating systems use splnet(). @@ -125,7 +175,8 @@ * - struct ifnet for loopback interface * BSDI3: struct ifnet loif; * BSDI4: struct ifnet *loifp; - * NetBSD, OpenBSD, FreeBSD2: struct ifnet loif[NLOOP]; + * NetBSD, OpenBSD 2.8, FreeBSD2: struct ifnet loif[NLOOP]; + * OpenBSD 2.9: struct ifnet *lo0ifp; * * odd thing is that many of them refers loif as ifnet *loif, * not loif[NLOOP], from outside of if_loop.c. @@ -145,6 +196,12 @@ * FreeBSD4: struct ipprotosw in netinet/ipprotosw.h * others: struct protosw in sys/protosw.h * + * - protosw in general. + * NetBSD 1.5 has extra member for ipfilter (netbsd-current dropped + * it so it will go away in 1.6). + * NetBSD 1.5 requires PR_LISTEN flag bit with protocols that permit + * listen/accept (like tcp). + * * - header files with defopt (opt_xx.h) * FreeBSD3: opt_{inet,ipsec,ip6fw,altq}.h * FreeBSD4: opt_{inet,inet6,ipsec,ip6fw,altq}.h @@ -154,6 +211,23 @@ * - IN_MULTICAST/IN_CLASS[A-D] macro. * OpenBSD and NetBSD: net endian (kernel) or host endian (userland) * others: always host endian + * + * - (m->m_flags & M_EXT) != 0 does *not* mean that the max data length of + * the mbuf == MCLBYTES. + * + * - sys/kern/uipc_mbuf.c:m_dup() + * freebsd[34]: copies the whole mbuf chain. + * netbsd: similar arg with m_copym(). + * others: no m_dup(). + * + * - ifa_refcnt (struct ifaddr) management (IFAREF/IFAFREE). + * NetBSD 1.5: always use IFAREF whenever reference gets added. + * always use IFAFREE whenever reference gets freed. + * IFAFREE frees ifaddr when ifa_refcnt reaches 0. + * others: do not increase refcnt for ifp->if_addrlist and in_ifaddr. + * use IFAFREE once when ifaddr is disconnected from + * ifp->if_addrlist and in_ifaddr. IFAFREE frees ifaddr when + * ifa_refcnt goes negative. */ #ifndef __NET_NET_OSDEP_H_DEFINED_ @@ -165,6 +239,19 @@ extern const char *if_name __P((struct ifnet *)); #define HAVE_OLD_BPF +#define ifa_list ifa_link +#define if_addrlist if_addrhead +#define if_list if_link + +/* sys/net/if.h */ +#define IFAREF(ifa) do { ++(ifa)->ifa_refcnt; } while (0) + +#define WITH_CONVERT_AND_STRIP_IP_LEN + +#if 1 /* at this moment, all OSes do this */ +#define WITH_CONVERT_IP_OFF +#endif + /* * Deprecated. */ diff --git a/sys/net/pfkeyv2.h b/sys/net/pfkeyv2.h index 4c41c8072b68..f1c22ad84da2 100644 --- a/sys/net/pfkeyv2.h +++ b/sys/net/pfkeyv2.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: pfkeyv2.h,v 1.17 2000/06/22 08:38:33 sakane Exp $ */ +/* $KAME: pfkeyv2.h,v 1.25 2001/03/12 08:34:06 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -73,7 +73,7 @@ you leave this credit intact on any copies of this file. #define SADB_X_SPDDUMP 18 #define SADB_X_SPDFLUSH 19 #define SADB_X_SPDSETIDX 20 -#define SADB_X_SPDEXPIRE 21 /* not yet */ +#define SADB_X_SPDEXPIRE 21 #define SADB_X_SPDDELETE2 22 /* by policy id */ #define SADB_MAX 22 @@ -298,22 +298,32 @@ struct sadb_x_ipsecrequest { #define SADB_SAFLAGS_PFS 1 -#define SADB_AALG_NONE 0 -#define SADB_AALG_MD5HMAC 1 /* 2 */ -#define SADB_AALG_SHA1HMAC 2 /* 3 */ -#define SADB_AALG_MD5 3 /* Keyed MD5 */ -#define SADB_AALG_SHA 4 /* Keyed SHA */ -#define SADB_AALG_NULL 5 /* null authentication */ -#define SADB_AALG_MAX 6 - -#define SADB_EALG_NONE 0 -#define SADB_EALG_DESCBC 1 /* 2 */ -#define SADB_EALG_3DESCBC 2 /* 3 */ -#define SADB_EALG_NULL 3 /* 11 */ -#define SADB_EALG_BLOWFISHCBC 4 -#define SADB_EALG_CAST128CBC 5 -#define SADB_EALG_RC5CBC 6 -#define SADB_EALG_MAX 7 +/* RFC2367 numbers - meets RFC2407 */ +#define SADB_AALG_NONE 0 +#define SADB_AALG_MD5HMAC 1 /*2*/ +#define SADB_AALG_SHA1HMAC 2 /*3*/ +#define SADB_AALG_MAX 8 +/* private allocations - based on RFC2407/IANA assignment */ +#define SADB_X_AALG_SHA2_256 6 /*5*/ +#define SADB_X_AALG_SHA2_384 7 /*6*/ +#define SADB_X_AALG_SHA2_512 8 /*7*/ +/* private allocations should use 249-255 (RFC2407) */ +#define SADB_X_AALG_MD5 3 /*249*/ /* Keyed MD5 */ +#define SADB_X_AALG_SHA 4 /*250*/ /* Keyed SHA */ +#define SADB_X_AALG_NULL 5 /*251*/ /* null authentication */ + +/* RFC2367 numbers - meets RFC2407 */ +#define SADB_EALG_NONE 0 +#define SADB_EALG_DESCBC 1 /*2*/ +#define SADB_EALG_3DESCBC 2 /*3*/ +#define SADB_EALG_NULL 3 /*11*/ +#define SADB_EALG_MAX 12 +/* private allocations - based on RFC2407/IANA assignment */ +#define SADB_X_EALG_CAST128CBC 5 /*6*/ +#define SADB_X_EALG_BLOWFISHCBC 4 /*7*/ +#define SADB_X_EALG_RIJNDAELCBC 12 +#define SADB_X_EALG_AES 12 +/* private allocations should use 249-255 (RFC2407) */ #if 1 /*nonstandard */ #define SADB_X_CALG_NONE 0 diff --git a/sys/net/ppp_defs.h b/sys/net/ppp_defs.h index ac86be268788..be891ee55ff3 100644 --- a/sys/net/ppp_defs.h +++ b/sys/net/ppp_defs.h @@ -69,6 +69,8 @@ #define PPP_LQR 0xc025 /* Link Quality Report protocol */ #define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ #define PPP_CBCP 0xc029 /* Callback Control Protocol */ +#define PPP_IPV6 0x57 /* Internet Protocol version 6*/ +#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */ /* * Values for FCS calculations. diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index dc4ae26e0d8f..6588f564dd86 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -573,9 +573,6 @@ rt_msg1(type, rtinfo) register struct sockaddr *sa; int len, dlen; - m = m_gethdr(M_DONTWAIT, MT_DATA); - if (m == 0) - return (m); switch (type) { case RTM_DELADDR: @@ -595,8 +592,18 @@ rt_msg1(type, rtinfo) default: len = sizeof(struct rt_msghdr); } - if (len > MHLEN) + if (len > MCLBYTES) panic("rt_msg1"); + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m && len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } + } + if (m == 0) + return (m); m->m_pkthdr.len = m->m_len = len; m->m_pkthdr.rcvif = 0; rtm = mtod(m, struct rt_msghdr *); diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index 3625ee48e41e..425495d524b8 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: icmp6.h,v 1.18 2000/07/03 02:51:08 itojun Exp $ */ +/* $KAME: icmp6.h,v 1.46 2001/04/27 15:09:48 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -80,7 +80,7 @@ struct icmp6_hdr { u_int16_t icmp6_un_data16[2]; /* type-specific field */ u_int8_t icmp6_un_data8[4]; /* type-specific field */ } icmp6_dataun; -}; +} __attribute__((__packed__)); #define icmp6_data32 icmp6_dataun.icmp6_un_data32 #define icmp6_data16 icmp6_dataun.icmp6_un_data16 @@ -124,7 +124,10 @@ struct icmp6_hdr { #define MLD6_MTRACE_RESP 141 /* mtrace response(to sender) */ #define MLD6_MTRACE 142 /* mtrace messages */ -#define ICMP6_MAXTYPE 142 +#define ICMP6_HADISCOV_REQUEST 143 /* XXX To be defined */ +#define ICMP6_HADISCOV_REPLY 144 /* XXX To be defined */ + +#define ICMP6_MAXTYPE 144 #define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */ #define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */ @@ -146,7 +149,7 @@ struct icmp6_hdr { #define ICMP6_NI_SUBJ_FQDN 1 /* Query Subject is a Domain name */ #define ICMP6_NI_SUBJ_IPV4 2 /* Query Subject is an IPv4 address */ -#define ICMP6_NI_SUCESS 0 /* node information successful reply */ +#define ICMP6_NI_SUCCESS 0 /* node information successful reply */ #define ICMP6_NI_REFUSED 1 /* node information request is refused */ #define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */ @@ -164,7 +167,7 @@ struct icmp6_hdr { struct mld6_hdr { struct icmp6_hdr mld6_hdr; struct in6_addr mld6_addr; /* multicast address */ -}; +} __attribute__((__packed__)); #define mld6_type mld6_hdr.icmp6_type #define mld6_code mld6_hdr.icmp6_code @@ -179,7 +182,7 @@ struct mld6_hdr { struct nd_router_solicit { /* router solicitation */ struct icmp6_hdr nd_rs_hdr; /* could be followed by options */ -}; +} __attribute__((__packed__)); #define nd_rs_type nd_rs_hdr.icmp6_type #define nd_rs_code nd_rs_hdr.icmp6_code @@ -191,7 +194,7 @@ struct nd_router_advert { /* router advertisement */ u_int32_t nd_ra_reachable; /* reachable time */ u_int32_t nd_ra_retransmit; /* retransmit timer */ /* could be followed by options */ -}; +} __attribute__((__packed__)); #define nd_ra_type nd_ra_hdr.icmp6_type #define nd_ra_code nd_ra_hdr.icmp6_code @@ -200,13 +203,26 @@ struct nd_router_advert { /* router advertisement */ #define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1] #define ND_RA_FLAG_MANAGED 0x80 #define ND_RA_FLAG_OTHER 0x40 +#define ND_RA_FLAG_HA 0x20 + +/* + * Router preference values based on draft-draves-ipngwg-router-selection-01. + * These are non-standard definitions. + */ +#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ + +#define ND_RA_FLAG_RTPREF_HIGH 0x08 /* 00001000 */ +#define ND_RA_FLAG_RTPREF_MEDIUM 0x00 /* 00000000 */ +#define ND_RA_FLAG_RTPREF_LOW 0x18 /* 00011000 */ +#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ + #define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1] struct nd_neighbor_solicit { /* neighbor solicitation */ struct icmp6_hdr nd_ns_hdr; struct in6_addr nd_ns_target; /*target address */ /* could be followed by options */ -}; +} __attribute__((__packed__)); #define nd_ns_type nd_ns_hdr.icmp6_type #define nd_ns_code nd_ns_hdr.icmp6_code @@ -217,7 +233,7 @@ struct nd_neighbor_advert { /* neighbor advertisement */ struct icmp6_hdr nd_na_hdr; struct in6_addr nd_na_target; /* target address */ /* could be followed by options */ -}; +} __attribute__((__packed__)); #define nd_na_type nd_na_hdr.icmp6_type #define nd_na_code nd_na_hdr.icmp6_code @@ -240,7 +256,7 @@ struct nd_redirect { /* redirect */ struct in6_addr nd_rd_target; /* target address */ struct in6_addr nd_rd_dst; /* destination address */ /* could be followed by options */ -}; +} __attribute__((__packed__)); #define nd_rd_type nd_rd_hdr.icmp6_type #define nd_rd_code nd_rd_hdr.icmp6_code @@ -251,13 +267,14 @@ struct nd_opt_hdr { /* Neighbor discovery option header */ u_int8_t nd_opt_type; u_int8_t nd_opt_len; /* followed by option specific data*/ -}; +} __attribute__((__packed__)); #define ND_OPT_SOURCE_LINKADDR 1 #define ND_OPT_TARGET_LINKADDR 2 #define ND_OPT_PREFIX_INFORMATION 3 #define ND_OPT_REDIRECTED_HEADER 4 #define ND_OPT_MTU 5 +#define ND_OPT_ROUTE_INFO 9 /* draft-draves-router-preference, not officially assigned yet */ struct nd_opt_prefix_info { /* prefix information */ u_int8_t nd_opt_pi_type; @@ -268,7 +285,7 @@ struct nd_opt_prefix_info { /* prefix information */ u_int32_t nd_opt_pi_preferred_time; u_int32_t nd_opt_pi_reserved2; struct in6_addr nd_opt_pi_prefix; -}; +} __attribute__((__packed__)); #define ND_OPT_PI_FLAG_ONLINK 0x80 #define ND_OPT_PI_FLAG_AUTO 0x40 @@ -279,15 +296,23 @@ struct nd_opt_rd_hdr { /* redirected header */ u_int16_t nd_opt_rh_reserved1; u_int32_t nd_opt_rh_reserved2; /* followed by IP header and data */ -}; +} __attribute__((__packed__)); struct nd_opt_mtu { /* MTU option */ u_int8_t nd_opt_mtu_type; u_int8_t nd_opt_mtu_len; u_int16_t nd_opt_mtu_reserved; u_int32_t nd_opt_mtu_mtu; -}; - +} __attribute__((__packed__)); + +struct nd_opt_route_info { /* route info */ + u_int8_t nd_opt_rti_type; + u_int8_t nd_opt_rti_len; + u_int8_t nd_opt_rti_prefixlen; + u_int8_t nd_opt_rti_flags; + u_int32_t nd_opt_rti_lifetime; + /* followed by prefix */ +} __attribute__((__packed__)); /* * icmp6 namelookup */ @@ -301,7 +326,7 @@ struct icmp6_namelookup { u_int8_t icmp6_nl_name[3]; #endif /* could be followed by options */ -}; +} __attribute__((__packed__)); /* * icmp6 node information @@ -310,7 +335,7 @@ struct icmp6_nodeinfo { struct icmp6_hdr icmp6_ni_hdr; u_int8_t icmp6_ni_nonce[8]; /* could be followed by reply data */ -}; +} __attribute__((__packed__)); #define ni_type icmp6_ni_hdr.icmp6_type #define ni_code icmp6_ni_hdr.icmp6_code @@ -320,8 +345,10 @@ struct icmp6_nodeinfo { #define NI_QTYPE_NOOP 0 /* NOOP */ #define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */ -#define NI_QTYPE_FQDN 2 /* FQDN */ -#define NI_QTYPE_NODEADDR 3 /* Node Addresses. XXX: spec says 2, but it may be a typo... */ +#define NI_QTYPE_FQDN 2 /* FQDN (draft 04) */ +#define NI_QTYPE_DNSNAME 2 /* DNS Name */ +#define NI_QTYPE_NODEADDR 3 /* Node Addresses */ +#define NI_QTYPE_IPV4ADDR 4 /* IPv4 Addresses */ #if BYTE_ORDER == BIG_ENDIAN #define NI_SUPTYPE_FLAG_COMPRESS 0x1 @@ -371,7 +398,7 @@ struct ni_reply_fqdn { u_int32_t ni_fqdn_ttl; /* TTL */ u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */ u_int8_t ni_fqdn_name[3]; /* XXX: alignment */ -}; +} __attribute__((__packed__)); /* * Router Renumbering. as router-renum-08.txt @@ -382,13 +409,13 @@ struct icmp6_router_renum { /* router renumbering header */ u_int8_t rr_flags; u_int16_t rr_maxdelay; u_int32_t rr_reserved; -}; -#define ICMP6_RR_FLAGS_SEGNUM 0x80 -#define ICMP6_RR_FLAGS_TEST 0x40 -#define ICMP6_RR_FLAGS_REQRESULT 0x20 -#define ICMP6_RR_FLAGS_FORCEAPPLY 0x10 -#define ICMP6_RR_FLAGS_SPECSITE 0x08 -#define ICMP6_RR_FLAGS_PREVDONE 0x04 +} __attribute__((__packed__)); + +#define ICMP6_RR_FLAGS_TEST 0x80 +#define ICMP6_RR_FLAGS_REQRESULT 0x40 +#define ICMP6_RR_FLAGS_FORCEAPPLY 0x20 +#define ICMP6_RR_FLAGS_SPECSITE 0x10 +#define ICMP6_RR_FLAGS_PREVDONE 0x08 #define rr_type rr_hdr.icmp6_type #define rr_code rr_hdr.icmp6_code @@ -404,7 +431,7 @@ struct rr_pco_match { /* match prefix part */ u_int8_t rpm_maxlen; u_int16_t rpm_reserved; struct in6_addr rpm_prefix; -}; +} __attribute__((__packed__)); #define RPM_PCO_ADD 1 #define RPM_PCO_CHANGE 2 @@ -420,7 +447,7 @@ struct rr_pco_use { /* use prefix part */ u_int32_t rpu_pltime; u_int32_t rpu_flags; struct in6_addr rpu_prefix; -}; +} __attribute__((__packed__)); #define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80 #define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40 @@ -438,13 +465,13 @@ struct rr_result { /* router renumbering result message */ u_int8_t rrr_matchedlen; u_int32_t rrr_ifid; struct in6_addr rrr_prefix; -}; +} __attribute__((__packed__)); #if BYTE_ORDER == BIG_ENDIAN #define ICMP6_RR_RESULT_FLAGS_OOB 0x0002 #define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0001 #elif BYTE_ORDER == LITTLE_ENDIAN -#define ICMP6_RR_RESULT_FLAGS_OOB 0x02 -#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x01 +#define ICMP6_RR_RESULT_FLAGS_OOB 0x0200 +#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0100 #endif /* @@ -534,6 +561,13 @@ struct icmp6stat { #define icp6s_oparamprob_option icp6s_outerrhist.icp6errs_paramprob_option #define icp6s_oredirect icp6s_outerrhist.icp6errs_redirect #define icp6s_ounknown icp6s_outerrhist.icp6errs_unknown + u_quad_t icp6s_pmtuchg; /* path MTU changes */ + u_quad_t icp6s_nd_badopt; /* bad ND options */ + u_quad_t icp6s_badns; /* bad neighbor solicitation */ + u_quad_t icp6s_badna; /* bad neighbor advertisement */ + u_quad_t icp6s_badrs; /* bad router advertisement */ + u_quad_t icp6s_badra; /* bad router advertisement */ + u_quad_t icp6s_badredirect; /* bad redirect message */ }; /* @@ -542,7 +576,9 @@ struct icmp6stat { #define ICMPV6CTL_STATS 1 #define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */ #define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */ +#if 0 /*obsoleted*/ #define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */ +#endif #define ICMPV6CTL_ND6_PRUNE 6 #define ICMPV6CTL_ND6_DELAY 8 #define ICMPV6CTL_ND6_UMAXTRIES 9 @@ -552,7 +588,12 @@ struct icmp6stat { #define ICMPV6CTL_NODEINFO 13 #define ICMPV6CTL_ERRPPSLIMIT 14 /* ICMPv6 error pps limitation */ #define ICMPV6CTL_ND6_MAXNUDHINT 15 -#define ICMPV6CTL_MAXID 16 +#define ICMPV6CTL_MTUDISC_HIWAT 16 +#define ICMPV6CTL_MTUDISC_LOWAT 17 +#define ICMPV6CTL_ND6_DEBUG 18 +#define ICMPV6CTL_ND6_DRLIST 19 +#define ICMPV6CTL_ND6_PRLIST 20 +#define ICMPV6CTL_MAXID 21 #define ICMPV6CTL_NAMES { \ { 0, 0 }, \ @@ -560,7 +601,7 @@ struct icmp6stat { { "rediraccept", CTLTYPE_INT }, \ { "redirtimeout", CTLTYPE_INT }, \ { 0, 0 }, \ - { "errratelimit", CTLTYPE_INT }, \ + { 0, 0 }, \ { "nd6_prune", CTLTYPE_INT }, \ { 0, 0 }, \ { "nd6_delay", CTLTYPE_INT }, \ @@ -571,6 +612,11 @@ struct icmp6stat { { "nodeinfo", CTLTYPE_INT }, \ { "errppslimit", CTLTYPE_INT }, \ { "nd6_maxnudhint", CTLTYPE_INT }, \ + { "mtudisc_hiwat", CTLTYPE_INT }, \ + { "mtudisc_lowat", CTLTYPE_INT }, \ + { "nd6_debug", CTLTYPE_INT }, \ + { 0, 0 }, \ + { 0, 0 }, \ } #define RTF_PROBEMTU RTF_PROTO1 @@ -591,6 +637,9 @@ void icmp6_prepare __P((struct mbuf *)); void icmp6_redirect_input __P((struct mbuf *, int)); void icmp6_redirect_output __P((struct mbuf *, struct rtentry *)); +struct ip6ctlparam; +void icmp6_mtudisc_update __P((struct ip6ctlparam *, int)); + /* XXX: is this the right place for these macros? */ #define icmp6_ifstat_inc(ifp, tag) \ do { \ diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 95abe3f21e89..170a34352128 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -51,11 +51,6 @@ #include <netinet/igmp_var.h> -#include "gif.h" -#if NGIF > 0 -#include <net/if_gif.h> -#endif - static MALLOC_DEFINE(M_IPMADDR, "in_multi", "internet multicast address"); static int in_mask2len __P((struct in_addr *)); @@ -200,21 +195,6 @@ in_control(so, cmd, data, ifp, p) int error, hostIsNew, maskIsNew, s; u_long i; -#if NGIF > 0 - if (ifp && ifp->if_type == IFT_GIF) { - switch (cmd) { - case SIOCSIFPHYADDR: - case SIOCDIFPHYADDR: - if (p && - (error = suser(p)) != 0) - return(error); - case SIOCGIFPSRCADDR: - case SIOCGIFPDSTADDR: - return gif_ioctl(ifp, cmd, data); - } - } -#endif - switch (cmd) { case SIOCALIFADDR: case SIOCDLIFADDR: @@ -713,6 +693,9 @@ in_ifinit(ifp, ia, sin, scrub) } if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) ia->ia_flags |= IFA_ROUTE; + /* XXX check if the subnet route points to the same interface */ + if (error == EEXIST) + error = 0; /* * If the interface supports multicast, join the "all hosts" diff --git a/sys/netinet/in_gif.c b/sys/netinet/in_gif.c index 17955ade5681..5ad92e1a5c7b 100644 --- a/sys/netinet/in_gif.c +++ b/sys/netinet/in_gif.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in_gif.c,v 1.44 2000/08/15 07:24:24 itojun Exp $ */ +/* $KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -88,7 +88,7 @@ in_gif_output(ifp, family, m, rt) struct mbuf *m; struct rtentry *rt; { - register struct gif_softc *sc = (struct gif_softc*)ifp; + struct gif_softc *sc = (struct gif_softc*)ifp; struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; @@ -146,29 +146,12 @@ in_gif_output(ifp, family, m, rt) bzero(&iphdr, sizeof(iphdr)); iphdr.ip_src = sin_src->sin_addr; - if (ifp->if_flags & IFF_LINK0) { - /* multi-destination mode */ - if (sin_dst->sin_addr.s_addr != INADDR_ANY) - iphdr.ip_dst = sin_dst->sin_addr; - else if (rt) { - if (family != AF_INET) { - m_freem(m); - return EINVAL; /*XXX*/ - } - iphdr.ip_dst = ((struct sockaddr_in *) - (rt->rt_gateway))->sin_addr; - } else { - m_freem(m); - return ENETUNREACH; - } - } else { - /* bidirectional configured tunnel mode */ - if (sin_dst->sin_addr.s_addr != INADDR_ANY) - iphdr.ip_dst = sin_dst->sin_addr; - else { - m_freem(m); - return ENETUNREACH; - } + /* bidirectional configured tunnel mode */ + if (sin_dst->sin_addr.s_addr != INADDR_ANY) + iphdr.ip_dst = sin_dst->sin_addr; + else { + m_freem(m); + return ENETUNREACH; } iphdr.ip_p = proto; /* version will be set in ip_output() */ @@ -176,6 +159,8 @@ in_gif_output(ifp, family, m, rt) iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); if (ifp->if_flags & IFF_LINK1) ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos); + else + ip_ecn_ingress(ECN_NOCARE, &iphdr.ip_tos, &tos); /* prepend new IP header */ M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); @@ -272,6 +257,8 @@ in_gif_input(m, va_alist) ip = mtod(m, struct ip *); if (gifp->if_flags & IFF_LINK1) ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos); + else + ip_ecn_egress(ECN_NOCARE, &otos, &ip->ip_tos); break; } #endif @@ -290,6 +277,8 @@ in_gif_input(m, va_alist) itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; if (gifp->if_flags & IFF_LINK1) ip_ecn_egress(ECN_ALLOWED, &otos, &itos); + else + ip_ecn_egress(ECN_NOCARE, &otos, &itos); ip6->ip6_flow &= ~htonl(0xff << 20); ip6->ip6_flow |= htonl((u_int32_t)itos << 20); break; @@ -335,10 +324,6 @@ gif_encapcheck4(m, off, proto, arg) addrmatch |= 1; if (dst->sin_addr.s_addr == ip.ip_src.s_addr) addrmatch |= 2; - else if ((sc->gif_if.if_flags & IFF_LINK0) != 0 && - dst->sin_addr.s_addr == INADDR_ANY) { - addrmatch |= 2; /* we accept any source */ - } if (addrmatch != 3) return 0; @@ -359,7 +344,8 @@ gif_encapcheck4(m, off, proto, arg) } /* ingress filters on outer source */ - if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { + if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && + (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { struct sockaddr_in sin; struct rtentry *rt; @@ -368,15 +354,18 @@ gif_encapcheck4(m, off, proto, arg) sin.sin_len = sizeof(struct sockaddr_in); sin.sin_addr = ip.ip_src; rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); - if (!rt) - return 0; - if (rt->rt_ifp != m->m_pkthdr.rcvif) { - rtfree(rt); + if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) { +#if 0 + log(LOG_WARNING, "%s: packet from 0x%x dropped " + "due to ingress filter\n", if_name(&sc->gif_if), + (u_int32_t)ntohl(sin.sin_addr.s_addr)); +#endif + if (rt) + rtfree(rt); return 0; } rtfree(rt); } - /* prioritize: IFF_LINK0 mode is less preferred */ - return (sc->gif_if.if_flags & IFF_LINK0) ? 32 : 32 * 2; + return 32 * 2; } diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index b24b4042628c..ba5f77f13807 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -151,14 +151,16 @@ in_pcballoc(so, pcbinfo, p) inp->inp_pcbinfo = pcbinfo; inp->inp_socket = so; #if defined(INET6) - if (ip6_mapped_addr_on) - inp->inp_flags &= ~IN6P_BINDV6ONLY; - else - inp->inp_flags |= IN6P_BINDV6ONLY; + if (INP_SOCKAF(so) == AF_INET6 && !ip6_mapped_addr_on) + inp->inp_flags |= IN6P_IPV6_V6ONLY; #endif LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); pcbinfo->ipi_count++; so->so_pcb = (caddr_t)inp; +#ifdef INET6 + if (ip6_auto_flowlabel) + inp->inp_flags |= IN6P_AUTOFLOWLABEL; +#endif return (0); } @@ -234,9 +236,7 @@ in_pcbbind(inp, nam, p) (so->so_cred->cr_uid != t->inp_socket->so_cred->cr_uid)) { #if defined(INET6) - if ((inp->inp_flags & - IN6P_BINDV6ONLY) != 0 || - ntohl(sin->sin_addr.s_addr) != + if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || ntohl(t->inp_laddr.s_addr) != INADDR_ANY || @@ -254,8 +254,7 @@ in_pcbbind(inp, nam, p) if (t && (reuseport & t->inp_socket->so_options) == 0) { #if defined(INET6) - if ((inp->inp_flags & IN6P_BINDV6ONLY) != 0 || - ntohl(sin->sin_addr.s_addr) != + if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || ntohl(t->inp_laddr.s_addr) != INADDR_ANY || diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index f4abb4d0a440..1ae93d284201 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -226,25 +226,27 @@ struct inpcbinfo { /* XXX documentation, prefixes */ #define INP_RECVIF 0x80 /* receive incoming interface */ #define INP_MTUDISC 0x100 /* user can do MTU discovery */ #define INP_FAITH 0x200 /* accept FAITH'ed connections */ -#define IN6P_PKTINFO 0x010000 -#define IN6P_HOPLIMIT 0x020000 -#define IN6P_NEXTHOP 0x040000 -#define IN6P_HOPOPTS 0x080000 -#define IN6P_DSTOPTS 0x100000 -#define IN6P_RTHDR 0x200000 -#define IN6P_BINDV6ONLY 0x400000 + +#define IN6P_IPV6_V6ONLY 0x008000 /* restrict AF_INET6 socket for v6 */ + +#define IN6P_PKTINFO 0x010000 /* receive IP6 dst and I/F */ +#define IN6P_HOPLIMIT 0x020000 /* receive hoplimit */ +#define IN6P_HOPOPTS 0x040000 /* receive hop-by-hop options */ +#define IN6P_DSTOPTS 0x080000 /* receive dst options after rthdr */ +#define IN6P_RTHDR 0x100000 /* receive routing header */ +#define IN6P_RTHDRDSTOPTS 0x200000 /* receive dstoptions before rthdr */ +#define IN6P_AUTOFLOWLABEL 0x800000 /* attach flowlabel automatically */ +#define IN6P_BINDV6ONLY 0x10000000 /* do not grab IPv4 traffic */ + #define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\ INP_RECVIF|\ - IN6P_PKTINFO|IN6P_HOPLIMIT|IN6P_NEXTHOP|\ - IN6P_HOPOPTS|IN6P_DSTOPTS|IN6P_RTHDR) - -#define INP_UNMAPPABLEOPTS (IN6P_HOPOPTS|IN6P_DSTOPTS|IN6P_RTHDR) + IN6P_PKTINFO|IN6P_HOPLIMIT|IN6P_HOPOPTS|\ + IN6P_DSTOPTS|IN6P_RTHDR|IN6P_RTHDRDSTOPTS|\ + IN6P_AUTOFLOWLABEL) +#define INP_UNMAPPABLEOPTS (IN6P_HOPOPTS|IN6P_DSTOPTS|IN6P_RTHDR|\ + IN6P_AUTOFLOWLABEL) /* for KAME src sync over BSD*'s */ -#define IN6P_RECVOPTS INP_RECVOPTS -#define IN6P_RECVRETOPTS INP_RECVRETOPTS -#define IN6P_RECVDSTADDR INP_RECVDSTADDR -#define IN6P_HDRINCL INP_HDRINCL #define IN6P_HIGHPORT INP_HIGHPORT #define IN6P_LOWPORT INP_LOWPORT #define IN6P_ANONPORT INP_ANONPORT diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index 02e6313295e8..4c07a04a8f23 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -75,6 +75,7 @@ #ifdef IPSEC_ESP #include <netinet6/esp.h> #endif +#include <netinet6/ipcomp.h> #endif /* IPSEC */ #include "gif.h" @@ -125,19 +126,19 @@ struct ipprotosw inetsw[] = { 0, 0, 0, 0, &rip_usrreqs }, -{ SOCK_RAW, &inetdomain, IPPROTO_ICMP, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inetdomain, IPPROTO_ICMP, PR_ATOMIC|PR_ADDR|PR_LASTHDR, icmp_input, 0, 0, rip_ctloutput, 0, 0, 0, 0, 0, &rip_usrreqs }, -{ SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR|PR_LASTHDR, igmp_input, 0, 0, rip_ctloutput, 0, igmp_init, igmp_fasttimo, igmp_slowtimo, 0, &rip_usrreqs }, -{ SOCK_RAW, &inetdomain, IPPROTO_RSVP, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inetdomain, IPPROTO_RSVP, PR_ATOMIC|PR_ADDR|PR_LASTHDR, rsvp_input, 0, 0, rip_ctloutput, 0, 0, 0, 0, 0, @@ -158,19 +159,25 @@ struct ipprotosw inetsw[] = { &nousrreqs }, #endif +{ SOCK_RAW, &inetdomain, IPPROTO_IPCOMP, PR_ATOMIC|PR_ADDR, + ipcomp4_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs +}, #endif /* IPSEC */ -{ SOCK_RAW, &inetdomain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inetdomain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR|PR_LASTHDR, encap4_input, 0, 0, rip_ctloutput, 0, encap_init, 0, 0, 0, - &nousrreqs + &rip_usrreqs }, # ifdef INET6 -{ SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR|PR_LASTHDR, encap4_input, 0, 0, rip_ctloutput, 0, - 0, 0, 0, 0, - &nousrreqs + encap_init, 0, 0, 0, + &rip_usrreqs }, #endif #ifdef IPDIVERT @@ -182,7 +189,7 @@ struct ipprotosw inetsw[] = { }, #endif #ifdef IPXIP -{ SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR|PR_LASTHDR, ipxip_input, 0, ipxip_ctlinput, 0, 0, 0, 0, 0, 0, @@ -190,7 +197,7 @@ struct ipprotosw inetsw[] = { }, #endif #ifdef NSIP -{ SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR|PR_LASTHDR, idpip_input, 0, nsip_ctlinput, 0, 0, 0, 0, 0, 0, diff --git a/sys/netinet/ip6.h b/sys/netinet/ip6.h index 77d1ab657612..ec2c21641dc0 100644 --- a/sys/netinet/ip6.h +++ b/sys/netinet/ip6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6.h,v 1.9 2000/07/02 21:01:32 itojun Exp $ */ +/* $KAME: ip6.h,v 1.18 2001/03/29 05:34:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -85,7 +85,7 @@ struct ip6_hdr { } ip6_ctlun; struct in6_addr ip6_src; /* source address */ struct in6_addr ip6_dst; /* destination address */ -}; +} __attribute__((__packed__)); #define ip6_vfc ip6_ctlun.ip6_un2_vfc #define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow @@ -106,18 +106,20 @@ struct ip6_hdr { #define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */ #endif /* LITTLE_ENDIAN */ #endif +#if 1 /* ECN bits proposed by Sally Floyd */ #define IP6TOS_CE 0x01 /* congestion experienced */ #define IP6TOS_ECT 0x02 /* ECN-capable transport */ +#endif /* * Extension Headers */ struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; + u_int8_t ip6e_nxt; + u_int8_t ip6e_len; +} __attribute__((__packed__)); /* Hop-by-Hop options header */ /* XXX should we pad it to force alignment on an 8-byte boundary? */ @@ -125,7 +127,7 @@ struct ip6_hbh { u_int8_t ip6h_nxt; /* next header */ u_int8_t ip6h_len; /* length in units of 8 octets */ /* followed by options */ -}; +} __attribute__((__packed__)); /* Destination options header */ /* XXX should we pad it to force alignment on an 8-byte boundary? */ @@ -133,20 +135,28 @@ struct ip6_dest { u_int8_t ip6d_nxt; /* next header */ u_int8_t ip6d_len; /* length in units of 8 octets */ /* followed by options */ -}; +} __attribute__((__packed__)); /* Option types and related macros */ #define IP6OPT_PAD1 0x00 /* 00 0 00000 */ #define IP6OPT_PADN 0x01 /* 00 0 00001 */ #define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ -#define IP6OPT_JUMBO_LEN 6 -#define IP6OPT_RTALERT 0x05 /* 00 0 00101 */ +#define IP6OPT_NSAP_ADDR 0xC3 /* 11 0 00011 */ +#define IP6OPT_TUNNEL_LIMIT 0x04 /* 00 0 00100 */ +#define IP6OPT_RTALERT 0x05 /* 00 0 00101 (KAME definition) */ + #define IP6OPT_RTALERT_LEN 4 #define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ #define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ #define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ #define IP6OPT_MINLEN 2 +#define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */ +#define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */ +#define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */ +#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */ +#define IP6OPT_EID 0x8a /* 10 0 01010 */ + #define IP6OPT_TYPE(o) ((o) & 0xC0) #define IP6OPT_TYPE_SKIP 0x00 #define IP6OPT_TYPE_DISCARD 0x40 @@ -155,6 +165,8 @@ struct ip6_dest { #define IP6OPT_MUTABLE 0x20 +#define IP6OPT_JUMBO_LEN 6 + /* Routing header */ struct ip6_rthdr { u_int8_t ip6r_nxt; /* next header */ @@ -162,7 +174,7 @@ struct ip6_rthdr { u_int8_t ip6r_type; /* routing type */ u_int8_t ip6r_segleft; /* segments left */ /* followed by routing type specific data */ -}; +} __attribute__((__packed__)); /* Type 0 Routing header */ struct ip6_rthdr0 { @@ -173,7 +185,7 @@ struct ip6_rthdr0 { u_int8_t ip6r0_reserved; /* reserved field */ u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */ struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */ -}; +} __attribute__((__packed__)); /* Fragment header */ struct ip6_frag { @@ -181,7 +193,7 @@ struct ip6_frag { u_int8_t ip6f_reserved; /* reserved field */ u_int16_t ip6f_offlg; /* offset, reserved, and flag */ u_int32_t ip6f_ident; /* identification */ -}; +} __attribute__((__packed__)); #if BYTE_ORDER == BIG_ENDIAN #define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */ diff --git a/sys/netinet/ip_ecn.c b/sys/netinet/ip_ecn.c index 047f82e1fb52..3abc3b683212 100644 --- a/sys/netinet/ip_ecn.c +++ b/sys/netinet/ip_ecn.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip_ecn.c,v 1.7 2000/05/05 11:00:56 sumikawa Exp $ */ +/* $KAME: ip_ecn.c,v 1.11 2001/05/03 16:09:29 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -43,16 +43,10 @@ #include <sys/mbuf.h> #include <sys/errno.h> -#ifdef INET #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> -#endif - #ifdef INET6 -#ifndef INET -#include <netinet/in.h> -#endif #include <netinet/ip6.h> #endif @@ -63,17 +57,17 @@ /* * modify outer ECN (TOS) field on ingress operation (tunnel encapsulation). - * call it after you've done the default initialization/copy for the outer. */ void ip_ecn_ingress(mode, outer, inner) int mode; u_int8_t *outer; - u_int8_t *inner; + const u_int8_t *inner; { if (!outer || !inner) panic("NULL pointer passed to ip_ecn_ingress"); + *outer = *inner; switch (mode) { case ECN_ALLOWED: /* ECN allowed */ *outer &= ~IPTOS_CE; @@ -88,12 +82,11 @@ ip_ecn_ingress(mode, outer, inner) /* * modify inner ECN (TOS) field on egress operation (tunnel decapsulation). - * call it after you've done the default initialization/copy for the inner. */ void ip_ecn_egress(mode, outer, inner) int mode; - u_int8_t *outer; + const u_int8_t *outer; u_int8_t *inner; { if (!outer || !inner) @@ -115,14 +108,13 @@ void ip6_ecn_ingress(mode, outer, inner) int mode; u_int32_t *outer; - u_int32_t *inner; + const u_int32_t *inner; { u_int8_t outer8, inner8; if (!outer || !inner) panic("NULL pointer passed to ip6_ecn_ingress"); - outer8 = (ntohl(*outer) >> 20) & 0xff; inner8 = (ntohl(*inner) >> 20) & 0xff; ip_ecn_ingress(mode, &outer8, &inner8); *outer &= ~htonl(0xff << 20); @@ -132,7 +124,7 @@ ip6_ecn_ingress(mode, outer, inner) void ip6_ecn_egress(mode, outer, inner) int mode; - u_int32_t *outer; + const u_int32_t *outer; u_int32_t *inner; { u_int8_t outer8, inner8; @@ -141,7 +133,6 @@ ip6_ecn_egress(mode, outer, inner) panic("NULL pointer passed to ip6_ecn_egress"); outer8 = (ntohl(*outer) >> 20) & 0xff; - inner8 = (ntohl(*inner) >> 20) & 0xff; ip_ecn_egress(mode, &outer8, &inner8); *inner &= ~htonl(0xff << 20); *inner |= htonl((u_int32_t)inner8 << 20); diff --git a/sys/netinet/ip_ecn.h b/sys/netinet/ip_ecn.h index 6445d0f8fce7..9aca7319df4d 100644 --- a/sys/netinet/ip_ecn.h +++ b/sys/netinet/ip_ecn.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip_ecn.h,v 1.5 2000/03/27 04:58:38 sumikawa Exp $ */ +/* $KAME: ip_ecn.h,v 1.6 2001/05/03 14:51:48 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -35,11 +35,15 @@ * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt */ +#if defined(_KERNEL) && !defined(_LKM) +#include "opt_inet.h" +#endif + #define ECN_ALLOWED 1 /* ECN allowed */ #define ECN_FORBIDDEN 0 /* ECN forbidden */ #define ECN_NOCARE (-1) /* no consideration to ECN */ #ifdef _KERNEL -extern void ip_ecn_ingress __P((int, u_int8_t *, u_int8_t *)); -extern void ip_ecn_egress __P((int, u_int8_t *, u_int8_t *)); +extern void ip_ecn_ingress __P((int, u_int8_t *, const u_int8_t *)); +extern void ip_ecn_egress __P((int, const u_int8_t *, u_int8_t *)); #endif diff --git a/sys/netinet/ip_encap.c b/sys/netinet/ip_encap.c index 7d623eab6a2d..746330056f3b 100644 --- a/sys/netinet/ip_encap.c +++ b/sys/netinet/ip_encap.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip_encap.c,v 1.36 2000/06/17 20:34:24 itojun Exp $ */ +/* $KAME: ip_encap.c,v 1.41 2001/03/15 08:35:08 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -67,6 +67,7 @@ #include <sys/mbuf.h> #include <sys/errno.h> #include <sys/protosw.h> +#include <sys/queue.h> #include <net/if.h> #include <net/route.h> @@ -100,12 +101,21 @@ static int mask_match __P((const struct encaptab *, const struct sockaddr *, const struct sockaddr *)); static void encap_fillarg __P((struct mbuf *, const struct encaptab *)); +#ifndef LIST_HEAD_INITIALIZER /* rely upon BSS initialization */ LIST_HEAD(, encaptab) encaptab; +#else +LIST_HEAD(, encaptab) encaptab = LIST_HEAD_INITIALIZER(&encaptab); +#endif void encap_init() { + static int initialized = 0; + + if (initialized) + return; + initialized++; #if 0 /* * we cannot use LIST_INIT() here, since drivers may want to call @@ -118,6 +128,7 @@ encap_init() #endif } +#ifdef INET void #if __STDC__ encap4_input(struct mbuf *m, ...) @@ -221,6 +232,7 @@ encap4_input(m, va_alist) /* last resort: inject to raw socket */ rip_input(m, off, proto); } +#endif #ifdef INET6 int diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index ddb95f01edc0..7e8c722cd821 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -296,15 +296,6 @@ icmp_input(m, off, proto) icp->icmp_code); #endif -#ifdef IPSEC - /* drop it if it does not match the policy */ - /* XXX Is there meaning of check in here ? */ - if (ipsec4_in_reject(m, NULL)) { - ipsecstat.in_polvio++; - goto freeit; - } -#endif - /* * Message type specific processing. */ diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 7cd856889801..3cf13cf0e6e9 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -350,6 +350,16 @@ ip_input(struct mbuf *m) } ip = mtod(m, struct ip *); } + + /* 127/8 must not appear on wire - RFC1122 */ + if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || + (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { + ipstat.ips_badaddr++; + goto bad; + } + } + if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); } else { @@ -393,15 +403,10 @@ tooshort: m_adj(m, ip->ip_len - m->m_pkthdr.len); } - /* - * Don't accept packets with a loopback destination address - * unless they arrived via the loopback interface. - */ - if ((ntohl(ip->ip_dst.s_addr) & IN_CLASSA_NET) == - (IN_LOOPBACKNET << IN_CLASSA_NSHIFT) && - (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { - goto bad; - } +#ifdef IPSEC + if (ipsec_gethist(m, NULL)) + goto pass; +#endif /* * IpHack's section. @@ -796,6 +801,19 @@ found: } #endif +#ifdef IPSEC + /* + * enforce IPsec policy checking if we are seeing last header. + * note that we do not visit this with protocols with pcb layer + * code - like udp/tcp/raw ip. + */ + if ((inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0 && + ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto bad; + } +#endif + /* * Switch out to protocol's input routine. */ @@ -1189,6 +1207,10 @@ ip_dooptions(m) */ case IPOPT_LSRR: case IPOPT_SSRR: + if (optlen < IPOPT_OFFSET + sizeof(*cp)) { + code = &cp[IPOPT_OLEN] - (u_char *)ip; + goto bad; + } if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; @@ -1308,12 +1330,21 @@ nosourcerouting: case IPOPT_TS: code = cp - (u_char *)ip; ipt = (struct ip_timestamp *)cp; - if (ipt->ipt_len < 5) + if (ipt->ipt_len < 4 || ipt->ipt_len > 40) { + code = (u_char *)&ipt->ipt_len - (u_char *)ip; goto bad; + } + if (ipt->ipt_ptr < 5) { + code = (u_char *)&ipt->ipt_ptr - (u_char *)ip; + goto bad; + } if (ipt->ipt_ptr > ipt->ipt_len - (int)sizeof(int32_t)) { - if (++ipt->ipt_oflw == 0) + if (++ipt->ipt_oflw == 0) { + code = (u_char *)&ipt->ipt_ptr - + (u_char *)ip; goto bad; + } break; } sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1); @@ -1324,8 +1355,11 @@ nosourcerouting: case IPOPT_TS_TSANDADDR: if (ipt->ipt_ptr - 1 + sizeof(n_time) + - sizeof(struct in_addr) > ipt->ipt_len) + sizeof(struct in_addr) > ipt->ipt_len) { + code = (u_char *)&ipt->ipt_ptr - + (u_char *)ip; goto bad; + } ipaddr.sin_addr = dst; ia = (INA)ifaof_ifpforaddr((SA)&ipaddr, m->m_pkthdr.rcvif); @@ -1338,8 +1372,11 @@ nosourcerouting: case IPOPT_TS_PRESPEC: if (ipt->ipt_ptr - 1 + sizeof(n_time) + - sizeof(struct in_addr) > ipt->ipt_len) + sizeof(struct in_addr) > ipt->ipt_len) { + code = (u_char *)&ipt->ipt_ptr - + (u_char *)ip; goto bad; + } (void)memcpy(&ipaddr.sin_addr, sin, sizeof(struct in_addr)); if (ifa_ifwithaddr((SA)&ipaddr) == 0) @@ -1348,6 +1385,9 @@ nosourcerouting: break; default: + /* XXX can't take &ipt->ipt_flg */ + code = (u_char *)&ipt->ipt_ptr - + (u_char *)ip + 1; goto bad; } ntime = iptime(); diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 1025e3764e20..10de694fde10 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -95,6 +95,7 @@ static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options"); u_short ip_id; static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); +static struct ifnet *ip_multicast_if __P((struct in_addr *, int *)); static void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *, int)); static int ip_getmoptions @@ -177,7 +178,7 @@ ip_output(m0, opt, ro, flags, imo) m0 = m = m->m_next ; #ifdef IPSEC so = ipsec_getsocket(m); - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif ip = mtod(m, struct ip *); hlen = IP_VHL_HL(ip->ip_vhl) << 2 ; @@ -188,7 +189,7 @@ ip_output(m0, opt, ro, flags, imo) #endif #ifdef IPSEC so = ipsec_getsocket(m); - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif #ifdef DIAGNOSTIC @@ -430,6 +431,133 @@ ip_output(m0, opt, ro, flags, imo) } sendit: +#ifdef IPSEC + /* get SP for this packet */ + if (so == NULL) + sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, flags, &error); + else + sp = ipsec4_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); + + if (sp == NULL) { + ipsecstat.out_inval++; + goto bad; + } + + error = 0; + + /* check policy */ + switch (sp->policy) { + case IPSEC_POLICY_DISCARD: + /* + * This packet is just discarded. + */ + ipsecstat.out_polvio++; + goto bad; + + case IPSEC_POLICY_BYPASS: + case IPSEC_POLICY_NONE: + /* no need to do IPsec. */ + goto skip_ipsec; + + case IPSEC_POLICY_IPSEC: + if (sp->req == NULL) { + /* acquire a policy */ + error = key_spdacquire(sp); + goto bad; + } + break; + + case IPSEC_POLICY_ENTRUST: + default: + printf("ip_output: Invalid policy found. %d\n", sp->policy); + } + { + struct ipsec_output_state state; + bzero(&state, sizeof(state)); + state.m = m; + if (flags & IP_ROUTETOIF) { + state.ro = &iproute; + bzero(&iproute, sizeof(iproute)); + } else + state.ro = ro; + state.dst = (struct sockaddr *)dst; + + ip->ip_sum = 0; + + /* + * XXX + * delayed checksums are not currently compatible with IPsec + */ + if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { + in_delayed_cksum(m); + m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + } + + HTONS(ip->ip_len); + HTONS(ip->ip_off); + + error = ipsec4_output(&state, sp, flags); + + m = state.m; + if (flags & IP_ROUTETOIF) { + /* + * if we have tunnel mode SA, we may need to ignore + * IP_ROUTETOIF. + */ + if (state.ro != &iproute || state.ro->ro_rt != NULL) { + flags &= ~IP_ROUTETOIF; + ro = state.ro; + } + } else + ro = state.ro; + dst = (struct sockaddr_in *)state.dst; + if (error) { + /* mbuf is already reclaimed in ipsec4_output. */ + m0 = NULL; + switch (error) { + case EHOSTUNREACH: + case ENETUNREACH: + case EMSGSIZE: + case ENOBUFS: + case ENOMEM: + break; + default: + printf("ip4_output (ipsec): error code %d\n", error); + /*fall through*/ + case ENOENT: + /* don't show these error codes to the user */ + error = 0; + break; + } + goto bad; + } + } + + /* be sure to update variables that are affected by ipsec4_output() */ + ip = mtod(m, struct ip *); +#ifdef _IP_VHL + hlen = IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + if (ro->ro_rt == NULL) { + if ((flags & IP_ROUTETOIF) == 0) { + printf("ip_output: " + "can't update route after IPsec processing\n"); + error = EHOSTUNREACH; /*XXX*/ + goto bad; + } + } else { + ia = ifatoia(ro->ro_rt->rt_ifa); + ifp = ro->ro_rt->rt_ifp; + } + + /* make it flipped, again. */ + NTOHS(ip->ip_len); + NTOHS(ip->ip_off); +skip_ipsec: +#endif /*IPSEC*/ + /* * IpHack's section. * - Xlate: translate packet's addr/port (NAT). @@ -661,134 +789,6 @@ sendit: } pass: -#ifdef IPSEC - /* get SP for this packet */ - if (so == NULL) - sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, flags, &error); - else - sp = ipsec4_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); - - if (sp == NULL) { - ipsecstat.out_inval++; - goto bad; - } - - error = 0; - - /* check policy */ - switch (sp->policy) { - case IPSEC_POLICY_DISCARD: - /* - * This packet is just discarded. - */ - ipsecstat.out_polvio++; - goto bad; - - case IPSEC_POLICY_BYPASS: - case IPSEC_POLICY_NONE: - /* no need to do IPsec. */ - goto skip_ipsec; - - case IPSEC_POLICY_IPSEC: - if (sp->req == NULL) { - /* XXX should be panic ? */ - printf("ip_output: No IPsec request specified.\n"); - error = EINVAL; - goto bad; - } - break; - - case IPSEC_POLICY_ENTRUST: - default: - printf("ip_output: Invalid policy found. %d\n", sp->policy); - } - { - struct ipsec_output_state state; - bzero(&state, sizeof(state)); - state.m = m; - if (flags & IP_ROUTETOIF) { - state.ro = &iproute; - bzero(&iproute, sizeof(iproute)); - } else - state.ro = ro; - state.dst = (struct sockaddr *)dst; - - ip->ip_sum = 0; - - /* - * XXX - * delayed checksums are not currently compatible with IPsec - */ - if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { - in_delayed_cksum(m); - m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; - } - - HTONS(ip->ip_len); - HTONS(ip->ip_off); - - error = ipsec4_output(&state, sp, flags); - - m = state.m; - if (flags & IP_ROUTETOIF) { - /* - * if we have tunnel mode SA, we may need to ignore - * IP_ROUTETOIF. - */ - if (state.ro != &iproute || state.ro->ro_rt != NULL) { - flags &= ~IP_ROUTETOIF; - ro = state.ro; - } - } else - ro = state.ro; - dst = (struct sockaddr_in *)state.dst; - if (error) { - /* mbuf is already reclaimed in ipsec4_output. */ - m0 = NULL; - switch (error) { - case EHOSTUNREACH: - case ENETUNREACH: - case EMSGSIZE: - case ENOBUFS: - case ENOMEM: - break; - default: - printf("ip4_output (ipsec): error code %d\n", error); - /*fall through*/ - case ENOENT: - /* don't show these error codes to the user */ - error = 0; - break; - } - goto bad; - } - } - - /* be sure to update variables that are affected by ipsec4_output() */ - ip = mtod(m, struct ip *); -#ifdef _IP_VHL - hlen = IP_VHL_HL(ip->ip_vhl) << 2; -#else - hlen = ip->ip_hl << 2; -#endif - if (ro->ro_rt == NULL) { - if ((flags & IP_ROUTETOIF) == 0) { - printf("ip_output: " - "can't update route after IPsec processing\n"); - error = EHOSTUNREACH; /*XXX*/ - goto bad; - } - } else { - ia = ifatoia(ro->ro_rt->rt_ifa); - ifp = ro->ro_rt->rt_ifp; - } - - /* make it flipped, again. */ - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); -skip_ipsec: -#endif /*IPSEC*/ - m->m_pkthdr.csum_flags |= CSUM_IP; sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist; if (sw_csum & CSUM_DELAY_DATA) { @@ -820,6 +820,11 @@ skip_ipsec: ia->ia_ifa.if_obytes += m->m_pkthdr.len; } +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif + error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); goto done; @@ -946,6 +951,10 @@ sendorfree: for (m = m0; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = 0; +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif if (error == 0) { /* Record statistics for this interface address. */ ia->ia_ifa.if_opackets++; @@ -1480,6 +1489,33 @@ bad: * transmission, and one (IP_MULTICAST_TTL) totally duplicates a * standard option (IP_TTL). */ + +/* + * following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index. + */ +static struct ifnet * +ip_multicast_if(a, ifindexp) + struct in_addr *a; + int *ifindexp; +{ + int ifindex; + struct ifnet *ifp; + + if (ifindexp) + *ifindexp = 0; + if (ntohl(a->s_addr) >> 24 == 0) { + ifindex = ntohl(a->s_addr) & 0xffffff; + if (ifindex < 0 || if_index < ifindex) + return NULL; + ifp = ifindex2ifnet[ifindex]; + if (ifindexp) + *ifindexp = ifindex; + } else { + INADDR_TO_IFP(*a, ifp); + } + return ifp; +} + /* * Set the IP multicast options in response to user setsockopt(). */ @@ -1496,6 +1532,7 @@ ip_setmoptions(sopt, imop) struct ip_moptions *imo = *imop; struct route ro; struct sockaddr_in *dst; + int ifindex; int s; if (imo == NULL) { @@ -1510,6 +1547,7 @@ ip_setmoptions(sopt, imop) return (ENOBUFS); *imop = imo; imo->imo_multicast_ifp = NULL; + imo->imo_multicast_addr.s_addr = INADDR_ANY; imo->imo_multicast_vif = -1; imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP; @@ -1555,13 +1593,17 @@ ip_setmoptions(sopt, imop) * it supports multicasting. */ s = splimp(); - INADDR_TO_IFP(addr, ifp); + ifp = ip_multicast_if(&addr, &ifindex); if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { splx(s); error = EADDRNOTAVAIL; break; } imo->imo_multicast_ifp = ifp; + if (ifindex) + imo->imo_multicast_addr = addr; + else + imo->imo_multicast_addr.s_addr = INADDR_ANY; splx(s); break; @@ -1648,7 +1690,7 @@ ip_setmoptions(sopt, imop) rtfree(ro.ro_rt); } else { - INADDR_TO_IFP(mreq.imr_interface, ifp); + ifp = ip_multicast_if(&mreq.imr_interface, NULL); } /* @@ -1716,7 +1758,7 @@ ip_setmoptions(sopt, imop) if (mreq.imr_interface.s_addr == INADDR_ANY) ifp = NULL; else { - INADDR_TO_IFP(mreq.imr_interface, ifp); + ifp = ip_multicast_if(&mreq.imr_interface, NULL); if (ifp == NULL) { error = EADDRNOTAVAIL; splx(s); @@ -1798,7 +1840,10 @@ ip_getmoptions(sopt, imo) case IP_MULTICAST_IF: if (imo == NULL || imo->imo_multicast_ifp == NULL) addr.s_addr = INADDR_ANY; - else { + else if (imo->imo_multicast_addr.s_addr) { + /* return the value user has set */ + addr = imo->imo_multicast_addr; + } else { IFP_TO_IA(imo->imo_multicast_ifp, ia); addr.s_addr = (ia == NULL) ? INADDR_ANY : IA_SIN(ia)->sin_addr.s_addr; diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index 6354d84da527..b318a1c001d4 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -37,6 +37,8 @@ #ifndef _NETINET_IP_VAR_H_ #define _NETINET_IP_VAR_H_ +#include <sys/queue.h> + /* * Overlay for ip header used by other protocols (tcp, udp). */ @@ -86,6 +88,7 @@ struct ipoption { */ struct ip_moptions { struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */ + struct in_addr imo_multicast_addr; /* ifindex/addr on MULTICAST_IF */ u_char imo_multicast_ttl; /* TTL for outgoing multicasts */ u_char imo_multicast_loop; /* 1 => hear sends if a member */ u_short imo_num_memberships; /* no. memberships this socket */ @@ -122,6 +125,7 @@ struct ipstat { u_long ips_toolong; /* ip length > max ip packet size */ u_long ips_notmember; /* multicasts for unregistered grps */ u_long ips_nogif; /* no match gif found */ + u_long ips_badaddr; /* invalid address on header */ }; #ifdef _KERNEL diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index 4fdcf9523ade..32c909a1fb2b 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -137,6 +137,15 @@ rip_input(m, off, proto) continue; if (last) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); + +#ifdef IPSEC + /* check AH/ESP integrity. */ + if (n && ipsec4_in_reject_so(n, last->inp_socket)) { + m_freem(n); + ipsecstat.in_polvio++; + /* do not inject data to pcb */ + } else +#endif /*IPSEC*/ if (n) { if (last->inp_flags & INP_CONTROLOPTS || last->inp_socket->so_options & SO_TIMESTAMP) @@ -155,6 +164,15 @@ rip_input(m, off, proto) } last = inp; } +#ifdef IPSEC + /* check AH/ESP integrity. */ + if (last && ipsec4_in_reject_so(m, last->inp_socket)) { + m_freem(m); + ipsecstat.in_polvio++; + ipstat.ips_delivered--; + /* do not inject data to pcb */ + } else +#endif /*IPSEC*/ if (last) { if (last->inp_flags & INP_CONTROLOPTS || last->inp_socket->so_options & SO_TIMESTAMP) @@ -168,9 +186,9 @@ rip_input(m, off, proto) sorwakeup(last->inp_socket); } else { m_freem(m); - ipstat.ips_noproto++; - ipstat.ips_delivered--; - } + ipstat.ips_noproto++; + ipstat.ips_delivered--; + } } /* @@ -232,7 +250,10 @@ rip_output(m, so, dst) } #ifdef IPSEC - ipsec_setsocket(m, so); + if (ipsec_setsocket(m, so) != 0) { + m_freem(m); + return ENOBUFS; + } #endif /*IPSEC*/ return (ip_output(m, inp->inp_options, &inp->inp_route, flags, diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 34c2006d3d22..3554dae2e9ac 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -305,6 +305,7 @@ tcp6_input(mp, offp, proto) int *offp, proto; { register struct mbuf *m = *mp; + struct in6_ifaddr *ia6; IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), IPPROTO_DONE); @@ -312,7 +313,8 @@ tcp6_input(mp, offp, proto) * draft-itojun-ipv6-tcp-to-anycast * better place to put this in? */ - if (m->m_flags & M_ANYCAST6) { + ia6 = ip6_getdstifaddr(m); + if (ia6 && (ia6->ia6_flags & IN6_IFF_ANYCAST)) { struct ip6_hdr *ip6; ip6 = mtod(m, struct ip6_hdr *); @@ -379,6 +381,19 @@ tcp_input(m, off0, proto) goto drop; } th = (struct tcphdr *)((caddr_t)ip6 + off0); + + /* + * Be proactive about unspecified IPv6 address in source. + * As we use all-zero to indicate unbounded/unconnected pcb, + * unspecified IPv6 address can be used to confuse us. + * + * Note that packets with unspecified IPv6 destination is + * already dropped in ip6_input. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { + /* XXX stat */ + goto drop; + } } else #endif /* INET6 */ { @@ -627,18 +642,6 @@ findpcb: else tiwin = th->th_win; -#ifdef INET6 - /* save packet options if user wanted */ - if (isipv6 && inp->in6p_flags & INP_CONTROLOPTS) { - if (inp->in6p_options) { - m_freem(inp->in6p_options); - inp->in6p_options = 0; - } - ip6_savecontrol(inp, &inp->in6p_options, ip6, m); - } - /* else, should also do ip_srcroute() here? */ -#endif /* INET6 */ - so = inp->inp_socket; if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) { #ifdef TCPDEBUG @@ -683,6 +686,50 @@ findpcb: goto drop; } #endif + +#ifdef INET6 + /* + * If deprecated address is forbidden, + * we do not accept SYN to deprecated interface + * address to prevent any new inbound connection from + * getting established. + * When we do not accept SYN, we send a TCP RST, + * with deprecated source address (instead of dropping + * it). We compromise it as it is much better for peer + * to send a RST, and RST will be the final packet + * for the exchange. + * + * If we do not forbid deprecated addresses, we accept + * the SYN packet. RFC2462 does not suggest dropping + * SYN in this case. + * If we decipher RFC2462 5.5.4, it says like this: + * 1. use of deprecated addr with existing + * communication is okay - "SHOULD continue to be + * used" + * 2. use of it with new communication: + * (2a) "SHOULD NOT be used if alternate address + * with sufficient scope is available" + * (2b) nothing mentioned otherwise. + * Here we fall into (2b) case as we have no choice in + * our source address selection - we must obey the peer. + * + * The wording in RFC2462 is confusing, and there are + * multiple description text for deprecated address + * handling - worse, they are not exactly the same. + * I believe 5.5.4 is the best one, so we follow 5.5.4. + */ + if (isipv6 && !ip6_use_deprecated) { + struct in6_ifaddr *ia6; + + if ((ia6 = ip6_getdstifaddr(m)) && + (ia6->ia6_flags & IN6_IFF_DEPRECATED)) { + tp = NULL; + rstreason = BANDLIM_RST_OPENPORT; + goto dropwithreset; + } + } +#endif + so2 = sonewconn(so, 0); if (so2 == 0) { /* @@ -731,10 +778,8 @@ findpcb: if (isipv6) inp->in6p_laddr = ip6->ip6_dst; else { - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { - inp->inp_vflag &= ~INP_IPV6; - inp->inp_vflag |= INP_IPV4; - } + inp->inp_vflag &= ~INP_IPV6; + inp->inp_vflag |= INP_IPV4; #endif /* INET6 */ inp->inp_laddr = ip->ip_dst; #ifdef INET6 @@ -779,21 +824,25 @@ findpcb: #endif #ifdef INET6 if (isipv6) { - /* - * inherit socket options from the listening - * socket. - */ + /* + * Inherit socket options from the listening + * socket. + * Note that in6p_inputopts are not (even + * should not be) copied, since it stores + * previously received options and is used to + * detect if each new option is different than + * the previous one and hence should be passed + * to a user. + * If we copied in6p_inputopts, a user would + * not be able to receive options just after + * calling the accept system call. + */ inp->inp_flags |= oinp->inp_flags & INP_CONTROLOPTS; - if (inp->inp_flags & INP_CONTROLOPTS) { - if (inp->in6p_options) { - m_freem(inp->in6p_options); - inp->in6p_options = 0; - } - ip6_savecontrol(inp, - &inp->in6p_options, - ip6, m); - } + if (oinp->in6p_outputopts) + inp->in6p_outputopts = + ip6_copypktopts(oinp->in6p_outputopts, + M_NOWAIT); } else #endif /* INET6 */ inp->inp_options = ip_srcroute(); diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index 286b420ac4ff..a2a2cf370cb6 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -376,7 +376,7 @@ send: * NOTE: we assume that the IP/TCP header plus TCP options * always fit in a single mbuf, leaving room for a maximum * link header, i.e. - * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN + * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MCLBYTES */ optlen = 0; #ifdef INET6 @@ -823,7 +823,11 @@ send: /* TODO: IPv6 IP6TOS_ECT bit on */ #ifdef IPSEC - ipsec_setsocket(m, so); + if (ipsec_setsocket(m, so) != 0) { + m_freem(m); + error = ENOBUFS; + goto out; + } #endif /*IPSEC*/ error = ip6_output(m, tp->t_inpcb->in6p_outputopts, diff --git a/sys/netinet/tcp_reass.c b/sys/netinet/tcp_reass.c index 34c2006d3d22..3554dae2e9ac 100644 --- a/sys/netinet/tcp_reass.c +++ b/sys/netinet/tcp_reass.c @@ -305,6 +305,7 @@ tcp6_input(mp, offp, proto) int *offp, proto; { register struct mbuf *m = *mp; + struct in6_ifaddr *ia6; IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), IPPROTO_DONE); @@ -312,7 +313,8 @@ tcp6_input(mp, offp, proto) * draft-itojun-ipv6-tcp-to-anycast * better place to put this in? */ - if (m->m_flags & M_ANYCAST6) { + ia6 = ip6_getdstifaddr(m); + if (ia6 && (ia6->ia6_flags & IN6_IFF_ANYCAST)) { struct ip6_hdr *ip6; ip6 = mtod(m, struct ip6_hdr *); @@ -379,6 +381,19 @@ tcp_input(m, off0, proto) goto drop; } th = (struct tcphdr *)((caddr_t)ip6 + off0); + + /* + * Be proactive about unspecified IPv6 address in source. + * As we use all-zero to indicate unbounded/unconnected pcb, + * unspecified IPv6 address can be used to confuse us. + * + * Note that packets with unspecified IPv6 destination is + * already dropped in ip6_input. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { + /* XXX stat */ + goto drop; + } } else #endif /* INET6 */ { @@ -627,18 +642,6 @@ findpcb: else tiwin = th->th_win; -#ifdef INET6 - /* save packet options if user wanted */ - if (isipv6 && inp->in6p_flags & INP_CONTROLOPTS) { - if (inp->in6p_options) { - m_freem(inp->in6p_options); - inp->in6p_options = 0; - } - ip6_savecontrol(inp, &inp->in6p_options, ip6, m); - } - /* else, should also do ip_srcroute() here? */ -#endif /* INET6 */ - so = inp->inp_socket; if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) { #ifdef TCPDEBUG @@ -683,6 +686,50 @@ findpcb: goto drop; } #endif + +#ifdef INET6 + /* + * If deprecated address is forbidden, + * we do not accept SYN to deprecated interface + * address to prevent any new inbound connection from + * getting established. + * When we do not accept SYN, we send a TCP RST, + * with deprecated source address (instead of dropping + * it). We compromise it as it is much better for peer + * to send a RST, and RST will be the final packet + * for the exchange. + * + * If we do not forbid deprecated addresses, we accept + * the SYN packet. RFC2462 does not suggest dropping + * SYN in this case. + * If we decipher RFC2462 5.5.4, it says like this: + * 1. use of deprecated addr with existing + * communication is okay - "SHOULD continue to be + * used" + * 2. use of it with new communication: + * (2a) "SHOULD NOT be used if alternate address + * with sufficient scope is available" + * (2b) nothing mentioned otherwise. + * Here we fall into (2b) case as we have no choice in + * our source address selection - we must obey the peer. + * + * The wording in RFC2462 is confusing, and there are + * multiple description text for deprecated address + * handling - worse, they are not exactly the same. + * I believe 5.5.4 is the best one, so we follow 5.5.4. + */ + if (isipv6 && !ip6_use_deprecated) { + struct in6_ifaddr *ia6; + + if ((ia6 = ip6_getdstifaddr(m)) && + (ia6->ia6_flags & IN6_IFF_DEPRECATED)) { + tp = NULL; + rstreason = BANDLIM_RST_OPENPORT; + goto dropwithreset; + } + } +#endif + so2 = sonewconn(so, 0); if (so2 == 0) { /* @@ -731,10 +778,8 @@ findpcb: if (isipv6) inp->in6p_laddr = ip6->ip6_dst; else { - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { - inp->inp_vflag &= ~INP_IPV6; - inp->inp_vflag |= INP_IPV4; - } + inp->inp_vflag &= ~INP_IPV6; + inp->inp_vflag |= INP_IPV4; #endif /* INET6 */ inp->inp_laddr = ip->ip_dst; #ifdef INET6 @@ -779,21 +824,25 @@ findpcb: #endif #ifdef INET6 if (isipv6) { - /* - * inherit socket options from the listening - * socket. - */ + /* + * Inherit socket options from the listening + * socket. + * Note that in6p_inputopts are not (even + * should not be) copied, since it stores + * previously received options and is used to + * detect if each new option is different than + * the previous one and hence should be passed + * to a user. + * If we copied in6p_inputopts, a user would + * not be able to receive options just after + * calling the accept system call. + */ inp->inp_flags |= oinp->inp_flags & INP_CONTROLOPTS; - if (inp->inp_flags & INP_CONTROLOPTS) { - if (inp->in6p_options) { - m_freem(inp->in6p_options); - inp->in6p_options = 0; - } - ip6_savecontrol(inp, - &inp->in6p_options, - ip6, m); - } + if (oinp->in6p_outputopts) + inp->in6p_outputopts = + ip6_copypktopts(oinp->in6p_outputopts, + M_NOWAIT); } else #endif /* INET6 */ inp->inp_options = ip_srcroute(); diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 3857b7513029..06849be35acd 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -443,7 +443,10 @@ tcp_respond(tp, ipgen, th, m, ack, seq, flags) tcp_trace(TA_OUTPUT, 0, tp, mtod(m, void *), th, 0); #endif #ifdef IPSEC - ipsec_setsocket(m, tp ? tp->t_inpcb->inp_socket : NULL); + if (ipsec_setsocket(m, tp ? tp->t_inpcb->inp_socket : NULL) != 0) { + m_freem(m); + return; + } #endif #ifdef INET6 if (isipv6) { @@ -1020,13 +1023,17 @@ tcp6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - register struct tcphdr *thp; struct tcphdr th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; + struct ip6ctlparam *ip6cp = NULL; + const struct sockaddr_in6 *sa6_src = NULL; int off; + struct tcp_portonly { + u_int16_t th_sport; + u_int16_t th_dport; + } *thp; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) @@ -1042,56 +1049,36 @@ tcp6_ctlinput(cmd, sa, d) /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; off = 0; /* fool gcc */ + sa6_src = &sa6_any; } - /* - * Translate addresses into internal form. - * Sa check if it is AF_INET6 is done at the top of this funciton. - */ - sa6 = *(struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) != 0 && m != NULL && - m->m_pkthdr.rcvif != NULL) - sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - if (ip6) { /* * XXX: We assume that when IPV6 is non NULL, * M and OFF are valid. */ - struct in6_addr s; - - /* translate addresses into internal form */ - memcpy(&s, &ip6->ip6_src, sizeof(s)); - if (IN6_IS_ADDR_LINKLOCAL(&s) != 0 && m != NULL && - m->m_pkthdr.rcvif != NULL) - s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); /* check if we can safely examine src and dst ports */ - if (m->m_pkthdr.len < off + sizeof(th)) + if (m->m_pkthdr.len < off + sizeof(*thp)) return; - if (m->m_len < off + sizeof(th)) { - /* - * this should be rare case - * because now MINCLSIZE is "(MHLEN + 1)", - * so we compromise on this copy... - */ - m_copydata(m, off, sizeof(th), (caddr_t)&th); - thp = &th; - } else - thp = (struct tcphdr *)(mtod(m, caddr_t) + off); - in6_pcbnotify(&tcb, (struct sockaddr *)&sa6, thp->th_dport, - &s, thp->th_sport, cmd, notify); + bzero(&th, sizeof(th)); + m_copydata(m, off, sizeof(*thp), (caddr_t)&th); + + in6_pcbnotify(&tcb, sa, th.th_dport, + (struct sockaddr *)ip6cp->ip6c_src, + th.th_sport, cmd, notify); } else - in6_pcbnotify(&tcb, (struct sockaddr *)&sa6, 0, &zeroin6_addr, + in6_pcbnotify(&tcb, sa, 0, (struct sockaddr *)sa6_src, 0, cmd, notify); } #endif /* INET6 */ @@ -1323,9 +1310,12 @@ tcp_rtlookup6(inp) if (rt == NULL || !(rt->rt_flags & RTF_UP)) { /* No route yet, so try to acquire one */ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { - ro6->ro_dst.sin6_family = AF_INET6; - ro6->ro_dst.sin6_len = sizeof(ro6->ro_dst); - ro6->ro_dst.sin6_addr = inp->in6p_faddr; + struct sockaddr_in6 *dst6; + + dst6 = (struct sockaddr_in6 *)&ro6->ro_dst; + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(ro6->ro_dst); + dst6->sin6_addr = inp->in6p_faddr; rtalloc((struct route *)ro6); rt = ro6->ro_rt; } @@ -1376,6 +1366,7 @@ ipsec_hdrsiz_tcp(tp) sizeof(struct ip)); bcopy((caddr_t)&tp->t_template->tt_t, (caddr_t)th, sizeof(struct tcphdr)); + ip->ip_vhl = IP_VHL_BORING; hdrsiz = ipsec4_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp); } diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index 3857b7513029..06849be35acd 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -443,7 +443,10 @@ tcp_respond(tp, ipgen, th, m, ack, seq, flags) tcp_trace(TA_OUTPUT, 0, tp, mtod(m, void *), th, 0); #endif #ifdef IPSEC - ipsec_setsocket(m, tp ? tp->t_inpcb->inp_socket : NULL); + if (ipsec_setsocket(m, tp ? tp->t_inpcb->inp_socket : NULL) != 0) { + m_freem(m); + return; + } #endif #ifdef INET6 if (isipv6) { @@ -1020,13 +1023,17 @@ tcp6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - register struct tcphdr *thp; struct tcphdr th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; + struct ip6ctlparam *ip6cp = NULL; + const struct sockaddr_in6 *sa6_src = NULL; int off; + struct tcp_portonly { + u_int16_t th_sport; + u_int16_t th_dport; + } *thp; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) @@ -1042,56 +1049,36 @@ tcp6_ctlinput(cmd, sa, d) /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; off = 0; /* fool gcc */ + sa6_src = &sa6_any; } - /* - * Translate addresses into internal form. - * Sa check if it is AF_INET6 is done at the top of this funciton. - */ - sa6 = *(struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) != 0 && m != NULL && - m->m_pkthdr.rcvif != NULL) - sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - if (ip6) { /* * XXX: We assume that when IPV6 is non NULL, * M and OFF are valid. */ - struct in6_addr s; - - /* translate addresses into internal form */ - memcpy(&s, &ip6->ip6_src, sizeof(s)); - if (IN6_IS_ADDR_LINKLOCAL(&s) != 0 && m != NULL && - m->m_pkthdr.rcvif != NULL) - s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); /* check if we can safely examine src and dst ports */ - if (m->m_pkthdr.len < off + sizeof(th)) + if (m->m_pkthdr.len < off + sizeof(*thp)) return; - if (m->m_len < off + sizeof(th)) { - /* - * this should be rare case - * because now MINCLSIZE is "(MHLEN + 1)", - * so we compromise on this copy... - */ - m_copydata(m, off, sizeof(th), (caddr_t)&th); - thp = &th; - } else - thp = (struct tcphdr *)(mtod(m, caddr_t) + off); - in6_pcbnotify(&tcb, (struct sockaddr *)&sa6, thp->th_dport, - &s, thp->th_sport, cmd, notify); + bzero(&th, sizeof(th)); + m_copydata(m, off, sizeof(*thp), (caddr_t)&th); + + in6_pcbnotify(&tcb, sa, th.th_dport, + (struct sockaddr *)ip6cp->ip6c_src, + th.th_sport, cmd, notify); } else - in6_pcbnotify(&tcb, (struct sockaddr *)&sa6, 0, &zeroin6_addr, + in6_pcbnotify(&tcb, sa, 0, (struct sockaddr *)sa6_src, 0, cmd, notify); } #endif /* INET6 */ @@ -1323,9 +1310,12 @@ tcp_rtlookup6(inp) if (rt == NULL || !(rt->rt_flags & RTF_UP)) { /* No route yet, so try to acquire one */ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { - ro6->ro_dst.sin6_family = AF_INET6; - ro6->ro_dst.sin6_len = sizeof(ro6->ro_dst); - ro6->ro_dst.sin6_addr = inp->in6p_faddr; + struct sockaddr_in6 *dst6; + + dst6 = (struct sockaddr_in6 *)&ro6->ro_dst; + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(ro6->ro_dst); + dst6->sin6_addr = inp->in6p_faddr; rtalloc((struct route *)ro6); rt = ro6->ro_rt; } @@ -1376,6 +1366,7 @@ ipsec_hdrsiz_tcp(tp) sizeof(struct ip)); bcopy((caddr_t)&tp->t_template->tt_t, (caddr_t)th, sizeof(struct tcphdr)); + ip->ip_vhl = IP_VHL_BORING; hdrsiz = ipsec4_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp); } diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 25834d412812..aea92c0d33cc 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -240,8 +240,7 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) } inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { - + if (ip6_mapped_addr_on && (inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) inp->inp_vflag |= INP_IPV4; else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { @@ -292,7 +291,8 @@ tcp6_usr_listen(struct socket *so, struct proc *p) COMMON_START(); if (inp->inp_lport == 0) { inp->inp_vflag &= ~INP_IPV4; - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) + if (ip6_mapped_addr_on && + (inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) inp->inp_vflag |= INP_IPV4; error = in6_pcbbind(inp, (struct sockaddr *)0, p); } @@ -361,10 +361,13 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) goto out; } - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0 && - IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { struct sockaddr_in sin; + if (!ip6_mapped_addr_on || + (inp->inp_flags & IN6P_IPV6_V6ONLY)) + return(EINVAL); + in6_sin6_2_sin(&sin, sin6p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 350a3842b39b..d546b1f30917 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -392,8 +392,7 @@ udp_input(m, off, proto) #endif ip_savecontrol(inp, &opts, ip, m); } - iphlen += sizeof(struct udphdr); - m_adj(m, iphlen); + m_adj(m, iphlen + sizeof(struct udphdr)); #ifdef INET6 if (inp->inp_vflag & INP_IPV6) { in6_sin_2_v4mapsin6(&udp_in, &udp_in6.uin6_sin); @@ -744,7 +743,10 @@ udp_output(inp, m, addr, control, p) udpstat.udps_opackets++; #ifdef IPSEC - ipsec_setsocket(m, inp->inp_socket); + if (ipsec_setsocket(m, inp->inp_socket) != 0) { + error = ENOBUFS; + goto release; + } #endif /*IPSEC*/ error = ip_output(m, inp->inp_options, &inp->inp_route, (inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)), diff --git a/sys/netinet6/ah.h b/sys/netinet6/ah.h index 3c7d0a671ae2..084646677f28 100644 --- a/sys/netinet6/ah.h +++ b/sys/netinet6/ah.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah.h,v 1.10 2000/07/02 13:23:33 itojun Exp $ */ +/* $KAME: ah.h,v 1.13 2000/10/18 21:28:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,7 +37,9 @@ #ifndef _NETINET6_AH_H_ #define _NETINET6_AH_H_ -struct secasvar; +#if defined(_KERNEL) && !defined(_LKM) +#include "opt_inet.h" +#endif struct ah { u_int8_t ah_nxt; /* Next Header */ @@ -56,6 +58,9 @@ struct newah { /* variable size, 32bit bound*/ /* Authentication data */ }; +#ifdef _KERNEL +struct secasvar; + struct ah_algorithm_state { struct secasvar *sav; void* foo; /*per algorithm data - maybe*/ @@ -74,8 +79,7 @@ struct ah_algorithm { #define AH_MAXSUMSIZE 16 -#ifdef _KERNEL -extern struct ah_algorithm ah_algorithms[]; +extern const struct ah_algorithm *ah_algorithm_lookup __P((int)); /* cksum routines */ extern int ah_hdrlen __P((struct secasvar *)); @@ -84,7 +88,7 @@ extern size_t ah_hdrsiz __P((struct ipsecrequest *)); extern void ah4_input __P((struct mbuf *, ...)); extern int ah4_output __P((struct mbuf *, struct ipsecrequest *)); extern int ah4_calccksum __P((struct mbuf *, caddr_t, size_t, - struct ah_algorithm *, struct secasvar *)); + const struct ah_algorithm *, struct secasvar *)); #endif /*_KERNEL*/ #endif /*_NETINET6_AH_H_*/ diff --git a/sys/netinet6/ah6.h b/sys/netinet6/ah6.h index 4010f1335cb8..ead07bfdadb0 100644 --- a/sys/netinet6/ah6.h +++ b/sys/netinet6/ah6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah.h,v 1.10 2000/07/02 13:23:33 itojun Exp $ */ +/* $KAME: ah.h,v 1.13 2000/10/18 21:28:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -44,7 +44,9 @@ extern int ah6_input __P((struct mbuf **, int *, int)); extern int ah6_output __P((struct mbuf *, u_char *, struct mbuf *, struct ipsecrequest *)); extern int ah6_calccksum __P((struct mbuf *, caddr_t, size_t, - struct ah_algorithm *, struct secasvar *)); + const struct ah_algorithm *, struct secasvar *)); + +extern void ah6_ctlinput __P((int, struct sockaddr *, void *)); #endif #endif /*_NETINET6_AH6_H_*/ diff --git a/sys/netinet6/ah_core.c b/sys/netinet6/ah_core.c index 477de5117b71..5c7afaf8d5fe 100644 --- a/sys/netinet6/ah_core.c +++ b/sys/netinet6/ah_core.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah_core.c,v 1.35 2000/06/14 11:14:03 itojun Exp $ */ +/* $KAME: ah_core.c,v 1.44 2001/03/12 11:24:39 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -34,6 +34,8 @@ * RFC1826/2402 authentication header. */ +/* TODO: have shared routines for hmac-* algorithms */ + #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" @@ -82,6 +84,7 @@ #include <netkey/keydb.h> #include <sys/md5.h> #include <crypto/sha1.h> +#include <crypto/sha2/sha2.h> #include <net/net_osdep.h> @@ -90,8 +93,7 @@ static int ah_sumsiz_1216 __P((struct secasvar *)); static int ah_sumsiz_zero __P((struct secasvar *)); static int ah_none_mature __P((struct secasvar *)); -static int ah_none_init __P((struct ah_algorithm_state *, - struct secasvar *)); +static int ah_none_init __P((struct ah_algorithm_state *, struct secasvar *)); static void ah_none_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_none_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_keyed_md5_mature __P((struct secasvar *)); @@ -118,25 +120,84 @@ static int ah_hmac_sha1_init __P((struct ah_algorithm_state *, static void ah_hmac_sha1_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_hmac_sha1_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_hmac_sha2_256_mature __P((struct secasvar *)); +static int ah_hmac_sha2_256_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_hmac_sha2_256_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_hmac_sha2_256_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_hmac_sha2_384_mature __P((struct secasvar *)); +static int ah_hmac_sha2_384_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_hmac_sha2_384_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_hmac_sha2_384_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_hmac_sha2_512_mature __P((struct secasvar *)); +static int ah_hmac_sha2_512_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_hmac_sha2_512_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_hmac_sha2_512_result __P((struct ah_algorithm_state *, caddr_t)); + +static void ah_update_mbuf __P((struct mbuf *, int, int, + const struct ah_algorithm *, struct ah_algorithm_state *)); + +const struct ah_algorithm * +ah_algorithm_lookup(idx) + int idx; +{ + /* checksum algorithms */ + static struct ah_algorithm ah_algorithms[] = { + { ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128, "hmac-md5", + ah_hmac_md5_init, ah_hmac_md5_loop, + ah_hmac_md5_result, }, + { ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, "hmac-sha1", + ah_hmac_sha1_init, ah_hmac_sha1_loop, + ah_hmac_sha1_result, }, + { ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, "keyed-md5", + ah_keyed_md5_init, ah_keyed_md5_loop, + ah_keyed_md5_result, }, + { ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, "keyed-sha1", + ah_keyed_sha1_init, ah_keyed_sha1_loop, + ah_keyed_sha1_result, }, + { ah_sumsiz_zero, ah_none_mature, 0, 2048, "none", + ah_none_init, ah_none_loop, ah_none_result, }, + { ah_sumsiz_1216, ah_hmac_sha2_256_mature, 256, 256, + "hmac-sha2-256", + ah_hmac_sha2_256_init, ah_hmac_sha2_256_loop, + ah_hmac_sha2_256_result, }, + { ah_sumsiz_1216, ah_hmac_sha2_384_mature, 384, 384, + "hmac-sha2-384", + ah_hmac_sha2_384_init, ah_hmac_sha2_384_loop, + ah_hmac_sha2_384_result, }, + { ah_sumsiz_1216, ah_hmac_sha2_512_mature, 512, 512, + "hmac-sha2-512", + ah_hmac_sha2_512_init, ah_hmac_sha2_512_loop, + ah_hmac_sha2_512_result, }, + }; + + switch (idx) { + case SADB_AALG_MD5HMAC: + return &ah_algorithms[0]; + case SADB_AALG_SHA1HMAC: + return &ah_algorithms[1]; + case SADB_X_AALG_MD5: + return &ah_algorithms[2]; + case SADB_X_AALG_SHA: + return &ah_algorithms[3]; + case SADB_X_AALG_NULL: + return &ah_algorithms[4]; + case SADB_X_AALG_SHA2_256: + return &ah_algorithms[5]; + case SADB_X_AALG_SHA2_384: + return &ah_algorithms[6]; + case SADB_X_AALG_SHA2_512: + return &ah_algorithms[7]; + default: + return NULL; + } +} -static void ah_update_mbuf __P((struct mbuf *, int, int, struct ah_algorithm *, - struct ah_algorithm_state *)); - -/* checksum algorithms */ -/* NOTE: The order depends on SADB_AALG_x in net/pfkeyv2.h */ -struct ah_algorithm ah_algorithms[] = { - { 0, 0, 0, 0, 0, 0, }, - { ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128, "hmac-md5", - ah_hmac_md5_init, ah_hmac_md5_loop, ah_hmac_md5_result, }, - { ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, "hmac-sha1", - ah_hmac_sha1_init, ah_hmac_sha1_loop, ah_hmac_sha1_result, }, - { ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, "keyed-md5", - ah_keyed_md5_init, ah_keyed_md5_loop, ah_keyed_md5_result, }, - { ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, "keyed-sha1", - ah_keyed_sha1_init, ah_keyed_sha1_loop, ah_keyed_sha1_result, }, - { ah_sumsiz_zero, ah_none_mature, 0, 2048, "none", - ah_none_init, ah_none_loop, ah_none_result, }, -}; static int ah_sumsiz_1216(sav) @@ -297,13 +358,19 @@ static int ah_keyed_sha1_mature(sav) struct secasvar *sav; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; if (!sav->key_auth) { ipseclog((LOG_ERR, "ah_keyed_sha1_mature: no key is given.\n")); return 1; } - algo = &ah_algorithms[sav->alg_auth]; + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah_keyed_sha1_mature: unsupported algorithm.\n")); + return 1; + } + if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { ipseclog((LOG_ERR, @@ -385,7 +452,7 @@ ah_keyed_sha1_loop(state, addr, len) panic("ah_keyed_sha1_loop: what?"); ctxt = (SHA1_CTX *)state->foo; - SHA1Update(ctxt, (u_int8_t *)addr, (size_t)len); + SHA1Update(ctxt, (caddr_t)addr, (size_t)len); } static void @@ -414,13 +481,19 @@ static int ah_hmac_md5_mature(sav) struct secasvar *sav; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; if (!sav->key_auth) { ipseclog((LOG_ERR, "ah_hmac_md5_mature: no key is given.\n")); return 1; } - algo = &ah_algorithms[sav->alg_auth]; + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah_hmac_md5_mature: unsupported algorithm.\n")); + return 1; + } + if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { ipseclog((LOG_ERR, @@ -532,13 +605,19 @@ static int ah_hmac_sha1_mature(sav) struct secasvar *sav; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; if (!sav->key_auth) { ipseclog((LOG_ERR, "ah_hmac_sha1_mature: no key is given.\n")); return 1; } - algo = &ah_algorithms[sav->alg_auth]; + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah_hmac_sha1_mature: unsupported algorithm.\n")); + return 1; + } + if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { ipseclog((LOG_ERR, @@ -579,7 +658,7 @@ ah_hmac_sha1_init(state, sav) /* compress the key if necessery */ if (64 < _KEYLEN(state->sav->key_auth)) { SHA1Init(ctxt); - SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth), + SHA1Update(ctxt, _KEYBUF(state->sav->key_auth), _KEYLEN(state->sav->key_auth)); SHA1Final(&tk[0], ctxt); key = &tk[0]; @@ -616,7 +695,7 @@ ah_hmac_sha1_loop(state, addr, len) panic("ah_hmac_sha1_loop: what?"); ctxt = (SHA1_CTX *)(((u_char *)state->foo) + 128); - SHA1Update(ctxt, (u_int8_t *)addr, (size_t)len); + SHA1Update(ctxt, (caddr_t)addr, (size_t)len); } static void @@ -640,7 +719,7 @@ ah_hmac_sha1_result(state, addr) SHA1Init(ctxt); SHA1Update(ctxt, opad, 64); - SHA1Update(ctxt, (u_int8_t *)&digest[0], sizeof(digest)); + SHA1Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); SHA1Final((caddr_t)&digest[0], ctxt); bcopy(&digest[0], (void *)addr, HMACSIZE); @@ -648,6 +727,404 @@ ah_hmac_sha1_result(state, addr) free(state->foo, M_TEMP); } +static int +ah_hmac_sha2_256_mature(sav) + struct secasvar *sav; +{ + const struct ah_algorithm *algo; + + if (!sav->key_auth) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_256_mature: no key is given.\n")); + return 1; + } + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_256_mature: unsupported algorithm.\n")); + return 1; + } + + if (sav->key_auth->sadb_key_bits < algo->keymin || + algo->keymax < sav->key_auth->sadb_key_bits) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_256_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); + return 1; + } + + return 0; +} + +static int +ah_hmac_sha2_256_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + u_char *ipad; + u_char *opad; + SHA256_CTX *ctxt; + u_char tk[SHA256_DIGEST_LENGTH]; + u_char *key; + size_t keylen; + size_t i; + + if (!state) + panic("ah_hmac_sha2_256_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(64 + 64 + sizeof(SHA256_CTX), + M_TEMP, M_NOWAIT); + if (!state->foo) + return ENOBUFS; + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA256_CTX *)(opad + 64); + + /* compress the key if necessery */ + if (64 < _KEYLEN(state->sav->key_auth)) { + bzero(tk, sizeof(tk)); + bzero(ctxt, sizeof(*ctxt)); + SHA256_Init(ctxt); + SHA256_Update(ctxt, _KEYBUF(state->sav->key_auth), + _KEYLEN(state->sav->key_auth)); + SHA256_Final(&tk[0], ctxt); + key = &tk[0]; + keylen = sizeof(tk) < 64 ? sizeof(tk) : 64; + } else { + key = _KEYBUF(state->sav->key_auth); + keylen = _KEYLEN(state->sav->key_auth); + } + + bzero(ipad, 64); + bzero(opad, 64); + bcopy(key, ipad, keylen); + bcopy(key, opad, keylen); + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + bzero(ctxt, sizeof(*ctxt)); + SHA256_Init(ctxt); + SHA256_Update(ctxt, ipad, 64); + + return 0; +} + +static void +ah_hmac_sha2_256_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + SHA256_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_256_loop: what?"); + + ctxt = (SHA256_CTX *)(((u_char *)state->foo) + 128); + SHA256_Update(ctxt, (caddr_t)addr, (size_t)len); +} + +static void +ah_hmac_sha2_256_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[SHA256_DIGEST_LENGTH]; + u_char *ipad; + u_char *opad; + SHA256_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_256_result: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA256_CTX *)(opad + 64); + + SHA256_Final((caddr_t)&digest[0], ctxt); + + bzero(ctxt, sizeof(*ctxt)); + SHA256_Init(ctxt); + SHA256_Update(ctxt, opad, 64); + SHA256_Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); + SHA256_Final((caddr_t)&digest[0], ctxt); + + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + +static int +ah_hmac_sha2_384_mature(sav) + struct secasvar *sav; +{ + const struct ah_algorithm *algo; + + if (!sav->key_auth) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_384_mature: no key is given.\n")); + return 1; + } + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_384_mature: unsupported algorithm.\n")); + return 1; + } + + if (sav->key_auth->sadb_key_bits < algo->keymin || + algo->keymax < sav->key_auth->sadb_key_bits) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_384_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); + return 1; + } + + return 0; +} + +static int +ah_hmac_sha2_384_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + u_char *ipad; + u_char *opad; + SHA384_CTX *ctxt; + u_char tk[SHA384_DIGEST_LENGTH]; + u_char *key; + size_t keylen; + size_t i; + + if (!state) + panic("ah_hmac_sha2_384_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(64 + 64 + sizeof(SHA384_CTX), + M_TEMP, M_NOWAIT); + if (!state->foo) + return ENOBUFS; + bzero(state->foo, 64 + 64 + sizeof(SHA384_CTX)); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA384_CTX *)(opad + 64); + + /* compress the key if necessery */ + if (64 < _KEYLEN(state->sav->key_auth)) { + bzero(tk, sizeof(tk)); + bzero(ctxt, sizeof(*ctxt)); + SHA384_Init(ctxt); + SHA384_Update(ctxt, _KEYBUF(state->sav->key_auth), + _KEYLEN(state->sav->key_auth)); + SHA384_Final(&tk[0], ctxt); + key = &tk[0]; + keylen = sizeof(tk) < 64 ? sizeof(tk) : 64; + } else { + key = _KEYBUF(state->sav->key_auth); + keylen = _KEYLEN(state->sav->key_auth); + } + + bzero(ipad, 64); + bzero(opad, 64); + bcopy(key, ipad, keylen); + bcopy(key, opad, keylen); + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + bzero(ctxt, sizeof(*ctxt)); + SHA384_Init(ctxt); + SHA384_Update(ctxt, ipad, 64); + + return 0; +} + +static void +ah_hmac_sha2_384_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + SHA384_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_384_loop: what?"); + + ctxt = (SHA384_CTX *)(((u_char *)state->foo) + 128); + SHA384_Update(ctxt, (caddr_t)addr, (size_t)len); +} + +static void +ah_hmac_sha2_384_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[SHA384_DIGEST_LENGTH]; + u_char *ipad; + u_char *opad; + SHA384_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_384_result: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA384_CTX *)(opad + 64); + + SHA384_Final((caddr_t)&digest[0], ctxt); + + bzero(ctxt, sizeof(*ctxt)); + SHA384_Init(ctxt); + SHA384_Update(ctxt, opad, 64); + SHA384_Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); + SHA384_Final((caddr_t)&digest[0], ctxt); + + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + +static int +ah_hmac_sha2_512_mature(sav) + struct secasvar *sav; +{ + const struct ah_algorithm *algo; + + if (!sav->key_auth) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_512_mature: no key is given.\n")); + return 1; + } + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_512_mature: unsupported algorithm.\n")); + return 1; + } + + if (sav->key_auth->sadb_key_bits < algo->keymin || + algo->keymax < sav->key_auth->sadb_key_bits) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_512_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); + return 1; + } + + return 0; +} + +static int +ah_hmac_sha2_512_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + u_char *ipad; + u_char *opad; + SHA512_CTX *ctxt; + u_char tk[SHA512_DIGEST_LENGTH]; + u_char *key; + size_t keylen; + size_t i; + + if (!state) + panic("ah_hmac_sha2_512_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(64 + 64 + sizeof(SHA512_CTX), + M_TEMP, M_NOWAIT); + if (!state->foo) + return ENOBUFS; + bzero(state->foo, 64 + 64 + sizeof(SHA512_CTX)); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA512_CTX *)(opad + 64); + + /* compress the key if necessery */ + if (64 < _KEYLEN(state->sav->key_auth)) { + bzero(tk, sizeof(tk)); + bzero(ctxt, sizeof(*ctxt)); + SHA512_Init(ctxt); + SHA512_Update(ctxt, _KEYBUF(state->sav->key_auth), + _KEYLEN(state->sav->key_auth)); + SHA512_Final(&tk[0], ctxt); + key = &tk[0]; + keylen = sizeof(tk) < 64 ? sizeof(tk) : 64; + } else { + key = _KEYBUF(state->sav->key_auth); + keylen = _KEYLEN(state->sav->key_auth); + } + + bzero(ipad, 64); + bzero(opad, 64); + bcopy(key, ipad, keylen); + bcopy(key, opad, keylen); + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + bzero(ctxt, sizeof(*ctxt)); + SHA512_Init(ctxt); + SHA512_Update(ctxt, ipad, 64); + + return 0; +} + +static void +ah_hmac_sha2_512_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + SHA512_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_512_loop: what?"); + + ctxt = (SHA512_CTX *)(((u_char *)state->foo) + 128); + SHA512_Update(ctxt, (caddr_t)addr, (size_t)len); +} + +static void +ah_hmac_sha2_512_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[SHA512_DIGEST_LENGTH]; + u_char *ipad; + u_char *opad; + SHA512_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_512_result: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA512_CTX *)(opad + 64); + + SHA512_Final((caddr_t)&digest[0], ctxt); + + bzero(ctxt, sizeof(*ctxt)); + SHA512_Init(ctxt); + SHA512_Update(ctxt, opad, 64); + SHA512_Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); + SHA512_Final((caddr_t)&digest[0], ctxt); + + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + /*------------------------------------------------------------*/ /* @@ -658,7 +1135,7 @@ ah_update_mbuf(m, off, len, algo, algos) struct mbuf *m; int off; int len; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; struct ah_algorithm_state *algos; { struct mbuf *n; @@ -695,6 +1172,7 @@ ah_update_mbuf(m, off, len, algo, algos) } } +#ifdef INET /* * Go generate the checksum. This function won't modify the mbuf chain * except AH itself. @@ -707,7 +1185,7 @@ ah4_calccksum(m, ahdat, len, algo, sav) struct mbuf *m; caddr_t ahdat; size_t len; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; struct secasvar *sav; { int off; @@ -935,6 +1413,7 @@ fail: m_free(n); return error; } +#endif #ifdef INET6 /* @@ -949,7 +1428,7 @@ ah6_calccksum(m, ahdat, len, algo, sav) struct mbuf *m; caddr_t ahdat; size_t len; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; struct secasvar *sav; { int newoff, off; diff --git a/sys/netinet6/ah_input.c b/sys/netinet6/ah_input.c index b0489d1d5e35..070883e7a624 100644 --- a/sys/netinet6/ah_input.c +++ b/sys/netinet6/ah_input.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah_input.c,v 1.29 2000/05/29 08:33:53 itojun Exp $ */ +/* $KAME: ah_input.c,v 1.59 2001/05/16 04:01:27 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -66,7 +66,9 @@ #ifdef INET6 #include <netinet/ip6.h> #include <netinet6/ip6_var.h> +#include <netinet6/in6_pcb.h> #include <netinet/icmp6.h> +#include <netinet6/ip6protosw.h> #endif #include <netinet6/ipsec.h> @@ -107,16 +109,16 @@ ah4_input(m, va_alist) struct ip *ip; struct ah *ah; u_int32_t spi; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t siz; size_t siz1; u_char *cksum; struct secasvar *sav = NULL; u_int16_t nxt; size_t hlen; - int s; int off, proto; va_list ap; + size_t stripsiz = 0; va_start(ap, m); off = va_arg(ap, int); @@ -175,16 +177,16 @@ ah4_input(m, va_alist) ipsecstat.in_badspi++; goto fail; } - if (sav->alg_auth == SADB_AALG_NONE) { + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { ipseclog((LOG_DEBUG, "IPv4 AH input: " - "unspecified authentication algorithm for spi %u\n", + "unsupported authentication algorithm for spi %u\n", (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto fail; } - algo = &ah_algorithms[sav->alg_auth]; - siz = (*algo->sumsiz)(sav); siz1 = ((siz + 3) & ~(4 - 1)); @@ -284,29 +286,23 @@ ah4_input(m, va_alist) goto fail; } - { -#if 1 /* * some of IP header fields are flipped to the host endian. * convert them back to network endian. VERY stupid. */ ip->ip_len = htons(ip->ip_len + hlen); ip->ip_off = htons(ip->ip_off); -#endif if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) { free(cksum, M_TEMP); ipsecstat.in_inval++; goto fail; } ipsecstat.in_ahhist[sav->alg_auth]++; -#if 1 /* * flip them back. */ ip->ip_len = ntohs(ip->ip_len) - hlen; ip->ip_off = ntohs(ip->ip_off); -#endif - } { caddr_t sumpos = NULL; @@ -397,7 +393,14 @@ ah4_input(m, va_alist) } /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav)) { /* * strip off all the headers that precedes AH. * IP xx AH IP' payload -> IP' payload @@ -405,17 +408,9 @@ ah4_input(m, va_alist) * XXX more sanity checks * XXX relationship with gif? */ - size_t stripsiz = 0; u_int8_t tos; tos = ip->ip_tos; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } m_adj(m, off + stripsiz); if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); @@ -469,6 +464,11 @@ ah4_input(m, va_alist) #endif key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) { + ipsecstat.in_nomem++; + goto fail; + } if (! IF_HANDOFF(&ipintrq, m, NULL)) { ipsecstat.in_inval++; @@ -482,15 +482,6 @@ ah4_input(m, va_alist) /* * strip off AH. */ - size_t stripsiz = 0; - - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } ip = mtod(m, struct ip *); #ifndef PULLDOWN_TEST @@ -548,10 +539,19 @@ ah4_input(m, va_alist) /* forget about IP hdr checksum, the check has already been passed */ key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) { + ipsecstat.in_nomem++; + goto fail; + } - if (nxt != IPPROTO_DONE) + if (nxt != IPPROTO_DONE) { + if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto fail; + } (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); - else + } else m_freem(m); m = NULL; } @@ -587,12 +587,13 @@ ah6_input(mp, offp, proto) struct ip6_hdr *ip6; struct ah *ah; u_int32_t spi; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t siz; size_t siz1; u_char *cksum; struct secasvar *sav = NULL; u_int16_t nxt; + size_t stripsiz = 0; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE); @@ -601,7 +602,7 @@ ah6_input(mp, offp, proto) IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); if (ah == NULL) { ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n")); - ipsecstat.in_inval++; + ipsec6stat.in_inval++; return IPPROTO_DONE; } #endif @@ -637,16 +638,16 @@ ah6_input(mp, offp, proto) ipsec6stat.in_badspi++; goto fail; } - if (sav->alg_auth == SADB_AALG_NONE) { + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { ipseclog((LOG_DEBUG, "IPv6 AH input: " - "unspecified authentication algorithm for spi %u\n", + "unsupported authentication algorithm for spi %u\n", (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto fail; } - algo = &ah_algorithms[sav->alg_auth]; - siz = (*algo->sumsiz)(sav); siz1 = ((siz + 3) & ~(4 - 1)); @@ -685,7 +686,7 @@ ah6_input(mp, offp, proto) sizeof(struct ah) + sizoff + siz1); if (ah == NULL) { ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part")); - ipsecstat.in_inval++; + ipsec6stat.in_inval++; m = NULL; goto fail; } @@ -808,7 +809,14 @@ ah6_input(mp, offp, proto) } /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav)) { /* * strip off all the headers that precedes AH. * IP6 xx AH IP6' payload -> IP6' payload @@ -816,17 +824,9 @@ ah6_input(mp, offp, proto) * XXX more sanity checks * XXX relationship with gif? */ - size_t stripsiz = 0; u_int32_t flowinfo; /*net endian*/ flowinfo = ip6->ip6_flow; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } m_adj(m, off + stripsiz); if (m->m_len < sizeof(*ip6)) { /* @@ -870,6 +870,11 @@ ah6_input(mp, offp, proto) #endif key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) { + ipsec6stat.in_nomem++; + goto fail; + } if (! IF_HANDOFF(&ip6intrq, m, NULL)) { ipsec6stat.in_inval++; @@ -883,7 +888,6 @@ ah6_input(mp, offp, proto) /* * strip off AH. */ - size_t stripsiz = 0; char *prvnxtp; /* @@ -894,14 +898,6 @@ ah6_input(mp, offp, proto) prvnxtp = ip6_get_prevhdr(m, off); /* XXX */ *prvnxtp = nxt; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } - ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST /* @@ -945,6 +941,10 @@ ah6_input(mp, offp, proto) ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) { + ipsec6stat.in_nomem++; + goto fail; + } } *offp = off; @@ -968,4 +968,92 @@ fail: m_freem(m); return IPPROTO_DONE; } + +void +ah6_ctlinput(cmd, sa, d) + int cmd; + struct sockaddr *sa; + void *d; +{ + const struct newah *ahp; + struct newah ah; + struct secasvar *sav; + struct ip6_hdr *ip6; + struct mbuf *m; + struct ip6ctlparam *ip6cp = NULL; + int off; + struct sockaddr_in6 sa6_src, sa6_dst; + + if (sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(struct sockaddr_in6)) + return; + if ((unsigned)cmd >= PRC_NCMDS) + return; + + /* if the parameter is from icmp6, decode it. */ + if (d != NULL) { + ip6cp = (struct ip6ctlparam *)d; + m = ip6cp->ip6c_m; + ip6 = ip6cp->ip6c_ip6; + off = ip6cp->ip6c_off; + } else { + m = NULL; + ip6 = NULL; + } + + if (ip6) { + /* + * XXX: We assume that when ip6 is non NULL, + * M and OFF are valid. + */ + + /* check if we can safely examine src and dst ports */ + if (m->m_pkthdr.len < off + sizeof(ah)) + return; + + if (m->m_len < off + sizeof(ah)) { + /* + * this should be rare case, + * so we compromise on this copy... + */ + m_copydata(m, off, sizeof(ah), (caddr_t)&ah); + ahp = &ah; + } else + ahp = (struct newah *)(mtod(m, caddr_t) + off); + + if (cmd == PRC_MSGSIZE) { + int valid = 0; + + /* + * Check to see if we have a valid SA corresponding to + * the address in the ICMP message payload. + */ + sav = key_allocsa(AF_INET6, + (caddr_t)&sa6_src.sin6_addr, + (caddr_t)&sa6_dst.sin6_addr, + IPPROTO_AH, ahp->ah_spi); + if (sav) { + if (sav->state == SADB_SASTATE_MATURE || + sav->state == SADB_SASTATE_DYING) + valid++; + key_freesav(sav); + } + + /* XXX Further validation? */ + + /* + * Depending on the value of "valid" and routing table + * size (mtudisc_{hi,lo}wat), we will: + * - recalcurate the new MTU and create the + * corresponding routing entry, or + * - ignore the MTU change notification. + */ + icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); + } + + /* we normally notify single pcb here */ + } else { + /* we normally notify any pcb here */ + } +} #endif /* INET6 */ diff --git a/sys/netinet6/ah_output.c b/sys/netinet6/ah_output.c index 59263cd1b05e..983ecab95196 100644 --- a/sys/netinet6/ah_output.c +++ b/sys/netinet6/ah_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah_output.c,v 1.22 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: ah_output.c,v 1.30 2001/02/21 00:50:53 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -76,7 +76,9 @@ #include <net/net_osdep.h> +#ifdef INET static struct in_addr *ah4_finaldst __P((struct mbuf *)); +#endif /* * compute AH header size. @@ -87,7 +89,7 @@ size_t ah_hdrsiz(isr) struct ipsecrequest *isr; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t hdrsiz; /* sanity check */ @@ -104,7 +106,7 @@ ah_hdrsiz(isr) goto estimate; /* we need transport mode AH. */ - algo = &ah_algorithms[isr->sav->alg_auth]; + algo = ah_algorithm_lookup(isr->sav->alg_auth); if (!algo) goto estimate; @@ -131,6 +133,7 @@ ah_hdrsiz(isr) return sizeof(struct newah) + 16; } +#ifdef INET /* * Modify the packet so that it includes the authentication data. * The mbuf passed must start with IPv4 header. @@ -144,7 +147,7 @@ ah4_output(m, isr) struct ipsecrequest *isr; { struct secasvar *sav = isr->sav; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; u_int32_t spi; u_char *ahdrpos; u_char *ahsumpos = NULL; @@ -171,7 +174,14 @@ ah4_output(m, isr) return EINVAL; } - algo = &ah_algorithms[sav->alg_auth]; + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: " + "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } spi = sav->spi; /* @@ -314,16 +324,19 @@ ah4_output(m, isr) return 0; } +#endif /* Calculate AH length */ int ah_hdrlen(sav) struct secasvar *sav; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; int plen, ahlen; - algo = &ah_algorithms[sav->alg_auth]; + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) + return 0; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ @@ -351,7 +364,7 @@ ah6_output(m, nexthdrp, md, isr) struct mbuf *mprev; struct mbuf *mah; struct secasvar *sav = isr->sav; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; u_int32_t spi; u_char *ahsumpos = NULL; size_t plen; /*AH payload size in bytes*/ @@ -414,7 +427,14 @@ ah6_output(m, nexthdrp, md, isr) return EINVAL; } - algo = &ah_algorithms[sav->alg_auth]; + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: " + "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); + ipsec6stat.out_inval++; + m_freem(m); + return EINVAL; + } spi = sav->spi; /* @@ -447,7 +467,7 @@ ah6_output(m, nexthdrp, md, isr) ipseclog((LOG_WARNING, "replay counter overflowed. %s\n", ipsec_logsastr(sav))); - ipsecstat.out_inval++; + ipsec6stat.out_inval++; m_freem(m); return EINVAL; } @@ -479,6 +499,7 @@ ah6_output(m, nexthdrp, md, isr) } #endif +#ifdef INET /* * Find the final destination if there is loose/strict source routing option. * Returns NULL if there's no source routing options. @@ -564,3 +585,4 @@ ah4_finaldst(m) } return NULL; } +#endif diff --git a/sys/netinet6/dest6.c b/sys/netinet6/dest6.c index 8d3987cb5476..8ecde86f1ac6 100644 --- a/sys/netinet6/dest6.c +++ b/sys/netinet6/dest6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: dest6.c,v 1.12 2000/05/05 11:00:57 sumikawa Exp $ */ +/* $KAME: dest6.c,v 1.27 2001/03/29 05:34:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -35,12 +35,14 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/protosw.h> #include <sys/socket.h> #include <sys/errno.h> #include <sys/time.h> +#include <sys/kernel.h> #include <net/if.h> #include <net/route.h> @@ -59,10 +61,13 @@ dest6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { - register struct mbuf *m = *mp; + struct mbuf *m = *mp; int off = *offp, dstoptlen, optlen; struct ip6_dest *dstopts; u_int8_t *opt; + struct ip6_hdr *ip6; + + ip6 = mtod(m, struct ip6_hdr *); /* validation of the length of the header */ #ifndef PULLDOWN_TEST @@ -102,19 +107,21 @@ dest6_input(mp, offp, proto) case IP6OPT_PADN: optlen = *(opt + 1) + 2; break; - default: /* unknown option */ - if ((optlen = ip6_unknown_opt(opt, m, - opt-mtod(m, u_int8_t *))) == -1) - return(IPPROTO_DONE); - optlen += 2; - break; + + default: /* unknown option */ + optlen = ip6_unknown_opt(opt, m, + opt - mtod(m, u_int8_t *)); + if (optlen == -1) + return (IPPROTO_DONE); + optlen += 2; + break; } } *offp = off; - return(dstopts->ip6d_nxt); + return (dstopts->ip6d_nxt); bad: m_freem(m); - return(IPPROTO_DONE); + return (IPPROTO_DONE); } diff --git a/sys/netinet6/esp.h b/sys/netinet6/esp.h index 95deec32a8ff..6f794a8df78f 100644 --- a/sys/netinet6/esp.h +++ b/sys/netinet6/esp.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp.h,v 1.8 2000/07/02 13:23:33 itojun Exp $ */ +/* $KAME: esp.h,v 1.16 2000/10/18 21:28:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,7 +37,9 @@ #ifndef _NETINET6_ESP_H_ #define _NETINET6_ESP_H_ -struct secasvar; +#if defined(_KERNEL) && !defined(_LKM) +#include "opt_inet.h" +#endif struct esp { u_int32_t esp_spi; /* ESP */ @@ -67,35 +69,41 @@ struct esptail { /*variable size, 32bit bound*/ /* Authentication data (new IPsec)*/ }; -struct esp_algorithm_state { - struct secasvar *sav; - void* foo; /*per algorithm data - maybe*/ -}; +#ifdef _KERNEL +struct secasvar; -/* XXX yet to be defined */ struct esp_algorithm { size_t padbound; /* pad boundary, in byte */ + int ivlenval; /* iv length, in byte */ int (*mature) __P((struct secasvar *)); int keymin; /* in bits */ int keymax; /* in bits */ + int (*schedlen) __P((const struct esp_algorithm *)); const char *name; - int (*ivlen) __P((struct secasvar *)); + int (*ivlen) __P((const struct esp_algorithm *, struct secasvar *)); int (*decrypt) __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); + struct secasvar *, const struct esp_algorithm *, int)); int (*encrypt) __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); + struct secasvar *, const struct esp_algorithm *, int)); + /* not supposed to be called directly */ + int (*schedule) __P((const struct esp_algorithm *, struct secasvar *)); + int (*blockdecrypt) __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); + int (*blockencrypt) __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); }; -#ifdef _KERNEL -extern struct esp_algorithm esp_algorithms[]; +extern const struct esp_algorithm *esp_algorithm_lookup __P((int)); +extern int esp_max_ivlen __P((void)); /* crypt routines */ extern int esp4_output __P((struct mbuf *, struct ipsecrequest *)); extern void esp4_input __P((struct mbuf *, ...)); extern size_t esp_hdrsiz __P((struct ipsecrequest *)); -#endif /*_KERNEL*/ +extern int esp_schedule __P((const struct esp_algorithm *, struct secasvar *)); extern int esp_auth __P((struct mbuf *, size_t, size_t, struct secasvar *, u_char *)); +#endif /*_KERNEL*/ #endif /*_NETINET6_ESP_H_*/ diff --git a/sys/netinet6/esp6.h b/sys/netinet6/esp6.h index 5d2f984b75cd..d774d2470652 100644 --- a/sys/netinet6/esp6.h +++ b/sys/netinet6/esp6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp.h,v 1.8 2000/07/02 13:23:33 itojun Exp $ */ +/* $KAME: esp.h,v 1.16 2000/10/18 21:28:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -41,6 +41,8 @@ extern int esp6_output __P((struct mbuf *, u_char *, struct mbuf *, struct ipsecrequest *)); extern int esp6_input __P((struct mbuf **, int *, int)); + +extern void esp6_ctlinput __P((int, struct sockaddr *, void *)); #endif /*_KERNEL*/ #endif /*_NETINET6_ESP6_H_*/ diff --git a/sys/netinet6/esp_core.c b/sys/netinet6/esp_core.c index e5f8ec5d2a04..0319255d6904 100644 --- a/sys/netinet6/esp_core.c +++ b/sys/netinet6/esp_core.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp_core.c,v 1.15 2000/06/14 10:41:18 itojun Exp $ */ +/* $KAME: esp_core.c,v 1.50 2000/11/02 12:27:38 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -35,6 +35,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/protosw.h> @@ -66,87 +67,181 @@ #ifdef INET6 #include <netinet6/esp6.h> #endif +#include <netinet6/esp_rijndael.h> #include <net/pfkeyv2.h> #include <netkey/keydb.h> +#include <netkey/key.h> #include <crypto/des/des.h> #include <crypto/blowfish/blowfish.h> #include <crypto/cast128/cast128.h> -#include <crypto/rc5/rc5.h> #include <net/net_osdep.h> static int esp_null_mature __P((struct secasvar *)); -static int esp_null_ivlen __P((struct secasvar *)); static int esp_null_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); + struct secasvar *, const struct esp_algorithm *, int)); static int esp_null_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); + struct secasvar *, const struct esp_algorithm *, int)); static int esp_descbc_mature __P((struct secasvar *)); -static int esp_descbc_ivlen __P((struct secasvar *)); -static int esp_descbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_descbc_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); +static int esp_descbc_ivlen __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_des_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_des_schedlen __P((const struct esp_algorithm *)); +static int esp_des_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_des_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); static int esp_cbc_mature __P((struct secasvar *)); -static int esp_blowfish_cbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_blowfish_cbc_encrypt __P((struct mbuf *, size_t, - size_t, struct secasvar *, struct esp_algorithm *, int)); -static int esp_blowfish_cbc_ivlen __P((struct secasvar *)); -static int esp_cast128cbc_ivlen __P((struct secasvar *)); -static int esp_cast128cbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_cast128cbc_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_3descbc_ivlen __P((struct secasvar *)); -static int esp_3descbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_3descbc_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_rc5cbc_ivlen __P((struct secasvar *)); -static int esp_rc5cbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_rc5cbc_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static void esp_increment_iv __P((struct secasvar *)); -static caddr_t mbuf_find_offset __P((struct mbuf *, size_t, size_t)); - -/* NOTE: The order depends on SADB_EALG_x in netkey/keyv2.h */ -struct esp_algorithm esp_algorithms[] = { - { 0, 0, 0, 0, 0, 0, 0, }, - { 8, esp_descbc_mature, 64, 64, "des-cbc", - esp_descbc_ivlen, esp_descbc_decrypt, esp_descbc_encrypt, }, - { 8, esp_cbc_mature, 192, 192, "3des-cbc", - esp_3descbc_ivlen, esp_3descbc_decrypt, esp_3descbc_encrypt, }, - { 1, esp_null_mature, 0, 2048, "null", - esp_null_ivlen, esp_null_decrypt, esp_null_encrypt, }, - { 8, esp_cbc_mature, 40, 448, "blowfish-cbc", - esp_blowfish_cbc_ivlen, esp_blowfish_cbc_decrypt, - esp_blowfish_cbc_encrypt, }, - { 8, esp_cbc_mature, 40, 128, "cast128-cbc", - esp_cast128cbc_ivlen, esp_cast128cbc_decrypt, - esp_cast128cbc_encrypt, }, - { 8, esp_cbc_mature, 40, 2040, "rc5-cbc", - esp_rc5cbc_ivlen, esp_rc5cbc_decrypt, esp_rc5cbc_encrypt, }, +static int esp_blowfish_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_blowfish_schedlen __P((const struct esp_algorithm *)); +static int esp_blowfish_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_blowfish_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_cast128_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_cast128_schedlen __P((const struct esp_algorithm *)); +static int esp_cast128_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_cast128_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_3des_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_3des_schedlen __P((const struct esp_algorithm *)); +static int esp_3des_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_3des_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_common_ivlen __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_cbc_decrypt __P((struct mbuf *, size_t, + struct secasvar *, const struct esp_algorithm *, int)); +static int esp_cbc_encrypt __P((struct mbuf *, size_t, size_t, + struct secasvar *, const struct esp_algorithm *, int)); + +#define MAXIVLEN 16 + +static const struct esp_algorithm esp_algorithms[] = { + { 8, -1, esp_descbc_mature, 64, 64, esp_des_schedlen, + "des-cbc", + esp_descbc_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_des_schedule, + esp_des_blockdecrypt, esp_des_blockencrypt, }, + { 8, 8, esp_cbc_mature, 192, 192, esp_3des_schedlen, + "3des-cbc", + esp_common_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_3des_schedule, + esp_3des_blockdecrypt, esp_3des_blockencrypt, }, + { 1, 0, esp_null_mature, 0, 2048, 0, "null", + esp_common_ivlen, esp_null_decrypt, + esp_null_encrypt, NULL, }, + { 8, 8, esp_cbc_mature, 40, 448, esp_blowfish_schedlen, "blowfish-cbc", + esp_common_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_blowfish_schedule, + esp_blowfish_blockdecrypt, esp_blowfish_blockencrypt, }, + { 8, 8, esp_cbc_mature, 40, 128, esp_cast128_schedlen, + "cast128-cbc", + esp_common_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_cast128_schedule, + esp_cast128_blockdecrypt, esp_cast128_blockencrypt, }, + { 16, 16, esp_cbc_mature, 128, 256, esp_rijndael_schedlen, + "rijndael-cbc", + esp_common_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_rijndael_schedule, + esp_rijndael_blockdecrypt, esp_rijndael_blockencrypt }, }; -/* - * mbuf assumption: foo_encrypt() assumes that IV part is placed in a single - * mbuf, not across multiple mbufs. - */ +const struct esp_algorithm * +esp_algorithm_lookup(idx) + int idx; +{ -static int -esp_null_mature(sav) + switch (idx) { + case SADB_EALG_DESCBC: + return &esp_algorithms[0]; + case SADB_EALG_3DESCBC: + return &esp_algorithms[1]; + case SADB_EALG_NULL: + return &esp_algorithms[2]; + case SADB_X_EALG_BLOWFISHCBC: + return &esp_algorithms[3]; + case SADB_X_EALG_CAST128CBC: + return &esp_algorithms[4]; + case SADB_X_EALG_RIJNDAELCBC: + return &esp_algorithms[5]; + default: + return NULL; + } +} + +int +esp_max_ivlen() +{ + int idx; + int ivlen; + + ivlen = 0; + for (idx = 0; idx < sizeof(esp_algorithms)/sizeof(esp_algorithms[0]); + idx++) { + if (esp_algorithms[idx].ivlenval > ivlen) + ivlen = esp_algorithms[idx].ivlenval; + } + + return ivlen; +} + +int +esp_schedule(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; { - /* anything is okay */ - return 0; + int error; + + /* check for key length */ + if (_KEYBITS(sav->key_enc) < algo->keymin || + _KEYBITS(sav->key_enc) > algo->keymax) { + ipseclog((LOG_ERR, + "esp_schedule %s: unsupported key length %d: " + "needs %d to %d bits\n", algo->name, _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); + return EINVAL; + } + + /* already allocated */ + if (sav->sched && sav->schedlen != 0) + return 0; + /* no schedule necessary */ + if (!algo->schedule || !algo->schedlen) + return 0; + + sav->schedlen = (*algo->schedlen)(algo); + if (sav->schedlen < 0) + return EINVAL; + sav->sched = malloc(sav->schedlen, M_SECA, M_DONTWAIT); + if (!sav->sched) { + sav->schedlen = 0; + return ENOBUFS; + } + + error = (*algo->schedule)(algo, sav); + if (error) { + ipseclog((LOG_ERR, "esp_schedule %s: error %d\n", + algo->name, error)); + free(sav->sched, M_SECA); + sav->sched = NULL; + sav->schedlen = 0; + } + return error; } static int -esp_null_ivlen(sav) +esp_null_mature(sav) struct secasvar *sav; { + + /* anything is okay */ return 0; } @@ -155,9 +250,10 @@ esp_null_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; /* offset to ESP header */ struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; { + return 0; /* do nothing */ } @@ -167,9 +263,10 @@ esp_null_encrypt(m, off, plen, sav, algo, ivlen) size_t off; /* offset to ESP header */ size_t plen; /* payload length (to be encrypted) */ struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; { + return 0; /* do nothing */ } @@ -177,7 +274,7 @@ static int esp_descbc_mature(sav) struct secasvar *sav; { - struct esp_algorithm *algo; + const struct esp_algorithm *algo; if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) { ipseclog((LOG_ERR, "esp_cbc_mature: " @@ -189,9 +286,16 @@ esp_descbc_mature(sav) ipseclog((LOG_ERR, "esp_descbc_mature: no key is given.\n")); return 1; } - algo = &esp_algorithms[sav->alg_enc]; - if (_KEYBITS(sav->key_enc) < algo->keymin - || algo->keymax < _KEYBITS(sav->key_enc)) { + + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { + ipseclog((LOG_ERR, + "esp_descbc_mature: unsupported algorithm.\n")); + return 1; + } + + if (_KEYBITS(sav->key_enc) < algo->keymin || + _KEYBITS(sav->key_enc) > algo->keymax) { ipseclog((LOG_ERR, "esp_descbc_mature: invalid key length %d.\n", _KEYBITS(sav->key_enc))); @@ -199,7 +303,7 @@ esp_descbc_mature(sav) } /* weak key check */ - if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc))) { + if (des_is_weak_key((des_cblock *)_KEYBUF(sav->key_enc))) { ipseclog((LOG_ERR, "esp_descbc_mature: weak key was passed.\n")); return 1; @@ -209,246 +313,69 @@ esp_descbc_mature(sav) } static int -esp_descbc_ivlen(sav) +esp_descbc_ivlen(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; { - if (sav && (sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) - return 4; - if (sav && !(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_DERIV)) - return 4; - else + if (!sav) return 8; + if ((sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) + return 4; + if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_DERIV)) + return 4; + return 8; } static int -esp_descbc_decrypt(m, off, sav, algo, ivlen) - struct mbuf *m; - size_t off; /* offset to ESP header */ - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; +esp_des_schedlen(algo) + const struct esp_algorithm *algo; { - size_t ivoff = 0; - size_t bodyoff = 0; - u_int8_t *iv; - size_t plen; - u_int8_t tiv[8]; - int derived; - int error; - - derived = 0; - /* sanity check */ - if (ivlen != sav->ivlen) { - ipseclog((LOG_ERR, "esp_descbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; - } - if (_KEYBITS(sav->key_enc) < algo->keymin - || algo->keymax < _KEYBITS(sav->key_enc)) { - ipseclog((LOG_ERR, "esp_descbc_decrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc))); - return EINVAL; - } - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1827 */ - ivoff = off + sizeof(struct esp); - bodyoff = off + sizeof(struct esp) + ivlen; - derived = 0; - } else { - /* RFC 2406 */ - if (sav->flags & SADB_X_EXT_DERIV) { - /* - * draft-ietf-ipsec-ciph-des-derived-00.txt - * uses sequence number field as IV field. - * This draft has been deleted, but you can get from - * ftp://ftp.kame.net/pub/internet-drafts/. - */ - ivoff = off + sizeof(struct esp); - bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); - ivlen = sizeof(u_int32_t); - derived = 1; - } else { - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - derived = 0; - } - } - if (ivlen == 4) { - iv = &tiv[0]; - m_copydata(m, ivoff, 4, &tiv[0]); - m_copydata(m, ivoff, 4, &tiv[4]); - tiv[4] ^= 0xff; - tiv[5] ^= 0xff; - tiv[6] ^= 0xff; - tiv[7] ^= 0xff; - } else if (ivlen == 8) { - iv = &tiv[0]; - m_copydata(m, ivoff, 8, &tiv[0]); - } else { - ipseclog((LOG_ERR, "esp_descbc_decrypt: unsupported ivlen %d\n", - ivlen)); - return EINVAL; - } - - plen = m->m_pkthdr.len; - if (plen < bodyoff) - panic("esp_descbc_decrypt: too short packet: len=%lu", - (u_long)plen); - plen -= bodyoff; - - if (plen % 8) { - ipseclog((LOG_ERR, "esp_descbc_decrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - - { - int deserr; - des_key_schedule ks; - - deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); - if (deserr != 0) { - ipseclog((LOG_ERR, - "esp_descbc_decrypt: key error %d\n", deserr)); - return EINVAL; - } - - error = des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, - DES_DECRYPT); - - /* for safety */ - bzero(&ks, sizeof(des_key_schedule)); - } - - /* for safety */ - bzero(&tiv[0], sizeof(tiv)); - - return error; + return sizeof(des_key_schedule); } static int -esp_descbc_encrypt(m, off, plen, sav, algo, ivlen) - struct mbuf *m; - size_t off; /* offset to ESP header */ - size_t plen; /* payload length (to be decrypted) */ +esp_des_schedule(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; { - size_t ivoff = 0; - size_t bodyoff = 0; - u_int8_t *iv; - u_int8_t tiv[8]; - int derived; - int error; - - derived = 0; - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_descbc_encrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, "esp_descbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; - } - if (_KEYBITS(sav->key_enc) < algo->keymin - || algo->keymax < _KEYBITS(sav->key_enc)) { - ipseclog((LOG_ERR, "esp_descbc_encrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc))); + if (des_key_sched((des_cblock *)_KEYBUF(sav->key_enc), + *(des_key_schedule *)sav->sched)) return EINVAL; - } - - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1827 */ - /* - * draft-ietf-ipsec-ciph-des-derived-00.txt - * uses sequence number field as IV field. - * This draft has been deleted, see above. - */ - ivoff = off + sizeof(struct esp); - bodyoff = off + sizeof(struct esp) + ivlen; - derived = 0; - } else { - /* RFC 2406 */ - if (sav->flags & SADB_X_EXT_DERIV) { - /* - * draft-ietf-ipsec-ciph-des-derived-00.txt - * uses sequence number field as IV field. - * This draft has been deleted, see above. - */ - ivoff = off + sizeof(struct esp); - bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); - ivlen = sizeof(u_int32_t); - derived = 1; - } else { - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - derived = 0; - } - } - - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); - if (ivlen == 4) { - if (!derived) { - bcopy(sav->iv, &tiv[0], 4); - bcopy(sav->iv, &tiv[4], 4); - tiv[4] ^= 0xff; - tiv[5] ^= 0xff; - tiv[6] ^= 0xff; - tiv[7] ^= 0xff; - bcopy(&tiv[0], iv, 4); - iv = &tiv[0]; - } else { - bcopy(iv, &tiv[0], 4); - bcopy(iv, &tiv[4], 4); - tiv[4] ^= 0xff; - tiv[5] ^= 0xff; - tiv[6] ^= 0xff; - tiv[7] ^= 0xff; - iv = &tiv[0]; - } - } else if (ivlen == 8) - bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); - else { - ipseclog((LOG_ERR, - "esp_descbc_encrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - { - int deserr; - des_key_schedule ks; - - deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); - if (deserr != 0) { - ipseclog((LOG_ERR, - "esp_descbc_encrypt: key error %d\n", deserr)); - return EINVAL; - } - - error = des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, - DES_ENCRYPT); + else + return 0; +} - /* for safety */ - bzero(&ks, sizeof(des_key_schedule)); - } +static int +esp_des_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ - esp_increment_iv(sav); + /* assumption: d has a good alignment */ + bcopy(s, d, sizeof(DES_LONG) * 2); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, + *(des_key_schedule *)sav->sched, DES_DECRYPT); + return 0; +} - /* for safety */ - bzero(&tiv[0], sizeof(tiv)); +static int +esp_des_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ - return error; + /* assumption: d has a good alignment */ + bcopy(s, d, sizeof(DES_LONG) * 2); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, + *(des_key_schedule *)sav->sched, DES_ENCRYPT); + return 0; } static int @@ -456,7 +383,7 @@ esp_cbc_mature(sav) struct secasvar *sav; { int keylen; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; if (sav->flags & SADB_X_EXT_OLD) { ipseclog((LOG_ERR, @@ -470,31 +397,47 @@ esp_cbc_mature(sav) } if (!sav->key_enc) { + ipseclog((LOG_ERR, "esp_cbc_mature: no key is given.\n")); + return 1; + } + + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { ipseclog((LOG_ERR, - "esp_cbc_mature: no key is given.\n")); + "esp_cbc_mature %s: unsupported algorithm.\n", algo->name)); return 1; } - algo = &esp_algorithms[sav->alg_enc]; + keylen = sav->key_enc->sadb_key_bits; if (keylen < algo->keymin || algo->keymax < keylen) { - ipseclog((LOG_ERR, "esp_cbc_mature: invalid key length %d.\n", - sav->key_enc->sadb_key_bits)); + ipseclog((LOG_ERR, + "esp_cbc_mature %s: invalid key length %d.\n", + algo->name, sav->key_enc->sadb_key_bits)); return 1; } switch (sav->alg_enc) { case SADB_EALG_3DESCBC: /* weak key check */ - if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc)) - || des_is_weak_key((C_Block *)(_KEYBUF(sav->key_enc) + 8)) - || des_is_weak_key((C_Block *)(_KEYBUF(sav->key_enc) + 16))) { + if (des_is_weak_key((des_cblock *)_KEYBUF(sav->key_enc)) || + des_is_weak_key((des_cblock *)(_KEYBUF(sav->key_enc) + 8)) || + des_is_weak_key((des_cblock *)(_KEYBUF(sav->key_enc) + 16))) { ipseclog((LOG_ERR, - "esp_cbc_mature: weak key was passed.\n")); + "esp_cbc_mature %s: weak key was passed.\n", + algo->name)); return 1; } break; - case SADB_EALG_BLOWFISHCBC: - case SADB_EALG_CAST128CBC: - case SADB_EALG_RC5CBC: + case SADB_X_EALG_BLOWFISHCBC: + case SADB_X_EALG_CAST128CBC: + break; + case SADB_X_EALG_RIJNDAELCBC: + /* allows specific key sizes only */ + if (!(keylen == 128 || keylen == 192 || keylen == 256)) { + ipseclog((LOG_ERR, + "esp_cbc_mature %s: invalid key length %d.\n", + algo->name, keylen)); + return 1; + } break; } @@ -502,694 +445,596 @@ esp_cbc_mature(sav) } static int -esp_blowfish_cbc_decrypt(m, off, sav, algo, ivlen) - struct mbuf *m; - size_t off; /* offset to ESP header */ - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; +esp_blowfish_schedlen(algo) + const struct esp_algorithm *algo; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - u_int8_t tiv[8]; - size_t plen; - static BF_KEY key; /* made static to avoid kernel stack overflow */ - int s; - int error; - - /* sanity check */ - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; - } - if (_KEYBITS(sav->key_enc) < algo->keymin - || algo->keymax < _KEYBITS(sav->key_enc)) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_decrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax)); - return EINVAL; - } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_decrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_decrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - iv = &tiv[0]; - m_copydata(m, ivoff, 8, &tiv[0]); - - plen = m->m_pkthdr.len; - if (plen < bodyoff) - panic("esp_blowfish_cbc_decrypt: too short packet: len=%lu", - (u_long)plen); - plen -= bodyoff; - - if (plen % 8) { - ipseclog((LOG_ERR, "esp_blowfish_cbc_decrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - -#ifdef __NetBSD__ - s = splsoftnet(); /* XXX correct? */ -#else - s = splnet(); /* XXX correct? */ -#endif - - BF_set_key(&key, _KEYBITS(sav->key_enc) / 8, _KEYBUF(sav->key_enc)); - error = BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_DECRYPT); - - /* for safety */ - bzero(&key, sizeof(BF_KEY)); - splx(s); - - /* for safety */ - bzero(&tiv[0], sizeof(tiv)); - - return error; + return sizeof(BF_KEY); } static int -esp_blowfish_cbc_encrypt(m, off, plen, sav, algo, ivlen) - struct mbuf *m; - size_t off; /* offset to ESP header */ - size_t plen; /* payload length (to be decrypted) */ +esp_blowfish_schedule(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - static BF_KEY key; /* made static to avoid kernel stack overflow */ - int s; - int error; - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_blowfish_cbc_encrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; - } - if (_KEYBITS(sav->key_enc) < algo->keymin - || algo->keymax < _KEYBITS(sav->key_enc)) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_encrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax)); - return EINVAL; - } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_encrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_encrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); - - bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); - -#ifdef __NetBSD__ - s = splsoftnet(); /* XXX correct? */ -#else - s = splnet(); /* XXX correct? */ -#endif - - BF_set_key(&key, _KEYBITS(sav->key_enc) / 8, _KEYBUF(sav->key_enc)); - error = BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_ENCRYPT); - - /* for safety */ - bzero(&key, sizeof(BF_KEY)); - - splx(s); - - esp_increment_iv(sav); - - return error; + BF_set_key((BF_KEY *)sav->sched, _KEYLEN(sav->key_enc), + _KEYBUF(sav->key_enc)); + return 0; } static int -esp_blowfish_cbc_ivlen(sav) +esp_blowfish_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; { - return 8; + /* HOLY COW! BF_encrypt() takes values in host byteorder */ + BF_LONG t[2]; + + bcopy(s, t, sizeof(t)); + t[0] = ntohl(t[0]); + t[1] = ntohl(t[1]); + BF_encrypt(t, (BF_KEY *)sav->sched, BF_DECRYPT); + t[0] = htonl(t[0]); + t[1] = htonl(t[1]); + bcopy(t, d, sizeof(t)); + return 0; } static int -esp_cast128cbc_ivlen(sav) +esp_blowfish_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; { - return 8; + /* HOLY COW! BF_encrypt() takes values in host byteorder */ + BF_LONG t[2]; + + bcopy(s, t, sizeof(t)); + t[0] = ntohl(t[0]); + t[1] = ntohl(t[1]); + BF_encrypt(t, (BF_KEY *)sav->sched, BF_ENCRYPT); + t[0] = htonl(t[0]); + t[1] = htonl(t[1]); + bcopy(t, d, sizeof(t)); + return 0; } static int -esp_cast128cbc_decrypt(m, off, sav, algo, ivlen) - struct mbuf *m; - size_t off; - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; +esp_cast128_schedlen(algo) + const struct esp_algorithm *algo; { - size_t ivoff; - size_t bodyoff; - u_int8_t iv[8]; - size_t plen; - int error; - /* sanity check */ - if (ivlen != sav->ivlen) { - ipseclog((LOG_ERR, "esp_cast128cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; - } - if (_KEYBITS(sav->key_enc) < algo->keymin - || _KEYBITS(sav->key_enc) > algo->keymax) { - ipseclog((LOG_ERR, - "esp_cast128cbc_decrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax)); - return EINVAL; - } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_cast128cbc_decrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_cast128cbc_decrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; + return sizeof(u_int32_t) * 32; +} - /* copy mbuf's IV into iv */ - m_copydata(m, ivoff, 8, iv); +static int +esp_cast128_schedule(algo, sav) + const struct esp_algorithm *algo; + struct secasvar *sav; +{ - plen = m->m_pkthdr.len; - if (plen < bodyoff) { - panic("esp_cast128cbc_decrypt: too short packet: len=%lu\n", - (u_long)plen); - } - plen -= bodyoff; + set_cast128_subkey((u_int32_t *)sav->sched, _KEYBUF(sav->key_enc)); + return 0; +} - if (plen % 8) { - ipseclog((LOG_ERR, "esp_cast128cbc_decrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } +static int +esp_cast128_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ - /* decrypt */ - { - u_int8_t key[16]; - u_int32_t subkey[32]; + if (_KEYLEN(sav->key_enc) <= 80 / 8) + cast128_decrypt_round12(d, s, (u_int32_t *)sav->sched); + else + cast128_decrypt_round16(d, s, (u_int32_t *)sav->sched); + return 0; +} - bzero(key, sizeof(key)); - bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); +static int +esp_cast128_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ - set_cast128_subkey(subkey, key); - error = cast128_cbc_process(m, bodyoff, plen, subkey, iv, - _KEYBITS(sav->key_enc) / 8, CAST128_DECRYPT); + if (_KEYLEN(sav->key_enc) <= 80 / 8) + cast128_encrypt_round12(d, s, (u_int32_t *)sav->sched); + else + cast128_encrypt_round16(d, s, (u_int32_t *)sav->sched); + return 0; +} - /* for safety */ - bzero(subkey, sizeof(subkey)); - bzero(key, sizeof(key)); - } +static int +esp_3des_schedlen(algo) + const struct esp_algorithm *algo; +{ - return error; + return sizeof(des_key_schedule) * 3; } static int -esp_cast128cbc_encrypt(m, off, plen, sav, algo, ivlen) - struct mbuf *m; - size_t off; - size_t plen; +esp_3des_schedule(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; int error; + des_key_schedule *p; + int i; + char *k; - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_cast128cbc_encrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, "esp_cast128cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; + p = (des_key_schedule *)sav->sched; + k = _KEYBUF(sav->key_enc); + for (i = 0; i < 3; i++) { + error = des_key_sched((des_cblock *)(k + 8 * i), p[i]); + if (error) + return EINVAL; } - if (_KEYBITS(sav->key_enc) < algo->keymin - || _KEYBITS(sav->key_enc) > algo->keymax) { - ipseclog((LOG_ERR, - "esp_cast128cbc_encrypt: unsupported key length %d: " - "needs %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax)); - return EINVAL; - } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_cast128cbc_encrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_cast128cbc_encrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); - - bcopy(sav->iv, iv, ivlen); - - /* encrypt */ - { - u_int8_t key[16]; - u_int32_t subkey[32]; - - bzero(key, sizeof(key)); - bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); - - set_cast128_subkey(subkey, key); - error = cast128_cbc_process(m, bodyoff, plen, subkey, iv, - _KEYBITS(sav->key_enc) / 8, CAST128_ENCRYPT); - - /* for safety */ - bzero(subkey, sizeof(subkey)); - bzero(key, sizeof(key)); - } + return 0; +} - esp_increment_iv(sav); +static int +esp_3des_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ + des_key_schedule *p; + + /* assumption: d has a good alignment */ + p = (des_key_schedule *)sav->sched; + bcopy(s, d, sizeof(DES_LONG) * 2); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[2], DES_DECRYPT); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[1], DES_ENCRYPT); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[0], DES_DECRYPT); + return 0; +} - return error; +static int +esp_3des_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ + des_key_schedule *p; + + /* assumption: d has a good alignment */ + p = (des_key_schedule *)sav->sched; + bcopy(s, d, sizeof(DES_LONG) * 2); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[0], DES_ENCRYPT); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[1], DES_DECRYPT); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[2], DES_ENCRYPT); + return 0; } static int -esp_3descbc_ivlen(sav) +esp_common_ivlen(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; { - return 8; + + if (!algo) + panic("esp_common_ivlen: unknown algorithm"); + return algo->ivlenval; } static int -esp_3descbc_decrypt(m, off, sav, algo, ivlen) +esp_cbc_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - size_t plen; - u_int8_t tiv[8]; + struct mbuf *s; + struct mbuf *d, *d0, *dp; + int soff, doff; /*offset from the head of chain, to head of this mbuf */ + int sn, dn; /*offset from the head of the mbuf, to meat */ + size_t ivoff, bodyoff; + u_int8_t iv[MAXIVLEN], *ivp; + u_int8_t sbuf[MAXIVLEN], *sp; + u_int8_t *p, *q; + struct mbuf *scut; + int scutoff; + int i; + int blocklen; + int derived; - /* sanity check */ - if (ivlen != sav->ivlen) { - ipseclog((LOG_ERR, "esp_3descbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); + if (ivlen != sav->ivlen || ivlen > sizeof(iv)) { + ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " + "unsupported ivlen %d\n", algo->name, ivlen)); + m_freem(m); return EINVAL; } - if (_KEYBITS(sav->key_enc) < algo->keymin - || algo->keymax < _KEYBITS(sav->key_enc)) { - ipseclog((LOG_ERR, "esp_3descbc_decrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc))); - return EINVAL; - } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_3descbc_decrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_3descbc_decrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - iv = &tiv[0]; - m_copydata(m, ivoff, 8, &tiv[0]); - plen = m->m_pkthdr.len; - if (plen < bodyoff) - panic("esp_3descbc_decrypt: too short packet: len=%lu", - (u_long)plen); + /* assumes blocklen == padbound */ + blocklen = algo->padbound; - plen -= bodyoff; - - if (plen % 8) { - ipseclog((LOG_ERR, "esp_3descbc_decrypt: " - "payload length must be multiple of 8\n")); +#ifdef DIAGNOSTIC + if (blocklen > sizeof(iv)) { + ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " + "unsupported blocklen %d\n", algo->name, blocklen)); + m_freem(m); return EINVAL; } +#endif - /* decrypt packet */ - { - int deserr[3]; - des_key_schedule ks[3]; - - deserr[0] = des_key_sched((C_Block *)_KEYBUF(sav->key_enc),ks[0]); - deserr[1] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), ks[1]); - deserr[2] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), ks[2]); - if ((deserr[0] != 0) || (deserr[1] != 0) || (deserr[2] != 0)) { - ipseclog((LOG_ERR, "esp_3descbc_decrypt: key error %d/%d/%d\n", - deserr[0], deserr[1], deserr[2])); - return EINVAL; + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + ivlen; + derived = 0; + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) { + /* + * draft-ietf-ipsec-ciph-des-derived-00.txt + * uses sequence number field as IV field. + */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); + ivlen = sizeof(u_int32_t); + derived = 1; + } else { + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + derived = 0; + } } - des_3cbc_process(m, bodyoff, plen, ks, (C_Block *)iv, DES_DECRYPT); - - /* for safety */ - bzero(ks[0], sizeof(des_key_schedule)*3); - } + /* grab iv */ + m_copydata(m, ivoff, ivlen, iv); - /* for safety */ - bzero(&tiv[0], sizeof(tiv)); - - return 0; -} - -static int -esp_3descbc_encrypt(m, off, plen, sav, algo, ivlen) - struct mbuf *m; - size_t off; - size_t plen; - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; -{ - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_3descbc_encrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, "esp_3descbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; - } - if (_KEYBITS(sav->key_enc) < algo->keymin - || algo->keymax < _KEYBITS(sav->key_enc)) { - ipseclog((LOG_ERR, "esp_3descbc_encrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc))); + /* extend iv */ + if (ivlen == blocklen) + ; + else if (ivlen == 4 && blocklen == 8) { + bcopy(&iv[0], &iv[4], 4); + iv[4] ^= 0xff; + iv[5] ^= 0xff; + iv[6] ^= 0xff; + iv[7] ^= 0xff; + } else { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "unsupported ivlen/blocklen: %d %d\n", + algo->name, ivlen, blocklen)); + m_freem(m); return EINVAL; } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_3descbc_encrypt: unsupported ESP version\n")); + + if (m->m_pkthdr.len < bodyoff) { + ipseclog((LOG_ERR, "esp_cbc_decrypt %s: bad len %d/%lu\n", + algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); + m_freem(m); return EINVAL; } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_3descbc_encrypt: unsupported ivlen %d\n", ivlen)); + if ((m->m_pkthdr.len - bodyoff) % blocklen) { + ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " + "payload length must be multiple of %d\n", + algo->name, blocklen)); + m_freem(m); return EINVAL; } - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; + s = m; + d = d0 = dp = NULL; + soff = doff = sn = dn = 0; + ivp = sp = NULL; - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); - - bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); - - /* encrypt packet */ - { - int deserr[3]; - des_key_schedule ks[3]; + /* skip bodyoff */ + while (soff < bodyoff) { + if (soff + s->m_len > bodyoff) { + sn = bodyoff - soff; + break; + } - deserr[0] = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks[0]); - deserr[1] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), ks[1]); - deserr[2] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), ks[2]); - if ((deserr[0] != 0) || (deserr[1] != 0) || (deserr[2] != 0)) { - ipseclog((LOG_ERR, "esp_3descbc_encrypt: key error %d/%d/%d\n", - deserr[0], deserr[1], deserr[2])); - return EINVAL; + soff += s->m_len; + s = s->m_next; } + scut = s; + scutoff = sn; - des_3cbc_process(m, bodyoff, plen, ks, (C_Block *)iv, DES_ENCRYPT); - - /* for safety */ - bzero(ks[0], sizeof(des_key_schedule)*3); - } + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; - esp_increment_iv(sav); - - return 0; -} + while (soff < m->m_pkthdr.len) { + /* source */ + if (sn + blocklen <= s->m_len) { + /* body is continuous */ + sp = mtod(s, u_int8_t *) + sn; + } else { + /* body is non-continuous */ + m_copydata(s, sn, blocklen, sbuf); + sp = sbuf; + } -static int -esp_rc5cbc_ivlen(sav) - struct secasvar *sav; -{ - return 8; -} + /* destination */ + if (!d || dn + blocklen > d->m_len) { + if (d) + dp = d; + MGET(d, M_DONTWAIT, MT_DATA); + i = m->m_pkthdr.len - (soff + sn); + if (d && i > MLEN) { + MCLGET(d, M_DONTWAIT); + if ((d->m_flags & M_EXT) == 0) { + m_free(d); + d = NULL; + } + } + if (!d) { + m_freem(m); + if (d0) + m_freem(d0); + return ENOBUFS; + } + if (!d0) + d0 = d; + if (dp) + dp->m_next = d; + d->m_len = 0; + d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen; + if (d->m_len > i) + d->m_len = i; + dn = 0; + } -static int -esp_rc5cbc_decrypt(m, off, sav, algo, ivlen) - struct mbuf *m; - size_t off; - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; -{ - size_t ivoff; - size_t bodyoff; - u_int8_t iv[8]; - size_t plen; - int error; + /* decrypt */ + (*algo->blockdecrypt)(algo, sav, sp, mtod(d, u_int8_t *) + dn); - /* sanity check */ - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; - } - if ((_KEYBITS(sav->key_enc) < 40) || (_KEYBITS(sav->key_enc) > 2040)) { - ipseclog((LOG_ERR, - "esp_rc5cbc_decrypt: unsupported key length %d: " - "need 40 to 2040 bit\n", _KEYBITS(sav->key_enc))); - return EINVAL; - } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_rc5cbc_decrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: unsupported ivlen %d\n", - ivlen)); - return EINVAL; - } + /* xor */ + p = ivp ? ivp : iv; + q = mtod(d, u_int8_t *) + dn; + for (i = 0; i < blocklen; i++) + q[i] ^= p[i]; - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; + /* next iv */ + if (sp == sbuf) { + bcopy(sbuf, iv, blocklen); + ivp = NULL; + } else + ivp = sp; - /* copy mbuf's IV into iv */ - m_copydata(m, ivoff, 8, iv); + sn += blocklen; + dn += blocklen; - plen = m->m_pkthdr.len; - if (plen < bodyoff) { - panic("esp_rc5cbc_decrypt: too short packet: len=%lu", - (u_long)plen); - } - plen -= bodyoff; + /* find the next source block */ + while (s && sn >= s->m_len) { + sn -= s->m_len; + soff += s->m_len; + s = s->m_next; + } - if (plen % 8) { - ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; } - /* decrypt */ - { - RC5_WORD e_key[34]; + m_freem(scut->m_next); + scut->m_len = scutoff; + scut->m_next = d0; - set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), - _KEYBITS(sav->key_enc) / 8, 16); - error = rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_DECRYPT); + /* just in case */ + bzero(iv, sizeof(iv)); + bzero(sbuf, sizeof(sbuf)); - /* for safety */ - bzero(e_key, sizeof(e_key)); - } - - return error; + return 0; } static int -esp_rc5cbc_encrypt(m, off, plen, sav, algo, ivlen) +esp_cbc_encrypt(m, off, plen, sav, algo, ivlen) struct mbuf *m; size_t off; size_t plen; struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - int error; + struct mbuf *s; + struct mbuf *d, *d0, *dp; + int soff, doff; /*offset from the head of chain, to head of this mbuf */ + int sn, dn; /*offset from the head of the mbuf, to meat */ + size_t ivoff, bodyoff; + u_int8_t iv[MAXIVLEN], *ivp; + u_int8_t sbuf[MAXIVLEN], *sp; + u_int8_t *p, *q; + struct mbuf *scut; + int scutoff; + int i; + int blocklen; + int derived; - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: " - "payload length must be multiple of 8\n")); + if (ivlen != sav->ivlen || ivlen > sizeof(iv)) { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "unsupported ivlen %d\n", algo->name, ivlen)); + m_freem(m); return EINVAL; } - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); + + /* assumes blocklen == padbound */ + blocklen = algo->padbound; + +#ifdef DIAGNOSTIC + if (blocklen > sizeof(iv)) { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "unsupported blocklen %d\n", algo->name, blocklen)); + m_freem(m); return EINVAL; } - if (_KEYBITS(sav->key_enc) < algo->keymin - || _KEYBITS(sav->key_enc) > algo->keymax) { - ipseclog((LOG_ERR, - "esp_rc5cbc_encrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax)); +#endif + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + ivlen; + derived = 0; + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) { + /* + * draft-ietf-ipsec-ciph-des-derived-00.txt + * uses sequence number field as IV field. + */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); + ivlen = sizeof(u_int32_t); + derived = 1; + } else { + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + derived = 0; + } + } + + /* put iv into the packet. if we are in derived mode, use seqno. */ + if (derived) + m_copydata(m, ivoff, ivlen, iv); + else { + bcopy(sav->iv, iv, ivlen); + /* maybe it is better to overwrite dest, not source */ + m_copyback(m, ivoff, ivlen, iv); + } + + /* extend iv */ + if (ivlen == blocklen) + ; + else if (ivlen == 4 && blocklen == 8) { + bcopy(&iv[0], &iv[4], 4); + iv[4] ^= 0xff; + iv[5] ^= 0xff; + iv[6] ^= 0xff; + iv[7] ^= 0xff; + } else { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "unsupported ivlen/blocklen: %d %d\n", + algo->name, ivlen, blocklen)); + m_freem(m); return EINVAL; } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_rc5cbc_encrypt: unsupported ESP version\n")); + + if (m->m_pkthdr.len < bodyoff) { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: bad len %d/%lu\n", + algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); + m_freem(m); return EINVAL; } - if (ivlen != 8) { - ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: unsupported ivlen %d\n", - ivlen)); + if ((m->m_pkthdr.len - bodyoff) % blocklen) { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "payload length must be multiple of %lu\n", + algo->name, (unsigned long)algo->padbound)); + m_freem(m); return EINVAL; } - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; + s = m; + d = d0 = dp = NULL; + soff = doff = sn = dn = 0; + ivp = sp = NULL; - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); + /* skip bodyoff */ + while (soff < bodyoff) { + if (soff + s->m_len > bodyoff) { + sn = bodyoff - soff; + break; + } - bcopy(sav->iv, iv, ivlen); + soff += s->m_len; + s = s->m_next; + } + scut = s; + scutoff = sn; - /* encrypt */ - { - RC5_WORD e_key[34]; + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; - set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), - _KEYBITS(sav->key_enc) / 8, 16); - error = rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_ENCRYPT); + while (soff < m->m_pkthdr.len) { + /* source */ + if (sn + blocklen <= s->m_len) { + /* body is continuous */ + sp = mtod(s, u_int8_t *) + sn; + } else { + /* body is non-continuous */ + m_copydata(s, sn, blocklen, sbuf); + sp = sbuf; + } - /* for safety */ - bzero(e_key, sizeof(e_key)); - } + /* destination */ + if (!d || dn + blocklen > d->m_len) { + if (d) + dp = d; + MGET(d, M_DONTWAIT, MT_DATA); + i = m->m_pkthdr.len - (soff + sn); + if (d && i > MLEN) { + MCLGET(d, M_DONTWAIT); + if ((d->m_flags & M_EXT) == 0) { + m_free(d); + d = NULL; + } + } + if (!d) { + m_freem(m); + if (d0) + m_freem(d0); + return ENOBUFS; + } + if (!d0) + d0 = d; + if (dp) + dp->m_next = d; + d->m_len = 0; + d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen; + if (d->m_len > i) + d->m_len = i; + dn = 0; + } - esp_increment_iv(sav); + /* xor */ + p = ivp ? ivp : iv; + q = sp; + for (i = 0; i < blocklen; i++) + q[i] ^= p[i]; - return error; -} + /* encrypt */ + (*algo->blockencrypt)(algo, sav, sp, mtod(d, u_int8_t *) + dn); -/* - * increment iv. - */ -static void -esp_increment_iv(sav) - struct secasvar *sav; -{ - u_int8_t *x; - u_int8_t y; - int i; + /* next iv */ + ivp = mtod(d, u_int8_t *) + dn; -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) - y = time.tv_sec & 0xff; -#else - y = time_second & 0xff; -#endif - if (!y) y++; - x = (u_int8_t *)sav->iv; - for (i = 0; i < sav->ivlen; i++) { - *x = (*x + y) & 0xff; - x++; - } -} + sn += blocklen; + dn += blocklen; -static caddr_t -mbuf_find_offset(m, off, len) - struct mbuf *m; - size_t off; - size_t len; -{ - struct mbuf *n; - size_t cnt; - - if (m->m_pkthdr.len < off || m->m_pkthdr.len < off + len) - return (caddr_t)NULL; - cnt = 0; - for (n = m; n; n = n->m_next) { - if (cnt + n->m_len <= off) { - cnt += n->m_len; - continue; + /* find the next source block */ + while (s && sn >= s->m_len) { + sn -= s->m_len; + soff += s->m_len; + s = s->m_next; } - if (cnt <= off && off < cnt + n->m_len - && cnt <= off + len && off + len <= cnt + n->m_len) { - return mtod(n, caddr_t) + off - cnt; - } else - return (caddr_t)NULL; + + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; } - return (caddr_t)NULL; + + m_freem(scut->m_next); + scut->m_len = scutoff; + scut->m_next = d0; + + /* just in case */ + bzero(iv, sizeof(iv)); + bzero(sbuf, sizeof(sbuf)); + + key_sa_stir_iv(sav); + + return 0; } /*------------------------------------------------------------*/ @@ -1207,7 +1052,7 @@ esp_auth(m0, skip, length, sav, sum) size_t off; struct ah_algorithm_state s; u_char sumbuf[AH_MAXSUMSIZE]; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t siz; int error; @@ -1233,7 +1078,8 @@ esp_auth(m0, skip, length, sav, sum) ipseclog((LOG_DEBUG, "esp_auth: NULL SA passed\n")); return EINVAL; } - if (!sav->alg_auth) { + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { ipseclog((LOG_ERR, "esp_auth: bad ESP auth algorithm passed: %d\n", sav->alg_auth)); @@ -1243,7 +1089,6 @@ esp_auth(m0, skip, length, sav, sum) m = m0; off = 0; - algo = &ah_algorithms[sav->alg_auth]; siz = (((*algo->sumsiz)(sav) + 3) & ~(4 - 1)); if (sizeof(sumbuf) < siz) { ipseclog((LOG_DEBUG, diff --git a/sys/netinet6/esp_input.c b/sys/netinet6/esp_input.c index 4d4344b974f6..4542f135d560 100644 --- a/sys/netinet6/esp_input.c +++ b/sys/netinet6/esp_input.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp_input.c,v 1.25 2000/05/08 08:04:30 itojun Exp $ */ +/* $KAME: esp_input.c,v 1.55 2001/03/23 08:08:47 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -64,8 +64,10 @@ #ifdef INET6 #include <netinet/ip6.h> +#include <netinet6/in6_pcb.h> #include <netinet6/ip6_var.h> #include <netinet/icmp6.h> +#include <netinet6/ip6protosw.h> #endif #include <netinet6/ipsec.h> @@ -82,11 +84,7 @@ #endif #include <netkey/key.h> #include <netkey/keydb.h> -#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #include <machine/stdarg.h> @@ -94,14 +92,14 @@ #define IPLEN_FLIPPED -#ifdef INET -#include <netinet/ipprotosw.h> -extern struct ipprotosw inetsw[]; - #define ESPMAXLEN \ (sizeof(struct esp) < sizeof(struct newesp) \ ? sizeof(struct newesp) : sizeof(struct esp)) +#ifdef INET +#include <netinet/ipprotosw.h> +extern struct ipprotosw inetsw[]; + void #if __STDC__ esp4_input(struct mbuf *m, ...) @@ -118,7 +116,7 @@ esp4_input(m, va_alist) struct secasvar *sav = NULL; size_t taillen; u_int16_t nxt; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; size_t hlen; size_t esplen; @@ -178,16 +176,15 @@ esp4_input(m, va_alist) ipsecstat.in_badspi++; goto bad; } - if (sav->alg_enc == SADB_EALG_NONE) { + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { ipseclog((LOG_DEBUG, "IPv4 ESP input: " - "unspecified encryption algorithm for spi %u\n", + "unsupported encryption algorithm for spi %u\n", (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto bad; } - algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ - /* check if we have proper ivlen information */ ivlen = sav->ivlen; if (ivlen < 0) { @@ -201,7 +198,8 @@ esp4_input(m, va_alist) && (sav->alg_auth && sav->key_auth))) goto noreplaycheck; - if (sav->alg_auth == SADB_AALG_NULL) + if (sav->alg_auth == SADB_X_AALG_NULL || + sav->alg_auth == SADB_AALG_NONE) goto noreplaycheck; /* @@ -221,10 +219,12 @@ esp4_input(m, va_alist) { u_char sum0[AH_MAXSUMSIZE]; u_char sum[AH_MAXSUMSIZE]; - struct ah_algorithm *sumalgo; + const struct ah_algorithm *sumalgo; size_t siz; - sumalgo = &ah_algorithms[sav->alg_auth]; + sumalgo = ah_algorithm_lookup(sav->alg_auth); + if (!sumalgo) + goto noreplaycheck; siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) { ipseclog((LOG_DEBUG, @@ -303,22 +303,30 @@ noreplaycheck: } } - { + /* + * pre-compute and cache intermediate key + */ + if (esp_schedule(algo, sav) != 0) { + ipsecstat.in_inval++; + goto bad; + } + /* * decrypt the packet. */ if (!algo->decrypt) panic("internal error: no decrypt function"); if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { - ipseclog((LOG_ERR, "decrypt fail in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); + /* m is already freed */ + m = NULL; + ipseclog((LOG_ERR, "decrypt fail in IPv4 ESP input: %s\n", + ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } ipsecstat.in_esphist[sav->alg_enc]++; m->m_flags |= M_DECRYPTED; - } /* * find the trailer of the ESP. @@ -347,7 +355,7 @@ noreplaycheck: #endif /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec4_tunnel_validate(ip, nxt, sav)) { + if (ipsec4_tunnel_validate(m, off + esplen + ivlen, nxt, sav)) { /* * strip off all the headers that precedes ESP header. * IP4 xx ESP IP4' payload -> IP4' payload @@ -387,6 +395,11 @@ noreplaycheck: #endif key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) { + ipsecstat.in_nomem++; + goto bad; + } if (! IF_HANDOFF(&ipintrq, m, NULL)) { ipsecstat.in_inval++; @@ -421,10 +434,19 @@ noreplaycheck: ip->ip_p = nxt; key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0) { + ipsecstat.in_nomem++; + goto bad; + } - if (nxt != IPPROTO_DONE) + if (nxt != IPPROTO_DONE) { + if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto bad; + } (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); - else + } else m_freem(m); m = NULL; } @@ -464,10 +486,9 @@ esp6_input(mp, offp, proto) struct secasvar *sav = NULL; size_t taillen; u_int16_t nxt; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; size_t esplen; - int s; /* sanity check for alignment. */ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { @@ -518,16 +539,15 @@ esp6_input(mp, offp, proto) ipsec6stat.in_badspi++; goto bad; } - if (sav->alg_enc == SADB_EALG_NONE) { + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { ipseclog((LOG_DEBUG, "IPv6 ESP input: " - "unspecified encryption algorithm for spi %u\n", + "unsupported encryption algorithm for spi %u\n", (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto bad; } - algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ - /* check if we have proper ivlen information */ ivlen = sav->ivlen; if (ivlen < 0) { @@ -541,7 +561,8 @@ esp6_input(mp, offp, proto) && (sav->alg_auth && sav->key_auth))) goto noreplaycheck; - if (sav->alg_auth == SADB_AALG_NULL) + if (sav->alg_auth == SADB_X_AALG_NULL || + sav->alg_auth == SADB_AALG_NONE) goto noreplaycheck; /* @@ -561,10 +582,12 @@ esp6_input(mp, offp, proto) { u_char sum0[AH_MAXSUMSIZE]; u_char sum[AH_MAXSUMSIZE]; - struct ah_algorithm *sumalgo; + const struct ah_algorithm *sumalgo; size_t siz; - sumalgo = &ah_algorithms[sav->alg_auth]; + sumalgo = ah_algorithm_lookup(sav->alg_auth); + if (!sumalgo) + goto noreplaycheck; siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) { ipseclog((LOG_DEBUG, @@ -643,13 +666,23 @@ noreplaycheck: ip6 = mtod(m, struct ip6_hdr *); /*set it again just in case*/ /* + * pre-compute and cache intermediate key + */ + if (esp_schedule(algo, sav) != 0) { + ipsec6stat.in_inval++; + goto bad; + } + + /* * decrypt the packet. */ if (!algo->decrypt) panic("internal error: no decrypt function"); if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { - ipseclog((LOG_ERR, "decrypt fail in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); + /* m is already freed */ + m = NULL; + ipseclog((LOG_ERR, "decrypt fail in IPv6 ESP input: %s\n", + ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto bad; } @@ -680,7 +713,7 @@ noreplaycheck: ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - taillen); /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec6_tunnel_validate(ip6, nxt, sav)) { + if (ipsec6_tunnel_validate(m, off + esplen + ivlen, nxt, sav)) { /* * strip off all the headers that precedes ESP header. * IP6 xx ESP IP6' payload -> IP6' payload @@ -728,6 +761,11 @@ noreplaycheck: #endif key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) { + ipsec6stat.in_nomem++; + goto bad; + } if (! IF_HANDOFF(&ip6intrq, m, NULL)) { ipsec6stat.in_inval++; @@ -778,10 +816,60 @@ noreplaycheck: m->m_pkthdr.len += n->m_pkthdr.len; } +#ifndef PULLDOWN_TEST + /* + * KAME requires that the packet to be contiguous on the + * mbuf. We need to make that sure. + * this kind of code should be avoided. + * XXX other conditions to avoid running this part? + */ + if (m->m_len != m->m_pkthdr.len) { + struct mbuf *n = NULL; + int maxlen; + + MGETHDR(n, M_DONTWAIT, MT_HEADER); + maxlen = MHLEN; + if (n) + M_COPY_PKTHDR(n, m); + if (n && m->m_pkthdr.len > maxlen) { + MCLGET(n, M_DONTWAIT); + maxlen = MCLBYTES; + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } + if (!n) { + printf("esp6_input: mbuf allocation failed\n"); + goto bad; + } + + if (m->m_pkthdr.len <= maxlen) { + m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t)); + n->m_len = m->m_pkthdr.len; + n->m_pkthdr.len = m->m_pkthdr.len; + n->m_next = NULL; + m_freem(m); + } else { + m_copydata(m, 0, maxlen, mtod(n, caddr_t)); + m_adj(m, maxlen); + n->m_len = maxlen; + n->m_pkthdr.len = m->m_pkthdr.len; + n->m_next = m; + m->m_flags &= ~M_PKTHDR; + } + m = n; + } +#endif + ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0) { + ipsec6stat.in_nomem++; + goto bad; + } } *offp = off; @@ -805,4 +893,108 @@ bad: m_freem(m); return IPPROTO_DONE; } + +void +esp6_ctlinput(cmd, sa, d) + int cmd; + struct sockaddr *sa; + void *d; +{ + const struct newesp *espp; + struct newesp esp; + struct ip6ctlparam *ip6cp = NULL, ip6cp1; + struct secasvar *sav; + struct ip6_hdr *ip6; + struct mbuf *m; + int off; + struct sockaddr_in6 sa6_src, sa6_dst; + + if (sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(struct sockaddr_in6)) + return; + if ((unsigned)cmd >= PRC_NCMDS) + return; + + /* if the parameter is from icmp6, decode it. */ + if (d != NULL) { + ip6cp = (struct ip6ctlparam *)d; + m = ip6cp->ip6c_m; + ip6 = ip6cp->ip6c_ip6; + off = ip6cp->ip6c_off; + } else { + m = NULL; + ip6 = NULL; + } + + if (ip6) { + /* + * Notify the error to all possible sockets via pfctlinput2. + * Since the upper layer information (such as protocol type, + * source and destination ports) is embedded in the encrypted + * data and might have been cut, we can't directly call + * an upper layer ctlinput function. However, the pcbnotify + * function will consider source and destination addresses + * as well as the flow info value, and may be able to find + * some PCB that should be notified. + * Although pfctlinput2 will call esp6_ctlinput(), there is + * no possibility of an infinite loop of function calls, + * because we don't pass the inner IPv6 header. + */ + bzero(&ip6cp1, sizeof(ip6cp1)); + ip6cp1.ip6c_src = ip6cp->ip6c_src; + pfctlinput2(cmd, sa, (void *)&ip6cp1); + + /* + * Then go to special cases that need ESP header information. + * XXX: We assume that when ip6 is non NULL, + * M and OFF are valid. + */ + + /* check if we can safely examine src and dst ports */ + if (m->m_pkthdr.len < off + sizeof(esp)) + return; + + if (m->m_len < off + sizeof(esp)) { + /* + * this should be rare case, + * so we compromise on this copy... + */ + m_copydata(m, off, sizeof(esp), (caddr_t)&esp); + espp = &esp; + } else + espp = (struct newesp*)(mtod(m, caddr_t) + off); + + if (cmd == PRC_MSGSIZE) { + int valid = 0; + + /* + * Check to see if we have a valid SA corresponding to + * the address in the ICMP message payload. + */ + sav = key_allocsa(AF_INET6, + (caddr_t)&sa6_src.sin6_addr, + (caddr_t)&sa6_dst, IPPROTO_ESP, + espp->esp_spi); + if (sav) { + if (sav->state == SADB_SASTATE_MATURE || + sav->state == SADB_SASTATE_DYING) + valid++; + key_freesav(sav); + } + + /* XXX Further validation? */ + + /* + * Depending on the value of "valid" and routing table + * size (mtudisc_{hi,lo}wat), we will: + * - recalcurate the new MTU and create the + * corresponding routing entry, or + * - ignore the MTU change notification. + */ + icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); + } + } else { + /* we normally notify any pcb here */ + } +} #endif /* INET6 */ diff --git a/sys/netinet6/esp_output.c b/sys/netinet6/esp_output.c index 8d505ae0a72a..7770b787cdaa 100644 --- a/sys/netinet6/esp_output.c +++ b/sys/netinet6/esp_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp_output.c,v 1.22 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: esp_output.c,v 1.43 2001/03/01 07:10:45 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -90,7 +90,8 @@ esp_hdrsiz(isr) struct ipsecrequest *isr; { struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; + const struct ah_algorithm *aalgo; size_t ivlen; size_t authlen; size_t hdrsiz; @@ -111,7 +112,7 @@ esp_hdrsiz(isr) goto estimate; /* we need transport mode ESP. */ - algo = &esp_algorithms[sav->alg_enc]; + algo = esp_algorithm_lookup(sav->alg_enc); if (!algo) goto estimate; ivlen = sav->ivlen; @@ -130,8 +131,9 @@ esp_hdrsiz(isr) hdrsiz = sizeof(struct esp) + ivlen + 9; } else { /* RFC 2406 */ - if (sav->replay && sav->alg_auth && sav->key_auth) - authlen = (*ah_algorithms[sav->alg_auth].sumsiz)(sav); + aalgo = ah_algorithm_lookup(sav->alg_auth); + if (aalgo && sav->replay && sav->key_auth) + authlen = (aalgo->sumsiz)(sav); else authlen = 0; hdrsiz = sizeof(struct newesp) + ivlen + 9 + authlen; @@ -143,12 +145,12 @@ esp_hdrsiz(isr) /* * ASSUMING: * sizeof(struct newesp) > sizeof(struct esp). - * 8 = ivlen for CBC mode (RFC2451). + * esp_max_ivlen() = max ivlen for CBC mode * 9 = (maximum padding length without random padding length) * + (Pad Length field) + (Next Header field). * 16 = maximum ICV we support. */ - return sizeof(struct newesp) + 8 + 9 + 16; + return sizeof(struct newesp) + esp_max_ivlen() + 9 + 16; } /* @@ -184,7 +186,7 @@ esp_output(m, nexthdrp, md, isr, af) struct esp *esp; struct esptail *esptail; struct secasvar *sav = isr->sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; u_int32_t spi; u_int8_t nxt = 0; size_t plen; /*payload length to be encrypted*/ @@ -193,16 +195,19 @@ esp_output(m, nexthdrp, md, isr, af) int afnumber; size_t extendsiz; int error = 0; + struct ipsecstat *stat; switch (af) { #ifdef INET case AF_INET: afnumber = 4; + stat = &ipsecstat; break; #endif #ifdef INET6 case AF_INET6: afnumber = 6; + stat = &ipsec6stat; break; #endif default: @@ -225,28 +230,31 @@ esp_output(m, nexthdrp, md, isr, af) (u_int32_t)ntohl(ip->ip_dst.s_addr), (u_int32_t)ntohl(sav->spi))); ipsecstat.out_inval++; - m_freem(m); - return EINVAL; + break; } #endif /*INET*/ #ifdef INET6 case AF_INET6: - { - struct ip6_hdr *ip6; - - ip6 = mtod(m, struct ip6_hdr *); ipseclog((LOG_DEBUG, "esp6_output: internal error: " "sav->replay is null: SPI=%u\n", (u_int32_t)ntohl(sav->spi))); ipsec6stat.out_inval++; - m_freem(m); - return EINVAL; - } + break; #endif /*INET6*/ + default: + panic("esp_output: should not reach here"); } + m_freem(m); + return EINVAL; } - algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { + ipseclog((LOG_ERR, "esp_output: unsupported algorithm: " + "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); + m_freem(m); + return EINVAL; + } spi = sav->spi; ivlen = sav->ivlen; /* should be okey */ @@ -331,7 +339,7 @@ esp_output(m, nexthdrp, md, isr, af) * before: IP ... payload * after: IP ... ESP IV payload */ - if (M_LEADINGSPACE(md) < esphlen) { + if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) { MGET(n, M_DONTWAIT, MT_DATA); if (!n) { m_freem(m); @@ -386,7 +394,7 @@ esp_output(m, nexthdrp, md, isr, af) ipseclog((LOG_WARNING, "replay counter overflowed. %s\n", ipsec_logsastr(sav))); - ipsecstat.out_inval++; + stat->out_inval++; m_freem(m); return EINVAL; } @@ -402,7 +410,6 @@ esp_output(m, nexthdrp, md, isr, af) { /* * find the last mbuf. make some room for ESP trailer. - * XXX new-esp authentication data */ #ifdef INET struct ip *ip = NULL; @@ -410,6 +417,7 @@ esp_output(m, nexthdrp, md, isr, af) size_t padbound; u_char *extend; int i; + int randpadmax; if (algo->padbound) padbound = algo->padbound; @@ -423,13 +431,62 @@ esp_output(m, nexthdrp, md, isr, af) if (extendsiz == 1) extendsiz = padbound + 1; + /* random padding */ + switch (af) { +#ifdef INET + case AF_INET: + randpadmax = ip4_esp_randpad; + break; +#endif +#ifdef INET6 + case AF_INET6: + randpadmax = ip6_esp_randpad; + break; +#endif + default: + randpadmax = -1; + break; + } + if (randpadmax < 0 || plen + extendsiz >= randpadmax) + ; + else { + int n; + + /* round */ + randpadmax = (randpadmax / padbound) * padbound; + n = (randpadmax - plen + extendsiz) / padbound; + + if (n > 0) + n = (random() % n) * padbound; + else + n = 0; + + /* + * make sure we do not pad too much. + * MLEN limitation comes from the trailer attachment + * code below. + * 256 limitation comes from sequential padding. + * also, the 1-octet length field in ESP trailer imposes + * limitation (but is less strict than sequential padding + * as length field do not count the last 2 octets). + */ + if (extendsiz + n <= MLEN && extendsiz + n < 256) + extendsiz += n; + } + +#ifdef DIAGNOSTIC + if (extendsiz > MLEN || extendsiz >= 256) + panic("extendsiz too big in esp_output"); +#endif + n = m; while (n->m_next) n = n->m_next; /* - * if M_EXT, the external part may be shared among - * two consequtive TCP packets. + * if M_EXT, the external mbuf data may be shared among + * two consequtive TCP packets, and it may be unsafe to use the + * trailing space. */ if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) { extend = mtod(n, u_char *) + n->m_len; @@ -455,8 +512,7 @@ esp_output(m, nexthdrp, md, isr, af) } switch (sav->flags & SADB_X_EXT_PMASK) { case SADB_X_EXT_PRAND: - for (i = 0; i < extendsiz; i++) - extend[i] = random() & 0xff; + key_randomfill(extend, extendsiz); break; case SADB_X_EXT_PZERO: bzero(extend, extendsiz); @@ -499,26 +555,25 @@ esp_output(m, nexthdrp, md, isr, af) } /* + * pre-compute and cache intermediate key + */ + error = esp_schedule(algo, sav); + if (error) { + m_freem(m); + stat->out_inval++; + goto fail; + } + + /* * encrypt the packet, based on security association * and the algorithm specified. */ if (!algo->encrypt) panic("internal error: no encrypt function"); if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) { + /* m is already freed */ ipseclog((LOG_ERR, "packet encryption failure\n")); - m_freem(m); - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_inval++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_inval++; - break; -#endif - } + stat->out_inval++; error = EINVAL; goto fail; } @@ -530,16 +585,23 @@ esp_output(m, nexthdrp, md, isr, af) goto noantireplay; if (!sav->key_auth) goto noantireplay; - if (!sav->alg_auth) + if (sav->key_auth == SADB_AALG_NONE) goto noantireplay; + { + const struct ah_algorithm *aalgo; u_char authbuf[AH_MAXSUMSIZE]; struct mbuf *n; u_char *p; size_t siz; +#ifdef INET struct ip *ip; +#endif - siz = (((*ah_algorithms[sav->alg_auth].sumsiz)(sav) + 3) & ~(4 - 1)); + aalgo = ah_algorithm_lookup(sav->alg_auth); + if (!aalgo) + goto noantireplay; + siz = ((aalgo->sumsiz)(sav) + 3) & ~(4 - 1); if (AH_MAXSUMSIZE < siz) panic("assertion failed for AH_MAXSUMSIZE"); @@ -547,18 +609,7 @@ esp_output(m, nexthdrp, md, isr, af) ipseclog((LOG_ERR, "ESP checksum generation failure\n")); m_freem(m); error = EINVAL; - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_inval++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_inval++; - break; -#endif - } + stat->out_inval++; goto fail; } @@ -619,32 +670,9 @@ noantireplay: if (!m) { ipseclog((LOG_ERR, "NULL mbuf after encryption in esp%d_output", afnumber)); - } else { - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_success++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_success++; - break; -#endif - } - } - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_esphist[sav->alg_enc]++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_esphist[sav->alg_enc]++; - break; -#endif - } + } else + stat->out_success++; + stat->out_esphist[sav->alg_enc]++; key_sa_recordxfer(sav, m); return 0; diff --git a/sys/netinet6/esp_rijndael.c b/sys/netinet6/esp_rijndael.c new file mode 100644 index 000000000000..5b0e5fab47b1 --- /dev/null +++ b/sys/netinet6/esp_rijndael.c @@ -0,0 +1,116 @@ +/* $FreeBSD$ */ +/* $KAME: esp_rijndael.c,v 1.4 2001/03/02 05:53:05 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet6/ipsec.h> +#include <netinet6/esp.h> +#include <netinet6/esp_rijndael.h> + +#include <crypto/rijndael/rijndael.h> + +#include <net/net_osdep.h> + +/* as rijndael uses assymetric scheduled keys, we need to do it twice. */ +int +esp_rijndael_schedlen(algo) + const struct esp_algorithm *algo; +{ + + return sizeof(keyInstance) * 2; +} + +int +esp_rijndael_schedule(algo, sav) + const struct esp_algorithm *algo; + struct secasvar *sav; +{ + keyInstance *k; + + k = (keyInstance *)sav->sched; + if (rijndael_makeKey(&k[0], DIR_DECRYPT, _KEYLEN(sav->key_enc) * 8, + _KEYBUF(sav->key_enc)) < 0) + return -1; + if (rijndael_makeKey(&k[1], DIR_ENCRYPT, _KEYLEN(sav->key_enc) * 8, + _KEYBUF(sav->key_enc)) < 0) + return -1; + return 0; +} + +int +esp_rijndael_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ + cipherInstance c; + keyInstance *p; + + /* does not take advantage of CBC mode support */ + bzero(&c, sizeof(c)); + if (rijndael_cipherInit(&c, MODE_ECB, NULL) < 0) + return -1; + p = (keyInstance *)sav->sched; + if (rijndael_blockDecrypt(&c, &p[0], s, algo->padbound * 8, d) < 0) + return -1; + return 0; +} + +int +esp_rijndael_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ + cipherInstance c; + keyInstance *p; + + /* does not take advantage of CBC mode support */ + bzero(&c, sizeof(c)); + if (rijndael_cipherInit(&c, MODE_ECB, NULL) < 0) + return -1; + p = (keyInstance *)sav->sched; + if (rijndael_blockEncrypt(&c, &p[1], s, algo->padbound * 8, d) < 0) + return -1; + return 0; +} diff --git a/sys/crypto/rc5/rc5.h b/sys/netinet6/esp_rijndael.h index ae2339b757a1..0c40d78c5363 100644 --- a/sys/crypto/rc5/rc5.h +++ b/sys/netinet6/esp_rijndael.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: rc5.h,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ +/* $KAME: esp_rijndael.h,v 1.1 2000/09/20 18:15:22 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -30,58 +30,10 @@ * SUCH DAMAGE. */ -#ifndef _RFC2040_RC5_H_ -#define _RFC2040_RC5_H_ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/mbuf.h> - -/* - * if RC5_WORD change, W also may be changed. - */ -typedef u_int32_t RC5_WORD; - -#define W (32) -#define WW (W / 8) -#define ROT_MASK (W - 1) -#define BB ((2 * W) / 8) - -#define SHLL(x, s) ((RC5_WORD)((x) << ((s)&ROT_MASK))) -#define SHLR(x, s, w) ((RC5_WORD)((x) >> ((w)-((s)&ROT_MASK)))) -#define SHRL(x, s, w) ((RC5_WORD)((x) << ((w)-((s)&ROT_MASK)))) -#define SHRR(x, s) ((RC5_WORD)((x) >> ((s)&ROT_MASK))) - -#define ROTL(x, s, w) ((RC5_WORD)(SHLL((x), (s))|SHLR((x), (s), (w)))) -#define ROTR(x, s, w) ((RC5_WORD)(SHRL((x), (s), (w))|SHRR((x), (s)))) - -#define P16 0xb7e1 -#define Q16 0x9e37 -#define P32 0xb7e15163 -#define Q32 0x9e3779b9 -#define P64 0xb7e151628aed2a6b -#define Q64 0x9e3779b97f4a7c15 - -#if W == 16 -#define Pw P16 -#define Qw Q16 -#elif W == 32 -#define Pw P32 -#define Qw Q32 -#elif W == 64 -#define Pw P64 -#define Qw Q64 -#endif - -#define RC5_ENCRYPT 1 -#define RC5_DECRYPT 0 - -extern void set_rc5_expandkey __P((RC5_WORD *, u_int8_t *, size_t, int)); -extern void rc5_encrypt_round16 __P((u_int8_t *, const u_int8_t *, - const RC5_WORD *)); -extern void rc5_decrypt_round16 __P((u_int8_t *, const u_int8_t *, - const RC5_WORD *)); -extern int rc5_cbc_process __P((struct mbuf *, size_t, size_t, RC5_WORD *, - u_int8_t *, int)); - -#endif +int esp_rijndael_schedlen __P((const struct esp_algorithm *)); +int esp_rijndael_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +int esp_rijndael_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +int esp_rijndael_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c index ea4e6ca55089..dbf9279771c2 100644 --- a/sys/netinet6/frag6.c +++ b/sys/netinet6/frag6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: frag6.c,v 1.24 2000/03/25 07:23:41 sumikawa Exp $ */ +/* $KAME: frag6.c,v 1.31 2001/05/17 13:45:34 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -66,6 +66,7 @@ static void frag6_insque __P((struct ip6q *, struct ip6q *)); static void frag6_remque __P((struct ip6q *)); static void frag6_freef __P((struct ip6q *)); +/* XXX we eventually need splreass6, or some real semaphore */ int frag6_doing_reass; u_int frag6_nfragpackets; struct ip6q ip6q; /* ip6 reassemble queue */ @@ -206,6 +207,8 @@ frag6_input(mp, offp, proto) /* offset now points to data portion */ offset += sizeof(struct ip6_frag); + frag6_doing_reass = 1; + for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next) if (ip6f->ip6f_ident == q6->ip6q_ident && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && @@ -217,7 +220,6 @@ frag6_input(mp, offp, proto) * the first fragment to arrive, create a reassembly queue. */ first_frag = 1; - frag6_nfragpackets++; /* * Enforce upper bound on number of fragmented packets @@ -225,11 +227,11 @@ frag6_input(mp, offp, proto) * If maxfrag is 0, never accept fragments. * If maxfrag is -1, accept all fragments without limitation. */ - if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) { - ip6stat.ip6s_fragoverflow++; - in6_ifstat_inc(dstifp, ifs6_reass_fail); - frag6_freef(ip6q.ip6q_prev); - } + if (ip6_maxfragpackets < 0) + ; + else if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) + goto dropfrag; + frag6_nfragpackets++; q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE, M_DONTWAIT); if (q6 == NULL) @@ -274,6 +276,7 @@ frag6_input(mp, offp, proto) icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset - sizeof(struct ip6_frag) + offsetof(struct ip6_frag, ip6f_offlg)); + frag6_doing_reass = 0; return(IPPROTO_DONE); } } @@ -281,6 +284,7 @@ frag6_input(mp, offp, proto) icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset - sizeof(struct ip6_frag) + offsetof(struct ip6_frag, ip6f_offlg)); + frag6_doing_reass = 0; return(IPPROTO_DONE); } /* @@ -531,6 +535,7 @@ insert: in6_ifstat_inc(dstifp, ifs6_reass_fail); ip6stat.ip6s_fragdropped++; m_freem(m); + frag6_doing_reass = 0; return IPPROTO_DONE; } @@ -620,7 +625,7 @@ frag6_remque(p6) } /* - * IP timer processing; + * IPv6 reassembling timer processing; * if a timer expires on a reassembly * queue, discard it. */ @@ -629,9 +634,6 @@ frag6_slowtimo() { struct ip6q *q6; int s = splnet(); -#if 0 - extern struct route_in6 ip6_forward_rt; -#endif frag6_doing_reass = 1; q6 = ip6q.ip6q_next; @@ -650,7 +652,8 @@ frag6_slowtimo() * (due to the limit being lowered), drain off * enough to get down to the new limit. */ - while (frag6_nfragpackets > (u_int)ip6_maxfragpackets) { + while (frag6_nfragpackets > (u_int)ip6_maxfragpackets && + ip6q.ip6q_prev) { ip6stat.ip6s_fragoverflow++; /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ frag6_freef(ip6q.ip6q_prev); diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 9dca71e71902..4ea9a3ac2d1d 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: icmp6.c,v 1.119 2000/07/03 14:16:46 itojun Exp $ */ +/* $KAME: icmp6.c,v 1.211 2001/04/04 05:56:20 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -71,6 +71,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -99,16 +100,45 @@ #ifdef IPSEC #include <netinet6/ipsec.h> -#ifdef INET6 -#include <netinet6/ipsec6.h> -#endif #include <netkey/key.h> #endif #include "faith.h" +#if defined(NFAITH) && 0 < NFAITH +#include <net/if_faith.h> +#endif #include <net/net_osdep.h> +#ifdef HAVE_NRL_INPCB +/* inpcb members */ +#define in6pcb inpcb +#define in6p_laddr inp_laddr6 +#define in6p_faddr inp_faddr6 +#define in6p_icmp6filt inp_icmp6filt +#define in6p_route inp_route +#define in6p_socket inp_socket +#define in6p_flags inp_flags +#define in6p_moptions inp_moptions6 +#define in6p_outputopts inp_outputopts6 +#define in6p_ip6 inp_ipv6 +#define in6p_flowinfo inp_flowinfo +#define in6p_sp inp_sp +#define in6p_next inp_next +#define in6p_prev inp_prev +/* macro names */ +#define sotoin6pcb sotoinpcb +/* function names */ +#define in6_pcbdetach in_pcbdetach +#define in6_rtchange in_rtchange + +/* + * for KAME src sync over BSD*'s. XXX: FreeBSD (>=3) are VERY different from + * others... + */ +#define in6p_ip6_nxt inp_ipv6.ip6_nxt +#endif + extern struct domain inet6domain; extern struct ip6protosw inet6sw[]; extern u_char ip6_protox[]; @@ -116,34 +146,33 @@ extern u_char ip6_protox[]; struct icmp6stat icmp6stat; extern struct inpcbhead ripcb; -extern struct timeval icmp6errratelim; -static struct timeval icmp6errratelim_last; extern int icmp6errppslim; static int icmp6errpps_count = 0; +static struct timeval icmp6errppslim_last; extern int icmp6_nodeinfo; static void icmp6_errcount __P((struct icmp6errstat *, int, int)); static int icmp6_rip6_input __P((struct mbuf **, int)); -static void icmp6_mtudisc_update __P((struct in6_addr *, struct icmp6_hdr *, - struct mbuf *)); static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); static const char *icmp6_redirect_diag __P((struct in6_addr *, struct in6_addr *, struct in6_addr *)); -#ifndef HAVE_RATECHECK -static int ratecheck __P((struct timeval *, struct timeval *)); +#ifndef HAVE_PPSRATECHECK +static int ppsratecheck __P((struct timeval *, int *, int)); #endif static struct mbuf *ni6_input __P((struct mbuf *, int)); static struct mbuf *ni6_nametodns __P((const char *, int, int)); static int ni6_dnsmatch __P((const char *, int, const char *, int)); static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, - struct ifnet **)); + struct ifnet **, char *)); static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, struct ifnet *, int)); +static int icmp6_notify_error __P((struct mbuf *, int, int, int)); #ifdef COMPAT_RFC1885 static struct route_in6 icmp6_reflect_rt; #endif + void icmp6_init() { @@ -155,7 +184,7 @@ icmp6_errcount(stat, type, code) struct icmp6errstat *stat; int type, code; { - switch(type) { + switch (type) { case ICMP6_DST_UNREACH: switch (code) { case ICMP6_DST_UNREACH_NOROUTE: @@ -179,7 +208,7 @@ icmp6_errcount(stat, type, code) stat->icp6errs_packet_too_big++; return; case ICMP6_TIME_EXCEEDED: - switch(code) { + switch (code) { case ICMP6_TIME_EXCEED_TRANSIT: stat->icp6errs_time_exceed_transit++; return; @@ -189,7 +218,7 @@ icmp6_errcount(stat, type, code) } break; case ICMP6_PARAM_PROB: - switch(code) { + switch (code) { case ICMP6_PARAMPROB_HEADER: stat->icp6errs_paramprob_header++; return; @@ -318,7 +347,7 @@ icmp6_error(m, type, code, param) if (m && m->m_len < preplen) m = m_pullup(m, preplen); if (m == NULL) { - printf("ENOBUFS in icmp6_error %d\n", __LINE__); + nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__)); return; } @@ -336,6 +365,15 @@ icmp6_error(m, type, code, param) icmp6->icmp6_code = code; icmp6->icmp6_pptr = htonl((u_int32_t)param); + /* + * icmp6_reflect() is designed to be in the input path. + * icmp6_error() can be called from both input and outut path, + * and if we are in output path rcvif could contain bogus value. + * clear m->m_pkthdr.rcvif for safety, we should have enough scope + * information in ip header (nip6). + */ + m->m_pkthdr.rcvif = NULL; + icmp6stat.icp6s_outhist[type]++; icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/ @@ -362,7 +400,6 @@ icmp6_input(mp, offp, proto) int off = *offp; int icmp6len = m->m_pkthdr.len - *offp; int code, sum, noff; - struct sockaddr_in6 icmp6src; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); @@ -395,17 +432,15 @@ icmp6_input(mp, offp, proto) code = icmp6->icmp6_code; if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 checksum error(%d|%x) %s\n", - icmp6->icmp6_type, - sum, - ip6_sprintf(&ip6->ip6_src)); + icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src))); icmp6stat.icp6s_checksum++; goto freeit; } #if defined(NFAITH) && 0 < NFAITH - if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { + if (faithprefix(&ip6->ip6_dst)) { /* * Deliver very specific ICMP6 type only. * This is important to deilver TOOBIG. Otherwise PMTUD @@ -422,21 +457,12 @@ icmp6_input(mp, offp, proto) } #endif -#ifdef IPSEC - /* drop it if it does not match the default policy */ - if (ipsec6_in_reject(m, NULL)) { - ipsecstat.in_polvio++; - goto freeit; - } -#endif - icmp6stat.icp6s_inhist[icmp6->icmp6_type]++; icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg); if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error); switch (icmp6->icmp6_type) { - case ICMP6_DST_UNREACH: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach); switch (code) { @@ -530,9 +556,6 @@ icmp6_input(mp, offp, proto) * always copy the length we specified. */ if (maxlen >= MCLBYTES) { -#ifdef DIAGNOSTIC - printf("MCLBYTES too small\n"); -#endif /* Give up remote */ m_freem(n0); break; @@ -648,13 +671,13 @@ icmp6_input(mp, offp, proto) u_char *p; int maxlen, maxhlen; + if ((icmp6_nodeinfo & 5) != 5) + break; + if (code != 0) goto badcode; maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4; if (maxlen >= MCLBYTES) { -#ifdef DIAGNOSTIC - printf("MCLBYTES too small\n"); -#endif /* Give up remote */ break; } @@ -670,6 +693,7 @@ icmp6_input(mp, offp, proto) /* Give up remote */ break; } + n->m_pkthdr.rcvif = NULL; n->m_len = 0; maxhlen = M_TRAILINGSPACE(n) - maxlen; if (maxhlen > hostnamelen) @@ -794,10 +818,11 @@ icmp6_input(mp, offp, proto) break; default: - printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", - icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), - ip6_sprintf(&ip6->ip6_dst), - m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0); + nd6log((LOG_DEBUG, + "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", + icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), + m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0)); if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { /* ICMPv6 error: MUST deliver it by spec... */ code = PRC_NCMDS; @@ -807,32 +832,63 @@ icmp6_input(mp, offp, proto) break; } deliver: - if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { - icmp6stat.icp6s_tooshort++; - goto freeit; + if (icmp6_notify_error(m, off, icmp6len, code)) { + /* In this case, m should've been freed. */ + return(IPPROTO_DONE); } + break; + + badcode: + icmp6stat.icp6s_badcode++; + break; + + badlen: + icmp6stat.icp6s_badlen++; + break; + } + + /* deliver the packet to appropriate sockets */ + icmp6_rip6_input(&m, *offp); + + return IPPROTO_DONE; + + freeit: + m_freem(m); + return IPPROTO_DONE; +} + +static int +icmp6_notify_error(m, off, icmp6len, code) + struct mbuf *m; + int off, icmp6len; +{ + struct icmp6_hdr *icmp6; + struct ip6_hdr *eip6; + u_int32_t notifymtu; + struct sockaddr_in6 icmp6src, icmp6dst; + + if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { + icmp6stat.icp6s_tooshort++; + goto freeit; + } #ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, - sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), - IPPROTO_DONE); - icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); + IP6_EXTHDR_CHECK(m, off, + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), + -1); + icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); #else - IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, - sizeof(*icmp6) + sizeof(struct ip6_hdr)); - if (icmp6 == NULL) { - icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; - } + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, + sizeof(*icmp6) + sizeof(struct ip6_hdr)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return(-1); + } #endif - bzero(&icmp6src, sizeof(icmp6src)); - icmp6src.sin6_len = sizeof(struct sockaddr_in6); - icmp6src.sin6_family = AF_INET6; - icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + eip6 = (struct ip6_hdr *)(icmp6 + 1); - /* Detect the upper level protocol */ - { + /* Detect the upper level protocol */ + { void (*ctlfunc) __P((int, struct sockaddr *, void *)); - struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1); u_int8_t nxt = eip6->ip6_nxt; int eoff = off + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr); @@ -847,22 +903,22 @@ icmp6_input(mp, offp, proto) while (1) { /* XXX: should avoid inf. loop explicitly? */ struct ip6_ext *eh; - switch(nxt) { + switch (nxt) { case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: case IPPROTO_AH: #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_ext), - IPPROTO_DONE); + -1); eh = (struct ip6_ext *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(eh, struct ip6_ext *, m, - eoff, sizeof(*eh)); + eoff, sizeof(*eh)); if (eh == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif @@ -883,15 +939,15 @@ icmp6_input(mp, offp, proto) */ #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth), - IPPROTO_DONE); + -1); rth = (struct ip6_rthdr *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, - eoff, sizeof(*rth)); + eoff, sizeof(*rth)); if (rth == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif rthlen = (rth->ip6r_len + 1) << 3; @@ -909,7 +965,7 @@ icmp6_input(mp, offp, proto) #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, - IPPROTO_DONE); + -1); rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(rth0, @@ -917,7 +973,7 @@ icmp6_input(mp, offp, proto) eoff, rthlen); if (rth0 == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif /* just ignore a bogus header */ @@ -932,15 +988,15 @@ icmp6_input(mp, offp, proto) #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_frag), - IPPROTO_DONE); + -1); fh = (struct ip6_frag *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(fh, struct ip6_frag *, m, - eoff, sizeof(*fh)); + eoff, sizeof(*fh)); if (fh == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif /* @@ -967,69 +1023,114 @@ icmp6_input(mp, offp, proto) goto notify; } } - notify: + notify: #ifndef PULLDOWN_TEST icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); #else IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, - sizeof(*icmp6) + sizeof(struct ip6_hdr)); + sizeof(*icmp6) + sizeof(struct ip6_hdr)); if (icmp6 == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); + } +#endif + + eip6 = (struct ip6_hdr *)(icmp6 + 1); + bzero(&icmp6dst, sizeof(icmp6dst)); + icmp6dst.sin6_len = sizeof(struct sockaddr_in6); + icmp6dst.sin6_family = AF_INET6; + if (finaldst == NULL) + icmp6dst.sin6_addr = eip6->ip6_dst; + else + icmp6dst.sin6_addr = *finaldst; + icmp6dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &icmp6dst.sin6_addr); +#ifndef SCOPEDROUTING + if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst, + NULL, NULL)) { + /* should be impossbile */ + nd6log((LOG_DEBUG, + "icmp6_notify_error: in6_embedscope failed\n")); + goto freeit; + } +#endif + + /* + * retrieve parameters from the inner IPv6 header, and convert + * them into sockaddr structures. + */ + bzero(&icmp6src, sizeof(icmp6src)); + icmp6src.sin6_len = sizeof(struct sockaddr_in6); + icmp6src.sin6_family = AF_INET6; + icmp6src.sin6_addr = eip6->ip6_src; + icmp6src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &icmp6src.sin6_addr); +#ifndef SCOPEDROUTING + if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src, + NULL, NULL)) { + /* should be impossbile */ + nd6log((LOG_DEBUG, + "icmp6_notify_error: in6_embedscope failed\n")); + goto freeit; } #endif + icmp6src.sin6_flowinfo = + (eip6->ip6_flow & IPV6_FLOWLABEL_MASK); + + if (finaldst == NULL) + finaldst = &eip6->ip6_dst; + ip6cp.ip6c_m = m; + ip6cp.ip6c_icmp6 = icmp6; + ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); + ip6cp.ip6c_off = eoff; + ip6cp.ip6c_finaldst = finaldst; + ip6cp.ip6c_src = &icmp6src; + ip6cp.ip6c_nxt = nxt; + if (icmp6type == ICMP6_PACKET_TOO_BIG) { - if (finaldst == NULL) - finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; - icmp6_mtudisc_update(finaldst, icmp6, m); + notifymtu = ntohl(icmp6->icmp6_mtu); + ip6cp.ip6c_cmdarg = (void *)¬ifymtu; + icmp6_mtudisc_update(&ip6cp, 1); /*XXX*/ } ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) (inet6sw[ip6_protox[nxt]].pr_ctlinput); if (ctlfunc) { - ip6cp.ip6c_m = m; - ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); - ip6cp.ip6c_off = eoff; - (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp); + (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst, + &ip6cp); } - } - break; - - badcode: - icmp6stat.icp6s_badcode++; - break; - - badlen: - icmp6stat.icp6s_badlen++; - break; } + return(0); -#ifdef HAVE_NRL_INPCB - rip6_input(&m, offp, IPPROTO_ICMPV6); -#else - icmp6_rip6_input(&m, *offp); -#endif - return IPPROTO_DONE; - - freeit: + freeit: m_freem(m); - return IPPROTO_DONE; + return(-1); } -static void -icmp6_mtudisc_update(dst, icmp6, m) - struct in6_addr *dst; - struct icmp6_hdr *icmp6;/* we can assume the validity of the pointer */ - struct mbuf *m; /* currently unused but added for scoped addrs */ +void +icmp6_mtudisc_update(ip6cp, validated) + struct ip6ctlparam *ip6cp; + int validated; { + struct in6_addr *dst = ip6cp->ip6c_finaldst; + struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6; + struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */ u_int mtu = ntohl(icmp6->icmp6_mtu); struct rtentry *rt = NULL; struct sockaddr_in6 sin6; + if (!validated) + return; + bzero(&sin6, sizeof(sin6)); sin6.sin6_family = PF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_addr = *dst; + /* XXX normally, this won't happen */ + if (IN6_IS_ADDR_LINKLOCAL(dst)) { + sin6.sin6_addr.s6_addr16[1] = + htons(m->m_pkthdr.rcvif->if_index); + } /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */ rt = rtalloc1((struct sockaddr *)&sin6, 0, RTF_CLONING | RTF_PRCLONING); @@ -1041,6 +1142,7 @@ icmp6_mtudisc_update(dst, icmp6, m) rt->rt_rmx.rmx_locks |= RTV_MTU; } else if (mtu < rt->rt_ifp->if_mtu && rt->rt_rmx.rmx_mtu > mtu) { + icmp6stat.icp6s_pmtuchg++; rt->rt_rmx.rmx_mtu = mtu; } } @@ -1049,19 +1151,17 @@ icmp6_mtudisc_update(dst, icmp6, m) } /* - * Process a Node Information Query packet, (roughly) based on - * draft-ietf-ipngwg-icmp-name-lookups-05. + * Process a Node Information Query packet, based on + * draft-ietf-ipngwg-icmp-name-lookups-07. * * Spec incompatibilities: * - IPv6 Subject address handling * - IPv4 Subject address handling support missing * - Proxy reply (answer even if it's not for me) - * - "Supported Qtypes" support missing * - joins NI group address at in6_ifattach() time only, does not cope * with hostname changes by sethostname(3) */ #define hostnamelen strlen(hostname) - static struct mbuf * ni6_input(m, off) struct mbuf *m; @@ -1075,10 +1175,12 @@ ni6_input(m, off) struct ni_reply_fqdn *fqdn; int addrs; /* for NI_QTYPE_NODEADDR */ struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ - struct sockaddr_in6 sin6; + struct sockaddr_in6 sin6; /* double meaning; ip6_dst and subjectaddr */ + struct sockaddr_in6 sin6_d; /* XXX: we should retrieve this from m_aux */ struct ip6_hdr *ip6; int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ - char *subj; + char *subj = NULL; + struct in6_ifaddr *ia6 = NULL; ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST @@ -1094,81 +1196,40 @@ ni6_input(m, off) /* * Validate IPv6 destination address. * - * We accept packets with the following IPv6 destination address: - * - Responder's unicast/anycast address, - * - link-local multicast address - * This is a violation to last paragraph in icmp-name-lookups-05 - * page 4, which restricts IPv6 destination address of a query to: - * - Responder's unicast/anycast address, - * - NI group address for a name belongs to the Responder, or - * - NI group address for a name for which the Responder is providing - * proxy service. - * (note: NI group address is a link-local multicast address) - * - * We allow any link-local multicast address, since "ping6 -w ff02::1" - * has been really useful for us debugging our network. Also this is - * still questionable if the restriction in spec buy us security at all, - * since RFC2463 permits echo packet to multicast destination. - * Even if we forbid NI query to ff02::1, we can effectively get the - * same result as "ping6 -w ff02::1" by the following steps: - * - run "ping6 ff02::1", then - * - run "ping6 -w" for all addresses replied. + * The Responder must discard the Query without further processing + * unless it is one of the Responder's unicast or anycast addresses, or + * a link-local scope multicast address which the Responder has joined. + * [icmp-name-lookups-07, Section 4.] */ bzero(&sin6, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); bcopy(&ip6->ip6_dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); /* XXX scopeid */ - if (ifa_ifwithaddr((struct sockaddr *)&sin6)) - ; /*unicast/anycast, fine*/ - else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) - ; /*violates spec slightly, see above*/ + if ((ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)&sin6)) != NULL) { + /* unicast/anycast, fine */ + if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + (icmp6_nodeinfo & 4) == 0) { + nd6log((LOG_DEBUG, "ni6_input: ignore node info to " + "a temporary address in %s:%d", + __FILE__, __LINE__)); + goto bad; + } + } else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) + ; /* link-local multicast, fine */ else goto bad; - /* guess reply length */ - qtype = ntohs(ni6->ni_qtype); - switch (qtype) { - case NI_QTYPE_NOOP: - break; /* no reply data */ - case NI_QTYPE_SUPTYPES: - goto bad; /* xxx: to be implemented */ - break; - case NI_QTYPE_FQDN: - /* XXX will append a mbuf */ - replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); - break; - case NI_QTYPE_NODEADDR: - addrs = ni6_addrs(ni6, m, &ifp); - if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES) - replylen = MCLBYTES; /* XXX: we'll truncate later */ - break; - default: - /* - * XXX: We must return a reply with the ICMP6 code - * `unknown Qtype' in this case. However we regard the case - * as an FQDN query for backward compatibility. - * Older versions set a random value to this field, - * so it rarely varies in the defined qtypes. - * But the mechanism is not reliable... - * maybe we should obsolete older versions. - */ - qtype = NI_QTYPE_FQDN; - /* XXX will append a mbuf */ - replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); - oldfqdn++; - break; - } - /* validate query Subject field. */ + qtype = ntohs(ni6->ni_qtype); subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo); switch (qtype) { case NI_QTYPE_NOOP: case NI_QTYPE_SUPTYPES: - if (subjlen != 0) - goto bad; - break; - + /* 07 draft */ + if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0) + break; + /* FALLTHROUGH */ case NI_QTYPE_FQDN: case NI_QTYPE_NODEADDR: switch (ni6->ni_code) { @@ -1180,10 +1241,15 @@ ni6_input(m, off) * backward compatibility - try to accept 03 draft * format, where no Subject is present. */ - if (subjlen == 0) { + if (qtype == NI_QTYPE_FQDN && ni6->ni_code == 0 && + subjlen == 0) { oldfqdn++; break; } +#if ICMP6_NI_SUBJ_IPV6 != 0 + if (ni6->ni_code != ICMP6_NI_SUBJ_IPV6) + goto bad; +#endif if (subjlen != sizeof(sin6.sin6_addr)) goto bad; @@ -1191,8 +1257,8 @@ ni6_input(m, off) /* * Validate Subject address. * - * Not sure what exactly does "address belongs to the - * node" mean in the spec, is it just unicast, or what? + * Not sure what exactly "address belongs to the node" + * means in the spec, is it just unicast, or what? * * At this moment we consider Subject address as * "belong to the node" if the Subject address equals @@ -1205,23 +1271,24 @@ ni6_input(m, off) /* m_pulldown instead of copy? */ m_copydata(m, off + sizeof(struct icmp6_nodeinfo), subjlen, (caddr_t)&sin6.sin6_addr); - /* XXX kame scope hack */ - if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { -#ifdef FAKE_LOOPBACK_IF - if ((m->m_flags & M_PKTHDR) != 0 && - m->m_pkthdr.rcvif) { - sin6.sin6_addr.s6_addr16[1] = - htons(m->m_pkthdr.rcvif->if_index); - } -#else - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { - sin6.sin6_addr.s6_addr16[1] = - ip6->ip6_dst.s6_addr16[1]; - } + sin6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &sin6.sin6_addr); +#ifndef SCOPEDROUTING + in6_embedscope(&sin6.sin6_addr, &sin6, NULL, NULL); #endif - } - if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6.sin6_addr)) + bzero(&sin6_d, sizeof(sin6_d)); + sin6_d.sin6_family = AF_INET6; /* not used, actually */ + sin6_d.sin6_len = sizeof(sin6_d); /* ditto */ + sin6_d.sin6_addr = ip6->ip6_dst; + sin6_d.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &ip6->ip6_dst); +#ifndef SCOPEDROUTING + in6_embedscope(&sin6_d.sin6_addr, &sin6_d, NULL, NULL); +#endif + subj = (char *)&sin6; + if (SA6_ARE_ADDR_EQUAL(&sin6, &sin6_d)) break; + /* * XXX if we are to allow other cases, we should really * be careful about scope here. @@ -1259,18 +1326,60 @@ ni6_input(m, off) n = NULL; break; - case ICMP6_NI_SUBJ_IPV4: /* xxx: to be implemented? */ + case ICMP6_NI_SUBJ_IPV4: /* XXX: to be implemented? */ default: goto bad; } break; + } + + /* refuse based on configuration. XXX ICMP6_NI_REFUSED? */ + switch (qtype) { + case NI_QTYPE_FQDN: + if ((icmp6_nodeinfo & 1) == 0) + goto bad; + break; + case NI_QTYPE_NODEADDR: + if ((icmp6_nodeinfo & 2) == 0) + goto bad; + break; + } + /* guess reply length */ + switch (qtype) { + case NI_QTYPE_NOOP: + break; /* no reply data */ + case NI_QTYPE_SUPTYPES: + replylen += sizeof(u_int32_t); + break; + case NI_QTYPE_FQDN: + /* XXX will append an mbuf */ + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); + break; + case NI_QTYPE_NODEADDR: + addrs = ni6_addrs(ni6, m, &ifp, subj); + if ((replylen += addrs * (sizeof(struct in6_addr) + + sizeof(u_int32_t))) > MCLBYTES) + replylen = MCLBYTES; /* XXX: will truncate pkt later */ + break; default: - /* should never be here due to "switch (qtype)" above */ - goto bad; + /* + * XXX: We must return a reply with the ICMP6 code + * `unknown Qtype' in this case. However we regard the case + * as an FQDN query for backward compatibility. + * Older versions set a random value to this field, + * so it rarely varies in the defined qtypes. + * But the mechanism is not reliable... + * maybe we should obsolete older versions. + */ + qtype = NI_QTYPE_FQDN; + /* XXX will append an mbuf */ + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); + oldfqdn++; + break; } - /* allocate a mbuf to reply. */ + /* allocate an mbuf to reply. */ MGETHDR(n, M_DONTWAIT, m->m_type); if (n == NULL) { m_freem(m); @@ -1279,10 +1388,10 @@ ni6_input(m, off) M_COPY_PKTHDR(n, m); /* just for recvif */ if (replylen > MHLEN) { if (replylen > MCLBYTES) { - /* - * XXX: should we try to allocate more? But MCLBYTES is - * probably much larger than IPV6_MMTU... - */ + /* + * XXX: should we try to allocate more? But MCLBYTES + * is probably much larger than IPV6_MMTU... + */ goto bad; } MCLGET(n, M_DONTWAIT); @@ -1300,12 +1409,21 @@ ni6_input(m, off) /* qtype dependent procedure */ switch (qtype) { case NI_QTYPE_NOOP: + nni6->ni_code = ICMP6_NI_SUCCESS; nni6->ni_flags = 0; break; case NI_QTYPE_SUPTYPES: - goto bad; /* xxx: to be implemented */ + { + u_int32_t v; + nni6->ni_code = ICMP6_NI_SUCCESS; + nni6->ni_flags = htons(0x0000); /* raw bitmap */ + /* supports NOOP, SUPTYPES, FQDN, and NODEADDR */ + v = (u_int32_t)htonl(0x0000000f); + bcopy(&v, nni6 + 1, sizeof(u_int32_t)); break; + } case NI_QTYPE_FQDN: + nni6->ni_code = ICMP6_NI_SUCCESS; fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo)); @@ -1326,12 +1444,10 @@ ni6_input(m, off) { int lenlim, copied; - if (n->m_flags & M_EXT) - lenlim = MCLBYTES - sizeof(struct ip6_hdr) - - sizeof(struct icmp6_nodeinfo); - else - lenlim = MHLEN - sizeof(struct ip6_hdr) - - sizeof(struct icmp6_nodeinfo); + nni6->ni_code = ICMP6_NI_SUCCESS; + n->m_pkthdr.len = n->m_len = + sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); + lenlim = M_TRAILINGSPACE(n); copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); /* XXX: reset mbuf length */ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + @@ -1343,7 +1459,6 @@ ni6_input(m, off) } nni6->ni_type = ICMP6_NI_REPLY; - nni6->ni_code = ICMP6_NI_SUCESS; m_freem(m); return(n); @@ -1455,6 +1570,7 @@ ni6_nametodns(name, namelen, old) /* * check if two DNS-encoded string matches. takes care of truncated * form (with \0\0 at the end). no compression support. + * XXX upper/lowercase match (see RFC2065) */ static int ni6_dnsmatch(a, alen, b, blen) @@ -1521,16 +1637,34 @@ ni6_dnsmatch(a, alen, b, blen) * calculate the number of addresses to be returned in the node info reply. */ static int -ni6_addrs(ni6, m, ifpp) +ni6_addrs(ni6, m, ifpp, subj) struct icmp6_nodeinfo *ni6; struct mbuf *m; struct ifnet **ifpp; + char *subj; { - register struct ifnet *ifp; - register struct in6_ifaddr *ifa6; - register struct ifaddr *ifa; - struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct ifnet *ifp; + struct in6_ifaddr *ifa6; + struct ifaddr *ifa; + struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */ int addrs = 0, addrsofif, iffound = 0; + int niflags = ni6->ni_flags; + + if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) { + switch (ni6->ni_code) { + case ICMP6_NI_SUBJ_IPV6: + if (subj == NULL) /* must be impossible... */ + return(0); + subj_ip6 = (struct sockaddr_in6 *)subj; + break; + default: + /* + * XXX: we only support IPv6 subject address for + * this Qtype. + */ + return(0); + } + } for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { @@ -1542,8 +1676,8 @@ ni6_addrs(ni6, m, ifpp) continue; ifa6 = (struct in6_ifaddr *)ifa; - if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) && - IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 && + IN6_ARE_ADDR_EQUAL(&subj_ip6->sin6_addr, &ifa6->ia_addr.sin6_addr)) iffound = 1; @@ -1552,36 +1686,41 @@ ni6_addrs(ni6, m, ifpp) * Node Information proxy, since they represent * addresses of IPv4-only nodes, which perforce do * not implement this protocol. - * [icmp-name-lookups-05] + * [icmp-name-lookups-07, Section 5.4] * So we don't support NI_NODEADDR_FLAG_COMPAT in * this function at this moment. */ - if (ifa6->ia6_flags & IN6_IFF_ANYCAST) - continue; /* we need only unicast addresses */ - - if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL | - NI_NODEADDR_FLAG_SITELOCAL | - NI_NODEADDR_FLAG_GLOBAL)) == 0) - continue; - /* What do we have to do about ::1? */ - switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { - case IPV6_ADDR_SCOPE_LINKLOCAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) - addrsofif++; + switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) { + case IPV6_ADDR_SCOPE_LINKLOCAL: + if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) + continue; break; - case IPV6_ADDR_SCOPE_SITELOCAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) - addrsofif++; + case IPV6_ADDR_SCOPE_SITELOCAL: + if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0) + continue; + break; + case IPV6_ADDR_SCOPE_GLOBAL: + if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0) + continue; break; - case IPV6_ADDR_SCOPE_GLOBAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) - addrsofif++; - break; - default: - continue; + default: + continue; + } + + /* + * check if anycast is okay. + * XXX: just experimental. not in the spec. + */ + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 && + (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0) + continue; /* we need only unicast addresses */ + if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + (icmp6_nodeinfo & 4) == 0) { + continue; } + addrsofif++; /* count the address */ } if (iffound) { *ifpp = ifp; @@ -1600,82 +1739,139 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) struct ifnet *ifp0; int resid; { - register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet); - register struct in6_ifaddr *ifa6; - register struct ifaddr *ifa; - int docopy, copied = 0; + struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet); + struct in6_ifaddr *ifa6; + struct ifaddr *ifa; + struct ifnet *ifp_dep = NULL; + int copied = 0, allow_deprecated = 0; u_char *cp = (u_char *)(nni6 + 1); + int niflags = ni6->ni_flags; + u_int32_t ltime; - if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL)) + if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL)) return(0); /* needless to copy */ + again: + for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) { for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { - docopy = 0; - if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifa6 = (struct in6_ifaddr *)ifa; - if (ifa6->ia6_flags & IN6_IFF_ANYCAST) { - /* just experimental. not in the spec. */ - if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) - docopy = 1; - else - continue; - } - else { /* unicast address */ - if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) - continue; - else - docopy = 1; + if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) != 0 && + allow_deprecated == 0) { + /* + * prefererred address should be put before + * deprecated addresses. + */ + + /* record the interface for later search */ + if (ifp_dep == NULL) + ifp_dep = ifp; + + continue; } + else if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) == 0 && + allow_deprecated != 0) + continue; /* we now collect deprecated addrs */ /* What do we have to do about ::1? */ - switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { - case IPV6_ADDR_SCOPE_LINKLOCAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) - docopy = 1; + switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) { + case IPV6_ADDR_SCOPE_LINKLOCAL: + if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) + continue; + break; + case IPV6_ADDR_SCOPE_SITELOCAL: + if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0) + continue; break; - case IPV6_ADDR_SCOPE_SITELOCAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) - docopy = 1; + case IPV6_ADDR_SCOPE_GLOBAL: + if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0) + continue; break; - case IPV6_ADDR_SCOPE_GLOBAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) - docopy = 1; - break; - default: - continue; + default: + continue; } - if (docopy) { - if (resid < sizeof(struct in6_addr)) { - /* - * We give up much more copy. - * Set the truncate flag and return. - */ - nni6->ni_flags |= - NI_NODEADDR_FLAG_TRUNCATE; - return(copied); - } - bcopy(&ifa6->ia_addr.sin6_addr, cp, - sizeof(struct in6_addr)); - /* XXX: KAME link-local hack; remove ifindex */ - if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) - ((struct in6_addr *)cp)->s6_addr16[1] = 0; - cp += sizeof(struct in6_addr); - resid -= sizeof(struct in6_addr); - copied += sizeof(struct in6_addr); + /* + * check if anycast is okay. + * XXX: just experimental. not in the spec. + */ + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 && + (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0) + continue; + if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + (icmp6_nodeinfo & 4) == 0) { + continue; } + + /* now we can copy the address */ + if (resid < sizeof(struct in6_addr) + + sizeof(u_int32_t)) { + /* + * We give up much more copy. + * Set the truncate flag and return. + */ + nni6->ni_flags |= + NI_NODEADDR_FLAG_TRUNCATE; + return(copied); + } + + /* + * Set the TTL of the address. + * The TTL value should be one of the following + * according to the specification: + * + * 1. The remaining lifetime of a DHCP lease on the + * address, or + * 2. The remaining Valid Lifetime of a prefix from + * which the address was derived through Stateless + * Autoconfiguration. + * + * Note that we currently do not support stateful + * address configuration by DHCPv6, so the former + * case can't happen. + */ + if (ifa6->ia6_lifetime.ia6t_expire == 0) + ltime = ND6_INFINITE_LIFETIME; + else { + if (ifa6->ia6_lifetime.ia6t_expire > + time_second) + ltime = htonl(ifa6->ia6_lifetime.ia6t_expire - time_second); + else + ltime = 0; + } + + bcopy(<ime, cp, sizeof(u_int32_t)); + cp += sizeof(u_int32_t); + + /* copy the address itself */ + bcopy(&ifa6->ia_addr.sin6_addr, cp, + sizeof(struct in6_addr)); + /* XXX: KAME link-local hack; remove ifindex */ + if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) + ((struct in6_addr *)cp)->s6_addr16[1] = 0; + cp += sizeof(struct in6_addr); + + resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t)); + copied += (sizeof(struct in6_addr) + + sizeof(u_int32_t)); } if (ifp0) /* we need search only on the specified IF */ break; } + if (allow_deprecated == 0 && ifp_dep != NULL) { + ifp = ifp_dep; + allow_deprecated = 1; + + goto again; + } + return(copied); } @@ -1688,8 +1884,8 @@ icmp6_rip6_input(mp, off) int off; { struct mbuf *m = *mp; - register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - register struct in6pcb *in6p; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct in6pcb *in6p; struct in6pcb *last = NULL; struct sockaddr_in6 rip6src; struct icmp6_hdr *icmp6; @@ -1714,8 +1910,12 @@ icmp6_rip6_input(mp, off) LIST_FOREACH(in6p, &ripcb, inp_list) { - if ((in6p->inp_vflag & INP_IPV6) == NULL) + if ((in6p->inp_vflag & INP_IPV6) == 0) + continue; +#ifdef HAVE_NRL_INPCB + if (!(in6p->in6p_flags & INP_IPV6)) continue; +#endif if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && @@ -1740,8 +1940,9 @@ icmp6_rip6_input(mp, off) n, opts) == 0) { /* should notify about lost packet */ m_freem(n); - if (opts) + if (opts) { m_freem(opts); + } } else sorwakeup(last->in6p_socket); opts = NULL; @@ -1755,7 +1956,7 @@ icmp6_rip6_input(mp, off) /* strip intermediate headers */ m_adj(m, off); if (sbappendaddr(&last->in6p_socket->so_rcv, - (struct sockaddr *)&rip6src, m, opts) == 0) { + (struct sockaddr *)&rip6src, m, opts) == 0) { m_freem(m); if (opts) m_freem(opts); @@ -1784,6 +1985,7 @@ icmp6_reflect(m, off) int plen; int type, code; struct ifnet *outif = NULL; + struct sockaddr_in6 sa6_src, sa6_dst; #ifdef COMPAT_RFC1885 int mtu = IPV6_MMTU; struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; @@ -1791,9 +1993,10 @@ icmp6_reflect(m, off) /* too short to reflect */ if (off < sizeof(struct ip6_hdr)) { - printf("sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", - (u_long)off, (u_long)sizeof(struct ip6_hdr), - __FILE__, __LINE__); + nd6log((LOG_DEBUG, + "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", + (u_long)off, (u_long)sizeof(struct ip6_hdr), + __FILE__, __LINE__)); goto bad; } @@ -1840,12 +2043,24 @@ icmp6_reflect(m, off) */ ip6->ip6_dst = ip6->ip6_src; - /* XXX hack for link-local addresses */ - if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] = - htons(m->m_pkthdr.rcvif->if_index); - if (IN6_IS_ADDR_LINKLOCAL(&t)) - t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + /* + * XXX: make sure to embed scope zone information, using + * already embedded IDs or the received interface (if any). + * Note that rcvif may be NULL. + * TODO: scoped routing case (XXX). + */ + bzero(&sa6_src, sizeof(sa6_src)); + sa6_src.sin6_family = AF_INET6; + sa6_src.sin6_len = sizeof(sa6_src); + sa6_src.sin6_addr = ip6->ip6_dst; + in6_recoverscope(&sa6_src, &ip6->ip6_dst, m->m_pkthdr.rcvif); + in6_embedscope(&ip6->ip6_dst, &sa6_src, NULL, NULL); + bzero(&sa6_dst, sizeof(sa6_dst)); + sa6_dst.sin6_family = AF_INET6; + sa6_dst.sin6_len = sizeof(sa6_dst); + sa6_dst.sin6_addr = t; + in6_recoverscope(&sa6_dst, &t, m->m_pkthdr.rcvif); + in6_embedscope(&t, &sa6_dst, NULL, NULL); #ifdef COMPAT_RFC1885 /* @@ -1902,19 +2117,27 @@ icmp6_reflect(m, off) src = &t; } - if (src == 0) + if (src == 0) { + int e; + struct route_in6 ro; + /* * This case matches to multicasts, our anycast, or unicasts - * that we do not own. Select a source address which has the - * same scope. - * XXX: for (non link-local) multicast addresses, this might - * not be a good choice. + * that we do not own. Select a source address based on the + * source address of the erroneous packet. */ - if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0) - src = &IA6_SIN6(ia)->sin6_addr; - - if (src == 0) - goto bad; + bzero(&ro, sizeof(ro)); + src = in6_selectsrc(&sa6_src, NULL, NULL, &ro, NULL, &e); + if (ro.ro_rt) + RTFREE(ro.ro_rt); /* XXX: we could use this */ + if (src == NULL) { + nd6log((LOG_DEBUG, + "icmp6_reflect: source can't be determined: " + "dst=%s, error=%d\n", + ip6_sprintf(&sa6_src.sin6_addr), e)); + goto bad; + } + } ip6->ip6_src = *src; @@ -1925,20 +2148,21 @@ icmp6_reflect(m, off) if (m->m_pkthdr.rcvif) { /* XXX: This may not be the outgoing interface */ ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim; - } + } else + ip6->ip6_hlim = ip6_defhlim; icmp6->icmp6_cksum = 0; icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), plen); /* - * xxx option handling + * XXX option handling */ m->m_flags &= ~(M_BCAST|M_MCAST); #ifdef IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif /*IPSEC*/ #ifdef COMPAT_RFC1885 @@ -1961,9 +2185,6 @@ icmp6_fasttimo() { mld6_fasttimeo(); - - /* reset ICMPv6 pps limit */ - icmp6errpps_count = 0; } static const char * @@ -1980,7 +2201,7 @@ icmp6_redirect_diag(src6, dst6, tgt6) void icmp6_redirect_input(m, off) - register struct mbuf *m; + struct mbuf *m; int off; { struct ifnet *ifp = m->m_pkthdr.rcvif; @@ -2028,17 +2249,17 @@ icmp6_redirect_input(m, off) /* validation */ if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect sent from %s rejected; " - "must be from linklocal\n", ip6_sprintf(&src6)); - goto freeit; + "must be from linklocal\n", ip6_sprintf(&src6))); + goto bad; } if (ip6->ip6_hlim != 255) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect sent from %s rejected; " "hlim=%d (must be 255)\n", - ip6_sprintf(&src6), ip6->ip6_hlim); - goto freeit; + ip6_sprintf(&src6), ip6->ip6_hlim)); + goto bad; } { /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ @@ -2053,41 +2274,41 @@ icmp6_redirect_input(m, off) if (rt) { if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; no route " "with inet6 gateway found for redirect dst: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); RTFREE(rt); - goto freeit; + goto bad; } gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; " "not equal to gw-for-src=%s (must be same): " "%s\n", ip6_sprintf(gw6), - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); RTFREE(rt); - goto freeit; + goto bad; } } else { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; " "no route found for redirect dst: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - goto freeit; + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + goto bad; } RTFREE(rt); rt = NULL; } if (IN6_IS_ADDR_MULTICAST(&reddst6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; " "redirect dst must be unicast: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - goto freeit; + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + goto bad; } is_router = is_onlink = 0; @@ -2096,20 +2317,21 @@ icmp6_redirect_input(m, off) if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) is_onlink = 1; /* on-link destination case */ if (!is_router && !is_onlink) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; " "neither router case nor onlink case: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - goto freeit; + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + goto bad; } /* validation passed */ icmp6len -= sizeof(*nd_rd); nd6_option_init(nd_rd + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "icmp6_redirect_input: " + nd6log((LOG_INFO, "icmp6_redirect_input: " "invalid ND option, rejected: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + /* nd6_options have incremented stats */ goto freeit; } @@ -2124,11 +2346,12 @@ icmp6_redirect_input(m, off) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "icmp6_redirect_input: lladdrlen mismatch for %s " "(if %d, icmp6 packet %d): %s\n", ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2, - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + goto bad; } /* RFC 2461 8.3 */ @@ -2171,6 +2394,11 @@ icmp6_redirect_input(m, off) freeit: m_freem(m); + return; + + bad: + icmp6stat.icp6s_badredirect++; + m_freem(m); } void @@ -2235,7 +2463,9 @@ icmp6_redirect_output(m0, rt) MCLGET(m, M_DONTWAIT); if (!m) goto fail; - maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; + m->m_pkthdr.rcvif = NULL; + m->m_len = 0; + maxlen = M_TRAILINGSPACE(m); maxlen = min(IPV6_MMTU, maxlen); /* just for safety */ if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + @@ -2440,7 +2670,7 @@ noredhdropt:; /* send the packet to outside... */ #ifdef IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif /*IPSEC*/ ip6_output(m, NULL, NULL, 0, NULL, &outif); if (outif) { @@ -2458,11 +2688,13 @@ fail: m_freem(m0); } +#ifdef HAVE_NRL_INPCB +#define sotoin6pcb sotoinpcb +#define in6pcb inpcb +#define in6p_icmp6filt inp_icmp6filt +#endif /* * ICMPv6 socket option processing. - * - * NOTE: for OSes that use NRL inpcb (bsdi4/openbsd), do not forget to modify - * sys/netinet6/raw_ipv6.c:rip6_ctloutput(). */ int icmp6_ctloutput(so, sopt) @@ -2471,7 +2703,7 @@ icmp6_ctloutput(so, sopt) { int error = 0; int optlen; - register struct inpcb *inp = sotoinpcb(so); + struct inpcb *inp = sotoinpcb(so); int level, op, optname; if (sopt) { @@ -2481,11 +2713,12 @@ icmp6_ctloutput(so, sopt) optlen = sopt->sopt_valsize; } else level = op = optname = optlen = 0; + if (level != IPPROTO_ICMPV6) { return EINVAL; } - switch(op) { + switch (op) { case PRCO_SETOPT: switch (optname) { case ICMP6_FILTER: @@ -2533,42 +2766,79 @@ icmp6_ctloutput(so, sopt) return(error); } +#ifdef HAVE_NRL_INPCB +#undef sotoin6pcb +#undef in6pcb +#undef in6p_icmp6filt +#endif + +#ifndef HAVE_PPSRATECHECK +#ifndef timersub +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif -#ifndef HAVE_RATECHECK /* - * ratecheck() returns true if it is okay to send. We return - * true if it is not okay to send. + * ppsratecheck(): packets (or events) per second limitation. */ static int -ratecheck(last, limit) - struct timeval *last; - struct timeval *limit; +ppsratecheck(lasttime, curpps, maxpps) + struct timeval *lasttime; + int *curpps; + int maxpps; /* maximum pps allowed */ { - struct timeval tp; - struct timeval nextsend; + struct timeval tv, delta; + int s, rv; - microtime(&tp); - tp.tv_sec = time_second; + s = splclock(); + microtime(&tv); + splx(s); - /* rate limit */ - if (last->tv_sec != 0 || last->tv_usec != 0) { - nextsend.tv_sec = last->tv_sec + limit->tv_sec; - nextsend.tv_usec = last->tv_usec + limit->tv_usec; - nextsend.tv_sec += (nextsend.tv_usec / 1000000); - nextsend.tv_usec %= 1000000; - - if (nextsend.tv_sec == tp.tv_sec && nextsend.tv_usec <= tp.tv_usec) - ; - else if (nextsend.tv_sec <= tp.tv_sec) - ; - else { - /* The packet is subject to rate limit */ - return 0; - } - } + timersub(&tv, lasttime, &delta); + + /* + * check for 0,0 is so that the message will be seen at least once. + * if more than one second have passed since the last update of + * lasttime, reset the counter. + * + * we do increment *curpps even in *curpps < maxpps case, as some may + * try to use *curpps for stat purposes as well. + */ + if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || + delta.tv_sec >= 1) { + *lasttime = tv; + *curpps = 0; + rv = 1; + } else if (maxpps < 0) + rv = 1; + else if (*curpps < maxpps) + rv = 1; + else + rv = 0; + +#if 1 /*DIAGNOSTIC?*/ + /* be careful about wrap-around */ + if (*curpps + 1 > *curpps) + *curpps = *curpps + 1; +#else + /* + * assume that there's not too many calls to this function. + * not sure if the assumption holds, as it depends on *caller's* + * behavior, not the behavior of this function. + * IMHO it is wrong to make assumption on the caller's behavior, + * so the above #if is #if 1, not #ifdef DIAGNOSTIC. + */ + *curpps = *curpps + 1; +#endif - *last = tp; - return 1; + return (rv); } #endif @@ -2578,14 +2848,6 @@ ratecheck(last, limit) * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate * limitation. * - * There are two limitations defined: - * - pps limit: ICMPv6 error packet cannot exceed defined packet-per-second. - * we measure it every 0.2 second, since fasttimo works every 0.2 second. - * - rate limit: ICMPv6 error packet cannot appear more than once per - * defined interval. - * In any case, if we perform rate limitation, we'll see jitter in the ICMPv6 - * error packets. - * * XXX per-destination/type check necessary? */ static int @@ -2599,13 +2861,8 @@ icmp6_ratelimit(dst, type, code) ret = 0; /*okay to send*/ /* PPS limit */ - icmp6errpps_count++; - if (icmp6errppslim && icmp6errpps_count > icmp6errppslim / 5) { - /* The packet is subject to pps limit */ - ret++; - } - - if (!ratecheck(&icmp6errratelim_last, &icmp6errratelim)) { + if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count, + icmp6errppslim)) { /* The packet is subject to rate limit */ ret++; } diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 5f51cef11179..1fd566b42106 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6.c,v 1.99 2000/07/11 17:00:58 jinmei Exp $ */ +/* $KAME: in6.c,v 1.187 2001/05/24 07:43:59 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -88,6 +88,11 @@ #include <netinet/in.h> #include <netinet/in_var.h> #include <netinet/if_ether.h> +#ifndef SCOPEDROUTING +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/in_pcb.h> +#endif #include <netinet6/nd6.h> #include <netinet/ip6.h> @@ -96,6 +101,9 @@ #include <netinet6/ip6_mroute.h> #include <netinet6/in6_ifattach.h> #include <netinet6/scope6_var.h> +#ifndef SCOPEDROUTING +#include <netinet6/in6_pcb.h> +#endif #include "gif.h" #if NGIF > 0 @@ -124,103 +132,105 @@ const struct in6_addr in6mask64 = IN6MASK64; const struct in6_addr in6mask96 = IN6MASK96; const struct in6_addr in6mask128 = IN6MASK128; +const struct sockaddr_in6 sa6_any = {sizeof(sa6_any), AF_INET6, + 0, 0, IN6ADDR_ANY_INIT, 0}; + static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *, struct proc *)); +static int in6_ifinit __P((struct ifnet *, struct in6_ifaddr *, + struct sockaddr_in6 *, int)); +static void in6_unlink_ifa __P((struct in6_ifaddr *, struct ifnet *)); struct in6_multihead in6_multihead; /* XXX BSS initialization */ /* - * Check if the loopback entry will be automatically generated. - * if 0 returned, will not be automatically generated. - * if 1 returned, will be automatically generated. - */ -static int -in6_is_ifloop_auto(struct ifaddr *ifa) -{ -#define SIN6(s) ((struct sockaddr_in6 *)s) - /* - * If RTF_CLONING is unset, or (IFF_LOOPBACK | IFF_POINTOPOINT), - * or netmask is all0 or all1, then cloning will not happen, - * then we can't rely on its loopback entry generation. - */ - if ((ifa->ifa_flags & RTF_CLONING) == 0 || - (ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) || - (SIN6(ifa->ifa_netmask)->sin6_len == sizeof(struct sockaddr_in6) - && - IN6_ARE_ADDR_EQUAL(&SIN6(ifa->ifa_netmask)->sin6_addr, - &in6mask128)) || - ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_len == 0) - return 0; - else - return 1; -#undef SIN6 -} - -/* * Subroutine for in6_ifaddloop() and in6_ifremloop(). * This routine does actual work. */ static void in6_ifloop_request(int cmd, struct ifaddr *ifa) { - struct sockaddr_in6 lo_sa; struct sockaddr_in6 all1_sa; - struct rtentry *nrt = NULL, **nrtp = NULL; + struct rtentry *nrt = NULL; + int e; - bzero(&lo_sa, sizeof(lo_sa)); bzero(&all1_sa, sizeof(all1_sa)); - lo_sa.sin6_family = AF_INET6; - lo_sa.sin6_len = sizeof(struct sockaddr_in6); - all1_sa = lo_sa; - lo_sa.sin6_addr = in6addr_loopback; + all1_sa.sin6_family = AF_INET6; + all1_sa.sin6_len = sizeof(struct sockaddr_in6); all1_sa.sin6_addr = in6mask128; - + /* - * So we add or remove static loopback entry, here. - * This request for deletion could fail, e.g. when we remove - * an address right after adding it. + * We specify the address itself as the gateway, and set the + * RTF_LLINFO flag, so that the corresponding host route would have + * the flag, and thus applications that assume traditional behavior + * would be happy. Note that we assume the caller of the function + * (probably implicitly) set nd6_rtrequest() to ifa->ifa_rtrequest, + * which changes the outgoing interface to the loopback interface. */ - if (cmd == RTM_ADD) - nrtp = &nrt; - rtrequest(cmd, ifa->ifa_addr, - (struct sockaddr *)&lo_sa, - (struct sockaddr *)&all1_sa, - RTF_UP|RTF_HOST, nrtp); + e = rtrequest(cmd, ifa->ifa_addr, ifa->ifa_addr, + (struct sockaddr *)&all1_sa, + RTF_UP|RTF_HOST|RTF_LLINFO, &nrt); + if (e != 0) { + log(LOG_ERR, "in6_ifloop_request: " + "%s operation failed for %s (errno=%d)\n", + cmd == RTM_ADD ? "ADD" : "DELETE", + ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr), + e); + } /* * Make sure rt_ifa be equal to IFA, the second argument of the * function. - * We need this because when we refer rt_ifa->ia6_flags in ip6_input, - * we assume that the rt_ifa points to the address instead of the - * loopback address. + * We need this because when we refer to rt_ifa->ia6_flags in + * ip6_input, we assume that the rt_ifa points to the address instead + * of the loopback address. */ if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) { IFAFREE(nrt->rt_ifa); - ifa->ifa_refcnt++; + IFAREF(ifa); nrt->rt_ifa = ifa; } - if (nrt) - nrt->rt_refcnt--; + + /* + * Report the addition/removal of the address to the routing socket. + * XXX: since we called rtinit for a p2p interface with a destination, + * we end up reporting twice in such a case. Should we rather + * omit the second report? + */ + if (nrt) { + rt_newaddrmsg(cmd, ifa, e, nrt); + if (cmd == RTM_DELETE) { + if (nrt->rt_refcnt <= 0) { + /* XXX: we should free the entry ourselves. */ + nrt->rt_refcnt++; + rtfree(nrt); + } + } else { + /* the cmd must be RTM_ADD here */ + nrt->rt_refcnt--; + } + } } /* - * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). - * Because, KAME needs loopback rtentry for ownaddr check in - * ip6_input(). + * Add ownaddr as loopback rtentry. We previously add the route only if + * necessary (ex. on a p2p link). However, since we now manage addresses + * separately from prefixes, we should always add the route. We can't + * rely on the cloning mechanism from the corresponding interface route + * any more. */ static void in6_ifaddloop(struct ifaddr *ifa) { - if (!in6_is_ifloop_auto(ifa)) { - struct rtentry *rt; - - /* If there is no loopback entry, allocate one. */ - rt = rtalloc1(ifa->ifa_addr, 0, 0); - if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) - in6_ifloop_request(RTM_ADD, ifa); - if (rt) - rt->rt_refcnt--; - } + struct rtentry *rt; + + /* If there is no loopback entry, allocate one. */ + rt = rtalloc1(ifa->ifa_addr, 0, 0); + if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 || + (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) + in6_ifloop_request(RTM_ADD, ifa); + if (rt) + rt->rt_refcnt--; } /* @@ -231,28 +241,48 @@ static void in6_ifremloop(struct ifaddr *ifa) { struct in6_ifaddr *ia; + struct rtentry *rt; int ia_count = 0; /* - * All BSD variants except BSD/OS do not remove cloned routes + * Some of BSD variants do not remove cloned routes * from an interface direct route, when removing the direct route - * (see commens in net/net_osdep.h). - * So we should remove the route corresponding to the deleted address + * (see comments in net/net_osdep.h). Even for variants that do remove + * cloned routes, they could fail to remove the cloned routes when + * we handle multple addresses that share a common prefix. + * So, we should remove the route corresponding to the deleted address * regardless of the result of in6_is_ifloop_auto(). */ - if (1) - { - /* If only one ifa for the loopback entry, delete it. */ - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), - &ia->ia_addr.sin6_addr)) { - ia_count++; - if (ia_count > 1) - break; - } + + /* + * Delete the entry only if exact one ifa exists. More than one ifa + * can exist if we assign a same single address to multiple + * (probably p2p) interfaces. + * XXX: we should avoid such a configuration in IPv6... + */ + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) { + ia_count++; + if (ia_count > 1) + break; } - if (ia_count == 1) + } + + if (ia_count == 1) { + /* + * Before deleting, check if a corresponding loopbacked host + * route surely exists. With this check, we can avoid to + * delete an interface direct route whose destination is same + * as the address being removed. This can happen when remofing + * a subnet-router anycast address on an interface attahced + * to a shared medium. + */ + rt = rtalloc1(ifa->ifa_addr, 0, 0); + if (rt != NULL && (rt->rt_flags & RTF_HOST) != 0 && + (rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { + rt->rt_refcnt--; in6_ifloop_request(RTM_DELETE, ifa); + } } } @@ -281,22 +311,40 @@ in6_ifindex2scopeid(idx) } int -in6_mask2len(mask) +in6_mask2len(mask, lim0) struct in6_addr *mask; + u_char *lim0; { - int x, y; - - for (x = 0; x < sizeof(*mask); x++) { - if (mask->s6_addr8[x] != 0xff) + int x = 0, y; + u_char *lim = lim0, *p; + + if (lim0 == NULL || + lim0 - (u_char *)mask > sizeof(*mask)) /* ignore the scope_id part */ + lim = (u_char *)mask + sizeof(*mask); + for (p = (u_char *)mask; p < lim; x++, p++) { + if (*p != 0xff) break; } y = 0; - if (x < sizeof(*mask)) { + if (p < lim) { for (y = 0; y < 8; y++) { - if ((mask->s6_addr8[x] & (0x80 >> y)) == 0) + if ((*p & (0x80 >> y)) == 0) break; } } + + /* + * when the limit pointer is given, do a stricter check on the + * remaining bits. + */ + if (p < lim) { + if (y != 0 && (*p & (0x00ff >> y)) != 0) + return(-1); + for (p = p + 1; p < lim; p++) + if (*p != 0) + return(-1); + } + return x * 8 + y; } @@ -326,36 +374,14 @@ in6_control(so, cmd, data, ifp, p) struct proc *p; { struct in6_ifreq *ifr = (struct in6_ifreq *)data; - struct in6_ifaddr *ia = NULL, *oia; + struct in6_ifaddr *ia = NULL; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; - struct sockaddr_in6 oldaddr; -#ifdef COMPAT_IN6IFIOCTL - struct sockaddr_in6 net; -#endif - int error = 0, hostIsNew, prefixIsNew; - int newifaddr; int privileged; privileged = 0; if (p == NULL || !suser(p)) privileged++; - /* - * xxx should prevent processes for link-local addresses? - */ -#if NGIF > 0 - if (ifp && ifp->if_type == IFT_GIF) { - switch (cmd) { - case SIOCSIFPHYADDR_IN6: - if (!privileged) - return(EPERM); - /*fall through*/ - case SIOCGIFPSRCADDR_IN6: - case SIOCGIFPDSTADDR_IN6: - return gif_ioctl(ifp, cmd, data); - } - } -#endif switch (cmd) { case SIOCGETSGCNT_IN6: case SIOCGETMIFCNT_IN6: @@ -374,6 +400,7 @@ in6_control(so, cmd, data, ifp, p) if (!privileged) return(EPERM); /*fall through*/ + case OSIOCGIFINFO_IN6: case SIOCGIFINFO_IN6: case SIOCGDRLST_IN6: case SIOCGPRLST_IN6: @@ -388,13 +415,11 @@ in6_control(so, cmd, data, ifp, p) case SIOCAIFPREFIX_IN6: case SIOCCIFPREFIX_IN6: case SIOCSGIFPREFIX_IN6: - if (!privileged) - return(EPERM); - /*fall through*/ case SIOCGIFPREFIX_IN6: - if (ip6_forwarding == 0) - return(EPERM); - return(in6_prefix_ioctl(so, cmd, data, ifp)); + log(LOG_NOTICE, + "prefix ioctls are now invalidated. " + "please use ifconfig.\n"); + return(EOPNOTSUPP); } switch(cmd) { @@ -430,12 +455,12 @@ in6_control(so, cmd, data, ifp, p) if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { if (sa6->sin6_addr.s6_addr16[1] == 0) { - /* interface ID is not embedded by the user */ + /* link ID is not embedded by the user */ sa6->sin6_addr.s6_addr16[1] = htons(ifp->if_index); } else if (sa6->sin6_addr.s6_addr16[1] != htons(ifp->if_index)) { - return(EINVAL); /* ifid is contradict */ + return(EINVAL); /* link ID contradicts */ } if (sa6->sin6_scope_id) { if (sa6->sin6_scope_id != @@ -448,92 +473,39 @@ in6_control(so, cmd, data, ifp, p) } switch (cmd) { + case SIOCSIFADDR_IN6: + case SIOCSIFDSTADDR_IN6: + case SIOCSIFNETMASK_IN6: + /* + * Since IPv6 allows a node to assign multiple addresses + * on a single interface, SIOCSIFxxx ioctls are not suitable + * and should be unused. + */ + /* we decided to obsolete this command (20000704) */ + return(EINVAL); case SIOCDIFADDR_IN6: /* - * for IPv4, we look for existing in6_ifaddr here to allow + * for IPv4, we look for existing in_ifaddr here to allow * "ifconfig if0 delete" to remove first IPv4 address on the * interface. For IPv6, as the spec allow multiple interface * address from the day one, we consider "remove the first one" - * semantics to be not preferrable. + * semantics to be not preferable. */ if (ia == NULL) return(EADDRNOTAVAIL); /* FALLTHROUGH */ case SIOCAIFADDR_IN6: - case SIOCSIFADDR_IN6: -#ifdef COMPAT_IN6IFIOCTL - case SIOCSIFDSTADDR_IN6: - case SIOCSIFNETMASK_IN6: /* - * Since IPv6 allows a node to assign multiple addresses - * on a single interface, SIOCSIFxxx ioctls are not suitable - * and should be unused. + * We always require users to specify a valid IPv6 address for + * the corresponding operation. */ -#endif - if (ifra->ifra_addr.sin6_family != AF_INET6) + if (ifra->ifra_addr.sin6_family != AF_INET6 || + ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) return(EAFNOSUPPORT); if (!privileged) return(EPERM); - if (ia == NULL) { - ia = (struct in6_ifaddr *) - malloc(sizeof(*ia), M_IFADDR, M_WAITOK); - if (ia == NULL) - return (ENOBUFS); - bzero((caddr_t)ia, sizeof(*ia)); - /* Initialize the address and masks */ - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_len = sizeof(ia->ia_addr); -#if 1 - if (ifp->if_flags & IFF_POINTOPOINT) { - ia->ia_ifa.ifa_dstaddr - = (struct sockaddr *)&ia->ia_dstaddr; - ia->ia_dstaddr.sin6_family = AF_INET6; - ia->ia_dstaddr.sin6_len = sizeof(ia->ia_dstaddr); - } else { - ia->ia_ifa.ifa_dstaddr = NULL; - bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); - } -#else /* always initilize by NULL */ - ia->ia_ifa.ifa_dstaddr = NULL; - bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); -#endif - ia->ia_ifa.ifa_netmask - = (struct sockaddr *)&ia->ia_prefixmask; - - ia->ia_ifp = ifp; - if ((oia = in6_ifaddr) != NULL) { - for ( ; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else - in6_ifaddr = ia; - /* gain a refcnt for the link from in6_ifaddr */ - ia->ia_ifa.ifa_refcnt++; - TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, - ifa_list); - /* gain another refcnt for the link from if_addrlist */ - ia->ia_ifa.ifa_refcnt++; - - newifaddr = 1; - } else - newifaddr = 0; - - if (cmd == SIOCAIFADDR_IN6) { - /* sanity for overflow - beware unsigned */ - struct in6_addrlifetime *lt; - lt = &ifra->ifra_lifetime; - if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME - && lt->ia6t_vltime + time_second < time_second) { - return EINVAL; - } - if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME - && lt->ia6t_pltime + time_second < time_second) { - return EINVAL; - } - } break; case SIOCGIFADDR_IN6: @@ -618,42 +590,6 @@ in6_control(so, cmd, data, ifp, p) *icmp6_ifstat[ifp->if_index]; break; -#ifdef COMPAT_IN6IFIOCTL /* should be unused */ - case SIOCSIFDSTADDR_IN6: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) - return(EINVAL); - oldaddr = ia->ia_dstaddr; - ia->ia_dstaddr = ifr->ifr_dstaddr; - - /* link-local index check */ - if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { - /* interface ID is not embedded by the user */ - ia->ia_dstaddr.sin6_addr.s6_addr16[1] - = htons(ifp->if_index); - } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != - htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid is contradict */ - } - } - - if (ifp->if_ioctl && (error = (ifp->if_ioctl) - (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) { - ia->ia_dstaddr = oldaddr; - return(error); - } - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - if (ia->ia_flags & IFA_ROUTE) { - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); - ia->ia_ifa.ifa_dstaddr = - (struct sockaddr *)&ia->ia_dstaddr; - rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); - } - break; - -#endif case SIOCGIFALIFETIME_IN6: ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; break; @@ -673,129 +609,400 @@ in6_control(so, cmd, data, ifp, p) ia->ia6_lifetime.ia6t_preferred = 0; break; - case SIOCSIFADDR_IN6: - error = in6_ifinit(ifp, ia, &ifr->ifr_addr, 1); -#if 0 + case SIOCAIFADDR_IN6: + { + int i, error = 0; + struct nd_prefix pr0, *pr; + + /* + * first, make or update the interface address structure, + * and link it to the list. + */ + if ((error = in6_update_ifa(ifp, ifra, ia)) != 0) + return(error); + /* - * the code chokes if we are to assign multiple addresses with - * the same address prefix (rtinit() will return EEXIST, which - * is not fatal actually). we will get memory leak if we - * don't do it. - * -> we may want to hide EEXIST from rtinit(). + * then, make the prefix on-link on the interface. + * XXX: we'd rather create the prefix before the address, but + * we need at least one address to install the corresponding + * interface route, so we configure the address first. */ - undo: - if (error && newifaddr) { - TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); - /* release a refcnt for the link from if_addrlist */ - IFAFREE(&ia->ia_ifa); - - oia = ia; - if (oia == (ia = in6_ifaddr)) - in6_ifaddr = ia->ia_next; - else { - while (ia->ia_next && (ia->ia_next != oia)) - ia = ia->ia_next; - if (ia->ia_next) - ia->ia_next = oia->ia_next; - else { - printf("Didn't unlink in6_ifaddr " - "from list\n"); + + /* + * convert mask to prefix length (prefixmask has already + * been validated in in6_update_ifa(). + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + NULL); + if (pr0.ndpr_plen == 128) + break; /* we don't need to install a host route. */ + pr0.ndpr_prefix = ifra->ifra_addr; + pr0.ndpr_mask = ifra->ifra_prefixmask.sin6_addr; + /* apply the mask for safety. */ + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + ifra->ifra_prefixmask.sin6_addr.s6_addr32[i]; + } + /* + * XXX: since we don't have enough APIs, we just set inifinity + * to lifetimes. They can be overridden by later advertised + * RAs (when accept_rtadv is non 0), but we'd rather intend + * such a behavior. + */ + pr0.ndpr_raf_onlink = 1; /* should be configurable? */ + pr0.ndpr_raf_auto = + ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); + pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; + pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; + + /* add the prefix if there's one. */ + if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { + /* + * nd6_prelist_add will install the corresponding + * interface route. + */ + if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) + return(error); + if (pr == NULL) { + log(LOG_ERR, "nd6_prelist_add succedded but " + "no prefix\n"); + return(EINVAL); /* XXX panic here? */ + } + } + if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) + == NULL) { + /* XXX: this should not happen! */ + log(LOG_ERR, "in6_control: addition succeeded, but" + " no ifaddr\n"); + } else { + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0 && + ia->ia6_ndpr == NULL) { /* new autoconfed addr */ + ia->ia6_ndpr = pr; + pr->ndpr_refcnt++; + + /* + * If this is the first autoconf address from + * the prefix, create a temporary address + * as well (when specified). + */ + if (ip6_use_tempaddr && + pr->ndpr_refcnt == 1) { + int e; + if ((e = in6_tmpifadd(ia, 1)) != 0) { + log(LOG_NOTICE, "in6_control: " + "failed to create a " + "temporary address, " + "errno=%d\n", + e); + } } } - /* release another refcnt for the link from in6_ifaddr */ - IFAFREE(&oia->ia_ifa); + + /* + * this might affect the status of autoconfigured + * addresses, that is, this address might make + * other addresses detached. + */ + pfxlist_onlink_check(); } -#endif - return error; + break; + } -#ifdef COMPAT_IN6IFIOCTL /* XXX should be unused */ - case SIOCSIFNETMASK_IN6: - ia->ia_prefixmask = ifr->ifr_addr; - bzero(&net, sizeof(net)); - net.sin6_len = sizeof(struct sockaddr_in6); - net.sin6_family = AF_INET6; - net.sin6_port = htons(0); - net.sin6_flowinfo = htonl(0); - net.sin6_addr.s6_addr32[0] - = ia->ia_addr.sin6_addr.s6_addr32[0] & - ia->ia_prefixmask.sin6_addr.s6_addr32[0]; - net.sin6_addr.s6_addr32[1] - = ia->ia_addr.sin6_addr.s6_addr32[1] & - ia->ia_prefixmask.sin6_addr.s6_addr32[1]; - net.sin6_addr.s6_addr32[2] - = ia->ia_addr.sin6_addr.s6_addr32[2] & - ia->ia_prefixmask.sin6_addr.s6_addr32[2]; - net.sin6_addr.s6_addr32[3] - = ia->ia_addr.sin6_addr.s6_addr32[3] & - ia->ia_prefixmask.sin6_addr.s6_addr32[3]; - ia->ia_net = net; + case SIOCDIFADDR_IN6: + { + int i = 0; + struct nd_prefix pr0, *pr; + + /* + * If the address being deleted is the only one that owns + * the corresponding prefix, expire the prefix as well. + * XXX: theoretically, we don't have to warry about such + * relationship, since we separate the address management + * and the prefix management. We do this, however, to provide + * as much backward compatibility as possible in terms of + * the ioctl operation. + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + pr0.ndpr_plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, + NULL); + if (pr0.ndpr_plen == 128) + goto purgeaddr; + pr0.ndpr_prefix = ia->ia_addr; + pr0.ndpr_mask = ia->ia_prefixmask.sin6_addr; + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + ia->ia_prefixmask.sin6_addr.s6_addr32[i]; + } + /* + * The logic of the following condition is a bit complicated. + * We expire the prefix when + * 1. the address obeys autoconfiguration and it is the + * only owner of the associated prefix, or + * 2. the address does not obey autoconf and there is no + * other owner of the prefix. + */ + if ((pr = nd6_prefix_lookup(&pr0)) != NULL && + (((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0 && + pr->ndpr_refcnt == 1) || + ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0 && + pr->ndpr_refcnt == 0))) { + pr->ndpr_expire = 1; /* XXX: just for expiration */ + } + + purgeaddr: + in6_purgeaddr(&ia->ia_ifa); break; -#endif + } - case SIOCAIFADDR_IN6: - prefixIsNew = 0; - hostIsNew = 1; + default: + if (ifp == NULL || ifp->if_ioctl == 0) + return(EOPNOTSUPP); + return((*ifp->if_ioctl)(ifp, cmd, data)); + } + + return(0); +} + +/* + * Update parameters of an IPv6 interface address. + * If necessary, a new entry is created and linked into address chains. + * This function is separated from in6_control(). + * XXX: should this be performed under splnet()? + */ +int +in6_update_ifa(ifp, ifra, ia) + struct ifnet *ifp; + struct in6_aliasreq *ifra; + struct in6_ifaddr *ia; +{ + int error = 0, hostIsNew = 0, plen = -1; + struct in6_ifaddr *oia; + struct sockaddr_in6 dst6; + struct in6_addrlifetime *lt; - if (ifra->ifra_addr.sin6_len == 0) { - ifra->ifra_addr = ia->ia_addr; - hostIsNew = 0; - } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, - &ia->ia_addr.sin6_addr)) - hostIsNew = 0; + /* Validate parameters */ + if (ifp == NULL || ifra == NULL) /* this maybe redundant */ + return(EINVAL); - /* Validate address families: */ + /* + * The destination address for a p2p link must have a family + * of AF_UNSPEC or AF_INET6. + */ + if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && + ifra->ifra_dstaddr.sin6_family != AF_INET6 && + ifra->ifra_dstaddr.sin6_family != AF_UNSPEC) + return(EAFNOSUPPORT); + /* + * validate ifra_prefixmask. don't check sin6_family, netmask + * does not carry fields other than sin6_len. + */ + if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6)) + return(EINVAL); + /* + * Because the IPv6 address architecture is classless, we require + * users to specify a (non 0) prefix length (mask) for a new address. + * We also require the prefix (when specified) mask is valid, and thus + * reject a non-consecutive mask. + */ + if (ia == NULL && ifra->ifra_prefixmask.sin6_len == 0) + return(EINVAL); + if (ifra->ifra_prefixmask.sin6_len != 0) { + plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + (u_char *)&ifra->ifra_prefixmask + + ifra->ifra_prefixmask.sin6_len); + if (plen <= 0) + return(EINVAL); + } + else { /* - * The destination address for a p2p link must have a family - * of AF_UNSPEC or AF_INET6. + * In this case, ia must not be NULL. We just use its prefix + * length. */ - if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && - ifra->ifra_dstaddr.sin6_family != AF_INET6 && - ifra->ifra_dstaddr.sin6_family != AF_UNSPEC) - return(EAFNOSUPPORT); + plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); + } + /* + * If the destination address on a p2p interface is specified, + * and the address is a scoped one, validate/set the scope + * zone identifier. + */ + dst6 = ifra->ifra_dstaddr; + if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) && + (dst6.sin6_family == AF_INET6)) { + int scopeid; + +#ifndef SCOPEDROUTING + if ((error = in6_recoverscope(&dst6, + &ifra->ifra_dstaddr.sin6_addr, + ifp)) != 0) + return(error); +#endif + scopeid = in6_addr2scopeid(ifp, &dst6.sin6_addr); + if (dst6.sin6_scope_id == 0) /* user omit to specify the ID. */ + dst6.sin6_scope_id = scopeid; + else if (dst6.sin6_scope_id != scopeid) + return(EINVAL); /* scope ID mismatch. */ +#ifndef SCOPEDROUTING + if ((error = in6_embedscope(&dst6.sin6_addr, &dst6, NULL, NULL)) + != 0) + return(error); + dst6.sin6_scope_id = 0; /* XXX */ +#endif + } + /* + * The destination address can be specified only for a p2p or a + * loopback interface. If specified, the corresponding prefix length + * must be 128. + */ + if (ifra->ifra_dstaddr.sin6_family == AF_INET6) { + if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) { + /* XXX: noisy message */ + log(LOG_INFO, "in6_update_ifa: a destination can be " + "specified for a p2p or a loopback IF only\n"); + return(EINVAL); + } + if (plen != 128) { + /* + * The following message seems noisy, but we dare to + * add it for diagnosis. + */ + log(LOG_INFO, "in6_update_ifa: prefixlen must be 128 " + "when dstaddr is specified\n"); + return(EINVAL); + } + } + /* lifetime consistency check */ + lt = &ifra->ifra_lifetime; + if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME + && lt->ia6t_vltime + time_second < time_second) { + return EINVAL; + } + if (lt->ia6t_vltime == 0) { /* - * The prefixmask must have a family of AF_UNSPEC or AF_INET6. + * the following log might be noisy, but this is a typical + * configuration mistake or a tool's bug. */ - if (ifra->ifra_prefixmask.sin6_family != AF_INET6 && - ifra->ifra_prefixmask.sin6_family != AF_UNSPEC) - return(EAFNOSUPPORT); + log(LOG_INFO, + "in6_update_ifa: valid lifetime is 0 for %s\n", + ip6_sprintf(&ifra->ifra_addr.sin6_addr)); + } + if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME + && lt->ia6t_pltime + time_second < time_second) { + return EINVAL; + } - if (ifra->ifra_prefixmask.sin6_len) { - in6_ifscrub(ifp, ia); - ia->ia_prefixmask = ifra->ifra_prefixmask; - prefixIsNew = 1; + /* + * If this is a new address, allocate a new ifaddr and link it + * into chains. + */ + if (ia == NULL) { + hostIsNew = 1; + ia = (struct in6_ifaddr *) + malloc(sizeof(*ia), M_IFADDR, M_WAITOK); + if (ia == NULL) + return (ENOBUFS); + bzero((caddr_t)ia, sizeof(*ia)); + /* Initialize the address and masks */ + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + ia->ia_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_len = sizeof(ia->ia_addr); + if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { + /* + * XXX: some functions expect that ifa_dstaddr is not + * NULL for p2p interfaces. + */ + ia->ia_ifa.ifa_dstaddr + = (struct sockaddr *)&ia->ia_dstaddr; + } else { + ia->ia_ifa.ifa_dstaddr = NULL; } - if ((ifp->if_flags & IFF_POINTOPOINT) && - (ifra->ifra_dstaddr.sin6_family == AF_INET6)) { - in6_ifscrub(ifp, ia); - oldaddr = ia->ia_dstaddr; - ia->ia_dstaddr = ifra->ifra_dstaddr; - /* link-local index check: should be a separate function? */ - if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { - /* - * interface ID is not embedded by - * the user - */ - ia->ia_dstaddr.sin6_addr.s6_addr16[1] - = htons(ifp->if_index); - } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != - htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid is contradict */ - } - } - prefixIsNew = 1; /* We lie; but effect's the same */ + ia->ia_ifa.ifa_netmask + = (struct sockaddr *)&ia->ia_prefixmask; + + ia->ia_ifp = ifp; + if ((oia = in6_ifaddr) != NULL) { + for ( ; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; + } else + in6_ifaddr = ia; + + TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, + ifa_list); + } + + /* set prefix mask */ + if (ifra->ifra_prefixmask.sin6_len) { + /* + * We prohibit changing the prefix length of an existing + * address, because + * + such an operation should be rare in IPv6, and + * + the operation would confuse prefix management. + */ + if (ia->ia_prefixmask.sin6_len && + in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { + log(LOG_INFO, "in6_update_ifa: the prefix length of an" + " existing (%s) address should not be changed\n", + ip6_sprintf(&ia->ia_addr.sin6_addr)); + error = EINVAL; + goto unlink; } - if (hostIsNew || prefixIsNew) { - error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0); -#if 0 - if (error) - goto undo; -#endif + ia->ia_prefixmask = ifra->ifra_prefixmask; + } + + /* + * If a new destination address is specified, scrub the old one and + * install the new destination. Note that the interface must be + * p2p or loopback (see the check above.) + */ + if (dst6.sin6_family == AF_INET6 && + !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, + &ia->ia_dstaddr.sin6_addr)) { + int e; + + if ((ia->ia_flags & IFA_ROUTE) != 0 && + (e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) + != 0) { + log(LOG_ERR, "in6_update_ifa: failed to remove " + "a route to the old destination: %s\n", + ip6_sprintf(&ia->ia_addr.sin6_addr)); + /* proceed anyway... */ } - if (hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { - int error_local = 0; + else + ia->ia_flags &= ~IFA_ROUTE; + ia->ia_dstaddr = dst6; + } + /* reset the interface and routing table appropriately. */ + if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) + goto unlink; + + /* + * Beyond this point, we should call in6_purgeaddr upon an error, + * not just go to unlink. + */ + +#if 0 /* disable this mechanism for now */ + /* update prefix list */ + if (hostIsNew && + (ifra->ifra_flags & IN6_IFF_NOPFX) == 0) { /* XXX */ + int iilen; + + iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - plen; + if ((error = in6_prefix_add_ifid(iilen, ia)) != 0) { + in6_purgeaddr((struct ifaddr *)ia); + return(error); + } + } +#endif + + if ((ifp->if_flags & IFF_MULTICAST) != 0) { + struct sockaddr_in6 mltaddr, mltmask; + struct in6_multi *in6m; + + if (hostIsNew) { /* * join solicited multicast addr for new host id */ @@ -808,98 +1015,182 @@ in6_control(so, cmd, data, ifp, p) llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; llsol.s6_addr8[12] = 0xff; - (void)in6_addmulti(&llsol, ifp, &error_local); - if (error == 0) - error = error_local; + (void)in6_addmulti(&llsol, ifp, &error); + if (error != 0) { + log(LOG_WARNING, + "in6_update_ifa: addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&llsol), if_name(ifp), + error); + in6_purgeaddr((struct ifaddr *)ia); + return(error); + } } - ia->ia6_flags = ifra->ifra_flags; - ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ - - ia->ia6_lifetime = ifra->ifra_lifetime; - /* for sanity */ - if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { - ia->ia6_lifetime.ia6t_expire = - time_second + ia->ia6_lifetime.ia6t_vltime; - } else - ia->ia6_lifetime.ia6t_expire = 0; - if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { - ia->ia6_lifetime.ia6t_preferred = - time_second + ia->ia6_lifetime.ia6t_pltime; - } else - ia->ia6_lifetime.ia6t_preferred = 0; + bzero(&mltmask, sizeof(mltmask)); + mltmask.sin6_len = sizeof(struct sockaddr_in6); + mltmask.sin6_family = AF_INET6; + mltmask.sin6_addr = in6mask32; /* - * make sure to initialize ND6 information. this is to - * workaround issues with interfaces with IPv6 addresses, - * which have never brought # up. we are assuming that it is - * safe to nd6_ifattach multiple times. + * join link-local all-nodes address */ - nd6_ifattach(ifp); + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_addr = in6addr_linklocal_allnodes; + mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL) { + rtrequest(RTM_ADD, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, + RTF_UP|RTF_CLONING, /* xxx */ + (struct rtentry **)0); + (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + if (error != 0) { + log(LOG_WARNING, + "in6_update_ifa: addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error); + } + } /* - * Perform DAD, if needed. - * XXX It may be of use, if we can administratively - * disable DAD. + * join node information group address */ - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: -#if 0 - case IFT_ATM: - case IFT_SLIP: - case IFT_PPP: -#endif - { - ia->ia6_flags |= IN6_IFF_TENTATIVE; - nd6_dad_start((struct ifaddr *)ia, NULL); +#define hostnamelen strlen(hostname) + if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) + == 0) { + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL && ia != NULL) { + (void)in6_addmulti(&mltaddr.sin6_addr, + ifp, &error); + if (error != 0) { + log(LOG_WARNING, "in6_update_ifa: " + "addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error); + } } - break; -#ifdef IFT_DUMMY - case IFT_DUMMY: -#endif - case IFT_FAITH: - case IFT_GIF: - case IFT_LOOP: - default: - break; } +#undef hostnamelen - if (hostIsNew) { - int iilen; - int error_local = 0; - - iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - - in6_mask2len(&ia->ia_prefixmask.sin6_addr); - error_local = in6_prefix_add_ifid(iilen, ia); - if (error == 0) - error = error_local; + /* + * join node-local all-nodes address, on loopback. + * XXX: since "node-local" is obsoleted by interface-local, + * we have to join the group on every interface with + * some interface-boundary restriction. + */ + if (ifp->if_flags & IFF_LOOPBACK) { + struct in6_addr loop6 = in6addr_loopback; + ia = in6ifa_ifpwithaddr(ifp, &loop6); + + mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL && ia != NULL) { + rtrequest(RTM_ADD, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, + RTF_UP, + (struct rtentry **)0); + (void)in6_addmulti(&mltaddr.sin6_addr, ifp, + &error); + if (error != 0) { + log(LOG_WARNING, "in6_update_ifa: " + "addmulti failed for %s on %s " + "(errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error); + } + } } + } - return(error); + ia->ia6_flags = ifra->ifra_flags; + ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ + ia->ia6_flags &= ~IN6_IFF_NODAD; /* Mobile IPv6 */ + + ia->ia6_lifetime = ifra->ifra_lifetime; + /* for sanity */ + if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_expire = + time_second + ia->ia6_lifetime.ia6t_vltime; + } else + ia->ia6_lifetime.ia6t_expire = 0; + if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_preferred = + time_second + ia->ia6_lifetime.ia6t_pltime; + } else + ia->ia6_lifetime.ia6t_preferred = 0; - case SIOCDIFADDR_IN6: - in6_purgeaddr(&ia->ia_ifa, ifp); - break; + /* + * make sure to initialize ND6 information. this is to workaround + * issues with interfaces with IPv6 addresses, which have never brought + * up. We are assuming that it is safe to nd6_ifattach multiple times. + */ + nd6_ifattach(ifp); - default: - if (ifp == NULL || ifp->if_ioctl == 0) - return(EOPNOTSUPP); - return((*ifp->if_ioctl)(ifp, cmd, data)); + /* + * Perform DAD, if needed. + * XXX It may be of use, if we can administratively + * disable DAD. + */ + if (in6if_do_dad(ifp) && (ifra->ifra_flags & IN6_IFF_NODAD) == 0) { + ia->ia6_flags |= IN6_IFF_TENTATIVE; + nd6_dad_start((struct ifaddr *)ia, NULL); } - return(0); + + return(error); + + unlink: + /* + * XXX: if a change of an existing address failed, keep the entry + * anyway. + */ + if (hostIsNew) + in6_unlink_ifa(ia, ifp); + return(error); } void -in6_purgeaddr(ifa, ifp) +in6_purgeaddr(ifa) struct ifaddr *ifa; - struct ifnet *ifp; { - struct in6_ifaddr *oia, *ia = (void *) ifa; - int plen; + struct ifnet *ifp = ifa->ifa_ifp; + struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; - in6_ifscrub(ifp, ia); + /* stop DAD processing */ + nd6_dad_stop(ifa); + + /* + * delete route to the destination of the address being purged. + * The interface must be p2p or loopback in this case. + */ + if ((ia->ia_flags & IFA_ROUTE) != 0 && ia->ia_dstaddr.sin6_len != 0) { + int e; + + if ((e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) + != 0) { + log(LOG_ERR, "in6_purgeaddr: failed to remove " + "a route to the p2p destination: %s on %s, " + "errno=%d\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), if_name(ifp), + e); + /* proceed anyway... */ + } + else + ia->ia_flags &= ~IFA_ROUTE; + } + + /* Remove ownaddr's loopback rtentry, if it exists. */ + in6_ifremloop(&(ia->ia_ifa)); if (ifp->if_flags & IFF_MULTICAST) { /* @@ -921,9 +1212,19 @@ in6_purgeaddr(ifa, ifp) in6_delmulti(in6m); } + in6_unlink_ifa(ia, ifp); +} + +static void +in6_unlink_ifa(ia, ifp) + struct in6_ifaddr *ia; + struct ifnet *ifp; +{ + int plen, iilen; + struct in6_ifaddr *oia; + int s = splnet(); + TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); - /* release a refcnt for the link from if_addrlist */ - IFAFREE(&ia->ia_ifa); oia = ia; if (oia == (ia = in6_ifaddr)) @@ -933,45 +1234,61 @@ in6_purgeaddr(ifa, ifp) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; - else - printf("Didn't unlink in6_ifaddr from list\n"); + else { + /* search failed */ + printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n"); + } } - { - int iilen; - plen = in6_mask2len(&oia->ia_prefixmask.sin6_addr); + if (oia->ia6_ifpr) { /* check for safety */ + plen = in6_mask2len(&oia->ia_prefixmask.sin6_addr, NULL); iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - plen; in6_prefix_remove_ifid(iilen, oia); } /* - * Check if we have another address that has the same prefix of - * the purged address. If we have one, reinstall the corresponding - * interface route. + * When an autoconfigured address is being removed, release the + * reference to the base prefix. Also, since the release might + * affect the status of other (detached) addresses, call + * pfxlist_onlink_check(). */ - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - int e; - - if (in6_are_prefix_equal(&ia->ia_addr.sin6_addr, - &oia->ia_addr.sin6_addr, plen)) { - if ((e = rtinit(&(ia->ia_ifa), (int)RTM_ADD, - ia->ia_flags)) == 0) { - ia->ia_flags |= IFA_ROUTE; - break; - } - else { - log(LOG_NOTICE, - "in6_purgeaddr: failed to add an interface" - " route for %s/%d on %s, errno = %d\n", - ip6_sprintf(&ia->ia_addr.sin6_addr), - plen, if_name(ia->ia_ifp), e); - /* still trying */ - } + if ((oia->ia6_flags & IN6_IFF_AUTOCONF) != 0) { + if (oia->ia6_ndpr == NULL) { + log(LOG_NOTICE, "in6_unlink_ifa: autoconf'ed address " + "%p has no prefix\n", oia); + } else { + oia->ia6_ndpr->ndpr_refcnt--; + oia->ia6_flags &= ~IN6_IFF_AUTOCONF; + oia->ia6_ndpr = NULL; } + + pfxlist_onlink_check(); } - - /* release another refcnt for the link from in6_ifaddr */ + + /* + * release another refcnt for the link from in6_ifaddr. + * Note that we should decrement the refcnt at least once for all *BSD. + */ IFAFREE(&oia->ia_ifa); + + splx(s); +} + +void +in6_purgeif(ifp) + struct ifnet *ifp; +{ + struct ifaddr *ifa, *nifa; + + for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa) + { + nifa = TAILQ_NEXT(ifa, ifa_list); + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + in6_purgeaddr(ifa); + } + + in6_ifdetach(ifp); } /* @@ -1107,7 +1424,6 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) } } - ifra.ifra_prefixmask.sin6_family = AF_INET6; ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); @@ -1159,7 +1475,17 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) continue; if (!cmp) break; + bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); +#ifndef SCOPEDROUTING + /* + * XXX: this is adhoc, but is necessary to allow + * a user to specify fe80::/64 (not /10) for a + * link-local address. + */ + if (IN6_IS_ADDR_LINKLOCAL(&candidate)) + candidate.s6_addr16[1] = 0; +#endif candidate.s6_addr32[0] &= mask.s6_addr32[0]; candidate.s6_addr32[1] &= mask.s6_addr32[1]; candidate.s6_addr32[2] &= mask.s6_addr32[2]; @@ -1172,17 +1498,38 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) ia = ifa2ia6(ifa); if (cmd == SIOCGLIFADDR) { +#ifndef SCOPEDROUTING + struct sockaddr_in6 *s6; +#endif + /* fill in the if_laddrreq structure */ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); - +#ifndef SCOPEDROUTING /* XXX see above */ + s6 = (struct sockaddr_in6 *)&iflr->addr; + if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) { + s6->sin6_addr.s6_addr16[1] = 0; + s6->sin6_scope_id = + in6_addr2scopeid(ifp, &s6->sin6_addr); + } +#endif if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &iflr->dstaddr, ia->ia_dstaddr.sin6_len); +#ifndef SCOPEDROUTING /* XXX see above */ + s6 = (struct sockaddr_in6 *)&iflr->dstaddr; + if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) { + s6->sin6_addr.s6_addr16[1] = 0; + s6->sin6_scope_id = + in6_addr2scopeid(ifp, + &s6->sin6_addr); + } +#endif } else bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); iflr->prefixlen = - in6_mask2len(&ia->ia_prefixmask.sin6_addr); + in6_mask2len(&ia->ia_prefixmask.sin6_addr, + NULL); iflr->flags = ia->ia6_flags; /*XXX*/ @@ -1218,96 +1565,73 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) } /* - * Delete any existing route for an interface. - */ -void -in6_ifscrub(ifp, ia) - register struct ifnet *ifp; - register struct in6_ifaddr *ia; -{ - if ((ia->ia_flags & IFA_ROUTE) == 0) - return; - if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); - else - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); - ia->ia_flags &= ~IFA_ROUTE; - - /* Remove ownaddr's loopback rtentry, if it exists. */ - in6_ifremloop(&(ia->ia_ifa)); -} - -/* * Initialize an interface's intetnet6 address * and routing table entry. */ -int -in6_ifinit(ifp, ia, sin6, scrub) +static int +in6_ifinit(ifp, ia, sin6, newhost) struct ifnet *ifp; struct in6_ifaddr *ia; struct sockaddr_in6 *sin6; - int scrub; + int newhost; { - struct sockaddr_in6 oldaddr; - int error, flags = RTF_UP; + int error = 0, plen, ifacount = 0; int s = splimp(); + struct ifaddr *ifa; - oldaddr = ia->ia_addr; - ia->ia_addr = *sin6; /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ - if (ifp->if_ioctl && - (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + if (ifa->ifa_addr == NULL) + continue; /* just for safety */ + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + ifacount++; + } + + ia->ia_addr = *sin6; + + if (ifacount <= 1 && ifp->if_ioctl && + (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { splx(s); - ia->ia_addr = oldaddr; return(error); } + splx(s); - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - break; - case IFT_PPP: - ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - break; - } + ia->ia_ifa.ifa_metric = ifp->if_metric; + + /* we could do in(6)_socktrim here, but just omit it at this moment. */ - splx(s); - if (scrub) { - ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; - in6_ifscrub(ifp, ia); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - } - /* xxx - * in_socktrim - */ /* - * Add route for the network. + * Special case: + * If the destination address is specified for a point-to-point + * interface, install a route to the destination as an interface + * direct route. */ - ia->ia_ifa.ifa_metric = ifp->if_metric; - if (ifp->if_flags & IFF_LOOPBACK) { - ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; - flags |= RTF_HOST; - } else if (ifp->if_flags & IFF_POINTOPOINT) { - if (ia->ia_dstaddr.sin6_family != AF_INET6) - return(0); - flags |= RTF_HOST; - } - if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) + plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ + if (plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { + if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, + RTF_UP | RTF_HOST)) != 0) + return(error); ia->ia_flags |= IFA_ROUTE; - /* XXX check if the subnet route points to the same interface */ - if (error == EEXIST) - error = 0; + } + if (plen < 128) { + /* + * The RTF_CLONING flag is necessary for in6_is_ifloop_auto(). + */ + ia->ia_ifa.ifa_flags |= RTF_CLONING; + } /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */ - in6_ifaddloop(&(ia->ia_ifa)); + if (newhost) { + /* set the rtrequest function to create llinfo */ + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + in6_ifaddloop(&(ia->ia_ifa)); + } return(error); } @@ -1318,8 +1642,8 @@ in6_ifinit(ifp, ia, sin6, scrub) */ struct in6_multi * in6_addmulti(maddr6, ifp, errorp) - register struct in6_addr *maddr6; - register struct ifnet *ifp; + struct in6_addr *maddr6; + struct ifnet *ifp; int *errorp; { struct in6_multi *in6m; @@ -1408,7 +1732,7 @@ in6ifa_ifpforlinklocal(ifp, ignoreflags) struct ifnet *ifp; int ignoreflags; { - register struct ifaddr *ifa; + struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { @@ -1436,7 +1760,7 @@ in6ifa_ifpwithaddr(ifp, addr) struct ifnet *ifp; struct in6_addr *addr; { - register struct ifaddr *ifa; + struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { @@ -1458,13 +1782,13 @@ static char digits[] = "0123456789abcdef"; static int ip6round = 0; char * ip6_sprintf(addr) -register struct in6_addr *addr; + const struct in6_addr *addr; { static char ip6buf[8][48]; - register int i; - register char *cp; - register u_short *a = (u_short *)addr; - register u_char *d; + int i; + char *cp; + u_short *a = (u_short *)addr; + u_char *d; int dcolon = 0; ip6round = (ip6round + 1) & 7; @@ -1522,6 +1846,27 @@ in6_localaddr(in6) return (0); } +int +in6_is_addr_deprecated(sa6) + struct sockaddr_in6 *sa6; +{ + struct in6_ifaddr *ia; + + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, + &sa6->sin6_addr) && +#ifdef SCOPEDROUTING + ia->ia_addr.sin6_scope_id == sa6->sin6_scope_id && +#endif + (ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) + return(1); /* true */ + + /* XXX: do we still have to go thru the rest of the list? */ + } + + return(0); /* false */ +} + /* * return length of part which dst and src are equal * hard coding... @@ -1602,8 +1947,8 @@ in6_prefixlen2mask(maskp, len) */ struct in6_ifaddr * in6_ifawithscope(oifp, dst) - register struct ifnet *oifp; - register struct in6_addr *dst; + struct ifnet *oifp; + struct in6_addr *dst; { int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0; int blen = -1; @@ -1612,7 +1957,9 @@ in6_ifawithscope(oifp, dst) struct in6_ifaddr *ifa_best = NULL; if (oifp == NULL) { +#if 0 printf("in6_ifawithscope: output interface is not specified\n"); +#endif return(NULL); } @@ -1675,13 +2022,20 @@ in6_ifawithscope(oifp, dst) * Also, if the current address has a smaller scope * than dst, ignore it unless ifa_best also has a * smaller scope. + * Consequently, after the two if-clause below, + * the followings must be satisfied: + * (scope(src) < scope(dst) && + * scope(best) < scope(dst)) + * OR + * (scope(best) >= scope(dst) && + * scope(src) >= scope(dst)) */ if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0 && IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0) - goto replace; + goto replace; /* (A) */ if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 && IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0) - continue; + continue; /* (B) */ /* * A deprecated address SHOULD NOT be used in new @@ -1710,7 +2064,8 @@ in6_ifawithscope(oifp, dst) /* * A non-deprecated address is always preferred * to a deprecated one regardless of scopes and - * address matching. + * address matching (Note invariants ensured by the + * conditions (A) and (B) above.) */ if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) && (((struct in6_ifaddr *)ifa)->ia6_flags & @@ -1718,6 +2073,37 @@ in6_ifawithscope(oifp, dst) goto replace; /* + * When we use temporary addresses described in + * RFC 3041, we prefer temporary addresses to + * public autoconf addresses. Again, note the + * invariants from (A) and (B). Also note that we + * don't have any preference between static addresses + * and autoconf addresses (despite of whether or not + * the latter is temporary or public.) + */ + if (ip6_use_tempaddr) { + struct in6_ifaddr *ifat; + + ifat = (struct in6_ifaddr *)ifa; + if ((ifa_best->ia6_flags & + (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + == IN6_IFF_AUTOCONF && + (ifat->ia6_flags & + (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + == (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) { + goto replace; + } + if ((ifa_best->ia6_flags & + (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + == (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY) && + (ifat->ia6_flags & + (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + == IN6_IFF_AUTOCONF) { + continue; + } + } + + /* * At this point, we have two cases: * 1. we are looking at a non-deprecated address, * and ifa_best is also non-deprecated. @@ -1743,64 +2129,68 @@ in6_ifawithscope(oifp, dst) * Smaller scopes are the last resort. * - A deprecated address is chosen only when we have * no address that has an enough scope, but is - * prefered to any addresses of smaller scopes. - * - Longest address match against dst is considered - * only for addresses that has the same scope of dst. + * prefered to any addresses of smaller scopes + * (this must be already done above.) + * - addresses on the outgoing I/F are preferred to + * ones on other interfaces if none of above + * tiebreaks. In the table below, the column "bI" + * means if the best_ifa is on the outgoing + * interface, and the column "sI" means if the ifa + * is on the outgoing interface. * - If there is no other reasons to choose one, - * addresses on the outgoing I/F are preferred. + * longest address match against dst is considered. * * The precise decision table is as follows: - * dscopecmp bscopecmp matchcmp outI/F | replace? - * !equal equal N/A Yes | Yes (1) - * !equal equal N/A No | No (2) - * larger larger N/A N/A | No (3) - * larger smaller N/A N/A | Yes (4) - * smaller larger N/A N/A | Yes (5) - * smaller smaller N/A N/A | No (6) - * equal smaller N/A N/A | Yes (7) - * equal larger (already done) - * equal equal larger N/A | Yes (8) - * equal equal smaller N/A | No (9) - * equal equal equal Yes | Yes (a) - * eaual eqaul equal No | No (b) + * dscopecmp bscopecmp match bI oI | replace? + * N/A equal N/A Y N | No (1) + * N/A equal N/A N Y | Yes (2) + * N/A equal larger N/A | Yes (3) + * N/A equal !larger N/A | No (4) + * larger larger N/A N/A | No (5) + * larger smaller N/A N/A | Yes (6) + * smaller larger N/A N/A | Yes (7) + * smaller smaller N/A N/A | No (8) + * equal smaller N/A N/A | Yes (9) + * equal larger (already done at A above) */ dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope); bscopecmp = IN6_ARE_SCOPE_CMP(src_scope, best_scope); - if (dscopecmp && bscopecmp == 0) { - if (oifp == ifp) /* (1) */ + if (bscopecmp == 0) { + struct ifnet *bifp = ifa_best->ia_ifp; + + if (bifp == oifp && ifp != oifp) /* (1) */ + continue; + if (bifp != oifp && ifp == oifp) /* (2) */ + goto replace; + + /* + * Both bifp and ifp are on the outgoing + * interface, or both two are on a different + * interface from the outgoing I/F. + * now we need address matching against dst + * for tiebreaking. + */ + tlen = in6_matchlen(IFA_IN6(ifa), dst); + matchcmp = tlen - blen; + if (matchcmp > 0) /* (3) */ goto replace; - continue; /* (2) */ + continue; /* (4) */ } if (dscopecmp > 0) { - if (bscopecmp > 0) /* (3) */ + if (bscopecmp > 0) /* (5) */ continue; - goto replace; /* (4) */ + goto replace; /* (6) */ } if (dscopecmp < 0) { - if (bscopecmp > 0) /* (5) */ + if (bscopecmp > 0) /* (7) */ goto replace; - continue; /* (6) */ + continue; /* (8) */ } /* now dscopecmp must be 0 */ if (bscopecmp < 0) - goto replace; /* (7) */ - - /* - * At last both dscopecmp and bscopecmp must be 0. - * We need address matching against dst for - * tiebreaking. - */ - tlen = in6_matchlen(IFA_IN6(ifa), dst); - matchcmp = tlen - blen; - if (matchcmp > 0) /* (8) */ - goto replace; - if (matchcmp < 0) /* (9) */ - continue; - if (oifp == ifp) /* (a) */ - goto replace; - continue; /* (b) */ + goto replace; /* (9) */ replace: ifa_best = (struct in6_ifaddr *)ifa; @@ -1837,8 +2227,8 @@ in6_ifawithscope(oifp, dst) */ struct in6_ifaddr * in6_ifawithifp(ifp, dst) - register struct ifnet *ifp; - register struct in6_addr *dst; + struct ifnet *ifp; + struct in6_addr *dst; { int dst_scope = in6_addrscope(dst), blen = -1, tlen; struct ifaddr *ifa; @@ -1943,6 +2333,43 @@ in6_if_up(ifp) } } +int +in6if_do_dad(ifp) + struct ifnet *ifp; +{ + if ((ifp->if_flags & IFF_LOOPBACK) != 0) + return(0); + + switch (ifp->if_type) { +#ifdef IFT_DUMMY + case IFT_DUMMY: +#endif + case IFT_FAITH: + /* + * These interfaces do not have the IFF_LOOPBACK flag, + * but loop packets back. We do not have to do DAD on such + * interfaces. We should even omit it, because loop-backed + * NS would confuse the DAD procedure. + */ + return(0); + default: + /* + * Our DAD routine requires the interface up and running. + * However, some interfaces can be up before the RUNNING + * status. Additionaly, users may try to assign addresses + * before the interface becomes up (or running). + * We simply skip DAD in such a case as a work around. + * XXX: we should rather mark "tentative" on such addresses, + * and do DAD after the interface becomes ready. + */ + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != + (IFF_UP|IFF_RUNNING)) + return(0); + + return(1); + } +} + /* * Calculate max IPv6 MTU through all the interfaces and store it * to in6_maxmtu. diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 1159e5251e32..168d1df64dc0 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6.h,v 1.48 2000/06/26 15:55:32 itojun Exp $ */ +/* $KAME: in6.h,v 1.89 2001/05/27 13:28:35 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -66,21 +66,19 @@ */ #ifndef __KAME_NETINET_IN_H_INCLUDED_ -#error "do not include netinet6/in6.h directly, include netinet/in.h" +#error "do not include netinet6/in6.h directly, include netinet/in.h. see RFC2553" #endif #ifndef _NETINET6_IN6_H_ #define _NETINET6_IN6_H_ -#ifndef _XOPEN_SOURCE -#include <sys/queue.h> -#endif - /* * Identification of the network protocol stack + * for *BSD-current/release: http://www.kame.net/dev/cvsweb.cgi/kame/COVERAGE + * has the table of implementation/integration differences. */ #define __KAME__ -#define __KAME_VERSION "20000701/FreeBSD-current" +#define __KAME_VERSION "20010528/FreeBSD" /* * Local port number conventions: @@ -148,7 +146,7 @@ struct sockaddr_in6 { u_int16_t sin6_port; /* Transport layer port # (in_port_t)*/ u_int32_t sin6_flowinfo; /* IP6 flow information */ struct in6_addr sin6_addr; /* IP6 address */ - u_int32_t sin6_scope_id; /* intface scope id */ + u_int32_t sin6_scope_id; /* scope zone index */ }; /* @@ -167,6 +165,8 @@ struct sockaddr_in6 { #endif #ifdef _KERNEL +extern const struct sockaddr_in6 sa6_any; + extern const struct in6_addr in6mask0; extern const struct in6_addr in6mask32; extern const struct in6_addr in6mask64; @@ -238,41 +238,49 @@ extern const struct in6_addr in6addr_linklocal_allrouters; (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #endif +#ifdef _KERNEL /* non standard */ +/* see if two addresses are equal in a scope-conscious manner. */ +#define SA6_ARE_ADDR_EQUAL(a, b) \ + (((a)->sin6_scope_id == 0 || (b)->sin6_scope_id == 0 || \ + ((a)->sin6_scope_id == (b)->sin6_scope_id)) && \ + (bcmp(&(a)->sin6_addr, &(b)->sin6_addr, sizeof(struct in6_addr)) == 0)) +#endif + /* * Unspecified */ #define IN6_IS_ADDR_UNSPECIFIED(a) \ - ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[12]) == 0)) + ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[12]) == 0)) /* * Loopback */ #define IN6_IS_ADDR_LOOPBACK(a) \ - ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[12]) == ntohl(1))) + ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[12]) == ntohl(1))) /* * IPv4 compatible */ #define IN6_IS_ADDR_V4COMPAT(a) \ - ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[12]) != 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[12]) != ntohl(1))) + ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[12]) != 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[12]) != ntohl(1))) /* * Mapped */ #define IN6_IS_ADDR_V4MAPPED(a) \ - ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff))) + ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff))) /* * KAME Scope Values @@ -350,13 +358,27 @@ extern const struct in6_addr in6addr_linklocal_allrouters; #endif /* - * KAME Scope + * Wildcard Socket */ +#if 0 /*pre-RFC2553*/ +#define IN6_IS_ADDR_ANY(a) IN6_IS_ADDR_UNSPECIFIED(a) +#endif + #ifdef _KERNEL /*nonstandard*/ +/* + * KAME Scope + */ #define IN6_IS_SCOPE_LINKLOCAL(a) \ ((IN6_IS_ADDR_LINKLOCAL(a)) || \ (IN6_IS_ADDR_MC_LINKLOCAL(a))) -#endif + +#define IFA6_IS_DEPRECATED(a) \ + ((a)->ia6_lifetime.ia6t_preferred != 0 && \ + (a)->ia6_lifetime.ia6t_preferred < time_second) +#define IFA6_IS_INVALID(a) \ + ((a)->ia6_lifetime.ia6t_expire != 0 && \ + (a)->ia6_lifetime.ia6t_expire < time_second) +#endif /* _KERNEL */ /* * IP6 route structure @@ -389,26 +411,34 @@ struct route_in6 { #define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */ #define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */ #define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */ -#define IPV6_PKTINFO 19 /* bool; send/rcv if, src/dst addr */ +/* RFC2292 options */ +#define IPV6_PKTINFO 19 /* bool; send/recv if, src/dst addr */ #define IPV6_HOPLIMIT 20 /* bool; hop limit */ #define IPV6_NEXTHOP 21 /* bool; next hop addr */ #define IPV6_HOPOPTS 22 /* bool; hop-by-hop option */ #define IPV6_DSTOPTS 23 /* bool; destination option */ #define IPV6_RTHDR 24 /* bool; routing header */ #define IPV6_PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */ + #define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */ -#define IPV6_BINDV6ONLY 27 /* bool; only bind INET6 at null bind */ +#define IPV6_V6ONLY 27 /* bool; only bind INET6 at wildcard bind */ +#ifndef _KERNEL +#define IPV6_BINDV6ONLY IPV6_V6ONLY +#endif -/* for IPsec */ +#if 1 /*IPSEC*/ #define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */ +#endif #define IPV6_FAITH 29 /* bool; accept FAITH'ed connections */ -/* for IPV6FIREWALL */ +#if 1 /*IPV6FIREWALL*/ #define IPV6_FW_ADD 30 /* add a firewall rule to chain */ #define IPV6_FW_DEL 31 /* delete a firewall rule from chain */ #define IPV6_FW_FLUSH 32 /* flush firewall rule chain */ #define IPV6_FW_ZERO 33 /* clear single/all firewall counter(s) */ #define IPV6_FW_GET 34 /* get entire firewall rule chain */ +#endif + /* to define items, should talk with KAME guys first, for *BSD compatibility */ #define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */ @@ -528,39 +558,44 @@ struct in6_pktinfo { #define IPV6CTL_KAME_VERSION 20 #define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */ #define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */ +#if 0 /*obsolete*/ #define IPV6CTL_MAPPED_ADDR 23 -#ifdef notdef /*__NetBSD__ - reserved, don't delete*/ -#define IPV6CTL_BINDV6ONLY 24 #endif +#define IPV6CTL_V6ONLY 24 #define IPV6CTL_RTEXPIRE 25 /* cloned route expiration time */ #define IPV6CTL_RTMINEXPIRE 26 /* min value for expiration time */ #define IPV6CTL_RTMAXCACHE 27 /* trigger level for dynamic expire */ + +#define IPV6CTL_USETEMPADDR 32 /* use temporary addresses (RFC3041) */ +#define IPV6CTL_TEMPPLTIME 33 /* preferred lifetime for tmpaddrs */ +#define IPV6CTL_TEMPVLTIME 34 /* valid lifetime for tmpaddrs */ +#define IPV6CTL_AUTO_LINKLOCAL 35 /* automatic link-local addr assign */ +#define IPV6CTL_RIP6STATS 36 /* raw_ip6 stats */ + /* New entries should be added here from current IPV6CTL_MAXID value. */ /* to define items, should talk with KAME guys first, for *BSD compatibility */ -#define IPV6CTL_MAXID 28 +#define IPV6CTL_MAXID 37 + #endif /* !_XOPEN_SOURCE */ /* * Redefinition of mbuf flags */ -#define M_ANYCAST6 M_PROTO1 -#define M_AUTHIPHDR M_PROTO2 -#define M_DECRYPTED M_PROTO3 -#define M_LOOP M_PROTO4 -#define M_AUTHIPDGM M_PROTO5 +#define M_AUTHIPHDR M_PROTO2 +#define M_DECRYPTED M_PROTO3 +#define M_LOOP M_PROTO4 +#define M_AUTHIPDGM M_PROTO5 #ifdef _KERNEL -struct cmsghdr; -struct mbuf; -struct ifnet; +struct cmsghdr; int in6_cksum __P((struct mbuf *, u_int8_t, u_int32_t, u_int32_t)); int in6_localaddr __P((struct in6_addr *)); int in6_addrscope __P((struct in6_addr *)); struct in6_ifaddr *in6_ifawithscope __P((struct ifnet *, struct in6_addr *)); struct in6_ifaddr *in6_ifawithifp __P((struct ifnet *, struct in6_addr *)); -extern void in6_if_up __P((struct ifnet *)); -struct sockaddr; +extern void in6_if_up __P((struct ifnet *)); +struct sockaddr; void in6_sin6_2_sin __P((struct sockaddr_in *sin, struct sockaddr_in6 *sin6)); @@ -569,33 +604,51 @@ void in6_sin_2_v4mapsin6 __P((struct sockaddr_in *sin, void in6_sin6_2_sin_in_sock __P((struct sockaddr *nam)); void in6_sin_2_v4mapsin6_in_sock __P((struct sockaddr **nam)); -#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) -#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) -#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) +#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) +#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) +#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) #endif /* _KERNEL */ __BEGIN_DECLS struct cmsghdr; -extern int inet6_option_space __P((int)); -extern int inet6_option_init __P((void *, struct cmsghdr **, int)); -extern int inet6_option_append __P((struct cmsghdr *, const u_int8_t *, - int, int)); -extern u_int8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int)); -extern int inet6_option_next __P((const struct cmsghdr *, u_int8_t **)); -extern int inet6_option_find __P((const struct cmsghdr *, u_int8_t **, - int)); - -extern size_t inet6_rthdr_space __P((int, int)); -extern struct cmsghdr *inet6_rthdr_init __P((void *, int)); -extern int inet6_rthdr_add __P((struct cmsghdr *, const struct in6_addr *, - u_int)); -extern int inet6_rthdr_lasthop __P((struct cmsghdr *, u_int)); -extern int inet6_rthdr_segments __P((const struct cmsghdr *)); -extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int)); -extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int)); -extern int inet6_rthdr_reverse __P((const struct cmsghdr *, - struct cmsghdr *)); +extern int inet6_option_space __P((int)); +extern int inet6_option_init __P((void *, struct cmsghdr **, int)); +extern int inet6_option_append __P((struct cmsghdr *, const u_int8_t *, + int, int)); +extern u_int8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int)); +extern int inet6_option_next __P((const struct cmsghdr *, u_int8_t **)); +extern int inet6_option_find __P((const struct cmsghdr *, u_int8_t **, int)); + +extern size_t inet6_rthdr_space __P((int, int)); +extern struct cmsghdr *inet6_rthdr_init __P((void *, int)); +extern int inet6_rthdr_add __P((struct cmsghdr *, const struct in6_addr *, + unsigned int)); +extern int inet6_rthdr_lasthop __P((struct cmsghdr *, unsigned int)); +#if 0 /* not implemented yet */ +extern int inet6_rthdr_reverse __P((const struct cmsghdr *, struct cmsghdr *)); +#endif +extern int inet6_rthdr_segments __P((const struct cmsghdr *)); +extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int)); +extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int)); + +extern int inet6_opt_init __P((void *, size_t)); +extern int inet6_opt_append __P((void *, size_t, int, u_int8_t, + size_t, u_int8_t, void **)); +extern int inet6_opt_finish __P((void *, size_t, int)); +extern int inet6_opt_set_val __P((void *, size_t, void *, int)); + +extern int inet6_opt_next __P((void *, size_t, int, u_int8_t *, + size_t *, void **)); +extern int inet6_opt_find __P((void *, size_t, int, u_int8_t, + size_t *, void **)); +extern int inet6_opt_get_val __P((void *, size_t, void *, int)); +extern size_t inet6_rth_space __P((int, int)); +extern void *inet6_rth_init __P((void *, int, int, int)); +extern int inet6_rth_add __P((void *, const struct in6_addr *)); +extern int inet6_rth_reverse __P((const void *, void *)); +extern int inet6_rth_segments __P((const void *)); +extern struct in6_addr *inet6_rth_getaddr __P((const void *, int)); __END_DECLS #endif /* !_NETINET6_IN6_H_ */ diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c index db564cf571d9..fbd95cbbaaa0 100644 --- a/sys/netinet6/in6_cksum.c +++ b/sys/netinet6/in6_cksum.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_cksum.c,v 1.9 2000/09/09 15:33:31 itojun Exp $ */ +/* $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -92,13 +92,13 @@ int in6_cksum(m, nxt, off, len) - register struct mbuf *m; + struct mbuf *m; u_int8_t nxt; u_int32_t off, len; { - register u_int16_t *w; - register int sum = 0; - register int mlen = 0; + u_int16_t *w; + int sum = 0; + int mlen = 0; int byte_swapped = 0; #if 0 int srcifid = 0, dstifid = 0; diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c index c8c1450f02fe..57bec0ef1893 100644 --- a/sys/netinet6/in6_gif.c +++ b/sys/netinet6/in6_gif.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_gif.c,v 1.37 2000/06/17 20:34:25 itojun Exp $ */ +/* $KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -30,10 +30,6 @@ * SUCH DAMAGE. */ -/* - * in6_gif.c - */ - #include "opt_inet.h" #include "opt_inet6.h" @@ -43,6 +39,10 @@ #include <sys/sockio.h> #include <sys/mbuf.h> #include <sys/errno.h> +#include <sys/queue.h> +#include <sys/syslog.h> + +#include <sys/malloc.h> #include <net/if.h> #include <net/route.h> @@ -148,34 +148,19 @@ in6_gif_output(ifp, family, m, rt) ip6->ip6_nxt = proto; ip6->ip6_hlim = ip6_gif_hlim; ip6->ip6_src = sin6_src->sin6_addr; - if (ifp->if_flags & IFF_LINK0) { - /* multi-destination mode */ - if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) - ip6->ip6_dst = sin6_dst->sin6_addr; - else if (rt) { - if (family != AF_INET6) { - m_freem(m); - return EINVAL; /*XXX*/ - } - ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr; - } else { - m_freem(m); - return ENETUNREACH; - } - } else { - /* bidirectional configured tunnel mode */ - if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) - ip6->ip6_dst = sin6_dst->sin6_addr; - else { - m_freem(m); - return ENETUNREACH; - } + /* bidirectional configured tunnel mode */ + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) + ip6->ip6_dst = sin6_dst->sin6_addr; + else { + m_freem(m); + return ENETUNREACH; } - if (ifp->if_flags & IFF_LINK1) { - otos = 0; + if (ifp->if_flags & IFF_LINK1) ip_ecn_ingress(ECN_ALLOWED, &otos, &itos); - ip6->ip6_flow |= htonl((u_int32_t)otos << 20); - } + else + ip_ecn_ingress(ECN_NOCARE, &otos, &itos); + ip6->ip6_flow &= ~ntohl(0xff00000); + ip6->ip6_flow |= htonl((u_int32_t)otos << 20); if (dst->sin6_family != sin6_dst->sin6_family || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { @@ -262,6 +247,8 @@ int in6_gif_input(mp, offp, proto) ip = mtod(m, struct ip *); if (gifp->if_flags & IFF_LINK1) ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos); + else + ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos); break; } #endif /* INET */ @@ -278,6 +265,8 @@ int in6_gif_input(mp, offp, proto) ip6 = mtod(m, struct ip6_hdr *); if (gifp->if_flags & IFF_LINK1) ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); + else + ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow); break; } #endif @@ -321,17 +310,14 @@ gif_encapcheck6(m, off, proto, arg) addrmatch |= 1; if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src)) addrmatch |= 2; - else if ((sc->gif_if.if_flags & IFF_LINK0) != 0 && - IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) { - addrmatch |= 2; /* we accept any source */ - } if (addrmatch != 3) return 0; /* martian filters on outer source - done in ip6_input */ /* ingress filters on outer source */ - if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { + if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && + (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { struct sockaddr_in6 sin6; struct rtentry *rt; @@ -341,15 +327,18 @@ gif_encapcheck6(m, off, proto, arg) sin6.sin6_addr = ip6.ip6_src; /* XXX scopeid */ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); - if (!rt) - return 0; - if (rt->rt_ifp != m->m_pkthdr.rcvif) { - rtfree(rt); + if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) { +#if 0 + log(LOG_WARNING, "%s: packet from %s dropped " + "due to ingress filter\n", if_name(&sc->gif_if), + ip6_sprintf(&sin6.sin6_addr)); +#endif + if (rt) + rtfree(rt); return 0; } rtfree(rt); } - /* prioritize: IFF_LINK0 mode is less preferred */ - return (sc->gif_if.if_flags & IFF_LINK0) ? 128 : 128 * 2; + return 128 * 2; } diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index c839d014a00d..516826d76b38 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_ifattach.c,v 1.67 2000/10/01 10:51:54 itojun Exp $ */ +/* $KAME: in6_ifattach.c,v 1.118 2001/05/24 07:44:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -36,6 +36,7 @@ #include <sys/socket.h> #include <sys/sockio.h> #include <sys/kernel.h> +#include <sys/syslog.h> #include <sys/md5.h> #include <net/if.h> @@ -49,6 +50,7 @@ #include <netinet/ip6.h> #include <netinet6/ip6_var.h> +#include <netinet6/in6_var.h> #include <netinet6/in6_ifattach.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> @@ -62,13 +64,20 @@ size_t in6_ifstatmax = 0; size_t icmp6_ifstatmax = 0; unsigned long in6_maxmtu = 0; +#ifdef IP6_AUTO_LINKLOCAL +int ip6_auto_linklocal = IP6_AUTO_LINKLOCAL; +#else +int ip6_auto_linklocal = 1; /* enable by default */ +#endif + +struct callout in6_tmpaddrtimer_ch; + static int get_rand_ifid __P((struct ifnet *, struct in6_addr *)); +static int generate_tmp_ifid __P((u_int8_t *, const u_int8_t *, u_int8_t *)); static int get_hw_ifid __P((struct ifnet *, struct in6_addr *)); static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *)); -static int in6_ifattach_addaddr __P((struct ifnet *, struct in6_ifaddr *)); static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *)); static int in6_ifattach_loopback __P((struct ifnet *)); -static int nigroup __P((struct ifnet *, const char *, int, struct in6_addr *)); #define EUI64_GBIT 0x01 #define EUI64_UBIT 0x02 @@ -122,6 +131,90 @@ get_rand_ifid(ifp, in6) return 0; } +static int +generate_tmp_ifid(seed0, seed1, ret) + u_int8_t *seed0, *ret; + const u_int8_t *seed1; +{ + MD5_CTX ctxt; + u_int8_t seed[16], digest[16], nullbuf[8]; + u_int32_t val32; + struct timeval tv; + + /* If there's no hisotry, start with a random seed. */ + bzero(nullbuf, sizeof(nullbuf)); + if (bcmp(nullbuf, seed0, sizeof(nullbuf)) == 0) { + int i; + + for (i = 0; i < 2; i++) { + microtime(&tv); + val32 = random() ^ tv.tv_usec; + bcopy(&val32, seed + sizeof(val32) * i, sizeof(val32)); + } + } else + bcopy(seed0, seed, 8); + + /* copy the right-most 64-bits of the given address */ + /* XXX assumption on the size of IFID */ + bcopy(seed1, &seed[8], 8); + + if (0) { /* for debugging purposes only */ + int i; + + printf("generate_tmp_ifid: new randomized ID from: "); + for (i = 0; i < 16; i++) + printf("%02x", seed[i]); + printf(" "); + } + + /* generate 16 bytes of pseudo-random value. */ + bzero(&ctxt, sizeof(ctxt)); + MD5Init(&ctxt); + MD5Update(&ctxt, seed, sizeof(seed)); + MD5Final(digest, &ctxt); + + /* + * RFC 3041 3.2.1. (3) + * Take the left-most 64-bits of the MD5 digest and set bit 6 (the + * left-most bit is numbered 0) to zero. + */ + bcopy(digest, ret, 8); + ret[0] &= ~EUI64_UBIT; + + /* + * XXX: we'd like to ensure that the generated value is not zero + * for simplicity. If the caclculated digest happens to be zero, + * use a random non-zero value as the last resort. + */ + if (bcmp(nullbuf, ret, sizeof(nullbuf)) == 0) { + log(LOG_INFO, + "generate_tmp_ifid: computed MD5 value is zero.\n"); + + microtime(&tv); + val32 = random() ^ tv.tv_usec; + val32 = 1 + (val32 % (0xffffffff - 1)); + } + + /* + * RFC 3041 3.2.1. (4) + * Take the rightmost 64-bits of the MD5 digest and save them in + * stable storage as the history value to be used in the next + * iteration of the algorithm. + */ + bcopy(&digest[8], seed0, 8); + + if (0) { /* for debugging purposes only */ + int i; + + printf("to: "); + for (i = 0; i < 16; i++) + printf("%02x", digest[i]); + printf("\n"); + } + + return 0; +} + /* * Get interface identifier for the specified interface. * XXX assumes single sockaddr_dl (AF_LINK address) per an interface @@ -165,7 +258,14 @@ found: case IFT_ETHER: case IFT_FDDI: case IFT_ATM: + case IFT_IEEE1394: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif /* IEEE802/EUI64 cases - what others? */ + /* IEEE1394 uses 16byte length address starting with EUI64 */ + if (addrlen > 8) + addrlen = 8; /* look at IEEE802/EUI64 only */ if (addrlen != 8 && addrlen != 6) @@ -217,7 +317,7 @@ found: case IFT_STF: #endif /* - * mech-06 says: "SHOULD use IPv4 address as ifid source". + * RFC2893 says: "SHOULD use IPv4 address as ifid source". * however, IPv4 address is not very suitable as unique * identifier source (can be renumbered). * we don't do this. @@ -262,19 +362,15 @@ get_ifid(ifp0, altifp, in6) /* first, try to get it from the interface itself */ if (get_hw_ifid(ifp0, in6) == 0) { -#ifdef ND6_DEBUG - printf("%s: got interface identifier from itself\n", - if_name(ifp0)); -#endif + nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n", + if_name(ifp0))); goto success; } /* try secondary EUI64 source. this basically is for ATM PVC */ if (altifp && get_hw_ifid(altifp, in6) == 0) { -#ifdef ND6_DEBUG - printf("%s: got interface identifier from %s\n", - if_name(ifp0), if_name(altifp)); -#endif + nd6log((LOG_DEBUG, "%s: got interface identifier from %s\n", + if_name(ifp0), if_name(altifp))); goto success; } @@ -291,21 +387,18 @@ get_ifid(ifp0, altifp, in6) * globally unique */ if (IFID_UNIVERSAL(in6)) { - -#ifdef ND6_DEBUG - printf("%s: borrow interface identifier from %s\n", - if_name(ifp0), if_name(ifp)); -#endif + nd6log((LOG_DEBUG, + "%s: borrow interface identifier from %s\n", + if_name(ifp0), if_name(ifp))); goto success; } } /* last resort: get from random number source */ if (get_rand_ifid(ifp, in6) == 0) { -#ifdef ND6_DEBUG - printf("%s: interface identifier generated by random number\n", - if_name(ifp0)); -#endif + nd6log((LOG_DEBUG, + "%s: interface identifier generated by random number\n", + if_name(ifp0))); goto success; } @@ -313,223 +406,152 @@ get_ifid(ifp0, altifp, in6) return -1; success: -#ifdef ND6_DEBUG - printf("%s: ifid: " + nd6log((LOG_INFO, "%s: ifid: " "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", if_name(ifp0), in6->s6_addr[8], in6->s6_addr[9], in6->s6_addr[10], in6->s6_addr[11], in6->s6_addr[12], in6->s6_addr[13], - in6->s6_addr[14], in6->s6_addr[15]); -#endif + in6->s6_addr[14], in6->s6_addr[15])); return 0; } -/* - * configure IPv6 interface address. XXX code duplicated with in.c - */ static int -in6_ifattach_addaddr(ifp, ia) +in6_ifattach_linklocal(ifp, altifp) struct ifnet *ifp; - struct in6_ifaddr *ia; + struct ifnet *altifp; /* secondary EUI64 source */ { - struct in6_ifaddr *oia; - struct ifaddr *ifa; - int error; - int rtflag; - struct in6_addr llsol; - - /* - * initialize if_addrlist, if we are the very first one - */ - ifa = TAILQ_FIRST(&ifp->if_addrlist); - if (ifa == NULL) { - TAILQ_INIT(&ifp->if_addrlist); - } + struct in6_ifaddr *ia; + struct in6_aliasreq ifra; + struct nd_prefix pr0; + int i, error; /* - * link the interface address to global list + * configure link-local address. */ - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - /* gain a refcnt for the link from if_addrlist */ - ia->ia_ifa.ifa_refcnt++; + bzero(&ifra, sizeof(ifra)); /* - * Also link into the IPv6 address chain beginning with in6_ifaddr. - * kazu opposed it, but itojun & jinmei wanted. + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. */ - if ((oia = in6_ifaddr) != NULL) { - for (; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else - in6_ifaddr = ia; - /* gain another refcnt for the link from in6_ifaddr */ - ia->ia_ifa.ifa_refcnt++; + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); - /* - * give the interface a chance to initialize, in case this - * is the first address to be added. - */ - if (ifp->if_ioctl != NULL) { - int s; - s = splimp(); - error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); - splx(s); - } else - error = 0; - if (error) { - switch (error) { - case EAFNOSUPPORT: - printf("%s: IPv6 not supported\n", if_name(ifp)); - break; - default: - printf("%s: SIOCSIFADDR error %d\n", if_name(ifp), - error); - break; + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); +#ifdef SCOPEDROUTING + ifra.ifra_addr.sin6_addr.s6_addr16[1] = 0 +#else + ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */ +#endif + ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0; + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0; + ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1); + } else { + if (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) { + nd6log((LOG_ERR, + "%s: no ifid available\n", if_name(ifp))); + return -1; } - - /* undo changes */ - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - IFAFREE(&ia->ia_ifa); - if (oia) - oia->ia_next = ia->ia_next; - else - in6_ifaddr = ia->ia_next; - IFAFREE(&ia->ia_ifa); - return -1; } +#ifdef SCOPEDROUTING + ifra.ifra_addr.sin6_scope_id = + in6_addr2scopeid(ifp, &ifra.ifra_addr.sin6_addr); +#endif - /* configure link-layer address resolution */ - rtflag = 0; - if (IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, &in6mask128)) - rtflag = RTF_HOST; - else { - switch (ifp->if_type) { - case IFT_LOOP: -#ifdef IFT_STF - case IFT_STF: + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + ifra.ifra_prefixmask.sin6_addr = in6mask64; +#ifdef SCOPEDROUTING + /* take into accound the sin6_scope_id field for routing */ + ifra.ifra_prefixmask.sin6_scope_id = 0xffffffff; #endif - rtflag = 0; - break; - default: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - rtflag = RTF_CLONING; - break; - } - } + /* link-local addresses should NEVER expire. */ + ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; - /* add route to the interface. */ - { - int e; - - e = rtrequest(RTM_ADD, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_prefixmask, - RTF_UP | rtflag, - (struct rtentry **)0); - if (e) { - printf("in6_ifattach_addaddr: rtrequest failed. errno = %d\n", e); - } - } - ia->ia_flags |= IFA_ROUTE; + /* + * Do not let in6_update_ifa() do DAD, since we need a random delay + * before sending an NS at the first time the inteface becomes up. + * Instead, in6_if_up() will start DAD with a proper random delay. + */ + ifra.ifra_flags |= IN6_IFF_NODAD; - if ((rtflag & RTF_CLONING) != 0 && - (ifp->if_flags & IFF_MULTICAST) != 0) { + /* + * Now call in6_update_ifa() to do a bunch of procedures to configure + * a link-local address. We can set NULL to the 3rd argument, because + * we know there's no other link-local address on the interface. + */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { /* - * join solicited multicast address + * XXX: When the interface does not support IPv6, this call + * would fail in the SIOCSIFADDR ioctl. I believe the + * notification is rather confusing in this case, so just + * supress it. (jinmei@kame.net 20010130) */ - bzero(&llsol, sizeof(llsol)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - (void)in6_addmulti(&llsol, ifp, &error); - - /* XXX should we run DAD on other interface types? */ - switch (ifp->if_type) { -#if 1 - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: -#else - default: -#endif - /* mark the address TENTATIVE, if needed. */ - ia->ia6_flags |= IN6_IFF_TENTATIVE; - /* nd6_dad_start() will be called in in6_if_up */ - } + if (error != EAFNOSUPPORT) + log(LOG_NOTICE, "in6_ifattach_linklocal: failed to " + "configure a link-local address on %s " + "(errno=%d)\n", + if_name(ifp), error); + return(-1); } - return 0; -} - -static int -in6_ifattach_linklocal(ifp, altifp) - struct ifnet *ifp; - struct ifnet *altifp; /*secondary EUI64 source*/ -{ - struct in6_ifaddr *ia; - /* - * configure link-local address + * Adjust ia6_flags so that in6_if_up will perform DAD. + * XXX: Some P2P interfaces seem not to send packets just after + * becoming up, so we skip p2p interfaces for safety. */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); - bzero((caddr_t)ia, sizeof(*ia)); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - if (ifp->if_flags & IFF_POINTOPOINT) - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - else - ia->ia_ifa.ifa_dstaddr = NULL; - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - - bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; -#ifdef SCOPEDROUTING - /* take into accound the sin6_scope_id field for routing */ - ia->ia_prefixmask.sin6_scope_id = 0xffffffff; -#endif - ia->ia_prefixmask.sin6_addr = in6mask64; - - /* just in case */ - bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); - ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_dstaddr.sin6_family = AF_INET6; - - bzero(&ia->ia_addr, sizeof(ia->ia_addr)); - ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); - ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - ia->ia_addr.sin6_addr.s6_addr32[1] = 0; - if (ifp->if_flags & IFF_LOOPBACK) { - ia->ia_addr.sin6_addr.s6_addr32[2] = 0; - ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); - } else { - if (get_ifid(ifp, altifp, &ia->ia_addr.sin6_addr) != 0) { -#ifdef ND6_DEBUG - printf("%s: no ifid available\n", if_name(ifp)); -#endif - free(ia, M_IFADDR); - return -1; - } + ia = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */ +#ifdef DIAGNOSTIC + if (!ia) { + panic("ia == NULL in in6_ifattach_linklocal"); + /*NOTREACHED*/ } -#ifdef SCOPEDROUTING - ia->ia_addr.sin6_scope_id = in6_addr2scopeid(ifp, - &ia->ia_addr.sin6_addr); #endif + if (in6if_do_dad(ifp) && (ifp->if_flags & IFF_POINTOPOINT) == 0) { + ia->ia6_flags &= ~IN6_IFF_NODAD; + ia->ia6_flags |= IN6_IFF_TENTATIVE; + } - ia->ia_ifa.ifa_metric = ifp->if_metric; - - if (in6_ifattach_addaddr(ifp, ia) != 0) { - /* ia will be freed on failure */ - return -1; + /* + * Make the link-local prefix (fe80::/64%link) as on-link. + * Since we'd like to manage prefixes separately from addresses, + * we make an ND6 prefix structure for the link-local prefix, + * and add it to the prefix list as a never-expire prefix. + * XXX: this change might affect some existing code base... + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + /* this should be 64 at this moment. */ + pr0.ndpr_plen = in6_mask2len(&ifra.ifra_prefixmask.sin6_addr, NULL); + pr0.ndpr_mask = ifra.ifra_prefixmask.sin6_addr; + pr0.ndpr_prefix = ifra.ifra_addr; + /* apply the mask for safety. (nd6_prelist_add will apply it again) */ + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + in6mask64.s6_addr32[i]; + } + /* + * Initialize parameters. The link-local prefix must always be + * on-link, and its lifetimes never expire. + */ + pr0.ndpr_raf_onlink = 1; + pr0.ndpr_raf_auto = 1; /* probably meaningless */ + pr0.ndpr_vltime = ND6_INFINITE_LIFETIME; + pr0.ndpr_pltime = ND6_INFINITE_LIFETIME; + /* + * Since there is no other link-local addresses, nd6_prefix_lookup() + * probably returns NULL. However, we cannot always expect the result. + * For example, if we first remove the (only) existing link-local + * address, and then reconfigure another one, the prefix is still + * valid with referring to the old link-local address. + */ + if (nd6_prefix_lookup(&pr0) == NULL) { + if ((error = nd6_prelist_add(&pr0, NULL, NULL)) != 0) + return(error); } return 0; @@ -539,47 +561,52 @@ static int in6_ifattach_loopback(ifp) struct ifnet *ifp; /* must be IFT_LOOP */ { - struct in6_ifaddr *ia; + struct in6_aliasreq ifra; + int error; + + bzero(&ifra, sizeof(ifra)); /* - * configure link-local address + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); - bzero((caddr_t)ia, sizeof(*ia)); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - - bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; - ia->ia_prefixmask.sin6_addr = in6mask128; + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); + + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + ifra.ifra_prefixmask.sin6_addr = in6mask128; /* * Always initialize ia_dstaddr (= broadcast address) to loopback - * address, to make getifaddr happier. - * - * For BSDI, it is mandatory. The BSDI version of - * ifa_ifwithroute() rejects to add a route to the loopback - * interface. Even for other systems, loopback looks somewhat - * special. + * address. Follows IPv4 practice - see in_ifinit(). */ - bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); - ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_dstaddr.sin6_family = AF_INET6; - ia->ia_dstaddr.sin6_addr = in6addr_loopback; + ifra.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_dstaddr.sin6_family = AF_INET6; + ifra.ifra_dstaddr.sin6_addr = in6addr_loopback; - bzero(&ia->ia_addr, sizeof(ia->ia_addr)); - ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_addr = in6addr_loopback; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_addr = in6addr_loopback; - ia->ia_ifa.ifa_metric = ifp->if_metric; + /* the loopback address should NEVER expire. */ + ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; - if (in6_ifattach_addaddr(ifp, ia) != 0) { - /* ia will be freed on failure */ - return -1; + /* we don't need to perfrom DAD on loopback interfaces. */ + ifra.ifra_flags |= IN6_IFF_NODAD; + + /* skip registration to the prefix list. XXX should be temporary. */ + ifra.ifra_flags |= IN6_IFF_NOPFX; + + /* + * We can set NULL to the 3rd arg. See comments in + * in6_ifattach_linklocal(). + */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { + log(LOG_ERR, "in6_ifattach_loopback: failed to configure " + "the loopback address on %s (errno=%d)\n", + if_name(ifp), error); + return(-1); } return 0; @@ -591,17 +618,19 @@ in6_ifattach_loopback(ifp) * * when ifp == NULL, the caller is responsible for filling scopeid. */ -static int -nigroup(ifp, name, namelen, in6) +int +in6_nigroup(ifp, name, namelen, in6) struct ifnet *ifp; const char *name; int namelen; struct in6_addr *in6; { const char *p; + u_char *q; MD5_CTX ctxt; u_int8_t digest[16]; char l; + char n[64]; /* a single label must not exceed 63 chars */ if (!namelen || !name) return -1; @@ -609,16 +638,21 @@ nigroup(ifp, name, namelen, in6) p = name; while (p && *p && *p != '.' && p - name < namelen) p++; - if (p - name > 63) + if (p - name > sizeof(n) - 1) return -1; /*label too long*/ l = p - name; + strncpy(n, name, l); + n[(int)l] = '\0'; + for (q = n; *q; q++) { + if ('A' <= *q && *q <= 'Z') + *q = *q - 'A' + 'a'; + } /* generate 8 bytes of pseudo-random value. */ bzero(&ctxt, sizeof(ctxt)); MD5Init(&ctxt); MD5Update(&ctxt, &l, sizeof(l)); - /* LINTED const cast */ - MD5Update(&ctxt, (void *)name, p - name); + MD5Update(&ctxt, n, l); MD5Final(digest, &ctxt); bzero(in6, sizeof(*in6)); @@ -644,15 +678,21 @@ in6_nigroup_attach(name, namelen) bzero(&mltaddr, sizeof(mltaddr)); mltaddr.sin6_family = AF_INET6; mltaddr.sin6_len = sizeof(struct sockaddr_in6); - if (nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) + if (in6_nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) return; for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) { mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); - if (!in6m) - (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + if (!in6m) { + if (!in6_addmulti(&mltaddr.sin6_addr, ifp, &error)) { + nd6log((LOG_ERR, "%s: failed to join %s " + "(errno=%d)\n", if_name(ifp), + ip6_sprintf(&mltaddr.sin6_addr), + error)); + } + } } } @@ -668,7 +708,7 @@ in6_nigroup_detach(name, namelen) bzero(&mltaddr, sizeof(mltaddr)); mltaddr.sin6_family = AF_INET6; mltaddr.sin6_len = sizeof(struct sockaddr_in6); - if (nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) + if (in6_nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) return; for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) @@ -691,13 +731,16 @@ in6_ifattach(ifp, altifp) struct ifnet *altifp; /* secondary EUI64 source */ { static size_t if_indexlim = 8; - struct sockaddr_in6 mltaddr; - struct sockaddr_in6 mltmask; - struct sockaddr_in6 gate; - struct sockaddr_in6 mask; struct in6_ifaddr *ia; struct in6_addr in6; - int hostnamelen = strlen(hostname); + + /* some of the interfaces are inherently not IPv6 capable */ + switch (ifp->if_type) { +#ifdef IFT_BRIDGE /*OpenBSD 2.8*/ + case IFT_BRIDGE: + return; +#endif + } /* * We have some arrays that should be indexed by if_index. @@ -763,134 +806,41 @@ in6_ifattach(ifp, altifp) * usually, we require multicast capability to the interface */ if ((ifp->if_flags & IFF_MULTICAST) == 0) { - printf("%s: not multicast capable, IPv6 not enabled\n", + log(LOG_INFO, "in6_ifattach: " + "%s is not multicast capable, IPv6 not enabled\n", if_name(ifp)); return; } /* - * assign link-local address, if there's none - */ - ia = in6ifa_ifpforlinklocal(ifp, 0); - if (ia == NULL) { - if (in6_ifattach_linklocal(ifp, altifp) != 0) - return; - ia = in6ifa_ifpforlinklocal(ifp, 0); - - if (ia == NULL) { - printf("%s: failed to add link-local address\n", - if_name(ifp)); - - /* we can't initialize multicasts without link-local */ - goto statinit; - } - } - - if (ifp->if_flags & IFF_POINTOPOINT) { - /* - * route local address to loopback - */ - bzero(&gate, sizeof(gate)); - gate.sin6_len = sizeof(struct sockaddr_in6); - gate.sin6_family = AF_INET6; - gate.sin6_addr = in6addr_loopback; - bzero(&mask, sizeof(mask)); - mask.sin6_len = sizeof(struct sockaddr_in6); - mask.sin6_family = AF_INET6; - mask.sin6_addr = in6mask64; - rtrequest(RTM_ADD, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&gate, - (struct sockaddr *)&mask, - RTF_UP|RTF_HOST, - (struct rtentry **)0); - } - - /* - * assign loopback address for loopback interface - * XXX multiple loopback interface case + * assign loopback address for loopback interface. + * XXX multiple loopback interface case. */ - in6 = in6addr_loopback; - if (ifp->if_flags & IFF_LOOPBACK) { + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + in6 = in6addr_loopback; if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { if (in6_ifattach_loopback(ifp) != 0) return; } } -#ifdef DIAGNOSTIC - if (!ia) { - panic("ia == NULL in in6_ifattach"); - /*NOTREACHED*/ - } -#endif - /* - * join multicast + * assign a link-local address, if there's none. */ - if (ifp->if_flags & IFF_MULTICAST) { - int error; /* not used */ - struct in6_multi *in6m; - - bzero(&mltmask, sizeof(mltmask)); - mltmask.sin6_len = sizeof(struct sockaddr_in6); - mltmask.sin6_family = AF_INET6; - mltmask.sin6_addr = in6mask32; - - /* - * join link-local all-nodes address - */ - bzero(&mltaddr, sizeof(mltaddr)); - mltaddr.sin6_len = sizeof(struct sockaddr_in6); - mltaddr.sin6_family = AF_INET6; - mltaddr.sin6_addr = in6addr_linklocal_allnodes; - mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - - IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); - if (in6m == NULL) { - rtrequest(RTM_ADD, - (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, - RTF_UP|RTF_CLONING, /* xxx */ - (struct rtentry **)0); - (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); - } - - /* - * join node information group address - */ - if (nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) - == 0) { - IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); - if (in6m == NULL && ia != NULL) { - (void)in6_addmulti(&mltaddr.sin6_addr, - ifp, &error); - } - } - - if (ifp->if_flags & IFF_LOOPBACK) { - in6 = in6addr_loopback; - ia = in6ifa_ifpwithaddr(ifp, &in6); - /* - * join node-local all-nodes address, on loopback - */ - mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - - IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); - if (in6m == NULL && ia != NULL) { - rtrequest(RTM_ADD, - (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, - RTF_UP, - (struct rtentry **)0); - (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + if (ip6_auto_linklocal) { + ia = in6ifa_ifpforlinklocal(ifp, 0); + if (ia == NULL) { + if (in6_ifattach_linklocal(ifp, altifp) == 0) { + /* linklocal address assigned */ + } else { + /* failed to assign linklocal address. bark? */ } } } -statinit:; +#ifdef IFT_STF /* XXX */ +statinit: +#endif /* update dynamically. */ if (in6_maxmtu < ifp->if_mtu) @@ -913,6 +863,8 @@ statinit:; /* * NOTE: in6_ifdetach() does not support loopback if at this moment. + * We don't need this function in bsdi, because interfaces are never removed + * from the ifnet list in bsdi. */ void in6_ifdetach(ifp) @@ -938,7 +890,7 @@ in6_ifdetach(ifp) next = ifa->ifa_list.tqe_next; if (ifa->ifa_addr->sa_family != AF_INET6) continue; - in6_purgeaddr(ifa, ifp); + in6_purgeaddr(ifa); } /* undo everything done by in6_ifattach(), just in case */ @@ -979,11 +931,11 @@ in6_ifdetach(ifp) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; -#ifdef ND6_DEBUG - else - printf("%s: didn't unlink in6ifaddr from " - "list\n", if_name(ifp)); -#endif + else { + nd6log((LOG_ERR, + "%s: didn't unlink in6ifaddr from " + "list\n", if_name(ifp))); + } } IFAFREE(&oia->ia_ifa); @@ -998,7 +950,14 @@ in6_ifdetach(ifp) in6m = NULL; } - /* remove neighbor management table */ + /* + * remove neighbor management table. we call it twice just to make + * sure we nuke everything. maybe we need just one call. + * XXX: since the first call did not release addresses, some prefixes + * might remain. We should call nd6_purge() again to release the + * prefixes after removing all addresses above. + * (Or can we just delay calling nd6_purge until at this point?) + */ nd6_purge(ifp); /* remove route to link-local allnodes multicast (ff02::1) */ @@ -1014,3 +973,60 @@ in6_ifdetach(ifp) rtfree(rt); } } + +void +in6_get_tmpifid(ifp, retbuf, baseid, generate) + struct ifnet *ifp; + u_int8_t *retbuf; + const u_int8_t *baseid; + int generate; +{ + u_int8_t nullbuf[8]; + struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; + + bzero(nullbuf, sizeof(nullbuf)); + if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) == 0) { + /* we've never created a random ID. Create a new one. */ + generate = 1; + } + + if (generate) { + bcopy(baseid, ndi->randomseed1, sizeof(ndi->randomseed1)); + + /* generate_tmp_ifid will update seedn and buf */ + (void)generate_tmp_ifid(ndi->randomseed0, ndi->randomseed1, + ndi->randomid); + } + bcopy(ndi->randomid, retbuf, 8); +} + +void +in6_tmpaddrtimer(ignored_arg) + void *ignored_arg; +{ + int i; + struct nd_ifinfo *ndi; + u_int8_t nullbuf[8]; + int s = splnet(); + + callout_reset(&in6_tmpaddrtimer_ch, + (ip6_temp_preferred_lifetime - ip6_desync_factor - + ip6_temp_regen_advance) * hz, + in6_tmpaddrtimer, NULL); + + bzero(nullbuf, sizeof(nullbuf)); + for (i = 1; i < if_index + 1; i++) { + ndi = &nd_ifinfo[i]; + if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) != 0) { + /* + * We've been generating a random ID on this interface. + * Create a new one. + */ + (void)generate_tmp_ifid(ndi->randomseed0, + ndi->randomseed1, + ndi->randomid); + } + } + + splx(s); +} diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h index 4f82e4153085..fa4434f48a44 100644 --- a/sys/netinet6/in6_ifattach.h +++ b/sys/netinet6/in6_ifattach.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_ifattach.h,v 1.10 2000/05/27 02:57:05 itojun Exp $ */ +/* $KAME: in6_ifattach.h,v 1.14 2001/02/08 12:48:39 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -38,6 +38,9 @@ void in6_nigroup_attach __P((const char *, int)); void in6_nigroup_detach __P((const char *, int)); void in6_ifattach __P((struct ifnet *, struct ifnet *)); void in6_ifdetach __P((struct ifnet *)); +void in6_get_tmpifid __P((struct ifnet *, u_int8_t *, const u_int8_t *, int)); +void in6_tmpaddrtimer __P((void *)); +int in6_nigroup __P((struct ifnet *, const char *, int, struct in6_addr *)); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_IFATTACH_H_ */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 67409bed1c2b..9175a65431fa 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_pcb.c,v 1.8 2000/06/09 00:37:02 itojun Exp $ */ +/* $KAME: in6_pcb.c,v 1.31 2001/05/21 05:45:10 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -66,6 +66,8 @@ * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include <sys/param.h> @@ -99,12 +101,19 @@ #include <netinet6/in6_pcb.h> #include "faith.h" +#if defined(NFAITH) && NFAITH > 0 +#include <net/if_faith.h> +#endif #ifdef IPSEC #include <netinet6/ipsec.h> -#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ipsec6.h> +#endif +#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> +#endif #include <netkey/key.h> #endif /* IPSEC */ @@ -165,11 +174,12 @@ in6_pcbbind(inp, nam, p) /* * XXX: bind to an anycast address might accidentally * cause sending a packet with anycast source address. + * We should allow to bind to a deprecated address, since + * the application dare to use it. */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & - (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| - IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { + (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) { return(EADDRNOTAVAIL); } } @@ -193,7 +203,7 @@ in6_pcbbind(inp, nam, p) (so->so_cred->cr_uid != t->inp_socket->so_cred->cr_uid)) return (EADDRINUSE); - if (ip6_mapped_addr_on != 0 && + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; @@ -215,7 +225,7 @@ in6_pcbbind(inp, nam, p) lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return(EADDRINUSE); - if (ip6_mapped_addr_on != 0 && + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; @@ -247,7 +257,6 @@ in6_pcbbind(inp, nam, p) return (EAGAIN); } } - inp->in6p_flowinfo = sin6 ? sin6->sin6_flowinfo : 0; /*XXX*/ return(0); } @@ -270,7 +279,6 @@ in6_pcbladdr(inp, nam, plocal_addr6) struct in6_addr **plocal_addr6; { register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; - struct in6_pktinfo *pi; struct ifnet *ifp = NULL; int error = 0; @@ -361,15 +369,11 @@ in6_pcbconnect(inp, nam, p) } inp->in6p_faddr = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; - /* - * xxx kazu flowlabel is necessary for connect? - * but if this line is missing, the garbage value remains. - */ - inp->in6p_flowinfo = sin6->sin6_flowinfo; - if ((inp->in6p_flowinfo & IPV6_FLOWLABEL_MASK) == 0 && - ip6_auto_flowlabel != 0) + /* update flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ + inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; + if (inp->in6p_flags & IN6P_AUTOFLOWLABEL) inp->in6p_flowinfo |= - (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); + (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); in_pcbrehash(inp); return (0); @@ -521,11 +525,14 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) } if (ro->ro_rt == (struct rtentry *)0 || ro->ro_rt->rt_ifp == (struct ifnet *)0) { + struct sockaddr_in6 *dst6; + /* No route yet, so try to acquire one */ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); - ro->ro_dst.sin6_family = AF_INET6; - ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro->ro_dst.sin6_addr = *dst; + dst6 = (struct sockaddr_in6 *)&ro->ro_dst; + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(struct sockaddr_in6); + dst6->sin6_addr = *dst; if (IN6_IS_ADDR_MULTICAST(dst)) { ro->ro_rt = rtalloc1(&((struct route *)ro) ->ro_dst, 0, 0UL); @@ -584,6 +591,8 @@ in6_pcbdisconnect(inp) { bzero((caddr_t)&inp->in6p_faddr, sizeof(inp->in6p_faddr)); inp->inp_fport = 0; + /* clear flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ + inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; in_pcbrehash(inp); if (inp->inp_socket->so_state & SS_NOFDREF) in6_pcbdetach(inp); @@ -604,20 +613,13 @@ in6_pcbdetach(inp) in_pcbremlists(inp); sotoinpcb(so) = 0; sofree(so); + if (inp->in6p_options) m_freem(inp->in6p_options); - if (inp->in6p_outputopts) { - if (inp->in6p_outputopts->ip6po_rthdr && - inp->in6p_outputopts->ip6po_route.ro_rt) - RTFREE(inp->in6p_outputopts->ip6po_route.ro_rt); - if (inp->in6p_outputopts->ip6po_m) - (void)m_free(inp->in6p_outputopts->ip6po_m); - free(inp->in6p_outputopts, M_IP6OPT); - } + ip6_freepcbopts(inp->in6p_outputopts); + ip6_freemoptions(inp->in6p_moptions); if (inp->in6p_route.ro_rt) rtfree(inp->in6p_route.ro_rt); - ip6_freemoptions(inp->in6p_moptions); - /* Check and free IPv4 related resources in case of mapped addr */ if (inp->inp_options) (void)m_free(inp->inp_options); @@ -761,27 +763,33 @@ in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam) * Must be called at splnet. */ void -in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) +in6_pcbnotify(head, dst, fport_arg, src, lport_arg, cmd, notify) struct inpcbhead *head; - struct sockaddr *dst; + struct sockaddr *dst, *src; u_int fport_arg, lport_arg; - struct in6_addr *laddr6; int cmd; void (*notify) __P((struct inpcb *, int)); { struct inpcb *inp, *ninp; - struct in6_addr faddr6; + struct sockaddr_in6 sa6_src, *sa6_dst; u_short fport = fport_arg, lport = lport_arg; + u_int32_t flowinfo; int errno, s; - int do_rtchange = (notify == in6_rtchange); if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6) return; - faddr6 = ((struct sockaddr_in6 *)dst)->sin6_addr; - if (IN6_IS_ADDR_UNSPECIFIED(&faddr6)) + + sa6_dst = (struct sockaddr_in6 *)dst; + if (IN6_IS_ADDR_UNSPECIFIED(&sa6_dst->sin6_addr)) return; /* + * note that src can be NULL when we get notify by local fragmentation. + */ + sa6_src = (src == NULL) ? sa6_any : *(struct sockaddr_in6 *)src; + flowinfo = sa6_src.sin6_flowinfo; + + /* * Redirects go to all references to the destination, * and use in6_rtchange to invalidate the route cache. * Dead host indications: also use in6_rtchange to invalidate @@ -792,44 +800,45 @@ in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { fport = 0; lport = 0; - bzero((caddr_t)laddr6, sizeof(*laddr6)); + bzero((caddr_t)&sa6_src.sin6_addr, sizeof(sa6_src.sin6_addr)); - do_rtchange = 1; + if (cmd != PRC_HOSTDEAD) + notify = in6_rtchange; } errno = inet6ctlerrmap[cmd]; s = splnet(); for (inp = LIST_FIRST(head); inp != NULL; inp = ninp) { ninp = LIST_NEXT(inp, inp_list); - if ((inp->inp_vflag & INP_IPV6) == NULL) + if ((inp->inp_vflag & INP_IPV6) == 0) continue; - if (do_rtchange) { - /* - * Since a non-connected PCB might have a cached route, - * we always call in6_rtchange without matching - * the PCB to the src/dst pair. - * - * XXX: we assume in6_rtchange does not free the PCB. - */ - if (IN6_ARE_ADDR_EQUAL(&inp->in6p_route.ro_dst.sin6_addr, - &faddr6)) - in6_rtchange(inp, errno); - - if (notify == in6_rtchange) - continue; /* there's nothing to do any more */ - } - - if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &faddr6) || - inp->inp_socket == 0 || - (lport && inp->inp_lport != lport) || - (!IN6_IS_ADDR_UNSPECIFIED(laddr6) && - !IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr6)) || - (fport && inp->inp_fport != fport)) + /* + * Detect if we should notify the error. If no source and + * destination ports are specifed, but non-zero flowinfo and + * local address match, notify the error. This is the case + * when the error is delivered with an encrypted buffer + * by ESP. Otherwise, just compare addresses and ports + * as usual. + */ + if (lport == 0 && fport == 0 && flowinfo && + inp->inp_socket != NULL && + flowinfo == (inp->in6p_flowinfo & IPV6_FLOWLABEL_MASK) && + IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, &sa6_src.sin6_addr)) + goto do_notify; + else if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, + &sa6_dst->sin6_addr) || + inp->inp_socket == 0 || + (lport && inp->inp_lport != lport) || + (!IN6_IS_ADDR_UNSPECIFIED(&sa6_src.sin6_addr) && + !IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, + &sa6_src.sin6_addr)) || + (fport && inp->inp_fport != fport)) continue; + do_notify: if (notify) - (*notify)(inp, errno); + (*notify)(inp, errno); } splx(s); } @@ -990,6 +999,13 @@ in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp) struct inpcbhead *head; register struct inpcb *inp; u_short fport = fport_arg, lport = lport_arg; + int faith; + +#if defined(NFAITH) && NFAITH > 0 + faith = faithprefix(laddr); +#else + faith = 0; +#endif /* * First look for an exact match. @@ -1020,11 +1036,8 @@ in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp) continue; if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && inp->inp_lport == lport) { -#if defined(NFAITH) && NFAITH > 0 - if (ifp && ifp->if_type == IFT_FAITH && - (inp->inp_flags & INP_FAITH) == 0) + if (faith && (inp->inp_flags & INP_FAITH) == 0) continue; -#endif if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr)) return (inp); diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h index 2ea310728a17..15cd033ff0a2 100644 --- a/sys/netinet6/in6_pcb.h +++ b/sys/netinet6/in6_pcb.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_pcb.h,v 1.5 2000/07/03 06:19:53 itojun Exp $ */ +/* $KAME: in6_pcb.h,v 1.13 2001/02/06 09:16:53 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -90,7 +90,7 @@ struct inpcb * struct in6_addr *, u_int, struct in6_addr *, u_int, int, struct ifnet *)); void in6_pcbnotify __P((struct inpcbhead *, struct sockaddr *, - u_int, struct in6_addr *, u_int, int, + u_int, struct sockaddr *, u_int, int, void (*)(struct inpcb *, int))); void in6_rtchange __P((struct inpcb *, int)); int in6_setpeeraddr __P((struct socket *so, struct sockaddr **nam)); @@ -105,11 +105,6 @@ struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *, int in6_selecthlim __P((struct in6pcb *, struct ifnet *)); int in6_pcbsetport __P((struct in6_addr *, struct inpcb *, struct proc *)); void init_sin6 __P((struct sockaddr_in6 *sin6, struct mbuf *m)); - -int in6_embedscope __P((struct in6_addr *, const struct sockaddr_in6 *, - struct inpcb *, struct ifnet **)); -int in6_recoverscope __P((struct sockaddr_in6 *, const struct in6_addr *, - struct ifnet *)); #endif /* _KERNEL */ #endif /* !_NETINET6_IN6_PCB_H_ */ diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c index 1eaea506bf98..e3325f9325cd 100644 --- a/sys/netinet6/in6_prefix.c +++ b/sys/netinet6/in6_prefix.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_prefix.c,v 1.30 2000/06/12 14:53:17 jinmei Exp $ */ +/* $KAME: in6_prefix.c,v 1.47 2001/03/25 08:41:39 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -88,6 +88,8 @@ static MALLOC_DEFINE(M_RR_ADDR, "rp_addr", "IPv6 Router Renumbering Ifid"); struct rr_prhead rr_prefix; +struct callout in6_rr_timer_ch; + #include <net/net_osdep.h> static void add_each_addr __P((struct socket *so, struct rr_prefix *rpp, @@ -399,7 +401,7 @@ assign_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen); /* link to ia, and put into list */ rap->ra_addr = ia; - rap->ra_addr->ia_ifa.ifa_refcnt++; + IFAREF(&rap->ra_addr->ia_ifa); #if 0 /* Can't do this now, because rpp may be on th stack. should fix it? */ ia->ia6_ifpr = rp2ifpr(rpp); #endif @@ -465,7 +467,7 @@ in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) if (ifpr == NULL) { struct rr_prefix rp; struct socket so; - int pplen = (plen == 128) ? 64 : plen; + int pplen = (plen == 128) ? 64 : plen; /* XXX hardcoded 64 is bad */ /* allocate a prefix for ia, with default properties */ @@ -514,11 +516,11 @@ in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) if (rap != NULL) { if (rap->ra_addr == NULL) { rap->ra_addr = ia; - rap->ra_addr->ia_ifa.ifa_refcnt++; + IFAREF(&rap->ra_addr->ia_ifa); } else if (rap->ra_addr != ia) { /* There may be some inconsistencies between addrs. */ log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix" - "has already another ia %p(%s) on its ifid list\n", + " already has another ia %p(%s) on its ifid list\n", ip6_sprintf(IA6_IN6(ia)), plen, rap->ra_addr, ip6_sprintf(IA6_IN6(rap->ra_addr))); @@ -599,6 +601,14 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen); /* don't care ifra_flags for now */ + /* + * XXX: if we did this with finite lifetime values, the lifetimes would + * decrese in time and never incremented. + * we should need more clarifications on the prefix mechanism... + */ + ifra.ifra_lifetime.ia6t_vltime = rpp->rp_vltime; + ifra.ifra_lifetime.ia6t_pltime = rpp->rp_pltime; + ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr); if (ia6 != NULL) { if (ia6->ia6_ifpr == NULL) { @@ -606,7 +616,7 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) if (rap->ra_addr) IFAFREE(&rap->ra_addr->ia_ifa); rap->ra_addr = ia6; - rap->ra_addr->ia_ifa.ifa_refcnt++; + IFAREF(&rap->ra_addr->ia_ifa); ia6->ia6_ifpr = rp2ifpr(rpp); return; } @@ -614,7 +624,7 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) if (rap->ra_addr) IFAFREE(&rap->ra_addr->ia_ifa); rap->ra_addr = ia6; - rap->ra_addr->ia_ifa.ifa_refcnt++; + IFAREF(&rap->ra_addr->ia_ifa); return; } /* @@ -627,24 +637,26 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) * Or, completely duplicated prefixes? * log it and return. */ - log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" - "%s/%d failed because there is already another addr %s/%d\n", + log(LOG_ERR, + "in6_prefix.c: add_each_addr: addition of an addr %s/%d " + "failed because there is already another addr %s/%d\n", ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, ip6_sprintf(IA6_IN6(ia6)), - in6_mask2len(&ia6->ia_prefixmask.sin6_addr)); + in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL)); return; } /* propagate ANYCAST flag if it is set for ancestor addr */ if (rap->ra_flags.anycast != 0) ifra.ifra_flags |= IN6_IFF_ANYCAST; error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp, - curproc); - if (error != 0) + curproc); + if (error != 0) { log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" "%s/%d failed because in6_control failed for error %d\n", ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, error); return; + } /* * link beween this addr and the prefix will be done @@ -957,8 +969,10 @@ delete_each_prefix(struct rr_prefix *rpp, u_char origin) s = splnet(); rap = LIST_FIRST(&rpp->rp_addrhead); - if (rap == NULL) + if (rap == NULL) { + splx(s); break; + } LIST_REMOVE(rap, ra_entry); splx(s); if (rap->ra_addr == NULL) { @@ -967,7 +981,7 @@ delete_each_prefix(struct rr_prefix *rpp, u_char origin) } rap->ra_addr->ia6_ifpr = NULL; - in6_purgeaddr(&rap->ra_addr->ia_ifa, rpp->rp_ifp); + in6_purgeaddr(&rap->ra_addr->ia_ifa); IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); } @@ -1166,7 +1180,8 @@ in6_rr_timer(void *ignored_arg) int s; struct rr_prefix *rpp; - timeout(in6_rr_timer, (caddr_t)0, ip6_rr_prune * hz); + callout_reset(&in6_rr_timer_ch, ip6_rr_prune * hz, + in6_rr_timer, NULL); s = splnet(); /* expire */ diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h index 2ce18db551b3..3ae6a63640a4 100644 --- a/sys/netinet6/in6_prefix.h +++ b/sys/netinet6/in6_prefix.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_prefix.h,v 1.6 2000/03/25 07:23:45 sumikawa Exp $ */ +/* $KAME: in6_prefix.h,v 1.10 2001/02/08 16:30:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998 and 1999 WIDE Project. @@ -30,6 +30,8 @@ * SUCH DAMAGE. */ +#include <sys/callout.h> + struct rr_prefix { struct ifprefix rp_ifpr; LIST_ENTRY(rr_prefix) rp_entry; @@ -85,4 +87,5 @@ LIST_HEAD(rr_prhead, rr_prefix); extern struct rr_prhead rr_prefix; void in6_rr_timer __P((void *)); +extern struct callout in6_rr_timer_ch; int delete_each_prefix __P((struct rr_prefix *rpp, u_char origin)); diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c index b221f8afd83b..e73e4bb70ea3 100644 --- a/sys/netinet6/in6_proto.c +++ b/sys/netinet6/in6_proto.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_proto.c,v 1.64 2000/06/20 16:20:27 itojun Exp $ */ +/* $KAME: in6_proto.c,v 1.91 2001/05/27 13:28:35 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -99,25 +99,31 @@ #include <netinet/udp.h> #include <netinet/udp_var.h> #include <netinet6/tcp6_var.h> - +#include <netinet6/raw_ip6.h> #include <netinet6/udp6_var.h> - #include <netinet6/pim6_var.h> - #include <netinet6/nd6.h> #include <netinet6/in6_prefix.h> #ifdef IPSEC #include <netinet6/ipsec.h> +#ifdef INET6 #include <netinet6/ipsec6.h> +#endif #include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> +#endif #ifdef IPSEC_ESP #include <netinet6/esp.h> +#ifdef INET6 #include <netinet6/esp6.h> #endif +#endif #include <netinet6/ipcomp.h> +#ifdef INET6 #include <netinet6/ipcomp6.h> +#endif #endif /*IPSEC*/ #include <netinet6/ip6protosw.h> @@ -136,6 +142,9 @@ extern struct domain inet6domain; static struct pr_usrreqs nousrreqs; +#define PR_LISTEN 0 +#define PR_ABRTACPTDIS 0 + struct ip6protosw inet6sw[] = { { 0, &inet6domain, IPPROTO_IPV6, 0, 0, 0, 0, 0, @@ -143,30 +152,30 @@ struct ip6protosw inet6sw[] = { ip6_init, 0, frag6_slowtimo, frag6_drain, &nousrreqs, }, -{ SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC | PR_ADDR, +{ SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR, udp6_input, 0, udp6_ctlinput, ip6_ctloutput, 0, 0, 0, 0, 0, &udp6_usrreqs, }, -{ SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED | PR_WANTRCVD, +{ SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD|PR_LISTEN, tcp6_input, 0, tcp6_ctlinput, tcp_ctloutput, 0, -#ifdef INET /* don't call timeout routines twice */ - tcp_init, 0, 0, tcp_drain, +#ifdef INET /* don't call initialization and timeout routines twice */ + 0, 0, 0, tcp_drain, #else tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain, #endif &tcp6_usrreqs, }, -{ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC | PR_ADDR, +{ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR, rip6_input, rip6_output, rip6_ctlinput, rip6_ctloutput, 0, 0, 0, 0, 0, &rip6_usrreqs }, -{ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC | PR_ADDR, - icmp6_input, rip6_output, 0, rip6_ctloutput, +{ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC|PR_ADDR|PR_LASTHDR, + icmp6_input, rip6_output, rip6_ctlinput, rip6_ctloutput, 0, icmp6_init, icmp6_fasttimo, 0, 0, &rip6_usrreqs @@ -191,15 +200,17 @@ struct ip6protosw inet6sw[] = { }, #ifdef IPSEC { SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR, - ah6_input, 0, 0, 0, + ah6_input, 0, 0, 0, 0, 0, 0, 0, 0, &nousrreqs, }, #ifdef IPSEC_ESP { SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR, - esp6_input, 0, 0, 0, - 0, + esp6_input, 0, + esp6_ctlinput, + 0, + 0, 0, 0, 0, 0, &nousrreqs, }, @@ -212,34 +223,30 @@ struct ip6protosw inet6sw[] = { }, #endif /* IPSEC */ #ifdef INET -{ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR|PR_LASTHDR, encap6_input, rip6_output, 0, rip6_ctloutput, 0, - 0, 0, 0, 0, + encap_init, 0, 0, 0, &rip6_usrreqs }, #endif /*INET*/ -{ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, - encap6_input, rip6_output, 0, rip6_ctloutput, +{ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR|PR_LASTHDR, + encap6_input, rip6_output, 0, rip6_ctloutput, 0, -#ifndef INET encap_init, 0, 0, 0, -#else - 0, 0, 0, 0, -#endif &rip6_usrreqs }, -{ SOCK_RAW, &inet6domain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR, - pim6_input, rip6_output, 0, rip6_ctloutput, +{ SOCK_RAW, &inet6domain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR|PR_LASTHDR, + pim6_input, rip6_output, 0, rip6_ctloutput, 0, 0, 0, 0, 0, &rip6_usrreqs }, /* raw wildcard */ -{ SOCK_RAW, &inet6domain, 0, PR_ATOMIC | PR_ADDR, +{ SOCK_RAW, &inet6domain, 0, PR_ATOMIC|PR_ADDR, rip6_input, rip6_output, 0, rip6_ctloutput, - 0, 0, - 0, 0, 0, + 0, + 0, 0, 0, 0, &rip6_usrreqs }, }; @@ -300,7 +307,7 @@ int ip6_gif_hlim = 0; int ip6_use_deprecated = 1; /* allow deprecated addr (RFC2462 5.5.4) */ int ip6_rr_prune = 5; /* router renumbering prefix * walk list every 5 sec. */ -int ip6_mapped_addr_on = 1; +int ip6_v6only = 0; u_int32_t ip6_id = 0UL; int ip6_keepfaith = 0; @@ -328,84 +335,8 @@ u_long rip6_recvspace = RIPV6RCVQ; /* ICMPV6 parameters */ int icmp6_rediraccept = 1; /* accept and process redirects */ int icmp6_redirtimeout = 10 * 60; /* 10 minutes */ -struct timeval icmp6errratelim = { 0, 0 }; /* no ratelimit */ int icmp6errppslim = 100; /* 100pps */ -int icmp6_nodeinfo = 1; /* enable/disable NI response */ - -#ifdef TCP6 -/* TCP on IP6 parameters */ -int tcp6_sendspace = 1024 * 8; -int tcp6_recvspace = 1024 * 8; -int tcp6_mssdflt = TCP6_MSS; -int tcp6_rttdflt = TCP6TV_SRTTDFLT / PR_SLOWHZ; -int tcp6_do_rfc1323 = 1; -int tcp6_conntimeo = TCP6TV_KEEP_INIT; /* initial connection timeout */ -int tcp6_43maxseg = 0; -int tcp6_pmtu = 0; - -/* - * Parameters for keepalive option. - * Connections for which SO_KEEPALIVE is set will be probed - * after being idle for a time of tcp6_keepidle (in units of PR_SLOWHZ). - * Starting at that time, the connection is probed at intervals - * of tcp6_keepintvl (same units) until a response is received - * or until tcp6_keepcnt probes have been made, at which time - * the connection is dropped. Note that a tcp6_keepidle value - * under 2 hours is nonconformant with RFC-1122, Internet Host Requirements. - */ -int tcp6_keepidle = TCP6TV_KEEP_IDLE; /* time before probing idle */ -int tcp6_keepintvl = TCP6TV_KEEPINTVL; /* interval betwn idle probes */ -int tcp6_keepcnt = TCP6TV_KEEPCNT; /* max idle probes */ -int tcp6_maxpersistidle = TCP6TV_KEEP_IDLE; /* max idle time in persist */ - -#ifndef INET_SERVER -#define TCP6_LISTEN_HASH_SIZE 17 -#define TCP6_CONN_HASH_SIZE 97 -#define TCP6_SYN_HASH_SIZE 293 -#define TCP6_SYN_BUCKET_SIZE 35 -#else -#define TCP6_LISTEN_HASH_SIZE 97 -#define TCP6_CONN_HASH_SIZE 9973 -#define TCP6_SYN_HASH_SIZE 997 -#define TCP6_SYN_BUCKET_SIZE 35 -#endif -int tcp6_listen_hash_size = TCP6_LISTEN_HASH_SIZE; -int tcp6_conn_hash_size = TCP6_CONN_HASH_SIZE; -struct tcp6_hash_list tcp6_listen_hash[TCP6_LISTEN_HASH_SIZE], - tcp6_conn_hash[TCP6_CONN_HASH_SIZE]; - -int tcp6_syn_cache_size = TCP6_SYN_HASH_SIZE; -int tcp6_syn_cache_limit = TCP6_SYN_HASH_SIZE*TCP6_SYN_BUCKET_SIZE; -int tcp6_syn_bucket_limit = 3*TCP6_SYN_BUCKET_SIZE; -struct syn_cache_head6 tcp6_syn_cache[TCP6_SYN_HASH_SIZE]; -struct syn_cache_head6 *tcp6_syn_cache_first; -int tcp6_syn_cache_interval = 8; /* runs timer every 4 seconds */ -int tcp6_syn_cache_timeo = TCP6TV_KEEP_INIT; - -/* - * Parameters for computing a desirable data segment size - * given an upper bound (either interface MTU, or peer's MSS option)_. - * As applications tend to use a buffer size that is a multiple - * of kilobytes, try for something that divides evenly. However, - * do not round down too much. - * - * Round segment size down to a multiple of TCP6_ROUNDSIZE if this - * does not result in lowering by more than (size/TCP6_ROUNDFRAC). - * For example, round 536 to 512. Older versions of the system - * effectively used MCLBYTES (1K or 2K) as TCP6_ROUNDSIZE, with - * a value of 1 for TCP6_ROUNDFRAC (eliminating its effect). - * We round to a multiple of 256 for SLIP. - */ -#ifndef TCP6_ROUNDSIZE -#define TCP6_ROUNDSIZE 256 /* round to multiple of 256 */ -#endif -#ifndef TCP6_ROUNDFRAC -#define TCP6_ROUNDFRAC 10 /* round down at most N/10, or 10% */ -#endif - -int tcp6_roundsize = TCP6_ROUNDSIZE; -int tcp6_roundfrac = TCP6_ROUNDFRAC; -#endif /*TCP6*/ +int icmp6_nodeinfo = 3; /* enable/disable NI response */ /* UDP on IP6 parameters */ int udp6_sendspace = 9216; /* really max datagram size */ @@ -429,76 +360,44 @@ SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6"); /* net.inet6.ip6 */ static int -sysctl_ip6_forwarding(SYSCTL_HANDLER_ARGS) +sysctl_ip6_temppltime(SYSCTL_HANDLER_ARGS) { int error = 0; - int old_ip6_forwarding; - int changed; + int old; error = SYSCTL_OUT(req, arg1, sizeof(int)); if (error || !req->newptr) return (error); - old_ip6_forwarding = ip6_forwarding; + old = ip6_temp_preferred_lifetime; error = SYSCTL_IN(req, arg1, sizeof(int)); - if (error != 0) - return (error); - changed = (ip6_forwarding ? 1 : 0) ^ (old_ip6_forwarding ? 1 : 0); - if (changed == 0) - return (error); - /* - * XXX while host->router removes prefix got from RA, - * router->host case nukes all the prefixes managed by in6_prefix.c - * (both RR and static). therefore, switching from host->router->host - * will remove statically configured addresses/prefixes. - * not sure if it is intended behavior or not. - */ - if (ip6_forwarding != 0) { /* host becomes router */ - int s = splnet(); - struct nd_prefix *pr, *next; - - for (pr = nd_prefix.lh_first; pr; pr = next) { - next = pr->ndpr_next; - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); - prelist_remove(pr); - } - splx(s); - } else { /* router becomes host */ - while(!LIST_EMPTY(&rr_prefix)) - delete_each_prefix(LIST_FIRST(&rr_prefix), - PR_ORIG_KERNEL); + if (ip6_temp_preferred_lifetime < + ip6_desync_factor + ip6_temp_regen_advance) { + ip6_temp_preferred_lifetime = old; + return(EINVAL); } - - return (error); + return(error); } static int -sysctl_icmp6_ratelimit (SYSCTL_HANDLER_ARGS) +sysctl_ip6_tempvltime(SYSCTL_HANDLER_ARGS) { - int rate_usec, error, s; - - /* - * The sysctl specifies the rate in usec-between-icmp, - * so we must convert from/to a timeval. - */ - rate_usec = (icmp6errratelim.tv_sec * 1000000) + - icmp6errratelim.tv_usec; - error = sysctl_handle_int(oidp, &rate_usec, 0, req); - if (error) + int error = 0; + int old; + + error = SYSCTL_OUT(req, arg1, sizeof(int)); + if (error || !req->newptr) return (error); - if (rate_usec < 0) - return (EINVAL); - s = splnet(); - icmp6errratelim.tv_sec = rate_usec / 1000000; - icmp6errratelim.tv_usec = rate_usec % 1000000; - splx(s); - - return (0); + old = ip6_temp_valid_lifetime; + error = SYSCTL_IN(req, arg1, sizeof(int)); + if (ip6_temp_valid_lifetime < ip6_temp_preferred_lifetime) { + ip6_temp_preferred_lifetime = old; + return(EINVAL); + } + return(error); } -SYSCTL_OID(_net_inet6_ip6, IPV6CTL_FORWARDING, forwarding, - CTLTYPE_INT|CTLFLAG_RW, &ip6_forwarding, 0, sysctl_ip6_forwarding, - "I", ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_FORWARDING, + forwarding, CTLFLAG_RW, &ip6_forwarding, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_SENDREDIRECTS, redirect, CTLFLAG_RW, &ip6_sendredirects, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFHLIM, @@ -527,8 +426,20 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USE_DEPRECATED, use_deprecated, CTLFLAG_RW, &ip6_use_deprecated, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RR_PRUNE, rr_prune, CTLFLAG_RW, &ip6_rr_prune, 0, ""); -SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAPPED_ADDR, - mapped_addr, CTLFLAG_RW, &ip6_mapped_addr_on, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USETEMPADDR, + use_tempaddr, CTLFLAG_RW, &ip6_use_tempaddr, 0, ""); +SYSCTL_OID(_net_inet6_ip6, IPV6CTL_TEMPPLTIME, temppltime, + CTLTYPE_INT|CTLFLAG_RW, &ip6_temp_preferred_lifetime, 0, + sysctl_ip6_temppltime, "I", ""); +SYSCTL_OID(_net_inet6_ip6, IPV6CTL_TEMPVLTIME, tempvltime, + CTLTYPE_INT|CTLFLAG_RW, &ip6_temp_valid_lifetime, 0, + sysctl_ip6_tempvltime, "I", ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_V6ONLY, + v6only, CTLFLAG_RW, &ip6_v6only, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_LINKLOCAL, + auto_linklocal, CTLFLAG_RW, &ip6_auto_linklocal, 0, ""); +SYSCTL_STRUCT(_net_inet6_ip6, IPV6CTL_RIP6STATS, rip6stats, CTLFLAG_RD, + &rip6stat, rip6stat, ""); /* net.inet6.icmp6 */ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRACCEPT, @@ -537,9 +448,6 @@ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRTIMEOUT, redirtimeout, CTLFLAG_RW, &icmp6_redirtimeout, 0, ""); SYSCTL_STRUCT(_net_inet6_icmp6, ICMPV6CTL_STATS, stats, CTLFLAG_RD, &icmp6stat, icmp6stat, ""); -SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ERRRATELIMIT, - errratelimit, CTLTYPE_INT|CTLFLAG_RW, - 0, sizeof(int), sysctl_icmp6_ratelimit, "I", ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PRUNE, nd6_prune, CTLFLAG_RW, &nd6_prune, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_DELAY, @@ -556,3 +464,5 @@ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRPPSLIMIT, errppslimit, CTLFLAG_RW, &icmp6errppslim, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXNUDHINT, nd6_maxnudhint, CTLFLAG_RW, &nd6_maxnudhint, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_DEBUG, + nd6_debug, CTLFLAG_RW, &nd6_debug, 0, ""); diff --git a/sys/netinet6/in6_rmx.c b/sys/netinet6/in6_rmx.c index 14f72d2ff39f..a56cfe250796 100644 --- a/sys/netinet6/in6_rmx.c +++ b/sys/netinet6/in6_rmx.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_rmx.c,v 1.7 2000/04/06 08:30:43 sumikawa Exp $ */ +/* $KAME: in6_rmx.c,v 1.10 2001/05/24 05:44:58 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -146,23 +146,6 @@ in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, } } - /* - * We also specify a send and receive pipe size for every - * route added, to help TCP a bit. TCP doesn't actually - * want a true pipe size, which would be prohibitive in memory - * costs and is hard to compute anyway; it simply uses these - * values to size its buffers. So, we fill them in with the - * same values that TCP would have used anyway, and allow the - * installing program or the link layer to override these values - * as it sees fit. This will hopefully allow TCP more - * opportunities to save its ssthresh value. - */ - if (!rt->rt_rmx.rmx_sendpipe && !(rt->rt_rmx.rmx_locks & RTV_SPIPE)) - rt->rt_rmx.rmx_sendpipe = tcp_sendspace; - - if (!rt->rt_rmx.rmx_recvpipe && !(rt->rt_rmx.rmx_locks & RTV_RPIPE)) - rt->rt_rmx.rmx_recvpipe = tcp_recvspace; - if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU) && rt->rt_ifp) rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 765a6928b4d9..7bf28d2c626f 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_src.c,v 1.27 2000/06/21 08:07:13 itojun Exp $ */ +/* $KAME: in6_src.c,v 1.37 2001/03/29 05:34:31 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -70,6 +70,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -97,9 +98,9 @@ #include <net/net_osdep.h> /* - * Return an IPv6 address, which is the most appropriate for given + * Return an IPv6 address, which is the most appropriate for a given * destination and user specified options. - * If necessary, this function lookups the routing table and return + * If necessary, this function lookups the routing table and returns * an entry to the caller for later use. */ struct in6_addr * @@ -241,12 +242,15 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) } if (ro->ro_rt == (struct rtentry *)0 || ro->ro_rt->rt_ifp == (struct ifnet *)0) { + struct sockaddr_in6 *sa6; + /* No route yet, so try to acquire one */ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); - ro->ro_dst.sin6_family = AF_INET6; - ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro->ro_dst.sin6_addr = *dst; - ro->ro_dst.sin6_scope_id = dstsock->sin6_scope_id; + sa6 = (struct sockaddr_in6 *)&ro->ro_dst; + sa6->sin6_family = AF_INET6; + sa6->sin6_len = sizeof(struct sockaddr_in6); + sa6->sin6_addr = *dst; + sa6->sin6_scope_id = dstsock->sin6_scope_id; if (IN6_IS_ADDR_MULTICAST(dst)) { ro->ro_rt = rtalloc1(&((struct route *)ro) ->ro_dst, 0, 0UL); @@ -529,15 +533,8 @@ in6_recoverscope(sin6, in6, ifp) /* sanity check */ if (scopeid < 0 || if_index < scopeid) return ENXIO; -#ifndef FAKE_LOOPBACK_IF - if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0 && - ifp->if_index != scopeid) { - return ENXIO; - } -#else if (ifp && ifp->if_index != scopeid) return ENXIO; -#endif sin6->sin6_addr.s6_addr16[1] = 0; sin6->sin6_scope_id = scopeid; } @@ -545,3 +542,15 @@ in6_recoverscope(sin6, in6, ifp) return 0; } + +/* + * just clear the embedded scope identifer. + * XXX: currently used for bsdi4 only as a supplement function. + */ +void +in6_clearscope(addr) + struct in6_addr *addr; +{ + if (IN6_IS_SCOPE_LINKLOCAL(addr)) + addr->s6_addr16[1] = 0; +} diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index f2f644e8a5fb..bb5abc972568 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_var.h,v 1.33 2000/05/17 05:07:26 jinmei Exp $ */ +/* $KAME: in6_var.h,v 1.56 2001/03/29 05:34:31 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -102,8 +102,12 @@ struct in6_ifaddr { struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */ int ia6_flags; - struct in6_addrlifetime ia6_lifetime; /* NULL = infty */ + struct in6_addrlifetime ia6_lifetime; struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ + + struct nd_prefix *ia6_ndpr; /* back pointer to the ND prefix + * (for autoconfigured addresses only) + */ }; /* @@ -380,7 +384,10 @@ struct in6_rrenumreq { #define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist) #define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist) -#define SIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ndireq) +#ifdef _KERNEL +#define OSIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ondireq) +#endif +#define SIOCGIFINFO_IN6 _IOWR('i', 108, struct in6_ndireq) #define SIOCSNDFLUSH_IN6 _IOWR('i', 77, struct in6_ifreq) #define SIOCGNBRINFO_IN6 _IOWR('i', 78, struct in6_nbrinfo) #define SIOCSPFXFLUSH_IN6 _IOWR('i', 79, struct in6_ifreq) @@ -419,6 +426,14 @@ struct in6_rrenumreq { #define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */ #define IN6_IFF_DETACHED 0x08 /* may be detached from the link */ #define IN6_IFF_DEPRECATED 0x10 /* deprecated address */ +#define IN6_IFF_NODAD 0x20 /* don't perform DAD on this address + * (used only at first SIOC* call) + */ +#define IN6_IFF_AUTOCONF 0x40 /* autoconfigurable address. */ +#define IN6_IFF_TEMPORARY 0x80 /* temporary (anonymous) address. */ +#define IN6_IFF_NOPFX 0x8000 /* skip kernel prefix management. + * XXX: this should be temporary. + */ /* do not input/output */ #define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED) @@ -516,7 +531,7 @@ struct in6_multistep { /* struct ifnet *ifp; */ \ /* struct in6_multi *in6m; */ \ do { \ - register struct ifmultiaddr *ifma; \ + struct ifmultiaddr *ifma; \ TAILQ_FOREACH(ifma, &(ifp)->if_multiaddrs, ifma_link) { \ if (ifma->ifma_addr->sa_family == AF_INET6 \ && IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifma->ifma_addr)->sin6_addr, \ @@ -549,18 +564,19 @@ do { \ IN6_NEXT_MULTI((step), (in6m)); \ } while(0) -int in6_ifinit __P((struct ifnet *, - struct in6_ifaddr *, struct sockaddr_in6 *, int)); struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *, int *)); void in6_delmulti __P((struct in6_multi *)); -void in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *)); extern int in6_ifindex2scopeid __P((int)); -extern int in6_mask2len __P((struct in6_addr *)); +extern int in6_mask2len __P((struct in6_addr *, u_char *)); extern void in6_len2mask __P((struct in6_addr *, int)); int in6_control __P((struct socket *, u_long, caddr_t, struct ifnet *, struct proc *)); -void in6_purgeaddr __P((struct ifaddr *, struct ifnet *)); +int in6_update_ifa __P((struct ifnet *, struct in6_aliasreq *, + struct in6_ifaddr *)); +void in6_purgeaddr __P((struct ifaddr *)); +int in6if_do_dad __P((struct ifnet *)); +void in6_purgeif __P((struct ifnet *)); void in6_savemkludge __P((struct in6_ifaddr *)); void in6_setmaxmtu __P((void)); void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); @@ -568,7 +584,7 @@ void in6_purgemkludge __P((struct ifnet *)); struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int)); struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *, struct in6_addr *)); -char *ip6_sprintf __P((struct in6_addr *)); +char *ip6_sprintf __P((const struct in6_addr *)); int in6_addr2scopeid __P((struct ifnet *, struct in6_addr *)); int in6_matchlen __P((struct in6_addr *, struct in6_addr *)); int in6_are_prefix_equal __P((struct in6_addr *p1, struct in6_addr *p2, @@ -579,6 +595,14 @@ int in6_prefix_ioctl __P((struct socket *so, u_long cmd, caddr_t data, int in6_prefix_add_ifid __P((int iilen, struct in6_ifaddr *ia)); void in6_prefix_remove_ifid __P((int iilen, struct in6_ifaddr *ia)); void in6_purgeprefix __P((struct ifnet *)); + +int in6_is_addr_deprecated __P((struct sockaddr_in6 *)); +struct inpcb; +int in6_embedscope __P((struct in6_addr *, const struct sockaddr_in6 *, + struct inpcb *, struct ifnet **)); +int in6_recoverscope __P((struct sockaddr_in6 *, const struct in6_addr *, + struct ifnet *)); +void in6_clearscope __P((struct in6_addr *)); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_VAR_H_ */ diff --git a/sys/netinet6/ip6_ecn.h b/sys/netinet6/ip6_ecn.h index e8dd11f71107..4107cf09af25 100644 --- a/sys/netinet6/ip6_ecn.h +++ b/sys/netinet6/ip6_ecn.h @@ -36,6 +36,6 @@ */ #ifdef _KERNEL -extern void ip6_ecn_ingress __P((int, u_int32_t *, u_int32_t *)); -extern void ip6_ecn_egress __P((int, u_int32_t *, u_int32_t *)); +extern void ip6_ecn_ingress __P((int, u_int32_t *, const u_int32_t *)); +extern void ip6_ecn_egress __P((int, const u_int32_t *, u_int32_t *)); #endif diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 2664ccbf746d..444cd2c653a0 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_forward.c,v 1.43 2000/07/16 07:50:49 itojun Exp $ */ +/* $KAME: ip6_forward.c,v 1.69 2001/05/17 03:48:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,12 +37,14 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/protosw.h> #include <sys/socket.h> #include <sys/errno.h> #include <sys/time.h> +#include <sys/kernel.h> #include <sys/syslog.h> #include <net/if.h> @@ -50,15 +52,22 @@ #include <netinet/in.h> #include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> #include <netinet/ip_var.h> +#include <netinet6/in6_var.h> #include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet/icmp6.h> #include <netinet6/nd6.h> +#include <netinet/in_pcb.h> + #ifdef IPSEC #include <netinet6/ipsec.h> +#ifdef INET6 #include <netinet6/ipsec6.h> +#endif #include <netkey/key.h> #endif /* IPSEC */ @@ -87,8 +96,8 @@ ip6_forward(m, srcrt) int srcrt; { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - register struct sockaddr_in6 *dst; - register struct rtentry *rt; + struct sockaddr_in6 *dst; + struct rtentry *rt; int error, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ @@ -111,8 +120,15 @@ ip6_forward(m, srcrt) } #endif /*IPSEC*/ + /* + * Do not forward packets to multicast destination (should be handled + * by ip6_mforward(). + * Do not forward packets with unspecified source. It was discussed + * in July 2000, on ipngwg mailing list. + */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || - IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || + IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6stat.ip6s_cantforward++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { @@ -150,7 +166,8 @@ ip6_forward(m, srcrt) #ifdef IPSEC /* get a security policy for this packet */ - sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); + sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING, + &error); if (sp == NULL) { ipsec6stat.out_inval++; ip6stat.ip6s_cantforward++; @@ -238,10 +255,6 @@ ip6_forward(m, srcrt) error = ipsec6_output_tunnel(&state, sp, 0); m = state.m; -#if 0 /* XXX allocate a route (ro, dst) again later */ - ro = (struct route_in6 *)state.ro; - dst = (struct sockaddr_in6 *)state.dst; -#endif key_freesp(sp); if (error) { @@ -275,7 +288,7 @@ ip6_forward(m, srcrt) skip_ipsec: #endif /* IPSEC */ - dst = &ip6_forward_rt.ro_dst; + dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; if (!srcrt) { /* * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst @@ -293,7 +306,7 @@ ip6_forward(m, srcrt) if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; - /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); @@ -315,7 +328,7 @@ ip6_forward(m, srcrt) rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; - /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); @@ -410,8 +423,25 @@ ip6_forward(m, srcrt) * modified by a redirect. */ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && - (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) + (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { + if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) { + /* + * If the incoming interface is equal to the outgoing + * one, and the link attached to the interface is + * point-to-point, then it will be highly probable + * that a routing loop occurs. Thus, we immediately + * drop the packet and send an ICMPv6 error message. + * + * type/code is based on suggestion by Rich Draves. + * not sure if it is the best pick. + */ + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, 0); + m_freem(m); + return; + } type = ND_REDIRECT; + } /* * Check with the firewall... @@ -432,8 +462,8 @@ ip6_forward(m, srcrt) * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded - * link identifiers, we can do this stuff after make a copy for - * returning error. + * link identifiers, we can do this stuff after making a copy for + * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* @@ -459,34 +489,21 @@ ip6_forward(m, srcrt) if_name(rt->rt_ifp)); } - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) - origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])]; - else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) - origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])]; - else - origifp = rt->rt_ifp; + /* we can just use rcvif in forwarding. */ + origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; -#ifndef FAKE_LOOPBACK_IF - if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) -#else - if (1) +#ifndef SCOPEDROUTING + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); #endif - { - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) - ip6->ip6_src.s6_addr16[1] = 0; - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] = 0; - } -#ifdef OLDIP6OUTPUT - error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m, - (struct sockaddr *)dst, - ip6_forward_rt.ro_rt); -#else error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); -#endif if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); ip6stat.ip6s_cantforward++; diff --git a/sys/netinet6/ip6_fw.c b/sys/netinet6/ip6_fw.c index ae1c0f1251a6..f0245cff005b 100644 --- a/sys/netinet6/ip6_fw.c +++ b/sys/netinet6/ip6_fw.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_fw.c,v 1.15 2000/07/02 14:17:37 itojun Exp $ */ +/* $KAME: ip6_fw.c,v 1.21 2001/01/24 01:25:32 itojun Exp $ */ /* * Copyright (c) 1993 Daniel Boulet @@ -87,7 +87,7 @@ LIST_HEAD (ip6_fw_head, ip6_fw_chain) ip6_fw_chain; SYSCTL_DECL(_net_inet6_ip6); SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, enable, CTLFLAG_RW, - &ip6_fw_enable, 0, "Enable ip6fw"); + &ip6_fw_enable, 0, "Enable ip6fw"); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, debug, CTLFLAG_RW, &fw6_debug, 0, ""); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw6_verbose, 0, ""); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw6_verbose_limit, 0, ""); @@ -479,7 +479,7 @@ ip6_fw_chk(struct ip6_hdr **pip6, } #endif /* IP6FW_DIVERT_RESTART */ for (; chain; chain = LIST_NEXT(chain, chain)) { - register struct ip6_fw *const f = chain->rule; + struct ip6_fw *const f = chain->rule; if (oif) { /* Check direction outbound */ @@ -758,7 +758,8 @@ got_match: flags = TH_RST|TH_ACK; } bcopy(&ti, ip6, sizeof(ti)); - m_freem(*m); + tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1), + *m, ack, seq, flags); *m = NULL; break; } @@ -1064,7 +1065,7 @@ ip6_fw_ctl(int stage, struct mbuf **mm) } } for (; fcp; fcp = fcp->chain.le_next) { - memcpy(m->m_data, fcp->rule, sizeof *(fcp->rule)); + bcopy(fcp->rule, m->m_data, sizeof *(fcp->rule)); m->m_len = sizeof *(fcp->rule); m->m_next = m_get(M_TRYWAIT, MT_DATA); /* XXX */ if (!m->m_next) { @@ -1204,7 +1205,7 @@ static int ip6fw_modevent(module_t mod, int type, void *unused) { int s; - + switch (type) { case MOD_LOAD: s = splnet(); @@ -1225,7 +1226,7 @@ ip6fw_modevent(module_t mod, int type, void *unused) free(fcp->rule, M_IP6FW); free(fcp, M_IP6FW); } - + splx(s); printf("IPv6 firewall unloaded\n"); return 0; diff --git a/sys/netinet6/ip6_fw.h b/sys/netinet6/ip6_fw.h index bcd1a037cf93..2298804ab48f 100644 --- a/sys/netinet6/ip6_fw.h +++ b/sys/netinet6/ip6_fw.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_fw.h,v 1.3 2000/04/06 08:30:44 sumikawa Exp $ */ +/* $KAME: ip6_fw.h,v 1.7 2001/01/24 01:25:33 itojun Exp $ */ /* * Copyright (c) 1993 Daniel Boulet diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 474534724bf9..4b10d8e9f4b0 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_input.c,v 1.95 2000/07/02 07:49:37 jinmei Exp $ */ +/* $KAME: ip6_input.c,v 1.194 2001/05/27 13:28:35 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -73,6 +73,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/protosw.h> @@ -108,6 +109,13 @@ #include <netinet6/nd6.h> #include <netinet6/in6_prefix.h> +#ifdef IPSEC +#include <netinet6/ipsec.h> +#ifdef INET6 +#include <netinet6/ipsec6.h> +#endif +#endif + #include <netinet6/ip6_fw.h> #include <netinet6/ip6protosw.h> @@ -124,11 +132,16 @@ u_char ip6_protox[IPPROTO_MAX]; static int ip6qmaxlen = IFQ_MAXLEN; struct in6_ifaddr *in6_ifaddr; +extern struct callout in6_tmpaddrtimer_ch; + int ip6_forward_srcrt; /* XXX */ int ip6_sourcecheck; /* XXX */ int ip6_sourcecheck_interval; /* XXX */ const int int6intrq_present = 1; +int ip6_ours_check_algorithm; + + /* firewall hooks */ ip6_fw_chk_t *ip6_fw_chk_ptr; ip6_fw_ctl_t *ip6_fw_ctl_ptr; @@ -137,12 +150,14 @@ int ip6_fw_enable = 1; struct ip6stat ip6stat; static void ip6_init2 __P((void *)); +static struct mbuf *ip6_setdstifaddr __P((struct mbuf *, struct in6_ifaddr *)); static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); #ifdef PULLDOWN_TEST static struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int)); #endif + /* * IP6 initialization: fill in IP6 protocol switch table. * All protocols not implemented in kernel go to raw IP6 protocol handler. @@ -150,10 +165,14 @@ static struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int)); void ip6_init() { - register struct ip6protosw *pr; - register int i; + struct ip6protosw *pr; + int i; struct timeval tv; +#ifdef DIAGNOSTIC + if (sizeof(struct protosw) != sizeof(struct ip6protosw)) + panic("sizeof(protosw) != sizeof(ip6protosw)"); +#endif pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); if (pr == 0) panic("ip6_init"); @@ -175,6 +194,8 @@ ip6_init() */ microtime(&tv); ip6_flow_seq = random() ^ tv.tv_usec; + microtime(&tv); + ip6_desync_factor = (random() ^ tv.tv_usec) % MAX_TEMP_DESYNC_FACTOR; } static void @@ -189,9 +210,18 @@ ip6_init2(dummy) in6_ifattach(&loif[0], NULL); /* nd6_timer_init */ - timeout(nd6_timer, (caddr_t)0, hz); + callout_init(&nd6_timer_ch, 0); + callout_reset(&nd6_timer_ch, hz, nd6_timer, NULL); + /* router renumbering prefix list maintenance */ - timeout(in6_rr_timer, (caddr_t)0, hz); + callout_init(&in6_rr_timer_ch, 0); + callout_reset(&in6_rr_timer_ch, hz, in6_rr_timer, NULL); + + /* timer for regeneranation of temporary addresses randomize ID */ + callout_reset(&in6_tmpaddrtimer_ch, + (ip6_temp_preferred_lifetime - ip6_desync_factor - + ip6_temp_regen_advance) * hz, + in6_tmpaddrtimer, NULL); } /* cheat */ @@ -247,6 +277,11 @@ ip6_input(m) #endif /* + * make sure we don't have onion peering information into m_aux. + */ + ip6_delaux(m); + + /* * mbuf statistics by kazu */ if (m->m_flags & M_EXT) { @@ -255,15 +290,17 @@ ip6_input(m) else ip6stat.ip6s_mext1++; } else { +#define M2MMAX (sizeof(ip6stat.ip6s_m2m)/sizeof(ip6stat.ip6s_m2m[0])) if (m->m_next) { if (m->m_flags & M_LOOP) { ip6stat.ip6s_m2m[loif[0].if_index]++; /*XXX*/ - } else if (m->m_pkthdr.rcvif->if_index <= 31) + } else if (m->m_pkthdr.rcvif->if_index < M2MMAX) ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; else ip6stat.ip6s_m2m[0]++; } else ip6stat.ip6s_m1++; +#undef M2MMAX } in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); @@ -360,20 +397,42 @@ ip6_input(m) } /* - * Scope check + * Check against address spoofing/corruption. */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { + /* + * XXX: "badscope" is not very suitable for a multicast source. + */ + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; + } + if ((IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || + IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) && + (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } - /* - * Don't check IPv4 mapped address here. SIIT assumes that - * routers would forward IPv6 native packets with IPv4 mapped - * address normally. + * The following check is not documented in specs. A malicious + * party may be able to use IPv4 mapped addr to confuse tcp/udp stack + * and bypass security checks (act as if it was from 127.0.0.1 by using + * IPv6 src ::ffff:127.0.0.1). Be cautious. + * + * This check chokes if we are in an SIIT cloud. As none of BSDs + * support IPv4-less kernel compilation, we cannot support SIIT + * environment at all. So, it makes more sense for us to reject any + * malicious packets for non-SIIT environment, than try to do a + * partical support for SIIT environment. */ + if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || + IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; + } #if 0 /* * Reject packets with IPv4 compatible addresses (auto tunnel). @@ -389,105 +448,52 @@ ip6_input(m) goto bad; } #endif - if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || - IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { - if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { - struct in6_ifaddr *ia6; - - if ((ia6 = in6ifa_ifpwithaddr(m->m_pkthdr.rcvif, - &ip6->ip6_dst)) != NULL) { - ia6->ia_ifa.if_ipackets++; - ia6->ia_ifa.if_ibytes += m->m_pkthdr.len; - } else { - /* - * The packet is looped back, but we do not - * have the destination address for some - * reason. - * XXX: should we return an icmp6 error? - */ - goto bad; - } - ours = 1; - deliverifp = m->m_pkthdr.rcvif; - goto hbhcheck; - } else { + + /* drop packets if interface ID portion is already filled */ + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src) && + ip6->ip6_src.s6_addr16[1]) { + ip6stat.ip6s_badscope++; + goto bad; + } + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst) && + ip6->ip6_dst.s6_addr16[1]) { ip6stat.ip6s_badscope++; - in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } } -#ifndef FAKE_LOOPBACK_IF - if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) -#else - if (1) -#endif - { - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) - ip6->ip6_src.s6_addr16[1] - = htons(m->m_pkthdr.rcvif->if_index); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] - = htons(m->m_pkthdr.rcvif->if_index); - } + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] + = htons(m->m_pkthdr.rcvif->if_index); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] + = htons(m->m_pkthdr.rcvif->if_index); +#if 0 /* this case seems to be unnecessary. (jinmei, 20010401) */ /* - * XXX we need this since we do not have "goto ours" hack route - * for some of our ifaddrs on loopback interface. - * we should correct it by changing in6_ifattach to install - * "goto ours" hack route. + * We use rt->rt_ifp to determine if the address is ours or not. + * If rt_ifp is lo0, the address is ours. + * The problem here is, rt->rt_ifp for fe80::%lo0/64 is set to lo0, + * so any address under fe80::%lo0/64 will be mistakenly considered + * local. The special case is supplied to handle the case properly + * by actually looking at interface addresses + * (using in6ifa_ifpwithaddr). */ - if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0) { - if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { - struct in6_ifaddr *ia6; -#ifndef FAKE_LOOPBACK_IF - int deliverifid; - - /* - * Get the "real" delivered interface, which should be - * embedded in the second 16 bits of the destination - * address. We can probably trust the value, but we - * add validation for the value just for safety. - */ - deliverifid = ntohs(ip6->ip6_dst.s6_addr16[1]); - if (deliverifid > 0 && deliverifid <= if_index) { - deliverifp = ifindex2ifnet[deliverifid]; - - /* - * XXX: fake the rcvif to the real interface. - * Since m_pkthdr.rcvif should be lo0 (or a - * variant), it would confuse scope handling - * code later. - */ - m->m_pkthdr.rcvif = deliverifp; - } - else { - /* - * Last resort; just use rcvif. - * XXX: the packet would be discarded by the - * succeeding check. - */ - deliverifp = m->m_pkthdr.rcvif; - } -#else - deliverifp = m->m_pkthdr.rcvif; -#endif - if ((ia6 = in6ifa_ifpwithaddr(deliverifp, - &ip6->ip6_dst)) != NULL) { - ia6->ia_ifa.if_ipackets++; - ia6->ia_ifa.if_ibytes += m->m_pkthdr.len; - } else { - /* - * We do not have the link-local address - * specified as the destination. - * XXX: should we return an icmp6 error? - */ - goto bad; - } - ours = 1; - goto hbhcheck; + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0 && + IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { + if (!in6ifa_ifpwithaddr(m->m_pkthdr.rcvif, &ip6->ip6_dst)) { + icmp6_error(m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, 0); + /* m is already freed */ + return; } + + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; } +#endif /* * Multicast check @@ -516,12 +522,21 @@ ip6_input(m) /* * Unicast check */ + switch (ip6_ours_check_algorithm) { + default: + /* + * XXX: I intentionally broke our indentation rule here, + * since this switch-case is just for measurement and + * therefore should soon be removed. + */ if (ip6_forward_rt.ro_rt != NULL && (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, - &ip6_forward_rt.ro_dst.sin6_addr)) + &((struct sockaddr_in6 *)(&ip6_forward_rt.ro_dst))->sin6_addr)) ip6stat.ip6s_forward_cachehit++; else { + struct sockaddr_in6 *dst6; + if (ip6_forward_rt.ro_rt) { /* route is down or destination is different */ ip6stat.ip6s_forward_cachemiss++; @@ -530,9 +545,10 @@ ip6_input(m) } bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); - ip6_forward_rt.ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ip6_forward_rt.ro_dst.sin6_family = AF_INET6; - ip6_forward_rt.ro_dst.sin6_addr = ip6->ip6_dst; + dst6 = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; + dst6->sin6_len = sizeof(struct sockaddr_in6); + dst6->sin6_family = AF_INET6; + dst6->sin6_addr = ip6->ip6_dst; #ifdef SCOPEDROUTING ip6_forward_rt.ro_dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_dst); @@ -551,10 +567,27 @@ ip6_input(m) * route to the loopback interface for the destination of the packet. * But we think it's even useful in some situations, e.g. when using * a special daemon which wants to intercept the packet. + * + * XXX: some OSes automatically make a cloned route for the destination + * of an outgoing packet. If the outgoing interface of the packet + * is a loopback one, the kernel would consider the packet to be + * accepted, even if we have no such address assinged on the interface. + * We check the cloned flag of the route entry to reject such cases, + * assuming that route entries for our own addresses are not made by + * cloning (it should be true because in6_addloop explicitly installs + * the host route). However, we might have to do an explicit check + * while it would be less efficient. Or, should we rather install a + * reject route for such a case? */ if (ip6_forward_rt.ro_rt && (ip6_forward_rt.ro_rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST && +#ifdef RTF_WASCLONED + !(ip6_forward_rt.ro_rt->rt_flags & RTF_WASCLONED) && +#endif +#ifdef RTF_CLONED + !(ip6_forward_rt.ro_rt->rt_flags & RTF_CLONED) && +#endif #if 0 /* * The check below is redundant since the comparison of @@ -562,13 +595,17 @@ ip6_input(m) * already done through looking up the routing table. */ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, - &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) && + &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) #endif ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { struct in6_ifaddr *ia6 = (struct in6_ifaddr *)ip6_forward_rt.ro_rt->rt_ifa; - if (ia6->ia6_flags & IN6_IFF_ANYCAST) - m->m_flags |= M_ANYCAST6; + + /* + * record address information into m_aux. + */ + (void)ip6_setdstifaddr(m, ia6); + /* * packets to a tentative, duplicated, or somehow invalid * address must not be accepted. @@ -577,22 +614,21 @@ ip6_input(m) /* this address is ready */ ours = 1; deliverifp = ia6->ia_ifp; /* correct? */ - /* Count the packet in the ip address stats */ ia6->ia_ifa.if_ipackets++; ia6->ia_ifa.if_ibytes += m->m_pkthdr.len; - goto hbhcheck; } else { /* address is not ready, so discard the packet. */ - log(LOG_INFO, - "ip6_input: packet to an unready address %s->%s", + nd6log((LOG_INFO, + "ip6_input: packet to an unready address %s->%s\n", ip6_sprintf(&ip6->ip6_src), - ip6_sprintf(&ip6->ip6_dst)); + ip6_sprintf(&ip6->ip6_dst))); goto bad; } } + } /* XXX indentation (see above) */ /* * FAITH(Firewall Aided Internet Translator) @@ -621,6 +657,27 @@ ip6_input(m) hbhcheck: /* + * record address information into m_aux, if we don't have one yet. + * note that we are unable to record it, if the address is not listed + * as our interface address (e.g. multicast addresses, addresses + * within FAITH prefixes and such). + */ + if (deliverifp && !ip6_getdstifaddr(m)) { + struct in6_ifaddr *ia6; + + ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); + if (ia6) { + if (!ip6_setdstifaddr(m, ia6)) { + /* + * XXX maybe we should drop the packet here, + * as we could not provide enough information + * to the upper layers. + */ + } + } + } + + /* * Process Hop-by-Hop options header if it's contained. * m may be modified in ip6_hopopts_input(). * If a JumboPayload option is included, plen will also be modified. @@ -749,6 +806,7 @@ ip6_input(m) ip6stat.ip6s_delivered++; in6_ifstat_inc(deliverifp, ifs6_in_deliver); nest = 0; + while (nxt != IPPROTO_DONE) { if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { ip6stat.ip6s_toomanyhdr++; @@ -765,6 +823,35 @@ ip6_input(m) goto bad; } +#if 0 + /* + * do we need to do it for every header? yeah, other + * functions can play with it (like re-allocate and copy). + */ + mhist = ip6_addaux(m); + if (mhist && M_TRAILINGSPACE(mhist) >= sizeof(nxt)) { + hist = mtod(mhist, caddr_t) + mhist->m_len; + bcopy(&nxt, hist, sizeof(nxt)); + mhist->m_len += sizeof(nxt); + } else { + ip6stat.ip6s_toomanyhdr++; + goto bad; + } +#endif + +#ifdef IPSEC + /* + * enforce IPsec policy checking if we are seeing last header. + * note that we do not visit this with protocols with pcb layer + * code - like udp/tcp/raw ip. + */ + if ((inet6sw[ip6_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec6_in_reject(m, NULL)) { + ipsec6stat.in_polvio++; + goto bad; + } +#endif + nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); } return; @@ -773,6 +860,36 @@ ip6_input(m) } /* + * set/grab in6_ifaddr correspond to IPv6 destination address. + * XXX backward compatibility wrapper + */ +static struct mbuf * +ip6_setdstifaddr(m, ia6) + struct mbuf *m; + struct in6_ifaddr *ia6; +{ + struct mbuf *n; + + n = ip6_addaux(m); + if (n) + mtod(n, struct ip6aux *)->ip6a_dstia6 = ia6; + return n; /* NULL if failed to set */ +} + +struct in6_ifaddr * +ip6_getdstifaddr(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = ip6_findaux(m); + if (n) + return mtod(n, struct ip6aux *)->ip6a_dstia6; + else + return NULL; +} + +/* * Hop-by-Hop options header processing. If a valid jumbo payload option is * included, the real payload length will be stored in plenp. */ @@ -783,7 +900,7 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp) struct mbuf **mp; int *offp; { - register struct mbuf *m = *mp; + struct mbuf *m = *mp; int off = *offp, hbhlen; struct ip6_hbh *hbh; u_int8_t *opt; @@ -829,6 +946,10 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp) * This function is separate from ip6_hopopts_input() in order to * handle a case where the sending node itself process its hop-by-hop * options header. In such a case, the function is called from ip6_output(). + * + * The function assumes that hbh header is located right after the IPv6 header + * (RFC2460 p7), opthead is pointer into data content in m, and opthead to + * opthead + hbhlen is located in continuous memory region. */ int ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) @@ -843,58 +964,62 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) u_int8_t *opt = opthead; u_int16_t rtalert_val; u_int32_t jumboplen; + const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh); for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { - switch(*opt) { - case IP6OPT_PAD1: - optlen = 1; - break; - case IP6OPT_PADN: - if (hbhlen < IP6OPT_MINLEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - optlen = *(opt + 1) + 2; - break; - case IP6OPT_RTALERT: - /* XXX may need check for alignment */ - if (hbhlen < IP6OPT_RTALERT_LEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) - /* XXX: should we discard the packet? */ - log(LOG_ERR, "length of router alert opt is inconsitent(%d)", - *(opt + 1)); - optlen = IP6OPT_RTALERT_LEN; - bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); - *rtalertp = ntohs(rtalert_val); - break; - case IP6OPT_JUMBO: + switch (*opt) { + case IP6OPT_PAD1: + optlen = 1; + break; + case IP6OPT_PADN: + if (hbhlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + optlen = *(opt + 1) + 2; + break; + case IP6OPT_RTALERT: + /* XXX may need check for alignment */ + if (hbhlen < IP6OPT_RTALERT_LEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) { + /* XXX stat */ + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + erroff + opt + 1 - opthead); + return(-1); + } + optlen = IP6OPT_RTALERT_LEN; + bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); + *rtalertp = ntohs(rtalert_val); + break; + case IP6OPT_JUMBO: /* XXX may need check for alignment */ if (hbhlen < IP6OPT_JUMBO_LEN) { ip6stat.ip6s_toosmall++; goto bad; } - if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) - /* XXX: should we discard the packet? */ - log(LOG_ERR, "length of jumbopayload opt " - "is inconsistent(%d)", - *(opt + 1)); + if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) { + /* XXX stat */ + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + erroff + opt + 1 - opthead); + return(-1); + } optlen = IP6OPT_JUMBO_LEN; /* * IPv6 packets that have non 0 payload length - * must not contain a jumbo paylod option. + * must not contain a jumbo payload option. */ ip6 = mtod(m, struct ip6_hdr *); if (ip6->ip6_plen) { ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt - opthead); + erroff + opt - opthead); return(-1); } @@ -918,9 +1043,7 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); + erroff + opt + 2 - opthead); return(-1); } #endif @@ -932,26 +1055,23 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); + erroff + opt + 2 - opthead); return(-1); } *plenp = jumboplen; break; - default: /* unknown option */ - if (hbhlen < IP6OPT_MINLEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - if ((optlen = ip6_unknown_opt(opt, m, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt - opthead)) == -1) - return(-1); - optlen += 2; - break; + default: /* unknown option */ + if (hbhlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + optlen = ip6_unknown_opt(opt, m, + erroff + opt - opthead); + if (optlen == -1) + return(-1); + optlen += 2; + break; } } @@ -976,26 +1096,26 @@ ip6_unknown_opt(optp, m, off) { struct ip6_hdr *ip6; - switch(IP6OPT_TYPE(*optp)) { - case IP6OPT_TYPE_SKIP: /* ignore the option */ - return((int)*(optp + 1)); - case IP6OPT_TYPE_DISCARD: /* silently discard */ - m_freem(m); - return(-1); - case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); - return(-1); - case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ - ip6stat.ip6s_badoptions++; - ip6 = mtod(m, struct ip6_hdr *); - if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || - (m->m_flags & (M_BCAST|M_MCAST))) - m_freem(m); - else - icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_OPTION, off); - return(-1); + switch (IP6OPT_TYPE(*optp)) { + case IP6OPT_TYPE_SKIP: /* ignore the option */ + return((int)*(optp + 1)); + case IP6OPT_TYPE_DISCARD: /* silently discard */ + m_freem(m); + return(-1); + case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); + return(-1); + case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ + ip6stat.ip6s_badoptions++; + ip6 = mtod(m, struct ip6_hdr *); + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || + (m->m_flags & (M_BCAST|M_MCAST))) + m_freem(m); + else + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_OPTION, off); + return(-1); } m_freem(m); /* XXX: NOTREACHED */ @@ -1004,50 +1124,44 @@ ip6_unknown_opt(optp, m, off) /* * Create the "control" list for this pcb. + * The function will not modify mbuf chain at all. * + * with KAME mbuf chain restriction: * The routine will be called from upper layer handlers like tcp6_input(). * Thus the routine assumes that the caller (tcp6_input) have already * called IP6_EXTHDR_CHECK() and all the extension headers are located in the * very first mbuf on the mbuf chain. - * We may want to add some infinite loop prevention or sanity checks for safety. - * (This applies only when you are using KAME mbuf chain restriction, i.e. - * you are using IP6_EXTHDR_CHECK() not m_pulldown()) */ void ip6_savecontrol(in6p, mp, ip6, m) - register struct in6pcb *in6p; - register struct mbuf **mp; - register struct ip6_hdr *ip6; - register struct mbuf *m; + struct inpcb *in6p; + struct mbuf **mp; + struct ip6_hdr *ip6; + struct mbuf *m; { struct proc *p = curproc; /* XXX */ - int privileged; + int privileged = 0; + int rthdr_exist = 0; + - privileged = 0; if (p && !suser(p)) - privileged++; + privileged++; - if (in6p->in6p_socket->so_options & SO_TIMESTAMP) { +#ifdef SO_TIMESTAMP + if ((in6p->in6p_socket->so_options & SO_TIMESTAMP) != 0) { struct timeval tv; microtime(&tv); *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), - SCM_TIMESTAMP, SOL_SOCKET); - if (*mp) + SCM_TIMESTAMP, SOL_SOCKET); + if (*mp) { mp = &(*mp)->m_next; + } } - -#ifdef noyet - /* options were tossed above */ - if (in6p->in6p_flags & IN6P_RECVOPTS) - /* broken */ - /* ip6_srcroute doesn't do what we want here, need to fix */ - if (in6p->in6p_flags & IPV6P_RECVRETOPTS) - /* broken */ #endif /* RFC 2292 sec. 5 */ - if (in6p->in6p_flags & IN6P_PKTINFO) { + if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) { struct in6_pktinfo pi6; bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr)) @@ -1061,14 +1175,14 @@ ip6_savecontrol(in6p, mp, ip6, m) if (*mp) mp = &(*mp)->m_next; } - if (in6p->in6p_flags & IN6P_HOPLIMIT) { + + if ((in6p->in6p_flags & IN6P_HOPLIMIT) != 0) { int hlim = ip6->ip6_hlim & 0xff; *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } - /* IN6P_NEXTHOP - for outgoing packet only */ /* * IPV6_HOPOPTS socket option. We require super-user privilege @@ -1076,7 +1190,7 @@ ip6_savecontrol(in6p, mp, ip6, m) * be some hop-by-hop options which can be returned to normal user. * See RFC 2292 section 6. */ - if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { + if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0 && privileged) { /* * Check if a hop-by-hop options header is contatined in the * received packet, and if so, store the options as ancillary @@ -1087,22 +1201,25 @@ ip6_savecontrol(in6p, mp, ip6, m) struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { struct ip6_hbh *hbh; - int hbhlen; + int hbhlen = 0; +#ifdef PULLDOWN_TEST + struct mbuf *ext; +#endif #ifndef PULLDOWN_TEST hbh = (struct ip6_hbh *)(ip6 + 1); hbhlen = (hbh->ip6h_len + 1) << 3; #else - IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, - sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); - if (hbh == NULL) { + ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr), + ip6->ip6_nxt); + if (ext == NULL) { ip6stat.ip6s_tooshort++; return; } + hbh = mtod(ext, struct ip6_hbh *); hbhlen = (hbh->ip6h_len + 1) << 3; - IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, - sizeof(struct ip6_hdr), hbhlen); - if (hbh == NULL) { + if (hbhlen != ext->m_len) { + m_freem(ext); ip6stat.ip6s_tooshort++; return; } @@ -1112,19 +1229,53 @@ ip6_savecontrol(in6p, mp, ip6, m) * XXX: We copy whole the header even if a jumbo * payload option is included, which option is to * be removed before returning in the RFC 2292. - * But it's too painful operation... + * Note: this constraint is removed in 2292bis. */ *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, IPV6_HOPOPTS, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; +#ifdef PULLDOWN_TEST + m_freem(ext); +#endif } } /* IPV6_DSTOPTS and IPV6_RTHDR socket options */ - if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) { + if ((in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) { + int proto, off, nxt; + + /* + * go through the header chain to see if a routing header is + * contained in the packet. We need this information to store + * destination options headers (if any) properly. + * XXX: performance issue. We should record this info when + * processing extension headers in incoming routine. + * (todo) use m_aux? + */ + proto = IPPROTO_IPV6; + off = 0; + nxt = -1; + while (1) { + int newoff; + + newoff = ip6_nexthdr(m, off, proto, &nxt); + if (newoff < 0) + break; + if (newoff < off) /* invalid, check for safety */ + break; + if ((proto = nxt) == IPPROTO_ROUTING) { + rthdr_exist = 1; + break; + } + off = newoff; + } + } + + if ((in6p->in6p_flags & + (IN6P_RTHDR | IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);; + int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr); /* * Search for destination options headers or routing @@ -1133,95 +1284,172 @@ ip6_savecontrol(in6p, mp, ip6, m) * Note that the order of the headers remains in * the chain of ancillary data. */ - while(1) { /* is explicit loop prevention necessary? */ - struct ip6_ext *ip6e; + while (1) { /* is explicit loop prevention necessary? */ + struct ip6_ext *ip6e = NULL; int elen; +#ifdef PULLDOWN_TEST + struct mbuf *ext = NULL; +#endif + + /* + * if it is not an extension header, don't try to + * pull it from the chain. + */ + switch (nxt) { + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + case IPPROTO_AH: /* is it possible? */ + break; + default: + goto loopend; + } #ifndef PULLDOWN_TEST + if (off + sizeof(*ip6e) > m->m_len) + goto loopend; ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); if (nxt == IPPROTO_AH) elen = (ip6e->ip6e_len + 2) << 2; else elen = (ip6e->ip6e_len + 1) << 3; + if (off + elen > m->m_len) + goto loopend; #else - IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, - sizeof(struct ip6_ext)); - if (ip6e == NULL) { + ext = ip6_pullexthdr(m, off, nxt); + if (ext == NULL) { ip6stat.ip6s_tooshort++; return; } + ip6e = mtod(ext, struct ip6_ext *); if (nxt == IPPROTO_AH) elen = (ip6e->ip6e_len + 2) << 2; else elen = (ip6e->ip6e_len + 1) << 3; - IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, elen); - if (ip6e == NULL) { + if (elen != ext->m_len) { + m_freem(ext); ip6stat.ip6s_tooshort++; return; } #endif - switch(nxt) { - case IPPROTO_DSTOPTS: - if (!in6p->in6p_flags & IN6P_DSTOPTS) - break; - - /* - * We also require super-user privilege for - * the option. - * See the comments on IN6_HOPOPTS. - */ - if (!privileged) - break; - - *mp = sbcreatecontrol((caddr_t)ip6e, elen, - IPV6_DSTOPTS, - IPPROTO_IPV6); - if (*mp) - mp = &(*mp)->m_next; - break; - - case IPPROTO_ROUTING: - if (!in6p->in6p_flags & IN6P_RTHDR) - break; - - *mp = sbcreatecontrol((caddr_t)ip6e, elen, - IPV6_RTHDR, - IPPROTO_IPV6); - if (*mp) - mp = &(*mp)->m_next; - break; - - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_ICMPV6: - default: - /* - * stop search if we encounter an upper - * layer protocol headers. - */ - goto loopend; - - case IPPROTO_HOPOPTS: - case IPPROTO_AH: /* is it possible? */ - break; + switch (nxt) { + case IPPROTO_DSTOPTS: + if ((in6p->in6p_flags & IN6P_DSTOPTS) == 0) + break; + + /* + * We also require super-user privilege for + * the option. + * See the comments on IN6_HOPOPTS. + */ + if (!privileged) + break; + + *mp = sbcreatecontrol((caddr_t)ip6e, elen, + IPV6_DSTOPTS, + IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + break; + case IPPROTO_ROUTING: + if (!in6p->in6p_flags & IN6P_RTHDR) + break; + + *mp = sbcreatecontrol((caddr_t)ip6e, elen, + IPV6_RTHDR, + IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + break; + case IPPROTO_HOPOPTS: + case IPPROTO_AH: /* is it possible? */ + break; + + default: + /* + * other cases have been filtered in the above. + * none will visit this case. here we supply + * the code just in case (nxt overwritten or + * other cases). + */ +#ifdef PULLDOWN_TEST + m_freem(ext); +#endif + goto loopend; + } /* proceed with the next header. */ off += elen; nxt = ip6e->ip6e_nxt; + ip6e = NULL; +#ifdef PULLDOWN_TEST + m_freem(ext); + ext = NULL; +#endif } loopend: + ; } - if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { - /* to be done */ + +} + +#ifdef PULLDOWN_TEST +/* + * pull single extension header from mbuf chain. returns single mbuf that + * contains the result, or NULL on error. + */ +static struct mbuf * +ip6_pullexthdr(m, off, nxt) + struct mbuf *m; + size_t off; + int nxt; +{ + struct ip6_ext ip6e; + size_t elen; + struct mbuf *n; + +#ifdef DIAGNOSTIC + switch (nxt) { + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + case IPPROTO_AH: /* is it possible? */ + break; + default: + printf("ip6_pullexthdr: invalid nxt=%d\n", nxt); + } +#endif + + m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); + if (nxt == IPPROTO_AH) + elen = (ip6e.ip6e_len + 2) << 2; + else + elen = (ip6e.ip6e_len + 1) << 3; + + MGET(n, M_DONTWAIT, MT_DATA); + if (n && elen >= MLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } } - if ((in6p->in6p_flags & IN6P_DSTOPTS) && privileged) { - /* to be done */ + if (!n) + return NULL; + + n->m_len = 0; + if (elen >= M_TRAILINGSPACE(n)) { + m_free(n); + return NULL; } - /* IN6P_RTHDR - to be done */ + m_copydata(m, off, elen, mtod(n, caddr_t)); + n->m_len = elen; + return n; } +#endif /* * Get pointer to the previous header followed by the header @@ -1253,7 +1481,7 @@ ip6_get_prevhdr(m, off) while (len < off) { ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len); - switch(nxt) { + switch (nxt) { case IPPROTO_FRAGMENT: len += sizeof(struct ip6_frag); break; @@ -1382,6 +1610,55 @@ ip6_lasthdr(m, off, proto, nxtp) } } +struct mbuf * +ip6_addaux(m) + struct mbuf *m; +{ + struct mbuf *n; + +#ifdef DIAGNOSTIC + if (sizeof(struct ip6aux) > MHLEN) + panic("assumption failed on sizeof(ip6aux)"); +#endif + n = m_aux_find(m, AF_INET6, -1); + if (n) { + if (n->m_len < sizeof(struct ip6aux)) { + printf("conflicting use of ip6aux"); + return NULL; + } + } else { + n = m_aux_add(m, AF_INET6, -1); + n->m_len = sizeof(struct ip6aux); + bzero(mtod(n, caddr_t), n->m_len); + } + return n; +} + +struct mbuf * +ip6_findaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET6, -1); + if (n && n->m_len < sizeof(struct ip6aux)) { + printf("conflicting use of ip6aux"); + n = NULL; + } + return n; +} + +void +ip6_delaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET6, -1); + if (n) + m_aux_delete(m, n); +} + /* * System control for IP6 */ diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c index 82b0d4b60da1..2be8796f6754 100644 --- a/sys/netinet6/ip6_mroute.c +++ b/sys/netinet6/ip6_mroute.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_mroute.c,v 1.33 2000/10/19 02:23:43 jinmei Exp $ */ +/* $KAME: ip6_mroute.c,v 1.46 2001/04/04 05:17:30 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -50,6 +50,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/callout.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/socket.h> @@ -143,7 +144,6 @@ static mifi_t nummifs = 0; static mifi_t reg_mif_num = (mifi_t)-1; static struct pim6stat pim6stat; -static struct callout_handle expire_upcalls_ch; /* * one-back cache used by ipip_input to locate a tunnel's mif @@ -165,7 +165,7 @@ static int pim6; */ #define MF6CFIND(o, g, rt) do { \ - register struct mf6c *_rt = mf6ctable[MF6CHASH(o,g)]; \ + struct mf6c *_rt = mf6ctable[MF6CHASH(o,g)]; \ rt = NULL; \ mrt6stat.mrt6s_mfc_lookups++; \ while (_rt) { \ @@ -187,7 +187,7 @@ static int pim6; * Borrowed from Van Jacobson's scheduling code */ #define TV_DELTA(a, b, delta) do { \ - register int xxs; \ + int xxs; \ \ delta = (a).tv_usec - (b).tv_usec; \ if ((xxs = (a).tv_sec - (b).tv_sec)) { \ @@ -221,6 +221,8 @@ static int del_m6if __P((mifi_t *)); static int add_m6fc __P((struct mf6cctl *)); static int del_m6fc __P((struct mf6cctl *)); +static struct callout expire_upcalls_ch; + /* * Handle MRT setsockopt commands to modify the multicast routing tables. */ @@ -241,33 +243,33 @@ ip6_mrouter_set(so, sopt) return (error); switch (sopt->sopt_name) { - case MRT6_INIT: + case MRT6_INIT: #ifdef MRT6_OINIT - case MRT6_OINIT: + case MRT6_OINIT: #endif - error = ip6_mrouter_init(so, m, sopt->sopt_name); - break; - case MRT6_DONE: - error = ip6_mrouter_done(); - break; - case MRT6_ADD_MIF: - error = add_m6if(mtod(m, struct mif6ctl *)); - break; - case MRT6_DEL_MIF: - error = del_m6if(mtod(m, mifi_t *)); - break; - case MRT6_ADD_MFC: - error = add_m6fc(mtod(m, struct mf6cctl *)); - break; - case MRT6_DEL_MFC: - error = del_m6fc(mtod(m, struct mf6cctl *)); - break; - case MRT6_PIM: - error = set_pim6(mtod(m, int *)); - break; - default: - error = EOPNOTSUPP; - break; + error = ip6_mrouter_init(so, m, sopt->sopt_name); + break; + case MRT6_DONE: + error = ip6_mrouter_done(); + break; + case MRT6_ADD_MIF: + error = add_m6if(mtod(m, struct mif6ctl *)); + break; + case MRT6_DEL_MIF: + error = del_m6if(mtod(m, mifi_t *)); + break; + case MRT6_ADD_MFC: + error = add_m6fc(mtod(m, struct mf6cctl *)); + break; + case MRT6_DEL_MFC: + error = del_m6fc(mtod(m, struct mf6cctl *)); + break; + case MRT6_PIM: + error = set_pim6(mtod(m, int *)); + break; + default: + error = EOPNOTSUPP; + break; } (void)m_freem(m); @@ -302,20 +304,20 @@ mrt6_ioctl(cmd, data) int cmd; caddr_t data; { - int error = 0; - - switch (cmd) { - case SIOCGETSGCNT_IN6: - return(get_sg_cnt((struct sioc_sg_req6 *)data)); - break; /* for safety */ - case SIOCGETMIFCNT_IN6: - return(get_mif6_cnt((struct sioc_mif_req6 *)data)); - break; /* for safety */ - default: - return (EINVAL); - break; - } - return error; + int error = 0; + + switch (cmd) { + case SIOCGETSGCNT_IN6: + return(get_sg_cnt((struct sioc_sg_req6 *)data)); + break; /* for safety */ + case SIOCGETMIFCNT_IN6: + return(get_mif6_cnt((struct sioc_mif_req6 *)data)); + break; /* for safety */ + default: + return (EINVAL); + break; + } + return error; } /* @@ -323,9 +325,9 @@ mrt6_ioctl(cmd, data) */ static int get_sg_cnt(req) - register struct sioc_sg_req6 *req; + struct sioc_sg_req6 *req; { - register struct mf6c *rt; + struct mf6c *rt; int s; s = splnet(); @@ -349,9 +351,9 @@ get_sg_cnt(req) */ static int get_mif6_cnt(req) - register struct sioc_mif_req6 *req; + struct sioc_mif_req6 *req; { - register mifi_t mifi = req->mifi; + mifi_t mifi = req->mifi; if (mifi >= nummifs) return EINVAL; @@ -415,8 +417,8 @@ ip6_mrouter_init(so, m, cmd) pim6 = 0;/* used for stubbing out/in pim stuff */ - expire_upcalls_ch = - timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); + callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT, + expire_upcalls, NULL); #ifdef MRT6DEBUG if (mrt6debug) @@ -478,7 +480,7 @@ ip6_mrouter_done() pim6 = 0; /* used to stub out/in pim specific code */ - untimeout(expire_upcalls, (caddr_t)NULL, expire_upcalls_ch); + callout_stop(&expire_upcalls_ch); /* * Free all multicast forwarding cache entries. @@ -528,9 +530,9 @@ static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 }; */ static int add_m6if(mifcp) - register struct mif6ctl *mifcp; + struct mif6ctl *mifcp; { - register struct mif6 *mifp; + struct mif6 *mifp; struct ifnet *ifp; int error, s; #ifdef notyet @@ -605,8 +607,8 @@ static int del_m6if(mifip) mifi_t *mifip; { - register struct mif6 *mifp = mif6table + *mifip; - register mifi_t mifi; + struct mif6 *mifp = mif6table + *mifip; + mifi_t mifi; struct ifnet *ifp; int s; @@ -659,7 +661,7 @@ add_m6fc(mfccp) struct mf6c *rt; u_long hash; struct rtdetq *rte; - register u_short nstl; + u_short nstl; int s; MF6CFIND(mfccp->mf6cc_origin.sin6_addr, @@ -809,11 +811,11 @@ add_m6fc(mfccp) */ static void collate(t) - register struct timeval *t; + struct timeval *t; { - register u_long d; - register struct timeval tp; - register u_long delta; + u_long d; + struct timeval tp; + u_long delta; GET_TIME(tp); @@ -912,13 +914,13 @@ socket_send(s, mm, src) int ip6_mforward(ip6, ifp, m) - register struct ip6_hdr *ip6; + struct ip6_hdr *ip6; struct ifnet *ifp; struct mbuf *m; { - register struct mf6c *rt; - register struct mif6 *mifp; - register struct mbuf *mm; + struct mf6c *rt; + struct mif6 *mifp; + struct mbuf *mm; int s; mifi_t mifi; @@ -977,10 +979,10 @@ ip6_mforward(ip6, ifp, m) * send message to routing daemon */ - register struct mbuf *mb0; - register struct rtdetq *rte; - register u_long hash; -/* register int i, npkts;*/ + struct mbuf *mb0; + struct rtdetq *rte; + u_long hash; +/* int i, npkts;*/ #ifdef UPCALL_TIMING struct timeval tp; @@ -1144,7 +1146,7 @@ ip6_mforward(ip6, ifp, m) } else { /* determine if q has overflowed */ struct rtdetq **p; - register int npkts = 0; + int npkts = 0; for (p = &rt->mf6c_stall; *p != NULL; p = &(*p)->next) if (++npkts > MAX_UPQ6) { @@ -1227,8 +1229,8 @@ expire_upcalls(unused) } } splx(s); - expire_upcalls_ch = - timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); + callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT, + expire_upcalls, NULL); } /* @@ -1236,14 +1238,14 @@ expire_upcalls(unused) */ static int ip6_mdq(m, ifp, rt) - register struct mbuf *m; - register struct ifnet *ifp; - register struct mf6c *rt; + struct mbuf *m; + struct ifnet *ifp; + struct mf6c *rt; { - register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - register mifi_t mifi, iif; - register struct mif6 *mifp; - register int plen = m->m_pkthdr.len; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + mifi_t mifi, iif; + struct mif6 *mifp; + int plen = m->m_pkthdr.len; /* * Macro to send packet on mif. Since RSVP packets don't get counted on @@ -1290,7 +1292,7 @@ ip6_mdq(m, ifp, rt) static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 }; - register struct mbuf *mm; + struct mbuf *mm; struct mrt6msg *im; #ifdef MRT6_OINIT struct omrt6msg *oim; @@ -1319,6 +1321,7 @@ ip6_mdq(m, ifp, rt) case MRT6_INIT: im = mtod(mm, struct mrt6msg *); im->im6_msgtype = MRT6MSG_WRONGMIF; + im->im6_mbz = 0; break; default: m_freem(mm); @@ -1408,12 +1411,13 @@ phyint_send(ip6, mifp, m) struct mif6 *mifp; struct mbuf *m; { - register struct mbuf *mb_copy; + struct mbuf *mb_copy; struct ifnet *ifp = mifp->m6_ifp; int error = 0; - int s = splnet(); - static struct route_in6 ro6; + int s = splnet(); /* needs to protect static "ro" below. */ + static struct route_in6 ro; struct in6_multi *in6m; + struct sockaddr_in6 *dst6; /* * Make a new reference to the packet; make sure that @@ -1424,8 +1428,10 @@ phyint_send(ip6, mifp, m) if (mb_copy && (M_HASCL(mb_copy) || mb_copy->m_len < sizeof(struct ip6_hdr))) mb_copy = m_pullup(mb_copy, sizeof(struct ip6_hdr)); - if (mb_copy == NULL) + if (mb_copy == NULL) { + splx(s); return; + } /* set MCAST flag to the outgoing packet */ mb_copy->m_flags |= M_MCAST; @@ -1443,7 +1449,7 @@ phyint_send(ip6, mifp, m) /* XXX: ip6_output will override ip6->ip6_hlim */ im6o.im6o_multicast_hlim = ip6->ip6_hlim; im6o.im6o_multicast_loop = 1; - error = ip6_output(mb_copy, NULL, &ro6, + error = ip6_output(mb_copy, NULL, &ro, IPV6_FORWARDING, &im6o, NULL); #ifdef MRT6DEBUG @@ -1459,63 +1465,62 @@ phyint_send(ip6, mifp, m) * If we belong to the destination multicast group * on the outgoing interface, loop back a copy. */ + dst6 = (struct sockaddr_in6 *)&ro.ro_dst; IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m); if (in6m != NULL) { - ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro6.ro_dst.sin6_family = AF_INET6; - ro6.ro_dst.sin6_addr = ip6->ip6_dst; - ip6_mloopback(ifp, m, &ro6.ro_dst); + dst6->sin6_len = sizeof(struct sockaddr_in6); + dst6->sin6_family = AF_INET6; + dst6->sin6_addr = ip6->ip6_dst; + ip6_mloopback(ifp, m, (struct sockaddr_in6 *)&ro.ro_dst); } /* * Put the packet into the sending queue of the outgoing interface * if it would fit in the MTU of the interface. */ if (mb_copy->m_pkthdr.len < ifp->if_mtu || ifp->if_mtu < IPV6_MMTU) { - ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro6.ro_dst.sin6_family = AF_INET6; - ro6.ro_dst.sin6_addr = ip6->ip6_dst; + dst6->sin6_len = sizeof(struct sockaddr_in6); + dst6->sin6_family = AF_INET6; + dst6->sin6_addr = ip6->ip6_dst; /* * We just call if_output instead of nd6_output here, since * we need no ND for a multicast forwarded packet...right? */ error = (*ifp->if_output)(ifp, mb_copy, - (struct sockaddr *)&ro6.ro_dst, - NULL); + (struct sockaddr *)&ro.ro_dst, NULL); #ifdef MRT6DEBUG if (mrt6debug & DEBUG_XMIT) log(LOG_DEBUG, "phyint_send on mif %d err %d\n", mifp - mif6table, error); #endif - } - else { + } else { #ifdef MULTICAST_PMTUD icmp6_error(mb_copy, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); - return; #else #ifdef MRT6DEBUG if (mrt6debug & DEBUG_XMIT) log(LOG_DEBUG, - "phyint_send: packet too big on %s%u o %s g %s" + "phyint_send: packet too big on %s o %s g %s" " size %d(discarded)\n", - ifp->if_name, ifp->if_unit, + if_name(ifp), ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), mb_copy->m_pkthdr.len); #endif /* MRT6DEBUG */ m_freem(mb_copy); /* simply discard the packet */ - return; #endif } + + splx(s); } static int register_send(ip6, mif, m) - register struct ip6_hdr *ip6; + struct ip6_hdr *ip6; struct mif6 *mif; - register struct mbuf *m; + struct mbuf *m; { - register struct mbuf *mm; - register int i, len = m->m_pkthdr.len; + struct mbuf *mm; + int i, len = m->m_pkthdr.len; static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 }; struct mrt6msg *im6; @@ -1530,6 +1535,7 @@ register_send(ip6, mif, m) MGETHDR(mm, M_DONTWAIT, MT_HEADER); if (mm == NULL) return ENOBUFS; + mm->m_pkthdr.rcvif = NULL; mm->m_data += max_linkhdr; mm->m_len = sizeof(struct ip6_hdr); @@ -1568,8 +1574,8 @@ register_send(ip6, mif, m) log(LOG_WARNING, "register_send: ip_mrouter socket queue full\n"); #endif - ++mrt6stat.mrt6s_upq_sockfull; - return ENOBUFS; + ++mrt6stat.mrt6s_upq_sockfull; + return ENOBUFS; } return 0; } @@ -1586,21 +1592,21 @@ pim6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { - register struct pim *pim; /* pointer to a pim struct */ - register struct ip6_hdr *ip6; - register int pimlen; + struct pim *pim; /* pointer to a pim struct */ + struct ip6_hdr *ip6; + int pimlen; struct mbuf *m = *mp; - int minlen; + int minlen; int off = *offp; ++pim6stat.pim6s_rcv_total; - ip6 = mtod(m, struct ip6_hdr *); - pimlen = m->m_pkthdr.len - *offp; + ip6 = mtod(m, struct ip6_hdr *); + pimlen = m->m_pkthdr.len - *offp; - /* - * Validate lengths - */ + /* + * Validate lengths + */ if (pimlen < PIM_MINLEN) { ++pim6stat.pim6s_rcv_tooshort; #ifdef MRT6DEBUG @@ -1736,6 +1742,18 @@ pim6_input(mp, offp, proto) ip6_sprintf(&eip6->ip6_dst), ntohs(eip6->ip6_plen)); #endif + + /* verify the version number of the inner packet */ + if ((eip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + ++pim6stat.pim6s_rcv_badregisters; +#ifdef MRT6DEBUG + log(LOG_DEBUG, "pim6_input: invalid IP version (%d) " + "of the inner packet\n", + (eip6->ip6_vfc & IPV6_VERSION)); +#endif + m_freem(m); + return(IPPROTO_NONE); + } /* verify the inner packet is destined to a mcast group */ if (!IN6_IS_ADDR_MULTICAST(&eip6->ip6_dst)) { @@ -1781,7 +1799,7 @@ pim6_input(mp, offp, proto) #endif rc = if_simloop(mif6table[reg_mif_num].m6_ifp, m, - dst.sin6_family, NULL); + dst.sin6_family, NULL); /* prepare the register head to send to the mrouting daemon */ m = mcp; diff --git a/sys/netinet6/ip6_mroute.h b/sys/netinet6/ip6_mroute.h index 34ec5388940f..7871150b6047 100644 --- a/sys/netinet6/ip6_mroute.h +++ b/sys/netinet6/ip6_mroute.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_mroute.h,v 1.10 2000/05/19 02:38:53 itojun Exp $ */ +/* $KAME: ip6_mroute.h,v 1.17 2001/02/10 02:05:52 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -79,7 +79,7 @@ typedef u_short mifi_t; /* type of a mif index */ #define IF_SETSIZE 256 #endif -typedef long if_mask; +typedef u_int32_t if_mask; #define NIFBITS (sizeof(if_mask) * NBBY) /* bits per mask */ #ifndef howmany @@ -87,7 +87,7 @@ typedef long if_mask; #endif typedef struct if_set { - fd_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)]; + if_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)]; } if_set; #define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS))) diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 4176bc47667f..928f3d3ffbf3 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_output.c,v 1.115 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: ip6_output.c,v 1.180 2001/05/21 05:37:50 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -89,6 +89,7 @@ #include <netinet/in.h> #include <netinet/in_var.h> +#include <netinet6/in6_var.h> #include <netinet/ip6.h> #include <netinet/icmp6.h> #include <netinet6/ip6_var.h> @@ -103,10 +104,10 @@ #include <netkey/key.h> #endif /* IPSEC */ -#include <net/net_osdep.h> - #include <netinet6/ip6_fw.h> +#include <net/net_osdep.h> + #include <netinet6/ip6protosw.h> static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); @@ -138,6 +139,22 @@ extern u_char ip6_protox[IPPROTO_MAX]; * This function may modify ver and hlim only. * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. + * + * type of "mtu": rt_rmx.rmx_mtu is u_long, ifnet.ifr_mtu is int, and + * nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one, + * which is rt_rmx.rmx_mtu. + * + * If MIP6 is active it will have to add a Home Address option to DH1 if + * the mobile node is roaming or a Routing Header type 0 if there exist + * a Binding Cache entry for the destination node or a BU option to DH2 + * if the mobile node initiates communication and no BUL entry exist. + * The only way to do this is to allocate new memory, copy the user data + * to the new buffer and then add the Home Address option, BU option and + * routing header type 0 respectively. MIP6 will set two flags in "struct + * pktopts" to restore the original contents once ip6_output is completed. + * To make this work, make sure that function exit is made through label + * alldone. + * */ int ip6_output(m0, opt, ro, flags, im6o, ifpp) @@ -175,7 +192,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) /* for AH processing. stupid to have "socket" variable in IP layer... */ so = ipsec_getsocket(m); - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); ip6 = mtod(m, struct ip6_hdr *); #endif /* IPSEC */ @@ -419,13 +436,13 @@ skip_ipsec2:; struct ip6_rthdr0 *rh0; finaldst = ip6->ip6_dst; - switch(rh->ip6r_type) { + switch (rh->ip6r_type) { case IPV6_RTHDR_TYPE_0: rh0 = (struct ip6_rthdr0 *)rh; ip6->ip6_dst = rh0->ip6r0_addr[0]; bcopy((caddr_t)&rh0->ip6r0_addr[1], - (caddr_t)&rh0->ip6r0_addr[0], - sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) + (caddr_t)&rh0->ip6r0_addr[0], + sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) ); rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst; break; @@ -745,7 +762,7 @@ skip_ipsec2:; u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu; mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; - if (mtu > ifmtu) { + if (mtu > ifmtu || mtu == 0) { /* * The MTU on the route is larger than the MTU on * the interface! This shouldn't happen, unless the @@ -753,6 +770,9 @@ skip_ipsec2:; * interface was brought up. Change the MTU in the * route to match the interface MTU (as long as the * field isn't locked). + * + * if MTU on the route is 0, we need to fix the MTU. + * this case happens with path MTU discovery timeouts. */ mtu = ifmtu; if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0) @@ -762,6 +782,12 @@ skip_ipsec2:; mtu = nd_ifinfo[ifp->if_index].linkmtu; } + /* + * advanced API (IPV6_USE_MIN_MTU) overrides mtu setting + */ + if ((flags & IPV6_MINMTU) != 0 && mtu > IPV6_MMTU) + mtu = IPV6_MMTU; + /* Fake scoped addresses */ if ((ifp->if_flags & IFF_LOOPBACK) != 0) { /* @@ -776,34 +802,44 @@ skip_ipsec2:; * field of the structure here. * We rely on the consistency between two scope zone ids * of source add destination, which should already be assured - * Larger scopes than link will be supported in the near + * larger scopes than link will be supported in the near * future. */ + origifp = NULL; if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])]; else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])]; - else + /* + * XXX: origifp can be NULL even in those two cases above. + * For example, if we remove the (only) link-local address + * from the loopback interface, and try to send a link-local + * address without link-id information. Then the source + * address is ::1, and the destination address is the + * link-local address with its s6_addr16[1] being zero. + * What is worse, if the packet goes to the loopback interface + * by a default rejected route, the null pointer would be + * passed to looutput, and the kernel would hang. + * The following last resort would prevent such disaster. + */ + if (origifp == NULL) origifp = ifp; } else origifp = ifp; -#ifndef FAKE_LOOPBACK_IF - if ((ifp->if_flags & IFF_LOOPBACK) == 0) -#else - if (1) +#ifndef SCOPEDROUTING + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); #endif - { - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) - ip6->ip6_src.s6_addr16[1] = 0; - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] = 0; - } /* * Check with the firewall... */ - if (ip6_fw_enable && ip6_fw_chk_ptr) { + if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; m->m_pkthdr.rcvif = NULL; /*XXX*/ /* If ipfw says divert, we have to just drop packet */ @@ -823,11 +859,14 @@ skip_ipsec2:; * (RFC 2460, section 4.) */ if (exthdrs.ip6e_hbh) { - struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, - struct ip6_hbh *); + struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); u_int32_t dummy1; /* XXX unused */ u_int32_t dummy2; /* XXX unused */ +#ifdef DIAGNOSTIC + if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len) + panic("ip6e_hbh is not continuous"); +#endif /* * XXX: if we have to send an ICMPv6 error to the sender, * we need the M_LOOP flag since icmp6_error() expects @@ -889,12 +928,15 @@ skip_ipsec2:; #endif ) { - /* Record statistics for this interface address. */ - if (ia && !(flags & IPV6_FORWARDING)) { - ia->ia_ifa.if_opackets++; - ia->ia_ifa.if_obytes += m->m_pkthdr.len; - } - + /* Record statistics for this interface address. */ + if (ia && !(flags & IPV6_FORWARDING)) { + ia->ia_ifa.if_opackets++; + ia->ia_ifa.if_obytes += m->m_pkthdr.len; + } +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); goto done; } else if (mtu < IPV6_MMTU) { @@ -923,6 +965,7 @@ skip_ipsec2:; hlen = unfragpartlen; if (mtu > IPV6_MAXPACKET) mtu = IPV6_MAXPACKET; + len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; if (len < 8) { error = EMSGSIZE; @@ -962,6 +1005,7 @@ skip_ipsec2:; ip6stat.ip6s_odropped++; goto sendorfree; } + m->m_pkthdr.rcvif = NULL; m->m_flags = m0->m_flags & M_COPYFLAGS; *mnext = m; mnext = &m->m_nextpkt; @@ -1011,12 +1055,15 @@ sendorfree: m0 = m->m_nextpkt; m->m_nextpkt = 0; if (error == 0) { - /* Record statistics for this interface address. */ - if (ia) { - ia->ia_ifa.if_opackets++; - ia->ia_ifa.if_obytes += m->m_pkthdr.len; - } - + /* Record statistics for this interface address. */ + if (ia) { + ia->ia_ifa.if_opackets++; + ia->ia_ifa.if_obytes += m->m_pkthdr.len; + } +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); } else m_freem(m); @@ -1090,6 +1137,7 @@ ip6_insert_jumboopt(exthdrs, plen) { struct mbuf *mopt; u_char *optbuf; + u_int32_t v; #define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ @@ -1112,18 +1160,42 @@ ip6_insert_jumboopt(exthdrs, plen) mopt = exthdrs->ip6e_hbh; if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) { - caddr_t oldoptp = mtod(mopt, caddr_t); + /* + * XXX assumption: + * - exthdrs->ip6e_hbh is not referenced from places + * other than exthdrs. + * - exthdrs->ip6e_hbh is not an mbuf chain. + */ int oldoptlen = mopt->m_len; + struct mbuf *n; - if (mopt->m_flags & M_EXT) - return(ENOBUFS); /* XXX */ - MCLGET(mopt, M_DONTWAIT); - if ((mopt->m_flags & M_EXT) == 0) + /* + * XXX: give up if the whole (new) hbh header does + * not fit even in an mbuf cluster. + */ + if (oldoptlen + JUMBOOPTLEN > MCLBYTES) return(ENOBUFS); - bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen); - optbuf = mtod(mopt, caddr_t) + oldoptlen; - mopt->m_len = oldoptlen + JUMBOOPTLEN; + /* + * As a consequence, we must always prepare a cluster + * at this point. + */ + MGET(n, M_DONTWAIT, MT_DATA); + if (n) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_freem(n); + n = NULL; + } + } + if (!n) + return(ENOBUFS); + n->m_len = oldoptlen + JUMBOOPTLEN; + bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t), + oldoptlen); + optbuf = mtod(n, caddr_t) + oldoptlen; + m_freem(mopt); + mopt = exthdrs->ip6e_hbh = n; } else { optbuf = mtod(mopt, u_char *) + mopt->m_len; mopt->m_len += JUMBOOPTLEN; @@ -1142,7 +1214,8 @@ ip6_insert_jumboopt(exthdrs, plen) /* fill in the option. */ optbuf[2] = IP6OPT_JUMBO; optbuf[3] = 4; - *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN); + v = (u_int32_t)htonl(plen + JUMBOOPTLEN); + bcopy(&v, &optbuf[4], sizeof(u_int32_t)); /* finally, adjust the packet header length */ exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; @@ -1206,7 +1279,7 @@ ip6_ctloutput(so, sopt) struct sockopt *sopt; { int privileged; - register struct inpcb *in6p = sotoinpcb(so); + struct inpcb *in6p = sotoinpcb(so); int error, optval; int level, op, optname; int optlen; @@ -1227,93 +1300,131 @@ ip6_ctloutput(so, sopt) if (level == IPPROTO_IPV6) { switch (op) { + case SOPT_SET: switch (optname) { case IPV6_PKTOPTIONS: - { + { struct mbuf *m; error = soopt_getm(sopt, &m); /* XXX */ - if (error != 0) + if (error != NULL) break; error = soopt_mcopyin(sopt, m); /* XXX */ - if (error != 0) - break; - return (ip6_pcbopts(&in6p->in6p_outputopts, - m, so, sopt)); - } - case IPV6_HOPOPTS: - case IPV6_DSTOPTS: - if (!privileged) { - error = EPERM; + if (error != NULL) break; - } - /* fall through */ + error = ip6_pcbopts(&in6p->in6p_outputopts, + m, so, sopt); + m_freem(m); /* XXX */ + break; + } + + /* + * Use of some Hop-by-Hop options or some + * Destination options, might require special + * privilege. That is, normal applications + * (without special privilege) might be forbidden + * from setting certain options in outgoing packets, + * and might never see certain options in received + * packets. [RFC 2292 Section 6] + * KAME specific note: + * KAME prevents non-privileged users from sending or + * receiving ANY hbh/dst options in order to avoid + * overhead of parsing options in the kernel. + */ case IPV6_UNICAST_HOPS: - case IPV6_PKTINFO: - case IPV6_HOPLIMIT: - case IPV6_RTHDR: case IPV6_CHECKSUM: case IPV6_FAITH: - case IPV6_BINDV6ONLY: - if (optlen != sizeof(int)) + + case IPV6_V6ONLY: + if (optlen != sizeof(int)) { error = EINVAL; - else { - error = sooptcopyin(sopt, &optval, - sizeof optval, sizeof optval); - if (error) - break; - switch (optname) { - - case IPV6_UNICAST_HOPS: - if (optval < -1 || optval >= 256) - error = EINVAL; - else { - /* -1 = kernel default */ - in6p->in6p_hops = optval; - if ((in6p->in6p_vflag & - INP_IPV4) != 0) - in6p->inp_ip_ttl = optval; - } - break; + break; + } + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; + switch (optname) { + + case IPV6_UNICAST_HOPS: + if (optval < -1 || optval >= 256) + error = EINVAL; + else { + /* -1 = kernel default */ + in6p->in6p_hops = optval; + + if ((in6p->in6p_vflag & + INP_IPV4) != 0) + in6p->inp_ip_ttl = optval; + } + break; #define OPTSET(bit) \ +do { \ if (optval) \ - in6p->in6p_flags |= bit; \ + in6p->in6p_flags |= (bit); \ else \ - in6p->in6p_flags &= ~bit; - - case IPV6_PKTINFO: - OPTSET(IN6P_PKTINFO); - break; - - case IPV6_HOPLIMIT: - OPTSET(IN6P_HOPLIMIT); - break; - - case IPV6_HOPOPTS: - OPTSET(IN6P_HOPOPTS); - break; - - case IPV6_DSTOPTS: - OPTSET(IN6P_DSTOPTS); - break; + in6p->in6p_flags &= ~(bit); \ +} while (0) +#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0) - case IPV6_RTHDR: - OPTSET(IN6P_RTHDR); - break; + case IPV6_CHECKSUM: + in6p->in6p_cksum = optval; + break; - case IPV6_CHECKSUM: - in6p->in6p_cksum = optval; - break; + case IPV6_FAITH: + OPTSET(IN6P_FAITH); + break; - case IPV6_FAITH: - OPTSET(IN6P_FAITH); - break; + case IPV6_V6ONLY: + /* + * XXX: BINDV6ONLY should be integrated + * into V6ONLY. + */ + OPTSET(IN6P_BINDV6ONLY); + OPTSET(IN6P_IPV6_V6ONLY); + break; + } + break; - case IPV6_BINDV6ONLY: - OPTSET(IN6P_BINDV6ONLY); - break; - } + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: + case IPV6_HOPOPTS: + case IPV6_DSTOPTS: + case IPV6_RTHDR: + /* RFC 2292 */ + if (optlen != sizeof(int)) { + error = EINVAL; + break; + } + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; + switch (optname) { + case IPV6_PKTINFO: + OPTSET(IN6P_PKTINFO); + break; + case IPV6_HOPLIMIT: + OPTSET(IN6P_HOPLIMIT); + break; + case IPV6_HOPOPTS: + /* + * Check super-user privilege. + * See comments for IPV6_RECVHOPOPTS. + */ + if (!privileged) + return(EPERM); + OPTSET(IN6P_HOPOPTS); + break; + case IPV6_DSTOPTS: + if (!privileged) + return(EPERM); + OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */ + break; + case IPV6_RTHDR: + OPTSET(IN6P_RTHDR); + break; } break; #undef OPTSET @@ -1393,7 +1504,7 @@ ip6_ctloutput(so, sopt) m_freem(m); } break; -#endif /* IPSEC */ +#endif /* KAME IPSEC */ case IPV6_FW_ADD: case IPV6_FW_DEL: @@ -1405,11 +1516,9 @@ ip6_ctloutput(so, sopt) if (ip6_fw_ctl_ptr == NULL) return EINVAL; - if ((error = soopt_getm(sopt, &m)) - != 0) /* XXX */ + if (error = soopt_getm(sopt, &m)) /* XXX */ break; - if ((error = soopt_mcopyin(sopt, m)) - != 0) /* XXX */ + if (error = soopt_mcopyin(sopt, m)) /* XXX */ break; error = (*ip6_fw_ctl_ptr)(optname, mp); m = *mp; @@ -1433,20 +1542,11 @@ ip6_ctloutput(so, sopt) sopt->sopt_valsize = 0; break; - case IPV6_HOPOPTS: - case IPV6_DSTOPTS: - if (!privileged) { - error = EPERM; - break; - } - /* fall through */ case IPV6_UNICAST_HOPS: - case IPV6_PKTINFO: - case IPV6_HOPLIMIT: - case IPV6_RTHDR: case IPV6_CHECKSUM: + case IPV6_FAITH: - case IPV6_BINDV6ONLY: + case IPV6_V6ONLY: case IPV6_PORTRANGE: switch (optname) { @@ -1454,28 +1554,6 @@ ip6_ctloutput(so, sopt) optval = in6p->in6p_hops; break; -#define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0) - - case IPV6_PKTINFO: - optval = OPTBIT(IN6P_PKTINFO); - break; - - case IPV6_HOPLIMIT: - optval = OPTBIT(IN6P_HOPLIMIT); - break; - - case IPV6_HOPOPTS: - optval = OPTBIT(IN6P_HOPOPTS); - break; - - case IPV6_DSTOPTS: - optval = OPTBIT(IN6P_DSTOPTS); - break; - - case IPV6_RTHDR: - optval = OPTBIT(IN6P_RTHDR); - break; - case IPV6_CHECKSUM: optval = in6p->in6p_cksum; break; @@ -1484,14 +1562,14 @@ ip6_ctloutput(so, sopt) optval = OPTBIT(IN6P_FAITH); break; - case IPV6_BINDV6ONLY: + case IPV6_V6ONLY: + /* XXX: see the setopt case. */ optval = OPTBIT(IN6P_BINDV6ONLY); break; case IPV6_PORTRANGE: { int flags; - flags = in6p->in6p_flags; if (flags & IN6P_HIGHPORT) optval = IPV6_PORTRANGE_HIGH; @@ -1506,6 +1584,40 @@ ip6_ctloutput(so, sopt) sizeof optval); break; + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: + case IPV6_HOPOPTS: + case IPV6_RTHDR: + case IPV6_DSTOPTS: + if (optname == IPV6_HOPOPTS || + optname == IPV6_DSTOPTS || + !privileged) + return(EPERM); + switch (optname) { + case IPV6_PKTINFO: + optval = OPTBIT(IN6P_PKTINFO); + break; + case IPV6_HOPLIMIT: + optval = OPTBIT(IN6P_HOPLIMIT); + break; + case IPV6_HOPOPTS: + if (!privileged) + return(EPERM); + optval = OPTBIT(IN6P_HOPOPTS); + break; + case IPV6_RTHDR: + optval = OPTBIT(IN6P_RTHDR); + break; + case IPV6_DSTOPTS: + if (!privileged) + return(EPERM); + optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); + break; + } + error = sooptcopyout(sopt, &optval, + sizeof optval); + break; + case IPV6_MULTICAST_IF: case IPV6_MULTICAST_HOPS: case IPV6_MULTICAST_LOOP: @@ -1543,10 +1655,11 @@ ip6_ctloutput(so, sopt) error = ipsec6_get_policy(in6p, req, len, mp); if (error == 0) error = soopt_mcopyout(sopt, m); /*XXX*/ - m_freem(m); + if (error == 0 && m) + m_freem(m); break; } -#endif /* IPSEC */ +#endif /* KAME IPSEC */ case IPV6_FW_GET: { @@ -1555,6 +1668,8 @@ ip6_ctloutput(so, sopt) if (ip6_fw_ctl_ptr == NULL) { + if (m) + (void)m_free(m); return EINVAL; } error = (*ip6_fw_ctl_ptr)(optname, mp); @@ -1578,29 +1693,33 @@ ip6_ctloutput(so, sopt) } /* - * Set up IP6 options in pcb for insertion in output packets. - * Store in mbuf with pointer in pcbopt, adding pseudo-option - * with destination address if source routed. + * Set up IP6 options in pcb for insertion in output packets or + * specifying behavior of outgoing packets. */ static int ip6_pcbopts(pktopt, m, so, sopt) struct ip6_pktopts **pktopt; - register struct mbuf *m; + struct mbuf *m; struct socket *so; struct sockopt *sopt; { - register struct ip6_pktopts *opt = *pktopt; + struct ip6_pktopts *opt = *pktopt; int error = 0; struct proc *p = sopt->sopt_p; int priv = 0; /* turn off any old options. */ if (opt) { - if (opt->ip6po_m) - (void)m_free(opt->ip6po_m); +#ifdef DIAGNOSTIC + if (opt->ip6po_pktinfo || opt->ip6po_nexthop || + opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || + opt->ip6po_rhinfo.ip6po_rhi_rthdr) + printf("ip6_pcbopts: all specified options are cleared.\n"); +#endif + ip6_clearpktopts(opt, 1, -1); } else opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK); - *pktopt = 0; + *pktopt = NULL; if (!m || m->m_len == 0) { /* @@ -1608,16 +1727,14 @@ ip6_pcbopts(pktopt, m, so, sopt) */ if (opt) free(opt, M_IP6OPT); - if (m) - (void)m_free(m); return(0); } /* set options specified by user. */ if (p && !suser(p)) priv = 1; - if ((error = ip6_setpktoptions(m, opt, priv)) != 0) { - (void)m_free(m); + if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) { + ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */ return(error); } *pktopt = opt; @@ -1625,6 +1742,140 @@ ip6_pcbopts(pktopt, m, so, sopt) } /* + * initialize ip6_pktopts. beware that there are non-zero default values in + * the struct. + */ +void +init_ip6pktopts(opt) + struct ip6_pktopts *opt; +{ + + bzero(opt, sizeof(*opt)); + opt->ip6po_hlim = -1; /* -1 means default hop limit */ +} + +void +ip6_clearpktopts(pktopt, needfree, optname) + struct ip6_pktopts *pktopt; + int needfree, optname; +{ + if (pktopt == NULL) + return; + + if (optname == -1) { + if (needfree && pktopt->ip6po_pktinfo) + free(pktopt->ip6po_pktinfo, M_IP6OPT); + pktopt->ip6po_pktinfo = NULL; + } + if (optname == -1) + pktopt->ip6po_hlim = -1; + if (optname == -1) { + if (needfree && pktopt->ip6po_nexthop) + free(pktopt->ip6po_nexthop, M_IP6OPT); + pktopt->ip6po_nexthop = NULL; + } + if (optname == -1) { + if (needfree && pktopt->ip6po_hbh) + free(pktopt->ip6po_hbh, M_IP6OPT); + pktopt->ip6po_hbh = NULL; + } + if (optname == -1) { + if (needfree && pktopt->ip6po_dest1) + free(pktopt->ip6po_dest1, M_IP6OPT); + pktopt->ip6po_dest1 = NULL; + } + if (optname == -1) { + if (needfree && pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) + free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT); + pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL; + if (pktopt->ip6po_route.ro_rt) { + RTFREE(pktopt->ip6po_route.ro_rt); + pktopt->ip6po_route.ro_rt = NULL; + } + } + if (optname == -1) { + if (needfree && pktopt->ip6po_dest2) + free(pktopt->ip6po_dest2, M_IP6OPT); + pktopt->ip6po_dest2 = NULL; + } +} + +#define PKTOPT_EXTHDRCPY(type) \ +do {\ + if (src->type) {\ + int hlen =\ + (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\ + dst->type = malloc(hlen, M_IP6OPT, canwait);\ + if (dst->type == NULL && canwait == M_NOWAIT)\ + goto bad;\ + bcopy(src->type, dst->type, hlen);\ + }\ +} while (0) + +struct ip6_pktopts * +ip6_copypktopts(src, canwait) + struct ip6_pktopts *src; + int canwait; +{ + struct ip6_pktopts *dst; + + if (src == NULL) { + printf("ip6_clearpktopts: invalid argument\n"); + return(NULL); + } + + dst = malloc(sizeof(*dst), M_IP6OPT, canwait); + if (dst == NULL && canwait == M_NOWAIT) + goto bad; + bzero(dst, sizeof(*dst)); + + dst->ip6po_hlim = src->ip6po_hlim; + if (src->ip6po_pktinfo) { + dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo), + M_IP6OPT, canwait); + if (dst->ip6po_pktinfo == NULL && canwait == M_NOWAIT) + goto bad; + *dst->ip6po_pktinfo = *src->ip6po_pktinfo; + } + if (src->ip6po_nexthop) { + dst->ip6po_nexthop = malloc(src->ip6po_nexthop->sa_len, + M_IP6OPT, canwait); + if (dst->ip6po_nexthop == NULL && canwait == M_NOWAIT) + goto bad; + bcopy(src->ip6po_nexthop, dst->ip6po_nexthop, + src->ip6po_nexthop->sa_len); + } + PKTOPT_EXTHDRCPY(ip6po_hbh); + PKTOPT_EXTHDRCPY(ip6po_dest1); + PKTOPT_EXTHDRCPY(ip6po_dest2); + PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */ + return(dst); + + bad: + printf("ip6_copypktopts: copy failed"); + if (dst->ip6po_pktinfo) free(dst->ip6po_pktinfo, M_IP6OPT); + if (dst->ip6po_nexthop) free(dst->ip6po_nexthop, M_IP6OPT); + if (dst->ip6po_hbh) free(dst->ip6po_hbh, M_IP6OPT); + if (dst->ip6po_dest1) free(dst->ip6po_dest1, M_IP6OPT); + if (dst->ip6po_dest2) free(dst->ip6po_dest2, M_IP6OPT); + if (dst->ip6po_rthdr) free(dst->ip6po_rthdr, M_IP6OPT); + return(NULL); +} +#undef PKTOPT_EXTHDRCPY + +void +ip6_freepcbopts(pktopt) + struct ip6_pktopts *pktopt; +{ + if (pktopt == NULL) + return; + + ip6_clearpktopts(pktopt, 1, -1); + + free(pktopt, M_IP6OPT); +} + +/* * Set the IP6 multicast options in response to user setsockopt(). */ static int @@ -1670,7 +1921,7 @@ ip6_setmoptions(optname, im6op, m) error = EINVAL; break; } - ifindex = *(mtod(m, u_int *)); + bcopy(mtod(m, u_int *), &ifindex, sizeof(ifindex)); if (ifindex < 0 || if_index < ifindex) { error = ENXIO; /* XXX EINVAL? */ break; @@ -1693,7 +1944,7 @@ ip6_setmoptions(optname, im6op, m) error = EINVAL; break; } - optval = *(mtod(m, u_int *)); + bcopy(mtod(m, u_int *), &optval, sizeof(optval)); if (optval < -1 || optval >= 256) error = EINVAL; else if (optval == -1) @@ -1708,8 +1959,12 @@ ip6_setmoptions(optname, im6op, m) * Set the loopback flag for outgoing multicast packets. * Must be zero or one. */ - if (m == NULL || m->m_len != sizeof(u_int) || - (loop = *(mtod(m, u_int *))) > 1) { + if (m == NULL || m->m_len != sizeof(u_int)) { + error = EINVAL; + break; + } + bcopy(mtod(m, u_int *), &loop, sizeof(loop)); + if (loop > 1) { error = EINVAL; break; } @@ -1915,8 +2170,8 @@ ip6_setmoptions(optname, im6op, m) static int ip6_getmoptions(optname, im6o, mp) int optname; - register struct ip6_moptions *im6o; - register struct mbuf **mp; + struct ip6_moptions *im6o; + struct mbuf **mp; { u_int *hlim, *loop, *ifindex; @@ -1961,7 +2216,7 @@ ip6_getmoptions(optname, im6o, mp) */ void ip6_freemoptions(im6o) - register struct ip6_moptions *im6o; + struct ip6_moptions *im6o; { struct in6_multi_mship *imm; @@ -1981,18 +2236,17 @@ ip6_freemoptions(im6o) * Set IPv6 outgoing packet options based on advanced API. */ int -ip6_setpktoptions(control, opt, priv) +ip6_setpktoptions(control, opt, priv, needcopy) struct mbuf *control; struct ip6_pktopts *opt; - int priv; + int priv, needcopy; { - register struct cmsghdr *cm = 0; + struct cmsghdr *cm = 0; if (control == 0 || opt == 0) return(EINVAL); - bzero(opt, sizeof(*opt)); - opt->ip6po_hlim = -1; /* -1 means to use default hop limit */ + init_ip6pktopts(opt); /* * XXX: Currently, we assume all the optional information is stored @@ -2001,21 +2255,31 @@ ip6_setpktoptions(control, opt, priv) if (control->m_next) return(EINVAL); - opt->ip6po_m = control; - - for (; control->m_len; control->m_data += ALIGN(cm->cmsg_len), - control->m_len -= ALIGN(cm->cmsg_len)) { + for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len), + control->m_len -= CMSG_ALIGN(cm->cmsg_len)) { cm = mtod(control, struct cmsghdr *); if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) return(EINVAL); if (cm->cmsg_level != IPPROTO_IPV6) continue; - switch(cm->cmsg_type) { + /* + * XXX should check if RFC2292 API is mixed with 2292bis API + */ + switch (cm->cmsg_type) { case IPV6_PKTINFO: if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) return(EINVAL); - opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm); + if (needcopy) { + /* XXX: Is it really WAITOK? */ + opt->ip6po_pktinfo = + malloc(sizeof(struct in6_pktinfo), + M_IP6OPT, M_WAITOK); + bcopy(CMSG_DATA(cm), opt->ip6po_pktinfo, + sizeof(struct in6_pktinfo)); + } else + opt->ip6po_pktinfo = + (struct in6_pktinfo *)CMSG_DATA(cm); if (opt->ip6po_pktinfo->ipi6_ifindex && IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr)) opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] = @@ -2026,8 +2290,13 @@ ip6_setpktoptions(control, opt, priv) return(ENXIO); } + /* + * Check if the requested source address is indeed a + * unicast address assigned to the node, and can be + * used as the packet's source address. + */ if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) { - struct ifaddr *ia; + struct in6_ifaddr *ia6; struct sockaddr_in6 sin6; bzero(&sin6, sizeof(sin6)); @@ -2035,19 +2304,10 @@ ip6_setpktoptions(control, opt, priv) sin6.sin6_family = AF_INET6; sin6.sin6_addr = opt->ip6po_pktinfo->ipi6_addr; - ia = ifa_ifwithaddr(sin6tosa(&sin6)); - if (ia == NULL || - (opt->ip6po_pktinfo->ipi6_ifindex && - (ia->ifa_ifp->if_index != - opt->ip6po_pktinfo->ipi6_ifindex))) { - return(EADDRNOTAVAIL); - } - /* - * Check if the requested source address is - * indeed a unicast address assigned to the - * node. - */ - if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr)) + ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6)); + if (ia6 == NULL || + (ia6->ia6_flags & (IN6_IFF_ANYCAST | + IN6_IFF_NOTREADY)) != 0) return(EADDRNOTAVAIL); } break; @@ -2064,66 +2324,120 @@ ip6_setpktoptions(control, opt, priv) case IPV6_NEXTHOP: if (!priv) return(EPERM); + if (cm->cmsg_len < sizeof(u_char) || + /* check if cmsg_len is large enough for sa_len */ cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm))) return(EINVAL); - opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm); - + if (needcopy) { + opt->ip6po_nexthop = + malloc(*CMSG_DATA(cm), + M_IP6OPT, M_WAITOK); + bcopy(CMSG_DATA(cm), + opt->ip6po_nexthop, + *CMSG_DATA(cm)); + } else + opt->ip6po_nexthop = + (struct sockaddr *)CMSG_DATA(cm); break; case IPV6_HOPOPTS: + { + struct ip6_hbh *hbh; + int hbhlen; + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh))) return(EINVAL); - opt->ip6po_hbh = (struct ip6_hbh *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_hbh->ip6h_len + 1) << 3)) + hbh = (struct ip6_hbh *)CMSG_DATA(cm); + hbhlen = (hbh->ip6h_len + 1) << 3; + if (cm->cmsg_len != CMSG_LEN(hbhlen)) return(EINVAL); + + if (needcopy) { + opt->ip6po_hbh = + malloc(hbhlen, M_IP6OPT, M_WAITOK); + bcopy(hbh, opt->ip6po_hbh, hbhlen); + } else + opt->ip6po_hbh = hbh; break; + } case IPV6_DSTOPTS: + { + struct ip6_dest *dest, **newdest; + int destlen; + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) return(EINVAL); + dest = (struct ip6_dest *)CMSG_DATA(cm); + destlen = (dest->ip6d_len + 1) << 3; + if (cm->cmsg_len != CMSG_LEN(destlen)) + return(EINVAL); - /* - * If there is no routing header yet, the destination - * options header should be put on the 1st part. - * Otherwise, the header should be on the 2nd part. - * (See RFC 2460, section 4.1) + /* + * The old advacned API is ambiguous on this + * point. Our approach is to determine the + * position based according to the existence + * of a routing header. Note, however, that + * this depends on the order of the extension + * headers in the ancillary data; the 1st part + * of the destination options header must + * appear before the routing header in the + * ancillary data, too. + * RFC2292bis solved the ambiguity by + * introducing separate cmsg types. */ - if (opt->ip6po_rthdr == NULL) { - opt->ip6po_dest1 = - (struct ip6_dest *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_dest1->ip6d_len + 1) - << 3)) - return(EINVAL); - } else { - opt->ip6po_dest2 = - (struct ip6_dest *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1) - << 3)) - return(EINVAL); - } + if (opt->ip6po_rthdr == NULL) + newdest = &opt->ip6po_dest1; + else + newdest = &opt->ip6po_dest2; + + if (needcopy) { + *newdest = malloc(destlen, M_IP6OPT, M_WAITOK); + bcopy(dest, *newdest, destlen); + } else + *newdest = dest; + break; + } case IPV6_RTHDR: + { + struct ip6_rthdr *rth; + int rthlen; + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) return(EINVAL); - opt->ip6po_rthdr = (struct ip6_rthdr *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_rthdr->ip6r_len + 1) << 3)) + rth = (struct ip6_rthdr *)CMSG_DATA(cm); + rthlen = (rth->ip6r_len + 1) << 3; + if (cm->cmsg_len != CMSG_LEN(rthlen)) return(EINVAL); - switch(opt->ip6po_rthdr->ip6r_type) { + + switch (rth->ip6r_type) { case IPV6_RTHDR_TYPE_0: - if (opt->ip6po_rthdr->ip6r_segleft == 0) + /* must contain one addr */ + if (rth->ip6r_len == 0) + return(EINVAL); + /* length must be even */ + if (rth->ip6r_len % 2) + return(EINVAL); + if (rth->ip6r_len / 2 != rth->ip6r_segleft) return(EINVAL); break; default: - return(EINVAL); + return(EINVAL); /* not supported */ } + + if (needcopy) { + opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, + M_WAITOK); + bcopy(rth, opt->ip6po_rthdr, rthlen); + } else + opt->ip6po_rthdr = rth; + break; + } default: return(ENOPROTOOPT); @@ -2142,8 +2456,8 @@ ip6_setpktoptions(control, opt, priv) void ip6_mloopback(ifp, m, dst) struct ifnet *ifp; - register struct mbuf *m; - register struct sockaddr_in6 *dst; + struct mbuf *m; + struct sockaddr_in6 *dst; { struct mbuf *copym; struct ip6_hdr *ip6; @@ -2171,18 +2485,15 @@ ip6_mloopback(ifp, m, dst) } #endif -#ifndef FAKE_LOOPBACK_IF - if ((ifp->if_flags & IFF_LOOPBACK) == 0) -#else - if (1) + ip6 = mtod(copym, struct ip6_hdr *); +#ifndef SCOPEDROUTING + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); #endif - { - ip6 = mtod(copym, struct ip6_hdr *); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) - ip6->ip6_src.s6_addr16[1] = 0; - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] = 0; - } (void)if_simloop(ifp, copym, dst->sin6_family, NULL); } @@ -2236,10 +2547,11 @@ ip6_optlen(in6p) (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0) len += elen(in6p->in6p_outputopts->ip6po_hbh); - len += elen(in6p->in6p_outputopts->ip6po_dest1); + if (in6p->in6p_outputopts->ip6po_rthdr) + /* dest1 is valid with rthdr only */ + len += elen(in6p->in6p_outputopts->ip6po_dest1); len += elen(in6p->in6p_outputopts->ip6po_rthdr); len += elen(in6p->in6p_outputopts->ip6po_dest2); return len; #undef elen } - diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index 234b2e9b13d0..dea9c0705d94 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */ +/* $KAME: ip6_var.h,v 1.62 2001/05/03 14:51:48 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -129,15 +129,29 @@ struct ip6po_rhinfo { struct ip6_pktopts { struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */ - int ip6po_hlim; /* Hoplimit for outgoing packets */ - struct in6_pktinfo *ip6po_pktinfo; /* Outgoing IF/address information */ - struct sockaddr *ip6po_nexthop; /* Next-hop address */ + int ip6po_hlim; /* Hoplimit for outgoing packets */ + + /* Outgoing IF/address information */ + struct in6_pktinfo *ip6po_pktinfo; + + struct sockaddr *ip6po_nexthop; /* Next-hop address */ + struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */ - struct ip6_dest *ip6po_dest1; /* Destination options header(1st part) */ - struct ip6po_rhinfo ip6po_rhinfo; /* Routing header related info. */ - struct ip6_dest *ip6po_dest2; /* Destination options header(2nd part) */ + + /* Destination options header (before a routing header) */ + struct ip6_dest *ip6po_dest1; + + /* Routing header related info. */ + struct ip6po_rhinfo ip6po_rhinfo; + + /* Destination options header (after a routing header) */ + struct ip6_dest *ip6po_dest2; }; +/* + * Control options for incoming packets + */ + struct ip6stat { u_quad_t ip6s_total; /* total packets received */ u_quad_t ip6s_tooshort; /* packet too short */ @@ -200,6 +214,37 @@ struct ip6stat { }; #ifdef _KERNEL +/* + * IPv6 onion peeling state. + * it will be initialized when we come into ip6_input(). + * XXX do not make it a kitchen sink! + */ +struct ip6aux { + u_int32_t ip6a_flags; +#define IP6A_SWAP 0x01 /* swapped home/care-of on packet */ +#define IP6A_HASEEN 0x02 /* HA was present */ +#define IP6A_BRUID 0x04 /* BR Unique Identifier was present */ +#define IP6A_RTALERTSEEN 0x08 /* rtalert present */ + + /* ip6.ip6_src */ + struct in6_addr ip6a_careof; /* care-of address of the peer */ + struct in6_addr ip6a_home; /* home address of the peer */ + u_int16_t ip6a_bruid; /* BR unique identifier */ + + /* ip6.ip6_dst */ + struct in6_ifaddr *ip6a_dstia6; /* my ifaddr that matches ip6_dst */ + + /* rtalert */ + u_int16_t ip6a_rtalert; /* rtalert option value */ + + /* + * decapsulation history will be here. + * with IPsec it may not be accurate. + */ +}; +#endif + +#ifdef _KERNEL /* flags passed to ip6_output as last parameter */ #define IPV6_DADOUTPUT 0x01 /* DAD */ #define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */ @@ -215,7 +260,8 @@ extern int ip6_gif_hlim; /* Hop limit for gif encap packet */ extern int ip6_use_deprecated; /* allow deprecated addr as source */ extern int ip6_rr_prune; /* router renumbering prefix * walk list every 5 sec. */ -extern int ip6_mapped_addr_on; +#define ip6_mapped_addr_on (!ip6_v6only) +extern int ip6_v6only; extern struct socket *ip6_mrouter; /* multicast routing daemon */ extern int ip6_sendredirects; /* send IP redirects when forwarding? */ @@ -231,6 +277,14 @@ extern int ip6_dad_count; /* DupAddrDetectionTransmits */ extern u_int32_t ip6_flow_seq; extern int ip6_auto_flowlabel; +extern int ip6_auto_linklocal; + +extern int ip6_anonportmin; /* minimum ephemeral port */ +extern int ip6_anonportmax; /* maximum ephemeral port */ +extern int ip6_lowportmin; /* minimum reserved port */ +extern int ip6_lowportmax; /* maximum reserved port */ + +extern int ip6_use_tempaddr; /* whether to use temporary addresses. */ extern struct pr_usrreqs rip6_usrreqs; struct sockopt; @@ -239,29 +293,41 @@ struct inpcb; int icmp6_ctloutput __P((struct socket *, struct sockopt *sopt)); +struct in6_ifaddr; void ip6_init __P((void)); void ip6intr __P((void)); void ip6_input __P((struct mbuf *)); +struct in6_ifaddr *ip6_getdstifaddr __P((struct mbuf *)); +void ip6_freepcbopts __P((struct ip6_pktopts *)); void ip6_freemoptions __P((struct ip6_moptions *)); int ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int)); char * ip6_get_prevhdr __P((struct mbuf *, int)); int ip6_nexthdr __P((struct mbuf *, int, int, int *)); int ip6_lasthdr __P((struct mbuf *, int, int, int *)); + +struct mbuf *ip6_addaux __P((struct mbuf *)); +struct mbuf *ip6_findaux __P((struct mbuf *)); +void ip6_delaux __P((struct mbuf *)); + int ip6_mforward __P((struct ip6_hdr *, struct ifnet *, struct mbuf *)); int ip6_process_hopopts __P((struct mbuf *, u_int8_t *, int, u_int32_t *, u_int32_t *)); void ip6_savecontrol __P((struct inpcb *, struct mbuf **, struct ip6_hdr *, - struct mbuf *)); + struct mbuf *)); +void ip6_notify_pmtu __P((struct inpcb *, struct sockaddr_in6 *, + u_int32_t *)); int ip6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); void ip6_forward __P((struct mbuf *, int)); void ip6_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *)); int ip6_output __P((struct mbuf *, struct ip6_pktopts *, - struct route_in6 *, int, + struct route_in6 *, + int, struct ip6_moptions *, struct ifnet **)); int ip6_ctloutput __P((struct socket *, struct sockopt *sopt)); -int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int)); +void init_ip6pktopts __P((struct ip6_pktopts *)); +int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int, int)); void ip6_clearpktopts __P((struct ip6_pktopts *, int, int)); struct ip6_pktopts *ip6_copypktopts __P((struct ip6_pktopts *, int)); int ip6_optlen __P((struct inpcb *)); diff --git a/sys/netinet6/ip6protosw.h b/sys/netinet6/ip6protosw.h index bd897931dcd3..c2f38fc57637 100644 --- a/sys/netinet6/ip6protosw.h +++ b/sys/netinet6/ip6protosw.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6protosw.h,v 1.11 2000/10/03 09:59:35 jinmei Exp $ */ +/* $KAME: ip6protosw.h,v 1.22 2001/02/08 18:02:08 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -87,16 +87,38 @@ struct socket; struct domain; struct proc; struct ip6_hdr; +struct icmp6_hdr; +struct in6_addr; struct pr_usrreqs; /* * argument type for the last arg of pr_ctlinput(). * should be consulted only with AF_INET6 family. + * + * IPv6 ICMP IPv6 [exthdrs] finalhdr paylaod + * ^ ^ ^ ^ + * | | ip6c_ip6 ip6c_off + * | ip6c_icmp6 + * ip6c_m + * + * ip6c_finaldst usually points to ip6c_ip6->ip6_dst. if the original + * (internal) packet carries a routing header, it may point the final + * dstination address in the routing header. + * + * ip6c_src: ip6c_ip6->ip6_src + scope info + flowlabel in ip6c_ip6 + * (beware of flowlabel, if you try to compare it against others) + * ip6c_dst: ip6c_finaldst + scope info */ struct ip6ctlparam { struct mbuf *ip6c_m; /* start of mbuf chain */ + struct icmp6_hdr *ip6c_icmp6; /* icmp6 header of target packet */ struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ int ip6c_off; /* offset of the target proto header */ + struct sockaddr_in6 *ip6c_src; /* srcaddr w/ additional info */ + struct sockaddr_in6 *ip6c_dst; /* (final) dstaddr w/ additional info */ + struct in6_addr *ip6c_finaldst; /* final destination address */ + void *ip6c_cmdarg; /* control command dependent data */ + u_int8_t ip6c_nxt; /* final next header field */ }; struct ip6protosw { diff --git a/sys/netinet6/ipcomp.h b/sys/netinet6/ipcomp.h index dea5ea8e7097..08d62da2b7b4 100644 --- a/sys/netinet6/ipcomp.h +++ b/sys/netinet6/ipcomp.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp.h,v 1.7 2000/05/18 12:45:13 sumikawa Exp $ */ +/* $KAME: ipcomp.h,v 1.8 2000/09/26 07:55:14 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -37,6 +37,10 @@ #ifndef _NETINET6_IPCOMP_H_ #define _NETINET6_IPCOMP_H_ +#if defined(_KERNEL) && !defined(_LKM) +#include "opt_inet.h" +#endif + struct ipcomp { u_int8_t comp_nxt; /* Next Header */ u_int8_t comp_flags; /* reserved, must be zero */ @@ -59,7 +63,7 @@ struct ipcomp_algorithm { }; struct ipsecrequest; -extern struct ipcomp_algorithm ipcomp_algorithms[]; +extern const struct ipcomp_algorithm *ipcomp_algorithm_lookup __P((int)); extern void ipcomp4_input __P((struct mbuf *, ...)); extern int ipcomp4_output __P((struct mbuf *, struct ipsecrequest *)); #endif /*KERNEL*/ diff --git a/sys/netinet6/ipcomp6.h b/sys/netinet6/ipcomp6.h index 553c8df7c4f4..4a1046af9074 100644 --- a/sys/netinet6/ipcomp6.h +++ b/sys/netinet6/ipcomp6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp.h,v 1.7 2000/05/18 12:45:13 sumikawa Exp $ */ +/* $KAME: ipcomp.h,v 1.8 2000/09/26 07:55:14 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. diff --git a/sys/netinet6/ipcomp_core.c b/sys/netinet6/ipcomp_core.c index 1eee2538b968..ec031f68e721 100644 --- a/sys/netinet6/ipcomp_core.c +++ b/sys/netinet6/ipcomp_core.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp_core.c,v 1.12 2000/05/05 11:01:01 sumikawa Exp $ */ +/* $KAME: ipcomp_core.c,v 1.24 2000/10/23 04:24:22 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -85,13 +85,20 @@ static int deflate_window_out = -12; static const int deflate_window_in = -1 * MAX_WBITS; /* don't change it */ static int deflate_memlevel = MAX_MEM_LEVEL; -struct ipcomp_algorithm ipcomp_algorithms[] = { - { NULL, NULL, -1 }, - { NULL, NULL, -1 }, +static const struct ipcomp_algorithm ipcomp_algorithms[] = { { deflate_compress, deflate_decompress, 90 }, - { NULL, NULL, 90 }, }; +const struct ipcomp_algorithm * +ipcomp_algorithm_lookup(idx) + int idx; +{ + + if (idx == SADB_X_CALG_DEFLATE) + return &ipcomp_algorithms[0]; + return NULL; +} + static void * deflate_alloc(aux, items, siz) void *aux; @@ -99,7 +106,7 @@ deflate_alloc(aux, items, siz) u_int siz; { void *ptr; - MALLOC(ptr, void *, items * siz, M_TEMP, M_NOWAIT); + ptr = malloc(items * siz, M_TEMP, M_NOWAIT); return ptr; } @@ -108,7 +115,7 @@ deflate_free(aux, ptr) void *aux; void *ptr; { - FREE(ptr, M_TEMP); + free(ptr, M_TEMP); } static int @@ -120,12 +127,47 @@ deflate_common(m, md, lenp, mode) { struct mbuf *mprev; struct mbuf *p; - struct mbuf *n, *n0 = NULL, **np; + struct mbuf *n = NULL, *n0 = NULL, **np; z_stream zs; int error = 0; int zerror; size_t offset; - int firsttime, final, flush; + +#define MOREBLOCK() \ +do { \ + /* keep the reply buffer into our chain */ \ + if (n) { \ + n->m_len = zs.total_out - offset; \ + offset = zs.total_out; \ + *np = n; \ + np = &n->m_next; \ + n = NULL; \ + } \ + \ + /* get a fresh reply buffer */ \ + MGET(n, M_DONTWAIT, MT_DATA); \ + if (n) { \ + MCLGET(n, M_DONTWAIT); \ + } \ + if (!n) { \ + error = ENOBUFS; \ + goto fail; \ + } \ + n->m_len = 0; \ + n->m_len = M_TRAILINGSPACE(n); \ + n->m_next = NULL; \ + /* \ + * if this is the first reply buffer, reserve \ + * region for ipcomp header. \ + */ \ + if (*np == NULL) { \ + n->m_len -= sizeof(struct ipcomp); \ + n->m_data += sizeof(struct ipcomp); \ + } \ + \ + zs.next_out = mtod(n, u_int8_t *); \ + zs.avail_out = n->m_len; \ +} while (0) for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; @@ -148,113 +190,107 @@ deflate_common(m, md, lenp, mode) n0 = n = NULL; np = &n0; offset = 0; - firsttime = 1; - final = 0; - flush = Z_NO_FLUSH; zerror = 0; p = md; - while (1) { - /* - * first time, we need to setup the buffer before calling - * compression function. - */ - if (firsttime) - firsttime = 0; - else { - zerror = mode ? inflate(&zs, flush) - : deflate(&zs, flush); - } + while (p && p->m_len == 0) { + p = p->m_next; + } + /* input stream and output stream are available */ + while (p && zs.avail_in == 0) { /* get input buffer */ if (p && zs.avail_in == 0) { zs.next_in = mtod(p, u_int8_t *); zs.avail_in = p->m_len; p = p->m_next; - if (!p) { - final = 1; - flush = Z_PARTIAL_FLUSH; + while (p && p->m_len == 0) { + p = p->m_next; } } /* get output buffer */ if (zs.next_out == NULL || zs.avail_out == 0) { - /* keep the reply buffer into our chain */ - if (n) { - n->m_len = zs.total_out - offset; - offset = zs.total_out; - *np = n; - np = &n->m_next; - } + MOREBLOCK(); + } - /* get a fresh reply buffer */ - MGET(n, M_DONTWAIT, MT_DATA); - if (n) { - MCLGET(n, M_DONTWAIT); - } - if (!n) { - error = ENOBUFS; - goto fail; - } - n->m_len = 0; - n->m_len = M_TRAILINGSPACE(n); - n->m_next = NULL; - /* - * if this is the first reply buffer, reserve - * region for ipcomp header. - */ - if (*np == NULL) { - n->m_len -= sizeof(struct ipcomp); - n->m_data += sizeof(struct ipcomp); + zerror = mode ? inflate(&zs, Z_NO_FLUSH) + : deflate(&zs, Z_NO_FLUSH); + + if (zerror == Z_STREAM_END) + ; /*once more.*/ + else if (zerror == Z_OK) { + /* inflate: Z_OK can indicate the end of decode */ + if (mode && !p && zs.avail_out != 0) + goto terminate; + else + ; /*once more.*/ + } else { + if (zs.msg) { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflate(Z_NO_FLUSH): %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg)); + } else { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflate(Z_NO_FLUSH): unknown error (%d)\n", + mode ? "de" : "", mode ? "in" : "de", + zerror)); } + mode ? inflateEnd(&zs) : deflateEnd(&zs); + error = EINVAL; + goto fail; + } + } - zs.next_out = mtod(n, u_int8_t *); - zs.avail_out = n->m_len; + if (zerror == Z_STREAM_END) + goto terminate; + + /* termination */ + while (1) { + /* get output buffer */ + if (zs.next_out == NULL || zs.avail_out == 0) { + MOREBLOCK(); } - if (zerror == Z_OK) { - /* - * to terminate deflate/inflate process, we need to - * call {in,de}flate() with different flushing methods. - * - * deflate() needs at least one Z_PARTIAL_FLUSH, - * then use Z_FINISH until we get to the end. - * (if we use Z_FLUSH without Z_PARTIAL_FLUSH, deflate() - * will assume contiguous single output buffer, and that - * is not what we want) - * inflate() does not care about flushing method, but - * needs output buffer until it gets to the end. - * - * the most outer loop will be terminated with - * Z_STREAM_END. - */ - if (final == 1) { - /* reached end of mbuf chain */ - if (mode == 0) - final = 2; - else - final = 3; - } else if (final == 2) { - /* terminate deflate case */ - flush = Z_FINISH; - } else if (final == 3) { - /* terminate inflate case */ - ; - } - } else if (zerror == Z_STREAM_END) + zerror = mode ? inflate(&zs, Z_FINISH) + : deflate(&zs, Z_FINISH); + + if (zerror == Z_STREAM_END) break; + else if (zerror == Z_OK) + ; /*once more.*/ else { - ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n", - mode ? "de" : "", mode ? "in" : "de", - zs.msg ? zs.msg : "unknown error")); + if (zs.msg) { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflate(Z_FINISH): %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg)); + } else { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflate(Z_FINISH): unknown error (%d)\n", + mode ? "de" : "", mode ? "in" : "de", + zerror)); + } + mode ? inflateEnd(&zs) : deflateEnd(&zs); error = EINVAL; goto fail; } } + +terminate: zerror = mode ? inflateEnd(&zs) : deflateEnd(&zs); if (zerror != Z_OK) { - ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n", - mode ? "de" : "", mode ? "in" : "de", - zs.msg ? zs.msg : "unknown error")); + if (zs.msg) { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflateEnd: %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg)); + } else { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflateEnd: unknown error (%d)\n", + mode ? "de" : "", mode ? "in" : "de", + zerror)); + } error = EINVAL; goto fail; } @@ -264,6 +300,7 @@ deflate_common(m, md, lenp, mode) offset = zs.total_out; *np = n; np = &n->m_next; + n = NULL; } /* switch the mbuf to the new one */ @@ -276,9 +313,12 @@ deflate_common(m, md, lenp, mode) fail: if (m) m_freem(m); + if (n) + m_freem(n); if (n0) m_freem(n0); return error; +#undef MOREBLOCK } static int diff --git a/sys/netinet6/ipcomp_input.c b/sys/netinet6/ipcomp_input.c index da0184c82c7a..0a6e2f5d57f1 100644 --- a/sys/netinet6/ipcomp_input.c +++ b/sys/netinet6/ipcomp_input.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp_input.c,v 1.15 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: ipcomp_input.c,v 1.25 2001/03/01 09:12:09 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -95,9 +95,10 @@ ipcomp4_input(m, va_alist) va_dcl #endif { + struct mbuf *md; struct ip *ip; struct ipcomp *ipcomp; - struct ipcomp_algorithm *algo; + const struct ipcomp_algorithm *algo; u_int16_t cpi; /* host order */ u_int16_t nxt; size_t hlen; @@ -112,42 +113,23 @@ ipcomp4_input(m, va_alist) proto = va_arg(ap, int); va_end(ap); - if (off + sizeof(struct ipcomp) > MHLEN) { - /*XXX the restriction should be relaxed*/ + if (m->m_pkthdr.len < off + sizeof(struct ipcomp)) { ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " - "(header too long)\n")); + "(packet too short)\n")); ipsecstat.in_inval++; goto fail; } - if (m->m_len < off + sizeof(struct ipcomp)) { - m = m_pullup(m, off + sizeof(struct ipcomp)); - if (!m) { - ipseclog((LOG_DEBUG, "IPv4 IPComp input: can't pullup;" - "dropping the packet for simplicity\n")); - ipsecstat.in_nomem++; - goto fail; - } - } else if (m->m_len > off + sizeof(struct ipcomp)) { - /* chop header part from the packet header chain */ - struct mbuf *n; - MGETHDR(n, M_DONTWAIT, MT_HEADER); - if (!n) { - ipsecstat.in_nomem++; - goto fail; - } - M_COPY_PKTHDR(n, m); - MH_ALIGN(n, off + sizeof(struct ipcomp)); - n->m_len = off + sizeof(struct ipcomp); - bcopy(mtod(m, caddr_t), mtod(n, caddr_t), - off + sizeof(struct ipcomp)); - m_adj(m, off + sizeof(struct ipcomp)); - m->m_flags &= ~M_PKTHDR; - n->m_next = m; - m = n; - } + md = m_pulldown(m, off, sizeof(*ipcomp), NULL); + if (!m) { + m = NULL; /*already freed*/ + ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " + "(pulldown failure)\n")); + ipsecstat.in_inval++; + goto fail; + } + ipcomp = mtod(md, struct ipcomp *); ip = mtod(m, struct ip *); - ipcomp = (struct ipcomp *)(((caddr_t)ip) + off); nxt = ipcomp->comp_nxt; #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; @@ -167,10 +149,7 @@ ipcomp4_input(m, va_alist) /* other parameters to look at? */ } } - if (cpi < IPCOMP_MAX && ipcomp_algorithms[cpi].decompress != NULL) - algo = &ipcomp_algorithms[cpi]; - else - algo = NULL; + algo = ipcomp_algorithm_lookup(cpi); if (!algo) { ipseclog((LOG_WARNING, "IPv4 IPComp input: unknown cpi %u\n", cpi)); @@ -180,7 +159,8 @@ ipcomp4_input(m, va_alist) /* chop ipcomp header */ ipcomp = NULL; - m->m_len -= sizeof(struct ipcomp); + md->m_data += sizeof(struct ipcomp); + md->m_len -= sizeof(struct ipcomp); m->m_pkthdr.len -= sizeof(struct ipcomp); #ifdef IPLEN_FLIPPED ip->ip_len -= sizeof(struct ipcomp); @@ -237,13 +217,22 @@ ipcomp4_input(m, va_alist) if (sav) { key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) { + ipsecstat.in_nomem++; + goto fail; + } key_freesav(sav); sav = NULL; } - if (nxt != IPPROTO_DONE) + if (nxt != IPPROTO_DONE) { + if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto fail; + } (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); - else + } else m_freem(m); m = NULL; @@ -268,66 +257,30 @@ ipcomp6_input(mp, offp, proto) struct mbuf *m, *md; int off; struct ip6_hdr *ip6; - struct mbuf *ipcompm; struct ipcomp *ipcomp; - struct ipcomp_algorithm *algo; + const struct ipcomp_algorithm *algo; u_int16_t cpi; /* host order */ u_int16_t nxt; int error; size_t newlen; struct secasvar *sav = NULL; + char *prvnxtp; m = *mp; off = *offp; - IP6_EXTHDR_CHECK(m, off, sizeof(struct ipcomp), IPPROTO_DONE); - - { - int skip; - struct mbuf *n; - struct mbuf *p, *q; - size_t l; - - skip = off; - for (n = m; n && skip > 0; n = n->m_next) { - if (n->m_len <= skip) { - skip -= n->m_len; - continue; - } - break; - } - if (!n) { - ipseclog((LOG_DEBUG, "IPv6 IPComp input: wrong mbuf chain\n")); - ipsecstat.in_inval++; - goto fail; - } - if (n->m_len < skip + sizeof(struct ipcomp)) { - ipseclog((LOG_DEBUG, "IPv6 IPComp input: wrong mbuf chain\n")); - ipsecstat.in_inval++; + md = m_pulldown(m, off, sizeof(*ipcomp), NULL); + if (!m) { + m = NULL; /*already freed*/ + ipseclog((LOG_DEBUG, "IPv6 IPComp input: assumption failed " + "(pulldown failure)\n")); + ipsec6stat.in_inval++; goto fail; } + ipcomp = mtod(md, struct ipcomp *); ip6 = mtod(m, struct ip6_hdr *); - ipcompm = n; - ipcomp = (struct ipcomp *)(mtod(n, caddr_t) + skip); - if (n->m_len > skip + sizeof(struct ipcomp)) { - /* split mbuf to ease the following steps*/ - l = n->m_len - (skip + sizeof(struct ipcomp)); - p = m_copym(n, skip + sizeof(struct ipcomp), l , M_DONTWAIT); - if (!p) { - ipsecstat.in_nomem++; - goto fail; - } - for (q = p; q && q->m_next; q = q->m_next) - ; - q->m_next = n->m_next; - n->m_next = p; - n->m_len -= l; - md = p; - } else - md = n->m_next; - } - nxt = ipcomp->comp_nxt; + cpi = ntohs(ipcomp->comp_cpi); if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { @@ -340,10 +293,7 @@ ipcomp6_input(mp, offp, proto) /* other parameters to look at? */ } } - if (cpi < IPCOMP_MAX && ipcomp_algorithms[cpi].decompress != NULL) - algo = &ipcomp_algorithms[cpi]; - else - algo = NULL; + algo = ipcomp_algorithm_lookup(cpi); if (!algo) { ipseclog((LOG_WARNING, "IPv6 IPComp input: unknown cpi %u; " "dropping the packet for simplicity\n", cpi)); @@ -351,7 +301,13 @@ ipcomp6_input(mp, offp, proto) goto fail; } - newlen = m->m_pkthdr.len - off - sizeof(struct ipcomp); + /* chop ipcomp header */ + ipcomp = NULL; + md->m_data += sizeof(struct ipcomp); + md->m_len -= sizeof(struct ipcomp); + m->m_pkthdr.len -= sizeof(struct ipcomp); + + newlen = m->m_pkthdr.len - off; error = (*algo->decompress)(m, md, &newlen); if (error != 0) { if (error == EINVAL) @@ -362,7 +318,7 @@ ipcomp6_input(mp, offp, proto) goto fail; } ipsec6stat.in_comphist[cpi]++; - m->m_pkthdr.len = off + sizeof(struct ipcomp) + newlen; + m->m_pkthdr.len = off + newlen; /* * returning decompressed packet onto icmp is meaningless. @@ -370,25 +326,21 @@ ipcomp6_input(mp, offp, proto) */ m->m_flags |= M_DECRYPTED; - { - char *prvnxtp; - - /* chop IPComp header */ + /* update next header field */ prvnxtp = ip6_get_prevhdr(m, off); *prvnxtp = nxt; - ipcompm->m_len -= sizeof(struct ipcomp); - ipcompm->m_pkthdr.len -= sizeof(struct ipcomp); - /* adjust payload length */ - ip6 = mtod(m, struct ip6_hdr *); - if (((m->m_pkthdr.len - sizeof(struct ip6_hdr)) & ~0xffff) != 0) - ip6->ip6_plen = 0; /*now a jumbogram*/ - else - ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); - } + /* + * no need to adjust payload length, as all the IPv6 protocols + * look at m->m_pkthdr.len + */ if (sav) { key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) { + ipsec6stat.in_nomem++; + goto fail; + } key_freesav(sav); sav = NULL; } diff --git a/sys/netinet6/ipcomp_output.c b/sys/netinet6/ipcomp_output.c index 265700ee68af..b82840a640a5 100644 --- a/sys/netinet6/ipcomp_output.c +++ b/sys/netinet6/ipcomp_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp_output.c,v 1.15 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: ipcomp_output.c,v 1.23 2001/01/23 08:59:37 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -87,7 +87,7 @@ static int ipcomp_output __P((struct mbuf *, u_char *, struct mbuf *, /* * Modify the packet so that the payload is compressed. * The mbuf (m) must start with IPv4 or IPv6 header. - * On failure, free the given mbuf and return NULL. + * On failure, free the given mbuf and return non-zero. * * on invocation: * m nexthdrp md @@ -112,25 +112,29 @@ ipcomp_output(m, nexthdrp, md, isr, af) { struct mbuf *n; struct mbuf *md0; + struct mbuf *mcopy; struct mbuf *mprev; struct ipcomp *ipcomp; struct secasvar *sav = isr->sav; - struct ipcomp_algorithm *algo; + const struct ipcomp_algorithm *algo; u_int16_t cpi; /* host order */ size_t plen0, plen; /*payload length to be compressed*/ size_t compoff; int afnumber; int error = 0; + struct ipsecstat *stat; switch (af) { #ifdef INET case AF_INET: afnumber = 4; + stat = &ipsecstat; break; #endif #ifdef INET6 case AF_INET6: afnumber = 6; + stat = &ipsec6stat; break; #endif default: @@ -139,9 +143,9 @@ ipcomp_output(m, nexthdrp, md, isr, af) } /* grab parameters */ - if ((ntohl(sav->spi) & ~0xffff) != 0 || sav->alg_enc >= IPCOMP_MAX - || ipcomp_algorithms[sav->alg_enc].compress == NULL) { - ipsecstat.out_inval++; + algo = ipcomp_algorithm_lookup(sav->alg_enc); + if ((ntohl(sav->spi) & ~0xffff) != 0 || !algo) { + stat->out_inval++; m_freem(m); return EINVAL; } @@ -149,7 +153,6 @@ ipcomp_output(m, nexthdrp, md, isr, af) cpi = sav->alg_enc; else cpi = ntohl(sav->spi) & 0xffff; - algo = &ipcomp_algorithms[sav->alg_enc]; /*XXX*/ /* compute original payload length */ plen = 0; @@ -161,11 +164,21 @@ ipcomp_output(m, nexthdrp, md, isr, af) return 0; /* - * keep the original data packet, so that we can backout - * our changes when compression is not necessary. + * retain the original packet for two purposes: + * (1) we need to backout our changes when compression is not necessary. + * (2) byte lifetime computation should use the original packet. + * see RFC2401 page 23. + * compromise two m_copym(). we will be going through every byte of + * the payload during compression process anyways. */ + mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT); + if (mcopy == NULL) { + error = ENOBUFS; + return 0; + } md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT); if (md0 == NULL) { + m_freem(mcopy); error = ENOBUFS; return 0; } @@ -177,26 +190,17 @@ ipcomp_output(m, nexthdrp, md, isr, af) if (mprev == NULL || mprev->m_next != md) { ipseclog((LOG_DEBUG, "ipcomp%d_output: md is not in chain\n", afnumber)); - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_inval++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_inval++; - break; -#endif - } + stat->out_inval++; m_freem(m); m_freem(md0); + m_freem(mcopy); return EINVAL; } mprev->m_next = NULL; if ((md = ipsec_copypkt(md)) == NULL) { m_freem(m); m_freem(md0); + m_freem(mcopy); error = ENOBUFS; goto fail; } @@ -207,33 +211,12 @@ ipcomp_output(m, nexthdrp, md, isr, af) ipseclog((LOG_ERR, "packet compression failure\n")); m = NULL; m_freem(md0); - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_inval++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_inval++; - break; -#endif - } + m_freem(mcopy); + stat->out_inval++; error = EINVAL; goto fail; } - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_comphist[sav->alg_enc]++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_comphist[sav->alg_enc]++; - break; -#endif - } + stat->out_comphist[sav->alg_enc]++; md = mprev->m_next; /* @@ -242,13 +225,17 @@ ipcomp_output(m, nexthdrp, md, isr, af) */ if (plen0 < plen) { m_freem(md); + m_freem(mcopy); mprev->m_next = md0; return 0; } - /* no need to backout change beyond here */ + /* + * no need to backout change beyond here. + */ m_freem(md0); md0 = NULL; + m->m_pkthdr.len -= plen0; m->m_pkthdr.len += plen; @@ -341,47 +328,14 @@ ipcomp_output(m, nexthdrp, md, isr, af) ipseclog((LOG_DEBUG, "NULL mbuf after compression in ipcomp%d_output", afnumber)); - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_inval++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_inval++; - break; -#endif - } - } else { - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_success++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_success++; - break; -#endif - } + stat->out_inval++; } -#if 0 - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_esphist[sav->alg_enc]++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_esphist[sav->alg_enc]++; - break; -#endif - } -#endif - key_sa_recordxfer(sav, m); + stat->out_success++; + + /* compute byte lifetime against original packet */ + key_sa_recordxfer(sav, mcopy); + m_freem(mcopy); + return 0; fail: diff --git a/sys/netinet6/ipsec.c b/sys/netinet6/ipsec.c index b8a24471a94f..3cb835da4e83 100644 --- a/sys/netinet6/ipsec.c +++ b/sys/netinet6/ipsec.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipsec.c,v 1.66 2000/06/15 04:08:54 itojun Exp $ */ +/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -67,9 +67,11 @@ #ifdef INET6 #include <netinet6/ip6_ecn.h> #endif +#include <netinet/tcp.h> +#include <netinet/udp.h> -#ifdef INET6 #include <netinet/ip6.h> +#ifdef INET6 #include <netinet6/ip6_var.h> #endif #include <netinet/in_pcb.h> @@ -97,25 +99,12 @@ #endif #include <netkey/key.h> #include <netkey/keydb.h> -#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #include <machine/in_cksum.h> #include <net/net_osdep.h> -#ifdef HAVE_NRL_INPCB -#define in6pcb inpcb -#define in6p_sp inp_sp -#define in6p_fport inp_fport -#define in6p_lport inp_lport -#define in6p_socket inp_socket -#define sotoin6pcb(so) ((struct inpcb *)(so)->so_pcb) -#endif - #ifdef IPSEC_DEBUG int ipsec_debug = 1; #else @@ -132,11 +121,14 @@ int ip4_ah_trans_deflev = IPSEC_LEVEL_USE; int ip4_ah_net_deflev = IPSEC_LEVEL_USE; struct secpolicy ip4_def_policy; int ip4_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ +int ip4_esp_randpad = -1; +#ifdef SYSCTL_DECL SYSCTL_DECL(_net_inet_ipsec); #ifdef INET6 SYSCTL_DECL(_net_inet6_ipsec6); #endif +#endif /* net.inet.ipsec */ SYSCTL_STRUCT(_net_inet_ipsec, IPSECCTL_STATS, @@ -161,6 +153,8 @@ SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn, CTLFLAG_RW, &ip4_ipsec_ecn, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG, debug, CTLFLAG_RW, &ipsec_debug, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ESP_RANDPAD, + esp_randpad, CTLFLAG_RW, &ip4_esp_randpad, 0, ""); #ifdef INET6 struct ipsecstat ipsec6stat; @@ -170,6 +164,7 @@ int ip6_ah_trans_deflev = IPSEC_LEVEL_USE; int ip6_ah_net_deflev = IPSEC_LEVEL_USE; struct secpolicy ip6_def_policy; int ip6_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ +int ip6_esp_randpad = -1; /* net.inet6.ipsec6 */ SYSCTL_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS, @@ -188,16 +183,22 @@ SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN, ecn, CTLFLAG_RW, &ip6_ipsec_ecn, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, debug, CTLFLAG_RW, &ipsec_debug, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ESP_RANDPAD, + esp_randpad, CTLFLAG_RW, &ip6_esp_randpad, 0, ""); #endif /* INET6 */ static int ipsec_setspidx_mbuf - __P((struct secpolicyindex *, u_int, u_int, struct mbuf *)); -static void ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb)); -static void ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); + __P((struct secpolicyindex *, u_int, u_int, struct mbuf *, int)); +static int ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb)); +#ifdef INET6 +static int ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb)); +#endif +static int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int)); +static void ipsec4_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); +static int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #ifdef INET6 -static void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *)); -static void ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb)); -static void ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); +static void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); +static int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #endif static struct inpcbpolicy *ipsec_newpcbpolicy __P((void)); static void ipsec_delpcbpolicy __P((struct inpcbpolicy *)); @@ -208,14 +209,21 @@ static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp)); static void vshiftl __P((unsigned char *, int, int)); static int ipsec_in_reject __P((struct secpolicy *, struct mbuf *)); static size_t ipsec_hdrsiz __P((struct secpolicy *)); +#ifdef INET static struct mbuf *ipsec4_splithdr __P((struct mbuf *)); +#endif #ifdef INET6 static struct mbuf *ipsec6_splithdr __P((struct mbuf *)); #endif +#ifdef INET static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *)); +#endif #ifdef INET6 static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *)); #endif +static struct mbuf *ipsec_addaux __P((struct mbuf *)); +static struct mbuf *ipsec_findaux __P((struct mbuf *)); +static void ipsec_optaux __P((struct mbuf *, struct mbuf *)); /* * For OUTBOUND packet having a socket. Searching SPD for packet, @@ -247,19 +255,29 @@ ipsec4_getpolicybysock(m, dir, so, error) switch (so->so_proto->pr_domain->dom_family) { case AF_INET: /* set spidx in pcb */ - ipsec4_setspidx_inpcb(m, sotoinpcb(so)); - pcbsp = sotoinpcb(so)->inp_sp; + *error = ipsec4_setspidx_inpcb(m, sotoinpcb(so)); break; #ifdef INET6 case AF_INET6: /* set spidx in pcb */ - ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); - pcbsp = sotoin6pcb(so)->in6p_sp; + *error = ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); break; #endif default: panic("ipsec4_getpolicybysock: unsupported address family\n"); } + if (*error) + return NULL; + switch (so->so_proto->pr_domain->dom_family) { + case AF_INET: + pcbsp = sotoinpcb(so)->inp_sp; + break; +#ifdef INET6 + case AF_INET6: + pcbsp = sotoin6pcb(so)->in6p_sp; + break; +#endif + } /* sanity check */ if (pcbsp == NULL) @@ -404,7 +422,8 @@ ipsec4_getpolicybyaddr(m, dir, flag, error) bzero(&spidx, sizeof(spidx)); /* make a index to look for a policy */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m, + (flag & IP_FORWARDING) ? 0 : 1); if (*error != 0) return NULL; @@ -460,6 +479,11 @@ ipsec6_getpolicybysock(m, dir, so, error) if (m == NULL || so == NULL || error == NULL) panic("ipsec6_getpolicybysock: NULL pointer was passed.\n"); +#ifdef DIAGNOSTIC + if (so->so_proto->pr_domain->dom_family != AF_INET6) + panic("ipsec6_getpolicybysock: socket domain != inet6\n"); +#endif + /* set spidx in pcb */ ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); @@ -615,7 +639,8 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) bzero(&spidx, sizeof(spidx)); /* make a index to look for a policy */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m, + (flag & IP_FORWARDING) ? 0 : 1); if (*error != 0) return NULL; @@ -656,302 +681,286 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) * other: failure, and set errno. */ int -ipsec_setspidx_mbuf(spidx, dir, family, m) +ipsec_setspidx_mbuf(spidx, dir, family, m, needport) struct secpolicyindex *spidx; u_int dir, family; struct mbuf *m; + int needport; { + int error; /* sanity check */ if (spidx == NULL || m == NULL) panic("ipsec_setspidx_mbuf: NULL pointer was passed.\n"); - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: begin\n"); kdebug_mbuf(m)); - - /* initialize */ bzero(spidx, sizeof(*spidx)); + error = ipsec_setspidx(m, spidx, needport); + if (error) + goto bad; spidx->dir = dir; - { - /* sanity check for packet length. */ - struct mbuf *n; - int tlen; - - tlen = 0; - for (n = m; n; n = n->m_next) - tlen += n->m_len; - if (m->m_pkthdr.len != tlen) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "total of m_len(%d) != pkthdr.len(%d), " - "ignored.\n", - tlen, m->m_pkthdr.len)); - goto bad; - } - } + return 0; - switch (family) { - case AF_INET: - { - struct ip *ip; - struct ip ipbuf; - struct sockaddr_in *sin; + bad: + /* XXX initialize */ + bzero(spidx, sizeof(*spidx)); + return EINVAL; +} - /* sanity check 1 for minimum ip header length */ - if (m->m_pkthdr.len < sizeof(struct ip)) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "pkthdr.len(%d) < sizeof(struct ip), " - "ignored.\n", - m->m_pkthdr.len)); - goto bad; - } +static int +ipsec4_setspidx_inpcb(m, pcb) + struct mbuf *m; + struct inpcb *pcb; +{ + struct secpolicyindex *spidx; + int error; - /* - * get IPv4 header packet. usually the mbuf is contiguous - * and we need no copies. - */ - if (m->m_len >= sizeof(*ip)) - ip = mtod(m, struct ip *); - else { - m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); - ip = &ipbuf; - } + /* sanity check */ + if (pcb == NULL) + panic("ipsec4_setspidx_inpcb: no PCB found.\n"); + if (pcb->inp_sp == NULL) + panic("ipsec4_setspidx_inpcb: no inp_sp found.\n"); + if (pcb->inp_sp->sp_out == NULL || pcb->inp_sp->sp_in == NULL) + panic("ipsec4_setspidx_inpcb: no sp_in/out found.\n"); - /* XXX some more checks on IPv4 header. */ + bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); - sin = (struct sockaddr_in *)&spidx->src; - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); - bcopy(&ip->ip_src, &sin->sin_addr, sizeof(sin->sin_addr)); - sin->sin_port = IPSEC_PORT_ANY; + spidx = &pcb->inp_sp->sp_in->spidx; + error = ipsec_setspidx(m, spidx, 1); + if (error) + goto bad; + spidx->dir = IPSEC_DIR_INBOUND; - sin = (struct sockaddr_in *)&spidx->dst; - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); - bcopy(&ip->ip_dst, &sin->sin_addr, sizeof(sin->sin_addr)); - sin->sin_port = IPSEC_PORT_ANY; + spidx = &pcb->inp_sp->sp_out->spidx; + error = ipsec_setspidx(m, spidx, 1); + if (error) + goto bad; + spidx->dir = IPSEC_DIR_OUTBOUND; - spidx->prefs = spidx->prefd = sizeof(struct in_addr) << 3; + return 0; - spidx->ul_proto = ip->ip_p; - break; - } +bad: + bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); + return error; +} #ifdef INET6 - case AF_INET6: - { - struct ip6_hdr *ip6; - struct ip6_hdr ip6buf; - struct sockaddr_in6 *sin6; +static int +ipsec6_setspidx_in6pcb(m, pcb) + struct mbuf *m; + struct in6pcb *pcb; +{ + struct secpolicyindex *spidx; + int error; - /* sanity check 1 for minimum ip header length */ - if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "pkthdr.len(%d) < sizeof(struct ip6_hdr), " - "ignored.\n", - m->m_pkthdr.len)); - goto bad; - } + /* sanity check */ + if (pcb == NULL) + panic("ipsec6_setspidx_in6pcb: no PCB found.\n"); + if (pcb->in6p_sp == NULL) + panic("ipsec6_setspidx_in6pcb: no in6p_sp found.\n"); + if (pcb->in6p_sp->sp_out == NULL || pcb->in6p_sp->sp_in == NULL) + panic("ipsec6_setspidx_in6pcb: no sp_in/out found.\n"); - /* - * get IPv6 header packet. usually the mbuf is contiguous - * and we need no copies. - */ - if (m->m_len >= sizeof(*ip6)) - ip6 = mtod(m, struct ip6_hdr *); - else { - m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); - ip6 = &ip6buf; - } + bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); - /* some more checks on IPv4 header. */ - if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "wrong ip version on packet " - "(expected IPv6), ignored.\n")); - goto bad; - } + spidx = &pcb->in6p_sp->sp_in->spidx; + error = ipsec_setspidx(m, spidx, 1); + if (error) + goto bad; + spidx->dir = IPSEC_DIR_INBOUND; - sin6 = (struct sockaddr_in6 *)&spidx->src; - sin6->sin6_family = AF_INET6; - sin6->sin6_len = sizeof(*sin6); - bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); - sin6->sin6_port = IPSEC_PORT_ANY; - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { - /* fix scope id for comparing SPD */ - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); - } + spidx = &pcb->in6p_sp->sp_out->spidx; + error = ipsec_setspidx(m, spidx, 1); + if (error) + goto bad; + spidx->dir = IPSEC_DIR_OUTBOUND; - sin6 = (struct sockaddr_in6 *)&spidx->dst; - sin6->sin6_family = AF_INET6; - sin6->sin6_len = sizeof(*sin6); - bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); - sin6->sin6_port = IPSEC_PORT_ANY; - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { - /* fix scope id for comparing SPD */ - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); - } + return 0; - spidx->prefs = spidx->prefd = sizeof(struct in6_addr) << 3; +bad: + bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); + return error; +} +#endif - ipsec6_get_ulp(m, spidx); - break; - } -#endif /* INET6 */ - default: - panic("ipsec_secsecidx: no supported family passed.\n"); - } +/* + * configure security policy index (src/dst/proto/sport/dport) + * by looking at the content of mbuf. + * the caller is responsible for error recovery (like clearing up spidx). + */ +static int +ipsec_setspidx(m, spidx, needport) + struct mbuf *m; + struct secpolicyindex *spidx; + int needport; +{ + struct ip *ip = NULL; + struct ip ipbuf; + u_int v; + struct mbuf *n; + int len; + int error; - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: end\n"); - kdebug_secpolicyindex(spidx)); + if (m == NULL) + panic("ipsec_setspidx: m == 0 passed.\n"); - return 0; + /* + * validate m->m_pkthdr.len. we see incorrect length if we + * mistakenly call this function with inconsistent mbuf chain + * (like 4.4BSD tcp/udp processing). XXX should we panic here? + */ + len = 0; + for (n = m; n; n = n->m_next) + len += n->m_len; + if (m->m_pkthdr.len != len) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx: " + "total of m_len(%d) != pkthdr.len(%d), " + "ignored.\n", + len, m->m_pkthdr.len)); + return EINVAL; + } - bad: - /* XXX initialize */ - bzero(spidx, sizeof(*spidx)); - return EINVAL; -} + if (m->m_pkthdr.len < sizeof(struct ip)) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx: " + "pkthdr.len(%d) < sizeof(struct ip), ignored.\n", + m->m_pkthdr.len)); + return EINVAL; + } + if (m->m_len >= sizeof(*ip)) + ip = mtod(m, struct ip *); + else { + m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); + ip = &ipbuf; + } +#ifdef _IP_VHL + v = _IP_VHL_V(ip->ip_vhl); +#else + v = ip->ip_v; +#endif + switch (v) { + case 4: + error = ipsec4_setspidx_ipaddr(m, spidx); + if (error) + return error; + ipsec4_get_ulp(m, spidx, needport); + return 0; #ifdef INET6 -/* - * Get upper layer protocol number and port number if there. - * Assumed all extension headers are in single mbuf. - */ -#include <netinet/tcp.h> -#include <netinet/udp.h> + case 6: + if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx: " + "pkthdr.len(%d) < sizeof(struct ip6_hdr), " + "ignored.\n", m->m_pkthdr.len)); + return EINVAL; + } + error = ipsec6_setspidx_ipaddr(m, spidx); + if (error) + return error; + ipsec6_get_ulp(m, spidx, needport); + return 0; +#endif + default: + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx: " + "unknown IP version %u, ignored.\n", v)); + return EINVAL; + } +} + static void -ipsec6_get_ulp(m, spidx) +ipsec4_get_ulp(m, spidx, needport) struct mbuf *m; struct secpolicyindex *spidx; + int needport; { - int off, nxt; + struct ip ip; + struct ip6_ext ip6e; + u_int8_t nxt; + int off; + struct tcphdr th; + struct udphdr uh; /* sanity check */ if (m == NULL) - panic("ipsec6_get_ulp: NULL pointer was passed.\n"); - - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m)); + panic("ipsec4_get_ulp: NULL pointer was passed.\n"); + if (m->m_pkthdr.len < sizeof(ip)) + panic("ipsec4_get_ulp: too short\n"); /* set default */ spidx->ul_proto = IPSEC_ULPROTO_ANY; - ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY; - ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY; + ((struct sockaddr_in *)&spidx->src)->sin_port = IPSEC_PORT_ANY; + ((struct sockaddr_in *)&spidx->dst)->sin_port = IPSEC_PORT_ANY; - nxt = -1; - off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); - if (off < 0 || m->m_pkthdr.len < off) + m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); + /* ip_input() flips it into host endian XXX need more checking */ + if (ip.ip_off & (IP_MF | IP_OFFMASK)) return; - switch (nxt) { - case IPPROTO_TCP: - spidx->ul_proto = nxt; - if (off + sizeof(struct tcphdr) <= m->m_pkthdr.len) { - struct tcphdr th; + nxt = ip.ip_p; +#ifdef _IP_VHL + off = _IP_VHL_HL(ip->ip_vhl) << 2; +#else + off = ip.ip_hl << 2; +#endif + while (off < m->m_pkthdr.len) { + switch (nxt) { + case IPPROTO_TCP: + spidx->ul_proto = nxt; + if (!needport) + return; + if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) + return; m_copydata(m, off, sizeof(th), (caddr_t)&th); - ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + ((struct sockaddr_in *)&spidx->src)->sin_port = th.th_sport; - ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + ((struct sockaddr_in *)&spidx->dst)->sin_port = th.th_dport; - } - break; - case IPPROTO_UDP: - spidx->ul_proto = nxt; - if (off + sizeof(struct udphdr) <= m->m_pkthdr.len) { - struct udphdr uh; + return; + case IPPROTO_UDP: + spidx->ul_proto = nxt; + if (!needport) + return; + if (off + sizeof(struct udphdr) > m->m_pkthdr.len) + return; m_copydata(m, off, sizeof(uh), (caddr_t)&uh); - ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + ((struct sockaddr_in *)&spidx->src)->sin_port = uh.uh_sport; - ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + ((struct sockaddr_in *)&spidx->dst)->sin_port = uh.uh_dport; + return; + case IPPROTO_AH: + if (m->m_pkthdr.len > off + sizeof(ip6e)) + return; + m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); + off += (ip6e.ip6e_len + 2) << 2; + nxt = ip6e.ip6e_nxt; + break; + case IPPROTO_ICMP: + default: + /* XXX intermediate headers??? */ + spidx->ul_proto = nxt; + return; } - break; - case IPPROTO_ICMPV6: - spidx->ul_proto = nxt; - break; - default: - break; } } -#endif - -static void -ipsec4_setspidx_inpcb(m, pcb) - struct mbuf *m; - struct inpcb *pcb; -{ - struct secpolicyindex *spidx; - struct sockaddr_in *sin1, *sin2; - - /* sanity check */ - if (pcb == NULL) - panic("ipsec4_setspidx_inpcb: no PCB found.\n"); - if (pcb->inp_sp == NULL) - panic("ipsec4_setspidx_inpcb: no inp_sp found.\n"); - if (pcb->inp_sp->sp_out ==NULL || pcb->inp_sp->sp_in == NULL) - panic("ipsec4_setspidx_inpcb: no sp_in/out found.\n"); - - bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); - bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); - - spidx = &pcb->inp_sp->sp_in->spidx; - spidx->dir = IPSEC_DIR_INBOUND; - sin1 = (struct sockaddr_in *)&spidx->src; - sin2 = (struct sockaddr_in *)&spidx->dst; - sin1->sin_len = sin2->sin_len = sizeof(struct sockaddr_in); - sin1->sin_family = sin2->sin_family = AF_INET; - spidx->prefs = sizeof(struct in_addr) << 3; - spidx->prefd = sizeof(struct in_addr) << 3; - spidx->ul_proto = pcb->inp_socket->so_proto->pr_protocol; - sin1->sin_port = pcb->inp_fport; - sin2->sin_port = pcb->inp_lport; - ipsec4_setspidx_ipaddr(m, spidx); - - spidx = &pcb->inp_sp->sp_out->spidx; - spidx->dir = IPSEC_DIR_OUTBOUND; - sin1 = (struct sockaddr_in *)&spidx->src; - sin2 = (struct sockaddr_in *)&spidx->dst; - sin1->sin_len = sin2->sin_len = sizeof(struct sockaddr_in); - sin1->sin_family = sin2->sin_family = AF_INET; - spidx->prefs = sizeof(struct in_addr) << 3; - spidx->prefd = sizeof(struct in_addr) << 3; - spidx->ul_proto = pcb->inp_socket->so_proto->pr_protocol; - sin1->sin_port = pcb->inp_lport; - sin2->sin_port = pcb->inp_fport; - ipsec4_setspidx_ipaddr(m, spidx); - - return; -} -static void +/* assumes that m is sane */ +static int ipsec4_setspidx_ipaddr(m, spidx) struct mbuf *m; struct secpolicyindex *spidx; { struct ip *ip = NULL; struct ip ipbuf; - - /* sanity check 1 for minimum ip header length */ - if (m == NULL) - panic("ipsec4_setspidx_ipaddr: m == 0 passed.\n"); - - if (m->m_pkthdr.len < sizeof(struct ip)) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec4_setspidx_ipaddr: " - "pkthdr.len(%d) < sizeof(struct ip), " - "ignored.\n", - m->m_pkthdr.len)); - return; - } + struct sockaddr_in *sin; if (m->m_len >= sizeof(*ip)) ip = mtod(m, struct ip *); @@ -960,64 +969,81 @@ ipsec4_setspidx_ipaddr(m, spidx) ip = &ipbuf; } - bcopy(&ip->ip_src, &((struct sockaddr_in *)&spidx->src)->sin_addr, - sizeof(ip->ip_src)); - bcopy(&ip->ip_dst, &((struct sockaddr_in *)&spidx->dst)->sin_addr, - sizeof(ip->ip_dst)); + sin = (struct sockaddr_in *)&spidx->src; + bzero(sin, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + bcopy(&ip->ip_src, &sin->sin_addr, sizeof(ip->ip_src)); + spidx->prefs = sizeof(struct in_addr) << 3; - return; + sin = (struct sockaddr_in *)&spidx->dst; + bzero(sin, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + bcopy(&ip->ip_dst, &sin->sin_addr, sizeof(ip->ip_dst)); + spidx->prefd = sizeof(struct in_addr) << 3; + return 0; } #ifdef INET6 static void -ipsec6_setspidx_in6pcb(m, pcb) +ipsec6_get_ulp(m, spidx, needport) struct mbuf *m; - struct in6pcb *pcb; -{ struct secpolicyindex *spidx; - struct sockaddr_in6 *sin1, *sin2; + int needport; +{ + int off, nxt; + struct tcphdr th; + struct udphdr uh; /* sanity check */ - if (pcb == NULL) - panic("ipsec6_setspidx_in6pcb: no PCB found.\n"); - if (pcb->in6p_sp == NULL) - panic("ipsec6_setspidx_in6pcb: no in6p_sp found.\n"); - if (pcb->in6p_sp->sp_out ==NULL || pcb->in6p_sp->sp_in == NULL) - panic("ipsec6_setspidx_in6pcb: no sp_in/out found.\n"); + if (m == NULL) + panic("ipsec6_get_ulp: NULL pointer was passed.\n"); - bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); - bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m)); - spidx = &pcb->in6p_sp->sp_in->spidx; - spidx->dir = IPSEC_DIR_INBOUND; - sin1 = (struct sockaddr_in6 *)&spidx->src; - sin2 = (struct sockaddr_in6 *)&spidx->dst; - sin1->sin6_len = sin2->sin6_len = sizeof(struct sockaddr_in6); - sin1->sin6_family = sin2->sin6_family = AF_INET6; - spidx->prefs = sizeof(struct in6_addr) << 3; - spidx->prefd = sizeof(struct in6_addr) << 3; - spidx->ul_proto = pcb->in6p_socket->so_proto->pr_protocol; - sin1->sin6_port = pcb->in6p_fport; - sin2->sin6_port = pcb->in6p_lport; - ipsec6_setspidx_ipaddr(m, spidx); + /* set default */ + spidx->ul_proto = IPSEC_ULPROTO_ANY; + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY; - spidx = &pcb->in6p_sp->sp_out->spidx; - spidx->dir = IPSEC_DIR_OUTBOUND; - sin1 = (struct sockaddr_in6 *)&spidx->src; - sin2 = (struct sockaddr_in6 *)&spidx->dst; - sin1->sin6_len = sin2->sin6_len = sizeof(struct sockaddr_in6); - sin1->sin6_family = sin2->sin6_family = AF_INET6; - spidx->prefs = sizeof(struct in6_addr) << 3; - spidx->prefd = sizeof(struct in6_addr) << 3; - spidx->ul_proto = pcb->in6p_socket->so_proto->pr_protocol; - sin1->sin6_port = pcb->in6p_lport; - sin2->sin6_port = pcb->in6p_fport; - ipsec6_setspidx_ipaddr(m, spidx); + nxt = -1; + off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); + if (off < 0 || m->m_pkthdr.len < off) + return; - return; + switch (nxt) { + case IPPROTO_TCP: + spidx->ul_proto = nxt; + if (!needport) + break; + if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) + break; + m_copydata(m, off, sizeof(th), (caddr_t)&th); + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport; + break; + case IPPROTO_UDP: + spidx->ul_proto = nxt; + if (!needport) + break; + if (off + sizeof(struct udphdr) > m->m_pkthdr.len) + break; + m_copydata(m, off, sizeof(uh), (caddr_t)&uh); + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport; + break; + case IPPROTO_ICMPV6: + default: + /* XXX intermediate headers??? */ + spidx->ul_proto = nxt; + break; + } } -static void +/* assumes that m is sane */ +static int ipsec6_setspidx_ipaddr(m, spidx) struct mbuf *m; struct secpolicyindex *spidx; @@ -1026,19 +1052,6 @@ ipsec6_setspidx_ipaddr(m, spidx) struct ip6_hdr ip6buf; struct sockaddr_in6 *sin6; - /* sanity check 1 for minimum ip header length */ - if (m == NULL) - panic("ipsec6_setspidx_in6pcb: m == 0 passed.\n"); - - if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec6_setspidx_ipaddr: " - "pkthdr.len(%d) < sizeof(struct ip6_hdr), " - "ignored.\n", - m->m_pkthdr.len)); - return; - } - if (m->m_len >= sizeof(*ip6)) ip6 = mtod(m, struct ip6_hdr *); else { @@ -1046,29 +1059,29 @@ ipsec6_setspidx_ipaddr(m, spidx) ip6 = &ip6buf; } - if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "wrong ip version on packet " - "(expected IPv6), ignored.\n")); - return; - } - sin6 = (struct sockaddr_in6 *)&spidx->src; + bzero(sin6, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src)); if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { sin6->sin6_addr.s6_addr16[1] = 0; sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); } + spidx->prefs = sizeof(struct in6_addr) << 3; sin6 = (struct sockaddr_in6 *)&spidx->dst; + bzero(sin6, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst)); if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { sin6->sin6_addr.s6_addr16[1] = 0; sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); } + spidx->prefd = sizeof(struct in6_addr) << 3; - return; + return 0; } #endif @@ -1628,6 +1641,8 @@ ipsec_in_reject(sp, m) need_conf = 0; need_icv = 0; + /* XXX should compare policy against ipsec header history */ + for (isr = sp->req; isr != NULL; isr = isr->next) { /* get current level */ @@ -1653,7 +1668,8 @@ ipsec_in_reject(sp, m) case IPPROTO_IPCOMP: /* * we don't really care, as IPcomp document says that - * we shouldn't compress small packets + * we shouldn't compress small packets, IPComp policy + * should always be treated as being in "use" level. */ break; } @@ -1717,12 +1733,10 @@ ipsec4_in_reject(m, inp) { if (inp == NULL) return ipsec4_in_reject_so(m, NULL); - else { - if (inp->inp_socket) - return ipsec4_in_reject_so(m, inp->inp_socket); - else - panic("ipsec4_in_reject: invalid inpcb/socket"); - } + if (inp->inp_socket) + return ipsec4_in_reject_so(m, inp->inp_socket); + else + panic("ipsec4_in_reject: invalid inpcb/socket"); } #ifdef INET6 @@ -1771,12 +1785,10 @@ ipsec6_in_reject(m, in6p) { if (in6p == NULL) return ipsec6_in_reject_so(m, NULL); - else { - if (in6p->in6p_socket) - return ipsec6_in_reject_so(m, in6p->in6p_socket); - else - panic("ipsec6_in_reject: invalid in6p/socket"); - } + if (in6p->in6p_socket) + return ipsec6_in_reject_so(m, in6p->in6p_socket); + else + panic("ipsec6_in_reject: invalid in6p/socket"); } #endif @@ -2054,6 +2066,7 @@ ipsec4_encapsulate(m, sav) &ip->ip_src, sizeof(ip->ip_src)); bcopy(&((struct sockaddr_in *)&sav->sah->saidx.dst)->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)); + ip->ip_ttl = IPDEFTTL; /* XXX Should ip_src be updated later ? */ @@ -2133,6 +2146,7 @@ ipsec6_encapsulate(m, sav) &ip6->ip6_src, sizeof(ip6->ip6_src)); bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.dst)->sin6_addr, &ip6->ip6_dst, sizeof(ip6->ip6_dst)); + ip6->ip6_hlim = IPV6_DEFHLIM; /* XXX Should ip6_src be updated later ? */ @@ -2345,11 +2359,11 @@ ipsec4_logpacketstr(ip, spi) snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi)); while (p && *p) p++; - snprintf(p, sizeof(buf) - (p - buf), "src=%d.%d.%d.%d", + snprintf(p, sizeof(buf) - (p - buf), "src=%u.%u.%u.%u", s[0], s[1], s[2], s[3]); while (p && *p) p++; - snprintf(p, sizeof(buf) - (p - buf), " dst=%d.%d.%d.%d", + snprintf(p, sizeof(buf) - (p - buf), " dst=%u.%u.%u.%u", d[0], d[1], d[2], d[3]); while (p && *p) p++; @@ -2454,6 +2468,7 @@ ipsec_dumpmbuf(m) printf("---\n"); } +#ifdef INET /* * IPsec output logic for IPv4. */ @@ -2500,6 +2515,8 @@ ipsec4_output(state, sp, flags) /* make SA index for search proper SA */ ip = mtod(state->m, struct ip *); bcopy(&isr->saidx, &saidx, sizeof(saidx)); + saidx.mode = isr->saidx.mode; + saidx.reqid = isr->saidx.reqid; sin = (struct sockaddr_in *)&saidx.src; if (sin->sin_len == 0) { sin->sin_len = sizeof(*sin); @@ -2595,7 +2612,7 @@ ipsec4_output(state, sp, flags) && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 || dst4->sin_addr.s_addr != ip->ip_dst.s_addr)) { RTFREE(state->ro->ro_rt); - bzero((caddr_t)state->ro, sizeof (*state->ro)); + state->ro->ro_rt = NULL; } if (state->ro->ro_rt == 0) { dst4->sin_family = AF_INET; @@ -2672,6 +2689,7 @@ bad: state->m = NULL; return error; } +#endif #ifdef INET6 /* @@ -2720,6 +2738,8 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) /* make SA index for search proper SA */ ip6 = mtod(state->m, struct ip6_hdr *); bcopy(&isr->saidx, &saidx, sizeof(saidx)); + saidx.mode = isr->saidx.mode; + saidx.reqid = isr->saidx.reqid; sin6 = (struct sockaddr_in6 *)&saidx.src; if (sin6->sin6_len == 0) { sin6->sin6_len = sizeof(*sin6); @@ -2757,6 +2777,18 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) */ ipsec6stat.out_nosa++; error = ENOENT; + + /* + * Notify the fact that the packet is discarded + * to ourselves. I believe this is better than + * just silently discarding. (jinmei@kame.net) + * XXX: should we restrict the error to TCP packets? + * XXX: should we directly notify sockets via + * pfctlinputs? + */ + icmp6_error(state->m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADMIN, 0); + state->m = NULL; /* icmp6_error freed the mbuf */ goto bad; } @@ -2951,7 +2983,7 @@ ipsec6_output_tunnel(state, sp, flags) && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { RTFREE(state->ro->ro_rt); - bzero((caddr_t)state->ro, sizeof (*state->ro)); + state->ro->ro_rt = NULL; } if (state->ro->ro_rt == 0) { bzero(dst6, sizeof(*dst6)); @@ -3030,6 +3062,7 @@ bad: } #endif /*INET6*/ +#ifdef INET /* * Chop IP header and option off from the payload. */ @@ -3071,6 +3104,7 @@ ipsec4_splithdr(m) } return m; } +#endif #ifdef INET6 static struct mbuf * @@ -3111,38 +3145,89 @@ ipsec6_splithdr(m) /* validate inbound IPsec tunnel packet. */ int -ipsec4_tunnel_validate(ip, nxt0, sav) - struct ip *ip; +ipsec4_tunnel_validate(m, off, nxt0, sav) + struct mbuf *m; /* no pullup permitted, m->m_len >= ip */ + int off; u_int nxt0; struct secasvar *sav; { u_int8_t nxt = nxt0 & 0xff; struct sockaddr_in *sin; + struct sockaddr_in osrc, odst, isrc, idst; int hlen; + struct secpolicy *sp; + struct ip *oip; +#ifdef DIAGNOSTIC + if (m->m_len < sizeof(struct ip)) + panic("too short mbuf on ipsec4_tunnel_validate"); +#endif if (nxt != IPPROTO_IPV4) return 0; + if (m->m_pkthdr.len < off + sizeof(struct ip)) + return 0; + /* do not decapsulate if the SA is for transport mode only */ + if (sav->sah->saidx.mode == IPSEC_MODE_TRANSPORT) + return 0; + + oip = mtod(m, struct ip *); #ifdef _IP_VHL - hlen = _IP_VHL_HL(ip->ip_vhl) << 2; + hlen = _IP_VHL_HL(oip->ip_vhl) << 2; #else - hlen = ip->ip_hl << 2; + hlen = oip->ip_hl << 2; #endif if (hlen != sizeof(struct ip)) return 0; - switch (((struct sockaddr *)&sav->sah->saidx.dst)->sa_family) { - case AF_INET: - sin = (struct sockaddr_in *)&sav->sah->saidx.dst; - if (bcmp(&ip->ip_dst, &sin->sin_addr, sizeof(ip->ip_dst)) != 0) - return 0; - break; -#ifdef INET6 - case AF_INET6: - /* should be supported, but at this moment we don't. */ - /*FALLTHROUGH*/ -#endif - default: + + /* AF_INET6 should be supported, but at this moment we don't. */ + sin = (struct sockaddr_in *)&sav->sah->saidx.dst; + if (sin->sin_family != AF_INET) return 0; - } + if (bcmp(&oip->ip_dst, &sin->sin_addr, sizeof(oip->ip_dst)) != 0) + return 0; + + /* XXX slow */ + bzero(&osrc, sizeof(osrc)); + bzero(&odst, sizeof(odst)); + bzero(&isrc, sizeof(isrc)); + bzero(&idst, sizeof(idst)); + osrc.sin_family = odst.sin_family = isrc.sin_family = idst.sin_family = + AF_INET; + osrc.sin_len = odst.sin_len = isrc.sin_len = idst.sin_len = + sizeof(struct sockaddr_in); + osrc.sin_addr = oip->ip_src; + odst.sin_addr = oip->ip_dst; + m_copydata(m, off + offsetof(struct ip, ip_src), sizeof(isrc.sin_addr), + (caddr_t)&isrc.sin_addr); + m_copydata(m, off + offsetof(struct ip, ip_dst), sizeof(idst.sin_addr), + (caddr_t)&idst.sin_addr); + + /* + * RFC2401 5.2.1 (b): (assume that we are using tunnel mode) + * - if the inner destination is multicast address, there can be + * multiple permissible inner source address. implementation + * may want to skip verification of inner source address against + * SPD selector. + * - if the inner protocol is ICMP, the packet may be an error report + * from routers on the other side of the VPN cloud (R in the + * following diagram). in this case, we cannot verify inner source + * address against SPD selector. + * me -- gw === gw -- R -- you + * + * we consider the first bullet to be users responsibility on SPD entry + * configuration (if you need to encrypt multicast traffic, set + * the source range of SPD selector to 0.0.0.0/0, or have explicit + * address ranges for possible senders). + * the second bullet is not taken care of (yet). + * + * therefore, we do not do anything special about inner source. + */ + + sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst, + (struct sockaddr *)&isrc, (struct sockaddr *)&idst); + if (!sp) + return 0; + key_freesp(sp); return 1; } @@ -3150,28 +3235,64 @@ ipsec4_tunnel_validate(ip, nxt0, sav) #ifdef INET6 /* validate inbound IPsec tunnel packet. */ int -ipsec6_tunnel_validate(ip6, nxt0, sav) - struct ip6_hdr *ip6; +ipsec6_tunnel_validate(m, off, nxt0, sav) + struct mbuf *m; /* no pullup permitted, m->m_len >= ip */ + int off; u_int nxt0; struct secasvar *sav; { u_int8_t nxt = nxt0 & 0xff; struct sockaddr_in6 *sin6; + struct sockaddr_in6 osrc, odst, isrc, idst; + struct secpolicy *sp; + struct ip6_hdr *oip6; +#ifdef DIAGNOSTIC + if (m->m_len < sizeof(struct ip6_hdr)) + panic("too short mbuf on ipsec6_tunnel_validate"); +#endif if (nxt != IPPROTO_IPV6) return 0; - switch (((struct sockaddr *)&sav->sah->saidx.dst)->sa_family) { - case AF_INET6: - sin6 = ((struct sockaddr_in6 *)&sav->sah->saidx.dst); - if (!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6->sin6_addr)) - return 0; - break; - case AF_INET: - /* should be supported, but at this moment we don't. */ - /*FALLTHROUGH*/ - default: + if (m->m_pkthdr.len < off + sizeof(struct ip6_hdr)) + return 0; + /* do not decapsulate if the SA is for transport mode only */ + if (sav->sah->saidx.mode == IPSEC_MODE_TRANSPORT) + return 0; + + oip6 = mtod(m, struct ip6_hdr *); + /* AF_INET should be supported, but at this moment we don't. */ + sin6 = (struct sockaddr_in6 *)&sav->sah->saidx.dst; + if (sin6->sin6_family != AF_INET6) + return 0; + if (!IN6_ARE_ADDR_EQUAL(&oip6->ip6_dst, &sin6->sin6_addr)) return 0; - } + + /* XXX slow */ + bzero(&osrc, sizeof(osrc)); + bzero(&odst, sizeof(odst)); + bzero(&isrc, sizeof(isrc)); + bzero(&idst, sizeof(idst)); + osrc.sin6_family = odst.sin6_family = isrc.sin6_family = + idst.sin6_family = AF_INET6; + osrc.sin6_len = odst.sin6_len = isrc.sin6_len = idst.sin6_len = + sizeof(struct sockaddr_in6); + osrc.sin6_addr = oip6->ip6_src; + odst.sin6_addr = oip6->ip6_dst; + m_copydata(m, off + offsetof(struct ip6_hdr, ip6_src), + sizeof(isrc.sin6_addr), (caddr_t)&isrc.sin6_addr); + m_copydata(m, off + offsetof(struct ip6_hdr, ip6_dst), + sizeof(idst.sin6_addr), (caddr_t)&idst.sin6_addr); + + /* + * regarding to inner source address validation, see a long comment + * in ipsec4_tunnel_validate. + */ + + sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst, + (struct sockaddr *)&isrc, (struct sockaddr *)&idst); + if (!sp) + return 0; + key_freesp(sp); return 1; } @@ -3235,7 +3356,7 @@ ipsec_copypkt(m) */ remain = n->m_len; copied = 0; - while(1) { + while (1) { int len; struct mbuf *mn; @@ -3265,6 +3386,7 @@ ipsec_copypkt(m) MGETHDR(mn, M_DONTWAIT, MT_HEADER); if (mn == NULL) goto fail; + mn->m_pkthdr.rcvif = NULL; mm->m_next = mn; mm = mn; } @@ -3288,27 +3410,78 @@ ipsec_copypkt(m) return(NULL); } +static struct mbuf * +ipsec_addaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); + if (!n) + n = m_aux_add(m, AF_INET, IPPROTO_ESP); + if (!n) + return n; /* ENOBUFS */ + n->m_len = sizeof(struct socket *); + bzero(mtod(n, void *), n->m_len); + return n; +} + +static struct mbuf * +ipsec_findaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); +#ifdef DIAGNOSTIC + if (n && n->m_len < sizeof(struct socket *)) + panic("invalid ipsec m_aux"); +#endif + return n; +} + void +ipsec_delaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); + if (n) + m_aux_delete(m, n); +} + +/* if the aux buffer is unnecessary, nuke it. */ +static void +ipsec_optaux(m, n) + struct mbuf *m; + struct mbuf *n; +{ + + if (!n) + return; + if (n->m_len == sizeof(struct socket *) && !*mtod(n, struct socket **)) + ipsec_delaux(m); +} + +int ipsec_setsocket(m, so) struct mbuf *m; struct socket *so; { struct mbuf *n; - n = m_aux_find(m, AF_INET, IPPROTO_ESP); - if (so && !n) - n = m_aux_add(m, AF_INET, IPPROTO_ESP); - if (n) { - if (so) { - *mtod(n, struct socket **) = so; - /* - * XXX think again about it when we put decryption - * histrory into aux mbuf - */ - n->m_len = sizeof(struct socket *); - } else - m_aux_delete(m, n); - } + /* if so == NULL, don't insist on getting the aux mbuf */ + if (so) { + n = ipsec_addaux(m); + if (!n) + return ENOBUFS; + } else + n = ipsec_findaux(m); + if (n && n->m_len >= sizeof(struct socket *)) + *mtod(n, struct socket **) = so; + ipsec_optaux(m, n); + return 0; } struct socket * @@ -3317,9 +3490,66 @@ ipsec_getsocket(m) { struct mbuf *n; - n = m_aux_find(m, AF_INET, IPPROTO_ESP); + n = ipsec_findaux(m); if (n && n->m_len >= sizeof(struct socket *)) return *mtod(n, struct socket **); else return NULL; } + +int +ipsec_addhist(m, proto, spi) + struct mbuf *m; + int proto; + u_int32_t spi; +{ + struct mbuf *n; + struct ipsec_history *p; + + n = ipsec_addaux(m); + if (!n) + return ENOBUFS; + if (M_TRAILINGSPACE(n) < sizeof(*p)) + return ENOSPC; /*XXX*/ + p = (struct ipsec_history *)(mtod(n, caddr_t) + n->m_len); + n->m_len += sizeof(*p); + bzero(p, sizeof(*p)); + p->ih_proto = proto; + p->ih_spi = spi; + return 0; +} + +struct ipsec_history * +ipsec_gethist(m, lenp) + struct mbuf *m; + int *lenp; +{ + struct mbuf *n; + int l; + + n = ipsec_findaux(m); + if (!n) + return NULL; + l = n->m_len; + if (sizeof(struct socket *) > l) + return NULL; + if ((l - sizeof(struct socket *)) % sizeof(struct ipsec_history)) + return NULL; + /* XXX does it make more sense to divide by sizeof(ipsec_history)? */ + if (lenp) + *lenp = l - sizeof(struct socket *); + return (struct ipsec_history *) + (mtod(n, caddr_t) + sizeof(struct socket *)); +} + +void +ipsec_clearhist(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = ipsec_findaux(m); + if ((n) && n->m_len > sizeof(struct socket *)) + n->m_len = sizeof(struct socket *); + ipsec_optaux(m, n); +} diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h index 80be10c0312c..33cef87a1736 100644 --- a/sys/netinet6/ipsec.h +++ b/sys/netinet6/ipsec.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipsec.h,v 1.33 2000/06/19 14:31:49 sakane Exp $ */ +/* $KAME: ipsec.h,v 1.44 2001/03/23 08:08:47 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,6 +37,11 @@ #ifndef _NETINET6_IPSEC_H_ #define _NETINET6_IPSEC_H_ +#if defined(_KERNEL) && !defined(_LKM) && !defined(KLD_MODULE) +#include "opt_inet.h" +#include "opt_ipsec.h" +#endif + #include <net/pfkeyv2.h> #include <netkey/keydb.h> @@ -79,6 +84,18 @@ struct secpolicy { struct ipsecrequest *req; /* pointer to the ipsec request tree, */ /* if policy == IPSEC else this value == NULL.*/ + + /* + * lifetime handler. + * the policy can be used without limitiation if both lifetime and + * validtime are zero. + * "lifetime" is passed by sadb_lifetime.sadb_lifetime_addtime. + * "validtime" is passed by sadb_lifetime.sadb_lifetime_usetime. + */ + long created; /* time created the policy */ + long lastused; /* updated every when kernel sends a packet */ + long lifetime; /* duration of the lifetime of this policy */ + long validtime; /* duration this policy is valid without use */ }; /* Request for IPsec */ @@ -107,7 +124,7 @@ struct secspacq { struct secpolicyindex spidx; - u_int32_t tick; /* for lifetime */ + long created; /* for lifetime */ int count; /* for lifetime */ /* XXX: here is mbuf place holder to be sent ? */ }; @@ -137,9 +154,9 @@ struct secspacq { /* Policy level */ /* - * IPSEC, ENTRUST and BYPASS are allowd for setsockopt() in PCB, - * DISCARD, IPSEC and NONE are allowd for setkey() in SPD. - * DISCARD and NONE are allowd for system default. + * IPSEC, ENTRUST and BYPASS are allowed for setsockopt() in PCB, + * DISCARD, IPSEC and NONE are allowed for setkey() in SPD. + * DISCARD and NONE are allowed for system default. */ #define IPSEC_POLICY_DISCARD 0 /* discarding packet */ #define IPSEC_POLICY_NONE 1 /* through IPsec engine */ @@ -216,7 +233,8 @@ struct ipsecstat { #define IPSECCTL_DFBIT 10 #define IPSECCTL_ECN 11 #define IPSECCTL_DEBUG 12 -#define IPSECCTL_MAXID 13 +#define IPSECCTL_ESP_RANDPAD 13 +#define IPSECCTL_MAXID 14 #define IPSECCTL_NAMES { \ { 0, 0 }, \ @@ -232,6 +250,7 @@ struct ipsecstat { { "dfbit", CTLTYPE_INT }, \ { "ecn", CTLTYPE_INT }, \ { "debug", CTLTYPE_INT }, \ + { "esp_randpad", CTLTYPE_INT }, \ } #define IPSEC6CTL_NAMES { \ @@ -248,6 +267,7 @@ struct ipsecstat { { 0, 0 }, \ { "ecn", CTLTYPE_INT }, \ { "debug", CTLTYPE_INT }, \ + { "esp_randpad", CTLTYPE_INT }, \ } #ifdef _KERNEL @@ -257,6 +277,11 @@ struct ipsec_output_state { struct sockaddr *dst; }; +struct ipsec_history { + int ih_proto; + u_int32_t ih_spi; +}; + extern int ipsec_debug; extern struct ipsecstat ipsecstat; @@ -269,6 +294,7 @@ extern int ip4_ah_cleartos; extern int ip4_ah_offsetmask; extern int ip4_ipsec_dfbit; extern int ip4_ipsec_ecn; +extern int ip4_esp_randpad; #define ipseclog(x) do { if (ipsec_debug) log x; } while (0) @@ -307,10 +333,15 @@ extern void ipsec_dumpmbuf __P((struct mbuf *)); extern int ipsec4_output __P((struct ipsec_output_state *, struct secpolicy *, int)); -extern int ipsec4_tunnel_validate __P((struct ip *, u_int, struct secasvar *)); +extern int ipsec4_tunnel_validate __P((struct mbuf *, int, u_int, + struct secasvar *)); extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); -extern void ipsec_setsocket __P((struct mbuf *, struct socket *)); +extern void ipsec_delaux __P((struct mbuf *)); +extern int ipsec_setsocket __P((struct mbuf *, struct socket *)); extern struct socket *ipsec_getsocket __P((struct mbuf *)); +extern int ipsec_addhist __P((struct mbuf *, int, u_int32_t)); +extern struct ipsec_history *ipsec_gethist __P((struct mbuf *, int *)); +extern void ipsec_clearhist __P((struct mbuf *)); #endif /*_KERNEL*/ #ifndef _KERNEL @@ -318,7 +349,7 @@ extern caddr_t ipsec_set_policy __P((char *, int)); extern int ipsec_get_policylen __P((caddr_t)); extern char *ipsec_dump_policy __P((caddr_t, char *)); -extern char *ipsec_strerror __P((void)); +extern const char *ipsec_strerror __P((void)); #endif /*!_KERNEL*/ #endif /*_NETINET6_IPSEC_H_*/ diff --git a/sys/netinet6/ipsec6.h b/sys/netinet6/ipsec6.h index 383c12581995..e9b8a2c9d3b7 100644 --- a/sys/netinet6/ipsec6.h +++ b/sys/netinet6/ipsec6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipsec.h,v 1.33 2000/06/19 14:31:49 sakane Exp $ */ +/* $KAME: ipsec.h,v 1.44 2001/03/23 08:08:47 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -48,6 +48,7 @@ extern int ip6_esp_net_deflev; extern int ip6_ah_trans_deflev; extern int ip6_ah_net_deflev; extern int ip6_ipsec_ecn; +extern int ip6_esp_randpad; extern struct secpolicy *ipsec6_getpolicybysock __P((struct mbuf *, u_int, struct socket *, int *)); @@ -64,6 +65,8 @@ extern int ipsec6_get_policy __P((struct inpcb *inp, caddr_t request, size_t len, struct mbuf **mp)); extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *)); +struct tcp6cb; + extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); struct ip6_hdr; @@ -73,7 +76,7 @@ extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *, struct mbuf *, struct secpolicy *, int, int *)); extern int ipsec6_output_tunnel __P((struct ipsec_output_state *, struct secpolicy *, int)); -extern int ipsec6_tunnel_validate __P((struct ip6_hdr *, u_int, +extern int ipsec6_tunnel_validate __P((struct mbuf *, int, u_int, struct secasvar *)); #endif /*_KERNEL*/ diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 473985648458..27b8480ec9b8 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: mld6.c,v 1.19 2000/05/05 11:01:03 sumikawa Exp $ */ +/* $KAME: mld6.c,v 1.27 2001/04/04 05:17:30 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -129,9 +129,8 @@ mld6_init() hbh_buf[5] = IP6OPT_RTALERT_LEN - 2; bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t)); + init_ip6pktopts(&ip6_opts); ip6_opts.ip6po_hbh = hbh; - /* We will specify the hoplimit by a multicast option. */ - ip6_opts.ip6po_hlim = -1; } void @@ -192,31 +191,34 @@ mld6_input(m, off) struct ifmultiaddr *ifma; int timer; /* timer value in the MLD query header */ +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),); + mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh)); + if (mldh == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + /* source address validation */ + ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */ if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { log(LOG_ERR, - "mld6_input: src %s is not link-local\n", - ip6_sprintf(&ip6->ip6_src)); + "mld6_input: src %s is not link-local (grp=%s)\n", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&mldh->mld6_addr)); /* * spec (RFC2710) does not explicitly * specify to discard the packet from a non link-local * source address. But we believe it's expected to do so. + * XXX: do we have to allow :: as source? */ m_freem(m); return; } -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),); - mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); -#else - IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh)); - if (mldh == NULL) { - icmp6stat.icp6s_tooshort++; - return; - } -#endif - /* * In the MLD6 specification, there are 3 states and a flag. * @@ -234,7 +236,7 @@ mld6_input(m, off) break; if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) && - !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) + !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) break; /* print error or log stat? */ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = @@ -343,7 +345,7 @@ mld6_input(m, off) void mld6_fasttimeo() { - register struct in6_multi *in6m; + struct in6_multi *in6m; struct in6_multistep step; int s; @@ -408,6 +410,7 @@ mld6_sendpkt(in6m, type, dst) } mh->m_next = md; + mh->m_pkthdr.rcvif = NULL; mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr); mh->m_len = sizeof(struct ip6_hdr); MH_ALIGN(mh, sizeof(struct ip6_hdr)); @@ -455,16 +458,16 @@ mld6_sendpkt(in6m, type, dst) ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); - switch(type) { - case MLD6_LISTENER_QUERY: - icmp6_ifstat_inc(outif, ifs6_out_mldquery); - break; - case MLD6_LISTENER_REPORT: - icmp6_ifstat_inc(outif, ifs6_out_mldreport); - break; - case MLD6_LISTENER_DONE: - icmp6_ifstat_inc(outif, ifs6_out_mlddone); - break; + switch (type) { + case MLD6_LISTENER_QUERY: + icmp6_ifstat_inc(outif, ifs6_out_mldquery); + break; + case MLD6_LISTENER_REPORT: + icmp6_ifstat_inc(outif, ifs6_out_mldreport); + break; + case MLD6_LISTENER_DONE: + icmp6_ifstat_inc(outif, ifs6_out_mlddone); + break; } } } diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 833e90262bc9..270550bf4903 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: nd6.c,v 1.68 2000/07/02 14:48:02 itojun Exp $ */ +/* $KAME: nd6.c,v 1.144 2001/05/24 07:44:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -43,6 +43,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/callout.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/socket.h> @@ -53,6 +54,7 @@ #include <sys/errno.h> #include <sys/syslog.h> #include <sys/queue.h> +#include <sys/sysctl.h> #include <net/if.h> #include <net/if_dl.h> @@ -84,12 +86,19 @@ int nd6_delay = 5; /* delay first probe time 5 second */ int nd6_umaxtries = 3; /* maximum unicast query */ int nd6_mmaxtries = 3; /* maximum multicast query */ int nd6_useloopback = 1; /* use loopback interface for local traffic */ +int nd6_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */ /* preventing too many loops in ND option parsing */ int nd6_maxndopt = 10; /* max # of ND options allowed */ int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */ +#ifdef ND6_DEBUG +int nd6_debug = 1; +#else +int nd6_debug = 0; +#endif + /* for debugging? */ static int nd6_inuse, nd6_allocated; @@ -103,6 +112,11 @@ int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; static struct sockaddr_in6 all1_sa; static void nd6_slowtimo __P((void *)); +static int regen_tmpaddr __P((struct in6_ifaddr *)); + +struct callout nd6_slowtimo_ch; +struct callout nd6_timer_ch; +extern struct callout in6_tmpaddrtimer_ch; void nd6_init() @@ -126,7 +140,8 @@ nd6_init() nd6_init_done = 1; /* start timer */ - timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); + callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, + nd6_slowtimo, NULL); } void @@ -192,22 +207,30 @@ nd6_setmtu(ifp) u_long oldmaxmtu = ndi->maxmtu; u_long oldlinkmtu = ndi->linkmtu; - switch(ifp->if_type) { - case IFT_ARCNET: /* XXX MTU handling needs more work */ - ndi->maxmtu = MIN(60480, ifp->if_mtu); - break; - case IFT_ETHER: - ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); - break; - case IFT_FDDI: - ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu); - break; - case IFT_ATM: - ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu); - break; - default: - ndi->maxmtu = ifp->if_mtu; - break; + switch (ifp->if_type) { + case IFT_ARCNET: /* XXX MTU handling needs more work */ + ndi->maxmtu = MIN(60480, ifp->if_mtu); + break; + case IFT_ETHER: + ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); + break; + case IFT_FDDI: + ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu); + break; + case IFT_ATM: + ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu); + break; + case IFT_IEEE1394: /* XXX should be IEEE1394MTU(1500) */ + ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); + break; +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: /* XXX should be IEEE80211MTU(1500) */ + ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); + break; +#endif + default: + ndi->maxmtu = ifp->if_mtu; + break; } if (oldmaxmtu != ndi->maxmtu) { @@ -329,6 +352,7 @@ nd6_options(ndopts) * Message validation requires that all included * options have a length that is greater than zero. */ + icmp6stat.icp6s_nd_badopt++; bzero(ndopts, sizeof(*ndopts)); return -1; } @@ -342,8 +366,9 @@ nd6_options(ndopts) case ND_OPT_MTU: case ND_OPT_REDIRECTED_HEADER: if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { - printf("duplicated ND6 option found " - "(type=%d)\n", nd_opt->nd_opt_type); + nd6log((LOG_INFO, + "duplicated ND6 option found (type=%d)\n", + nd_opt->nd_opt_type)); /* XXX bark? */ } else { ndopts->nd_opt_array[nd_opt->nd_opt_type] @@ -363,16 +388,16 @@ nd6_options(ndopts) * Unknown options must be silently ignored, * to accomodate future extension to the protocol. */ - log(LOG_DEBUG, + nd6log((LOG_DEBUG, "nd6_options: unsupported option %d - " - "option ignored\n", nd_opt->nd_opt_type); + "option ignored\n", nd_opt->nd_opt_type)); } skip1: i++; if (i > nd6_maxndopt) { icmp6stat.icp6s_nd_toomanyopt++; - printf("too many loop in nd opt\n"); + nd6log((LOG_INFO, "too many loop in nd opt\n")); break; } @@ -391,18 +416,21 @@ nd6_timer(ignored_arg) void *ignored_arg; { int s; - register struct llinfo_nd6 *ln; - register struct nd_defrouter *dr; - register struct nd_prefix *pr; + struct llinfo_nd6 *ln; + struct nd_defrouter *dr; + struct nd_prefix *pr; + struct ifnet *ifp; + struct in6_ifaddr *ia6, *nia6; + struct in6_addrlifetime *lt6; s = splnet(); - timeout(nd6_timer, (caddr_t)0, nd6_prune * hz); + callout_reset(&nd6_timer_ch, nd6_prune * hz, + nd6_timer, NULL); ln = llinfo_nd6.ln_next; /* XXX BSD/OS separates this code -- itojun */ while (ln && ln != &llinfo_nd6) { struct rtentry *rt; - struct ifnet *ifp; struct sockaddr_in6 *dst; struct llinfo_nd6 *next = ln->ln_next; /* XXX: used for the DELAY case only: */ @@ -458,17 +486,22 @@ nd6_timer(ignored_arg) ICMP6_DST_UNREACH_ADDR, 0); ln->ln_hold = NULL; } - nd6_free(rt); + next = nd6_free(rt); } break; case ND6_LLINFO_REACHABLE: - if (ln->ln_expire) + if (ln->ln_expire) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = time_second + nd6_gctimer; + } break; - /* - * ND6_LLINFO_STALE state requires nothing for timer - * routine. - */ + + case ND6_LLINFO_STALE: + /* Garbage Collection(RFC 2461 5.3) */ + if (ln->ln_expire) + next = nd6_free(rt); + break; + case ND6_LLINFO_DELAY: if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) { /* We need NUD */ @@ -479,8 +512,10 @@ nd6_timer(ignored_arg) nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, ln, 0); - } else + } else { ln->ln_state = ND6_LLINFO_STALE; /* XXX */ + ln->ln_expire = time_second + nd6_gctimer; + } break; case ND6_LLINFO_PROBE: if (ln->ln_asked < nd6_umaxtries) { @@ -490,17 +525,14 @@ nd6_timer(ignored_arg) nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, ln, 0); } else { - nd6_free(rt); + next = nd6_free(rt); } break; - case ND6_LLINFO_WAITDELETE: - nd6_free(rt); - break; } ln = next; } - /* expire */ + /* expire default router list */ dr = TAILQ_FIRST(&nd_defrouter); while (dr) { if (dr->expire && dr->expire < time_second) { @@ -512,28 +544,82 @@ nd6_timer(ignored_arg) dr = TAILQ_NEXT(dr, dr_entry); } } - pr = nd_prefix.lh_first; - while (pr) { - struct in6_ifaddr *ia6; - struct in6_addrlifetime *lt6; - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - - if (ia6) { - /* check address lifetime */ - lt6 = &ia6->ia6_lifetime; - if (lt6->ia6t_preferred && lt6->ia6t_preferred < time_second) - ia6->ia6_flags |= IN6_IFF_DEPRECATED; - if (lt6->ia6t_expire && lt6->ia6t_expire < time_second) { - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); - /* xxx ND_OPT_PI_FLAG_ONLINK processing */ + /* + * expire interface addresses. + * in the past the loop was inside prefix expiry processing. + * However, from a stricter speci-confrmance standpoint, we should + * rather separate address lifetimes and prefix lifetimes. + */ + addrloop: + for (ia6 = in6_ifaddr; ia6; ia6 = nia6) { + nia6 = ia6->ia_next; + /* check address lifetime */ + lt6 = &ia6->ia6_lifetime; + if (IFA6_IS_INVALID(ia6)) { + int regen = 0; + + /* + * If the expiring address is temporary, try + * regenerating a new one. This would be useful when + * we suspended a laptop PC, then turned on after a + * period that could invalidate all temporary + * addresses. Although we may have to restart the + * loop (see below), it must be after purging the + * address. Otherwise, we'd see an infinite loop of + * regeneration. + */ + if (ip6_use_tempaddr && + (ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { + if (regen_tmpaddr(ia6) == 0) + regen = 1; } + + in6_purgeaddr(&ia6->ia_ifa); + + if (regen) + goto addrloop; /* XXX: see below */ + } else if (IFA6_IS_DEPRECATED(ia6)) { + int oldflags = ia6->ia6_flags; + + ia6->ia6_flags |= IN6_IFF_DEPRECATED; + + /* + * If a temporary address has just become deprecated, + * regenerate a new one if possible. + */ + if (ip6_use_tempaddr && + (ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + (oldflags & IN6_IFF_DEPRECATED) == 0) { + + if (regen_tmpaddr(ia6) == 0) { + /* + * A new temporary address is + * generated. + * XXX: this means the address chain + * has changed while we are still in + * the loop. Although the change + * would not cause disaster (because + * it's not an addition, but a + * deletion,) we'd rather restart the + * loop just for safety. Or does this + * significantly reduce performance?? + */ + goto addrloop; + } + } + } else if (IFA6_IS_DEPRECATED(ia6)) { + /* + * A new RA might have made a deprecated address + * preferred. + */ + ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; } + } + /* expire prefix list */ + pr = nd_prefix.lh_first; + while (pr) { /* * check prefix lifetime. * since pltime is just for autoconf, pltime processing for @@ -543,15 +629,17 @@ nd6_timer(ignored_arg) * can use the old prefix information to validate the * next prefix information to come. See prelist_update() * for actual validation. + * + * I don't think such an offset is necessary. + * (jinmei@kame.net, 20010130). */ - if (pr->ndpr_expire - && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) { + if (pr->ndpr_expire && pr->ndpr_expire < time_second) { struct nd_prefix *t; t = pr->ndpr_next; /* * address expiration and prefix expiration are - * separate. NEVER perform in6_ifdel here. + * separate. NEVER perform in6_purgeaddr here. */ prelist_remove(pr); @@ -562,6 +650,70 @@ nd6_timer(ignored_arg) splx(s); } +static int +regen_tmpaddr(ia6) + struct in6_ifaddr *ia6; /* deprecated/invalidated temporary address */ +{ + struct ifaddr *ifa; + struct ifnet *ifp; + struct in6_ifaddr *public_ifa6 = NULL; + + ifp = ia6->ia_ifa.ifa_ifp; + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) + { + struct in6_ifaddr *it6; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + it6 = (struct in6_ifaddr *)ifa; + + /* ignore no autoconf addresses. */ + if ((it6->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + /* ignore autoconf addresses with different prefixes. */ + if (it6->ia6_ndpr == NULL || it6->ia6_ndpr != ia6->ia6_ndpr) + continue; + + /* + * Now we are looking at an autoconf address with the same + * prefix as ours. If the address is temporary and is still + * preferred, do not create another one. It would be rare, but + * could happen, for example, when we resume a laptop PC after + * a long period. + */ + if ((it6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + !IFA6_IS_DEPRECATED(it6)) { + public_ifa6 = NULL; + break; + } + + /* + * This is a public autoconf address that has the same prefix + * as ours. If it is preferred, keep it. We can't break the + * loop here, because there may be a still-preferred temporary + * address with the prefix. + */ + if (!IFA6_IS_DEPRECATED(it6)) + public_ifa6 = it6; + } + + if (public_ifa6 != NULL) { + int e; + + if ((e = in6_tmpifadd(public_ifa6, 0)) != 0) { + log(LOG_NOTICE, "regen_tmpaddr: failed to create a new" + " tmp addr,errno=%d\n", e); + return(-1); + } + return(0); + } + + return(-1); +} + /* * Nuke neighbor cache/prefix/default router management table, right before * ifp goes away. @@ -594,8 +746,14 @@ nd6_purge(ifp) for (pr = nd_prefix.lh_first; pr; pr = npr) { npr = pr->ndpr_next; if (pr->ndpr_ifp == ifp) { - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + /* + * Previously, pr->ndpr_addr is removed as well, + * but I strongly believe we don't have to do it. + * nd6_purge() is only called from in6_ifdetach(), + * which removes all the associated interface addresses + * by itself. + * (jinmei@kame.net 20010129) + */ prelist_remove(pr); } } @@ -626,30 +784,7 @@ nd6_purge(ifp) rt->rt_gateway->sa_family == AF_LINK) { sdl = (struct sockaddr_dl *)rt->rt_gateway; if (sdl->sdl_index == ifp->if_index) - nd6_free(rt); - } - ln = nln; - } - - /* - * Neighbor cache entry for interface route will be retained - * with ND6_LLINFO_WAITDELETE state, by nd6_free(). Nuke it. - */ - ln = llinfo_nd6.ln_next; - while (ln && ln != &llinfo_nd6) { - struct rtentry *rt; - struct sockaddr_dl *sdl; - - nln = ln->ln_next; - rt = ln->ln_rt; - if (rt && rt->rt_gateway && - rt->rt_gateway->sa_family == AF_LINK) { - sdl = (struct sockaddr_dl *)rt->rt_gateway; - if (sdl->sdl_index == ifp->if_index) { - rtrequest(RTM_DELETE, rt_key(rt), - (struct sockaddr *)0, rt_mask(rt), 0, - (struct rtentry **)0); - } + nln = nd6_free(rt); } ln = nln; } @@ -757,7 +892,7 @@ nd6_is_addr_neighbor(addr, ifp) struct sockaddr_in6 *addr; struct ifnet *ifp; { - register struct ifaddr *ifa; + struct ifaddr *ifa; int i; #define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr) @@ -807,26 +942,25 @@ nd6_is_addr_neighbor(addr, ifp) /* * Free an nd6 llinfo entry. */ -void +struct llinfo_nd6 * nd6_free(rt) struct rtentry *rt; { - struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; - struct sockaddr_dl *sdl; + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; struct nd_defrouter *dr; /* - * Clear all destination cache entries for the neighbor. - * XXX: is it better to restrict this to hosts? + * we used to have pfctlinput(PRC_HOSTDEAD) here. + * even though it is not harmful, it was not really necessary. */ - pfctlinput(PRC_HOSTDEAD, rt_key(rt)); if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */ int s; s = splnet(); dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, rt->rt_ifp); + if (ln->ln_router || dr) { /* * rt6_flush must be called whether or not the neighbor @@ -852,6 +986,14 @@ nd6_free(rt) */ ln->ln_state = ND6_LLINFO_INCOMPLETE; + /* + * Since defrouter_select() does not affect the + * on-link determination and MIP6 needs the check + * before the default router selection, we perform + * the check now. + */ + pfxlist_onlink_check(); + if (dr == TAILQ_FIRST(&nd_defrouter)) { /* * It is used as the current default router, @@ -865,22 +1007,27 @@ nd6_free(rt) defrouter_select(); } - pfxlist_onlink_check(); } splx(s); } - if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) && - sdl->sdl_family == AF_LINK) { - sdl->sdl_alen = 0; - ln->ln_state = ND6_LLINFO_WAITDELETE; - ln->ln_asked = 0; - rt->rt_flags &= ~RTF_REJECT; - return; - } + /* + * Before deleting the entry, remember the next entry as the + * return value. We need this because pfxlist_onlink_check() above + * might have freed other entries (particularly the old next entry) as + * a side effect (XXX). + */ + next = ln->ln_next; + /* + * Detach the route from the routing tree and the list of neighbor + * caches, and disable the route entry not to be used in already + * cached routes. + */ rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), 0, (struct rtentry **)0); + + return(next); } /* @@ -935,103 +1082,6 @@ nd6_nud_hint(rt, dst6, force) nd_ifinfo[rt->rt_ifp->if_index].reachable; } -#ifdef OLDIP6OUTPUT -/* - * Resolve an IP6 address into an ethernet address. If success, - * desten is filled in. If there is no entry in ndptab, - * set one up and multicast a solicitation for the IP6 address. - * Hold onto this mbuf and resend it once the address - * is finally resolved. A return value of 1 indicates - * that desten has been filled in and the packet should be sent - * normally; a 0 return indicates that the packet has been - * taken over here, either now or for later transmission. - */ -int -nd6_resolve(ifp, rt, m, dst, desten) - struct ifnet *ifp; - struct rtentry *rt; - struct mbuf *m; - struct sockaddr *dst; - u_char *desten; -{ - struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL; - struct sockaddr_dl *sdl; - - if (m->m_flags & M_MCAST) { - switch (ifp->if_type) { - case IFT_ETHER: - case IFT_FDDI: - ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, - desten); - return(1); - break; - case IFT_ARCNET: - *desten = 0; - return(1); - break; - default: - m_freem(m); - return(0); - } - } - if (rt && (rt->rt_flags & RTF_LLINFO) != 0) - ln = (struct llinfo_nd6 *)rt->rt_llinfo; - else { - if ((rt = nd6_lookup(&(SIN6(dst)->sin6_addr), 1, ifp)) != NULL) - ln = (struct llinfo_nd6 *)rt->rt_llinfo; - } - if (!ln || !rt) { - log(LOG_DEBUG, "nd6_resolve: can't allocate llinfo for %s\n", - ip6_sprintf(&(SIN6(dst)->sin6_addr))); - m_freem(m); - return(0); - } - sdl = SDL(rt->rt_gateway); - /* - * Ckeck the address family and length is valid, the address - * is resolved; otherwise, try to resolve. - */ - if (ln->ln_state >= ND6_LLINFO_REACHABLE - && sdl->sdl_family == AF_LINK - && sdl->sdl_alen != 0) { - bcopy(LLADDR(sdl), desten, sdl->sdl_alen); - if (ln->ln_state == ND6_LLINFO_STALE) { - ln->ln_asked = 0; - ln->ln_state = ND6_LLINFO_DELAY; - ln->ln_expire = time_second + nd6_delay; - } - return(1); - } - /* - * There is an ndp entry, but no ethernet address - * response yet. Replace the held mbuf with this - * latest one. - * - * XXX Does the code conform to rate-limiting rule? - * (RFC 2461 7.2.2) - */ - if (ln->ln_state == ND6_LLINFO_WAITDELETE || - ln->ln_state == ND6_LLINFO_NOSTATE) - ln->ln_state = ND6_LLINFO_INCOMPLETE; - if (ln->ln_hold) - m_freem(ln->ln_hold); - ln->ln_hold = m; - if (ln->ln_expire) { - rt->rt_flags &= ~RTF_REJECT; - if (ln->ln_asked < nd6_mmaxtries && - ln->ln_expire < time_second) { - ln->ln_asked++; - ln->ln_expire = time_second + - nd_ifinfo[ifp->if_index].retrans / 1000; - nd6_ns_output(ifp, NULL, &(SIN6(dst)->sin6_addr), - ln, 0); - } - } - /* Do not free mbuf chain here as it is queued in llinfo_nd6 */ - return(0); -} -#endif /* OLDIP6OUTPUT */ - void nd6_rtrequest(req, rt, sa) int req; @@ -1047,6 +1097,17 @@ nd6_rtrequest(req, rt, sa) if (rt->rt_flags & RTF_GATEWAY) return; + if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) { + /* + * This is probably an interface direct route for a link + * which does not need neighbor caches (e.g. fe80::%lo0/64). + * We do not need special treatment below for such a route. + * Moreover, the RTF_LLINFO flag which would be set below + * would annoy the ndp(8) command. + */ + return; + } + switch (req) { case RTM_ADD: /* @@ -1072,7 +1133,7 @@ nd6_rtrequest(req, rt, sa) ln->ln_expire = time_second; #if 1 if (ln && ln->ln_expire == 0) { - /* cludge for desktops */ + /* kludge for desktops */ #if 0 printf("nd6_request: time.tv_sec is zero; " "treat it as 1\n"); @@ -1093,10 +1154,10 @@ nd6_rtrequest(req, rt, sa) * (7.2.6 paragraph 4), however, it also says that we * SHOULD provide a mechanism to prevent multicast NA storm. * we don't have anything like it right now. - * note that the mechanism need a mutual agreement + * note that the mechanism needs a mutual agreement * between proxies, which means that we need to implement - * a new protocol, or new kludge. - * - from RFC2461 6.2.4, host MUST NOT send unsolicited NA. + * a new protocol, or a new kludge. + * - from RFC2461 6.2.4, host MUST NOT send an unsolicited NA. * we need to check ip6forwarding before sending it. * (or should we allow proxy ND configuration only for * routers? there's no mention about proxy ND from hosts) @@ -1112,7 +1173,7 @@ nd6_rtrequest(req, rt, sa) #endif /* FALLTHROUGH */ case RTM_RESOLVE: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { /* * Address resolution isn't necessary for a point to * point link, so we can skip this test for a p2p link. @@ -1120,7 +1181,8 @@ nd6_rtrequest(req, rt, sa) if (gate->sa_family != AF_LINK || gate->sa_len < sizeof(null_sdl)) { log(LOG_DEBUG, - "nd6_rtrequest: bad gateway value\n"); + "nd6_rtrequest: bad gateway value: %s\n", + if_name(ifp)); break; } SDL(gate)->sdl_type = ifp->if_type; @@ -1192,7 +1254,7 @@ nd6_rtrequest(req, rt, sa) */ if (ifa != rt->rt_ifa) { IFAFREE(rt->rt_ifa); - ifa->ifa_refcnt++; + IFAREF(ifa); rt->rt_ifa = ifa; } } @@ -1213,10 +1275,11 @@ nd6_rtrequest(req, rt, sa) llsol.s6_addr32[2] = htonl(1); llsol.s6_addr8[12] = 0xff; - (void)in6_addmulti(&llsol, ifp, &error); - if (error) - printf( -"nd6_rtrequest: could not join solicited node multicast (errno=%d)\n", error); + if (!in6_addmulti(&llsol, ifp, &error)) { + nd6log((LOG_ERR, "%s: failed to join " + "%s (errno=%d)\n", if_name(ifp), + ip6_sprintf(&llsol), error)); + } } } break; @@ -1253,65 +1316,6 @@ nd6_rtrequest(req, rt, sa) } } -void -nd6_p2p_rtrequest(req, rt, sa) - int req; - struct rtentry *rt; - struct sockaddr *sa; /* xxx unused */ -{ - struct sockaddr *gate = rt->rt_gateway; - static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; - struct ifnet *ifp = rt->rt_ifp; - struct ifaddr *ifa; - - if (rt->rt_flags & RTF_GATEWAY) - return; - - switch (req) { - case RTM_ADD: - /* - * There is no backward compatibility :) - * - * if ((rt->rt_flags & RTF_HOST) == 0 && - * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) - * rt->rt_flags |= RTF_CLONING; - */ - if (rt->rt_flags & RTF_CLONING) { - /* - * Case 1: This route should come from - * a route to interface. - */ - rt_setgate(rt, rt_key(rt), - (struct sockaddr *)&null_sdl); - gate = rt->rt_gateway; - SDL(gate)->sdl_type = ifp->if_type; - SDL(gate)->sdl_index = ifp->if_index; - break; - } - /* Announce a new entry if requested. */ - if (rt->rt_flags & RTF_ANNOUNCE) - nd6_na_output(ifp, - &SIN6(rt_key(rt))->sin6_addr, - &SIN6(rt_key(rt))->sin6_addr, - ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1, NULL); - /* FALLTHROUGH */ - case RTM_RESOLVE: - /* - * check if rt_key(rt) is one of my address assigned - * to the interface. - */ - ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp, - &SIN6(rt_key(rt))->sin6_addr); - if (ifa) { - if (nd6_useloopback) { - rt->rt_ifp = &loif[0]; /*XXX*/ - } - } - break; - } -} - int nd6_ioctl(cmd, data, ifp) u_long cmd; @@ -1331,6 +1335,9 @@ nd6_ioctl(cmd, data, ifp) switch (cmd) { case SIOCGDRLST_IN6: + /* + * obsolete API, use sysctl under net.inet6.icmp6 + */ bzero(drl, sizeof(*drl)); s = splnet(); dr = TAILQ_FIRST(&nd_defrouter); @@ -1356,6 +1363,9 @@ nd6_ioctl(cmd, data, ifp) break; case SIOCGPRLST_IN6: /* + * obsolete API, use sysctl under net.inet6.icmp6 + */ + /* * XXX meaning of fields, especialy "raflags", is very * differnet between RA prefix list and RR/static prefix list. * how about separating ioctls into two? @@ -1367,7 +1377,8 @@ nd6_ioctl(cmd, data, ifp) struct nd_pfxrouter *pfr; int j; - prl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr; + (void)in6_embedscope(&prl->prefix[i].prefix, + &pr->ndpr_prefix, NULL, NULL); prl->prefix[i].raflags = pr->ndpr_raf; prl->prefix[i].prefixlen = pr->ndpr_plen; prl->prefix[i].vltime = pr->ndpr_vltime; @@ -1377,7 +1388,7 @@ nd6_ioctl(cmd, data, ifp) pfr = pr->ndpr_advrtrs.lh_first; j = 0; - while(pfr) { + while (pfr) { if (j < DRLSTSIZ) { #define RTRADDR prl->prefix[i].advrtr[j] RTRADDR = pfr->router->rtaddr; @@ -1408,7 +1419,8 @@ nd6_ioctl(cmd, data, ifp) rpp = LIST_NEXT(rpp, rp_entry)) { if (i >= PRLSTSIZ) break; - prl->prefix[i].prefix = rpp->rp_prefix.sin6_addr; + (void)in6_embedscope(&prl->prefix[i].prefix, + &pr->ndpr_prefix, NULL, NULL); prl->prefix[i].raflags = rpp->rp_raf; prl->prefix[i].prefixlen = rpp->rp_plen; prl->prefix[i].vltime = rpp->rp_vltime; @@ -1423,6 +1435,22 @@ nd6_ioctl(cmd, data, ifp) splx(s); break; + case OSIOCGIFINFO_IN6: + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { + error = EINVAL; + break; + } + ndi->ndi.linkmtu = nd_ifinfo[ifp->if_index].linkmtu; + ndi->ndi.maxmtu = nd_ifinfo[ifp->if_index].maxmtu; + ndi->ndi.basereachable = + nd_ifinfo[ifp->if_index].basereachable; + ndi->ndi.reachable = nd_ifinfo[ifp->if_index].reachable; + ndi->ndi.retrans = nd_ifinfo[ifp->if_index].retrans; + ndi->ndi.flags = nd_ifinfo[ifp->if_index].flags; + ndi->ndi.recalctm = nd_ifinfo[ifp->if_index].recalctm; + ndi->ndi.chlim = nd_ifinfo[ifp->if_index].chlim; + ndi->ndi.receivedra = nd_ifinfo[ifp->if_index].receivedra; + break; case SIOCGIFINFO_IN6: if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { error = EINVAL; @@ -1456,9 +1484,24 @@ nd6_ioctl(cmd, data, ifp) s = splnet(); for (pr = nd_prefix.lh_first; pr; pr = next) { + struct in6_ifaddr *ia, *ia_next; + next = pr->ndpr_next; - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; /* XXX */ + + /* do we really have to remove addresses as well? */ + for (ia = in6_ifaddr; ia; ia = ia_next) { + /* ia might be removed. keep the next ptr. */ + ia_next = ia->ia_next; + + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if (ia->ia6_ndpr == pr) + in6_purgeaddr(&ia->ia_ifa); + } prelist_remove(pr); } splx(s); @@ -1577,14 +1620,18 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code) rt = nd6_lookup(from, 1, ifp); is_newentry = 1; - } else + } else { + /* do nothing if static ndp is set */ + if (rt->rt_flags & RTF_STATIC) + return NULL; is_newentry = 0; + } if (!rt) return NULL; if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) { fail: - nd6_free(rt); + (void)nd6_free(rt); return NULL; } ln = (struct llinfo_nd6 *)rt->rt_llinfo; @@ -1647,12 +1694,15 @@ fail: ln->ln_state = newstate; if (ln->ln_state == ND6_LLINFO_STALE) { - rt->rt_flags &= ~RTF_REJECT; + /* + * XXX: since nd6_output() below will cause + * state tansition to DELAY and reset the timer, + * we must set the timer now, although it is actually + * meaningless. + */ + ln->ln_expire = time_second + nd6_gctimer; + if (ln->ln_hold) { -#ifdef OLDIP6OUTPUT - (*ifp->if_output)(ifp, ln->ln_hold, - rt_key(rt), rt); -#else /* * we assume ifp is not a p2p here, so just * set the 2nd argument as the 1st one. @@ -1660,8 +1710,7 @@ fail: nd6_output(ifp, ifp, ln->ln_hold, (struct sockaddr_in6 *)rt_key(rt), rt); -#endif - ln->ln_hold = 0; + ln->ln_hold = NULL; } } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* probe right away */ @@ -1742,10 +1791,11 @@ nd6_slowtimo(ignored_arg) void *ignored_arg; { int s = splnet(); - register int i; - register struct nd_ifinfo *nd6if; + int i; + struct nd_ifinfo *nd6if; - timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); + callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, + nd6_slowtimo, NULL); for (i = 1; i < if_index + 1; i++) { if (!nd_ifinfo || i >= nd_ifinfo_indexlim) continue; @@ -1768,14 +1818,14 @@ nd6_slowtimo(ignored_arg) #define senderr(e) { error = (e); goto bad;} int nd6_output(ifp, origifp, m0, dst, rt0) - register struct ifnet *ifp; + struct ifnet *ifp; struct ifnet *origifp; struct mbuf *m0; struct sockaddr_in6 *dst; struct rtentry *rt0; { - register struct mbuf *m = m0; - register struct rtentry *rt = rt0; + struct mbuf *m = m0; + struct rtentry *rt = rt0; struct sockaddr_in6 *gw6 = NULL; struct llinfo_nd6 *ln = NULL; int error = 0; @@ -1783,22 +1833,8 @@ nd6_output(ifp, origifp, m0, dst, rt0) if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) goto sendpkt; - /* - * XXX: we currently do not make neighbor cache on any interface - * other than ARCnet, Ethernet, FDDI and GIF. - * - * draft-ietf-ngtrans-mech-06.txt says: - * - unidirectional tunnels needs no ND - */ - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: - case IFT_GIF: /* XXX need more cases? */ - break; - default: + if (nd6_need_cache(ifp) == 0) goto sendpkt; - } /* * next hop determination. This routine is derived from ether_outpout. @@ -1824,7 +1860,7 @@ nd6_output(ifp, origifp, m0, dst, rt0) /* * We skip link-layer address resolution and NUD * if the gateway is not a neighbor from ND point - * of view, regardless the value of the value of + * of view, regardless the value of the * nd_ifinfo.flags. * The second condition is a bit tricky: we skip * if the gateway is our own address, which is @@ -1832,9 +1868,6 @@ nd6_output(ifp, origifp, m0, dst, rt0) */ if (!nd6_is_addr_neighbor(gw6, ifp) || in6ifa_ifpwithaddr(ifp, &gw6->sin6_addr)) { - if (rt->rt_flags & RTF_REJECT) - senderr(EHOSTDOWN); - /* * We allow this kind of tricky route only * when the outgoing interface is p2p. @@ -1855,8 +1888,6 @@ nd6_output(ifp, origifp, m0, dst, rt0) senderr(EHOSTUNREACH); } } - if (rt->rt_flags & RTF_REJECT) - senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } /* @@ -1894,8 +1925,10 @@ nd6_output(ifp, origifp, m0, dst, rt0) /* We don't have to do link-layer address resolution on a p2p link. */ if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && - ln->ln_state < ND6_LLINFO_REACHABLE) + ln->ln_state < ND6_LLINFO_REACHABLE) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = time_second + nd6_gctimer; + } /* * The first time we send a packet to a neighbor whose entry is @@ -1926,14 +1959,12 @@ nd6_output(ifp, origifp, m0, dst, rt0) * XXX Does the code conform to rate-limiting rule? * (RFC 2461 7.2.2) */ - if (ln->ln_state == ND6_LLINFO_WAITDELETE || - ln->ln_state == ND6_LLINFO_NOSTATE) + if (ln->ln_state == ND6_LLINFO_NOSTATE) ln->ln_state = ND6_LLINFO_INCOMPLETE; if (ln->ln_hold) m_freem(ln->ln_hold); ln->ln_hold = m; if (ln->ln_expire) { - rt->rt_flags &= ~RTF_REJECT; if (ln->ln_asked < nd6_mmaxtries && ln->ln_expire < time_second) { ln->ln_asked++; @@ -1946,12 +1977,10 @@ nd6_output(ifp, origifp, m0, dst, rt0) sendpkt: -#ifdef FAKE_LOOPBACK_IF - if (ifp->if_flags & IFF_LOOPBACK) { + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { return((*ifp->if_output)(origifp, m, (struct sockaddr *)dst, rt)); } -#endif return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt)); bad: @@ -1962,6 +1991,32 @@ nd6_output(ifp, origifp, m0, dst, rt0) #undef senderr int +nd6_need_cache(ifp) + struct ifnet *ifp; +{ + /* + * XXX: we currently do not make neighbor cache on any interface + * other than ARCnet, Ethernet, FDDI and GIF. + * + * RFC2893 says: + * - unidirectional tunnels needs no ND + */ + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + case IFT_IEEE1394: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif + case IFT_GIF: /* XXX need more cases? */ + return(1); + default: + return(0); + } +} + +int nd6_storelladdr(ifp, rt, m, dst, desten) struct ifnet *ifp; struct rtentry *rt; @@ -1969,16 +2024,23 @@ nd6_storelladdr(ifp, rt, m, dst, desten) struct sockaddr *dst; u_char *desten; { + int i; struct sockaddr_dl *sdl; if (m->m_flags & M_MCAST) { switch (ifp->if_type) { case IFT_ETHER: - case IFT_FDDI: + case IFT_FDDI: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, desten); return(1); - break; + case IFT_IEEE1394: + for (i = 0; i < ifp->if_addrlen; i++) + desten[i] = ~0; + return(1); case IFT_ARCNET: *desten = 0; return(1); @@ -1989,12 +2051,12 @@ nd6_storelladdr(ifp, rt, m, dst, desten) } if (rt == NULL) { - /* This could happen if we could not allocate memory */ + /* this could happen, if we could not allocate memory */ m_freem(m); return(0); } if (rt->rt_gateway->sa_family != AF_LINK) { - printf("nd6_storelladdr: something odd happened\n"); + printf("nd6_storelladdr: something odd happens\n"); m_freem(m); return(0); } @@ -2009,3 +2071,129 @@ nd6_storelladdr(ifp, rt, m, dst, desten) bcopy(LLADDR(sdl), desten, sdl->sdl_alen); return(1); } + +static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS); +static int nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS); +#ifdef SYSCTL_DECL +SYSCTL_DECL(_net_inet6_icmp6); +#endif +SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist, + CTLFLAG_RD, nd6_sysctl_drlist, ""); +SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist, + CTLFLAG_RD, nd6_sysctl_prlist, ""); + +static int +nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) +{ + int error; + char buf[1024]; + struct in6_defrouter *d, *de; + struct nd_defrouter *dr; + + if (req->newptr) + return EPERM; + error = 0; + + for (dr = TAILQ_FIRST(&nd_defrouter); + dr; + dr = TAILQ_NEXT(dr, dr_entry)) { + d = (struct in6_defrouter *)buf; + de = (struct in6_defrouter *)(buf + sizeof(buf)); + + if (d + 1 <= de) { + bzero(d, sizeof(*d)); + d->rtaddr.sin6_family = AF_INET6; + d->rtaddr.sin6_len = sizeof(d->rtaddr); + if (in6_recoverscope(&d->rtaddr, &dr->rtaddr, + dr->ifp) != 0) + log(LOG_ERR, + "scope error in " + "default router list (%s)\n", + ip6_sprintf(&dr->rtaddr)); + d->flags = dr->flags; + d->rtlifetime = dr->rtlifetime; + d->expire = dr->expire; + d->if_index = dr->ifp->if_index; + } else + panic("buffer too short"); + + error = SYSCTL_OUT(req, buf, sizeof(*d)); + if (error) + break; + } + return error; +} + +static int +nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS) +{ + int error; + char buf[1024]; + struct in6_prefix *p, *pe; + struct nd_prefix *pr; + + if (req->newptr) + return EPERM; + error = 0; + + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + u_short advrtrs; + size_t advance; + struct sockaddr_in6 *sin6, *s6; + struct nd_pfxrouter *pfr; + + p = (struct in6_prefix *)buf; + pe = (struct in6_prefix *)(buf + sizeof(buf)); + + if (p + 1 <= pe) { + bzero(p, sizeof(*p)); + sin6 = (struct sockaddr_in6 *)(p + 1); + + p->prefix = pr->ndpr_prefix; + if (in6_recoverscope(&p->prefix, + &p->prefix.sin6_addr, pr->ndpr_ifp) != 0) + log(LOG_ERR, + "scope error in prefix list (%s)\n", + ip6_sprintf(&p->prefix.sin6_addr)); + p->raflags = pr->ndpr_raf; + p->prefixlen = pr->ndpr_plen; + p->vltime = pr->ndpr_vltime; + p->pltime = pr->ndpr_pltime; + p->if_index = pr->ndpr_ifp->if_index; + p->expire = pr->ndpr_expire; + p->refcnt = pr->ndpr_refcnt; + p->flags = pr->ndpr_stateflags; + p->origin = PR_ORIG_RA; + advrtrs = 0; + for (pfr = pr->ndpr_advrtrs.lh_first; + pfr; + pfr = pfr->pfr_next) { + if ((void *)&sin6[advrtrs + 1] > + (void *)pe) { + advrtrs++; + continue; + } + s6 = &sin6[advrtrs]; + bzero(s6, sizeof(*s6)); + s6->sin6_family = AF_INET6; + s6->sin6_len = sizeof(*sin6); + if (in6_recoverscope(s6, + &pfr->router->rtaddr, + pfr->router->ifp) != 0) + log(LOG_ERR, + "scope error in " + "prefix list (%s)\n", + ip6_sprintf(&pfr->router->rtaddr)); + advrtrs++; + } + p->advrtrs = advrtrs; + } else + panic("buffer too short"); + + advance = sizeof(*p) + sizeof(*sin6) * advrtrs; + error = SYSCTL_OUT(req, buf, advance); + if (error) + break; + } + return error; +} diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 26f58b50a991..73bbcd5aef0e 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: nd6.h,v 1.23 2000/06/04 12:54:57 itojun Exp $ */ +/* $KAME: nd6.h,v 1.55 2001/04/27 15:09:49 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -39,6 +39,7 @@ #endif #include <sys/queue.h> +#include <sys/callout.h> struct llinfo_nd6 { struct llinfo_nd6 *ln_next; @@ -53,7 +54,14 @@ struct llinfo_nd6 { }; #define ND6_LLINFO_NOSTATE -2 -#define ND6_LLINFO_WAITDELETE -1 +/* + * We don't need the WAITDELETE state any more, but we keep the definition + * in a comment line instead of removing it. This is necessary to avoid + * unintentionally reusing the value for another purpose, which might + * affect backward compatibility with old applications. + * (20000711 jinmei@kame.net) + */ +/* #define ND6_LLINFO_WAITDELETE -1 */ #define ND6_LLINFO_INCOMPLETE 0 #define ND6_LLINFO_REACHABLE 1 #define ND6_LLINFO_STALE 2 @@ -72,6 +80,10 @@ struct nd_ifinfo { int recalctm; /* BaseReacable re-calculation timer */ u_int8_t chlim; /* CurHopLimit */ u_int8_t receivedra; + /* the followings are for privacy extension for addrconf */ + u_int8_t randomseed0[8]; /* upper 64 bits of MD5 digest */ + u_int8_t randomseed1[8]; /* lower 64 bits (usually the EUI64 IFID) */ + u_int8_t randomid[8]; /* current random ID */ }; #define ND6_IFF_PERFORMNUD 0x1 @@ -98,6 +110,14 @@ struct in6_drlist { } defrouter[DRLSTSIZ]; }; +struct in6_defrouter { + struct sockaddr_in6 rtaddr; + u_char flags; + u_short rtlifetime; + u_long expire; + u_short if_index; +} __attribute__((__packed__)); + struct in6_prlist { char ifname[IFNAMSIZ]; struct { @@ -114,6 +134,38 @@ struct in6_prlist { } prefix[PRLSTSIZ]; }; +struct in6_prefix { + struct sockaddr_in6 prefix; + struct prf_ra raflags; + u_char prefixlen; + u_char origin; + u_long vltime; + u_long pltime; + u_long expire; + u_int32_t flags; + int refcnt; + u_short if_index; + u_short advrtrs; /* number of advertisement routers */ + /* struct sockaddr_in6 advrtr[] */ +} __attribute__((__packed__)); + +#ifdef _KERNEL +struct in6_ondireq { + char ifname[IFNAMSIZ]; + struct { + u_int32_t linkmtu; /* LinkMTU */ + u_int32_t maxmtu; /* Upper bound of LinkMTU */ + u_int32_t basereachable; /* BaseReachableTime */ + u_int32_t reachable; /* Reachable Time */ + u_int32_t retrans; /* Retrans Timer */ + u_int32_t flags; /* Flags */ + int recalctm; /* BaseReacable re-calculation timer */ + u_int8_t chlim; /* CurHopLimit */ + u_int8_t receivedra; + } ndi; +}; +#endif + struct in6_ndireq { char ifname[IFNAMSIZ]; struct nd_ifinfo ndi; @@ -124,6 +176,9 @@ struct in6_ndifreq { u_long ifindex; }; +/* Prefix status */ +#define NDPRF_ONLINK 0x1 +#define NDPRF_DETACHED 0x2 /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/ @@ -139,6 +194,10 @@ struct in6_ndifreq { #define RETRANS_TIMER 1000 /* msec */ #define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ #define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ +#define DEF_TEMP_VALID_LIFETIME 604800 /* 1 week */ +#define DEF_TEMP_PREFERRED_LIFETIME 86400 /* 1 day */ +#define TEMPADDR_REGEN_ADVANCE 5 /* sec */ +#define MAX_TEMP_DESYNC_FACTOR 600 /* 10 min */ #define ND_COMPUTE_RTIME(x) \ (((MIN_RANDOM_FACTOR * (x >> 10)) + (random() & \ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) @@ -167,13 +226,11 @@ struct nd_prefix { time_t ndpr_expire; /* expiration time of the prefix */ time_t ndpr_preferred; /* preferred time of the prefix */ struct prf_ra ndpr_flags; + u_int32_t ndpr_stateflags; /* actual state flags */ /* list of routers that advertise the prefix: */ LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs; u_char ndpr_plen; - struct ndpr_stateflags { - /* if this prefix can be regarded as on-link */ - u_char onlink : 1; - } ndpr_stateflags; + int ndpr_refcnt; /* reference couter from addresses */ }; #define ndpr_next ndpr_entry.le_next @@ -182,9 +239,6 @@ struct nd_prefix { #define ndpr_raf_onlink ndpr_flags.onlink #define ndpr_raf_auto ndpr_flags.autonomous -#define ndpr_statef_onlink ndpr_stateflags.onlink -#define ndpr_statef_addmark ndpr_stateflags.addmark - /* * We keep expired prefix for certain amount of time, for validation purposes. * 1800s = MaxRtrAdvInterval @@ -235,13 +289,23 @@ extern int nd6_umaxtries; extern int nd6_mmaxtries; extern int nd6_useloopback; extern int nd6_maxnudhint; +extern int nd6_gctimer; extern struct llinfo_nd6 llinfo_nd6; extern struct nd_ifinfo *nd_ifinfo; extern struct nd_drhead nd_defrouter; extern struct nd_prhead nd_prefix; +extern int nd6_debug; + +#define nd6log(x) do { if (nd6_debug) log x; } while (0) + +extern struct callout nd6_timer_ch; /* nd6_rtr.c */ extern int nd6_defifindex; +extern int ip6_desync_factor; /* seconds */ +extern u_int32_t ip6_temp_preferred_lifetime; /* seconds */ +extern u_int32_t ip6_temp_valid_lifetime; /* seconds */ +extern int ip6_temp_regen_advance; /* seconds */ union nd_opts { struct nd_opt_hdr *nd_opt_array[9]; /*max = home agent info*/ @@ -285,30 +349,30 @@ struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *)); void nd6_setmtu __P((struct ifnet *)); void nd6_timer __P((void *)); void nd6_purge __P((struct ifnet *)); -void nd6_free __P((struct rtentry *)); +struct llinfo_nd6 *nd6_free __P((struct rtentry *)); void nd6_nud_hint __P((struct rtentry *, struct in6_addr *, int)); int nd6_resolve __P((struct ifnet *, struct rtentry *, struct mbuf *, struct sockaddr *, u_char *)); void nd6_rtrequest __P((int, struct rtentry *, struct sockaddr *)); -void nd6_p2p_rtrequest __P((int, struct rtentry *, struct sockaddr *)); int nd6_ioctl __P((u_long, caddr_t, struct ifnet *)); struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *, char *, int, int, int)); -/* for test */ int nd6_output __P((struct ifnet *, struct ifnet *, struct mbuf *, struct sockaddr_in6 *, struct rtentry *)); int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, struct sockaddr *, u_char *)); +int nd6_need_cache __P((struct ifnet *)); /* nd6_nbr.c */ void nd6_na_input __P((struct mbuf *, int, int)); -void nd6_na_output __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, u_long, int, struct sockaddr *)); +void nd6_na_output __P((struct ifnet *, const struct in6_addr *, + const struct in6_addr *, u_long, int, struct sockaddr *)); void nd6_ns_input __P((struct mbuf *, int, int)); -void nd6_ns_output __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, struct llinfo_nd6 *, int)); +void nd6_ns_output __P((struct ifnet *, const struct in6_addr *, + const struct in6_addr *, struct llinfo_nd6 *, int)); caddr_t nd6_ifptomac __P((struct ifnet *)); void nd6_dad_start __P((struct ifaddr *, int *)); +void nd6_dad_stop __P((struct ifaddr *)); void nd6_dad_duplicated __P((struct ifaddr *)); /* nd6_rtr.c */ @@ -321,14 +385,19 @@ void defrouter_select __P((void)); void defrtrlist_del __P((struct nd_defrouter *)); void prelist_remove __P((struct nd_prefix *)); int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, - struct mbuf *)); + struct mbuf *)); +int nd6_prelist_add __P((struct nd_prefix *, struct nd_defrouter *, + struct nd_prefix **)); +int nd6_prefix_onlink __P((struct nd_prefix *)); +int nd6_prefix_offlink __P((struct nd_prefix *)); void pfxlist_onlink_check __P((void)); struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, struct ifnet *)); -int in6_ifdel __P((struct ifnet *, struct in6_addr *)); +struct nd_prefix *nd6_prefix_lookup __P((struct nd_prefix *)); int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr)); void rt6_flush __P((struct in6_addr *, struct ifnet *)); int nd6_setdefaultiface __P((int)); +int in6_tmpifadd __P((const struct in6_ifaddr *, int)); #endif /* _KERNEL */ diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index d3fa831fa695..7527d432ba83 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: nd6_nbr.c,v 1.37 2000/06/04 12:46:13 itojun Exp $ */ +/* $KAME: nd6_nbr.c,v 1.64 2001/05/17 03:48:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -45,6 +45,7 @@ #include <sys/errno.h> #include <sys/syslog.h> #include <sys/queue.h> +#include <sys/callout.h> #include <net/if.h> #include <net/if_types.h> @@ -72,6 +73,8 @@ struct dadq; static struct dadq *nd6_dad_find __P((struct ifaddr *)); +static void nd6_dad_starttimer __P((struct dadq *, int)); +static void nd6_dad_stoptimer __P((struct dadq *)); static void nd6_dad_timer __P((struct ifaddr *)); static void nd6_dad_ns_output __P((struct dadq *, struct ifaddr *)); static void nd6_dad_ns_input __P((struct ifaddr *)); @@ -106,10 +109,25 @@ nd6_ns_input(m, off, icmp6len) union nd_opts ndopts; struct sockaddr_dl *proxydl = NULL; +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); + if (nd_ns == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ + taddr6 = nd_ns->nd_ns_target; + if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { @@ -121,26 +139,14 @@ nd6_ns_input(m, off, icmp6len) && daddr6.s6_addr8[12] == 0xff) { ; /*good*/ } else { - log(LOG_INFO, "nd6_ns_input: bad DAD packet " - "(wrong ip6 dst)\n"); + nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " + "(wrong ip6 dst)\n")); goto bad; } } -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, icmp6len,); - nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); -#else - IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); - if (nd_ns == NULL) { - icmp6stat.icp6s_tooshort++; - return; - } -#endif - taddr6 = nd_ns->nd_ns_target; - if (IN6_IS_ADDR_MULTICAST(&taddr6)) { - log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"); + nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } @@ -150,8 +156,10 @@ nd6_ns_input(m, off, icmp6len) icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n"); - goto bad; + nd6log((LOG_INFO, + "nd6_ns_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ + goto freeit; } if (ndopts.nd_opts_src_lladdr) { @@ -160,8 +168,8 @@ nd6_ns_input(m, off, icmp6len) } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { - log(LOG_INFO, "nd6_ns_input: bad DAD packet " - "(link-layer address option)\n"); + nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " + "(link-layer address option)\n")); goto bad; } @@ -223,7 +231,7 @@ nd6_ns_input(m, off, icmp6len) } if (!ifa) { /* - * We've got a NS packet, and we don't have that adddress + * We've got an NS packet, and we don't have that adddress * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ @@ -236,10 +244,11 @@ nd6_ns_input(m, off, icmp6len) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", - ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { @@ -306,9 +315,10 @@ nd6_ns_input(m, off, icmp6len) return; bad: - log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)); - log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)); - log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)); + nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6))); + nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6))); + nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6))); + icmp6stat.icp6s_badns++; m_freem(m); } @@ -324,7 +334,7 @@ nd6_ns_input(m, off, icmp6len) void nd6_ns_output(ifp, daddr6, taddr6, ln, dad) struct ifnet *ifp; - struct in6_addr *daddr6, *taddr6; + const struct in6_addr *daddr6, *taddr6; struct llinfo_nd6 *ln; /* for source address determination */ int dad; /* duplicated address detection */ { @@ -362,6 +372,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) } if (m == NULL) return; + m->m_pkthdr.rcvif = NULL; if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -495,7 +506,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) #ifdef IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif); if (outif) { @@ -541,9 +552,11 @@ nd6_na_input(m, off, icmp6len) union nd_opts ndopts; if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } #ifndef PULLDOWN_TEST @@ -566,22 +579,24 @@ nd6_na_input(m, off, icmp6len) taddr6.s6_addr16[1] = htons(ifp->if_index); if (IN6_IS_ADDR_MULTICAST(&taddr6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "nd6_na_input: invalid target address %s\n", - ip6_sprintf(&taddr6)); - goto freeit; + ip6_sprintf(&taddr6))); + goto bad; } if (IN6_IS_ADDR_MULTICAST(&daddr6)) if (is_solicited) { - log(LOG_ERR, - "nd6_na_input: a solicited adv is multicasted\n"); - goto freeit; + nd6log((LOG_ERR, + "nd6_na_input: a solicited adv is multicasted\n")); + goto bad; } icmp6len -= sizeof(*nd_na); nd6_option_init(nd_na + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n"); + nd6log((LOG_INFO, + "nd6_na_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ goto freeit; } @@ -616,10 +631,11 @@ nd6_na_input(m, off, icmp6len) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s " "(if %d, NA packet %d)\n", - ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } /* @@ -649,10 +665,19 @@ nd6_na_input(m, off, icmp6len) ln->ln_byhint = 0; if (ln->ln_expire) ln->ln_expire = time_second + - nd_ifinfo[rt->rt_ifp->if_index].reachable; - } else + nd_ifinfo[rt->rt_ifp->if_index].reachable; + } else { ln->ln_state = ND6_LLINFO_STALE; - ln->ln_router = is_router; + ln->ln_expire = time_second + nd6_gctimer; + } + if ((ln->ln_router = is_router) != 0) { + /* + * This means a router's state has changed from + * non-reachable to probably reachable, and might + * affect the status of associated prefixes.. + */ + pfxlist_onlink_check(); + } } else { int llchange; @@ -695,8 +720,10 @@ nd6_na_input(m, off, icmp6len) * If state is REACHABLE, make it STALE. * no other updates should be done. */ - if (ln->ln_state == ND6_LLINFO_REACHABLE) + if (ln->ln_state == ND6_LLINFO_REACHABLE) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = time_second + nd6_gctimer; + } goto freeit; } else if (is_override /* (2a) */ || (!is_override && (lladdr && !llchange)) /* (2b) */ @@ -719,11 +746,13 @@ nd6_na_input(m, off, icmp6len) ln->ln_byhint = 0; if (ln->ln_expire) { ln->ln_expire = time_second + - nd_ifinfo[ifp->if_index].reachable; + nd_ifinfo[ifp->if_index].reachable; } } else { - if (lladdr && llchange) + if (lladdr && llchange) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = time_second + nd6_gctimer; + } } } @@ -759,21 +788,22 @@ nd6_na_input(m, off, icmp6len) rt->rt_flags &= ~RTF_REJECT; ln->ln_asked = 0; if (ln->ln_hold) { -#ifdef OLDIP6OUTPUT - (*ifp->if_output)(ifp, ln->ln_hold, rt_key(rt), rt); -#else /* * we assume ifp is not a p2p here, so just set the 2nd * argument as the 1st one. */ nd6_output(ifp, ifp, ln->ln_hold, (struct sockaddr_in6 *)rt_key(rt), rt); -#endif ln->ln_hold = 0; } freeit: m_freem(m); + return; + + bad: + icmp6stat.icp6s_badna++; + m_freem(m); } /* @@ -788,7 +818,7 @@ nd6_na_input(m, off, icmp6len) void nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) struct ifnet *ifp; - struct in6_addr *daddr6, *taddr6; + const struct in6_addr *daddr6, *taddr6; u_long flags; int tlladdr; /* 1 if include target link-layer address */ struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */ @@ -824,6 +854,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) } if (m == NULL) return; + m->m_pkthdr.rcvif = NULL; if (IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -917,7 +948,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) #ifdef IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif ip6_output(m, NULL, NULL, 0, &im6o, &outif); if (outif) { @@ -935,6 +966,10 @@ nd6_ifptomac(ifp) case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: + case IFT_IEEE1394: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif return ((caddr_t)(ifp + 1)); break; default: @@ -951,10 +986,11 @@ struct dadq { int dad_ns_ocount; /* NS sent so far */ int dad_ns_icount; int dad_na_icount; - struct callout_handle dad_timer; + struct callout dad_timer_ch; }; static struct dadq_head dadq; +static int dad_init = 0; static struct dadq * nd6_dad_find(ifa) @@ -969,6 +1005,24 @@ nd6_dad_find(ifa) return NULL; } +static void +nd6_dad_starttimer(dp, ticks) + struct dadq *dp; + int ticks; +{ + + callout_reset(&dp->dad_timer_ch, ticks, + (void (*) __P((void *)))nd6_dad_timer, (void *)dp->dad_ifa); +} + +static void +nd6_dad_stoptimer(dp) + struct dadq *dp; +{ + + callout_stop(&dp->dad_timer_ch); +} + /* * Start Duplicated Address Detection (DAD) for specified interface address. */ @@ -979,7 +1033,6 @@ nd6_dad_start(ifa, tick) { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; - static int dad_init = 0; if (!dad_init) { TAILQ_INIT(&dadq); @@ -1026,12 +1079,11 @@ nd6_dad_start(ifa, tick) return; } bzero(dp, sizeof(*dp)); + callout_init(&dp->dad_timer_ch, 0); TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); -#ifdef ND6_DEBUG - log(LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr)); -#endif + nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), + ip6_sprintf(&ia->ia_addr.sin6_addr))); /* * Send NS packet for DAD, ip6_dad_count times. @@ -1040,15 +1092,14 @@ nd6_dad_start(ifa, tick) * (re)initialization. */ dp->dad_ifa = ifa; - ifa->ifa_refcnt++; /*just for safety*/ + IFAREF(ifa); /*just for safety*/ dp->dad_count = ip6_dad_count; dp->dad_ns_icount = dp->dad_na_icount = 0; dp->dad_ns_ocount = dp->dad_ns_tcount = 0; if (!tick) { nd6_dad_ns_output(dp, ifa); - dp->dad_timer = - timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, - nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); + nd6_dad_starttimer(dp, + nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); } else { int ntick; @@ -1057,12 +1108,35 @@ nd6_dad_start(ifa, tick) else ntick = *tick + random() % (hz / 2); *tick = ntick; - dp->dad_timer = - timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, - ntick); + nd6_dad_starttimer(dp, ntick); } } +/* + * terminate DAD unconditionally. used for address removals. + */ +void +nd6_dad_stop(ifa) + struct ifaddr *ifa; +{ + struct dadq *dp; + + if (!dad_init) + return; + dp = nd6_dad_find(ifa); + if (!dp) { + /* DAD wasn't started yet */ + return; + } + + nd6_dad_stoptimer(dp); + + TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + free(dp, M_IP6NDP); + dp = NULL; + IFAFREE(ifa); +} + static void nd6_dad_timer(ifa) struct ifaddr *ifa; @@ -1100,8 +1174,8 @@ nd6_dad_timer(ifa) /* timeouted with IFF_{RUNNING,UP} check */ if (dp->dad_ns_tcount > dad_maxtry) { - log(LOG_ERR, "%s: could not run DAD, driver problem?\n", - if_name(ifa->ifa_ifp)); + nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n", + if_name(ifa->ifa_ifp))); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); free(dp, M_IP6NDP); @@ -1116,9 +1190,8 @@ nd6_dad_timer(ifa) * We have more NS to go. Send NS packet for DAD. */ nd6_dad_ns_output(dp, ifa); - dp->dad_timer = - timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, - nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); + nd6_dad_starttimer(dp, + nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); } else { /* * We have transmitted sufficient number of DAD packets. @@ -1177,12 +1250,10 @@ nd6_dad_timer(ifa) */ ia->ia6_flags &= ~IN6_IFF_TENTATIVE; -#ifdef ND6_DEBUG - log(LOG_INFO, + nd6log((LOG_DEBUG, "%s: DAD complete for %s - no duplicates found\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr)); -#endif + ip6_sprintf(&ia->ia_addr.sin6_addr))); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); free(dp, M_IP6NDP); @@ -1208,18 +1279,16 @@ nd6_dad_duplicated(ifa) return; } - log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, " - "%d NA\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr), - dp->dad_ns_icount, dp->dad_na_icount); + log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " + "NS in/out=%d/%d, NA in=%d\n", + if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr), + dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount); ia->ia6_flags &= ~IN6_IFF_TENTATIVE; ia->ia6_flags |= IN6_IFF_DUPLICATED; /* We are done with DAD, with duplicated address found. (failure) */ - untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa - , dp->dad_timer - ); + nd6_dad_stoptimer(dp); log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); @@ -1264,7 +1333,7 @@ nd6_dad_ns_input(ifa) { struct in6_ifaddr *ia; struct ifnet *ifp; - struct in6_addr *taddr6; + const struct in6_addr *taddr6; struct dadq *dp; int duplicate; @@ -1277,17 +1346,12 @@ nd6_dad_ns_input(ifa) duplicate = 0; dp = nd6_dad_find(ifa); - /* - * If it is from myself, ignore this. - */ - if (ifp && (ifp->if_flags & IFF_LOOPBACK)) - return; - /* Quickhack - completely ignore DAD NS packets */ if (dad_ignore_ns) { - log(LOG_INFO, "nd6_dad_ns_input: ignoring DAD NS packet for " + nd6log((LOG_INFO, + "nd6_dad_ns_input: ignoring DAD NS packet for " "address %s(%s)\n", ip6_sprintf(taddr6), - if_name(ifa->ifa_ifp)); + if_name(ifa->ifa_ifp))); return; } diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 258a59c144df..715ccf09785f 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: nd6_rtr.c,v 1.47 2000/08/08 08:58:42 jinmei Exp $ */ +/* $KAME: nd6_rtr.c,v 1.111 2001/04/27 01:37:15 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -40,8 +40,10 @@ #include <sys/socket.h> #include <sys/sockio.h> #include <sys/time.h> +#include <sys/kernel.h> #include <sys/errno.h> #include <sys/syslog.h> +#include <sys/queue.h> #include <net/if.h> #include <net/if_types.h> @@ -51,6 +53,7 @@ #include <netinet/in.h> #include <netinet6/in6_var.h> +#include <netinet6/in6_ifattach.h> #include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> @@ -62,34 +65,39 @@ #define SDL(s) ((struct sockaddr_dl *)s) static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *)); -static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *)); -static struct nd_prefix *prefix_lookup __P((struct nd_prefix *)); -static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, int)); +static struct in6_ifaddr *in6_ifadd __P((struct nd_prefix *, + struct in6_addr *)); static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, - struct nd_defrouter *)); + struct nd_defrouter *)); static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *)); static void pfxrtr_del __P((struct nd_pfxrouter *)); static struct nd_pfxrouter *find_pfxlist_reachable_router - __P((struct nd_prefix *)); -static void nd6_detach_prefix __P((struct nd_prefix *)); -static void nd6_attach_prefix __P((struct nd_prefix *)); + __P((struct nd_prefix *)); static void defrouter_addifreq __P((struct ifnet *)); -#ifdef ND6_USE_RTSOCK -static void defrouter_msg __P((int, struct rtentry *)); -#endif +static void nd6_rtmsg __P((int, struct rtentry *)); static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, - struct in6_addrlifetime *lt6, - int update_vltime)); + struct in6_addrlifetime *lt6)); static int rt6_deleteroute __P((struct radix_node *, void *)); extern int nd6_recalc_reachtm_interval; -struct ifnet *nd6_defifp; +static struct ifnet *nd6_defifp; int nd6_defifindex; +int ip6_use_tempaddr = 0; + +int ip6_desync_factor; +u_int32_t ip6_temp_preferred_lifetime = DEF_TEMP_PREFERRED_LIFETIME; +u_int32_t ip6_temp_valid_lifetime = DEF_TEMP_VALID_LIFETIME; +/* + * shorter lifetimes for debugging purposes. +int ip6_temp_preferred_lifetime = 800; +static int ip6_temp_valid_lifetime = 1800; +*/ +int ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE; + /* * Receive Router Solicitation Message - just for routers. * Router solicitation/advertisement is mostly managed by userland program @@ -125,9 +133,11 @@ nd6_rs_input(m, off, icmp6len) /* Sanity checks */ if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_rs_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } /* @@ -151,7 +161,9 @@ nd6_rs_input(m, off, icmp6len) icmp6len -= sizeof(*nd_rs); nd6_option_init(nd_rs + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n"); + nd6log((LOG_INFO, + "nd6_rs_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ goto freeit; } @@ -161,16 +173,22 @@ nd6_rs_input(m, off, icmp6len) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_rs_input: lladdrlen mismatch for %s " "(if %d, RS packet %d)\n", - ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); freeit: m_freem(m); + return; + + bad: + icmp6stat.icp6s_badrs++; + m_freem(m); } /* @@ -203,16 +221,18 @@ nd6_ra_input(m, off, icmp6len) goto freeit; if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_ra_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "nd6_ra_input: src %s is not link-local\n", - ip6_sprintf(&saddr6)); - goto freeit; + ip6_sprintf(&saddr6))); + goto bad; } #ifndef PULLDOWN_TEST @@ -229,7 +249,9 @@ nd6_ra_input(m, off, icmp6len) icmp6len -= sizeof(*nd_ra); nd6_option_init(nd_ra + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n"); + nd6log((LOG_INFO, + "nd6_ra_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ goto freeit; } @@ -267,7 +289,7 @@ nd6_ra_input(m, off, icmp6len) */ if (ndopts.nd_opts_pi) { struct nd_opt_hdr *pt; - struct nd_opt_prefix_info *pi; + struct nd_opt_prefix_info *pi = NULL; struct nd_prefix pr; for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; @@ -279,34 +301,38 @@ nd6_ra_input(m, off, icmp6len) pi = (struct nd_opt_prefix_info *)pt; if (pi->nd_opt_pi_len != 4) { - log(LOG_INFO, "nd6_ra_input: invalid option " - "len %d for prefix information option, " - "ignored\n", pi->nd_opt_pi_len); + nd6log((LOG_INFO, + "nd6_ra_input: invalid option " + "len %d for prefix information option, " + "ignored\n", pi->nd_opt_pi_len)); continue; } if (128 < pi->nd_opt_pi_prefix_len) { - log(LOG_INFO, "nd6_ra_input: invalid prefix " - "len %d for prefix information option, " - "ignored\n", pi->nd_opt_pi_prefix_len); + nd6log((LOG_INFO, + "nd6_ra_input: invalid prefix " + "len %d for prefix information option, " + "ignored\n", pi->nd_opt_pi_prefix_len)); continue; } if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { - log(LOG_INFO, "nd6_ra_input: invalid prefix " - "%s, ignored\n", - ip6_sprintf(&pi->nd_opt_pi_prefix)); + nd6log((LOG_INFO, + "nd6_ra_input: invalid prefix " + "%s, ignored\n", + ip6_sprintf(&pi->nd_opt_pi_prefix))); continue; } /* aggregatable unicast address, rfc2374 */ if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20 && pi->nd_opt_pi_prefix_len != 64) { - log(LOG_INFO, "nd6_ra_input: invalid prefixlen " - "%d for rfc2374 prefix %s, ignored\n", - pi->nd_opt_pi_prefix_len, - ip6_sprintf(&pi->nd_opt_pi_prefix)); + nd6log((LOG_INFO, + "nd6_ra_input: invalid prefixlen " + "%d for rfc2374 prefix %s, ignored\n", + pi->nd_opt_pi_prefix_len, + ip6_sprintf(&pi->nd_opt_pi_prefix))); continue; } @@ -340,9 +366,9 @@ nd6_ra_input(m, off, icmp6len) /* lower bound */ if (mtu < IPV6_MMTU) { - log(LOG_INFO, "nd6_ra_input: bogus mtu option " + nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option " "mtu=%d sent from %s, ignoring\n", - mtu, ip6_sprintf(&ip6->ip6_src)); + mtu, ip6_sprintf(&ip6->ip6_src))); goto skip; } @@ -355,17 +381,17 @@ nd6_ra_input(m, off, icmp6len) if (change) /* in6_maxmtu may change */ in6_setmaxmtu(); } else { - log(LOG_INFO, "nd6_ra_input: bogus mtu " + nd6log((LOG_INFO, "nd6_ra_input: bogus mtu " "mtu=%d sent from %s; " "exceeds maxmtu %d, ignoring\n", mtu, ip6_sprintf(&ip6->ip6_src), - ndi->maxmtu); + ndi->maxmtu)); } } else { - log(LOG_INFO, "nd6_ra_input: mtu option " + nd6log((LOG_INFO, "nd6_ra_input: mtu option " "mtu=%d sent from %s; maxmtu unknown, " "ignoring\n", - mtu, ip6_sprintf(&ip6->ip6_src)); + mtu, ip6_sprintf(&ip6->ip6_src))); } } @@ -384,10 +410,11 @@ nd6_ra_input(m, off, icmp6len) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_ra_input: lladdrlen mismatch for %s " "(if %d, RA packet %d)\n", - ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); @@ -400,7 +427,12 @@ nd6_ra_input(m, off, icmp6len) pfxlist_onlink_check(); } -freeit: + freeit: + m_freem(m); + return; + + bad: + icmp6stat.icp6s_badra++; m_freem(m); } @@ -408,10 +440,9 @@ freeit: * default router list proccessing sub routines */ -#ifdef ND6_USE_RTSOCK /* tell the change to user processes watching the routing socket. */ static void -defrouter_msg(cmd, rt) +nd6_rtmsg(cmd, rt) int cmd; struct rtentry *rt; { @@ -421,10 +452,12 @@ defrouter_msg(cmd, rt) info.rti_info[RTAX_DST] = rt_key(rt); info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_info[RTAX_NETMASK] = rt_mask(rt); + info.rti_info[RTAX_IFP] = + (struct sockaddr *)TAILQ_FIRST(&rt->rt_ifp->if_addrlist); + info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; rt_missmsg(cmd, &info, rt->rt_flags, 0); } -#endif void defrouter_addreq(new) @@ -448,9 +481,7 @@ defrouter_addreq(new) (struct sockaddr *)&gate, (struct sockaddr *)&mask, RTF_GATEWAY, &newrt); if (newrt) { -#ifdef ND6_USE_RTSOCK - defrouter_msg(RTM_ADD, newrt); /* tell user process */ -#endif + nd6_rtmsg(RTM_ADD, newrt); /* tell user process */ newrt->rt_refcnt--; } splx(s); @@ -478,31 +509,27 @@ defrouter_addifreq(ifp) * XXX: An IPv6 address are required to be assigned on the interface. */ if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) { - log(LOG_ERR, /* better error? */ + nd6log((LOG_ERR, /* better error? */ "defrouter_addifreq: failed to find an ifaddr " "to install a route to interface %s\n", - if_name(ifp)); + if_name(ifp))); return; } flags = ifa->ifa_flags; - if ((ifp->if_flags & IFF_POINTOPOINT) != 0) - flags &= ~RTF_CLONING; - if ((error = rtrequest(RTM_ADD, (struct sockaddr *)&def, - ifa->ifa_addr, (struct sockaddr *)&mask, - flags, &newrt)) != 0) { - log(LOG_ERR, + error = rtrequest(RTM_ADD, (struct sockaddr *)&def, ifa->ifa_addr, + (struct sockaddr *)&mask, flags, &newrt); + if (error != 0) { + nd6log((LOG_ERR, "defrouter_addifreq: failed to install a route to " "interface %s (errno = %d)\n", - if_name(ifp), error); + if_name(ifp), error)); if (newrt) /* maybe unnecessary, but do it for safety */ newrt->rt_refcnt--; } else { if (newrt) { -#ifdef ND6_USE_RTSOCK - defrouter_msg(RTM_ADD, newrt); -#endif + nd6_rtmsg(RTM_ADD, newrt); newrt->rt_refcnt--; } } @@ -546,9 +573,7 @@ defrouter_delreq(dr, dofree) (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt); if (oldrt) { -#ifdef ND6_USE_RTSOCK - defrouter_msg(RTM_DELETE, oldrt); -#endif + nd6_rtmsg(RTM_DELETE, oldrt); if (oldrt->rt_refcnt <= 0) { /* * XXX: borrowed from the RTM_DELETE case of @@ -669,15 +694,17 @@ defrouter_select() /* * Install a route to the default interface * as default route. + * XXX: we enable this for host only, because + * this may override a default route installed + * a user process (e.g. routing daemon) in a + * router case. */ defrouter_addifreq(nd6_defifp); - } -#ifdef ND6_DEBUG - else /* noisy log? */ - log(LOG_INFO, "defrouter_select: " + } else { + nd6log((LOG_INFO, "defrouter_select: " "there's no default router and no default" - " interface\n"); -#endif + " interface\n")); + } } } @@ -775,8 +802,8 @@ pfxrtr_del(pfr) free(pfr, M_IP6NDP); } -static struct nd_prefix * -prefix_lookup(pr) +struct nd_prefix * +nd6_prefix_lookup(pr) struct nd_prefix *pr; { struct nd_prefix *search; @@ -795,12 +822,12 @@ prefix_lookup(pr) return(search); } -static int -prelist_add(pr, dr) - struct nd_prefix *pr; +int +nd6_prelist_add(pr, dr, newp) + struct nd_prefix *pr, **newp; struct nd_defrouter *dr; { - struct nd_prefix *new; + struct nd_prefix *new = NULL; int i, s; new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); @@ -808,9 +835,10 @@ prelist_add(pr, dr) return ENOMEM; bzero(new, sizeof(*new)); *new = *pr; + if (newp != NULL) + *newp = new; /* initilization */ - new->ndpr_statef_onlink = pr->ndpr_statef_onlink; LIST_INIT(&new->ndpr_advrtrs); in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); /* make prefix in the canonical form */ @@ -818,13 +846,24 @@ prelist_add(pr, dr) new->ndpr_prefix.sin6_addr.s6_addr32[i] &= new->ndpr_mask.s6_addr32[i]; - /* xxx ND_OPT_PI_FLAG_ONLINK processing */ - s = splnet(); /* link ndpr_entry to nd_prefix list */ LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); splx(s); + /* ND_OPT_PI_FLAG_ONLINK processing */ + if (new->ndpr_raf_onlink) { + int e; + + if ((e = nd6_prefix_onlink(new)) != 0) { + nd6log((LOG_ERR, "nd6_prelist_add: failed to make " + "the prefix %s/%d on-link on %s (errno=%d)\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); + /* proceed anyway. XXX: is it correct? */ + } + } + if (dr) { pfxrtr_add(new, dr); } @@ -837,12 +876,35 @@ prelist_remove(pr) struct nd_prefix *pr; { struct nd_pfxrouter *pfr, *next; - int s; + int e, s; + + /* make sure to invalidate the prefix until it is really freed. */ + pr->ndpr_vltime = 0; + pr->ndpr_pltime = 0; +#if 0 + /* + * Though these flags are now meaningless, we'd rather keep the value + * not to confuse users when executing "ndp -p". + */ + pr->ndpr_raf_onlink = 0; + pr->ndpr_raf_auto = 0; +#endif + if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 && + (e = nd6_prefix_offlink(pr)) != 0) { + nd6log((LOG_ERR, "prelist_remove: failed to make %s/%d offlink " + "on %s, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); + /* what should we do? */ + } + + if (pr->ndpr_refcnt > 0) + return; /* notice here? */ s = splnet(); + /* unlink ndpr_entry from nd_prefix list */ LIST_REMOVE(pr, ndpr_entry); - splx(s); /* free list of routers that adversed the prefix */ for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { @@ -850,30 +912,28 @@ prelist_remove(pr) free(pfr, M_IP6NDP); } + splx(s); + free(pr, M_IP6NDP); pfxlist_onlink_check(); } -/* - * NOTE: We set address lifetime to keep - * address lifetime <= prefix lifetime - * invariant. This is to simplify on-link determination code. - * If onlink determination is udated, this routine may have to be updated too. - */ int prelist_update(new, dr, m) struct nd_prefix *new; struct nd_defrouter *dr; /* may be NULL */ struct mbuf *m; { - struct in6_ifaddr *ia6 = NULL; + struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL; + struct ifaddr *ifa; + struct ifnet *ifp = new->ndpr_ifp; struct nd_prefix *pr; int s = splnet(); int error = 0; + int newprefix = 0; int auth; - struct in6_addrlifetime *lt6; - u_char onlink; /* Mobile IPv6 */ + struct in6_addrlifetime lt6_tmp; auth = 0; if (m) { @@ -887,170 +947,259 @@ prelist_update(new, dr, m) #endif } - if ((pr = prefix_lookup(new)) != NULL) { - if (pr->ndpr_ifp != new->ndpr_ifp) { - error = EADDRNOTAVAIL; - goto end; - } - - /* update prefix information */ - pr->ndpr_flags = new->ndpr_flags; - pr->ndpr_vltime = new->ndpr_vltime; - pr->ndpr_pltime = new->ndpr_pltime; - pr->ndpr_preferred = new->ndpr_preferred; - pr->ndpr_expire = new->ndpr_expire; + if ((pr = nd6_prefix_lookup(new)) != NULL) { /* - * RFC 2462 5.5.3 (d) or (e) - * We got a prefix which we have seen in the past. + * nd6_prefix_lookup() ensures that pr and new have the same + * prefix on a same interface. */ - if (!new->ndpr_raf_auto) - goto noautoconf1; - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + /* + * Update prefix information. Note that the on-link (L) bit + * and the autonomous (A) bit should NOT be changed from 1 + * to 0. + */ + if (new->ndpr_raf_onlink == 1) + pr->ndpr_raf_onlink = 1; + if (new->ndpr_raf_auto == 1) + pr->ndpr_raf_auto = 1; + if (new->ndpr_raf_onlink) { + pr->ndpr_vltime = new->ndpr_vltime; + pr->ndpr_pltime = new->ndpr_pltime; + pr->ndpr_preferred = new->ndpr_preferred; + pr->ndpr_expire = new->ndpr_expire; + } - if (ia6 == NULL) { - /* - * Special case: - * (1) We have seen the prefix advertised before, but - * we have never performed autoconfig for this prefix. - * This is because Autonomous bit was 0 previously, or - * autoconfig failed due to some other reasons. - * (2) We have seen the prefix advertised before and - * we have performed autoconfig in the past, but - * we seem to have no interface address right now. - * This is because the interface address have expired. - * - * This prefix is fresh, with respect to autoconfig - * process. - * - * Add an address based on RFC 2462 5.5.3 (d). - */ - ia6 = in6_ifadd(pr->ndpr_ifp, - &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr, - new->ndpr_plen); - if (!ia6) { - error = EADDRNOTAVAIL; - log(LOG_ERR, "prelist_update: failed to add a " - "new address\n"); - goto noautoconf1; + if (new->ndpr_raf_onlink && + (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { + int e; + + if ((e = nd6_prefix_onlink(pr)) != 0) { + nd6log((LOG_ERR, + "prelist_update: failed to make " + "the prefix %s/%d on-link on %s " + "(errno=%d)\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); + /* proceed anyway. XXX: is it correct? */ } + } - lt6 = &ia6->ia6_lifetime; + if (dr && pfxrtr_lookup(pr, dr) == NULL) + pfxrtr_add(pr, dr); + } else { + struct nd_prefix *newpr = NULL; - /* address lifetime <= prefix lifetime */ - lt6->ia6t_vltime = new->ndpr_vltime; - lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6, 1); - } else { -#define TWOHOUR (120*60) - /* - * We have seen the prefix before, and we have added - * interface address in the past. We still have - * the interface address assigned. - * - * update address lifetime based on RFC 2462 - * 5.5.3 (e). - */ - int update = 0; - - lt6 = &ia6->ia6_lifetime; - -#if 0 /* RFC 2462 5.5.3 (e) */ - lt6->ia6t_pltime = new->ndpr_pltime; - if (TWOHOUR < new->ndpr_vltime - || lt6pr->nd < new->ndpr_vltime) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } else if (auth - && lt6->ia6t_vltime <= TWOHOUR0 - && new->ndpr_vltime <= lt6->ia6t_vltime) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } else { - lt6->ia6t_vltime = TWOHOUR; - update++; - } + newprefix = 1; - /* 2 hour rule is not imposed for pref lifetime */ - new->ndpr_apltime = new->ndpr_pltime; - lt6->ia6t_pltime = new->ndpr_pltime; -#else /* update from Jim Bound, (ipng 6712) */ - if (TWOHOUR < new->ndpr_vltime - || lt6->ia6t_vltime < new->ndpr_vltime) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } else if (auth) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } + if (new->ndpr_vltime == 0) + goto end; + if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0) + goto end; - /* jim bound rule is not imposed for pref lifetime */ - lt6->ia6t_pltime = new->ndpr_pltime; -#endif - in6_init_address_ltimes(new, lt6, update); + bzero(&new->ndpr_addr, sizeof(struct in6_addr)); + + error = nd6_prelist_add(new, dr, &newpr); + if (error != 0 || newpr == NULL) { + nd6log((LOG_NOTICE, "prelist_update: " + "nd6_prelist_add failed for %s/%d on %s " + "errno=%d, returnpr=%p\n", + ip6_sprintf(&new->ndpr_prefix.sin6_addr), + new->ndpr_plen, if_name(new->ndpr_ifp), + error, newpr)); + goto end; /* we should just give up in this case. */ } - noautoconf1: + /* + * XXX: from the ND point of view, we can ignore a prefix + * with the on-link bit being zero. However, we need a + * prefix structure for references from autoconfigured + * addresses. Thus, we explicitly make suret that the prefix + * itself expires now. + */ + if (newpr->ndpr_raf_onlink == 0) { + newpr->ndpr_vltime = 0; + newpr->ndpr_pltime = 0; + in6_init_prefix_ltimes(newpr); + } -#if 0 - /* address lifetime expire processing, RFC 2462 5.5.4. */ - if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) { - struct in6_ifaddr *ia6; + pr = newpr; + } - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) - ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; - } -#endif + /* + * Address autoconfiguration based on Section 5.5.3 of RFC 2462. + * Note that pr must be non NULL at this point. + */ - onlink = pr->ndpr_statef_onlink; /* Mobile IPv6 */ + /* 5.5.3 (a). Ignore the prefix without the A bit set. */ + if (!new->ndpr_raf_auto) + goto afteraddrconf; - if (dr && pfxrtr_lookup(pr, dr) == NULL) - pfxrtr_add(pr, dr); + /* + * 5.5.3 (b). the link-local prefix should have been ignored in + * nd6_ra_input. + */ - } else { - int error_tmp; + /* + * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. + * This should have been done in nd6_ra_input. + */ - if (new->ndpr_vltime == 0) goto end; + /* + * 5.5.3 (d). If the prefix advertised does not match the prefix of an + * address already in the list, and the Valid Lifetime is not 0, + * form an address. Note that even a manually configured address + * should reject autoconfiguration of a new address. + */ + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + struct in6_ifaddr *ifa6; + int ifa_plen; + u_int32_t storedlifetime; - bzero(&new->ndpr_addr, sizeof(struct in6_addr)); + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + ifa6 = (struct in6_ifaddr *)ifa; + + /* + * Spec is not clear here, but I believe we should concentrate + * on unicast (i.e. not anycast) addresses. + * XXX: other ia6_flags? detached or duplicated? + */ + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0) + continue; + + ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL); + if (ifa_plen != new->ndpr_plen || + !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr, + &new->ndpr_prefix.sin6_addr, + ifa_plen)) + continue; + + if (ia6_match == NULL) /* remember the first one */ + ia6_match = ifa6; + + if ((ifa6->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; /* - * RFC 2462 5.5.3 (d) - * We got a fresh prefix. Perform some sanity checks - * and add an interface address by appending interface ID - * to the advertised prefix. + * An already autoconfigured address matched. Now that we + * are sure there is at least one matched address, we can + * proceed to 5.5.3. (e): update the lifetimes according to the + * "two hours" rule and the privacy extension. */ - if (!new->ndpr_raf_auto) - goto noautoconf2; - - ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr, - &new->ndpr_addr, new->ndpr_plen); - if (!ia6) { - error = EADDRNOTAVAIL; - log(LOG_ERR, "prelist_update: " - "failed to add a new address\n"); - goto noautoconf2; +#define TWOHOUR (120*60) + lt6_tmp = ifa6->ia6_lifetime; + + storedlifetime = IFA6_IS_INVALID(ifa6) ? 0 : + (lt6_tmp.ia6t_expire - time_second); + + if (TWOHOUR < new->ndpr_vltime || + storedlifetime < new->ndpr_vltime) { + lt6_tmp.ia6t_vltime = new->ndpr_vltime; + } else if (storedlifetime <= TWOHOUR +#if 0 + /* + * This condition is logically redundant, so we just + * omit it. + * See IPng 6712, 6717, and 6721. + */ + && new->ndpr_vltime <= storedlifetime +#endif + ) { + if (auth) { + lt6_tmp.ia6t_vltime = new->ndpr_vltime; + } + } else { + /* + * new->ndpr_vltime <= TWOHOUR && + * TWOHOUR < storedlifetime + */ + lt6_tmp.ia6t_vltime = TWOHOUR; } - /* set onlink bit if an interface route is configured */ - new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0; - lt6 = &ia6->ia6_lifetime; + /* The 2 hour rule is not imposed for preferred lifetime. */ + lt6_tmp.ia6t_pltime = new->ndpr_pltime; + + in6_init_address_ltimes(pr, <6_tmp); + + /* + * When adjusting the lifetimes of an existing temporary + * address, only lower the lifetimes. + * RFC 3041 3.3. (1). + * XXX: how should we modify ia6t_[pv]ltime? + */ + if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { + if (lt6_tmp.ia6t_expire == 0 || /* no expire */ + lt6_tmp.ia6t_expire > + ifa6->ia6_lifetime.ia6t_expire) { + lt6_tmp.ia6t_expire = + ifa6->ia6_lifetime.ia6t_expire; + } + if (lt6_tmp.ia6t_preferred == 0 || /* no expire */ + lt6_tmp.ia6t_preferred > + ifa6->ia6_lifetime.ia6t_preferred) { + lt6_tmp.ia6t_preferred = + ifa6->ia6_lifetime.ia6t_preferred; + } + } + + ifa6->ia6_lifetime = lt6_tmp; + } + if (ia6_match == NULL && new->ndpr_vltime) { + /* + * No address matched and the valid lifetime is non-zero. + * Create a new address. + */ + if ((ia6 = in6_ifadd(new, NULL)) != NULL) { + /* + * note that we should use pr (not new) for reference. + */ + pr->ndpr_refcnt++; + ia6->ia6_ndpr = pr; + +#if 0 + /* XXXYYY Don't do this, according to Jinmei. */ + pr->ndpr_addr = new->ndpr_addr; +#endif - /* address lifetime <= prefix lifetime */ - lt6->ia6t_vltime = new->ndpr_vltime; - lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6, 1); + /* + * RFC 3041 3.3 (2). + * When a new public address is created as described + * in RFC2462, also create a new temporary address. + * + * RFC 3041 3.5. + * When an interface connects to a new link, a new + * randomized interface identifier should be generated + * immediately together with a new set of temporary + * addresses. Thus, we specifiy 1 as the 2nd arg of + * in6_tmpifadd(). + */ + if (ip6_use_tempaddr) { + int e; + if ((e = in6_tmpifadd(ia6, 1)) != 0) { + nd6log((LOG_NOTICE, "prelist_update: " + "failed to create a temporary " + "address, errno=%d\n", + e)); + } + } - noautoconf2: - error_tmp = prelist_add(new, dr); - error = error_tmp ? error_tmp : error; + /* + * A newly added address might affect the status + * of other addresses, so we check and update it. + * XXX: what if address duplication happens? + */ + pfxlist_onlink_check(); + } else { + /* just set an error. do not bark here. */ + error = EADDRNOTAVAIL; /* XXX: might be unused. */ + } } + afteraddrconf: + end: splx(s); return error; @@ -1061,7 +1210,7 @@ prelist_update(new, dr, m) * detect if a given prefix has a (probably) reachable advertising router. * XXX: lengthy function name... */ -struct nd_pfxrouter * +static struct nd_pfxrouter * find_pfxlist_reachable_router(pr) struct nd_prefix *pr; { @@ -1084,14 +1233,14 @@ find_pfxlist_reachable_router(pr) /* * Check if each prefix in the prefix list has at least one available router - * that advertised the prefix (A router is "available" if its neighbor cache - * entry has reachable or probably reachable). + * that advertised the prefix (a router is "available" if its neighbor cache + * entry is reachable or probably reachable). * If the check fails, the prefix may be off-link, because, for example, * we have moved from the network but the lifetime of the prefix has not - * been expired yet. So we should not use the prefix if there is another - * prefix that has an available router. - * But if there is no prefix that has an available router, we still regards - * all the prefixes as on-link. This is because we can't tell if all the + * expired yet. So we should not use the prefix if there is another prefix + * that has an available router. + * But, if there is no prefix that has an available router, we still regards + * all the prefixes as on-link. This is because we can't tell if all the * routers are simply dead or if we really moved from the network and there * is no router around us. */ @@ -1099,55 +1248,275 @@ void pfxlist_onlink_check() { struct nd_prefix *pr; + struct in6_ifaddr *ifa; /* * Check if there is a prefix that has a reachable advertising * router. */ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { - if (find_pfxlist_reachable_router(pr)) + if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr)) break; } if (pr) { /* * There is at least one prefix that has a reachable router. - * First, detach prefixes which has no reachable advertising - * router and then attach other prefixes. - * The order is important since an attached prefix and a - * detached prefix may have a same interface route. + * Detach prefixes which have no reachable advertising + * router, and attach other prefixes. */ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { - if (find_pfxlist_reachable_router(pr) == NULL && - pr->ndpr_statef_onlink) { - pr->ndpr_statef_onlink = 0; - nd6_detach_prefix(pr); - } + /* XXX: a link-local prefix should never be detached */ + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + /* + * we aren't interested in prefixes without the L bit + * set. + */ + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && + find_pfxlist_reachable_router(pr) == NULL) + pr->ndpr_stateflags |= NDPRF_DETACHED; + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && + find_pfxlist_reachable_router(pr) != 0) + pr->ndpr_stateflags &= ~NDPRF_DETACHED; } + } else { + /* there is no prefix that has a reachable router */ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { - if (find_pfxlist_reachable_router(pr) && - pr->ndpr_statef_onlink == 0) - nd6_attach_prefix(pr); + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0) + pr->ndpr_stateflags &= ~NDPRF_DETACHED; + } + } + + /* + * Remove each interface route associated with a (just) detached + * prefix, and reinstall the interface route for a (just) attached + * prefix. Note that all attempt of reinstallation does not + * necessarily success, when a same prefix is shared among multiple + * interfaces. Such cases will be handled in nd6_prefix_onlink, + * so we don't have to care about them. + */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + int e; + + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && + (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { + if ((e = nd6_prefix_offlink(pr)) != 0) { + nd6log((LOG_ERR, + "pfxlist_onlink_check: failed to " + "make %s/%d offlink, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, e)); + } + } + if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && + (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 && + pr->ndpr_raf_onlink) { + if ((e = nd6_prefix_onlink(pr)) != 0) { + nd6log((LOG_ERR, + "pfxlist_onlink_check: failed to " + "make %s/%d offlink, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, e)); + } + } + } + + /* + * Changes on the prefix status might affect address status as well. + * Make sure that all addresses derived from an attached prefix are + * attached, and that all addresses derived from a detached prefix are + * detached. Note, however, that a manually configured address should + * always be attached. + * The precise detection logic is same as the one for prefixes. + */ + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if (ifa->ia6_ndpr == NULL) { + /* + * This can happen when we first configure the address + * (i.e. the address exists, but the prefix does not). + * XXX: complicated relationships... + */ + continue; + } + + if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) + break; + } + if (ifa) { + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if (ifa->ia6_ndpr == NULL) /* XXX: see above. */ + continue; + + if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) + ifa->ia6_flags &= ~IN6_IFF_DETACHED; + else + ifa->ia6_flags |= IN6_IFF_DETACHED; } } else { - /* there is no prefix that has a reachable router */ - for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) - if (pr->ndpr_statef_onlink == 0) - nd6_attach_prefix(pr); + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + ifa->ia6_flags &= ~IN6_IFF_DETACHED; + } } } -static void -nd6_detach_prefix(pr) +int +nd6_prefix_onlink(pr) struct nd_prefix *pr; { - struct in6_ifaddr *ia6; - struct sockaddr_in6 sa6, mask6; + struct ifaddr *ifa; + struct ifnet *ifp = pr->ndpr_ifp; + struct sockaddr_in6 mask6; + struct nd_prefix *opr; + u_long rtflags; + int error = 0; + struct rtentry *rt = NULL; + + /* sanity check */ + if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { + nd6log((LOG_ERR, + "nd6_prefix_onlink: %s/%d is already on-link\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen); + return(EEXIST)); + } /* - * Delete the interface route associated with the prefix. + * Add the interface route associated with the prefix. Before + * installing the route, check if there's the same prefix on another + * interface, and the prefix has already installed the interface route. + * Although such a configuration is expected to be rare, we explicitly + * allow it. */ + for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { + if (opr == pr) + continue; + + if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) + continue; + + if (opr->ndpr_plen == pr->ndpr_plen && + in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, + &opr->ndpr_prefix.sin6_addr, + pr->ndpr_plen)) + return(0); + } + + /* + * We prefer link-local addresses as the associated interface address. + */ + /* search for a link-local addr */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY| + IN6_IFF_ANYCAST); + if (ifa == NULL) { + /* XXX: freebsd does not have ifa_ifwithaf */ + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + if (ifa->ifa_addr->sa_family == AF_INET6) + break; + } + /* should we care about ia6_flags? */ + } + if (ifa == NULL) { + /* + * This can still happen, when, for example, we receive an RA + * containing a prefix with the L bit set and the A bit clear, + * after removing all IPv6 addresses on the receiving + * interface. This should, of course, be rare though. + */ + nd6log((LOG_NOTICE, + "nd6_prefix_onlink: failed to find any ifaddr" + " to add route for a prefix(%s/%d) on %s\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(ifp))); + return(0); + } + + /* + * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. + * ifa->ifa_rtrequest = nd6_rtrequest; + */ + bzero(&mask6, sizeof(mask6)); + mask6.sin6_len = sizeof(mask6); + mask6.sin6_addr = pr->ndpr_mask; + rtflags = ifa->ifa_flags | RTF_CLONING | RTF_UP; + if (nd6_need_cache(ifp)) { + /* explicitly set in case ifa_flags does not set the flag. */ + rtflags |= RTF_CLONING; + } else { + /* + * explicitly clear the cloning bit in case ifa_flags sets it. + */ + rtflags &= ~RTF_CLONING; + } + error = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, + ifa->ifa_addr, (struct sockaddr *)&mask6, + rtflags, &rt); + if (error == 0) { + if (rt != NULL) /* this should be non NULL, though */ + nd6_rtmsg(RTM_ADD, rt); + pr->ndpr_stateflags |= NDPRF_ONLINK; + } + else { + nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add route for a" + " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx " + "errno = %d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(ifp), + ip6_sprintf(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr), + ip6_sprintf(&mask6.sin6_addr), rtflags, error)); + } + + if (rt != NULL) + rt->rt_refcnt--; + + return(error); +} + +int +nd6_prefix_offlink(pr) + struct nd_prefix *pr; +{ + int error = 0; + struct ifnet *ifp = pr->ndpr_ifp; + struct nd_prefix *opr; + struct sockaddr_in6 sa6, mask6; + struct rtentry *rt = NULL; + + /* sanity check */ + if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { + nd6log((LOG_ERR, + "nd6_prefix_offlink: %s/%d is already off-link\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen)); + return(EEXIST); + } + bzero(&sa6, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); @@ -1157,103 +1526,113 @@ nd6_detach_prefix(pr) mask6.sin6_family = AF_INET6; mask6.sin6_len = sizeof(sa6); bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); - { - int e; + error = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, + (struct sockaddr *)&mask6, 0, &rt); + if (error == 0) { + pr->ndpr_stateflags &= ~NDPRF_ONLINK; - e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, - (struct sockaddr *)&mask6, 0, NULL); - if (e) { - log(LOG_ERR, - "nd6_detach_prefix: failed to delete route: " - "%s/%d (errno = %d)\n", - ip6_sprintf(&sa6.sin6_addr), - pr->ndpr_plen, - e); - } - } + /* report the route deletion to the routing socket. */ + if (rt != NULL) + nd6_rtmsg(RTM_DELETE, rt); - /* - * Mark the address derived from the prefix detached so that - * it won't be used as a source address for a new connection. - */ - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) - ia6->ia6_flags |= IN6_IFF_DETACHED; -} + /* + * There might be the same prefix on another interface, + * the prefix which could not be on-link just because we have + * the interface route (see comments in nd6_prefix_onlink). + * If there's one, try to make the prefix on-link on the + * interface. + */ + for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { + if (opr == pr) + continue; -static void -nd6_attach_prefix(pr) - struct nd_prefix *pr; -{ - struct ifaddr *ifa; - struct in6_ifaddr *ia6; + if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0) + continue; - /* - * Add the interface route associated with the prefix(if necessary) - * Should we consider if the L bit is set in pr->ndpr_flags? - */ - ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix, - pr->ndpr_ifp); - if (ifa == NULL) { - log(LOG_ERR, - "nd6_attach_prefix: failed to find any ifaddr" - " to add route for a prefix(%s/%d)\n", - ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen); + /* + * KAME specific: detached prefixes should not be + * on-link. + */ + if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0) + continue; + + if (opr->ndpr_plen == pr->ndpr_plen && + in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, + &opr->ndpr_prefix.sin6_addr, + pr->ndpr_plen)) { + int e; + + if ((e = nd6_prefix_onlink(opr)) != 0) { + nd6log((LOG_ERR, + "nd6_prefix_offlink: failed to " + "recover a prefix %s/%d from %s " + "to %s (errno = %d)\n", + ip6_sprintf(&opr->ndpr_prefix.sin6_addr), + opr->ndpr_plen, if_name(ifp), + if_name(opr->ndpr_ifp), e)); + } + } + } } else { - int e; - struct sockaddr_in6 mask6; - - bzero(&mask6, sizeof(mask6)); - mask6.sin6_family = AF_INET6; - mask6.sin6_len = sizeof(mask6); - mask6.sin6_addr = pr->ndpr_mask; - e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, - ifa->ifa_addr, (struct sockaddr *)&mask6, - ifa->ifa_flags, NULL); - if (e == 0) - pr->ndpr_statef_onlink = 1; - else { - log(LOG_ERR, - "nd6_attach_prefix: failed to add route for" - " a prefix(%s/%d), errno = %d\n", - ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e); - } + /* XXX: can we still set the NDPRF_ONLINK flag? */ + nd6log((LOG_ERR, + "nd6_prefix_offlink: failed to delete route: " + "%s/%d on %s (errno = %d)\n", + ip6_sprintf(&sa6.sin6_addr), pr->ndpr_plen, if_name(ifp), + error)); } - /* - * Now the address derived from the prefix can be used as a source - * for a new connection, so clear the detached flag. - */ - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) { - ia6->ia6_flags &= ~IN6_IFF_DETACHED; - if (pr->ndpr_statef_onlink) - ia6->ia_flags |= IFA_ROUTE; + if (rt != NULL) { + if (rt->rt_refcnt <= 0) { + /* XXX: we should free the entry ourselves. */ + rt->rt_refcnt++; + rtfree(rt); + } } + + return(error); } static struct in6_ifaddr * -in6_ifadd(ifp, in6, addr, prefixlen) - struct ifnet *ifp; - struct in6_addr *in6; - struct in6_addr *addr; - int prefixlen; /* prefix len of the new prefix in "in6" */ +in6_ifadd(pr, ifid) + struct nd_prefix *pr; + struct in6_addr *ifid; /* Mobile IPv6 addition */ { + struct ifnet *ifp = pr->ndpr_ifp; struct ifaddr *ifa; - struct in6_ifaddr *ia, *ib, *oia; - int s, error; + struct in6_aliasreq ifra; + struct in6_ifaddr *ia, *ib; + int error, plen0; struct in6_addr mask; + int prefixlen = pr->ndpr_plen; in6_len2mask(&mask, prefixlen); - /* find link-local address (will be interface ID) */ + /* + * find a link-local address (will be interface ID). + * Is it really mandatory? Theoretically, a global or a site-local + * address can be configured without a link-local address, if we + * have a unique interface identifier... + * + * it is not mandatory to have a link-local address, we can generate + * interface identifier on the fly. we do this because: + * (1) it should be the easiest way to find interface identifier. + * (2) RFC2462 5.4 suggesting the use of the same interface identifier + * for multiple addresses on a single interface, and possible shortcut + * of DAD. we omitted DAD for this reason in the past. + * (3) a user can prevent autoconfiguration of global address + * by removing link-local address by hand (this is partly because we + * don't have other way to control the use of IPv6 on a interface. + * this has been our design choice - cf. NRL's "ifconfig auto"). + * (4) it is easier to manage when an interface has addresses + * with the same interface identifier, than to have multiple addresses + * with different interface identifiers. + * + * Mobile IPv6 addition: allow for caller to specify a wished interface + * ID. This is to not break connections when moving addresses between + * interfaces. + */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ if (ifa) ib = (struct in6_ifaddr *)ifa; @@ -1269,204 +1648,199 @@ in6_ifadd(ifp, in6, addr, prefixlen) #endif /* prefixlen + ifidlen must be equal to 128 */ - if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) { - log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s" - "(prefix=%d ifid=%d)\n", if_name(ifp), - prefixlen, - 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr)); + plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); + if (prefixlen != plen0) { + nd6log((LOG_INFO, "in6_ifadd: wrong prefixlen for %s " + "(prefix=%d ifid=%d)\n", + if_name(ifp), prefixlen, 128 - plen0)); return NULL; } /* make ifaddr */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT); - if (ia == NULL) { - printf("ENOBUFS in in6_ifadd %d\n", __LINE__); - return NULL; - } - bzero((caddr_t)ia, sizeof(*ia)); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - if (ifp->if_flags & IFF_POINTOPOINT) - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - else - ia->ia_ifa.ifa_dstaddr = NULL; - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - - /* link to in6_ifaddr */ - if ((oia = in6_ifaddr) != NULL) { - for( ; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else { - /* - * This should be impossible, since we have at least one - * link-local address (see the beginning of this function). - * XXX: should we rather panic here? - */ - printf("in6_ifadd: in6_ifaddr is NULL (impossible!)\n"); - in6_ifaddr = ia; - } - /* gain a refcnt for the link from in6_ifaddr */ - ia->ia_ifa.ifa_refcnt++; - - /* link to if_addrlist */ - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - /* gain another refcnt for the link from if_addrlist */ - ia->ia_ifa.ifa_refcnt++; - - /* new address */ - ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_addr.sin6_family = AF_INET6; + bzero(&ifra, sizeof(ifra)); + /* + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. + */ + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); /* prefix */ - bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)); - ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; - ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; - ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; - ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; - /* interface ID */ - ia->ia_addr.sin6_addr.s6_addr32[0] - |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); - ia->ia_addr.sin6_addr.s6_addr32[1] - |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); - ia->ia_addr.sin6_addr.s6_addr32[2] - |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); - ia->ia_addr.sin6_addr.s6_addr32[3] - |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); - - /* new prefix */ - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; - bcopy(&mask, &ia->ia_prefixmask.sin6_addr, - sizeof(ia->ia_prefixmask.sin6_addr)); - - /* same routine */ - ia->ia_ifa.ifa_rtrequest = - (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - ia->ia_ifa.ifa_metric = ifp->if_metric; - - /* add interface route */ - if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) { - log(LOG_NOTICE, "in6_ifadd: failed to add an interface route " - "for %s/%d on %s, errno = %d\n", - ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen, - if_name(ifp), error); - } else - ia->ia_flags |= IFA_ROUTE; + bcopy(&pr->ndpr_prefix.sin6_addr, &ifra.ifra_addr.sin6_addr, + sizeof(ifra.ifra_addr.sin6_addr)); + ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; + ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; + ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; + ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; - *addr = ia->ia_addr.sin6_addr; + /* interface ID */ + if (ifid == NULL || IN6_IS_ADDR_UNSPECIFIED(ifid)) + ifid = &ib->ia_addr.sin6_addr; + ifra.ifra_addr.sin6_addr.s6_addr32[0] + |= (ifid->s6_addr32[0] & ~mask.s6_addr32[0]); + ifra.ifra_addr.sin6_addr.s6_addr32[1] + |= (ifid->s6_addr32[1] & ~mask.s6_addr32[1]); + ifra.ifra_addr.sin6_addr.s6_addr32[2] + |= (ifid->s6_addr32[2] & ~mask.s6_addr32[2]); + ifra.ifra_addr.sin6_addr.s6_addr32[3] + |= (ifid->s6_addr32[3] & ~mask.s6_addr32[3]); + + /* new prefix mask. */ + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr, + sizeof(ifra.ifra_prefixmask.sin6_addr)); - if (ifp->if_flags & IFF_MULTICAST) { - int error; /* not used */ - struct in6_addr sol6; + /* + * lifetime. + * XXX: in6_init_address_ltimes would override these values later. + * We should reconsider this logic. + */ + ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime; + ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime; - /* join solicited node multicast address */ - bzero(&sol6, sizeof(sol6)); - sol6.s6_addr16[0] = htons(0xff02); - sol6.s6_addr16[1] = htons(ifp->if_index); - sol6.s6_addr32[1] = 0; - sol6.s6_addr32[2] = htonl(1); - sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; - sol6.s6_addr8[12] = 0xff; - (void)in6_addmulti(&sol6, ifp, &error); - } + /* XXX: scope zone ID? */ - ia->ia6_flags |= IN6_IFF_TENTATIVE; + ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */ + /* + * temporarily set the nopfx flag to avoid conflict. + * XXX: we should reconsider the entire mechanism about prefix + * manipulation. + */ + ifra.ifra_flags |= IN6_IFF_NOPFX; /* - * To make the interface up. Only AF_INET6 in ia is used... + * keep the new address, regardless of the result of in6_update_ifa. + * XXX: this address is now meaningless. + * We should reconsider its role. */ - s = splimp(); - if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) { - splx(s); - return NULL; + pr->ndpr_addr = ifra.ifra_addr.sin6_addr; + + /* allocate ifaddr structure, link into chain, etc. */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { + nd6log((LOG_ERR, + "in6_ifadd: failed to make ifaddr %s on %s (errno=%d)\n", + ip6_sprintf(&ifra.ifra_addr.sin6_addr), if_name(ifp), + error)); + return(NULL); /* ifaddr must not have been allocated. */ } - splx(s); - /* Perform DAD, if needed. */ - nd6_dad_start((struct ifaddr *)ia, NULL); + ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); - return ia; + return(ia); /* this must NOT be NULL. */ } int -in6_ifdel(ifp, in6) - struct ifnet *ifp; - struct in6_addr *in6; +in6_tmpifadd(ia0, forcegen) + const struct in6_ifaddr *ia0; /* corresponding public address */ { - struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL; - struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL; + struct ifnet *ifp = ia0->ia_ifa.ifa_ifp; + struct in6_ifaddr *newia; + struct in6_aliasreq ifra; + int i, error; + int trylimit = 3; /* XXX: adhoc value */ + u_int32_t randid[2]; + time_t vltime0, pltime0; + + bzero(&ifra, sizeof(ifra)); + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); + ifra.ifra_addr = ia0->ia_addr; + /* copy prefix mask */ + ifra.ifra_prefixmask = ia0->ia_prefixmask; + /* clear the old IFID */ + for (i = 0; i < 4; i++) { + ifra.ifra_addr.sin6_addr.s6_addr32[i] + &= ifra.ifra_prefixmask.sin6_addr.s6_addr32[i]; + } - if (!ifp) - return -1; + again: + in6_get_tmpifid(ifp, (u_int8_t *)randid, + (const u_int8_t *)&ia0->ia_addr.sin6_addr.s6_addr[8], + forcegen); + ifra.ifra_addr.sin6_addr.s6_addr32[2] + |= (randid[0] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[2])); + ifra.ifra_addr.sin6_addr.s6_addr32[3] + |= (randid[1] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[3])); - ia = in6ifa_ifpwithaddr(ifp, in6); - if (!ia) - return -1; + /* + * If by chance the new temporary address is the same as an address + * already assigned to the interface, generate a new randomized + * interface identifier and repeat this step. + * RFC 3041 3.3 (4). + */ + if (in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr) != NULL) { + if (trylimit-- == 0) { + nd6log((LOG_NOTICE, "in6_tmpifadd: failed to find " + "a unique random IFID\n")); + return(EEXIST); + } + forcegen = 1; + goto again; + } - if (ifp->if_flags & IFF_MULTICAST) { - /* - * delete solicited multicast addr for deleting host id - */ - struct in6_multi *in6m; - struct in6_addr llsol; - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - - IN6_LOOKUP_MULTI(llsol, ifp, in6m); - if (in6m) - in6_delmulti(in6m); - } - - if (ia->ia_flags & IFA_ROUTE) { - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); - ia->ia_flags &= ~IFA_ROUTE; - } - - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - IFAFREE(&ia->ia_ifa); - - /* lladdr is never deleted */ - oia = ia; - if (oia == (ia = in6_ifaddr)) - in6_ifaddr = ia->ia_next; - else { - while (ia->ia_next && (ia->ia_next != oia)) - ia = ia->ia_next; - if (ia->ia_next) - ia->ia_next = oia->ia_next; - else - return -1; + /* + * The Valid Lifetime is the lower of the Valid Lifetime of the + * public address or TEMP_VALID_LIFETIME. + * The Preferred Lifetime is the lower of the Preferred Lifetime + * of the public address or TEMP_PREFERRED_LIFETIME - + * DESYNC_FACTOR. + */ + if (ia0->ia6_lifetime.ia6t_expire != 0) { + vltime0 = IFA6_IS_INVALID(ia0) ? 0 : + (ia0->ia6_lifetime.ia6t_expire - time_second); + if (vltime0 > ip6_temp_valid_lifetime) + vltime0 = ip6_temp_valid_lifetime; + } else + vltime0 = ip6_temp_valid_lifetime; + if (ia0->ia6_lifetime.ia6t_preferred != 0) { + pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 : + (ia0->ia6_lifetime.ia6t_preferred - time_second); + if (pltime0 > ip6_temp_preferred_lifetime - ip6_desync_factor){ + pltime0 = ip6_temp_preferred_lifetime - + ip6_desync_factor; + } + } else + pltime0 = ip6_temp_preferred_lifetime - ip6_desync_factor; + ifra.ifra_lifetime.ia6t_vltime = vltime0; + ifra.ifra_lifetime.ia6t_pltime = pltime0; + + /* + * A temporary address is created only if this calculated Preferred + * Lifetime is greater than REGEN_ADVANCE time units. + */ + if (ifra.ifra_lifetime.ia6t_pltime <= ip6_temp_regen_advance) + return(0); + + /* XXX: scope zone ID? */ + + ifra.ifra_flags |= (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY); + + /* allocate ifaddr structure, link into chain, etc. */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) + return(error); + + newia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); + if (newia == NULL) { /* XXX: can it happen? */ + nd6log((LOG_ERR, + "in6_tmpifadd: ifa update succeeded, but we got " + "no ifaddr\n")); + return(EINVAL); /* XXX */ } + newia->ia6_ndpr = ia0->ia6_ndpr; + newia->ia6_ndpr->ndpr_refcnt++; - IFAFREE((&oia->ia_ifa)); -/* xxx - rtrequest(RTM_DELETE, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)0 - (struct sockaddr *)&ia->ia_prefixmask, - RTF_UP|RTF_CLONING, - (struct rtentry **)0); -*/ - return 0; -} + return(0); +} int in6_init_prefix_ltimes(struct nd_prefix *ndpr) { - - /* check if preferred lifetime > valid lifetime */ + /* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */ if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { - log(LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" + nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" "(%d) is greater than valid lifetime(%d)\n", - (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime); + (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime)); return (EINVAL); } if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) @@ -1482,24 +1856,15 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr) } static void -in6_init_address_ltimes(struct nd_prefix *new, - struct in6_addrlifetime *lt6, - int update_vltime) +in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) { - /* Valid lifetime must not be updated unless explicitly specified. */ - if (update_vltime) { - /* init ia6t_expire */ - if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) - lt6->ia6t_expire = 0; - else { - lt6->ia6t_expire = time_second; - lt6->ia6t_expire += lt6->ia6t_vltime; - } - /* Ensure addr lifetime <= prefix lifetime. */ - if (new->ndpr_expire && lt6->ia6t_expire && - new->ndpr_expire < lt6->ia6t_expire) - lt6->ia6t_expire = new->ndpr_expire; + /* init ia6t_expire */ + if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) + lt6->ia6t_expire = 0; + else { + lt6->ia6t_expire = time_second; + lt6->ia6t_expire += lt6->ia6t_vltime; } /* init ia6t_preferred */ @@ -1509,10 +1874,6 @@ in6_init_address_ltimes(struct nd_prefix *new, lt6->ia6t_preferred = time_second; lt6->ia6t_preferred += lt6->ia6t_pltime; } - /* Ensure addr lifetime <= prefix lifetime. */ - if (new->ndpr_preferred && lt6->ia6t_preferred - && new->ndpr_preferred < lt6->ia6t_preferred) - lt6->ia6t_preferred = new->ndpr_preferred; } /* @@ -1522,8 +1883,8 @@ in6_init_address_ltimes(struct nd_prefix *new, */ void rt6_flush(gateway, ifp) - struct in6_addr *gateway; - struct ifnet *ifp; + struct in6_addr *gateway; + struct ifnet *ifp; { struct radix_node_head *rnh = rt_tables[AF_INET6]; int s = splnet(); @@ -1556,6 +1917,14 @@ rt6_deleteroute(rn, arg) return(0); /* + * Do not delete a static route. + * XXX: this seems to be a bit ad-hoc. Should we consider the + * 'cloned' bit instead? + */ + if ((rt->rt_flags & RTF_STATIC) != 0) + return(0); + + /* * We delete only host route. This means, in particular, we don't * delete default route. */ diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 42f38fd5d62f..eda8bfa5eb73 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -65,6 +65,7 @@ */ #include "opt_ipsec.h" +#include "opt_inet6.h" #include <sys/param.h> #include <sys/malloc.h> @@ -94,6 +95,7 @@ #ifdef ENABLE_DEFAULT_SCOPE #include <netinet6/scope6_var.h> #endif +#include <netinet6/raw_ip6.h> #ifdef IPSEC #include <netinet6/ipsec.h> @@ -103,6 +105,9 @@ #include <machine/stdarg.h> #include "faith.h" +#if defined(NFAITH) && 0 < NFAITH +#include <net/if_faith.h> +#endif #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) @@ -116,6 +121,8 @@ extern struct inpcbinfo ripcbinfo; extern u_long rip_sendspace; extern u_long rip_recvspace; +struct rip6stat rip6stat; + /* * Setup generic address and protocol structures * for raw_input routine, then pass them along with @@ -130,18 +137,19 @@ rip6_input(mp, offp, proto) register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); register struct inpcb *in6p; struct inpcb *last = 0; - struct mbuf *opts = 0; + struct mbuf *opts = NULL; struct sockaddr_in6 rip6src; + rip6stat.rip6s_ipackets++; + #if defined(NFAITH) && 0 < NFAITH - if (m->m_pkthdr.rcvif) { - if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) { - /* XXX send icmp6 host/port unreach? */ - m_freem(m); - return IPPROTO_DONE; - } + if (faithprefix(&ip6->ip6_dst)) { + /* XXX send icmp6 host/port unreach? */ + m_freem(m); + return IPPROTO_DONE; } #endif + init_sin6(&rip6src, m); /* general init */ LIST_FOREACH(in6p, &ripcb, inp_list) { @@ -156,14 +164,27 @@ rip6_input(mp, offp, proto) if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; - if (in6p->in6p_cksum != -1 - && in6_cksum(m, ip6->ip6_nxt, *offp, - m->m_pkthdr.len - *offp)) { - /* XXX bark something */ - continue; + if (in6p->in6p_cksum != -1) { + rip6stat.rip6s_isum++; + if (in6_cksum(m, ip6->ip6_nxt, *offp, + m->m_pkthdr.len - *offp)) { + rip6stat.rip6s_badsum++; + continue; + } } if (last) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); + +#ifdef IPSEC + /* + * Check AH/ESP integrity. + */ + if (n && ipsec6_in_reject_so(n, last->inp_socket)) { + m_freem(n); + ipsec6stat.in_polvio++; + /* do not inject data into pcb */ + } else +#endif /*IPSEC*/ if (n) { if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) @@ -173,10 +194,10 @@ rip6_input(mp, offp, proto) if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, n, opts) == 0) { - /* should notify about lost packet */ m_freem(n); if (opts) m_freem(opts); + rip6stat.rip6s_fullsock++; } else sorwakeup(last->in6p_socket); opts = NULL; @@ -184,6 +205,17 @@ rip6_input(mp, offp, proto) } last = in6p; } +#ifdef IPSEC + /* + * Check AH/ESP integrity. + */ + if (last && ipsec6_in_reject_so(m, last->inp_socket)) { + m_freem(m); + ipsec6stat.in_polvio++; + ip6stat.ip6s_delivered--; + /* do not inject data into pcb */ + } else +#endif /*IPSEC*/ if (last) { if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) @@ -195,9 +227,13 @@ rip6_input(mp, offp, proto) m_freem(m); if (opts) m_freem(opts); + rip6stat.rip6s_fullsock++; } else sorwakeup(last->in6p_socket); } else { + rip6stat.rip6s_nosock++; + if (m->m_flags & M_MCAST) + rip6stat.rip6s_nosockmcast++; if (proto == IPPROTO_NONE) m_freem(m); else { @@ -217,10 +253,11 @@ rip6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; int off = 0; + struct ip6ctlparam *ip6cp = NULL; + const struct sockaddr_in6 *sa6_src = NULL; void (*notify) __P((struct inpcb *, int)) = in6_rtchange; if (sa->sa_family != AF_INET6 || @@ -238,37 +275,19 @@ rip6_ctlinput(cmd, sa, d) /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; + sa6_src = &sa6_any; } - /* translate addresses into internal form */ - sa6 = *(struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) - sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - - if (ip6) { - /* - * XXX: We assume that when IPV6 is non NULL, - * M and OFF are valid. - */ - struct in6_addr s; - - /* translate addresses into internal form */ - memcpy(&s, &ip6->ip6_src, sizeof(s)); - if (IN6_IS_ADDR_LINKLOCAL(&s)) - s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - - (void) in6_pcbnotify(&ripcb, (struct sockaddr *)&sa6, - 0, &s, 0, cmd, notify); - } else - (void) in6_pcbnotify(&ripcb, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, notify); + (void) in6_pcbnotify(&ripcb, sa, 0, (struct sockaddr *)sa6_src, + 0, cmd, notify); } /* @@ -311,7 +330,7 @@ rip6_output(m, va_alist) priv = 1; dst = &dstsock->sin6_addr; if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) goto bad; optp = &opt; } else @@ -431,7 +450,10 @@ rip6_output(m, va_alist) } #ifdef IPSEC - ipsec_setsocket(m, so); + if (ipsec_setsocket(m, so) != 0) { + error = ENOBUFS; + goto bad; + } #endif /*IPSEC*/ error = ip6_output(m, optp, &in6p->in6p_route, 0, @@ -440,7 +462,8 @@ rip6_output(m, va_alist) if (oifp) icmp6_ifoutstat_inc(oifp, type, code); icmp6stat.icp6s_outhist[type]++; - } + } else + rip6stat.rip6s_opackets++; goto freectl; @@ -451,8 +474,11 @@ rip6_output(m, va_alist) freectl: if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt) RTFREE(optp->ip6po_route.ro_rt); - if (control) + if (control) { + if (optp == &opt) + ip6_clearpktopts(optp, 0, -1); m_freem(control); + } return(error); } diff --git a/sys/netinet6/raw_ip6.h b/sys/netinet6/raw_ip6.h new file mode 100644 index 000000000000..cfb390b4cc1d --- /dev/null +++ b/sys/netinet6/raw_ip6.h @@ -0,0 +1,54 @@ +/* $FreeBSD$ */ +/* $KAME: raw_ip6.h,v 1.2 2001/05/27 13:28:35 itojun Exp $ */ + +/* + * Copyright (C) 2001 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NETINET6_RAW_IP6_H_ +#define _NETINET6_RAW_IP6_H_ + +/* + * ICMPv6 stat is counted separately. see netinet/icmp6.h + */ +struct rip6stat { + u_quad_t rip6s_ipackets; /* total input packets */ + u_quad_t rip6s_isum; /* input checksum computations */ + u_quad_t rip6s_badsum; /* of above, checksum error */ + u_quad_t rip6s_nosock; /* no matching socket */ + u_quad_t rip6s_nosockmcast; /* of above, arrived as multicast */ + u_quad_t rip6s_fullsock; /* not delivered, input socket full */ + + u_quad_t rip6s_opackets; /* total output packets */ +}; + +#ifdef _KERNEL +extern struct rip6stat rip6stat; +#endif + +#endif diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c index 3cd95a10a959..807c6ad36590 100644 --- a/sys/netinet6/route6.c +++ b/sys/netinet6/route6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: route6.c,v 1.15 2000/06/23 16:18:20 itojun Exp $ */ +/* $KAME: route6.c,v 1.24 2001/03/14 03:07:05 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,6 +37,7 @@ #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/systm.h> +#include <sys/queue.h> #include <net/if.h> @@ -55,10 +56,22 @@ route6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; /* proto is unused */ { - register struct ip6_hdr *ip6; - register struct mbuf *m = *mp; - register struct ip6_rthdr *rh; + struct ip6_hdr *ip6; + struct mbuf *m = *mp; + struct ip6_rthdr *rh; int off = *offp, rhlen; + struct mbuf *n; + + n = ip6_findaux(m); + if (n) { + struct ip6aux *ip6a = mtod(n, struct ip6aux *); + /* XXX reject home-address option before rthdr */ + if (ip6a->ip6a_flags & IP6A_SWAP) { + ip6stat.ip6s_badoptions++; + m_freem(m); + return IPPROTO_DONE; + } + } #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE); @@ -119,6 +132,9 @@ route6_input(mp, offp, proto) /* * Type0 routing header processing + * + * RFC2292 backward compatibility warning: no support for strict/loose bitmap, + * as it was dropped between RFC1883 and RFC2460. */ static int ip6_rthdr0(m, ip6, rh0) @@ -176,7 +192,7 @@ ip6_rthdr0(m, ip6, rh0) if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) || IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) || - IN6_IS_ADDR_V4COMPAT(nextaddr)) { + IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { ip6stat.ip6s_badoptions++; m_freem(m); return(-1); diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c index 43a6fb722afa..09bba0c4a4b0 100644 --- a/sys/netinet6/scope6.c +++ b/sys/netinet6/scope6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: scope6.c,v 1.9 2000/05/18 15:03:26 jinmei Exp $ */ +/* $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ */ /* * Copyright (C) 2000 WIDE Project. @@ -35,6 +35,7 @@ #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/systm.h> +#include <sys/queue.h> #include <net/route.h> #include <net/if.h> diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index ee050190c2f7..fd4d6f3f1cd6 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: udp6_output.c,v 1.14 2000/06/13 10:31:23 itojun Exp $ */ +/* $KAME: udp6_output.c,v 1.31 2001/05/21 16:39:15 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -69,6 +69,7 @@ #include "opt_inet.h" #include <sys/param.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -120,22 +121,22 @@ int udp6_output(in6p, m, addr6, control, p) - register struct in6pcb *in6p; - register struct mbuf *m; + struct in6pcb *in6p; + struct mbuf *m; struct mbuf *control; struct sockaddr *addr6; struct proc *p; { - register u_int32_t ulen = m->m_pkthdr.len; + u_int32_t ulen = m->m_pkthdr.len; u_int32_t plen = sizeof(struct udphdr) + ulen; struct ip6_hdr *ip6; struct udphdr *udp6; - struct in6_addr *laddr, *faddr; + struct in6_addr *laddr, *faddr; u_short fport; int error = 0; struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts; int priv; - int af, hlen; + int af = AF_INET6, hlen = sizeof(struct ip6_hdr); int flags; struct sockaddr_in6 tmp; @@ -143,7 +144,7 @@ udp6_output(in6p, m, addr6, control, p) if (p && !suser(p)) priv = 1; if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) goto release; in6p->in6p_outputopts = &opt; } @@ -164,6 +165,7 @@ udp6_output(in6p, m, addr6, control, p) } if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { + /* how about ::ffff:0.0.0.0 case? */ error = EISCONN; goto release; } @@ -175,6 +177,24 @@ udp6_output(in6p, m, addr6, control, p) faddr = &sin6->sin6_addr; fport = sin6->sin6_port; /* allow 0 port */ + if (IN6_IS_ADDR_V4MAPPED(faddr)) { + if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY)) { + /* + * I believe we should explicitly discard the + * packet when mapped addresses are disabled, + * rather than send the packet as an IPv6 one. + * If we chose the latter approach, the packet + * might be sent out on the wire based on the + * default route, the situation which we'd + * probably want to avoid. + * (20010421 jinmei@kame.net) + */ + error = EINVAL; + goto release; + } else + af = AF_INET; + } + /* KAME hack: embed scopeid */ if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) { error = EINVAL; @@ -187,7 +207,7 @@ udp6_output(in6p, m, addr6, control, p) &in6p->in6p_route, &in6p->in6p_laddr, &error); } else - laddr = &in6p->in6p_laddr; /*XXX*/ + laddr = &in6p->in6p_laddr; /* XXX */ if (laddr == NULL) { if (error == 0) error = EADDRNOTAVAIL; @@ -201,18 +221,29 @@ udp6_output(in6p, m, addr6, control, p) error = ENOTCONN; goto release; } + if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_faddr)) { + if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY)) { + /* + * XXX: this case would happen when the + * application sets the V6ONLY flag after + * connecting the foreign address. + * Such applications should be fixed, + * so we bark here. + */ + log(LOG_INFO, "udp6_output: IPV6_V6ONLY " + "option was set for a connected socket\n"); + error = EINVAL; + goto release; + } else + af = AF_INET; + } laddr = &in6p->in6p_laddr; faddr = &in6p->in6p_faddr; fport = in6p->in6p_fport; } - if (!IN6_IS_ADDR_V4MAPPED(faddr)) { - af = AF_INET6; - hlen = sizeof(struct ip6_hdr); - } else { - af = AF_INET; + if (af == AF_INET) hlen = sizeof(struct ip); - } /* * Calculate data length and get a mbuf @@ -261,10 +292,13 @@ udp6_output(in6p, m, addr6, control, p) udp6stat.udp6s_opackets++; #ifdef IPSEC - ipsec_setsocket(m, in6p->in6p_socket); + if (ipsec_setsocket(m, in6p->in6p_socket) != 0) { + error = ENOBUFS; + goto release; + } #endif /*IPSEC*/ error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, - flags, in6p->in6p_moptions, NULL); + flags, in6p->in6p_moptions, NULL); break; case AF_INET: error = EAFNOSUPPORT; @@ -277,6 +311,7 @@ release: releaseopt: if (control) { + ip6_clearpktopts(in6p->in6p_outputopts, 0, -1); in6p->in6p_outputopts = stickyopt; m_freem(control); } diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index ca9ce2fe25a8..bb5a38a466ad 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: udp6_usrreq.c,v 1.17 2000/10/13 17:46:21 itojun Exp $ */ +/* $KAME: udp6_usrreq.c,v 1.27 2001/05/21 05:45:10 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -107,6 +107,9 @@ #endif /*IPSEC*/ #include "faith.h" +#if defined(NFAITH) && NFAITH > 0 +#include <net/if_faith.h> +#endif /* * UDP protocol inplementation. @@ -149,25 +152,25 @@ udp6_input(mp, offp, proto) register struct ip6_hdr *ip6; register struct udphdr *uh; register struct inpcb *in6p; - struct mbuf *opts = 0; + struct mbuf *opts = NULL; int off = *offp; int plen, ulen; struct sockaddr_in6 udp_in6; + IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); + + ip6 = mtod(m, struct ip6_hdr *); + #if defined(NFAITH) && 0 < NFAITH - if (m->m_pkthdr.rcvif) { - if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) { - /* XXX send icmp6 host/port unreach? */ - m_freem(m); - return IPPROTO_DONE; - } + if (faithprefix(&ip6->ip6_dst)) { + /* XXX send icmp6 host/port unreach? */ + m_freem(m); + return IPPROTO_DONE; } #endif - udpstat.udps_ipackets++; - IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); + udpstat.udps_ipackets++; - ip6 = mtod(m, struct ip6_hdr *); plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6); uh = (struct udphdr *)((caddr_t)ip6 + off); ulen = ntohs((u_short)uh->uh_ulen); @@ -274,6 +277,7 @@ udp6_input(mp, offp, proto) || last->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(last, &opts, ip6, n); + m_adj(n, off + sizeof(struct udphdr)); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&udp_in6, @@ -284,7 +288,7 @@ udp6_input(mp, offp, proto) udpstat.udps_fullsock++; } else sorwakeup(last->in6p_socket); - opts = 0; + opts = NULL; } } last = in6p; @@ -401,13 +405,17 @@ udp6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - register struct udphdr *uhp; struct udphdr uh; - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; int off = 0; + struct ip6ctlparam *ip6cp = NULL; + const struct sockaddr_in6 *sa6_src = NULL; void (*notify) __P((struct inpcb *, int)) = udp_notify; + struct udp_portonly { + u_int16_t uh_sport; + u_int16_t uh_dport; + } *uhp; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) @@ -424,51 +432,35 @@ udp6_ctlinput(cmd, sa, d) /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; + sa6_src = &sa6_any; } - /* translate addresses into internal form */ - sa6 = *(struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) - sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); - if (ip6) { /* * XXX: We assume that when IPV6 is non NULL, * M and OFF are valid. */ - struct in6_addr s; - - /* translate addresses into internal form */ - memcpy(&s, &ip6->ip6_src, sizeof(s)); - if (IN6_IS_ADDR_LINKLOCAL(&s)) - s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); /* check if we can safely examine src and dst ports */ - if (m->m_pkthdr.len < off + sizeof(uh)) + if (m->m_pkthdr.len < off + sizeof(*uhp)) return; - if (m->m_len < off + sizeof(uh)) { - /* - * this should be rare case, - * so we compromise on this copy... - */ - m_copydata(m, off, sizeof(uh), (caddr_t)&uh); - uhp = &uh; - } else - uhp = (struct udphdr *)(mtod(m, caddr_t) + off); - (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, - uhp->uh_dport, &s, - uhp->uh_sport, cmd, notify); + bzero(&uh, sizeof(uh)); + m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh); + + (void) in6_pcbnotify(&udb, sa, uh.uh_dport, ip6cp->ip6c_src, + uh.uh_sport, cmd, notify); } else - (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, notify); + (void) in6_pcbnotify(&udb, sa, 0, (struct sockaddr *)&sa6_src, + 0, cmd, notify); } static int @@ -583,7 +575,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { struct sockaddr_in6 *sin6_p; sin6_p = (struct sockaddr_in6 *)nam; @@ -618,7 +610,8 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) inp = sotoinpcb(so); if (inp == 0) return EINVAL; - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { + + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { struct sockaddr_in6 *sin6_p; sin6_p = (struct sockaddr_in6 *)nam; @@ -644,15 +637,12 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) return EISCONN; s = splnet(); error = in6_pcbconnect(inp, nam, p); - if (ip6_auto_flowlabel) { - inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; - inp->in6p_flowinfo |= - (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); - } splx(s); if (error == 0) { - inp->inp_vflag &= ~INP_IPV4; - inp->inp_vflag |= INP_IPV6; + if (ip6_mapped_addr_on) { /* should be non mapped addr */ + inp->inp_vflag &= ~INP_IPV4; + inp->inp_vflag |= INP_IPV6; + } soisconnected(so); } return error; diff --git a/sys/netkey/key.c b/sys/netkey/key.c index 497695aefc57..31dc63b1c320 100644 --- a/sys/netkey/key.c +++ b/sys/netkey/key.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: key.c,v 1.137 2000/06/24 00:47:07 itojun Exp $ */ +/* $KAME: key.c,v 1.187 2001/05/24 07:41:22 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -79,11 +79,7 @@ #include <netkey/keydb.h> #include <netkey/key.h> #include <netkey/keysock.h> -#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #include <netinet6/ipsec.h> #ifdef INET6 @@ -100,9 +96,15 @@ #endif #endif #include <netinet6/ipcomp.h> +#ifdef INET6 +#include <netinet6/ipcomp6.h> +#endif #include <machine/stdarg.h> +/* randomness */ +#include <sys/random.h> + #include <net/net_osdep.h> #ifndef satosin @@ -208,6 +210,10 @@ static const int maxsize[] = { sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ }; +static int ipsec_esp_keymin = 256; +static int ipsec_esp_auth = 0; +static int ipsec_ah_keymin = 128; + #ifdef SYSCTL_DECL SYSCTL_DECL(_net_key); #endif @@ -245,9 +251,13 @@ SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_COUNT, blockacq_count, CTLFLAG_RW, \ SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_LIFETIME, blockacq_lifetime, CTLFLAG_RW, \ &key_blockacq_lifetime, 0, ""); -static const int ipsec_esp_keymin = 256; -static const int ipsec_esp_auth = 0; -static const int ipsec_ah_keymin = 128; +/* minimum ESP key length */ +SYSCTL_INT(_net_key, KEYCTL_ESP_KEYMIN, esp_keymin, CTLFLAG_RW, \ + &ipsec_esp_keymin, 0, ""); + +/* minimum AH key length */ +SYSCTL_INT(_net_key, KEYCTL_AH_KEYMIN, ah_keymin, CTLFLAG_RW, \ + &ipsec_ah_keymin, 0, ""); #ifndef LIST_FOREACH #define LIST_FOREACH(elm, head, field) \ @@ -371,6 +381,7 @@ static int key_spddump __P((struct socket *, struct mbuf *, static struct mbuf *key_setdumpsp __P((struct secpolicy *, u_int8_t, u_int32_t, u_int32_t)); static u_int key_getspreqmsglen __P((struct secpolicy *)); +static int key_spdexpire __P((struct secpolicy *)); static struct secashead *key_newsah __P((struct secasindex *)); static void key_delsah __P((struct secashead *)); static struct secasvar *key_newsav __P((struct mbuf *, @@ -404,6 +415,8 @@ static int key_cmpsaidx_exactly __P((struct secasindex *, struct secasindex *)); static int key_cmpsaidx_withmode __P((struct secasindex *, struct secasindex *)); +static int key_cmpsaidx_withoutmode2 + __P((struct secasindex *, struct secasindex *)); static int key_cmpsaidx_withoutmode __P((struct secasindex *, struct secasindex *)); static int key_cmpspidx_exactly @@ -413,7 +426,6 @@ static int key_cmpspidx_withmask static int key_sockaddrcmp __P((struct sockaddr *, struct sockaddr *, int)); static int key_bbcmp __P((caddr_t, caddr_t, u_int)); static void key_srandom __P((void)); -static u_long key_random __P((void)); static u_int16_t key_satype2proto __P((u_int8_t)); static u_int8_t key_proto2satype __P((u_int16_t)); @@ -437,10 +449,12 @@ static int key_delete __P((struct socket *, struct mbuf *, static int key_get __P((struct socket *, struct mbuf *, const struct sadb_msghdr *)); +static void key_getcomb_setlifetime __P((struct sadb_comb *)); #ifdef IPSEC_ESP static struct mbuf *key_getcomb_esp __P((void)); #endif static struct mbuf *key_getcomb_ah __P((void)); +static struct mbuf *key_getcomb_ipcomp __P((void)); static struct mbuf *key_getprop __P((const struct secasindex *)); static int key_acquire __P((struct secasindex *, struct secpolicy *)); @@ -485,6 +499,7 @@ key_allocsp(spidx, dir) u_int dir; { struct secpolicy *sp; + struct timeval tv; int s; /* sanity check */ @@ -525,6 +540,8 @@ found: KEY_CHKSPDIR(sp->spidx.dir, dir, "key_allocsp"); /* found a SPD entry */ + microtime(&tv); + sp->lastused = tv.tv_sec; sp->refcnt++; splx(s); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, @@ -535,8 +552,75 @@ found: } /* - * allocating a SA entry for a *OUTBOUND* packet. - * checking each request entries in SP, and acquire SA if need. + * return a policy that matches this particular inbound packet. + * XXX slow + */ +struct secpolicy * +key_gettunnel(osrc, odst, isrc, idst) + struct sockaddr *osrc, *odst, *isrc, *idst; +{ + struct secpolicy *sp; + const int dir = IPSEC_DIR_INBOUND; + struct timeval tv; + int s; + struct ipsecrequest *r1, *r2, *p; + struct sockaddr *os, *od, *is, *id; + struct secpolicyindex spidx; + + s = splnet(); /*called from softclock()*/ + LIST_FOREACH(sp, &sptree[dir], chain) { + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + + r1 = r2 = NULL; + for (p = sp->req; p; p = p->next) { + if (p->saidx.mode != IPSEC_MODE_TUNNEL) + continue; + + r1 = r2; + r2 = p; + + if (!r1) { + /* here we look at address matches only */ + spidx = sp->spidx; + if (isrc->sa_len > sizeof(spidx.src) || + idst->sa_len > sizeof(spidx.dst)) + continue; + bcopy(isrc, &spidx.src, isrc->sa_len); + bcopy(idst, &spidx.dst, idst->sa_len); + if (!key_cmpspidx_withmask(&sp->spidx, &spidx)) + continue; + } else { + is = (struct sockaddr *)&r1->saidx.src; + id = (struct sockaddr *)&r1->saidx.dst; + if (key_sockaddrcmp(is, isrc, 0) || + key_sockaddrcmp(id, idst, 0)) + continue; + } + + os = (struct sockaddr *)&r2->saidx.src; + od = (struct sockaddr *)&r2->saidx.dst; + if (key_sockaddrcmp(os, osrc, 0) || + key_sockaddrcmp(od, odst, 0)) + continue; + + goto found; + } + } + splx(s); + return NULL; + +found: + microtime(&tv); + sp->lastused = tv.tv_sec; + sp->refcnt++; + splx(s); + return sp; +} + +/* + * allocating an SA entry for an *OUTBOUND* packet. + * checking each request entries in SP, and acquire an SA if need. * OUT: 0: there are valid requests. * ENOENT: policy may be valid, but SA with REQUIRE is on acquiring. */ @@ -987,7 +1071,7 @@ key_freesav(sav) sav->refcnt--; KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP freesav cause refcnt--:%d SA:%p SPI %d\n", + printf("DP freesav cause refcnt--:%d SA:%p SPI %u\n", sav->refcnt, sav, (u_int32_t)ntohl(sav->spi))); if (sav->refcnt == 0) @@ -1212,9 +1296,7 @@ key_msg2sp(xpl0, len, error) switch (xisr->sadb_x_ipsecrequest_proto) { case IPPROTO_ESP: case IPPROTO_AH: -#if 1 /*nonstandard*/ case IPPROTO_IPCOMP: -#endif break; default: #ifdef IPSEC_DEBUG @@ -1534,11 +1616,11 @@ fail: /* * SADB_X_SPDADD, SADB_X_SPDSETIDX or SADB_X_SPDUPDATE processing * add a entry to SP database, when received - * <base, address(SD), policy> + * <base, address(SD), (lifetime(H),) policy> * from the user(?). * Adding to SP database, * and send - * <base, address(SD), policy> + * <base, address(SD), (lifetime(H),) policy> * to the socket which was send. * * SPDADD set a unique policy entry. @@ -1555,8 +1637,10 @@ key_spdadd(so, m, mhp) { struct sadb_address *src0, *dst0; struct sadb_x_policy *xpl0, *xpl; + struct sadb_lifetime *lft = NULL; struct secpolicyindex spidx; struct secpolicy *newsp; + struct timeval tv; int error; /* sanity check */ @@ -1579,6 +1663,16 @@ key_spdadd(so, m, mhp) #endif return key_senderror(so, m, EINVAL); } + if (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL) { + if (mhp->extlen[SADB_EXT_LIFETIME_HARD] + < sizeof(struct sadb_lifetime)) { +#ifdef IPSEC_DEBUG + printf("key_spdadd: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } + lft = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD]; + } src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; @@ -1702,6 +1796,12 @@ key_spdadd(so, m, mhp) } #endif + microtime(&tv); + newsp->created = tv.tv_sec; + newsp->lastused = tv.tv_sec; + newsp->lifetime = lft ? lft->sadb_lifetime_addtime : 0; + newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0; + newsp->refcnt = 1; /* do not reclaim until I say I do */ newsp->state = IPSEC_SPSTATE_ALIVE; LIST_INSERT_TAIL(&sptree[newsp->spidx.dir], newsp, secpolicy, chain); @@ -1710,8 +1810,9 @@ key_spdadd(so, m, mhp) if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { struct secspacq *spacq; if ((spacq = key_getspacq(&spidx)) != NULL) { - /* reset counter in order to deletion by timehander. */ - spacq->tick = key_blockacq_lifetime; + /* reset counter in order to deletion by timehandler. */ + microtime(&tv); + spacq->created = tv.tv_sec; spacq->count = 0; } } @@ -1722,8 +1823,15 @@ key_spdadd(so, m, mhp) int off; /* create new sadb_msg to reply. */ - n = key_gather_mbuf(m, mhp, 2, 4, SADB_EXT_RESERVED, - SADB_X_EXT_POLICY, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + if (lft) { + n = key_gather_mbuf(m, mhp, 2, 4, SADB_EXT_RESERVED, + SADB_X_EXT_POLICY, SADB_EXT_LIFETIME_HARD, + SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + } else { + n = key_gather_mbuf(m, mhp, 2, 4, SADB_EXT_RESERVED, + SADB_X_EXT_POLICY, + SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + } if (!n) return key_senderror(so, m, ENOBUFS); @@ -2308,6 +2416,123 @@ key_getspreqmsglen(sp) return tlen; } +/* + * SADB_SPDEXPIRE processing + * send + * <base, address(SD), lifetime(CH), policy> + * to KMD by PF_KEY. + * + * OUT: 0 : succeed + * others : error number + */ +static int +key_spdexpire(sp) + struct secpolicy *sp; +{ + int s; + struct mbuf *result = NULL, *m; + int len; + int error = -1; + struct sadb_lifetime *lt; + + /* XXX: Why do we lock ? */ + s = splnet(); /*called from softclock()*/ + + /* sanity check */ + if (sp == NULL) + panic("key_spdexpire: NULL pointer is passed.\n"); + + /* set msg header */ + m = key_setsadbmsg(SADB_X_SPDEXPIRE, 0, 0, 0, 0, 0); + if (!m) { + error = ENOBUFS; + goto fail; + } + result = m; + + /* create lifetime extension (current and hard) */ + len = PFKEY_ALIGN8(sizeof(*lt)) * 2; + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + error = ENOBUFS; + goto fail; + } + bzero(mtod(m, caddr_t), len); + lt = mtod(m, struct sadb_lifetime *); + lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; + lt->sadb_lifetime_allocations = 0; + lt->sadb_lifetime_bytes = 0; + lt->sadb_lifetime_addtime = sp->created; + lt->sadb_lifetime_usetime = sp->lastused; + lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2); + lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; + lt->sadb_lifetime_allocations = 0; + lt->sadb_lifetime_bytes = 0; + lt->sadb_lifetime_addtime = sp->lifetime; + lt->sadb_lifetime_usetime = sp->validtime; + m_cat(result, m); + + /* set sadb_address for source */ + m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sp->spidx.src, + sp->spidx.prefs, sp->spidx.ul_proto); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); + + /* set sadb_address for destination */ + m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sp->spidx.dst, + sp->spidx.prefd, sp->spidx.ul_proto); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); + + /* set secpolicy */ + m = key_sp2msg(sp); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); + + if ((result->m_flags & M_PKTHDR) == 0) { + error = EINVAL; + goto fail; + } + + if (result->m_len < sizeof(struct sadb_msg)) { + result = m_pullup(result, sizeof(struct sadb_msg)); + if (result == NULL) { + error = ENOBUFS; + goto fail; + } + } + + result->m_pkthdr.len = 0; + for (m = result; m; m = m->m_next) + result->m_pkthdr.len += m->m_len; + + mtod(result, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(result->m_pkthdr.len); + + return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED); + + fail: + if (result) + m_freem(result); + splx(s); + return error; +} + /* %%% SAD management */ /* * allocating a memory for new SA head, and copy from the values of mhp. @@ -2484,8 +2709,12 @@ key_newsav(m, mhp, sah, errp) } } - /* reset tick */ - newsav->tick = 0; + /* reset created */ + { + struct timeval tv; + microtime(&tv); + newsav->created = tv.tv_sec; + } newsav->pid = mhp->msg->sadb_msg_pid; @@ -2517,28 +2746,41 @@ key_delsav(sav) if (__LIST_CHAINED(sav)) LIST_REMOVE(sav, chain); - if (sav->key_auth != NULL) + if (sav->key_auth != NULL) { + bzero(_KEYBUF(sav->key_auth), _KEYLEN(sav->key_auth)); KFREE(sav->key_auth); - if (sav->key_enc != NULL) + sav->key_auth = NULL; + } + if (sav->key_enc != NULL) { + bzero(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc)); KFREE(sav->key_enc); - if (sav->replay != NULL) + sav->key_enc = NULL; + } + if (sav->sched) { + bzero(sav->sched, sav->schedlen); + KFREE(sav->sched); + sav->sched = NULL; + } + if (sav->replay != NULL) { keydb_delsecreplay(sav->replay); - if (sav->lft_c != NULL) + sav->replay = NULL; + } + if (sav->lft_c != NULL) { KFREE(sav->lft_c); - if (sav->lft_h != NULL) + sav->lft_c = NULL; + } + if (sav->lft_h != NULL) { KFREE(sav->lft_h); - if (sav->lft_s != NULL) + sav->lft_h = NULL; + } + if (sav->lft_s != NULL) { KFREE(sav->lft_s); - if (sav->iv != NULL) + sav->lft_s = NULL; + } + if (sav->iv != NULL) { KFREE(sav->iv); -#if notyet - if (sav->misc1 != NULL) - KFREE(sav->misc1); - if (sav->misc2 != NULL) - KFREE(sav->misc2); - if (sav->misc3 != NULL) - KFREE(sav->misc3); -#endif + sav->iv = NULL; + } KFREE(sav); @@ -2560,8 +2802,8 @@ key_getsah(saidx) LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) continue; - if (key_cmpsaidx_exactly(&sah->saidx, saidx)) - return(sah); + if (key_cmpsaidx_withoutmode2(&sah->saidx, saidx)) + return sah; } return NULL; @@ -2671,15 +2913,12 @@ key_setsaval(sav, m, mhp) sav->replay = NULL; sav->key_auth = NULL; sav->key_enc = NULL; + sav->sched = NULL; + sav->schedlen = 0; sav->iv = NULL; sav->lft_c = NULL; sav->lft_h = NULL; sav->lft_s = NULL; -#if notyet - sav->misc1 = NULL; - sav->misc2 = NULL; - sav->misc3 = NULL; -#endif /* SA */ if (mhp->ext[SADB_EXT_SA] != NULL) { @@ -2725,7 +2964,7 @@ key_setsaval(sav, m, mhp) case SADB_SATYPE_AH: case SADB_SATYPE_ESP: if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) && - sav->alg_auth != SADB_AALG_NULL) + sav->alg_auth != SADB_X_AALG_NULL) error = EINVAL; break; case SADB_X_SATYPE_IPCOMP: @@ -2766,11 +3005,26 @@ key_setsaval(sav, m, mhp) switch (mhp->msg->sadb_msg_satype) { case SADB_SATYPE_ESP: if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) && - sav->alg_enc != SADB_EALG_NULL) + sav->alg_enc != SADB_EALG_NULL) { error = EINVAL; + break; + } + sav->key_enc = (struct sadb_key *)key_newbuf(key0, len); + if (sav->key_enc == NULL) { +#ifdef IPSEC_DEBUG + printf("key_setsaval: No more memory.\n"); +#endif + error = ENOBUFS; + goto fail; + } break; - case SADB_SATYPE_AH: case SADB_X_SATYPE_IPCOMP: + if (len != PFKEY_ALIGN8(sizeof(struct sadb_key))) + error = EINVAL; + sav->key_enc = NULL; /*just in case*/ + break; + case SADB_SATYPE_AH: + default: error = EINVAL; break; } @@ -2780,15 +3034,6 @@ key_setsaval(sav, m, mhp) #endif goto fail; } - - sav->key_enc = (struct sadb_key *)key_newbuf(key0, len); - if (sav->key_enc == NULL) { -#ifdef IPSEC_DEBUG - printf("key_setsaval: No more memory.\n"); -#endif - error = ENOBUFS; - goto fail; - } } /* set iv */ @@ -2797,9 +3042,9 @@ key_setsaval(sav, m, mhp) switch (mhp->msg->sadb_msg_satype) { case SADB_SATYPE_ESP: #ifdef IPSEC_ESP - algo = &esp_algorithms[sav->alg_enc]; + algo = esp_algorithm_lookup(sav->alg_enc); if (algo && algo->ivlen) - sav->ivlen = (*algo->ivlen)(sav); + sav->ivlen = (*algo->ivlen)(algo, sav); if (sav->ivlen == 0) break; KMALLOC(sav->iv, caddr_t, sav->ivlen); @@ -2812,20 +3057,11 @@ key_setsaval(sav, m, mhp) } /* initialize */ - { - int i; - u_int8_t *p = (u_int8_t *)sav->iv; - for (i = 0; i < sav->ivlen; i++) - p[i] = key_random() & 0xff; - } - break; -#else - break; + key_randomfill(sav->iv, sav->ivlen); #endif + break; case SADB_SATYPE_AH: -#if 1 /*nonstandard*/ case SADB_X_SATYPE_IPCOMP: -#endif break; default: #ifdef IPSEC_DEBUG @@ -2835,8 +3071,9 @@ key_setsaval(sav, m, mhp) goto fail; } - /* reset tick */ - sav->tick = 0; + /* reset created */ + microtime(&tv); + sav->created = tv.tv_sec; /* make lifetime for CURRENT */ KMALLOC(sav->lft_c, struct sadb_lifetime *, @@ -2900,63 +3137,42 @@ key_setsaval(sav, m, mhp) } } -#if notyet - /* pre-processing for DES */ - switch (sav->alg_enc) { - case SADB_EALG_DESCBC: - if (des_key_sched((C_Block *)_KEYBUF(sav->key_enc), - (des_key_schedule)sav->misc1) != 0) { -#ifdef IPSEC_DEBUG - printf("key_setsaval: error des_key_sched.\n"); -#endif - sav->misc1 = NULL; - /* THROUGH */ - } - break; - case SADB_EALG_3DESCBC: - if (des_key_sched((C_Block *)_KEYBUF(sav->key_enc), - (des_key_schedule)sav->misc1) != 0 - || des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), - (des_key_schedule)sav->misc2) != 0 - || des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), - (des_key_schedule)sav->misc3) != 0) { -#ifdef IPSEC_DEBUG - printf("key_setsaval: error des_key_sched.\n"); -#endif - sav->misc1 = NULL; - sav->misc2 = NULL; - sav->misc3 = NULL; - /* THROUGH */ - } - } -#endif - return 0; fail: /* initialization */ - if (sav->replay != NULL) + if (sav->replay != NULL) { keydb_delsecreplay(sav->replay); - if (sav->key_auth != NULL) + sav->replay = NULL; + } + if (sav->key_auth != NULL) { KFREE(sav->key_auth); - if (sav->key_enc != NULL) + sav->key_auth = NULL; + } + if (sav->key_enc != NULL) { KFREE(sav->key_enc); - if (sav->iv != NULL) + sav->key_enc = NULL; + } + if (sav->sched) { + KFREE(sav->sched); + sav->sched = NULL; + } + if (sav->iv != NULL) { KFREE(sav->iv); - if (sav->lft_c != NULL) + sav->iv = NULL; + } + if (sav->lft_c != NULL) { KFREE(sav->lft_c); - if (sav->lft_h != NULL) + sav->lft_c = NULL; + } + if (sav->lft_h != NULL) { KFREE(sav->lft_h); - if (sav->lft_s != NULL) + sav->lft_h = NULL; + } + if (sav->lft_s != NULL) { KFREE(sav->lft_s); -#if notyet - if (sav->misc1 != NULL) - KFREE(sav->misc1); - if (sav->misc2 != NULL) - KFREE(sav->misc2); - if (sav->misc3 != NULL) - KFREE(sav->misc3); -#endif + sav->lft_s = NULL; + } return error; } @@ -2977,11 +3193,17 @@ key_mature(sav) mature = 0; /* check SPI value */ - if (ntohl(sav->spi) >= 0 && ntohl(sav->spi) <= 255) { + switch (sav->sah->saidx.proto) { + case IPPROTO_ESP: + case IPPROTO_AH: + if (ntohl(sav->spi) >= 0 && ntohl(sav->spi) <= 255) { #ifdef IPSEC_DEBUG - printf("key_mature: illegal range of SPI %d.\n", sav->spi); + printf("key_mature: illegal range of SPI %u.\n", + (u_int32_t)ntohl(sav->spi)); #endif - return EINVAL; + return EINVAL; + } + break; } /* check satype */ @@ -2996,7 +3218,10 @@ key_mature(sav) #endif return EINVAL; } - checkmask = 3; + if (sav->alg_auth == SADB_AALG_NONE) + checkmask = 1; + else + checkmask = 3; mustmask = 1; break; case IPPROTO_AH: @@ -3018,7 +3243,6 @@ key_mature(sav) checkmask = 2; mustmask = 2; break; -#if 1 /*nonstandard*/ case IPPROTO_IPCOMP: if (sav->alg_auth != SADB_AALG_NONE) { #ifdef IPSEC_DEBUG @@ -3037,7 +3261,6 @@ key_mature(sav) checkmask = 4; mustmask = 4; break; -#endif default: #ifdef IPSEC_DEBUG printf("key_mature: Invalid satype.\n"); @@ -3047,19 +3270,11 @@ key_mature(sav) /* check authentication algorithm */ if ((checkmask & 2) != 0) { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; int keylen; - /* XXX: should use algorithm map to check. */ - switch (sav->alg_auth) { - case SADB_AALG_NONE: - case SADB_AALG_MD5HMAC: - case SADB_AALG_SHA1HMAC: - case SADB_AALG_MD5: - case SADB_AALG_SHA: - case SADB_AALG_NULL: - break; - default: + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { #ifdef IPSEC_DEBUG printf("key_mature: " "unknown authentication algorithm.\n"); @@ -3068,8 +3283,6 @@ key_mature(sav) } /* algorithm-dependent check */ - algo = &ah_algorithms[sav->alg_auth]; - if (sav->key_auth) keylen = sav->key_auth->sadb_key_bits; else @@ -3102,19 +3315,11 @@ key_mature(sav) /* check encryption algorithm */ if ((checkmask & 1) != 0) { #ifdef IPSEC_ESP - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int keylen; - switch (sav->alg_enc) { - case SADB_EALG_NONE: - case SADB_EALG_DESCBC: - case SADB_EALG_3DESCBC: - case SADB_EALG_NULL: - case SADB_EALG_BLOWFISHCBC: - case SADB_EALG_CAST128CBC: - case SADB_EALG_RC5CBC: - break; - default: + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { #ifdef IPSEC_DEBUG printf("key_mature: unknown encryption algorithm.\n"); #endif @@ -3122,8 +3327,6 @@ key_mature(sav) } /* algorithm-dependent check */ - algo = &esp_algorithms[sav->alg_enc]; - if (sav->key_enc) keylen = sav->key_enc->sadb_key_bits; else @@ -3161,28 +3364,13 @@ key_mature(sav) /* check compression algorithm */ if ((checkmask & 4) != 0) { - struct ipcomp_algorithm *algo; - - switch (sav->alg_enc) { - case SADB_X_CALG_NONE: - case SADB_X_CALG_OUI: - case SADB_X_CALG_DEFLATE: - case SADB_X_CALG_LZS: - break; - default: -#ifdef IPSEC_DEBUG - printf("key_mature: unknown compression algorithm.\n"); -#endif - return EINVAL; - } + const struct ipcomp_algorithm *algo; /* algorithm-dependent check */ - algo = &ipcomp_algorithms[sav->alg_enc]; - - if (!(algo->compress && algo->decompress)) { + algo = ipcomp_algorithm_lookup(sav->alg_enc); + if (!algo) { #ifdef IPSEC_DEBUG - printf("key_mature: " - "unsupported compression algorithm.\n"); + printf("key_mature: unknown compression algorithm.\n"); #endif return EINVAL; } @@ -3752,7 +3940,50 @@ key_cmpsaidx_withmode(saidx0, saidx1) } /* - * compare two secasindex structure without mode. + * compare two secasindex structure without mode, but think reqid. + * don't compare port. + * IN: + * saidx0: source, it is often in SAD. + * saidx1: object, it is often from user. + * OUT: + * 1 : equal + * 0 : not equal + */ +static int +key_cmpsaidx_withoutmode2(saidx0, saidx1) + struct secasindex *saidx0, *saidx1; +{ + /* sanity */ + if (saidx0 == NULL && saidx1 == NULL) + return 1; + + if (saidx0 == NULL || saidx1 == NULL) + return 0; + + if (saidx0->proto != saidx1->proto) + return 0; + + /* + * If reqid of SPD is non-zero, unique SA is required. + * The result must be of same reqid in this case. + */ + if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid) + return 0; + + if (key_sockaddrcmp((struct sockaddr *)&saidx0->src, + (struct sockaddr *)&saidx1->src, 0) != 0) { + return 0; + } + if (key_sockaddrcmp((struct sockaddr *)&saidx0->dst, + (struct sockaddr *)&saidx1->dst, 0) != 0) { + return 0; + } + + return 1; +} + +/* + * compare two secasindex structure without both mode and reqid. * don't compare port. * IN: * saidx0: source, it is often in SAD. @@ -3870,7 +4101,13 @@ key_cmpspidx_withmask(spidx0, spidx1) && satosin6(&spidx0->src)->sin6_port != satosin6(&spidx1->src)->sin6_port) return 0; - if (satosin6(&spidx0->src)->sin6_scope_id != + /* + * scope_id check. if sin6_scope_id is 0, we regard it + * as a wildcard scope, which matches any scope zone ID. + */ + if (satosin6(&spidx0->src)->sin6_scope_id && + satosin6(&spidx1->src)->sin6_scope_id && + satosin6(&spidx0->src)->sin6_scope_id != satosin6(&spidx1->src)->sin6_scope_id) return 0; if (!key_bbcmp((caddr_t)&satosin6(&spidx0->src)->sin6_addr, @@ -3899,7 +4136,13 @@ key_cmpspidx_withmask(spidx0, spidx1) && satosin6(&spidx0->dst)->sin6_port != satosin6(&spidx1->dst)->sin6_port) return 0; - if (satosin6(&spidx0->dst)->sin6_scope_id != + /* + * scope_id check. if sin6_scope_id is 0, we regard it + * as a wildcard scope, which matches any scope zone ID. + */ + if (satosin6(&spidx0->src)->sin6_scope_id && + satosin6(&spidx1->src)->sin6_scope_id && + satosin6(&spidx0->dst)->sin6_scope_id != satosin6(&spidx1->dst)->sin6_scope_id) return 0; if (!key_bbcmp((caddr_t)&satosin6(&spidx0->dst)->sin6_addr, @@ -4005,12 +4248,16 @@ key_bbcmp(p1, p2, bits) * time handler. * scanning SPD and SAD to check status for each entries, * and do to remove or to expire. + * XXX: year 2038 problem may remain. */ void key_timehandler(void) { u_int dir; int s; + struct timeval tv; + + microtime(&tv); s = splnet(); /*called from softclock()*/ @@ -4025,8 +4272,23 @@ key_timehandler(void) nextsp = LIST_NEXT(sp, chain); - if (sp->state == IPSEC_SPSTATE_DEAD) + if (sp->state == IPSEC_SPSTATE_DEAD) { key_freesp(sp); + continue; + } + + if (sp->lifetime == 0 && sp->validtime == 0) + continue; + + /* the deletion will occur next time */ + if ((sp->lifetime + && tv.tv_sec - sp->created > sp->lifetime) + || (sp->validtime + && tv.tv_sec - sp->lastused > sp->validtime)) { + sp->state = IPSEC_SPSTATE_DEAD; + key_spdexpire(sp); + continue; + } } } } @@ -4055,9 +4317,7 @@ key_timehandler(void) nextsav = LIST_NEXT(sav, chain); - sav->tick++; - - if (key_larval_lifetime < sav->tick) { + if (tv.tv_sec - sav->created > key_larval_lifetime) { key_freesav(sav); } } @@ -4072,8 +4332,6 @@ key_timehandler(void) nextsav = LIST_NEXT(sav, chain); - sav->tick++; - /* we don't need to check. */ if (sav->lft_s == NULL) continue; @@ -4087,9 +4345,9 @@ key_timehandler(void) continue; } - /* compare SOFT lifetime and tick */ + /* check SOFT lifetime */ if (sav->lft_s->sadb_lifetime_addtime != 0 - && sav->lft_s->sadb_lifetime_addtime < sav->tick) { + && tv.tv_sec - sav->created > sav->lft_s->sadb_lifetime_addtime) { /* * check SA to be used whether or not. * when SA hasn't been used, delete it. @@ -4134,8 +4392,6 @@ key_timehandler(void) nextsav = LIST_NEXT(sav, chain); - sav->tick++; - /* we don't need to check. */ if (sav->lft_h == NULL) continue; @@ -4149,9 +4405,8 @@ key_timehandler(void) continue; } - /* compare HARD lifetime and tick */ if (sav->lft_h->sadb_lifetime_addtime != 0 - && sav->lft_h->sadb_lifetime_addtime < sav->tick) { + && tv.tv_sec - sav->created > sav->lft_h->sadb_lifetime_addtime) { key_sa_chgstate(sav, SADB_SASTATE_DEAD); key_freesav(sav); sav = NULL; @@ -4159,7 +4414,7 @@ key_timehandler(void) #if 0 /* XXX Should we keep to send expire message until HARD lifetime ? */ else if (sav->lft_s != NULL && sav->lft_s->sadb_lifetime_addtime != 0 - && sav->lft_s->sadb_lifetime_addtime < sav->tick) { + && tv.tv_sec - sav->created > sav->lft_s->sadb_lifetime_addtime) { /* * XXX: should be checked to be * installed the valid SA. @@ -4220,9 +4475,8 @@ key_timehandler(void) nextacq = LIST_NEXT(acq, chain); - acq->tick++; - - if (key_blockacq_lifetime < acq->tick && __LIST_CHAINED(acq)) { + if (tv.tv_sec - acq->created > key_blockacq_lifetime + && __LIST_CHAINED(acq)) { LIST_REMOVE(acq, chain); KFREE(acq); } @@ -4240,9 +4494,8 @@ key_timehandler(void) nextacq = LIST_NEXT(acq, chain); - acq->tick++; - - if (key_blockacq_lifetime < acq->tick && __LIST_CHAINED(acq)) { + if (tv.tv_sec - acq->created > key_blockacq_lifetime + && __LIST_CHAINED(acq)) { LIST_REMOVE(acq, chain); KFREE(acq); } @@ -4257,7 +4510,7 @@ key_timehandler(void) #ifndef IPSEC_DEBUG2 /* do exchange to tick time !! */ - (void)timeout((void *)key_timehandler, (void *)0, 100); + (void)timeout((void *)key_timehandler, (void *)0, hz); #endif /* IPSEC_DEBUG2 */ splx(s); @@ -4279,19 +4532,41 @@ key_srandom() return; } -/* - * to initialize a seed for random() - */ -static u_long +u_long key_random() { u_long value; - value = random(); - + key_randomfill(&value, sizeof(value)); return value; } +void +key_randomfill(p, l) + void *p; + size_t l; +{ + size_t n; + u_long v; + static int warn = 1; + + n = 0; + n = (size_t)read_random(p, (u_int)l); + /* last resort */ + while (n < l) { + v = random(); + bcopy(&v, (u_int8_t *)p + n, + l - n < sizeof(v) ? l - n : sizeof(v)); + n += sizeof(v); + + if (warn) { + printf("WARNING: pseudo-random number generator " + "used for IPsec processing\n"); + warn = 0; + } + } +} + /* * map SADB_SATYPE_* to IPPROTO_*. * if satype == SADB_SATYPE then satype is mapped to ~0. @@ -4309,11 +4584,9 @@ key_satype2proto(satype) return IPPROTO_AH; case SADB_SATYPE_ESP: return IPPROTO_ESP; -#if 1 /*nonstandard*/ case SADB_X_SATYPE_IPCOMP: return IPPROTO_IPCOMP; break; -#endif default: return 0; } @@ -4334,11 +4607,9 @@ key_proto2satype(proto) return SADB_SATYPE_AH; case IPPROTO_ESP: return SADB_SATYPE_ESP; -#if 1 /*nonstandard*/ case IPPROTO_IPCOMP: return SADB_X_SATYPE_IPCOMP; break; -#endif default: return 0; } @@ -4481,8 +4752,10 @@ key_getspi(so, m, mhp) if (mhp->msg->sadb_msg_seq != 0) { struct secacq *acq; if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) != NULL) { - /* reset counter in order to deletion by timehander. */ - acq->tick = key_blockacq_lifetime; + /* reset counter in order to deletion by timehandler. */ + struct timeval tv; + microtime(&tv); + acq->created = tv.tv_sec; acq->count = 0; } } @@ -5047,6 +5320,7 @@ key_setident(sah, m, mhp) KMALLOC(sah->identd, struct sadb_ident *, iddstlen); if (sah->identd == NULL) { KFREE(sah->idents); + sah->idents = NULL; #ifdef IPSEC_DEBUG printf("key_setident: No more memory.\n"); #endif @@ -5094,6 +5368,9 @@ key_getmsgbuf_x1(m, mhp) return n; } +static int key_delete_all __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *, u_int16_t)); + /* * SADB_DELETE processing * receive @@ -5130,16 +5407,15 @@ key_delete(so, m, mhp) return key_senderror(so, m, EINVAL); } - if (mhp->ext[SADB_EXT_SA] == NULL || - mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) { #ifdef IPSEC_DEBUG printf("key_delete: invalid message is passed.\n"); #endif return key_senderror(so, m, EINVAL); } - if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) || - mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + + if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { #ifdef IPSEC_DEBUG printf("key_delete: invalid message is passed.\n"); @@ -5147,6 +5423,23 @@ key_delete(so, m, mhp) return key_senderror(so, m, EINVAL); } + if (mhp->ext[SADB_EXT_SA] == NULL) { + /* + * Caller wants us to delete all non-LARVAL SAs + * that match the src/dst. This is used during + * IKE INITIAL-CONTACT. + */ +#ifdef IPSEC_DEBUG + printf("key_delete: doing delete all.\n"); +#endif + return key_delete_all(so, m, mhp, proto); + } else if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa)) { +#ifdef IPSEC_DEBUG + printf("key_delete: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } + sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); @@ -5202,6 +5495,84 @@ key_delete(so, m, mhp) } /* + * delete all SAs for src/dst. Called from key_delete(). + */ +static int +key_delete_all(so, m, mhp, proto) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; + u_int16_t proto; +{ + struct sadb_address *src0, *dst0; + struct secasindex saidx; + struct secashead *sah; + struct secasvar *sav, *nextsav; + u_int stateidx, state; + + src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); + + /* XXX boundary check against sa_len */ + KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); + + LIST_FOREACH(sah, &sahtree, chain) { + if (sah->state == SADB_SASTATE_DEAD) + continue; + if (key_cmpsaidx_withoutmode(&sah->saidx, &saidx) == 0) + continue; + + /* Delete all non-LARVAL SAs. */ + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_alive); + stateidx++) { + state = saorder_state_alive[stateidx]; + if (state == SADB_SASTATE_LARVAL) + continue; + for (sav = LIST_FIRST(&sah->savtree[state]); + sav != NULL; sav = nextsav) { + nextsav = LIST_NEXT(sav, chain); + /* sanity check */ + if (sav->state != state) { +#ifdef IPSEC_DEBUG + printf("key_delete_all: " + "invalid sav->state " + "(queue: %d SA: %d)\n", + state, sav->state); +#endif + continue; + } + + key_sa_chgstate(sav, SADB_SASTATE_DEAD); + key_freesav(sav); + } + } + } + { + struct mbuf *n; + struct sadb_msg *newmsg; + + /* create new sadb_msg to reply. */ + n = key_gather_mbuf(m, mhp, 1, 3, SADB_EXT_RESERVED, + SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + if (!n) + return key_senderror(so, m, ENOBUFS); + + if (n->m_len < sizeof(struct sadb_msg)) { + n = m_pullup(n, sizeof(struct sadb_msg)); + if (n == NULL) + return key_senderror(so, m, ENOBUFS); + } + newmsg = mtod(n, struct sadb_msg *); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); + + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); + } +} + +/* * SADB_GET processing * receive * <base, SA(*), address(SD)> @@ -5304,17 +5675,32 @@ key_get(so, m, mhp) } } +/* XXX make it sysctl-configurable? */ +static void +key_getcomb_setlifetime(comb) + struct sadb_comb *comb; +{ + + comb->sadb_comb_soft_allocations = 1; + comb->sadb_comb_hard_allocations = 1; + comb->sadb_comb_soft_bytes = 0; + comb->sadb_comb_hard_bytes = 0; + comb->sadb_comb_hard_addtime = 86400; /* 1 day */ + comb->sadb_comb_soft_addtime = comb->sadb_comb_soft_addtime * 80 / 100; + comb->sadb_comb_soft_usetime = 28800; /* 8 hours */ + comb->sadb_comb_hard_usetime = comb->sadb_comb_hard_usetime * 80 / 100; +} + #ifdef IPSEC_ESP /* * XXX reorder combinations by preference * XXX no idea if the user wants ESP authentication or not - * XXX lifetime - should be in policy? */ static struct mbuf * key_getcomb_esp() { struct sadb_comb *comb; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; struct mbuf *result = NULL, *m, *n; int encmin; int i, off, o; @@ -5322,8 +5708,10 @@ key_getcomb_esp() const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb)); m = NULL; - for (i = 1; i < SADB_EALG_MAX; i++) { - algo = &esp_algorithms[i]; + for (i = 1; i <= SADB_EALG_MAX; i++) { + algo = esp_algorithm_lookup(i); + if (!algo) + continue; if (algo->keymax < ipsec_esp_keymin) continue; @@ -5365,6 +5753,8 @@ key_getcomb_esp() goto fail; } comb = (struct sadb_comb *)(mtod(n, caddr_t) + o); + bzero(comb, sizeof(*comb)); + key_getcomb_setlifetime(comb); comb->sadb_comb_encrypt = i; comb->sadb_comb_encrypt_minbits = encmin; comb->sadb_comb_encrypt_maxbits = algo->keymax; @@ -5387,26 +5777,27 @@ key_getcomb_esp() /* * XXX reorder combinations by preference - * XXX lifetime - should be in policy? */ static struct mbuf * key_getcomb_ah() { struct sadb_comb *comb; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; struct mbuf *m; int min; int i; const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb)); m = NULL; - for (i = 1; i < SADB_AALG_MAX; i++) { + for (i = 1; i <= SADB_AALG_MAX; i++) { #if 1 /* we prefer HMAC algorithms, not old algorithms */ if (i != SADB_AALG_SHA1HMAC && i != SADB_AALG_MD5HMAC) continue; #endif - algo = &ah_algorithms[i]; + algo = ah_algorithm_lookup(i); + if (!algo) + continue; if (algo->keymax < ipsec_ah_keymin) continue; @@ -5433,6 +5824,7 @@ key_getcomb_ah() comb = mtod(m, struct sadb_comb *); bzero(comb, sizeof(*comb)); + key_getcomb_setlifetime(comb); comb->sadb_comb_auth = i; comb->sadb_comb_auth_minbits = min; comb->sadb_comb_auth_maxbits = algo->keymax; @@ -5442,6 +5834,51 @@ key_getcomb_ah() } /* + * not really an official behavior. discussed in pf_key@inner.net in Sep2000. + * XXX reorder combinations by preference + */ +static struct mbuf * +key_getcomb_ipcomp() +{ + struct sadb_comb *comb; + const struct ipcomp_algorithm *algo; + struct mbuf *m; + int i; + const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb)); + + m = NULL; + for (i = 1; i <= SADB_X_CALG_MAX; i++) { + algo = ipcomp_algorithm_lookup(i); + if (!algo) + continue; + + if (!m) { +#ifdef DIAGNOSTIC + if (l > MLEN) + panic("assumption failed in key_getcomb_ipcomp"); +#endif + MGET(m, M_DONTWAIT, MT_DATA); + if (m) { + M_ALIGN(m, l); + m->m_len = l; + m->m_next = NULL; + } + } else + M_PREPEND(m, l, M_DONTWAIT); + if (!m) + return NULL; + + comb = mtod(m, struct sadb_comb *); + bzero(comb, sizeof(*comb)); + key_getcomb_setlifetime(comb); + comb->sadb_comb_encrypt = i; + /* what should we set into sadb_comb_*_{min,max}bits? */ + } + + return m; +} + +/* * XXX no way to pass mode (transport/tunnel) to userland * XXX replay checking? * XXX sysctl interface to ipsec_{ah,esp}_keymin @@ -5464,6 +5901,9 @@ key_getprop(saidx) case IPPROTO_AH: m = key_getcomb_ah(); break; + case IPPROTO_IPCOMP: + m = key_getcomb_ipcomp(); + break; default: return NULL; } @@ -5490,7 +5930,7 @@ key_getprop(saidx) /* * SADB_ACQUIRE processing called by key_checkrequest() and key_acquire2(). * send - * <base, SA, address(SD), (address(P)), + * <base, SA, address(SD), (address(P)), x_policy, * (identity(SD),) (sensitivity,) proposal> * to KMD, and expect to receive * <base> with SADB_ACQUIRE if error occured, @@ -5498,7 +5938,10 @@ key_getprop(saidx) * <base, src address, dst address, (SPI range)> with SADB_GETSPI * from KMD by PF_KEY. * - * sensitivity is not supported. + * XXX x_policy is outside of RFC2367 (KAME extension). + * XXX sensitivity is not supported. + * XXX for ipcomp, RFC2367 does not define how to fill in proposal. + * see comment for key_getcomb_ipcomp(). * * OUT: * 0 : succeed @@ -5639,11 +6082,24 @@ key_acquire(saidx, sp) /* create proposal/combination extension */ m = key_getprop(saidx); +#if 0 + /* + * spec conformant: always attach proposal/combination extension, + * the problem is that we have no way to attach it for ipcomp, + * due to the way sadb_comb is declared in RFC2367. + */ if (!m) { error = ENOBUFS; goto fail; } m_cat(result, m); +#else + /* + * outside of spec; make proposal/combination extension optional. + */ + if (m) + m_cat(result, m); +#endif if ((result->m_flags & M_PKTHDR) == 0) { error = EINVAL; @@ -5679,6 +6135,7 @@ key_newacq(saidx) struct secasindex *saidx; { struct secacq *newacq; + struct timeval tv; /* get new entry */ KMALLOC(newacq, struct secacq *, sizeof(struct secacq)); @@ -5693,7 +6150,8 @@ key_newacq(saidx) /* copy secindex */ bcopy(saidx, &newacq->saidx, sizeof(newacq->saidx)); newacq->seq = (acq_seq == ~0 ? 1 : ++acq_seq); - newacq->tick = 0; + microtime(&tv); + newacq->created = tv.tv_sec; newacq->count = 0; return newacq; @@ -5733,6 +6191,7 @@ key_newspacq(spidx) struct secpolicyindex *spidx; { struct secspacq *acq; + struct timeval tv; /* get new entry */ KMALLOC(acq, struct secspacq *, sizeof(struct secspacq)); @@ -5746,7 +6205,8 @@ key_newspacq(spidx) /* copy secindex */ bcopy(spidx, &acq->spidx, sizeof(acq->spidx)); - acq->tick = 0; + microtime(&tv); + acq->created = tv.tv_sec; acq->count = 0; return acq; @@ -5778,7 +6238,7 @@ key_getspacq(spidx) * <base, address(SD), (address(P),) (identity(SD),) (sensitivity,) proposal> * to the socket. * - * m will always e freed. + * m will always be freed. */ static int key_acquire2(so, m, mhp) @@ -5805,6 +6265,7 @@ key_acquire2(so, m, mhp) if (mhp->msg->sadb_msg_len == PFKEY_UNIT64(sizeof(struct sadb_msg))) { #ifndef IPSEC_NONBLOCK_ACQUIRE struct secacq *acq; + struct timeval tv; /* check sequence number */ if (mhp->msg->sadb_msg_seq == 0) { @@ -5816,16 +6277,17 @@ key_acquire2(so, m, mhp) } if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) == NULL) { -#ifdef IPSEC_DEBUG - printf("key_acquire2: " - "invalid sequence number is passed.\n"); -#endif + /* + * the specified larval SA is already gone, or we got + * a bogus sequence number. we can silently ignore it. + */ m_freem(m); return 0; } /* reset acq counter in order to deletion by timehander. */ - acq->tick = key_blockacq_lifetime; + microtime(&tv); + acq->created = tv.tv_sec; acq->count = 0; #endif m_freem(m); @@ -5906,7 +6368,7 @@ key_acquire2(so, m, mhp) * to KMD by PF_KEY. * If socket is detached, must free from regnode. * - * m will always e freed. + * m will always be freed. */ static int key_register(so, m, mhp) @@ -5965,13 +6427,21 @@ key_register(so, m, mhp) struct sadb_alg *alg; /* create new sadb_msg to reply. */ - alen = sizeof(struct sadb_supported) - + ((SADB_AALG_MAX - 1) * sizeof(struct sadb_alg)); -#ifdef IPSEC_ESP - elen = sizeof(struct sadb_supported) - + ((SADB_EALG_MAX - 1) * sizeof(struct sadb_alg)); -#else + alen = 0; + for (i = 1; i <= SADB_AALG_MAX; i++) { + if (ah_algorithm_lookup(i)) + alen += sizeof(struct sadb_alg); + } + if (alen) + alen += sizeof(struct sadb_supported); elen = 0; +#ifdef IPSEC_ESP + for (i = 1; i <= SADB_EALG_MAX; i++) { + if (esp_algorithm_lookup(i)) + elen += sizeof(struct sadb_alg); + } + if (elen) + elen += sizeof(struct sadb_supported); #endif len = sizeof(struct sadb_msg) + alen + elen; @@ -6007,10 +6477,12 @@ key_register(so, m, mhp) sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; off += PFKEY_ALIGN8(sizeof(*sup)); - for (i = 1; i < SADB_AALG_MAX; i++) { - struct ah_algorithm *aalgo; + for (i = 1; i <= SADB_AALG_MAX; i++) { + const struct ah_algorithm *aalgo; - aalgo = &ah_algorithms[i]; + aalgo = ah_algorithm_lookup(i); + if (!aalgo) + continue; alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); alg->sadb_alg_id = i; alg->sadb_alg_ivlen = 0; @@ -6028,10 +6500,12 @@ key_register(so, m, mhp) sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT; off += PFKEY_ALIGN8(sizeof(*sup)); - for (i = 1; i < SADB_EALG_MAX; i++) { - struct esp_algorithm *ealgo; + for (i = 1; i <= SADB_EALG_MAX; i++) { + const struct esp_algorithm *ealgo; - ealgo = &esp_algorithms[i]; + ealgo = esp_algorithm_lookup(i); + if (!ealgo) + continue; alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); alg->sadb_alg_id = i; if (ealgo && ealgo->ivlen) { @@ -6039,7 +6513,8 @@ key_register(so, m, mhp) * give NULL to get the value preferred by * algorithm XXX SADB_X_EXT_DERIV ? */ - alg->sadb_alg_ivlen = (*ealgo->ivlen)(NULL); + alg->sadb_alg_ivlen = + (*ealgo->ivlen)(ealgo, NULL); } else alg->sadb_alg_ivlen = 0; alg->sadb_alg_minbits = ealgo->keymin; @@ -6190,13 +6665,17 @@ key_expire(sav) } m_cat(result, m); - if ((result->m_flags & M_PKTHDR) == 0) + if ((result->m_flags & M_PKTHDR) == 0) { + error = EINVAL; goto fail; + } if (result->m_len < sizeof(struct sadb_msg)) { result = m_pullup(result, sizeof(struct sadb_msg)); - if (result == NULL) + if (result == NULL) { + error = ENOBUFS; goto fail; + } } result->m_pkthdr.len = 0; @@ -6608,9 +7087,7 @@ key_parse(m, so) break; case SADB_SATYPE_AH: case SADB_SATYPE_ESP: -#if 1 /*nonstandard*/ case SADB_X_SATYPE_IPCOMP: -#endif switch (msg->sadb_msg_type) { case SADB_X_SPDADD: case SADB_X_SPDDELETE: @@ -6973,8 +7450,10 @@ key_init() LIST_INIT(&spacqtree); /* system default */ +#ifdef INET ip4_def_policy.policy = IPSEC_POLICY_NONE; ip4_def_policy.refcnt++; /*never reclaim this*/ +#endif #ifdef INET6 ip6_def_policy.policy = IPSEC_POLICY_NONE; ip6_def_policy.refcnt++; /*never reclaim this*/ @@ -7167,6 +7646,16 @@ key_sa_chgstate(sav, state) LIST_INSERT_HEAD(&sav->sah->savtree[state], sav, chain); } +void +key_sa_stir_iv(sav) + struct secasvar *sav; +{ + + if (!sav->iv) + panic("key_sa_stir_iv called with sav == NULL"); + key_randomfill(sav->iv, sav->ivlen); +} + /* XXX too much? */ static struct mbuf * key_alloc_mbuf(l) diff --git a/sys/netkey/key.h b/sys/netkey/key.h index 65a275533093..20bcaa0eec44 100644 --- a/sys/netkey/key.h +++ b/sys/netkey/key.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: key.h,v 1.17 2000/06/12 07:01:13 itojun Exp $ */ +/* $KAME: key.h,v 1.20 2001/03/22 08:09:32 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -47,6 +47,8 @@ struct sadb_msg; struct sadb_x_policy; extern struct secpolicy *key_allocsp __P((struct secpolicyindex *, u_int)); +extern struct secpolicy *key_gettunnel __P((struct sockaddr *, + struct sockaddr *, struct sockaddr *, struct sockaddr *)); extern int key_checkrequest __P((struct ipsecrequest *isr, struct secasindex *)); extern struct secasvar *key_allocsa __P((u_int, caddr_t, caddr_t, @@ -61,6 +63,8 @@ extern struct mbuf *key_sp2msg __P((struct secpolicy *)); extern int key_ismyaddr __P((struct sockaddr *)); extern int key_spdacquire __P((struct secpolicy *)); extern void key_timehandler __P((void)); +extern u_long key_random __P((void)); +extern void key_randomfill __P((void *, size_t)); extern void key_freereg __P((struct socket *)); extern int key_parse __P((struct mbuf *, struct socket *)); extern void key_init __P((void)); @@ -68,6 +72,7 @@ extern int key_checktunnelsanity __P((struct secasvar *, u_int, caddr_t, caddr_t)); extern void key_sa_recordxfer __P((struct secasvar *, struct mbuf *)); extern void key_sa_routechange __P((struct sockaddr *)); +extern void key_sa_stir_iv __P((struct secasvar *)); #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_SECA); diff --git a/sys/netkey/key_debug.c b/sys/netkey/key_debug.c index 3e8b12b30681..76a59f321821 100644 --- a/sys/netkey/key_debug.c +++ b/sys/netkey/key_debug.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: key_debug.c,v 1.23 2000/07/04 04:08:15 itojun Exp $ */ +/* $KAME: key_debug.c,v 1.25 2000/07/24 13:23:12 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -41,17 +41,14 @@ #ifdef _KERNEL #include <sys/systm.h> #include <sys/mbuf.h> +#include <sys/queue.h> #endif #include <sys/socket.h> #include <net/route.h> #include <netkey/key_var.h> -#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #include <netinet/in.h> #include <netinet6/ipsec.h> diff --git a/sys/netkey/key_debug.h b/sys/netkey/key_debug.h index 383a0e8f5341..f75a5353692a 100644 --- a/sys/netkey/key_debug.h +++ b/sys/netkey/key_debug.h @@ -33,6 +33,8 @@ #ifndef _NETKEY_KEY_DEBUG_H_ #define _NETKEY_KEY_DEBUG_H_ +#if !defined(_KERNEL) || (defined(_KERNEL) && defined(IPSEC_DEBUG)) + /* debug flags */ #define KEYDEBUG_STAMP 0x00000001 /* path */ #define KEYDEBUG_DATA 0x00000002 /* data */ @@ -84,5 +86,10 @@ extern void kdebug_sockaddr __P((struct sockaddr *)); extern void ipsec_hexdump __P((caddr_t, int)); extern void ipsec_bindump __P((caddr_t, int)); -#endif /* _NETKEY_KEY_DEBUG_H_ */ +#else + +#define KEYDEBUG(lev,arg) +#endif /*!defined(_KERNEL) || (defined(_KERNEL) && defined(IPSEC_DEBUG))*/ + +#endif /* _NETKEY_KEY_DEBUG_H_ */ diff --git a/sys/netkey/key_var.h b/sys/netkey/key_var.h index b7a62bacb990..4043a0385c49 100644 --- a/sys/netkey/key_var.h +++ b/sys/netkey/key_var.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: key_var.h,v 1.8 2000/05/24 17:28:23 itojun Exp $ */ +/* $KAME: key_var.h,v 1.9 2000/10/04 11:13:57 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -42,7 +42,10 @@ #define KEYCTL_LARVAL_LIFETIME 6 #define KEYCTL_BLOCKACQ_COUNT 7 #define KEYCTL_BLOCKACQ_LIFETIME 8 -#define KEYCTL_MAXID 9 +#define KEYCTL_ESP_KEYMIN 9 +#define KEYCTL_ESP_AUTH 10 +#define KEYCTL_AH_KEYMIN 11 +#define KEYCTL_MAXID 12 #define KEYCTL_NAMES { \ { 0, 0 }, \ @@ -54,8 +57,40 @@ { "larval_lifetime", CTLTYPE_INT }, \ { "blockacq_count", CTLTYPE_INT }, \ { "blockacq_lifetime", CTLTYPE_INT }, \ + { "esp_keymin", CTLTYPE_INT }, \ + { "ah_keymin", CTLTYPE_INT }, \ } +#ifdef IPSEC_DEBUG +#define KEYCTL_VARS { \ + 0, \ + &key_debug_level, \ + &key_spi_trycnt, \ + &key_spi_minval, \ + &key_spi_maxval, \ + &key_int_random, \ + &key_larval_lifetime, \ + &key_blockacq_count, \ + &key_blockacq_lifetime, \ + &ipsec_esp_keymin, \ + &ipsec_ah_keymin, \ +} +#else +#define KEYCTL_VARS { \ + 0, \ + 0, \ + &key_spi_trycnt, \ + &key_spi_minval, \ + &key_spi_maxval, \ + &key_int_random, \ + &key_larval_lifetime, \ + &key_blockacq_count, \ + &key_blockacq_lifetime, \ + &ipsec_esp_keymin, \ + &ipsec_ah_keymin, \ +} +#endif + #ifdef _KERNEL #define _ARRAYLEN(p) (sizeof(p)/sizeof(p[0])) #define _KEYLEN(key) ((u_int)((key)->sadb_key_bits >> 3)) diff --git a/sys/netkey/keydb.h b/sys/netkey/keydb.h index fcb478c1296d..1aff5beaaf87 100644 --- a/sys/netkey/keydb.h +++ b/sys/netkey/keydb.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: keydb.h,v 1.11 2000/06/15 12:20:50 sakane Exp $ */ +/* $KAME: keydb.h,v 1.14 2000/08/02 17:58:26 sakane Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -82,14 +82,11 @@ struct secasvar { struct sadb_key *key_enc; /* Key for Encryption */ caddr_t iv; /* Initilization Vector */ u_int ivlen; /* length of IV */ -#if 0 - caddr_t misc1; - caddr_t misc2; - caddr_t misc3; -#endif + void *sched; /* intermediate encryption key */ + size_t schedlen; struct secreplay *replay; /* replay prevention */ - u_int32_t tick; /* for lifetime */ + long created; /* for lifetime */ struct sadb_lifetime *lft_c; /* CURRENT lifetime, it's constant. */ struct sadb_lifetime *lft_h; /* HARD lifetime */ @@ -126,7 +123,7 @@ struct secacq { struct secasindex saidx; u_int32_t seq; /* sequence number */ - u_int32_t tick; /* for lifetime */ + long created; /* for lifetime */ int count; /* for lifetime */ }; #endif diff --git a/sys/netkey/keysock.c b/sys/netkey/keysock.c index 68c70c89a149..5eab1471eaa2 100644 --- a/sys/netkey/keysock.c +++ b/sys/netkey/keysock.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: keysock.c,v 1.22 2000/05/23 13:19:21 itojun Exp $ */ +/* $KAME: keysock.c,v 1.24 2000/12/03 00:41:48 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -56,11 +56,7 @@ #include <netkey/keydb.h> #include <netkey/key.h> #include <netkey/keysock.h> -#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #include <machine/stdarg.h> @@ -158,6 +154,8 @@ key_sendup0(rp, m, promisc) struct mbuf *m; int promisc; { + int error; + if (promisc) { struct sadb_msg *pmsg; @@ -184,17 +182,18 @@ key_sendup0(rp, m, promisc) pfkeystat.in_msgtype[pmsg->sadb_msg_type]++; } - if (!sbappendaddr(&rp->rcb_socket->so_rcv, - (struct sockaddr *)&key_src, m, NULL)) { + if (!sbappendaddr(&rp->rcb_socket->so_rcv, (struct sockaddr *)&key_src, + m, NULL)) { #ifdef IPSEC_DEBUG printf("key_sendup0: sbappendaddr failed\n"); #endif pfkeystat.in_nomem++; m_freem(m); - return ENOBUFS; - } + error = ENOBUFS; + } else + error = 0; sorwakeup(rp->rcb_socket); - return 0; + return error; } /* XXX this interface should be obsoleted. */ diff --git a/sys/netsmb/smb_crypt.c b/sys/netsmb/smb_crypt.c index e72a385304f9..89b6d9e6bb39 100644 --- a/sys/netsmb/smb_crypt.c +++ b/sys/netsmb/smb_crypt.c @@ -74,8 +74,8 @@ smb_E(const u_char *key, u_char *data, u_char *dest) kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe); kk[7] = key[6] << 1; ksp = malloc(sizeof(des_key_schedule), M_SMBTEMP, M_WAITOK); - des_set_key((C_Block*)kk, *ksp); - des_ecb_encrypt((C_Block*)data, (C_Block*)dest, *ksp, 1); + des_set_key((des_cblock *)kk, *ksp); + des_ecb_encrypt((des_cblock *)data, (des_cblock *)dest, *ksp, 1); free(ksp, M_SMBTEMP); } #endif diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index de7a3c944c80..da23e278f4aa 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -228,6 +228,20 @@ struct mbstat { u_long m_minclsize; /* min length of data to allocate a cluster */ u_long m_mlen; /* length of data in an mbuf */ u_long m_mhlen; /* length of data in a header mbuf */ + + u_quad_t m_exthdrget; /* # of calls to IP6_EXTHDR_GET */ + u_quad_t m_exthdrget0; /* # of calls to IP6_EXTHDR_GET0 */ + u_quad_t m_pulldowns; /* # of calls to m_pulldown */ + u_quad_t m_pulldown_copy; /* # of mbuf copies in m_pulldown */ + u_quad_t m_pulldown_alloc; /* # of mbuf allocs in m_pulldown */ + u_quad_t m_pullups; /* # of calls to m_pullup */ + u_quad_t m_pullup_copy; /* # of possible m_pullup copies */ + u_quad_t m_pullup_alloc; /* # of possible m_pullup mallocs */ + u_quad_t m_pullup_fail; /* # of possible m_pullup failures */ + u_quad_t m_pullup2; /* # of calls to m_pullup2 */ + u_quad_t m_pullup2_copy; /* # of possible m_pullup2 copies */ + u_quad_t m_pullup2_alloc; /* # of possible m_pullup2 mallocs */ + u_quad_t m_pullup2_fail; /* # of possible m_pullup2 failures */ }; /* flags to m_get/MGET */ @@ -524,6 +538,10 @@ struct mcntfree_lst { * MFREE(struct mbuf *m, struct mbuf *n) * Free a single mbuf and associated external storage. * Place the successor, if any, in n. + * + * we do need to check non-first mbuf for m_aux, since some of existing + * code does not call M_PREPEND properly. + * (example: call to bpf_mtap from drivers) */ #define MFREE(m, n) do { \ struct mbuf *_mm = (m); \ @@ -533,6 +551,10 @@ struct mcntfree_lst { MEXTFREE(_mm); \ mtx_lock(&mbuf_mtx); \ mbtypes[_mm->m_type]--; \ + if ((_mm->m_flags & M_PKTHDR) != 0 && _mm->m_pkthdr.aux) { \ + m_freem(_mm->m_pkthdr.aux); \ + _mm->m_pkthdr.aux = NULL; \ + } \ _mm->m_type = MT_FREE; \ mbtypes[MT_FREE]++; \ (n) = _mm->m_next; \ @@ -649,6 +671,7 @@ struct mcntfree_lst { struct mauxtag { int af; int type; + void* p; }; extern u_long m_clalloc_wid; /* mbuf cluster wait count */ @@ -672,6 +695,8 @@ extern int nsfbufs; void m_adj(struct mbuf *, int); int m_alloc_ref(u_int, int); +struct mbuf *m_aux_add2 __P((struct mbuf *, int, int, void *)); +struct mbuf *m_aux_find2 __P((struct mbuf *, int, int, void *)); struct mbuf *m_aux_add(struct mbuf *, int, int); void m_aux_delete(struct mbuf *, struct mbuf *); struct mbuf *m_aux_find(struct mbuf *, int, int); diff --git a/sys/sys/protosw.h b/sys/sys/protosw.h index 6742f7ac4dc8..00d50ffe6d6a 100644 --- a/sys/sys/protosw.h +++ b/sys/sys/protosw.h @@ -119,6 +119,7 @@ struct protosw { #define PR_WANTRCVD 0x08 /* want PRU_RCVD calls */ #define PR_RIGHTS 0x10 /* passes capabilities */ #define PR_IMPLOPCL 0x20 /* implied open/close */ +#define PR_LASTHDR 0x40 /* enforce ipsec policy; last header */ /* * The arguments to usrreq are: @@ -319,6 +320,7 @@ char *prcorequests[] = { #ifdef _KERNEL void pfctlinput __P((int, struct sockaddr *)); +void pfctlinput2 __P((int, struct sockaddr *, void *)); struct protosw *pffindproto __P((int family, int protocol, int type)); struct protosw *pffindtype __P((int family, int type)); #endif diff --git a/sys/sys/socket.h b/sys/sys/socket.h index dfa90fa56fc3..3673ff646ae2 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -49,6 +49,13 @@ /* * Data types. */ +#include <machine/types.h> + +/* + * needed for __CMSG_ALIGN + */ +#include <machine/param.h> + typedef u_char sa_family_t; #ifdef _BSD_SOCKLEN_T_ typedef _BSD_SOCKLEN_T_ socklen_t; @@ -373,22 +380,27 @@ struct cmsgcred { /* given pointer to struct cmsghdr, return pointer to data */ #define CMSG_DATA(cmsg) ((u_char *)(cmsg) + \ - _ALIGN(sizeof(struct cmsghdr))) + __CMSG_ALIGN(sizeof(struct cmsghdr))) + +#define __CMSG_ALIGN(n) ALIGN(n) +#ifdef _KERNEL +#define CMSG_ALIGN(n) __CMSG_ALIGN(n) +#endif /* given pointer to struct cmsghdr, return pointer to next cmsghdr */ #define CMSG_NXTHDR(mhdr, cmsg) \ - (((caddr_t)(cmsg) + _ALIGN((cmsg)->cmsg_len) + \ - _ALIGN(sizeof(struct cmsghdr)) > \ + (((caddr_t)(cmsg) + __CMSG_ALIGN((cmsg)->cmsg_len) + \ + __CMSG_ALIGN(sizeof(struct cmsghdr)) > \ (caddr_t)(mhdr)->msg_control + (mhdr)->msg_controllen) ? \ (struct cmsghdr *)NULL : \ - (struct cmsghdr *)((caddr_t)(cmsg) + _ALIGN((cmsg)->cmsg_len))) + (struct cmsghdr *)((caddr_t)(cmsg) + __CMSG_ALIGN((cmsg)->cmsg_len))) #define CMSG_FIRSTHDR(mhdr) ((struct cmsghdr *)(mhdr)->msg_control) /* RFC 2292 additions */ - -#define CMSG_SPACE(l) (_ALIGN(sizeof(struct cmsghdr)) + _ALIGN(l)) -#define CMSG_LEN(l) (_ALIGN(sizeof(struct cmsghdr)) + (l)) + +#define CMSG_SPACE(l) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(l)) +#define CMSG_LEN(l) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + (l)) /* "Socket"-level control message types: */ #define SCM_RIGHTS 0x01 /* access rights (array of int) */ diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h index c107562eaec3..74c2a95257f9 100644 --- a/sys/sys/sockio.h +++ b/sys/sys/sockio.h @@ -91,6 +91,8 @@ #define SIOCGIFPSRCADDR _IOWR('i', 71, struct ifreq) /* get gif psrc addr */ #define SIOCGIFPDSTADDR _IOWR('i', 72, struct ifreq) /* get gif pdst addr */ #define SIOCDIFPHYADDR _IOW('i', 73, struct ifreq) /* delete gif addrs */ +#define SIOCSLIFPHYADDR _IOW('i', 74, struct if_laddrreq) /* set gif addrs */ +#define SIOCGLIFPHYADDR _IOWR('i', 75, struct if_laddrreq) /* get gif addrs */ #define SIOCSIFGENERIC _IOW('i', 57, struct ifreq) /* generic IF set op */ #define SIOCGIFGENERIC _IOWR('i', 58, struct ifreq) /* generic IF get op */ |