diff --git a/html/app.js b/html/app.js index 97960236..613ff769 100644 --- a/html/app.js +++ b/html/app.js @@ -1,6 +1,7 @@ "use strict"; var LANG={ en:{ +ROBTODO:"TODOTODOTODO", nav_dash:"Dashboard",nav_ports:"Ports",nav_stp:"Spanning tree",nav_stats:"Statistics", nav_vlan:"VLANs",nav_l2:"MAC table",nav_mirror:"Mirroring",nav_isolate:"Isolation",nav_lag:"LAG",nav_eee:"EEE", nav_bw:"Bandwidth",nav_system:"System",nav_fw:"Firmware",nav_lldp:"LLDP", @@ -47,8 +48,8 @@ st_autoref:"auto-refresh",st_counter:"Counter",st_value:"Value",st_fail:"failed v_title:"VLANs",v_vid:"VID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on", v_empty:"No VLANs configured.",v_mgmt:"Management VLAN",v_mgmt_none:"untagged",v_editor:"VLAN editor", v_optional:"optional",v_setmgmt:"Set as mgmt VLAN",v_setmgmt_t:"Make this the management VLAN", -v_member:"Member",v_pvid:"PVID", -v_legend:"U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.", +v_member:"Member",v_pvid:"PVID",v_add:"Add a new VLAN", +v_legend:"U = untagged member, T = tagged member, - = not a member. PVID is automatically assigned based on the untagged membership.", v_ingress:"Ingress filtering",v_ingress_h:"write-only: the state is not readable from the switch", v_accept:"Accept",v_ing_all:"All",v_ing_apply:"Apply ingress modes",v_del_t:"Delete VLAN", v_del_q:"Delete VLAN {n}?",v_del_d:"Ports keep their PVID until reassigned.", @@ -1464,60 +1465,103 @@ function rangeStr(list){ } return parts.join(", "); } -function vlanRefresh(){ - var tb=$("vtable").tBodies[0]; - return getJSON("/vlanlist").then(function(d){ - var vl=d.vlan||[]; - $("vmgmtcur").textContent=d.mgmt?String(d.mgmt):t("v_mgmt_none"); - $("vempty").style.display=vl.length?"none":""; - tb.innerHTML=""; - var p=Promise.resolve(); - vl.forEach(function(v){ - p=p.then(function(){return getJSON("/vlan.json?vid="+v.id).catch(function(){return null})}).then(function(d){ - var tr=tb.insertRow(); - tr.insertCell().appendChild(h("a",{href:"#vlan",text:String(v.id),onclick:function(e){ - e.preventDefault();$("vvid").value=v.id;vlanLoad(); - }})); - tr.insertCell().textContent=v.name||""; - if(!d){for(var c=0;c<4;c++)tr.insertCell().textContent="?";} - else{ - var m=parseInt(d.members,16),mem=m&0x3ff,unt=((m>>10)&0x3ff)&mem; - var pv=parseInt(d.pvid,16)&0x3ff; - tr.insertCell().textContent=rangeStr(maskToPorts(mem)); - tr.insertCell().textContent=rangeStr(maskToPorts(mem&~unt)); - tr.insertCell().textContent=rangeStr(maskToPorts(unt)); - tr.insertCell().textContent=rangeStr(maskToPorts(pv)); +//ROBSTART +function vlanRefresh() { + var tb = $("vtable").tBodies[0]; + return getJSON("/vlanlist").then(function(d) { + var vl = d.vlan || []; + $("vmgmtcur").textContent = d.mgmt ? String(d.mgmt) : t("v_mgmt_none"); + $("vempty").style.display = vl.length ? "none" : ""; + tb.innerHTML = ""; + var p = Promise.resolve(); + vl.forEach(function(v) { + p = p.then(function() { + return getJSON("/vlan.json?vid=" + v.id).catch(function() { + return null + }) + }).then(function(d) { + var tr = tb.insertRow(); + var l = h("a", { + href: "#vlan", + text: String(v.id), + "data-id": v.id, + "data-name": v.name, + onclick: function(e) { + e.preventDefault(); + vlanLoad(v.id); + } + }); + tr.insertCell().appendChild(l); + tr.insertCell().textContent = v.name || ""; + if (!d) { + for (var c = 0; c < 4; c++) tr.insertCell().textContent = "?"; + } else { + var m = parseInt(d.members, 16), + mem = m & 0x3ff, + unt = ((m >> 10) & 0x3ff) & mem, + tag = mem & ~unt; + var pv = parseInt(d.pvid, 16) & 0x3ff; + tr.insertCell().textContent = rangeStr(maskToPorts(mem)); + tr.insertCell().textContent = rangeStr(maskToPorts(tag)); + tr.insertCell().textContent = rangeStr(maskToPorts(unt)); + tr.insertCell().textContent = rangeStr(maskToPorts(pv)); + l.dataset.t = maskToPorts(tag); + l.dataset.u = maskToPorts(unt); } - var del=tr.insertCell(); - if(v.id!==1)del.appendChild(h("button",{class:"ctl",text:"\u2715",title:t("v_del_t"),onclick:function(){ - confirmModal(t("v_del_q",{n:v.id}),t("v_del_d"),function(){ - postCmd("vlan "+v.id+" d").then(vlanRefresh).catch(function(){}); - }); - }})); - }).catch(function(){}); + var del = tr.insertCell(); + if (v.id !== 1) del.appendChild(h("button", { + class: "ctl", + text: "\u2715", + title: t("v_del_t"), + onclick: function() { + confirmModal(t("v_del_q", { + n: v.id + }), t("v_del_d"), function() { + postCmd("vlan " + v.id + " d").then(vlanRefresh).catch(function() {}); + }); + } + })); + }).catch(function() {}); }); return p; }); } -function buildVlanEdit(){ - var tb=$("vedit").tBodies[0]; - if(tb.rows.length||!S.n)return; - var hd=tb.insertRow();hd.insertCell().className="mut"; - var rM=tb.insertRow();rM.insertCell().textContent=t("v_member"); - var rP=tb.insertRow();rP.insertCell().textContent=t("v_pvid"); - for(var p=1;p<=S.n;p++)(function(p){ - hd.insertCell().innerHTML=''+p+''; - var seg=h("span",{class:"seg",id:"vm"+p}); - ["-","U","T"].forEach(function(s,ix){ - seg.appendChild(h("button",{text:s,"data-v":ix,onclick:function(){ - seg.querySelectorAll("button").forEach(function(b){b.classList.remove("on")}); - this.classList.add("on"); - }})); + +function buildVlanEdit() { + var tb = $("vedit").tBodies[0]; + if (tb.rows.length || !S.n) return; + var hd = tb.insertRow(); + hd.insertCell().className = "mut"; + var rM = tb.insertRow(); + rM.insertCell().textContent = t("v_member"); + var rP = tb.insertRow(); + rP.insertCell().textContent = t("v_pvid"); + for (var p = 1; p <= S.n; p++)(function(p) { + hd.insertCell().innerHTML = '' + p + ''; + var seg = h("span", { + class: "seg", + id: "vm" + p + }); + ["-", "U", "T"].forEach(function(s, ix) { + seg.appendChild(h("button", { + text: s, + "data-v": ix, + onclick: function() { + seg.querySelectorAll("button").forEach(function(b) { + b.classList.remove("on") + }); + this.classList.add("on"); + } + })); }); seg.children[0].classList.add("on"); rM.insertCell().appendChild(seg); - rP.insertCell().appendChild(h("input",{type:"checkbox",id:"vp"+p})); + rP.insertCell().appendChild(h("span", { + id: "vp" + p + })); })(p); + //ROBTEST TODO + /* var it=$("ingress").tBodies[0]; var ih=it.insertRow();ih.insertCell().className="mut"; var ir=it.insertRow();ir.insertCell().textContent=t("v_accept"); @@ -1528,67 +1572,200 @@ function buildVlanEdit(){ sel.appendChild(h("option",{value:o[0],text:o[1]})); }); ir.insertCell().appendChild(sel); - })(q); + })(q);*/ } -function segVal(p){ - return Number($("vm"+p).querySelector("button.on").getAttribute("data-v")); + +function segVal(p) { + return Number($("vm" + p).querySelector("button.on").getAttribute("data-v")); } -function segSet(p,v){ - $("vm"+p).querySelectorAll("button").forEach(function(b,i){b.classList.toggle("on",i===v)}); + +function segSet(p, v) { + $("vm" + p).querySelectorAll("button").forEach(function(b, i) { + b.classList.toggle("on", i === v) + }); } -function vlanLoad(){ - var vid=parseInt($("vvid").value,10); - if(!vid||vid<1||vid>4094){toast(t("v_vid_err"),"err");return;} - getJSON("/vlan.json?vid="+vid).then(function(d){ - $("vname").value=d.name||""; - var m=parseInt(d.members,16),mem=m&0x3ff,unt=((m>>10)&0x3ff)&mem; - var pv=parseInt(d.pvid,16)&0x3ff; - for(var p=1;p<=S.n;p++){ - var bit=S.physToLog[p-1]; - segSet(p,(mem>>bit)&1?(((unt>>bit)&1)?1:2):0); - $("vp"+p).checked=!!((pv>>bit)&1); + +function vlanLoad(vid) { + if (!vid || vid < 1 || vid > 4094) { + toast(t("v_vid_err"), "err"); + return; + } + getJSON("/vlan.json?vid=" + vid).then(function(d) { + $("vtitle").innerText = "VLAN " + vid + (d.name ? " (" + (d.name) + ")" : ""); + $("vtitle").dataset.id = vid; + $("vtitle").dataset.name = d.name; + var m = parseInt(d.members, 16), + mem = m & 0x3ff, + unt = ((m >> 10) & 0x3ff) & mem; + var pv = parseInt(d.pvid, 16) & 0x3ff; + for (var p = 1; p <= S.n; p++) { + var bit = S.physToLog[p - 1]; + segSet(p, (mem >> bit) & 1 ? (((unt >> bit) & 1) ? 1 : 2) : 0); + $("vp" + p).innerText = !!((pv >> bit) & 1); } - toast(t("v_loaded",{n:vid}),"ok"); - }).catch(function(){toast(t("v_notfound",{n:vid}),"err")}); + toast(t("v_loaded", { + n: vid + }), "ok"); + }).catch(function() { + toast(t("v_notfound", { + n: vid + }), "err") + }); } -function vlanApply(){ - var vid=parseInt($("vvid").value,10); - if(!vid||vid<1||vid>4094){toast(t("v_vid_err"),"err");return;} - var name=$("vname").value.trim(); - if(name&&!/^[a-zA-Z]\w*$/.test(name)){toast(t("v_name_err"),"err");return;} - var cmd="vlan "+vid+(name?" "+name:""),members=0; - for(var p=1;p<=S.n;p++){ - var v=segVal(p); - if(v===1){cmd+=" "+p;members++;} - else if(v===2){cmd+=" "+p+"t";members++;} + +function vlan2str(i, n, t, u) { + return ("vlan " + i + (n ? " " + n : "") + " " + t.map((x)=>x+"t").concat(u).join(" ")).trim(); +} +function pvidcmd(i, p){ + return "pvid " + p + " " + i; +} +function vlanApply() { + var vid = $("vtitle").dataset.id; + var vname = $("vtitle").dataset.name; + if (!vid || vid < 1 || vid > 4094) { + toast(t("v_vid_err"), "err"); + return; } - if(!members){toast(t("v_nomember"),"err");return;} - var cmds=[cmd]; - for(var q=1;q<=S.n;q++)if($("vp"+q).checked)cmds.push("pvid "+q+" "+vid); - postCmds(cmds).then(vlanRefresh).catch(function(){}); -} -function ingressApply(){ - var cmd="ingress",any=false; - for(var p=1;p<=S.n;p++){ - var v=$("ing"+p).value; - if(v){cmd+=" "+p+v;any=true;} + var top_cmds = [], + tail_cmds = [], + thisVlanInStructure={id:vid, tagged:[], untagged:[]}; + + for (var p = 1; p <= S.n; p++) { + var v = segVal(p); + if (v === 1) { + thisVlanInStructure.untagged.push(p); + } else if (v === 2) { + thisVlanInStructure.tagged.push(p); + } } - if(!any){toast(t("v_ing_none"),"err");return;} - postCmd(cmd).catch(function(){}); + if (thisVlanInStructure.tagged.length + thisVlanInStructure.untagged.length == 0) { + toast(t("v_nomember"), "err"); + return; + } + function mySplit(a){ + if(a=="") return []; + return a.split(","); + } + for(const vlanRow of $("vtable").tBodies[0].rows){ + var rowData = vlanRow.firstChild.firstChild.dataset, + rowUntagged = mySplit(rowData.u), + rowTagged = mySplit(rowData.t); + if(rowData.id == vid) continue; + for(const newVlanPort of thisVlanInStructure.untagged) { + if(rowUntagged.includes(""+newVlanPort)){ + rowUntagged.splice(rowUntagged.indexOf(""+newVlanPort),1); + } + } + top_cmds.push(vlan2str(rowData.id,rowData.name,rowTagged,rowUntagged)); + } + tail_cmds.push(vlan2str(vid, vname, thisVlanInStructure.tagged, thisVlanInStructure.untagged)); + //TODO: if the PVID is equal to this vid, we need to set it something else (probably 1) + + for(const up of thisVlanInStructure.untagged){ + tail_cmds.push(pvidcmd(vid,up)); + } + + var cmds = top_cmds.concat(tail_cmds); + console.log(cmds); + + postCmds(cmds).then(vlanRefresh).catch(function() {}).then(function() { + vlanLoad(vid) + }); } -$("vload").addEventListener("click",vlanLoad); -$("vapply").addEventListener("click",vlanApply); -$("ingapply").addEventListener("click",ingressApply); + +function ingressApply() { + var cmd = "ingress", + any = false; + for (var p = 1; p <= S.n; p++) { + var v = $("ing" + p).value; + if (v) { + cmd += " " + p + v; + any = true; + } + } + if (!any) { + toast(t("v_ing_none"), "err"); + return; + } + postCmd(cmd).catch(function() {}); +} +$("vapply").addEventListener("click", vlanApply); +$("vlan_add").addEventListener("click", function() { + var body = h("div"); + body.appendChild(document.createTextNode("ROBTODO: styling,layout etc")); + body.appendChild(h("p", null, [ + h("label", null, [document.createTextNode(t("v_vid"))]), + h("input", { + id: "nvlid", + class: "in sm", + type: "number", + min: "1", + max: "4094" + }) + ])); + body.appendChild(h("p", null, [ + h("label", null, [document.createTextNode(t("c_name"))]), + h("input", { + id: "nvlname", + class: "in", + size: "12", + maxlength: "15", + "data-i18n-p": "v_opt ional" + }) + ])); + modal("Add VLAN", body, [h("button", { + class: "ctl pri", + text: t("ROBTODO"), + onclick: function() { + addVlan() + } + })]); +}); + +function addVlan() { + var vid = parseInt($("nvlid").value, 10); + for (const r of $("vtable").tBodies[0].rows) { + if (r.firstChild.firstChild.dataset.id == vid) { + toast(t("ROBTODO"), "err"); + return; + } + } + var name = $("nvlname").value; + if (name && !/^[a-zA-Z]\w*$/.test(name)) { + toast(t("v_name_err"), "err"); + return; + } + var cmd = "vlan " + vid + (name ? " " + name : ""); + postCmd(cmd).then(function() { + closeModal(); + vlanRefresh() + }).catch(function() {}); +} +//ROBTODO +/* +//$("ingapply").addEventListener("click",ingressApply); $("vmgmt").addEventListener("click",function(){ var vid=parseInt($("vvid").value,10); if(!vid){toast(t("v_vid_first"),"err");return;} confirmModal(t("v_mgmt_q",{n:vid}),t("v_mgmt_d"), function(){postCmd("vlan "+vid+" mgmt").then(vlanRefresh).catch(function(){})}); }); -tabHooks.vlan={ - enter:function(){needPorts(function(){buildVlanEdit();vlanRefresh().catch(function(){})})}, +*/ +tabHooks.vlan = { + enter: function() { + needPorts(function() { + buildVlanEdit(); + vlanRefresh().catch(function() {}).then(function() { + VlanInit(); + }) + }) + }, }; +function VlanInit() { + $("vtable").tBodies[0].firstChild.firstChild.firstChild.click(); +}; +//ROBSTOP var l2Rows=[],l2SortCol="pport",l2SortDir=1,l2Gen=0; function l2Load(){ var seen={},all=[],idx=0,guard=0,gen=++l2Gen; diff --git a/html/index.html b/html/index.html index f042c0a8..74a2426e 100644 --- a/html/index.html +++ b/html/index.html @@ -291,22 +291,26 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent} VIDNameMembersTaggedUntaggedPVID on + -

VLAN editor

-
+

VLAN editor

+
+

U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.

+
diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index a9452c90..64654aac 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -19,7 +19,9 @@ #define NSFP 1 #define N_COUNTERS 52 -#define VLAN_MAX 1 +#define VLAN_MAX_ID 4094 + +int currentVLANCount = 1; #if PORTS == 9 const uint8_t physToLogPort[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; @@ -27,6 +29,14 @@ const uint8_t physToLogPort[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; const uint8_t physToLogPort[] = { 4, 5, 6, 7, 3, 8}; #endif +struct mem_vlan { + bool active; + char name[20]; + bool untagged[PORTS]; + bool tagged[PORTS]; +}; +struct mem_vlan mem_vlans[VLAN_MAX_ID+1]; +//mem_vlans[1] = {.active=true, .name="", .untagged={}, .tagged={}}; struct l2_entry { char mac[18]; char vlan[4]; @@ -135,13 +145,20 @@ void send_vlan(int s, int vlan) "Content-Type: application/json; charset=UTF-8\r\n\r\n"; v = json_object_new_object(); + if(vlan == 1) { + sprintf(num_buff, "0x%08x", 0x207ffff); + json_object_object_add(v, "members", json_object_new_string(num_buff)); + + json_object_object_add(v, "name", json_object_new_string("native")); + json_object_object_add(v, "pvid", json_object_new_string("0x01ff")); + } else { + if(mem_vlans[vlan].active) { + json_object_object_add(v, "name", json_object_new_string(mem_vlans[vlan].name)); + json_object_object_add(v, "members", json_object_new_string("0x0000")); + json_object_object_add(v, "pvid", json_object_new_string("0x0000")); + } + } - sprintf(num_buff, "0x%08x", 0x207ffff); - json_object_object_add(v, "members", json_object_new_string(num_buff)); - - json_object_object_add(v, "name", json_object_new_string("")); - json_object_object_add(v, "pvid", json_object_new_string("0x01ff")); - write(s, header, strlen(header)); jstring = json_object_to_json_string_ext(v, JSON_C_TO_STRING_PLAIN); @@ -160,17 +177,18 @@ void send_vlanlist(int s) json_object_object_add(v, "mgmt", json_object_new_int(1)); - vlans = json_object_new_array_ext(VLAN_MAX); + vlans = json_object_new_array_ext(currentVLANCount); //foreach VLAN - for (int x=1;x<=VLAN_MAX;++x) { + for (int x=1;x<=VLAN_MAX_ID;++x) { vlan = json_object_new_object(); - json_object_object_add(vlan, "id", json_object_new_int(x)); - json_object_object_add(vlan, "name", json_object_new_string("")); + if(x ==1 || mem_vlans[x].active) { + json_object_object_add(vlan, "id", json_object_new_int(x)); + json_object_object_add(vlan, "name", json_object_new_string(mem_vlans[x].name)); - json_object_array_add(vlans, vlan); + json_object_array_add(vlans, vlan); + } } - json_object_object_add(v, "vlan", vlans); @@ -680,6 +698,65 @@ void print_cmd_history(void) printf("%d: %s\n", i, cmd_history[i]); } +void delete_vlan(int vid) { + mem_vlans[vid] = (struct mem_vlan){}; + currentVLANCount--; +} + +void parse_cmd(char* cmd) +{ + char *token = strtok(cmd, " "); + char *name; + printf("\t OK FIRST TOKEN: '%s'\n",token); + if (!strncmp(token, "vlan", 4)) { + printf("\t OK IT IS VLAN\n"); + token = strtok(NULL, " "); + int vid = atoi(token); + if (vid<1 || vid > VLAN_MAX_ID) { + printf("\t REJECT BECAUSE OF VID:%d\n",vid); + return; + } + + token = strtok(NULL, " "); + if(!strncmp(token,"mgmt",4)) { + printf("\t MGMT LEAVE:\n"); + return; //TODO + } else if (!strncmp(token,"d",1)) { + printf("\t DELETE VID %d:\n",vid); + delete_vlan(vid); + } else { + if (token[0] >= 'A' && token[0] <= 'z' ){ + name = token; + token = strtok(NULL, " "); + + printf("\tIS NAME %s:\n", name); + } + + struct mem_vlan newvlan = {.active=true}; + strcpy(newvlan.name, name); + + // a string of [0-9][t]? + printf("\t TOKEN: %s:\n", token); + while(token != NULL) { + int portNum = atoi(token); + bool isTagged = token[strlen(token)] == 't'; + printf("\tVLAN: %d Name: %s Port: %d tagged:%d\n", vid, name, portNum, isTagged); + token = strtok(NULL, " "); + if(isTagged) { + newvlan.tagged[portNum] = true; + } else { + newvlan.untagged[portNum] = true; + } + } + + mem_vlans[vid] = newvlan; + currentVLANCount++; + } + } else { + + } +} + void launch(struct Server *server) { char buffer[BUFFER_SIZE]; @@ -872,6 +949,8 @@ void launch(struct Server *server) printf("POST cmd\n"); printf("CMD: %s\n", p + 4); strcpy(cmd_history[cmd_ptr], p + 4); + printf("\t GOING TO PARSE THIS: %s\n", p+4); + parse_cmd(p+4); cmd_ptr++; print_cmd_history(); char *response = "HTTP/1.1 200 OK\r\n"