Skip to content
Snippets Groups Projects
parser.y 169 KiB
Newer Older
%{
/*
 * 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
Hoelscher's avatar
Hoelscher committed
 * 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 "ip.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);
extern const char *cleanup_cmd;

/* 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. */
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).
 */
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;

/* Temporary variables to allow passing absolute or ignore timestamp flags to
 * the tcp_options struct from the timestamp options. Adding fields
 * to struct tcp_option, which might be cleaner, affects the on-wire format.
 */
bool absolute_ts_ecr = false;

/* 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(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);
	    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,
{
	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_FASTOPEN_BASE + cookie_bytes;
	struct tcp_option *option;
	option = tcp_option_new(TCPOPT_FASTOPEN, option_bytes);
	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;
}

static struct tcp_option *new_tcp_exp_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_EXP_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_EXP_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.exp_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.exp_fast_open.cookie,
			     sizeof(option->data.exp_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;
	struct abs_integer abs_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 */
		u16 udp_src_port;
		u16 udp_dst_port;
	} transport_info;
	struct {
		u16 udp_src_port;
		u16 udp_dst_port;
	} udp_encaps_info;
	struct {
		bool bad_crc32c;
		s64 tag;
	} sctp_header_spec;
	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_u16_list *u16_list;
	struct sctp_u16_list_item *u16_item;
	struct sctp_sack_block_list_item *sack_block_list_item;
	struct sctp_sack_block_list *sack_block_list;
	struct sctp_forward_tsn_ids_list *forward_tsn_ids_list;
	struct sctp_forward_tsn_ids_list_item  *forward_tsn_ids_list_item;
	struct sctp_i_forward_tsn_ids_list *i_forward_tsn_ids_list;
	struct sctp_i_forward_tsn_ids_list_item  *i_forward_tsn_ids_list_item;
	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_ _HTONL_ INET_ADDR
%token <reserved> MSG_NAME MSG_IOV MSG_FLAGS MSG_CONTROL _CMSG_LEN_ CMSG_LEVEL CMSG_TYPE _CMSG_DATA_
%token <reserved> SF_HDTR_HEADERS SF_HDTR_TRAILERS
%token <reserved> FD EVENTS REVENTS ONOFF LINGER
%token <reserved> ACK ECR EOL MSS NOP SACK NR_SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO
%token <reserved> EXP_FAST_OPEN FAST_OPEN
hoelscher's avatar
hoelscher committed
%token <reserved> IOV_BASE IOV_LEN
%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> AF_NAME AF_ARG
%token <reserved> FUNCTION_SET_NAME PCBCNT
hoelscher's avatar
hoelscher committed
%token <reserved> SRTO_ASSOC_ID 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_ID ASSOC_VALUE SHMAC_NUMBER_OF_IDENTS SHMAC_IDENTS
%token <reserved> STREAM_ID STREAM_VALUE
%token <reserved> SCACT_ASSOC_ID SCACT_KEYNUMBER SACK_ASSOC_ID SACK_DELAY SACK_FREQ
%token <reserved> SSTAT_ASSOC_ID SSTAT_STATE SSTAT_RWND SSTAT_UNACKDATA SSTAT_PENDDATA
%token <reserved> SSTAT_INSTRMS SSTAT_OUTSTRMS SSTAT_FRAGMENTATION_POINT
%token <reserved> SSTAT_PRIMARY
%token <reserved> SPINFO_ASSOC_ID SPINFO_ADDRESS SPINFO_STATE SPINFO_CWND SPINFO_SRTT SPINFO_RTO
%token <reserved> SPINFO_MTU GAUTH_ASSOC_ID GAUTH_NUMBER_OF_CHUNKS GAUTH_CHUNKS
%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 I_DATA PAD RECONFIG FORWARD_TSN I_FORWARD_TSN
%token <reserved> AUTH ASCONF ASCONF_ACK
%token <reserved> TYPE FLAGS LEN
%token <reserved> TAG A_RWND OS IS TSN SID SSN MID PPID FSN CUM_TSN GAPS NR_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 FORWARD_TSN_SUPPORTED
%token <reserved> SUPPORTED_EXTENSIONS ADAPTATION_CODE_POINT ADAPTATION_INDICATION
%token <reserved> OUTGOING_SSN_RESET REQ_SN RESP_SN LAST_TSN IDS SIDS INCOMING_SSN_RESET
%token <reserved> RECONFIG_RESPONSE RESULT SENDER_NEXT_TSN RECEIVER_NEXT_TSN
%token <reserved> SSN_TSN_RESET ADD_INCOMING_STREAMS NUMBER_OF_NEW_STREAMS
Hoelscher's avatar
Hoelscher committed
%token <reserved> ADD_OUTGOING_STREAMS RECONFIG_REQUEST_GENERIC
%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_ASSOC_ID SPP_ADDRESS SPP_HBINTERVAL SPP_PATHMAXRXT SPP_PATHMTU
%token <reserved> SPP_FLAGS SPP_IPV6_FLOWLABEL_ SPP_DSCP_
%token <reserved> SASOC_ASOCMAXRXT SASOC_ASSOC_ID SASOC_NUMBER_PEER_DESTINATIONS SASOC_PEER_RWND
%token <reserved> SASOC_LOCAL_RWND SASOC_COOKIE_LIFE SE_ASSOC_ID SE_TYPE SE_ON
%token <reserved> SSP_ASSOC_ID SSP_ADDR
%token <reserved> SND_SID SND_FLAGS SND_PPID SND_CONTEXT SND_ASSOC_ID SSB_ADAPTATION_IND
%token <reserved> BAD_CRC32C NULL_
%token <reserved> SINFO_STREAM SINFO_SSN SINFO_FLAGS SINFO_PPID SINFO_CONTEXT SINFO_ASSOC_ID
%token <reserved> SINFO_TIMETOLIVE SINFO_TSN SINFO_CUMTSN SINFO_PR_VALUE SERINFO_NEXT_FLAGS
%token <reserved> SERINFO_NEXT_STREAM SERINFO_NEXT_AID SERINFO_NEXT_LENGTH SERINFO_NEXT_PPID
%token <reserved> PR_POLICY PR_VALUE PR_ASSOC_ID AUTH_KEYNUMBER SENDV_FLAGS SENDV_SNDINFO
hoelscher's avatar
hoelscher committed
%token <reserved> SENDV_PRINFO SENDV_AUTHINFO
%token <reserved> RCV_SID RCV_SSN RCV_FLAGS RCV_PPID RCV_TSN RCV_CUMTSN RCV_CONTEXT RCV_ASSOC_ID
%token <reserved> NXT_SID NXT_FLAGS NXT_PPID NXT_LENGTH NXT_ASSOC_ID
hoelscher's avatar
hoelscher committed
%token <reserved> RECVV_RCVINFO RECVV_NXTINFO
%token <reserved> SSE_TYPE SSE_FLAGS SSE_LENGTH SSE_ASSOC_ID
%token <reserved> SENDER_DRY_TYPE SENDER_DRY_FLAGS SENDER_DRY_LENGTH SENDER_DRY_ASSOC_ID
%token <reserved> _SCTP_DATA_IO_EVENT_ _SCTP_ASSOCIATION_EVENT_ _SCTP_ADDRESS_EVENT_
%token <reserved> _SCTP_SEND_FAILURE_EVENT_ _SCTP_PEER_ERROR_EVENT_ _SCTP_SHUTDOWN_EVENT_
%token <reserved> _SCTP_PARTIAL_DELIVERY_EVENT_ _SCTP_ADAPTATION_LAYER_EVENT_
%token <reserved> _SCTP_AUTHENTICATION_EVENT_ _SCTP_SENDER_DRY_EVENT_
%token <reserved> SAC_TYPE SAC_FLAGS SAC_LENGTH SAC_STATE SAC_ERROR SAC_OUTBOUND_STREAMS
%token <reserved> SAC_INBOUND_STREAMS SAC_ASSOC_ID SAC_INFO
%token <reserved> SSFE_TYPE SSFE_FLAGS SSFE_LENGTH SSFE_ERROR SSFE_INFO SSFE_ASSOC_ID SSFE_DATA
%token <reserved> AUTH_TYPE AUTH_FLAGS AUTH_LENGTH AUTH_INDICATION AUTH_ASSOC_ID
%token <reserved> SRE_TYPE SRE_FLAGS SRE_LENGTH SRE_ERROR SRE_ASSOC_ID SRE_DATA PDAPI_ASSOC_ID
%token <reserved> PDAPI_TYPE PDAPI_FLAGS PDAPI_LENGTH PDAPI_INDICATION PDAPI_STREAM PDAPI_SEQ
%token <reserved> SPC_TYPE SPC_FLAGS SPC_LENGTH SPC_AADDR SPC_STATE SPC_ERROR SPC_ASSOC_ID
%token <reserved> SSF_TYPE SSF_LENGTH SSF_FLAGS SSF_ERROR SSF_INFO SSF_ASSOC_ID SSF_DATA
%token <reserved> SAI_TYPE SAI_FLAGS SAI_LENGTH SAI_ADAPTATION_IND SAI_ASSOC_ID
%token <reserved> GAIDS_NUMBER_OF_IDS GAIDS_ASSOC_ID SSPP_ASSOC_ID SSPP_ADDR
%token <reserved> SN_TYPE SN_FLAGS SN_LENGTH SAUTH_CHUNK
%token <reserved> SCA_ASSOC_ID SCA_KEYNUMBER SCA_KEYLENGTH SCA_KEY
%token <reserved> SRS_ASSOC_ID SRS_FLAGS SRS_NUMBER_STREAMS SRS_STREAM_LIST
%token <reserved> SAS_ASSOC_ID SAS_INSTRMS SAS_OUTSTRMS
%token <reserved> STRRESET_TYPE STRRESET_FLAGS STRRESET_LENGTH STRRESET_ASSOC_ID STRRESET_STREAM_LIST
%token <reserved> ASSOCRESET_TYPE ASSOCRESET_FLAGS ASSOCRESET_LENGTH ASSOCRESET_ASSOC_ID ASSOCRESET_LOCAL_TSN ASSOCRESET_REMOTE_TSN
%token <reserved> STRCHANGE_TYPE STRCHANGE_FLAGS STRCHANGE_LENGTH STRCHANGE_ASSOC_ID STRCHANGE_INSTRMS STRCHANGE_OUTSTRMS
%token <reserved> SUE_ASSOC_ID SUE_ADDRESS SUE_PORT
%token <floating> FLOAT
%token <integer> INTEGER HEX_INTEGER
%token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
%type <abs_integer> abs_integer
%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 data
%type <expression> inaddr sockaddr msghdr cmsghdr cmsg_level cmsg_type cmsg_data
%type <expression> sf_hdtr iovec pollfd opt_revents
%type <expression> linger l_onoff l_linger
%type <expression> accept_filter_arg af_name af_arg
%type <expression> tcp_function_set function_set_name pcbcnt
%type <udp_encaps_info> opt_udp_encaps_info
%type <sctp_header_spec> sctp_header_spec
%type <expression> sctp_assoc_id
%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 sinit_num_ostreams sinit_max_instreams sinit_max_attempts
%type <expression> sinit_max_init_timeo sctp_assoc_value sctp_stream_value sctp_hmacalgo
%type <expression> shmac_number_of_idents
%type <expression> sctp_authkeyid scact_keynumber sctp_sackinfo sack_delay sack_freq
%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> spp_flags spp_ipv6_flowlabel spp_dscp ssp_addr
%type <expression> spinfo_address spinfo_state spinfo_cwnd spinfo_srtt spinfo_rto spinfo_mtu
%type <expression> sctp_authchunks gauth_number_of_chunks
%type <expression> sasoc_asocmaxrxt sasoc_number_peer_destinations sasoc_peer_rwnd
%type <expression> sasoc_local_rwnd sasoc_cookie_life sctp_assocparams
%type <expression> sctp_sndinfo snd_sid snd_flags snd_ppid snd_context
%type <expression> sctp_event se_type se_on sctp_setadaptation sctp_setprim null
%type <expression> sctp_sndrcvinfo sinfo_stream sinfo_ssn sinfo_flags sinfo_ppid sinfo_context
%type <expression> sinfo_timetolive sinfo_tsn sinfo_cumtsn sinfo_pr_value serinfo_next_flags
%type <expression> serinfo_next_stream serinfo_next_aid serinfo_next_length serinfo_next_ppid sctp_extrcvinfo
%type <expression> sctp_prinfo sctp_authinfo pr_policy sctp_sendv_spa sctp_default_prinfo
%type <expression> sctp_rcvinfo rcv_sid rcv_ssn rcv_flags rcv_ppid rcv_tsn rcv_cumtsn rcv_context
%type <expression> sctp_nxtinfo nxt_sid nxt_flags nxt_ppid nxt_length sctp_recvv_rn
%type <expression> sctp_shutdown_event sse_type sse_flags sse_length
%type <expression> sctp_sender_dry_event sender_dry_type sender_dry_flags sender_dry_length
%type <expression> sctp_event_subscribe
%type <expression> sctp_assoc_change sac_type sac_flags sac_length sac_state sac_error sac_outbound_streams
%type <expression> sac_inbound_streams sac_info
%type <expression> sctp_send_failed_event ssfe_type ssfe_flags ssfe_length ssfe_error ssfe_data
%type <expression> sctp_authkey_event auth_type auth_flags auth_length auth_keynumber auth_indication
%type <expression> sctp_remote_error sre_type sre_flags sre_length sre_error sre_data
%type <expression> sctp_pdapi_event pdapi_type pdapi_flags pdapi_length pdapi_indication pdapi_stream pdapi_seq
%type <expression> sctp_paddr_change spc_type spc_flags spc_length spc_aaddr spc_error spc_state
%type <expression> sctp_send_failed ssf_type ssf_length ssf_flags ssf_error ssf_info ssf_data
%type <expression> sctp_adaptation_event sai_type sai_flags sai_length sai_adaptation_ind
%type <expression> sctp_tlv sn_type sn_flags sn_length sctp_assoc_ids gaids_number_of_ids
%type <expression> sctp_setpeerprim sctp_authchunk sctp_authkey
%type <expression> sctp_reset_streams srs_flags
%type <expression> sctp_stream_reset_event strreset_type strreset_flags strreset_length
%type <expression> sctp_assoc_reset_event assocreset_type assocreset_flags assocreset_length
%type <expression> assocreset_local_tsn assocreset_remote_tsn
%type <expression> sctp_stream_change_event strchange_type strchange_flags strchange_length strchange_instrms strchange_outstrms
%type <expression> sctp_add_streams
%type <expression> sctp_udpencaps sue_address sue_port
%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 sctp_nr_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_i_data_chunk_spec
%type <chunk_list_item> sctp_pad_chunk_spec sctp_reconfig_chunk_spec
%type <chunk_list_item> sctp_forward_tsn_spec sctp_i_forward_tsn_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_fornward_tsn_supported_spec
%type <parameter_list_item> sctp_supported_extensions_parameter_spec
%type <parameter_list_item> sctp_adaptation_indication_parameter_spec
%type <parameter_list_item> sctp_pad_parameter_spec
%type <parameter_list_item> outgoing_ssn_reset_request incoming_ssn_reset_request
Hoelscher's avatar
Hoelscher committed
%type <parameter_list_item> reconfig_response ssn_tsn_reset_request generic_reconfig_request
%type <parameter_list_item> add_outgoing_streams_request add_incoming_streams_request
%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> chunk_type 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_i_data_flags opt_len
%type <integer> opt_tag opt_a_rwnd opt_os opt_is opt_tsn opt_sid opt_ssn
%type <integer> opt_mid opt_fsn
%type <integer> opt_cum_tsn opt_ppid opt_sender_next_tsn opt_receiver_next_tsn
%type <integer> opt_req_sn opt_resp_sn opt_last_tsn opt_result opt_number_of_new_streams
%type <byte_list> opt_val opt_info byte_list chunk_types_list
Hoelscher's avatar
Hoelscher committed
%type <byte_list_item> byte
%type <u16_list> u16_list
%type <u16_item> u16_item
%type <sack_block_list> opt_gaps opt_nr_gaps gap_list opt_dups dup_list
%type <sack_block_list_item> gap dup
%type <forward_tsn_ids_list> opt_stream_identifier  ids_list
%type <forward_tsn_ids_list_item> id
%type <i_forward_tsn_ids_list> opt_i_forward_tsn_stream_identifier i_forward_tsn_ids_list
%type <i_forward_tsn_ids_list_item> i_forward_tsn_id
%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 opt_cleanup_command {
	$$ = 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 {
	$$ = new_option($1, NULL);
}

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

option_value
: INTEGER	{ $$ = strdup(yytext); }
| WORD		{ $$ = $1; }
| STRING	{ $$ = $1; }
| IPV6_ADDR	{ $$ = $1; }
| IPV4		{ $$ = strdup("ipv4"); }
| IPV6		{ $$ = strdup("ipv6"); }
| WORD '=' INTEGER {
	/* For consistency, allow syntax like: --define=PROTO=132 */
	char *lhs = $1;
	s64 rhs = $3;

	asprintf(&($$), "%s=%lld", lhs, rhs);
	free(lhs);
}
| WORD '=' WORD {
	/* For consistency, allow syntax like: --define=PROTO=IPPROTO_TCP */
	char *lhs = $1, *rhs = $3;

	asprintf(&($$), "%s=%s", lhs, rhs);
	free(lhs);
	free(rhs);
}
| WORD '=' STRING {
	/* For consistency, allow syntax like: --define=CC="reno" */
	char *lhs = $1, *rhs = $3;

	asprintf(&($$), "%s=\"%s\"", lhs, rhs);
	free(lhs);
	free(rhs);
}
| WORD '=' BACK_QUOTED {
	/* For consistency, allow syntax like: --define=SCRIPT=`cleanup` */
	char *lhs = $1, *rhs = $3;

	asprintf(&($$), "%s=`%s`", lhs, rhs);
	free(lhs);
	free(rhs);
}
;

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; }
;