Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions kernel_ai/services/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@

def _get_default_iface():
try:
with open("/proc/net/route", "r", encoding="utf-8", errors="ignore") as f:

Check failure on line 127 in kernel_ai/services/network.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "/proc/net/route" 3 times.

See more on https://sonarcloud.io/project/issues?id=devops-stack_ring-0&issues=AZ-ZPOOEtfFoT9tNnU1e&open=AZ-ZPOOEtfFoT9tNnU1e&pullRequest=148
lines = f.readlines()[1:]
for line in lines:
parts = line.strip().split()
Expand Down Expand Up @@ -282,6 +282,123 @@
return v * scale


def _hex_le_ipv4(hex_str: str) -> str:
raw = (hex_str or "").strip()
if len(raw) != 8:
return "0.0.0.0"
try:
parts = [raw[i : i + 2] for i in range(0, 8, 2)]
parts.reverse()
return ".".join(str(int(b, 16)) for b in parts)
except ValueError:
return "0.0.0.0"


def _mask_to_prefix(mask_hex: str) -> int:
try:
value = int(mask_hex, 16)
except ValueError:
return 0
return bin(value).count("1")


def get_ip_layer_map(limit_routes: int = 12, limit_neigh: int = 10) -> dict:

Check failure on line 305 in kernel_ai/services/network.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 29 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=devops-stack_ring-0&issues=AZ-ZPOOEtfFoT9tNnU1f&open=AZ-ZPOOEtfFoT9tNnU1f&pullRequest=148
"""Live IP-layer map: FIB routes, ARP/neigh, ICMP + IP SNMP counters."""
routes: list[dict] = []
try:
with open("/proc/net/route", "r", encoding="utf-8", errors="ignore") as fh:
for line in fh.readlines()[1:]:
parts = line.strip().split()
if len(parts) < 8:
continue
iface, dest_h, gw_h, flags_h = parts[0], parts[1], parts[2], parts[3]
metric = int(parts[6]) if parts[6].isdigit() else 0
mask_h = parts[7]
try:
flags = int(flags_h, 16)
except ValueError:
flags = 0
if not (flags & 0x1): # RTF_UP
continue
dest = _hex_le_ipv4(dest_h)
gateway = _hex_le_ipv4(gw_h)
prefix = _mask_to_prefix(mask_h)
is_default = dest_h == "00000000" and (flags & 0x2)
routes.append(
{
"iface": iface,
"destination": "default" if is_default else f"{dest}/{prefix}",
"gateway": gateway if gateway != "0.0.0.0" else "*",
"metric": metric,
"flags": flags,
"default": bool(is_default),
}
)
routes.sort(key=lambda r: (0 if r.get("default") else 1, r.get("metric", 0), r.get("destination", "")))
routes = routes[: max(1, int(limit_routes))]
except OSError:
routes = []

neigh: list[dict] = []
try:
with open("/proc/net/arp", "r", encoding="utf-8", errors="ignore") as fh:
for line in fh.readlines()[1:]:
parts = line.split()
if len(parts) < 6:
continue
ip_addr, _hw_type, flags_h, mac, _mask, device = parts[:6]
try:
flags = int(flags_h, 16)
except ValueError:
flags = 0
if mac in ("00:00:00:00:00:00", "0:0:0:0:0:0"):
continue
state = "REACHABLE" if flags & 0x2 else ("INCOMPLETE" if flags & 0x1 else "STALE")

Check warning on line 356 in kernel_ai/services/network.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested conditional expression into an independent statement.

See more on https://sonarcloud.io/project/issues?id=devops-stack_ring-0&issues=AZ-ZPOOEtfFoT9tNnU1g&open=AZ-ZPOOEtfFoT9tNnU1g&pullRequest=148
neigh.append(
{
"ip": ip_addr,
"mac": mac,
"iface": device,
"state": state,
"flags": flags,
}
)
neigh = neigh[: max(1, int(limit_neigh))]
except OSError:
neigh = []

ip_stats = _parse_snmp_section("Ip")
icmp_stats = _parse_snmp_section("Icmp")
return {
"routes": routes,
"neigh": neigh,
"icmp": {
"in_msgs": int(icmp_stats.get("InMsgs", 0)),
"out_msgs": int(icmp_stats.get("OutMsgs", 0)),
"in_errors": int(icmp_stats.get("InErrors", 0)),
"out_errors": int(icmp_stats.get("OutErrors", 0)),
"in_dest_unreach": int(icmp_stats.get("InDestUnreachs", 0)),
"out_dest_unreach": int(icmp_stats.get("OutDestUnreachs", 0)),
"in_time_excds": int(icmp_stats.get("InTimeExcds", 0)),
"in_echo_reps": int(icmp_stats.get("InEchoReps", 0)),
"out_echos": int(icmp_stats.get("OutEchos", 0)),
},
"ip": {
"forwarding": int(ip_stats.get("Forwarding", 0)),
"default_ttl": int(ip_stats.get("DefaultTTL", 0)),
"in_receives": int(ip_stats.get("InReceives", 0)),
"out_requests": int(ip_stats.get("OutRequests", 0)),
"in_discards": int(ip_stats.get("InDiscards", 0)),
"out_discards": int(ip_stats.get("OutDiscards", 0)),
"in_addr_errors": int(ip_stats.get("InAddrErrors", 0)),
"in_hdr_errors": int(ip_stats.get("InHdrErrors", 0)),
"reasm_reqds": int(ip_stats.get("ReasmReqds", 0)),
"frag_oks": int(ip_stats.get("FragOKs", 0)),
},
"source": {"routes": "/proc/net/route", "neigh": "/proc/net/arp", "snmp": "/proc/net/snmp"},
}


def get_network_stack_realtime(network_stack_prev=None):
network_stack_prev = _NETWORK_STACK_PREV_DEFAULT if network_stack_prev is None else network_stack_prev
now = time.time()
Expand Down Expand Up @@ -439,6 +556,7 @@
"out_segs": int(tcp_stats.get("OutSegs", 0)),
"retrans_segs_total": int(retrans_total),
},
"ip_map": get_ip_layer_map(),
}


Expand Down
158 changes: 156 additions & 2 deletions static/js/network-stack.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Network Stack Visualization - vertical packet flow through Linux networking layers
// Version: 5
// Version: 6 — L04 IP drill-in replaces puzzle with FIB/route/neigh/ICMP map

debugLog('🌐 network-stack.js v5: Script loading...');
debugLog('🌐 network-stack.js v6: Script loading...');

class NetworkStackVisualization {
constructor() {
Expand Down Expand Up @@ -95,6 +95,7 @@
this.selectedGalaxy = 'state';
this.galaxyStateData = null;
this.lifecyclePanelNode = null;
this.ipMapPinned = false;
this.hideOsiTiles = true;
this.hideVerticalOrbs = true;
this.packetLifecycleStages = [
Expand Down Expand Up @@ -1946,6 +1947,10 @@

updatePacketLifecycleUI() {
if (!this.lifecyclePanelNode) return;
if (this.ipMapPinned || this.drillLayerId === 'ip') {
this.renderIpLayerMapPanel();
return;
}
const idx = this.getPacketLifecycleIndex();
const stage = this.packetLifecycleStages[Math.max(0, Math.min(this.packetLifecycleStages.length - 1, idx))] || 'NIC RX';
const activeCoreNode = this.lifecycleStageToNode[stage] || 'skb';
Expand Down Expand Up @@ -2945,13 +2950,157 @@
return (rows[layerId] || (() => []))();
}

getIpMapData() {
return this.telemetryData?.ip_map || { routes: [], neigh: [], icmp: {}, ip: {} };
}

buildIpLayerMapHtml({ compact = false } = {}) {
const map = this.getIpMapData();
const routes = Array.isArray(map.routes) ? map.routes : [];
const neigh = Array.isArray(map.neigh) ? map.neigh : [];
const icmp = map.icmp || {};
const ip = map.ip || {};
const n = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
const fwd = n(ip.forwarding) === 1 ? 'forwarding ON' : 'host (no forward)';
const routeRows = routes.length
? routes.map((r) => {
const dest = r.default
? '<span style="color:#e6c15a">default</span>'
: `<span style="color:#d4dde7">${r.destination}</span>`;
const via = r.gateway && r.gateway !== '*'
? `via <span style="color:#a9d4e8">${r.gateway}</span>`
: 'on-link';
return `<div style="display:flex; gap:8px; flex-wrap:wrap; padding:3px 0; border-bottom:1px solid rgba(70,82,98,0.25);">
<span style="min-width:118px;">${dest}</span>
<span style="color:#8d99a7;">${via}</span>
<span style="color:#6f8597;">dev ${r.iface}</span>
<span style="color:#556273;margin-left:auto;">metric ${r.metric ?? 0}</span>
</div>`;
}).join('')
: '<div style="color:#6f8597;">no routes in /proc/net/route</div>';
const neighRows = neigh.length
? neigh.map((h) => {
const st = String(h.state || 'STALE');
const stCol = st === 'REACHABLE' ? '#96ffbe' : (st === 'INCOMPLETE' ? '#e69696' : '#e6c15a');

Check warning on line 2984 in static/js/network-stack.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=devops-stack_ring-0&issues=AZ-ZPOSJtfFoT9tNnU1h&open=AZ-ZPOSJtfFoT9tNnU1h&pullRequest=148
return `<div style="display:flex; gap:8px; flex-wrap:wrap; padding:3px 0; border-bottom:1px solid rgba(70,82,98,0.25);">
<span style="color:#a9d4e8;min-width:110px;">${h.ip}</span>
<span style="color:#c2cede;">${h.mac}</span>
<span style="color:#6f8597;">dev ${h.iface}</span>
<span style="color:${stCol};margin-left:auto;letter-spacing:0.5px;">${st}</span>
</div>`;
}).join('')
: '<div style="color:#6f8597;">no ARP/neigh entries</div>';
const chip = (label, value, hot = false) => `
<div style="background:rgba(8,12,20,0.7); border:1px solid ${hot ? 'rgba(230,193,90,0.45)' : 'rgba(96,110,128,0.32)'}; border-radius:4px; padding:7px 9px; min-width:88px;">
<div style="font-size:8px; letter-spacing:0.6px; color:#7f93a6; text-transform:uppercase;">${label}</div>
<div style="font-size:${compact ? '14px' : '16px'}; color:#e2edf5; margin-top:2px;">${value}</div>
</div>`;
const icmpHot = n(icmp.in_errors) + n(icmp.out_errors) + n(icmp.in_dest_unreach) > 0;
const flowDiagram = `
<div style="display:flex; align-items:center; gap:6px; flex-wrap:wrap; margin:8px 0 10px; font-size:9px; letter-spacing:0.4px;">
<span style="color:#6f8597;">LOOKUP</span>
<span style="padding:3px 8px; border:1px solid rgba(230,193,90,0.45); border-radius:12px; color:#e6c15a;">FIB</span>
<span style="color:#556273;">→</span>
<span style="padding:3px 8px; border:1px solid rgba(103,190,224,0.45); border-radius:12px; color:#a9d4e8;">NEIGH</span>
<span style="color:#556273;">→</span>
<span style="padding:3px 8px; border:1px solid rgba(150,255,190,0.35); border-radius:12px; color:#96ffbe;">L2 / NIC</span>
<span style="color:#556273; margin:0 4px;">|</span>
<span style="padding:3px 8px; border:1px solid rgba(232,96,104,0.4); border-radius:12px; color:#e69696;">ICMP</span>
<span style="color:#6f8597;">control / errors</span>
</div>`;
return `
<div style="display:flex; align-items:center; gap:10px; margin-bottom:6px;">
<div style="flex:1;">
<div style="font-size:10px;color:#7f8fa2;letter-spacing:0.6px;">IP LAYER MAP · L04 · ${fwd}</div>
<div style="font-size:9px;color:#556273;margin-top:2px;">/proc/net/route · /proc/net/arp · /proc/net/snmp</div>
</div>
${compact ? `<button type="button" class="ns-ip-back-puzzle" style="cursor:pointer; font:inherit; font-size:9px; letter-spacing:0.6px; color:#a9d4e8; background:rgba(103,190,224,0.1); border:1px solid rgba(103,190,224,0.4); border-radius:12px; padding:4px 10px;">← PUZZLE</button>` : ''}
</div>
${flowDiagram}
<div style="display:grid; grid-template-columns:${compact ? '1.2fr 1fr 0.9fr' : '1.15fr 1fr 0.95fr'}; gap:10px;">
<div>
<div style="font-size:8px; letter-spacing:1px; color:#e6c15a; margin-bottom:4px;">FIB / ROUTES</div>
<div style="max-height:${compact ? '12vh' : '28vh'}; overflow:auto; font-size:9.5px; line-height:1.45;">${routeRows}</div>
</div>
<div>
<div style="font-size:8px; letter-spacing:1px; color:#a9d4e8; margin-bottom:4px;">NEIGH / ARP</div>
<div style="max-height:${compact ? '12vh' : '28vh'}; overflow:auto; font-size:9.5px; line-height:1.45;">${neighRows}</div>
</div>
<div>
<div style="font-size:8px; letter-spacing:1px; color:#e69696; margin-bottom:4px;">ICMP · IP COUNTERS</div>
<div style="display:grid; grid-template-columns:1fr 1fr; gap:6px;">
${chip('ICMP in', n(icmp.in_msgs))}
${chip('ICMP out', n(icmp.out_msgs))}
${chip('errors', n(icmp.in_errors) + n(icmp.out_errors), icmpHot)}
${chip('unreach', n(icmp.in_dest_unreach) + n(icmp.out_dest_unreach), n(icmp.in_dest_unreach) > 0)}
${chip('TTL / echo', `${n(ip.default_ttl) || '—'} / ${n(icmp.out_echos)}`)}
${chip('IP discards', n(ip.in_discards) + n(ip.out_discards), n(ip.in_discards) + n(ip.out_discards) > 0)}
</div>
</div>
</div>`;
}

renderIpLayerMapPanel() {
if (!this.lifecyclePanelNode) return;
window.setSafeHtml(this.lifecyclePanelNode, this.buildIpLayerMapHtml({ compact: true }));
const back = this.lifecyclePanelNode.querySelector('.ns-ip-back-puzzle');
if (back) {
back.addEventListener('click', () => {
this.ipMapPinned = false;
if (this.drillLayerId === 'ip') this.closeLayerDrilldown();
else this.updatePacketLifecycleUI();
});
}
}

openLayerDrilldown(layerId) {

Check failure on line 3056 in static/js/network-stack.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=devops-stack_ring-0&issues=AZ-ZPOSJtfFoT9tNnU1i&open=AZ-ZPOSJtfFoT9tNnU1i&pullRequest=148
if (!this.drillScrim || !this.drillPanel) return;
const info = this.getLayerDrillInfo(layerId);
if (!info) return;
this.drillLayerId = layerId;

if (layerId === 'ip') {
this.ipMapPinned = true;
this.drillPanel.style.width = 'min(980px, 92vw)';
const act = Math.round(Math.max(0, Math.min(1, Number(this.layerActivity.ip ?? 0))) * 100);
const actCol = act > 80 ? 'rgba(232,96,104,0.95)' : (act > 55 ? 'rgba(230,193,90,0.95)' : 'rgba(103,190,224,0.95)');
const metrics = this.getLayerDrillMetrics('ip');
const metricCells = metrics.map(([k, v]) => `
<div style="background:rgba(8,12,20,0.7); border:1px solid rgba(96,110,128,0.32); border-radius:4px; padding:8px 10px;">
<div style="font-size:8.5px; letter-spacing:0.6px; color:#7f93a6; text-transform:uppercase;">${k}</div>
<div style="font-size:18px; color:#e2edf5; line-height:1.15; margin-top:2px;">${v}</div>
</div>`).join('');
const html = `
<div style="display:flex; align-items:center; gap:12px; padding:14px 18px; border-bottom:1px solid rgba(230,193,90,0.35); background:linear-gradient(90deg, rgba(230,193,90,0.12), rgba(103,190,224,0.05));">
<div style="flex:1 1 auto;">
<div style="font-size:8px; letter-spacing:1.4px; color:#6f8597;">STACK LAYER · NETWORK · L04</div>
<div style="font-size:20px; letter-spacing:1.2px; color:#e8f2f9;">IP · FIB / ROUTE / NEIGH / ICMP</div>
</div>
<div style="flex:none; text-align:right;">
<div style="font-size:8px; letter-spacing:1px; color:#6f8597;">LIVE ACTIVITY</div>
<div style="font-size:22px; color:${actCol};">${act}<span style="font-size:11px; color:#7f93a6;">%</span></div>
</div>
<div class="ns-ov-close" style="flex:none; cursor:pointer; width:26px; height:26px; border:1px solid rgba(160,170,190,0.4); border-radius:4px; display:flex; align-items:center; justify-content:center; color:#c8ccd4; font-size:14px;">✕</div>
</div>
<div style="padding:14px 18px 16px; max-height:78vh; overflow:auto;">
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(120px, 1fr)); gap:8px; margin-bottom:12px;">${metricCells}</div>
<div style="font-size:11.5px; line-height:1.6; color:#c2cede; margin-bottom:10px;">${info.what}</div>
<div style="margin-bottom:8px; font-size:10.5px; line-height:1.55; color:#9db6c8; border-left:2px solid rgba(230,193,90,0.6); padding-left:9px;"><span style="color:#e6c15a; letter-spacing:0.5px;">WATCH · </span>${info.watch}</div>
${this.buildIpLayerMapHtml({ compact: false })}
</div>`;
window.setSafeHtml(this.drillPanel, html);
const closeBtn = this.drillPanel.querySelector('.ns-ov-close');
if (closeBtn) closeBtn.addEventListener('click', () => this.closeLayerDrilldown());
this.renderIpLayerMapPanel();
this.drillScrim.style.display = 'block';
if (this.layerTooltipNode) this.layerTooltipNode.style.display = 'none';
return;
}

this.ipMapPinned = false;
this.drillPanel.style.width = 'min(680px, 78vw)';
const act = Math.round(Math.max(0, Math.min(1, Number(this.layerActivity[layerId] ?? 0))) * 100);
const actCol = act > 80 ? 'rgba(232,96,104,0.95)' : (act > 55 ? 'rgba(230,193,90,0.95)' : 'rgba(103,190,224,0.95)');

Check warning on line 3103 in static/js/network-stack.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=devops-stack_ring-0&issues=AZ-ZPOSJtfFoT9tNnU1j&open=AZ-ZPOSJtfFoT9tNnU1j&pullRequest=148
const metrics = this.getLayerDrillMetrics(layerId);
const metricCells = metrics.map(([k, v]) => `
<div style="background:rgba(8,12,20,0.7); border:1px solid rgba(96,110,128,0.32); border-radius:4px; padding:8px 10px;">
Expand Down Expand Up @@ -2993,8 +3142,13 @@
}

closeLayerDrilldown() {
const wasIp = this.drillLayerId === 'ip';
this.drillLayerId = null;
if (this.drillScrim) this.drillScrim.style.display = 'none';
if (this.drillPanel) this.drillPanel.style.width = 'min(680px, 78vw)';
// Keep IP map pinned in the bottom panel after closing the overlay
// until the user hits ← PUZZLE or opens another layer.
if (wasIp) this.renderIpLayerMapPanel();
}

// Push the latest BBR sample into a rolling window (used to estimate the
Expand Down
2 changes: 1 addition & 1 deletion templates/linux-network-subsystem.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ <h2>TCP BBR Path Model</h2>
</section>
<script src="/static/js/safe-utils.js?v=2"></script>
<script src="/static/js/frontend-logger.js?v=2"></script>
<script src="/static/js/network-stack.js?v=41"></script>
<script src="/static/js/network-stack.js?v=42"></script>
<script>
(function startNetworkPage() {
if (typeof window.NetworkStackVisualization !== 'function') {
Expand Down
Loading