1 2 3
Nu am intampinat probleme pentru acest task, rezolvarea mea este foarte similara cu pseudocodul oferit.
Am dat ping la host 3 de pe host 0 si a functionat, desi sunt in VLAN-uri separate, dar logica de VLAN inca nu a fost implementata. Se poate vedea rafala de ICMP-uri datorita buclei create de SWITCH-uri.
Pentru acest task a trebuit sa consider toate cele 4 cazuri posibile, respectiv A-A, A-T, T-T, T-A (T = Trunk, A = Access).
Dificultatea a fost in cum faceam diferenta intre un cadru cu tag si un cadru fara tag. Pentru A-A sau T-T, trebuia efectuat forwarding clasic, dar pentru A-T trebuia inclus acel tag de 802.1q custom, iar pentru T-A trebuia scos. Pentru a nu scrie cod duplicat sau greu de inteles, am decis sa-mi creez de la inceput ambele cadre, atat cel cu tag, cat si cel fara tag, pentru ca in functie de interfata, daca trebuia sa trimit atat catre un port Acces cat si catre un port Trunk, trebuia sa am nevoie de ambele.
M-am folosit de un dictionar pentru a tine mapari de tip
interface -> <vlan_id> / "T". De asemenea, aici trebuie verificata logica
pentru ca un cadru sa fie trimis doar pe vlan-ul corespunzator lui, iar
pentru adresele MAC destinatie care se aflau in tabela CAM trebuia verificat
si daca suma aceea a nibble-urilor este egala cu primii 4 biti (MSB) ai
vlan_tci-ului.
In poza se poate vedea in fereastra de wireshark rafala instantanee de cadre, intrucat neavand STP implementat, cele 3 switch-uri formeaza o bucla si fac un broadcast storm. De asemenea, am dat ping intre statiile 3 si 5, deoarece acestea apartin aceluiasi VLAN, daca as fi dat intre 3 si 4 pachetul nu ajungea, concluzionandu-se cu "Host unreachable"
Acesta a fost de departe cel mai greu task, deoarece nu intelesesem foarte bine cum functioneaza acest protocol.
Pentru a trimite un cadru HPDU la fiecare secunda am folosit un thread separat, care ruleaza intr-o bucla si trimite cadrul apoi sta in sleep 1 secunda.
Pentru a crea un cadru PPDU, pe langa adrese MAC, LLC Length si LLC Header,
PPDU_HEADER, trebuie creat si PPDU_CONFIG, care nu foloseste doar valori date
din cerinta. Aici m-am folosit de IEEE Std 802.1D-2004, mai exact pagina 165 din
281 (paginile pdf-ului, nu ale lucrarii), unde este un tabel cu HELLO_TIME,
MAX_AGE, FORWARD_DELAY, PORT_PRIORITY, care contine atat valorile
default cat si un range. Pentru a construi port id, m-am folosit de valoarea
standard, 128, care trebuie shiftata la stanga cu 8 biti pentru a concatena
cu port_number, respectiv interfata switch-ului, rezultand 16 biti pt
port id, cum este specificat in enunt (pagina 72/281, sectiunea 9.2.7 - despre
cati biti ar trebui sa aiba port number si port priority).
De asemenea, pentru a trimite cadre PPDU, am folosit un thread separat,
care la fiecare HELLO_TIME secunde trimite pe toate interfetele de tip
Trunk si cu port DESIGNATED un cadru PPDU. Acest cadru contine informatii
de stare globala a switch-ului in variabila stp_info: root bridge id si
root path cost. Pentru ca le foloseste in header, trebuie sa folosesc un lock
pentru a nu avea race condition, intrucat variabila stp_info este modificata
si in thread-ul principal, in main.
In logica din main, cand primesc un cadru, mai intai verific sa vad daca e de tip HPDU pentru a ii da discard, apoi verific daca e PPDU, si in final tratez cazul cand este un cadru de date (ICMP de ex). Singura diferenta intre logica de forwarding de la task-ul 2 si task-ul 3 este ca acum se verifica si daca interfata pe care ar trebui sa trimit cadrul este blocata, daca este nemaitrimitandu-se acesta.
Logica de STP este tratata in cazul unui cadru PPDU. Daca datele pe care le avertizeaza switch-ul, respectiv root BID, BID-ul sau si costul catre bridge sunt mai mari decat datele primite de la un switch vecin, se modifica starea globala a switch-ului, respectiv stp_info. Apoi se verifica din nou pentru a seta starile si rolurile fiecarui port.
In poza, in fereastra de Wireshark se vad cadrele urmatoare: cel cu Ethernet II este de tip ICMP, cadrul PPDU corespunzator STP-ului si cateva cadre cu destinatia Broadcast corespunzatoare HPDU-urilor lansate la o secunda distanta.
Initial, inainte sa se ruleze STP-ul, toate interfetele switch-urilor
sunt in Starea FORWARDING in DESIGNATED role, deoarece initial toate
pot trimite. In urma rularii STP-ului, sunt 3 stari posibile:
- Root port: ROOT_PORT (Role) si FORWARDING (State)
- Designated port: DESIGNATED (Role) si FORWARDING (State)
- Non-Designated port: BLOCKING (Role) si BLOCKING (State)
Practic am 3 tranzitii posibile din orice stare. Orice port poate trece intr-una din starile astea indiferent de starea lui curenta. Conteaza aceste comparatii:
my_adv = (stp_info.root_bid, my_cost, stp_info.my_bid, my_port)
neigh = (root_bid, root_cost, bid, pid)
if stp_info.root_port == interface:
port_roles[interface] = PortRole.ROOT_PORT
port_states[interface] = PortState.FORWARDING
elif my_adv < neigh:
port_roles[interface] = PortRole.DESIGNATED
port_states[interface] = PortState.FORWARDING
else:
port_roles[interface] = PortRole.BLOCKING
port_states[interface] = PortState.BLOCKINGAceste if-uri se rezuma la: daca oferi calea cea mai buna, inseamna ca esti root port, altfel daca anunti calea cea mai buna, esti designated port, iar la final, daca nu oferi calea cea mai buna si nici nu o desemnezi, esti port blocat.
E posibil sa fi facut mici optimizari, cum ar fi sa nu folosesc un dictionar pentru a mapa interfetele la diferite obiecte (stari, roluri, vlan_id), ci sa folosesc o lista din python (adica un vector), intrucat lista ar face accesul mai rapid la date, dar trebuie alocata in prealabil, si ar putea fi indexata dupa interfata, intrucat acestea sunt de la 1 la num_interfaces. Am ales dict() pt ca mi se pare mai usor de folosit, si teoretic ofera aceeasi complexitate. In acelasi timp, daca as fi indexat dupa numele interfetei (ex: FastEthernet0/1), mi ar fi fost de folos dict, deci e mai flexibil.
De asemenea, este posibil sa nu fi verificat toate cazurile teoretice pentru input, dar asta pentru ca in enunt se specifica aspecte precum: "vom presupune că switch-urile nu se pot strica".


