%{ /* * 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; } ;