%{
/*
 * Copyright 2013 Google Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */
/*
 * Author: Author: ncardwell@google.com (Neal Cardwell)
 *
 * This is the parser for the packetdrill script language. It is
 * processed by the bison parser generator.
 *
 * For full documentation see: http://www.gnu.org/software/bison/manual/
 *
 * Here is a quick and dirty tutorial on bison:
 *
 * A bison parser specification is basically a BNF grammar for the
 * language you are parsing. Each rule specifies a nonterminal symbol
 * on the left-hand side and a sequence of terminal symbols (lexical
 * tokens) and or nonterminal symbols on the right-hand side that can
 * "reduce" to the symbol on the left hand side. When the parser sees
 * the sequence of symbols on the right where it "wants" to see a
 * nonterminal on the left, the rule fires, executing the semantic
 * action code in curly {} braces as it reduces the right hand side to
 * the left hand side.
 *
 * The semantic action code for a rule produces an output, which it
 * can reference using the $$ token. The set of possible types
 * returned in output expressions is given in the %union section of
 * the .y file. The specific type of the output for a terminal or
 * nonterminal symbol (corresponding to a field in the %union) is
 * given by the %type directive in the .y file. The action code can
 * access the outputs of the symbols on the right hand side by using
 * the notation $1 for the first symbol, $2 for the second symbol, and
 * so on.
 *
 * The lexer (generated by flex from lexer.l) feeds a stream of
 * terminal symbols up to this parser. Parser semantic actions can
 * access the lexer output for a terminal symbol with the same
 * notation they use for nonterminals.
 *
 * Here's an example rule with its semantic action in {} braces:
 *
 *     tcp_option
 *     ...
 *      | MSS INTEGER   {
 *        $$ = tcp_option_new(...);
 *        ...
 *        $$->data.mss.bytes = htons($2);
 *      }
 *
 * This rule basically says:
 *
 *   When the parser wants to see a tcp_option, if it sees an MSS from
 *   the lexer followed by an INTEGER from the lexer then run the
 *   action code that (a) stores in the output $$ a pointer to a
 *   struct tcp_option object, and then (b) stores in that object the
 *   value of the INTEGER token (accessed with $2).
 *
 */

/* The first part of the .y file consists of C code that bison copies
 * directly into the top of the .c file it generates.
 */

#include "types.h"

#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "gre_packet.h"
#include "ip.h"
#include "ip_packet.h"
#include "icmp_packet.h"
#include "logging.h"
#include "mpls.h"
#include "mpls_packet.h"
#include "sctp_packet.h"
#include "tcp_packet.h"
#include "udp_packet.h"
#include "udplite_packet.h"
#include "parse.h"
#include "script.h"
#include "tcp.h"
#include "tcp_options.h"

/* This include of the bison-generated .h file must go last so that we
 * can first include all of the declarations on which it depends.
 */
#include "parser.h"

/* Change this YYDEBUG to 1 to get verbose debug output for parsing: */
#define YYDEBUG 0
#if YYDEBUG
extern int yydebug;
#endif

extern FILE *yyin;
extern int yylineno;
extern char *yytext;
extern int yylex(void);
extern int yyparse(void);
extern int yywrap(void);

/* This mutex guards all parser global variables declared in this file. */
pthread_mutex_t parser_mutex = PTHREAD_MUTEX_INITIALIZER;

/* The input to the parser: the path name of the script file to parse. */
static const char* current_script_path = NULL;

/* The starting line number of the input script statement that we're
 * currently parsing. This may be different than yylineno if bison had
 * to look ahead and lexically scan a token on the following line to
 * decide that the current statement is done.
 */
static int current_script_line = -1;

/*
 * We uses this object to look up configuration info needed during
 * parsing (such as whether packets are IPv4 or IPv6).
 */
static const struct config *in_config = NULL;

/* The output of the parser: an output script containing
 * 1) a linked list of options
 * 2) a linked list of events
 */
static struct script *out_script = NULL;

/* The test invocation to pass back to parse_and_finalize_config(). */
struct invocation *invocation;

/* Copy the script contents into our single linear buffer. */
void copy_script(const char *script_buffer, struct script *script)
{
	DEBUGP("copy_script\n");

	free(script->buffer);
	script->length = strlen(script_buffer);
	script->buffer = strdup(script_buffer);
	assert(script->buffer != NULL);

	DEBUGP("copy_script: %d bytes\n", script->length);
}

/* Read the script file into a single linear buffer. */
void read_script(const char *script_path, struct script *script)
{
	int size = 0;

	DEBUGP("read_script(%s)\n", script_path);

	while (script->buffer == NULL) {
		struct stat script_info;
		int fd = -1;

		/* Allocate a buffer big enough for the whole file. */
		if (stat(script_path, &script_info) != 0)
			die("parse error: stat() of script file '%s': %s\n",
			    script_path, strerror(errno));

		/* Pick a buffer size larger than the file, so we'll
		 * know if the file grew.
		 */
		size = max((int)script_info.st_size, size) + 1;

		script->buffer = malloc(size);
		assert(script->buffer != NULL);

		/* Read the file into our buffer. */
		fd = open(script_path, O_RDONLY);
		if (fd < 0)
			die("parse error opening script file '%s': %s\n",
			    script_path, strerror(errno));

		script->length = read(fd, script->buffer, size);
		if (script->length < 0)
			die("parse error reading script file '%s': %s\n",
			    script_path, strerror(errno));

		/* If we filled the buffer, then probably another
		 * process wrote more to the file since our stat call,
		 * so we should try again.
		 */
		if (script->length == size) {
			free(script->buffer);
			script->buffer = NULL;
			script->length = 0;
		}

		if (close(fd))
			die_perror("close");
	}
	DEBUGP("read_script: %d bytes\n", script->length);
}


/* The public entry point for the script parser. Parses the
 * text script file with the given path name and fills in the script
 * object with the parsed representation.
 */
int parse_script(const struct config *config,
			 struct script *script,
			 struct invocation *callback_invocation)
{
	/* This bison-generated parser is not multi-thread safe, so we
	 * have a lock to prevent more than one thread using the
	 * parser at the same time. This is useful in the wire server
	 * context, where in general we may have more than one test
	 * thread running at the same time.
	 */
	if (pthread_mutex_lock(&parser_mutex) != 0)
		die_perror("pthread_mutex_lock");

#if YYDEBUG
	yydebug = 1;
#endif

	/* Now parse the script from our buffer. */
	yyin = fmemopen(script->buffer, script->length, "r");
	if (yyin == NULL)
		die_perror("fmemopen: parse error opening script buffer");

	current_script_path = config->script_path;
	in_config = config;
	out_script = script;
	invocation = callback_invocation;

	/* We have to reset the line number here since the wire server
	 * can do more than one yyparse().
	 */
	yylineno = 1;

	int result = yyparse();		/* invoke bison-generated parser */
	current_script_path = NULL;

	if (fclose(yyin))
		die_perror("fclose: error closing script buffer");

	/* Unlock parser. */
	if (pthread_mutex_unlock(&parser_mutex) != 0)
		die_perror("pthread_mutex_unlock");

	return result ? STATUS_ERR : STATUS_OK;
}

/* Bison emits code to call this method when there's a parse-time error.
 * We print the line number and the error message.
 */
static void yyerror(const char *message)
{
	fprintf(stderr, "%s:%d: parse error at '%s': %s\n",
		current_script_path, yylineno, yytext, message);
}

/* After we finish parsing each line of a script, we analyze the
 * semantics of the line. If we encounter an error then we print the
 * error message to stderr and exit with an error.
 */
static void semantic_error(const char* message)
{
	assert(current_script_line >= 0);
	die("%s:%d: semantic error: %s\n",
	    current_script_path, current_script_line, message);
}

/* This standard callback is invoked by flex when it encounters
 * the end of a file. We return 1 to tell flex to return EOF.
 */
int yywrap(void)
{
	return 1;
}

/* Create and initalize a new expression. */
static struct expression *new_expression(enum expression_t type)
{
	struct expression *expression = calloc(1, sizeof(struct expression));
	expression->type = type;
	return expression;
}

/* Create and initalize a new integer expression with the given
 * literal value and format string.
 */
static struct expression *new_integer_expression(s64 num, const char *format)
{
	struct expression *expression = new_expression(EXPR_INTEGER);
	expression->value.num = num;
	expression->format = format;
	return expression;
}

/* Create and initalize a new one-element expression_list. */
static struct expression_list *new_expression_list(
	struct expression *expression)
{
	struct expression_list *list;
	list = calloc(1, sizeof(struct expression_list));
	list->expression = expression;
	list->next = NULL;
	return list;
}

/* Add the expression to the end of the list. */
static void expression_list_append(struct expression_list *list,
				   struct expression *expression)
{
	while (list->next != NULL) {
		list = list->next;
	}
	list->next = new_expression_list(expression);
}

/* Create and initialize a new option. */
static struct option_list *new_option(char *name, char *value)
{
	struct option_list *opt = calloc(1, sizeof(struct option_list));
	opt->name = name;
	opt->value = value;
	return opt;
}

/* Create and initialize a new event. */
static struct event *new_event(enum event_t type)
{
	struct event *e = calloc(1, sizeof(struct event));
	e->type = type;
	e->time_usecs_end = NO_TIME_RANGE;
	e->offset_usecs = NO_TIME_RANGE;
	return e;
}

static int parse_hex_byte(const char *hex, u8 *byte)
{
	if (!isxdigit((int)hex[0]) || !isxdigit((int)hex[1])) {
		return STATUS_ERR;	/* need two hex digits per byte */
	}
	char buf[] = { hex[0], hex[1], '\0' };
	char* buf_end = NULL;
	u32 byte_value = strtoul(buf, &buf_end, 16);
	assert(byte_value <= 0xff);
	assert(buf_end == buf + 2);
	*byte = byte_value;
	return STATUS_OK;
}

/* Converts a hex string in 'hex' into bytes and stores them in a
 * buffer 'buf' of length 'buf_len' bytes; returns number of bytes in
 * out_len. Works for hex strings of arbitrary size, such as very long
 * TCP Fast Open cookies.
 */
static int parse_hex_string(const char *hex, u8 *buf, int buf_len,
			    int *out_len)
{
	u8 *out = buf;
	u8 *buf_end = buf + buf_len;
	while (hex[0] != '\0') {
		if (out >= buf_end) {
			return STATUS_ERR;	/* ran out of output space */
		}
		if (parse_hex_byte(hex, out))
			return STATUS_ERR;	/* bad character */
		hex += 2;
		out += 1;
	}
	*out_len = out - buf;
	assert(*out_len <= buf_len);
	return STATUS_OK;
}

static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
						    char **error)
{
	int cookie_string_len = strlen(cookie_string);
	if (cookie_string_len & 1) {
		asprintf(error,
			 "TCP fast open cookie has an odd number of digits");
		return NULL;
	}
	int cookie_bytes = cookie_string_len / 2;  /* 2 hex chars per byte */
	if (cookie_bytes > MAX_TCP_FAST_OPEN_COOKIE_BYTES) {
		asprintf(error, "TCP fast open cookie too long");
		asprintf(error, "TCP fast open cookie of %d bytes "
			 "exceeds maximum cookie length of %d bytes",
			 cookie_bytes, MAX_TCP_FAST_OPEN_COOKIE_BYTES);
		return NULL;
	}
	u8 option_bytes = TCPOLEN_EXP_FASTOPEN_BASE + cookie_bytes;
	struct tcp_option *option;
	option = tcp_option_new(TCPOPT_EXP, option_bytes);
	option->data.fast_open.magic = htons(TCPOPT_FASTOPEN_MAGIC);
	int parsed_bytes = 0;
	/* Parse cookie. This should be an ASCII hex string
	 * representing an even number of bytes (4-16 bytes). But we
	 * do not enforce this, since we want to allow test cases that
	 * supply invalid cookies.
	 */
	if (parse_hex_string(cookie_string, option->data.fast_open.cookie,
			     sizeof(option->data.fast_open.cookie),
			     &parsed_bytes)) {
		free(option);
		asprintf(error,
			 "TCP fast open cookie is not a valid hex string");
		return NULL;
	}
	assert(parsed_bytes == cookie_bytes);
	return option;
}

%}

%locations
%expect 1  /* we expect a shift/reduce conflict for the | binary expression */
/* The %union section specifies the set of possible types for values
 * for all nonterminal and terminal symbols in the grammar.
 */
%union {
	s64 integer;
	double floating;
	char *string;
	char *reserved;
	s64 time_usecs;
	enum direction_t direction;
	enum ip_ecn_t ip_ecn;
	struct mpls_stack *mpls_stack;
	struct mpls mpls_stack_entry;
	u16 port;
	s32 window;
	u32 sequence_number;
	struct {
		u32 start_sequence;
		u16 payload_bytes;
	} tcp_sequence_info;
	struct {
		int protocol;
		u16 payload_bytes;
		u32 verification_tag;	/* used for SCTP */
		u32 start_sequence;	/* used for TCP */
		u16 checksum_coverage;	/* used for UDPLite */
	} transport_info;
	struct option_list *option;
	struct event *event;
	struct packet *packet;
	struct sctp_chunk_list_item *chunk_list_item;
	struct sctp_chunk_list *chunk_list;
	struct sctp_byte_list_item *byte_list_item;
	struct sctp_byte_list *byte_list;
	struct sctp_sack_block_list_item *sack_block_list_item;
	struct sctp_sack_block_list *sack_block_list;
	struct sctp_address_type_list_item *address_type_list_item;
	struct sctp_address_type_list *address_type_list;
	struct sctp_parameter_type_list_item *parameter_type_list_item;
	struct sctp_parameter_type_list *parameter_type_list;
	struct sctp_parameter_list_item *parameter_list_item;
	struct sctp_parameter_list *parameter_list;
	struct sctp_cause_list_item *cause_list_item;
	struct sctp_cause_list *cause_list;
	struct syscall_spec *syscall;
	struct command_spec *command;
	struct code_spec *code;
	struct tcp_option *tcp_option;
	struct tcp_options *tcp_options;
	struct expression *expression;
	struct expression_list *expression_list;
	struct errno_spec *errno_info;
}

/* The specific type of the output for a symbol is given by the %type
 * directive. By convention terminal symbols returned from the lexer
 * have ALL_CAPS names, and nonterminal symbols have lower_case names.
 */
%token ELLIPSIS
%token <reserved> SA_FAMILY SIN_PORT SIN_ADDR _HTONS_ INET_ADDR
%token <reserved> MSG_NAME MSG_IOV MSG_FLAGS
%token <reserved> FD EVENTS REVENTS ONOFF LINGER
%token <reserved> ACK ECR EOL MSS NOP SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO
%token <reserved> FAST_OPEN
%token <reserved> ECT0 ECT1 CE ECT01 NO_ECN
%token <reserved> IPV4 IPV6 ICMP SCTP UDP UDPLITE GRE MTU
%token <reserved> MPLS LABEL TC TTL
%token <reserved> OPTION
%token <reserved> SRTO_INITIAL SRTO_MAX SRTO_MIN
%token <reserved> SINIT_NUM_OSTREAMS SINIT_MAX_INSTREAMS SINIT_MAX_ATTEMPTS
%token <reserved> SINIT_MAX_INIT_TIMEO
%token <reserved> ASSOC_VALUE
%token <reserved> SACK_DELAY SACK_FREQ
%token <reserved> SSTAT_STATE SSTAT_RWND SSTAT_UNACKDATA SSTAT_PENDDATA
%token <reserved> SSTAT_INSTRMS SSTAT_OUTSTRMS SSTAT_FRAGMENTATION_POINT
%token <reserved> SSTAT_PRIMARY
%token <reserved> SPINFO_STATE SPINFO_CWND SPINFO_SRTT SPINFO_RTO SPINFO_MTU
%token <reserved> CHUNK DATA INIT INIT_ACK HEARTBEAT HEARTBEAT_ACK ABORT
%token <reserved> SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECNE CWR
%token <reserved> SHUTDOWN_COMPLETE PAD
%token <reserved> TYPE FLAGS LEN
%token <reserved> TAG A_RWND OS IS TSN SID SSN PPID CUM_TSN GAPS DUPS
%token <reserved> PARAMETER HEARTBEAT_INFORMATION IPV4_ADDRESS IPV6_ADDRESS
%token <reserved> STATE_COOKIE UNRECOGNIZED_PARAMETER COOKIE_PRESERVATIVE
%token <reserved> HOSTNAME_ADDRESS SUPPORTED_ADDRESS_TYPES ECN_CAPABLE
%token <reserved> ADDR INCR TYPES PARAMS
%token <reserved> IPV4_TYPE IPV6_TYPE HOSTNAME_TYPE
%token <reserved> CAUSE
%token <reserved> CAUSE_CODE CAUSE_INFO
%token <reserved> INVALID_STREAM_IDENTIFIER MISSING_MANDATORY_PARAMETER
%token <reserved> STALE_COOKIE_ERROR OUT_OF_RESOURCE
%token <reserved> UNRESOLVABLE_ADDRESS UNRECOGNIZED_CHUNK_TYPE
%token <reserved> INVALID_MANDATORY_PARAMETER NO_USER_DATA
%token <reserved> COOKIE_RECEIVED_WHILE_SHUTDOWN RESTART_WITH_NEW_ADDRESSES
%token <reserved> USER_INITIATED_ABORT PROTOCOL_VIOLATION
%token <reserved> STALENESS CHK PARAM UNRECOGNIZED_PARAMETERS
%token <reserved> SPP_ADDRESS SPP_HBINTERVAL SPP_PATHMAXRXT SPP_PATHMTU 
%token <floating> FLOAT
%token <integer> INTEGER HEX_INTEGER
%token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
%type <direction> direction
%type <ip_ecn> opt_ip_info
%type <ip_ecn> ip_ecn
%type <option> option options opt_options
%type <event> event events event_time action
%type <time_usecs> time opt_end_time
%type <packet> packet_spec
%type <packet> sctp_packet_spec tcp_packet_spec
%type <packet> udp_packet_spec udplite_packet_spec
%type <packet> icmp_packet_spec
%type <packet> packet_prefix
%type <syscall> syscall_spec
%type <command> command_spec
%type <code> code_spec
%type <mpls_stack> mpls_stack
%type <mpls_stack_entry> mpls_stack_entry
%type <integer> opt_mpls_stack_bottom
%type <integer> opt_icmp_mtu
%type <string> icmp_type opt_icmp_code flags
%type <string> opt_tcp_fast_open_cookie tcp_fast_open_cookie
%type <string> opt_note note word_list
%type <string> option_flag option_value script
%type <window> opt_window
%type <sequence_number> opt_ack
%type <tcp_sequence_info> seq
%type <transport_info> opt_icmp_echoed
%type <tcp_options> opt_tcp_options tcp_option_list
%type <tcp_option> tcp_option sack_block_list sack_block
%type <string> function_name
%type <expression_list> expression_list function_arguments
%type <expression> expression binary_expression array
%type <expression> decimal_integer hex_integer
%type <expression> inaddr sockaddr msghdr iovec pollfd opt_revents
%type <expression> linger l_onoff l_linger
%type <expression> sctp_status sstat_state sstat_rwnd sstat_unackdata sstat_penddata
%type <expression> sstat_instrms sstat_outstrms sstat_fragmentation_point sstat_primary
%type <expression> sctp_initmsg sctp_assocval sctp_sackinfo
%type <expression> sctp_rtoinfo srto_initial srto_max srto_min sctp_paddrinfo
%type <expression> sctp_paddrparams spp_address spp_hbinterval spp_pathmtu spp_pathmaxrxt
%type <expression> spinfo_state spinfo_cwnd spinfo_srtt spinfo_rto spinfo_mtu
%type <errno_info> opt_errno 
%type <chunk_list> sctp_chunk_list_spec
%type <chunk_list_item> sctp_chunk_spec
%type <chunk_list_item> sctp_generic_chunk_spec
%type <chunk_list_item> sctp_data_chunk_spec
%type <chunk_list_item> sctp_init_chunk_spec sctp_init_ack_chunk_spec
%type <chunk_list_item> sctp_sack_chunk_spec
%type <chunk_list_item> sctp_heartbeat_chunk_spec sctp_heartbeat_ack_chunk_spec
%type <chunk_list_item> sctp_abort_chunk_spec
%type <chunk_list_item> sctp_shutdown_chunk_spec
%type <chunk_list_item> sctp_shutdown_ack_chunk_spec
%type <chunk_list_item> sctp_error_chunk_spec
%type <chunk_list_item> sctp_cookie_echo_chunk_spec sctp_cookie_ack_chunk_spec
%type <chunk_list_item> sctp_ecne_chunk_spec sctp_cwr_chunk_spec
%type <chunk_list_item> sctp_shutdown_complete_chunk_spec
%type <chunk_list_item> sctp_pad_chunk_spec
%type <parameter_list> opt_parameter_list_spec sctp_parameter_list_spec
%type <parameter_list_item> sctp_parameter_spec
%type <parameter_list_item> sctp_generic_parameter_spec
%type <parameter_list_item> sctp_heartbeat_information_parameter_spec
%type <parameter_list_item> sctp_ipv4_address_parameter_spec
%type <parameter_list_item> sctp_ipv6_address_parameter_spec
%type <parameter_list_item> sctp_state_cookie_parameter_spec
%type <parameter_list_item> sctp_unrecognized_parameter_parameter_spec
%type <parameter_list_item> sctp_cookie_preservative_parameter_spec
%type <parameter_list_item> sctp_hostname_address_parameter_spec
%type <parameter_list_item> sctp_supported_address_types_parameter_spec
%type <parameter_list_item> sctp_ecn_capable_parameter_spec
%type <parameter_list_item> sctp_pad_parameter_spec
%type <cause_list> opt_cause_list_spec sctp_cause_list_spec
%type <cause_list_item> sctp_cause_spec
%type <cause_list_item> sctp_generic_cause_spec
%type <cause_list_item> sctp_invalid_stream_identifier_cause_spec
%type <cause_list_item> sctp_missing_mandatory_parameter_cause_spec
%type <cause_list_item> sctp_stale_cookie_error_cause_spec
%type <cause_list_item> sctp_out_of_resources_cause_spec
%type <cause_list_item> sctp_unresolvable_address_cause_spec
%type <cause_list_item> sctp_unrecognized_chunk_type_cause_spec
%type <cause_list_item> sctp_invalid_mandatory_parameter_cause_spec
%type <cause_list_item> sctp_unrecognized_parameters_cause_spec
%type <cause_list_item> sctp_no_user_data_cause_spec
%type <cause_list_item> sctp_cookie_received_while_shutdown_cause_spec
%type <cause_list_item> sctp_restart_with_new_addresses_cause_spec
%type <cause_list_item> sctp_user_initiated_abort_cause_spec
%type <cause_list_item> sctp_protocol_violation_cause_spec
%type <integer> opt_chunk_type opt_parameter_type opt_cause_code
%type <integer> opt_flags opt_data_flags opt_abort_flags
%type <integer> opt_shutdown_complete_flags opt_len
%type <integer> opt_tag opt_a_rwnd opt_os opt_is opt_tsn opt_sid opt_ssn
%type <integer> opt_cum_tsn opt_ppid
%type <byte_list> opt_val opt_info byte_list
%type <byte_list_item> byte
%type <sack_block_list> opt_gaps gap_list opt_dups dup_list
%type <sack_block_list_item> gap dup
%type <address_type_list> address_types_list
%type <address_type_list_item> address_type
%type <parameter_type_list> parameter_types_list
%type <parameter_type_list_item> parameter_type

%%  /* The grammar follows. */

script
: opt_options opt_init_command events {
	$$ = NULL;		/* The parser output is in out_script */
}
;

opt_options
:		{
	$$ = NULL;
	parse_and_finalize_config(invocation);
}
| options	{
	$$ = $1;
	parse_and_finalize_config(invocation);
}
;

options
: option		{
	out_script->option_list = $1;
	$$ = $1;		/* return the tail so we can append to it */
}
| options option	{
	$1->next = $2;
	$$ = $2;		/* return the tail so we can append to it */
}
;

option
: option_flag '=' option_value {
	$$ = new_option($1, $3);
}

option_flag
: OPTION	{ $$ = $1; }
;

option_value
: INTEGER	{ $$ = strdup(yytext); }
| WORD		{ $$ = $1; }
| STRING	{ $$ = $1; }
| IPV4_ADDR	{ $$ = $1; }
| IPV6_ADDR	{ $$ = $1; }
| IPV4		{ $$ = strdup("ipv4"); }
| IPV6		{ $$ = strdup("ipv6"); }
;

opt_init_command
:               { }
| init_command  { }
;

init_command
: command_spec  { out_script->init_command = $1; }
;

events
: event        {
	out_script->event_list = $1;  /* save pointer to event list as output
				       * of parser */
	$$ = $1;          /* return the tail so that we can append to it */
}
| events event {
	$1->next = $2;    /* link new event to the end of the existing list */
	$$ = $2;          /* return the tail so that we can append to it */
}
;

event
: event_time action  {
	$$ = $2;
	$$->line_number = $1->line_number;   /* use timestamp's line */
	$$->time_usecs  = $1->time_usecs;
	$$->time_usecs_end  = $1->time_usecs_end;
	$$->time_type = $1->time_type;

	if ($$->time_usecs_end != NO_TIME_RANGE) {
		if ($$->time_usecs_end < $$->time_usecs)
			semantic_error("time range is backwards");
	}
	if ($$->time_type == ANY_TIME &&  ($$->type != PACKET_EVENT ||
	    packet_direction($$->event.packet) != DIRECTION_OUTBOUND)) {
		yylineno = $$->line_number;
		semantic_error("event time <star> can only be used with "
			       "outbound packets");
	} else if (($$->time_type == ABSOLUTE_RANGE_TIME ||
		    $$->time_type == RELATIVE_RANGE_TIME) &&
	           ($$->type != PACKET_EVENT ||
		    packet_direction($$->event.packet) != DIRECTION_OUTBOUND)) {
		yylineno = $$->line_number;
		semantic_error("event time range can only be used with "
			       "outbound packets");
	}
	free($1);
}
;

event_time
: '+' time	{
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @2.first_line;
	$$->time_usecs = $2;
	$$->time_type = RELATIVE_TIME;
}
| time         {
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @1.first_line;
	$$->time_usecs = $1;
	$$->time_type = ABSOLUTE_TIME;
}
| '*'		{
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @1.first_line;
	$$->time_type = ANY_TIME;
}
| time '~' time	{
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @1.first_line;
	$$->time_type = ABSOLUTE_RANGE_TIME;
	$$->time_usecs = $1;
	$$->time_usecs_end = $3;
}
| '+' time '~' '+' time {
	$$ = new_event(INVALID_EVENT);
	$$->line_number = @1.first_line;
	$$->time_type = RELATIVE_RANGE_TIME;
	$$->time_usecs = $2;
	$$->time_usecs_end = $5;
}
;

time
: FLOAT        {
	if ($1 < 0) {
		semantic_error("negative time");
	}
	$$ = (s64)($1 * 1.0e6); /* convert float secs to s64 microseconds */
}
| INTEGER	{
	if ($1 < 0) {
		semantic_error("negative time");
	}
	$$ = (s64)($1 * 1000000); /* convert int secs to s64 microseconds */
}
;

action
: packet_spec  { $$ = new_event(PACKET_EVENT);  $$->event.packet  = $1; }
| syscall_spec { $$ = new_event(SYSCALL_EVENT); $$->event.syscall = $1; }
| command_spec { $$ = new_event(COMMAND_EVENT); $$->event.command = $1; }
| code_spec    { $$ = new_event(CODE_EVENT);    $$->event.code    = $1; }
;

packet_spec
: sctp_packet_spec    { $$ = $1; }
| tcp_packet_spec     { $$ = $1; }
| udp_packet_spec     { $$ = $1; }
| udplite_packet_spec { $$ = $1; }
| icmp_packet_spec    { $$ = $1; }
;

sctp_packet_spec
: packet_prefix opt_ip_info SCTP ':' sctp_chunk_list_spec {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	inner = new_sctp_packet(in_config->wire_protocol, direction, $2, $5,
				&error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;

sctp_chunk_list_spec
:                                          { $$ = sctp_chunk_list_new(); }
| sctp_chunk_spec                          { $$ = sctp_chunk_list_new();
                                             sctp_chunk_list_append($$, $1); }
| sctp_chunk_list_spec ';' sctp_chunk_spec { $$ = $1;
                                             sctp_chunk_list_append($1, $3); }
;

sctp_chunk_spec
: sctp_generic_chunk_spec           { $$ = $1; }
| sctp_data_chunk_spec              { $$ = $1; }
| sctp_init_chunk_spec              { $$ = $1; }
| sctp_init_ack_chunk_spec          { $$ = $1; }
| sctp_sack_chunk_spec              { $$ = $1; }
| sctp_heartbeat_chunk_spec         { $$ = $1; }
| sctp_heartbeat_ack_chunk_spec     { $$ = $1; }
| sctp_abort_chunk_spec             { $$ = $1; }
| sctp_shutdown_chunk_spec          { $$ = $1; }
| sctp_shutdown_ack_chunk_spec      { $$ = $1; }
| sctp_error_chunk_spec             { $$ = $1; }
| sctp_cookie_echo_chunk_spec       { $$ = $1; }
| sctp_cookie_ack_chunk_spec        { $$ = $1; }
| sctp_ecne_chunk_spec              { $$ = $1; }
| sctp_cwr_chunk_spec               { $$ = $1; }
| sctp_shutdown_complete_chunk_spec { $$ = $1; }
| sctp_pad_chunk_spec               { $$ = $1; }
;

opt_chunk_type
: TYPE '=' ELLIPSIS    { $$ = -1; }
| TYPE '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("type value out of range");
        }
	$$ = $3;
}
| TYPE '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("type value out of range");
        }
	$$ = $3;
}
;

opt_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
        }
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
        }
	$$ = $3;
}
;

opt_len
: LEN '=' ELLIPSIS { $$ = -1; }
| LEN '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("length value out of range");
	}
	$$ = $3;
}
;

opt_val
: VAL '=' ELLIPSIS          { $$ = NULL; }
| VAL '=' '[' ELLIPSIS ']'  { $$ = NULL; }
| VAL '=' '[' byte_list ']' { $$ = $4; }
;

opt_info
: CAUSE_INFO '=' ELLIPSIS          { $$ = NULL; }
| CAUSE_INFO '=' '[' ELLIPSIS ']'  { $$ = NULL; }
| CAUSE_INFO '=' '[' byte_list ']' { $$ = $4; }
;

byte_list
:                    { $$ = sctp_byte_list_new(); }
| byte               { $$ = sctp_byte_list_new();
                       sctp_byte_list_append($$, $1); }
| byte_list ',' byte { $$ = $1;
                       sctp_byte_list_append($1, $3); }
;

byte
: HEX_INTEGER {
	if (!is_valid_u8($1)) {
		semantic_error("byte value out of range");
	}
	$$ = sctp_byte_list_item_new($1);
}
| INTEGER {
	if (!is_valid_u8($1)) {
		semantic_error("byte value out of range");
	}
	$$ = sctp_byte_list_item_new($1);
}
;

opt_data_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
        }
	$$ = $3;
}
| FLAGS '=' WORD        {
	u64 flags;
	char *c;

	flags = 0;
	for (c = $3; *c != '\0'; c++) {
		switch (*c) {
		case 'I':
			if (flags & SCTP_DATA_CHUNK_I_BIT) {
				semantic_error("I-bit specified multiple times");
			} else {
				flags |= SCTP_DATA_CHUNK_I_BIT;
			}
			break;
		case 'U':
			if (flags & SCTP_DATA_CHUNK_U_BIT) {
				semantic_error("U-bit specified multiple times");
			} else {
				flags |= SCTP_DATA_CHUNK_U_BIT;
			}
			break;
		case 'B':
			if (flags & SCTP_DATA_CHUNK_B_BIT) {
				semantic_error("B-bit specified multiple times");
			} else {
				flags |= SCTP_DATA_CHUNK_B_BIT;
			}
			break;
		case 'E':
			if (flags & SCTP_DATA_CHUNK_E_BIT) {
				semantic_error("E-bit specified multiple times");
			} else {
				flags |= SCTP_DATA_CHUNK_E_BIT;
			}
			break;
		default:
			semantic_error("Only expecting IUBE as flags");
			break;
		}
	}
	$$ = flags;
}
;

opt_abort_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
        }
	$$ = $3;
}
| FLAGS '=' WORD        {
	u64 flags;
	char *c;

	flags = 0;
	for (c = $3; *c != '\0'; c++) {
		switch (*c) {
		case 'T':
			if (flags & SCTP_ABORT_CHUNK_T_BIT) {
				semantic_error("T-bit specified multiple times");
			} else {
				flags |= SCTP_ABORT_CHUNK_T_BIT;
			}
			break;
		default:
			semantic_error("Only expecting T as flags");
			break;
		}
	}
	$$ = flags;
}
;

opt_shutdown_complete_flags
: FLAGS '=' ELLIPSIS    { $$ = -1; }
| FLAGS '=' HEX_INTEGER {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' INTEGER     {
	if (!is_valid_u8($3)) {
		semantic_error("flags value out of range");
	}
	$$ = $3;
}
| FLAGS '=' WORD        {
	u64 flags;
	char *c;

	flags = 0;
	for (c = $3; *c != '\0'; c++) {
		switch (*c) {
		case 'T':
			if (flags & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) {
				semantic_error("T-bit specified multiple times");
			} else {
				flags |= SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT;
			}
			break;
		default:
			semantic_error("Only expecting T as flags");
			break;
		}
	}
	$$ = flags;
}
;

opt_tag
: TAG '=' ELLIPSIS { $$ = -1; }
| TAG '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("tag value out of range");
	}
	$$ = $3;
}
;

opt_a_rwnd
: A_RWND '=' ELLIPSIS   { $$ = -1; }
| A_RWND '=' INTEGER    {
	if (!is_valid_u32($3)) {
		semantic_error("a_rwnd value out of range");
	}
	$$ = $3;
}
;

opt_os
: OS '=' ELLIPSIS { $$ = -1; }
| OS '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("os value out of range");
	}
	$$ = $3;
}
;

opt_is
: IS '=' ELLIPSIS { $$ = -1; }
| IS '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("is value out of range");
	}
	$$ = $3;
}
;

opt_tsn
: TSN '=' ELLIPSIS { $$ = -1; }
| TSN '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("tsn value out of range");
	}
	$$ = $3;
}
;

opt_sid
: SID '=' ELLIPSIS { $$ = -1; }
| SID '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("sid value out of range");
	}
	$$ = $3;
}
;

opt_ssn
: SSN '=' ELLIPSIS { $$ = -1; }
| SSN '=' INTEGER  {
	if (!is_valid_u16($3)) {
		semantic_error("ssn value out of range");
	}
	$$ = $3;
}
;

opt_ppid
: PPID '=' ELLIPSIS { $$ = -1; }
| PPID '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("ppid value out of range");
	}
	$$ = $3;
}
;

opt_cum_tsn
: CUM_TSN '=' ELLIPSIS { $$ = -1; }
| CUM_TSN '=' INTEGER  {
	if (!is_valid_u32($3)) {
		semantic_error("cum_tsn value out of range");
	}
	$$ = $3;
}
;

opt_gaps
: GAPS '=' ELLIPSIS         { $$ = NULL; }
| GAPS '=' '[' ELLIPSIS ']' { $$ = NULL; }
| GAPS '=' '[' gap_list ']' { $$ = $4; }
;

gap_list
:                  { $$ = sctp_sack_block_list_new(); }
| gap              { $$ = sctp_sack_block_list_new();
                     sctp_sack_block_list_append($$, $1); }
| gap_list ',' gap { $$ = $1;
                     sctp_sack_block_list_append($1, $3); }
;

gap
: INTEGER ':' INTEGER {
	if (!is_valid_u16($1)) {
		semantic_error("start value out of range");
	}
	if (!is_valid_u16($3)) {
		semantic_error("end value out of range");
	}
	$$ = sctp_sack_block_list_item_gap_new($1, $3);
}
;

opt_dups
: DUPS '=' ELLIPSIS         { $$ = NULL; }
| DUPS '=' '[' ELLIPSIS ']' { $$ = NULL; }
| DUPS '=' '[' dup_list ']' { $$ = $4; }
;

dup_list
:                  { $$ = sctp_sack_block_list_new(); }
| dup              { $$ = sctp_sack_block_list_new();
                     sctp_sack_block_list_append($$, $1); }
| dup_list ',' dup { $$ = $1;
                     sctp_sack_block_list_append($1, $3); }
;

dup
: INTEGER {
	if (!is_valid_u32($1)) {
		semantic_error("tsn value out of range");
	}
	$$ = sctp_sack_block_list_item_dup_new($1);
}
;

sctp_generic_chunk_spec
: CHUNK '[' opt_chunk_type ',' opt_flags ',' opt_len ',' opt_val ']' {
	if (($7 != -1) &&
	    (!is_valid_u16($7) || ($7 < sizeof(struct sctp_chunk)))) {
		semantic_error("length value out of range");
	}
	if (($7 != -1) && ($9 != NULL) &&
	    ($7 != sizeof(struct sctp_chunk) + $9->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($7 == -1) && ($9 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_generic_chunk_new($3, $5, $7, $9);
}

sctp_data_chunk_spec
: DATA '[' opt_data_flags ',' opt_len ',' opt_tsn ',' opt_sid ',' opt_ssn ',' opt_ppid ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_data_chunk)))) {
		semantic_error("length value out of range");
	}
	$$ = sctp_data_chunk_new($3, $5, $7, $9, $11, $13);
}

sctp_init_chunk_spec
: INIT '[' opt_flags ',' opt_tag ',' opt_a_rwnd ',' opt_os ',' opt_is ',' opt_tsn opt_parameter_list_spec ']' {
	$$ = sctp_init_chunk_new($3, $5, $7, $9, $11, $13, $14);
}

sctp_init_ack_chunk_spec
: INIT_ACK '[' opt_flags ',' opt_tag ',' opt_a_rwnd ',' opt_os ',' opt_is ',' opt_tsn opt_parameter_list_spec ']' {
	$$ = sctp_init_ack_chunk_new($3, $5, $7, $9, $11, $13, $14);
}

sctp_sack_chunk_spec
: SACK '[' opt_flags ',' opt_cum_tsn ',' opt_a_rwnd ',' opt_gaps ',' opt_dups']' {
	$$ = sctp_sack_chunk_new($3, $5, $7, $9, $11);
}

sctp_heartbeat_chunk_spec
: HEARTBEAT '[' opt_flags ',' sctp_heartbeat_information_parameter_spec ']' {
	$$ = sctp_heartbeat_chunk_new($3, $5);
}

sctp_heartbeat_ack_chunk_spec
: HEARTBEAT_ACK '[' opt_flags ',' sctp_heartbeat_information_parameter_spec ']' {
	$$ = sctp_heartbeat_ack_chunk_new($3, $5);
}

sctp_abort_chunk_spec
: ABORT '[' opt_abort_flags opt_cause_list_spec ']' {
	$$ = sctp_abort_chunk_new($3, $4);
}

sctp_shutdown_chunk_spec
: SHUTDOWN '[' opt_flags ',' opt_cum_tsn ']' {
	$$ = sctp_shutdown_chunk_new($3, $5);
}

sctp_shutdown_ack_chunk_spec
: SHUTDOWN_ACK '[' opt_flags ']' {
	$$ = sctp_shutdown_ack_chunk_new($3);
}

sctp_error_chunk_spec
: ERROR '[' opt_flags opt_cause_list_spec ']' {
	$$ = sctp_error_chunk_new($3, $4);
}

sctp_cookie_echo_chunk_spec
: COOKIE_ECHO '[' opt_flags ',' opt_len ',' opt_val ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_cookie_echo_chunk)))) {
		semantic_error("length value out of range");
	}
        if (($5 != -1) && ($7 != NULL) &&
	    ($5 != sizeof(struct sctp_cookie_echo_chunk) + $7->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($5 == -1) && ($7 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_cookie_echo_chunk_new($3, $5, $7);
}

sctp_cookie_ack_chunk_spec
: COOKIE_ACK '[' opt_flags ']' {
	$$ = sctp_cookie_ack_chunk_new($3);
}

sctp_ecne_chunk_spec
: ECNE '[' opt_flags ',' opt_tsn ']' {
	$$ = sctp_ecne_chunk_new($3, $5);
}

sctp_cwr_chunk_spec
: CWR '[' opt_flags ',' opt_tsn ']' {
	$$ = sctp_cwr_chunk_new($3, $5);
}

sctp_shutdown_complete_chunk_spec
: SHUTDOWN_COMPLETE '[' opt_shutdown_complete_flags ']' {
	$$ = sctp_shutdown_complete_chunk_new($3);
}

sctp_pad_chunk_spec
: PAD '[' opt_flags ',' opt_len ',' VAL '=' ELLIPSIS ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_pad_chunk)))) {
		semantic_error("length value out of range");
	}
	$$ = sctp_pad_chunk_new($3, $5, NULL);
}

opt_parameter_list_spec
: ',' ELLIPSIS                 { $$ = NULL; }
|                              { $$ = sctp_parameter_list_new(); }
| ',' sctp_parameter_list_spec { $$ = $2; }
;

sctp_parameter_list_spec
: sctp_parameter_spec                              { $$ = sctp_parameter_list_new();
                                                     sctp_parameter_list_append($$, $1); }
| sctp_parameter_list_spec ',' sctp_parameter_spec { $$ = $1;
                                                     sctp_parameter_list_append($1, $3); }
;

sctp_parameter_spec
: sctp_generic_parameter_spec                 { $$ = $1; }
| sctp_heartbeat_information_parameter_spec   { $$ = $1; }
| sctp_ipv4_address_parameter_spec            { $$ = $1; }
| sctp_ipv6_address_parameter_spec            { $$ = $1; }
| sctp_state_cookie_parameter_spec            { $$ = $1; }
| sctp_unrecognized_parameter_parameter_spec  { $$ = $1; }
| sctp_cookie_preservative_parameter_spec     { $$ = $1; }
| sctp_hostname_address_parameter_spec        { $$ = $1; }
| sctp_supported_address_types_parameter_spec { $$ = $1; }
| sctp_ecn_capable_parameter_spec             { $$ = $1; }
| sctp_pad_parameter_spec                     { $$ = $1; }
;

opt_parameter_type
: TYPE '=' ELLIPSIS    { $$ = -1; }
| TYPE '=' HEX_INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("type value out of range");
	}
	$$ = $3;
}
| TYPE '=' INTEGER     {
	if (!is_valid_u16($3)) {
		semantic_error("type value out of range");
	}
	$$ = $3;
}
;

sctp_generic_parameter_spec
: PARAMETER '[' opt_parameter_type ',' opt_len ',' opt_val ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_parameter)))) {
		semantic_error("length value out of range");
	}
	if (($5 != -1) && ($7 != NULL) &&
	    ($5 != sizeof(struct sctp_parameter) + $7->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($5 == -1) && ($7 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_generic_parameter_new($3, $5, $7);
}

sctp_heartbeat_information_parameter_spec
: HEARTBEAT_INFORMATION '[' ELLIPSIS ']' {
	$$ = sctp_heartbeat_information_parameter_new(-1, NULL);
}
| HEARTBEAT_INFORMATION '[' opt_len ',' opt_val ']' {
	if (($3 != -1) &&
	    (!is_valid_u16($3) || ($3 < sizeof(struct sctp_heartbeat_information_parameter)))) {
		semantic_error("length value out of range");
	}
	if (($3 != -1) && ($5 != NULL) &&
	    ($3 != sizeof(struct sctp_heartbeat_information_parameter) + $5->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($3 == -1) && ($5 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_heartbeat_information_parameter_new($3, $5);
}

sctp_ipv4_address_parameter_spec
: IPV4_ADDRESS '[' ADDR '=' IPV4_ADDR ']' {
	struct in_addr addr;

	if (inet_pton(AF_INET, $5, &addr) != 1) {
		semantic_error("Invalid address");
	}
	$$ = sctp_ipv4_address_parameter_new(&addr);
}
| IPV4_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
	$$ = sctp_ipv4_address_parameter_new(NULL);
}

sctp_ipv6_address_parameter_spec
: IPV6_ADDRESS '[' ADDR '=' IPV6_ADDR ']' {
	struct in6_addr addr;

	if (inet_pton(AF_INET6, $5, &addr) != 1) {
		semantic_error("Invalid address");
	}
	$$ = sctp_ipv6_address_parameter_new(&addr);
}
| IPV6_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
	$$ = sctp_ipv6_address_parameter_new(NULL);
}

sctp_state_cookie_parameter_spec
: STATE_COOKIE '[' ELLIPSIS ']' {
	$$ = sctp_state_cookie_parameter_new(-1, NULL);
}
| STATE_COOKIE '[' LEN '=' ELLIPSIS ',' VAL '=' ELLIPSIS ']' {
	$$ = sctp_state_cookie_parameter_new(-1, NULL);
}
| STATE_COOKIE '[' LEN '=' INTEGER ',' VAL '=' ELLIPSIS ']' {
	if (($5 < sizeof(struct sctp_state_cookie_parameter)) ||
	    !is_valid_u32($5)) {
		semantic_error("len value out of range");
	}
	$$ = sctp_state_cookie_parameter_new($5, NULL);
}

sctp_unrecognized_parameter_parameter_spec
: UNRECOGNIZED_PARAMETER '[' PARAMS '=' ELLIPSIS ']' {
	$$ = sctp_unrecognized_parameters_parameter_new(NULL);
}
| UNRECOGNIZED_PARAMETER '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' {
	$$ = sctp_unrecognized_parameters_parameter_new($6);
}

sctp_cookie_preservative_parameter_spec
: COOKIE_PRESERVATIVE '[' INCR '=' INTEGER ']' {
	if (!is_valid_u32($5)) {
		semantic_error("increment value out of range");
	}
	$$ = sctp_cookie_preservative_parameter_new($5);
}
| COOKIE_PRESERVATIVE '[' INCR '=' ELLIPSIS ']' {
	$$ = sctp_cookie_preservative_parameter_new(-1);
}

sctp_hostname_address_parameter_spec
: HOSTNAME_ADDRESS '[' ADDR '=' STRING ']' {
	$$ = sctp_hostname_address_parameter_new($5);
}
| HOSTNAME_ADDRESS '[' ADDR '=' ELLIPSIS ']' {
	$$ = sctp_hostname_address_parameter_new(NULL);
}

address_types_list
:                                     { $$ = sctp_address_type_list_new(); }
| address_type                        { $$ = sctp_address_type_list_new();
                                        sctp_address_type_list_append($$, $1); }
| address_types_list ',' address_type { $$ = $1;
                                        sctp_address_type_list_append($1, $3); }
;

address_type
: INTEGER       { if (!is_valid_u16($1)) {
                  semantic_error("address type value out of range");
                  }
                  $$ = sctp_address_type_list_item_new($1); }
| IPV4_TYPE     { $$ = sctp_address_type_list_item_new(SCTP_IPV4_ADDRESS_PARAMETER_TYPE); }
| IPV6_TYPE     { $$ = sctp_address_type_list_item_new(SCTP_IPV6_ADDRESS_PARAMETER_TYPE); }
| HOSTNAME_TYPE { $$ = sctp_address_type_list_item_new(SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE); }
;

sctp_supported_address_types_parameter_spec
: SUPPORTED_ADDRESS_TYPES '[' TYPES '=' ELLIPSIS ']' {
	$$ = sctp_supported_address_types_parameter_new(NULL);
}
| SUPPORTED_ADDRESS_TYPES '[' TYPES '=' '[' address_types_list ']' ']' {
	$$ = sctp_supported_address_types_parameter_new($6);
}


sctp_ecn_capable_parameter_spec
: ECN_CAPABLE '[' ']' {
	$$ = sctp_ecn_capable_parameter_new();
}

sctp_pad_parameter_spec
: PAD '[' ELLIPSIS ']' {
	$$ = sctp_pad_parameter_new(-1, NULL);
}
| PAD '[' LEN '=' ELLIPSIS ',' VAL '=' ELLIPSIS ']' {
	$$ = sctp_pad_parameter_new(-1, NULL);
}
| PAD '[' LEN '=' INTEGER ',' VAL '=' ELLIPSIS ']' {
	if (($5 < sizeof(struct sctp_pad_parameter)) ||
	    !is_valid_u32($5)) {
		semantic_error("len value out of range");
	}
	$$ = sctp_pad_parameter_new($5, NULL);
}

opt_cause_list_spec
: ',' ELLIPSIS             { $$ = NULL; }
|                          { $$ = sctp_cause_list_new(); }
| ',' sctp_cause_list_spec { $$ = $2; }
;

sctp_cause_list_spec
: sctp_cause_spec                          { $$ = sctp_cause_list_new();
                                             sctp_cause_list_append($$, $1); }
| sctp_cause_list_spec ',' sctp_cause_spec { $$ = $1;
                                             sctp_cause_list_append($1, $3); }
;

sctp_cause_spec
: sctp_generic_cause_spec                        { $$ = $1; }
| sctp_invalid_stream_identifier_cause_spec      { $$ = $1; }
| sctp_missing_mandatory_parameter_cause_spec    { $$ = $1; }
| sctp_stale_cookie_error_cause_spec             { $$ = $1; }
| sctp_out_of_resources_cause_spec               { $$ = $1; }
| sctp_unresolvable_address_cause_spec           { $$ = $1; }
| sctp_unrecognized_chunk_type_cause_spec        { $$ = $1; }
| sctp_invalid_mandatory_parameter_cause_spec    { $$ = $1; }
| sctp_unrecognized_parameters_cause_spec        { $$ = $1; }
| sctp_no_user_data_cause_spec                   { $$ = $1; }
| sctp_cookie_received_while_shutdown_cause_spec { $$ = $1; }
| sctp_restart_with_new_addresses_cause_spec     { $$ = $1; }
| sctp_user_initiated_abort_cause_spec           { $$ = $1; }
| sctp_protocol_violation_cause_spec             { $$ = $1; }
;

opt_cause_code
: CAUSE_CODE '=' ELLIPSIS    { $$ = -1; }
| CAUSE_CODE '=' HEX_INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("cause value out of range");
	}
	$$ = $3;
}
| CAUSE_CODE '=' INTEGER     {
	if (!is_valid_u16($3)) {
		semantic_error("cause value out of range");
	}
	$$ = $3;
}
;

sctp_generic_cause_spec
: CAUSE '[' opt_cause_code ',' opt_len ',' opt_info ']' {
	if (($5 != -1) &&
	    (!is_valid_u16($5) || ($5 < sizeof(struct sctp_cause)))) {
		semantic_error("length value out of range");
	}
	if (($5 != -1) && ($7 != NULL) &&
	    ($5 != sizeof(struct sctp_cause) + $7->nr_entries)) {
		semantic_error("length value incompatible with val");
	}
	if (($5 == -1) && ($7 != NULL)) {
		semantic_error("length needs to be specified");
	}
	$$ = sctp_generic_cause_new($3, $5, $7);
}

sctp_invalid_stream_identifier_cause_spec
: INVALID_STREAM_IDENTIFIER '[' SID '=' INTEGER ']' {
	if (!is_valid_u16($5)) {
		semantic_error("stream identifier out of range");
	}
	$$ = sctp_invalid_stream_identifier_cause_new($5);
}
| INVALID_STREAM_IDENTIFIER '[' SID '=' ELLIPSIS ']' {
	$$ = sctp_invalid_stream_identifier_cause_new(-1);
}

parameter_types_list
:                                         { $$ = sctp_parameter_type_list_new(); }
| parameter_type                          { $$ = sctp_parameter_type_list_new();
                                            sctp_parameter_type_list_append($$, $1); }
| parameter_types_list ',' parameter_type { $$ = $1;
                                            sctp_parameter_type_list_append($1, $3); }
;

parameter_type
: INTEGER       { if (!is_valid_u16($1)) {
                  semantic_error("parameter type value out of range");
                  }
                  $$ = sctp_parameter_type_list_item_new($1); }
;

sctp_missing_mandatory_parameter_cause_spec
: MISSING_MANDATORY_PARAMETER '[' TYPES '=' ELLIPSIS ']' {
	$$ = sctp_missing_mandatory_parameter_cause_new(NULL);
}
| MISSING_MANDATORY_PARAMETER '[' TYPES '=' '[' parameter_types_list ']' ']' {
	$$ = sctp_missing_mandatory_parameter_cause_new($6);
}

sctp_stale_cookie_error_cause_spec
: STALE_COOKIE_ERROR '[' STALENESS '=' INTEGER ']' {
	if (!is_valid_u32($5)) {
		semantic_error("staleness out of range");
	}
	$$ = sctp_stale_cookie_error_cause_new($5);
}
| STALE_COOKIE_ERROR '[' STALENESS '=' ELLIPSIS ']' {
	$$ = sctp_stale_cookie_error_cause_new(-1);
}

sctp_out_of_resources_cause_spec
: OUT_OF_RESOURCE '[' ']' {
	$$ = sctp_out_of_resources_cause_new();
}

sctp_unresolvable_address_cause_spec
: UNRESOLVABLE_ADDRESS '[' PARAM '=' sctp_parameter_spec ']' {
	$$ = sctp_unresolvable_address_cause_new($5);
}
| UNRESOLVABLE_ADDRESS '[' PARAM '=' ELLIPSIS ']' {
	$$ = sctp_unresolvable_address_cause_new(NULL);
}

sctp_unrecognized_chunk_type_cause_spec
: UNRECOGNIZED_CHUNK_TYPE '[' CHK '=' sctp_chunk_spec ']' {
	$$ = sctp_unrecognized_chunk_type_cause_new($5);
}
| UNRECOGNIZED_CHUNK_TYPE '[' CHK '=' ELLIPSIS ']' {
	$$ = sctp_unrecognized_chunk_type_cause_new(NULL);
}

sctp_invalid_mandatory_parameter_cause_spec
: INVALID_MANDATORY_PARAMETER '[' ']' {
	$$ = sctp_invalid_mandatory_parameter_cause_new();
}

sctp_unrecognized_parameters_cause_spec
: UNRECOGNIZED_PARAMETERS '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' {
	$$ = sctp_unrecognized_parameters_cause_new($6);
}
| UNRECOGNIZED_PARAMETERS '[' PARAMS '=' ELLIPSIS ']' {
	$$ = sctp_unrecognized_parameters_cause_new(NULL);
}

sctp_no_user_data_cause_spec
: NO_USER_DATA '[' TSN '=' INTEGER ']' {
	if (!is_valid_u32($5)) {
		semantic_error("tsn out of range");
	}
	$$ = sctp_no_user_data_cause_new($5);
}
| NO_USER_DATA '[' TSN '=' ELLIPSIS ']' {
	$$ = sctp_no_user_data_cause_new(-1);
}

sctp_cookie_received_while_shutdown_cause_spec
: COOKIE_RECEIVED_WHILE_SHUTDOWN '[' ']' {
	$$ = sctp_cookie_received_while_shutdown_cause_new();
}

sctp_restart_with_new_addresses_cause_spec
: RESTART_WITH_NEW_ADDRESSES '[' PARAMS '=' '[' sctp_parameter_list_spec ']' ']' {
	$$ = sctp_restart_with_new_addresses_cause_new($6);
}
| RESTART_WITH_NEW_ADDRESSES '[' PARAMS '=' ELLIPSIS ']' {
	$$ = sctp_restart_with_new_addresses_cause_new(NULL);
}

sctp_user_initiated_abort_cause_spec
: USER_INITIATED_ABORT '[' CAUSE_INFO '=' STRING ']' {
	$$ = sctp_user_initiated_abort_cause_new($5);
}
| USER_INITIATED_ABORT '[' CAUSE_INFO '=' ELLIPSIS ']' {
	$$ = sctp_user_initiated_abort_cause_new(NULL);
}

sctp_protocol_violation_cause_spec
: PROTOCOL_VIOLATION '[' CAUSE_INFO '=' STRING ']' {
	$$ = sctp_protocol_violation_cause_new($5);
}
| PROTOCOL_VIOLATION '[' CAUSE_INFO '=' ELLIPSIS ']' {
	$$ = sctp_protocol_violation_cause_new(NULL);
}

tcp_packet_spec
: packet_prefix opt_ip_info flags seq opt_ack opt_window opt_tcp_options {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	if (($7 == NULL) && (direction != DIRECTION_OUTBOUND)) {
		yylineno = @7.first_line;
		semantic_error("<...> for TCP options can only be used with "
			       "outbound packets");
	}

	inner = new_tcp_packet(in_config->wire_protocol,
			       direction, $2, $3,
			       $4.start_sequence, $4.payload_bytes,
			       $5, $6, $7, &error);
	free($3);
	free($7);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;

udp_packet_spec
: packet_prefix UDP '(' INTEGER ')' {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	if (!is_valid_u16($4)) {
		semantic_error("UDP payload size out of range");
	}

	inner = new_udp_packet(in_config->wire_protocol, direction, $4, &error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;

udplite_packet_spec
: packet_prefix UDPLITE '(' INTEGER ',' INTEGER ')' {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	if (!is_valid_u16($4)) {
		semantic_error("UDPLite payload size out of range");
	}
	if (!is_valid_u16($6)) {
		semantic_error("UDPLite checksum coverage out of range");
	}

	inner = new_udplite_packet(in_config->wire_protocol, direction, $4, $6,
				   &error);
	if (inner == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;

icmp_packet_spec
: packet_prefix opt_icmp_echoed ICMP icmp_type opt_icmp_code opt_icmp_mtu {
	char *error = NULL;
	struct packet *outer = $1, *inner = NULL;
	enum direction_t direction = outer->direction;

	inner = new_icmp_packet(in_config->wire_protocol, direction, $4, $5,
				$2.protocol, $2.payload_bytes,
				$2.start_sequence, $2.checksum_coverage,
				$2.verification_tag, $6, &error);
	free($4);
	free($5);
	if (inner == NULL) {
		semantic_error(error);
		free(error);
	}

	$$ = packet_encapsulate_and_free(outer, inner);
}
;


packet_prefix
: direction {
	$$ = packet_new(PACKET_MAX_HEADER_BYTES);
	$$->direction = $1;
}
| packet_prefix IPV4 IPV4_ADDR '>' IPV4_ADDR ':' {
	char *error = NULL;
	struct packet *packet = $1;
	char *ip_src = $3;
	char *ip_dst = $5;
	if (ipv4_header_append(packet, ip_src, ip_dst, &error))
		semantic_error(error);
	free(ip_src);
	free(ip_dst);
	$$ = packet;
}
| packet_prefix IPV6 IPV6_ADDR '>' IPV6_ADDR ':' {
	char *error = NULL;
	struct packet *packet = $1;
	char *ip_src = $3;
	char *ip_dst = $5;
	if (ipv6_header_append(packet, ip_src, ip_dst, &error))
		semantic_error(error);
	free(ip_src);
	free(ip_dst);
	$$ = packet;
}
| packet_prefix GRE ':' {
	char *error = NULL;
	struct packet *packet = $1;
	if (gre_header_append(packet, &error))
		semantic_error(error);
	$$ = packet;
}
| packet_prefix MPLS mpls_stack ':' {
	char *error = NULL;
	struct packet *packet = $1;
	struct mpls_stack *mpls_stack = $3;

	if (mpls_header_append(packet, mpls_stack, &error))
		semantic_error(error);
	free(mpls_stack);
	$$ = packet;
}
;

mpls_stack
:				{
	$$ = mpls_stack_new();
}
| mpls_stack mpls_stack_entry	{
	if (mpls_stack_append($1, $2))
		semantic_error("too many MPLS labels");
	$$ = $1;
}
;

mpls_stack_entry
:
'(' LABEL INTEGER ',' TC INTEGER ',' opt_mpls_stack_bottom TTL INTEGER ')' {
	char *error = NULL;
	s64 label = $3;
	s64 traffic_class = $6;
	bool is_stack_bottom = $8;
	s64 ttl = $10;
	struct mpls mpls;

	if (new_mpls_stack_entry(label, traffic_class, is_stack_bottom, ttl,
				 &mpls, &error))
		semantic_error(error);
	$$ = mpls;
}
;

opt_mpls_stack_bottom
:			{ $$ = 0; }
| '[' WORD ']' ','	{
	if (strcmp($2, "S") != 0)
		semantic_error("expected [S] for MPLS label stack bottom");
	free($2);
	$$ = 1;
}
;

icmp_type
: WORD		{ $$ = $1; }
;

opt_icmp_code
:		{ $$ = NULL; }
| WORD		{ $$ = $1; }
;

/* This specifies the relevant details about the packet echoed by ICMP. */
opt_icmp_echoed
:			{
	$$.protocol		= IPPROTO_TCP;
	$$.payload_bytes	= 0;
	$$.start_sequence	= 0;
}
| '[' SCTP '(' INTEGER ')' ']'	{
	$$.protocol		= IPPROTO_SCTP;
	$$.payload_bytes	= 0;
	$$.verification_tag	= $4;
}
| '[' UDP '(' INTEGER ')' ']'	{
	$$.protocol		= IPPROTO_UDP;
	$$.payload_bytes	= $4;
	$$.start_sequence	= 0;
}
| '[' UDPLITE '(' INTEGER ',' INTEGER ')' ']'	{
	$$.protocol		= IPPROTO_UDPLITE;
	$$.payload_bytes	= $4;
	$$.checksum_coverage	= $6;
}
| '[' seq ']'		{
	$$.protocol		= IPPROTO_TCP;
	$$.payload_bytes	= $2.payload_bytes;
	$$.start_sequence	= $2.start_sequence;
}
;

opt_icmp_mtu
:		{ $$ = -1; }
| MTU INTEGER	{ $$ = $2; }
;

direction
: '<'          { $$ = DIRECTION_INBOUND;  current_script_line = yylineno; }
| '>'          { $$ = DIRECTION_OUTBOUND; current_script_line = yylineno; }
;

opt_ip_info
:			{ $$ = ECN_NOCHECK; }
| '[' ip_ecn ']'	{ $$ = $2; }
;

ip_ecn
: NO_ECN		{ $$ = ECN_NONE; }
| ECT0		{ $$ = ECN_ECT0; }
| ECT1		{ $$ = ECN_ECT1; }
| ECT01		{ $$ = ECN_ECT01; }
| CE		{ $$ = ECN_CE; }
;

flags
: WORD         { $$ = $1; }
| '.'          { $$ = strdup("."); }
| WORD '.'     { asprintf(&($$), "%s.", $1); free($1); }
| '-'          { $$ = strdup(""); }  /* no TCP flags set in segment */
;

seq
: INTEGER ':' INTEGER '(' INTEGER ')' {
	if (!is_valid_u32($1)) {
		semantic_error("TCP start sequence number out of range");
	}
	if (!is_valid_u32($3)) {
		semantic_error("TCP end sequence number out of range");
	}
	if (!is_valid_u16($5)) {
		semantic_error("TCP payload size out of range");
	}
	if ($3 != ($1 +$5)) {
		semantic_error("inconsistent TCP sequence numbers and "
			       "payload size");
	}
	$$.start_sequence = $1;
	$$.payload_bytes = $5;
}
;

opt_ack
:              { $$ = 0; }
| ACK INTEGER  {
	if (!is_valid_u32($2)) {
		semantic_error("TCP ack sequence number out of range");
	}
	$$ = $2;
}
;

opt_window
:		{ $$ = -1; }
| WIN INTEGER	{
	if (!is_valid_u16($2)) {
		semantic_error("TCP window value out of range");
	}
	$$ = $2;
}
;

opt_tcp_options
:                             { $$ = tcp_options_new(); }
| '<' tcp_option_list '>'     { $$ = $2; }
| '<' ELLIPSIS '>'            { $$ = NULL; /* FLAG_OPTIONS_NOCHECK */ }
;

tcp_option_list
: tcp_option                       {
	$$ = tcp_options_new();
	if (tcp_options_append($$, $1)) {
		semantic_error("TCP option list too long");
	}
}
| tcp_option_list ',' tcp_option   {
	$$ = $1;
	if (tcp_options_append($$, $3)) {
		semantic_error("TCP option list too long");
	}
}
;

opt_tcp_fast_open_cookie
:			{ $$ = strdup(""); }
| tcp_fast_open_cookie	{ $$ = $1; }
;

tcp_fast_open_cookie
: WORD    { $$ = strdup(yytext); }
| INTEGER { $$ = strdup(yytext); }
;

tcp_option
: NOP              { $$ = tcp_option_new(TCPOPT_NOP, 1); }
| EOL              { $$ = tcp_option_new(TCPOPT_EOL, 1); }
| MSS INTEGER      {
	$$ = tcp_option_new(TCPOPT_MAXSEG, TCPOLEN_MAXSEG);
	if (!is_valid_u16($2)) {
		semantic_error("mss value out of range");
	}
	$$->data.mss.bytes = htons($2);
}
| WSCALE INTEGER   {
	$$ = tcp_option_new(TCPOPT_WINDOW, TCPOLEN_WINDOW);
	if (!is_valid_u8($2)) {
		semantic_error("window scale shift count out of range");
	}
	$$->data.window_scale.shift_count = $2;
}
| SACKOK           {
	$$ = tcp_option_new(TCPOPT_SACK_PERMITTED,
				    TCPOLEN_SACK_PERMITTED);
}
| SACK sack_block_list {
	$$ = $2;
}
| TIMESTAMP VAL INTEGER ECR INTEGER  {
	u32 val, ecr;
	$$ = tcp_option_new(TCPOPT_TIMESTAMP, TCPOLEN_TIMESTAMP);
	if (!is_valid_u32($3)) {
		semantic_error("ts val out of range");
	}
	if (!is_valid_u32($5)) {
		semantic_error("ecr val out of range");
	}
	val = $3;
	ecr = $5;
	$$->data.time_stamp.val = htonl(val);
	$$->data.time_stamp.ecr = htonl(ecr);
}
| FAST_OPEN opt_tcp_fast_open_cookie  {
	char *error = NULL;
	$$ = new_tcp_fast_open_option($2, &error);
	free($2);
	if ($$ == NULL) {
		assert(error != NULL);
		semantic_error(error);
		free(error);
	}
}
;

sack_block_list
: sack_block                 { $$ = $1; }
| sack_block_list sack_block {
	const int list_block_bytes =  $1->length - 2;
	assert(list_block_bytes > 0);
	assert((list_block_bytes % sizeof(struct sack_block)) == 0);
	const int num_blocks = list_block_bytes / sizeof(struct sack_block);
	/* Append this SACK block to the end of the array of blocks. */
	memcpy($1->data.sack.block + num_blocks, $2->data.sack.block,
		sizeof(struct sack_block));
	$1->length += sizeof(struct sack_block);
	free($2);
	$$ = $1;
}
;

sack_block
: INTEGER ':' INTEGER {
	$$ = tcp_option_new(TCPOPT_SACK, 2 + sizeof(struct sack_block));
	if (!is_valid_u32($1)) {
		semantic_error("TCP SACK left sequence number out of range");
	}
	if (!is_valid_u32($3)) {
		semantic_error("TCP SACK right sequence number out of range");
	}
	$$->data.sack.block[0].left = htonl($1);
	$$->data.sack.block[0].right = htonl($3);
}
;

syscall_spec
: opt_end_time function_name function_arguments '='
  expression opt_errno opt_note  {
	$$ = calloc(1, sizeof(struct syscall_spec));
	$$->end_usecs	= $1;
	$$->name	= $2;
	$$->arguments	= $3;
	$$->result	= $5;
	$$->error	= $6;
	$$->note	= $7;
}
;

opt_end_time
:                         { $$ = SYSCALL_NON_BLOCKING; }
| ELLIPSIS time           { $$ = $2; }
;

function_name
: WORD                    { $$ = $1; current_script_line = yylineno; }
;

function_arguments
: '(' ')'                 { $$ = NULL; }
| '(' expression_list ')' { $$ = $2; }
;

expression_list
: expression                     { $$ = new_expression_list($1); }
| expression_list ',' expression { $$ = $1; expression_list_append($1, $3); }
;

expression
: ELLIPSIS          {
	$$ = new_expression(EXPR_ELLIPSIS);
}
| decimal_integer   { $$ = $1; }
| hex_integer       { $$ = $1; }
| WORD              {
	$$ = new_expression(EXPR_WORD);
	$$->value.string = $1;
}
| STRING            {
	$$ = new_expression(EXPR_STRING);
	$$->value.string = $1;
	$$->format = "\"%s\"";
}
| STRING ELLIPSIS   {
	$$ = new_expression(EXPR_STRING);
	$$->value.string = $1;
	$$->format = "\"%s\"...";
}
| binary_expression {
	$$ = $1;
}
| array             {
	$$ = $1;
}
| inaddr          {
	$$ = $1;
}
| sockaddr          {
	$$ = $1;
}
| msghdr            {
	$$ = $1;
}
| iovec             {
	$$ = $1;
}
| pollfd            {
	$$ = $1;
}
| linger            {
	$$ = $1;
}
| sctp_rtoinfo      {
	$$ = $1;
}
| sctp_initmsg      {
	$$ = $1;
}
| sctp_assocval     {
	$$ = $1;
}
| sctp_sackinfo     {
	$$ = $1;
}
| sctp_status       {
	$$ = $1;
}
| sctp_paddrinfo    {
	$$ = $1;
}
| sctp_paddrparams  {
	$$ = $1;
}
;

decimal_integer
: INTEGER           {
	$$ = new_integer_expression($1, "%ld");
}
;

hex_integer
: HEX_INTEGER       {
	$$ = new_integer_expression($1, "%#lx");
}
;

binary_expression
: expression '|' expression {       /* bitwise OR */
	$$ = new_expression(EXPR_BINARY);
	struct binary_expression *binary =
			  malloc(sizeof(struct binary_expression));
	binary->op = strdup("|");
	binary->lhs = $1;
	binary->rhs = $3;
	$$->value.binary = binary;
}
;

array
: '[' ']'                 {
	$$ = new_expression(EXPR_LIST);
	$$->value.list = NULL;
}
| '[' expression_list ']' {
	$$ = new_expression(EXPR_LIST);
	$$->value.list = $2;
}
;

inaddr
: INET_ADDR '(' STRING ')' {
	__be32 ip_address = inet_addr($3);
	$$ = new_integer_expression(ip_address, "%#lx");
}
;

sockaddr
:   '{' SA_FAMILY '=' WORD ','
	SIN_PORT '=' _HTONS_ '(' INTEGER ')' ','
	SIN_ADDR '=' INET_ADDR '(' STRING ')' '}' {
	if (strcmp($4, "AF_INET") == 0) {
		struct sockaddr_in *ipv4 = malloc(sizeof(struct sockaddr_in));
		memset(ipv4, 0, sizeof(*ipv4));
		ipv4->sin_family = AF_INET;
		ipv4->sin_port = htons($10);
		if (inet_pton(AF_INET, $17, &ipv4->sin_addr) == 1) {
			$$ = new_expression(EXPR_SOCKET_ADDRESS_IPV4);
			$$->value.socket_address_ipv4 = ipv4;
		} else {
			free(ipv4);
			semantic_error("invalid IPv4 address");
		}
	} else if (strcmp($4, "AF_INET6") == 0) {
		struct sockaddr_in6 *ipv6 = malloc(sizeof(struct sockaddr_in6));
		memset(ipv6, 0, sizeof(*ipv6));
		ipv6->sin6_family = AF_INET6;
		ipv6->sin6_port = htons($10);
		if (inet_pton(AF_INET6, $17, &ipv6->sin6_addr) == 1) {
			$$ = new_expression(EXPR_SOCKET_ADDRESS_IPV6);
			$$->value.socket_address_ipv6 = ipv6;
		} else {
			free(ipv6);
			semantic_error("invalid IPv6 address");
		}
	}
}
;

msghdr
: '{' MSG_NAME '(' ELLIPSIS ')' '=' ELLIPSIS ','
      MSG_IOV '(' decimal_integer ')' '=' array ','
      MSG_FLAGS '=' expression '}' {
	struct msghdr_expr *msg_expr = calloc(1, sizeof(struct msghdr_expr));
	$$ = new_expression(EXPR_MSGHDR);
	$$->value.msghdr = msg_expr;
	msg_expr->msg_name	= new_expression(EXPR_ELLIPSIS);
	msg_expr->msg_namelen	= new_expression(EXPR_ELLIPSIS);
	msg_expr->msg_iov	= $14;
	msg_expr->msg_iovlen	= $11;
	msg_expr->msg_flags	= $18;
	/* TODO(ncardwell): msg_control, msg_controllen */
}
;

iovec
: '{' ELLIPSIS ',' decimal_integer '}' {
	struct iovec_expr *iov_expr = calloc(1, sizeof(struct iovec_expr));
	$$ = new_expression(EXPR_IOVEC);
	$$->value.iovec = iov_expr;
	iov_expr->iov_base = new_expression(EXPR_ELLIPSIS);
	iov_expr->iov_len = $4;
}
;

pollfd
: '{' FD '=' expression ',' EVENTS '=' expression opt_revents '}' {
	struct pollfd_expr *pollfd_expr = calloc(1, sizeof(struct pollfd_expr));
	$$ = new_expression(EXPR_POLLFD);
	$$->value.pollfd = pollfd_expr;
	pollfd_expr->fd = $4;
	pollfd_expr->events = $8;
	pollfd_expr->revents = $9;
}
;

opt_revents
:                                { $$ = new_integer_expression(0, "%ld"); }
| ',' REVENTS '=' expression     { $$ = $4; }
;

l_onoff
: ONOFF '=' INTEGER {
	if (!is_valid_s32($3)) {
		semantic_error("linger onoff out of range");
	}
	$$ = new_integer_expression($3, "%ld");
}
| ONOFF '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

l_linger
: LINGER '=' INTEGER {
	if (!is_valid_s32($3)) {
		semantic_error("linger out of range");
	}
	$$ = new_integer_expression($3, "%ld");
}
| LINGER '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

linger
: '{' l_onoff ',' l_linger '}' {
	$$ = new_expression(EXPR_LINGER);
	$$->value.linger = (struct linger_expr*) calloc(1, sizeof(struct linger_expr));
	$$->value.linger->l_onoff  = $2;
	$$->value.linger->l_linger = $4;
}
;

srto_initial
: SRTO_INITIAL '=' INTEGER {
	if (!is_valid_u32($3)){
		semantic_error("srto_initial out of range");
	}
        $$ = new_integer_expression($3, "%u");
}
| SRTO_INITIAL '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

srto_max
: SRTO_MAX '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("srto_max out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SRTO_MAX '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

srto_min
: SRTO_MIN '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("srto_min out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SRTO_MIN '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_rtoinfo
: '{' srto_initial ',' srto_max ',' srto_min '}' {
#ifdef SCTP_RTOINFO
	$$ = new_expression(EXPR_SCTP_RTOINFO);
	$$->value.sctp_rtoinfo = (struct sctp_rtoinfo_expr*) calloc(1, sizeof(struct sctp_rtoinfo_expr));
	$$->value.sctp_rtoinfo->srto_initial = $2;
	$$->value.sctp_rtoinfo->srto_max = $4;
	$$->value.sctp_rtoinfo->srto_min = $6;
#else
	$$ = NULL;
#endif
}
;

sctp_initmsg
: '{' SINIT_NUM_OSTREAMS '=' INTEGER ',' SINIT_MAX_INSTREAMS '=' INTEGER ',' SINIT_MAX_ATTEMPTS '=' INTEGER ',' SINIT_MAX_INIT_TIMEO '=' INTEGER '}' {
#ifdef SCTP_INITMSG
	$$ = new_expression(EXPR_SCTP_INITMSG);
	if (!is_valid_u16($4)) {
		semantic_error("sinit_num_ostreams out of range");
	}
	$$->value.sctp_initmsg.sinit_num_ostreams = $4;
	if (!is_valid_u16($8)) {
		semantic_error("sinit_max_instreams out of range");
	}
	$$->value.sctp_initmsg.sinit_max_instreams = $8;
	if (!is_valid_u16($12)) {
		semantic_error("sinit_max_attempts out of range");
	}
	$$->value.sctp_initmsg.sinit_max_attempts = $12;
	if (!is_valid_u16($16)) {
		semantic_error("sinit_max_init_timeo out of range");
	}
	$$->value.sctp_initmsg.sinit_max_init_timeo = $16;
#else
	$$ = NULL;
#endif
}
;

sctp_assocval
: '{' ASSOC_VALUE '=' INTEGER '}' {
#if defined(SCTP_MAXSEG) || defined(SCTP_MAX_BURST)
	$$ = new_expression(EXPR_SCTP_ASSOCVAL);
	if (!is_valid_u32($4)) {
		semantic_error("assoc_value out of range");
	}
	$$->value.sctp_assoc_value.assoc_value = $4;
#else
	$$ = NULL;
#endif
}
;

sctp_sackinfo
: '{' SACK_DELAY '=' INTEGER ',' SACK_FREQ '=' INTEGER '}' {
#ifdef SCTP_DELAYED_SACK
	$$ = new_expression(EXPR_SCTP_SACKINFO);
	if (!is_valid_u32($4)) {
		semantic_error("sack_delay out of range");
	}
	$$->value.sctp_sack_info.sack_delay = $4;
	if (!is_valid_u32($8)) {
		semantic_error("sack_freq out of range");
	}
	$$->value.sctp_sack_info.sack_freq  = $8;
#else
	$$ = NULL;
#endif
}
;

sstat_state
: SSTAT_STATE '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sstat_state out of range");
	}
	$$ = new_integer_expression($3, "%ld");
}
| SSTAT_STATE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_rwnd
: SSTAT_RWND '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sstat_rwnd out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSTAT_RWND '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_unackdata
: SSTAT_UNACKDATA '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sstat_unackdata out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSTAT_UNACKDATA '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_penddata
: SSTAT_PENDDATA '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sstat_penddata out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSTAT_PENDDATA '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_instrms
: SSTAT_INSTRMS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sstat_instrms out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSTAT_INSTRMS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_outstrms
: SSTAT_OUTSTRMS '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("sstat_outstrms out of range");
	}
	$$ = new_integer_expression($3, "%hu");
}
| SSTAT_OUTSTRMS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_fragmentation_point
: SSTAT_FRAGMENTATION_POINT '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("sstat_fragmentation_point out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SSTAT_FRAGMENTATION_POINT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sstat_primary
: SSTAT_PRIMARY '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SSTAT_PRIMARY '=' sctp_paddrinfo  { $$ = $3; }
;

spinfo_state
: SPINFO_STATE '=' INTEGER { 
	if (!is_valid_s32($3)) {
		semantic_error("spinfo_state out of range");
	}
	$$ = new_integer_expression($3, "%d"); 
}
| SPINFO_STATE '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spinfo_cwnd
: SPINFO_CWND '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spinfo_cwnd out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPINFO_CWND '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spinfo_srtt
: SPINFO_SRTT '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spinfo_srtt out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPINFO_SRTT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spinfo_rto
: SPINFO_RTO '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spinfo_rto out of range");	
	}
	$$ = new_integer_expression($3, "%u");
}
| SPINFO_RTO '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spinfo_mtu
: SPINFO_MTU '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spinfo_mtu out of range");
	}
	$$ = new_integer_expression($3, "%u");
}
| SPINFO_MTU '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_paddrinfo
: '{' spinfo_state ',' spinfo_cwnd ',' spinfo_srtt ',' spinfo_rto ',' spinfo_mtu '}' {
	$$ = new_expression(EXPR_SCTP_PADDRINFO);
	$$->value.sctp_paddrinfo = (struct sctp_paddrinfo_expr*) calloc(1, sizeof(struct sctp_paddrinfo_expr));
	$$->value.sctp_paddrinfo->spinfo_state = $2;
	$$->value.sctp_paddrinfo->spinfo_cwnd = $4;
	$$->value.sctp_paddrinfo->spinfo_srtt = $6;
	$$->value.sctp_paddrinfo->spinfo_rto = $8;
	$$->value.sctp_paddrinfo->spinfo_mtu = $10;
}
;

sctp_status
: '{' sstat_state ',' sstat_rwnd ',' sstat_unackdata ',' sstat_penddata ',' sstat_instrms ',' sstat_outstrms ','
	sstat_fragmentation_point ',' sstat_primary  '}' {
	$$ = new_expression(EXPR_SCTP_STATUS);
	$$->value.sctp_status = (struct sctp_status_expr*) calloc(1, sizeof(struct sctp_status_expr));
	$$->value.sctp_status->sstat_state = $2;
	$$->value.sctp_status->sstat_rwnd = $4;
	$$->value.sctp_status->sstat_unackdata = $6;
	$$->value.sctp_status->sstat_penddata = $8;
	$$->value.sctp_status->sstat_instrms = $10;
	$$->value.sctp_status->sstat_outstrms = $12;
	$$->value.sctp_status->sstat_fragmentation_point = $14;
	$$->value.sctp_status->sstat_primary = $16;
}
;

spp_address
: SPP_ADDRESS '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
| SPP_ADDRESS '=' sockaddr { $$ = $3; }
;

spp_hbinterval
: SPP_HBINTERVAL '=' INTEGER { 
	if (!is_valid_u32($3)) {
		semantic_error("spp_hbinterval out of range");
	}
	$$ = new_integer_expression($3, "%u"); }
| SPP_HBINTERVAL '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spp_pathmtu
: SPP_PATHMTU '=' INTEGER {
	if (!is_valid_u32($3)) {
		semantic_error("spp_pathmtu out of range");
	}
	$$ = new_integer_expression($3, "%u"); }
| SPP_PATHMTU '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

spp_pathmaxrxt
: SPP_PATHMAXRXT '=' INTEGER {
	if (!is_valid_u16($3)) {
		semantic_error("spp_pathmaxrxt out of range");
	}
	$$ = new_integer_expression($3, "%u"); }
| SPP_PATHMAXRXT '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
;

sctp_paddrparams
: '{' spp_address ',' spp_hbinterval ',' spp_pathmtu ',' spp_pathmaxrxt '}' {
	$$ = new_expression(EXPR_SCTP_PEER_ADDR_PARAMS);
	$$->value.sctp_paddrparams = (struct sctp_paddrparams_expr*) calloc(1, sizeof(struct sctp_paddrparams_expr));
	$$->value.sctp_paddrparams->spp_address = $2;
	$$->value.sctp_paddrparams->spp_hbinterval = $4;
	$$->value.sctp_paddrparams->spp_pathmtu = $6;
	$$->value.sctp_paddrparams->spp_pathmaxrxt = $8;
}
;

opt_errno
:                   { $$ = NULL; }
| WORD note         {
	$$ = malloc(sizeof(struct errno_spec));
	$$->errno_macro = $1;
	$$->strerror    = $2;
}
;

opt_note
:                   { $$ = NULL; }
| note              { $$ = $1; }
;

note
: '(' word_list ')' { $$ = $2; }
;

word_list
: WORD              { $$ = $1; }
| word_list WORD    { asprintf(&($$), "%s %s", $1, $2); free($1); free($2); }
;

command_spec
: BACK_QUOTED       {
	$$ = malloc(sizeof(struct command_spec));
	$$->command_line = $1;
	current_script_line = yylineno;
}
;

code_spec
: CODE              {
	$$ = calloc(1, sizeof(struct code_spec));
	$$->text = $1;
	current_script_line = yylineno;
 }
;