generateNfqueueDropProbe creates the fexit probe for NFQUEUE drops. Detects packets sent to NFQUEUE when no userspace consumer is bound to the queue. Uses fexit:vmlinux:__nf_queue which provides access to both function arguments and return value in a single probe. Requires kernel >= 5.5 with BTF sup
()
| 428 | // -ENOMEM (-12): failed to allocate queue entry |
| 429 | // -ENETDOWN (-100): network interface down during queue |
| 430 | func (g *ScriptGenerator) generateNfqueueDropProbe() string { |
| 431 | var sb strings.Builder |
| 432 | |
| 433 | sb.WriteString("fexit:vmlinux:__nf_queue\n") |
| 434 | sb.WriteString("{\n") |
| 435 | |
| 436 | // Only report failures (retval < 0 means queue delivery failed) |
| 437 | sb.WriteString(` if (retval >= 0) { return; } |
| 438 | |
| 439 | $skb = args->skb; |
| 440 | |
| 441 | // Only process IPv4 |
| 442 | $protocol = $skb->protocol; |
| 443 | if (bswap($protocol) != 0x0800) { return; } |
| 444 | |
| 445 | $iph = (struct iphdr *)($skb->head + $skb->network_header); |
| 446 | $saddr_raw = $iph->saddr; |
| 447 | $daddr_raw = $iph->daddr; |
| 448 | |
| 449 | `) |
| 450 | |
| 451 | // Add IP/CIDR filter if specified (reuses the same bswap-based filter as kfree_skb) |
| 452 | ipFilter := g.buildSkbIPFilterCondition() |
| 453 | if ipFilter != "" { |
| 454 | sb.WriteString(ipFilter) |
| 455 | } |
| 456 | |
| 457 | sb.WriteString(` $saddr = ntop(2, $saddr_raw); |
| 458 | $daddr = ntop(2, $daddr_raw); |
| 459 | $queuenum = args->queuenum; |
| 460 | $ipproto = $iph->protocol; |
| 461 | |
| 462 | $sport = (uint16)0; |
| 463 | $dport = (uint16)0; |
| 464 | |
| 465 | // Extract ports for TCP/UDP |
| 466 | if ($ipproto == 6 || $ipproto == 17) { |
| 467 | $transport_off = $skb->transport_header; |
| 468 | if ($transport_off > 0 && $transport_off < 65535) { |
| 469 | $th = $skb->head + $transport_off; |
| 470 | $sport = bswap(*(uint16*)($th)); |
| 471 | $dport = bswap(*(uint16*)($th + 2)); |
| 472 | } |
| 473 | } |
| 474 | |
| 475 | `) |
| 476 | |
| 477 | if g.config.OutputJSON { |
| 478 | sb.WriteString(` printf("{\"time\":\"%s\",\"type\":\"NFQ_DROP\",\"queue\":%d,\"errno\":%d,\"probe\":\"__nf_queue\",\"src_ip\":\"%s\",\"src_port\":%d,\"dst_ip\":\"%s\",\"dst_port\":%d}\n", |
| 479 | strftime("%H:%M:%S", nsecs), |
| 480 | $queuenum, retval, |
| 481 | $saddr, $sport, |
| 482 | $daddr, $dport); |
| 483 | `) |
| 484 | } else { |
| 485 | sb.WriteString(` // Decode errno to human-readable name |
| 486 | $errno_name = retval == -3 ? "ESRCH" : |
| 487 | retval == -12 ? "ENOMEM" : |
no test coverage detected