Here we present the steps taken for setting up a test that uses both
the ETF qdisc and the TAPRIO one. That means that we'll use a (Qbv-like)
port scheduler with a fixed Tx schedule for traffic classes (TC), while
using Time-based transmission for controlling the Tx time of packets within
each TC.

The 'talker' side of the example described below will have 2 applications
transmitting time-sensitive traffic following a strict cyclic schedule.
In addition to that, iperf3 is used to transmit best-effort traffic on the
port. The port schedule is thus comprised by 3 time-slices, with a total
cycle-time of 1 millisecond allocated as:

 - Traffic Class 0 (TC 0): duration of 300us, 'strict txtime' is used.
 - Traffic Class 1 (TC 1): duration of 300us, 'deadline txtime' is used.
 - Traffic Class 2 (TC 2): duration of 400us, best-effort traffic.

The system is configured so the application enqueueing packets for
TC 0 will set its packets *Tx time* with an offset of 250us within its
time-slice.

The application enqueueing packets for TC 1 will set its packets *deadline*
with an offset of 250us within its time-slice. However, because this TC is
using the deadline mode of SO_TXTIME + etf, then a packet maybe transmitted
at anytime within its time slice that is before its deadline.

Best-effort traffic is transmitted at anytime during the third time slice.

A away to visualize this cycle and its time-slices is:

          |______x_|......D_|bbbbbbbbbb|
          0        299      599        999us


The application for each time-sensitive traffic class will transmit a packet
every 1ms. The packet's txtime is set through the SO_TXTIME api, and is
copied into the packet's payload.

At the 'listener' side, we capture traffic and then post-process it to
verify if packets are arriving outside of the time-slice they belong to.

ptp4l is used for synchronizing the PHC clocks over the network and
phc2sys is used on the 'talker' size for synchronizing the system
clock to the PHC.

CLOCK_TAI is the reference clockid used throughout the example for the
qdiscs and the applications.


# LISTENER #
    1) Setup network
	sudo ip addr add 192.168.0.78/4 broadcast 192.168.0.255 dev enp3s0


    2) Start time sync (ptp master)
	sudo ./setup_clock_sync.sh -i enp3s0 -m -v


    3) Start iperf server
	iperf3 -s


    4) Prepare 'dump-classifier' files. Please refer to
       README.classifier for further information.

	--filters--
	talker_strict :: udp port 7788
	talker_deadline :: udp port 7798

	--gates.sched--
	S 0x1 300000
	S 0x2 300000
	S 0xc 400000


    5) Start capturing traffic:

	sudo tcpdump -c 600000 -i enp3s0 -w tmp.pcap -j adapter_unsynced \
	  -tt --time-stamp-precision=nano

    6) Use the talkers to transmit packets as described on the next section.


    6) After traffic was captured, check if packets arrived outside of their
       time-slices. The base-time comes from the udp_tai for TC 0 minus the
       250us txtime offset as used below. For example:

	./dump-classifier -b 1528320726000000000 -d tmp.pcap -f filter \
	  -s gates.sched | grep -v ontime



# TALKER #
    1) Setup network
	sudo ip addr add 192.168.0.77/4 broadcast 192.168.0.255 dev enp3s0

    2) Setup qdiscs

      2.0) Prepare sched file for taprio
	--gates.sched--
	S 0x1 300000
	S 0x2 300000
	S 0xc 400000


      2.1) Setup taprio with a base-time starting in 2min from now rounded down.
           We must add the 37s UTC-TAI offset to the timestamp we get with 'date'.

	i=$((`date +%s%N` + 37000000000 + (2 * 60 * 1000000000))) ;       \
	base=$(($i - ($i % 1000000000))) ;                                \
	tc qdisc add dev enp3s0 parent root handle 100 taprio num_tc 3    \
	  map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 0 1 2 2              \
	  sched-file gates.sched base-time $base clockid CLOCK_TAI

           We can read the above as:
            - there are 3 traffic classes (num_tc 3);
            - SO_PRIORITY value 3 maps to TC 0, while value 2 maps to TC 1. 
              Everything else maps to the other (best-effort) traffic classes;
            - "queues 0 1 2 2" is a positional argument, meaning that TC 0 maps
              to queue 0, TC 1 maps to queue 1 and TC 2 maps to queues 2 and 3.
            - gates.sched is used as schedule file;
            - the reference clock is CLOCK_TAI;


      2.2) Setup etf for queue TC 0:

	tc qdisc replace dev enp3s0 parent 100:1 etf clockid CLOCK_TAI \
	  delta 200000 offload


      2.3) Setup etf in deadline mode for TC 1:

	tc qdisc replace dev enp3s0 parent 100:2 etf clockid CLOCK_TAI \
	  delta 200000 offload deadline_mode


    3) Start time sync (ptp slave):
	sudo ./setup_clock_sync.sh -i enp3s0 -s -v


    4) Start iperf3 client:
	iperf3 -c 192.168.0.78 -t 600 --fq-rate 100M


    5) Start udp_tai for TC 0. Use a base-time starting in 1min from now + a
       250us offset for txtime:

	now=`date +%s%N` ; i=$(($now + 37000000000 + (60 * 1000000000))) ; \
	base=$(($i - ($i % 1000000000) + 250000)) ;                        \
	sudo ./udp_tai -i enp3s0 -b $base -P 1000000 -t 3 -p 90 -d 600000  \
          -u 7788


    6) Start udp_tai in deadline mode for TC 1. Use the txtime computed for
       the previous traffic class (above) and add 300us so it falls under the
       second time slice (TC 1). For example, if the instance of udp_tai executed
       on the previous step printed
       "txtime of 1st packet is: 1528320726000250000", then the now we should do:

	sudo ./udp_tai -i enp3s0 -t 2 -p 90 -D -d 600000 \
          -b 1528320726000550000 -u 7798