next up previous contents
Next: Netzwerkgeräte Up: Netzwerkkommunikation Previous: TCP/UDP   Contents

IP

Die IP-Schicht stellt einen allgemeinen ''Paketübermittlungsdienst'' zur Verfügung. Man übergibt ihr ein Paket und einen Adressaten und sie kümmert sich um die Übermittlung. Es wird nicht garantiert, dass das Paket auch ankommt. Dafür sind dann übergeordnete Schichten wie z.B. TCP zuständig.

Als wichtigste Datenstruktur der Implementierung der IP-Schicht im Linux Kernel ist die struct iphdr [include/linux/ip.h]:

struct iphdr { 
#if defined(__LITTLE_ENDIAN_BITFIELD) 
  __u8 ihl:4, 
  version:4; 
#elif defined (__BIG_ENDIAN_BITFIELD) 
  __u8 version:4, 
  ihl:4; 
#else 
#error "Please fix <asm/byteorder.h>" 
#endif 
  __u8 tos; 
  __u16 tot_len; 
  __u16 id; 
  __u16 frag_off; 
  __u8 ttl; 
  __u8 protocol; 
  __u16 check; 
  __u32 saddr; 
  __u32 daddr; 
  /*The options start here. */ 
};
Man kann die Aufgaben der IP-Schicht in das Empfangen, das Weiterleiten und Senden von IP-Paketen unterteilen. Dabei sieht der Ablauf wie folgt aus (alles IPv4):

Empfangen:

  1. Erhalt des Pakets

    Mit der Funktion 
    int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
    [net/ipv4/ip_input.c] empfängt die IP-Schickt einen Puffer skb vom Netzwerkgerät dev der darunter liegenden Schicht.

  2. Überprüfen des IP-Headers

    RFC 1122 konform wird in der obigen Funktion geprüft ob:
    1. die angegebene Länge des Paktes gültig ist (mind. die Länge eines IP Headers)
    2. die IP-Versionsnummer korrekt ist (IPv4 == 4)
    3. die IP Checksumme stimmt

  3. Vergleich der Zieladresse mit der eigenen Adresse

    Ist das Paket nicht für den lokalen Rechner, dann wird es weitergeleitet. (In Funktion
    ip_rcv_finish(struct sk_buff *skb), Abfrage von
    ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)
    Ist es für den lokalen Rechner wird
    int ip_local_deliver(struct sk_buff *skb) 
    aufgerufen, sonst 
    int ip_forward(struct sk_buff *skb)
    [net/ipv4/ip_forward.c]

  4. Defragmentierung des IP-Pakets

    Wurde das Paket nicht weitergeleitet, wird es per ip_local_deliver() auf dem Host weiterverarbeitet. Ist das Paket ein IP-Fragment, wird versucht mit Hilfe schon empfangender Pakete es zu defragmentieren. Ist das noch nicht möglich (weil noch Fragmente fehlen), dann wird das Paket in einer Fragmentliste gespeichert. Dies ist durch die Funktion struct sk_buff *ip_defrag(struct sk_buff *skb) [net/ipv4/ip_fragment.c] implementiert.

  5. Übergabe des Pakets an das nächste Protokoll

    Die Funktion ip_rcv_finish() ruft die Funktion skb->dst->input(skb) auf, welche vorher auf den Funktions-Zeiger der entsprechenden input Funktion der höheren Transportschicht gesetzt wurde.
Weiterleiten:

  1. Überprüfen des ttl Feldes im IP-Header

    Ist der Wert kleiner oder gleich 1, wird das Paket verworfen und eine entsprechende ICMP-Nachricht an den Absender geschickt.
    (in int ip_forward(struct sk_buff *skb) [net/ipv4/ip_forward.c])
    Wirklich dekrementiert wird das TTL Feld erst später.

  2. Überprüfen des Pakets auf fehlerhaftes/ungewolltes Routing

    (zum Beispiel wenn ''strict routing'' verlangt wird und das Ziel kein Gateway ist)

  3. Kopieren des Paket-Headers

    Da im folgenden der IP-Header verändert wird, muss der Header des sk_buff kopiert werden (copy on write, Funktion skb_cow() in [include/linux/skbuff.h]).

  4. Setzen/Ändern von IP Optionen

    In int ip_forward_finish(struct sk_buff *skb) [net/ipv4/ip_forward.c] Aufruf von
    void ip_forward_options(struct sk_buff *skb) [net/ipv4/ip_options.c]

  5. Senden

    In int ip_forward_finish(struct sk_buff *skb) [net/ipv4/ip_forward.c] Aufruf von
    int ip_send(struct sk_buff *skb) (als static inline in [include/net/ip.h] implementiert).
    Dort wird vorher überprüft ob das Paket die MTU Grösse überschritten hat, und dann entweder per
    int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) 
    fragmentiert oder gleich per 
    int ip_finish_output(struct sk_buff *skb) 
    verschickt. (beide Funktionen implementiert in [net/ipv4/ip_output.c])
Senden:

  1. Erhalt des Pakets

    (z.B. durch Aufruf der Funktion int ip_queue_xmit(struct sk_buff *skb) [net/ipv4/ip_output.c])

  2. Ermitteln der Ziel-Route

    Aufruf der Funktion ip_route_output(&rt, daddr, sk->saddr, RT_CONN_FLAGS(sk), sk->bound_dev_if) [include/net/route.h] um das Netzwerkgerät zu ermitteln, über welches das Paket verschickt werden soll.

  3. Füllen des IP-Headers

    Mit Hilfe der Funktion unsigned char *skb_push(struct sk_buff *skb, unsigned int len) [include/linux/skbuff.h] wird am Anfang des sk_buff Puffers Platz für einen IP-Header geschaffen. Dieser wird dann mit den entsprechenden Werten wie Quell- und Ziel-Adresse gefüllt.

  4. Fragmentierung des IP-Pakets

    In int ip_queue_xmit2(struct sk_buff *skb) wird überprüft ob das Paket die MTU Grösse überschritten hat. Ist das der Fall, dann wird es mit Hilfe der Funktion int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) [net/ipv4/ip_output.c] in mehrere Teile zerlegt.

  5. Setzen der IP-Checksumme

    Aufruf der Funktion void ip_send_check(struct iphdr *iph) [net/ipv4/output.c], welche die Checksumme berechnet und im IP-Header einträgt.

  6. Übergabe an das entsprechende Netzwerkgerät

    Durch den Aufruf von skb->dst->output(skb) wird die dev_queue_xmit() des Netzwerkgerätes aufgerufen, welches sich dann um das Verschicken des Paktes kümmert.


next up previous contents
Next: Netzwerkgeräte Up: Netzwerkkommunikation Previous: TCP/UDP   Contents
2002-02-17