Skip to content

Commit 582bd66

Browse files
committed
Topology, SNMP/LLDP mapping
1 parent 4e09fee commit 582bd66

File tree

4 files changed

+194
-43
lines changed

4 files changed

+194
-43
lines changed

Protest/Front/deviceview.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3742,10 +3742,10 @@ class DeviceView extends View {
37423742
}
37433743

37443744
GetVlanColor(vlan) {
3745-
if (!vlan || vlan === "") return null;
3745+
if (!vlan || vlan == "") return null;
37463746

37473747
for (let i=0; i<KEEP.zones.length; i++) {
3748-
if (KEEP.zones[i].vlan === vlan) {
3748+
if (KEEP.zones[i].vlan == vlan) {
37493749
return KEEP.zones[i].color;
37503750
}
37513751
}

Protest/Front/topology.css

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,58 @@
2020
overflow-y: auto;
2121
}
2222

23+
.topology-sidebar-grid {
24+
display: grid;
25+
grid-template-columns: 88px auto;
26+
grid-template-rows: 16px 16px 16px 16px;
27+
margin-bottom: 16px;
28+
-webkit-user-select: text; user-select: text;
29+
}
30+
2331
.topology-interface-list {
2432
margin-top: 16px;
2533
}
2634

27-
.topology-interface-list > div {
28-
margin: 2px;
29-
padding: 4px;
30-
border-radius: 2px;
35+
.topology-interface-list > div > div {
36+
display: inline-block;
37+
box-sizing: border-box;
38+
background-color: var(--clr-control);
39+
width: calc(50% - 8px);
40+
min-height: 24px;
41+
padding: 2px 4px;
42+
margin: 2px 0;
43+
border-radius: 4px;
44+
text-align: center;
45+
overflow: hidden;
46+
text-overflow: ellipsis;
47+
white-space: nowrap;
48+
-webkit-user-select: text; user-select: text;
3149
}
3250

33-
.topology-interface-list > div:hover {
34-
background-color: var(--clr-highlight);
51+
.topology-interface-list > div > div:nth-child(1) {
52+
margin-right: 8px;
3553
}
3654

37-
.topology-sidebar-grid {
38-
display: grid;
55+
.topology-interface-list > div > div:nth-child(2) {
56+
margin-left: 8px;
57+
}
3958

40-
grid-template-columns: 88px auto;
41-
grid-template-rows: 12px 20px 20px 12px;
42-
-webkit-user-select: text; user-select: text;
59+
.topology-interface-list > div > div:nth-child(2)::before {
60+
content: "";
61+
position: absolute;
62+
background-color: var(--clr-control);
63+
width: 16px;
64+
height: 4px;
65+
left: calc(50% - 8px);
66+
transform: translateY(8px);
67+
}
68+
69+
.topology-interface-list > div:hover > div,
70+
.topology-interface-list > div:hover > div:nth-child(2)::before {
71+
background-color: var(--clr-select);
4372
}
4473

45-
.topology-selected > rect:first-child {
74+
.topology-selected {
4675
fill: color-mix(in srgb, var(--clr-select) 60%, transparent 40%);
4776
stroke: var(--clr-select);
4877
stroke-width: 2px;

Protest/Front/topology.js

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ class Topology extends Window {
1717
this.ws = null;
1818

1919
this.devices = {};
20-
this.links = [];
2120

2221
this.AddCssDependencies("topology.css");
2322

@@ -51,7 +50,7 @@ class Topology extends Window {
5150
this.sideBar.textContent = "";
5251

5352
if (this.selected) {
54-
this.selected.element.root.classList.remove("topology-selected");
53+
this.selected.element.rect.classList.remove("topology-selected");
5554
this.selected = null;
5655
}
5756
};
@@ -335,19 +334,20 @@ class Topology extends Window {
335334
device.element.icon.classList.add("topology-loading");
336335
}
337336
}
338-
else if (json.nosnmp) {
339-
const device = this.devices[json.nosnmp];
337+
else if (json.nolldp) {
338+
const device = this.devices[json.nolldp];
340339
if (device) {
341340
device.element.icon.classList.remove("topology-loading");
342341
setTimeout(()=>{
343342
device.element.icon.style.fill = "var(--clr-error)";
344343
}, 10);
345344
}
346345
}
347-
else if (json.snmp) {
348-
const device = this.devices[json.snmp.file];
346+
else if (json.lldp) {
347+
const device = this.devices[json.lldp.file];
349348
if (device) {
350-
device.snmp = json.snmp;
349+
device.lldp = json.lldp;
350+
this.ComputeLinks(device);
351351

352352
device.element.icon.classList.remove("topology-loading");
353353
setTimeout(()=>{
@@ -428,12 +428,20 @@ class Topology extends Window {
428428
};
429429
}
430430

431+
ComputeLinks(device) {
432+
device.links = {};
433+
434+
for (let i=0; i<ldap.localPortName.length; i++) {
435+
console.log(ldap.localPortName[i]);
436+
}
437+
}
438+
431439
SelectDevice(file) {
432440
if (this.selected) {
433-
this.selected.element.root.classList.remove("topology-selected");
441+
this.selected.element.rect.classList.remove("topology-selected");
434442
}
435443

436-
this.devices[file].element.root.classList.add("topology-selected");
444+
this.devices[file].element.rect.classList.add("topology-selected");
437445

438446
this.selected = this.devices[file];
439447
this.dragging = this.devices[file];
@@ -465,15 +473,57 @@ class Topology extends Window {
465473
ipLabel.textContent = initial.ip;
466474
grid.appendChild(ipLabel);
467475

468-
if (file in this.devices && this.devices[file].snmp) {
476+
if (file in this.devices && this.devices[file].lldp) {
477+
const id = this.devices[file].lldp.localChassisId;
478+
const idLabel = document.createElement("div");
479+
if (id && initial.hostname != id && initial.ip != id) {
480+
idLabel.style.gridArea = "4 / 2";
481+
idLabel.textContent = id;
482+
grid.appendChild(idLabel);
483+
}
484+
485+
const systemName = this.devices[file].lldp.localHostname;
486+
if (systemName) {
487+
hostnameLabel.textContent = systemName;
488+
hostnameLabel.style.gridArea = "1 / 2";
489+
ipLabel.style.gridArea = "2 / 2";
490+
idLabel.style.gridArea = "3 / 2";
491+
}
492+
469493
const intList = document.createElement("div");
470494
intList.className = "topology-interface-list";
471495
this.sideBar.appendChild(intList);
472496

473-
for (let i=0; i<this.devices[file].snmp.localPortName.length; i++) {
497+
for (let i=0; i<this.devices[file].lldp.localPortName.length; i++) {
474498
const interfaceBox = document.createElement("div");
475-
interfaceBox.textContent = this.devices[file].snmp.localPortName[i];
499+
const localPort = document.createElement("div");
500+
const remotePort = document.createElement("div");
501+
502+
let localPortName = this.devices[file].lldp.localPortName[i];
503+
if (!localPortName || localPortName.length === 0) {
504+
localPortName = `(${i+1})`;
505+
localPort.style.color = "#404040";
506+
localPort.style.fontStyle = "italic";
507+
}
508+
509+
let remotePortName = "";
510+
if (i in this.devices[file].lldp.remoteSystemName) {
511+
const remoteName = this.devices[file].lldp.remoteSystemName[i];
512+
if (remoteName.length === 1) {
513+
remotePortName = remoteName[0];
514+
}
515+
else {
516+
remotePortName = "[...]";
517+
}
518+
}
519+
476520
intList.appendChild(interfaceBox);
521+
522+
localPort.textContent = localPortName;
523+
interfaceBox.appendChild(localPort);
524+
525+
remotePort.textContent = remotePortName;
526+
interfaceBox.appendChild(remotePort);
477527
}
478528
}
479529
}

Protest/Tools/Topology.cs

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ namespace Protest.Tools;
1313

1414
internal static class Topology {
1515

16+
private static bool Push(this Dictionary<int, List<string>> dic, int key, string value) {
17+
if (dic.TryGetValue(key, out List<string> list)) {
18+
list.Add(value);
19+
}
20+
else {
21+
dic.Add(key, new List<string> { value });
22+
}
23+
24+
return true;
25+
}
26+
1627
private static async Task WsWriteText(WebSocket ws, string data) {
1728
if (ws.State == WebSocketState.Open) {
1829
await ws.SendAsync(new ArraySegment<byte>(Encoding.ASCII.GetBytes(data), 0, data.Length), WebSocketMessageType.Text, true, CancellationToken.None);
@@ -96,7 +107,7 @@ internal static async void WebSocketHandler(HttpListenerContext ctx) {
96107
//Dictionary<string, string> remote = Polling.ParseResponse(rawRemote, true);
97108

98109
if (rawLocal is null || rawLocal.Count == 0 || rawRemote is null || rawRemote.Count == 0) {
99-
await WsWriteText(ws, System.Text.Encoding.UTF8.GetBytes($"{{\"nosnmp\":\"{candidates[i].filename}\"}}"));
110+
await WsWriteText(ws, System.Text.Encoding.UTF8.GetBytes($"{{\"nolldp\":\"{candidates[i].filename}\"}}"));
100111
}
101112
else {
102113
byte[] response = ComputeSnmpResponse(candidates[i].filename, rawLocal, rawRemote);
@@ -133,17 +144,17 @@ private static byte[] ComputeSnmpResponse(string file, IList<Variable> rawLocal,
133144
local.TryGetValue("1.0.8802.1.1.2.1.3.1.0", out Variable localChassisIdSuptype);
134145
local.TryGetValue("1.0.8802.1.1.2.1.3.2.0", out Variable localChassisId);
135146
local.TryGetValue("1.0.8802.1.1.2.1.3.3.0", out Variable localHostname);
136-
local.TryGetValue("1.0.8802.1.1.2.1.3.4.0", out Variable localDescription);
147+
//local.TryGetValue("1.0.8802.1.1.2.1.3.4.0", out Variable localDescription);
137148

138149
List<(int, string)> localPortIdSuptype = new List<(int, string)>();
139150
List<(int, string)> localPortId = new List<(int, string)>();
140151
List<(int, string)> localPortName = new List<(int, string)>();
141152

142-
List<(int, string)> remoteChassisIdSuptype = new List<(int, string)>();
143-
List<(int, string)> remoteChassisId = new List<(int, string)>();
144-
List<(int, string)> remotePortIdSuptype = new List<(int, string)>();
145-
List<(int, string)> remotePortId = new List<(int, string)>();
146-
List<(int, string)> remoteSystemName = new List<(int, string)>();
153+
Dictionary<int, List<string>> remoteChassisIdSuptype = new Dictionary<int, List<string>>();
154+
Dictionary<int, List<string>> remoteChassisId = new Dictionary<int, List<string>>();
155+
Dictionary<int, List<string>> remotePortIdSuptype = new Dictionary<int, List<string>>();
156+
Dictionary<int, List<string>> remotePortId = new Dictionary<int, List<string>>();
157+
Dictionary<int, List<string>> remoteSystemName = new Dictionary<int, List<string>>();
147158

148159
foreach (KeyValuePair<string, Variable> pair in local) {
149160
if (!int.TryParse(pair.Key.Split('.')[^1], out int index)) continue;
@@ -152,7 +163,13 @@ private static byte[] ComputeSnmpResponse(string file, IList<Variable> rawLocal,
152163
localPortIdSuptype.Add((index, pair.Value.Data.ToString()));
153164
}
154165
else if (pair.Key.StartsWith("1.0.8802.1.1.2.1.3.7.1.3")) {
155-
localPortId.Add((index, pair.Value.Data.ToString()));
166+
string typeString = pair.Key.Replace("1.0.8802.1.1.2.1.3.7.1.3", "1.0.8802.1.1.2.1.3.7.1.2");
167+
if (local.TryGetValue(typeString, out Variable typeValue)) {
168+
localPortId.Add((index, GetPortId(typeValue.Data.ToString(), pair.Value.Data)));
169+
}
170+
else {
171+
localPortId.Add((index, pair.Value.Data.ToString()));
172+
}
156173
}
157174
else if (pair.Key.StartsWith("1.0.8802.1.1.2.1.3.7.1.4")) {
158175
localPortName.Add((index, pair.Value.Data.ToString()));
@@ -163,29 +180,51 @@ private static byte[] ComputeSnmpResponse(string file, IList<Variable> rawLocal,
163180
if (!int.TryParse(pair.Key.Split('.')[^2], out int index)) continue;
164181

165182
if (pair.Key.StartsWith("1.0.8802.1.1.2.1.4.1.1.4")) {
166-
remoteChassisIdSuptype.Add((index, pair.Value.Data.ToString()));
183+
remoteChassisIdSuptype.Push(index - 1, pair.Value.Data.ToString());
167184
}
168185
else if (pair.Key.StartsWith("1.0.8802.1.1.2.1.4.1.1.5")) {
169-
remoteChassisId.Add((index, pair.Value.Data.ToString()));
186+
string typeString = pair.Key.Replace("1.0.8802.1.1.2.1.4.1.1.5", "1.0.8802.1.1.2.1.4.1.1.4");
187+
if (remote.TryGetValue(typeString, out Variable typeValue)) {
188+
remoteChassisId.Push(index - 1, GetChassisId(typeValue.Data.ToString(), pair.Value.Data));
189+
}
190+
else {
191+
remoteChassisId.Push(index - 1, pair.Value.Data.ToString());
192+
}
170193
}
171194
if (pair.Key.StartsWith("1.0.8802.1.1.2.1.4.1.1.6")) {
172-
remotePortIdSuptype.Add((index, pair.Value.Data.ToString()));
195+
remotePortIdSuptype.Push(index - 1, pair.Value.Data.ToString());
173196
}
174197
else if (pair.Key.StartsWith("1.0.8802.1.1.2.1.4.1.1.7")) {
175-
remotePortId.Add((index, pair.Value.Data.ToString()));
198+
string typeString = pair.Key.Replace("1.0.8802.1.1.2.1.4.1.1.7", "1.0.8802.1.1.2.1.4.1.1.6");
199+
if (remote.TryGetValue(typeString, out Variable typeValue)) {
200+
remotePortId.Push(index - 1, GetPortId(typeValue.Data.ToString(), pair.Value.Data));
201+
}
202+
else {
203+
remotePortId.Push(index - 1, pair.Value.Data.ToString());
204+
}
176205
}
177206
else if (pair.Key.StartsWith("1.0.8802.1.1.2.1.4.1.1.9")) {
178-
remoteSystemName.Add((index, pair.Value.Data.ToString()));
207+
remoteSystemName.Push(index - 1, pair.Value.Data.ToString());
179208
}
180209
}
181210

182211
byte[] payload = JsonSerializer.SerializeToUtf8Bytes(new {
183-
snmp = new {
184-
file = file,
212+
lldp = new {
213+
file = file,
214+
185215
localChassisId = GetChassisId(localChassisIdSuptype.Data.ToString(), localChassisId.Data),
216+
localHostname = localHostname.Data.ToString(),
217+
//localDescription = localDescription.Data.ToString(),
218+
186219
localPortIdSuptype = localPortIdSuptype.Select(o=>o.Item2),
187220
localPortId = localPortId.Select(o=>o.Item2),
188-
localPortName = localPortName.Select(o=>o.Item2)
221+
localPortName = localPortName.Select(o=>o.Item2),
222+
223+
remoteChassisIdSuptype = remoteChassisIdSuptype,
224+
remoteChassisId = remoteChassisId,
225+
remotePortIdSuptype = remotePortIdSuptype,
226+
remotePortId = remotePortId,
227+
remoteSystemName = remoteSystemName,
189228
}
190229
});
191230

@@ -198,7 +237,7 @@ private static string GetChassisId(string subtype, ISnmpData value) {
198237
switch (subtype) {
199238
case "4": //mac address
200239
if (bytes.Length - 2 == 6) {
201-
return $"{(bytes[2]).ToString("X2")}:{(bytes[3]).ToString("X2")}:{(bytes[4]).ToString("X2")}:{(bytes[5]).ToString("X2")}:{(bytes[6]).ToString("X2")}:{(bytes[7]).ToString("X2")}";
240+
return $"{(bytes[2]).ToString("x2")}{(bytes[3]).ToString("x2")}{(bytes[4]).ToString("x2")}{(bytes[5]).ToString("x2")}{(bytes[6]).ToString("x2")}{(bytes[7]).ToString("x2")}";
202241
}
203242
return value.ToString();
204243

@@ -224,4 +263,37 @@ private static string GetChassisId(string subtype, ISnmpData value) {
224263
}
225264
}
226265

266+
private static string GetPortId(string subtype, ISnmpData value) {
267+
byte[] bytes = value.ToBytes();
268+
269+
switch (subtype) {
270+
case "3": // mac address
271+
if (bytes.Length - 2 == 6) {
272+
return $"{(bytes[2]).ToString("x2")}{(bytes[3]).ToString("x2")}{(bytes[4]).ToString("x2")}{(bytes[5]).ToString("x2")}{(bytes[6]).ToString("x2")}{(bytes[7]).ToString("x2")}";
273+
}
274+
return value.ToString();
275+
276+
case "4": //network address
277+
if (bytes.Length - 2 == 4) {
278+
return $"{bytes[2]}.{bytes[3]}.{bytes[4]}.{bytes[5]}";
279+
}
280+
else if (bytes.Length - 2 == 16) {
281+
return $"""
282+
{bytes[2].ToString("x2")}){bytes[3].ToString("x2")}:
283+
{bytes[4].ToString("x2")}){bytes[5].ToString("x2")}:
284+
{bytes[6].ToString("x2")}){bytes[7].ToString("x2")}:
285+
{bytes[8].ToString("x2")}){bytes[9].ToString("x2")}:
286+
{bytes[10].ToString("x2")}){bytes[11].ToString("x2")}:
287+
{bytes[12].ToString("x2")}){bytes[13].ToString("x2")}:
288+
{bytes[14].ToString("x2")}){bytes[15].ToString("x2")}:
289+
{bytes[16].ToString("x2")}){bytes[17].ToString("x2")}
290+
""";
291+
}
292+
return value.ToString();
293+
294+
default:
295+
return value.ToString();
296+
}
297+
}
298+
227299
}

0 commit comments

Comments
 (0)