|
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
#
|
|
# Suricata AF_PACKET / high-speed NIC tuning
|
|
# Oracle Linux 9 / RHEL 9
|
|
#
|
|
# Defaults for your host:
|
|
# IFNAME=ens2f1np1
|
|
# PCIE=0000:05:00.1
|
|
# QUEUES=10
|
|
#
|
|
# WARNING:
|
|
# - briefly takes the capture interface DOWN
|
|
# - stops irqbalance
|
|
#
|
|
|
|
IFNAME="${IFNAME:-ens2f1np1}"
|
|
PCIE="${PCIE:-0000:05:00.1}"
|
|
QUEUES="${QUEUES:-10}"
|
|
|
|
# Starting point for interrupt moderation.
|
|
# You can later sweep 0 / 25 / 50 / 125 for the burst test.
|
|
RX_USECS="${RX_USECS:-125}"
|
|
|
|
STOP_IRQBALANCE="${STOP_IRQBALANCE:-1}"
|
|
|
|
#
|
|
# Optional manual CPU assignment:
|
|
#
|
|
# IRQ_CPUS=1-10 \
|
|
# WORKER_CPUS=11-20 \
|
|
# ./suricata-afp-tune.sh
|
|
#
|
|
# Otherwise CPU sets are selected automatically from the NIC-local NUMA node.
|
|
#
|
|
IRQ_CPUS="${IRQ_CPUS:-}"
|
|
WORKER_CPUS="${WORKER_CPUS:-}"
|
|
|
|
|
|
log()
|
|
{
|
|
printf '\n==> %s\n' "$*"
|
|
}
|
|
|
|
warn()
|
|
{
|
|
printf 'WARN: %s\n' "$*" >&2
|
|
}
|
|
|
|
die()
|
|
{
|
|
printf 'ERROR: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
need()
|
|
{
|
|
command -v "$1" >/dev/null 2>&1 ||
|
|
die "missing command: $1"
|
|
}
|
|
|
|
|
|
for c in \
|
|
ip \
|
|
ethtool \
|
|
awk \
|
|
grep \
|
|
sort \
|
|
sed \
|
|
systemctl \
|
|
sysctl \
|
|
readlink \
|
|
basename \
|
|
cat
|
|
do
|
|
need "$c"
|
|
done
|
|
|
|
|
|
[[ $EUID -eq 0 ]] ||
|
|
die "run as root"
|
|
|
|
|
|
[[ -d "/sys/class/net/$IFNAME" ]] ||
|
|
die "interface $IFNAME does not exist"
|
|
|
|
|
|
[[ -e "/sys/bus/pci/devices/$PCIE" ]] ||
|
|
die "PCI device $PCIE does not exist"
|
|
|
|
|
|
###############################################################################
|
|
# Verify IFNAME <-> PCI mapping
|
|
###############################################################################
|
|
|
|
ACTUAL_PCIE="$(
|
|
basename "$(
|
|
readlink -f "/sys/class/net/$IFNAME/device"
|
|
)"
|
|
)"
|
|
|
|
[[ "$ACTUAL_PCIE" == "$PCIE" ]] ||
|
|
die "$IFNAME maps to $ACTUAL_PCIE, not $PCIE"
|
|
|
|
|
|
[[ "$QUEUES" =~ ^[0-9]+$ && "$QUEUES" -gt 0 ]] ||
|
|
die "QUEUES must be a positive integer"
|
|
|
|
|
|
###############################################################################
|
|
# Helpers
|
|
###############################################################################
|
|
|
|
try()
|
|
{
|
|
printf '+ '
|
|
printf '%q ' "$@"
|
|
printf '\n'
|
|
|
|
"$@" ||
|
|
{
|
|
warn "command failed/unsupported: $*"
|
|
return 0
|
|
}
|
|
}
|
|
|
|
|
|
expand_cpulist()
|
|
{
|
|
local spec="$1"
|
|
local part a b i
|
|
local -a parts
|
|
|
|
IFS=',' read -ra parts <<< "$spec"
|
|
|
|
for part in "${parts[@]}"
|
|
do
|
|
|
|
if [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]
|
|
then
|
|
|
|
a="${BASH_REMATCH[1]}"
|
|
b="${BASH_REMATCH[2]}"
|
|
|
|
(( a <= b )) ||
|
|
die "bad CPU range: $part"
|
|
|
|
for ((i=a; i<=b; i++))
|
|
do
|
|
printf '%s\n' "$i"
|
|
done
|
|
|
|
elif [[ "$part" =~ ^[0-9]+$ ]]
|
|
then
|
|
|
|
printf '%s\n' "$part"
|
|
|
|
else
|
|
|
|
die "bad CPU-list token: $part"
|
|
|
|
fi
|
|
|
|
done
|
|
}
|
|
|
|
|
|
join_by_comma()
|
|
{
|
|
local IFS=,
|
|
echo "$*"
|
|
}
|
|
|
|
|
|
###############################################################################
|
|
# NIC identification
|
|
###############################################################################
|
|
|
|
log "NIC identity"
|
|
|
|
ethtool -i "$IFNAME"
|
|
|
|
|
|
if command -v lspci >/dev/null 2>&1
|
|
then
|
|
lspci -s "$PCIE" -nnk || true
|
|
fi
|
|
|
|
|
|
NODE="$(
|
|
cat "/sys/bus/pci/devices/$PCIE/numa_node"
|
|
)"
|
|
|
|
|
|
printf \
|
|
'interface=%s pci=%s numa_node=%s queues=%s\n' \
|
|
"$IFNAME" \
|
|
"$PCIE" \
|
|
"$NODE" \
|
|
"$QUEUES"
|
|
|
|
|
|
###############################################################################
|
|
# CPU selection
|
|
#
|
|
# Use sysfs topology, not lscpu --parse.
|
|
#
|
|
# One logical CPU is selected from each physical core.
|
|
###############################################################################
|
|
|
|
log "Selecting CPUs"
|
|
|
|
|
|
if (( NODE >= 0 )) &&
|
|
[[ -r "/sys/devices/system/node/node${NODE}/cpulist" ]]
|
|
then
|
|
|
|
CPU_SPEC="$(
|
|
cat "/sys/devices/system/node/node${NODE}/cpulist"
|
|
)"
|
|
|
|
else
|
|
|
|
warn "NIC has no usable NUMA node; using all online CPUs"
|
|
|
|
CPU_SPEC="$(
|
|
cat /sys/devices/system/cpu/online
|
|
)"
|
|
|
|
fi
|
|
|
|
|
|
mapfile -t CANDIDATE_CPUS < <(
|
|
expand_cpulist "$CPU_SPEC"
|
|
)
|
|
|
|
|
|
declare -A SEEN_CORE=()
|
|
|
|
LOCAL_PHYS=()
|
|
|
|
|
|
for cpu in "${CANDIDATE_CPUS[@]}"
|
|
do
|
|
|
|
#
|
|
# Leave CPU0 for housekeeping.
|
|
#
|
|
(( cpu == 0 )) &&
|
|
continue
|
|
|
|
|
|
TOPO="/sys/devices/system/cpu/cpu${cpu}/topology"
|
|
|
|
|
|
[[ -r "$TOPO/core_id" ]] ||
|
|
continue
|
|
|
|
[[ -r "$TOPO/physical_package_id" ]] ||
|
|
continue
|
|
|
|
|
|
CORE_ID="$(
|
|
cat "$TOPO/core_id"
|
|
)"
|
|
|
|
PACKAGE_ID="$(
|
|
cat "$TOPO/physical_package_id"
|
|
)"
|
|
|
|
|
|
KEY="${PACKAGE_ID}:${CORE_ID}"
|
|
|
|
|
|
if [[ -z "${SEEN_CORE[$KEY]+x}" ]]
|
|
then
|
|
|
|
SEEN_CORE["$KEY"]=1
|
|
|
|
LOCAL_PHYS+=(
|
|
"$cpu"
|
|
)
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
printf \
|
|
'NIC NUMA CPU list: %s\n' \
|
|
"$CPU_SPEC"
|
|
|
|
|
|
printf \
|
|
'Usable physical-core CPUs: %s\n' \
|
|
"$(join_by_comma "${LOCAL_PHYS[@]}")"
|
|
|
|
|
|
((${#LOCAL_PHYS[@]} >= QUEUES)) ||
|
|
die \
|
|
"need $QUEUES physical cores on NUMA node $NODE, only found ${#LOCAL_PHYS[@]}"
|
|
|
|
|
|
###############################################################################
|
|
# Decide IRQ / Suricata worker CPUs
|
|
###############################################################################
|
|
|
|
if [[ -n "$IRQ_CPUS" || -n "$WORKER_CPUS" ]]
|
|
then
|
|
|
|
[[ -n "$IRQ_CPUS" && -n "$WORKER_CPUS" ]] ||
|
|
die "set both IRQ_CPUS and WORKER_CPUS, or neither"
|
|
|
|
|
|
mapfile -t IRQ_ARR < <(
|
|
expand_cpulist "$IRQ_CPUS"
|
|
)
|
|
|
|
|
|
mapfile -t WORKER_ARR < <(
|
|
expand_cpulist "$WORKER_CPUS"
|
|
)
|
|
|
|
|
|
((${#IRQ_ARR[@]} >= QUEUES)) ||
|
|
die "IRQ_CPUS provides fewer than $QUEUES CPUs"
|
|
|
|
|
|
((${#WORKER_ARR[@]} >= QUEUES)) ||
|
|
die "WORKER_CPUS provides fewer than $QUEUES CPUs"
|
|
|
|
|
|
IRQ_ARR=(
|
|
"${IRQ_ARR[@]:0:QUEUES}"
|
|
)
|
|
|
|
|
|
WORKER_ARR=(
|
|
"${WORKER_ARR[@]:0:QUEUES}"
|
|
)
|
|
|
|
|
|
CPU_MODE="manual"
|
|
|
|
else
|
|
|
|
#
|
|
# Best case:
|
|
# separate physical cores for NIC IRQ/NAPI and Suricata workers.
|
|
#
|
|
if ((${#LOCAL_PHYS[@]} >= 2 * QUEUES))
|
|
then
|
|
|
|
IRQ_ARR=(
|
|
"${LOCAL_PHYS[@]:0:QUEUES}"
|
|
)
|
|
|
|
|
|
WORKER_ARR=(
|
|
"${LOCAL_PHYS[@]:QUEUES:QUEUES}"
|
|
)
|
|
|
|
|
|
CPU_MODE="dedicated physical IRQ + worker cores"
|
|
|
|
else
|
|
|
|
#
|
|
# Not enough physical cores for separate sets.
|
|
#
|
|
# Use the same deterministic physical cores for both.
|
|
#
|
|
WORKER_ARR=(
|
|
"${LOCAL_PHYS[@]:0:QUEUES}"
|
|
)
|
|
|
|
|
|
IRQ_ARR=(
|
|
"${WORKER_ARR[@]}"
|
|
)
|
|
|
|
|
|
CPU_MODE="shared IRQ/worker physical cores"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
IRQ_CPUS="$(
|
|
join_by_comma "${IRQ_ARR[@]}"
|
|
)"
|
|
|
|
|
|
WORKER_CPUS="$(
|
|
join_by_comma "${WORKER_ARR[@]}"
|
|
)"
|
|
|
|
|
|
printf \
|
|
'CPU mode: %s\n' \
|
|
"$CPU_MODE"
|
|
|
|
|
|
printf \
|
|
'IRQ CPUs: %s\n' \
|
|
"$IRQ_CPUS"
|
|
|
|
|
|
printf \
|
|
'Suricata worker CPUs: %s\n' \
|
|
"$WORKER_CPUS"
|
|
|
|
|
|
###############################################################################
|
|
# irqbalance
|
|
###############################################################################
|
|
|
|
if [[ "$STOP_IRQBALANCE" == 1 ]]
|
|
then
|
|
|
|
log "Stopping irqbalance"
|
|
|
|
systemctl stop irqbalance 2>/dev/null ||
|
|
true
|
|
|
|
fi
|
|
|
|
|
|
###############################################################################
|
|
# Queue capability
|
|
###############################################################################
|
|
|
|
log "Checking queue capability"
|
|
|
|
|
|
CHANNELS="$(
|
|
ethtool -l "$IFNAME"
|
|
)"
|
|
|
|
|
|
printf '%s\n' "$CHANNELS"
|
|
|
|
|
|
MAX_COMBINED="$(
|
|
printf '%s\n' "$CHANNELS" |
|
|
awk '
|
|
/Pre-set maximums:/ {
|
|
p=1
|
|
next
|
|
}
|
|
|
|
/Current hardware settings:/ {
|
|
p=0
|
|
}
|
|
|
|
p && $1=="Combined:" {
|
|
print $2
|
|
exit
|
|
}
|
|
'
|
|
)"
|
|
|
|
|
|
[[ "$MAX_COMBINED" =~ ^[0-9]+$ ]] ||
|
|
die "could not determine maximum combined queues"
|
|
|
|
|
|
(( QUEUES <= MAX_COMBINED )) ||
|
|
die "requested $QUEUES queues; NIC maximum is $MAX_COMBINED"
|
|
|
|
|
|
###############################################################################
|
|
# Link down while modifying queue topology
|
|
###############################################################################
|
|
|
|
log "Reconfiguring NIC -- link will bounce"
|
|
|
|
|
|
ip link set dev "$IFNAME" down
|
|
|
|
|
|
ethtool -L "$IFNAME" combined "$QUEUES"
|
|
|
|
|
|
###############################################################################
|
|
# Offloads
|
|
###############################################################################
|
|
|
|
#
|
|
# Preserve checksum offloading.
|
|
#
|
|
try ethtool -K "$IFNAME" rx on
|
|
try ethtool -K "$IFNAME" tx on
|
|
|
|
|
|
#
|
|
# Disable aggregation/segmentation features which alter packet semantics.
|
|
#
|
|
try ethtool -K "$IFNAME" gro off
|
|
try ethtool -K "$IFNAME" lro off
|
|
try ethtool -K "$IFNAME" gso off
|
|
try ethtool -K "$IFNAME" tso off
|
|
try ethtool -K "$IFNAME" ufo off
|
|
try ethtool -K "$IFNAME" sg off
|
|
|
|
|
|
#
|
|
# Preserve VLAN tags for Suricata.
|
|
#
|
|
try ethtool -K "$IFNAME" rxvlan off
|
|
try ethtool -K "$IFNAME" txvlan off
|
|
|
|
|
|
#
|
|
# Hardware GRO / UDP GRO if supported.
|
|
#
|
|
try ethtool -K "$IFNAME" rx-gro-hw off
|
|
try ethtool -K "$IFNAME" rx-udp-gro-forwarding off
|
|
|
|
|
|
#
|
|
# RSS / classification support.
|
|
#
|
|
try ethtool -K "$IFNAME" rxhash on
|
|
try ethtool -K "$IFNAME" ntuple on
|
|
|
|
|
|
###############################################################################
|
|
# Pause frames / EEE
|
|
###############################################################################
|
|
|
|
#
|
|
# Passive IDS should not change sender behavior through PAUSE frames.
|
|
#
|
|
try ethtool -A "$IFNAME" rx off tx off
|
|
|
|
|
|
#
|
|
# Avoid Energy Efficient Ethernet power transitions.
|
|
#
|
|
try ethtool --set-eee "$IFNAME" eee off
|
|
|
|
|
|
###############################################################################
|
|
# Maximum hardware RX descriptor ring
|
|
###############################################################################
|
|
|
|
RING="$(
|
|
ethtool -g "$IFNAME" 2>/dev/null ||
|
|
true
|
|
)"
|
|
|
|
|
|
MAX_RX="$(
|
|
printf '%s\n' "$RING" |
|
|
awk '
|
|
/Pre-set maximums:/ {
|
|
p=1
|
|
next
|
|
}
|
|
|
|
/Current hardware settings:/ {
|
|
p=0
|
|
}
|
|
|
|
p && $1=="RX:" {
|
|
print $2
|
|
exit
|
|
}
|
|
'
|
|
)"
|
|
|
|
|
|
if [[ "$MAX_RX" =~ ^[0-9]+$ ]]
|
|
then
|
|
|
|
log \
|
|
"Setting RX descriptor ring to NIC maximum: $MAX_RX"
|
|
|
|
|
|
try ethtool -G "$IFNAME" \
|
|
rx "$MAX_RX"
|
|
|
|
else
|
|
|
|
warn \
|
|
"could not determine maximum RX ring; leaving unchanged"
|
|
|
|
fi
|
|
|
|
|
|
###############################################################################
|
|
# Bring interface back
|
|
###############################################################################
|
|
|
|
ip link set dev "$IFNAME" up
|
|
|
|
ip link set dev "$IFNAME" promisc on
|
|
|
|
|
|
###############################################################################
|
|
# Verify queue count
|
|
###############################################################################
|
|
|
|
CUR_COMBINED="$(
|
|
ethtool -l "$IFNAME" |
|
|
awk '
|
|
/Current hardware settings:/ {
|
|
p=1
|
|
next
|
|
}
|
|
|
|
p && $1=="Combined:" {
|
|
print $2
|
|
exit
|
|
}
|
|
'
|
|
)"
|
|
|
|
|
|
[[ "$CUR_COMBINED" == "$QUEUES" ]] ||
|
|
die \
|
|
"combined queues are $CUR_COMBINED; expected $QUEUES"
|
|
|
|
|
|
###############################################################################
|
|
# Interrupt moderation
|
|
###############################################################################
|
|
|
|
log "Interrupt moderation"
|
|
|
|
|
|
try ethtool -C "$IFNAME" \
|
|
adaptive-rx off \
|
|
adaptive-tx off \
|
|
rx-usecs "$RX_USECS"
|
|
|
|
|
|
###############################################################################
|
|
# RSS
|
|
###############################################################################
|
|
|
|
log "RSS Toeplitz + equal indirection"
|
|
|
|
|
|
try ethtool -X "$IFNAME" \
|
|
hfunc toeplitz
|
|
|
|
|
|
#
|
|
# Detect RSS key size exposed by the driver.
|
|
#
|
|
RSS_HEX="$(
|
|
ethtool -x "$IFNAME" 2>/dev/null |
|
|
awk '
|
|
/RSS hash key:/ {
|
|
p=1
|
|
next
|
|
}
|
|
|
|
/RSS hash function:/ {
|
|
p=0
|
|
}
|
|
|
|
p {
|
|
gsub(/[^0-9A-Fa-f]/, "")
|
|
printf "%s", $0
|
|
}
|
|
'
|
|
)"
|
|
|
|
|
|
if [[ -n "$RSS_HEX" &&
|
|
$(( ${#RSS_HEX} % 2 )) -eq 0 ]]
|
|
then
|
|
|
|
KEY_BYTES=$(( ${#RSS_HEX} / 2 ))
|
|
|
|
SYM_KEY=""
|
|
|
|
|
|
for ((i=0; i<KEY_BYTES; i++))
|
|
do
|
|
|
|
if (( i % 2 == 0 ))
|
|
then
|
|
B="6D"
|
|
else
|
|
B="5A"
|
|
fi
|
|
|
|
|
|
[[ -n "$SYM_KEY" ]] &&
|
|
SYM_KEY+=":"
|
|
|
|
|
|
SYM_KEY+="$B"
|
|
|
|
done
|
|
|
|
|
|
printf \
|
|
'RSS key length: %d bytes\n' \
|
|
"$KEY_BYTES"
|
|
|
|
|
|
try ethtool -X "$IFNAME" \
|
|
hkey "$SYM_KEY" \
|
|
equal "$QUEUES"
|
|
|
|
else
|
|
|
|
warn \
|
|
"could not determine RSS key length"
|
|
|
|
|
|
try ethtool -X "$IFNAME" \
|
|
equal "$QUEUES"
|
|
|
|
fi
|
|
|
|
|
|
###############################################################################
|
|
# RSS hash fields
|
|
###############################################################################
|
|
|
|
for proto in tcp4 udp4 tcp6 udp6
|
|
do
|
|
|
|
if ethtool -N "$IFNAME" \
|
|
rx-flow-hash "$proto" sdfn \
|
|
>/dev/null 2>&1
|
|
then
|
|
|
|
printf \
|
|
'+ ethtool -N %s rx-flow-hash %s sdfn\n' \
|
|
"$IFNAME" \
|
|
"$proto"
|
|
|
|
else
|
|
|
|
warn \
|
|
"$proto sdfn unsupported; trying sd"
|
|
|
|
|
|
try ethtool -N "$IFNAME" \
|
|
rx-flow-hash "$proto" sd
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
###############################################################################
|
|
# Disable software RPS/RFS
|
|
###############################################################################
|
|
|
|
log "Disabling software RPS/RFS"
|
|
|
|
|
|
sysctl -w \
|
|
net.core.rps_sock_flow_entries=0 \
|
|
>/dev/null
|
|
|
|
|
|
for q in /sys/class/net/"$IFNAME"/queues/rx-*
|
|
do
|
|
|
|
if [[ -e "$q/rps_cpus" ]]
|
|
then
|
|
echo 0 > "$q/rps_cpus"
|
|
fi
|
|
|
|
|
|
if [[ -e "$q/rps_flow_cnt" ]]
|
|
then
|
|
echo 0 > "$q/rps_flow_cnt"
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
###############################################################################
|
|
# OL9/RHEL9 NAPI / softirq headroom
|
|
###############################################################################
|
|
|
|
log "Increasing OL9 networking softirq headroom"
|
|
|
|
|
|
sysctl -w \
|
|
net.core.netdev_budget=600 \
|
|
>/dev/null
|
|
|
|
|
|
sysctl -w \
|
|
net.core.netdev_budget_usecs=4000 \
|
|
>/dev/null
|
|
|
|
|
|
sysctl -w \
|
|
net.core.netdev_max_backlog=8192 \
|
|
>/dev/null
|
|
|
|
|
|
###############################################################################
|
|
# IRQ affinity
|
|
###############################################################################
|
|
|
|
log "Pinning NIC queue IRQs"
|
|
|
|
|
|
mapfile -t IRQS < <(
|
|
|
|
grep -F "$IFNAME" /proc/interrupts |
|
|
|
|
grep -Ei \
|
|
'TxRx|rx[-_ ]|queue' |
|
|
|
|
awk '
|
|
{
|
|
gsub(":", "", $1)
|
|
|
|
if ($1 ~ /^[0-9]+$/)
|
|
print $1
|
|
}
|
|
' |
|
|
|
|
sort -n -u
|
|
|
|
)
|
|
|
|
|
|
#
|
|
# Driver naming varies.
|
|
#
|
|
# If the specific queue matching didn't work,
|
|
# fall back to every IRQ containing IFNAME.
|
|
#
|
|
if ((${#IRQS[@]} == 0))
|
|
then
|
|
|
|
warn \
|
|
"could not identify queue IRQ names; falling back to all IRQs mentioning $IFNAME"
|
|
|
|
|
|
mapfile -t IRQS < <(
|
|
|
|
grep -F "$IFNAME" /proc/interrupts |
|
|
|
|
awk '
|
|
{
|
|
gsub(":", "", $1)
|
|
|
|
if ($1 ~ /^[0-9]+$/)
|
|
print $1
|
|
}
|
|
' |
|
|
|
|
sort -n -u
|
|
|
|
)
|
|
|
|
fi
|
|
|
|
|
|
if ((${#IRQS[@]} == 0))
|
|
then
|
|
|
|
warn \
|
|
"no IRQs found for $IFNAME; inspect /proc/interrupts manually"
|
|
|
|
else
|
|
|
|
for i in "${!IRQS[@]}"
|
|
do
|
|
|
|
irq="${IRQS[$i]}"
|
|
|
|
idx=$(( i % ${#IRQ_ARR[@]} ))
|
|
|
|
cpu="${IRQ_ARR[$idx]}"
|
|
|
|
|
|
if [[ -w "/proc/irq/$irq/smp_affinity_list" ]]
|
|
then
|
|
|
|
echo "$cpu" \
|
|
> "/proc/irq/$irq/smp_affinity_list" ||
|
|
|
|
warn \
|
|
"could not pin IRQ $irq to CPU $cpu"
|
|
|
|
else
|
|
|
|
warn \
|
|
"IRQ $irq affinity not writable; possibly a managed IRQ"
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
|
|
###############################################################################
|
|
# Final state
|
|
###############################################################################
|
|
|
|
log "Final NIC state"
|
|
|
|
|
|
printf '\n-- channels --\n'
|
|
|
|
ethtool -l "$IFNAME"
|
|
|
|
|
|
printf '\n-- RX/TX rings --\n'
|
|
|
|
ethtool -g "$IFNAME" 2>/dev/null ||
|
|
true
|
|
|
|
|
|
printf '\n-- interrupt coalescing --\n'
|
|
|
|
ethtool -c "$IFNAME" 2>/dev/null ||
|
|
true
|
|
|
|
|
|
printf '\n-- RSS --\n'
|
|
|
|
ethtool -x "$IFNAME" 2>/dev/null ||
|
|
true
|
|
|
|
|
|
printf '\n-- important offloads --\n'
|
|
|
|
ethtool -k "$IFNAME" |
|
|
grep -E \
|
|
'(^rx-checksumming:|^tx-checksumming:|scatter-gather:|tcp-segmentation|udp-fragmentation|generic-segmentation|generic-receive|large-receive|rx-vlan-offload|tx-vlan-offload|receive-hashing|ntuple)' ||
|
|
true
|
|
|
|
|
|
printf '\n-- IRQ affinity --\n'
|
|
|
|
|
|
for irq in "${IRQS[@]:-}"
|
|
do
|
|
|
|
if [[ -r "/proc/irq/$irq/effective_affinity_list" ]]
|
|
then
|
|
|
|
printf \
|
|
'IRQ %-5s effective=%s\n' \
|
|
"$irq" \
|
|
"$(
|
|
cat \
|
|
"/proc/irq/$irq/effective_affinity_list"
|
|
)"
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
printf '\n-- drop/error counters --\n'
|
|
|
|
|
|
ethtool -S "$IFNAME" 2>/dev/null |
|
|
grep -Ei \
|
|
'rx.*(drop|disc|miss|err|no.?buf)|drop|discard|missed|error|no.?buf' ||
|
|
true
|
|
|
|
|
|
###############################################################################
|
|
# Recommended Suricata configuration summary
|
|
###############################################################################
|
|
|
|
cat <<EOF
|
|
|
|
============================================================
|
|
Recommended matching Suricata configuration
|
|
============================================================
|
|
|
|
interface: $IFNAME
|
|
AF_PACKET threads: $QUEUES
|
|
cluster-type: cluster_qm
|
|
|
|
IRQ CPUs:
|
|
$IRQ_CPUS
|
|
|
|
Suricata worker CPUs:
|
|
$WORKER_CPUS
|
|
|
|
AF_PACKET:
|
|
tpacket-v3: yes
|
|
ring-size: 100000
|
|
block-size: 1048576
|
|
|
|
Before and after every benchmark:
|
|
|
|
ethtool -S $IFNAME | egrep -i 'drop|disc|miss|error|no.?buf'
|
|
|
|
cat /proc/net/softnet_stat
|
|
|
|
If /proc/net/softnet_stat column 3 increases during the test,
|
|
increase net.core.netdev_budget and
|
|
net.core.netdev_budget_usecs further.
|
|
|
|
============================================================
|
|
|
|
EOF
|