aboutsummaryrefslogtreecommitdiffstats
path: root/test/packetdrill/packet_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/packetdrill/packet_parser.c')
-rw-r--r--test/packetdrill/packet_parser.c625
1 files changed, 625 insertions, 0 deletions
diff --git a/test/packetdrill/packet_parser.c b/test/packetdrill/packet_parser.c
new file mode 100644
index 0000000..f593233
--- /dev/null
+++ b/test/packetdrill/packet_parser.c
@@ -0,0 +1,625 @@
+/*
+ * 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)
+ *
+ * Implementation for a module to parse TCP/IP packets.
+ */
+
+#include "packet_parser.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "assert.h"
+#include "checksum.h"
+#include "ethernet.h"
+#include "gre.h"
+#include "ip.h"
+#include "ip_address.h"
+#include "logging.h"
+#include "packet.h"
+#include "tcp.h"
+
+static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end,
+ char **error);
+static int parse_ipv6(struct packet *packet, u8 *header_start, u8 *packet_end,
+ char **error);
+static int parse_mpls(struct packet *packet, u8 *header_start, u8 *packet_end,
+ char **error);
+static int parse_layer3_packet_by_proto(struct packet *packet,
+ u16 proto, u8 *header_start,
+ u8 *packet_end, char **error);
+static int parse_layer4(struct packet *packet, u8 *header_start,
+ int layer4_protocol, int layer4_bytes,
+ u8 *packet_end, char **error);
+
+static int parse_layer2_packet(struct packet *packet,
+ u8 *header_start, u8 *packet_end,
+ char **error)
+{
+ u8 *p = header_start;
+ struct ether_header *ether = NULL;
+
+ /* Find Ethernet header */
+ if (p + sizeof(*ether) > packet_end) {
+ asprintf(error, "Ethernet header overflows packet");
+ goto error_out;
+ }
+ ether = (struct ether_header *)p;
+ p += sizeof(*ether);
+ packet->l2_header_bytes = sizeof(*ether);
+
+ return parse_layer3_packet_by_proto(packet, ntohs(ether->ether_type),
+ p, packet_end, error);
+
+error_out:
+ return PACKET_BAD;
+}
+
+static int parse_layer3_packet_by_proto(struct packet *packet,
+ u16 proto, u8 *header_start,
+ u8 *packet_end, char **error)
+{
+ u8 *p = header_start;
+
+ if (proto == ETHERTYPE_IP) {
+ struct ipv4 *ip = NULL;
+
+ /* Examine IPv4 header. */
+ if (p + sizeof(struct ipv4) > packet_end) {
+ asprintf(error, "IPv4 header overflows packet");
+ goto error_out;
+ }
+
+ /* Look at the IP version number, which is in the first 4 bits
+ * of both IPv4 and IPv6 packets.
+ */
+ ip = (struct ipv4 *)p;
+ if (ip->version == 4) {
+ return parse_ipv4(packet, p, packet_end, error);
+ } else {
+ asprintf(error, "Bad IP version (%d) for ETHERTYPE_IP", ip->version);
+ goto error_out;
+ }
+ } else if (proto == ETHERTYPE_IPV6) {
+ struct ipv6 *ip = NULL;
+
+ /* Examine IPv6 header. */
+ if (p + sizeof(struct ipv6) > packet_end) {
+ asprintf(error, "IPv6 header overflows packet");
+ goto error_out;
+ }
+
+ /* Look at the IP version number, which is in the first 4 bits
+ * of both IPv4 and IPv6 packets.
+ */
+ ip = (struct ipv6 *)p;
+ if (ip->version == 6) {
+ return parse_ipv6(packet, p, packet_end, error);
+ } else {
+ asprintf(error, "Bad IP version for ETHERTYPE_IPV6");
+ goto error_out;
+ }
+ } else if ((proto == ETHERTYPE_MPLS_UC) ||
+ (proto == ETHERTYPE_MPLS_MC)) {
+ return parse_mpls(packet, p, packet_end, error);
+ } else {
+ return PACKET_UNKNOWN_L4;
+ }
+
+error_out:
+ return PACKET_BAD;
+}
+
+static int parse_layer3_packet(struct packet *packet,
+ u8 *header_start, u8 *packet_end,
+ char **error)
+{
+ u8 *p = header_start;
+ /* Note that packet_end points to the byte beyond the end of packet. */
+ struct ipv4 *ip = NULL;
+
+ /* Examine IPv4/IPv6 header. */
+ if (p + sizeof(struct ipv4) > packet_end) {
+ asprintf(error, "IP header overflows packet");
+ return PACKET_BAD;
+ }
+
+ /* Look at the IP version number, which is in the first 4 bits
+ * of both IPv4 and IPv6 packets.
+ */
+ ip = (struct ipv4 *) (p);
+ if (ip->version == 4)
+ return parse_ipv4(packet, p, packet_end, error);
+ else if (ip->version == 6)
+ return parse_ipv6(packet, p, packet_end, error);
+
+ asprintf(error, "Unsupported IP version");
+ return PACKET_BAD;
+}
+
+int parse_packet(struct packet *packet, int in_bytes,
+ enum packet_layer_t layer, char **error)
+{
+ assert(in_bytes <= packet->buffer_bytes);
+ char *message = NULL; /* human-readable error summary */
+ char *hex = NULL; /* hex dump of bad packet */
+ enum packet_parse_result_t result = PACKET_BAD;
+ u8 *header_start = packet->buffer;
+ /* packet_end points to the byte beyond the end of packet. */
+ u8 *packet_end = packet->buffer + in_bytes;
+
+ if (layer == PACKET_LAYER_2_ETHERNET)
+ result = parse_layer2_packet(packet, header_start, packet_end,
+ error);
+ else if (layer == PACKET_LAYER_3_IP)
+ result = parse_layer3_packet(packet, header_start, packet_end,
+ error);
+ else
+ assert(!"bad layer");
+
+ if (result != PACKET_BAD)
+ return result;
+
+ /* Error. Add a packet hex dump to the error string we're returning. */
+ hex_dump(packet->buffer, in_bytes, &hex);
+ message = *error;
+ asprintf(error, "%s: packet of %d bytes:\n%s", message, in_bytes, hex);
+ free(message);
+ free(hex);
+
+ return PACKET_BAD;
+}
+
+/* Parse the IPv4 header and the TCP header inside. Return a
+ * packet_parse_result_t.
+ * Note that packet_end points to the byte beyond the end of packet.
+ */
+static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end,
+ char **error)
+{
+ struct header *ip_header = NULL;
+ u8 *p = header_start;
+ const bool is_outer = (packet->ip_bytes == 0);
+ enum packet_parse_result_t result = PACKET_BAD;
+ struct ipv4 *ipv4 = (struct ipv4 *) (p);
+
+ const int ip_header_bytes = ipv4_header_len(ipv4);
+ assert(ip_header_bytes >= 0);
+ if (ip_header_bytes < sizeof(*ipv4)) {
+ asprintf(error, "IP header too short");
+ goto error_out;
+ }
+ if (p + ip_header_bytes > packet_end) {
+ asprintf(error, "Full IP header overflows packet");
+ goto error_out;
+ }
+ const int ip_total_bytes = ntohs(ipv4->tot_len);
+
+ if (p + ip_total_bytes > packet_end) {
+ asprintf(error, "IP payload overflows packet");
+ goto error_out;
+ }
+ if (ip_header_bytes > ip_total_bytes) {
+ asprintf(error, "IP header bigger than datagram");
+ goto error_out;
+ }
+ if (ntohs(ipv4->frag_off) & IP_MF) { /* more fragments? */
+ asprintf(error, "More fragments remaining");
+ goto error_out;
+ }
+ if (ntohs(ipv4->frag_off) & IP_OFFMASK) { /* fragment offset */
+ asprintf(error, "Non-zero fragment offset");
+ goto error_out;
+ }
+ const u16 checksum = ipv4_checksum(ipv4, ip_header_bytes);
+ if (checksum != 0) {
+ asprintf(error, "Bad IP checksum");
+ goto error_out;
+ }
+
+ ip_header = packet_append_header(packet, HEADER_IPV4, ip_header_bytes);
+ if (ip_header == NULL) {
+ asprintf(error, "Too many nested headers at IPv4 header");
+ goto error_out;
+ }
+ ip_header->total_bytes = ip_total_bytes;
+
+ /* Move on to the header inside. */
+ p += ip_header_bytes;
+ assert(p <= packet_end);
+
+ if (DEBUG_LOGGING) {
+ char src_string[ADDR_STR_LEN];
+ char dst_string[ADDR_STR_LEN];
+ struct ip_address src_ip, dst_ip;
+ ip_from_ipv4(&ipv4->src_ip, &src_ip);
+ ip_from_ipv4(&ipv4->dst_ip, &dst_ip);
+ DEBUGP("src IP: %s\n", ip_to_string(&src_ip, src_string));
+ DEBUGP("dst IP: %s\n", ip_to_string(&dst_ip, dst_string));
+ }
+
+ /* Examine the L4 header. */
+ const int layer4_bytes = ip_total_bytes - ip_header_bytes;
+ const int layer4_protocol = ipv4->protocol;
+ result = parse_layer4(packet, p, layer4_protocol, layer4_bytes,
+ packet_end, error);
+
+ /* If this is the innermost L3 header then this is the primary. */
+ if (!packet->ipv4 && !packet->ipv6)
+ packet->ipv4 = ipv4;
+ /* If this is the outermost IP header then this is the packet length. */
+ if (is_outer)
+ packet->ip_bytes = ip_total_bytes;
+
+ return result;
+
+error_out:
+ return PACKET_BAD;
+}
+
+/* Parse the IPv6 header and the TCP header inside. We do not
+ * currently support parsing IPv6 extension headers or any layer 4
+ * protocol other than TCP. Return a packet_parse_result_t.
+ * Note that packet_end points to the byte beyond the end of packet.
+ */
+static int parse_ipv6(struct packet *packet, u8 *header_start, u8 *packet_end,
+ char **error)
+{
+ struct header *ip_header = NULL;
+ u8 *p = header_start;
+ const bool is_outer = (packet->ip_bytes == 0);
+ struct ipv6 *ipv6 = (struct ipv6 *) (p);
+ enum packet_parse_result_t result = PACKET_BAD;
+
+ /* Check that header fits in sniffed packet. */
+ const int ip_header_bytes = sizeof(*ipv6);
+ if (p + ip_header_bytes > packet_end) {
+ asprintf(error, "IPv6 header overflows packet");
+ goto error_out;
+ }
+
+ /* Check that payload fits in sniffed packet. */
+ const int ip_total_bytes = (ip_header_bytes +
+ ntohs(ipv6->payload_len));
+
+ if (p + ip_total_bytes > packet_end) {
+ asprintf(error, "IPv6 payload overflows packet");
+ goto error_out;
+ }
+ assert(ip_header_bytes <= ip_total_bytes);
+
+ ip_header = packet_append_header(packet, HEADER_IPV6, ip_header_bytes);
+ if (ip_header == NULL) {
+ asprintf(error, "Too many nested headers at IPv6 header");
+ goto error_out;
+ }
+ ip_header->total_bytes = ip_total_bytes;
+
+ /* Move on to the header inside. */
+ p += ip_header_bytes;
+ assert(p <= packet_end);
+
+ if (DEBUG_LOGGING) {
+ char src_string[ADDR_STR_LEN];
+ char dst_string[ADDR_STR_LEN];
+ struct ip_address src_ip, dst_ip;
+ ip_from_ipv6(&ipv6->src_ip, &src_ip);
+ ip_from_ipv6(&ipv6->dst_ip, &dst_ip);
+ DEBUGP("src IP: %s\n", ip_to_string(&src_ip, src_string));
+ DEBUGP("dst IP: %s\n", ip_to_string(&dst_ip, dst_string));
+ }
+
+ /* Examine the L4 header. */
+ const int layer4_bytes = ip_total_bytes - ip_header_bytes;
+ const int layer4_protocol = ipv6->next_header;
+ result = parse_layer4(packet, p, layer4_protocol, layer4_bytes,
+ packet_end, error);
+
+ /* If this is the innermost L3 header then this is the primary. */
+ if (!packet->ipv4 && !packet->ipv6)
+ packet->ipv6 = ipv6;
+ /* If this is the outermost IP header then this is the packet length. */
+ if (is_outer)
+ packet->ip_bytes = ip_total_bytes;
+
+ return result;
+
+error_out:
+ return PACKET_BAD;
+}
+
+/* Parse the TCP header. Return a packet_parse_result_t. */
+static int parse_tcp(struct packet *packet, u8 *layer4_start, int layer4_bytes,
+ u8 *packet_end, char **error)
+{
+ struct header *tcp_header = NULL;
+ u8 *p = layer4_start;
+
+ assert(layer4_bytes >= 0);
+ if (layer4_bytes < sizeof(struct tcp)) {
+ asprintf(error, "Truncated TCP header");
+ goto error_out;
+ }
+ packet->tcp = (struct tcp *) p;
+ const int tcp_header_len = packet_tcp_header_len(packet);
+ if (tcp_header_len < sizeof(struct tcp)) {
+ asprintf(error, "TCP data offset too small");
+ goto error_out;
+ }
+ if (tcp_header_len > layer4_bytes) {
+ asprintf(error, "TCP data offset too big");
+ goto error_out;
+ }
+
+ tcp_header = packet_append_header(packet, HEADER_TCP, tcp_header_len);
+ if (tcp_header == NULL) {
+ asprintf(error, "Too many nested headers at TCP header");
+ goto error_out;
+ }
+ tcp_header->total_bytes = layer4_bytes;
+
+ p += layer4_bytes;
+ assert(p <= packet_end);
+
+ DEBUGP("TCP src port: %d\n", ntohs(packet->tcp->src_port));
+ DEBUGP("TCP dst port: %d\n", ntohs(packet->tcp->dst_port));
+ return PACKET_OK;
+
+error_out:
+ return PACKET_BAD;
+}
+
+/* Parse the UDP header. Return a packet_parse_result_t. */
+static int parse_udp(struct packet *packet, u8 *layer4_start, int layer4_bytes,
+ u8 *packet_end, char **error)
+{
+ struct header *udp_header = NULL;
+ u8 *p = layer4_start;
+
+ assert(layer4_bytes >= 0);
+ if (layer4_bytes < sizeof(struct udp)) {
+ asprintf(error, "Truncated UDP header");
+ goto error_out;
+ }
+ packet->udp = (struct udp *) p;
+ const int udp_len = ntohs(packet->udp->len);
+ const int udp_header_len = sizeof(struct udp);
+ if (udp_len < udp_header_len) {
+ asprintf(error, "UDP datagram length too small for UDP header");
+ goto error_out;
+ }
+ if (udp_len < layer4_bytes) {
+ asprintf(error, "UDP datagram length too small");
+ goto error_out;
+ }
+ if (udp_len > layer4_bytes) {
+ asprintf(error, "UDP datagram length too big");
+ goto error_out;
+ }
+
+ udp_header = packet_append_header(packet, HEADER_UDP, udp_header_len);
+ if (udp_header == NULL) {
+ asprintf(error, "Too many nested headers at UDP header");
+ goto error_out;
+ }
+ udp_header->total_bytes = layer4_bytes;
+
+ p += layer4_bytes;
+ assert(p <= packet_end);
+
+ DEBUGP("UDP src port: %d\n", ntohs(packet->udp->src_port));
+ DEBUGP("UDP dst port: %d\n", ntohs(packet->udp->dst_port));
+ return PACKET_OK;
+
+error_out:
+ return PACKET_BAD;
+}
+
+/* Parse the ICMP header. Return a packet_parse_result_t. */
+static int parse_icmpv4(struct packet *packet, u8 *layer4_start, int layer4_bytes,
+ u8 *packet_end, char **error)
+{
+ struct header *icmp_header = NULL;
+ u8 *p = layer4_start;
+
+ assert(layer4_bytes >= 0);
+ const int icmpv4_len = sizeof(struct icmpv4);
+ if (layer4_bytes < icmpv4_len) {
+ asprintf(error, "Truncated ICMPv4 header");
+ goto error_out;
+ }
+ packet->icmpv4 = (struct icmpv4 *) p;
+ icmp_header = packet_append_header(packet, HEADER_ICMPV4, icmpv4_len);
+
+ if (icmp_header == NULL) {
+ asprintf(error, "Too many nested headers at ICMP header");
+ goto error_out;
+ }
+ icmp_header->total_bytes = layer4_bytes;
+
+ p += layer4_bytes;
+ assert(p <= packet_end);
+
+ DEBUGP("ICMPv4 type: %d\n", packet->icmpv4->type);
+ DEBUGP("ICMPv4 code: %d\n", packet->icmpv4->code);
+ return PACKET_OK;
+
+error_out:
+ return PACKET_BAD;
+}
+
+static int parse_icmpv6(struct packet *packet, u8 *layer4_start, int layer4_bytes,
+ u8 *packet_end, char **error)
+{
+ struct header *icmp_header = NULL;
+ u8 *p = layer4_start;
+
+ assert(layer4_bytes >= 0);
+ const int icmpv6_len = sizeof(struct icmpv6);
+ if (layer4_bytes < icmpv6_len) {
+ asprintf(error, "Truncated ICMPv6 header");
+ goto error_out;
+ }
+ packet->icmpv6 = (struct icmpv6 *) p;
+ icmp_header = packet_append_header(packet, HEADER_ICMPV6, icmpv6_len);
+
+ if (icmp_header == NULL) {
+ asprintf(error, "Too many nested headers at ICMP header");
+ goto error_out;
+ }
+ icmp_header->total_bytes = layer4_bytes;
+
+ p += layer4_bytes;
+ assert(p <= packet_end);
+
+ DEBUGP("ICMPv6 type: %d\n", packet->icmpv6->type);
+ DEBUGP("ICMPv6 code: %d\n", packet->icmpv6->code);
+ return PACKET_OK;
+
+error_out:
+ return PACKET_BAD;
+}
+
+/* Parse the GRE header. Return a packet_parse_result_t. */
+static int parse_gre(struct packet *packet, u8 *layer4_start, int layer4_bytes,
+ u8 *packet_end, char **error)
+{
+ struct header *gre_header = NULL;
+ u8 *p = layer4_start;
+ struct gre *gre = (struct gre *) p;
+
+ assert(layer4_bytes >= 0);
+ if (layer4_bytes < GRE_MINLEN) {
+ asprintf(error, "Truncated GRE header");
+ goto error_out;
+ }
+ if (gre->version != 0) {
+ asprintf(error, "GRE header has unsupported version number");
+ goto error_out;
+ }
+ if (gre->has_routing) {
+ asprintf(error, "GRE header has unsupported routing info");
+ goto error_out;
+ }
+ const int gre_header_len = gre_len(gre);
+ if (gre_header_len < GRE_MINLEN) {
+ asprintf(error, "GRE header length too small for GRE header");
+ goto error_out;
+ }
+ if (gre_header_len > layer4_bytes) {
+ asprintf(error, "GRE header length too big");
+ goto error_out;
+ }
+
+ assert(p + layer4_bytes <= packet_end);
+
+ DEBUGP("GRE header len: %d\n", gre_header_len);
+
+ gre_header = packet_append_header(packet, HEADER_GRE, gre_header_len);
+ if (gre_header == NULL) {
+ asprintf(error, "Too many nested headers at GRE header");
+ goto error_out;
+ }
+ gre_header->total_bytes = layer4_bytes;
+
+ p += gre_header_len;
+ assert(p <= packet_end);
+ return parse_layer3_packet_by_proto(packet, ntohs(gre->proto),
+ p, packet_end, error);
+
+error_out:
+ return PACKET_BAD;
+}
+
+int parse_mpls(struct packet *packet, u8 *header_start, u8 *packet_end,
+ char **error)
+{
+ struct header *mpls_header = NULL;
+ u8 *p = header_start;
+ int mpls_header_bytes = 0;
+ int mpls_total_bytes = packet_end - p;
+ bool is_stack_bottom = false;
+
+ do {
+ struct mpls *mpls_entry = (struct mpls *)(p);
+
+ if (p + sizeof(struct mpls) > packet_end) {
+ asprintf(error, "MPLS stack entry overflows packet");
+ goto error_out;
+ }
+
+ is_stack_bottom = mpls_entry_stack(mpls_entry);
+
+ p += sizeof(struct mpls);
+ mpls_header_bytes += sizeof(struct mpls);
+ } while (!is_stack_bottom && p < packet_end);
+
+ assert(mpls_header_bytes <= mpls_total_bytes);
+
+ mpls_header = packet_append_header(packet, HEADER_MPLS,
+ mpls_header_bytes);
+ if (mpls_header == NULL) {
+ asprintf(error, "Too many nested headers at MPLS header");
+ goto error_out;
+ }
+ mpls_header->total_bytes = mpls_total_bytes;
+
+ /* Move on to the header inside the MPLS label stack. */
+ assert(p <= packet_end);
+ return parse_layer3_packet(packet, p, packet_end, error);
+
+error_out:
+ return PACKET_BAD;
+}
+
+static int parse_layer4(struct packet *packet, u8 *layer4_start,
+ int layer4_protocol, int layer4_bytes,
+ u8 *packet_end, char **error)
+{
+ if (layer4_protocol == IPPROTO_TCP) {
+ return parse_tcp(packet, layer4_start, layer4_bytes, packet_end,
+ error);
+ } else if (layer4_protocol == IPPROTO_UDP) {
+ return parse_udp(packet, layer4_start, layer4_bytes, packet_end,
+ error);
+ } else if (layer4_protocol == IPPROTO_ICMP) {
+ return parse_icmpv4(packet, layer4_start, layer4_bytes, packet_end,
+ error);
+ } else if (layer4_protocol == IPPROTO_ICMPV6) {
+ return parse_icmpv6(packet, layer4_start, layer4_bytes, packet_end,
+ error);
+ } else if (layer4_protocol == IPPROTO_GRE) {
+ return parse_gre(packet, layer4_start, layer4_bytes, packet_end,
+ error);
+ } else if (layer4_protocol == IPPROTO_IPIP) {
+ return parse_ipv4(packet, layer4_start, packet_end, error);
+ } else if (layer4_protocol == IPPROTO_IPV6) {
+ return parse_ipv6(packet, layer4_start, packet_end, error);
+ }
+ return PACKET_UNKNOWN_L4;
+}