Mittwoch, 5. Dezember 2007

Versenden eines Ethernet - Frames mit richtiger Frame Check Sequence

Problem
Als ich in einer Projektarbeit einen „Rapid Spanning Tree“ - Treiber für die Linux – Bridge implementierte hatte ich Probleme ein Ethernet – Frame korrekt nach Standard zu versenden. Da im Internet und in Büchern nur spärlich Informationen hierzu vorhanden sind werde ich zwei wichtige Erkenntnisse in diesem Post besonders hervorheben; Die Länge eines Ethernet – Frames und das korrekte Erstellen einer Frame Check Sequence (FCS).

Länge
Im IEEE 802.3 Standard ist definiert, dass ein Frame mindestens 64 Byte gross sein muss. Ist das zu versendende Packet nun kürzer, muss der Rest mindestens bis auf 64 Byte gepaddet werden. In meiner Variante wird einfach herumliegender Speicher gelesen, was einwenig unschön ist. Ich gebe es ja zu. ;)


// Length of a Frame
size = length + 2*ETH_ALEN + 2 + 4; // + 4 wegen CRC-32
if (size < 64) size = 64; // Ethernet Standard min 64 Byte

Im Sourcefile br_stp_bpdu.c Version 1.3 des 2.6er Kernels ist dies nicht korrekt implementiert.


Frame Check Sequence
Über die Art und Weise wie und über welche Teile eines Ethernet – Frames eine FCS tatsächlich generiert werden muss ist es schwierig Informationen zu erhalten. Meine Lösung überprüfte ich mit dem Programm Wireshark Version 0.99.5. Und zwar wird die FCS über den gesamten Datenbereich des sk_buff berechnet, natürlich exklusive der CRC.


// IEEE 802.3 CRC-32 (ganzes Ethernet-Packet ohne die CRC selbst)
fcs = ~(crc32_le(~0, skb->data, skb->len - 4));

In der besagten Version der Linux – Bridge wird keine FCS angehängt.


Sourcecode der gesamten Funktion
Hier noch das Listing der gesamten Funktion, damit der Leser an einem konkreten Beispiel sehen kann, wie ein Ethernet – Frame verschickt werden kann.

In dem „Rapid Spanning Tree Protocol“ (RSTP) werden zur Kommunikation zwischen den Bridges sogenannte „Bridge Protocol Data Units“ (BPDU) verschickt. In dieser Funktion wird ein BPDU in ein Ethernet – Frame gepackt. Der Funktion wird das entsprechende Portdevice, das Datenfeld und die Länge des Datenfeldes übergeben. Mit dem NF_HOOK – Makro übergibt man das fertige Frame dem Kernel.

static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int length)
{
struct net_device *dev;
struct sk_buff *skb;
int size;
u32 fcs;

if (!p->br->stp_enabled)
return;

// Length of a Frame
size = length + 2*ETH_ALEN + 2 + 4; // + 4 wegen CRC-32
if (size < 64) size = 64; // Ethernet Standard min 64 Byte

dev = p->dev;

if ((skb = dev_alloc_skb(size)) == NULL) {
printk(KERN_INFO "br: memory squeeze!\n");
return;
}

skb->dev = dev;
skb->protocol = htons(ETH_P_802_2);
skb->mac.raw = skb_put(skb, size);
memcpy(skb->mac.raw, bridge_ula, ETH_ALEN);
memcpy(skb->mac.raw+ETH_ALEN, dev->dev_addr, ETH_ALEN);
skb->mac.raw[2*ETH_ALEN] = 0;
skb->mac.raw[2*ETH_ALEN+1] = length;
skb->nh.raw = skb->mac.raw + 2*ETH_ALEN + 2;
memcpy(skb->nh.raw, data, length);

// - 4 wegen CRC-32
memset(skb->nh.raw + length, 0xa5, size - length - 2*ETH_ALEN - 2 - 4);

// IEEE 802.3 CRC-32 (ganzes Ethernet-Packet ohne die CRC selbst)
fcs = ~(crc32_le(~0, skb->data, skb->len - 4));

memcpy(skb->nh.raw + length + (size - length - 2*ETH_ALEN - 2 - 4), &fcs, 4);

NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, dev_queue_xmit);
}

Keine Kommentare: