Home / Alt manpages / tc-ife(8)

  • tc-ife(8)
  • Admin command
  • linux

Carry tc Metadata Across Ethernet with tc-ife

tc-ife lets you stash extra socket metadata inside an Ethernet frame and pull it back out on a second host, no VLAN tag required. You'll build the encoding sender and the decoding receiver, using the installed iproute2 6.1.0-1ubuntu6.4 documented in tc-ife(8). Budget about twenty minutes for a controlled test, plus time to track down the right interface and traffic.

  • Two test points. Two Linux hosts, or two network namespaces joined by a test link, both with tc from iproute2.
  • Root on both ends. Qdisc and filter changes need it.
  • A real interface name. The commands below use eth0 as a placeholder; swap in the interface actually carrying the traffic.

Warning

Do not paste these commands onto a production interface until you have checked the filter direction and have the rollback commands ready.

1. Confirm the installed action

Check the binary and package version first. These read-only commands need no elevated privileges:

$ command -v tc
/usr/sbin/tc
$ tc -V
tc utility, iproute2-6.1.0, libbpf 1.3.0

Then ask the action parser for its local syntax as a second check:

$ tc action add action ife help
Usage:... ife {decode|encode} [{ALLOW|USE} ATTR] [dst DMAC] [src SMAC] [type TYPE] [CONTROL] [index INDEX]
  • encode runs on the sending side.
  • decode runs on the receiving side.
  • Match the ethertype on both ends. This guide sets it explicitly to 0xdead rather than relying on the documented default of 0xED3E, so a capture and a filter always agree.

2. Record the current traffic-control state

Run these on each host before changing anything. They're ordinary inspection commands, though the output depends on whether the interface already has an ingress or root qdisc:

$ tc qdisc show dev eth0
$ tc filter show dev eth0 ingress
$ tc filter show dev eth0 egress

Save the output somewhere outside the shell history if this is a shared system: you'll need it if the interface already has filters that must be restored. The examples below use a root prio qdisc on the sender and an ingress qdisc on the receiver. If either qdisc already exists, fold the filter into the existing arrangement instead of bolting on another one.

Checkpoint

You should know which host sends the traffic, which receives it, and which qdisc handles the relevant direction.

3. Prepare the receiver for decoded metadata

This step changes the receiver's traffic-control configuration and needs root. The ingress qdisc gives the filter a place to run:

# tc qdisc add dev eth0 handle ffff: ingress

Now match the IFE ethertype and decode the metadata. The reclassify control action tells tc to classify the packet again after decoding, so a later filter can use the recovered mark:

# tc filter add dev eth0 parent ffff: prio 2 protocol 0xdead \
    u32 match u32 0 0 flowid 1:1 \
    action ife decode reclassify

On success, tc normally prints nothing. Verify the filter rather than trusting the exit status alone:

# tc -s filter show dev eth0 parent ffff:
# tc -s qdisc show dev eth0

The listing should show an ife decode reclassify action. Packet and byte counters stay at zero until matching traffic arrives.

4. Add a receiver filter for the recovered mark

The sender will encode an skb mark of decimal 17. Add a later receiver filter that matches that mark. Also root-only, also state-changing:

# tc filter add dev eth0 parent ffff: prio 4 protocol ip \
    handle 0x11 fw flowid 1:1 \
    action ok

The priority is deliberate: the decode filter runs at priority 2, the mark filter at priority 4, after reclassification. action ok accepts the classification path without touching the packet. If your real policy needs a different class or action, change only that later rule, once you've proved the mark arrives.

5. Configure the sender to encode the mark

Warning

This changes packet handling on the sender. The example targets ICMP traffic to one destination and rewrites the destination MAC in the encoded IFE metadata. Confirm the address is a test endpoint before applying it.

# tc qdisc add dev eth0 root handle 1: prio
# tc filter add dev eth0 parent 1: protocol ip prio 10 u32 \
    match ip dst 192.168.122.237/24 \
    match ip protocol 1 0xff flowid 1:2 \
    action skbedit mark 17 \
    action ife encode type 0xdead allow mark \
    dst 02:15:15:15:15:15

The first action sets the mark. The IFE action's allow mark clause whitelists that metadata for encoding. dst is an optional six-byte destination MAC carried by the action, not the same thing as the IP destination match. Keep the ethertype, mark value and MAC addresses consistent with the receiver and the real link.

Verify the sender-side filter and its counters:

# tc -s filter show dev eth0 parent 1:
# tc -s qdisc show dev eth0

Generate one known matching packet, then repeat the two inspection commands. A rising counter proves traffic matched the sender rule. It does not prove the receiver accepted the packet, so check the receiver counters too.

6. Choose static metadata only when you mean it

allow copies the selected value from the packet's current state. For a fixed value instead, the manual gives you use, which takes the value directly in the action. This encodes a fixed mark of 17:

# tc filter add dev eth0 parent 1: protocol ip prio 20 u32 \
    match ip dst 192.168.122.237/24 \
    action ife encode type 0xdead use mark 17 \
    dst 02:15:15:15:15:15
  • Do not stack this with the earlier rule unless the matches and priorities are genuinely distinct: two rules can both process the same packet and muddy your counters and captures.
  • The same split applies to prio and tcindex. allow selects existing metadata; use requires a value.
  • A zero mark is not encoded. The action increments its overlimits statistic and follows its configured control action instead.

7. Inspect failures before changing the design

  • Receiver counter at zero, sender counter also zero. The IP or protocol match did not select the traffic. Check the exact destination prefix, the ICMP protocol match, and the filter's parent qdisc.
  • Sender matches but receiver does not. Check the path and the ethertype: both IFE actions must use 0xdead in this example. A capture on the link can confirm the packet arrived, but do not infer decoded metadata from the outer packet alone; inspect the receiver's decode and later-filter counters once the packet lands.
  • Filter command reports an unknown device, wrong parent or missing qdisc. Stop and correct that topology detail. Do not keep changing priorities until the parent qdisc is valid. A successful tc filter add only confirms the kernel accepted the configuration; it does not validate your traffic match.

8. Remove the test configuration

Removal disrupts the traffic path, so stop the test traffic first. On the sender, remove the filter then the root qdisc:

# tc filter del dev eth0 parent 1: prio 10
# tc qdisc del dev eth0 root handle 1:

On the receiver, remove the filters then the ingress qdisc:

# tc filter del dev eth0 parent ffff: prio 2
# tc filter del dev eth0 parent ffff: prio 4
# tc qdisc del dev eth0 ingress

Those deletions remove the example qdiscs, plus any other filters attached to those exact parents that you did not preserve. If the interface had an existing configuration, restore it from the output saved in step 2 rather than treating this as a universal rollback. Finish by checking:

# tc qdisc show dev eth0
# tc filter show dev eth0 ingress
# tc filter show dev eth0 egress

Done means

  • Roles are right. The sender uses ife encode and the receiver uses ife decode.
  • Ethertypes match. Both actions use the same explicit value, 0xdead.
  • Sender counter moves. It increases for a deliberately matching packet.
  • Receiver counters confirm the path. Both the decode and later mark-filter counters show the metadata arriving.
  • Original state is restored. Any existing qdisc and filter configuration is back in place after the test.