/* See if the live packet matches the live 4-tuple of the socket under test. */ static struct socket *find_socket_for_live_packet( struct state *state, const struct packet *packet, enum direction_t *direction) { struct socket *socket = state->socket_under_test; /* shortcut */ if (socket == NULL) return NULL; struct tuple packet_tuple, live_outbound, live_inbound; get_packet_tuple(packet, &packet_tuple); /* Is packet inbound to the socket under test? */ socket_get_inbound(&socket->live, &live_inbound); if (is_equal_tuple(&packet_tuple, &live_inbound)) { *direction = DIRECTION_INBOUND; DEBUGP("inbound live packet, socket in state %d\n", socket->state); return socket; } /* Is packet outbound from the socket under test? */ socket_get_outbound(&socket->live, &live_outbound); if (is_equal_tuple(&packet_tuple, &live_outbound)) { *direction = DIRECTION_OUTBOUND; DEBUGP("outbound live packet, socket in state %d\n", socket->state); return socket; } return NULL; }
/* See if the socket under test is a connecting socket that would emit * this outgoing script SYN. If so, return a pointer to the socket; * otherwise, return NULL. */ static struct socket *handle_connect_for_script_packet( struct state *state, const struct packet *packet, enum direction_t direction) { /* Does this packet match this socket? For now we only support * testing one socket at a time, so we merely check whether * the socket is connecting. (If we were to support testing * more than one socket at a time then we'd want to check to * see if the address tuples in the packet and socket match.) */ struct config *config = state->config; struct socket *socket = state->socket_under_test; /* shortcut */ bool match = ((direction == DIRECTION_OUTBOUND) && packet->tcp->syn && !packet->tcp->ack); if (!match) return NULL; if (config->is_wire_server) { /* On wire servers we don't see the system calls, so * we won't have any socket_under_test yet. */ match = (socket == NULL); } else { /* In local mode we will certainly know about this socket. */ match = ((socket != NULL) && (socket->state == SOCKET_ACTIVE_CONNECTING)); } if (!match) return NULL; if (socket == NULL) { /* Wire server. Create a socket for this outbound SYN * packet. Any further packets in the test script are * mapped here. */ socket = socket_new(state); state->socket_under_test = socket; assert(socket->state == SOCKET_INIT); socket->address_family = packet_address_family(packet); socket->protocol = packet_ip_protocol(packet); socket->script.fd = -1; socket->live.remote.ip = config->live_remote_ip; socket->live.remote.port = htons(config->live_connect_port); socket->live.fd = -1; } /* Fill in the new info about this connection. */ struct tuple tuple; get_packet_tuple(packet, &tuple); socket->state = SOCKET_ACTIVE_SYN_SENT; socket->script.remote = tuple.dst; socket->script.local = tuple.src; socket->script.local_isn = ntohl(packet->tcp->seq); return socket; }
static void endpoints_to_string(FILE *s, const struct packet *packet) { char src_string[ADDR_STR_LEN]; char dst_string[ADDR_STR_LEN]; struct tuple tuple; get_packet_tuple(packet, &tuple); fprintf(s, "%s:%u > %s:%u", ip_to_string(&tuple.src.ip, src_string), ntohs(tuple.src.port), ip_to_string(&tuple.dst.ip, dst_string), ntohs(tuple.dst.port)); }
/* Look for a connecting socket that would emit this outgoing live packet. */ static struct socket *find_connect_for_live_packet( struct state *state, struct packet *packet, enum direction_t *direction) { struct tuple tuple; get_packet_tuple(packet, &tuple); *direction = DIRECTION_INVALID; struct socket *socket = state->socket_under_test; /* shortcut */ if (!socket) return NULL; bool is_udp_match = (packet->udp && (socket->protocol == IPPROTO_UDP) && (socket->state == SOCKET_ACTIVE_CONNECTING)); bool is_tcp_match = (packet->tcp && packet->tcp->syn && !packet->tcp->ack && (socket->protocol == IPPROTO_TCP) && (socket->state == SOCKET_ACTIVE_SYN_SENT)); if (!is_udp_match && !is_tcp_match) return NULL; if (!is_equal_ip(&tuple.dst.ip, &socket->live.remote.ip) || !is_equal_port(tuple.dst.port, socket->live.remote.port)) return NULL; *direction = DIRECTION_OUTBOUND; /* Using the details in this outgoing packet, fill in the * new details we've learned about this actively initiated * connection (for which we've seen a connect() call). */ socket->live.local.ip = tuple.src.ip; socket->live.local.port = tuple.src.port; if (packet->tcp) socket->live.local_isn = ntohl(packet->tcp->seq); return socket; }
/* Transforms values in the 'actual_packet' by mapping outbound packet * values in the sniffed 'live_packet' (address 4-tuple, sequence * number in seq, timestamp value) from live values to script values * in the space of 'script_packet'. This will allow us to compare a * packet sent by the kernel to the packet expected by the script. */ static int map_outbound_live_packet( struct socket *socket, struct packet *live_packet, struct packet *actual_packet, struct packet *script_packet, char **error) { DEBUGP("map_outbound_live_packet\n"); struct tuple live_packet_tuple, live_outbound, script_outbound; /* Verify packet addresses are outbound and live for this socket. */ get_packet_tuple(live_packet, &live_packet_tuple); socket_get_outbound(&socket->live, &live_outbound); assert(is_equal_tuple(&live_packet_tuple, &live_outbound)); /* Rewrite 4-tuple to be outbound script values. */ socket_get_outbound(&socket->script, &script_outbound); set_packet_tuple(actual_packet, &script_outbound); /* If no TCP headers to rewrite, then we're done. */ if (live_packet->tcp == NULL) return STATUS_OK; /* Rewrite TCP sequence number from live to script space. */ const bool is_syn = live_packet->tcp->syn; const u32 seq_offset = local_seq_live_to_script_offset(socket, is_syn); actual_packet->tcp->seq = htonl(ntohl(live_packet->tcp->seq) + seq_offset); /* Rewrite ACKs and SACKs from live to script space. */ const u32 ack_offset = remote_seq_live_to_script_offset(socket, is_syn); if (actual_packet->tcp->ack) actual_packet->tcp->ack_seq = htonl(ntohl(live_packet->tcp->ack_seq) + ack_offset); if (offset_sack_blocks(actual_packet, ack_offset, error)) return STATUS_ERR; /* Extract location of script and actual TCP timestamp values. */ if (find_tcp_timestamp(script_packet, error)) return STATUS_ERR; if (find_tcp_timestamp(actual_packet, error)) return STATUS_ERR; if ((script_packet->tcp_ts_val != NULL) && (actual_packet->tcp_ts_val != NULL)) { u32 script_ts_val = packet_tcp_ts_val(script_packet); u32 actual_ts_val = packet_tcp_ts_val(actual_packet); /* Remember script->actual TS val mapping for later. */ set_outbound_ts_val_mapping(socket, script_ts_val, actual_ts_val); /* Find baseline for socket's live->script TS val mapping. */ if (!socket->found_first_tcp_ts) { socket->found_first_tcp_ts = true; socket->first_script_ts_val = script_ts_val; socket->first_actual_ts_val = actual_ts_val; } /* Rewrite TCP timestamp value to script space, so we * can compare the script and actual outbound TCP * timestamp val. */ packet_set_tcp_ts_val(actual_packet, socket->first_script_ts_val + (actual_ts_val - socket->first_actual_ts_val)); } return STATUS_OK; }
/* See if the socket under test is listening and is willing to receive * this incoming SYN packet. If so, create a new child socket, anoint * it as the new socket under test, and return a pointer to * it. Otherwise, return NULL. */ static struct socket *handle_listen_for_script_packet( struct state *state, const struct packet *packet, enum direction_t direction) { /* Does this packet match this socket? For now we only support * testing one socket at a time, so we merely check whether * the socket is listening. (If we were to support testing * more than one socket at a time then we'd want to check to * see if the address tuples in the packet and socket match.) */ struct config *config = state->config; struct socket *socket = state->socket_under_test; /* shortcut */ bool match = (direction == DIRECTION_INBOUND); if (!match) return NULL; if (config->is_wire_server) { /* On wire servers we don't see the system calls, so * we won't have any socket_under_test yet. */ match = (socket == NULL); } else { /* In local mode we will certainly know about this socket. */ match = ((socket != NULL) && (socket->state == SOCKET_PASSIVE_LISTENING)); } if (!match) return NULL; /* Create a child passive socket for this incoming SYN packet. * Any further packets in the test script will be directed to * this child socket. */ socket = socket_new(state); state->socket_under_test = socket; assert(socket->state == SOCKET_INIT); socket->state = SOCKET_PASSIVE_PACKET_RECEIVED; socket->address_family = packet_address_family(packet); socket->protocol = packet_ip_protocol(packet); /* Set script info for this socket using script packet. */ struct tuple tuple; get_packet_tuple(packet, &tuple); socket->script.remote = tuple.src; socket->script.local = tuple.dst; socket->script.remote_isn = ntohl(packet->tcp->seq); socket->script.fd = -1; /* Set up the live info for this socket based * on the script packet and our overall config. */ socket->live.remote.ip = config->live_remote_ip; socket->live.remote.port = htons(next_ephemeral_port(state)); socket->live.local.ip = config->live_local_ip; socket->live.local.port = htons(config->live_bind_port); socket->live.remote_isn = ntohl(packet->tcp->seq); socket->live.fd = -1; if (DEBUG_LOGGING) { char local_string[ADDR_STR_LEN]; char remote_string[ADDR_STR_LEN]; DEBUGP("live: local: %s.%d\n", ip_to_string(&socket->live.local.ip, local_string), ntohs(socket->live.local.port)); DEBUGP("live: remote: %s.%d\n", ip_to_string(&socket->live.remote.ip, remote_string), ntohs(socket->live.remote.port)); DEBUGP("live: ISN: %u\n", socket->live.remote_isn); } return socket; }