- Posted on
- • Artificial Intelligence
Artificial Intelligence VLAN Management
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence VLAN Management with Bash: Detect, Decide, and Deploy
Your VLANs are multiplying. What started as a neat set of 10 is now 200+, spread across trunks and servers, with odd traffic bursts and “mystery” tags showing up at 3 AM. One mis-tag and a compliance report lights up. You don’t need a whole new SDN stack to tame this—just the right blend of Linux tooling, Bash, and a pinch of machine learning.
In this article you’ll learn how to:
Inventory and validate VLAN state in seconds
Detect anomalous VLAN behavior with an unsupervised model
Apply intent-based VLAN provisioning safely (with dry-runs)
Close the loop by notifying your team or auto-remediating
All from the command line, on any mainstream Linux distribution.
Why AI for VLAN Management?
Scale and churn: VLAN counts grow with microservices, VMs, and multi-tenant hosts. Manual tracking breaks down.
Drift and risk: Trunks accrete tags. Orphaned VLANs and wrong pvids silently increase blast radius.
Signal in noise: Traffic patterns vary by time and tenant. Unsupervised ML spots unusual VLAN behavior faster than eyeballing counters.
Bash-first: You can capture, model, and act with lightweight scripts and familiar tools—no vendor lock-in.
Prerequisites and Installation
You’ll need:
iproute2 (usually preinstalled: ip, bridge)
lldpd (optional but handy)
jq (JSON parsing)
tshark (Wireshark CLI) for VLAN-aware capture
Python 3 + pip for a tiny ML helper (scikit-learn, pandas)
Install on your distro:
- Debian/Ubuntu:
sudo apt update
sudo apt install -y lldpd jq tshark python3 python3-pip
- Fedora/RHEL/CentOS Stream:
sudo dnf install -y lldpd jq wireshark-cli python3 python3-pip
- openSUSE/SLE:
sudo zypper install -y lldpd jq wireshark-cli python3 python3-pip
Create a virtual environment for Python bits:
python3 -m venv ~/venvs/ai-vlan
source ~/venvs/ai-vlan/bin/activate
pip install -U pip pandas scikit-learn
Note: Packet capture may require root. For non-root capture:
On Debian/Ubuntu:
sudo dpkg-reconfigure wireshark-commonthen add your user towireshark.Or set capabilities:
sudo setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' $(which dumpcap)
1) Inventory and Validate VLAN State
Start by creating a current, machine-readable map of VLANs per interface. The bridge command can emit JSON we’ll slice with jq.
Save as vlan_inventory.sh:
#!/usr/bin/env bash
set -euo pipefail
OUT_CSV=${1:-vlan_inventory.csv}
OUT_JSON=${2:-vlan_inventory.json}
# Requires: iproute2 (bridge), jq
bridge -json vlan show | tee "$OUT_JSON" \
| jq -r '
["ifname","vid","is_pvid","is_egress_untagged","state"],
(.. | objects | select(has("ifname") and has("vlans")) | .ifname as $if |
.vlans[]? | [ $if, .vlan, ( .pvid // false), ( .egressUntagged // false), ( .state // "unknown") ])
| @csv' \
> "$OUT_CSV"
echo "Wrote $OUT_CSV and $OUT_JSON"
Run it:
bash vlan_inventory.sh
column -s, -t vlan_inventory.csv | less -S
Tip: Check for drift (e.g., unexpected VLANs on trunk ports), mismatched pvids, and untagged settings that don’t match your standards.
Optional LLDP peek (helps correlate neighbors and expected VLANs):
sudo systemctl enable --now lldpd
sudo lldpcli show neighbors
2) Detect Anomalous VLAN Behavior with Unsupervised ML
We’ll stream VLAN-tagged traffic, compute simple per-VLAN features, and feed them into an Isolation Forest model. No labels needed—great for catching spikes, scans, or leakage between segments.
Collector: vlan_features.sh
#!/usr/bin/env bash
set -euo pipefail
IFACE=${1:?Usage: $0 <iface> [duration_sec]}
DUR=${2:-300} # seconds
OUT=${3:-features.csv}
# Header
echo "bucket_start,bucket_end,vlan,pkts,bytes,uniq_src,uniq_dst" > "$OUT"
# Stream and aggregate to 60s buckets
# Fields: time, vlan.id, frame.len, eth.src, eth.dst
sudo timeout "$DUR" tshark -i "$IFACE" -l -T fields \
-e frame.time_epoch -e vlan.id -e frame.len -e eth.src -e eth.dst \
-Y "vlan" 2>/dev/null \
| awk -F'\t' '
BEGIN { bucket=0 }
{
t=$1+0; vid=$2; len=$3+0; s=$4; d=$5;
if (vid == "" || vid ~ /[A-Za-z]/) next; # skip empty/bad
b=int(t/60)*60; # 60s bucket
key=b","(b+59)","vid
pkts[key]++; bytes[key]+=len;
src[key","s]=1; dst[key","d]=1;
buckets[key]=1;
}
END {
for (k in buckets) {
# Count unique src/dst per key
us=0; ud=0;
for (x in src) if (index(x, k",")==1) us++;
for (y in dst) if (index(y, k",")==1) ud++;
split(k, f, ",");
printf "%s,%s,%s,%d,%d,%d,%d\n", f[1], f[2], f[3], pkts[k]+0, bytes[k]+0, us, ud;
}
}' >> "$OUT"
echo "Wrote $OUT"
Model: anomaly_iforest.py
#!/usr/bin/env python3
import sys, json
import pandas as pd
from sklearn.ensemble import IsolationForest
import argparse
p = argparse.ArgumentParser()
p.add_argument("--csv", required=True)
p.add_argument("--contamination", type=float, default=0.05, help="Expected outlier fraction")
p.add_argument("--seed", type=int, default=42)
args = p.parse_args()
df = pd.read_csv(args.csv)
if df.empty:
print(json.dumps({"anomalies": [], "note":"no data"}))
sys.exit(0)
features = df[["pkts","bytes","uniq_src","uniq_dst"]].fillna(0)
model = IsolationForest(contamination=args.contamination, random_state=args.seed)
df["pred"] = model.fit_predict(features) # -1 = anomaly
df["score"] = model.decision_function(features)
anom = df[df["pred"] == -1].copy()
# Summarize by VLAN across buckets for a simpler alert
summary = (anom.groupby("vlan")
.agg(buckets=("vlan","count"),
worst_score=("score","min"),
pkts=("pkts","sum"),
bytes=("bytes","sum"))
.reset_index()
.sort_values("worst_score"))
print(json.dumps({"anomalies": summary.to_dict(orient="records")}, indent=2))
Wrapper: detect_anomalies.sh
#!/usr/bin/env bash
set -euo pipefail
IFACE=${1:-eth0}
DUR=${2:-300}
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
bash vlan_features.sh "$IFACE" "$DUR" "$TMP/features.csv" >/dev/null
source ~/venvs/ai-vlan/bin/activate
python3 anomaly_iforest.py --csv "$TMP/features.csv" > "$TMP/out.json"
cat "$TMP/out.json"
# Optional: send to a webhook (Slack example)
if [[ -n "${SLACK_WEBHOOK_URL:-}" ]]; then
text="AI VLAN anomalies on $(hostname): $(cat "$TMP/out.json")"
curl -s -X POST -H 'Content-type: application/json' \
--data "$(jq -n --arg t "$text" '{text:$t}')" \
"$SLACK_WEBHOOK_URL" >/dev/null || true
fi
Run a quick test window:
bash detect_anomalies.sh eth0 120
Schedule every 5 minutes:
crontab -e
*/5 * * * * /path/to/detect_anomalies.sh eth0 300 >> /var/log/vlan-ai.log 2>&1
What to look for:
VLANs with sudden spikes in packets/bytes
Unusually high unique MAC counts (scans, flooding, misconfig)
VLANs appearing on unexpected interfaces (combine with the inventory)
3) Intent-Based VLAN Provisioning with Dry-Run Safety
Codify desired VLANs, then have Bash reconcile the state. We’ll keep it simple, JSON in, ip/bridge out.
Example intent: intent.json
{
"interfaces": [
{
"parent": "eth0",
"vlans": [
{"id": 10, "name": "users", "ipv4": "192.0.2.1/24", "mtu": 1500, "bridge": "br0"},
{"id": 20, "name": "voice", "ipv4": null, "mtu": 1500}
]
}
]
}
Apply script: apply_intent.sh
#!/usr/bin/env bash
set -euo pipefail
INTENT=${1:?Usage: $0 intent.json}
DRY_RUN=${DRY_RUN:-1} # set to 0 to apply
apply() {
echo "+ $*"
if [[ "${DRY_RUN}" -eq 0 ]]; then
eval "$@"
fi
}
# Ensure bridge exists if referenced
for br in $(jq -r '..|objects|select(has("bridge"))|.bridge' "$INTENT" | sort -u); do
if [[ "$br" != "null" ]]; then
if ! ip link show "$br" &>/dev/null; then
apply "ip link add name $br type bridge"
apply "ip link set $br up"
fi
fi
done
# Reconcile VLANs
len=$(jq '.interfaces|length' "$INTENT")
for ((i=0;i<len;i++)); do
PARENT=$(jq -r ".interfaces[$i].parent" "$INTENT")
vlen=$(jq ".interfaces[$i].vlans|length" "$INTENT")
for ((j=0;j<vlen;j++)); do
VID=$(jq -r ".interfaces[$i].vlans[$j].id" "$INTENT")
NAME=$(jq -r ".interfaces[$i].vlans[$j].name" "$INTENT")
IPV4=$(jq -r ".interfaces[$i].vlans[$j].ipv4" "$INTENT")
MTU=$(jq -r ".interfaces[$i].vlans[$j].mtu" "$INTENT")
BR=$(jq -r ".interfaces[$i].vlans[$j].bridge" "$INTENT")
VIF="${PARENT}.${VID}"
if ip -d link show "$VIF" &>/dev/null; then
echo "VIF exists: $VIF"
else
apply "ip link add link $PARENT name $VIF type vlan id $VID"
fi
if [[ "$MTU" != "null" ]]; then
apply "ip link set dev $VIF mtu $MTU"
fi
apply "ip link set dev $VIF up"
if [[ "$IPV4" != "null" ]]; then
# avoid duplicate addresses
if ! ip addr show dev "$VIF" | grep -q "$IPV4"; then
apply "ip addr add $IPV4 dev $VIF"
fi
fi
if [[ "$BR" != "null" ]]; then
# ensure master assignment
if ! bridge link show dev "$VIF" | grep -q "master $BR"; then
apply "ip link set dev $VIF master $BR"
fi
fi
done
done
echo "Done. DRY_RUN=${DRY_RUN} (set DRY_RUN=0 to enforce)."
Try a dry run:
DRY_RUN=1 bash apply_intent.sh intent.json
Then apply:
sudo DRY_RUN=0 bash apply_intent.sh intent.json
Pro tip:
Preflight check for collisions by diffing
vlan_inventory.jsonagainstintent.json.Store
intent.jsonin Git and runapply_intent.shin CI after a policy check.
4) Close the Loop: Notify and (Carefully) Auto-Remediate
Turn insights into action. When the anomaly detector flags a VLAN on an unexpected trunk, you might:
Notify your chat room with a compact summary and links to runbooks
Remove the offending tag from a trunk (only if guardrails pass)
Drop a temporary iptables/nft rule for a noisy VLAN
Example: auto-remove a rogue VLAN from a given trunk if it’s not in your intent (use with caution; dry-run first).
auto_remediate.sh
#!/usr/bin/env bash
set -euo pipefail
TRUNK=${1:?Usage: $0 <trunk_if>}
INTENT=${2:-intent.json}
DRY_RUN=${DRY_RUN:-1}
# VLANs permitted on this trunk per intent
ALLOW=$(jq -r --arg if "$TRUNK" '
.interfaces[]? | select(.parent==$if) | .vlans[]?.id' "$INTENT" | sort -n | tr '\n' ' ')
echo "Allowed VLANs on $TRUNK: $ALLOW"
# VLANs currently present
CURRENT=$(bridge -json vlan show dev "$TRUNK" \
| jq -r '.[0].vlans[]?.vlan' | sort -n | tr '\n' ' ')
echo "Current VLANs on $TRUNK: $CURRENT"
# Compute rogue set
ROGUE=$(comm -13 <(echo "$ALLOW" | tr ' ' '\n') <(echo "$CURRENT" | tr ' ' '\n') | tr '\n' ' ')
if [[ -z "$ROGUE" ]]; then
echo "No rogue VLANs found."
exit 0
fi
for vid in $ROGUE; do
echo "Rogue VLAN $vid on $TRUNK"
echo "+ bridge vlan del dev $TRUNK vid $vid"
if [[ "$DRY_RUN" -eq 0 ]]; then
sudo bridge vlan del dev "$TRUNK" vid "$vid" || true
fi
done
Run it in dry-run mode first:
DRY_RUN=1 bash auto_remediate.sh eth0
Tie it to alerts: call auto_remediate.sh only if the anomaly JSON also shows the VLAN is not permitted by intent and after human approval (e.g., require a “/approve” in ChatOps).
Real-World Usage Patterns
Brownfield servers: Baseline all trunks with
vlan_inventory.sh, compare to a CSV of “expected” VLANs, fix drift gradually.Host-based segmentation: Use
apply_intent.shto ensure every host gets the same user/voice/storage VLANs with consistent MTU and bridge membership.Nightly watch:
detect_anomalies.shcatches VLANs with midnight scan patterns or data-exfil bursts.Change windows: Dry-run intent, produce a diff, and post it in Slack for peer review before applying.
Conclusion and Next Steps
You don’t need a massive platform to get smart about VLANs. With a handful of Linux tools and a lightweight ML step, you can:
Keep accurate, auditable inventory
Spot abnormal VLAN behavior fast
Apply consistent, versioned intent safely
Automate the boring and risky bits, under your control
Next steps:
Put these scripts in a Git repo and wire them into CI
Add a policy check that fails on disallowed VLAN IDs or untagged trunk ports
Expand features (e.g., per-VLAN flow entropy) to improve anomaly signal
Export your features to Prometheus/Grafana for ongoing visibility
Have a cool improvement or a tricky VLAN story? Share it with the community—and happy automating!