Forum: Mikrocontroller und Digitale Elektronik lwip auf Microcontroller akzeptiert UDP packet nicht (Port Unreachable)


von Tom (Gast)


Angehängte Dateien:

Lesenswert?

Hallo miteinander

Ich möchte mittels UDP zwischen einem TMS320F28388D Microcontroller 
(192.168.0.4) und einem Linux PC (192.168.0.1) kommunizieren habe aber 
das Problem, dass der uC auf welchem der lwip stack läuft, nicht richtig 
auf den Empfang des UDP Packets reagiert.

Die Sockets auf beiden Seiten sind mit dem Port 3030 verbunden.

Wenn der Linux PC das erste Packet sendet, antwortet der lwip stack mit 
einer ICMP Nachricht "Destination unreachable (Port unreachable)", 
obwohl ich eigtl. einen Socket auf dem Mikrocontroller erzeuge, der mit 
diesem Port verbunden sein sollte.

Hat vielleicht jemand eine Idee, wo mein Problem liegen könnte?

Nachfolgend mein Code:

Aufsetzen des UDP sockets:
1
const u16_t COMM_PORT=3030;
2
struct udp_pcb* current_udp_pcb;
3
ip_addr_t IPAddr_remote={0xC0A80001};     //ip address of the remote pc
4
     
5
void setupCommInterface(void){
6
    //create a new udp pcb
7
    current_udp_pcb = udp_new();
8
    //bind the pcb to the port comm_port
9
    //this receives on port COMM_PORT from any IP address
10
    err_t err=udp_bind(current_udp_pcb,IP_ANY_TYPE,COMM_PORT);
11
    if(err!=ERR_OK){
12
        if(err==ERR_USE){
13
            while(1){}
14
        }
15
        else if(err==ERR_VAL){
16
            while(1){}
17
        }
18
        while(1){}
19
    }
20
    //set the remote IP address and port for sending data via UDP
21
    err=udp_connect(current_udp_pcb,&IPAddr_remote,COMM_PORT);
22
23
    udp_recv(current_udp_pcb,udp_recv_cb,NULL);
24
}

Definition des UDP Callbacks:
1
bool command_available=false;
2
uint8_t buffer[2048];
3
void udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port){
4
    // -- process the data in p --
5
    memcpy(buffer,p->payload,p->len);
6
    u16_t bytes_read=p->len;
7
    // TODO : check size to ensure that an entire command has been received
8
    //indicate that a new command is available for processing in the buffer
9
    command_available=true;
10
    pbuf_free(p);
11
    return;
12
}

Main Methode
1
int main(void)
2
{
3
    unsigned long ulUser0, ulUser1;
4
    unsigned char pucMACArray[8];
5
    //
6
    // User specific IP Address Configuration.
7
    // Current implementation works with Static IP address only.
8
    //
9
    unsigned long IPAddr = 0xC0A80004;
10
    unsigned long NetMask = 0xFFFFFF00;
11
    unsigned long GWAddr = 0x00000000;
12
    //
13
    // Initializing the CM. Loading the required functions to SRAM.
14
    //
15
    CM_init();
16
    SYSTICK_setPeriod(systickPeriodValue);
17
    SYSTICK_enableCounter();
18
    SYSTICK_registerInterruptHandler(SysTickIntHandler);
19
    SYSTICK_enableInterrupt();
20
    //
21
    // Enable processor interrupts.
22
    //
23
    Interrupt_enableInProcessor();
24
        
25
    // Set user/company specific MAC octets
26
    // (for this code we are using A8-63-F2-00-00-80)
27
    // 0x00 MACOCT3 MACOCT2 MACOCT1
28
    ulUser0 = 0x00F263A8;
29
    // 0x00 MACOCT6 MACOCT5 MACOCT4
30
    ulUser1 = 0x00800000;
31
    //
32
    // Convert the 24/24 split MAC address from NV ram into a 32/16 split MAC
33
    // address needed to program the hardware registers, then program the MAC
34
    // address into the Ethernet Controller registers.
35
    //
36
    pucMACArray[0] = ((ulUser0 >>  0) & 0xff);
37
    pucMACArray[1] = ((ulUser0 >>  8) & 0xff);
38
    pucMACArray[2] = ((ulUser0 >> 16) & 0xff);
39
    pucMACArray[3] = ((ulUser1 >>  0) & 0xff);
40
    pucMACArray[4] = ((ulUser1 >>  8) & 0xff);
41
    pucMACArray[5] = ((ulUser1 >> 16) & 0xff);
42
    //
43
    // Initialize ethernet module.
44
    //
45
    Ethernet_init(pucMACArray);
46
    //
47
    // Initialze the lwIP library, using static IP addresses.
48
    //
49
    lwIPInit(0, pucMACArray, IPAddr, NetMask, GWAddr, IPADDR_USE_STATIC);
50
    //
51
    // Initialize the ccard communication interface
52
    //
53
    setupCommInterface();
54
    //
55
    // Loop forever. All the work is done in interrupt handlers.
56
    //
57
    while(1){
58
        if(command_available){
59
            processCommand();
60
            command_available=false;
61
        }
62
    }
63
}

von (prx) A. K. (prx)


Lesenswert?

Bei TCP/IP-APIs müssen IP-Adressen und Ports oft in "network byte order" 
angegeben werden. Ob im lwip auch weiss ich nicht. Dazu gibts 
üblicherweise sowas wie htons().

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

die Portangabe sollte richtig sein. Ist für diese Portierung noch eine 
ISR nötig? Oder muss das lwip_process() zyklisch im main aufgerufen 
werden?

von Stefan F. (Gast)


Lesenswert?

Tom schrieb:
> Ich möchte mittels UDP kommunizieren
> Die Sockets auf beiden Seiten sind mit dem Port 3030 verbunden.

Das kann nicht sein. Sockets (Verbindungen) gibt es nur bei TCP.

UDP ist wie Postkarten bei der Deutschen Post. Du schickst ein UDP Paket 
irgendwohin und hoffentlich kommt es an. Vielleicht auch nicht. Du wirst 
es nie erfahren.

Das heißt, die Meldung "Destination unreachable (Port unreachable)" 
halte ich für unmöglich.

Zum Testen empfehle ich dir das Programm "netcat". Damit kannst du 
sowohl TCP als auch UDP in beide Richtungen testen. Beispiel:

In einem Fenster starte ich einen UDP Listener:
1
sfrings@stefanspc:~$ nc -l -u -p 3000

In einem anderen Fenster sende ich ein UDP Paket:
1
sfrings@stefanspc:~$ nc -u localhost 3000
2
Hallo

Ergebnis im ersten Fenster:
1
sfrings@stefanspc:~$ nc -l -u -p 3000
2
Hallo

Gegentest: Sende etwas an einen "falschen" Port:
1
sfrings@stefanspc:~$ nc -u localhost 3001
2
Möööp

Du bekommst keine Fehlermeldung. Das Paket landet im Nirvana. Das ist 
bei UDP so beabsichtigt.

Ich möchte nochmal auf meinen obigen Satz zurück kommen:
> Das kann nicht sein. Sockets (Verbindungen) gibt es nur bei TCP.

Bei der uIP Biblipthek (vom selben Author) wird der Begriff "connection" 
im Kontext von UDP missbraucht. Man tut so, als würde man eine 
Verbindung zu einem partner aufbauen. Tatsächlich hat man aber nur 
festgelegt, auf welchem Port man künftig etwas empfangen will, bzw. 
wohin man etwas Senden will. Das "öffnen" einer UDP Verbindung findet 
nur im RAM des Mikrocontrollers statt, dabei findet noch keine 
Kommunikation auf dem Kabel statt.

Mir ist klar, dass damit dein Problem nicht gelöst ist. Aber ich hoffe, 
dass diese Info dir hilft, das UDP Protokoll zu verstehen damit du das 
Problem gezielter weiter untersuchen kannst.

von c-hater (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:

> Das kann nicht sein. Sockets (Verbindungen) gibt es nur bei TCP.

Das stimmt natürlich nicht. Sockets sind zwar irgendwie schon 
"Verbindungen", aber nicht im Sinne einer TCP-Verbindung, wie du 
scheinbar glaubst.

Sockets sind eine Abstraktion und können dann Ausprägungen in 
verschiedene konkrete Typen haben. Such' einfach mal mit Google nach 
"SocketType" und staune, wieviele (durchaus verschiedene) Enumerationen 
du findest. Das zeigt ganz klar, dass dieses allgemeine Socket-Konzept 
in vielen Sprachen und Frameworks verwendet wird.

Bei lwip (um was es im Thread ging), heißt das ein wenig anders als 
üblich, aber es ist das gleiche Konzept wie üblich. Siehe sockets.c ab 
Zeile 158.
1
/* Socket protocol types (TCP/UDP/RAW) */
2
#define SOCK_STREAM     1
3
#define SOCK_DGRAM      2
4
#define SOCK_RAW        3

von c-hater (Gast)


Lesenswert?

c-hater schrieb:

> üblich, aber es ist das gleiche Konzept wie üblich. Siehe sockets.c

Ist natürlich socket.h, nicht socket.c.

von Hmmm (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Tom schrieb:
>> Ich möchte mittels UDP kommunizieren
>> Die Sockets auf beiden Seiten sind mit dem Port 3030 verbunden.
>
> Das kann nicht sein. Sockets (Verbindungen) gibt es nur bei TCP.
>
> UDP ist wie Postkarten bei der Deutschen Post. Du schickst ein UDP Paket
> irgendwohin und hoffentlich kommt es an. Vielleicht auch nicht. Du wirst
> es nie erfahren.
>
> Das heißt, die Meldung "Destination unreachable (Port unreachable)"
> halte ich für unmöglich.

Och Stefan...

Auch bei UDP unterscheidet der IP-Stack, ob auf dem jeweiligen Port 
etwas lauscht (was per bind() passiert), und wenn nicht, geht die o.g. 
ICMP-Message zurück.

$ echo "test" | nc -u 127.0.0.1 12345

16:48:13.581316 IP 127.0.0.1.55734 > 127.0.0.1.12345: UDP, length 5

16:48:13.581398 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 udp port 12345 
unreachable, length 41

Stefan ⛄ F. schrieb:
> Bei der uIP Biblipthek (vom selben Author) wird der Begriff "connection"
> im Kontext von UDP missbraucht. Man tut so, als würde man eine
> Verbindung zu einem partner aufbauen. Tatsächlich hat man aber nur
> festgelegt, auf welchem Port man künftig etwas empfangen will, bzw.
> wohin man etwas Senden will.

Da wird nichts missbraucht, so ist es im POSIX-Umfeld bei connect() 
auch:

https://pubs.opengroup.org/onlinepubs/009695399/functions/connect.html

Dort wird natürlich keine Verbindung im Sinne eines Handshakes 
hergestellt, sondern dafür gesorgt, dass zum einen per send() statt 
sendto() verschickte Pakete dort hingehen und zum anderen nur noch 
Pakete angenommen werden, die von dieser Gegenstelle kommen.

von Stefan F. (Gast)


Lesenswert?

Ok, streichen wir die Sockets aus meiner Antwort.

Ich bleibe dabei, dass UDP keine Verbindungen kennt. Ob ein gesendetes 
Paket den Empfänger erreicht hat und ob der Empfänger empfangsbereit 
war, erfährt der Sender nicht. Es gibt da keinerlei Rückmeldung.

Die müsste man ggf. selbst auf Anwendungs-Ebene implementieren.

von TTL (Gast)


Lesenswert?

Destination unreachable hört sich doch sehr nach IP an, also Layer 3. 
Kannst du das Ding überhaupt anpingen? Probier das erstmal, bevor du UDP 
ins Layer 4 schickst.

von Hmmm (Gast)


Lesenswert?

TTL schrieb:
> Destination unreachable hört sich doch sehr nach IP an, also Layer 3.
> Kannst du das Ding überhaupt anpingen?

Type "Destination unreachable", Code "Port unreachable" und kommt von 
der Zieladresse.

Auf Layer 3 ist alles in Ordnung, der IP-Stack ist bloss der Meinung, 
dass auf diesem UDP-Port nichts lauscht.

Es wäre mal interessant zu wissen, was udp_bind() zurückliefert. Die 
momentane Fehlerbehandlung sieht ja noch eher rudimentär aus.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Stefan ⛄ F. schrieb:
> Ich bleibe dabei, dass UDP keine Verbindungen kennt. Ob ein gesendetes
> Paket den Empfänger erreicht hat und ob der Empfänger empfangsbereit
> war, erfährt der Sender nicht.

Der Sender muss es nicht erfahren. Wenn aber der Netzwerkstack des 
Empfaengers "nett" ist, dann kann er sehr wohl dem Sender Bescheid 
sagen; es ist nicht verboten, dann ein entsprechendes ICMP wie im 
Wireshark im 1. Post sichtbar zurueckzuschicken...

Gruss
WK

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:

> Ok, streichen wir die Sockets aus meiner Antwort.

Besser ist das...

> Ich bleibe dabei, dass UDP keine Verbindungen kennt. Ob ein gesendetes
> Paket den Empfänger erreicht hat und ob der Empfänger empfangsbereit
> war, erfährt der Sender nicht. Es gibt da keinerlei Rückmeldung.

Natürlich ist das so. Deswegen halt "Datagram" und nicht "Stream". Das 
unterscheidet die beiden Socket-Typen. Aber es gibt trotzdem viele 
Gemeinsamkeiten. Auch bei der Benutzung. Genau deswegen war es ja 
sinnvoll, die Socket-Sache als eine gemeinsame Abstraktion einzuführen.

> Die müsste man ggf. selbst auf Anwendungs-Ebene implementieren.

Oder besser gleich TCP verwenden? Das existiert, ist gut durchgetestet 
und funktioniert.

von (prx) A. K. (prx)


Lesenswert?

c-hater schrieb:
> Oder besser gleich TCP verwenden? Das existiert, ist gut durchgetestet
> und funktioniert.

Je nach Anwendung kann TCP auch nachteilig sein.

von c-hater (Gast)


Lesenswert?

(prx) A. K. schrieb:

> Je nach Anwendung kann TCP auch nachteilig sein.

Natürlich. Wäre es nicht so, gäbe es UDP nicht. Aber wenn man die 
Eigenschaften von TCP will/braucht, dann ist es definitiv Schwachsinn, 
auf höherer Ebene was eigenes, auf UDP basierendes zu bauen, um das zu 
erreichen. Da nimmt man natürlich TCP.

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.