aboutsummaryrefslogtreecommitdiffstats
path: root/test/packetdrill/packet_to_string.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/packetdrill/packet_to_string.c')
-rw-r--r--test/packetdrill/packet_to_string.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/test/packetdrill/packet_to_string.c b/test/packetdrill/packet_to_string.c
new file mode 100644
index 0000000..1fd90b2
--- /dev/null
+++ b/test/packetdrill/packet_to_string.c
@@ -0,0 +1,303 @@
+/*
+ * 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 generating human-readable representations of IP
+ * packets.
+ */
+
+#include "packet_to_string.h"
+
+#include <stdlib.h>
+#include "socket.h"
+#include "tcp_options_to_string.h"
+
+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));
+}
+
+static void packet_buffer_to_string(FILE *s, struct packet *packet)
+{
+ char *hex = NULL;
+ hex_dump(packet->buffer, packet_end(packet) - packet->buffer, &hex);
+ fputc('\n', s);
+ fprintf(s, "%s", hex);
+ free(hex);
+}
+
+static int ipv4_header_to_string(FILE *s, struct packet *packet, int layer,
+ enum dump_format_t format, char **error)
+{
+ char src_string[ADDR_STR_LEN];
+ char dst_string[ADDR_STR_LEN];
+ struct ip_address src_ip, dst_ip;
+ const struct ipv4 *ipv4 = packet->headers[layer].h.ipv4;
+
+ ip_from_ipv4(&ipv4->src_ip, &src_ip);
+ ip_from_ipv4(&ipv4->dst_ip, &dst_ip);
+
+ fprintf(s, "ipv4 %s > %s: ",
+ ip_to_string(&src_ip, src_string),
+ ip_to_string(&dst_ip, dst_string));
+
+ return STATUS_OK;
+}
+
+static int ipv6_header_to_string(FILE *s, struct packet *packet, int layer,
+ enum dump_format_t format, char **error)
+{
+ char src_string[ADDR_STR_LEN];
+ char dst_string[ADDR_STR_LEN];
+ struct ip_address src_ip, dst_ip;
+ const struct ipv6 *ipv6 = packet->headers[layer].h.ipv6;
+
+ ip_from_ipv6(&ipv6->src_ip, &src_ip);
+ ip_from_ipv6(&ipv6->dst_ip, &dst_ip);
+
+ fprintf(s, "ipv6 %s > %s: ",
+ ip_to_string(&src_ip, src_string),
+ ip_to_string(&dst_ip, dst_string));
+
+ return STATUS_OK;
+}
+
+static int gre_header_to_string(FILE *s, struct packet *packet, int layer,
+ enum dump_format_t format, char **error)
+{
+ const struct gre *gre = packet->headers[layer].h.gre;
+ int i = 0;
+
+ fprintf(s, "gre flags 0x%x proto 0x%04x",
+ ntohs(gre->flags),
+ ntohs(gre->proto));
+
+ if (gre->has_checksum || gre->has_routing) {
+ fprintf(s, " sum 0x%x off 0x%x",
+ ntohs(gre->be16[0]),
+ ntohs(gre->be16[1]));
+ i++;
+ }
+
+ if (gre->has_key) {
+ fprintf(s, " key 0x%x", ntohl(gre->be32[i]));
+ i++;
+ }
+
+ if (gre->has_seq) {
+ fprintf(s, " seq 0x%x", ntohl(gre->be32[i]));
+ i++;
+ }
+
+ fprintf(s, ": ");
+ return STATUS_OK;
+}
+
+static int mpls_header_to_string(FILE *s, struct packet *packet, int layer,
+ enum dump_format_t format, char **error)
+{
+ struct header *header = &packet->headers[layer];
+ int num_entries = header->header_bytes / sizeof(struct mpls);
+ int i = 0;
+
+ fprintf(s, "mpls");
+
+ for (i = 0; i < num_entries; ++i) {
+ const struct mpls *mpls = header->h.mpls + i;
+
+ fprintf(s, " (label %u, tc %u,%s ttl %u)",
+ mpls_entry_label(mpls),
+ mpls_entry_tc(mpls),
+ mpls_entry_stack(mpls) ? " [S]," : "",
+ mpls_entry_ttl(mpls));
+ }
+
+ fprintf(s, ": ");
+ return STATUS_OK;
+}
+
+/* Print a string representation of the TCP packet:
+ * direction opt_ip_info flags seq ack window tcp_options
+ */
+static int tcp_packet_to_string(FILE *s, struct packet *packet,
+ enum dump_format_t format, char **error)
+{
+ int result = STATUS_OK; /* return value */
+
+ if ((format == DUMP_FULL) || (format == DUMP_VERBOSE)) {
+ endpoints_to_string(s, packet);
+ fputc(' ', s);
+ }
+
+
+ /* We print flags in the same order as tcpdump 4.1.1. */
+ if (packet->tcp->fin)
+ fputc('F', s);
+ if (packet->tcp->syn)
+ fputc('S', s);
+ if (packet->tcp->rst)
+ fputc('R', s);
+ if (packet->tcp->psh)
+ fputc('P', s);
+ if (packet->tcp->ack)
+ fputc('.', s);
+ if (packet->tcp->urg)
+ fputc('U', s);
+ if (packet->tcp->ece)
+ fputc('E', s); /* ECN *E*cho sent (ECN) */
+ if (packet->tcp->cwr)
+ fputc('W', s); /* Congestion *W*indow reduced (ECN) */
+
+ fprintf(s, " %u:%u(%u) ",
+ ntohl(packet->tcp->seq),
+ ntohl(packet->tcp->seq) + packet_payload_len(packet),
+ packet_payload_len(packet));
+
+ if (packet->tcp->ack)
+ fprintf(s, "ack %u ", ntohl(packet->tcp->ack_seq));
+
+ if (!(packet->flags & FLAG_WIN_NOCHECK))
+ fprintf(s, "win %u ", ntohs(packet->tcp->window));
+
+ if (packet_tcp_options_len(packet) > 0) {
+ char *tcp_options = NULL;
+ if (tcp_options_to_string(packet, &tcp_options, error))
+ result = STATUS_ERR;
+ else
+ fprintf(s, "<%s>", tcp_options);
+ free(tcp_options);
+ }
+
+ if (format == DUMP_VERBOSE)
+ packet_buffer_to_string(s, packet);
+
+ return result;
+}
+
+static int udp_packet_to_string(FILE *s, struct packet *packet,
+ enum dump_format_t format, char **error)
+{
+ int result = STATUS_OK; /* return value */
+
+ if ((format == DUMP_FULL) || (format == DUMP_VERBOSE)) {
+ endpoints_to_string(s, packet);
+ fputc(' ', s);
+ }
+
+ fprintf(s, "udp (%u)", packet_payload_len(packet));
+
+ if (format == DUMP_VERBOSE)
+ packet_buffer_to_string(s, packet);
+
+ return result;
+}
+
+static int icmpv4_packet_to_string(FILE *s, struct packet *packet,
+ enum dump_format_t format, char **error)
+{
+ fprintf(s, "icmpv4");
+ /* TODO(ncardwell): print type, code; use tables from icmp_packet.c */
+ return STATUS_OK;
+}
+
+static int icmpv6_packet_to_string(FILE *s, struct packet *packet,
+ enum dump_format_t format, char **error)
+{
+ fprintf(s, "icmpv6");
+ /* TODO(ncardwell): print type, code; use tables from icmp_packet.c */
+ return STATUS_OK;
+}
+
+typedef int (*header_to_string_func)(FILE *s, struct packet *packet, int layer,
+ enum dump_format_t format, char **error);
+
+static int encap_header_to_string(FILE *s, struct packet *packet, int layer,
+ enum dump_format_t format, char **error)
+{
+ header_to_string_func printers[HEADER_NUM_TYPES] = {
+ [HEADER_IPV4] = ipv4_header_to_string,
+ [HEADER_IPV6] = ipv6_header_to_string,
+ [HEADER_GRE] = gre_header_to_string,
+ [HEADER_MPLS] = mpls_header_to_string,
+ };
+ header_to_string_func printer = NULL;
+ enum header_t type = packet->headers[layer].type;
+
+ assert(type > HEADER_NONE);
+ assert(type < HEADER_NUM_TYPES);
+ printer = printers[type];
+ assert(printer != NULL);
+ return printer(s, packet, layer, format, error);
+}
+
+
+int packet_to_string(struct packet *packet,
+ enum dump_format_t format,
+ char **ascii_string, char **error)
+{
+ assert(packet != NULL);
+ int result = STATUS_ERR; /* return value */
+ size_t size = 0;
+ FILE *s = open_memstream(ascii_string, &size); /* output string */
+ int i;
+ int header_count = packet_header_count(packet);
+
+ /* Print any encapsulation headers preceding layer 3 and 4 headers. */
+ for (i = 0; i < header_count - 2; ++i) {
+ if (packet->headers[i].type == HEADER_NONE)
+ break;
+ if (encap_header_to_string(s, packet, i, format, error))
+ goto out;
+ }
+
+ if ((packet->ipv4 == NULL) && (packet->ipv6 == NULL)) {
+ fprintf(s, "[NO IP HEADER]");
+ } else {
+ if (packet->tcp != NULL) {
+ if (tcp_packet_to_string(s, packet, format, error))
+ goto out;
+ } else if (packet->udp != NULL) {
+ if (udp_packet_to_string(s, packet, format, error))
+ goto out;
+ } else if (packet->icmpv4 != NULL) {
+ if (icmpv4_packet_to_string(s, packet, format, error))
+ goto out;
+ } else if (packet->icmpv6 != NULL) {
+ if (icmpv6_packet_to_string(s, packet, format, error))
+ goto out;
+ } else {
+ fprintf(s, "[NO TCP OR ICMP HEADER]");
+ }
+ }
+
+ result = STATUS_OK;
+
+out:
+ fclose(s);
+ return result;
+}