aboutsummaryrefslogblamecommitdiff
path: root/sys/netinet6/ah_core.c
blob: 8ef0f390d2401f6c8d524780eeec81fab6259096 (plain) (tree)
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125















































                                                                             















































                                                          

                                                                         



                                                                       
                                                                              




                                                                            
                                                                               




                                                                             
                                                                             




                                                                           
                                                                              





























































                                                                                
                           














































































                                                                               
                           














































































































                                                                             
                           











































































































                                                                                 
                           


















































































































                                                                       
                           











































































































































































































































































































































































































































































































































                                                                                             
/*
 * 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.
 *
 * $FreeBSD$
 */

/*
 * RFC1826/2402 authentication header.
 */

#include "opt_inet6.h"
#include "opt_ipsec.h"

#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/socketvar.h>
#include <sys/errno.h>
#include <sys/time.h>

#include <net/if.h>
#include <net/route.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_var.h>
#include <netinet/in_pcb.h>

#ifdef INET6
#include <netinet6/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/icmp6.h>
#endif

#include <netinet6/ipsec.h>
#include <netinet6/ah.h>
#ifdef INET6
#include <netinet6/ipsec6.h>
#include <netinet6/ah6.h>
#endif
#ifdef IPSEC_ESP
#include <netinet6/esp.h>
#ifdef INET6
#include <netinet6/esp6.h>
#endif
#endif
#include <net/pfkeyv2.h>
#include <netkey/key_var.h>
#include <netkey/keydb.h>
#include <sys/md5.h>
#include <crypto/sha1.h>

#include <net/net_osdep.h>

#define	HMACSIZE	16

#ifdef INET6
#define	ZEROBUFLEN	256
static char zerobuf[ZEROBUFLEN];
#endif

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 void ah_none_init __P((struct ah_algorithm_state *,
	struct secasvar *));
static void ah_none_loop __P((struct ah_algorithm_state *, const 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 *));
static void ah_keyed_md5_init __P((struct ah_algorithm_state *,
	struct secasvar *));
static void ah_keyed_md5_loop __P((struct ah_algorithm_state *, const caddr_t,
	size_t));
static void ah_keyed_md5_result __P((struct ah_algorithm_state *, caddr_t));
static int ah_keyed_sha1_mature __P((struct secasvar *));
static void ah_keyed_sha1_init __P((struct ah_algorithm_state *,
	struct secasvar *));
static void ah_keyed_sha1_loop __P((struct ah_algorithm_state *, const caddr_t,
	size_t));
static void ah_keyed_sha1_result __P((struct ah_algorithm_state *, caddr_t));
static int ah_hmac_md5_mature __P((struct secasvar *));
static void ah_hmac_md5_init __P((struct ah_algorithm_state *,
	struct secasvar *));
static void ah_hmac_md5_loop __P((struct ah_algorithm_state *, const caddr_t,
	size_t));
static void ah_hmac_md5_result __P((struct ah_algorithm_state *, caddr_t));
static int ah_hmac_sha1_mature __P((struct secasvar *));
static void ah_hmac_sha1_init __P((struct ah_algorithm_state *,
	struct secasvar *));
static void ah_hmac_sha1_loop __P((struct ah_algorithm_state *, const caddr_t,
	size_t));
static void ah_hmac_sha1_result __P((struct ah_algorithm_state *, caddr_t));

/* checksum algorithms */
/* NOTE: The order depends on SADB_AALG_x in netkey/keyv2.h */
struct ah_algorithm ah_algorithms[] = {
	{ 0, 0, 0, 0, 0, 0, },
	{ ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128,
		ah_hmac_md5_init, ah_hmac_md5_loop, ah_hmac_md5_result, },
	{ ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160,
		ah_hmac_sha1_init, ah_hmac_sha1_loop, ah_hmac_sha1_result, },
	{ ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128,
		ah_keyed_md5_init, ah_keyed_md5_loop, ah_keyed_md5_result, },
	{ ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160,
		ah_keyed_sha1_init, ah_keyed_sha1_loop, ah_keyed_sha1_result, },
	{ ah_sumsiz_zero, ah_none_mature, 0, 2048,
		ah_none_init, ah_none_loop, ah_none_result, },
};

static int
ah_sumsiz_1216(sav)
	struct secasvar *sav;
{
	if (!sav)
		return -1;
	if (sav->flags & SADB_X_EXT_OLD)
		return 16;
	else
		return 12;
}

static int
ah_sumsiz_zero(sav)
	struct secasvar *sav;
{
	if (!sav)
		return -1;
	return 0;
}

static int
ah_none_mature(sav)
	struct secasvar *sav;
{
	if (sav->sah->saidx.proto == IPPROTO_AH) {
		printf("ah_none_mature: protocol and algorithm mismatch.\n");
		return 1;
	}
	return 0;
}

static void
ah_none_init(state, sav)
	struct ah_algorithm_state *state;
	struct secasvar *sav;
{
	state->foo = NULL;
}

static void
ah_none_loop(state, addr, len)
	struct ah_algorithm_state *state;
	const caddr_t addr;
	size_t len;
{
}

static void
ah_none_result(state, addr)
	struct ah_algorithm_state *state;
	caddr_t addr;
{
}

static int
ah_keyed_md5_mature(sav)
	struct secasvar *sav;
{
	/* anything is okay */
	return 0;
}

static void
ah_keyed_md5_init(state, sav)
	struct ah_algorithm_state *state;
	struct secasvar *sav;
{
	if (!state)
		panic("ah_keyed_md5_init: what?");

	state->sav = sav;
	state->foo = (void *)malloc(sizeof(MD5_CTX), M_TEMP, M_NOWAIT);
	if (state->foo == NULL)
		panic("ah_keyed_md5_init: what?");
	MD5Init((MD5_CTX *)state->foo);
	if (state->sav) {
		MD5Update((MD5_CTX *)state->foo,
			(u_int8_t *)_KEYBUF(state->sav->key_auth),
			(u_int)_KEYLEN(state->sav->key_auth));

	    {
		/*
		 * Pad after the key.
		 * We cannot simply use md5_pad() since the function
		 * won't update the total length.
		 */
		size_t padlen;
		size_t keybitlen;
		u_int8_t buf[32];

		if (_KEYLEN(state->sav->key_auth) < 56)
			padlen = 64 - 8 - _KEYLEN(state->sav->key_auth);
		else
			padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth);
		keybitlen = _KEYLEN(state->sav->key_auth);
		keybitlen *= 8;

		buf[0] = 0x80;
		MD5Update((MD5_CTX *)state->foo, &buf[0], 1);
		padlen--;

		bzero(buf, sizeof(buf));
		while (sizeof(buf) < padlen) {
			MD5Update((MD5_CTX *)state->foo, &buf[0], sizeof(buf));
			padlen -= sizeof(buf);
		}
		if (padlen) {
			MD5Update((MD5_CTX *)state->foo, &buf[0], padlen);
		}

		buf[0] = (keybitlen >> 0) & 0xff;
		buf[1] = (keybitlen >> 8) & 0xff;
		buf[2] = (keybitlen >> 16) & 0xff;
		buf[3] = (keybitlen >> 24) & 0xff;
		MD5Update((MD5_CTX *)state->foo, buf, 8);
	    }
	}
}

static void
ah_keyed_md5_loop(state, addr, len)
	struct ah_algorithm_state *state;
	const caddr_t addr;
	size_t len;
{
	if (!state)
		panic("ah_keyed_md5_loop: what?");

	MD5Update((MD5_CTX *)state->foo, addr, len);
}

static void
ah_keyed_md5_result(state, addr)
	struct ah_algorithm_state *state;
	caddr_t addr;
{
	u_char digest[16];

	if (!state)
		panic("ah_keyed_md5_result: what?");

	if (state->sav) {
		MD5Update((MD5_CTX *)state->foo,
			(u_int8_t *)_KEYBUF(state->sav->key_auth),
			(u_int)_KEYLEN(state->sav->key_auth));
	}
	MD5Final(&digest[0], (MD5_CTX *)state->foo);
	free(state->foo, M_TEMP);
	bcopy(&digest[0], (void *)addr, sizeof(digest));
}

static int
ah_keyed_sha1_mature(sav)
	struct secasvar *sav;
{
	struct ah_algorithm *algo;

	if (!sav->key_auth) {
		printf("esp_keyed_sha1_mature: no key is given.\n");
		return 1;
	}
	algo = &ah_algorithms[sav->alg_auth];
	if (sav->key_auth->sadb_key_bits < algo->keymin
	 || algo->keymax < sav->key_auth->sadb_key_bits) {
		printf("ah_keyed_sha1_mature: invalid key length %d.\n",
			sav->key_auth->sadb_key_bits);
		return 1;
	}

	return 0;
}

static void
ah_keyed_sha1_init(state, sav)
	struct ah_algorithm_state *state;
	struct secasvar *sav;
{
	SHA1_CTX *ctxt;

	if (!state)
		panic("ah_keyed_sha1_init: what?");

	state->sav = sav;
	state->foo = (void *)malloc(sizeof(SHA1_CTX), M_TEMP, M_NOWAIT);
	if (!state->foo)
		panic("ah_keyed_sha1_init: what?");

	ctxt = (SHA1_CTX *)state->foo;
	SHA1Init(ctxt);

	if (state->sav) {
		SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth),
			(u_int)_KEYLEN(state->sav->key_auth));

	    {
		/*
		 * Pad after the key.
		 */
		size_t padlen;
		size_t keybitlen;
		u_int8_t buf[32];

		if (_KEYLEN(state->sav->key_auth) < 56)
			padlen = 64 - 8 - _KEYLEN(state->sav->key_auth);
		else
			padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth);
		keybitlen = _KEYLEN(state->sav->key_auth);
		keybitlen *= 8;

		buf[0] = 0x80;
		SHA1Update(ctxt, &buf[0], 1);
		padlen--;

		bzero(buf, sizeof(buf));
		while (sizeof(buf) < padlen) {
			SHA1Update(ctxt, &buf[0], sizeof(buf));
			padlen -= sizeof(buf);
		}
		if (padlen) {
			SHA1Update(ctxt, &buf[0], padlen);
		}

		buf[0] = (keybitlen >> 0) & 0xff;
		buf[1] = (keybitlen >> 8) & 0xff;
		buf[2] = (keybitlen >> 16) & 0xff;
		buf[3] = (keybitlen >> 24) & 0xff;
		SHA1Update(ctxt, buf, 8);
	    }
	}
}

static void
ah_keyed_sha1_loop(state, addr, len)
	struct ah_algorithm_state *state;
	const caddr_t addr;
	size_t len;
{
	SHA1_CTX *ctxt;

	if (!state || !state->foo)
		panic("ah_keyed_sha1_loop: what?");
	ctxt = (SHA1_CTX *)state->foo;

	sha1_loop(ctxt, (caddr_t)addr, (size_t)len);
}

static void
ah_keyed_sha1_result(state, addr)
	struct ah_algorithm_state *state;
	caddr_t addr;
{
	u_char digest[SHA1_RESULTLEN];	/* SHA-1 generates 160 bits */
	SHA1_CTX *ctxt;

	if (!state || !state->foo)
		panic("ah_keyed_sha1_result: what?");
	ctxt = (SHA1_CTX *)state->foo;

	if (state->sav) {
		SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth),
			(u_int)_KEYLEN(state->sav->key_auth));
	}
	SHA1Final((caddr_t)&digest[0], ctxt);
	bcopy(&digest[0], (void *)addr, HMACSIZE);

	free(state->foo, M_TEMP);
}

static int
ah_hmac_md5_mature(sav)
	struct secasvar *sav;
{
	struct ah_algorithm *algo;

	if (!sav->key_auth) {
		printf("esp_hmac_md5_mature: no key is given.\n");
		return 1;
	}
	algo = &ah_algorithms[sav->alg_auth];
	if (sav->key_auth->sadb_key_bits < algo->keymin
	 || algo->keymax < sav->key_auth->sadb_key_bits) {
		printf("ah_hmac_md5_mature: invalid key length %d.\n",
			sav->key_auth->sadb_key_bits);
		return 1;
	}

	return 0;
}

static void
ah_hmac_md5_init(state, sav)
	struct ah_algorithm_state *state;
	struct secasvar *sav;
{
	u_char *ipad;
	u_char *opad;
	u_char tk[16];
	u_char *key;
	size_t keylen;
	size_t i;
	MD5_CTX *ctxt;

	if (!state)
		panic("ah_hmac_md5_init: what?");

	state->sav = sav;
	state->foo = (void *)malloc(64 + 64 + sizeof(MD5_CTX), M_TEMP, M_NOWAIT);
	if (!state->foo)
		panic("ah_hmac_md5_init: what?");

	ipad = (u_char *)state->foo;
	opad = (u_char *)(ipad + 64);
	ctxt = (MD5_CTX *)(opad + 64);

	/* compress the key if necessery */
	if (64 < _KEYLEN(state->sav->key_auth)) {
		MD5Init(ctxt);
		MD5Update(ctxt, _KEYBUF(state->sav->key_auth),
			_KEYLEN(state->sav->key_auth));
		MD5Final(&tk[0], ctxt);
		key = &tk[0];
		keylen = 16;
	} 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;
	}

	MD5Init(ctxt);
	MD5Update(ctxt, ipad, 64);
}

static void
ah_hmac_md5_loop(state, addr, len)
	struct ah_algorithm_state *state;
	const caddr_t addr;
	size_t len;
{
	MD5_CTX *ctxt;

	if (!state || !state->foo)
		panic("ah_hmac_md5_loop: what?");
	ctxt = (MD5_CTX *)(((caddr_t)state->foo) + 128);
	MD5Update(ctxt, addr, len);
}

static void
ah_hmac_md5_result(state, addr)
	struct ah_algorithm_state *state;
	caddr_t addr;
{
	u_char digest[16];
	u_char *ipad;
	u_char *opad;
	MD5_CTX *ctxt;

	if (!state || !state->foo)
		panic("ah_hmac_md5_result: what?");

	ipad = (u_char *)state->foo;
	opad = (u_char *)(ipad + 64);
	ctxt = (MD5_CTX *)(opad + 64);

	MD5Final(&digest[0], ctxt);

	MD5Init(ctxt);
	MD5Update(ctxt, opad, 64);
	MD5Update(ctxt, &digest[0], sizeof(digest));
	MD5Final(&digest[0], ctxt);

	bcopy(&digest[0], (void *)addr, HMACSIZE);

	free(state->foo, M_TEMP);
}

static int
ah_hmac_sha1_mature(sav)
	struct secasvar *sav;
{
	struct ah_algorithm *algo;

	if (!sav->key_auth) {
		printf("esp_hmac_sha1_mature: no key is given.\n");
		return 1;
	}
	algo = &ah_algorithms[sav->alg_auth];
	if (sav->key_auth->sadb_key_bits < algo->keymin
	 || algo->keymax < sav->key_auth->sadb_key_bits) {
		printf("ah_hmac_sha1_mature: invalid key length %d.\n",
			sav->key_auth->sadb_key_bits);
		return 1;
	}

	return 0;
}

static void
ah_hmac_sha1_init(state, sav)
	struct ah_algorithm_state *state;
	struct secasvar *sav;
{
	u_char *ipad;
	u_char *opad;
	SHA1_CTX *ctxt;
	u_char tk[SHA1_RESULTLEN];	/* SHA-1 generates 160 bits */
	u_char *key;
	size_t keylen;
	size_t i;

	if (!state)
		panic("ah_hmac_sha1_init: what?");

	state->sav = sav;
	state->foo = (void *)malloc(64 + 64 + sizeof(SHA1_CTX),
			M_TEMP, M_NOWAIT);
	if (!state->foo)
		panic("ah_hmac_sha1_init: what?");

	ipad = (u_char *)state->foo;
	opad = (u_char *)(ipad + 64);
	ctxt = (SHA1_CTX *)(opad + 64);

	/* compress the key if necessery */
	if (64 < _KEYLEN(state->sav->key_auth)) {
		SHA1Init(ctxt);
		SHA1Update(ctxt, _KEYBUF(state->sav->key_auth),
			_KEYLEN(state->sav->key_auth));
		SHA1Final(&tk[0], ctxt);
		key = &tk[0];
		keylen = SHA1_RESULTLEN;
	} 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;
	}

	SHA1Init(ctxt);
	SHA1Update(ctxt, ipad, 64);
}

static void
ah_hmac_sha1_loop(state, addr, len)
	struct ah_algorithm_state *state;
	const caddr_t addr;
	size_t len;
{
	SHA1_CTX *ctxt;

	if (!state || !state->foo)
		panic("ah_hmac_sha1_loop: what?");

	ctxt = (SHA1_CTX *)(((u_char *)state->foo) + 128);
	SHA1Update(ctxt, (caddr_t)addr, (size_t)len);
}

static void
ah_hmac_sha1_result(state, addr)
	struct ah_algorithm_state *state;
	caddr_t addr;
{
	u_char digest[SHA1_RESULTLEN];	/* SHA-1 generates 160 bits */
	u_char *ipad;
	u_char *opad;
	SHA1_CTX *ctxt;

	if (!state || !state->foo)
		panic("ah_hmac_sha1_result: what?");

	ipad = (u_char *)state->foo;
	opad = (u_char *)(ipad + 64);
	ctxt = (SHA1_CTX *)(opad + 64);

	SHA1Final((caddr_t)&digest[0], ctxt);

	SHA1Init(ctxt);
	SHA1Update(ctxt, opad, 64);
	SHA1Update(ctxt, (caddr_t)&digest[0], sizeof(digest));
	SHA1Final((caddr_t)&digest[0], ctxt);

	bcopy(&digest[0], (void *)addr, HMACSIZE);

	free(state->foo, M_TEMP);
}

/*------------------------------------------------------------*/

/*
 * go generate the checksum.
 */
int
ah4_calccksum(m0, ahdat, algo, sav)
	struct mbuf *m0;
	caddr_t ahdat;
	struct ah_algorithm *algo;
	struct secasvar *sav;
{
	struct mbuf *m;
	int hdrtype;
	u_char *p;
	size_t advancewidth;
	struct ah_algorithm_state algos;
	int tlen;
	u_char sumbuf[AH_MAXSUMSIZE];
	int error = 0;

	hdrtype = -1;	/*dummy, it is called IPPROTO_IP*/

	m = m0;

	p = mtod(m, u_char *);

	(algo->init)(&algos, sav);

	advancewidth = 0;	/*safety*/

again:
	/* gory. */
	switch (hdrtype) {
	case -1:	/*first one*/
	    {
		/*
		 * copy ip hdr, modify to fit the AH checksum rule,
		 * then take a checksum.
		 * XXX need to care about source routing... jesus.
		 */
		struct ip iphdr;
		size_t hlen;

		bcopy((caddr_t)p, (caddr_t)&iphdr, sizeof(struct ip));
#ifdef _IP_VHL
		hlen = IP_VHL_HL(iphdr.ip_vhl) << 2;
#else
		hlen = iphdr.ip_hl << 2;
#endif
		iphdr.ip_ttl = 0;
		iphdr.ip_sum = htons(0);
		if (ip4_ah_cleartos) iphdr.ip_tos = 0;
		iphdr.ip_off = htons(ntohs(iphdr.ip_off) & ip4_ah_offsetmask);
		(algo->update)(&algos, (caddr_t)&iphdr, sizeof(struct ip));

		if (hlen != sizeof(struct ip)) {
			u_char *p;
			int i, j;
			int l, skip;
			u_char dummy[4];

			/*
			 * IP options processing.
			 * See RFC2402 appendix A.
			 */
			bzero(dummy, sizeof(dummy));
			p = mtod(m, u_char *);
			i = sizeof(struct ip);
			while (i < hlen) {
				skip = 1;
				switch (p[i + IPOPT_OPTVAL]) {
				case IPOPT_EOL:
				case IPOPT_NOP:
					l = 1;
					skip = 0;
					break;
				case IPOPT_SECURITY:	/* 0x82 */
				case 0x85:	/* Extended security */
				case 0x86:	/* Commercial security */
				case 0x94:	/* Router alert */
				case 0x95:	/* RFC1770 */
					l = p[i + IPOPT_OLEN];
					skip = 0;
					break;
				default:
					l = p[i + IPOPT_OLEN];
					skip = 1;
					break;
				}
				if (l <= 0 || hlen - i < l) {
					printf("ah4_input: invalid IP option "
						"(type=%02x len=%02x)\n",
						p[i + IPOPT_OPTVAL],
						p[i + IPOPT_OLEN]);
					break;
				}
				if (skip) {
					for (j = 0; j < l / sizeof(dummy); j++)
						(algo->update)(&algos, dummy, sizeof(dummy));

					(algo->update)(&algos, dummy, l % sizeof(dummy));
				} else
					(algo->update)(&algos, p + i, l);
				if (p[i + IPOPT_OPTVAL] == IPOPT_EOL)
					break;
				i += l;
			}
		}

		hdrtype = (iphdr.ip_p) & 0xff;
		advancewidth = hlen;
		break;
	    }

	case IPPROTO_AH:
	    {
		u_char dummy[4];
		int siz;
		int hdrsiz;

		hdrsiz = (sav->flags & SADB_X_EXT_OLD) ?
				sizeof(struct ah) : sizeof(struct newah);

		(algo->update)(&algos, p, hdrsiz);

		/* key data region. */
		siz = (*algo->sumsiz)(sav);
		bzero(&dummy[0], sizeof(dummy));
		while (sizeof(dummy) <= siz) {
			(algo->update)(&algos, dummy, sizeof(dummy));
			siz -= sizeof(dummy);
		}
		/* can't happen, but just in case */
		if (siz)
			(algo->update)(&algos, dummy, siz);

		/* padding region, just in case */
		siz = (((struct ah *)p)->ah_len << 2) - (*algo->sumsiz)(sav);
		if ((sav->flags & SADB_X_EXT_OLD) == 0)
			siz -= 4;		/* sequence number field */
		if (0 < siz) {
			/* RFC 1826 */
			(algo->update)(&algos, p + hdrsiz + (*algo->sumsiz)(sav),
				siz);
		}

		hdrtype = ((struct ah *)p)->ah_nxt;
		advancewidth = hdrsiz;
		advancewidth += ((struct ah *)p)->ah_len << 2;
		if ((sav->flags & SADB_X_EXT_OLD) == 0)
			advancewidth -= 4;	/* sequence number field */
		break;
	    }

	default:
		printf("ah4_calccksum: unexpected hdrtype=%x; "
			"treating rest as payload\n", hdrtype);
		/*fall through*/
	case IPPROTO_ICMP:
	case IPPROTO_IGMP:
	case IPPROTO_IPIP:
#ifdef INET6
	case IPPROTO_IPV6:
	case IPPROTO_ICMPV6:
#endif
	case IPPROTO_UDP:
	case IPPROTO_TCP:
	case IPPROTO_ESP:
		while (m) {
			tlen = m->m_len - (p - mtod(m, u_char *));
			(algo->update)(&algos, p, tlen);
			m = m->m_next;
			p = m ? mtod(m, u_char *) : NULL;
		}

		advancewidth = 0;	/*loop finished*/
		break;
	}

	if (advancewidth) {
		/* is it safe? */
		while (m && advancewidth) {
			tlen = m->m_len - (p - mtod(m, u_char *));
			if (advancewidth < tlen) {
				p += advancewidth;
				advancewidth = 0;
			} else {
				advancewidth -= tlen;
				m = m->m_next;
				if (m)
					p = mtod(m, u_char *);
				else {
					printf("ERR: hit the end-of-mbuf...\n");
					p = NULL;
				}
			}
		}

		if (m)
			goto again;
	}

	/* for HMAC algorithms... */
	(algo->result)(&algos, &sumbuf[0]);
	bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));

	return error;
}

#ifdef INET6
/*
 * go generate the checksum. This function won't modify the mbuf chain
 * except AH itself.
 */
int
ah6_calccksum(m0, ahdat, algo, sav)
	struct mbuf *m0;
	caddr_t ahdat;
	struct ah_algorithm *algo;
	struct secasvar *sav;
{
	struct mbuf *m;
	int hdrtype;
	u_char *p;
	size_t advancewidth;
	struct ah_algorithm_state algos;
	int tlen;
	int error = 0;
	u_char sumbuf[AH_MAXSUMSIZE];
	int nest;

	hdrtype = -1;	/*dummy, it is called IPPROTO_IPV6 */

	m = m0;

	p = mtod(m, u_char *);

	(algo->init)(&algos, sav);

	advancewidth = 0;	/*safety*/
	nest = 0;

again:
	if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) {
		ip6stat.ip6s_toomanyhdr++;
		error = EINVAL;	/*XXX*/
		goto bad;
	}

	/* gory. */
	switch (hdrtype) {
	case -1:	/*first one*/
	    {
		struct ip6_hdr ip6copy;

		bcopy(p, &ip6copy, sizeof(struct ip6_hdr));
		/* RFC2402 */
		ip6copy.ip6_flow = 0;
		ip6copy.ip6_vfc = IPV6_VERSION;
		ip6copy.ip6_hlim = 0;
		if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src))
			ip6copy.ip6_src.s6_addr16[1] = 0x0000;
		if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst))
			ip6copy.ip6_dst.s6_addr16[1] = 0x0000;
		(algo->update)(&algos, (caddr_t)&ip6copy,
			       sizeof(struct ip6_hdr));
		hdrtype = (((struct ip6_hdr *)p)->ip6_nxt) & 0xff;
		advancewidth = sizeof(struct ip6_hdr);
		break;
	    }

	case IPPROTO_AH:
	    {
		u_char dummy[4];
		int siz;
		int hdrsiz;

		hdrsiz = (sav->flags & SADB_X_EXT_OLD) ?
				sizeof(struct ah) : sizeof(struct newah);

		(algo->update)(&algos, p, hdrsiz);

		/* key data region. */
		siz = (*algo->sumsiz)(sav);
		bzero(&dummy[0], 4);
		while (4 <= siz) {
			(algo->update)(&algos, dummy, 4);
			siz -= 4;
		}
		/* can't happen, but just in case */
		if (siz)
			(algo->update)(&algos, dummy, siz);

		/* padding region, just in case */
		siz = (((struct ah *)p)->ah_len << 2) - (*algo->sumsiz)(sav);
		if ((sav->flags & SADB_X_EXT_OLD) == 0)
			siz -= 4;		/* sequence number field */
		if (0 < siz) {
			(algo->update)(&algos, p + hdrsiz + (*algo->sumsiz)(sav),
				siz);
		}

		hdrtype = ((struct ah *)p)->ah_nxt;
		advancewidth = hdrsiz;
		advancewidth += ((struct ah *)p)->ah_len << 2;
		if ((sav->flags & SADB_X_EXT_OLD) == 0)
			advancewidth -= 4;	/* sequence number field */
		break;
	    }

	 case IPPROTO_HOPOPTS:
	 case IPPROTO_DSTOPTS:
	 {
		 int hdrlen, optlen;
		 u_int8_t *optp, *lastp = p, *optend, opt;

		 tlen = m->m_len - (p - mtod(m, u_char *));
		 /* We assume all the options is contained in a single mbuf */
		 if (tlen < sizeof(struct ip6_ext)) {
			 error = EINVAL;
			 goto bad;
		 }
		 hdrlen  = (((struct ip6_ext *)p)->ip6e_len + 1) << 3;
		 hdrtype = (int)((struct ip6_ext *)p)->ip6e_nxt;
		 if (tlen < hdrlen) {
			 error = EINVAL;
			 goto bad;
		 }
		 optend = p + hdrlen;

		 /*
		  * ICV calculation for the options header including all
		  * options. This part is a little tricky since there are
		  * two type of options; mutable and immutable. Our approach
		  * is to calculate ICV for a consecutive immutable options
		  * at once. Here is an example. In the following figure,
		  * suppose that we've calculated ICV from the top of the
		  * header to MutableOpt1, which is a mutable option.
		  * lastp points to the end of MutableOpt1. Some immutable
		  * options follows MutableOpt1, and we encounter a new
		  * mutable option; MutableOpt2. optp points to the head
		  * of MutableOpt2. In this situation, uncalculated immutable
		  * field is the field from lastp to optp+2 (note that the
		  * type and the length fields are considered as immutable
		  * even in a mutable option). So we first calculate ICV
		  * for the field as immutable, then calculate from optp+2
		  * to the end of MutableOpt2, whose length is optlen-2,
		  * where optlen is the length of MutableOpt2. Finally,
		  * lastp is updated to point to the end of MutableOpt2
		  * for further calculation. The updated point is shown as
		  * lastp' in the figure.
		  *                                <------ optlen ----->
		  * -----------+-------------------+---+---+-----------+
		  * MutableOpt1|ImmutableOptions...|typ|len|MutableOpt2|
		  * -----------+-------------------+---+---+-----------+
		  *            ^                   ^       ^
		  *            lastp               optp    optp+2
		  *            <---- optp + 2 - lastp -----><-optlen-2->
		  *                                                    ^
		  *                                                    lastp'
		  */
		 for (optp = p + 2; optp < optend; optp += optlen) {
			 opt = optp[0];
			 if (opt == IP6OPT_PAD1) {
				 optlen = 1;
			 } else {
				 if (optp + 2 > optend) {
					 error = EINVAL; /* malformed option */
					 goto bad;
				 }
				 optlen = optp[1] + 2;
				 if (opt & IP6OPT_MUTABLE) {
					 /*
					  * ICV calc. for the (consecutive)
					  * immutable field followd by the
					  * option.
					  */
					 (algo->update)(&algos, lastp,
							optp + 2 - lastp);
					 if (optlen - 2 > ZEROBUFLEN) {
						 error = EINVAL; /* XXX */
						 goto bad;
					 }
					 /*
					  * ICV calc. for the mutable
					  * option using an all-0 buffer.
					  */
					 (algo->update)(&algos, zerobuf,
							optlen - 2);
					 lastp = optp + optlen;
				 }
			 }
		 }
		 /*
		  * Wrap up the calulation; compute ICV for the consecutive
		  * immutable options at the end of the header(if any).
		  */
		 (algo->update)(&algos, lastp, p + hdrlen - lastp);
		 advancewidth = hdrlen;
		 break;
	 }
	 case IPPROTO_ROUTING:
	 {
		 /*
		  * For an input packet, we can just calculate `as is'.
		  * For an output packet, we assume ip6_output have already
		  * made packet how it will be received at the final destination.
		  * So we'll only check if the header is malformed.
		  */
		 int hdrlen;

		 tlen = m->m_len - (p - mtod(m, u_char *));
		 /* We assume all the options is contained in a single mbuf */
		 if (tlen < sizeof(struct ip6_ext)) {
			 error = EINVAL;
			 goto bad;
		 }
		 hdrlen  = (((struct ip6_ext *)p)->ip6e_len + 1) << 3;
		 hdrtype = (int)((struct ip6_ext *)p)->ip6e_nxt;
		 if (tlen < hdrlen) {
			 error = EINVAL;
			 goto bad;
		 }
		 advancewidth = hdrlen;
		 (algo->update)(&algos, p, hdrlen);
		 break;
	 }
	default:
		printf("ah6_calccksum: unexpected hdrtype=%x; "
			"treating rest as payload\n", hdrtype);
		/*fall through*/
	case IPPROTO_ICMP:
	case IPPROTO_IGMP:
	case IPPROTO_IPIP:	/*?*/
	case IPPROTO_IPV6:
	case IPPROTO_ICMPV6:
	case IPPROTO_UDP:
	case IPPROTO_TCP:
	case IPPROTO_ESP:
		while (m) {
			tlen = m->m_len - (p - mtod(m, u_char *));
			(algo->update)(&algos, p, tlen);
			m = m->m_next;
			p = m ? mtod(m, u_char *) : NULL;
		}

		advancewidth = 0;	/*loop finished*/
		break;
	}

	if (advancewidth) {
		/* is it safe? */
		while (m && advancewidth) {
			tlen = m->m_len - (p - mtod(m, u_char *));
			if (advancewidth < tlen) {
				p += advancewidth;
				advancewidth = 0;
			} else {
				advancewidth -= tlen;
				m = m->m_next;
				if (m)
					p = mtod(m, u_char *);
				else {
					printf("ERR: hit the end-of-mbuf...\n");
					p = NULL;
				}
			}
		}

		if (m)
			goto again;
	}

	/* for HMAC algorithms... */
	(algo->result)(&algos, &sumbuf[0]);
	bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));

	return(0);

  bad:
	return(error);
}
#endif