NetSim Source Code Help v14.4
All 13 Components
 
Loading...
Searching...
No Matches
SpanningTree.c
1/************************************************************************************
2* Copyright (C) 2023 *
3* TETCOS, Bangalore. India *
4* *
5* Tetcos owns the intellectual property rights in the Product and its content. *
6* The copying, redistribution, reselling or publication of any or all of the *
7* Product or its content without express prior written consent of Tetcos is *
8* prohibited. Ownership and / or any other right relating to the software and all *
9* intellectual property rights therein shall remain at all times with Tetcos. *
10* *
11* Author: Shashi Kant Suman *
12* *
13* ---------------------------------------------------------------------------------*/
14
15#include "main.h"
16#include "Ethernet.h"
17#include "Ethernet_enum.h"
18
19static void send_config_bpdu(NETSIM_ID d, UINT lanId, double time);
20
21static char* eth_get_lowest_mac(ptrETH_LAN lan)
22{
23 UINT i;
24 PNETSIM_MACADDRESS ret = NULL;
25 for (i = 0; i < lan->interfaceCount; i++)
26 {
27 ptrETH_IF in = lan->ethIfVar[i];
28 if (!ret)
29 ret = in->macAddress;
30 else if (in->macAddress->nmacaddress < ret->nmacaddress)
31 ret = in->macAddress;
32 }
33 return ret->szmacaddress;
34}
35
36static void configure_switch_id(ptrETH_LAN lan)
37{
38 char id[20];
39 strcpy(id, eth_get_lowest_mac(lan));
40 lan->spt->switchId = calloc(20, sizeof(char));
41 sprintf(lan->spt->switchId, "%d%s", lan->spt->priority, id);
42}
43
44static void eth_spt_init_port(NETSIM_ID d, UINT lanId)
45{
46 ptrETH_VAR eth = GET_ETH_VAR(d);
47 ptrETH_LAN lan = eth->lanVar[lanId];
48
49 UINT i;
50 for (i = 0; i < lan->interfaceCount; i++)
51 {
52 NETSIM_ID c, ci;
53 NETSIM_ID ifi = lan->interfaceId[i];
54 NETSIM_ID l = fn_NetSim_Stack_GetConnectedDevice(d, ifi, &c, &ci);
55 if (!l)
56 lan->ethIfVar[i]->portState = PORTSTATE_BLOCKING; // No link to port
57
58 ptrETH_VAR e = GET_ETH_VAR(c);
59 if (!e || !e->isSPT)
60 {
61 print_ethernet_log("Port %d, Setting status as Forwarding", ifi);
62 lan->ethIfVar[i]->portState = PORTSTATE_FORWARDING;
63 }
64 else
65 {
66 ptrETH_LAN la = GET_ETH_LAN(c, ci, 0);
67 if (!la->STPStatus)
68 {
69 print_ethernet_log("Port %d, Setting status as Forwarding", ifi);
70 lan->ethIfVar[i]->portState = PORTSTATE_FORWARDING;
71 }
72 else
73 {
74 lan->ethIfVar[i]->portState = PORTSTATE_FORWARDING;
75 lan->ethIfVar[i]->portType = PORTTYPE_DESIGNATED;
76 }
77 }
78 }
79}
80
81static void start_spt_for_lan(NETSIM_ID d, UINT lanId)
82{
83 ptrETH_VAR eth = GET_ETH_VAR(d);
84 ptrETH_LAN lan = eth->lanVar[lanId];
85
86 print_ethernet_log("Stating SPT for LAN %d", lanId);
87 eth_spt_init_port(d, lanId);
88
89 if (!lan->spt->switchId)
90 configure_switch_id(lan);
91
92 print_ethernet_log("Starting as root");
93 lan->spt->rootId = _strdup(lan->spt->switchId); //Start as root
94 print_ethernet_log("Root Id = %s", lan->spt->rootId);
95
96 print_ethernet_log("Sending config BPDU");
97 send_config_bpdu(d, lanId, 0);
98}
99
100static ptrETH_BPDU create_bpdu(ptrETH_LAN lan)
101{
102 ptrETH_BPDU bpdu = calloc(1, sizeof* bpdu);
103 bpdu->rootId = lan->spt->rootId;
104 bpdu->rootPathCost = lan->spt->stpCost;
105 bpdu->bridgeId = lan->spt->switchId;
106 bpdu->maxAge = 20;
107 bpdu->msgAge = 1;
108 bpdu->helloTime = 2;
109 bpdu->forwardDelay = 15;
110
111 print_ethernet_log("Root Id = %s, Root path cost = %d, Bridge Id = %s",
112 bpdu->rootId,
113 bpdu->rootPathCost,
114 bpdu->bridgeId);
115 return bpdu;
116}
117
118static ptrETH_BPDU copy_bpdu(ptrETH_LAN lan, ptrETH_BPDU b)
119{
120 ptrETH_BPDU bpdu = calloc(1, sizeof* bpdu);
121 bpdu->rootId = b->rootId;
122 bpdu->rootPathCost = lan->spt->stpCost + b->rootPathCost;
123 bpdu->bridgeId = lan->spt->switchId;
124 bpdu->maxAge = 20;
125 bpdu->msgAge = 1;
126 bpdu->helloTime = 2;
127 bpdu->forwardDelay = 15;
128
129 print_ethernet_log("Root Id = %s, Root path cost = %d, Bridge Id = %s",
130 bpdu->rootId,
131 bpdu->rootPathCost,
132 bpdu->bridgeId);
133 return bpdu;
134}
135
136static NetSim_PACKET* create_bpdu_packet(NETSIM_ID d, ptrETH_LAN lan, double time)
137{
138 NetSim_PACKET* packet = fn_NetSim_Packet_CreatePacket(MAC_LAYER);
139 packet->nControlDataType = ETH_CONFIGBPDU;
140 packet->nPacketType = PacketType_Control;
141 packet->nSourceId = d;
142 packet->nTransmitterId = d;
143 packet->pstruMacData->dArrivalTime = time;
144 packet->pstruMacData->dStartTime = time;
145 packet->pstruMacData->dEndTime = time;
146 packet->pstruMacData->dOverhead = ETH_BPDU_LEN;
147 packet->pstruMacData->dPacketSize = ETH_BPDU_LEN;
148 packet->pstruMacData->nMACProtocol = MAC_PROTOCOL_IEEE802_3;
149 //packet->pstruMacData->Packet_MACProtocol = create_bpdu(lan);
150 set_eth_hdr(packet, ETH_HDR_CBPDU, create_bpdu(lan), NULL, NULL);
151 packet->pstruMacData->szDestMac = multicastSPTMAC;
152 strcpy(packet->szPacketType, "CONFIG_BPDU");
153 return packet;
154}
155
156static NetSim_PACKET* copy_bpdu_packet(NETSIM_ID d, ptrETH_LAN lan, double time, ptrETH_BPDU bpdu)
157{
158 NetSim_PACKET* packet = fn_NetSim_Packet_CreatePacket(MAC_LAYER);
159 packet->nControlDataType = ETH_CONFIGBPDU;
160 packet->nPacketType = PacketType_Control;
161 packet->nSourceId = d;
162 packet->nTransmitterId = d;
163 packet->pstruMacData->dArrivalTime = time;
164 packet->pstruMacData->dStartTime = time;
165 packet->pstruMacData->dEndTime = time;
166 packet->pstruMacData->dOverhead = ETH_BPDU_LEN;
167 packet->pstruMacData->dPacketSize = ETH_BPDU_LEN;
168 packet->pstruMacData->nMACProtocol = MAC_PROTOCOL_IEEE802_3;
169 //packet->pstruMacData->Packet_MACProtocol = copy_bpdu(lan, bpdu);
170 set_eth_hdr(packet, ETH_HDR_CBPDU, copy_bpdu(lan, bpdu), NULL, NULL);
171 packet->pstruMacData->szDestMac = multicastSPTMAC;
172 strcpy(packet->szPacketType, "CONFIG_BPDU");
173 return packet;
174}
175
176static void send_config_bpdu(NETSIM_ID d, UINT lanId, double time)
177{
178 bool flag = false;
179 ptrETH_VAR eth = GET_ETH_VAR(d);
180 ptrETH_LAN lan = eth->lanVar[lanId];
181
182 NetSim_PACKET* packet = create_bpdu_packet(d, lan, time);
183
184 UINT i;
185 for (i = 0; i < lan->interfaceCount; i++)
186 {
187 NETSIM_ID c, ci;
188 NETSIM_ID in = lan->interfaceId[i];
189 NETSIM_ID l = fn_NetSim_Stack_GetConnectedDevice(d, in, &c, &ci);
190 if (l)
191 {
192 ptrETH_VAR ce = GET_ETH_VAR(c);
193 if (ce)
194 {
195 ptrETH_LAN cl = GET_ETH_LAN(c, ci, 0);
196 if (cl && cl->STPStatus)
197 {
198 add_dest_to_packet(packet, c);
199 flag = true;
200 }
201
202 }
203 }
204 }
205 if (flag)
206 {
207 NetSim_EVENTDETAILS pevent;
208 memset(&pevent, 0, sizeof pevent);
209 pevent.dEventTime = time;
210 pevent.dPacketSize = packet->pstruMacData->dPacketSize;
211 pevent.nDeviceId = d;
212 pevent.nDeviceType = DEVICE_TYPE(d);
213 pevent.nEventType = MAC_OUT_EVENT;
214 pevent.nProtocolId = MAC_PROTOCOL_IEEE802_3;
215 pevent.pPacket = packet;
216 pevent.nSubEventType = SEND_CONFIG_BPDU;
217 fnpAddEvent(&pevent);
218 }
219 else
220 {
221 fn_NetSim_Packet_FreePacket(packet);
222 }
223}
224
225static void start_spanning_tree_protocol(NETSIM_ID d)
226{
227 ptrETH_VAR eth = GET_ETH_VAR(d);
228 if (!eth->isSPT)
229 return; //SPT is not configured
230
231 UINT i;
232
233 for (i = 0; i < eth->lanCount; i++)
234 {
235 if (!eth->lanVar[i]->STPStatus)
236 continue; //SPT is disable for this LAN
237 print_ethernet_log("Starting SPT protocol for device %d, LAN %d", d, i + 1);
238 start_spt_for_lan(d, i);
239 print_ethernet_log("");
240 }
241}
242
243bool isSingleInterface(NETSIM_ID d, ptrETH_LAN lan)
244{
245 if (lan->interfaceCount == 1)
246 return true;
247 UINT i;
248 UINT k = 0;
249 for (i = 0; i< lan->interfaceCount; i++)
250 {
251 NETSIM_ID in = lan->interfaceId[i];
252 NETSIM_ID c, ci;
253 NETSIM_ID l = fn_NetSim_Stack_GetConnectedDevice(d, in, &c, &ci);
254 if (l)
255 {
256 ptrETH_VAR eth = GET_ETH_VAR(c);
257 if (eth->isSPT)
258 k++;
259 if (k > 1)
260 return false;
261 }
262 }
263 return true;
264}
265
266void init_spanning_tree_protocol()
267{
268 NETSIM_ID i;
269 for (i = 0; i < NETWORK->nDeviceCount; i++)
270 {
271 ptrETH_VAR eth = GET_ETH_VAR(i + 1);
272 if (!eth)
273 continue;
274 UINT j;
275 for (j = 0; j < eth->lanCount; j++)
276 {
277 if (!eth->isSPT || (isSingleInterface(i+1, eth->lanVar[j]) &&
278 eth->lanVar[j]->lanIP))
279 {
280 eth->lanVar[j]->STPStatus = false;
281 eth->lanVar[j]->ethIfVar[0]->portState = PORTSTATE_FORWARDING;
282 print_ethernet_log("Device %d, Port %d: Setting port state as Forwarding\n",
283 i + 1, eth->lanVar[j]->ethIfVar[0]->interfaceId);
284 }
285 else
286 {
287 eth->lanVar[j]->STPStatus = true;
288 }
289 }
290 }
291 for (i = 0; i < NETWORK->nDeviceCount; i++)
292 {
293 ptrETH_VAR eth = GET_ETH_VAR(i + 1);
294 if (!eth)
295 continue;
296 if (eth->isSPT)
297 start_spanning_tree_protocol(i + 1);
298 }
299}
300
301int multicast_config_bpdu()
302{
303 NETSIM_ID d = pstruEventDetails->nDeviceId;
304
305 NetSim_PACKET* packet = pstruEventDetails->pPacket;
306 UINT c;
307 NETSIM_ID* dest = get_dest_from_packet(packet, &c);
308 UINT i;
309
310 for (i = 0; i < c; i++)
311 {
312 NETSIM_ID des = dest[i];
313 NetSim_PACKET* p;
314 if (i)
315 p = fn_NetSim_Packet_CopyPacket(packet);
316 else
317 p = packet;
318
319 NETSIM_ID in = get_interface_id(d, des);
320
321 NetSim_EVENTDETAILS pevent;
322 memcpy(&pevent, pstruEventDetails, sizeof pevent);
323 pevent.nEventType = PHYSICAL_OUT_EVENT;
324 pevent.nInterfaceId = in;
325 pevent.nSubEventType = 0;
326 pevent.pPacket = p;
327 fnpAddEvent(&pevent);
328 }
329 return 0;
330}
331
332typedef enum
333{
334 BLOCK,
335 FORWARD,
336 DONT_FORWARD,
337}COMPARE_BPDU;
338static COMPARE_BPDU compare_bpdu(ptrETH_BPDU bpdu, ptrETH_SPT spt, NETSIM_ID port)
339{
340 int cid = _stricmp(bpdu->rootId, spt->rootId);
341
342 if (cid == 0)
343 {
344 if (!_stricmp(spt->rootId, spt->switchId))
345 {
346 return DONT_FORWARD; // I am root
347 }
348 else
349 {
350 if (bpdu->rootPathCost == spt->rootPathCost)
351 {
352 if (spt->rootPort == port)
353 return DONT_FORWARD;
354 else
355 return BLOCK;
356 }
357 else if (bpdu->rootPathCost < spt->rootPathCost)
358 {
359 return FORWARD;
360 }
361 else
362 {
363 return BLOCK;
364 }
365 }
366 }
367 else if (cid < 0)
368 {
369 return FORWARD;
370 }
371 else
372 {
373 return DONT_FORWARD;
374 }
375}
376
377static void block_port(NETSIM_ID d, ptrETH_LAN lan, NETSIM_ID in)
378{
379 print_ethernet_log("Setting switch %d, port %d state as BLOCKED", d, in);
380 ptrETH_IF inter = GET_ETH_IF(lan, in);
381 inter->portState = PORTSTATE_BLOCKING;
382 inter->portType = PORTTYPE_NONDESIGNATED;
383}
384
385static void forward_port(NETSIM_ID d, ptrETH_LAN lan, NETSIM_ID in)
386{
387 print_ethernet_log("Setting switch %d, port %d state as FORWARDING", d, in);
388 ptrETH_IF inter = GET_ETH_IF(lan, in);
389 inter->portState = PORTSTATE_FORWARDING;
390}
391
392static void update_spt(ptrETH_SPT spt, ptrETH_BPDU bpdu, NETSIM_ID port)
393{
394 print_ethernet_log("Updating SPT data");
395 spt->rootId = bpdu->rootId;
396 spt->rootPathCost = bpdu->rootPathCost;
397 spt->rootPort = port;
398 print_ethernet_log("Root id = %s, Cost = %d, Root port = %d", spt->rootId, spt->rootPathCost, port);
399}
400
401static void mark_port(ptrETH_LAN lan, NETSIM_ID port)
402{
403 UINT i;
404 lan->spt->rootPort = port;
405 for (i = 0; i < lan->interfaceCount; i++)
406 {
407 ptrETH_IF inter = lan->ethIfVar[i];
408 if (inter->interfaceId == port)
409 {
410 inter->portType = PORTTYPE_ROOT;
411 }
412 else
413 {
414 inter->portType = PORTTYPE_DESIGNATED;
415 }
416 }
417}
418
419static void mark_port_as_nondesignated(ptrETH_LAN lan, NETSIM_ID port)
420{
421 UINT i;
422 for (i = 0; i < lan->interfaceCount; i++)
423 {
424 ptrETH_IF inter = lan->ethIfVar[i];
425 if (inter->interfaceId == port)
426 {
427 inter->portType = PORTTYPE_NONDESIGNATED;
428 break;
429 }
430 }
431}
432
433static void forward_bpdu(NETSIM_ID d, NETSIM_ID inter, double time, ptrETH_LAN lan, ptrETH_BPDU configBPDU)
434{
435 bool flag = false;
436
437 print_ethernet_log("Forwarding config BPDU");
438 NetSim_PACKET* packet = copy_bpdu_packet(d, lan, time, configBPDU);
439
440 UINT i;
441 for (i = 0; i < lan->interfaceCount; i++)
442 {
443 NETSIM_ID c, ci;
444 NETSIM_ID in = lan->interfaceId[i];
445 if (in == inter)
446 continue;
447
448 NETSIM_ID l = fn_NetSim_Stack_GetConnectedDevice(d, in, &c, &ci);
449 if (l)
450 {
451 ptrETH_VAR ce = GET_ETH_VAR(c);
452 if (ce)
453 {
454 ptrETH_LAN cl = GET_ETH_LAN(c, ci, 0);
455 if (cl && cl->STPStatus)
456 {
457 add_dest_to_packet(packet, c);
458 flag = true;
459 }
460
461 }
462 }
463 }
464 if (flag)
465 {
466 NetSim_EVENTDETAILS pevent;
467 memset(&pevent, 0, sizeof pevent);
468 pevent.dEventTime = time;
469 pevent.dPacketSize = packet->pstruMacData->dPacketSize;
470 pevent.nDeviceId = d;
471 pevent.nDeviceType = DEVICE_TYPE(d);
472 pevent.nEventType = MAC_OUT_EVENT;
473 pevent.nProtocolId = MAC_PROTOCOL_IEEE802_3;
474 pevent.pPacket = packet;
475 pevent.nSubEventType = SEND_CONFIG_BPDU;
476 fnpAddEvent(&pevent);
477 }
478 else
479 {
480 fn_NetSim_Packet_FreePacket(packet);
481 }
482}
483
484void process_configbpdu()
485{
486 NETSIM_ID d = pstruEventDetails->nDeviceId;
487 NETSIM_ID in = pstruEventDetails->nInterfaceId;
488 double time = pstruEventDetails->dEventTime;
489 NetSim_PACKET* packet = pstruEventDetails->pPacket;
490 ptrETH_LAN lan = GET_ETH_LAN(d, in, 0);
491
492 if (!lan->STPStatus)
493 {
494 fnNetSimError("ConfigBPDU packet is arrived to device %d interface %d. Spanning tree is not enable.",
495 d, in);
496 fn_NetSim_Packet_FreePacket(packet);
497 pstruEventDetails->pPacket = NULL;
498 return;
499 }
500
501 ptrETH_BPDU bpdu = get_eth_hdr_var(packet);
502 if (!bpdu)
503 {
504 fnNetSimError("ConfigBPDU hdr is NULL\n");
505 fn_NetSim_Packet_FreePacket(packet);
506 pstruEventDetails->pPacket = NULL;
507 return;
508 }
509
510 print_ethernet_log("Switch %d, Port %d, Time %0.0lf: Config BPDU packet arrives",
511 d, in, time);
512
513 print_ethernet_log("Config BPDU data: Root = %s, Cost = %d, Bridge Id = %d",
514 bpdu->rootId,
515 bpdu->rootPathCost,
516 bpdu->bridgeId);
517 print_ethernet_log("SPT data: Root = %s, cost = %d, Bridge Id = %s, Root port = %d",
518 lan->spt->rootId,
519 lan->spt->rootPathCost,
520 lan->spt->switchId,
521 lan->spt->rootPort);
522
523 COMPARE_BPDU isBlocked = compare_bpdu(bpdu, lan->spt, in);
524 if (isBlocked == BLOCK)
525 {
526 block_port(d, lan, in);
527 }
528 else if (isBlocked == FORWARD)
529 {
530 mark_port(lan, in);
531 update_spt(lan->spt, bpdu, in);
532 forward_bpdu(d, in, time, lan, bpdu);
533 }
534 fn_NetSim_Packet_FreePacket(packet);
535 pstruEventDetails->pPacket = NULL;
536 print_ethernet_log("");
537}
538
539void print_spanning_tree()
540{
541 NETSIM_ID i;
542 FILE* fp;
543 char buf[BUFSIZ];
544 sprintf(buf, "%s%s%s",
545 pszIOPath,
546 pathSeperator,
547 "SpanningTree.txt");
548 fp = fopen(buf, "w");
549 if (!fp)
550 {
551 perror(buf);
552 fnSystemError("Unable to open SpanningTree.txt file\n");
553 return;
554 }
555
556 for (i = 0; i < NETWORK->nDeviceCount; i++)
557 {
558 ptrETH_VAR eth = GET_ETH_VAR(i + 1);
559 if (!eth || !eth->isSPT)
560 continue;
561
562 UINT j;
563 for (j = 0; j < eth->lanCount; j++)
564 {
565 ptrETH_LAN lan = eth->lanVar[j];
566 if (!lan->STPStatus)
567 continue;
568
569 UINT k;
570 for (k = 0; k < lan->interfaceCount; k++)
571 {
572 ptrETH_IF inter = lan->ethIfVar[k];
573 NETSIM_ID l, c, ci;
574 l = fn_NetSim_Stack_GetConnectedDevice(i+1, lan->ethIfVar[k]->interfaceId, &c, &ci);
575 ptrETH_LAN lm = GET_ETH_LAN(c, ci, 0);
576 if (!lm->STPStatus)
577 continue;
578
579 fprintf(fp, "%s,%d,%s\n",
580 DEVICE_NAME(i + 1),
581 DEVICE_INTERFACE_CONFIGID(i + 1, inter->interfaceId),
582 inter->portState == PORTSTATE_BLOCKING ? "BLOCKED" : "FORWARD");
583 }
584 }
585 }
586 fclose(fp);
587}