// networking tapset
// Copyright (C) 2005, 2006 IBM Corp.
//
// This file is part of systemtap, and is free software.  You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.
// <tapsetdescription>
// This family of probe points is used to probe the activities of the network device. 
// </tapsetdescription>

// ===================================================================
//
// BZ1546179 fix notes.
//
// The following kernel commit changed the sk_buff structure:
//
//   commit bffa72cf7f9df842f0016ba03586039296b4caaf
//   Author: Eric Dumazet <edumazet@google.com>
//   Date:   Tue Sep 19 05:14:24 2017 -0700
//
//     net: sk_buff rbnode reorg
//    
//     skb->rbnode shares space with skb->next, skb->prev and skb->tstamp
//
// The sk_buff structure now looks like this:
//
// ====
//   struct sk_buff {
//	union {
//		struct {
//			/* These two members must be first. */
//			struct sk_buff		*next;
//			struct sk_buff		*prev;
//
//			union {
//				struct net_device	*dev;
//				/* Some protocols might use this space to store information,
//				 * while device pointer would be NULL.
//				 * UDP receive path is one user.
//				 */
//				unsigned long		dev_scratch;
//			};
//		};
//		struct rb_node	rbnode; /* used in netem & tcp stack */
//	};
//        ... stuff deleted ...
//   };
// ====
//
// There doesn't seem to be a way by looking at the sk_buf structure
// to know which of those union values are valid. If a probe does
// something like "dev_name = kernel_string($skb->dev->name)", that
// could fail in a variety of ways (since it is difficult to know if
// $skb->dev is supposed to be valid). To avoid a probe user getting a
// read error (with no way to trap that error), accesses to sk_buff
// structs have been surrounded with "try { .. } catch { }" in probes.
//
// Note that this is done only in probes, not in functions. A user
// doing his own probing might want to try to catch an error in a
// function himself.
//
// Also note that statements accessing the same sk_buff structure
// field are grouped in a single try/catch statement. Statements
// accessing different sk_buff structure fields each have their own
// try/catch statement.
//
// ===================================================================

/* A function that returns the device name given the net_device struct */
function get_netdev_name:string (addr:long) {
	return kernel_string(@cast(addr, "net_device", "kernel")->name)
}

/**
 * probe netdev.receive - Data received from network device.
 * @dev_name: The name of the device. e.g: eth0, ath1.
 * @length: The length of the receiving buffer.
 * @protocol: Protocol of received packet.
 *
 */
///    <varlistentry><term>protocol</term>
///       <listitem><para>The possible values of protocol could be:
///         <table frame='all'><title>Protocol Values</title>
///         <tgroup cols='2' align='left' colsep='1' rowsep='1'>
///         <colspec colname='Value'/>
///         <colspec colname='Protocol'/>
///	    <thead>
///   	    <row><entry>Value(Hex)</entry><entry>Protocol</entry></row>
///	    </thead>
///	    <tbody>
///   	    <row><entry>0001</entry><entry>802.3</entry></row>
///   	    <row><entry>0002</entry><entry>AX.25</entry></row>
///   	    <row><entry>0004</entry><entry>802.2</entry></row>
///   	    <row><entry>0005</entry><entry>SNAP</entry></row>
///   	    <row><entry>0009</entry><entry>Localtalk</entry></row>
///   	    <row><entry>0800</entry><entry>IP</entry></row>
///   	    <row><entry>0805</entry><entry>X.25</entry></row>
///   	    <row><entry>0806</entry><entry>ARP</entry></row>
///   	    <row><entry>8035</entry><entry>RARP</entry></row>
///   	    <row><entry>8100</entry><entry>802.1Q VLAN</entry></row>
///   	    <row><entry>8137</entry><entry>IPX</entry></row>
///   	    <row><entry>86DD</entry><entry>IPv6</entry></row>
///	    </tbody>
///	    </tgroup>
///	    </table>
///       </para></listitem>
///    </varlistentry>
///
///    <varlistentry><term>truesize</term>
///       <listitem><para>
///           The size of the received data.
///       </para></listitem>
///    </varlistentry>
///
///  </variablelist>
///</para>
// Main device receive routine, be called when packet arrives on network device
probe netdev.receive
        = kernel.trace("netif_receive_skb") !,
          kernel.function("netif_receive_skb_internal") !,
	  kernel.function("netif_receive_skb")
{
	try { dev_name = get_netdev_name($skb->dev) } catch { }
	try { length = $skb->len } catch { }
	try { protocol = $skb->protocol } catch { }
	try { truesize = $skb->truesize } catch { }
}

/**
 * probe netdev.transmit - Network device transmitting buffer
 * @dev_name: The name of the device. e.g: eth0, ath1.
 * @length: The length of the transmit buffer.
 * @protocol: The protocol of this packet(defined in include/linux/if_ether.h).
 * @truesize: The size of the data to be transmitted.
 *
 */
// Queue a buffer for transmission to a network device
probe netdev.transmit
	= kernel.function("__dev_queue_xmit") !,
	  kernel.function("dev_queue_xmit")
{
	try { dev_name = get_netdev_name($skb->dev) } catch { }
	try { length = $skb->len } catch { }
	try { protocol = $skb->protocol } catch { }
	try { truesize = $skb->truesize } catch { }
}

/**
 * probe netdev.change_mtu - Called when the netdev MTU is changed
 * @dev_name: The device that will have the MTU changed
 * @old_mtu: The current MTU
 * @new_mtu: The new MTU
 */
probe netdev.change_mtu
	= kernel.function("dev_set_mtu")
{
	old_mtu = $dev->mtu
	new_mtu = $new_mtu
	dev_name = get_netdev_name($dev)
}

/**
 * probe netdev.open - Called when the device is opened
 * @dev_name: The device that is going to be opened
 */
probe netdev.open
	= kernel.function("dev_open")
{
	dev_name = get_netdev_name($dev)
}

/**
 * probe netdev.close - Called when the device is closed
 * @dev_name: The device that is going to be closed
 */
probe netdev.close
	= kernel.function("dev_close")
{
	dev_name = get_netdev_name($dev)
}

/**
 * probe netdev.hard_transmit - Called when the devices is going to TX (hard)
 * @dev_name: The device scheduled to transmit
 * @protocol: The protocol used in the transmission
 * @length: The length of the transmit buffer.
 * @truesize: The size of the data to be transmitted.
 */
probe netdev.hard_transmit
	= kernel.function("dev_hard_start_xmit") ?
{
	dev_name = get_netdev_name($dev)
	try { protocol = @choose_defined($first, $skb)->protocol } catch { }
	try { length = @choose_defined($first, $skb)->len } catch { }
	try { truesize = @choose_defined($first, $skb)->truesize } catch { }
}

/**
 * probe netdev.rx - Called when the device is going to receive a packet
 * @dev_name: The device received the packet
 * @protocol: The packet protocol
 */
probe netdev.rx
	= kernel.function("netif_rx")
{
	try {
		netdev = $skb->dev
		dev_name = get_netdev_name(netdev)
	} catch { }
	try { protocol = $skb->protocol } catch { }
}

/**
 * probe netdev.change_rx_flag - Called when the device RX flag will be changed
 * @dev_name: The device that will be changed
 * @flags: The new flags
 */
probe netdev.change_rx_flag
	= kernel.function("dev_change_rx_flags") ?
{
	dev_name = (@defined($dev) ? get_netdev_name($dev) : "unknown")
	flags = @choose_defined($flags, 0)
}

/**
 * probe netdev.set_promiscuity - Called when the device enters/leaves promiscuity
 * @dev_name: The device that is entering/leaving promiscuity mode
 * @enable: If the device is entering promiscuity mode
 * @disable: If the device is leaving promiscuity mode
 * @inc: Count the number of promiscuity openers
 */
probe netdev.set_promiscuity
	= kernel.function("dev_set_promiscuity")
{
	dev_name = get_netdev_name($dev)
	if ($inc){
		enable = 1
	} else {
		disable = 1
	}
	inc = $inc
}

/**
 * probe netdev.ioctl - Called when the device suffers an IOCTL
 * @cmd: The IOCTL request
 * @arg: The IOCTL argument (usually the netdev interface)
 */
probe netdev.ioctl
	= kernel.function("dev_ioctl")
{
	cmd = $cmd
	arg = user_string_quoted(@choose_defined($ifr, $arg))
}

/**
 * probe netdev.register - Called when the device is registered
 * @dev_name: The device that is going to be registered
 */
probe netdev.register
	= kernel.function("register_netdevice"),
	  kernel.function("register_netdev")
{
	dev_name = get_netdev_name($dev)
}

/**
 * probe netdev.unregister - Called when the device is being unregistered
 * @dev_name: The device that is going to be unregistered
 */
probe netdev.unregister
	= kernel.function("unregister_netdev")
{
	dev_name = get_netdev_name($dev)
}

/**
 * probe netdev.get_stats - Called when someone asks the device statistics
 * @dev_name: The device that is going to provide the statistics
 */
probe netdev.get_stats
	= kernel.function("dev_get_stats") ?
{
	dev_name = get_netdev_name($dev)
}

/**
 * probe netdev.change_mac - Called when the netdev_name has the MAC changed
 * @dev_name: The device that will have the MAC changed
 * @mac_len: The MAC length
 * @old_mac: The current MAC address
 * @new_mac: The new MAC address
 */
probe netdev.change_mac
	= kernel.function("dev_set_mac_address") ?
{
	dev_name = get_netdev_name($dev)
	mac_len = $dev->addr_len

	// Old MAC Address
	old_mac = sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
			  $dev->dev_addr[0], $dev->dev_addr[1],
			  $dev->dev_addr[2], $dev->dev_addr[3],
			  $dev->dev_addr[4], $dev->dev_addr[5])

	// New MAC Address
	new_mac = sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
			  $sa->sa_data[0], $sa->sa_data[1],
			  $sa->sa_data[2], $sa->sa_data[3],
			  $sa->sa_data[4], $sa->sa_data[5])
}
