Hallo.
Ich habe mich mal an das Thema Socket-Programmierung in C herangewagt,
nicht weil ich ein konkretes Projekt realisieren wollte, sondern
vielmehr, weil ich lernen wollte, was unter der Haube meines Browsers
und E-Mail-Clients im Verborgenen vor sich geht.
Hierbei habe ich mich im Wesentlichen auf zwei Kommunikationswege
beschränkt. Die klassische tcp/ip-Verbindung von einem Client zu einem
Server auf Port 80 und das Senden und Empfangen von E-Mails in Form
eines eigenen E-Mail-Clients.
Mit dem entsprechenden Einarbeitungs- und Arbeitsaufwand ist mir das
auch gut gelungen. Mittels openssl habe ich dann auch gleich die
Verschlüsselung in C implementiert.
Angefangen bei 'normalen' StreamSockets, über RawSockets, habe ich
vieles ausprobiert, mit Erfolg.
Mein letztes Projekt besteht nun darin, dass ich mittels
meinen eigenen tcp/ip-Handshake via selbstgebautem Client durchführen
will.
Weil das Thema sehr komplex ist, habe ich mich erst einmal auf mein
eigenes Netzwerk beschränkt, in dem ich einen eigenen Server aufgesetzt
habe, welchen ich dann über das interface 'wlan0' und die bekannte IP,
wie auch MAC ansprechen wollte. Gesagt getan, dass Programm ist fertig,
reagiert aber nicht wie erwartet.
Ich setze alle Header (ETH;IP;TCP) zusammen, die Pruefsummen werden
berechnet und stimmen, somit sendet sendto() ein Paket ohne Payload, bei
dem das SYN-Bit gesetzt ist, um den Handshake einzuleiten.
Da ich bei der nun folgenden Kommunikation auf meinem Rechner bleibe,
die IP und MAC kenne, kein Router oder weitere Stationen
zwischengeschaltet sind, sollte es relativ problemlos von statten gehen,
dachte ich.
Mein Programm meldet mir, dass es ein TCP-Paket gesendet hat und
Wireshark bestätigt, ja ein Paket wurde gesendet. Allerdings gibt es
zwei Probleme. Einmal erhalte ich nicht die gewünschte Reaktion auf
meinen Handshake-Versuch, so sehe ich bspw. gar keine tcp/ip-Daten in
Wireshark. Darüber hinaus ist mir noch aufgefallen, dass mein Programm
gar nicht, wie geplant, von 127.0.0.1 zu 127.0.0.1 sendet, sondern
zuerst einmal einen Broadcast durchführt...
Es ist nicht im Sinne des Erfinders, mittels RAW Sockets ein bestehendes
Protokoll mit der bestehenden Protokollkennung (d.h. IP/TCP) zu
überlagern. Selbst in jenen Fällen, wo das mal gemacht wird (ICMP) gibt
es gewisse Nebeneffekte. Windows verhindert zudem aktiv, dass man TCP
Frames über RAW Sockets nutzt, aus Sicherheitsgründen.
Du müsstest also eine andere Protokollkennung verwenden, damit kein
Konflikt mit dem bestehenden Protokollstack entsteht.
> Windows verhindert zudem aktiv..,
Ich benutze Linux
> Es ist nicht im Sinne des Erfinders, mittels RAW Sockets ein bestehendes> Protokoll mit der bestehenden Protokollkennung (d.h. IP/TCP) zu> überlagern.
Das verstehe ich nicht. Dafür gibt es doch RAW und PACKET Sockets. Raw
Sockets haben problemlos funktioniert. setsockopt und HDRINCL und dann
weiss der Kernel, dass ich selbst Hand anlege.
Ähnlich müsste es doch auch mit PACKET Sockets gehen oder irre ich mich
da?
> Du müsstest also eine andere Protokollkennung verwenden
Wenn Du schreibst, ich müsste eine andere Protokollkennung verwenden,
welche wäre das denn dann? Ich will einen tcp/ip-Handshake initiieren
und dafür wäre tcp/ip doch die richtige Protokollkennung oder welche
sollte ich sonst dafür verwenden?
Matthias schrieb:> und dafür wäre tcp/ip doch die richtige Protokollkennung oder welche> sollte ich sonst dafür verwenden?
Auf beiden Seiten eine eigene. Immerhin willst du den kompletten
Netzwerkstack oberhalb MAC|IP ersetzen. Woher soll der vorhandene Stack
denn sonst wissen, dass nicht sein IP|TCP sondern deines gemeint ist?
> Auf beiden Seiten eine eigene. Immerhin willst du den kompletten> Netzwerkstack oberhalb MAC ersetzen.
Ja, genauer gesagt ab der MAC (ETH,IPH,TCPH)
> Auf beiden Seiten eine eigene.
Nein, nur auf der Seite des Absenders. Der Empfänger ist ein Apache auf
meinem Localhost.
> Woher soll der vorhandene Stack> denn sonst wissen, dass nicht sein IP/TCP sondern deines gemeint ist?
Ich habe gedacht, dass es ähnlich im Falle der Raw Sockets (setsockopt),
entweder auch hier ein Äquivalent geben würde oder aber der Kernel, da
ich PACKET Sockets verwende, direkt darüber informiert wird, dass ich
dies von Hand tue. In der Literatur habe ich nichts darüber gefunden,
dass bei PACKET Sockets oder genauer gesagt PF_PACKET i.V.m. RAW_SOCKETS
ein Hinweis an den Kernel oder den Stack erfolgen müsste.
Matthias schrieb:> Einmal erhalte ich nicht die gewünschte Reaktion auf> meinen Handshake-Versuch, so sehe ich bspw. gar keine tcp/ip-Daten in> Wireshark. Darüber hinaus ist mir noch aufgefallen, dass mein Programm> gar nicht, wie geplant, von 127.0.0.1 zu 127.0.0.1 sendet, sondern> zuerst einmal einen Broadcast durchführt...>
> und ich nicht weiss wieso!
Nun, in deinem uns unbekannten Programm machst du etwas grundlegendes
falsch. Da hilft nur debuggen und RFCs lesen. Ausserdem solltest du
schrittweise vorgehen, also zuerst das korrekte erstellen von eth frames
DANACH ip und DANACH tcp. Du kannst hier den loopback 127.0.0.1 nicht
verwenden, da du wlan0 und nicht das loopback device nutzt.
Du kannst keine IP deines Rechners wiederverwenden, weil dieser schon
einen tcp stack besitzt. Wenn du den Standard für die Requirements
gelesen hättest, wüsstest du, dass der bereits vorhandene stack ein RST
senden muss.
http://tools.ietf.org/html/rfc1122#page-40
Lies auch folgendes durch:
http://tools.ietf.org/html/rfc793
Du kannst entweder deinen arp cache manipulieren, oder das arp Protokoll
implementieren, um der Netzwerkschnittstelle eine zusätzliche ip zu
verpassen, von der das OS nichts weiss Ich versuche gerade ebenfalls tcp
in c mit packet sockets zu Implementieren. Bisher habe ich schon eth,
arp, IPv4 und icmp implementiert. Falls interesse besteht, hier das
Projekt:
https://github.com/Daniel-Abrecht/DPA-UCS
Leider fehlt dort die Doku noch.
@ Daniel A.
> ...also zuerst das korrekte erstellen von eth frames> DANACH ip und DANACH tcp
Das habe ich auch so gemacht. Ich habe einen char pointer erstellt,
welcher der Größe der MTU für das Device entspricht. Danach wurden alle
drei Header nacheinander darüber gecastet und im Anschluss erstelle ich
erst den ether_header, dann den ip_header und danach den tcp_header bzw.
setze deren Werte ein.
> ...wüsstest du, dass der bereits vorhandene stack ein RST> senden muss.
Das hätte ich noch nachvollziehen können. Ich habe aber gar keine
Antwort erhalten. Weil mein Programm statt der 127.0.0.1 eine
Broadcast-Adresse verwendet hat, habe ich das Fehlen einer Bestätigung
oder eines RSTs darauf zurück geführt.
Weil das in den Antworten angeklungen ist, vielleicht noch ein Hinweis.
Sicher, ich habe in diverser Literatur und in Suchmaschinenergebnissen
teilweise gelesen, dass bei meinem Vorhaben eigentlich der Stack im Wege
steht.
Was mich dann aber doch bewogen hat es mit einem "einfachen" Client zu
versuchen, welcher die drei Header -den PseudoHeader mal ausgenommen-
direkt über das Netzwerkinterface sendet, war der Umstand, dass ich in
Büchern Beispiele dafür gefunden habe, die selbiges mit arp oder icmp
machen. Mir ist nicht klar, wieso das bei genannten Protokollen
funktioniert oder zu funktionieren scheint.
Bei arp müsste der Kernel doch auch oder gerade dazwischenfunken.
Dein Projekt werde ich mir ansehen, vielen Dank dafür.
Matthias schrieb:> @ Daniel A.>>> ...also zuerst das korrekte erstellen von eth frames>> DANACH ip und DANACH tcp>> Das habe ich auch so gemacht. Ich habe einen char pointer erstellt,> welcher der Größe der MTU für das Device entspricht.
Die Formulierung ist hier noch etwas ungenau. Ein Pointer hat immer die
gleiche grösse. Du wolltest vermutlich sagen, du hast einen
Speicherbereich dessen Grösse der MTU entspricht reserviert (mit malloc,
oder per array) und einen char pointer darauf zeigen lassen.
> Danach wurden alle> drei Header nacheinander darüber gecastet
Man kann nicht "darüber" casten, man castet einfach, sprich wandelt
einen Datentyp um. Du meintest vermutlich, du hast denn offset zum
Anfang der Header zu deinem Charpointer addiert und in den jeweiligen
structtyp, der diesen Hearer repräsentiert gecastet.
> und im Anschluss erstelle ich> erst den ether_header, dann den ip_header und danach den tcp_header bzw.> setze deren Werte ein.
Die reihenfolge, in der das Programm dies tut ist dem Empfänger egal.
Mir geht es um die Reihenfolge in der du es Implementierst:
1) Also ip und tcp erstmal ignorieren.
2) Das Empfangen von Ethernet frames implementieren
3) Das empfangen Testen, kommen sinnvolle werte an, siehst du das selbe
wie in wireshark, etc.
4) Das senden von Ethernet frames implementieren.
5) Das senden von Ethernet frames testen, zeigt wireshark die
gewünschten werte an, etc.
6) Das Empfangen von IP frames implementieren
...
Also das Implementieren in einzelschritte aufteilen und diese sofort
testen. Sofort alles implementieren zu wollen geht meistens schief.
>> ...wüsstest du, dass der bereits vorhandene stack ein RST>> senden muss.>> Das hätte ich noch nachvollziehen können. Ich habe aber gar keine> Antwort erhalten. Weil mein Programm statt der 127.0.0.1 eine> Broadcast-Adresse verwendet hat, habe ich das Fehlen einer Bestätigung> oder eines RSTs darauf zurück geführt.
Die Wireshark ausgabe sieht nicht so aus, als ob irgendein frame Korrekt
wäre. Allerdings preferiere ich das Lesen eines Hexdumps statt der
Wiresharkinterpretation.
> Beispiele dafür gefunden habe, die selbiges mit arp oder icmp> machen. Mir ist nicht klar, wieso das bei genannten Protokollen> funktioniert oder zu funktionieren scheint.>> Bei arp müsste der Kernel doch auch oder gerade dazwischenfunken.
Bei arp gibt es einen Request und eine Response. Eine arp-antwort ohne
request wird verworfen. Die eigene Implementierung muss nur für eine
noch nicht im Netzwerk vorhandene IP seine mac in einer arp Response
senden.
ICMP und TCP basieren auf IP. Wenn der Systemeigener arp dienst eine IP
nicht als seine eigene kennt, wird auf das IP packet nicht reagiert.
Deine eigene Implementierung kann dann auf dieses Packet reagieren.
Daniel A. schrieb:
> Die Formulierung ist hier noch etwas ungenau.
und
> Man kann nicht "darüber" casten,...
Du hast mich in beiden Punkten richtig verstanden und beides korrekt
formuliert.
> 1) Also ip und tcp erstmal ignorieren.
Das ich die tcp- und ip-Header direkt miteingesetzt habe, hatte den
einfachen Hintergrund, dass ich es für pragmatisch gehalten habe. Ich
habe beides aus meinem vorherigen Programm mit dem RawSocket, welches
einwandfrei funktioniert hat, übernommen. Die Bibliotheken waren einmal
erstellt, aber viel wichtiger, die hierin enthaltenen Funktionen, welche
die Header erstellt haben, sind gelaufen und haben das geliefert, was
erwartet wurde.
> Die Wireshark-Ausgabe sieht nicht so aus, als ob irgendein frame Korrekt> wäre.
Nein, wirklich nicht. Ich dachte, vielleicht sieht man anhand dessen,
was Wireshark ausgegeben oder auch nicht ausgegeben hat, welcher
Programm-Teil, welche spezielle Funktion oder dergleichen typisch für
derartige, falsche Ausgaben ist.
Ich werde das Ganze jetzt noch einmal versuchen, erst einmal mit einem
einzelnen, isolierten eth_hdr.
> Wenn der Systemeigener arp dienst eine IP> nicht als seine eigene kennt, wird auf das IP packet nicht reagiert.> Deine eigene Implementierung kann dann auf dieses Packet reagieren.
Verstehe ich das richtig, dass ich somit vermeide, dass mir der Stack
des OS dazwischen funkt und ich so mein eigenes Paket offiziell
erstellen kann? Einfach gesprochen...
Matthias schrieb:> Daniel A. schrieb:>> Wenn der Systemeigener arp dienst eine IP>> nicht als seine eigene kennt, wird auf das IP packet nicht reagiert.>> Deine eigene Implementierung kann dann auf dieses Packet reagieren.>> Verstehe ich das richtig, dass ich somit vermeide, dass mir der Stack> des OS dazwischen funkt und ich so mein eigenes Paket offiziell> erstellen kann? Einfach gesprochen...
Ja, genau so ist es.
Ich habe es ausprobiert, klappen tut es aber leider nicht.
Der von mir gesendete ARP-Reply ist in Ordnung, wird via Wireshark
angezeigt, aber der ARP-Cache meines Desktop-Rechners bleibt vollkommen
identisch.
Ich habe mal meine Suchmaschine angeworfen und bemerkt, dass es ähnliche
Probleme immer in Verbindung mit einem bestimmten OS gab, nämlich Ubuntu
12.04, dass benutze ich im Übrigen auch.
IP-Forwarding und dergleichen habe ich berücksichtigt, insofern scheiden
diese schon Mal als Ursache aus, auf den ersten Blick jedenfalls.
Ein Tipp war, in den ARP-Logs nach etwaigen Fehlern zu suchen, wo aber
sind ARP-Logs zu finden?? In meinem LOG-Verzeichnis finde ich nichts mit
einem Bezug hierauf.
Frage, da der Ansatz aus mir unerfindlichen Gründen nicht funktioniert,
kann ich nicht einfach unter '/etc/network/interfaces' eine statische IP
hinzufügen und das Problem so umgehen?
Eine zweite Möglichkeit, welche mir eingefallen ist, was wäre, wenn ich
einen Apache auf meinem Desktop-Rechner aufsetze und diesen mit DHCP
ausstatte, so dass dieser mir einfach eine zweite IP zuweist?!
Matthias schrieb:> Der von mir gesendete ARP-Reply ist in Ordnung, wird via Wireshark> angezeigt, aber der ARP-Cache meines Desktop-Rechners bleibt vollkommen> identisch.
Davon würde ich mich gerne selbst überzeugen. Dazu sind dia Ausgabe von
ifconfig, arp -a davor und danach, sowie ein Hexdump des ARP-Request und
def ARP-Reply erforderlich.
> Ich habe mal meine Suchmaschine angeworfen und bemerkt, dass es ähnliche> Probleme immer in Verbindung mit einem bestimmten OS gab, nämlich Ubuntu> 12.04, dass benutze ich im Übrigen auch.
Ich verwende auch ubuntu, aber ich hatte damit keine Probleme.
> kann ich nicht einfach unter '/etc/network/interfaces' eine statische IP> hinzufügen und das Problem so umgehen?
Nein, es gibt 2 andere Möglichkeiten. Aber man sollte die Uhrsache
bekämpfen, nicht die Wirkung.
> Eine zweite Möglichkeit, welche mir eingefallen ist, was wäre, wenn ich> einen Apache auf meinem Desktop-Rechner aufsetze und diesen mit DHCP> ausstatte, so dass dieser mir einfach eine zweite IP zuweist?!
Das ist zwar möglich, löst aber keine Probleme und umgeht diese auch
nicht.
PS: In Wireshark kann man von einem Packet einen Hexdump erzeugen, indem
man mit der rechten Maustaste auf das Packet klickt und dann
"Copy->Bytes->Hex Stream" anklickt.
Die Ausgaben von ifconfig, arp -a und ip addr sind vor, wie auch nach
dem reply, vollkommen identisch.
Das ist die Ausgabe des Wireshark-HexDumps eines gesendeten Reply
Paketes.
xxxxxxxxxxxxffffffffffff08060001080006040002ffffffffffffc0a80266xxxxxxxx
xxxxc0a80265
xxxxxxxxxxxx = empfaenger
ffffffffffff = spoof_sender
> ...sowie ein Hexdump des ARP-Request und def ARP-Reply erforderlich.
Ich habe gedacht, Du meintest Cache Poisoning und habe daher immer nur
das Reply gesendet.
Ich habe gerade bemerkt, dass 50% aller Router und Switches alle
Ethernetframes verwerfen, die die selbe Quell- und Ziel- mac adresse
haben. Ich empfehle deshalb ein ethernet loopback device zu bauen, wie
hier beschrieben:
https://www.juniper.net/documentation/en_US/junos14.2/topics/task/operational/fe-ge-loopback-plug-rj-45.html
Im Anhang ist ein Bild von meinem. Ist wirklich hilfreich.
Um es zu verwenden, einfach zuerst dem Ethernet-Interface eine ip
zuweisen:
1
# beispiel
2
sudo ifconfig eth0 192.168.8.5/24
und danach das Device einstecken. Den Packetsocket an das
Eternet-Interface binden, und mit dem arp-reply eine ip-addresse im
selben subnet wie die ip des Eternet-Interface senden, aber nicht die
selbe Adresse wie die des Eternet-Interface nutzen.