struct packet *new_udp_packet(int address_family, enum direction_t direction, u16 udp_payload_bytes, char **error) { struct packet *packet = NULL; /* the newly-allocated result packet */ struct header *udp_header = NULL; /* the UDP header info */ /* Calculate lengths in bytes of all sections of the packet */ const int ip_option_bytes = 0; const int ip_header_bytes = (ip_header_min_len(address_family) + ip_option_bytes); const int udp_header_bytes = sizeof(struct udp); const int ip_bytes = ip_header_bytes + udp_header_bytes + udp_payload_bytes; /* Sanity-check all the various lengths */ if (ip_option_bytes & 0x3) { asprintf(error, "IP options are not padded correctly " "to ensure IP header is a multiple of 4 bytes: " "%d excess bytes", ip_option_bytes & 0x3); return NULL; } assert((udp_header_bytes & 0x3) == 0); assert((ip_header_bytes & 0x3) == 0); if (ip_bytes > MAX_UDP_DATAGRAM_BYTES) { asprintf(error, "UDP datagram too large"); return NULL; } /* Allocate and zero out a packet object of the desired size */ packet = packet_new(ip_bytes); memset(packet->buffer, 0, ip_bytes); packet->direction = direction; packet->flags = 0; packet->ecn = ECN_NONE; /* Set IP header fields */ set_packet_ip_header(packet, address_family, ip_bytes, packet->ecn, IPPROTO_UDP); udp_header = packet_append_header(packet, HEADER_UDP, sizeof(struct udp)); udp_header->total_bytes = udp_header_bytes + udp_payload_bytes; /* Find the start of UDP section of the packet */ packet->udp = (struct udp *) (ip_start(packet) + ip_header_bytes); /* Set UDP header fields */ packet->udp->src_port = htons(0); packet->udp->dst_port = htons(0); packet->udp->len = htons(udp_header_bytes + udp_payload_bytes); packet->udp->check = 0; packet->ip_bytes = ip_bytes; return packet; }
struct packet *new_tcp_packet(int socket_fd, int address_family, enum direction_t direction, enum ip_ecn_t ecn, const char *flags, u32 start_sequence, u16 tcp_payload_bytes, u32 ack_sequence, s32 window, const struct tcp_options *tcp_options, char **error) { struct packet *packet = NULL; /* the newly-allocated result packet */ /* Calculate lengths in bytes of all sections of the packet */ const int ip_option_bytes = 0; const int tcp_option_bytes = tcp_options ? tcp_options->length : 0; const int ip_header_bytes = (ip_header_len(address_family) + ip_option_bytes); const int tcp_header_bytes = sizeof(struct tcp) + tcp_option_bytes; const int ip_bytes = ip_header_bytes + tcp_header_bytes + tcp_payload_bytes; /* Sanity-check all the various lengths */ if (ip_option_bytes & 0x3) { asprintf(error, "IP options are not padded correctly " "to ensure IP header is a multiple of 4 bytes: " "%d excess bytes", ip_option_bytes & 0x3); printf("1\n"); return NULL; } if (tcp_option_bytes & 0x3) { asprintf(error, "TCP options are not padded correctly " "to ensure TCP header is a multiple of 4 bytes: " "%d excess bytes", tcp_option_bytes & 0x3); printf("2\n"); return NULL; } assert((tcp_header_bytes & 0x3) == 0); assert((ip_header_bytes & 0x3) == 0); if (tcp_header_bytes > MAX_TCP_HEADER_BYTES) { asprintf(error, "TCP header too large"); return NULL; } if (ip_bytes > MAX_TCP_DATAGRAM_BYTES) { asprintf(error, "TCP segment too large"); return NULL; } if (!is_tcp_flags_spec_valid(flags, error)){ return NULL; } /* Allocate and zero out a packet object of the desired size */ packet = packet_new(ip_bytes); memset(packet->buffer, 0, ip_bytes); packet->ip_bytes = ip_bytes; packet->direction = direction; packet->socket_script_fd = socket_fd; packet->flags = 0; packet->ecn = ecn; /* Set IP header fields */ set_packet_ip_header(packet, address_family, ip_bytes, direction, ecn, IPPROTO_TCP); /* Find the start of TCP sections of the packet */ packet->tcp = (struct tcp *) (packet_start(packet) + ip_header_bytes); u8 *tcp_option_start = (u8 *) (packet->tcp + 1); /* Set TCP header fields */ packet->tcp->src_port = htons(0); packet->tcp->dst_port = htons(0); packet->tcp->seq = htonl(start_sequence); packet->tcp->ack_seq = htonl(ack_sequence); packet->tcp->doff = tcp_header_bytes / 4; if (window == -1) { if (direction == DIRECTION_INBOUND) { asprintf(error, "window must be specified" " for inbound packets"); return NULL; } packet->tcp->window = 0; packet->flags |= FLAG_WIN_NOCHECK; } else { packet->tcp->window = htons(window); } packet->tcp->check = 0; packet->tcp->urg_ptr = 0; packet->tcp->fin = is_tcp_flag_set('F', flags); packet->tcp->syn = is_tcp_flag_set('S', flags); packet->tcp->rst = is_tcp_flag_set('R', flags); packet->tcp->psh = is_tcp_flag_set('P', flags); packet->tcp->ack = is_tcp_flag_set('.', flags); packet->tcp->urg = 0; packet->tcp->ece = is_tcp_flag_set('E', flags); packet->tcp->cwr = is_tcp_flag_set('W', flags); if (tcp_options == NULL) { packet->flags |= FLAG_OPTIONS_NOCHECK; } else if (tcp_options->length > 0) { /* Copy TCP options into packet */ memcpy(tcp_option_start, tcp_options->data, tcp_options->length); } return packet; }
struct packet *new_icmp_packet(int address_family, enum direction_t direction, const char *type_string, const char *code_string, int protocol, u32 tcp_start_sequence, u32 payload_bytes, s64 mtu, char **error) { s32 type = -1; /* bad type; means "unknown so far" */ s32 code = -1; /* bad code; means "unknown so far" */ struct packet *packet = NULL; /* the newly-allocated result packet */ /* Calculate lengths in bytes of all sections of the packet. * For now we only support the most common ICMP message * format, which includes at the end the original outgoing IP * header and the first 8 bytes after that (which will * typically have the port info needed to demux the message). */ const int ip_fixed_bytes = ip_header_len(address_family); const int ip_option_bytes = 0; const int ip_header_bytes = ip_fixed_bytes + ip_option_bytes; const int echoed_bytes = ip_fixed_bytes + ICMP_ECHO_BYTES; const int icmp_bytes = icmp_header_len(address_family) + echoed_bytes; const int ip_bytes = ip_header_bytes + icmp_bytes; /* Sanity-check all the various lengths */ if (ip_option_bytes & 0x3) { asprintf(error, "IP options are not padded correctly " "to ensure IP header is a multiple of 4 bytes: " "%d excess bytes", ip_option_bytes & 0x3); goto error_out; } assert((ip_header_bytes & 0x3) == 0); /* Parse the ICMP type and code */ if (parse_icmp_type_and_code(address_family, type_string, code_string, &type, &code, error)) goto error_out; assert(is_valid_u8(type)); assert(is_valid_u8(code)); /* Allocate and zero out a packet object of the desired size */ packet = packet_new(ip_bytes); memset(packet->buffer, 0, ip_bytes); packet->ip_bytes = ip_bytes; packet->direction = direction; packet->flags = 0; packet->ecn = 0; /* Set IP header fields */ const enum ip_ecn_t ecn = ECN_NONE; set_packet_ip_header(packet, address_family, ip_bytes, direction, ecn, icmp_protocol(address_family)); /* Find the start of the ICMP header and then populate common fields. */ void *icmp_header = packet_start(packet) + ip_header_bytes; if (set_packet_icmp_header(packet, icmp_header, address_family, type, code, mtu, error)) goto error_out; /* All ICMP message types currently supported by this tool * include a copy of the outbound IP header and the first few * bytes inside. To ensure that the inbound ICMP message gets * demuxed to the correct socket in the kernel, here we * construct enough of a basic IP header and during test * execution we fill in the port numbers and (if specified) * TCP sequence number in the TCP header. */ u8 *echoed_ip = packet_echoed_ip_header(packet); const int echoed_ip_bytes = (ip_fixed_bytes + layer4_header_len(protocol) + payload_bytes); set_ip_header(echoed_ip, address_family, echoed_ip_bytes, reverse_direction(direction), ecn, protocol); if (protocol == IPPROTO_TCP) { u32 *seq = packet_echoed_tcp_seq(packet); *seq = htonl(tcp_start_sequence); } return packet; error_out: if (packet != NULL) packet_free(packet); return NULL; }