From e0c9338357ec116bb8a56ff96cebdbd0552052a8 Mon Sep 17 00:00:00 2001 From: logicog Date: Mon, 12 Jan 2026 14:33:54 +0100 Subject: [PATCH] Add support in web-interface for displaying L2 entries --- html/l2.html | 19 +++++++ html/l2.js | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 html/l2.html create mode 100644 html/l2.js diff --git a/html/l2.html b/html/l2.html new file mode 100644 index 0000000..298c82a --- /dev/null +++ b/html/l2.html @@ -0,0 +1,19 @@ + + + + + FreeSwitchOS L2 Configuration + + + +
+
+

L2 Configuration

+ + + +
Port MAC VLAN Type Remove Entry
+
+ + + diff --git a/html/l2.js b/html/l2.js new file mode 100644 index 0000000..ec7ff3a --- /dev/null +++ b/html/l2.js @@ -0,0 +1,143 @@ +var l2GetInterval; +var l2Entries = []; +var l2CurrentEntry = 0; + +function fillStats() { + var tbl = document.getElementById('statstable'); + if (!numPorts) + return; + if (tbl.rows.length > 1) { + for (let i = 0; i < numPorts; i++) { + console.log("Table Update row: " + i + " state " + pState[i] + " is " + linkS[pState[i] +1]); + tbl.rows[i+1].cells[1].innerHTML = `${linkS[pState[i]+1]}`; + tbl.rows[i+1].cells[2].innerHTML = `${txG[i]} pkts`; + tbl.rows[i+1].cells[3].innerHTML = `${txB[i]} pkts`; + tbl.rows[i+1].cells[4].innerHTML = `${rxG[i]} pkts`; + tbl.rows[i+1].cells[5].innerHTML = `${rxB[i]} pkts`; + } + } else { + for (let i = 0; i < numPorts; i++) { + console.log("Table row: " + i); + const tr = tbl.insertRow(); + let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i+1}`)); + td = tr.insertCell(); td.appendChild(document.createTextNode(`${linkS[pState[i]+1]}`)); + td = tr.insertCell(); td.appendChild(document.createTextNode(`${txG[i]} pkts`)); + td = tr.insertCell();td.appendChild(document.createTextNode(`${txB[i]} pkts`)); + td = tr.insertCell();td.appendChild(document.createTextNode(`${rxG[i]} pkts`)); + td = tr.insertCell();td.appendChild(document.createTextNode(`${rxB[i]} pkts`)); + } + } +} + +function l2CMP(a, b) +{ + if (a.port < b.port) + return -1; + if (a.port > b.port) + return 1; + if (a.mac < b.mac) + return -1; + if (a.mac > b.mac) + return 1; + if (a.vlan < b.vlan) + return -1; + if (a.vlan > b.vlan) + return 1; + return 0; +} + +function uniq(a) { + return a.sort().filter(function(item, pos, ary) { + return !pos || item != ary[pos - 1]; + }); +} + +function delL2(idx) { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + console.log("Entry deleted"); + } + }; + xhttp.open("GET", "/l2_del.json?idx=" + idx, true); + xhttp.timeout = 1500; xhttp.send(); +} + +function fillL2(s) +{ + console.log("L2: ", JSON.stringify(s)); + var tbl = document.getElementById('l2table'); + if (!s.length) + return; + s.sort(l2CMP); + s = uniq(s); + var s = s.map(function(e) { e.port = e.port != 9 ? e.port : "CPU"; return e; }); + console.log("L2 now: ", JSON.stringify(s)); + for (let i = 0; i < s.length; i++) { + var e = s[i]; + console.log(i, e); + if (tbl.rows[i+1]) { + console.log(i, " Updating: port ", e.port, ", mac: ", e.mac); + tbl.rows[i+1].cells[0].innerHTML = `${e.port}`; + tbl.rows[i+1].cells[1].innerHTML = `${e.mac}`; + tbl.rows[i+1].cells[2].innerHTML = `${e.vlan}`; + tbl.rows[i+1].cells[4].innerHTML = ''; + } else { + console.log(i, " Inserting: port ", e.port, ", mac: ", e.mac); + const tr = tbl.insertRow(); + let td = tr.insertCell(); td.innerHTML = `${e.port}`; + td = tr.insertCell(); td.innerHTML = `${e.mac}`; + td = tr.insertCell(); td.innerHTML = `${e.vlan}`; + td = tr.insertCell(); td.innerHTML = `${e.type}`; + td = tr.insertCell(); td.innerHTML = ''; + } + } + console.log("s-length: ", s.length, " table-length: ", tbl.rows.length); + for (let i = tbl.rows.length - 1; i > s.length; i--) + tbl.deleteRow(i); + l2Entries = []; + console.log("L2 interval now ", l2GetInterval); +} + +function getL2() { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + var s = JSON.parse(xhttp.responseText); + var s = s.map(function(e) { + e.vlan = parseInt(e.vlan, 16); + e.idx = parseInt(e.idx, 16); + e.type = e.type == "s" ? "static" : "learned"; + e.port = e.port == 9 ? 9 : logToPhysPort[e.port]; + return e; + }); + l2Entries.push(...s); + if (l2Entries >= 4096) { + l2Entries = []; + l2CurrentEntry = 0; + clearInterval(l2GetInterval); + return; + } + var w = 0; + for (var i = l2Entries.length-1; i > 0; i--) { + if (l2Entries[0].idx == l2Entries[i].idx) { + w = 1; + break; + } + } + if (w) { + l2CurrentEntry = 0; + fillL2(l2Entries); + } else { + l2CurrentEntry = s[s.length-1].idx + 1; + } + } + }; + xhttp.open("GET", "/l2.json?idx=" + l2CurrentEntry, true); + xhttp.timeout = 1500; xhttp.send(); +} + +window.addEventListener("load", function() { + l2GetInterval = setInterval(getL2, 300); +}); +