diff options
author | David Doan <daviddoan@Davids-MacBook-Pro-70.local> | 2023-11-02 18:57:56 -0400 |
---|---|---|
committer | David Doan <daviddoan@Davids-MacBook-Pro-70.local> | 2023-11-02 18:57:56 -0400 |
commit | 5d7ffd42b638f33d32c01c46f853e26a5028b552 (patch) | |
tree | ed7220178ce76a213341f14fb27c6af63e95209a /pkg/ipstack/ipstack.go | |
parent | a8c9b48821c85e072ca9054621928e415540b12c (diff) |
milestone 1
Diffstat (limited to 'pkg/ipstack/ipstack.go')
-rw-r--r-- | pkg/ipstack/ipstack.go | 350 |
1 files changed, 346 insertions, 4 deletions
diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index fe8d743..78a6b96 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -20,11 +20,15 @@ import ( "github.com/google/netstack/tcpip/header" "github.com/pkg/errors" "iptcp/pkg/lnxconfig" + "iptcp/pkg/iptcp_utils" "log" "net" "net/netip" "sync" "time" + "strings" + "math/rand" + // "github.com/google/netstack/tcpip/header" ) const ( @@ -35,6 +39,7 @@ const ( SIZE_OF_RIP_ENTRY = 12 RIP_PROTOCOL = 200 TEST_PROTOCOL = 0 + TCP_PROTOCOL = 6 SIZE_OF_RIP_HEADER = 4 MAX_TIMEOUT = 12 ) @@ -682,16 +687,20 @@ func StartRipRoutines() { // RegisterProtocolHandler registers a protocol handler for a given protocol number // Returns true if the protocol number is valid, false otherwise func RegisterProtocolHandler(protocolNum int) bool { - if protocolNum == RIP_PROTOCOL { + switch protocolNum { + case RIP_PROTOCOL: protocolHandlers[protocolNum] = HandleRIP go StartRipRoutines() return true - } - if protocolNum == TEST_PROTOCOL { + case TEST_PROTOCOL: protocolHandlers[protocolNum] = HandleTestPackets return true + case TCP_PROTOCOL: + protocolHandlers[protocolNum] = HandleTCP + return true + default: + return false } - return false } // HandleRIP handles incoming RIP packets in the following way: @@ -853,6 +862,132 @@ func HandleTestPackets(src *Interface, message []byte, hdr *ipv4header.IPv4Heade return nil } +func HandleTCP(src *Interface, message []byte, hdr *ipv4header.IPv4Header) error { + fmt.Println("I see a TCP packet") + // ipHeaderSize := hdr.Len + tcpHeaderAndData := message + tcpHdr := iptcp_utils.ParseTCPHeader(tcpHeaderAndData) + tcpPayload := tcpHeaderAndData[tcpHdr.DataOffset:] + tcpChecksumFromHeader := tcpHdr.Checksum + tcpHdr.Checksum = 0 + tcpComputedChecksum := iptcp_utils.ComputeTCPChecksum(&tcpHdr, hdr.Src, hdr.Dst, tcpPayload) + + var tcpChecksumState string + if tcpComputedChecksum == tcpChecksumFromHeader { + tcpChecksumState = "OK" + } else { + tcpChecksumState = "FAIL" + } + + if tcpChecksumState == "FAIL" { + // drop the packet + fmt.Println("checksum failed, dropping packet") + return nil + } + + switch tcpHdr.Flags { + case 0x02: + fmt.Println("I see a SYN flag") + // if the SYN flag is set, then send a SYNACK + available := false + mapMutex.Lock() + for _, socketEntry := range VHostSocketMaps { + if socketEntry.LocalPort == tcpHdr.DstPort && socketEntry.LocalIP == hdr.Dst.String() && socketEntry.State == Listening{ + // add a new socketEntry to the map + newEntry := &SocketEntry{ + LocalPort: tcpHdr.DstPort, + RemotePort: tcpHdr.SrcPort, + LocalIP: hdr.Dst.String(), + RemoteIP: hdr.Src.String(), + State: SYNRECIEVED, + Socket: socketsMade, + } + socketsMade += 1 + // add the entry to the map + VHostSocketMaps[socketsMade] = newEntry + available = true + break + } + } + mapMutex.Unlock() + if !available { + fmt.Println("no socket available") + return nil + } + // make the header + tcpHdr := &header.TCPFields{ + SrcPort: tcpHdr.DstPort, + DstPort: tcpHdr.SrcPort, + SeqNum: tcpHdr.SeqNum, + AckNum: tcpHdr.SeqNum + 1, + DataOffset: 20, + Flags: 0x12, + WindowSize: MAX_WINDOW_SIZE, + Checksum: 0, + UrgentPointer: 0, + } + // make the payload + synAckPayload := []byte{} + err := SendTCP(tcpHdr, synAckPayload, hdr.Dst, hdr.Src) + if err != nil { + fmt.Println(err) + } + break + case 0x12: + fmt.Println("I see a SYNACK flag") + // lookup for socket entry and update its state + mapMutex.Lock() + for _, socketEntry := range VHostSocketMaps { + if socketEntry.LocalPort == tcpHdr.DstPort && socketEntry.LocalIP == hdr.Dst.String() && socketEntry.State == SYNSENT { + socketEntry.State = Established + break + } + } + mapMutex.Unlock() + + // send an ACK + // make the header + tcpHdr := &header.TCPFields{ + SrcPort: tcpHdr.DstPort, + DstPort: tcpHdr.SrcPort, + SeqNum: tcpHdr.SeqNum, + AckNum: tcpHdr.SeqNum + 1, + DataOffset: 20, + Flags: 0x10, + WindowSize: MAX_WINDOW_SIZE, + Checksum: 0, + UrgentPointer: 0, + } + // make the payload + ackPayload := []byte{} + err := SendTCP(tcpHdr, ackPayload, hdr.Dst, hdr.Src) + if err != nil { + fmt.Println(err) + } + break + case 0x10: + fmt.Println("I see an ACK flag") + // lookup for socket entry and update its state + // set synChan to true (TODO) + + mapMutex.Lock() + for _, socketEntry := range VHostSocketMaps { + if socketEntry.LocalPort == tcpHdr.DstPort && socketEntry.LocalIP == hdr.Dst.String() && socketEntry.State == SYNRECIEVED { + socketEntry.State = Established + break + } + } + mapMutex.Unlock() + break + default: + fmt.Println("I see a non TCP packet") + break + } + + + return nil +} + // *********************************************** HELPERS ********************************************************** // Route returns the next HOP, based on longest prefix match for a given ip @@ -1006,3 +1141,210 @@ func CleanUp() { time.Sleep(5 * time.Millisecond) } + +// ************************************** TCP FUNCTIONS ********************************************************** + +type ConnectionState string +const ( + Established ConnectionState = "ESTABLISHED" + Listening ConnectionState = "LISTENING" + Closed ConnectionState = "CLOSED" + SYNSENT ConnectionState = "SYNSENT" + SYNRECIEVED ConnectionState = "SYNRECIEVED" + MAX_WINDOW_SIZE = 65535 +) + +// VTCPListener represents a listener socket (similar to Go’s net.TCPListener) +type VTCPListener struct { + LocalAddr string + LocalPort uint16 + RemoteAddr string + RemotePort uint16 + Socket int + State ConnectionState +} + +// // VTCPConn represents a “normal” socket for a TCP connection between two endpoints (similar to Go’s net.TCPConn) +type VTCPConn struct { + LocalAddr string + LocalPort uint16 + RemoteAddr string + RemotePort uint16 + Socket int + State ConnectionState + Buffer []byte +} + +type SocketEntry struct { + Socket int + LocalIP string + LocalPort uint16 + RemoteIP string + RemotePort uint16 + State ConnectionState +} + +// create a socket map to print the local and remote ip and port as well as the state of the socket +var VHostSocketMaps = make(map[int]*SocketEntry) +var mapMutex = &sync.Mutex{} +var socketsMade = 0 +var startingSeqNum = rand.Uint32() + +// Listen Sockets +func VListen(port uint16) (*VTCPListener, error) { + myIP := GetInterfaces()[0].IpPrefix.Addr() + listener := &VTCPListener{ + Socket: socketsMade, + State: Listening, + LocalPort: port, + LocalAddr: myIP.String(), + } + + // add the socket to the socket map + mapMutex.Lock() + VHostSocketMaps[socketsMade] = &SocketEntry{ + Socket: socketsMade, + LocalIP: myIP.String(), + LocalPort: port, + RemoteIP: "0.0.0.0", + RemotePort: 0, + State: Listening, + } + mapMutex.Unlock() + socketsMade += 1 + return listener, nil + +} + +func (l *VTCPListener) VAccept() (*VTCPConn, error) { + // synChan = make(chan bool) + for { + // wait for a SYN request + mapMutex.Lock() + for _, socketEntry := range VHostSocketMaps { + if socketEntry.State == Established { + // create a new VTCPConn + conn := &VTCPConn{ + LocalAddr: socketEntry.LocalIP, + LocalPort: socketEntry.LocalPort, + RemoteAddr: socketEntry.RemoteIP, + RemotePort: socketEntry.RemotePort, + Socket: socketEntry.Socket, + State: Established, + } + return conn, nil + } + } + mapMutex.Unlock() + + } +} + +func GetRandomPort() uint16 { + const ( + minDynamicPort = 49152 + maxDynamicPort = 65535 + ) + return uint16(rand.Intn(maxDynamicPort - minDynamicPort) + minDynamicPort) +} + +func VConnect(ip string, port uint16) (*VTCPConn, error) { + // get my ip address + myIP := GetInterfaces()[0].IpPrefix.Addr() + // get random port + portRand := GetRandomPort() + + tcpHdr := &header.TCPFields{ + SrcPort: portRand, + DstPort: port, + SeqNum: startingSeqNum, + AckNum: startingSeqNum, + DataOffset: 20, + Flags: header.TCPFlagSyn, + WindowSize: MAX_WINDOW_SIZE, + Checksum: 0, + UrgentPointer: 0, + } + payload := []byte{} + ipParsed, err := netip.ParseAddr(ip) + if err != nil { + return nil, err + } + err = SendTCP(tcpHdr, payload, myIP, ipParsed) + if err != nil { + return nil, err + } + + conn := &VTCPConn{ + LocalAddr: myIP.String(), + LocalPort: portRand, + RemoteAddr: ip, + RemotePort: port, + Socket: socketsMade, + State: Established, + Buffer: []byte{}, + } + + // add the socket to the socket map + mapMutex.Lock() + VHostSocketMaps[socketsMade] = &SocketEntry{ + Socket: socketsMade, + LocalIP: myIP.String(), + LocalPort: portRand, + RemoteIP: ip, + RemotePort: port, + State: SYNSENT, + } + mapMutex.Unlock() + socketsMade += 1 + + return conn, nil +} + +func SendTCP(tcpHdr *header.TCPFields, payload []byte, myIP netip.Addr, ipParsed netip.Addr) error { + checksum := iptcp_utils.ComputeTCPChecksum(tcpHdr, myIP, ipParsed, payload) + tcpHdr.Checksum = checksum + + tcpHeaderBytes := make(header.TCP, iptcp_utils.TcpHeaderLen) + tcpHeaderBytes.Encode(tcpHdr) + + ipPacketPayload := make([]byte, 0, len(tcpHeaderBytes)+len(payload)) + ipPacketPayload = append(ipPacketPayload, tcpHeaderBytes...) + ipPacketPayload = append(ipPacketPayload, []byte(payload)...) + + // lookup neighbor + address := ipParsed + hop, err := Route(address) + if err != nil { + fmt.Println(err) + return err + } + myAddr := hop.Interface.IpPrefix.Addr() + + for _, neighbor := range GetNeighbors()[hop.Interface.Name] { + if neighbor.VipAddr == address || + neighbor.VipAddr == hop.VIP && hop.Type == "S" { + bytesWritten, err := SendIP(&myAddr, neighbor, TCP_PROTOCOL, ipPacketPayload, ipParsed.String(), nil) + fmt.Printf("Sent %d bytes to %s\n", bytesWritten, neighbor.VipAddr.String()) + if err != nil { + fmt.Println(err) + } + } + } + return nil +} + +func SprintSockets() string { + tmp := "" + for _, socket := range VHostSocketMaps { + // remove the spaces of the local and remote ip variables + socket.LocalIP = strings.ReplaceAll(socket.LocalIP, " ", "") + socket.RemoteIP = strings.ReplaceAll(socket.RemoteIP, " ", "") + if socket.RemotePort == 0 { + tmp += fmt.Sprintf("%d\t%s\t%d\t%s\t\t%d\t%s\n", socket.Socket, socket.LocalIP, socket.LocalPort, socket.RemoteIP, socket.RemotePort, socket.State) + continue + } + tmp += fmt.Sprintf("%d\t%s\t%d\t%s\t%d\t%s\n", socket.Socket, socket.LocalIP, socket.LocalPort, socket.RemoteIP, socket.RemotePort, socket.State) + } + return tmp +}
\ No newline at end of file |