Die net_device-Struktur ist der innerste Kern der Netzwerk-Treiber-Schicht und verdient es daher, vollständig beschrieben zu werden. Beim ersten Lesen können Sie diesen Abschnitt allerdings überspringen, weil Sie die Struktur nicht vollständig verstehen müssen, um loslegen zu können. In dieser Liste werden alle Felder beschrieben, aber mehr in der Absicht, eine Referenz zu bieten, als daß die Felder auswendig gelernt werden sollten. Der Rest des Kapitels beschreibt kurz jedes Feld, sobald es im Beispiel-Code vorkommt, so daß Sie nicht jedesmal zu diesem Abschnitt hier zurückspringen müssen.
struct net_device kann vollständig in zwei Teile aufgeteilt werden, den “sichtbaren” und den “unsichtbaren”. Der sichtbare Teil der Struktur besteht aus den Feldern wie den beiden in snull oben gezeigten, die in statischen net_device-Strukturen explizit zugewiesen werden. Alle Strukturen in drivers/net/Space.c werden so initialisiert, ohne Verwendung der Tag-Syntax zur Initialisierung von Strukturen. Die verbleibenden Felder werden intern vom Netzwerk-Code verwendet und normalerweise nicht während des Kompilierens initialisiert, nicht einmal mit der Tag-Syntax. Manche der Felder werden von Treibern verwendet (etwa solche, die zur Initialisierung zugewiesen werden), während andere nicht angefaßt werden sollten.
Der erste Teil von struct net_device besteht aus den folgenden Feldern in der angegebenen Reihenfolge:
Der Name des Geräts. Wenn der Name einen %d-Format-String enthält, dann wird der erste verfügbare Geräte-Name mit der angegebenen Basis verwendet; die zugewiesenen Nummern fangen mit 0 an.
Geräte-Speicher-Informationen. Diese Felder enthalten die Anfangs- und Endadressen des vom Gerät gemeinsam genutzten Speichers. Wenn das Gerät unterschiedliche Speicherbereiche zum Senden und Empfangen verwendet, dann enthalten die mem-Felder den Bereich zum Senden und die rmem-Felder den zum Empfangen. mem_start und mem_end können beim Hochfahren des Systems auf der Kommandozeile des Kernels angegeben und mit ifconfig abgefragt werden. Die Felder rmem werden nie außerhalb des Treibers verwendet. Per Konvention werden die end-Felder so belegt, daß end - start die Menge des verfügbaren Speichers auf der Karte angibt.
Die I/O-Basisadresse der Netzwerk-Schnittstelle. Dieses Feld wird wie die vorigen beim Suchen des Gerätes belegt. Mit ifconfig kann der aktuelle Wert angezeigt und verändert werden. base_addr kann auf der Kommandozeile des Kernels beim Booten oder beim Laden des Moduls explizit angegeben werden. Das Feld wird wie die oben genannten Speicherfelder nicht vom Kernel verwendet.
Die zugewiesene Interrupt-Nummer. Der Wert von dev->irq wird von ifconfig ausgegeben, wenn die Schnittstellen aufgeführt werden. Dieser Wert kann üblicherweise beim Hochfahren oder Laden gesetzt und später mit ifconfig modifiziert werden.
Gibt an welcher Port auf Geräten mit mehreren Ports verwendet wird. Dieses Feld wird beispielsweise bei Geräten verwendet, die sowohl Koax- (IF_PORT_10BASE2-) als auch Twisted-Pair-Ethernet-(IF_PORT_10BASET)- Verbindungen unterstützen. Die bekannten Port-Typen sind in <linux/netdevice.h> definiert.
Der vom Gerät allozierte DMA-Kanal. Das Feld ist nur bei einigen Peripherie-Bus-Systemen wie ISA sinnvoll. Es wird außerhalb des Gerätetreibers selbst nur zu Informationszwecken (in ifconfig) verwendet.
Der Gerätezustand. Dieses Feld enthält mehrere Flags. Treiber manipulieren diese Flags normalerweise nicht direkt, sondern verwenden statt dessen eine Reihe von Hilfsfunktionen. Wir sprechen über diese Funktionen weiter unten, wenn wir auf den Treiber-Betrieb eingehen.
Ein Zeiger auf das nächste Gerät in der globalen verketteten Liste; der Treiber sollte dieses Feld nicht benutzen.
Die weiter oben beschriebene Initialisierungsfunktion.
Die Struktur net_device enthält mehrere zusätzliche Felder, die normalerweise bei der Initialisierung des Geräts zugewiesen werden. Einige dieser Felder enthalten Informationen über die Schnittstelle, während andere nur für den Treiber existieren (d. h., sie werden nicht vom Kernel verwendet). Es gibt auch noch weitere Felder, vor allem die Geräte-Methoden, die ein Bestandteil der Schnittstelle zwischen Kernel und Treiber sind.
Wir werden hier die drei Gruppen separat aufführen und dabei die eigentliche Reihenfolge der Felder, die nicht relevant ist, außer acht lassen.
Die größte Teil der Informationen über die Schnittstelle wird schon von ether_setup korrekt ausgefüllt. Ethernet-Karten können sich bei den meisten Feldern auf diese allgemein verwendbare Funktion verlassen, aber die Felder flags und dev_addr sind gerätespezifisch und müssen bei der Initialisierung explizit zugewiesen werden.
Auch einige Nicht-Ethernet-Schnittstellen können auf ähnliche Hilfsfunktionen wie ether_setup zurückgreifen. driver/net/net_init.c exportiert eine Reihe solcher Funktionen, darunter:
Richtet die Felder für ein LocalTalk-Gerät ein.
Initialisierung für Fiber Channel-Geräte.
Konfiguriert eine Schnittstelle für ein Fiber Distributed Data Interface-(FDDI)Netzwerk.
Bereitet die Felder für einen Treiber für den High-Performance Parallel Interface-(HIPPI)Standard vor.
Erledigt das Einrichten von Token Ring-Netzwerk-Schnittstellen. Beachten Sie, daß es im 2.4-Kernel auch eine Funktion tr_setup gibt, die interessanterweise gar nichts macht.
Die meisten Geräte werden in einer dieser Klassen enthalten sein. Wenn Ihres radikal neu und anders ist, müssen Sie aber alle Felder von Hand belegen.
Die Hardware-Header-Länge ist die Anzahl der Oktetts, die im übertragenen Paket vor dem IP-Header oder anderen Protokoll-Informationen stehen. Bei Ethernet-Schnittstellen ist dieser Wert 14.
Die Maximum Transfer Unit. Dieses Feld wird von der Netzwerk-Schicht während der Paket-Übertragung verwendet. Ethernet hat eine MTU von 1500 Oktetten (ETH_DATA_LEN).
Die maximale Anzahl der Frames, die in der Übertragungswarteschlange des Geräts abgelegt werden können. Dieser Wert wird von ether_setup auf 100 gesetzt, kann aber von Ihnen geändert werden. plip verwendet beispielsweise 10, um die Verschwendung von Systemspeicher zu vermeiden (plip hat einen geringeren Durchsatz als eine echte Ethernet-Schnittstelle).
Der Hardware-Typ der Schnittstelle. Dieses Feld wird von ARP benutzt, um festzustellen, welche Art von Hardware-Adressen die Schnittstelle unterstützt. Der korrekte Wert für Ethernet-Schnittstellen ist ARPHRD_ETHER, was auch von ether_setup gesetzt wird. Die bekannten Typen sind in <linux/if_arp.h> definiert.
Hardware-(MAC-)Adreßlänge und Geräte-Hardware-Adressen. Die Ethernet-Adreßlänge beträgt sechs Oktetts (hier geht es um die Hardware-ID der Schnittstellen-Karte), und die Broadcast-Adresse besteht aus sechs 0xff- Oktetten. ether_setup initialisiert diese Werte korrekt. Die Geräte-Adresse muß dagegen aus der Schnittstellen-Karte ausgelesen und nach dev_addr geschrieben werden. Das Auslesen ist gerätespezifisch. Die Hardware-Adresse wird verwendet, um korrekte Ethernet-Header zusammenzustellen, bevor das Paket an den Treiber zur Übertragung übergeben wird. snull verwendet keine physikalische Schnittstelle und denkt sich daher eine eigene Hardware-Adresse aus.
Flags für das Interface; werden gleich ausführlich besprochen.
Das Feld flags ist eine Bitmaske, die sich unter anderem aus den im Folgenden aufgeführten Bit-Werten zusammensetzt. Das Präfix IFF_ steht dabei für “InterFace Flags”. Einige Flags werden vom Kernel verwaltet, andere werden von der Schnittstelle bei der Initialisierung gesetzt, um diverse Capabilities (und andere Merkmale) der Schnittstelle bekanntzugeben. Die zulässigen Flags sind in <linux/if.h> definiert und lauten:
Der Treiber kann dieses Flag nur lesen. Der Kernel setzt es, wenn die Schnittstelle aktiv ist und Pakete übertragen kann.
Dieses Flag teilt mit, daß die Schnittstelle das Broadcasting zuläßt. Ethernet-Karten unterstützen das Broadcasting.
Debug-Modus. Dieses Flag kann gesetzt werden, um die Ausführlichkeit Ihrer printk-Ausgaben zu steuern oder andere Debugging-Möglichkeiten einzuschalten. Auch wenn derzeit kein offizieller Treiber dieses Flag unterstützt, kann es doch von Benutzerprogrammen via ioctl gesetzt und gelesen und von Ihrem Treiber verwendet werden. Mit dem Programm misc-progs/netifdebug kann dieses Flag ein- und ausgeschaltet werden.
Dieses Flag sollte nur in Loopback-Schnittstellen gesetzt werden. Der Kernel überprüft dieses Flag anstelle des hartcodierten Namens lo.
Dieses Flag kennzeichnet, daß die Schnittstelle mit einer Punkt-zu-Punkt-Verbindung verbunden ist. Es wird von ifconfig gesetzt, beispielsweise bei plip und dem PPP-Treiber.
Dieses Flag besagt, daß die Schnittstelle kein ARP beherrscht. Beispielsweise verwenden Point-to-Point-Schnittstellen kein ARP, weil das nur zu zusätzlichem Datenverkehr führen würde, ohne nützliche Informationen zu bringen. snull hat keine ARP-Fähigkeiten, setzt also dieses Flag.
Dieses Flag wird gesetzt, um den sogenannten “Promiscuous”-Modus zu aktivieren. Default-mäßig verwenden Ethernet-Schnittstellen einen Hardware-Filter, der sicherstellt, daß sie nur Broadcast- und an sie selbst gerichtete Pakete empfangen. Paket-Überwachungsprogramme wie tcpdump schalten den “Promiscuous”-Modus ein, um alle Pakete zu bekommen, die über das Übertragungsmedium der Schnittstelle übertragen werden.
Dieses Flag wird von solchen Schnittstellen gesetzt, die Multicast-Übertragungen beherrschen. ether_setup setzt dieses Flag default-mäßig; wenn Ihr Treiber also kein Multicasting unterstützt, dann müssen Sie bei der Initialisierung dieses Flag löschen.
Dieses Flag weist der Schnittstelle an, alle Multicast-Pakete zu empfangen. Der Kernel setzt es, wenn der Host ein Multicast-Routing durchführt, aber nur dann, wenn IFF_MULTICAST gesetzt ist. IFF_ALLMULTI kann von der Schnittstelle nur gelesen werden. Wir werden das im Abschnitt "the Section called Multicasting" weiter hinten in diesem Kapitel noch zeigen.
Diese Flags werden vom Lastausgleichscode verwendet. Der Schnittstellen-Treiber muß sie nicht benutzen.
Diese Flags signalisieren, daß das Gerät zwischen verschiedenen Medientypen wie etwa ungeschütztem Twisted Pair (UTP) und koaxialen Ethernet-Kabeln wechseln kann. Wenn IFF_AUTOMEDIA gesetzt ist, wählt das Gerät das korrekte Medium automatisch aus.
Dieses Flag zeigt an, daß sich die Adresse dieser Schnittstelle ändern kann; es wird bei Dialup-Geräten benutzt.
Dieses Flag zeigt an, daß die Schnittstelle betriebsbereit ist. Es existiert hauptsächlich für die BSD-Kompatibilität; der Kernel benutzt es kaum. Die meisten Netzwerk-Treiber müssen sich über IFF_RUNNING keine Gedanken machen.
Dieses Flag wird unter Linux nicht verwendet und ist nur für die BSD-Kompatibilität vorhanden.
Wenn ein Programm IFF_UP ändert, wird die Gerätemethode open oder close aufgerufen. Wird IFF_UP oder irgendein anderes Flag geändert, dann wird die Methode set_multicast_list aufgerufen. Wenn der Treiber bei der Modifikation eines der Flags bestimmte Aufgaben durchführen muß, dann muß er dies in set_multicast_list tun. Beispielsweise muß der Hardware-Filter auf der Karte informiert werden, wenn IFF_PROMISC gesetzt oder zurückgesetzt wird. Die Aufgaben dieser Gerätemethode werden später im Abschnitt “the Section called Multicasting” beschrieben.
Wie auch die Zeichen- und Block-Treiber deklariert jedes Netzwerk-Gerät die Funktionen, die auf ihm arbeiten. Die unten stehende Liste enthält die Operationen, die auf Netzwerk-Geräten definiert sind. Einige der Operationen können auch auf NULL gelassen werden, andere wiederum werden normalerweise nicht angefaßt, weil ether_setup bereits passende Methoden bereitstellt.
Die Gerätemethoden einer Netzwerk-Schnittstelle können in zwei Gruppen eingeteilt werden: fundamentale Methoden und optionale Methoden. Die fundamentalen Methoden sind diejenigen, die gebraucht werden, um die Schnittstelle zu benutzen; optionale Methoden implementieren fortgeschrittenere Funktionalitäten, die streng genommen nicht benötigt werden. Die folgenden Methoden sind fundamental:
Öffnen der Schnittstelle. Eine Schnittstelle wird geöffnet, wenn sie von ifconfig aktiviert wird. Die Methode sollte alle benötigten Systemressourcen (I/O-Ports, IRQ, DMA usw.) registrieren, die Hardware aktivieren und den Verwendungszähler des Moduls inkrementieren.
Hält die Schnittstelle an. Das geschieht, wenn die Schnittstelle deaktiviert wird. Hier sollten alle beim Öffnen vorgenommenen Operationen rückgängig gemacht werden.
Diese Methode fordert die Übertragung eines Pakets an. Das vollständige Paket (mit Headern und allem) ist in einer Socket-Buffer- (sk_buff-)Struktur enthalten. Socket-Buffer werden weiter unten eingeführt.
Diese Funktion baut den Hardware-Header aus den vorher geholten Quell- und Ziel-Hardware-Adressen auf. Sie hat die Aufgabe, die in den Argumenten übergebene Information in Form eines passenden, gerätespezifischen Hardware-Headers aufzubereiten. In Ethernet-ähnlichen Schnittstellen wird eth_header default-mäßig verwendet und von ether_setup entsprechend hier eingetragen.
Diese Funktion wird verwendet, um den Hardware-Header neu aufzubauen, bevor ein Paket übertragen wird. Die Default-Funktion, die in Ethernet-Geräten benutzt wird, verwendet ARP, um die fehlende Information einzutragen. Die Methode rebuild_header wird im 2.4-Kernel selten verwendet, statt dessen wird hard_header benutzt.
Diese Methode wird aufgerufen, wenn eine Paket-Übertragung innerhalb einer angemessenen Periode nicht durchgeführt werden kann. Dabei wird angenommen, daß ein Interrupt verpaßt wurde oder die Schnittstelle blockiert ist. Die Methode sollte das Problem beheben und die Paket-Übertragung fortsetzen.
Immer wenn eine Applikation Statistiken über eine Schnittstelle benötigt, wird diese Methode aufgerufen. Das geschieht beispielsweise beim Aufruf von ifconfig und netstat –i. Eine Beispiel-Implementation für snull finden Sie weiter unten in “the Section called Statistische Informationen”.
Ändert die Konfigurationsinformation. Diese Methode ist der Einsprungpunkt für die Konfiguration des Treibers. Die I/O-Adresse und die Interrupt-Nummer eines Geräts können zur Laufzeit mit set_config geändert werden. Diese Fähigkeit wird vom Systemadministrator verwendet, wenn das Gerät vom Treiber sonst nicht gefunden wird. Treiber für moderne Hardware müssen diese Methode normalerweise nicht implementieren.
Es verbleiben noch die Geräte-Operationen, die wir für optional halten:
Führt schnittstellenspezifische ioctl-Befehle aus. Die Implementation dieser Befehle wird weiter unten in “the Section called Eigene ioctl-Befehle” beschrieben. Das entsprechende Feld in struct net_device kann auf NULL gelassen werden, wenn die Schnittstelle keine schnittstellenspezifischen Befehle kennt.
Diese Methode wird aufgerufen, wenn sich die Multicast-Liste des Geräts ändert oder die Flags sich ändern. In “the Section called Multicasting” finden Sie nähere Beispiele und eine Implementation.
Diese Funktion kann implementiert werden, wenn die Schnittstelle in der Lage ist, ihre Hardware-Adresse zu ändern. Viele Schnittstellen sind dazu nicht in der Lage. Andere verwenden die Default-Implementation eth_mac_addr (aus drivers/net/net_init.c). eth_mac_addr kopiert nur die neue Adresse nach dev->dev_addr, und das auch nur, wenn die Schnittstelle nicht in Betrieb ist. Treiber, die eth_mac_addr verwenden, sollten die Hardware-MAC-Adresse bei der Konfiguration anhand von dev->dev_addr setzen.
Diese Funktion erledigt alles Notwendige, wenn sich die MTU (Maximum Transfer Unit) des Gerätes ändert. Falls der Treiber etwas Besonderes machen muß, wenn die MTU verändert wird, sollte er eine eigene Funktion deklarieren, ansonsten macht die Default-Version das Richtige. Wenn es Sie interessiert, können Sie ein Muster für diese Funktion in snull finden.
header_cache wird aufgerufen, um die hh_cache-Struktur mit dem Ergebnis einer ARP-Anfrage zu füllen. Fast alle Treiber können die Default-Implementation eth_header_cache verwenden.
Diese Methode aktualisiert nach einer Änderung die Zieladresse in der Struktur hh_cache. Ethernet-Geräte verwenden eth_header_cache_update.
> > Die Methode hard_header_parse extrahiert die Quell-Adresse des in skb enthaltenen Pakets und kopiert sie in einen Puffer ab haddr. Der Rückgabewert der Funktion ist die Länge dieser Adresse. Ethernet-Geräte verwenden normalerweise eth_header_parse.
Die Datenfelder, die in struct net_device noch übrig sind, werden von der Schnittstelle zum Abspeichern von Status-Informationen verwendet. Einige dieser Felder werden von ifconfig und netstat verwendet, um dem Benutzer Informationen über die aktuelle Konfiguration zu liefern. Eine Schnittstelle sollte daher diesen Feldern passende Werte zuweisen.
Diese beiden Felder sollten einen Jiffy-Wert enthalten. Der Treiber muß diese Werte beim Start einer Übertragung und beim Empfang eines Paketes aktualisieren. Das Feld trans_start kann darüber hinaus vom Netzwerk-Subsystem verwendet werden, um Blockaden des Transmitters zu entdecken. last_rx wird derzeit nicht verwendet, der Treiber sollte dieses Feld aber trotzdem pflegen, um auf zukünftige Versionen vorbereitet zu sein.
Die Minimumzeit (in Jiffies), die vergehen sollte, bevor die Netzwerk-Schicht entscheidet, daß ein Übertragungs-Timeout aufgetreten ist und die tx_timeout-Funktion des Treibers aufruft.
Das Äquivalent von filp->private_data. Der Treiber kann über diesen Zeiger nach Belieben verfügen. Üblicherweise enthält die private Datenstruktur ein struct net_device_stats-Element. Das Feld wird weiter unten in “the Section called Jedes Gerät initialisieren” beschrieben.
Diese beiden Felder werden in Multicast-Übertragungen benutzt. mc_count enthält die Anzahl der Elemente in mc_list. Ausführliche Informationen finden Sie in in“the Section called Multicasting”.
Das xmit_lock wird verwendet, um mehrfache gleichzeitige Aufrufe der Funktion hard_start_xmit des Treibers zu verhindern. xmit_lock_owner ist die Nummer der CPU, die die Sperre xmit_lock hält. Der Treiber sollte diese Felder nicht verändern.
Das Modul, das diese Geräte-Struktur “besitzt”. Dieses Feld wird verwendet, um den Verwendungszähler des Moduls zu pflegen.
Es gibt noch weitere Felder in struct net_device, die aber nicht von Treibern verwendet werden.