diff options
author | sotech117 <michael_foiani@brown.edu> | 2023-10-09 18:05:30 -0400 |
---|---|---|
committer | sotech117 <michael_foiani@brown.edu> | 2023-10-09 18:05:30 -0400 |
commit | 3932d9b5135c0f1831ed2cb714083559a251ff20 (patch) | |
tree | a17eaa94950729692d68d0f843c1fe28c6fef467 /pkg/ipstack | |
parent | 1253398ac33d51559573972149384d1019c3ec56 (diff) |
implement recvip and sendip, with tests to know it's working
Diffstat (limited to 'pkg/ipstack')
-rw-r--r-- | pkg/ipstack/ipstack.go | 270 | ||||
-rw-r--r-- | pkg/ipstack/ipstack_test.go | 148 |
2 files changed, 290 insertions, 128 deletions
diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 5546e32..2063c67 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -2,8 +2,11 @@ package ipstack import ( "fmt" + ipv4header "github.com/brown-csci1680/iptcp-headers" + "github.com/google/netstack/tcpip/header" "github.com/pkg/errors" "iptcp/pkg/lnxconfig" + "log" "net" "net/netip" "time" @@ -17,9 +20,9 @@ const ( // STRUCTS --------------------------------------------------------------------- type Interface struct { - Name string - IpPrefix netip.Prefix - + Name string + IpPrefix netip.Prefix + UdpAddr netip.AddrPort RecvSocket net.UDPConn SocketChannel chan bool State bool @@ -51,6 +54,7 @@ type Hop struct { } // GLOBAL VARIABLES (data structures) ------------------------------------------ +var myVIP netip.Addr var myInterfaces []*Interface var myNeighbors = make(map[string][]*Neighbor) @@ -98,6 +102,7 @@ func Initialize(lnxFilePath string) error { i := &Interface{ Name: iface.Name, IpPrefix: prefix, + UdpAddr: iface.UDPAddr, RecvSocket: net.UDPConn{}, SocketChannel: make(chan bool), State: false, @@ -137,6 +142,7 @@ func Initialize(lnxFilePath string) error { myNeighbors[neighbor.InterfaceName] = append(myNeighbors[neighbor.InterfaceName], n) // add to routing table + // TODO: REVISIT AND SEE IF "24" IS CORRECT neighborPrefix := netip.PrefixFrom(neighbor.DestAddr, 24) routingTable[neighborPrefix] = Hop{LOCAL_COST, neighbor.InterfaceName} } @@ -171,10 +177,13 @@ func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { if !isUp { // don't call the listeners if interface is down continue } - - fmt.Println("no activity, actively listening on ", socket.LocalAddr().String()) - // TODO: remove these training wheels, call the listener function + // TODO: remove these "training wheels" time.Sleep(1 * time.Millisecond) + err := RecvIP(socket, &isUp) + if err != nil { + fmt.Println("Error receiving IP packet", err) + return + } } }() @@ -205,126 +214,6 @@ func InterfaceDown(iface *Interface) { iface.State = false } -/* - -func ListerToInterfaces() { - for _, iface := range myInterfaces { - go RecvIp(iface) - } -} -func ValidateChecksum(b []byte, fromHeader uint16) uint16 { - checksum := header.Checksum(b, fromHeader) - - return checksum -} - -func SendIp(dst netip.Addr, port uint16, protocolNum uint16, data []byte, iface Interface) error { - bindLocalAddr, err := net.ResolveUDPAddr("udp4", iface.UDPAddr.String()) - if err != nil { - log.Panicln("Error resolving address: ", err) - } - - addrString := fmt.Sprintf("%s:%s", dst, port) - remoteAddr, err := net.ResolveUDPAddr("udp4", addrString) - if err != nil { - log.Panicln("Error resolving address: ", err) - } - - fmt.Printf("Sending to %s:%d\n", - remoteAddr.IP.String(), remoteAddr.Port) - - // Bind on the local UDP port: this sets the source port - // and creates a conn - conn, err := net.ListenUDP("udp4", bindLocalAddr) - if err != nil { - log.Panicln("Dial: ", err) - } - - // Start filling in the header - message := data[20:] - hdr := ipv4header.IPv4Header{ - Version: data[0] >> 4, - Len: 20, // Header length is always 20 when no IP options - TOS: data[1], - TotalLen: ipv4header.HeaderLen + len(message), - ID: data[4], - Flags: data[6] >> 5, - FragOff: data[6] & 0x1f, - TTL: data[8], - Protocol: data[9], - Checksum: 0, // Should be 0 until checksum is computed - Src: netip.MustParseAddr(iface.addr.String()), - Dst: netip.MustParseAddr(dst.String()), - Options: []byte{}, - } - - // Assemble the header into a byte array - headerBytes, err := hdr.Marshal() - if err != nil { - log.Fatalln("Error marshalling header: ", err) - } - - // Compute the checksum (see below) - // Cast back to an int, which is what the Header structure expects - hdr.Checksum = int(ComputeChecksum(headerBytes)) - - headerBytes, err = hdr.Marshal() - if err != nil { - log.Fatalln("Error marshalling header: ", err) - } - - bytesToSend := make([]byte, 0, len(headerBytes)+len(message)) - bytesToSend = append(bytesToSend, headerBytes...) - bytesToSend = append(bytesToSend, []byte(message)...) - - // Send the message to the "link-layer" addr:port on UDP - bytesWritten, err := conn.WriteToUDP(bytesToSend, remoteAddr) - if err != nil { - log.Panicln("Error writing to socket: ", err) - } - fmt.Printf("Sent %d bytes\n", bytesWritten) -} - -func ComputeChecksum(b []byte) uint16 { - checksum := header.Checksum(b, 0) - checksumInv := checksum ^ 0xffff - - return checksumInv -} - -func ForwardIP(data []byte) error { -} - -func AddRecvHandler(protocolNum uint8, callbackFunc HandlerFunc) error { - if protocolHandlers[protocolNum] != nil { - fmt.Printf("Warning: Handler for protocol %d already exists", protocolNum) - } - protocolHandlers[protocolNum] = callbackFunc - return nil -} - -func RemoveRecvHandler(protocolNum uint8) error { - // consider error - if protocolHandlers[protocolNum] == nil { - return errors.Errorf("No handler for protocol %d", protocolNum) - } - delete(protocolHandlers, protocolNum) - return nil -} - -// func routeRip(data []byte) (error) { -// // deconstruct packet -// newRIPMessage := RIPMessage{} -// newRIPMessage.command = data[0] -// newRIPMessage.numEntries = data[1] -// newRIPMessage.entries = make([]RIPEntry, newRIPMessage.numEntries) -// } - -func GetNeighbors() []netip.Addr { - return myNeighbors -} -*/ - func GetInterfaceByName(ifaceName string) (*Interface, error) { for _, iface := range myInterfaces { if iface.Name == ifaceName { @@ -335,6 +224,14 @@ func GetInterfaceByName(ifaceName string) (*Interface, error) { return nil, errors.Errorf("No interface with name %s", ifaceName) } +func GetNeighborsToInterface(ifaceName string) ([]*Neighbor, error) { + if neighbors, ok := myNeighbors[ifaceName]; ok { + return neighbors, nil + } + + return nil, errors.Errorf("No interface with name %s", ifaceName) +} + func SprintInterfaces() string { buf := "" for _, iface := range myInterfaces { @@ -376,7 +273,10 @@ func CleanUp() { if iface.SocketChannel != nil { close(iface.SocketChannel) } - iface.RecvSocket.Close() + err := iface.RecvSocket.Close() + if err != nil { + continue + } } // go through the neighbors, pop thread & close the UDP FDs @@ -385,7 +285,10 @@ func CleanUp() { if n.SocketChannel != nil { close(n.SocketChannel) } - n.SendSocket.Close() + err := n.SendSocket.Close() + if err != nil { + continue + } } } @@ -398,3 +301,114 @@ func CleanUp() { time.Sleep(5 * time.Millisecond) } + +func SendIP(src Interface, dest Neighbor, protocolNum int, message []byte) error { + hdr := ipv4header.IPv4Header{ + Version: 4, + Len: 20, // Header length is always 20 when no IP options + TOS: 0, + TotalLen: ipv4header.HeaderLen + len(message), + ID: 0, + Flags: 0, + FragOff: 0, + TTL: 32, + Protocol: protocolNum, + Checksum: 0, // Should be 0 until checksum is computed + Src: src.IpPrefix.Addr(), + Dst: dest.VipAddr, + Options: []byte{}, + } + + // Assemble the header into a byte array + headerBytes, err := hdr.Marshal() + if err != nil { + return err + } + + // Compute the checksum (see below) + // Cast back to an int, which is what the Header structure expects + hdr.Checksum = int(ComputeChecksum(headerBytes)) + + headerBytes, err = hdr.Marshal() + if err != nil { + log.Fatalln("Error marshalling header: ", err) + } + + bytesToSend := make([]byte, 0, len(headerBytes)+len(message)) + bytesToSend = append(bytesToSend, headerBytes...) + bytesToSend = append(bytesToSend, []byte(message)...) + + // Send the message to the "link-layer" addr:port on UDP + listenAddr, err := net.ResolveUDPAddr("udp4", dest.UdpAddr.String()) + if err != nil { + return err + } + bytesWritten, err := dest.SendSocket.WriteToUDP(bytesToSend, listenAddr) + if err != nil { + return err + } + fmt.Printf("Sent %d bytes to %s\n", bytesWritten, listenAddr.String()) + + return nil +} + +func RecvIP(conn net.UDPConn, isOpen *bool) error { + buffer := make([]byte, MAX_IP_PACKET_SIZE) // TODO: fix wordking + + // Read on the UDP port + fmt.Println("wating to read from UDP socket") + _, sourceAddr, err := conn.ReadFromUDP(buffer) + if err != nil { + return err + } + + if !*isOpen { + return errors.New("interface is down") + } + + // Marshal the received byte array into a UDP header + // NOTE: This does not validate the checksum or check any fields + // (You'll need to do this part yourself) + hdr, err := ipv4header.ParseHeader(buffer) + if err != nil { + // What should you if the message fails to parse? + // Your node should not crash or exit when you get a bad message. + // Instead, simply drop the packet and return to processing. + fmt.Println("Error parsing header", err) + return err + } + + headerSize := hdr.Len + headerBytes := buffer[:headerSize] + checksumFromHeader := uint16(hdr.Checksum) + computedChecksum := ValidateChecksum(headerBytes, checksumFromHeader) + + var checksumState string + if computedChecksum == checksumFromHeader { + checksumState = "OK" + } else { + checksumState = "FAIL" + } + + // Next, get the message, which starts after the header + message := buffer[headerSize:] + + // Finally, print everything out + fmt.Printf("Received IP packet from %s\nHeader: %v\nChecksum: %s\nMessage: %s\n", + sourceAddr.String(), hdr, checksumState, string(message)) + + return nil +} + +func ComputeChecksum(b []byte) uint16 { + checksum := header.Checksum(b, 0) + checksumInv := checksum ^ 0xffff + + return checksumInv +} + +func ValidateChecksum(b []byte, fromHeader uint16) uint16 { + checksum := header.Checksum(b, fromHeader) + + return checksum +} diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go index ae71bba..c6103ee 100644 --- a/pkg/ipstack/ipstack_test.go +++ b/pkg/ipstack/ipstack_test.go @@ -2,6 +2,9 @@ package ipstack import ( "fmt" + ipv4header "github.com/brown-csci1680/iptcp-headers" + "net" + "net/netip" "testing" "time" ) @@ -103,3 +106,148 @@ func TestInterfaceUpThenDownTwice(t *testing.T) { fmt.Println("TestInterfaceUpThenDownTwice successful") t.Cleanup(func() { CleanUp() }) } + +func TestSendIPToNeighbor(t *testing.T) { + lnxFilePath := "../../doc-example/r2.lnx" + err := Initialize(lnxFilePath) + if err != nil { + t.Error(err) + } + + // get the first neighbor of this interface + iface, err := GetInterfaceByName("if0") + if err != nil { + t.Error(err) + } + neighbors, err := GetNeighborsToInterface("if0") + if err != nil { + t.Error(err) + } + + // setup a neighbor listener socket + testNeighbor := neighbors[0] + // close the socket so we can listen on it + err = testNeighbor.SendSocket.Close() + if err != nil { + t.Error(err) + } + + fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) + fmt.Printf("Neighbors:\n%s\n", SprintNeighbors()) + + listenString := testNeighbor.UdpAddr.String() + fmt.Println("listening on " + listenString) + listenAddr, err := net.ResolveUDPAddr("udp4", listenString) + if err != nil { + t.Error(err) + } + recvSocket, err := net.ListenUDP("udp4", listenAddr) + if err != nil { + t.Error(err) + } + testNeighbor.SendSocket = *recvSocket + + sent := false + go func() { + buffer := make([]byte, MAX_IP_PACKET_SIZE) + fmt.Println("wating to read from UDP socket") + _, sourceAddr, err := recvSocket.ReadFromUDP(buffer) + if err != nil { + t.Error(err) + } + fmt.Println("read from UDP socket") + hdr, err := ipv4header.ParseHeader(buffer) + if err != nil { + t.Error(err) + } + headerSize := hdr.Len + headerBytes := buffer[:headerSize] + checksumFromHeader := uint16(hdr.Checksum) + computedChecksum := ValidateChecksum(headerBytes, checksumFromHeader) + + var checksumState string + if computedChecksum == checksumFromHeader { + checksumState = "OK" + } else { + checksumState = "FAIL" + } + message := buffer[headerSize:] + fmt.Printf("Received IP packet from %s\nHeader: %v\nChecksum: %s\nMessage: %s\n", + sourceAddr.String(), hdr, checksumState, string(message)) + if err != nil { + t.Error(err) + } + + sent = true + }() + + time.Sleep(10 * time.Millisecond) + + // send a message to the neighbor + fmt.Printf("sending message to neighbor\t%t\n", sent) + err = SendIP(*iface, *testNeighbor, 0, []byte("hello")) + if err != nil { + t.Error(err) + } + + fmt.Printf("SENT message to neighbor\t%t\n", sent) + // give a little time for the message to be sent + time.Sleep(1000 * time.Millisecond) + if !sent { + t.Error("Message not sent") + t.Fail() + } + + fmt.Println("TestSendIPToNeighbor successful") + t.Cleanup(func() { CleanUp() }) +} + +func TestRecvIP(t *testing.T) { + lnxFilePath := "../../doc-example/r2.lnx" + err := Initialize(lnxFilePath) + if err != nil { + t.Error(err) + } + + // get the first neighbor of this interface to RecvIP from + iface, err := GetInterfaceByName("if0") + if err != nil { + t.Error(err) + } + InterfaceUp(iface) + + // setup a random socket to send an ip packet from + listenAddr, err := net.ResolveUDPAddr("udp4", "127.0.0.1:6969") + sendSocket, err := net.ListenUDP("udp4", listenAddr) + + // send a message to the neighbor + ifaceAsNeighbor := Neighbor{ + VipAddr: iface.IpPrefix.Addr(), + UdpAddr: iface.UdpAddr, + SendSocket: iface.RecvSocket, + SocketChannel: iface.SocketChannel, + } + fakeIface := Interface{ + Name: "if69", + IpPrefix: netip.MustParsePrefix("10.69.0.1/24"), + UdpAddr: netip.MustParseAddrPort("127.0.0.1:6969"), + RecvSocket: net.UDPConn{}, + SocketChannel: nil, + State: true, + } + err = SendIP(fakeIface, ifaceAsNeighbor, 0, []byte("hello")) + if err != nil { + return + } + + time.Sleep(10 * time.Millisecond) + + // TODO: potenially make this a channel, so it actually checks values. + // For now, you must read the message from the console. + + err = sendSocket.Close() + if err != nil { + t.Error(err) + } + t.Cleanup(func() { CleanUp() }) +} |