#! /usr/bin/env stap
#
# This traces all transmit activity for a specified interface.
# It will show SKB header and data lengths and lengths of all data fragments.
#
# If the transmit is part of a TCP connection the source and destination
# address and port will be shown.
#
# It has been developed and tested on RHEL 7.0 and 7.1 kernels. It the SKB
# structure is different for other kernel versions this script may fail in
# interesting ways.
#
# Usage: tx_lengths <interface>
#

probe kernel.function("dev_hard_start_xmit@net/core/dev.c").call {
	skb = (@defined($skb) ? $skb : $first);
	name = kernel_string(@cast(skb->dev, "net_device")->name);
	if (name == @1) {
		first = 1;
		while (skb) {
			skb_shinfo = skb->head + skb->end;
			nr_frags = @cast(skb_shinfo, "skb_shared_info")->nr_frags;
			printf("hdr_len: %d; data_len: %d; frags: [",
				skb->len - skb->data_len, skb->data_len);
			for (i=0; i<nr_frags; i++) {
				frags = &@cast(skb_shinfo, "skb_shared_info")->frags[i];
				printf("%d,", @cast(frags, "skb_frag_struct")->size);
			}
			printf("];");

			gso_segs = @cast(skb_shinfo, "skb_shared_info")->gso_segs;
			if (gso_segs > 1) {
				gso_size = @cast(skb_shinfo, "skb_shared_info")->gso_size;
				tcphdr = skb->head + skb->transport_header;

				header_len = tcphdr - skb->data +
						((@cast(tcphdr, "tcphdr")->doff) << 2);

				printf(" TSO: %d*%d; Header len: %d;", gso_segs, gso_size, header_len);
			}

			if (first) {
				# We assume that all the packets in a call are
				# for the same flow.
				log_tcp_tuple(skb);
				first = 0;
			} else {
				printf(" MORE");
			}

			printf("\n");

			skb = skb->next;
		}
	}
}

function log_tcp_tuple (skb)
{
	protocol = htons(@cast(skb, "struct sk_buff")->protocol);
	if (protocol == 0x0800) {
		iphdr = @cast(skb, "struct sk_buff")->head +
		        @cast(skb, "struct sk_buff")->network_header;
		ip_protocol = @cast(iphdr, "iphdr")->protocol;
		if (ip_protocol == 6) {
			tcphdr = @cast(skb, "struct sk_buff")->head +
			         @cast(skb, "struct sk_buff")->transport_header;
			printf(" TCP tuple: %s:%d->%s:%d;",
				ip_ntop(@cast(iphdr, "iphdr")->saddr),
				htons(@cast(tcphdr, "tcphdr")->source),
				ip_ntop(@cast(iphdr, "iphdr")->daddr),
				htons(@cast(tcphdr, "tcphdr")->dest));
		}
	}
}
