Forum: Mikrocontroller und Digitale Elektronik IP-Stack bauen


von Tobias P. (hubertus)


Lesenswert?

Hallo Leuts,
ich habe gerade mein LPC2478 Experimentierboard fertiggestellt und 
bastle jetzt ein bisschen Software.
Im Moment befasse ich mich mit dem Ethernet-MAC, welche ich mittlerweile 
auch zum Laufen gebracht habe. Ich kann erfolgreich Pakete senden und 
empfangen, es funktioniert bestens! Die ganze Geschichte initialisiert 
sich von selber via einen Interrupt, sobald ein LAN-Kabel eingesteckt 
wird und Geschwindigkeit sowie Duplex und das ganze Gerümpel wird 
erkannt und der MAC dementsprechend konfiguriert. Soweit so gut.
Nun brauche ich ja, um sinnvolle Daten zu senden und zu empfangen, einen 
IP-Stack. Zwar wäre es wohl möglich, den von Grund auf selber zu bauen, 
aber mir erscheint dies reichlich fehleranfällig und schwierig. Also 
nimmt man am besten wohl was fertiges. Könnt ihr eine bestimmte Software 
empfehlen? von lwIP liest man immer wieder, aber es gibt ja auch noch 
diverse andere Stacks.
Welcher ist am einfachsten an die Zielhardware anzupassen?

Was das Teil auf jeden Fall können sollte:

- DHCP
- (S)NTP
- UDP
- IPv4
- evtl. TCP.

Der Rest ist nice to have, aber im Moment nicht notwendig.
Hat wer Tipps?

von Tobias P. (hubertus)


Lesenswert?

Ein Problem ist mir noch gerade aufgefallen:
Mein Compiler (ich verwende eine Testversion von IAR EWARM) scheint mir 
manchmal meine Variablen zu zerschiessen.
Mein Testprogramm macht folgendes: in einer Endlosschleife wird ein 
Paket gesendet, welches neben der Ziel- und der Quell-MAC-Adresse nur 
den Text "Hallo Welt, diese Nachricht wurde vom LPC2478 gesendet" 
enthält.
In Wireshark wird zu ca. 60% der richtige Text empfangen. In allen 
anderen Fällen lautet der empfangene Text "Hallo Welt, diese N.cropw 
urde vom LPC2478 gesendet".
Wo der Fehler wohl liegt? Ich konnte es auch nach mehrstündigem 
Debugging nicht herausfinden. Der Stack läuft nie über, auch 
irgendwelche spurious Interrupts kann ich ausschliessen.
Ich habe dann einen Daten-Breakpoint auf meine Variable, welche diesen 
Test-Text enthält, gesetzt. Und siehe da: mein eigener Programmcode, vom 
Compiler übersetzt, scheint diesen Text manuell zu verändern!
Dabei besteht der Code nur aus zwei Stellen, wo diese Variable verwendet 
wird:

1. Definition:
char test[] = "Hallo Welt, diese Nachricht wurde vom LPC2478 gesendet"

2. Zugriff
fSendPacket(test, sizeof(test)),
wobei die Funktion fSendPacket folgendes macht:

while(packet_size)
{
  *dest_buffer = *src_buffer;
  dest_buffer++;
  src_buffer++;
  packet_size--;
}

es wird auf die Quelle (in dem Fall die Variable test) nur lesend 
zugegriffen, und die Variable wird an KEINER EINZIGEN anderen Stelle im 
Code erwähnt. Aber WARUM ändert sie sich, und WARUM kommt mein Text 
falsch an? Solange ich den Debugger im Einzelschrittmode laufen lasse, 
funktioniert alles wie gewünscht. Sobald ich aber auf "Run" klicke, 
schlägt sofort mein Daten-Breakpoint zu, und die Variable verändert sich 
scheinbar willkürlich. Kann mir wer weiterhelfen?

von Tobias P. (hubertus)


Lesenswert?

Hat keiner was zu sagen?

Ich habe mir mal lwIP runtergeladen und versuche grade, den compiliert 
zu bekommen. Ich bin mir aber überhaupt nicht sicher, welche files ich 
alle brauche. Was ist die absolute Minimalkonfiguration?
Und eine Doku vermisse ich auch beim lwIP.

Grundsätzlich möchte ich einen Stack, der alle wichtigen, gängigen 
Protokolle beherrscht.
Notfalls implementiere ich sowas halt selber, aber im Moment fehlt mir 
ein kluger Ansatz.

von sonstwer (Gast)


Lesenswert?

Ich habe lwIP erfolgreich mit einem AT32UC3B zum laufen bekommen.

lwIP ist sehr vielseitig, und funktioniert bei mir bestens. Man sollte 
allerdings so ca. 15 - 20 kB SRAM entbehren können. Das ist aber auch 
von der Datenrate abhängig. Gut möglich, dass man bei Datenraten im 
RS232-Bereich auch mit wesentlich weniger hinkommt.

von Martin (Gast)


Lesenswert?

... Hat keiner was zu sagen? ...

Nö, alle verheiratet.

von sonstwer (Gast)


Lesenswert?

Ich vergaß zu erwähnen, dass dabei der Netzwerkzugang via WLAN aufgebaut 
wurde. Als Chipsatz diente hier der HDG104 von H&D Wireless.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Tobias Plüss schrieb:

> wobei die Funktion fSendPacket folgendes macht:
>
> while(packet_size)
> {
>   *dest_buffer = *src_buffer;

Wie groß ist der Buffer, den Du mittels dest_buffer füllst?

> es wird auf die Quelle (in dem Fall die Variable test) nur lesend
> zugegriffen, und die Variable wird an KEINER EINZIGEN anderen Stelle im
> Code erwähnt. Aber WARUM ändert sie sich, und WARUM kommt mein Text
> falsch an?

Ist die Definition von char test[] global oder lokal? Durch 
Überschreiten von Buffergrenzen können sich Inhalte von Strings durchaus 
ändern, das liegt daran, wie die Variablen im RAM (globale Variable) 
oder auf dem Stack (lokale Variable) hintereinanderliegen. Es muss auch 
nicht an der Funktion fSendPacket() selbst liegen, das Überschreiben 
kann an ganz anderer Stelle im Code vorkommen.

Merkwürdig ist allerdings, dass die Änderung mitten im String 
stattfindet. Normalerweise wird sie durch eine Bufferüberschreitung eher 
von vorn "aufgerollt". Noch merkwürdiger ist, dass sich die Nachricht 
durch diese Änderung verkürzt! Das Fragment "achricht " (9-Zeichen) 
wurde durch ".crop" (5 Zeichen) ersetzt, ist das wirklich korrekt? Schau 
Dir besser einen Hexdump an - auch im Wireshark. Wahrscheinlich sind da 
noch 4 nicht-druckbare Zeichen "versteckt".

Ein kompletter Code wäre da hilfreicher als ein Code-Schnippsel. Ich bin 
mir ziemlich sicher dass die Ursache des Problems nicht in den gezeigten 
Codezeilen zu finden ist. Checke bitte auch sämtliche Buffergrößen auf 
Überschreiten ab.

von Tobias P. (hubertus)


Lesenswert?

Hallo,
ich bin jetzt dabei, selber einmal eine kleine Implementation eines 
Stacks zu beginnen.
Die Ethernet-Frames werden jetzt schon mal empfangen und in einem 
Empfangsbuffer abgelegt. Danach wird ermittelt, um welches Protokoll es 
sich handelt (IP, ARP usw.).
Und anschliessend wird die jeweilige Funktion aufgerufen, welche das 
Paket weiter verarbeitet.
Es funktioniert schon mal so halbwegs, aber ich habe immer noch das 
Problem, dass mir manchmal gewisse Variablen-Inhalte zerschossen werden, 
und ich weiss nicht wieso. Da die Übertragung der Daten vom Phy zum 
LPC2468 via DMA läuft, habe ich so langsam das Gefühl, dass da was nicht 
richtig läuft.
Ich habe jetzt ein neues Testprojekt angelegt. Ich habe die Interrupts 
zwar eingeschaltet, da sie benötigt werden fürs Ethernet. Aber ich habe 
ausser meines MAC-Moduls keine weiteren Interruptfunktionen am Laufen.
Des weiteren ist die Stack-Grösse auf 0x8000 Bytes festgelegt, was 
_reichlich_ langen sollte. Trotzdem werden manchmal Variablen 
überschrieben.
Was auch interessant ist:

Ich habe ein Testprogramm gebastelt, das in einer Endlosschleife auf den 
Empfang eines neuen Pakets wartet.
Kommt dieses an, dann wird der Pakettyp geprüft. Ist es ein UDP-Paket, 
schalte ich eine rote LED ein, ist es ein ICMP-Paket wird die grüne LED 
eingeschaltet, und bei einem TCP-Paket werden alle LEDs wieder 
ausgeschaltet. Nun lasse ich auf meinem Notebook ein Programm zum 
generieren von Paketen laufen (Colasoft PacketBuilder).
Das generiert mir in einer Endlosschleife diese 3 Pakete und versendet 
sie an mein Board. Dann sehe ich die LEDs schön blinken, es scheint also 
zu funktionieren. ABER! nach einigen tausend Paketen resettet sich das 
Board einfach und der LPC2468 hängt im prefetch_abort oder data_abort 
fest.

von Tobias P. (hubertus)


Angehängte Dateien:

Lesenswert?

Anbei findet ihr noch meinen Code zur Initialisierung des MAC.
Ich hoffe, wenns einen Fehler hat, steckt der da drin. Ich sehe wohl 
mittlerweile den Wald vor lauter Bäumen nicht mehr...

von Tobias Plüss (Gast)


Lesenswert?

Hallo,
so ich habe nun mal den MAC usw. zum Laufen bekommen. Ein Register war 
nicht richtig gesetzt, deshalb hat der Datentransfer manchmal 
funktioniert und manchmal nicht.

Ich habe mich jetzt mal daran gemacht, selber ein bisschen mit den 
Ethernet-Paketen rumzuexperimentieren. Ich habe im main folgenden Code:
1
char buffer[1500];
2
3
while(1)
4
{
5
  process_packets();
6
}
7
8
void process_packets(void)
9
{
10
  int size = MAC_fReceivePacket(buffer);
11
  struct ethernetheader *header;
12
  if(size)
13
  {
14
    header = (struct ethernetheader*)buffer;
15
    switch(header->type)
16
    {
17
      case TYPE_IP:
18
        process_ip_packet(buffer, size);
19
      case TYPE_ARP:
20
        process_arp_packet(buffer, size);
21
    }
22
  }
23
}

das funktioniert auch ganz ordentlich, ich habe bereits mal ARP 
vollständig implementiert inclusive eines ARP-Cache. ICMP ist auch 
implementiert. Ich kann nun bereits ARP Requests an mein Board senden, 
und die werden dann auch korrekt beantwortet. Wenn ich es anpinge, kommt 
auch eine Antwort zurück. Soweit schon mal gut. Es funktioniert also 
Problemlos, Pakete zu beantworten. Jetzt möchte ich aber gern DHCP 
implementieren. Dazu muss ich ja erst die DHCPDISCOVER-Nachricht 
verschicken und dann auf eine Antwort warten - wenn möglich mit einem 
Timeout. Doch wie kann ich das hier realisieren? Wie ihr seht, werden 
die Pakete nur im Mainloop abgearbeitet, nachdem der MAC gepollt wurde, 
ob überhaupt Pakete vorhanden sind. Wenn ich jetzt aber eine 
DHCPO-funktion schreiben möchte, die auf die Antwort vom Router wartet - 
wie gehe ich das am besten an? Die Funktion muss ja irgendwo her wissen, 
ob der MAC Daten erhalten hat. Und das kann sie nur wissen, wenn vorher 
die Funktion process_packets aufgerufen worden ist, welche die Pakete 
der Reihe nach durch die Stacks durch reicht. Wie macht man das richtig? 
Es würde sicherlich funktionieren, die Abarbeitung der Pakete in einem 
Interrupt zu machen, aber das gefällt mir ganz und gar nicht, denn die 
Bearbeitungszeit dieser Pakete ist undefiniert und dauert möglicherweise 
zu lange.

Irgendwie stehe ich grade auf dem Schlauch. Kann mir wer weiterhelfen?

von andy (Gast)


Lesenswert?

es gibt auch noch den uIP-Stack. Ist bekannter als lwIP. Es gibt auch 
noch einen Stack von Microchip. Den kann ich sehr empfehlen, da er sehr 
gut dokumentiert und sauber geschrieben ist, und weil du deinen Stack ja 
jetzt selber schreibst, und eine gute Vorlage wäre von Vorteil.

Wenn du hier im Forum das Stichwort "Webserver" eingibst, wirst du auf 
eine Menge anderer nützlicher Projekte stoßen.

von Sascha W. (sascha-w)


Lesenswert?

Tobias Plüss schrieb:
> Wenn ich jetzt aber eine
> DHCPO-funktion schreiben möchte, die auf die Antwort vom Router wartet -
> wie gehe ich das am besten an? Die Funktion muss ja irgendwo her wissen,
> ob der MAC Daten erhalten hat.
??Warum?? - trenne die Anforderung und die Auswertung
aus Main sendest du den DHCPDISCOVER, dann beendet sich diese Funktion.
Die Antwort wertest du mit dem Stack aus, der dann die benötigten Daten 
aus dem Packet zieht. Wenn du in der Main wissen willst ob es 
funktioniert hat setze ein Flag, für einen Timeout nimm einen Timer oder 
einen per Timer gesteuerten Counter.


Sascha

von Tobias P. (hubertus)


Lesenswert?

Sascha,
ja das war jetzt ein einfaches Beispiel. In der Tat könnte man das 
Problem so lösen.
Anders gestaltet es sich aber, wenn ich z.B. von meinem Board aus einem 
Rechner ein IP-Paket schicken möchte, von dem ich im Moment nur die 
IP-Adrese kenne. Ich weiss, dass der Zielrechner die IP 192.168.1.13 
hat. Um ein Ethernet-Paket zu generieren, muss ich aber erst die 
MAC-Adresse rausfinden - sprich, ich muss erst einen ARP-Request senden, 
auf die ARP-Antwort mit einem Timeout warten, und erst dann kann ich 
fortfahren, das IP-Paket zu erstellen und zu senden.

von Sascha W. (sascha-w)


Lesenswert?

ist auch kein Problem, hier mal ein Lösungsansatz:

nehmen wir mal an ...
von MAIN wird jede Sekunde eine Funktion data_send aufgerufen
__________
zum Zeitpunkt X willst du eine Verbindung zu einem externen Rechner 
aufbauen also speicherst du IP_Adresse, Port usw. in entsprechenden 
Variablen. Dann wird sendstatus=1
__________
data_send:
? sendstatus=0
wenn JA =>return  ;nix zu tun

? sendstatus<10
wenn JA
   ? schauen ob zur IP-Addr schon eine MAC im APR-Cache liegt
   wenn JA => sendstatus=11
   wenn NEIN
     ? sendstatus=1
     wenn JA   => ARP-Request senden
     sendstatus++

? sendstatus=10
wenn JA => sendstatus=0 ;ARP-Timeout

? sendstatus<20
wenn JA
   ? TCP-Verbindung schon aufgebaut (im Stack Eintrag suchen)
   wenn JA => sendstatus=21
   wenn NEIN
      ? sendstatus=11
      wenn JA => TCP-Verbindungsaufbau anstossen
      sendstatus++

? sendstatus=20
wenn JA => sendstatus=0 ;TCP-Timeout, Verbindung nicht aufgebaut

usw.

Sascha

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.