Sourcecode Fortschritt
-
version = "0.0.2.109 - Rev: 948"
MEILENSTEIN: http://www.c-plusplus.net/forum/p2078201#2078201
Erfolg! (thx to Mrx for simulating TCP checksum)tcp.h:
typedef struct { uint8_t src[4]; uint8_t dest[4]; uint8_t res; uint8_t prot; uint16_t length; } __attribute__((packed)) tcpPseudoHeader_t;
network.c:
uint16_t udptcpCalculateChecksum(void* p, uint16_t length, uint8_t srcIP[4], uint8_t destIP[4], uint16_t protocol) { tcpPseudoHeader_t pseudo; for (uint8_t i=0; i<4; i++) { pseudo.src[i] = srcIP[i]; pseudo.dest[i] = destIP[i]; } pseudo.length = htons(length); pseudo.prot = protocol; pseudo.res = 0; uint32_t pseudoHeaderChecksum = 0; uint8_t count = 12; // pseudo header contains 12 byte uint8_t* data = (uint8_t*)&pseudo; while (count > 1) { // pseudo header contains 6 WORD pseudoHeaderChecksum += (data[0] << 8) | data[1]; // Big Endian data += 2; count -= 2; } return internetChecksum(p, length, pseudoHeaderChecksum); // util.c }
... und schon geht der Weg zum ESTABLISHED:
12 14.042894 192.168.1.23 192.168.1.97 TCP 62 shockwave > telnet [SYN] Seq=0 Win=65535 Len=0 MSS=1460 SACK_PERM=1
13 14.052667 192.168.1.97 192.168.1.23 TCP 60 telnet > shockwave [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0
14 14.052691 192.168.1.23 192.168.1.97 TCP 54 shockwave > telnet [ACK] Seq=1 Ack=1 Win=65535 Len=0syn - syn ack - ack (TCP 3-way-handshake)
Mit dieser Version kann man erste versuche bei TCP/IP unternehmen, z.B. TELNET nutzen: http://www.henkessoft.de/OS_Dev/Bilder/rev.948_TCP_TELNET.PNG
http://www.manpagez.com/man/1/telnet/ <--- Kommandos
-
rev. 949:
TCP weiter ausgebaut, vor allem werden nun die Transition States angezeigt:
(CLOSED -->) LISTEN --> SYN_RECEIVED --> ESTABLISHEDLength: 66 Rcv: 00-10-A7-0F-0A-0C Transm.: 00-13-D4-11-27-A5 Ethernet 2. Prot. type: IP. IP version: 4, IP Header Length: 20 byte TCP: src port: 1232 dest port: 23 URG: 0 ACK: 0 PSH: 0 RST: 0 SYN: 1 FIN: 0 TCP prev. state: CLOSED TCP set from CLOSED to LISTEN. TCP curr. state: LISTEN Length: 66 Rcv: 00-10-A7-0F-0A-0C Transm.: 00-13-D4-11-27-A5 Ethernet 2. Prot. type: IP. IP version: 4, IP Header Length: 20 byte TCP: src port: 1232 dest port: 23 URG: 0 ACK: 0 PSH: 0 RST: 0 SYN: 1 FIN: 0 TCP prev. state: LISTEN EthernetSend: length: 54. >>> Transmission starts <<< Physical Address of Tx Buffer = 014051D8h packet sent. TCP curr. state: SYN_RECEIVED Length: 64 Rcv: 00-10-A7-0F-0A-0C Transm.: 00-13-D4-11-27-A5 Ethernet 2. Prot. type: IP. IP version: 4, IP Header Length: 20 byte TCP: src port: 1232 dest port: 23 URG: 0 ACK: 1 PSH: 0 RST: 0 SYN: 0 FIN: 0 TCP prev. state: SYN_RECEIVED TCP curr. state: ESTABLISHED
Da wir mit CLOSED starten und noch keine sockets bauen, müssen wir den Übergang zu LISTEN noch etwas künstlich durchführen:
void tcpListen(network_adapter_t* adapter) { adapter->TCP_PrevState = adapter->TCP_CurrState; adapter->TCP_CurrState = LISTEN; // TODO: more action needed? } Erst ab dort geht es geregelt weiter: [cpp]if (tcp->SYN && !tcp->ACK) // SYN { adapter->TCP_PrevState = adapter->TCP_CurrState; if (adapter->TCP_CurrState == CLOSED) { printf("TCP set from CLOSED to LISTEN.\n"); tcpListen(adapter); } else if (adapter->TCP_CurrState == LISTEN) { tcpSend(adapter, 0, 0, htons(tcp->destPort), adapter->IP_address, htons(tcp->sourcePort), transmittingIP, SYN_ACK_FLAG, 0 /*seqNumber*/ , tcp->sequenceNumber+htonl(1) /*ackNumber*/); adapter->TCP_CurrState = SYN_RECEIVED; } //...
telnet baut zumindest einen "Kontakt" zu uns auf, erwartet aber wohl mehr von seinem "Server" als nur einen 3-way-handshake:
Microsoft Telnet> open 192.168.1.97 Verbindungsaufbau zu 192.168.1.97...
-
version = "0.0.2.111 - Rev: 950"
TCP/Telnet-Datenübertragung, allerdings noch ohne aktive Bestätigung seitens PrettyOS:
http://www.henkessoft.de/OS_Dev/Bilder/rev.950_TCP_TELNET_DATEN.PNGMit strg+c kann man für Experimente den Zustand LISTEN erneut setzen.
TODO: Im state "ESTABLISHED" den Empfang von Daten mit der korrekten ACK bestätigen, damit der "Retransmission Timer" des Senders keine erneute Versendung dieses Paktes veranlasst (s. Bild oben).
Hier ist eine gute Literaturstelle für den Datenaustausch-Mechanismus: http://www.rhyshaden.com/tcp.htm
-
version = "0.0.2.112 - Rev: 951"
Experimentelle Version: Man muss genau 10 Byte senden, damit es genau klappt.
TODO: Länge des TCP-Datenpakets bestimmen und übergeben.Foto: http://www.henkessoft.de/OS_Dev/Bilder/rev.951_TCP_TELNET_DATENAUSTAUSCH.PNG
if (!tcp->SYN && !tcp->FIN && tcp->ACK) // ACK { adapter->TCP_PrevState = adapter->TCP_CurrState; if (adapter->TCP_CurrState == ESTABLISHED) // ESTABLISHED --> DATA TRANSFER { uint32_t length_of_tcpData = 10; // TODO: find length of data ! printf("\ntcp packet data:"); for (uint16_t i=0; i<length_of_tcpData; i++) { printf("%c", *(((uint8_t*)(tcp+1))+i) ); } tcpSend(adapter, 0, 0, htons(tcp->destPort), adapter->IP_address, htons(tcp->sourcePort), transmittingIP, ACK_FLAG, tcp->acknowledgmentNumber /*seqNumber*/, tcp->sequenceNumber+htonl(length_of_tcpData) /*ackNumber*/); }
ACK bei Datenempfang klappt nun auch (Berechnung von Seq.- und Ack.-Nummer).
Tippt man bei telnet "close" ein, kommt auch ein ACK. Mit strg+c kann man dann wieder bei LISTEN starten. Alles noch etwas "getrickst". Aber die ACK-Technik kann man sehr schön testen, und vor allem klappt es.
-
Version 0.0.2.113:
- Kernel Stack von 0x190000 nach 0x1000000 verschoben (Vermeidung einer Kollision mit KERNEL.BIN)
- Code aufgeräumt
-
version = "0.0.2.114 - Rev: 953"
Nun sind auch variable Längen des TCP-Datenpakets möglich. Wir reichen uns die Paketlänge von unten nach tcpReceive(network_adapter_t* adapter, tcpPacket_t* tcp, uint8_t transmittingIP[4], size_t length) durch.
Codeausschnitt:
if (adapter->TCP_CurrState == ESTABLISHED) // ESTABLISHED --> DATA TRANSFER { uint32_t tcpDataLength = -4 /* frame ? */ + length - (tcp->dataOffset << 2); printf("\ntcp packet data:"); for (uint16_t i=0; i<tcpDataLength; i++) { printf("%c", *(((uint8_t*)(tcp+1))+i) ); } tcpSend(adapter, 0, 0, htons(tcp->destPort), adapter->IP_address, htons(tcp->sourcePort), transmittingIP, ACK_FLAG, tcp->acknowledgmentNumber /*seqNumber*/, tcp->sequenceNumber+htonl(tcpDataLength) /*ackNumber*/); }
telnet:
open 192.168.1.97 sen ... sen ................ sen ....... close
anschließend bei PrettyOS wieder strg+c und erneut connection aufbauen, oder auch direkt open ... .
Kann alles in wireshark und bei PrettyOS verfolgt werden.
-
version = "0.0.2.115 - Rev: 954"
tcp.h weiter mit structs (DRAFT) gefüllt, um mit connections und dazu gehörigen sockets arbeiten zu können:
typedef struct { uint32_t SND_UNA; // Send Unacknowledged uint32_t SND_NXT; // Send Next uint16_t SND_WND; // Send Window uint32_t SND_ISS; // Initial send sequence number uint32_t RCV_NXT; // Sequence number of next received set uint16_t RCV_WND; // Receive Window uint32_t RCV_IRS; // Initial receive sequence number } __attribute__((packed)) tcpTransmissionControlBlock_t; typedef struct { uint16_t port; uint8_t IP[4]; network_adapter_t* adapter; } __attribute__((packed)) tcpSocket_t; typedef struct { tcpSocket_t localSocket; tcpSocket_t remoteSocket; tcpTransmissionControlBlock_t tcb; TCP_state TCP_PrevState; TCP_state TCP_CurrState; } __attribute__((packed)) tcpConnection_t; typedef struct { uint32_t SEG_SEQ; // Sequence number uint32_t SEG_ACK; // Acknoledgement number uint32_t SEG_LEN; // segment length uint32_t SEG_WND; // segment windows tcpFlags SEG_CTL; // control bits } __attribute__((packed)) tcpSegment_t;
-
version = "0.0.2.116 - Rev: 955"
TCP weiter ausgebaut. Die TCP States zu tcpConn umgehängt.
In network.c das TCP Server "passive open" eingerichtet:
// open TCP Server with State "LISTEN" adapter->tcpConn = malloc(sizeof(tcpConnection_t), 0, "tcp connection"); adapter->tcpConn->localSocket.port = getFreeSocket(); memcpy(adapter->tcpConn->localSocket.IP, adapter->IP_address, 4); adapter->tcpConn->TCP_PrevState = CLOSED; adapter->tcpConn->TCP_CurrState = LISTEN; // TODO: ...
networktypes.h fehlt im Commit
-
version = "0.0.2.117 - Rev: 956"
nun auch mit networktypes.h
Wichtige Links z.Z.:
http://www.tcpipguide.com/free/t_TCPConnectionPreparationTransmissionControlBlocksT-2.htm
http://www.cs.northwestern.edu/~agupta/cs340/project2/TCPIP_State_Transition_Diagram.pdf
-
Version 0.0.2.118:
- Ausgabe verbessert
- Mutexes verbessert
- PCI nutzt nun eine Liste statt eines statischen Arrays
- Aufgeräumt
-
Version 0.0.2.119:
- TCP überarbeitet. (BUG: Datentransfer funktioniert nicht.)
-
version = "0.0.2.120 - Rev: 959"
Aktives öffnen und TCP ("SYN") senden (mit strg+w) klappt.
Problem: Internet Checksum ist falsch! (Ursache bisher unklar, da das Senden von SYN ACK oder FIN ACK als server bisher klappt)Ablauf:
- PrettyOS bootet, holt sich per DHCP seine IP
- Man pingt PrettyOS von 192.168.1.23 an, damit der rechner in die ARP-Tabelle eingetragen wird (diese IP steht konkret in strg+w in keyboard.c drinnen, also ändern auf eigenen rechner)
- Man gibt strg+w ein bei PrettyOS um aktiv ein SYN zu senden
Fazit: SYN senden klappt, aber checksum falsch
TCP-Paket: 0402001700000000000000005002ffff88b00000 Checksum: 0x88b0 [incorrect, should be 0x2801 ...]
Test mit dem Simulationsprogramm ergibt die korrekte Checksum, also stimmen die Eingangsdaten in die Berechnung für diese nicht.
PS: der datentransfer in rev. 958 klappt (war offenbar ein TCP-Netzproblem)
-
version = "0.0.2.121 - Rev: 960"
Nun erfolgt die korrekte Berechnung der internet checksum. Fehler war im Pseudoheader. Dort fehlte noch die source IP.
Erstes erfolgreiches "active open" mit Senden von SYN, das zum State SYN_SENT führt (wireshark):
8 22.328396 192.168.1.97 192.168.1.23 TCP 60 blackjack > telnet [SYN] Seq=0 Win=65535 Len=0
blackjack steht für den gewählten Port mit der Nr. 1025.
static uint16_t getFreeSocket() { static uint16_t srcPort = 1025; return srcPort++; }
Um den kompletten Handshake zu sehen, gibt es einen einfachen Weg:
- telnet dienst (server) in Windows starten
- firewall tcp port 23 öffnen
- strg+w (active open mit SYN)
Resultat:
http://www.henkessoft.de/OS_Dev/Bilder/rev.960_TCP_TELNET_ACTIVE_OPEN.PNGSYN - SYN ACK - ACK
MrX hat auf die Schnelle einen kleinen TCP-Server geschrieben:
#include <SFML/Network.hpp> #include <iostream> #include <string> #include <cstdint> sf::TcpSocket Socket; void Func() { while(true) { char buffer[512]; size_t size; if(Socket.Receive(buffer, 512, size) == sf::Socket::Disconnected) return; buffer[size] = 0; std::cout << buffer; } } sf::Thread Thr(&Func); int main() { sf::TcpListener Listener; Listener.SetBlocking(true); uint16_t port; std::cout << "Bitte Port eingeben: "; std::cin >> port; Listener.Listen(port); std::cout << "Server lauscht... "; Listener.Accept(Socket); std::cout << "Verbunden." << std::endl; Thr.Launch(); std::string str; std::cin >> str; while(str != "exit") { Socket.Send(str.c_str(), str.length()); std::cin >> str; } return(0); }
Download der exe-Datei zusammen mit dem Sourcecode: http://www.henkessoft.de/OS_Dev/Downloads/TCPServer.zip (EXE etwas weiter entwickelt bezüglich Ausgaben)
-
version = "0.0.2.122 - Rev: 960"
- Beseitigung eines Fehlers, der nur ab und zu aufgetreten ist: versehentlich wurde mit bigEndian-Zahlen gerechnet, dabei verschiebt sich der Überlauf - falls vorhanden - nach rechts, da wir immer mit littleEndian-Zahlen "rechnen". Daher immer erst nach littleEndian umwandeln, dann rechnen, anschließend zurück nach bigEndian umwandeln:
htonl(htonl(tcp->sequenceNumber)+1)
<--- EDIT: das zweite htonl müsste ein ntohl sein, wurde inzwischen behoben.
- internetChecksumTest.cpp (von MrX, für code::blocks umgeschrieben) in /tools
-
version = "0.0.2.123 - Rev: 961"
ntohs/ntohl und htons/htonl im Code aus Gründen der Dokumentation geordnet.
vgl. http://beej.us/guide/bgnet/output/html/multipage/htonsman.html
Die Inhalte der Macros sind für IA32 bei x86 zufällig gleich, aber es geht um den Weg (Netz <---> Host), der verschieden ist.
// htonl = Host To Network Long // htons = Host To Network Short #define htons(v) ((((v) >> 8) & 0xFF) | (((v) & 0xFF) << 8)) #define htonl(v) ((((v) >> 24) & 0xFF) | (((v) >> 8) & 0xFF00) | (((v) & 0xFF00) << 8) | (((v) & 0xFF) << 24)) // ntohl = Network To Host Long // ntohs = Network To Host Short #define ntohs(v) htons(v) #define ntohl(v) htonl(v)
Diese Macros machen den Code mehr portabel, denn wenn z.B. die CPU gemäß Big Endian arbeitet dann ist die Umsetzung eben einfach ein "no operation".
Beispiel aus tpc.c:
tcp_send(connection, 0, 0, ACK_FLAG, tcp->acknowledgmentNumber /*seqNumber*/, htonl(ntohl(tcp->sequenceNumber)+tcpDataLength) /*ackNumber*/);
-
version = "0.0.2.124 - Rev: 962"
Ausgaben bei networking etwas vereinfacht.
-
version = "0.0.2.125 - Rev: 963"
Kleine Veränderungen in networking Ausgabe
-
Version 0.0.2.126:
- netutils.c/h gebildet. Enthält ntohl (u.ä.) sowie checksum-Funktionen
- Syscall-Aufruf verwendet festgelegte Register (vermeidet Fehler mit -fomit-frame-pointer)
- Code wird nun mit -fomit-frame-pointer gebaut
- Kleinigkeiten
-
Version 0.0.2.127:
- IPs und MACs jetzt mit memcmp verglichen
- IP_address und MAC_address in IP und MAC umbenannt
-
version = "0.0.2.128 - Rev: 966" (versehentlich zu hoch gedreht in ckernel.c)
PrettyOS goes to INTERNET via routing (FIRST TEST)
if(retchar == 'w') // Create & Bind connection { connection = tcp_createConnection(); // 94.142.241.111 auf Port 23 uint8_t destIP[4] ={94,142,241,111}; memcpy(connection->remoteSocket.IP, destIP, 4); connection->remoteSocket.port = 23; uint8_t sourceIP[4] ={IP_1,IP_2,IP_3,IP_4}; //HACK memcpy(connection->localSocket.IP, sourceIP, 4); network_adapter_t* adapter = network_getAdapter(sourceIP); printf("network adapter: %Xh\n", adapter); // check connection->adapter = adapter; if(adapter) { tcp_connect(connection); } return 0; }
Da wird die Starwars Story erzählt von einem sehr geschwätzigen telnet server (IP: 94.142.241.111). Vielen Dank an Cuervo für diesen Bomben-Tipp!
Foto: http://www.henkessoft.de/OS_Dev/Bilder/rev.966_INTERNET.PNG
Wer dies nachstellen möchte, benötigt eine rtl8139 Netzwerkkarte, und bitte die router-mac (erhält man oft mit arp -a aus dem arp-cache) und die IP/RIP (gleich), die man von DHCP erhält, in network.h eintragen. Das muss alles noch automatisiert werden.
Start ist z.Z. mit strg+w
HTTP geht noch nicht (fehlt noch die ACH-Number aus dem SYN-ACK des handshake), kommt demnächst wenn der TCB in connection gefüttert wird.