This shows you the differences between two versions of the page.
|
rl:teme:tema1_sw [2025/10/30 22:17] vlad_andrei.badoiu |
rl:teme:tema1_sw [2025/10/31 18:11] (current) radu.mantu |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | f<hidden> | + | <hidden> |
| ====== Tema 1 - Implementare Switch ====== | ====== Tema 1 - Implementare Switch ====== | ||
| Line 76: | Line 76: | ||
| În contextul suportului VLAN apare următoarea terminologie: un port de tip **trunk** este un port prin care pot fi transmise cadre din mai multe VLAN-uri și se află între două switch-uri, în timp ce un port de tip **access** este un port care conectează un host la switch. | În contextul suportului VLAN apare următoarea terminologie: un port de tip **trunk** este un port prin care pot fi transmise cadre din mai multe VLAN-uri și se află între două switch-uri, în timp ce un port de tip **access** este un port care conectează un host la switch. | ||
| - | {{:rl:teme:vlans-33-768x532.png?400|}} | + | {{ :rl:teme:t1_new_topo.png?500 | }} |
| + | |||
| + | <html><center><i> | ||
| + | Topologia implementată în mininet. Valorile numerice scrise în roșu pe link-uri reprezinta VID-ul tip 802.1Q.<br> | ||
| + | Partiționarea rețelei pe baza VID-ului extins (unde se ia în calcul adresa MAC) este reprezentată prin culorile sistemelor H0-H5.<br> | ||
| + | Citiți restul cerinței pentru mai multe detalii. | ||
| + | </i></center></html> | ||
| <note> | <note> | ||
| - | VLAN-ul este configurat la nivelul portului de switch, nu la nivelul unui host, chiar dacă în multe reprezentări grafice stațiile sunt ilustrate ca făcând parte dintr-un VLAN. | + | În mod normal, VLAN-ul este configurat la nivelul portului de switch, nu la nivelul unui host, chiar dacă în multe reprezentări grafice stațiile sunt ilustrate ca făcând parte dintr-un VLAN. |
| + | |||
| + | În acest task, vom încălca acest principiu. | ||
| </note> | </note> | ||
| - | Protocolul [[https://www.ietf.org/rfc/rfc2674.txt|IEEE 802.1Q]] este folosit pentru a introduce sistemul de marcare VLAN (VLAN tagging) în Ethernet, sub forma unei extensii a antetului (header-ului) Ethernet. În imaginea de mai jos putem observa că au fost introduși 4 octeți (bytes). De interes pentru noi este câmpul VID pe 12 biți, ce reprezintă identificatorul VLAN-ului din care cadrul face parte. | + | Protocolul [[https://www.ietf.org/rfc/rfc2674.txt|IEEE 802.1Q]] este folosit pentru a introduce sistemul de marcare VLAN (VLAN tagging) în Ethernet, sub forma unei extensii a antetului (header-ului) Ethernet. În imaginea de mai jos putem observa că au fost introduși 4 octeți (bytes). De înteres pentru noi este câmpul VID pe 12 biți, ce reprezintă identificatorul VLAN-ului din care cadrul face parte. |
| - | De acum înainte, când ne vom referi la un pachet cu header-ul 802.1Q, vom înțelege că este vorba despre un cadru Ethernet plus acești 4 byți. | + | De acum înainte, când ne vom referi la un pachet cu header-ul 802.1Q, vom înțelege că este vorba despre un cadru Ethernet plus acești 4 octeți. |
| - | {{ :rl:teme:ethernet_802.png?600 |}} | + | {{ :rl:teme:ethernet_802.png?600 | }} |
| {{ :rl:teme:tag_format.png?400 | }} | {{ :rl:teme:tag_format.png?400 | }} | ||
| - | Switch-ul cand **primeste** un cadru de pe orice interfata va comuta cadrul mai departe astfel: | + | Noi vom implementa un sistem custom de VLAN tagging similar cu 802.1Q. Acesta se va distinge prin două aspecte: |
| + | * **TPID**-ul va fi //0x8200//. | ||
| + | * Biții **PCP** și **DEI** for fi reutilizați ca extensie pentru câmpul **VID**. Acesta din urmă va contine in continuare tag-ul de VLAN configurat pe portul switch-ului, dar cei mai semnificativi biți din **TCI** vor lua valoarea sumei [[https://en.wikipedia.org/wiki/Nibble|nibble]]-urilor adresei MAC a host-ului asociat access port-ului. Overflow-ul sumei poate fi ignorat. | ||
| - | * cu header-ul 802.1Q dacă se transmite pe un port de tip trunk (către un switch). De notat că, în funcție de portul sursă, cadrul primit poate fi sau nu în format 802.1Q. De exemplu, dacă este un cadru primit pe un port trunk, atunci acesta va fi în format 802.1Q. În schimb, dacă este un cadru primit de pe o interfață de tip access, acesta nu va avea header 802.1Q, iar implementarea noastră de switch va trebui să îl adauge. | + | Switch-ul cand **primește** un cadru de pe orice interfata va comuta cadrul mai departe astfel: |
| - | * fără header-ul 802.1Q dacă se transmite pe o interfață de tip access și VLAN ID-ul este egal cu cel al interfeței de pe care a venit (către un host direct conectat la switch, parte a aceluiași VLAN) | + | |
| - | Nu vom comuta cadrele atunci când VLAN ID port destinație != VLAN ID cadru. | + | * Cu header-ul nostru custom (802.1Q-like) dacă se transmite pe un port de tip trunk (către un switch). De notat că, în funcție de portul sursă, cadrul primit poate fi sau nu să conția deja acest tag. De exemplu, dacă este un cadru primit pe un port trunk, atunci acesta va fi tagged. În schimb, dacă este un cadru primit de pe o interfață de tip access, acesta nu va avea header-ul //EtherType=0x8200//, iar implementarea noastră de switch va trebui să îl adauge. |
| + | * Fără header-ul custom dacă se transmite pe o interfață de tip access. Comutarea pachetului este condiționată de verificarea câmpului **VID extins**. VID-ul stocat în cadru trebuie sa fie egal cu cel configurat pe portul switch-ului, iar cei mai semnificativi 4 biți din TCI trebuie să coincidă cu suma nibbleurilor adresei MAC a host-ului conectat pe portul respectiv. Pentru a simplifica implementarea, extensia de 4 biți poate fi ignorată daca frame-ul este multicast sau dacă adresa MAC a host-ului incă nu a fost adaugată in tabela CAM. | ||
| - | Stațiile din simulare rulează Linux, iar stiva de networking din Linux face VLAN filtering, pentru a nu pierde eticheta VLAN (VLAN tag), vom folosi pentru TPID valoarea **0x8200** în loc de **0x8100**. Practic vom avea o implementare custom de 802.1q. Câmpurile PCP și DEI vor fi setate la valoarea 0. | + | <note tip> |
| + | Un exemplu de calcul a extensiei de VID pentru host-ul H5 cu MAC-ul **de:ad:be:ef:09:01**: | ||
| + | |||
| + | <code> | ||
| + | (0xd + 0xe + 0xa + 0xd + 0xb + 0xe + 0xe + 0xf + 0x9 + 0x1) & 0xf = 114 & 0xf = 0x2 | ||
| + | </code> | ||
| + | |||
| + | Deoarece VLAN ID-ul clasic configurat pe switch este **2**, VID-ul extins va fi **0x2002**. | ||
| + | </note> | ||
| **Atentie, nu uitati ca dimensiunea cadrului creste cu 4 bytes** | **Atentie, nu uitati ca dimensiunea cadrului creste cu 4 bytes** | ||
| Line 122: | Line 140: | ||
| Astfel, vom avea un singur proces STP pentru toate VLAN-urile, iar scopul este de a bloca link-urile care conduc la formarea de bucle. Vom avea doua tipuri de cadre trimise in cadrul protocolului: | Astfel, vom avea un singur proces STP pentru toate VLAN-urile, iar scopul este de a bloca link-urile care conduc la formarea de bucle. Vom avea doua tipuri de cadre trimise in cadrul protocolului: | ||
| - | * Hello Protocol Data Units (HPDU). Este un pachet trimis la fiecare secundă de fiecare switch. Îl vom folosi ca formă de verificare a faptului că un link este activ. În această implementare doar vom trimite aceste cadre, nu ne interesează funcționalitatea de marcare a unui link ca fiind nefuncțional. | + | * Hello Protocol Data Units (HPDU). Este un pachet trimis la fiecare secundă de fiecare switch. Îl vom folosi ca formă de verificare a faptului că un link este activ. În această implementare doar vom trimite aceste cadre, nu ne interesează funcționalitatea de marcare a unui link ca fiind nefuncțional. **Acesta este trimis pe toate porturile, si cele trunk, si cele catre hosts** |
| - | * Poli Protocol Data Units (PPDU) și vor conține trei informații importante: identificatorul switch-ului rădăcină (root bridge ID - 64 biți), identificatorul switch-ului expeditor (sender bridge ID - 64 biți) și costul drumului până la rădăcină (root path cost - 64 biți). Switch-ul rădăcină (root bridge) este switch-ul cu identificatorul cel mai mic. | + | * Poli Protocol Data Units (PPDU) și vor conține trei informații importante: identificatorul switch-ului rădăcină (root bridge ID - 64 biți), identificatorul switch-ului expeditor (sender bridge ID - 64 biți) și costul drumului până la rădăcină (root path cost - 64 biți). Switch-ul rădăcină (root bridge) este switch-ul cu identificatorul cel mai mic. |
| Line 149: | Line 167: | ||
| * Protocol ID - Vom folosi 0x0002 pentru PPDU Protocol | * Protocol ID - Vom folosi 0x0002 pentru PPDU Protocol | ||
| - | * BPDU Type (1 byte): 0x80 - Vom folosi TCN BPDU | + | * PPDU Type (1 byte): Vom folosi TCN BPDU |
| * Sequence Number (4 bytes): Numărul de secvență. Fiecare cadru PPDU va avea acest număr incrementat. Vom porni numărul de secvență de la 0. Valoarea va fi modulo 100. Astfel, primul cadru trimis la prima cuantă de timp va avea numărul de secvență 0, al doilea va avea numărul de secvență 1, al 100-lea va avea numărul de secvență 99, iar al 101-lea va avea numărul de secvență 0. | * Sequence Number (4 bytes): Numărul de secvență. Fiecare cadru PPDU va avea acest număr incrementat. Vom porni numărul de secvență de la 0. Valoarea va fi modulo 100. Astfel, primul cadru trimis la prima cuantă de timp va avea numărul de secvență 0, al doilea va avea numărul de secvență 1, al 100-lea va avea numărul de secvență 99, iar al 101-lea va avea numărul de secvență 0. | ||
| Line 171: | Line 189: | ||
| <note> | <note> | ||
| - | Pentru a fi punctat acest subpunct, cadrele trebuie să respecte indicațiile de mai sus. Pentru fiecare valoare din cadru care nu este exact specificată în enunț (e.g., pentru cele unde se specifică că valoarea este conform 802.1D-2004), va trebui să argumentați decizia și să includeți o referință. Fie un link, fie un PDF în arhivă în directorul sources. Sursa trebuie să includă locația exactă în document, fie prin pagină, fie prin numele secțiunii etc. Checker-ul verifica doar functionalitatea, nu si respectarea cerintelor de fond. | + | Pentru a fi punctat acest subpunct, cadrele trebuie să respecte indicațiile de mai sus. Pentru fiecare valoare din cadru care nu este exact specificată în enunț (e.g., pentru cele unde se specifică că valoarea este conform 802.1D-2004), va trebui să argumentați decizia și să includeți o referință. Fie un link, fie un PDF în arhivă în directorul sources. Sursa trebuie să includă locația exactă în document, fie prin pagină, fie prin numele secțiunii etc. Checker-ul verifică doar funcționalitatea, nu și respectarea cerințelor de fond. |
| </note> | </note> | ||
| Line 185: | Line 203: | ||
| - | In pseudocodul de mai jos este descrisa o posibila varianta a algoritmului descris la curs. In aceasta implementare, un port de switch poate avea două stări: **Blocking** și **Listening**. În starea Listening, portul funcționează normal pentru comutarea cadrelor. La pornire, fiecare switch se consideră Root Bridge și își setează toate porturile în starea Listening, deoarece acestea sunt considerate porturi **designated** - porturi care au costul cel mai mic catre Root Bridge. | + | In pseudocodul de mai jos este descrisa o posibila varianta a algoritmului descris la curs. In aceasta implementare, un port de switch poate avea două stări: **Blocking** și **Forwarding**. In mod normal, tranzitia catre Forwarding este realziata astfel: Blocking -> Listening -> Learning -> Forwarding pentru a evita buclele in timpul schimbarii topologiei. Totusi, noi o sa trecem direct la forwarding. În starea forwarding, portul funcționează normal pentru comutarea cadrelor. La pornire, fiecare switch se consideră Root Bridge și își setează toate porturile în starea Forwarding. De asemenea, avem urmatoarele roluri pe porturi: |
| + | |||
| + | * Designated - când două switch-uri sunt conectate, switch-ul cu costul mai mic către root bridge va avea portul său ca port desemnat. Este un port prin care trimitem PPDU-uri către un switch care are un cost mai mare către root bridge | ||
| + | * Root - portul catre root bridge cu costul cel mai mic | ||
| + | * Non-Designated - port blocat pentru a evita buclele | ||
| <note warning> | <note warning> | ||
| Line 193: | Line 215: | ||
| <code Python> | <code Python> | ||
| Initialize: | Initialize: | ||
| - | # Punem pe block-ing port-urile trunk pentru ca | + | # Initial toate porturile sunt blocate, pentru a nu avea bucle. |
| - | # doar de acolo pot aparea bucle. Port-urile catre | + | # Similar cu ce putem observa in PackeTracer, cand link-urile |
| - | # statii sunt pe deschise (e.g. designated) | + | # catre switch-uri nu sunt imediat active |
| - | set_ports(trunk_ports, BLOCKING) | + | set_ports_state(BLOCKING) |
| | | ||
| + | # Initial, fiecare switch se considera root bridge | ||
| own_bridge_ID = compute_bridge_id(priority, mac) | own_bridge_ID = compute_bridge_id(priority, mac) | ||
| root_bridge_ID = own_bridge_ID | root_bridge_ID = own_bridge_ID | ||
| root_path_cost = 0 | root_path_cost = 0 | ||
| | | ||
| - | + | set_all_ports_role(DESIGNATED) | |
| - | set_all_ports(DESIGNATED) | + | |
| </code> | </code> | ||
| La fiecare secunda, daca suntem root bridge, vom trimite un cadru PPDU. | La fiecare secunda, daca suntem root bridge, vom trimite un cadru PPDU. | ||
| + | De asemenea, trebuie sa trimitem la fiecare secunda cadrul HPDU. | ||
| <code Python> | <code Python> | ||
| Every 1 second: | Every 1 second: | ||
| if root_bridge_ID == own_bridge_ID | if root_bridge_ID == own_bridge_ID | ||
| - | Send PPDU on all trunk ports | + | Send PPDU on all ports |
| - | | + | else |
| + | Send PPDU on all DESIGNATED ports | ||
| Send HPDU | Send HPDU | ||
| </code> | </code> | ||
| - | |||
| - | În cazul în care am primit un pachet de tip PPDU, dacă | ||
| - | acesta are un Bridge ID mai mic decât al nostru, atunci switch-ul de la | ||
| - | care am primit acest pachet devine root bridge pentru noi. Mai mult, | ||
| - | vom retransmite propriul nostru PPDU actualizat pe toate celelalte porturi. | ||
| <code Python> | <code Python> | ||
| - | On receiving a PPDU on port: | + | On receiving a PPDU on port P: |
| + | |||
| + | # Am descoperit un nou root bridge | ||
| if PPDU.root_bridge_ID < root_bridge_ID: | if PPDU.root_bridge_ID < root_bridge_ID: | ||
| - | root_bridge_ID = PPDU.root_bridge_ID | + | # Actualizam detalile noului root bridge, precum root_port |
| - | update_cost_to_root_bridge() | + | set_new_root_bridge() |
| - | + | # Ajusteaza rolurile si starile tuturor porturilor pentru a | |
| - | # Actualizam starea porturilor | + | # surprinde schimbarea de root bridge |
| - | adjust_ports_status() | + | adjust_all_port_roles_and_states() |
| + | |||
| + | else if PPDU.root_bridge_ID == root_bridge_ID: | ||
| + | # Am descoperit o cale mai buna catre root bridge | ||
| + | if PPDU.sender_path_cost + LINK_COST < root_path_cost: | ||
| + | # Ajustam root_path_cost si root_port | ||
| + | update_root_bridge() | ||
| + | adjust_all_port_roles_and_states() | ||
| + | | ||
| | | ||
| - | # Trimitem acest PPDU catre toate celelalte porturi | + | # Am primit un PPDU cu un cost mai mare |
| - | forward_ppdu_all_other_ports() | + | else if PPDU.sender_path_cost + LINK_COST > root_path_cost: |
| - | + | set P role to DESIGNATED | |
| - | + | set P state to FORWARDING | |
| - | Else if PPDU.root_bridge_ID == root_bridge_ID: | + | |
| - | + | else # Atat eu cat si switch-ul de la care am primit PPDU-ul avem acelasi cost | |
| - | If port == root_port and PPDU.sender_path_cost + LINK_COST < root_path_cost: | + | if PPDU.sender_bridge_ID < own_bridge_ID: |
| - | root_path_cost = PPDU.sender_path_cost + LINK_COST | + | set P role to ROOT |
| - | + | set P state to FORWARDING | |
| - | Else If port != Root_Port: | + | else: |
| - | # Verifica daca portul ar trebui trecut pe designated. | + | set P role to DESIGNATED |
| - | # Designated inseamna ca drumul catre root este prin | + | set P state to FORWARDING |
| - | # acest switch. Daca am bloca acest drum, celelalte | + | |
| - | # switch-uri nu ar mai putea comunica cu root bridge. | + | |
| - | # Nota: in mod normal ar trebui sa stocam ultimul BPDU | + | |
| - | # de pe fiecare port ca sa calculam designated port. | + | |
| - | if PPDU.sender_path_cost > root_path_cost: | + | |
| - | If port is not the Designated Port for this segment: | + | |
| - | Set port as the Designated Port and set state to LISTENING | + | |
| - | + | ||
| - | Else if PPDU.sender_bridge_ID == own_bridge_ID: | + | |
| - | Set port state to BLOCKING | + | |
| - | Else: | + | |
| - | Discard BPDU | + | |
| - | + | ||
| - | if own_bridge_ID == root_bridge_ID: | + | |
| - | update_ports_state(DESIGNATED_PORT) | + | |
| - | For each port on the bridge: | + | |
| - | Set port as DESIGNATED_PORT | + | |
| </code> | </code> | ||
| <note> | <note> | ||
| Pentru simplitate, vom presupune ca switch-urile nu se pot strica. | Pentru simplitate, vom presupune ca switch-urile nu se pot strica. | ||
| + | </note> | ||
| + | |||
| + | <note> | ||
| + | Pentru a primi punctaj pe acest subpunct, va trebui să includeți în arhivă un fișier **state_machine.drawio** care să includă reprezentarea state machine-ului implementat pentru PSTP. Vom folosi [[https://app.diagrams.net/|draw.io]] pentru asta. | ||
| </note> | </note> | ||
| Line 327: | Line 342: | ||
| - | Pentru a fi punctată o temă, arhiva trebuie să conțină câte un screenshot relevant pentru fiecare exercițiu în care să apară numele vostru (e.g. cu un echo), una sau mai multe ferestre de Wireshark și unul sau mai multe terminale care să dovedească rularea manuală a funcționalității relevante. Fiecare screenshot este insotit si de o explicație în README. | + | Pentru a fi punctată o temă, arhiva trebuie să conțină câte un screenshot relevant pentru fiecare exercițiu în care să apară numele vostru (e.g. cu un echo), una sau mai multe ferestre de Wireshark și unul sau mai multe terminale care să dovedească rularea manuală a funcționalității relevante. Fiecare screenshot este insotit si de o explicație în README. De asemenea, în README trebuie să documentați deciziile de design (e.g., cum ați stocat stările în STP), dificultățile întâmpinate și cum le-ați rezolvat, optimizările aduse dacă este cazul, și o secțiune de auto-evaluare a implementării. |
| > Screenshot-urile vor fi incluse în root-ul arhivei cu numele **ex1.jpg**, **ex2.jpg** și **ex3.jpg** | > Screenshot-urile vor fi incluse în root-ul arhivei cu numele **ex1.jpg**, **ex2.jpg** și **ex3.jpg** | ||