(status)
| 6679 | const d = await fetchAPI('/api/net/raguard'); |
| 6680 | if (!d || d.success === false) { out.innerHTML = '<p class="text-sm text-red-400">Error: ' + escapeHtml((d && d.error) || 'failed') + '</p>'; return; } |
| 6681 | _raguardRender(d, out); |
| 6682 | } catch (e) { |
| 6683 | out.innerHTML = '<p class="text-sm text-red-400">Failed: ' + escapeHtml(e.message) + '</p>'; |
| 6684 | } finally { _ndBusy(btn, false); } |
| 6685 | } |
| 6686 | async function raGuardHarden() { |
| 6687 | if (!confirm('Harden this host\'s IPv6 first-hop settings?\n\nSets accept_redirects=0 and accept_ra_rtr_pref=0 (live + persisted to /etc/sysctl.d). accept_ra is left untouched, so SLAAC connectivity keeps working.')) return; |
| 6688 | const out = document.getElementById('raguard-results'); |
| 6689 | if (!out) return; |
| 6690 | const btn = (typeof event !== 'undefined' && event && event.target) ? event.target : null; |
| 6691 | _ndBusy(btn, true, 'Hardening…'); |
| 6692 | out.classList.remove('hidden'); |
| 6693 | try { |
| 6694 | const d = await postAPI('/api/net/raguard', { action: 'harden' }); |
| 6695 | if (!d || d.success === false) { out.innerHTML = '<p class="text-sm text-red-400">Error: ' + escapeHtml((d && d.error) || 'failed') + '</p>'; return; } |
| 6696 | addConsoleMessage('IPv6 RA Guard: host hardened (accept_redirects=0, accept_ra_rtr_pref=0)', 'success'); |
| 6697 | _raguardRender(d, out); |
| 6698 | } catch (e) { |
| 6699 | out.innerHTML = '<p class="text-sm text-red-400">Failed: ' + escapeHtml(e.message) + '</p>'; |
| 6700 | } finally { _ndBusy(btn, false); } |
| 6701 | } |
| 6702 | |
| 6703 | // ---- IPv6 First-Hop Watch (rogue RA / DHCPv6, passive) --------------------- |
| 6704 | const _IPV6_VERDICT_STYLE = { |
| 6705 | clean: ['bg-green-950/40 border-green-900 text-green-400', '✓ No rogue IPv6 first-hop activity detected'], |
| 6706 | anomaly: ['bg-amber-950/50 border-amber-800 text-amber-300', '⚠ IPv6 first-hop anomaly — conflicting / deprecating RA'], |
| 6707 | 'rogue-dhcpv6': ['bg-red-950/60 border-red-800 text-red-300', '🛑 Rogue DHCPv6 server — mitm6 DNS-takeover signature'], |
| 6708 | 'rogue-redirect':['bg-red-950/60 border-red-800 text-red-300', '🛑 Rogue ICMPv6 Redirect — Layer-3 IPv6 MITM'], |
| 6709 | 'rogue-ra': ['bg-red-950/60 border-red-800 text-red-300', '🛑 Rogue Router Advertisement — SLAAC gateway/DNS takeover'], |
| 6710 | storm: ['bg-red-950/60 border-red-800 text-red-300', '🛑 Router Advertisement flood (RA-flood DoS)'], |
| 6711 | unknown: ['bg-slate-800 border-slate-700 text-slate-400', '— Could not determine'], |
| 6712 | }; |
| 6713 | function _ipv6FillIfaces() { |
| 6714 | const sel = document.getElementById('ipv6-iface'); |
| 6715 | if (!sel || sel.dataset.filled === '1') return Promise.resolve(); |
| 6716 | return fetchAPI('/api/net/interfaces').then(x => { |
| 6717 | (x.interfaces || []).forEach(i => { |
| 6718 | const o = document.createElement('option'); |
| 6719 | o.value = i.name; |
| 6720 | const tag = i.type === 'wifi' ? ' (WiFi)' : i.type === 'ethernet' ? ' (LAN)' : (i.type ? ' (' + i.type + ')' : ''); |
| 6721 | o.textContent = i.name + tag; |
| 6722 | sel.appendChild(o); |
| 6723 | }); |
| 6724 | sel.dataset.filled = '1'; |
| 6725 | }).catch(() => {}); |
| 6726 | } |
| 6727 | async function runIpv6Watch() { |
| 6728 | const out = document.getElementById('ipv6-results'); |
| 6729 | if (!out) return; |
| 6730 | const btn = (typeof event !== 'undefined' && event && event.target) ? event.target : null; |
| 6731 | const ifaceSel = document.getElementById('ipv6-iface'); |
| 6732 | const iface = ifaceSel && ifaceSel.value ? ifaceSel.value : ''; |
no outgoing calls
no test coverage detected