Network Infrastructure Discovery

1. Overview

Multi-vector infrastructure discovery combining DNS, network layer, and API sources with parallel execution, data correlation, and delta detection.

Network Discovery Framework
Figure 1. Infrastructure Discovery Framework

2. Phase 1: DNS Multi-Source Discovery

2.1. 1.1 pfSense DNS → Structured Extract

dsource d000 dev/network
netapi pfsense dns list 2>/dev/null | \
  awk 'BEGIN{FS="│"; OFS=","} NR>4 && NF>=5 {
    gsub(/^[ \t]+|[ \t]+$/, "", $2)
    gsub(/^[ \t]+|[ \t]+$/, "", $4)
    if ($2 != "" && $4 != "") print $2, $4
  }' > /tmp/pfsense-dns.csv

2.2. 1.2 BIND Zone Transfer → Cross-Validate

dig @10.50.1.90 inside.domusdigitalis.dev AXFR 2>/dev/null | \
  awk '/^[a-z].*IN\s+A\s+/ {
    sub(/\.inside\.domusdigitalis\.dev\.$/, "", $1)
    print $1","$5
  }' | sort > /tmp/bind-dns.csv

2.3. 1.3 AD SRV Records → Service Location

for srv in _ldap._tcp _kerberos._tcp _kpasswd._tcp _gc._tcp _kerberos._udp; do
  dig +short SRV ${srv}.inside.domusdigitalis.dev 2>/dev/null | \
    awk -v srv="$srv" '{printf "%-20s %-6s %-6s %s\n", srv, $3, $4, $1}'
done

2.4. 1.4 DNS Source Correlation (comm)

# Extract hostnames from both sources, sorted
awk -F, '{print $1}' /tmp/pfsense-dns.csv | sort > /tmp/pf-hosts.txt
awk -F, '{print $1}' /tmp/bind-dns.csv | sort > /tmp/bind-hosts.txt

# Set operations
echo "=== In pfSense ONLY (missing from BIND) ==="
comm -23 /tmp/pf-hosts.txt /tmp/bind-hosts.txt

echo ""
echo "=== In BIND ONLY (missing from pfSense) ==="
comm -13 /tmp/pf-hosts.txt /tmp/bind-hosts.txt

echo ""
echo "=== In BOTH (validated) ==="
comm -12 /tmp/pf-hosts.txt /tmp/bind-hosts.txt | wc -l

2.5. 1.5 Reverse DNS with Parallel + Progress

seq 1 254 | xargs -P64 -I{} sh -c '
  ip="10.50.1.{}"
  ptr=$(dig +short -x $ip @10.50.1.1 2>/dev/null | head -1 | sed "s/\\.$//" )
  [ -n "$ptr" ] && printf "%s,%s\n" "$ip" "$ptr"
' 2>/dev/null | sort -t. -k4 -n | tee /tmp/reverse-dns.csv

echo ""
awk -F, 'END{printf "=== %d PTR records found ===\n", NR}' /tmp/reverse-dns.csv

3. Phase 2: Network Layer Discovery

3.1. 2.1 ICMP Sweep with Timing Stats

START=$(date +%s.%N)
seq 1 254 | xargs -P128 -I{} sh -c '
  timeout 0.2 ping -c1 -W1 10.50.1.{} &>/dev/null && echo "10.50.1.{}"
' 2>/dev/null | sort -t. -k4 -n | tee /tmp/live-icmp.txt
END=$(date +%s.%N)

echo ""
awk -v start="$START" -v end="$END" 'END{
  printf "=== %d hosts in %.2fs (%.0f hosts/sec) ===\n", NR, end-start, NR/(end-start)
}' /tmp/live-icmp.txt

3.2. 2.2 ARP Cache with Vendor Lookup (Process Substitution)

ip neigh show | awk '/lladdr/ && /10\.50\.1\./ {print $1, $5}' | \
  while read ip mac; do
    oui=$(echo "$mac" | tr -d ':' | cut -c1-6 | tr 'a-f' 'A-F')
    printf "%-15s %-18s %s\n" "$ip" "$mac" "$oui"
  done | sort -t. -k4 -n

3.3. 2.3 Consolidated Host Discovery (join)

# Merge ICMP + PTR + ARP into single view
join -t',' -a1 -e'-' -o '1.1,2.2' \
  <(sort /tmp/live-icmp.txt) \
  <(sort -t',' -k1 /tmp/reverse-dns.csv) | \
join -t',' -a1 -e'-' -o '1.1,1.2,2.2' \
  - \
  <(ip neigh show | awk '/lladdr/ && /10\.50\.1\./ {print $1","$5}' | sort -t',' -k1) | \
column -t -s',' | tee /tmp/host-inventory.txt

4. Phase 3: Port Discovery Matrix

4.1. 3.1 Service Port Matrix (Parallel × Parallel)

HOSTS=$(cat /tmp/live-icmp.txt | head -30)
PORTS="22 80 443 1812 3389 5432 8443 9200"

# Header
printf "%-15s" "HOST"
for p in $PORTS; do printf "%6s" "$p"; done
echo ""
printf "%-15s" "----"
for p in $PORTS; do printf "%6s" "----"; done
echo ""

# Parallel scan with awk pivot
echo "$HOSTS" | xargs -P16 -I{} sh -c '
  host="{}"
  for port in '"$PORTS"'; do
    timeout 0.3 nc -z $host $port 2>/dev/null && echo "$host,$port,1" || echo "$host,$port,0"
  done
' 2>/dev/null | \
awk -F',' -v ports="$PORTS" '
  BEGIN { n=split(ports, p, " ") }
  { results[$1][$2] = $3 }
  END {
    for (host in results) {
      printf "%-15s", host
      for (i=1; i<=n; i++) {
        if (results[host][p[i]] == 1) printf "%6s", "✓"
        else printf "%6s", "-"
      }
      print ""
    }
  }
' | sort -t. -k4 -n

4.2. 3.2 Full Port Scan (Single Host, 65535 Ports)

TARGET="10.50.1.20"
echo "Scanning $TARGET (65535 ports)..."

START=$(date +%s)
seq 1 65535 | xargs -P256 -I{} sh -c "
  timeout 0.1 nc -z $TARGET {} 2>/dev/null && echo {}
" 2>/dev/null | sort -n | tee /tmp/open-ports-${TARGET}.txt
END=$(date +%s)

echo ""
awk -v t=$((END-START)) 'END{printf "=== %d open ports in %ds ===\n", NR, t}' /tmp/open-ports-${TARGET}.txt

4.3. 3.3 Service Fingerprint (Banner + SSL CN)

cat /tmp/live-icmp.txt | head -20 | xargs -P8 -I{} sh -c '
  host="{}"

  # SSH banner
  ssh_banner=$(timeout 2 nc -w1 $host 22 2>/dev/null | head -1 | cut -c1-40)
  [ -n "$ssh_banner" ] && echo "$host,22,SSH,$ssh_banner"

  # HTTPS cert CN
  for port in 443 8443; do
    cn=$(timeout 3 openssl s_client -connect $host:$port 2>/dev/null </dev/null | \
      openssl x509 -noout -subject 2>/dev/null | sed "s/.*CN *= *//" | cut -c1-40)
    [ -n "$cn" ] && echo "$host,$port,TLS,$cn"
  done
' 2>/dev/null | column -t -s','

5. Phase 4: API-Based Discovery

5.1. 4.1 ISE Infrastructure (jq Deep Dive)

dsource d000 dev/network

# Deployment nodes with roles
netapi ise api-call ers GET '/ers/config/node' 2>/dev/null | \
  jq -r '.SearchResult.resources[] | [.name, .id] | @tsv' | \
while read name id; do
  netapi ise api-call ers GET "/ers/config/node/$id" 2>/dev/null | \
    jq -r --arg name "$name" '.Node | [$name, .gateWay, (.services // [] | join(","))] | @tsv'
done | column -t

5.2. 4.2 ISE Network Devices → Port Correlation

netapi ise api-call ers GET '/ers/config/networkdevice?size=100' 2>/dev/null | \
  jq -r '.SearchResult.resources[] | .id' | head -10 | \
while read id; do
  netapi ise api-call ers GET "/ers/config/networkdevice/$id" 2>/dev/null | \
    jq -r '.NetworkDevice | [.name, .NetworkDeviceIPList[0].ipaddress, .NetworkDeviceGroupList[0]] | @tsv'
done | column -t

5.3. 4.3 k3s Services with LoadBalancer VIPs

ssh k3s-master-01 "kubectl get svc -A -o json" 2>/dev/null | \
  jq -r '
    .items[] |
    select(.status.loadBalancer.ingress) |
    [
      .metadata.namespace,
      .metadata.name,
      (.status.loadBalancer.ingress[0].ip // "pending"),
      (.spec.ports | map("\(.port)/\(.protocol)") | join(","))
    ] | @tsv
  ' | column -t

5.4. 4.4 k3s Pods by Namespace (awk Pivot Table)

ssh k3s-master-01 "kubectl get pods -A -o custom-columns='NS:.metadata.namespace,STATUS:.status.phase' --no-headers" 2>/dev/null | \
awk '
  { count[$1][$2]++ }
  END {
    printf "%-20s %8s %8s %8s\n", "NAMESPACE", "Running", "Pending", "Failed"
    printf "%-20s %8s %8s %8s\n", "----", "----", "----", "----"
    for (ns in count) {
      printf "%-20s %8d %8d %8d\n", ns, count[ns]["Running"]+0, count[ns]["Pending"]+0, count[ns]["Failed"]+0
    }
  }
' | sort -k2 -rn

5.5. 4.5 Vault PKI Certificate Inventory

dsource d000 dev/vault
vault list -format=json pki_int/certs 2>/dev/null | \
  jq -r '.[]' | head -10 | \
while read serial; do
  vault read -format=json "pki_int/cert/$serial" 2>/dev/null | \
    jq -r --arg serial "$serial" '
      .data | [
        $serial[0:16],
        (.certificate | split("\n")[0] | sub("-----BEGIN CERTIFICATE-----"; "")),
        .expiration_time
      ] | @tsv
    '
done | column -t

5.6. 4.6 WLC Client Distribution

dsource d000 dev/network
netapi wlc run "show wireless client summary" 2>/dev/null | \
  awk 'NR>5 && /^[0-9a-f]/ {
    ssid = $NF
    count[ssid]++
  }
  END {
    printf "%-30s %8s\n", "SSID", "Clients"
    printf "%-30s %8s\n", "----", "----"
    for (s in count) printf "%-30s %8d\n", s, count[s]
  }' | sort -k2 -rn

6. Phase 5: Infrastructure Report Generation

6.1. 5.1 Comprehensive JSON Export

{
  echo '{'
  echo '  "timestamp": "'$(date -Iseconds)'",'
  echo '  "subnet": "10.50.1.0/24",'

  # Live hosts array
  echo '  "hosts": ['
  first=true
  cat /tmp/live-icmp.txt | while read ip; do
    hostname=$(awk -F',' -v ip="$ip" '$1==ip {print $2; exit}' /tmp/reverse-dns.csv)
    mac=$(ip neigh show $ip 2>/dev/null | awk '/lladdr/{print $5}')
    ssh=$(timeout 0.2 nc -z $ip 22 2>/dev/null && echo "true" || echo "false")
    https=$(timeout 0.2 nc -z $ip 443 2>/dev/null && echo "true" || echo "false")

    [ "$first" = "false" ] && echo ','
    first=false

    printf '    {"ip":"%s","hostname":"%s","mac":"%s","ssh":%s,"https":%s}' \
      "$ip" "${hostname:--}" "${mac:--}" "$ssh" "$https"
  done
  echo ''
  echo '  ]'
  echo '}'
} | tee /tmp/infra-report-$(date +%Y%m%d).json

6.2. 5.2 CSV with All Vectors

echo "IP,Hostname,MAC,SSH,HTTPS,RADIUS,RDP,ISE,k3s"
cat /tmp/live-icmp.txt | xargs -P16 -I{} sh -c '
  ip="{}"
  hostname=$(awk -F"," -v ip="$ip" "\$1==ip {print \$2; exit}" /tmp/reverse-dns.csv)
  mac=$(ip neigh show $ip 2>/dev/null | awk "/lladdr/{print \$5}")

  ssh=$(timeout 0.2 nc -z $ip 22 2>/dev/null && echo "Y" || echo "-")
  https=$(timeout 0.2 nc -z $ip 443 2>/dev/null && echo "Y" || echo "-")
  radius=$(timeout 0.2 nc -z $ip 1812 2>/dev/null && echo "Y" || echo "-")
  rdp=$(timeout 0.2 nc -z $ip 3389 2>/dev/null && echo "Y" || echo "-")
  ise=$(timeout 0.2 nc -z $ip 9060 2>/dev/null && echo "Y" || echo "-")
  k3s=$(timeout 0.2 nc -z $ip 6443 2>/dev/null && echo "Y" || echo "-")

  echo "$ip,${hostname:--},${mac:--},$ssh,$https,$radius,$rdp,$ise,$k3s"
' 2>/dev/null | sort -t. -k4 -n | tee /tmp/infra-inventory.csv

column -t -s',' /tmp/infra-inventory.csv

7. Phase 6: Delta Detection

7.1. 6.1 Baseline Comparison

BASELINE="/tmp/baseline-hosts.txt"
CURRENT="/tmp/live-icmp.txt"

# Create baseline if missing
[ ! -f "$BASELINE" ] && cp "$CURRENT" "$BASELINE" && echo "Baseline created"

# Compare
echo "=== NEW HOSTS (not in baseline) ==="
comm -13 <(sort "$BASELINE") <(sort "$CURRENT")

echo ""
echo "=== MISSING HOSTS (in baseline, not responding) ==="
comm -23 <(sort "$BASELINE") <(sort "$CURRENT")

# Update baseline
cp "$CURRENT" "$BASELINE"

7.2. 6.2 Service Drift Detection (awk State Machine)

STATE_FILE="/tmp/port-state.txt"
HOSTS="10.50.1.20 10.50.1.21 10.50.1.60"
PORTS="22 443 1812 8443"

# Scan current state
for host in $HOSTS; do
  for port in $PORTS; do
    timeout 0.3 nc -z $host $port 2>/dev/null && state=1 || state=0
    echo "$host:$port:$state"
  done
done > /tmp/port-state-current.txt

# Compare with previous (if exists)
if [ -f "$STATE_FILE" ]; then
  echo "=== PORT STATE CHANGES ==="
  diff <(sort "$STATE_FILE") <(sort /tmp/port-state-current.txt) | \
    awk '/^[<>]/ {
      split($2, a, ":")
      dir = ($1 == "<") ? "CLOSED" : "OPENED"
      printf "%-15s %-6s %s\n", a[1], a[2], dir
    }'
fi

cp /tmp/port-state-current.txt "$STATE_FILE"

8. Phase 7: Automated Discovery Script

8.1. 7.1 Complete Infrastructure Audit

#!/usr/bin/env bash
# Save as: ~/.local/bin/infra-audit
# Usage: infra-audit [--full|--quick]

set -euo pipefail
SUBNET="10.50.1"
OUTDIR="/tmp/infra-audit-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$OUTDIR"

log() { printf "\033[1;34m[%s]\033[0m %s\n" "$(date +%H:%M:%S)" "$*"; }

log "Phase 1: DNS Discovery"
dsource d000 dev/network >/dev/null 2>&1
netapi pfsense dns list 2>/dev/null | \
  awk 'BEGIN{FS="│"} NR>4 && NF>=5 {gsub(/[ \t]+/,"",$2); gsub(/[ \t]+/,"",$4); if($2!="" && $4!="") print $2","$4}' \
  > "$OUTDIR/dns.csv"
log "  $(wc -l < "$OUTDIR/dns.csv") DNS records"

log "Phase 2: Host Discovery"
seq 1 254 | xargs -P128 -I{} sh -c "timeout 0.2 ping -c1 ${SUBNET}.{} &>/dev/null && echo ${SUBNET}.{}" \
  2>/dev/null | sort -t. -k4 -n > "$OUTDIR/live.txt"
log "  $(wc -l < "$OUTDIR/live.txt") live hosts"

log "Phase 3: Reverse DNS"
cat "$OUTDIR/live.txt" | xargs -P32 -I{} sh -c '
  ptr=$(dig +short -x {} 2>/dev/null | head -1 | sed "s/\\.$//" )
  [ -n "$ptr" ] && echo "{},${ptr}"
' > "$OUTDIR/ptr.csv"
log "  $(wc -l < "$OUTDIR/ptr.csv") PTR records"

log "Phase 4: Port Scan (critical services)"
PORTS="22 443 1812 3389 8443"
cat "$OUTDIR/live.txt" | xargs -P32 -I{} sh -c '
  for p in '"$PORTS"'; do
    timeout 0.3 nc -z {} $p 2>/dev/null && echo "{},${p}"
  done
' > "$OUTDIR/ports.csv"
log "  $(wc -l < "$OUTDIR/ports.csv") open ports"

log "Phase 5: Generate Report"
{
  echo "# Infrastructure Audit Report"
  echo "# Generated: $(date)"
  echo "# Subnet: ${SUBNET}.0/24"
  echo ""
  echo "## Summary"
  echo "- DNS Records: $(wc -l < "$OUTDIR/dns.csv")"
  echo "- Live Hosts: $(wc -l < "$OUTDIR/live.txt")"
  echo "- PTR Records: $(wc -l < "$OUTDIR/ptr.csv")"
  echo "- Open Ports: $(wc -l < "$OUTDIR/ports.csv")"
  echo ""
  echo "## Host Inventory"
  cat "$OUTDIR/live.txt" | while read ip; do
    hostname=$(awk -F',' -v ip="$ip" '$1==ip{print $2; exit}' "$OUTDIR/ptr.csv")
    ports=$(awk -F',' -v ip="$ip" '$1==ip{printf "%s ", $2}' "$OUTDIR/ports.csv")
    printf "%-15s %-40s [%s]\n" "$ip" "${hostname:--}" "${ports:--}"
  done
} > "$OUTDIR/report.txt"

log "Complete: $OUTDIR"
ls -la "$OUTDIR"

9. Quick Reference

Table 1. Advanced Discovery Patterns
Pattern Command

Parallel ping (128 workers)

seq 1 254 | xargs -P128 -I{} ping -c1 -W1 10.50.1.{}

Process substitution join

join -t',' <(sort file1) <(sort file2)

Set difference (comm)

comm -23 <(sort baseline) <(sort current)

awk associative arrays

awk '{count[$1]++} END{for(k in count) print k, count[k]}'

jq deep extraction

jq -r '.items[] | select(.status) | [.name, .ip] | @tsv'

Port matrix with pivot

awk -F',' '{r[$1][$2]=$3} END{…​}'

Parallel SSL cert grab

xargs -P8 -I{} openssl s_client -connect {}:443

Timed execution

START=$(date +%s.%N); …​; echo "$(bc <<< "$(date +%s.%N)-$START")s"