diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f2c8c65e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea/ +/.vscode/ \ No newline at end of file diff --git a/gtests/net/mutation-scripts/blocking-accept-fuzz-template.pkt b/gtests/net/mutation-scripts/blocking-accept-fuzz-template.pkt new file mode 100644 index 00000000..628534ad --- /dev/null +++ b/gtests/net/mutation-scripts/blocking-accept-fuzz-template.pkt @@ -0,0 +1,9 @@ + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0...0.200 accept(3, ..., ...) = 4 ++.1 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 ++.1 < . 1:1(0) ack 1 win 257 ++.1 < P. 1:21(20) ack 1 win 257 \ No newline at end of file diff --git a/gtests/net/mutation-scripts/blocking-accept-fuzz.pkt b/gtests/net/mutation-scripts/blocking-accept-fuzz.pkt new file mode 100644 index 00000000..0c7c9270 --- /dev/null +++ b/gtests/net/mutation-scripts/blocking-accept-fuzz.pkt @@ -0,0 +1,25 @@ +// Test for blocking accept. + +//`../common/defaults.sh` + +// Establish a connection. + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0...0.200 accept(3, ..., ...) = 4 + + +.1 < S 0:0(0) win 32792 { mut tcp data_offset 0xFF ; ins tcp 20 0x02040000 } + +0 > S. 0:0(0) ack 1 + +.1 < . 1:1(0) ack 1 win 257 + + +.1 write(4, ..., 200) = 200 + +0 > P. 1:201(200) ack 1 + +.1 < . 201:201(0) ack 1 win 257 + + +.1 < P. 1:11(10) ack 1 win 257 + +0 read(4, ..., 10) = 10 + + +0 close(3) = 0 + +0 > F. 201:201(0) ack 1 + +0 < . 1:1(0) ack 1002 win 257 \ No newline at end of file diff --git a/gtests/net/mutation-scripts/blocking-connect-fuzz.pkt b/gtests/net/mutation-scripts/blocking-connect-fuzz.pkt new file mode 100644 index 00000000..2499aa6e --- /dev/null +++ b/gtests/net/mutation-scripts/blocking-connect-fuzz.pkt @@ -0,0 +1,10 @@ +// Test for blocking connect. + +// Establish a connection. + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + + +.1...0.200 connect(3, ..., ...) = 0 + + +0 > S 0:0(0) + +.1 < S. 0:0(0) ack 1 win 5792 { mut tcp data_offset 0xFF ; ins tcp 20 0x02040000 } + +0 > . 1:1(0) ack 1 diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common index b614d085..8a5c6718 100644 --- a/gtests/net/packetdrill/Makefile.common +++ b/gtests/net/packetdrill/Makefile.common @@ -1,6 +1,6 @@ all: binaries -CFLAGS = -g -Wall -Werror +CFLAGS = -g -Wall -Werror -Wno-unused-result -Wno-stringop-truncation -Wno-int-to-pointer-cast parser.o: parser.y bison --output=parser.c --defines=parser.h --report=state parser.y @@ -29,7 +29,7 @@ packetdrill-lib := \ link_layer.o wire_conn.o wire_protocol.o \ wire_client.o wire_client_netdev.o \ wire_server.o wire_server_netdev.o \ - epoll.o pipe.o file.o so_testing.o wrap.o + epoll.o pipe.o file.o so_testing.o wrap.o fuzz_options.o fm_testing.o packetdrill-objs := packetdrill.o $(packetdrill-lib) diff --git a/gtests/net/packetdrill/code.c b/gtests/net/packetdrill/code.c index 47ec79e5..64c2c09b 100644 --- a/gtests/net/packetdrill/code.c +++ b/gtests/net/packetdrill/code.c @@ -777,7 +777,7 @@ void run_code_event(struct state *state, struct event *event, return; error_out: - die("%s:%d: runtime error in code: %s\n", + die_free_so(state, "%s:%d: runtime error in code: %s\n", state->config->script_path, event->line_number, error); free(error); } diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c index 37e2eb08..ca78ab23 100644 --- a/gtests/net/packetdrill/config.c +++ b/gtests/net/packetdrill/config.c @@ -65,6 +65,7 @@ enum option_codes { OPT_DRY_RUN, OPT_IS_ANYIP, OPT_SEND_OMIT_FREE, + OPT_FM_FILENAME, OPT_DEFINE = 'D', /* a '-D' single-letter option */ OPT_VERBOSE = 'v', /* a '-v' single-letter option */ }; @@ -103,6 +104,7 @@ struct option options[] = { { "send_omit_free", .has_arg = false, NULL, OPT_SEND_OMIT_FREE }, { "define", .has_arg = true, NULL, OPT_DEFINE }, { "verbose", .has_arg = false, NULL, OPT_VERBOSE }, + { "fm_filename", .has_arg = true, NULL, OPT_FM_FILENAME}, { NULL }, }; @@ -513,6 +515,11 @@ static void process_option(int opt, char *optarg, struct config *config, case OPT_VERBOSE: config->verbose = true; break; + + case OPT_FM_FILENAME: + config->fm_filename = strdup(optarg); + break; + default: show_usage(); exit(EXIT_FAILURE); diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h index 649a8c4f..b498343c 100644 --- a/gtests/net/packetdrill/config.h +++ b/gtests/net/packetdrill/config.h @@ -159,6 +159,9 @@ struct config { char *so_filename; char *so_flags; + /* Fuzz mutation file for fuzzing, using an external mutation file */ + char *fm_filename; + /* For anyip testing */ bool is_anyip; diff --git a/gtests/net/packetdrill/fd_state.h b/gtests/net/packetdrill/fd_state.h index f08a5591..d206734b 100644 --- a/gtests/net/packetdrill/fd_state.h +++ b/gtests/net/packetdrill/fd_state.h @@ -52,6 +52,7 @@ struct fd_state { int script_fd; /* file descriptor in the script source */ int live_fd; /* file descriptor in packetdrill runtime */ bool is_closed; /* has app called close(2) ? */ + bool so_managed; /* so_instance was used to create this syscall. Should be in charge of closing it */ struct fd_state *next; /* next fd in linked list */ }; diff --git a/gtests/net/packetdrill/fm_testing.c b/gtests/net/packetdrill/fm_testing.c new file mode 100644 index 00000000..3ebacf71 --- /dev/null +++ b/gtests/net/packetdrill/fm_testing.c @@ -0,0 +1,94 @@ +/* + * Author: pamusuo@purdue.edu (Paschal Amusuo) + * + * Testing using mutated packets + */ + +#include "fm_testing.h" +#include + +struct config; + +struct fm_packet { + u8 *buffer; /* data buffer: full contents of packet */ + u32 buffer_bytes; /* bytes of space in data buffer */ + + /* Layer 3 */ + u8 *ipv4; /* start of IPv4 header, if present */ + u8 *ipv6; /* start of IPv6 header, if present */ + + /* Layer 4 */ + u8 *tcp; /* start of TCP header, if present */ + u8 *udp; /* start of UDP header, if present */ + u8 *icmpv4; /* start of ICMPv4 header, if present */ + u8 *icmpv6; /* start of ICMPv6 header, if present */ +}; + +struct packet *handle_packet_mutation(struct packet *packet, struct fm_instance *fm_instance) { + + printf("ip_bytes: %d\n", packet->ip_bytes); + printf("buffer_bytes: %d\n", packet->buffer_bytes); + + struct fm_packet fm_packet = { + .buffer = (u8 *) packet->buffer, + .buffer_bytes = packet->ip_bytes, + .icmpv4 = (u8 *) packet->icmpv4, + .icmpv6 = (u8 *) packet->icmpv6, + .ipv4 = (u8 *) packet->ipv4, + .ipv6 = (u8 *) packet->ipv6, + .tcp = (u8 *) packet->tcp, + .udp = (u8 *) packet->udp + }; + + struct fm_packet *mutated_fm_packet = fm_instance->fm_interface.mutate(&fm_packet, packet->fuzz_options); + + packet->buffer = mutated_fm_packet->buffer; + packet->ip_bytes = mutated_fm_packet->buffer_bytes; + packet->icmpv4 = (struct icmpv4 *) mutated_fm_packet->icmpv4; + packet->icmpv6 = (struct icmpv6 *) mutated_fm_packet->icmpv6; + packet->ipv4 = (struct ipv4 *) mutated_fm_packet->ipv4; + packet->ipv6 = (struct ipv6 *) mutated_fm_packet->ipv6; + packet->tcp = (struct tcp *) mutated_fm_packet->tcp; + packet->udp = (struct udp *) mutated_fm_packet->udp; + + return packet; + +} + +struct fm_instance *fm_instance_new(void) { + return calloc(1, sizeof(struct fm_instance)); +} + +int fm_instance_init(struct fm_instance *instance, + const struct config *config) { + fm_interface_init_t init; + char *error; + + instance->handle = dlopen(config->fm_filename, + RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE | + RTLD_DEEPBIND); + if (!instance->handle) + die("%s\n", dlerror()); + dlerror(); /* clear any existing error */ + + init = dlsym(instance->handle, "fm_interface_init"); + error = dlerror(); + if (error) + die("%s\n", error); + + init(&instance->fm_interface); + return STATUS_OK; +} + +void fm_instance_free(struct fm_instance *instance) { + if (!instance) + return; + + instance->fm_interface.free(); + + if (instance->handle) + dlclose(instance->handle); + + memset(instance, 0, sizeof(*instance)); + free(instance); +} diff --git a/gtests/net/packetdrill/fm_testing.h b/gtests/net/packetdrill/fm_testing.h new file mode 100644 index 00000000..a80ed3bb --- /dev/null +++ b/gtests/net/packetdrill/fm_testing.h @@ -0,0 +1,45 @@ + +/* + * Author: pamusuo@purdue.edu (Paschal Amusuo) + * + * Testing using mutated packets + */ + +#ifndef __FM_TESTING_H__ +#define __FM_TESTING_H__ + +#include "fm_testing.h" +#include "packet.h" +#include "config.h" + +struct config; +struct fm_packet; + +struct fm_interface { + struct fm_packet *(*mutate)(struct fm_packet *original, struct fuzz_options *fuzz_options); + void (*free)(); +}; + +typedef void (*fm_interface_init_t)(struct fm_interface *); + +struct fm_instance { + struct fm_interface fm_interface; + void *handle; +}; + + +/* Invoke mutate method of fm_interface */ +struct packet *handle_packet_mutation(struct packet *packet, struct fm_instance *fm_instance); + + +/* Allocate a new fm_instance. */ +struct fm_instance *fm_instance_new(void); + +/* Load the fuzz mutation shared object and setup callback functions. */ +int fm_instance_init(struct fm_instance *instance, + const struct config *config); + +/* Delete a so_instance and its associated objects. */ +void fm_instance_free(struct fm_instance *instance); + +#endif /* __FM_TESTING_H__ */ diff --git a/gtests/net/packetdrill/fuzz_options.c b/gtests/net/packetdrill/fuzz_options.c new file mode 100644 index 00000000..e5ab0c5a --- /dev/null +++ b/gtests/net/packetdrill/fuzz_options.c @@ -0,0 +1,49 @@ +#include "fuzz_options.h" + +#include +#include + +struct fuzz_options* fuzz_options_new(void) { + struct fuzz_options *options = (struct fuzz_options *) malloc(sizeof(struct fuzz_options)); + + options->options = malloc(sizeof(struct fuzz_option)); + options->capacity = sizeof(struct fuzz_option); + options->count = 0; + options->size = 0; + + return options; +} + +void fuzz_options_grow(struct fuzz_options *fuzz_options, size_t capacity) { + if (capacity > fuzz_options->capacity) { + fuzz_options->options = realloc(fuzz_options->options, capacity); + fuzz_options->capacity = capacity; + } +} + +int fuzz_options_append(struct fuzz_options *fuzz_options, struct fuzz_option *option) { + size_t fuzz_option_size = sizeof(struct fuzz_option); + if (fuzz_option_size + fuzz_options->size > fuzz_options->capacity) { + fuzz_options_grow(fuzz_options, fuzz_options->capacity * 2); + } + + memcpy(fuzz_options->options + fuzz_options->count, option, fuzz_option_size); + fuzz_options->size += fuzz_option_size; + fuzz_options->count += 1; + + free(option); + + return STATUS_OK; +} + +struct fuzz_option* fuzz_option_new(enum fuzz_type_t fuzz_type, enum header_type_t header_type, u8 fuzz_field, struct fuzz_value_t fuzz_value) { + struct fuzz_option *option = calloc(1, sizeof(struct fuzz_option)); + + option->fuzz_type = fuzz_type; + option->header_type = header_type; + option->fuzz_field = fuzz_field; + option->fuzz_value = fuzz_value.value; + option->fuzz_value_byte_count = fuzz_value.byte_count; + + return option; +} \ No newline at end of file diff --git a/gtests/net/packetdrill/fuzz_options.h b/gtests/net/packetdrill/fuzz_options.h new file mode 100644 index 00000000..26630fbd --- /dev/null +++ b/gtests/net/packetdrill/fuzz_options.h @@ -0,0 +1,96 @@ +#ifndef __FUZZ_OPTIONS_H__ +#define __FUZZ_OPTIONS_H__ + +#include "types.h" + +struct fuzz_options { + struct fuzz_option *options; // Pointer to first fuzz_option + u8 capacity; // Allocated space in the buffer + u8 size; // Size (in bytes) of items in the buffer + u8 count; // Count of fuzz instructions in the buffer +}; + + +enum fuzz_type_t { + OP_REPLACE = 0, + OP_TRUNCATE = 1, + OP_INSERT = 2 +}; + +enum field_name_t { + F_SRC_PORT = 0, + F_DST_PORT = 1, + F_SEQ_NUM = 2, + F_ACK_NUM = 3, + F_DATA_OFF = 4, + F_RESERVED = 5, + F_FLAGS = 6, + F_CWR_FLAG = 7, + F_ECE_FLAG = 8, + F_URG_FLAG = 9, + F_ACK_FLAG = 10, + F_PSH_FLAG = 11, + F_RST_FLAG = 12, + F_SYN_FLAG = 13, + F_FIN_FLAG = 14, + F_WIN_SIZE = 15, + F_CHECKSUM = 16, + F_URG_POINTER = 17, + F_UDP_LEN = 18, + F_VERSION = 19, + F_IHL = 20, + F_DSCP = 21, + F_ECN = 22, + F_TOT_LEN = 23, + F_IDEN = 24, + F_RSV_FLAG = 25, + F_DF_FLAG = 26, + F_MF_FLAG = 27, + F_FRAG_OFF = 28, + F_TTL = 29, + F_PROTOCOL = 30, + F_SRC_ADDR = 31, + F_DST_ADDR = 32, + F_TRF_CLASS = 33, + F_FLOW_LABEL = 34, + F_PYLD_LEN = 35, + F_NEXT_HEADER = 36, + F_HOP_LIMIT = 37 +}; + + + +enum header_type_t { + IPv4 = 0, + IPv6 = 1, + xTCP = 2, + xUDP = 3 +}; + +struct fuzz_option { + enum fuzz_type_t fuzz_type; + enum header_type_t header_type; + u8 fuzz_field; + char *fuzz_value; + u8 fuzz_value_byte_count; +} __packed; + +struct fuzz_value_t { + char *value; + u8 byte_count; +}; + +struct fuzz_field { + u8 fuzz_offset; + u8 fuzz_length; +}; + +extern struct fuzz_options* fuzz_options_new(void); + +extern void fuzz_options_grow(struct fuzz_options *fuzz_options, size_t capacity); + +extern int fuzz_options_append(struct fuzz_options *fuzz_options, struct fuzz_option *option); + +extern struct fuzz_option* fuzz_option_new(enum fuzz_type_t fuzz_type, enum header_type_t header_type, u8 fuzz_field, struct fuzz_value_t fuzz_value); + +#endif /* __FUZZ_OPTIONS_H__ */ \ No newline at end of file diff --git a/gtests/net/packetdrill/fuzz_testing.h b/gtests/net/packetdrill/fuzz_testing.h new file mode 100644 index 00000000..567b05ab --- /dev/null +++ b/gtests/net/packetdrill/fuzz_testing.h @@ -0,0 +1,12 @@ + +/* + * Author: pamusuo@purdue.edu (Paschal Amusuo) + * + * Testing using mutated packets + */ + +#ifndef __FUZZ_TESTING_H__ +#define __FUZZ_TESTING_H__ + + +#endif /* __FUZZ_TESTING_H__ */ diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index 7d063d37..d4824b3d 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -51,6 +51,7 @@ #include "tcp_options.h" #include "parse.h" #include "config.h" +#include "fuzz_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. @@ -233,7 +234,6 @@ present return PRESENT; mpls return MPLS; label return LABEL; tc return TC; -ttl return TTL; inet_addr return INET_ADDR; inet6_addr return INET6_ADDR; ack return ACK; @@ -250,7 +250,6 @@ FO return FAST_OPEN; FOEXP return FAST_OPEN_EXP; tos return TOS; flowlabel return FLOWLABEL; -flags return FLAGS; Flags return FLAGS; val return VAL; win return WIN; @@ -262,6 +261,51 @@ ect1 return ECT1; noecn return NO_ECN; ce return CE; id return ID; +rep return REPLACE; +trun return TRUNCATE; +ins return INSERT; +tcp return TCP; +ihl return IHL; + +src_port return SRC_PORT; +dst_port return DST_PORT; +seq_num return SEQ_NUM; +ack_num return ACK_NUM; +data_off return DATA_OFF; +reserved return RESERVED; +flags return FLAGS; +cwr_flag return CWR_FLAG; +ece_flag return ECE_FLAG; +urg_flag return URG_FLAG; +ack_flag return ACK_FLAG; +psh_flag return PSH_FLAG; +rst_flag return RST_FLAG; +syn_flag return SYN_FLAG; +fin_flag return FIN_FLAG; +win_size return WIN_SIZE; +checksum return CHECKSUM; +urg_pointer return URG_POINTER; +udp_len return UDP_LEN; +version return VERSION; +dscp return DSCP; +ecn return ECN; +tot_len return TOT_LEN; +iden return IDEN; +rsv_flag return RSV_FLAG; +df_flag return DF_FLAG; +mf_flag return MF_FLAG; +frag_off return FRAG_OFF; +ttl return TTL; +protocol return PROTOCOL; +src_addr return SRC_ADDR; +dst_addr return DST_ADDR; +trf_class return TRF_CLASS; +flow_label return FLOW_LABEL; +pyld_len return PYLD_LEN; +next_header return NEXT_HEADER; +hop_limit return HOP_LIMIT; + + [.][.][.] return ELLIPSIS; --[a-zA-Z0-9_]+ yylval.string = option(yytext); return OPTION; [-]?[0-9]*[.][0-9]+ yylval.floating = atof(yytext); return FLOAT; diff --git a/gtests/net/packetdrill/logging.c b/gtests/net/packetdrill/logging.c index 730add4b..16483d47 100644 --- a/gtests/net/packetdrill/logging.c +++ b/gtests/net/packetdrill/logging.c @@ -49,3 +49,19 @@ void __attribute__((noreturn)) die_perror(char *message) exit(EXIT_FAILURE); } + +extern void __attribute__((noreturn)) die_free_so(struct state *state, char *format, ...) +{ + va_list ap; + + if (state->so_instance) + so_instance_free(state->so_instance); + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + + run_cleanup_command(); + + exit(EXIT_FAILURE); +} diff --git a/gtests/net/packetdrill/logging.h b/gtests/net/packetdrill/logging.h index d961c9d5..fed3dd62 100644 --- a/gtests/net/packetdrill/logging.h +++ b/gtests/net/packetdrill/logging.h @@ -37,10 +37,15 @@ fflush(stdout); \ } +struct state; + /* Log the message to stderr and then exit with a failure status code. */ extern void __attribute__((noreturn)) die(char *format, ...); /* Call perror() with message and then exit with a failure status code. */ extern void __attribute__((noreturn)) die_perror(char *message); +/* Call so_instance_free() to free the so instance, then log the message to stderr and exit */ +extern void __attribute__((noreturn)) die_free_so(struct state *state, char *format, ...); + #endif /* __LOGGING_H__ */ diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c index d2d792a2..2377f248 100644 --- a/gtests/net/packetdrill/packet.c +++ b/gtests/net/packetdrill/packet.c @@ -179,6 +179,7 @@ static void packet_duplicate_info(struct packet *packet, packet->tcp_ts_ecr = offset_ptr(old_base, new_base, old_packet->tcp_ts_ecr); packet->echoed_header = old_packet->echoed_header; + packet->fuzz_options = old_packet->fuzz_options; } /* Make a copy of the given old packet, but in the new copy reserve the diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index bd484b87..1e173fd9 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -39,6 +39,7 @@ #include "tcp.h" #include "udp.h" #include "unaligned.h" +#include "fuzz_options.h" /* The data offset field is 4 bits, and specifies the length of the TCP header, * including options, in 32-bit words. @@ -111,6 +112,8 @@ struct packet { __be32 *tcp_ts_val; /* location of TCP timestamp val, or NULL */ __be32 *tcp_ts_ecr; /* location of TCP timestamp ecr, or NULL */ int mss; + + struct fuzz_options *fuzz_options; /* specifies fuzz instructions that should be executed on this packet */ }; /* A simple list of packets. */ diff --git a/gtests/net/packetdrill/packet_checksum.c b/gtests/net/packetdrill/packet_checksum.c index d5164b34..31364e2c 100644 --- a/gtests/net/packetdrill/packet_checksum.c +++ b/gtests/net/packetdrill/packet_checksum.c @@ -42,7 +42,7 @@ static void checksum_ipv4_packet(struct packet *packet) /* Find the length of layer 4 header, options, and payload. */ const int l4_bytes = ntohs(ipv4->tot_len) - ipv4_header_len(ipv4); - assert(l4_bytes > 0); + //assert(l4_bytes > 0); // When fuzzing, we can remove the l4 header /* Fill in IPv4-based layer 4 checksum. */ if (packet->tcp != NULL) { @@ -76,7 +76,7 @@ static void checksum_ipv6_packet(struct packet *packet) /* Find the length of layer 4 header, options, and payload. */ const int l4_bytes = ntohs(ipv6->payload_len); - assert(l4_bytes > 0); + //assert(l4_bytes > 0); /* Fill in IPv6-based layer 4 checksum. */ if (packet->tcp != NULL) { diff --git a/gtests/net/packetdrill/packetdrill.c b/gtests/net/packetdrill/packetdrill.c index 4afa038e..8318c0e0 100644 --- a/gtests/net/packetdrill/packetdrill.c +++ b/gtests/net/packetdrill/packetdrill.c @@ -63,6 +63,7 @@ static void run_init_scripts(struct config *config) int main(int argc, char *argv[]) { + printf("Starting Packetdrill v1"); struct config config; set_default_config(&config); /* Get command line options and list of test scripts. */ diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 08aff3ea..ee750efb 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -103,6 +103,7 @@ #include "script.h" #include "tcp.h" #include "tcp_options.h" +#include "fuzz_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. @@ -439,6 +440,35 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, return option; } +static struct fuzz_value_t* parse_hex_blob(const char *hex_blob, + char **error) +{ + int hex_blob_len = strlen(hex_blob); + int hex_bytes = hex_blob_len / 2; + int parsed_bytes = 0; + + char *parsed_hex_blob = malloc(hex_bytes); + + /* Parse MD5 digest. This should be an ASCII hex string representing 16 + * bytes. But we allow smaller buffers, since we want to allow test + * cases that supply invalid cookies. + */ + if (parse_hex_string(hex_blob, + (u8 *) parsed_hex_blob, + hex_bytes, + &parsed_bytes)) { + free(parsed_hex_blob); + asprintf(error, "Hex blob is not a valid hex string"); + return NULL; + } + + struct fuzz_value_t *value = malloc(sizeof(struct fuzz_value_t)); + value->value = parsed_hex_blob; + value->byte_count = parsed_bytes; + + return value; +} + static struct tcp_option *new_md5_option(const char *digest_string, char **error) { @@ -523,6 +553,12 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) u16 src_port; u16 dst_port; } port_info; + struct fuzz_value_t fuzz_value_t; + struct fuzz_options *fuzz_options; + struct fuzz_option *fuzz_option; + enum fuzz_type_t fuzz_type; + enum header_type_t header_type; + enum field_name_t field_name; } /* The specific type of the output for a symbol is given by the %type @@ -546,6 +582,10 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) %token NONE CHECKSUM SEQUENCE PRESENT %token EE_ERRNO EE_CODE EE_DATA EE_INFO EE_ORIGIN EE_TYPE %token SCM_SEC SCM_NSEC +%token REPLACE TRUNCATE INSERT +%token IP TCP +%token IHL UDP_LEN SRC_PORT DST_PORT SEQ_NUM ACK_NUM DATA_OFF RESERVED WIN_SIZE URG_POINTER VERSION FRAG_OFF MF_FLAG DF_FLAG RSV_FLAG ECN DSCP FIN_FLAG SYN_FLAG RST_FLAG PSH_FLAG ACK_FLAG URG_FLAG ECE_FLAG CWR_FLAG; +%token DSCP_ESN TOT_LEN IDEN FLAGS_FLAGOFF PROTOCOL SRC_ADDR DST_ADDR TRF_CLASS VERSION_TRF TRF_FLOW FLOW_LABEL PYLD_LEN NEXT_HEADER HOP_LIMIT; %token FLOAT %token INTEGER HEX_INTEGER %token WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR @@ -568,7 +608,8 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) %type gre_flags_list gre_flags gre_flag %type gre_sum gre_off gre_key gre_seq %type opt_icmp_echo_id -%type flow_label +%type flow_label +%type fuzz_field field_offset %type icmp_type opt_icmp_code opt_ack_flag opt_word ack_and_ace flags %type opt_tcp_fast_open_cookie hex_blob %type opt_note note word_list @@ -580,7 +621,13 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) %type seq opt_icmp_echoed %type opt_tcp_options tcp_option_list %type tcp_option sack_block_list sack_block -%type function_name +%type opt_fuzz_options fuzz_option_list +%type fuzz_option +%type fuzz_type +%type header_type +%type field_name +%type function_name +%type fuzz_value %type expression_list function_arguments %type expression binary_expression array sub_expr_list %type any_int decimal_integer hex_integer @@ -784,7 +831,7 @@ packet_spec ; tcp_packet_spec -: packet_prefix opt_ip_info opt_port_info flags seq opt_ack opt_window opt_urg_ptr opt_tcp_options { +: packet_prefix opt_ip_info opt_port_info flags seq opt_ack opt_window opt_urg_ptr opt_tcp_options opt_fuzz_options { char *error = NULL; struct packet *outer = $1, *inner = NULL; enum direction_t direction = outer->direction; @@ -800,12 +847,15 @@ tcp_packet_spec "outbound packets"); } + //TODO: verify fuzz options is added to only incoming packets. + inner = new_tcp_packet(in_config->wire_protocol, direction, $2, $3.src_port, $3.dst_port, $4, $5.start_sequence, $5.payload_bytes, - $6, $7, $8, $9, &error); + $6, $7, $8, $9, $10, &error); free($4); free($9); + free($10); if (inner == NULL) { assert(error != NULL); semantic_error(error); @@ -817,7 +867,7 @@ tcp_packet_spec ; udp_packet_spec -: packet_prefix opt_ip_info UDP opt_port_info '(' INTEGER ')' { +: packet_prefix opt_ip_info UDP opt_port_info '(' INTEGER ')' opt_fuzz_options { char *error = NULL; struct packet *outer = $1, *inner = NULL; enum direction_t direction = outer->direction; @@ -831,13 +881,17 @@ udp_packet_spec } inner = new_udp_packet(in_config->wire_protocol, direction, $2, - $6, $4.src_port, $4.dst_port, &error); + $6, $4.src_port, $4.dst_port, $8, &error); + + free($8); + if (inner == NULL) { assert(error != NULL); semantic_error(error); free(error); } + $$ = packet_encapsulate_and_free(outer, inner); } ; @@ -1365,6 +1419,210 @@ sack_block } ; +opt_fuzz_options +: { $$ = NULL; } +| '{' fuzz_option_list '}' { $$ = $2; } +; + +fuzz_option_list +: fuzz_option { + $$ = fuzz_options_new(); + if (fuzz_options_append($$, $1)) { + semantic_error("Error adding to fuzz option list."); + } +} +| fuzz_option_list ';' fuzz_option { + $$ = $1; + if (fuzz_options_append($$, $3)) { + semantic_error("Error adding to fuzz option list."); + } +} +; + +fuzz_option +: fuzz_type header_type fuzz_field fuzz_value { + $$ = fuzz_option_new($1, $2, $3, $4); +} +; + +fuzz_type +: REPLACE { + $$ = OP_REPLACE; +} +| TRUNCATE { + $$ = OP_TRUNCATE; +} +| INSERT { + $$ = OP_INSERT; +} +; + +header_type +: IPV4 { + $$ = IPv4; +} +| IPV6 { + $$ = IPv6; +} +| TCP { + $$ = xTCP; +} +| UDP { + $$ = xUDP; +} +; + +fuzz_field +: field_name { + $$ = $1; +} +| field_offset { + $$ = $1; +} + +field_name +: SRC_PORT { + $$ = F_SRC_PORT; +} +| DST_PORT { + $$ = F_DST_PORT; +} +| SEQ_NUM { + $$ = F_SEQ_NUM; +} +| ACK_NUM { + $$ = F_ACK_NUM; +} +| DATA_OFF { + $$ = F_DATA_OFF; +} +| RESERVED { + $$ = F_RESERVED; +} +| FLAGS { + $$ = F_FLAGS; +} +| CWR_FLAG { + $$ = F_CWR_FLAG; +} +| ECE_FLAG { + $$ = F_ECE_FLAG; +} +| URG_FLAG { + $$ = F_URG_FLAG; +} +| ACK_FLAG { + $$ = F_ACK_FLAG; +} +| PSH_FLAG { + $$ = F_PSH_FLAG; +} +| RST_FLAG { + $$ = F_RST_FLAG; +} +| SYN_FLAG { + $$ = F_SYN_FLAG; +} +| FIN_FLAG { + $$ = F_FIN_FLAG; +} +| WIN_SIZE { + $$ = F_WIN_SIZE; +} +| CHECKSUM { + $$ = F_CHECKSUM; +} +| URG_POINTER { + $$ = F_URG_POINTER; +} +| UDP_LEN { + $$ = F_UDP_LEN; +} +| VERSION { + $$ = F_VERSION; +} +| IHL { + $$ = F_IHL; +} +| DSCP { + $$ = F_DSCP; +} +| ECN { + $$ = F_ECN; +} +| TOT_LEN { + $$ = F_TOT_LEN; +} +| IDEN { + $$ = F_IDEN; +} +| RSV_FLAG { + $$ = F_RSV_FLAG; +} +| DF_FLAG { + $$ = F_DF_FLAG; +} +| MF_FLAG { + $$ = F_MF_FLAG; +} +| FRAG_OFF { + $$ = F_FRAG_OFF; +} +| TTL { + $$ = F_TTL; +} +| PROTOCOL { + $$ = F_PROTOCOL; +} +| SRC_ADDR { + $$ = F_SRC_ADDR; +} +| DST_ADDR { + $$ = F_DST_ADDR; +} +| TRF_CLASS { + $$ = F_TRF_CLASS; +} +| FLOW_LABEL { + $$ = F_FLOW_LABEL; +} +| PYLD_LEN { + $$ = F_PYLD_LEN; +} +| NEXT_HEADER { + $$ = F_NEXT_HEADER; +} +| HOP_LIMIT { + $$ = F_HOP_LIMIT; +} +; + +field_offset +: INTEGER { + char *field_offset = strdup(yytext); + $$ = atoi(field_offset); +} +; + +fuzz_value +: INTEGER { + struct fuzz_value_t value = {strdup(yytext), 0}; + $$ = value; +} +| HEX_INTEGER { + char *error = NULL; + char *hex_string = strdup(yytext); + hex_string += 2; /* We remove the '0x' at the start of the hex */ + struct fuzz_value_t *value = parse_hex_blob(hex_string, &error); + if (value == NULL) { + assert(error != NULL); + semantic_error(error); + free(error); + } + $$ = *value; +} +; + syscall_spec : opt_end_time function_name function_arguments '=' expression opt_errno opt_note { diff --git a/gtests/net/packetdrill/run.c b/gtests/net/packetdrill/run.c index 37ec449e..72bfe3c2 100644 --- a/gtests/net/packetdrill/run.c +++ b/gtests/net/packetdrill/run.c @@ -49,6 +49,8 @@ #include "system.h" #include "tcp.h" #include "tcp_options.h" +#include "fm_testing.h" +#include "fuzz_testing.h" /* MAX_SPIN_USECS is the maximum amount of time (in microseconds) to * spin waiting for an event. We sleep up until this many microseconds @@ -119,6 +121,57 @@ static void close_all_fds(struct state *state) } } +int perform_termination_handshake(struct state *state, struct socket *socket) { + int sendResult = send_test_complete_signal(state, socket); + + if (sendResult == 0) { + struct packet *live_packet = NULL; + char *error = NULL; + + struct tuple packet_tuple, live_outbound; + + while (true) { + + if (netdev_receive(state->netdev, &live_packet, &error)) + return 1; + + get_packet_tuple(live_packet, &packet_tuple); + socket_get_outbound(&socket->live, &live_outbound); + + if (!is_equal_ip(&packet_tuple.dst.ip, &live_outbound.dst.ip) || + !is_equal_ip(&packet_tuple.src.ip, &live_outbound.src.ip)) { + + printf("Not expected outbound packet\n"); + packet_free(live_packet); + live_packet = NULL; + continue; + } + + uint8_t termination_offset = live_packet->ipv4 != NULL ? 28 : 48; + uint8_t termination_payload[5] = {0x11, 0x22, 0x11, 0x33, 0x44}; + + uint8_t comparisonBytes[5]; + memcpy(comparisonBytes, packet_start(live_packet) + termination_offset, sizeof(termination_payload)); // Get destination port field + + if (memcmp(comparisonBytes, termination_payload, 5) == 0) { + // The extracted bytes are equal + printf("Termination handshake successful\n"); + break; + } else { + printf("Termination handshake failed\n"); + } + + if (live_packet != NULL) { + packet_free(live_packet); + live_packet = NULL; + } + } + + } + + return sendResult; +} + void state_free(struct state *state) { /* We have to stop the system call thread first, since it's using @@ -126,12 +179,24 @@ void state_free(struct state *state) */ syscalls_free(state, state->syscalls); + /* We make a copy of the socket under test */ + struct socket *sut = malloc(sizeof(struct socket)); + memcpy(sut, state->socket_under_test, sizeof(struct socket)); + /* Then we close the sockets and reset the connections, while * we still have a netdev for injecting reset packets to free * per-connection kernel state. */ close_all_fds(state); + /* We perform the termination handshake */ + if (perform_termination_handshake(state, sut) != 0) { + printf("Termination handshake failed\n"); + } + + /* We free the socket under test */ + free(sut); + netdev_free(state->netdev); packets_free(state->packets); code_free(state->code); @@ -142,6 +207,9 @@ void state_free(struct state *state) if (state->so_instance) so_instance_free(state->so_instance); + if (state->fm_instance) + fm_instance_free(state->fm_instance); + run_unlock(state); if (pthread_mutex_destroy(&state->mutex) != 0) die_perror("pthread_mutex_destroy"); @@ -430,7 +498,9 @@ static void run_local_packet_event(struct state *state, struct event *event, fprintf(stderr, "%s", error); free(error); } else if (result == STATUS_ERR) { - die("%s", error); + + die_free_so(state, "%s", error); + } } @@ -581,6 +651,11 @@ void run_script(struct config *config, struct script *script) so_instance_init(state->so_instance, config, script, state); } + if (config->fm_filename) { + state->fm_instance = fm_instance_new(); + fm_instance_init(state->fm_instance, config); + } + init_cmd_exed = false; if (script->init_command != NULL) { if (safe_system(script->init_command->command_line, diff --git a/gtests/net/packetdrill/run.h b/gtests/net/packetdrill/run.h index da564547..4bb7b569 100644 --- a/gtests/net/packetdrill/run.h +++ b/gtests/net/packetdrill/run.h @@ -102,6 +102,7 @@ struct state { struct code_state *code; /* for running post-processing code */ struct wire_client *wire_client; /* for on-the-wire tests */ struct so_instance *so_instance; /* for SO testing */ + struct fm_instance *fm_instance; /* for fuzz mutations */ s64 script_start_time_usecs; /* time of first event in script */ s64 script_last_time_usecs; /* time of previous event in script */ s64 live_start_time_usecs; /* time of first event in live test */ diff --git a/gtests/net/packetdrill/run_command.c b/gtests/net/packetdrill/run_command.c index a55e596d..71d39ade 100644 --- a/gtests/net/packetdrill/run_command.c +++ b/gtests/net/packetdrill/run_command.c @@ -48,7 +48,7 @@ void run_command_event( return; error_out: - die("%s:%d: error executing `%s` command: %s\n", + die_free_so(state, "%s:%d: error executing `%s` command: %s\n", state->config->script_path, event->line_number, command->command_line, error); free(error); diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 7ca353ab..b092d591 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -44,7 +44,10 @@ #include "tcp_options_iterator.h" #include "tcp_options_to_string.h" #include "tcp_packet.h" +#include "udp_packet.h" #include "wrap.h" +#include "fm_testing.h" +#include "fuzz_testing.h" /* To avoid issues with TIME_WAIT, FIN_WAIT1, and FIN_WAIT2 we use * dynamically-chosen, unique 4-tuples for each test. We implement the @@ -112,7 +115,7 @@ static void verbose_packet_dump(struct state *state, const char *type, if (state->config->verbose) { char *dump = NULL, *dump_error = NULL; - packet_to_string(live_packet, DUMP_SHORT, + packet_to_string(live_packet, DUMP_VERBOSE, &dump, &dump_error); printf("%s packet: %9.6f %s%s%s\n", @@ -127,10 +130,12 @@ static void verbose_packet_dump(struct state *state, const char *type, /* See if the live packet matches the live 4-tuple of the socket (UDP/TCP) * or matches the src/dst IP addr for the ICMP socket + * + * With compare_ip, we allow matching a socket with only matching ip address */ static struct socket *find_socket_for_live_packet( struct state *state, const struct packet *packet, - enum direction_t *direction) + enum direction_t *direction, uint8_t compare_ip) { struct socket *socket = state->socket_under_test; /* shortcut */ if (socket == NULL) @@ -138,7 +143,7 @@ static struct socket *find_socket_for_live_packet( struct tuple packet_tuple, live_outbound, live_inbound; bool is_icmp = (socket->protocol == IPPROTO_ICMP && packet->icmpv4) || - (socket->protocol == IPPROTO_ICMPV6 && packet->icmpv6); + (socket->protocol == IPPROTO_ICMPV6 && packet->icmpv6) || compare_ip; get_packet_tuple(packet, &packet_tuple); /* Is packet inbound to the socket under test? */ @@ -1407,10 +1412,10 @@ static int verify_outbound_live_packet( out: add_packet_dump(error, "script", script_packet, script_usecs, - DUMP_SHORT); + DUMP_VERBOSE); if (actual_packet != NULL) { add_packet_dump(error, "actual", actual_packet, actual_usecs, - DUMP_SHORT); + DUMP_VERBOSE); packet_free(actual_packet); } if (result == STATUS_ERR && @@ -1459,9 +1464,9 @@ static int verify_packet_fragments( } /* Sniff the next outbound live packet and return it. */ -static int sniff_outbound_live_packet( +int sniff_outbound_live_packet( struct state *state, struct socket *expected_socket, - struct packet **packet, char **error) + struct packet **packet, char **error, uint8_t compare_ip) { DEBUGP("sniff_outbound_live_packet\n"); struct socket *socket = NULL; @@ -1472,7 +1477,7 @@ static int sniff_outbound_live_packet( return STATUS_ERR; /* See if the packet matches an existing, known socket. */ socket = find_socket_for_live_packet(state, *packet, - &direction); + &direction, compare_ip); if ((socket != NULL) && (direction == DIRECTION_OUTBOUND)) break; /* See if the packet matches a recent connect() call. */ @@ -1607,7 +1612,7 @@ static int do_outbound_script_packet( * socket. */ if (sniff_outbound_live_packet(state, socket, &live_packet, - error)) + error, 0)) goto out; live_payload = packet_payload_len(live_packet); DEBUGP("Sniffed packet with payload %d bytes\n", live_payload); @@ -1749,15 +1754,23 @@ static int do_outbound_script_packet( } /* Checksum the packet and inject it into the kernel under test. */ -static int send_live_ip_packet(struct netdev *netdev, +static int send_live_ip_packet(struct state *state, struct packet *packet) { + + struct netdev *netdev = state->netdev; + assert(packet->ip_bytes > 0); /* We do IPv4 and IPv6 */ assert(packet->ipv4 || packet->ipv6); /* We only do TCP, UDP, and ICMP */ assert(packet->tcp || packet->udp || packet->icmpv4 || packet->icmpv6); + /* Mutate packet if enabled */ + if (state->fm_instance) { + packet = handle_packet_mutation(packet, state->fm_instance); + } + /* Fill in layer 3 and layer 4 checksums */ checksum_packet(packet); @@ -1817,7 +1830,7 @@ static int do_inbound_script_packet( } /* Inject live packet into kernel. */ - result = send_live_ip_packet(state->netdev, live_packet); + result = send_live_ip_packet(state, live_packet); out: packet_free(live_packet); @@ -1919,7 +1932,7 @@ int reset_connection(struct state *state, struct socket *socket) packet = new_tcp_packet(socket->address_family, DIRECTION_INBOUND, ip_info, 0, 0, - "R.", seq, 0, ack_seq, window, 0, NULL, + "R.", seq, 0, ack_seq, window, 0, NULL, NULL, &error); if (packet == NULL) die("%s", error); @@ -1929,13 +1942,51 @@ int reset_connection(struct state *state, struct socket *socket) set_packet_tuple(packet, &live_inbound); /* Inject live packet into kernel. */ - result = send_live_ip_packet(state->netdev, packet); + result = send_live_ip_packet(state, packet); packet_free(packet); return result; } + +int send_test_complete_signal(struct state *state, struct socket *socket) +{ + char *error = NULL; + struct packet *packet = NULL; + struct tuple live_inbound; + const struct ip_info ip_info = {{TOS_CHECK_NONE, 0}, 0}; + int result = 0; + + /* Create UDP packet targeted at port 5700*/ + packet = new_udp_packet(socket->address_family, + DIRECTION_INBOUND, ip_info, 5, 0, + 0, NULL, &error); + + + if (packet == NULL) + die("%s", error); + + /* Rewrite addresses and port to match inbound live traffic. */ + socket_get_inbound(&socket->live, &live_inbound); + + uint8_t termination_offset = packet->ipv4 != NULL ? 28 : 48; + uint8_t termination_payload[5] = {0x11, 0x22, 0x11, 0x33, 0x44}; + + /* We update the destination port to 5700, which informs the target the test is completed */ + memcpy(packet->buffer + termination_offset, termination_payload, sizeof(termination_payload)); + + set_packet_tuple(packet, &live_inbound); + + /* Inject live packet into kernel. */ + result = send_live_ip_packet(state, packet); + + packet_free(packet); + + return result; +} + + struct packets *packets_new(const struct state *state) { struct packets *packets = calloc(1, sizeof(struct packets)); diff --git a/gtests/net/packetdrill/run_packet.h b/gtests/net/packetdrill/run_packet.h index b4220343..242eda05 100644 --- a/gtests/net/packetdrill/run_packet.h +++ b/gtests/net/packetdrill/run_packet.h @@ -58,4 +58,12 @@ extern int run_packet_event(struct state *state, extern int reset_connection(struct state *state, struct socket *socket); +/* Send a custom packet to tell the target this test has ended, and wait for response from target */ +extern int send_test_complete_signal(struct state *state, + struct socket *socket); + +extern int sniff_outbound_live_packet( + struct state *state, struct socket *expected_socket, + struct packet **packet, char **error, uint8_t compare_ip); + #endif /* __RUN_PACKET_H__ */ diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c index c35ea135..0997c07c 100644 --- a/gtests/net/packetdrill/run_system_call.c +++ b/gtests/net/packetdrill/run_system_call.c @@ -1355,6 +1355,7 @@ static int run_syscall_socket(struct state *state, int address_family, int type, socket->protocol = protocol; socket->fd.script_fd = script_fd; socket->fd.live_fd = live_fd; + socket->fd.so_managed = state->so_instance != NULL; /* Any later packets in the test script will now be mapped here. */ state->socket_under_test = socket; @@ -1472,6 +1473,8 @@ static int run_syscall_accept(struct state *state, htons(port))); socket->fd.script_fd = script_accepted_fd; socket->fd.live_fd = live_accepted_fd; + socket->fd.so_managed = state->so_instance != NULL; + return STATUS_OK; } } @@ -3336,7 +3339,7 @@ static void invoke_system_call( return; error_out: - die("%s:%d: runtime error in %s call: %s\n", + die_free_so(state, "%s:%d: runtime error in %s call: %s\n", state->config->script_path, event->line_number, syscall->name, error); free(error); @@ -3434,7 +3437,7 @@ static void enqueue_system_call( return; error_out: - die("%s:%d: runtime error in %s call: %s\n", + die_free_so(state, "%s:%d: runtime error in %s call: %s\n", state->config->script_path, event->line_number, syscall->name, error); free(error); @@ -3571,7 +3574,7 @@ void syscalls_free(struct state *state, struct syscalls *syscalls) { /* Wait a bit for the thread to go idle. */ if (await_idle_thread(state)) { - die("%s:%d: runtime error: exiting while " + die_free_so(state, "%s:%d: runtime error: exiting while " "a blocking system call is in progress\n", state->config->script_path, syscalls->event->line_number); diff --git a/gtests/net/packetdrill/so_testing.c b/gtests/net/packetdrill/so_testing.c index ee4b645f..28200922 100644 --- a/gtests/net/packetdrill/so_testing.c +++ b/gtests/net/packetdrill/so_testing.c @@ -61,7 +61,7 @@ static int so_netdev_send(struct netdev *a_netdev, struct packet *packet) assert(packet->tcp || packet->udp || packet->icmpv4 || packet->icmpv6); return netdev->ifc->netdev_send(netdev->ifc->userdata, - packet_start(packet), + packet->buffer, packet->ip_bytes); } diff --git a/gtests/net/packetdrill/socket.c b/gtests/net/packetdrill/socket.c index c661c5af..4eccbe85 100644 --- a/gtests/net/packetdrill/socket.c +++ b/gtests/net/packetdrill/socket.c @@ -27,12 +27,16 @@ #include #include #include "run.h" +#include "fuzz_testing.h" + void socket_close(struct state *state, struct fd_state *fd) { struct socket *socket = fd_to_socket(fd); - if (fd->live_fd >= 0 && !socket->fd.is_closed) { + + if (fd->live_fd >= 0 && !socket->fd.is_closed && !fd->so_managed) { + /* Let the so_instance be in charge of closing sockets it created as these sockets may not have been on linux */ assert(fd->script_fd >= 0); DEBUGP("closing struct state socket " "live.fd:%d script.fd:%d\n", @@ -48,6 +52,7 @@ void socket_close(struct state *state, struct fd_state *fd) die("error reseting connection\n"); } + socket_free(socket); } diff --git a/gtests/net/packetdrill/tcp_packet.c b/gtests/net/packetdrill/tcp_packet.c index d1ef65b5..f22488c5 100644 --- a/gtests/net/packetdrill/tcp_packet.c +++ b/gtests/net/packetdrill/tcp_packet.c @@ -100,6 +100,7 @@ struct packet *new_tcp_packet(int address_family, s32 window, u16 urg_ptr, const struct tcp_options *tcp_options, + const struct fuzz_options *fuzz_options, char **error) { struct packet *packet = NULL; /* the newly-allocated result packet */ @@ -217,5 +218,11 @@ struct packet *new_tcp_packet(int address_family, } packet->ip_bytes = ip_bytes; + + if (fuzz_options != NULL) { + packet->fuzz_options = malloc(fuzz_options->size); + memcpy(packet->fuzz_options, fuzz_options, fuzz_options->size); + } + return packet; } diff --git a/gtests/net/packetdrill/tcp_packet.h b/gtests/net/packetdrill/tcp_packet.h index 6e2a6b08..4fa41466 100644 --- a/gtests/net/packetdrill/tcp_packet.h +++ b/gtests/net/packetdrill/tcp_packet.h @@ -29,6 +29,7 @@ #include "packet.h" #include "tcp_options.h" +#include "fuzz_options.h" /* Create and initialize a new struct packet containing a TCP segment. * The 'flags' are a tcpdump-style sequence of TCP header flags. @@ -47,5 +48,6 @@ extern struct packet *new_tcp_packet(int address_family, s32 window, u16 urg_ptr, const struct tcp_options *tcp_options, + const struct fuzz_options *fuzz_options, char **error); #endif /* __TCP_PACKET_H__ */ diff --git a/gtests/net/packetdrill/udp_packet.c b/gtests/net/packetdrill/udp_packet.c index 81c91873..565c46f7 100644 --- a/gtests/net/packetdrill/udp_packet.c +++ b/gtests/net/packetdrill/udp_packet.c @@ -33,6 +33,7 @@ struct packet *new_udp_packet(int address_family, u16 udp_payload_bytes, u16 src_port, u16 dst_port, + const struct fuzz_options *fuzz_options, char **error) { struct packet *packet = NULL; /* the newly-allocated result packet */ @@ -87,5 +88,11 @@ struct packet *new_udp_packet(int address_family, packet->udp->check = 0; packet->ip_bytes = ip_bytes; + + if (fuzz_options != NULL) { + // TODO: This is a memory leak. We need to free the fuzz_options struct later + packet->fuzz_options = malloc(fuzz_options->size); + memcpy(packet->fuzz_options, fuzz_options, fuzz_options->size); + } return packet; } diff --git a/gtests/net/packetdrill/udp_packet.h b/gtests/net/packetdrill/udp_packet.h index 9ace4403..64be78a3 100644 --- a/gtests/net/packetdrill/udp_packet.h +++ b/gtests/net/packetdrill/udp_packet.h @@ -28,6 +28,7 @@ #include "types.h" #include "packet.h" +#include "fuzz_options.h" /* Create and initialize a new struct packet containing a UDP segment. * The 'flags' are a tcpdump-style sequence of UDP header flags. @@ -40,5 +41,6 @@ extern struct packet *new_udp_packet(int address_family, u16 udp_payload_bytes, u16 src_port, u16 dst_port, + const struct fuzz_options *fuzz_options, char **error); #endif /* __UDP_PACKET_H__ */ diff --git a/gtests/net/tcp/blocking/blocking-accept.pkt b/gtests/net/tcp/blocking/blocking-accept.pkt index 8d23a76f..95c5bc24 100644 --- a/gtests/net/tcp/blocking/blocking-accept.pkt +++ b/gtests/net/tcp/blocking/blocking-accept.pkt @@ -1,6 +1,6 @@ // Test for blocking accept. -`../common/defaults.sh` +//`../common/defaults.sh` // Establish a connection. 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 @@ -13,5 +13,4 @@ +0 > S. 0:0(0) ack 1 +.1 < . 1:1(0) ack 1 win 257 - +.1 write(4, ..., 2000) = 2000 - +0 > P. 1:2001(2000) ack 1 + \ No newline at end of file diff --git a/gtests/net/tcp/blocking/blocking-connect-data.pkt b/gtests/net/tcp/blocking/blocking-connect-data.pkt new file mode 100644 index 00000000..992d0e40 --- /dev/null +++ b/gtests/net/tcp/blocking/blocking-connect-data.pkt @@ -0,0 +1,24 @@ +// Test for blocking connect. + +//`../common/defaults.sh` + +// Establish a connection. + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + + +.1...0.200 connect(3, ..., ...) = 0 + + +0 > S 0:0(0) + +.1 < S. 0:0(0) ack 1 win 5792 + +0 > . 1:1(0) ack 1 + + +0 < . 1:101(100) ack 1 win 27510 + +0 < . 101:201(100) ack 1 win 27510 + +0 < . 201:401(200) ack 1 win 27510 + +0 < . 401:501(100) ack 1 win 27510 + +0 < . 501:1001(500) ack 1 win 27510 + +0 < . 1001:2001(1000) ack 1 win 27510 + +0 < . 2001:5001(3000) ack 1 win 27510 + +0 < . 1:10001(10000) ack 1 win 27510 + +0 < . 10001:30001(20000) ack 1 win 27510 + +0 < . 1:101(100) ack 1 win 27510 + diff --git a/gtests/net/tcp/blocking/blocking-connect.pkt b/gtests/net/tcp/blocking/blocking-connect.pkt index 602cca56..b67c1b47 100644 --- a/gtests/net/tcp/blocking/blocking-connect.pkt +++ b/gtests/net/tcp/blocking/blocking-connect.pkt @@ -1,6 +1,6 @@ // Test for blocking connect. -`../common/defaults.sh` +//`../common/defaults.sh` // Establish a connection. 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 diff --git a/gtests/net/tcp/blocking/blocking-read.pkt b/gtests/net/tcp/blocking/blocking-read.pkt index 47a2df06..32837fe9 100644 --- a/gtests/net/tcp/blocking/blocking-read.pkt +++ b/gtests/net/tcp/blocking/blocking-read.pkt @@ -1,7 +1,7 @@ // Test for blocking read. ---tolerance_usecs=10000 +//--tolerance_usecs=10000 -`../common/defaults.sh` +//`../common/defaults.sh` // Establish a connection. 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 diff --git a/gtests/net/tcp/close/close-remote-fin-then-close.pkt b/gtests/net/tcp/close/close-remote-fin-then-close.pkt index de5b1c72..45dd4a3a 100644 --- a/gtests/net/tcp/close/close-remote-fin-then-close.pkt +++ b/gtests/net/tcp/close/close-remote-fin-then-close.pkt @@ -1,7 +1,7 @@ // Verify behavior for the sequence: remote side sends FIN, then we close(). // Since the remote side (client) closes first, we test our LAST_ACK code path. -`../common/defaults.sh` +//`../common/defaults.sh` // Initialize a server socket. 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 @@ -20,7 +20,7 @@ +0 > . 1:1(0) ack 2 // App notices that client closed. - +0 read(4, ..., 1000) = 0 +// +0 read(4, ..., 1000) = 0 // Then we close. +.01 close(4) = 0 diff --git a/gtests/net/tcp/limited_transmit/limited-transmit-sack.pkt b/gtests/net/tcp/limited_transmit/limited-transmit-sack.pkt index 4b135231..b971ad65 100644 --- a/gtests/net/tcp/limited_transmit/limited-transmit-sack.pkt +++ b/gtests/net/tcp/limited_transmit/limited-transmit-sack.pkt @@ -3,7 +3,7 @@ // arrive at the sender". // This variation tests a receiver that supports SACK. -`../common/defaults.sh` +//`../common/defaults.sh` // Establish a connection. 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 diff --git a/gtests/net/tcp/md5/md5-only-on-client-ack.pkt b/gtests/net/tcp/md5/md5-only-on-client-ack.pkt index 1fb838c9..4240f20c 100644 --- a/gtests/net/tcp/md5/md5-only-on-client-ack.pkt +++ b/gtests/net/tcp/md5/md5-only-on-client-ack.pkt @@ -1,7 +1,7 @@ // Test what happens when client does not provide MD5 on SYN, // but then does on the ACK that completes the three-way handshake. -`../../common/defaults.sh` +//`../../common/defaults.sh` // Establish a connection. 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 @@ -16,7 +16,7 @@ +.01 < . 1:1(0) ack 1 win 514 // The TCP listener refcount should be 2, but on buggy kernels it can be 0: - +0 `grep " 0A " /proc/net/tcp /proc/net/tcp6 | grep ":1F90"` +// +0 `grep " 0A " /proc/net/tcp /proc/net/tcp6 | grep ":1F90"` // Now here comes the legit ACK: +.01 < . 1:1(0) ack 1 win 514 diff --git a/gtests/net/udp/blocking-write.pkt b/gtests/net/udp/blocking-write.pkt new file mode 100644 index 00000000..69153939 --- /dev/null +++ b/gtests/net/udp/blocking-write.pkt @@ -0,0 +1,20 @@ +// Test for blocking write. +//--tolerance_usecs=10000 + +//`../common/defaults.sh +//` + +// Establish a connection. + 0 socket(..., SOCK_DGRAM, IPPROTO_UDP) = 3 + +0 bind(3, ..., ...) = 0 + +// +0 sendto(3, ..., 20, MSG_CONFIRM, ..., ...) = 20 + +// +0 > udp (20) + + +0 < udp (0) + +// +0 recvfrom(3, ..., 1024, MSG_WAITALL, ..., ...) = 0 + + +