next up previous contents
Next: Die Event-Schnittstelle Up: Erweiterung des Kernels Previous: Vorhandener Code

Erweiterter Code

  Ansatzpunkt für die Implementation der VLAN Eigenschaften auf Port-Ebene ist zunächst einmal die Struktur Port_data, die alle für einen Port relevanten Daten enthält und um einen Eintrag für VLAN's erweitert werden muß. Port_data ist in der Headerdatei br.h deklariert, die unter /usr/src/linux/include/net zu finden ist. Ein Integerwert (unsigned int vlan_id) soll das VLAN eindeutig identifizieren. Zusätzlich wird noch eine Konstante DEFAULT_VLAN_ID definiert, die den Hexadezimalwert 0x01 erhält. DEFAULT_VLAN_ID steht wie der Name schon sagt für den Defaultwert des VLAN's, den der Port annimmt, wenn ihm kein anderer Wert zugewiesen wird.

Gesetzt wird der Defaultwert in der Funktion br_init_port in br.c, die den Port initialisiert. Dazu wurde eine Funktion set_port_vlan_id geschrieben, die auch später verwendet werden kann, um einen Port einem VLAN zuzuweisen.

771 static void set_port_vlan_id(int port_no, unsigned int vlan_id)
772 {
773        port_info[port_no].vlan_id = vlan_id;
774 }

Für den Fall, daß der Frame vom host stack empfangen wird und dieser noch keinem VLAN zugeordnet ist, wird diesem in der Funktion br_tx_frame explizit ein VLAN zugeordnet. Sinnvollerweise ist der Switch selbst Bestandteil aller VLAN's. Das führt zu der Frage, wie der Switch durch die Zuordnung nur einer VLAN ID mehreren VLAN's angehören kann. Offensichtlich besteht zwischen dem Objekt Port bzw. MAC-Adresse und dem Objekt VLAN eine n:n Beziehung. Ein Port (oder eine MAC-Adresse) kann einem oder mehreren VLAN's zugeordnet sein. Umgekehrt enthält ein VLAN einen oder mehrere Ports (oder MAC-Adressen). Um den rechnerischen Aufwand bei der Prüfung einer verketteten Liste von Objekten zu umgehen, wird an dieser Stelle eine binäre UND-Verknüpfung von Integerwerten angewandt. Somit ergeben sich 32 unterschiedliche VLAN's. Liegt ein Port bspw. in VLAN 1 und VLAN 2, so bekommt er die VLAN ID 3. Die Funktion check_vlan_id verdeutlicht dies:

1712 static int check_vlan_id(struct fdb *s, struct fdb *f)
1713 {

[...]

1736         if ((s->vlan_id & f->vlan_id) == 0)
1737                 return(FALSE);
1738         return (TRUE);
1729 }

Die Struktur fdb stellt den Datentyp dar, der als Knoten im AVL-Baum gespeichert ist. Verglichen werden hier die VLAN ID's von Quell- und Zieladresse. Bei Übereinstimmung liefert der Code true zurück, ansonsten false.

Analog dazu werden in br_forward die Portzuordnungen verglichen:

1650 /*
1651 *      Sending
1652 *      only if: source-port != destination-port
1653 *               port.state == Forwarding
1654 *               source.address.vlan_id == dest.address.vlan_id
1655 *               source.port.vlan_id == dest.port.vlan_id
1656 */
1657 if (f->port!=port && port_info[f->port].state == Forwarding
1658   &&
1659     check_vlan_id(s, f)
1660   &&
1661     ((port_info[f->port].vlan_id & port_info[port].vlan_id) != 0) )
1662 {

[...]

1701 }

Bei Erfolg wird der Frame an den entsprechenden Port geforwardet, bei Mißerfolg durch die Funktion br_dev_drop vernichtet.

Das Setzen der VLAN ID's ist ungleich aufwendiger. Die Funktion br_ioctl interagiert mittels I/O Aufrufen zwischen dem Konfigurationsprogramm und dem Kernel. Sie erwartet als Eingabe einen Parameter vom Typ Integer. Die Aufschlüsselung zwischen Integerwert und dem zugehörigen Define ist in br.h beschrieben. Für die VLAN-Zuordnung sind zwei neue Defines hinzugekommen: BRCMD_VLAN_CONFIG mit Wert 16 und BRCMD_MAC_VLAN mit Wert 17. Erhält der Kernel nun einen ioctl, dann ruft er die entsprechende Routine auf: set_port_vlan_id, um die Port-VLAN-Zuordnung zu realisieren und set_mac_vlan_id für die MAC-Adressen-VLAN Zuordnung. Als Argumente werden jeweils Strukturen vom Typ br_cf übergeben, die in br.h folgendermaßen deklariert wird:

212 struct br_cf {
213         unsigned int cmd;
214         unsigned int arg1;
215         unsigned int arg2;
216         unsigned char ula[6];
217 };

cmd ist das schon angesprochene Kommando, arg1 und arg2 können Zahlenwerte aufnehmen, bspw. die Portnummer. Interessant ist lediglich das 6 Byte große Array. Dieses wird nur für die Aufnahme der MAC-Adresse in hexadezimaler Form benötigt, der gesamte Kernelcode arbeitet mit diesem Array, so daß hier nicht davon abgewichen werden soll. Die Funktion set_port_vlan_id wurde weiter oben schon beschrieben.

Die Implementierung der Zuordnung von MAC-Adresse zu einem oder mehreren VLAN's wird im folgenden beschrieben. Zunächst wird die switchinterne Tabelle, die die Information über die Zuordnung von Port- zu MAC-Adressen beinhaltet, von der Tabelle für die MAC-Adressen zu VLAN Zuordnung getrennt. Der Einfachheit halber wurde dazu der vorhandene Code br_tree.c kopiert und in sw_tree.c umbenannt. Im Code selber wurden sämtliche Funktionen umbenannt. Aus addr_cmp wurde so sw_addr_cmp u.s.w. Weiterhin wurde die Struktur fdb aus br.h nach swdb kopiert und dessen Zeiger mit dem Präfix sw_ versehen. Damit entstand eine neue Tabelle für die Zuordnung von MAC-Adressen zu VLAN's in Form eines AVL-Baums.

Das Setzen der VLAN ID geschieht wieder durch einen Funktionsaufruf in br_ioctl.

1904 case BRCMD_MAC_VLAN:
1905          set_mac_vlan_id(bcf.arg1, &bcf.ula[0]);
1906          break;

Der Funktion set_mac_vlan_id werden als Parameter die VLAN ID und die MAC-Adresse übergeben. Die Funktion selber sieht folgendermaßen aus:

776 static int set_mac_vlan_id(unsigned int vlan_id, unsigned char *ula)
777 {
778     struct swdb *s, *m = NULL;
779
780     s = (struct swdb *)kmalloc(sizeof(struct swdb), GFP_ATOMIC);
781     if (!s) {
782             printk(KERN_DEBUG "br_learn: unable to malloc swdb\n");
783                return(-1);
784     }
785
786     m = sw_avl_find_addr(ula);
787     if (m) {
788             s->port = m->port;
789     }
790     else { /* unknown MAC-address */
791             printk(KERN_DEBUG "set_mac_vlan_id: unknown mac address\n");
792             return(-2); 
793     }
794
795     memcpy(s->ula, ula, 6); /* specified mac address */
796     s->timer = CURRENT_TIME;
797     s->flags = FDB_ENT_VALID;
798
799     s->vlan_id = vlan_id;   /* specified vlan_id */
800
801     if (sw_avl_insert(s) == 0) {    /* update */
802             kfree(s);
803             return(0);
804     }
805     else {                          /* unknown MAC-address */
806             printk(KERN_DEBUG "set_mac_vlan_id: unknown mac address\n");
807             kfree(s);
808             return(-2);
809     }
810 }

Die Variablen s und m sind Zeiger auf eine Variable der Struktur swbd. Zunächst wird Speicherplatz für die Struktur allokiert und mit s eine Referenz darauf angelegt. Sollte der Speicher korrekt allokiert worden sein, prüft der Aufruf von sw_avl_find_addr(ula), ob die übergebene MAC-Adresse in der Switchtabelle schon bekannt ist und übergibt den zugehörigen Port an die durch den Zeiger s referenzierte Struktur. Danach werden die Felder ula, timer, flags und vlan_id gefüllt. Die Variable ula beinhaltet die übergebene MAC-Adresse, vlan_id die Zugehörigkeit zu den gewünschten VLAN's. Durch das Setzen von timer auf die aktuelle Zeit verlängert sich der timestap des aktuellen Eintrages. flags zeigt an, daß es sich um einen gültigen Eintrag handelt. Als nächstes wird durch Aufruf der Funktion sw_avl_insert(s) der betreffende Eintrag im AVL-Baum durch die neuen Werte überschrieben und danach der Speicherplatz wieder freigegeben.

Die Initialisierung einer neuen MAC-Adresse mit einem Default-VLAN Wert geschieht in der Funktion br_learn nach einem ähnlichem Schema:

1472   m = sw_avl_find_addr(skb->mac.ethernet->h_source);
1473   if (m) {
1474            s->vlan_id = m->vlan_id;
1475   }
1476   else { /* set default vlan_id */ 
1477            s->vlan_id = DEFAULT_VLAN_ID;
1478   }

Hierbei wird zunächst überprüft, ob zu der neu hinzugekommenen MAC-Adresse schon eine VLAN-Zuordnung besteht. Wenn dem so ist, wird diese Zuordnung beibehalten, ansonsten die neue DTE mit dem Defaultwert versehen. Grund für diese Abfrage ist, daß die Funktion br_learn sowohl dann aufgerufen wird, wenn der Status des betreffenden Ports auf Learning steht, als auch dann, wenn Frames geforwarded werden sollen. Bei jedem Frame, den der Switch weiterforwarded, wird der timestamp der betreffenden DTE an dieser Stelle erneuert. Übergeben wird der Funktion br_learn jedoch nur ein Konstrukt des Typs sk_buff, das keinen Eintrag für VLAN's vorsieht. Der Erweiterung von sk_buff um VLAN's steht entgegen, daß diese Struktur in weiten Bereichen des Netzcodes innerhalb des Kernels als temporärer Puffer eingesetzt wird. Daher sind die Folgen einer Änderung von sk_buff unvorhersehbar.


next up previous contents
Next: Die Event-Schnittstelle Up: Erweiterung des Kernels Previous: Vorhandener Code
Root on HPHEGER0
3/3/1999