/*	$NetBSD: iplang_y.y,v 1.1.2.1 1999/12/20 21:01:47 he Exp $	*/

%{
/*
 * Copyright (C) 1997-1998 by Darren Reed.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and due credit is given
 * to the original author and the contributors.
 *
 * Id: iplang_y.y,v 2.1.2.1 1999/11/21 11:05:09 darrenr Exp
 */

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#if !defined(__SVR4) && !defined(__svr4__)
#include <strings.h>
#else
#include <sys/byteorder.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/time.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#ifndef	linux
#include <netinet/ip_var.h>
#endif
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <net/if.h>
#ifndef	linux
#include <netinet/if_ether.h>
#endif
#include <netdb.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <ctype.h>
#include "ipsend.h"
#include "ip_compat.h"
#include "ipf.h"
#include "iplang.h"

#ifndef __NetBSD__
extern	struct ether_addr *ether_aton __P((char *));
#endif

extern	int	opts;
extern	struct ipopt_names ionames[];
extern	int	state, state, lineNum, token;
extern	int	yylineno;
extern	char	yytext[];
extern	FILE	*yyin;
int	yylex	__P((void));
#define	YYDEBUG 1
#if !defined(ultrix) && !defined(hpux)
int	yydebug = 1;
#else
extern	int	yydebug;
#endif

iface_t *iflist = NULL, **iftail = &iflist;
iface_t *cifp = NULL;
arp_t *arplist = NULL, **arptail = &arplist, *carp = NULL;
struct in_addr defrouter;
send_t	sending;
char	*sclass = NULL;
u_short	c_chksum __P((u_short *, u_int, u_long));
u_long	p_chksum __P((u_short *, u_int));

u_long	ipbuffer[67584/sizeof(u_long)];		/* 66K */
aniphdr_t	*aniphead = NULL, *canip = NULL, **aniptail = &aniphead;
ip_t		*ip = NULL;
udphdr_t	*udp = NULL;
tcphdr_t	*tcp = NULL;
icmphdr_t	*icmp = NULL;

struct statetoopt {
	int	sto_st;
	int	sto_op;
};

struct	in_addr getipv4addr __P((char *arg));
u_short	getportnum __P((char *, char *));
struct	ether_addr *geteaddr __P((char *, struct ether_addr *));
void	*new_header __P((int));
void	free_aniplist __P((void));
void	inc_anipheaders __P((int));
void	new_data __P((void));
void	set_datalen __P((char **));
void	set_datafile __P((char **));
void	set_data __P((char **));
void	new_packet __P((void));
void	set_ipv4proto __P((char **));
void	set_ipv4src __P((char **));
void	set_ipv4dst __P((char **));
void	set_ipv4off __P((char **));
void	set_ipv4v __P((char **));
void	set_ipv4hl __P((char **));
void	set_ipv4ttl __P((char **));
void	set_ipv4tos __P((char **));
void	set_ipv4id __P((char **));
void	set_ipv4sum __P((char **));
void	set_ipv4len __P((char **));
void	new_tcpheader __P((void));
void	set_tcpsport __P((char **));
void	set_tcpdport __P((char **));
void	set_tcpseq __P((char **));
void	set_tcpack __P((char **));
void	set_tcpoff __P((char **));
void	set_tcpurp __P((char **));
void	set_tcpwin __P((char **));
void	set_tcpsum __P((char **));
void	set_tcpflags __P((char **));
void	set_tcpopt __P((int, char **));
void	end_tcpopt __P((void));
void	new_udpheader __P((void));
void	set_udplen __P((char **));
void	set_udpsum __P((char **));
void	prep_packet __P((void));
void	packet_done __P((void));
void	new_interface __P((void));
void	check_interface __P((void));
void	set_ifname __P((char **));
void	set_ifmtu __P((int));
void	set_ifv4addr __P((char **));
void	set_ifeaddr __P((char **));
void	new_arp __P((void));
void	set_arpeaddr __P((char **));
void	set_arpv4addr __P((char **));
void	reset_send __P((void));
void	set_sendif __P((char **));
void	set_sendvia __P((char **));
void	set_defaultrouter __P((char **));
void	new_icmpheader __P((void));
void	set_icmpcode __P((int));
void	set_icmptype __P((int));
void	set_icmpcodetok __P((char **));
void	set_icmptypetok __P((char **));
void	set_icmpid __P((int));
void	set_icmpseq __P((int));
void	set_icmpotime __P((int));
void	set_icmprtime __P((int));
void	set_icmpttime __P((int));
void	set_icmpmtu __P((int));
void	set_redir __P((int, char **));
void	new_ipv4opt __P((void));
void	set_icmppprob __P((int));
void	add_ipopt __P((int, void *));
void	end_ipopt __P((void));
void	set_secclass __P((char **));
void	free_anipheader __P((void));
void	end_ipv4 __P((void));
void	end_icmp __P((void));
void	end_udp __P((void));
void	end_tcp __P((void));
void	end_data __P((void));
void	yyerror __P((char *));
void	iplang __P((FILE *));
int	arp_getipv4 __P((char *, char *));
int	yyparse __P((void));
%}
%union {
	char	*str;
	int	num;
}
%token	<num> IL_NUMBER
%type	<num> number digits optnumber
%token	<str> IL_TOKEN
%type	<str> token optoken
%token	IL_HEXDIGIT IL_COLON IL_DOT IL_EOF IL_COMMENT
%token	IL_INTERFACE IL_IFNAME IL_MTU IL_EADDR
%token	IL_IPV4 IL_V4PROTO IL_V4SRC IL_V4DST IL_V4OFF IL_V4V IL_V4HL IL_V4TTL
%token	IL_V4TOS IL_V4SUM IL_V4LEN IL_V4OPT IL_V4ID
%token	IL_TCP IL_SPORT IL_DPORT IL_TCPFL IL_TCPSEQ IL_TCPACK IL_TCPOFF
%token	IL_TCPWIN IL_TCPSUM IL_TCPURP IL_TCPOPT IL_TCPO_NOP IL_TCPO_EOL
%token	IL_TCPO_MSS IL_TCPO_WSCALE IL_TCPO_TS
%token	IL_UDP IL_UDPLEN IL_UDPSUM
%token	IL_ICMP IL_ICMPTYPE IL_ICMPCODE
%token	IL_SEND IL_VIA
%token	IL_ARP
%token	IL_DEFROUTER
%token	IL_SUM IL_OFF IL_LEN IL_V4ADDR IL_OPT
%token	IL_DATA IL_DLEN IL_DVALUE IL_DFILE
%token	IL_IPO_NOP IL_IPO_RR IL_IPO_ZSU IL_IPO_MTUP IL_IPO_MTUR IL_IPO_EOL
%token	IL_IPO_TS IL_IPO_TR IL_IPO_SEC IL_IPO_LSRR IL_IPO_ESEC
%token	IL_IPO_SATID IL_IPO_SSRR IL_IPO_ADDEXT IL_IPO_VISA IL_IPO_IMITD
%token	IL_IPO_EIP IL_IPO_FINN IL_IPO_SECCLASS IL_IPO_CIPSO IL_IPO_ENCODE
%token	<str> IL_IPS_RESERV4 IL_IPS_TOPSECRET IL_IPS_SECRET IL_IPS_RESERV3
%token	<str> IL_IPS_CONFID IL_IPS_UNCLASS IL_IPS_RESERV2 IL_IPS_RESERV1
%token	IL_ICMP_ECHOREPLY IL_ICMP_UNREACH IL_ICMP_UNREACH_NET
%token	IL_ICMP_UNREACH_HOST IL_ICMP_UNREACH_PROTOCOL IL_ICMP_UNREACH_PORT
%token	IL_ICMP_UNREACH_NEEDFRAG IL_ICMP_UNREACH_SRCFAIL
%token	IL_ICMP_UNREACH_NET_UNKNOWN IL_ICMP_UNREACH_HOST_UNKNOWN
%token	IL_ICMP_UNREACH_ISOLATED IL_ICMP_UNREACH_NET_PROHIB
%token	IL_ICMP_UNREACH_HOST_PROHIB IL_ICMP_UNREACH_TOSNET
%token	IL_ICMP_UNREACH_TOSHOST IL_ICMP_UNREACH_FILTER_PROHIB
%token	IL_ICMP_UNREACH_HOST_PRECEDENCE IL_ICMP_UNREACH_PRECEDENCE_CUTOFF
%token	IL_ICMP_SOURCEQUENCH IL_ICMP_REDIRECT IL_ICMP_REDIRECT_NET
%token	IL_ICMP_REDIRECT_HOST IL_ICMP_REDIRECT_TOSNET
%token	IL_ICMP_REDIRECT_TOSHOST IL_ICMP_ECHO IL_ICMP_ROUTERADVERT
%token	IL_ICMP_ROUTERSOLICIT IL_ICMP_TIMXCEED IL_ICMP_TIMXCEED_INTRANS
%token	IL_ICMP_TIMXCEED_REASS IL_ICMP_PARAMPROB IL_ICMP_PARAMPROB_OPTABSENT
%token	IL_ICMP_TSTAMP IL_ICMP_TSTAMPREPLY IL_ICMP_IREQ IL_ICMP_IREQREPLY
%token	IL_ICMP_MASKREQ IL_ICMP_MASKREPLY IL_ICMP_SEQ IL_ICMP_ID
%token	IL_ICMP_OTIME IL_ICMP_RTIME IL_ICMP_TTIME

%%
file:	line
	| line file
	| IL_COMMENT
	| IL_COMMENT file
	;

line:	iface
	| arp
	| send
	| defrouter
	| ipline
	;

iface:  ifhdr '{' ifaceopts '}' ';'	{ check_interface(); }
	;

ifhdr:	IL_INTERFACE			{ new_interface(); }
	;

ifaceopts:
	ifaceopt
	| ifaceopt ifaceopts
	;

ifaceopt:
	IL_IFNAME token			{ set_ifname(&$2); }
	| IL_MTU number			{ set_ifmtu($2); }
	| IL_V4ADDR token		{ set_ifv4addr(&$2); }
	| IL_EADDR token		{ set_ifeaddr(&$2); }
	;

send:   sendhdr '{' sendbody '}' ';'	{ packet_done(); }
	| sendhdr ';'			{ packet_done(); }
	;

sendhdr:
	IL_SEND				{ reset_send(); }
	;

sendbody:
	sendopt
	| sendbody sendopt
	;

sendopt:
	IL_IFNAME token			{ set_sendif(&$2); }
	| IL_VIA token			{ set_sendvia(&$2); }
	;

arp:    arphdr '{' arpbody '}' ';'
	;

arphdr:	IL_ARP				{ new_arp(); }
	;

arpbody:
	arpopt
	| arpbody arpopt
	;

arpopt: IL_V4ADDR token			{ set_arpv4addr(&$2); }
	| IL_EADDR token		{ set_arpeaddr(&$2); }
	;

defrouter:
	IL_DEFROUTER token		{ set_defaultrouter(&$2); }
	;

bodyline:
	ipline
	| tcp tcpline
	| udp udpline
	| icmp icmpline
	| data dataline
	;

ipline:	ipv4 '{' ipv4body '}' ';'	{ end_ipv4(); }
	;

ipv4:	IL_IPV4				{ new_packet(); }

ipv4body:
	ipv4type
	| ipv4type ipv4body
	| bodyline
	;

ipv4type:
	IL_V4PROTO token		{ set_ipv4proto(&$2); }
	| IL_V4SRC token		{ set_ipv4src(&$2); }
	| IL_V4DST token		{ set_ipv4dst(&$2); }
	| IL_V4OFF token		{ set_ipv4off(&$2); }
	| IL_V4V token			{ set_ipv4v(&$2); }
	| IL_V4HL token			{ set_ipv4hl(&$2); }
	| IL_V4ID token			{ set_ipv4id(&$2); }
	| IL_V4TTL token		{ set_ipv4ttl(&$2); }
	| IL_V4TOS token		{ set_ipv4tos(&$2); }
	| IL_V4SUM token		{ set_ipv4sum(&$2); }
	| IL_V4LEN token		{ set_ipv4len(&$2); }
	| ipv4opt '{' ipv4optlist '}' ';'	{ end_ipopt(); }
	;

tcp:	IL_TCP				{ new_tcpheader(); }
	;

tcpline:
	'{' tcpheader '}' ';'		{ end_tcp(); }
	;

tcpheader:
	tcpbody
	| tcpbody tcpheader
	| bodyline
	;

tcpbody:
	IL_SPORT token			{ set_tcpsport(&$2); }
	| IL_DPORT token		{ set_tcpdport(&$2); }
	| IL_TCPSEQ token		{ set_tcpseq(&$2); }
	| IL_TCPACK token		{ set_tcpack(&$2); }
	| IL_TCPOFF token		{ set_tcpoff(&$2); }
	| IL_TCPURP token		{ set_tcpurp(&$2); }
	| IL_TCPWIN token		{ set_tcpwin(&$2); }
	| IL_TCPSUM token		{ set_tcpsum(&$2); }
	| IL_TCPFL token		{ set_tcpflags(&$2); }
	| IL_TCPOPT '{' tcpopts '}' ';'	{ end_tcpopt(); }
	;

tcpopts:
	| tcpopt tcpopts
	;

tcpopt:	IL_TCPO_NOP ';'			{ set_tcpopt(IL_TCPO_NOP, NULL); }
	| IL_TCPO_EOL ';'		{ set_tcpopt(IL_TCPO_EOL, NULL); }
	| IL_TCPO_MSS optoken		{ set_tcpopt(IL_TCPO_MSS,&$2);}
	| IL_TCPO_WSCALE optoken	{ set_tcpopt(IL_TCPO_WSCALE,&$2);}
	| IL_TCPO_TS optoken		{ set_tcpopt(IL_TCPO_TS, &$2);}
	;

udp:	IL_UDP				{ new_udpheader(); }
	;

udpline:
	'{' udpheader '}' ';'		{ end_udp(); }
	;


udpheader:
	udpbody
	| udpbody udpheader
	| bodyline
	;

udpbody:
	IL_SPORT token			{ set_tcpsport(&$2); }
	| IL_DPORT token		{ set_tcpdport(&$2); }
	| IL_UDPLEN token		{ set_udplen(&$2); }
	| IL_UDPSUM token		{ set_udpsum(&$2); }
	;

icmp:	IL_ICMP				{ new_icmpheader(); }
	;

icmpline:
	'{' icmpbody '}' ';'		{ end_icmp(); }
	;

icmpbody:
	icmpheader
	| icmpheader bodyline
	;

icmpheader:
	IL_ICMPTYPE icmptype
	| IL_ICMPTYPE icmptype icmpcode
	;

icmpcode:
	IL_ICMPCODE token		{ set_icmpcodetok(&$2); }
	;

icmptype:
	IL_ICMP_ECHOREPLY ';'		{ set_icmptype(ICMP_ECHOREPLY); }
	| IL_ICMP_ECHOREPLY '{' icmpechoopts '}' ';'
	| unreach
	| IL_ICMP_SOURCEQUENCH ';'	{ set_icmptype(ICMP_SOURCEQUENCH); }
	| redirect
	| IL_ICMP_ROUTERADVERT ';'	{ set_icmptype(ICMP_ROUTERADVERT); }
	| IL_ICMP_ROUTERSOLICIT ';'	{ set_icmptype(ICMP_ROUTERSOLICIT); }
	| IL_ICMP_ECHO ';'		{ set_icmptype(ICMP_ECHO); }
	| IL_ICMP_ECHO '{' icmpechoopts '}' ';'
	| IL_ICMP_TIMXCEED ';'		{ set_icmptype(ICMP_TIMXCEED); }
	| IL_ICMP_TIMXCEED '{' exceed '}' ';'
	| IL_ICMP_TSTAMP ';'		{ set_icmptype(ICMP_TSTAMP); }
	| IL_ICMP_TSTAMPREPLY ';'	{ set_icmptype(ICMP_TSTAMPREPLY); }
	| IL_ICMP_TSTAMPREPLY '{' icmptsopts '}' ';'
	| IL_ICMP_IREQ ';'		{ set_icmptype(ICMP_IREQ); }
	| IL_ICMP_IREQREPLY ';'		{ set_icmptype(ICMP_IREQREPLY); }
	| IL_ICMP_IREQREPLY '{' data dataline '}' ';'
	| IL_ICMP_MASKREQ ';'		{ set_icmptype(ICMP_MASKREQ); }
	| IL_ICMP_MASKREPLY ';'		{ set_icmptype(ICMP_MASKREPLY); }
	| IL_ICMP_MASKREPLY '{' token '}' ';'
	| IL_ICMP_PARAMPROB ';'		{ set_icmptype(ICMP_PARAMPROB); }
	| IL_ICMP_PARAMPROB '{' paramprob '}' ';'
	| IL_TOKEN ';'			{ set_icmptypetok(&$1); }
	;

icmpechoopts:
	| icmpechoopts icmpecho
	;

icmpecho:
	IL_ICMP_SEQ number 		{ set_icmpseq($2); }
	| IL_ICMP_ID number		{ set_icmpid($2); }
	;

icmptsopts:
	| icmptsopts icmpts ';'
	;

icmpts: IL_ICMP_OTIME number 		{ set_icmpotime($2); }
	| IL_ICMP_RTIME number 		{ set_icmprtime($2); }
	| IL_ICMP_TTIME number 		{ set_icmpttime($2); }
	;

unreach:
	IL_ICMP_UNREACH
	| IL_ICMP_UNREACH '{' unreachopts '}' ';'
	;

unreachopts:
	IL_ICMP_UNREACH_NET line
	| IL_ICMP_UNREACH_HOST line
	| IL_ICMP_UNREACH_PROTOCOL line
	| IL_ICMP_UNREACH_PORT line
	| IL_ICMP_UNREACH_NEEDFRAG number ';'	{ set_icmpmtu($2); }
	| IL_ICMP_UNREACH_SRCFAIL line
	| IL_ICMP_UNREACH_NET_UNKNOWN line
	| IL_ICMP_UNREACH_HOST_UNKNOWN line
	| IL_ICMP_UNREACH_ISOLATED line
	| IL_ICMP_UNREACH_NET_PROHIB line
	| IL_ICMP_UNREACH_HOST_PROHIB line
	| IL_ICMP_UNREACH_TOSNET line
	| IL_ICMP_UNREACH_TOSHOST line
	| IL_ICMP_UNREACH_FILTER_PROHIB line
	| IL_ICMP_UNREACH_HOST_PRECEDENCE line
	| IL_ICMP_UNREACH_PRECEDENCE_CUTOFF line
	;

redirect:
	IL_ICMP_REDIRECT
	| IL_ICMP_REDIRECT '{' redirectopts '}' ';'
	;

redirectopts:
	| IL_ICMP_REDIRECT_NET token		{ set_redir(0, &$2); }
	| IL_ICMP_REDIRECT_HOST token		{ set_redir(1, &$2); }
	| IL_ICMP_REDIRECT_TOSNET token		{ set_redir(2, &$2); }
	| IL_ICMP_REDIRECT_TOSHOST token	{ set_redir(3, &$2); }
	;

exceed:
	IL_ICMP_TIMXCEED_INTRANS line
	| IL_ICMP_TIMXCEED_REASS line
	;

paramprob:
	IL_ICMP_PARAMPROB_OPTABSENT
	| IL_ICMP_PARAMPROB_OPTABSENT paraprobarg

paraprobarg:
	'{' number '}' ';'		{ set_icmppprob($2); }
	;

ipv4opt:	IL_V4OPT		{ new_ipv4opt(); }
	;

ipv4optlist:
	| ipv4opts ipv4optlist
	;

ipv4opts:
	IL_IPO_NOP ';'			{ add_ipopt(IL_IPO_NOP, NULL); }
	| IL_IPO_RR optnumber		{ add_ipopt(IL_IPO_RR, &$2); }
	| IL_IPO_ZSU ';'		{ add_ipopt(IL_IPO_ZSU, NULL); }
	| IL_IPO_MTUP ';'		{ add_ipopt(IL_IPO_MTUP, NULL); }
	| IL_IPO_MTUR ';'		{ add_ipopt(IL_IPO_MTUR, NULL); }
	| IL_IPO_ENCODE ';'		{ add_ipopt(IL_IPO_ENCODE, NULL); }
	| IL_IPO_TS ';'			{ add_ipopt(IL_IPO_TS, NULL); }
	| IL_IPO_TR ';'			{ add_ipopt(IL_IPO_TR, NULL); }
	| IL_IPO_SEC ';'		{ add_ipopt(IL_IPO_SEC, NULL); }
	| IL_IPO_SECCLASS secclass	{ add_ipopt(IL_IPO_SECCLASS, sclass); }
	| IL_IPO_LSRR token		{ add_ipopt(IL_IPO_LSRR,&$2); }
	| IL_IPO_ESEC ';'		{ add_ipopt(IL_IPO_ESEC, NULL); }
	| IL_IPO_CIPSO ';'		{ add_ipopt(IL_IPO_CIPSO, NULL); }
	| IL_IPO_SATID optnumber	{ add_ipopt(IL_IPO_SATID,&$2);}
	| IL_IPO_SSRR token		{ add_ipopt(IL_IPO_SSRR,&$2); }
	| IL_IPO_ADDEXT ';'		{ add_ipopt(IL_IPO_ADDEXT, NULL); }
	| IL_IPO_VISA ';'		{ add_ipopt(IL_IPO_VISA, NULL); }
	| IL_IPO_IMITD ';'		{ add_ipopt(IL_IPO_IMITD, NULL); }
	| IL_IPO_EIP ';'		{ add_ipopt(IL_IPO_EIP, NULL); }
	| IL_IPO_FINN ';'		{ add_ipopt(IL_IPO_FINN, NULL); }
	;

secclass:
	IL_IPS_RESERV4 ';'		{ set_secclass(&$1); }
	| IL_IPS_TOPSECRET ';'		{ set_secclass(&$1); }
	| IL_IPS_SECRET ';'		{ set_secclass(&$1); }
	| IL_IPS_RESERV3 ';'		{ set_secclass(&$1); }
	| IL_IPS_CONFID ';'		{ set_secclass(&$1); }
	| IL_IPS_UNCLASS ';'		{ set_secclass(&$1); }
	| IL_IPS_RESERV2 ';'		{ set_secclass(&$1); }
	| IL_IPS_RESERV1 ';'		{ set_secclass(&$1); }
	;

data:	IL_DATA				{ new_data(); }
	;

dataline:
	'{' databody '}' ';'		{ end_data(); }
	;

databody: dataopts
	| dataopts databody
	;

dataopts:
	IL_DLEN token			{ set_datalen(&$2); }
	| IL_DVALUE token 		{ set_data(&$2); }
	| IL_DFILE token 		{ set_datafile(&$2); }
	;

token: IL_TOKEN ';'
	;

optoken: ';'				{ $$ = ""; }
	| token
	;

number: digits ';'
	;

optnumber: ';'				{ $$ = 0; }
	| number
	;

digits:	IL_NUMBER
	| digits IL_NUMBER
	;
%%

struct	statetoopt	toipopts[] = {
	{ IL_IPO_NOP,		IPOPT_NOP },
	{ IL_IPO_RR,		IPOPT_RR },
	{ IL_IPO_ZSU,		IPOPT_ZSU },
	{ IL_IPO_MTUP,		IPOPT_MTUP },
	{ IL_IPO_MTUR,		IPOPT_MTUR },
	{ IL_IPO_ENCODE,	IPOPT_ENCODE },
	{ IL_IPO_TS,		IPOPT_TS },
	{ IL_IPO_TR,		IPOPT_TR },
	{ IL_IPO_SEC,		IPOPT_SECURITY },
	{ IL_IPO_SECCLASS,	IPOPT_SECURITY },
	{ IL_IPO_LSRR,		IPOPT_LSRR },
	{ IL_IPO_ESEC,		IPOPT_E_SEC },
	{ IL_IPO_CIPSO,		IPOPT_CIPSO },
	{ IL_IPO_SATID,		IPOPT_SATID },
	{ IL_IPO_SSRR,		IPOPT_SSRR },
	{ IL_IPO_ADDEXT,	IPOPT_ADDEXT },
	{ IL_IPO_VISA,		IPOPT_VISA },
	{ IL_IPO_IMITD,		IPOPT_IMITD },
	{ IL_IPO_EIP,		IPOPT_EIP },
	{ IL_IPO_FINN,		IPOPT_FINN },
	{ 0, 0 }
};

struct	statetoopt	tosecopts[] = {
	{ IL_IPS_RESERV4,	IPSO_CLASS_RES4 },
	{ IL_IPS_TOPSECRET,	IPSO_CLASS_TOPS },
	{ IL_IPS_SECRET,	IPSO_CLASS_SECR },
	{ IL_IPS_RESERV3,	IPSO_CLASS_RES3 },
	{ IL_IPS_CONFID,	IPSO_CLASS_CONF },
	{ IL_IPS_UNCLASS,	IPSO_CLASS_UNCL },
	{ IL_IPS_RESERV2,	IPSO_CLASS_RES2 },
	{ IL_IPS_RESERV1,	IPSO_CLASS_RES1 },
	{ 0, 0 }
};

#ifdef	bsdi
struct ether_addr *
ether_aton(s)
	char *s;   
{
	static struct ether_addr n;
	u_int i[6];

	if (sscanf(s, " %x:%x:%x:%x:%x:%x ", &i[0], &i[1],
	    &i[2], &i[3], &i[4], &i[5]) == 6) {
		n.ether_addr_octet[0] = (u_char)i[0];
		n.ether_addr_octet[1] = (u_char)i[1];
		n.ether_addr_octet[2] = (u_char)i[2];
		n.ether_addr_octet[3] = (u_char)i[3];
		n.ether_addr_octet[4] = (u_char)i[4];
		n.ether_addr_octet[5] = (u_char)i[5];
		return &n;
	}
	return NULL;
}
#endif


struct in_addr getipv4addr(arg)
char *arg;
{
	struct hostent *hp;
	struct in_addr in;

	in.s_addr = 0xffffffff;

	if ((hp = gethostbyname(arg)))
		bcopy(hp->h_addr, &in.s_addr, sizeof(struct in_addr));
	else
		in.s_addr = inet_addr(arg);
	return in;
}


u_short getportnum(pr, name)
char *pr, *name;
{
	struct servent *sp;

	if (!(sp = getservbyname(name, pr)))
		return htons(atoi(name));
	return sp->s_port;
}


struct ether_addr *geteaddr(arg, buf)
char *arg;
struct ether_addr *buf;
{
	struct ether_addr *e;

#if !defined(hpux) && !defined(linux)
	e = ether_aton(arg);
	if (!e)
		fprintf(stderr, "Invalid ethernet address: %s\n", arg);
	else
# ifdef	__FreeBSD__
		bcopy(e->octet, buf->octet, sizeof(e->octet));
# else
		bcopy(e->ether_addr_octet, buf->ether_addr_octet,
		      sizeof(e->ether_addr_octet));
# endif
	return e;
#else
	return NULL;
#endif
}


void *new_header(type)
int type;
{
	aniphdr_t *aip, *oip = canip;
	int	sz = 0;

	aip = (aniphdr_t *)calloc(1, sizeof(*aip));
	*aniptail = aip;
	aniptail = &aip->ah_next;
	aip->ah_p = type;
	aip->ah_prev = oip;
	canip = aip;

	if (type == IPPROTO_UDP)
		sz = sizeof(udphdr_t);
	else if (type == IPPROTO_TCP)
		sz = sizeof(tcphdr_t);
	else if (type == IPPROTO_ICMP)
		sz = sizeof(icmphdr_t);
	else if (type == IPPROTO_IP)
		sz = sizeof(ip_t);

	if (oip)
		canip->ah_data = oip->ah_data + oip->ah_len;
	else
		canip->ah_data = (char *)ipbuffer;

	/*
	 * Increase the size fields in all wrapping headers.
	 */
	for (aip = aniphead; aip; aip = aip->ah_next) {
		aip->ah_len += sz;
		if (aip->ah_p == IPPROTO_IP)
			aip->ah_ip->ip_len += sz;
		else if (aip->ah_p == IPPROTO_UDP)
			aip->ah_udp->uh_ulen += sz;
	}
	return (void *)canip->ah_data;
}


void free_aniplist()
{
	aniphdr_t *aip, **aipp = &aniphead;

	while ((aip = *aipp)) {
		*aipp = aip->ah_next;
		free(aip);
	}
	aniptail = &aniphead;
}


void inc_anipheaders(inc)
int inc;
{
	aniphdr_t *aip;

	for (aip = aniphead; aip; aip = aip->ah_next) {
		aip->ah_len += inc;
		if (aip->ah_p == IPPROTO_IP)
			aip->ah_ip->ip_len += inc;
		else if (aip->ah_p == IPPROTO_UDP)
			aip->ah_udp->uh_ulen += inc;
	}
}


void new_data()
{
	(void) new_header(-1);
	canip->ah_len = 0;
}


void set_datalen(arg)
char **arg;
{
	int	len;

	len = strtol(*arg, NULL, 0);
	inc_anipheaders(len);
	free(*arg);
	*arg = NULL;
}


void set_data(arg)
char **arg;
{
	u_char *s = (u_char *)*arg, *t = (u_char *)canip->ah_data, c;
	int len = 0, todo = 0, quote = 0, val = 0;

	while ((c = *s++)) {
		if (todo) {
			if (isdigit(c)) {
				todo--;
				if (c > '7') {
					fprintf(stderr, "octal with %c!\n", c);
					break;
				}
				val <<= 3;
				val |= (c - '0');
			}
			if (!isdigit(c) || !todo) {
				*t++ = (u_char)(val & 0xff);
				todo = 0;
			}
			if (todo)
				continue;
		}
		if (quote) {
			if (isdigit(c)) {
				todo = 2;
				if (c > '7') {
					fprintf(stderr, "octal with %c!\n", c);
					break;
				}
				val = (c - '0');
			} else {
				switch (c)
				{
				case '\"' :
					*t++ = '\"';
					break;
				case '\\' :
					*t++ = '\\';
					break;
				case 'n' :
					*t++ = '\n';
					break;
				case 'r' :
					*t++ = '\r';
					break;
				case 't' :
					*t++ = '\t';
					break;
				}
			}
			quote = 0;
			continue;
		}

		if (c == '\\')
			quote = 1;
		else
			*t++ = c;
	}
	if (todo)
		*t++ = (u_char)(val & 0xff);
	if (quote)
		*t++ = '\\';
	len = t - (u_char *)canip->ah_data;
	inc_anipheaders(len - canip->ah_len);
	canip->ah_len = len;
}


void set_datafile(arg)
char **arg;
{
	struct stat sb;
	char *file = *arg;
	int fd, len;

	if ((fd = open(file, O_RDONLY)) == -1) {
		perror("open");
		exit(-1);
	}

	if (fstat(fd, &sb) == -1) {
		perror("fstat");
		exit(-1);
	}

	if ((sb.st_size + aniphead->ah_len ) > 65535) {
		fprintf(stderr, "data file %s too big to include.\n", file);
		close(fd);
		return;
	}
	if ((len = read(fd, canip->ah_data, sb.st_size)) == -1) {
		perror("read");
		close(fd);
		return;
	}
	inc_anipheaders(len);
	canip->ah_len += len;
	close(fd);
}


void new_packet()
{
	static	u_short	id = 0;

	if (!aniphead)
		bzero((char *)ipbuffer, sizeof(ipbuffer));

	ip = (ip_t *)new_header(IPPROTO_IP);
	ip->ip_v = IPVERSION;
	ip->ip_hl = sizeof(ip_t) >> 2;
	ip->ip_len = sizeof(ip_t);
	ip->ip_ttl = 63;
	ip->ip_id = htons(id++);
}


void set_ipv4proto(arg)
char **arg;
{
	struct protoent *pr;

	if ((pr = getprotobyname(*arg)))
		ip->ip_p = pr->p_proto;
	else
		if (!(ip->ip_p = atoi(*arg)))
			fprintf(stderr, "unknown protocol %s\n", *arg);
	free(*arg);
	*arg = NULL;
}


void set_ipv4src(arg)
char **arg;
{
	ip->ip_src = getipv4addr(*arg);
	free(*arg);
	*arg = NULL;
}


void set_ipv4dst(arg)
char **arg;
{
	ip->ip_dst = getipv4addr(*arg);
	free(*arg);
	*arg = NULL;
}


void set_ipv4off(arg)
char **arg;
{
	ip->ip_off = htons(strtol(*arg, NULL, 0));
	free(*arg);
	*arg = NULL;
}


void set_ipv4v(arg)
char **arg;
{
	ip->ip_v = strtol(*arg, NULL, 0);
	free(*arg);
	*arg = NULL;
}


void set_ipv4hl(arg)
char **arg;
{
	int newhl, inc;

	newhl = strtol(*arg, NULL, 0);
	inc = (newhl - ip->ip_hl) << 2;
	ip->ip_len += inc;
	ip->ip_hl = newhl;
	canip->ah_len += inc;
	free(*arg);
	*arg = NULL;
}


void set_ipv4ttl(arg)
char **arg;
{
	ip->ip_ttl = strtol(*arg, NULL, 0);
	free(*arg);
	*arg = NULL;
}


void set_ipv4tos(arg)
char **arg;
{
	ip->ip_tos = strtol(*arg, NULL, 0);
	free(*arg);
	*arg = NULL;
}


void set_ipv4id(arg)
char **arg;
{
	ip->ip_id = htons(strtol(*arg, NULL, 0));
	free(*arg);
	*arg = NULL;
}


void set_ipv4sum(arg)
char **arg;
{
	ip->ip_sum = strtol(*arg, NULL, 0);
	free(*arg);
	*arg = NULL;
}


void set_ipv4len(arg)
char **arg;
{
	int len;

	len = strtol(*arg, NULL, 0);
	inc_anipheaders(len - ip->ip_len);
	ip->ip_len = len;
	free(*arg);
	*arg = NULL;
}


void new_tcpheader()
{

	if ((ip->ip_p) && (ip->ip_p != IPPROTO_TCP)) {
		fprintf(stderr, "protocol %d specified with TCP!\n", ip->ip_p);
		return;
	}
	ip->ip_p = IPPROTO_TCP;

	tcp = (tcphdr_t *)new_header(IPPROTO_TCP);
	tcp->th_win = htons(4096);
	tcp->th_off = sizeof(*tcp) >> 2;
}


void set_tcpsport(arg)
char **arg;
{
	u_short *port;
	char *pr;

	if (ip->ip_p == IPPROTO_UDP) {
		port = &udp->uh_sport;
		pr = "udp";
	} else {
		port = &tcp->th_sport;
		pr = "udp";
	}

	*port = getportnum(pr, *arg);
	free(*arg);
	*arg = NULL;
}


void set_tcpdport(arg)
char **arg;
{
	u_short *port;
	char *pr;

	if (ip->ip_p == IPPROTO_UDP) {
		port = &udp->uh_dport;
		pr = "udp";
	} else {
		port = &tcp->th_dport;
		pr = "udp";
	}

	*port = getportnum(pr, *arg);
	free(*arg);
	*arg = NULL;
}


void set_tcpseq(arg)
char **arg;
{
	tcp->th_seq = htonl(strtol(*arg, NULL, 0));
	free(*arg);
	*arg = NULL;
}


void set_tcpack(arg)
char **arg;
{
	tcp->th_ack = htonl(strtol(*arg, NULL, 0));
	free(*arg);
	*arg = NULL;
}


void set_tcpoff(arg)
char **arg;
{
	int	off;

	off = strtol(*arg, NULL, 0);
	inc_anipheaders((off - tcp->th_off) << 2);
	tcp->th_off = off;
	free(*arg);
	*arg = NULL;
}


void set_tcpurp(arg)
char **arg;
{
	tcp->th_urp = htons(strtol(*arg, NULL, 0));
	free(*arg);
	*arg = NULL;
}


void set_tcpwin(arg)
char **arg;
{
	tcp->th_win = htons(strtol(*arg, NULL, 0));
	free(*arg);
	*arg = NULL;
}


void set_tcpsum(arg)
char **arg;
{
	tcp->th_sum = strtol(*arg, NULL, 0);
	free(*arg);
	*arg = NULL;
}


void set_tcpflags(arg)
char **arg;
{
	static	char	flags[] = "ASURPF";
	static	int	flagv[] = { TH_ACK, TH_SYN, TH_URG, TH_RST, TH_PUSH,
				    TH_FIN } ;
	char *s, *t;

	for (s = *arg; *s; s++)
		if (!(t = strchr(flags, *s))) {
			if (s - *arg) {
				fprintf(stderr, "unknown TCP flag %c\n", *s);
				break;
			}
			tcp->th_flags = strtol(*arg, NULL, 0);
			break;
		} else
			tcp->th_flags |= flagv[t - flags];
	free(*arg);
	*arg = NULL;
}


void set_tcpopt(state, arg)
int state;
char **arg;
{
	u_char *s;
	int val, len, val2, pad, optval;

	if (arg && *arg)
		val = atoi(*arg);
	else
		val = 0;

	s = (u_char *)tcp + sizeof(*tcp) + canip->ah_optlen;
	switch (state)
	{
	case IL_TCPO_EOL :
		optval = 0;
		len = 1;
		break;
	case IL_TCPO_NOP :
		optval = 1;
		len = 1;
		break;
	case IL_TCPO_MSS :
		optval = 2;
		len = 4;
		break;
	case IL_TCPO_WSCALE :
		optval = 3;
		len = 3;
		break;
	case IL_TCPO_TS :
		optval = 8;
		len = 10;
		break;
	default :
		optval = 0;
		len = 0;
		break;
	}

	if (len > 1) {
		/*
		 * prepend padding - if required.
		 */
		if (len & 3)
			for (pad = 4 - (len & 3); pad; pad--) {
				*s++ = 1;
				canip->ah_optlen++;
			}
		/*
		 * build tcp option
		 */
		*s++ = (u_char)optval;
		*s++ = (u_char)len;
		if (len > 2) {
			if (len == 3) {		/* 1 byte - char */
				*s++ = (u_char)val;
			} else if (len == 4) {	/* 2 bytes - short */
				*s++ = (u_char)((val >> 8) & 0xff);
				*s++ = (u_char)(val & 0xff);
			} else if (len >= 6) {	/* 4 bytes - long */
				val2 = htonl(val);
				bcopy((char *)&val2, s, 4);
			}
			s += (len - 2);
		}
	} else
		*s++ = (u_char)optval;

	canip->ah_lastopt = optval;
	canip->ah_optlen += len;

	if (arg && *arg) {
		free(*arg);
		*arg = NULL;
	}
}


void end_tcpopt()
{
	int pad;
	char *s = (char *)tcp;

	s += sizeof(*tcp) + canip->ah_optlen;
	/*
	 * pad out so that we have a multiple of 4 bytes in size fo the
	 * options.  make sure last byte is EOL.
	 */
	if (canip->ah_optlen & 3) {
		if (canip->ah_lastopt != 1) {
			for (pad = 3 - (canip->ah_optlen & 3); pad; pad--) {
				*s++ = 1;
				canip->ah_optlen++;
			}
			canip->ah_optlen++;
		} else {
			s -= 1;

			for (pad = 3 - (canip->ah_optlen & 3); pad; pad--) {
				*s++ = 1;
				canip->ah_optlen++;
			}
		}
		*s++ = 0;
	}
	tcp->th_off = (sizeof(*tcp) + canip->ah_optlen) >> 2;
	inc_anipheaders(canip->ah_optlen);
}


void new_udpheader()
{
	if ((ip->ip_p) && (ip->ip_p != IPPROTO_UDP)) {
		fprintf(stderr, "protocol %d specified with UDP!\n", ip->ip_p);
		return;
	}
	ip->ip_p = IPPROTO_UDP;

	udp = (udphdr_t *)new_header(IPPROTO_UDP);
	udp->uh_ulen = sizeof(*udp);
}


void set_udplen(arg)
char **arg;
{
	int len;

	len = strtol(*arg, NULL, 0);
	inc_anipheaders(len - udp->uh_ulen);
	udp->uh_ulen = len;
	free(*arg);
	*arg = NULL;
}


void set_udpsum(arg)
char **arg;
{
	udp->uh_sum = strtol(*arg, NULL, 0);
	free(*arg);
	*arg = NULL;
}


void prep_packet()
{
	iface_t *ifp;
	struct in_addr gwip;

	ifp = sending.snd_if;
	if (!ifp) {
		fprintf(stderr, "no interface defined for sending!\n");
		return;
	}
	if (ifp->if_fd == -1)
		ifp->if_fd = initdevice(ifp->if_name, 0, 5);
	gwip = sending.snd_gw;
	if (!gwip.s_addr)
		gwip = aniphead->ah_ip->ip_dst;
	(void) send_ip(ifp->if_fd, ifp->if_MTU, (ip_t *)ipbuffer, gwip, 2);
}


void packet_done()
{
	char    outline[80];
	int     i, j, k;
	u_char  *s = (u_char *)ipbuffer, *t = (u_char *)outline;

	if (opts & OPT_VERBOSE) {
		ip->ip_len = htons(ip->ip_len);
		for (i = ntohs(ip->ip_len), j = 0; i; i--, j++, s++) {
			if (j && !(j & 0xf)) {
				*t++ = '\n';
				*t = '\0';
				fputs(outline, stdout);
				fflush(stdout);
				t = (u_char *)outline;
				*t = '\0';
			}
			sprintf((char *)t, "%02x", *s & 0xff);
			t += 2;
			if (!((j + 1) & 0xf)) {
				s -= 15;
				sprintf((char *)t, "	");
				t += 8;
				for (k = 16; k; k--, s++)
					*t++ = (isprint(*s) ? *s : '.');
				s--;
			}

			if ((j + 1) & 0xf)
				*t++ = ' ';;
		}

		if (j & 0xf) {
			for (k = 16 - (j & 0xf); k; k--) {
				*t++ = ' ';
				*t++ = ' ';
				*t++ = ' ';
			}
			sprintf((char *)t, "       ");
			t += 7;
			s -= j & 0xf;
			for (k = j & 0xf; k; k--, s++)
				*t++ = (isprint(*s) ? *s : '.');
			*t++ = '\n';
			*t = '\0';
		}
		fputs(outline, stdout);
		fflush(stdout);
		ip->ip_len = ntohs(ip->ip_len);
	}

	prep_packet();
	free_aniplist();
}


void new_interface()
{
	cifp = (iface_t *)calloc(1, sizeof(iface_t));
	*iftail = cifp;
	iftail = &cifp->if_next;
	cifp->if_fd = -1;
}


void check_interface()
{
	if (!cifp->if_name || !*cifp->if_name)
		fprintf(stderr, "No interface name given!\n");
	if (!cifp->if_MTU || !*cifp->if_name)
		fprintf(stderr, "Interface %s has an MTU of 0!\n",
			cifp->if_name);
}


void set_ifname(arg)
char **arg;
{
	cifp->if_name = *arg;
	*arg = NULL;
}


void set_ifmtu(arg)
int arg;
{
	cifp->if_MTU = arg;
}


void set_ifv4addr(arg)
char **arg;
{
	cifp->if_addr = getipv4addr(*arg);
	free(*arg);
	*arg = NULL;
}


void set_ifeaddr(arg)
char **arg;
{
	(void) geteaddr(*arg, &cifp->if_eaddr);
	free(*arg);
	*arg = NULL;
}


void new_arp()
{
	carp = (arp_t *)calloc(1, sizeof(arp_t));
	*arptail = carp;
	arptail = &carp->arp_next;
}


void set_arpeaddr(arg)
char **arg;
{
	(void) geteaddr(*arg, &carp->arp_eaddr);
	free(*arg);
	*arg = NULL;
}


void set_arpv4addr(arg)
char **arg;
{
	carp->arp_addr = getipv4addr(*arg);
	free(*arg);
	*arg = NULL;
}


int arp_getipv4(ip, addr)
char *ip;
char *addr;
{
	arp_t *a;

	for (a = arplist; a; a = a->arp_next)
		if (!bcmp(ip, (char *)&a->arp_addr, 4)) {
			bcopy((char *)&a->arp_eaddr, addr, 6);
			return 0;
		}
	return -1;
}


void reset_send()
{
	sending.snd_if = iflist;
	sending.snd_gw = defrouter;
}


void set_sendif(arg)
char **arg;
{
	iface_t	*ifp;

	for (ifp = iflist; ifp; ifp = ifp->if_next)
		if (ifp->if_name && !strcmp(ifp->if_name, *arg))
			break;
	sending.snd_if = ifp;
	if (!ifp)
		fprintf(stderr, "couldn't find interface %s\n", *arg);
	free(*arg);
	*arg = NULL;
}


void set_sendvia(arg)
char **arg;
{
	sending.snd_gw = getipv4addr(*arg);
	free(*arg);
	*arg = NULL;
}


void set_defaultrouter(arg)
char **arg;
{
	defrouter = getipv4addr(*arg);
	free(*arg);
	*arg = NULL;
}


void new_icmpheader()
{
	if ((ip->ip_p) && (ip->ip_p != IPPROTO_ICMP)) {
		fprintf(stderr, "protocol %d specified with ICMP!\n",
			ip->ip_p);
		return;
	}
	ip->ip_p = IPPROTO_ICMP;
	icmp = (icmphdr_t *)new_header(IPPROTO_ICMP);
}


void set_icmpcode(code)
int code;
{
	icmp->icmp_code = code;
}


void set_icmptype(type)
int type;
{
	icmp->icmp_type = type;
}


static	char	*icmpcodes[] = {
	"net-unr", "host-unr", "proto-unr", "port-unr", "needfrag", "srcfail",
	"net-unk", "host-unk", "isolate", "net-prohib", "host-prohib",
	"net-tos", "host-tos", NULL };

void set_icmpcodetok(code)
char **code;
{
	char	*s;
	int	i;

	for (i = 0; (s = icmpcodes[i]); i++)
		if (!strcmp(s, *code)) {
			icmp->icmp_code = i;
			break;
		}
	if (!s)
		fprintf(stderr, "unknown ICMP code %s\n", *code);
	free(*code);
	*code = NULL;
}


static	char	*icmptypes[] = {
	"echorep", (char *)NULL, (char *)NULL, "unreach", "squench",
	"redir", (char *)NULL, (char *)NULL, "echo", (char *)NULL,
	(char *)NULL, "timex", "paramprob", "timest", "timestrep",
	"inforeq", "inforep", "maskreq", "maskrep", "END"
};

void set_icmptypetok(type)
char **type;
{
	char	*s;
	int	i, done = 0;

	for (i = 0; !(s = icmptypes[i]) || strcmp(s, "END"); i++)
		if (s && !strcmp(s, *type)) {
			icmp->icmp_type = i;
			done = 1;
			break;
		}
	if (!done)
		fprintf(stderr, "unknown ICMP type %s\n", *type);
	free(*type);
	*type = NULL;
}


void set_icmpid(arg)
int arg;
{
	icmp->icmp_id = htons(arg);
}


void set_icmpseq(arg)
int arg;
{
	icmp->icmp_seq = htons(arg);
}


void set_icmpotime(arg)
int arg;
{
	icmp->icmp_otime = htonl(arg);
}


void set_icmprtime(arg)
int arg;
{
	icmp->icmp_rtime = htonl(arg);
}


void set_icmpttime(arg)
int arg;
{
	icmp->icmp_ttime = htonl(arg);
}


void set_icmpmtu(arg)
int arg;
{
#if	BSD >= 199306
	icmp->icmp_nextmtu = htons(arg);
#endif
}


void set_redir(redir, arg)
int redir;
char **arg;
{
	icmp->icmp_code = redir;
	icmp->icmp_gwaddr = getipv4addr(*arg);
	free(*arg);
	*arg = NULL;
}


void set_icmppprob(num)
int num;
{
	icmp->icmp_pptr = num;
}


void new_ipv4opt()
{
	new_header(-2);
}


void add_ipopt(state, ptr)
int state;
void *ptr;
{
	struct ipopt_names *io;
	struct statetoopt *sto;
	char numbuf[16], *arg, **param = ptr;
	int inc, hlen;

	if (state == IL_IPO_RR || state == IL_IPO_SATID) {
		if (param)
			sprintf(numbuf, "%d", *(int *)param);
		else
			strcpy(numbuf, "0");
		arg = numbuf;
	} else
		arg = param ? *param : NULL;

	if (canip->ah_next) {
		fprintf(stderr, "cannot specify options after data body\n");
		return;
	}
	for (sto = toipopts; sto->sto_st; sto++)
		if (sto->sto_st == state)
			break;
	if (!sto || !sto->sto_st) {
		fprintf(stderr, "No mapping for state %d to IP option\n",
			state);
		return;
	}

	hlen = sizeof(ip_t) + canip->ah_optlen;
	for (io = ionames; io->on_name; io++)
		if (io->on_value == sto->sto_op)
			break;
	canip->ah_lastopt = io->on_value;

	if (io->on_name) {
		inc = addipopt((char *)ip + hlen, io, hlen - sizeof(ip_t),arg);
		if (inc > 0) {
			while (inc & 3) {
				((char *)ip)[sizeof(*ip) + inc] = IPOPT_NOP;
				canip->ah_lastopt = IPOPT_NOP;
				inc++;
			}
			hlen += inc;
		}
	}

	canip->ah_optlen = hlen - sizeof(ip_t);

	if (state != IL_IPO_RR && state != IL_IPO_SATID)
		if (param && *param) {
			free(*param);
			*param = NULL;
		}
	sclass = NULL;
}


void end_ipopt()
{
	int pad;
	char *s, *buf = (char *)ip;

	/*
	 * pad out so that we have a multiple of 4 bytes in size fo the
	 * options.  make sure last byte is EOL.
	 */
	if (canip->ah_lastopt == IPOPT_NOP) {
		buf[sizeof(*ip) + canip->ah_optlen - 1] = IPOPT_EOL;
	} else if (canip->ah_lastopt != IPOPT_EOL) {
		s = buf + sizeof(*ip) + canip->ah_optlen;

		for (pad = 3 - (canip->ah_optlen & 3); pad; pad--) {
			*s++ = IPOPT_NOP;
			*s = IPOPT_EOL;
			canip->ah_optlen++;
		}
		canip->ah_optlen++;
	} else {
		s = buf + sizeof(*ip) + canip->ah_optlen - 1;

		for (pad = 3 - (canip->ah_optlen & 3); pad; pad--) {
			*s++ = IPOPT_NOP;
			*s = IPOPT_EOL;
			canip->ah_optlen++;
		}
	}
	ip->ip_hl = (sizeof(*ip) + canip->ah_optlen) >> 2;
	inc_anipheaders(canip->ah_optlen);
	free_anipheader();
}


void set_secclass(arg)
char **arg;
{
	sclass = *arg;
	*arg = NULL;
}


void free_anipheader()
{
	aniphdr_t *aip;

	aip = canip;
	if ((canip = aip->ah_prev)) {
		canip->ah_next = NULL;
		aniptail = &canip->ah_next;
	}

	if (canip)
		free(aip);
}


void end_ipv4()
{
	aniphdr_t *aip;

	ip->ip_sum = 0;
	ip->ip_len = htons(ip->ip_len);
	ip->ip_sum = chksum((u_short *)ip, ip->ip_hl << 2);
	ip->ip_len = ntohs(ip->ip_len);
	free_anipheader();
	for (aip = aniphead, ip = NULL; aip; aip = aip->ah_next)
		if (aip->ah_p == IPPROTO_IP)
			ip = aip->ah_ip;
}


void end_icmp()
{
	aniphdr_t *aip;

	icmp->icmp_cksum = 0;
	icmp->icmp_cksum = chksum((u_short *)icmp, canip->ah_len);
	free_anipheader();
	for (aip = aniphead, icmp = NULL; aip; aip = aip->ah_next)
		if (aip->ah_p == IPPROTO_ICMP)
			icmp = aip->ah_icmp;
}


void end_udp()
{
	u_long	sum;
	aniphdr_t *aip;
	ip_t	iptmp;

	bzero((char *)&iptmp, sizeof(iptmp));
	iptmp.ip_p = ip->ip_p;
	iptmp.ip_src = ip->ip_src;
	iptmp.ip_dst = ip->ip_dst;
	iptmp.ip_len = htons(ip->ip_len - (ip->ip_hl << 2));
	sum = p_chksum((u_short *)&iptmp, (u_int)sizeof(iptmp));
	udp->uh_ulen = htons(udp->uh_ulen);
	udp->uh_sum = c_chksum((u_short *)udp, (u_int)ntohs(iptmp.ip_len), sum);
	free_anipheader();
	for (aip = aniphead, udp = NULL; aip; aip = aip->ah_next)
		if (aip->ah_p == IPPROTO_UDP)
			udp = aip->ah_udp;
}


void end_tcp()
{
	u_long	sum;
	aniphdr_t *aip;
	ip_t	iptmp;

	bzero((char *)&iptmp, sizeof(iptmp));
	iptmp.ip_p = ip->ip_p;
	iptmp.ip_src = ip->ip_src;
	iptmp.ip_dst = ip->ip_dst;
	iptmp.ip_len = htons(ip->ip_len - (ip->ip_hl << 2));
	sum = p_chksum((u_short *)&iptmp, (u_int)sizeof(iptmp));
	tcp->th_sum = 0;
	tcp->th_sum = c_chksum((u_short *)tcp, (u_int)ntohs(iptmp.ip_len), sum);
	free_anipheader();
	for (aip = aniphead, tcp = NULL; aip; aip = aip->ah_next)
		if (aip->ah_p == IPPROTO_TCP)
			tcp = aip->ah_tcp;
}


void end_data()
{
	free_anipheader();
}


void iplang(fp)
FILE *fp;
{
	yyin = fp;

	yydebug = (opts & OPT_DEBUG) ? 1 : 0;

	while (!feof(fp))
		yyparse();
}


u_short	c_chksum(buf, len, init)
u_short	*buf;
u_int	len;
u_long	init;
{
	u_long	sum = init;
	int	nwords = len >> 1;
 
	for(; nwords > 0; nwords--)
		sum += *buf++;
	sum = (sum>>16) + (sum & 0xffff);
	sum += (sum >>16);
	return (~sum);
}


u_long	p_chksum(buf,len)
u_short	*buf;
u_int	len;
{
	u_long	sum = 0;
	int	nwords = len >> 1;
 
	for(; nwords > 0; nwords--)
		sum += *buf++;
	return sum;
}
