A live trace: one TCP connection on a real hostЖивий приклад: одне TCP-з’єднання на справжньому хості

The simulation describes the path from the kernel source. Here is the same path recorded on a running machine: every kernel function that touched the packets of one curl request, with the matching step of the simulation next to it. Симуляція описує шлях за кодом ядра. Тут той самий шлях, записаний на живій машині: кожна функція ядра, через яку пройшли пакети одного запиту curl, і відповідний крок симуляції поруч.

The setupЩо і де

# terminal 1
pwru --output-caller --timestamp relative "host 1.1.1.1 and tcp port 80"
# terminal 2
curl http://1.1.1.1/

How to read the excerpts: the first column is the device (name:ifindex, 0 when skb->dev is not set), then skb->len, then the function and who called it. Small helpers (checksum, feature checks, freeing memory) are cut out; nothing is reordered.Як читати витяги: перша колонка — пристрій (ім’я:ifindex, 0, коли skb->dev не задано), далі skb->len, далі функція і хто її викликав. Дрібні допоміжні функції (контрольні суми, перевірки можливостей, звільнення пам’яті) прибрано; порядок не змінено.

What happens, in orderЩо відбувається, по порядку

One curl request is a handful of packets between this host and 1.1.1.1. Sections 1–6 follow them in order, each looked at from inside the kernel. Click a row to jump to it.Один запит curl — це кілька пакетів між цим хостом і 1.1.1.1. Розділи 1–6 йдуть за ними по порядку, кожен — зсередини ядра. Натисни рядок, щоб перейти до розділу.

Section 7 is not part of this request: it shows ordinary tools (ss, tc, tcpdump…) run on the same host during a longer download. Section 8 shows how to repeat all of this on your own machine.Розділ 7 — не частина цього запиту: там звичайні інструменти (ss, tc, tcpdump…), запущені на тому ж хості під час довшого завантаження. Розділ 8 — як повторити все це на своїй машині.

1. SYN: who creates it1. SYN: хто його створює

call stackстек викликів
ip_local_out
ip_queue_xmit
__tcp_transmit_skb
tcp_connect
tcp_v4_connect
__inet_stream_connect
inet_stream_connect
__sys_connect
__x64_sys_connect
do_syscall_64
entry_SYSCALL_64_after_hwframe

Read from the bottom: curl calls connect(), TCP builds the SYN in tcp_connect() and sends it through the same __tcp_transmit_skb() → ip_queue_xmit() → ip_local_out() as any data segment. Only the entry point differs from the Send scenario, which starts at write().Читати знизу: curl викликає connect(), TCP будує SYN у tcp_connect() і відправляє його тим самим шляхом __tcp_transmit_skb() → ip_queue_xmit() → ip_local_out(), що й будь-який сегмент з даними. Від сценарію «Відправка», який починається з write(), відрізняється лише точка входу.

In the simulation:У симуляції: Send 5–7Відправка 5–7

2. SYN: down through IP, the bridge and the card2. SYN: вниз через IP, бридж і карту

functions in orderфункції по черзі
       0    60  ip_local_out                       ← __ip_queue_xmit
       0    60  __ip_local_out                     ← ip_local_out
       0    60  nf_hook_slow                       ← __ip_local_out
       0    60  apparmor_ip_localout               ← nf_hook_slow
       0    60  ip_output                          ← ip_local_out
 vmbr0:4    60  nf_hook_slow                       ← ip_output
 vmbr0:4    60  apparmor_ip_postroute              ← nf_hook_slow
 vmbr0:4    60  ip_finish_output                   ← ip_output
 vmbr0:4    60  __ip_finish_output                 ← ip_finish_output
 vmbr0:4    60  ip_finish_output2                  ← __ip_finish_output
 vmbr0:4    74  __dev_queue_xmit                   ← ip_finish_output2
 vmbr0:4    74  validate_xmit_skb                  ← __dev_queue_xmit
 vmbr0:4    74  dev_hard_start_xmit                ← __dev_queue_xmit
 vmbr0:4    74  dev_queue_xmit_nit                 ← dev_hard_start_xmit
 vmbr0:4    74  skb_clone                          ← dev_queue_xmit_nit
 vmbr0:4    74  packet_rcv                         ← dev_queue_xmit_nit
 vmbr0:4    74  sk_skb_reason_drop(SKB_CONSUMED)   ← packet_rcv
 vmbr0:4    74  br_dev_xmit                        ← dev_hard_start_xmit
 vmbr0:4    74  br_nf_dev_xmit                     ← br_dev_xmit
 vmbr0:4    60  br_allowed_ingress                 ← br_dev_xmit
 vmbr0:4    60  br_forward                         ← br_dev_xmit
 vmbr0:4    60  __br_forward                       ← br_forward
  nic0:2    60  nf_hook_slow                       ← __br_forward
  nic0:2    60  br_forward_finish                  ← __br_forward
  nic0:2    60  nf_hook_slow                       ← br_forward_finish
  nic0:2    60  br_nf_post_routing                 ← nf_hook_slow
  nic0:2    60  br_dev_queue_push_xmit             ← br_forward_finish
  nic0:2    74  __dev_queue_xmit                   ← br_dev_queue_push_xmit
  nic0:2    74  sch_direct_xmit                    ← __dev_queue_xmit
  nic0:2    74  dev_hard_start_xmit                ← sch_direct_xmit
  nic0:2    74  skb_clone_tx_timestamp             ← e1000_xmit_frame[e1000e]
  nic0:2    74  dev_kfree_skb_any_reason           ← e1000_put_txbuf[e1000e]
  nic0:2    74  sk_skb_reason_drop(SKB_CONSUMED)   ← dev_kfree_skb_any_reason

3. SYN-ACK: from the interrupt up to the socket3. SYN-ACK: від переривання вгору до сокета

call stackстек викликів
tcp_v4_rcv
ip_local_deliver_finish
ip_local_deliver
ip_rcv
__netif_receive_skb_one_core
__netif_receive_skb
netif_receive_skb
br_pass_frame_up
br_handle_frame_finish
br_handle_frame
__netif_receive_skb_core
__netif_receive_skb_list_core
netif_receive_skb_list_internal
napi_complete_done
e1000e_poll
__napi_poll
net_rx_action
handle_softirqs
__irq_exit_rcu
irq_exit_rcu
common_interrupt

Again from the bottom. The card raised an interrupt; on the way out of it the kernel runs the network softirq, net_rx_action(), which calls the driver’s NAPI poll, e1000e_poll(). When the poll is done, GRO hands over its batch, and __netif_receive_skb_core() processes the frame on nic0. nic0 is a bridge port, so its rx_handler is br_handle_frame(). The frame is addressed to the bridge itself, so br_pass_frame_up() passes it up: netif_receive_skb() runs a second time, now on vmbr0, and from there it is the usual ip_rcv() → ip_local_deliver() → tcp_v4_rcv(). All of it in one softirq, with no process involved.Знову знизу. Карта підняла переривання; на виході з нього ядро запускає мережевий softirq, net_rx_action(), а той — NAPI-опитування драйвера, e1000e_poll(). Коли опитування закінчено, GRO віддає свою пачку, і __netif_receive_skb_core() обробляє кадр на nic0. nic0 — порт бриджа, тож його rx_handler — br_handle_frame(). Кадр адресовано самому бриджу, тому br_pass_frame_up() передає його вгору: netif_receive_skb() виконується вдруге, тепер на vmbr0, а далі звичний ip_rcv() → ip_local_deliver() → tcp_v4_rcv(). Усе в одному softirq, без жодного процесу.

In the simulation:У симуляції: Receive 3–8Прийом 3–8 · Bridge 5–6 (the rx_handler branch)Бридж 5–6 (гілка rx_handler)

  nic0:2    60  br_nf_pre_routing                  ← br_handle_frame
  nic0:2    60  br_handle_frame_finish             ← br_handle_frame
  nic0:2    60  br_pass_frame_up                   ← br_handle_frame_finish
 vmbr0:4    60  nf_hook_slow                       ← br_pass_frame_up
 vmbr0:4    60  br_nf_local_in                     ← nf_hook_slow
 vmbr0:4    60  netif_receive_skb                  ← br_pass_frame_up
 vmbr0:4    60  __netif_receive_skb                ← netif_receive_skb
 vmbr0:4    60  deliver_skb                        ← __netif_receive_skb_core
 vmbr0:4    60  packet_rcv                         ← deliver_skb
 vmbr0:4    60  ip_rcv                             ← __netif_receive_skb_one_core
 vmbr0:4    60  ip_rcv_core                        ← ip_rcv
 vmbr0:4    60  nf_hook_slow                       ← ip_rcv
 vmbr0:4    60  ip_sabotage_in                     ← nf_hook_slow
 vmbr0:4    60  ip_rcv_finish_core                 ← ip_rcv
 vmbr0:4    60  tcp_v4_early_demux                 ← ip_rcv_finish_core
 vmbr0:4    60  ip_route_input_noref               ← ip_rcv_finish_core
 vmbr0:4    60  ip_route_input_slow                ← ip_route_input_noref
 vmbr0:4    60  ip_local_deliver                   ← ip_rcv
 vmbr0:4    60  nf_hook_slow                       ← ip_local_deliver
 vmbr0:4    60  ip_local_deliver_finish            ← ip_local_deliver
 vmbr0:4    40  ip_protocol_deliver_rcu            ← ip_local_deliver_finish
 vmbr0:4    40  raw_local_deliver                  ← ip_protocol_deliver_rcu
 vmbr0:4    40  tcp_v4_rcv                         ← ip_protocol_deliver_rcu
       0    40  tcp_v4_do_rcv                      ← tcp_v4_rcv
       0    40  tcp_rcv_state_process              ← tcp_v4_do_rcv
       0    40  tcp_finish_connect                 ← tcp_rcv_state_process
       0    40  tcp_init_transfer                  ← tcp_finish_connect

4. ACK: sent without any system call4. ACK: відправлений без жодного системного виклику

call stackстек викликів
ip_local_out
ip_queue_xmit
__tcp_transmit_skb
__tcp_send_ack
tcp_rcv_state_process
tcp_v4_do_rcv
tcp_v4_rcv
… the same receive stack as in section 3, down to common_interruptтой самий стек прийому, що в розділі 3, до common_interrupt

The third packet of the handshake has no system call behind it. It is sent while the kernel is still processing the SYN-ACK, inside the same softirq: tcp_rcv_state_process() calls __tcp_send_ack(), and the ACK takes the usual send path from __tcp_transmit_skb() down. Most ACKs during a download are sent like this, from the receive path.За третім пакетом рукостискання немає системного виклику. Він відправляється, поки ядро ще обробляє SYN-ACK, у тому самому softirq: tcp_rcv_state_process() викликає __tcp_send_ack(), і ACK іде звичним шляхом відправки від __tcp_transmit_skb() вниз. Більшість ACK під час завантаження відправляються саме так, зі шляху прийому.

5. The request out, the response data in5. Запит назовні, дані відповіді всередину

functions in orderфункції по черзі
ip_local_out
ip_queue_xmit
__tcp_transmit_skb
tcp_write_xmit
__tcp_push_pending_frames
tcp_push
tcp_sendmsg_locked
tcp_sendmsg
inet_sendmsg
__sys_sendto
__x64_sys_sendto
do_syscall_64
entry_SYSCALL_64_after_hwframe

The HTTP request. curl uses send() (on x86-64 it is the sendto system call) rather than write(), but from inet_sendmsg() on it is the Send scenario as written: the data is copied into the socket write queue in tcp_sendmsg_locked(), and tcp_push() → tcp_write_xmit() sends it.HTTP-запит. curl використовує send() (на x86-64 це системний виклик sendto), а не write(), але від inet_sendmsg() далі це сценарій «Відправка» як він є: дані копіюються в чергу запису сокета в tcp_sendmsg_locked(), а tcp_push() → tcp_write_xmit() їх відправляє.

In the simulation:У симуляції: Send 2–5Відправка 2–5

 vmbr0:4   438  ip_rcv_finish_core                 ← ip_rcv
 vmbr0:4   438  tcp_v4_early_demux                 ← ip_rcv_finish_core
 vmbr0:4   438  ip_local_deliver                   ← ip_rcv
 vmbr0:4   438  ip_local_deliver_finish            ← ip_local_deliver
 vmbr0:4   418  ip_protocol_deliver_rcu            ← ip_local_deliver_finish
 vmbr0:4   418  tcp_v4_rcv                         ← ip_protocol_deliver_rcu
       0   418  tcp_v4_do_rcv                      ← tcp_v4_rcv
       0   418  tcp_rcv_established                ← tcp_v4_do_rcv
       0   418  tcp_data_queue                     ← tcp_rcv_established
       0   386  tcp_queue_rcv                      ← tcp_data_queue
       0   386  tcp_event_data_recv                ← tcp_data_queue

A packet of the response (the part before ip_rcv_finish_core() is the same as in section 3). Compare with the SYN-ACK: after tcp_v4_early_demux() comes ip_local_deliver() directly. There is no ip_route_input_*, because the route cached in the socket was used. The connection is established, so TCP goes through tcp_rcv_established(). tcp_data_queue() pulls the 32-byte TCP header (20 bytes plus the timestamp option; 418 → 386) and tcp_queue_rcv() puts the payload on the socket receive queue, where it waits for curl to read it.Пакет відповіді (до ip_rcv_finish_core() усе як у розділі 3). Порівняйте з SYN-ACK: після tcp_v4_early_demux() одразу йде ip_local_deliver(). ip_route_input_* немає, бо використано маршрут, збережений у сокеті. З’єднання встановлене, тож TCP іде через tcp_rcv_established(). tcp_data_queue() знімає 32-байтний TCP-заголовок (20 байтів плюс опція timestamp; 418 → 386), а tcp_queue_rcv() кладе дані в чергу прийому сокета, де вони чекають, поки curl їх прочитає.

In the simulation:У симуляції: Receive 9–10Прийом 9–10

6. Close: what was left in the queue6. Закриття: що лишилось у черзі

functions in orderфункції по черзі
       0     0  sk_skb_reason_drop(SKB_DROP_REASON_QUEUE_PURGE) ← sk_stream_kill_queues
       0     0  sock_rfree                         ← skb_release_head_state

The last line of the trace. When the socket is destroyed, sk_stream_kill_queues() empties its receive queue, and every skb still there is freed with the drop reason SKB_DROP_REASON_QUEUE_PURGE. This one carried no data (len 0). sock_rfree() is the destructor of received skbs: it returns the memory to the socket’s accounting. A drop reason in a trace is therefore not always a lost packet; here it is an ordinary close.Останній рядок трасування. Коли сокет знищується, sk_stream_kill_queues() спорожнює його чергу прийому, і кожен skb, що там лишився, звільняється з причиною SKB_DROP_REASON_QUEUE_PURGE. Цей не ніс даних (len 0). sock_rfree() — деструктор прийнятих skb: він повертає пам’ять в облік сокета. Тож причина «drop» у трасуванні не завжди означає втрачений пакет; тут це звичайне закриття.

7. Separately: the same host seen with ordinary tools7. Окремо: той самий хост звичайними інструментами

during a downloadпід час завантаження

These are the commands the simulation suggests at each step, run on the same host while it downloaded a file over HTTPS (the server is 172.66.0.218). Output is trimmed to the relevant lines.Це команди, які симуляція пропонує на кожному кроці, запущені на тому самому хості під час завантаження файлу по HTTPS (сервер — 172.66.0.218). Вивід скорочено до потрібних рядків.

ip route get · ip neigh Send 6, 10Відправка 6, 10

$ ip route get 172.66.0.218
172.66.0.218 via 10.0.0.1 dev vmbr0 src 10.0.0.2 uid 0
$ ip neigh show 10.0.0.1
10.0.0.1 dev vmbr0 lladdr 02:00:00:00:00:01 DELAY

The route goes through the router on vmbr0, not nic0: the bridge holds the IP address. The router’s neighbour entry is in DELAY: it had gone STALE, a packet was just sent through it, and the kernel now waits up to delay_first_probe_time (5 seconds here) for a confirmation that the router is reachable before it sends ARP probes.Маршрут іде через роутер на vmbr0, а не nic0: IP-адреса на бриджі. Запис сусіда-роутера в стані DELAY: він був STALE, через нього щойно відправили пакет, і ядро тепер до delay_first_probe_time (тут 5 секунд) чекає підтвердження, що роутер досяжний, перш ніж слати ARP-запити.

ss -tmi Receive 9–10Прийом 9–10

State Recv-Q Send-Q Local Address:Port  Peer Address:Port
ESTAB 67708  0      10.0.0.2:49120 172.66.0.218:https
  skmem:(r94848,rb131072,t0,tb46080,f3456,w0,o0,bl0,d2) cubic wscale:13,10 rtt:14.523/9.121
  mss:1388 pmtu:1500 rcvmss:1440 advmss:1448 cwnd:10 … rcv_wnd:23552

Recv-Q is data in the socket receive queue that curl has not read yet: 67 KB delivered by tcp_queue_rcv() and waiting for read(). skmem shows the memory behind it: r is the receive memory in use, rb the limit (sk_rcvbuf), d the packets the socket dropped.Recv-Q — дані в черзі прийому сокета, які curl ще не прочитав: 67 КБ, які поклав tcp_queue_rcv() і які чекають на read(). skmem показує пам’ять за ними: r — зайнята пам’ять прийому, rb — ліміт (sk_rcvbuf), d — пакети, які сокет відкинув.

tc -s qdisc Send 12Відправка 12

qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
 Sent 261828748172 bytes 303231108 pkt (dropped 0, overlimits 0 requeues 13109)
 backlog 0b 0p requeues 13109

The qdisc that the SYN bypassed. backlog 0b 0p: nothing waits in it, which is why bypass is the common case on a lightly loaded uplink. requeues counts the times the driver could not take a packet and it went back to the queue.Та сама qdisc, яку SYN обійшов. backlog 0b 0p: у ній нічого не чекає, тому на мало завантаженому каналі обхід — звичайна справа. requeues — скільки разів драйвер не зміг узяти пакет і той повертався в чергу.

tcpdump -e Receive 1Прийом 1 · Send 14Відправка 14

02:00:00:00:00:01 > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 1506: 172.66.0.218.443 > 10.0.0.2.49120: Flags [.], seq 502524510:502525950, ack 3776738090, win 16, options [nop,nop,TS val 3954928449 ecr 2048667830], length 1440
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype IPv4 (0x0800), length 66: 10.0.0.2.49120 > 172.66.0.218.443: Flags [.], ack 1440, win 2, options [nop,nop,TS val 2048667871 ecr 3954928420], length 0

One data frame in and one ACK out, captured on nic0. 1506 = 14 (Ethernet) + 20 (IP) + 32 (TCP with timestamps) + 1440 bytes of data; the ACK is 66 = 14 + 20 + 32. The capture itself is the AF_PACKET tap from sections 2 and 3: tcpdump gets its copies through dev_queue_xmit_nit() and deliver_skb().Один кадр з даними вхідний і один ACK вихідний, знято на nic0. 1506 = 14 (Ethernet) + 20 (IP) + 32 (TCP з timestamps) + 1440 байтів даних; ACK — 66 = 14 + 20 + 32. Сам запис — це той самий відвід AF_PACKET з розділів 2 і 3: tcpdump отримує копії через dev_queue_xmit_nit() і deliver_skb().

/proc/interrupts · /proc/net/softnet_stat Receive 3Прийом 3

$ grep nic0 /proc/interrupts
 124:  354817914          0          0          0          0          0 IR-PCI-MSI  0-edge  nic0
$ head -2 /proc/net/softnet_stat
30533ca3 00000000 00000007 00000000 …
0daa289e 00000000 00000001 00000000 …

The card has one interrupt line, and all of its 354 million interrupts landed on CPU 0. Each starts the NAPI poll seen in section 3. In softnet_stat each line is a CPU and the values are hex: packets processed, packets dropped because the backlog queue was full (0 here), and “time squeeze”: the times net_rx_action() stopped because it ran out of its packet budget or time (7 on CPU 0).У карти одна лінія переривань, і всі її 354 мільйони переривань прийшли на CPU 0. Кожне запускає NAPI-опитування з розділу 3. У softnet_stat кожен рядок — це CPU, значення шістнадцяткові: оброблені пакети, пакети, відкинуті через переповнену чергу backlog (тут 0), і «time squeeze»: скільки разів net_rx_action() зупинявся, бо вичерпав бюджет пакетів або час (7 на CPU 0).

8. Follow it on your own machine8. Як простежити це на своїй машині

pwru answers one question: which functions did this packet pass through. It does not show everything:pwru відповідає на одне питання: через які функції пройшов цей пакет. Але не все:

Repeat the traceПовторити трасування

Needs root and a kernel 5.3 or newer built with BTF: check that /sys/kernel/btf/vmlinux exists. pwru loads eBPF programs on hundreds of kernel functions, so use a test machine and a narrow filter.Потрібні root і ядро 5.3 або новіше, зібране з BTF: перевір, що існує /sys/kernel/btf/vmlinux. pwru ставить eBPF-програми на сотні функцій ядра, тож краще тестова машина і вузький фільтр.

ls /sys/kernel/btf/vmlinux
# x86-64; other builds are on https://github.com/cilium/pwru/releases
curl -LO https://github.com/cilium/pwru/releases/download/v1.0.12/pwru-linux-amd64.tar.gz
curl -LO https://github.com/cilium/pwru/releases/download/v1.0.12/pwru-linux-amd64.tar.gz.sha256sum
sha256sum -c pwru-linux-amd64.tar.gz.sha256sum
tar xzf pwru-linux-amd64.tar.gz

# terminal 1: sections 2, 3, 5, 6
sudo ./pwru --output-caller "host 1.1.1.1 and tcp port 80"
# terminal 1, another run: the call stacks of sections 1, 3, 4, 5
sudo ./pwru --output-stack --filter-func '^(ip_local_out|tcp_v4_rcv)$' "host 1.1.1.1 and tcp port 80"
# terminal 2
curl http://1.1.1.1/

The same request from the application’s sideТой самий запит з боку застосунку

$ strace -f -e trace=connect,sendto,recvfrom curl -s -o /dev/null http://1.1.1.1/
connect(4, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("1.1.1.1")}, 16) = -1 EINPROGRESS
sendto(4, "GET / HTTP/1.1\r\nHost: 1.1.1.1\r\nU"..., 71, MSG_NOSIGNAL, NULL, 0) = 71
recvfrom(4, 0x5d12589ebae0, 102400, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(4, "HTTP/1.1 301 Moved Permanently\r\n"..., 102400, 0, NULL, NULL) = 386

Only three kinds of calls, and they line up with the trace. connect() returns at once with EINPROGRESS: curl uses a non-blocking socket, so the SYN leaves (section 2) and the SYN-ACK and ACK (sections 3–4) are handled by the kernel while curl does something else. sendto() hands over the 71-byte request (section 5). The first recvfrom() finds the receive queue empty (EAGAIN); the second takes 386 bytes, the size of the response payload that tcp_queue_rcv() put in the queue in section 5. The server returns the same short redirect every time.Лише три види викликів, і вони збігаються з трасуванням. connect() одразу повертає EINPROGRESS: curl використовує неблокувальний сокет, тож SYN іде (розділ 2), а SYN-ACK і ACK (розділи 3–4) ядро обробляє, поки curl робить щось інше. sendto() віддає 71-байтний запит (розділ 5). Перший recvfrom() бачить порожню чергу прийому (EAGAIN); другий забирає 386 байтів — розмір даних відповіді, які tcp_queue_rcv() поклав у чергу в розділі 5. Сервер щоразу повертає той самий короткий редирект.

The state at each layerСтан на кожному рівні

Replace eth0 with your interface. Every step of the simulation lists more commands like these under “Try it on a live system”.Підстав свій інтерфейс замість eth0. На кожному кроці симуляції є більше таких команд у блоці «Подивитися на живій системі».

LayerРівеньCommandКомандаWhat it showsЩо показуєStepКрок
AppЗастосунокstrace -f -e trace=connect,sendto,recvfrom curl -s -o /dev/null http://1.1.1.1/the system calls and how many bytes each one movedсистемні виклики і скільки байтів кожен передавSend 1Відправка 1
SocketСокетss -tmi dst 1.1.1.1bytes waiting in the socket queues, socket memory, RTT, cwndбайти в чергах сокета, пам’ять сокета, RTT, cwndReceive 10Прийом 10
TCPTCPnstat -az TcpOutSegs TcpRetransSegssegments sent and retransmitted, for the whole hostвідправлені й повторно відправлені сегменти, по всьому хостуSend 4Відправка 4
IPIPip route get 1.1.1.1the route, gateway, device and source address chosenобраний маршрут, шлюз, пристрій і адреса відправникаSend 6Відправка 6
netfilternetfilternft list rulesetthe rules that run in the hooksправила, що виконуються в хукахSend 7Відправка 7
NeighbourСусідиip neigh showMAC addresses of next hops and the state of each entryMAC-адреси наступних вузлів і стан кожного записуSend 10Відправка 10
qdiscqdisctc -s qdisc show dev eth0the queue discipline, its backlog, drops and requeuesдисципліна черги, її backlog, втрати і requeuesSend 11Відправка 11
Driver / NICДрайвер / картаethtool -S eth0the driver’s own counters, per queueвласні лічильники драйвера, по чергахSend 13Відправка 13
InterruptsПерериванняgrep eth0 /proc/interruptswhich CPUs take the card’s interruptsякі CPU приймають переривання картиReceive 2Прийом 2
NAPINAPIcat /proc/net/softnet_statper CPU: packets processed, backlog drops, time squeezeпо кожному CPU: оброблені пакети, втрати backlog, time squeezeReceive 3Прийом 3
WireДрітtcpdump -ni eth0 -e host 1.1.1.1the frames themselves, as the device sees themсамі кадри, як їх бачить пристрійSend 14Відправка 14
DropsВтратиperf trace -e skb:kfree_skbevery freed packet with the place and the reasonкожен звільнений пакет із місцем і причиноюReceive 5Прийом 5