/* A helper to help translate SACK sequence numbers between live and * script space. Specifically, it offsets SACK block sequence numbers * by the given 'ack_offset'. Returns STATUS_OK on success; on * failure returns STATUS_ERR and sets error message. */ static int offset_sack_blocks(struct packet *packet, u32 ack_offset, char **error) { struct tcp_options_iterator iter; struct tcp_option *option = NULL; for (option = tcp_options_begin(packet, &iter); option != NULL; option = tcp_options_next(&iter, error)) { if (option->kind == TCPOPT_SACK) { int num_blocks = 0; if (num_sack_blocks(option->length, &num_blocks, error)) return STATUS_ERR; int i = 0; for (i = 0; i < num_blocks; ++i) { u32 val; val = ntohl(option->data.sack.block[i].left); val += ack_offset; option->data.sack.block[i].left = htonl(val); val = ntohl(option->data.sack.block[i].right); val += ack_offset; option->data.sack.block[i].right = htonl(val); } } } return *error ? STATUS_ERR : STATUS_OK; }
/* A helper to find the TCP timestamp option in a packet. Parse the * TCP options and fill in packet->tcp_ts_val with the location of the * TCP timestamp value field (or NULL if there isn't one), and * likewise fill in packet->tcp_ts_ecr with the location of the TCP * timestamp echo reply field (or NULL if there isn't one). Returns * STATUS_OK on success; on failure returns STATUS_ERR and sets * error message. */ static int find_tcp_timestamp(struct packet *packet, char **error) { struct tcp_options_iterator iter; struct tcp_option *option = NULL; packet->tcp_ts_val = NULL; packet->tcp_ts_ecr = NULL; for (option = tcp_options_begin(packet, &iter); option != NULL; option = tcp_options_next(&iter, error)) if (option->kind == TCPOPT_TIMESTAMP) { packet->tcp_ts_val = &(option->data.time_stamp.val); packet->tcp_ts_ecr = &(option->data.time_stamp.ecr); } return *error ? STATUS_ERR : STATUS_OK; }
int tcp_options_to_string(struct packet *packet, char **ascii_string, char **error) { int result = STATUS_ERR; /* return value */ size_t size = 0; FILE *s = open_memstream(ascii_string, &size); /* output string */ int index = 0; /* number of options seen so far */ struct tcp_options_iterator iter; struct tcp_option *option = NULL; for (option = tcp_options_begin(packet, &iter); option != NULL; option = tcp_options_next(&iter, error)) { if (index > 0) fputc(',', s); switch (option->kind) { case TCPOPT_EOL: fputs("eol", s); break; case TCPOPT_NOP: fputs("nop", s); break; case TCPOPT_MAXSEG: fprintf(s, "mss %u", ntohs(option->data.mss.bytes)); break; case TCPOPT_WINDOW: fprintf(s, "wscale %u", option->data.window_scale.shift_count); break; case TCPOPT_SACK_PERMITTED: fputs("sackOK", s); break; case TCPOPT_SACK: fprintf(s, "sack "); int num_blocks = 0; if (num_sack_blocks(option->length, &num_blocks, error)) goto out; int i = 0; for (i = 0; i < num_blocks; ++i) { if (i > 0) fputc(' ', s); fprintf(s, "%u:%u", ntohl(option->data.sack.block[i].left), ntohl(option->data.sack.block[i].right)); } break; case TCPOPT_TIMESTAMP: fprintf(s, "TS val %u ecr %u", ntohl(option->data.time_stamp.val), ntohl(option->data.time_stamp.ecr)); break; case TCPOPT_FASTOPEN: if (tcp_fast_open_option_to_string(s, option)) { asprintf(error, "invalid length: %u", option->length); goto out; } break; case TCPOPT_EXP: if (tcp_exp_fast_open_option_to_string(s, option)) { asprintf(error, "unknown experimental option"); goto out; } break; default: asprintf(error, "unexpected TCP option kind: %u", option->kind); goto out; } ++index; } if (*error != NULL) /* bogus TCP options prevented iteration */ goto out; result = STATUS_OK; out: fclose(s); return result; }