diff options
author | David Doan <daviddoan@Davids-MacBook-Pro-70.local> | 2023-10-18 00:14:46 -0400 |
---|---|---|
committer | David Doan <daviddoan@Davids-MacBook-Pro-70.local> | 2023-10-18 00:14:46 -0400 |
commit | 1e1f56b0dfe1b95d181ff40eeac0cc4260c937fb (patch) | |
tree | 64033021fe0943f4862a4ff751c92fcc721d3907 | |
parent | db0a9f0a4605d85ba4e535ba0ab590776cc4ba0a (diff) |
add REPL functionality
-rw-r--r-- | binaries.example.json | 17 | ||||
-rw-r--r-- | cmd/vhost/main.go | 71 | ||||
-rw-r--r-- | cmd/vrouter/main.go | 75 | ||||
-rw-r--r-- | nodes.json | 7 | ||||
-rw-r--r-- | pkg/ipstack/ipstack.go | 314 | ||||
-rwxr-xr-x | vhost | bin | 0 -> 3080475 bytes | |||
-rwxr-xr-x | vrouter | bin | 0 -> 3080644 bytes |
7 files changed, 432 insertions, 52 deletions
diff --git a/binaries.example.json b/binaries.example.json new file mode 100644 index 0000000..b6ff6d9 --- /dev/null +++ b/binaries.example.json @@ -0,0 +1,17 @@ +{ + "h1": { + "binary_path": "./vhost" + }, + "h2": { + "binary_path": "./vhost" + }, + "h3": { + "binary_path": "./vhost" + }, + "r1": { + "binary_path": "./vrouter" + }, + "r2": { + "binary_path": "./vrouter" + } +}
\ No newline at end of file diff --git a/cmd/vhost/main.go b/cmd/vhost/main.go index 82b8195..27e073f 100644 --- a/cmd/vhost/main.go +++ b/cmd/vhost/main.go @@ -4,14 +4,83 @@ import ( "bufio" "fmt" "os" + "iptcp/pkg/ipstack" + "strings" ) func main() { + if len(os.Args) != 2 { + fmt.Printf("Usage: %s <configFile>\n", os.Args[0]) + os.Exit(1) + } + + fileName := os.Args[1] + + go ipstack.Initialize(fileName) + + scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { line := scanner.Text() - fmt.Println(line) + + if line == "li" { + fmt.Println("Name\tAddr/Prefix\tState") + ipstack.SprintInterfaces() + } + + if line == "ln" { + fmt.Println("Iface\tVIP\tUDPAddr") + ipstack.SprintNeighbors() + } + + if line == "lr" { + fmt.Println("T\tPrefix\tNext Hop\tCost") + ipstack.SprintRoutingTable() + } + + if len(line) > 4 { + if line[:4] == "down" { + // get interface name + ifaceName := line[5:] + ipstack.InterfaceDownREPL(ifaceName) + } + + if line[:4] == "send" { + // get IP address and message that follows it + listOfWords := strings.Split(line, " ") + ipAddr := listOfWords[1] + message := listOfWords[2:] + // combine message into one string + messageToSend := strings.Join(message, " ") + // convert message to byte array + messageToSendBytes := []byte(messageToSend) + // get interface by ipAddr + iface, err := ipstack.GetNeighborByIP(ipAddr) + if err != nil { + fmt.Println(err) + iface, err = ipstack.GetRouteByIP(ipAddr) + if err != nil { + fmt.Println(err) + continue + } + ipstack.SendIP(ipstack.GetMyVIP(), iface, 0, messageToSendBytes, ipAddr) + } + ipstack.SendIP(ipstack.GetMyVIP(), iface, 0, messageToSendBytes, ipAddr) + } + } + if len(line) > 2 { + if line[:2] == "up" { + // get interface name + ifaceName := line[3:] + ipstack.InterfaceUpREPL(ifaceName) + } + } + + if line == "q" { + ipstack.CleanUp() + os.Exit(0) + } } }
\ No newline at end of file diff --git a/cmd/vrouter/main.go b/cmd/vrouter/main.go index 158016a..abd0fc0 100644 --- a/cmd/vrouter/main.go +++ b/cmd/vrouter/main.go @@ -5,19 +5,25 @@ import ( "fmt" "os" "time" + "iptcp/pkg/ipstack" + "strings" + ) func SendUpdates() { + myInterfaces := ipstack.GetInterfaces() + myNeighbors := ipstack.GetNeighbors() for _, iface := range myInterfaces { // send RIP updates to all neighbors - for _, neighbor := range myNeighbors { - iface.udp.Write(neighbor, data) + for _, _ = range myNeighbors { + // iface.udp.Write(neighbor, data) + // iface.RecvSocket.Write(neighbor, data) // wait for response for 12 seconds response := make([]byte, 512) - iface.udp.Read(response) + iface.RecvSocket.Read(response) time.Sleep(12 * time.Second) if len(response) == 0 { - RemoveNeighbor(neighbor) + // ipstack.RemoveNeighbor(neighbor) } } } @@ -32,7 +38,12 @@ func main() { fileName := os.Args[1] - initialize(fileName) + // initialize(fileName) + go ipstack.Initialize(fileName) + // myInterfaces := ipstack.GetInterfaces() + // myNeighbors := ipstack.GetNeighbors() + // myRoutes := ipstack.GetRoutes() + go SendUpdates() @@ -40,6 +51,58 @@ func main() { for scanner.Scan() { line := scanner.Text() - fmt.Println(line) + switch line { + case "li": + fmt.Println("Name\tAddr/Prefix\tState") + ipstack.SprintInterfaces() + case "ln": + fmt.Println("Iface\tVIP\tUDPAddr") + ipstack.SprintNeighbors() + case "lr": + fmt.Println("T\tPrefix\tNext Hop\tCost") + ipstack.SprintRoutingTable() + case "q": + ipstack.CleanUp() + os.Exit(0) + default: + if len(line) > 4 { + if line[:4] == "down" { + // get interface name + ifaceName := line[5:] + ipstack.InterfaceDownREPL(ifaceName) + } + + if line[:4] == "send" { + // get IP address and message that follows it + listOfWords := strings.Split(line, " ") + ipAddr := listOfWords[1] + message := listOfWords[2:] + // combine message into one string + messageToSend := strings.Join(message, " ") + // convert message to byte array + messageToSendBytes := []byte(messageToSend) + // get interface by ipAddr + iface, err := ipstack.GetNeighborByIP(ipAddr) + if err != nil { + fmt.Println(err) + iface, err = ipstack.GetRouteByIP(ipAddr) + if err != nil { + fmt.Println(err) + continue + } + ipstack.SendIP(ipstack.GetMyVIP(), iface, 0, messageToSendBytes, ipAddr) + } + ipstack.SendIP(ipstack.GetMyVIP(), iface, 0, messageToSendBytes, ipAddr) + } + } + if len(line) > 2 { + if line[:2] == "up" { + // get interface name + ifaceName := line[3:] + ipstack.InterfaceUpREPL(ifaceName) + } + } + continue + } } }
\ No newline at end of file diff --git a/nodes.json b/nodes.json new file mode 100644 index 0000000..7b91355 --- /dev/null +++ b/nodes.json @@ -0,0 +1,7 @@ +{ + "h1": "host", + "h2": "host", + "h3": "host", + "r1": "router", + "r2": "router" +}
\ No newline at end of file diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index be8bc1e..dca19d6 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -51,11 +51,13 @@ type RIPEntry struct { type Hop struct { Cost uint32 - VipAsStr string + VipAsStr string + Type string + // added this for printing purposes } // GLOBAL VARIABLES (data structures) ------------------------------------------ -var myVIP netip.Addr +var myVIP Interface var myInterfaces []*Interface var myNeighbors = make(map[string][]*Neighbor) @@ -68,28 +70,30 @@ var protocolHandlers = make(map[uint16]HandlerFunc) var routingTable = make(map[netip.Prefix]Hop) // reference: https://github.com/brown-csci1680/lecture-examples/blob/main/ip-demo/cmd/udp-ip-recv/main.go -func createUDPConn(UdpAddr netip.AddrPort, conn *net.UDPConn) error { +func createUDPConn(UdpAddr netip.AddrPort, conn *net.UDPConn, isN bool) error { listenString := UdpAddr.String() listenAddr, err := net.ResolveUDPAddr("udp4", listenString) if err != nil { return errors.WithMessage(err, "Error resolving address->\t"+listenString) } - tmpConn, err := net.ListenUDP("udp4", listenAddr) - if err != nil { - return errors.WithMessage(err, "Could not bind to UDP port->\t"+listenString) + if !isN { + tmpConn, err := net.ListenUDP("udp4", listenAddr) + if err != nil { + return errors.WithMessage(err, "Could not bind to UDP port->\t"+listenString) + } + *conn = *tmpConn + } else { + tmpConn, err := net.DialUDP("udp4", nil, listenAddr) + if err != nil { + return errors.WithMessage(err, "Could not bind to UDP port->\t"+listenString) + } + *conn = *tmpConn } - *conn = *tmpConn return nil } func Initialize(lnxFilePath string) error { - //if len(os.Args) != 2 { - // fmt.Printf("Usage: %s <configFile>\n", os.Args[0]) - // os.Exit(1) - //} - //lnxFilePath := os.Args[1] - // Parse the file lnxConfig, err := lnxconfig.ParseConfig(lnxFilePath) if err != nil { @@ -97,7 +101,8 @@ func Initialize(lnxFilePath string) error { } // 1) initialize the interfaces on this node here and into the routing table - static := false + // static := false + interfaceToReturn := Interface{} for _, iface := range lnxConfig.Interfaces { prefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) i := &Interface{ @@ -106,10 +111,15 @@ func Initialize(lnxFilePath string) error { UdpAddr: iface.UDPAddr, RecvSocket: net.UDPConn{}, SocketChannel: make(chan bool), - State: false, + State: true, + } + if interfaceToReturn == (Interface{}) { + interfaceToReturn = *i + myVIP = *i } + // Added this for printing purposes for REPL FYI, if you have a better way lmk - err := createUDPConn(iface.UDPAddr, &i.RecvSocket) + err := createUDPConn(iface.UDPAddr, &i.RecvSocket, false) if err != nil { return errors.WithMessage(err, "Error creating UDP socket for interface->\t"+iface.Name) } @@ -118,11 +128,12 @@ func Initialize(lnxFilePath string) error { // TODO: (FOR HOSTS ONLY) // add STATIC to routing table - if !static { - ifacePrefix := netip.MustParsePrefix("0.0.0.0/0") - routingTable[ifacePrefix] = Hop{STATIC_COST, iface.Name} - static = true - } + // if !static { + // ifacePrefix := netip.MustParsePrefix("0.0.0.0/0") + // routingTable[ifacePrefix] = Hop{STATIC_COST, iface.Name, "S"} + // static = true + // } + // Took this out for printing purposes for REPL FYI } // 2) initialize the neighbors connected to the node and into the routing table @@ -134,7 +145,7 @@ func Initialize(lnxFilePath string) error { SocketChannel: make(chan bool), } - err := createUDPConn(neighbor.UDPAddr, &n.SendSocket) + err := createUDPConn(neighbor.UDPAddr, &n.SendSocket, true) if err != nil { return errors.WithMessage(err, "Error creating UDP socket for neighbor->\t"+neighbor.DestAddr.String()) } @@ -144,14 +155,22 @@ func Initialize(lnxFilePath string) error { // 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} + routingTable[neighborPrefix] = Hop{LOCAL_COST, neighbor.InterfaceName, "L"} + } + + for _, route := range lnxConfig.StaticRoutes { + // add to routing table + prefix := netip.MustParsePrefix("0.0.0.0/0") + routingTable[prefix] = Hop{LOCAL_COST, route.String(), "S"} } + // added for printing purposes for REPL FYI return nil } func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { - isUp := false + isUp := true + // testing purposes set to TRUE closed := false // go routine that hangs on the recv @@ -199,11 +218,34 @@ func InterfaceUp(iface *Interface) { iface.SocketChannel <- true } +func InterfaceUpREPL(ifaceName string) { + iface, err := GetInterfaceByName(ifaceName) + if err != nil { + fmt.Println("Error getting interface by name", err) + return + } + iface.State = true + iface.SocketChannel <- true +} + +// we could do either of these but the REPL results in less work done in router and host + func InterfaceDown(iface *Interface) { iface.SocketChannel <- false iface.State = false } +func InterfaceDownREPL(ifaceName string) { + iface, err := GetInterfaceByName(ifaceName) + if err != nil { + fmt.Println("Error getting interface by name", err) + return + } + iface.SocketChannel <- false + iface.State = false +} +// same as above comment + func GetInterfaceByName(ifaceName string) (*Interface, error) { for _, iface := range myInterfaces { if iface.Name == ifaceName { @@ -214,6 +256,36 @@ func GetInterfaceByName(ifaceName string) (*Interface, error) { return nil, errors.Errorf("No interface with name %s", ifaceName) } +func GetNeighborByIP(ipAddr string) (*Neighbor, error) { + for _, neighbors := range myNeighbors { + for _, neighbor := range neighbors { + if neighbor.VipAddr.String() == ipAddr { + return neighbor, nil + } + } + } + + return nil, errors.Errorf("No interface with ip %s", ipAddr) +} + +func GetRouteByIP(ipAddr string) (*Neighbor, error) { + for prefix, hop := range routingTable { + if prefix.Contains(netip.MustParseAddr(ipAddr)) { + fmt.Println("found route", hop.VipAsStr) + neighbors, err := GetNeighborsToInterface(hop.VipAsStr) + if err != nil { + fmt.Println("Error getting neighbors to interface", err) + return nil, err + } + neighbor := neighbors[0] + + return neighbor, nil + } + } + + return nil, errors.Errorf("No interface with ip %s", ipAddr) +} + func GetNeighborsToInterface(ifaceName string) ([]*Neighbor, error) { if neighbors, ok := myNeighbors[ifaceName]; ok { return neighbors, nil @@ -222,30 +294,38 @@ func GetNeighborsToInterface(ifaceName string) ([]*Neighbor, error) { return nil, errors.Errorf("No interface with name %s", ifaceName) } -func SprintInterfaces() string { - buf := "" +func GetMyVIP() Interface { + return myVIP +} + +func SprintInterfaces() { for _, iface := range myInterfaces { - buf += fmt.Sprintf("%s\t%s\t%t\n", iface.Name, iface.IpPrefix.String(), iface.State) + if iface.State { + fmt.Printf("%s\t%s\t%s\n", iface.Name, iface.IpPrefix.String(), "UP") + } else { + fmt.Printf("%s\t%s\t%s\n", iface.Name, iface.IpPrefix.String(), "DOWN") + } } - return buf } -func SprintNeighbors() string { - buf := "" +func SprintNeighbors() { for ifaceName, neighbor := range myNeighbors { for _, n := range neighbor { - buf += fmt.Sprintf("%s\t%s\t%s\n", ifaceName, n.UdpAddr.String(), n.VipAddr.String()) + fmt.Printf("%s\t%s\t%s\n", ifaceName, n.VipAddr.String(), n.UdpAddr.String()) } } - return buf } -func SprintRoutingTable() string { - buf := "" +func SprintRoutingTable() { for prefix, hop := range routingTable { - buf += fmt.Sprintf("%s\t%s\t%d\n", prefix.String(), hop.VipAsStr, hop.Cost) + if hop.Type == "L" { + fmt.Printf("%s\t%s\tLOCAL:%s\t%d\n", hop.Type, prefix.String(), hop.VipAsStr, hop.Cost) + } else if hop.Type == "S" { + fmt.Printf("%s\t%s\t%s\t%s\n", hop.Type, prefix.String(), hop.VipAsStr, "-") + } else { + fmt.Printf("%s\t%s\t%s\t%d\n", hop.Type, prefix.String(), hop.VipAsStr, hop.Cost) + } } - return buf } func DebugNeighbors() { @@ -256,6 +336,27 @@ func DebugNeighbors() { } } +// func RemoveNeighbor(neighbor Neighbor) { +// // TODO: remove from routing table +// myRoutes := GetRoutes() +// for prefix, hop := range myRoutes { +// if hop.VipAsStr == neighbor.VipAddr.String() { +// delete(myRoutes, prefix) +// } +// } + +// // TODO: remove from myNeighbors +// myNeighbors[neighbor.VipAddr.String()] = nil + +// // TODO: close the UDP socket +// err := neighbor.SendSocket.Close() +// if err != nil { +// fmt.Println("Error closing UDP socket", err) +// } + +// } +// untested function above + func CleanUp() { fmt.Print("Cleaning up...\n") // go through the interfaces, pop thread & close the UDP FDs @@ -293,7 +394,7 @@ func CleanUp() { } // TODO: have it take TTL so we can decrement it when forwarding -func SendIP(src Interface, dest Neighbor, protocolNum int, message []byte) error { +func SendIP(src Interface, dest *Neighbor, protocolNum int, message []byte, destIP string) error { hdr := ipv4header.IPv4Header{ Version: 4, Len: 20, // Header length is always 20 when no IP options @@ -306,7 +407,7 @@ func SendIP(src Interface, dest Neighbor, protocolNum int, message []byte) error Protocol: protocolNum, Checksum: 0, // Should be 0 until checksum is computed Src: src.IpPrefix.Addr(), - Dst: dest.VipAddr, + Dst: netip.MustParseAddr(destIP), Options: []byte{}, } @@ -334,10 +435,25 @@ func SendIP(src Interface, dest Neighbor, protocolNum int, message []byte) error if err != nil { return err } - bytesWritten, err := dest.SendSocket.WriteToUDP(bytesToSend, listenAddr) + bytesWritten, err := dest.SendSocket.Write(bytesToSend) if err != nil { return err } + + // send to the dest.UdpAddr without the SendSocket + // conn, err := net.DialUDP("udp4", nil, listenAddr) + // if err != nil { + // fmt.Println(err, "here5") + // return err + // } + + // bytesWritten, err := conn.Write(bytesToSend) + // if err != nil { + // fmt.Println(err, "here6") + // return err + // } + + // what we had previously just in case fmt.Printf("Sent %d bytes to %s\n", bytesWritten, listenAddr.String()) return nil @@ -348,7 +464,7 @@ func RecvIP(conn net.UDPConn, isOpen *bool) error { // Read on the UDP port fmt.Println("wating to read from UDP socket") - _, sourceAddr, err := conn.ReadFromUDP(buffer) + _, _, err := conn.ReadFromUDP(buffer) if err != nil { return err } @@ -384,17 +500,94 @@ func RecvIP(conn net.UDPConn, isOpen *bool) error { // 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)) - // TODO: handle the message // 1) check if the TTL & checksum is valid + TTL := hdr.TTL + if TTL == 0 { + // drop the packet + return nil + } + + // check if the checksum is valid + if checksumState == "FAIL" { + // drop the packet + return nil + } + // 2) check if the message is for me, if so, sendUP (aka call the correct handler) + if hdr.Dst.String() == myVIP.IpPrefix.Addr().String() { + // call the correct handler in our case its just printing out the message + fmt.Println("for me") + if hdr.Protocol == 0 { + fmt.Printf("Received test packet: Src: %s, Dst: %s, TTL: %d, Data: %s\n", + hdr.Src.String(), hdr.Dst.String(), hdr.TTL, string(message)) + return nil + } + + } // if not, need to forward the packer to a neighbor or check the table // after decrementing TTL and updating checksum + hdr.TTL-- + // update checksum + headerBytes, err = hdr.Marshal() + if err != nil { + log.Fatalln("Error marshalling header: ", err) + } + 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)...) + + // 3) check if message is for a neighbor, if so, sendIP there - // 4) check if message is for a neighbor, if so, forward to the neighbor with that VIP + for _, neighbors := range myNeighbors { + // fmt.Println(neighbors) + for _, neighbor := range neighbors { + if hdr.Dst.String() == neighbor.VipAddr.String() { + fmt.Println("for neighbor") + // send the packet to the neighbor + err := SendIP(myVIP, neighbor, hdr.Protocol, bytesToSend, hdr.Dst.String()) + if err != nil { + fmt.Println("Error sending IP packet", err) + return err + } + return nil + } + } + } + fmt.Println("No neighbors, checking Routes") + // 4) check forwarding table. if so, forward to the neighbor with that VIP + + // needs RIP Protocol I believed + + for prefix, hop := range routingTable { + fmt.Println(prefix) + if prefix.Contains(hdr.Dst) { + // send the packet to the neighbor + fmt.Println("testing", prefix, hdr.Dst.String()) + neighbors, err := GetNeighborsToInterface(hop.VipAsStr) + if err != nil { + fmt.Println("Error getting neighbor by IP", err) + return err + } + // get the longest prefix match + for _, neighbor := range neighbors { + fmt.Println("for neighbor", neighbor.VipAddr.String()) + // send the packet to the neighbor + } + // err = SendIP(myVIP, neighbor, hdr.Protocol, bytesToSend, hdr.Dst.String()) + if err != nil { + fmt.Println("Error sending IP packet", err) + return err + } + } + } + // send 10.2.0.3 hello + // 5) if not, drop the packet return nil } @@ -411,3 +604,34 @@ func ValidateChecksum(b []byte, fromHeader uint16) uint16 { return checksum } + +func GetInterfaces() []*Interface { + return myInterfaces +} + +func GetNeighbors() map[string][]*Neighbor { + return myNeighbors +} + +func GetRoutes() map[netip.Prefix]Hop { + return routingTable +} + +// func LongestPrefixMatch(ipAddr string) (netip.Addr, error) { +// var longestMatch netip.Addr +// var longestMatchLength int +// ip := netip.MustParseAddr(ipAddr) +// for prefix := range routingTable { +// // Extract the prefix length using the Bits method +// prefixLen := prefix.Bits() +// if prefix.Contains(ip) && prefixLen > longestMatchLength { +// longestMatch = ip +// longestMatchLength = prefixLen +// } +// } +// if longestMatch == (netip.Addr{}) { +// return netip.Addr{}, errors.New("No match found in routing table") +// } +// return longestMatch, nil +// } +// A possible implementation of LongestPrefixMatch, need RIP Protocol to test?
\ No newline at end of file Binary files differBinary files differ |