Skip to content

Commit 7362fed

Browse files
committed
Refactor SNMP interface fetching methods.
Now switche interfaces are also fetched when fetching a device.
1 parent 32e886f commit 7362fed

File tree

3 files changed

+221
-117
lines changed

3 files changed

+221
-117
lines changed

Protest/Http/Listener.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private static readonly Dictionary<string, Func<HttpListenerContext, Dictionary<
120120
["/snmp/get"] = (ctx, parameters, username) => Protocols.Snmp.Polling.GetHandler(ctx, parameters),
121121
["/snmp/set"] = (ctx, parameters, username) => Protocols.Snmp.Polling.SetHandler(ctx, parameters),
122122
["/snmp/walk"] = (ctx, parameters, username) => Protocols.Snmp.Polling.WalkHandler(ctx, parameters),
123-
["/snmp/switchinterface"] = (ctx, parameters, username) => Protocols.Snmp.Polling.SwitchInterface(parameters),
123+
["/snmp/switchinterface"] = (ctx, parameters, username) => Protocols.Snmp.Polling.GetInterfaces(parameters),
124124

125125
["/wmi/query"] = (ctx, parameters, username) => OperatingSystem.IsWindows() ? Protocols.Wmi.Query(ctx, parameters) : null,
126126
["/wmi/killprocess"] = (ctx, parameters, username) => OperatingSystem.IsWindows() ? Protocols.Wmi.WmiKillProcess(parameters) : null,

Protest/Protocols/Snmp.Polling.Switch.cs

Lines changed: 217 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
namespace Protest.Protocols.Snmp;
99

1010
internal static partial class Polling {
11-
public static byte[] SwitchInterface(Dictionary<string, string> parameters) {
11+
12+
public static byte[] GetInterfaces(Dictionary<string, string> parameters) {
1213
if (parameters is null) { return Data.CODE_INVALID_ARGUMENT.Array; }
1314

1415
parameters.TryGetValue("file", out string file);
@@ -36,135 +37,143 @@ public static byte[] SwitchInterface(Dictionary<string, string> parameters) {
3637
return Data.CODE_INVALID_ARGUMENT.Array;
3738
}
3839

39-
return SwitchInterface(_ipAddress, _snmpProfile);
40+
try {
41+
IList<Variable> list = Polling.SnmpQuery(_ipAddress, _snmpProfile, Oid.SWITCH_OID, Polling.SnmpOperation.Walk);
42+
Dictionary<string, string> parsed = Polling.ParseResponse(list);
43+
if (parsed is null) return "{\"error\":\"Failed to fetch interfaces\"}"u8.ToArray();
44+
45+
Dictionary<int, string>[] dictionaries = ComputeInterface(list, parsed);
46+
47+
return SwitchInterfaces_LiveStatFormat(
48+
dictionaries[0],
49+
dictionaries[1],
50+
dictionaries[2],
51+
dictionaries[3],
52+
dictionaries[4],
53+
dictionaries[5],
54+
dictionaries[6]
55+
);
56+
}
57+
catch (Exception ex) {
58+
return JsonSerializer.SerializeToUtf8Bytes(new { error = ex.Message });
59+
}
4060
}
4161

42-
public static byte[] SwitchInterface(IPAddress target, SnmpProfiles.Profile snmpProfile) {
62+
public static string FetchInterfaces(System.Net.IPAddress target, SnmpProfiles.Profile snmpProfile) {
4363
try {
44-
IList<Variable> snmpResult = Polling.SnmpQuery(target, snmpProfile, Oid.SWITCH_OID, Polling.SnmpOperation.Walk);
45-
Dictionary<string, string> interfaces = Polling.ParseResponse(snmpResult);
46-
47-
if (interfaces is null) return "{\"error\":\"Failed to fetch interfaces\"}"u8.ToArray();
48-
49-
Dictionary<int, string> descriptor = new Dictionary<int, string>();
50-
Dictionary<int, string> alias = new Dictionary<int, string>();
51-
Dictionary<int, string> type = new Dictionary<int, string>();
52-
Dictionary<int, string> speed = new Dictionary<int, string>();
53-
Dictionary<int, string> untagged = new Dictionary<int, string>();
54-
Dictionary<int, string> tagged = new Dictionary<int, string>();
55-
56-
Dictionary<short, List<int>> taggedMap = new Dictionary<short, List<int>>();
57-
for (int i = 0; i < snmpResult.Count; i++) {
58-
string oid = snmpResult[i].Id.ToString();
59-
if (!oid.StartsWith(Oid.INTERFACE_1Q_VLAN_ENGRESS)) continue;
60-
61-
int dotIndex = oid.LastIndexOf('.');
62-
if (dotIndex == -1) continue;
63-
if (!short.TryParse(oid[(dotIndex + 1)..], out short vlanId)) continue;
64-
65-
byte[] raw = snmpResult[i].Data.ToBytes();
66-
67-
int startIndex = GetPortBitmapStart(raw);
68-
if (startIndex == -1) continue;
69-
70-
int maxIndex = Math.Min(raw.Length, startIndex + GetPortBitmapLength(raw, startIndex));
71-
72-
for (int j = startIndex; j < maxIndex; j++) {
73-
byte b = raw[j];
74-
for (int k = 0; k < 8; k++) {
75-
if ((b & (1 << (7 - k))) == 0) continue;
76-
77-
int portIndex = 8 * (j - startIndex) + (k + 1);
78-
if (!taggedMap.TryGetValue(vlanId, out var ports)) {
79-
ports = new List<int>();
80-
taggedMap[vlanId] = ports;
81-
}
82-
if (!ports.Contains(portIndex)) {
83-
ports.Add(portIndex);
64+
IList<Variable> list = Polling.SnmpQuery(target, snmpProfile, Oid.SWITCH_OID, Polling.SnmpOperation.Walk);
65+
Dictionary<string, string> parsed = Polling.ParseResponse(list);
66+
if (parsed is null) return null;
67+
68+
Dictionary<int, string>[] dictionaries = ComputeInterface(list, parsed);
69+
70+
return SwitchInterfaces_FetchFormat(
71+
dictionaries[0],
72+
dictionaries[1],
73+
dictionaries[2],
74+
dictionaries[3],
75+
dictionaries[4],
76+
dictionaries[5],
77+
dictionaries[6]
78+
);
79+
}
80+
catch {
81+
return null;
82+
}
83+
}
84+
85+
86+
87+
private static Dictionary<int, string>[] ComputeInterface(IList<Variable> list, Dictionary<string, string> parsed) {
88+
Dictionary<int, string> descriptor = new Dictionary<int, string>();
89+
Dictionary<int, string> alias = new Dictionary<int, string>();
90+
Dictionary<int, string> type = new Dictionary<int, string>();
91+
Dictionary<int, string> speed = new Dictionary<int, string>();
92+
Dictionary<int, string> untagged = new Dictionary<int, string>();
93+
Dictionary<int, string> tagged = new Dictionary<int, string>();
94+
95+
Dictionary<short, List<int>> taggedMap = new Dictionary<short, List<int>>();
96+
for (int i = 0; i < list.Count; i++) {
97+
string oid = list[i].Id.ToString();
98+
if (!oid.StartsWith(Oid.INTERFACE_1Q_VLAN_ENGRESS)) continue;
99+
100+
int dotIndex = oid.LastIndexOf('.');
101+
if (dotIndex == -1) continue;
102+
if (!short.TryParse(oid[(dotIndex + 1)..], out short vlanId)) continue;
103+
104+
byte[] raw = list[i].Data.ToBytes();
105+
106+
int startIndex = GetPortBitmapStart(raw);
107+
if (startIndex == -1) continue;
108+
109+
int maxIndex = Math.Min(raw.Length, startIndex + GetPortBitmapLength(raw, startIndex));
110+
111+
for (int j = startIndex; j < maxIndex; j++) {
112+
byte b = raw[j];
113+
for (int k = 0; k < 8; k++) {
114+
if ((b & (1 << (7 - k))) == 0) continue;
115+
116+
int portIndex = 8 * (j - startIndex) + (k + 1);
117+
if (!taggedMap.TryGetValue(vlanId, out var ports)) {
118+
ports = new List<int>();
119+
taggedMap[vlanId] = ports;
84120
}
121+
if (!ports.Contains(portIndex)) {
122+
ports.Add(portIndex);
85123
}
86124
}
87125
}
88-
foreach (KeyValuePair<short, List<int>> pair in taggedMap) {
89-
foreach (int port in pair.Value) {
90-
tagged.TryGetValue(port, out var existing);
91-
tagged[port] = string.IsNullOrEmpty(existing) ? pair.Key.ToString() : $"{existing},{pair.Key.ToString()}";
92-
}
126+
}
127+
foreach (KeyValuePair<short, List<int>> pair in taggedMap) {
128+
foreach (int port in pair.Value) {
129+
tagged.TryGetValue(port, out var existing);
130+
tagged[port] = string.IsNullOrEmpty(existing) ? pair.Key.ToString() : $"{existing},{pair.Key.ToString()}";
93131
}
132+
}
94133

95-
Dictionary<int, string> macTable = new Dictionary<int, string>();
96-
foreach (KeyValuePair<string, string> pair in interfaces) {
97-
if (!pair.Key.StartsWith(Oid.INTERFACE_1D_TP_FDB)) continue;
98-
if (!int.TryParse(pair.Value, out int port)) continue;
99-
string mac = String.Join(String.Empty, pair.Key.Split('.').TakeLast(6).Select(o=>int.Parse(o).ToString("x2")));
134+
Dictionary<int, string> macTable = new Dictionary<int, string>();
135+
foreach (KeyValuePair<string, string> pair in parsed) {
136+
if (!pair.Key.StartsWith(Oid.INTERFACE_1D_TP_FDB)) continue;
137+
if (!int.TryParse(pair.Value, out int port)) continue;
138+
string mac = String.Join(String.Empty, pair.Key.Split('.').TakeLast(6).Select(o=>int.Parse(o).ToString("x2")));
100139

101-
if (macTable.ContainsKey(port)) {
102-
macTable[port] = null;
103-
}
104-
else {
105-
macTable.Add(port, mac);
106-
}
140+
if (macTable.ContainsKey(port)) {
141+
macTable[port] = null;
107142
}
143+
else {
144+
macTable.Add(port, mac);
145+
}
146+
}
108147

109-
foreach (KeyValuePair<string, string> pair in interfaces) {
110-
int index = int.Parse(pair.Key.Split('.')[^1]);
148+
foreach (KeyValuePair<string, string> pair in parsed) {
149+
int index = int.Parse(pair.Key.Split('.')[^1]);
111150

112-
if (pair.Key.StartsWith(Oid.INTERFACE_DESCRIPTOR)) {
113-
descriptor.Add(index, pair.Value);
114-
}
115-
else if (pair.Key.StartsWith(Oid.INTERFACE_ALIAS)) {
116-
alias.Add(index, pair.Value);
117-
}
118-
else if (pair.Key.StartsWith(Oid.INTERFACE_TYPE)) {
119-
type.Add(index, pair.Value);
120-
}
121-
else if (pair.Key.StartsWith(Oid.INTERFACE_SPEED)) {
122-
speed.Add(index, pair.Value);
123-
}
124-
else if (pair.Key.StartsWith(Oid.INTERFACE_1Q_VLAN)) {
125-
untagged.Add(index, pair.Value);
126-
}
151+
if (pair.Key.StartsWith(Oid.INTERFACE_DESCRIPTOR)) {
152+
descriptor.Add(index, pair.Value);
153+
}
154+
else if (pair.Key.StartsWith(Oid.INTERFACE_ALIAS)) {
155+
alias.Add(index, pair.Value);
156+
}
157+
else if (pair.Key.StartsWith(Oid.INTERFACE_TYPE)) {
158+
type.Add(index, pair.Value);
159+
}
160+
else if (pair.Key.StartsWith(Oid.INTERFACE_SPEED)) {
161+
speed.Add(index, pair.Value);
162+
}
163+
else if (pair.Key.StartsWith(Oid.INTERFACE_1Q_VLAN)) {
164+
untagged.Add(index, pair.Value);
127165
}
128-
129-
return JsonSerializer.SerializeToUtf8Bytes(
130-
type.Where(o => o.Value == "6")
131-
.Select(pair => new {
132-
number = descriptor.GetValueOrDefault(pair.Key, null),
133-
port = speed.GetValueOrDefault(pair.Key, "N/A") switch {
134-
"10000" => "SFP+",
135-
"25000" => "SFP+",
136-
"40000" => "QSFP",
137-
"100000" => "QSFP",
138-
"200000" => "QSFP",
139-
"400000" => "QSFP",
140-
"800000" => "QSFP",
141-
_ => "Ethernet"
142-
},
143-
speed = speed.GetValueOrDefault(pair.Key, "N/A") switch {
144-
"10" => "10 Mbps",
145-
"100" => "100 Mbps",
146-
"1000" => "1 Gbps",
147-
"2500" => "2.5 Gbps",
148-
"5000" => "5 Gbps",
149-
"10000" => "10 Gbps",
150-
"25000" => "25 Gbps",
151-
"40000" => "40 Gbps",
152-
"100000" => "100 Gbps",
153-
"200000" => "200 Gbps",
154-
"400000" => "400 Gbps",
155-
"800000" => "800 Gbps",
156-
_ => "N/A"
157-
},
158-
untagged = untagged.GetValueOrDefault(pair.Key, ""),
159-
tagged = tagged.GetValueOrDefault(pair.Key, ""),
160-
comment = alias.GetValueOrDefault(pair.Key, String.Empty),
161-
link = DatabaseInstances.FindDeviceByMac(macTable.GetValueOrDefault(pair.Key, null)),
162-
})
163-
);
164-
}
165-
catch (Exception ex) {
166-
return JsonSerializer.SerializeToUtf8Bytes(new { error = ex.Message });
167166
}
167+
168+
return new Dictionary<int, string>[] {
169+
type,
170+
descriptor,
171+
speed,
172+
untagged,
173+
tagged,
174+
alias,
175+
macTable
176+
};
168177
}
169178

170179
internal static int GetPortBitmapStart(byte[] raw) {
@@ -182,4 +191,97 @@ internal static int GetPortBitmapLength(byte[] raw, int startIndex) {
182191
return raw.Length - startIndex;
183192
}
184193

194+
private static byte[] SwitchInterfaces_LiveStatFormat(
195+
Dictionary<int, string> type,
196+
Dictionary<int, string> descriptor,
197+
Dictionary<int, string> speed,
198+
Dictionary<int, string> untagged,
199+
Dictionary<int, string> tagged,
200+
Dictionary<int, string> alias,
201+
Dictionary<int, string> macTable) {
202+
203+
return JsonSerializer.SerializeToUtf8Bytes(
204+
type.Where(o => o.Value == "6")
205+
.Select(pair => new {
206+
number = descriptor.GetValueOrDefault(pair.Key, null),
207+
port = speed.GetValueOrDefault(pair.Key, "N/A") switch {
208+
"10000" => "SFP+",
209+
"25000" => "SFP+",
210+
"40000" => "QSFP",
211+
"100000" => "QSFP",
212+
"200000" => "QSFP",
213+
"400000" => "QSFP",
214+
"800000" => "QSFP",
215+
_ => "Ethernet"
216+
},
217+
speed = speed.GetValueOrDefault(pair.Key, "N/A") switch {
218+
"10" => "10 Mbps",
219+
"100" => "100 Mbps",
220+
"1000" => "1 Gbps",
221+
"2500" => "2.5 Gbps",
222+
"5000" => "5 Gbps",
223+
"10000" => "10 Gbps",
224+
"25000" => "25 Gbps",
225+
"40000" => "40 Gbps",
226+
"100000" => "100 Gbps",
227+
"200000" => "200 Gbps",
228+
"400000" => "400 Gbps",
229+
"800000" => "800 Gbps",
230+
_ => "N/A"
231+
},
232+
untagged = untagged.GetValueOrDefault(pair.Key, ""),
233+
tagged = tagged.GetValueOrDefault(pair.Key, ""),
234+
comment = alias.GetValueOrDefault(pair.Key, String.Empty),
235+
link = DatabaseInstances.FindDeviceByMac(macTable.GetValueOrDefault(pair.Key, null)),
236+
})
237+
);
238+
}
239+
240+
private static string SwitchInterfaces_FetchFormat(
241+
Dictionary<int, string> type,
242+
Dictionary<int, string> descriptor,
243+
Dictionary<int, string> speed,
244+
Dictionary<int, string> untagged,
245+
Dictionary<int, string> tagged,
246+
Dictionary<int, string> alias,
247+
Dictionary<int, string> macTable) {
248+
249+
return JsonSerializer.Serialize(new {
250+
i = type.Where(o => o.Value == "6")
251+
.Select(pair => new {
252+
n = descriptor.GetValueOrDefault(pair.Key, null),
253+
i = speed.GetValueOrDefault(pair.Key, "N/A") switch {
254+
"10000" => "SFP+",
255+
"25000" => "SFP+",
256+
"40000" => "QSFP",
257+
"100000" => "QSFP",
258+
"200000" => "QSFP",
259+
"400000" => "QSFP",
260+
"800000" => "QSFP",
261+
_ => "Ethernet"
262+
},
263+
s = speed.GetValueOrDefault(pair.Key, "N/A") switch {
264+
"10" => "10 Mbps",
265+
"100" => "100 Mbps",
266+
"1000" => "1 Gbps",
267+
"2500" => "2.5 Gbps",
268+
"5000" => "5 Gbps",
269+
"10000" => "10 Gbps",
270+
"25000" => "25 Gbps",
271+
"40000" => "40 Gbps",
272+
"100000" => "100 Gbps",
273+
"200000" => "200 Gbps",
274+
"400000" => "400 Gbps",
275+
"800000" => "800 Gbps",
276+
_ => "N/A"
277+
},
278+
v = untagged.GetValueOrDefault(pair.Key, ""),
279+
t = tagged.GetValueOrDefault(pair.Key, ""),
280+
c = alias.GetValueOrDefault(pair.Key, String.Empty),
281+
l = DatabaseInstances.FindDeviceByMac(macTable.GetValueOrDefault(pair.Key, null)),
282+
}),
283+
n = "horizontal"
284+
});
285+
}
286+
185287
}

Protest/Tasks/Fetch.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,9 @@ public static ConcurrentDictionary<string, string[]> SingleDevice(string target,
436436
case "firewall":
437437
case "router":
438438
case "switch":
439-
byte[] s = Protocols.Snmp.Polling.SwitchInterface(ipList.First(), profile);
439+
string interfaces = Protocols.Snmp.Polling.FetchInterfaces(ipList[0], profile);
440+
if (interfaces is null) break;
441+
data.TryAdd(".interfaces", new string[] { Data.EscapeJsonText(interfaces), "SNMP", string.Empty });
440442
break;
441443
}
442444
}

0 commit comments

Comments
 (0)