diff options
Diffstat (limited to 'test/packetdrill/wire_client.c')
-rw-r--r-- | test/packetdrill/wire_client.c | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/test/packetdrill/wire_client.c b/test/packetdrill/wire_client.c new file mode 100644 index 0000000..33ebbd6 --- /dev/null +++ b/test/packetdrill/wire_client.c @@ -0,0 +1,302 @@ +/* + * 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: ncardwell@google.com (Neal Cardwell) + * + * Client-side code for remote on-the-wire testing using a real NIC. + */ + +#include "wire_client.h" + +#include "config.h" +#include "link_layer.h" +#include "script.h" +#include "run.h" + +struct wire_client *wire_client_new(void) +{ + return calloc(1, sizeof(struct wire_client)); +} + +void wire_client_free(struct wire_client *wire_client) +{ + if (wire_client->wire_conn != NULL) + wire_conn_free(wire_client->wire_conn); + + memset(wire_client, 0, sizeof(*wire_client)); /* help catch bugs */ + free(wire_client); +} + +static void wire_client_die(struct wire_client *wire_client, + const char *message) +{ + die("error in TCP connection to wire server: %s\n", message); +} + +/* Serialize client-side argv into a single string with '\0' + * characters between args. We do not send the -wire_client argument, + * since we don't want to give the server an identity crisis. + */ +static void wire_client_serialize_argv(const char **argv, char **args_ptr, + int *args_len_ptr) +{ + int i; + char *args = NULL; + int args_len = 0; + char *end = NULL; + + for (i = 0; argv[i]; ++i) { + if (strstr(argv[i], "-wire_client")) + continue; + args_len += strlen(argv[i]) + 1; /* + 1 for '\0' */ + } + + args = calloc(args_len, 1); + end = args; + + for (i = 0; argv[i]; ++i) { + int len = 0; + if (strstr(argv[i], "-wire_client")) + continue; + len = strlen(argv[i]) + 1; /* + 1 for '\0' */ + memcpy(end, argv[i], len); + end += len; + } + + assert(end == args + args_len); + + *args_ptr = args; + *args_len_ptr = args_len; +} + +/* Send a WIRE_COMMAND_LINE_ARGS message with our command line + * arguments as a single serialized string. + */ +static void wire_client_send_args(struct wire_client *wire_client, + const struct config *config) +{ + char *args = NULL; + int args_len = 0; + + wire_client_serialize_argv(config->argv, &args, &args_len); + + if (wire_conn_write(wire_client->wire_conn, + WIRE_COMMAND_LINE_ARGS, + args, args_len)) + wire_client_die(wire_client, + "error sending WIRE_COMMAND_LINE_ARGS"); + free(args); +} + +/* Send the path name of the script we're about to run. */ +static void wire_client_send_script_path(struct wire_client *wire_client, + const struct config *config) +{ + if (wire_conn_write(wire_client->wire_conn, + WIRE_SCRIPT_PATH, + config->script_path, + strlen(config->script_path))) + wire_client_die(wire_client, + "error sending WIRE_SCRIPT_PATH"); +} + +/* Send the ASCII contents of the script we're about to run. */ +static void wire_client_send_script(struct wire_client *wire_client, + const struct script *script) +{ + if (wire_conn_write(wire_client->wire_conn, + WIRE_SCRIPT, + script->buffer, script->length)) + wire_client_die(wire_client, + "error sending WIRE_SCRIPT"); +} + +/* Send the ethernet address to which the server should send packets. */ +static void wire_client_send_hw_address(struct wire_client *wire_client, + const struct config *config) +{ + if (wire_conn_write(wire_client->wire_conn, + WIRE_HARDWARE_ADDR, + &wire_client->client_ether_addr, + sizeof(wire_client->client_ether_addr))) + wire_client_die(wire_client, + "error sending WIRE_HARDWARE_ADDR"); +} + +/* Receive server's message that the server is ready to execute the script. */ +static void wire_client_receive_server_ready(struct wire_client *wire_client) +{ + enum wire_op_t op = WIRE_INVALID; + void *buf = NULL; + int buf_len = -1; + + if (wire_conn_read(wire_client->wire_conn, + &op, &buf, &buf_len)) + wire_client_die(wire_client, "error reading WIRE_SERVER_READY"); + if (op != WIRE_SERVER_READY) { + wire_client_die(wire_client, + "bad wire server: expected WIRE_SERVER_READY"); + } + if (buf_len != 0) { + wire_client_die(wire_client, + "bad wire server: bad WIRE_SERVER_READY len"); + } +} + +/* Tell server that client is starting script execution. */ +void wire_client_send_client_starting(struct wire_client *wire_client) +{ + if (wire_conn_write(wire_client->wire_conn, + WIRE_CLIENT_STARTING, + NULL, 0)) + wire_client_die(wire_client, + "error sending WIRE_CLIENT_STARTING"); +} + +/* Send a client request for the server to execute some packet events. */ +static void wire_client_send_packets_start(struct wire_client *wire_client) +{ + struct wire_packets_start start; + start.num_events = htonl(wire_client->num_events); + if (wire_conn_write(wire_client->wire_conn, + WIRE_PACKETS_START, + &start, sizeof(start))) + wire_client_die(wire_client, + "error sending WIRE_PACKETS_START"); +} + +/* Receive a message from the server that the server is done executing + * some packet events. Print any warnings we receive along the way. + */ +static void wire_client_receive_packets_done(struct wire_client *wire_client) +{ + enum wire_op_t op; + struct wire_packets_done done; + void *buf = NULL; + int buf_len = -1; + + DEBUGP("wire_client_receive_packets_done\n"); + + while (1) { + if (wire_conn_read(wire_client->wire_conn, + &op, &buf, &buf_len)) + wire_client_die(wire_client, "error reading"); + if (op == WIRE_PACKETS_DONE) + break; + else if (op == WIRE_PACKETS_WARN) { + /* NULL-terminate the warning and print it. */ + char *warning = strndup(buf, buf_len); + fprintf(stderr, "%s", warning); + free(warning); + } else { + wire_client_die( + wire_client, + "bad wire server: expected " + "WIRE_PACKETS_DONE or WIRE_PACKETS_WARN"); + } + } + + if (buf_len < sizeof(done) + 1) { + wire_client_die(wire_client, + "bad wire server: bad WIRE_PACKETS_DONE len"); + } + if (((char *)buf)[buf_len - 1] != '\0') { + wire_client_die(wire_client, + "bad wire server: missing string terminator"); + } + + memcpy(&done, buf, sizeof(done)); + + if (ntohl(done.result) == STATUS_ERR) { + /* Die with the error message from the server, which + * is a C string following the fixed "done" message. + */ + die("%s", (char *)(buf + sizeof(done))); + } else if (ntohl(done.num_events) != wire_client->num_events) { + char *msg = NULL; + asprintf(&msg, "bad wire server: bad message count: " + "got: %d vs expected: %d", + ntohl(done.num_events), wire_client->num_events); + wire_client_die(wire_client, msg); + } +} + +/* Connect to the wire server, pass it our command line argument + * options, the script we're going to execute, and our MAC address. + */ +int wire_client_init(struct wire_client *wire_client, + const struct config *config, + const struct script *script) +{ + DEBUGP("wire_client_init\n"); + assert(config->is_wire_client); + + get_hw_address(config->wire_client_device, + &wire_client->client_ether_addr, + config->ip_version); + + wire_client->wire_conn = wire_conn_new(); + wire_conn_connect(wire_client->wire_conn, + &config->wire_server_ip, + config->wire_server_port, + config->ip_version); + + wire_client_send_args(wire_client, config); + + wire_client_send_script_path(wire_client, config); + + wire_client_send_script(wire_client, script); + + wire_client_send_hw_address(wire_client, config); + + wire_client_receive_server_ready(wire_client); + + return STATUS_OK; +} + + +/* Tell the wire client that the interpreter has moved on to the next + * event. Inform the wire server if need be. The client informs the + * server if (a) this event is a packet event and (b) the previous + * event was not a packet event. In any other cases the server either + * (i) does not care what time this event is happening at because it's + * not an on-the-wire event, or (ii) already knows what time to fire + * this on-the-wire event because the previous event was also an + * on-the-wire event. + */ +void wire_client_next_event(struct wire_client *wire_client, + struct event *event) +{ + /* Tell the server to start executing packet events. */ + if (event && (event->type == PACKET_EVENT) && + (wire_client->last_event_type != PACKET_EVENT)) { + wire_client_send_packets_start(wire_client); + } + + /* Get the result from server execution of one or more packet events. */ + if ((!event || (event->type != PACKET_EVENT)) && + (wire_client->last_event_type == PACKET_EVENT)) { + wire_client_receive_packets_done(wire_client); + } + + if (event) { + wire_client->last_event_type = event->type; + ++wire_client->num_events; + } +} |