Docs Read SIP inside tunnels

Read SIP inside tunnels

Which link types, EtherTypes and tunnels sipnab can read a SIP dialog out of, which it cannot, and what it reports when a frame does not decode.

On this page

Whether sipnab can read the SIP in your capture when something wraps it: a VLAN tag, an MPLS label, a tunnel. The first table answers that. The sections after it give:

  • how to check that sipnab decoded a capture,
  • what sipnab reports when a frame does not decode,
  • the exact link types, EtherTypes and tunnels.

Can sipnab read my capture?

Your SIP travels insideFrom a capture fileFrom a live capture
Ethernet, Linux cooked (any), raw IP, loopback, PPP or PPPoEyesyes
VLAN tags: 802.1Q, 802.1ad, QinQ, or legacy 0x9100yesyes
MPLSyes, from an Ethernet capture. Not from a Linux cooked onename the interface with -d, because the default any device produces a cooked capture
NSH, PBB or MACsecyes, from an Ethernet capture. MACsec only when it protects integrity without encryptingname the interface with -d and pass your own BPF filter that adds them, such as portrange 5060-5061 or ether proto 0x88e5 for MACsec
IP-in-IP, 6in4, GRE, GRE bridging, MPLS-in-IP or AHyespass your own BPF filter that adds them, such as portrange 5060-5061 or ip proto 47 for GRE, because the generated one matches UDP and TCP ports only
GTP-U, VXLAN or GENEVEyesadd --capture-tunnels
Teredo or L2TPv2yes (L2TPv2 data messages only)add --capture-tunnels=3544 or --capture-tunnels=1701
ESPonly when NULL-encryptedpass your own BPF filter that adds it, such as portrange 5060-5061 or ip proto 50
SCTPyes, including a message split across DATA chunksyes: the generated filter’s portrange matches SCTP as well as UDP and TCP
UDP-encapsulated ESP, or L2TPv3 over UDPnono
anything elsenono

Whenever the answer is no, sipnab says so.

  • It counts the frames it cannot decode in a NOT DECODED line at the end of the run.
  • The line gives the number that identifies each: the link type, the EtherType or the IP protocol.
  • What sipnab says when it cannot read a frame shows that line.

Check that sipnab decoded your capture

Read the capture in batch mode and look at the summary line at the end:

sipnab -N -I tests/pcap-samples/linux-sll2-pppoe.pcap --no-cli-print

This capture carries SIP and RTP inside PPPoE, on a Linux cooked v2 link. Output:

2026-10-18T15:22:21.376621Z  INFO sipnab::capture::file: Reading from 'tests/pcap-samples/linux-sll2-pppoe.pcap'
2026-10-18T15:22:21.376908Z  INFO sipnab::capture::file: File reader finished: 27 packets total, through 'tests/pcap-samples/linux-sll2-pppoe.pcap'
2026-10-18T15:22:21.376957Z  INFO sipnab::capture::file: Read 27 packets: 1 of 1 file(s) read in full
2026-10-18T15:22:21.378110Z  INFO sipnab::app::batch: sipnab: 27 packets captured, 7 SIP messages, 20 RTP packets across 2 streams

The 7 SIP messages and 20 RTP packets account for all 27 packets, and no NOT DECODED line follows. sipnab read every frame.

What sipnab says when it cannot read a frame

sipnab drops nothing on this page silently. A frame it does not decode still increments a counter keyed by the number it did not recognize. That count reaches:

  • the run summary,
  • --report,
  • --json,
  • /v1/stats,
  • MCP capture_status,
  • the Prometheus capture family.

The output below comes from a copy of tests/pcap-samples/sip-register.pcap with its file header set to link type 147, a private-use value sipnab has no decoder for:

sipnab -N -I link147.pcap --no-cli-print
2026-10-18T15:22:41.709205Z  WARN sipnab::capture::file: 'link147.pcap' has link type 147, which sipnab does not decode; its frames are counted as not decoded
2026-10-18T15:22:41.709370Z  INFO sipnab::capture::file: Reading from 'link147.pcap'
2026-10-18T15:22:41.709521Z  INFO sipnab::capture::file: File reader finished: 2 packets total, through 'link147.pcap'
2026-10-18T15:22:41.709568Z  INFO sipnab::capture::file: Read 2 packets: 1 of 1 file(s) read in full
2026-10-18T15:22:41.709954Z  INFO sipnab::app::batch: sipnab: 2 packets captured, 0 SIP messages, 0 RTP packets across 0 streams
NOT DECODED: 2 of 2 frame(s) (100.0%) produced nothing and are in none of the counts above. Reasons: unsupported link type 147 (2). NOTHING IN THIS CAPTURE WAS READ — every frame failed to decode, so the totals above describe no traffic whatsoever and a zero among them is not evidence of absence.
2026-10-18T15:22:41.710079Z  WARN sipnab::app::batch: capture quality: 2 frame(s) reached sipnab intact and could not be decoded at all, so nothing in them was analyzed (see the NOT DECODED line above for which link types, EtherTypes and IP protocols). THIS ANALYSIS IS INCOMPLETE — the counts above are what sipnab RECEIVED AND UNDERSTOOD, not what crossed the wire. See docs/tuning-capture.md.
No SIP traffic was decoded — but 2 of 2 frame(s) (100.0%) could not be decoded at all, so this is not a finding that the capture contains no SIP. Undecodable: unsupported link type 147 (2).
Fix the decode before reading anything into the zero above: convert the capture (editcap -T ether) or open an issue naming the number(s) reported.
  • A capture with no SIP and no undecodable frames is a finding: the SIP is not there.
  • One with no SIP and a pile of undecodable frames means sipnab could not read it. That is a different problem.

Troubleshooting says what each reason means and what to do about it.

sipnab declines a frame rather than guess

Every decoder checks the structure of a frame (reserved bits, version fields, lengths, the shape of the first inner header) before it decapsulates, and leaves a frame that fails any check as it is. If no other decoder reads that frame, it joins the NOT DECODED count. For example, sipnab does not decapsulate an RTP packet as VXLAN because it happens to use UDP port 4789.

DLTNameStatus
1Ethernetdecoded
0BSD loopback (DLT_NULL)decoded — address family in host byte order
108OpenBSD loopback (DLT_LOOP)decoded — address family always big-endian
12Raw IP (DLT_RAW)decoded — the version nibble picks v4 or v6
113Linux cooked v1 (SLL)decoded
276Linux cooked v2 (SLL2)decoded
9PPP (DLT_PPP)decoded — with or without RFC 1662 HDLC-like framing
50PPP in HDLC-like framing (DLT_PPP_SERIAL)decoded
51PPPoE session (DLT_PPP_ETHER)decoded
228bare IPv4 (DLT_IPV4)decoded — sipnab checks the version nibble against the link type and rejects a mismatch
229bare IPv6 (DLT_IPV6)decoded — same check, the other way
any other—counted and named as unsupported link type N

Loopback is DLT_EN10MB on Linux but DLT_NULL on macOS and BSD. That is why 0 and 108 matter for the common “SIP server listening on loopback” case.

EtherTypes

EtherTypeProtocolReferenceStatus
0x0800IPv4RFC 894decoded
0x86DDIPv6RFC 2464decoded
0x8100C-VLAN tagIEEE 802.1Qskipped to reach IP
0x88A8S-VLAN tagIEEE 802.1Qskipped to reach IP
0x9100legacy QinQunregisteredskipped to reach IP
0x8864PPPoE SessionRFC 2516decapsulated
0x8863PPPoE DiscoveryRFC 2516never decapsulated — counted and named like any EtherType sipnab does not walk
0x8847MPLS unicastRFC 5332decapsulated
0x8848MPLS upstream-assignedRFC 5332decapsulated
0x894FNSHRFC 8300decapsulated
0x88E7PBB I-TAGIEEE 802.1Qdecapsulated to the customer frame
0x88E5MACsecIEEE 802.1AEsee below
any other——counted and named with its hex value

0x9100 has no IEEE registration. sipnab reads it because older carrier equipment still sends it.

sipnab deliberately never decapsulates PPPoE Discovery. Its payload is TLV tags. So reading it as an IP header would report addresses the wire never carried.

MACsec is not always encrypted

The SecTAG’s E and C bits distinguish confidentiality from integrity.

  • sipnab decodes an integrity-protected frame that carries no encryption as the ordinary readable plaintext it is.
  • sipnab reports only an encrypted payload as encrypted.

A cooked capture reaches fewer of them

The table above describes the Ethernet walk.

  • A Linux cooked capture — SLL or SLL2 — runs a shorter one:
    • VLAN tags,
    • then IPv4, IPv6 or PPPoE Session,
    • and nothing else.
  • That matters more than it sounds:
    • SLL2 is what the any pseudo-device produces.
    • any is what sipnab opens on Linux when no -d names an interface.
EtherTypeEthernet (-d eth0)Cooked (any, or a file from tcpdump -i any)
0x8100 / 0x88A8 / 0x9100 VLANskipped to reach IPskipped to reach IP
0x0800 / 0x86DD IPdecodeddecoded
0x8864 PPPoE Sessiondecapsulateddecapsulated
0x8847 / 0x8848 MPLSdecapsulatednot walked
0x894F NSHdecapsulatednot walked
0x88E7 PBB I-TAGdecapsulatednot walked
0x88E5 MACsecdecoded, or named as encryptednot walked

So SIP inside an MPLS label stack reaches the parser from -d eth0. It does not reach it from the default device.

  • A frame the cooked walk declines invents nothing. It fails to decode and joins the undecodable count with its own reason.
  • But the remedy is the capture device rather than the filter.
  • Name the interface when the link carries any of the bottom four.

The same asymmetry applies to a saved file.

  • A capture somebody took with tcpdump -i any carries the cooked link type.
  • No sipnab flag can put back what the walk does not follow.
EncapsulationKeyReferenceStatus
IP-in-IP / 6-in-4IP proto 4 / 41RFC 2003 / RFC 4213decoded
GREIP proto 47RFC 2784decoded
GRE Transparent Ethernet BridgingGRE proto 0x6558RFC 7637 section 3.2decoded
MPLS-in-IPIP proto 137RFC 4023decoded
AHIP proto 51RFC 4302traversed — AH authenticates without encrypting, so the payload is readable
ESPIP proto 50RFC 4303decoded when NULL-encrypted — see below; otherwise named, never guessed
GTP-UUDP 21523GPP TS 29.281decoded
VXLANUDP 4789RFC 7348decoded
GENEVEUDP 6081RFC 8926decoded
TeredoUDP 3544RFC 4380decoded
UDP-encapsulated ESPUDP 4500RFC 3948encrypted — sipnab names it, never guesses
L2TPv2UDP 1701RFC 2661data messages only
L2TPv3 over UDPUDP 1701RFC 3931refused — see below

sipnab does not decode L2TPv3 over UDP. The size of its cookie field travels only in the L2TPv3 control channel, so a data packet on its own does not say where the inner frame starts.

sipnab reads ESP only when the payload proves NULL encryption.

  • An IMS core commonly protects the Gm interface between a phone and its P-CSCF with IPsec ESP.
  • A lab or test network often runs it with NULL encryption (RFC 2410). The SIP then travels in the clear inside an ESP header and trailer.

Nothing in the ESP header says which. So sipnab reads the payload as NULL-encrypted only when all of these hold:

  • an integrity check value of 12, 16, 24 or 32 octets,
  • the default padding of RFC 4303 section 2.4 and 4-octet alignment,
  • a next header of TCP or UDP,
  • a TCP or UDP header that fits the recovered segment and whose checksum verifies.

A frame that fails any of them joins the NOT DECODED line as ESP not NULL-encrypted (IP protocol 50). sipnab takes no keys for encrypted ESP. So capture inside the tunnel instead.

One shared budget bounds nesting.

  • It covers every layer of a frame.
  • So a frame combining MACsec, MPLS, GTP-U and IP-in-IP cannot walk further than one using a single encapsulation repeatedly.
  • sipnab refuses an over-nested frame rather than following it.

Live capture needs the tunnel-aware filter

Give sipnab no BPF expression of your own — the trailing argument, or a file named by --bpf-file. It then generates one that matches SIP inside VLAN, QinQ, PPPoE Session and MPLS as well as untagged traffic.

  • Do not confuse that with --filter.
  • That flag is sipnab’s own matching language over messages sipnab already decoded, long after the kernel has made its decision.

UDP-tunneled SIP is not covered by default.

  • BPF cannot parse a variable-length GTP-U header to reach the inner port.
  • So covering those means capturing every packet on those ports.
  • Use --capture-tunnels to opt in — see the CLI reference.

The encapsulated arm compiles on SLL and SLL2 as well as on Ethernet.

  • It selects the encapsulation through libpcap’s ether proto.
  • libpcap resolves that to the right offset for each link type while compiling.
  • So the kernel hands those frames up whichever device you opened.
  • What sipnab makes of them afterwards still depends on the link type. An MPLS frame arrives on the default device and the cooked walk does not follow it, as the table above says.

Writing your own expression replaces the generated one whole.

  • sipnab never edits it. So the encapsulated arm goes away and --capture-tunnels turns inert.
  • Two warnings cover that:
    • one when a port-based expression shows no sign of handling encapsulation,
    • one naming --capture-tunnels as ignored when you passed it alongside a filter of your own.

How the generated filter reaches encapsulated SIP

On a live capture with no filter of your own, sipnab installs one built from --portrange. It is not a bare portrange 5060-5061.

  • That one matches the outer headers only.
  • So on a tagged trunk, a PPPoE access link or an MPLS core it matches nothing.
  • The kernel then discards the frames where no sipnab counter, metric or report can see them.
  • You get “No SIP traffic found” on a link carrying calls.

The generated filter adds an encapsulated arm instead. It covers:

  • one VLAN tag (802.1Q, 802.1ad or 0x9100),
  • QinQ,
  • PPPoE Session,
  • VLAN over PPPoE,
  • one or two MPLS labels,
  • for IPv4 and IPv6, UDP and TCP.

The arm still demands a signaling port.

  • So it matches more of the same traffic, not a new class of it.
  • VLAN-tagged RTP reaches sipnab no more often than untagged RTP did.

It covers cooked captures too. So omitting -d costs the filter nothing. The cooked walk still skips MPLS, though, as A cooked capture reaches fewer of them says.

Two limits:

  • On the encapsulated arm an IPv4 header carrying options stays unmatched. The untagged portrange matches those, so this costs only IPv4-options traffic that is also encapsulated.
  • The one filter string carries inner offsets for Ethernet and both cooked link types. On a frame that already carries an encapsulation, an offset meant for another link type can occasionally pass a stray packet up to sipnab, whose parser then rejects it. Traffic without an encapsulation is not affected.

Encapsulation decoding records the byte offsets the filter probes and why.

SIP over WebSocket, IP fragments and TCP segments

sipnab unwraps SIP carried over WebSocket (RFC 7118) on the ports --ws-portrange names.

  • The ports are 80, 443, 8080 and 8443 unless you give a range.
  • A range replaces that set.
  • See --ws-portrange.

sipnab reassembles IP fragments and TCP segments before it parses. So a SIP message split across packets reads as one message. --no-reassembly turns this off and parses every packet on its own.

SIP over SCTP

sipnab reads SIP carried in SCTP DATA chunks (RFC 4168), as it reads UDP and TCP, and needs no flag for it. It puts a message split across DATA chunks in separate packets back together before it parses it.

sipnab -N -I sip-over-sctp.pcap --report

A five-message call over SCTP, built with RFC 5737 addresses, read as:

22:13:20.000 192.0.2.10:5060 -> 192.0.2.20:5060 INVITE (SDP) SCTP
22:13:20.500 192.0.2.20:5060 -> 192.0.2.10:5060 200 OK (SDP) SCTP
22:13:21.000 192.0.2.10:5060 -> 192.0.2.20:5060 ACK SCTP
22:13:21.500 192.0.2.10:5060 -> 192.0.2.20:5060 BYE SCTP
22:13:22.000 192.0.2.20:5060 -> 192.0.2.10:5060 200 OK SCTP

The same capture with the INVITE split into two DATA chunks in two packets read 6 packets as the same 5 SIP messages.

  • The transport column says SCTP.
  • --portrange applies to SCTP ports as it does to UDP and TCP.
  • A live capture needs nothing extra: libpcap compiles the generated filter’s portrange 5060-5061 to match SCTP too (tcpdump -d 'portrange 5060-5061' shows the comparison against IP protocol 132).

STUN and TURN

sipnab reads STUN (RFC 5389) and TURN (RFC 5766) because they are the first link in a one-way-audio chain. It does not read them because it is an ICE agent. What it takes from them is what changes a diagnosis:

  • whether an endpoint asked,
  • whether anything answered,
  • which address the answer named.

Three protocols share these ports. They separate cleanly on the two high bits of the first byte:

  • STUN 00,
  • TURN ChannelData 01,
  • RTP 10.

So checking STUN before RTP cannot swallow media.

ReadWhy
Header: type, length, magic cookie, transaction IDThe parser checks the cookie before believing anything else, which is what makes it safe to offer it every UDP payload
Class and methodBinding and TURN’s Allocate, Refresh, Send, Data, CreatePermission, ChannelBind. An unknown method reports as its number rather than as “unknown”, because the number is the thing to look up
XOR-MAPPED-ADDRESSThe answer the endpoint asked for, and what it then writes into its SDP
XOR-RELAYED-ADDRESS, XOR-PEER-ADDRESS, LIFETIMEThe TURN equivalents: the address a relay allocated, who a permission is about, and how long it lasts
ERROR-CODESo a refusal reads as a refusal. A server that says no is reachable, which is a different fault from silence
SOFTWARENames the stack, which tells one vendor’s retransmission pattern from the next
MAPPED-ADDRESS (legacy)The pre-RFC5389 form. Servers older than the cookie still answer with it, and without reading it their successful response reads as no answer at all. When both forms arrive the XOR one wins, whatever order they come in
ALTERNATE-SERVERSo a 300 redirect names where it points instead of reading as a dead end
REALM, NONCEA 401 with a realm is an authentication challenge, not a blocked path, and each sends you somewhere different. sipnab records the nonce as present only: its value is a server-chosen opaque string that means nothing to an observer
FINGERPRINTVerified. A CRC-32 over the message with no key involved, so a passive reader can check it honestly – and it is what separates a real STUN message from a payload that merely carried the cookie bytes. Reported as verified, present-and-wrong, or absent, which stay distinct
ICE USE-CANDIDATE, PRIORITY, ICE-CONTROLLING/ICE-CONTROLLEDThe nomination is the finding: without it, an ICE exchange that converged and one that never did look alike. Both sides claiming controlling is a role conflict whose only other symptom is media that never starts
TURN CHANNEL-NUMBER, REQUESTED-TRANSPORTSo sipnab can describe a relay path, not merely detect one
ChannelData framingsipnab recognizes and unwraps it, so RTP relayed through TURN reaches reconstruction. Without that a relayed call reports as having no media — the same answer sipnab gives for a call that carried none, which are opposite findings

sipnab tracks transactions.

  • So it reports a request that never came back, along with the number of attempts.
  • A retransmission is one unanswered question, not several.
  • An error response counts as ANSWERED.

What sipnab does not read

  • sipnab does not check MESSAGE-INTEGRITY or MESSAGE-INTEGRITY-SHA256. A passive observer has no credentials to verify them, so sipnab reports nothing about whether a message is authentic.
  • sipnab reads USERNAME, REALM and NONCE to tell an authentication challenge from a blocked path, never as evidence that authentication succeeded.
  • sipnab is not an ICE agent. It does not evaluate candidate pairs, compute priorities or decide nominations. It reports what it saw on the wire.