Forum: PC-Programmierung C - linux - raw-socket - recvfrom liefert daten wo keine sein sollten


von Wicki W. (wicki)


Lesenswert?

Moin zusammen,

ich tausche mit einem arduino daten per raw sockets aus.
(das habe ich vor 4 jahren mal angefangen und dann beiseite gelegt.
und nun wieder ausgegraben.)

es funktioniert auch, aber es kommen ständig daten auf dem socket an,
obwohl der arduino gar nichts schickt.
auch wenn ich das interface tot lege (kabel ab) liefert recvfrom daten.
und das sind keine "echten" daten. es ist kein traffic auf dem 
interface.
jedenfalls definitiv nicht so viel, wie recvfrom lesen kann.

während tcpdump nichts (oder nur wenig) ausgaben liefert,
wenn ich keine packets auf den weg schicke, liefert mir im
gleichen moment das hier:

        numbytes = recvfrom(rawrsockfd, recbufs, BUF_SIZ, 0, NULL, 
NULL);
        if (numbytes==-1) return 0;
       if (debug & deb_recpkt) printf("listener: got packet %lu 
bytes\n", numbytes);

so ein resultat:


listener: got packet 530 bytes
listener: got packet 626 bytes
listener: got packet 66 bytes
listener: got packet 242 bytes
listener: got packet 310 bytes
listener: got packet 66 bytes

und das in rasender geschwindigkeit.....


ich habe keine ahnung, was das sein soll und wo es her kommt.
sehe ich den wald vor lauter bäumen nicht?

irgendwer eine idee dazu?



hier wird das socket erzeugt:
--------------------------------
int init_raw_rec_socket(char devn[]){
        printf("init_rec_socket. DEV:[%s]\n",devn);
        rawrsockfd=socket( PF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
        if (rawrsockfd<0) {
                perror("setsockopt1");
                close(rawrsockfd);
                return(EXIT_FAILURE);
        }
        strncpy(ifopts.ifr_name, devn, IFNAMSIZ-1);
        ioctl(rawrsockfd, SIOCGIFFLAGS, &ifopts);
        ifopts.ifr_flags |= IFF_PROMISC;
        ioctl(rawrsockfd, SIOCSIFFLAGS, &ifopts);

        /* Bind to device */
        if (setsockopt(rawrsockfd, SOL_SOCKET, SO_BINDTODEVICE, 
dev_name, IFNAMSIZ-1) == -1) {
                perror("SO_BINDTODEVICE");
                close(rawrsockfd);
                return(EXIT_FAILURE);
        }

        int flags = fcntl(rawrsockfd, F_GETFL);
        flags |= O_NONBLOCK;
        fcntl(rawrsockfd, F_SETFL, flags);
        memset(&if_ip, 0, sizeof(struct ifreq));

        if ((rawrsockfd = socket( PF_PACKET, SOCK_RAW, htons( 
ETH_P_ALL))) == -1) {
                perror("non blocking listener: socket");
                return(EXIT_FAILURE);
        }

        flags = fcntl(rawrsockfd, F_GETFL);
        flags |= O_NONBLOCK;
        fcntl(rawrsockfd, F_SETFL, flags);
        return 0;


}


---------------------------

tcpdump benimmt sich von rechner zu rechner auch immer mal etwas 
seltsam:
commandlie ist immer "tcpdump -i interface -s 2000"

tcpdump version 4.9.2
libpcap version 1.8.1
OpenSSL 1.1.1  11 Sep 2018

das liefert:

07:44:55.904261 06:05:04:03:02:01 > 01:02:03:04:05:06, 802.3, length 0: 
LLC, dsap Null (0x00) Individual, ssap Null (0x00) Command, ctrl 0x3c00: 
Information, send seq 0, rcv seq 30, Flags [Command], length 50
  0x0000:  0000 003c 0000 0000 0000 2600 0000 0000  ...<......&.....
  0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................
  0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................
  0x0030:  0000                                     ..
07:44:55.910384 06:05:04:03:02:01 > 01:02:03:04:05:06, 802.3, length 0: 
LLC, dsap Null (0x00) Individual, ssap Null (0x00) Command, ctrl 0x3e00: 
Information, send seq 0, rcv seq 31, Flags [Command], length 50
  0x0000:  0000 003e 0000 0000 0000 2700 0000 0000  ...>......'.....
  0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................
  0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................
  0x0030:  0000                                     ..
07:44:55.918284 06:05:04:03:02:01 > 01:02:03:04:05:06, 802.3, length 0: 
LLC, dsap Null (0x00) Individual, ssap Null (0x00) Command, ctrl 0x3f00: 
Information, send seq 0, rcv seq 31, Flags [Poll], length 50
  0x0000:  0000 003f 0000 0000 0000 2800 0000 0000  ...?......(.....
  0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................
  0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................
  0x0030:  0000                                     ..


tcpdump version 4.99.1
libpcap version 1.10.1 (with TPACKET_V3)
OpenSSL 3.0.2 15 Mar 2022

das liefert:

07:40:42.950850  [|llc]
07:40:42.957939  [|llc]
07:40:42.959007  [|llc]
07:40:42.961276  [|llc]
07:40:42.966739  [|llc]
07:40:42.967930  [|llc]

von Wilhelm M. (wimalopaan)


Lesenswert?

Du setzt den Socket in den Non-Blocking-Mode.

Außerdem wertest Du den Fehlercode nach recvfrom(...) < 0 nicht aus.

von Klaus S. (kseege)


Lesenswert?

Wicki W. schrieb:
> es kommen ständig daten auf dem socket an

Ja, dafür ist ein "raw socket" nach meinem bescheidenen Halbwissen da. 
Ein Socket ist eine Telefonnummer zum Betriebssystem bezüglich 
"Networking" und kann alles geschickt kriegen, was im Netzwerkbereich 
wichtig ist. Raw sockets sind nicht einheitlich, sondern 
betriebssystemabhängig. Unter Anderem dienen sie dazu, entweder "network 
sniffer" für etablierte Protokolle zu bauen oder eigene Protokolle zu 
implementieren (wie ja auch in diesem Fall gewünscht).

Insofern erstaunt es mich, daß ein Programm wie tcpdump, das (zumindest 
dem Namen nach) zwei Ebenen höher arbeitet, hier überhaupt sinnvolle 
Daten liefern soll. Kann aber an meiner Unkenntnis liegen.

Ich würde beispielsweise vermuten, daß sämtliche Broadcastnachrichten im 
raw socket auftauchen, wie soll ich sonst eine eigene "address 
resolution" aufbauen, wenn ich ein eigenes Protokoll mit eigener 
Adressierung implementieren will?

@Wilhelm M.
Das mit der fehlenden Errorauswertung verstehe ich, aber was ist am "non 
blocking mode" kritisch? Wenn nichts da ist, bekomme ich "0 bytes 
received" zurück und weiter gehts.

Just my 2 cents
Klaus (der soundsovielte)

von Wilhelm M. (wimalopaan)


Lesenswert?

Klaus S. schrieb:
> @Wilhelm M.
> Das mit der fehlenden Errorauswertung verstehe ich, aber was ist am "non
> blocking mode" kritisch? Wenn nichts da ist, bekomme ich "0 bytes
> received" zurück und weiter gehts.

Gar nichts.
Nur an dem Code des TO kann ich nicht erkennen, dass er darauf 
vorbereitet ist bzw. weiß, dass er damit eine Polling-Strategie 
realisiert.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Klaus S. schrieb:
> Insofern erstaunt es mich, daß ein Programm wie tcpdump, das (zumindest
> dem Namen nach) zwei Ebenen höher arbeitet, hier überhaupt sinnvolle
> Daten liefern soll. Kann aber an meiner Unkenntnis liegen.

Offenbar trifft letzteres zu. Tcpdump kann schon lange nicht mehr nur 
TCP-Verkehr anzeigen, sondern alles mögliche. Unter MS Windows setzt man 
Tcpdump und Wireshark meist in Verbindung mit der libpcap o.ä. ein, 
wodurch der direkte Zugriff am Protokollstack vorbei auf die 
entsprechenden Netzwerkinterfaces erfolgt. Mittlerweile gibt es z.B. 
auch USBPcap, mit dem man auf die gleiche Art und Weise den USB-Verkehr 
mitschneiden kann, und zwar tatsächlich auch mit tcpdump und Wireshark.

von Klaus S. (kseege)


Lesenswert?

Wilhelm M. schrieb:
> Gar nichts.

Andreas S. schrieb:
> Offenbar trifft letzteres zu.

Danke für die Erläuterungen, ich lerne immer gern dazu.

Gruß Klaus (der soundsovielte)

von Wicki W. (wicki)


Lesenswert?

hi nochmal,

nein, einen error werte ich nicht aus.
und dass ich das ganze im no-blocking-mode laufen haben will, das
hat schon seinen grund. (das ist für einen RT-kern gebaut worden)
und dass sich auf dem socket natürlich alles mögliche
herumtreiben kann, das ist mir auch klar.
aber das sollte es halt eigentlich nicht - denn wenn da
was wäre, dann würde es z.b. auch von tcpdump oder w-shark
gemeldet.

aber hier sagt recvfrom ja:
"ich hab da xxx bytes daten für dich" - die können aber eigentlich
nicht da sein. weil auf dem interface nichts weiter passiert.

und wenn ich sie lese, dann steht auch müll drin.
ich konnte bislang jedesfalls noch nichts sinnvolles herauslesen
- ausser mal ein ARPs, DHCPs und sowas. aber nichts was dieser menge an
daten entspricht, die ich aus dem socket herauslesen kann.

daher frage ich mich nun schon länger:
was mach ich falsch? oder wo denke ich falsch?

ein kollege meinte schon, "das bind zum interface hat
vielleicht nicht geklappt und dann nimmt das socket
alle interfaces die da sind".
aber selbst auf allen interfaces zugleich dürfe nicht
so viel los sein.

von Foobar (asdfasd)


Lesenswert?

Was willst du überhaupt machen?

Ein raw-socket mit ETH_P_ALL bekommt alles, u.U. auch den eigenen 
ausgehenden Traffic.  Warum keine Beschränkung auf einen Protokolltyp? 
Dann promiscuous Mode: das schaltet den Paketfilter im Netzwerkchip ab - 
du bekommst alle Pakete, auch die, die nicht für diesen Rechner sind. 
Und letztendlich machst du einen zweiten (nicht an ein Interface 
gebundenen) Socket auf und vergisst den (gebundenen) ersten. 
Cut-n-Paste Fehler?

Mir scheint, du willst Schrauben mit nem Hammer einschlagen ...

von Wicki W. (wicki)


Lesenswert?

Foobar schrieb:
> Was willst du überhaupt machen?

ich will die daten haben, bevor irgendwer oder -was sich damit befasst.


> Ein raw-socket mit ETH_P_ALL bekommt alles.

genau so soll es auch sein.


> , u.U. auch den eigenen  ausgehenden Traffic.

das mit dem eigenen ausgehenden traffic, das wäre mir neu und mir
bestimt schon mal aufgefallen.
die manpage sagt auch nichts davon. (s.u.)



> Warum keine Beschränkung auf einen Protokolltyp?

weil jedes protokoll overhead bedeutet. und den will ich vermeiden.


> Dann promiscuous Mode: das schaltet den Paketfilter im Netzwerkchip ab -
> du bekommst alle Pakete, auch die, die nicht für diesen Rechner sind.
> Und letztendlich machst du einen zweiten (nicht an ein Interface
> gebundenen) Socket auf und vergisst den (gebundenen) ersten.
> Cut-n-Paste Fehler?

den verdacht hatte ich auch schon. aber wenn ich das raus nehme, dann
ändert sich dennoch nichts am verhalten.


> Mir scheint, du willst Schrauben mit nem Hammer einschlagen ...

ein unpassender vergleich. und ob die anwendung überhaupt so
realisiert wird, ist eh noch völlig offen. ist ein experiment, das
ich vor 4 jahren mal angefangen habe.

was bleibt, ist die frage:
warum sagt recvfrom "ich habe daten" wenn da keine sein sollten.
wo kommen sie her? und warum?

-------------


When protocol is set to htons(ETH_P_ALL), then all
protocols are received.  All incoming packets of that protocol
type will be passed to the packet socket before they are passed
to the protocols implemented in the kernel.

von Daniel A. (daniel-a)


Lesenswert?

Wicki W. schrieb:
>> Warum keine Beschränkung auf einen Protokolltyp?
>
> weil jedes protokoll overhead bedeutet. und den will ich vermeiden.

Der CPU Overhead davon dürfte weit darunter liegen, deine Anwendung bei 
jedem Paket informieren zu müssen, dort hin zu switchen, und dieses die 
Daten lesen/kopieren zu lassen, nur damit es dann meistens doch nichts 
damit macht.

von Foobar (asdfasd)


Lesenswert?

>> Was willst du überhaupt machen?
>
> ich will die daten haben, bevor irgendwer oder -was sich damit befasst.

Das wird schwer.  Du bekommst eh nur ne Kopie und bevor die im Userspace 
landet, hat das Kernel das Original schon längst weiterverarbeitet.


>> Ein raw-socket mit ETH_P_ALL bekommt alles.
>
> genau so soll es auch sein.

Für eine Kommunikation mit einem Arduino eher ungewöhnlich.


>> , u.U. auch den eigenen  ausgehenden Traffic.
>
> das mit dem eigenen ausgehenden traffic, das wäre mir neu und mir
> bestimt schon mal aufgefallen.

Dann schau noch mal genauer - insb das Feld sll_pkttype (i.e. 
PACKET_OUTGOING) in der von dir verworfenen sockaddr das recvfrom.  S. 
man 7 packet).  Btw, mein tcpdump zeigt ausgehenden Traffic ...

Ich weiß aber, dass einige wenige ausgehenden Pakete nicht gemeldet 
werden (an die genauen Bedingungen kann ich mich nicht erinnern).  Hatte 
das Problem (vor vielen Jahren) mal.  Kommentar vom Kernel-Maintainer: 
wissen wir, wird aber vorraussichtlich nicht gefixt.  Kann sein, dass 
das mit der Turbo-Packet API zusammenhing (bin mir aber nicht sicher).

>> Warum keine Beschränkung auf einen Protokolltyp?
>
> weil jedes protokoll overhead bedeutet. und den will ich vermeiden.

Das wird eh verarbeitet.  Es wird nur ein etwas späterer Hook aktiviert. 
Und das Kopieren/Weiterleiten jedes einzelnen Pakets zum Userspace ist 
um Größenordnungen aufwändiger als der Kernel-interner Filter, der dann 
nur relevante Pakete kopiert.


Wenn du wirklich sowas haben willst (im Prinzip ein Packet-sniffer), 
solltest du dir überlegen, ob du nicht eher libpcap einsetzt.  Da ist 
viel Kram drin, um das ganze halbwegs performant (Turbo-Paket btw dessen 
Nachfolger) und korrekt zu implementieren.  Außerdem wäre das weitgehend 
platformneutral.  Auf diese Weise ein Kommunikationsprotokoll zu 
implementieren, wäre mMn allerdings ein Irrweg.

von Wicki W. (wicki)


Lesenswert?

> >> Ein raw-socket mit ETH_P_ALL bekommt alles.
> > genau so soll es auch sein.

> Für eine Kommunikation mit einem Arduino eher ungewöhnlich.

ja - sicher. ungewöhnlich ist das....
im prinzip ist es so, dass ein shmem-bereich eines linuxcnc
auf die io-ports eines arduino oder raspberry oder das shmem
eines anderen PC (oder was auch immer) gemappt werden.

die echtzeitfähigkeiten, die ich mir gewünscht hatte, habe ich
aber vor 4 jahren leider nicht hinbekommen.
nun will ichs nochmal versuchen.


> >> , u.U. auch den eigenen  ausgehenden Traffic.
> >
> > das mit dem eigenen ausgehenden traffic, das wäre mir neu und mir
> > bestimt schon mal aufgefallen.

> Dann schau noch mal genauer - insb das Feld sll_pkttype (i.e.

bin dabei, genauer zu schauen.
und wie es scheint, komm ich der sache auf die spur.

ich habe das grade auf eine minimal-version reduziert
und es scheint so zu sein, dass der fehler irgendwo im
programm liegt (und nicht beim handling des recvfrom)



> PACKET_OUTGOING) in der von dir verworfenen sockaddr das recvfrom.  S.
> man 7 packet).  Btw, mein tcpdump zeigt ausgehenden Traffic ...

na klar zeigt der tcpdump ausgehenden traffic - aber das rec-socket
des sendenden devices sollte doch nicht den eigenen traffic.... ?
warum denn eigentlich nicht??? na klar!
vielleicht ist genau das mein denkfehler gewesen! ?
wirklich wald vor lauter bäumen nicht gesehen?

ich lote das mal weiter aus.

aber schön, das wir drüber geredet haben.

melde mich, wenn ich weiter bin.


munter bleiben

wicki

von Foobar (asdfasd)


Lesenswert?

> im prinzip ist es so, dass ein shmem-bereich eines linuxcnc
> auf die io-ports eines arduino oder raspberry oder das shmem
> eines anderen PC (oder was auch immer) gemappt werden.

Nun ja, ich hoffe nicht, dass du die Step-Impulse aufgelöst bekommen 
willst - das wird nichts.  Mit einem separaten Eth-Controller und einer 
direkten Verbindung (also kein Switch, keine anderen Rechner) wirst du 
<1ms gut hinbekommen, mehr als 0.1ms würd ich aber nicht erwarten. 
Entspräche Pulsraten von 500-5000Hz.  Die Ethernet-Verarbeitung im 
Arduino stell ich mir noch spannend vor - ist ja nicht so, dass die 
üblicherweise Ethernet on-Chip haben ...

Aber für so eine Anwendung nimmt man doch nicht ETH_P_ALL - definier dir 
einen eigenen Typ.  Und berücksichtigen: auch ein RAW-socket hat 
Empfangs- und Sendebuffer, die überlaufen können - wenn zuviel ankommt 
werden Pakete weggeschmissen.  Ein ausgelastetes 100mbit-Netz per 
simplen raw-socket mitzuschneiden, wird nicht ohne Verluste klappen. 
Dafür wurden z.B. die Turbo-Packets eingeführt (ein Ring-Buffer im 
Userspace, der direkt vom Kernel gefüllt wird, fast wie DMA).

Das sinnvollste wäre mMn aber reguläre UDP-Pakete.  Warum was neues 
Erfinden?

von Wicki W. (wicki)


Lesenswert?

nun bin ich wohl weiter....
aber leider gefrustet:

man kann scheinbar kein raw-socket an ein device binden.
das nimmt immer alle devices.

SO_BINDTODEVICE nickt das zwar brav ab - aber es funktioniert
nur bei nicht-raw-sockets.

wenn man von einem spezifischen device lesen will, dann muss
das mit "bind" gemacht werden. bind funktioniert aber nicht
mit raw sockets. weil die eben keine IP haben.

bislang habe ich noch keine möglichkeit gefunden, um herauszufinden,
von welchem device die daten, die von einem raw-socket kommen nun
wirklich stammen.

mit einem ID-header im telegramm könnte ich das problem zwar
umgehen - aber ich mag nicht so recht glauben, dass es keine
möglichkeit gibt, das auf der receiver-seite festzulegen.

vielleicht weiss ja jemand as dazu.
aber bitte keine grundsatzdiskussionen über IP-layer und sowas.
ich weiss, dass man damit diese probleme nicht hätte.

von Wicki W. (wicki)


Lesenswert?

> Nun ja, ich hoffe nicht, dass du die Step-Impulse aufgelöst bekommen
> willst -

doch, das war mal die grundidee


>  das wird nichts.  Mit einem separaten Eth-Controller und einer
> direkten Verbindung (also kein Switch, keine anderen Rechner) wirst du
> <1ms gut hinbekommen, mehr als 0.1ms würd ich aber nicht erwarten.
> Entspräche Pulsraten von 500-5000Hz.  Die Ethernet-Verarbeitung im
> Arduino stell ich mir noch spannend vor - ist ja nicht so, dass die
> üblicherweise Ethernet on-Chip haben ...

richtig - das ist auch das, was dabei herauskam. 5kHz
viel mehr war stabil nicht machbar.
aber mit einer neuen idee könnte das besser werden.


>  Die Ethernet-Verarbeitung im
> Arduino stell ich mir noch spannend vor - ist ja nicht so, dass die
> üblicherweise Ethernet on-Chip haben ...

ja, die ist "gewöhnungsbedürftig". aber ausbaufähig.
bei arduino war das aber mehr zur verarbeitung von rotary-encodern
gebaut und zur ansteuerung von gross-DROs, die dann aber auch via
rasberry und UDP funktionierten.



> Das sinnvollste wäre mMn aber reguläre UDP-Pakete.  Warum was neues
> Erfinden?

aus dem gleichen grund, warum sich hunde die eier lecken....
ich will einfach wissen ob es geht.
wegwerfen kann man es immer noch.

UDP ist ja schon drin..... ;-)

von Foobar (asdfasd)


Lesenswert?

> man kann scheinbar kein raw-socket an ein device binden.
> das nimmt immer alle devices.

Du hast aber schon den zweiten socket-call in deinem init_raw_rec_socket 
rausgenommen, oder?

> bind funktioniert aber nicht mit raw sockets. weil die eben keine IP haben.

Bei tcpdump geht das:
1
$ strace -e trace=network,close tcpdump -i ppp0
2
...
3
socket(PF_PACKET, SOCK_DGRAM, 768)      = 3
4
bind(3, {sa_family=AF_PACKET, proto=0x03, if30, pkttype=PACKET_HOST, addr(0)={0, }, 20) = 0
5
...

> bislang habe ich noch keine möglichkeit gefunden, um herauszufinden,
> von welchem device die daten, die von einem raw-socket kommen nun
> wirklich stammen.

Im sockaddr_ll gibt es nicht nur den Pakettyp (sll_pkttype) sondern auch 
noch eine Interfacenummer (sll_ifindex).  Check das mal.

von C-hater (c-hater)


Lesenswert?

Wicki W. schrieb:

> was bleibt, ist die frage:
> warum sagt recvfrom "ich habe daten" wenn da keine sein sollten.

Du siehst das Problem aus der falschen Richtung. Nur du glaubst, das da 
nichts sein sollte.

> wo kommen sie her? und warum?

Broadcasts. In jedem üblichen Netz anzutreffen und erreichen auch an 
jedem Port eines Switches den Peer. Genau das ist schließlich der Sinn 
von Broadcasts...

Wenn du noch nie was davon gehört hast, solltest du dich nicht mit 
Netzwerkprogrammierung beschäftigen, sondern erstmal Netzwerk-Kompetenz 
anlesen.

von Wicki W. (wicki)


Lesenswert?

>> wo kommen sie her? und warum?
>
> Broadcasts. In jedem üblichen Netz anzutreffen und erreichen auch an
> jedem Port eines Switches den Peer. Genau das ist schließlich der Sinn
> von Broadcasts...
>
> Wenn du noch nie was davon gehört hast, solltest du dich nicht mit
> Netzwerkprogrammierung beschäftigen, sondern erstmal Netzwerk-Kompetenz
> anlesen.


das ist es, was ich in solchen foren immer so liebe:
der liebevolle umgang miteinander.

so viele broadcasts können da nicht sein, weil das system nichts zu 
broadcasten hat. jedenfalls nicht so viel.
und wenn es broadcasts wären: warum sieht tcpdump sie nicht?

ein erklärungsversuch im nächsten post...

von Wicki W. (wicki)


Lesenswert?

Foobar schrieb:
>> man kann scheinbar kein raw-socket an ein device binden.
>> das nimmt immer alle devices.
>
> Du hast aber schon den zweiten socket-call in deinem init_raw_rec_socket
> rausgenommen, oder?

in dem testprog: ja
das hatte ich auch im hauptprogramm schon - ohne erfolg.
die ursache ist eine andere

ein erklärungsversuch:
(unvollständig und ins unreine geschrieben - und vielleicht auch falsch 
-  testprogramm dazu folgt noch)

dass tcpdump die ganze pakete nicht sah, das lag daran, dass ich
immer mit "-i ifname" gearbeitet habe. und nicht "-i any".


>> bind funktioniert aber nicht mit raw sockets. weil die eben keine IP haben.
>
> Bei tcpdump geht das:
>
1
> $ strace -e trace=network,close tcpdump -i ppp0
2
> ...
3
> socket(PF_PACKET, SOCK_DGRAM, 768)      = 3
4
> bind(3, {sa_family=AF_PACKET, proto=0x03, if30, pkttype=PACKET_HOST, 
5
> addr(0)={0, }, 20) = 0
6
> ...
7
>

da ist aber "pkttype=PACKET_HOST"

       *  sll_pkttype contains the packet type.  Valid types are
          PACKET_HOST for a packet addressed to the local host,

aber auf dieser ebene sind wir ja bei raw-packets gar nicht.
das ist nur ein datenstrom, der weder mac-adressen noch IPs
kennt.

die muss man ja selbst ausfiltern.
dass der datenstrom auch nicht interface kennt, von dem er stammt,
das erstaunt mich nun doch.


>> bislang habe ich noch keine möglichkeit gefunden, um herauszufinden,
>> von welchem device die daten, die von einem raw-socket kommen nun
>> wirklich stammen.
>
> Im sockaddr_ll gibt es nicht nur den Pakettyp (sll_pkttype) sondern auch
> noch eine Interfacenummer (sll_ifindex).  Check das mal.

ja, aber das setzt wieder voraus, dass man sich in einem bestimmten 
protokoll bewegt. aber:

 When protocol is set to htons(ETH_P_ALL), then all
 protocols are received.  All incoming packets of that protocol
 type will be passed to the packet socket before they are passed
 to the protocols implemented in the kernel.

und:
If a socket is bound to an interface, only packets received from that 
particular interface are processed by the socket. Note that this only 
works for some socket types, particularly AF_INET sockets. It is not 
supported for packet sockets (use normal bind(2) there).

und an dem problem haben sich auch bei stapelüberlauf schon vor
4 jahren leute festgebissen:

According to this other SO answer, and my experience on CentOS 7, this 
does not work for a raw socket. Instead, you need to use bind(), not the 
setsockopt() approach shown here

wie auch immer: ich teste weiter. ich finds grad spannend ;-)

von Wicki W. (wicki)


Lesenswert?

der testcode:

#include <stdbool.h>
#include <signal.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/io.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/ether.h>
#include <linux/if.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <arpa/inet.h>


#define BUF_SIZ         2048

        int rawrsockfd;
        int sockopt;
static char dev_name[20];

static   unsigned      char recbuf[BUF_SIZ];
        ssize_t numbytes;
        int tx_len = 0;
        int rawrsockfd;
        int sockopt;
        struct ifreq ifopts;    /* set promiscuous mode */
        struct ethhdr *rec_header = (struct ethhdr *)(recbuf);


        struct sockaddr_ll socket_address;

typedef struct {
         unsigned char  dh[6];
         unsigned char  sh[6];
         unsigned char  tp[2];
         unsigned char spare2[1500];  // spare....
} rasport_t;



static rasport_t portval;
static rasport_t* port_data_array =  &portval;
static rasport_t *recbufs = (rasport_t *)(recbuf);





int init_raw_rec_socket(char devn[]){


        printf("init_rec_socket. DEV:[%s]\n",devn);
       rawrsockfd = socket(AF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
//        rawrsockfd=socket( PF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
// aha..... Thus, today, there really should be no difference between 
AF_foo and PF_foo.
        if (rawrsockfd<0) {
                perror("setsockopt");
                close(rawrsockfd);
                return(EXIT_FAILURE);
        }
        strncpy(ifopts.ifr_name, devn, IFNAMSIZ-1);
        ioctl(rawrsockfd, SIOCGIFFLAGS, &ifopts);
        ifopts.ifr_flags |= IFF_PROMISC;
        ioctl(rawrsockfd, SIOCSIFFLAGS, &ifopts);

        /* Bind to device */
        if (setsockopt(rawrsockfd, SOL_SOCKET, SO_BINDTODEVICE, 
dev_name, IFNAMSIZ-1) == -1) {
                perror("SO_BINDTODEVICE");
                close(rawrsockfd);
                return(EXIT_FAILURE);
        }

        int flags = fcntl(rawrsockfd, F_GETFL);
        flags |= O_NONBLOCK;
        fcntl(rawrsockfd, F_SETFL, flags);
        return 0;


}


int rec_raw(){  // raw-eth-receive
                // we hatve to check: is this really a packet for us?
                bool isForMe=false;  // is it a packet for me?
//                numbytes = recvfrom(rawrsockfd, recbufs, BUF_SIZ, 0, 
NULL, NULL);

struct sockaddr_ll src_addr;
socklen_t addr_len = sizeof src_addr;
numbytes = recvfrom(rawrsockfd, recbufs, 2048, 0, (struct 
sockaddr*)&src_addr, &addr_len);
    if (numbytes==-1){
//                      perror("rec_raw");
                      printf("E\b");
                                 return 0;
                                }
        printf("listener: got packet %lu bytes from iface-index: %i\n", 
numbytes,src_addr.sll_ifindex);
  return 1;
}





void main()  {
  int c=0;
  printf("start\n");
  if (init_raw_rec_socket("lo")<0)exit(-1);
  printf("init done\n");
  while (c<10000000) {
    int got=rec_raw();
    c++;
  }
  printf("\nall done\n");
}


und so sieht es dann aus:

start
init_rec_socket. DEV:[lo]
init done
listener: got packet 203 bytes from iface-index: 17
listener: got packet 42 bytes from iface-index: 2
listener: got packet 42 bytes from iface-index: 2
listener: got packet 42 bytes from iface-index: 2
listener: got packet 42 bytes from iface-index: 2
listener: got packet 126 bytes from iface-index: 1
listener: got packet 126 bytes from iface-index: 1
listener: got packet 42 bytes from iface-index: 2
listener: got packet 182 bytes from iface-index: 2
listener: got packet 170 bytes from iface-index: 2
listener: got packet 353 bytes from iface-index: 17
listener: got packet 42 bytes from iface-index: 2
listener: got packet 170 bytes from iface-index: 2
listener: got packet 126 bytes from iface-index: 1
listener: got packet 42 bytes from iface-index: 2
listener: got packet 126 bytes from iface-index: 1
E
all done


das erklärt nun einiges - als nöchstes werde ich mal versuchen ob das 
mittels bind nun doch auf ein einzelnes interface einzugrenzen ist.

von Rolf M. (rmagnus)


Lesenswert?

Klaus S. schrieb:
> Wenn nichts da ist, bekomme ich "0 bytes received" zurück und weiter
> gehts.

Nein. Wenn nichts da ist, kommt -1 zurück und errno wird auf EAGAIN 
gesetzt.

Wicki W. schrieb:
> if ((rawrsockfd = socket( PF_PACKET, SOCK_RAW, htons(
> ETH_P_ALL))) == -1) {

Das ist nicht nur einfach ein Raw-Socket, sondern sogar ein Packet 
Socket. Der ist nochmal eine Ebene tiefer. Das heißt, dass das System 
dir beim Empfang sogar den MAC-Header mitschickt und du beim Senden 
diesen selbst erzeugen musst. Zum Thema packet sockets lohnt es sich, 
"man packet" durchzulesen. Zum Thema binden an ein Interface sagt diese 
Manpage:
"To get packets only from a specific interface use bind(2) specifying an 
address in a struct sockaddr_ll to bind the packet socket to an 
interface. Fields  used  for  binding  are  sll_family  (should  be 
AF_PACKET), sll_protocol, and sll_ifindex."
Zum lesen wäre evtl. recvmsg besser geeignet, denn das beinhaltet 
zusätzliche Informationen.

Wicki W. schrieb:
> der testcode:

Bitte die Regeln unter "Wichtige Regeln - erst lesen, dann posten!" erst 
lesen, und dann posten!

: Bearbeitet durch User
von Ein T. (ein_typ)


Lesenswert?

Wicki W. schrieb:
> Foobar schrieb:
>> Warum keine Beschränkung auf einen Protokolltyp?
>
> weil jedes protokoll overhead bedeutet. und den will ich vermeiden.

Du glaubst also, das im Userspace effizienter implementieren zu können 
als die optimierten Protokollstacks in gängiger Betriebssysteme? Das 
erscheint mir dann doch... ein bisschen optimistisch.

von Wicki W. (wicki)


Lesenswert?

Rolf M. schrieb:
> Klaus S. schrieb:
>> Wenn nichts da ist, bekomme ich "0 bytes received" zurück und weiter
>> gehts.
>
> Nein. Wenn nichts da ist, kommt -1 zurück und errno wird auf EAGAIN
> gesetzt.

das ist korrekt.
aber eine auswirkung hat es ja nicht, ob ich auf ==0 oder <=0 prüfe.
es heist immer: "da sind keine daten"




> Wicki W. schrieb:
>> if ((rawrsockfd = socket( PF_PACKET, SOCK_RAW, htons(
>> ETH_P_ALL))) == -1) {
>
> Das ist nicht nur einfach ein Raw-Socket, sondern sogar ein Packet
> Socket. Der ist nochmal eine Ebene tiefer. Das heißt, dass das System
> dir beim Empfang sogar den MAC-Header mitschickt und du beim Senden
> diesen selbst erzeugen musst.

genau das mache ich auch.

ich habs aber noch nicht hinbekommen, das revfrom nur an
ein interface zu binden.
das ist aber zunächst nicht schlimm, weil ich inzischen  die
quelle mit ll_ifindex ausfindig mache.
und da ich mir die mac-adressen eh selbst zusammenbaue bzw.
diese prüfe kann ich auch gleich das IF mit prüfen.


> Manpage:
> "To get packets only from a specific interface use bind(2) specifying an
> address in a struct sockaddr_ll to bind the packet socket to an
> interface. Fields  used  for  binding  are  sll_family  (should  be
> AF_PACKET), sll_protocol, and sll_ifindex."

ich hatte weiter oben schon mal was widersprüchliches gepostet: dass
das "bind" bei raw-sockets nicht gehen soll. (war in mehreren foren zu 
finden)
aber gerade eben habe ich es getestet. mit

bind(sock_fd, (struct sockaddr*)&socket_address, sizeof(socket_address))

kommt tatsächlich nur das an, was auf dem interface passiert.

grundsätzlich läuft es aber nun erst mal wieder so, wie es vor 4 jahren
schonmal lief bzw. laufen sollte.

besten dank an alle, die hier mit guten tips und schubsen
in die richtige richtung  geholfen haben ;-)

>> der testcode:
>
> Bitte die Regeln unter "Wichtige Regeln - erst lesen, dann posten!" erst
> lesen, und dann posten!

den fand ich mit rund 100 zeilen nicht so dramatisch lang....
aber ok.

von Wicki W. (wicki)


Lesenswert?

> Du glaubst also, das im Userspace effizienter implementieren zu können
> als die optimierten Protokollstacks in gängiger Betriebssysteme? Das
> erscheint mir dann doch... ein bisschen optimistisch.

das weiss ich nicht - ich werde es sehen.
ich sehe die probleme ehr auf der seite von geräten, die
kleine "optimierten Protokollstacks" haben.

beim arduino ist es so, dass er eth-packtes
maximal im ms-bereich versenden kann.
da scheint die limitierung in der hardware zu liegen.

das ergebnis 2019 war:
Frustrierenderweise ist aber beim Arduino tatsaechlich
bei rund 1ms Zykuszeit Schluss.

dann hatte ich noch mit einem raspberry und einem
STM32 NUCLEO-H743ZI experimentiert - aber dann alles
beiseite gelegt.

schaun wir mal....

von Klaus S. (kseege)


Lesenswert?

Rolf M. schrieb:
> Klaus S. schrieb:
>> Wenn nichts da ist, bekomme ich "0 bytes received" zurück und weiter
>> gehts.
>
> Nein. Wenn nichts da ist, kommt -1 zurück und errno wird auf EAGAIN
> gesetzt.

So kann man sich die Wirklichkeit zurechtbiegen und mit 
Detailkenntnissen glänzen. Ich habe absichtlich "ich bekomme... " 
geschrieben, da ich die krude Logik der Berkeleysockets nur mit einem 
drüberliegenden Layer ertrage, den ich schon seit MS-DOS mit Wollongong 
TCP-Paket darüberlege, wenn ich denn überhaupt C nutze. Im Moment nutze 
ich Tcl/Tk, und da kommt auch keine -1 zurück :-))
Insofern ist mein Satz ohne weitere Einschränkungen richtig. Die 
Korrektur meines Satzes ist nur dann richtig, wenn man implizit die 
Logik der Berkeley-Sockets voraussetzt. Soviel Genauigkeit sollte meines 
Erachtens schon sein, wenn man komplizierte Probleme lösen möchte, sonst 
wirds eventuell nichts.

Peace, Shalom umd Salam aleikum

Klaus (der soundsovielte)

P.S. Die Logik, eine Kommunikation mit zwei ungleichen Partnern durch 
ein fettes Host- und ein mageres Client-Protokoll zu erledigen hat USB 
doch ebenfalls vorgemacht. Auf Ethernet war meines Wissens das 
IPX-Protokoll auch asymmetrisch aufgebaut, aber eben ziemlich "closed 
source".
Mich würde ein solches Protokoll ebenfalls reizen, der Aufwand hat mich 
aber bisher abgeschreckt. Isofern bin ich gespannt auf Fortschritte.

von Rolf M. (rmagnus)


Lesenswert?

Wicki W. schrieb:
> aber eine auswirkung hat es ja nicht, ob ich auf ==0 oder <=0 prüfe.
> es heist immer: "da sind keine daten"

Wenn eine 0 zurück kommt, hat das eine andere Bedeutung. Es sagt, dass 
ein Datagramm mit einer Länge von 0 angekommen ist. Ob das bei 
Packet-Sockets passieren kann, weiß ich nicht so genau, aber es ist 
generell was anderes als "kein Datagramm".

>> Bitte die Regeln unter "Wichtige Regeln - erst lesen, dann posten!" erst
>> lesen, und dann posten!
>
> den fand ich mit rund 100 zeilen nicht so dramatisch lang....
> aber ok.

Du hast noch nicht alles gelesen. 😉
Quellcode sollte man in Code-Tags stecken.

Klaus S. schrieb:
> Insofern ist mein Satz ohne weitere Einschränkungen richtig. Die
> Korrektur meines Satzes ist nur dann richtig, wenn man implizit die
> Logik der Berkeley-Sockets voraussetzt.

Du hast aber schon gesehen, dass es hier genau um diese geht? Natürlich 
kann das auch komplett anders aussehen, wenn du irgendeine ganz andere 
API nutzt, aber das bringt uns hier nicht wirklich weiter.

> die krude Logik der Berkeleysockets

Man sollte halt bedenken, dass sie 1. eine low-level-API direkt auf 
syscall-Ebene und 2. bereits 40 Jahre alt sind. Sie stammen noch aus 
einer Zeit, in der es in C den Datentyp void noch nicht gab. Deshalb 
braucht man bei den Adressen auch diese nervigen Casts. Ansonsten haben 
sie finde ich  ihre Vor- und Nachteile.

: Bearbeitet durch User
von Klaus S. (kseege)


Lesenswert?

Rolf M. schrieb:
> aber das bringt uns hier nicht wirklich weiter

Sehr einverstanden, wie mit fast allem, was Du sagst. Mir war nur die 
eine Klarstellung für spätere Leser wichtig, so wie Wilhelm M. ja auch 
seine Gründe für die Erwähnung des non-blocking-modes erläuterte.

Gruß Klaus (der soundsovielte)

von Foobar (asdfasd)


Lesenswert?

> beim arduino ist es so, dass er eth-packtes
> maximal im ms-bereich versenden kann.

Redest du etwa von einem Arduino mit einem WizNet W5100 
Ethernet-Krüppel?

von Wicki W. (wicki)


Lesenswert?

Foobar schrieb:
>> beim arduino ist es so, dass er eth-packtes
>> maximal im ms-bereich versenden kann.
>
> Redest du etwa von einem Arduino mit einem WizNet W5100
> Ethernet-Krüppel?

ja - für sachen, die sich im ms-bereich abspielen scheint er
ganz brauchbar zu sein.

ansonsten halte ich den arduino mit seinen 1000en von sich
dauend ändernden und dann auch noch gleichnamigen libs für
recht ätzend. aver irgendwie alternativlos.

als die teile noch für spotpreise aus chinesien zu bekommen
waren, da waren sie schon OK.

inzwischen ist alles unlustig: 200 eur für raspberry werden teilweise
aufgerufen:

Raspberry Pi 4 Model B 8 GB RAM 64-Bit 1,5 GHz Quad-Core-Board - 8 GB 
RAM Motherboard (8 GB)
229,99€ Statt: 239,99€
Lieferung Mittwoch, 3. Mai – Donnerstag, 4. Mai
KOSTENLOSE Lieferung

hallo???

vielleicht sollte ich meine sammlung verkaufen.... ?

man könnte fast in die versuchung geraten, wieder TTLs zu löten ;-)

von Foobar (asdfasd)


Lesenswert?

>> Redest du etwa von einem Arduino mit einem WizNet W5100
>> Ethernet-Krüppel?
>
> ja

Das macht den ganzen Thread zu ner Farce.

Ciao.

von Wicki W. (wicki)


Lesenswert?

Foobar schrieb:
> Das macht den ganzen Thread zu ner Farce.

aber das war jetzt soo wichtig, dass du das noch eben schnell
um mittern8 posten musstest?

hier ging es um die linux-seite von 2 kommunikationspartnern.
was auf der anderen seite hängt spielt für die anwendung keine rolle.
das kann ein PC, eine rapsi, ein arduino oder irgendeine andere
hardware sein.
meine fragen sind beantwortet und das problem gelöst.

schönen tag noch....

von C-hater (c-hater)


Lesenswert?

Wicki W. schrieb:

> so viele broadcasts können da nicht sein, weil das system nichts zu
> broadcasten hat. jedenfalls nicht so viel.

Das "System" ist hier aber typisch die Gesamtheit aller an den Switch 
angeschlossenen Geräte (bzw. natürlich bei managebaren Switches nur der 
Teil davon, der in derselben Broadcast-Domäne liegt). Und die Normalität 
bei praktisch genutzten Geräten ist: sie sondern recht häufig Broadcasts 
ab, weil sehr viele Protokolle halt direkt oder indirekt Broadcasts 
benötigen, um funktionieren zu können. Eine Aufzählung erspare ich mir 
hier...

> und wenn es broadcasts wären: warum sieht tcpdump sie nicht?

Das könnte z.B. daran liegen, dass du zu blöd oder zu faul bist, die 
Doku zu tcpdump zu lesen und es passend zu konfigurieren, um diese zu 
sehen...

von Wicki W. (wicki)


Lesenswert?

C-hater schrieb:
> Wicki W. schrieb:
>
>> so viele broadcasts können da nicht sein, weil das system nichts zu
>> broadcasten hat. jedenfalls nicht so viel.
>
> Das "System" ist hier aber typisch die Gesamtheit aller an den Switch
> angeschlossenen Geräte (bzw. natürlich bei managebaren Switches nur der
> Teil davon, der in derselben Broadcast-Domäne liegt).

wenn das aber ein system ist, an dem nichts dran hängt, dann
sollte sich auch die broadcasts in_sehr_ überschaubaren grenzen halten.



>> und wenn es broadcasts wären: warum sieht tcpdump sie nicht?
>
> Das könnte z.B. daran liegen, dass du zu blöd oder zu faul bist, die
> Doku zu tcpdump zu lesen und es passend zu konfigurieren, um diese zu
> sehen...

das könnte auch dran liegen, dass tcpdump in unterschiedlichen
implementationen bei gleichen parametern eine unterschiedliches
verhalten zeigt. genau das ist nämlich der fall.
s.o.

es ist wenig konstruktiv, anderen erstemal faul- oder blödheit
oder eine geistige behinderung zu unterstellen....
die ursache war:
SO_BINDTODEVICE funktioniert bei raw sockets nicht.
(ohne dass es einen error gibt)
und ein tcpdump ohne "-i any" liefert im gegensatz
dem recvfrom nicht den traffic aller interfaces.

von Rolf M. (rmagnus)


Lesenswert?

Wicki W. schrieb:
> wenn das aber ein system ist, an dem nichts dran hängt, dann
> sollte sich auch die broadcasts in_sehr_ überschaubaren grenzen halten.

Dass auf dem loopback-Device Sachen kommmen, sollte klar sein. Dann 
gibt's ggf. noch andere virtuelle Netzwerkgeräte wie z.B. für Docker 
oder VirtualBox u.s.w., wo auch Traffic entstehen kann, ohne dass 
physisch irgendwas angeschlossen sein muss. Lass dir doch mal die Namen 
der Devices ausgeben, auf denen du den Traffic empfängst.

> es ist wenig konstruktiv, anderen erstemal faul- oder blödheit
> oder eine geistige behinderung zu unterstellen....

C-Hater schafft es leider nicht sehr oft, Antworten zu verfassen, ohne 
dabei sein Gegenüber zu beleidigen.

von C-hater (c-hater)


Lesenswert?

Wicki W. schrieb:

> wenn das aber ein system ist, an dem nichts dran hängt, dann
> sollte sich auch die broadcasts in_sehr_ überschaubaren grenzen halten.

Bleibt immer noch noch mehr als genug über. Je nachdem, was halt auf den 
beteiligten Maschinen an Software läuft. Darüber hast du aber nichts 
verlauten lassen.

> das könnte auch dran liegen, dass tcpdump in unterschiedlichen
> implementationen bei gleichen parametern eine unterschiedliches
> verhalten zeigt.

Man liest typisch die Doku, die zu der konkret verwendeten 
Implementierung passt. Alles andere ist mehr oder weniger offensichtlich 
sinnlos.

von C-hater (c-hater)


Lesenswert?

Rolf M. schrieb:

> C-Hater schafft es leider nicht sehr oft, Antworten zu verfassen, ohne
> dabei sein Gegenüber zu beleidigen.

Das sieht nur für diejenigen so aus, die die ungeschminkte Nennung 
offensichtlicher Fakten für eine Beleidigung halten.

Etwas entspanntere Leute bemängeln höchstens das Fehlen von 
"Höflichkeit". Ja, meine Lebenszeit mit der Erfindung falscher (und 
letztlich wohl im sinne der Sache irreführender!) Höflichkeiten zu 
verschwenden, das ist tatsächlich nicht mein Ding.

von Wicki W. (wicki)


Lesenswert?

können wir vielleicht wieder zum thema zurückkehren?


den "bind" löst das problem doch nicht:

listener: got packet 98 bytes from iface-index: 1
listener: got packet 125 bytes from iface-index: 3



das passiert, wenn ich es mit der unten stehenden
und  auf 58 zeilen abgespeckten version versuche.
egal, mit welchem interface ich das probiere.

und das gilt - wie es scheint - für "bind" und "bindtodevice"
gleichermassen.
dass es mit raw-packets und bindtodevice nicht geht (auch wenn
ich IFF_PROMISC rausnehme) und immer alle interfaces geliefert
werden, das kann ja wohl als gesichert gelten.
(obwohl openai und manche suchmaschinen was andere behaupten)

aber auch "bind" liefert den traffic aller interfaces.
dabei war ich mir sicher, dass es mit "bind" schon mal funktioniert
hatte beim testen.
1
#include <stdbool.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <unistd.h>
5
#include <fcntl.h>
6
#include <stdio.h>
7
#include <string.h>
8
#include <sys/ioctl.h>
9
#include <sys/socket.h>
10
#include <linux/if.h>
11
#include <linux/if_packet.h>
12
#include <linux/if_ether.h>
13
#include <arpa/inet.h>
14
15
int rawrsockfd;
16
int sockopt;
17
char dev_name[20];
18
unsigned      char recbuf[2048];
19
struct ifreq ifopts;    /* set promiscuous mode */
20
struct sockaddr_ll socket_address;
21
ssize_t numbytes;
22
23
int init_raw_rec_socket(char devn[]){
24
        printf("init_rec_socket. DEV:[%s]\n",devn);
25
        rawrsockfd = socket(AF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
26
        if (rawrsockfd<0) { perror("setsockopt"); close(rawrsockfd); return(EXIT_FAILURE); }
27
        strncpy(ifopts.ifr_name, devn, IFNAMSIZ-1);
28
        ioctl(rawrsockfd, SIOCGIFFLAGS, &ifopts);
29
        ifopts.ifr_flags |= IFF_PROMISC;
30
        ioctl(rawrsockfd, SIOCSIFFLAGS, &ifopts);
31
        /* Bind to device */
32
  bind(rawrsockfd, (struct sockaddr*)&socket_address, sizeof(socket_address));
33
  int flags = fcntl(rawrsockfd, F_GETFL);
34
        flags |= O_NONBLOCK;
35
        fcntl(rawrsockfd, F_SETFL, flags);
36
        return 0;
37
}
38
39
40
int rec_raw(){  // raw-eth-receive
41
socklen_t addr_len = sizeof socket_address;
42
numbytes = recvfrom(rawrsockfd, recbuf, 2048, 0, (struct 
43
sockaddr*)&socket_address, &addr_len);
44
    if (numbytes<=-1){ printf("wait\b\b\b\b\b"); return 0; }
45
printf("listener: got packet %lu bytes from iface-index: %i\n", 
46
numbytes,socket_address.sll_ifindex);
47
return 1;
48
}
49
50
void main()  {
51
  int c=0;
52
  printf("start\n");
53
  if (init_raw_rec_socket("lo")<0)exit(-1); 
54
  printf("init done\n");
55
  while (c<100000000) { int got=rec_raw(); c++; }
56
  printf("\nall done\n");
57
}
[Mod: C-Tags zur Formatierung eingefügt]

: Bearbeitet durch Moderator
von Frank M. (ukw) (Moderator) Benutzerseite


Angehängte Dateien:

Lesenswert?

@Wicki:

Bitte bei C-Code auch die C-Tags verwenden, sonst sieht Dein Quellcode 
auf Mobilgeräten nur wie Matsche aus, siehe Anhang.

Ich habe das für Deinen Beitrag oben mal gemacht. Dazu einfach
1
[ c ]
2
C-Code....
3
[ /c ]
(ohne die Leerzeichen) schreiben.

von Wicki W. (wicki)


Lesenswert?

> auf Mobilgeräten nur wie Matsche aus, siehe Anhang.
> Ich habe das für Deinen Beitrag oben mal gemacht. Dazu einfach

> [ c ]C-Code....[ /c ]

> (ohne die Leerzeichen) schreiben.

hätt ichs gewusst, hätt ichs gemacht - aber
nach der antwort durfte ich nicht mehr editieren.

von Wicki W. (wicki)


Lesenswert?

guten morgen zusammen,

nachdem ich nun das problem auf 58 zeilen heruntergebrochen habe
kommt plötzlich von an den vielen erfahrenen entwicklern hier keine
antwort mehr....

daher habe ich meine theorie und den obigen code dazu mit
openai diskutiert.
und die ist zum einen genauso widersprüchlich, wie die vielen
infos, die man dazu im netz findet. zum anderen kommt sie aber
ebenfalls zu dem gleichen ergebnis:

"Das heißt, dieser Code empfängt tatsächlich Pakete von allen 
Netzwerkinterfaces auf dem System."

denn für mich stellt es sich so dar, dass der gesamte
eth-verkehr im system ein einzigerdatenstrom (respektive ein
einziger speicherbereich) ist, in dem neben den header- und
payload-informationen ebenfalls das interfache (bzw. dessen
index aller interfaces) enthalten ist.
deshalb kann ein "bind" nicht nach interfacen filtern, wenn man
auf der untersten protokollebene ist.

wenn ich mit dieser - ja, sehr vereinfachten - darstellung falsch
liege, dann freue ich mich über rückmeldungen.

von Foobar (asdfasd)


Lesenswert?

Schau halt einfach mal, an was für eine Adresse du dich binden 
willst(Argumente vom bind-call). Da sind noch mehr bugs ...

von Wicki W. (wicki)


Lesenswert?

Foobar schrieb:
> Schau halt einfach mal, an was für eine Adresse du dich binden
> willst(Argumente vom bind-call).

danke!

mit
socket_address.sll_ifindex = if_nametoindex("lo");
funktioniert auch "bind" wieder.

wenn ich
socket_address.sll_ifindex = 0;
setze, dann kommen wieder alle interfaces durch.

klingt auch irgendwie logisch oder zumindest
nachvollziehbar.
das war jetzt aber nur ein kurztest.

ich bin ganz sicher kein-entwickler und inzwischen
reichlich betriebsblind.
war mir aber fast sicher, dass es mal funktioniert
hatte.


> Da sind noch mehr bugs ...

bestimmt.
welche siehst du noch?

von Rolf M. (rmagnus)


Lesenswert?

Wicki W. schrieb:
> hätt ichs gewusst, hätt ichs gemacht

Deshalb hatte ich das oben bereits erwähnt.

Wicki W. schrieb:
> wenn ich
> socket_address.sll_ifindex = 0;
> setze, dann kommen wieder alle interfaces durch.
>
> klingt auch irgendwie logisch oder zumindest
> nachvollziehbar.

Steht ja auch genau so in der auch schon erwähnten man-Page drin:

"sll_ifindex is the interface index of the interface (see netdevice(7)); 
0 matches any interface (only permitted for binding)."

Wie gesagt: Es lohnt sich, die auch zu lesen…

von Wicki W. (wicki)


Lesenswert?

Rolf M. schrieb:
> "sll_ifindex is the interface index of the interface (see netdevice(7));
> 0 matches any interface (only permitted for binding)."
>
> Wie gesagt: Es lohnt sich, die auch zu lesen…

ja, du hast recht.

und ich muss gestehen, dass ich dem inhalt der dieser alten nachricht
nicht wirklich bedeutung beigemessen habe, weil er mir auch noch
erklärte, dass in einem raw-packet auch die mac-adressen und das
protokoll drin stehen. (genau deswegen wollte ich das ja benutzen)

meine gestrige mails konnte ich nicht mehr korrigieren.
da sollte stehen: ich bin kein c-entwickler.
und auch deswegen sehe ich oft den wald vor lautern bäumen nicht.
jemand der das seit jahren täglich macht, der wirft einen blick
drauf und sieht sofort wo das problem liegt.

leider sagen aber auch oft leute, die das selbst nur mal
angelesen haben, etwas zu einem thema - gern dann auch mal im
"du-hast-ja-gar-keine-ahnung-tonfall". und dann gehen die
wirklich wichtigen und hilfreichen nachrichten unter.

und bei diesem bind-prpblem haben sich schon seit jahren die
leute in den foren die köpfe heissgeredet und man weiss nie,
welche infos nur wirklich stimmen und welche inzwischen schon
überholt oder nur bei einer bestimmten hardware oder os-version
richtig sind.

ich habe mehr als 4 jahre lang keine einzige zeile c programmiert
und auch davor nur dann, wenn es gar nicht zu vermeiden war.

jedenfalls werde ich mir das bind/bind2device noch mal näher
ansehen, das ergebnis zusammenfassen und posten.

ich finde es grade sehr spannend ;-)

von Wicki W. (wicki)


Lesenswert?

so, hier nun mal die zusammenfassung....

je nachdem, ob man "bind" oder "bindtodevice" aktiviert,
klappt es mal (bind) oder eben nicht (bind2dev) mit der
beschränkung auf ein bestimmtes interface.
vergisst man aber beim bind socket_address.sll_ifindex zu setzen
oder hat verstehentlich eine 0 drin stehen, dann wird auch wieder
jedes interace geliefert.

nun bin ich nur mal gespannt, ob das hier mit c-tags im
posting funktioniert.....

1
#include <stdbool.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <unistd.h>
5
#include <fcntl.h>
6
#include <stdio.h>
7
#include <string.h>
8
#include <sys/ioctl.h>
9
#include <sys/socket.h>
10
#include <linux/if_packet.h>
11
#include <linux/if_ether.h>
12
#include <arpa/inet.h>
13
#include <net/if.h> // if_nametoindex
14
15
/*  
16
gcc socket_read.c -o socket_read
17
18
blockierendes/nicht-blockierendes lesen von einem 
19
nw-interface oder von _allen_.
20
21
die beschränkung auf ein einzelnes device funktioniert _nur_
22
mit "bind" und nicht mit setsockopt/SO_BINDTODEVICE.
23
24
nur root kann ETH_P_ALL und SOCK_RAW verwenden!
25
26
*/
27
28
int rawrsockfd;
29
int sockopt;
30
unsigned      char recbuf[2048];
31
struct ifreq ifopts;   
32
struct sockaddr_ll socket_address;
33
ssize_t numbytes;
34
35
36
int init_raw_rec_socket(char devn[]){
37
        printf("init_rec_socket. DEV:[%s]\n",devn);
38
        rawrsockfd = socket(AF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
39
        if (rawrsockfd<0) { perror("setsockopt - (root?)"); close(rawrsockfd); return(EXIT_FAILURE); }
40
        strncpy(ifopts.ifr_name, devn, IFNAMSIZ-1);
41
        ioctl(rawrsockfd, SIOCGIFFLAGS, &ifopts);
42
        ifopts.ifr_flags |= IFF_PROMISC;
43
        ioctl(rawrsockfd, SIOCSIFFLAGS, &ifopts);
44
45
46
        /* Bind to device */
47
  socket_address.sll_family = AF_PACKET;
48
  //socket_address.sll_protocol = htons(ETH_P_ALL); // z.B. IPv4
49
  socket_address.sll_ifindex = if_nametoindex(devn); // Index der Netzwerkschnittstelle
50
  printf("sll_ifindex of %s is :%i\n",devn,socket_address.sll_ifindex);
51
  //socket_address.sll_ifindex = 0;  // 0 = lies _alle_ schnittstellen
52
53
54
/* 
55
//funktioniert _nicht_ bei raw-sockets !  
56
if (setsockopt(rawrsockfd, SOL_SOCKET, SO_BINDTODEVICE, devn, IFNAMSIZ-1) == -1)  {
57
  perror("SO_BINDTODEVICE");
58
  close(rawrsockfd);
59
  return -4;
60
}
61
*/
62
63
// da klappt es nur mit "bind"
64
  bind(rawrsockfd, (struct sockaddr*)&socket_address, sizeof(socket_address));
65
66
67
  int flags = fcntl(rawrsockfd, F_GETFL);
68
        
69
//        flags |= O_ASYNC;   // blockierend lesen oder
70
        flags |= O_NONBLOCK;  // nicht blockierend lesen
71
72
        fcntl(rawrsockfd, F_SETFL, flags);
73
        return 0;
74
}
75
76
77
int rec_raw(){  // raw-eth-receive
78
char ifname[IFNAMSIZ];
79
socklen_t addr_len = sizeof socket_address;
80
numbytes = recvfrom(rawrsockfd, recbuf, 2048, 0, (struct 
81
sockaddr*)&socket_address, &addr_len);
82
    if (numbytes<=-1){ printf("wait\b\b\b\b\b"); return 0; }
83
printf("listener: got packet %lu bytes from iface-index: %i ", 
84
numbytes,socket_address.sll_ifindex);
85
86
if (if_indextoname(socket_address.sll_ifindex, ifname) == NULL) {
87
        perror("if_indextoname");
88
        return 1;
89
    }
90
91
    printf("interface name: %s\n", ifname);
92
93
94
95
return 1;
96
}
97
98
void main()  {
99
  int c=0;
100
  printf("start\n");
101
  if (init_raw_rec_socket("lo")<0)exit(-1); 
102
  printf("init done\n");
103
  while (c<100000000) { int got=rec_raw(); c++; }
104
  printf("\nall done\n");
105
}

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

Hi,

in meinem eigenen Projekt hatte ich, statt Raw-Sockets
selber zu programmieren, einfach die libpcap eingebunden und
verwendet. Damit bekommt man auch schöne Filtermöglichkeiten.
Und es hat auch unter Linux und Windows gleichermaßen funktioniert.

Bei Interesse kann ich ein Beispiel liefern.

Gruß
Olaf

von Wicki W. (wicki)


Lesenswert?

hi olaf,



Olaf D. schrieb:
> .....einfach die libpcap eingebunden und
> verwendet. Damit bekommt man auch schöne Filtermöglichkeiten.
> Und es hat auch unter Linux und Windows gleichermaßen funktioniert.
>
> Bei Interesse kann ich ein Beispiel liefern.

ich hab mich jetzt auf den weg zu fuss eingeschossen - aber es ist
sicher nicht schlecht, wenn man das hier in dem zusammenhang
mal direkt vergleichen kann.

also gern ein beispiel... ;-)


viele gruesse

wicki

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Angehängte Dateien:

Lesenswert?

Hi,

sorry für die späte Antwort.
Dies Beispiel ist Qt basiert.

Bei Unklarheiten, einfach nochmal fragen.
Helfe gerne.

Gruß
Olaf

von Patrick B. (p51d)


Lesenswert?

Foobar schrieb:
> Ein raw-socket mit ETH_P_ALL bekommt alles, u.U. auch den eigenen
> ausgehenden Traffic.

Also soweit ich weiss kann das kein Socket einfach so. Dazu ist das 
loopback Interface da, welches durch ein separates Socket & Binding 
Daten zurücklesen könnte... (sind meine Erfahrungen unter Ubuntu, da 
habe ich einmal Wochen mit Fehlersuchen verbracht)

Wicki W. schrieb:
> Foobar schrieb:
>> Was willst du überhaupt machen?
>
> ich will die daten haben, bevor irgendwer oder -was sich damit befasst.

Dann wäre XDP allenfalls ein Thema für dich. Aber da funktioniert dann 
tcpdump nicht mehr, da die Daten komplett beim TCP/IP Stack vorbei 
geschleust werden!

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.