diff options
author | sotech117 <michael_foiani@brown.edu> | 2023-10-22 06:18:06 -0400 |
---|---|---|
committer | sotech117 <michael_foiani@brown.edu> | 2023-10-22 06:18:06 -0400 |
commit | a935739d332ccb82174ddb925dcf5e473dfae41f (patch) | |
tree | d6b1d7cd0853e25eec59467be059b958be2e078b /pkg | |
parent | 6618f59d55ae5bbf99426b06e3cdd891f9a0f0c4 (diff) |
rewrite table to be more robust and efficient for sending.
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/ipstack/ipstack.go | 548 | ||||
-rw-r--r-- | pkg/ipstack/ipstack_test.go | 517 |
2 files changed, 552 insertions, 513 deletions
diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 0cc1fff..e47c741 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -1,6 +1,7 @@ package ipstack import ( + "encoding/binary" "fmt" ipv4header "github.com/brown-csci1680/iptcp-headers" "github.com/google/netstack/tcpip/header" @@ -10,21 +11,20 @@ import ( "net" "net/netip" "time" - "encoding/binary" // "bytes" // "unicode" ) const ( - MAX_IP_PACKET_SIZE = 1400 - LOCAL_COST uint32 = 0 - STATIC_COST uint32 = 4294967295 // 2^32 - 1 - MaxEntries = 64 - INFINITY = 15 - SIZE_OF_RIP_ENTRY = 12 - SIZE_OF_RIP_MESSAGE = 6 - RIP_PROTOCOL = 12 - TEST_PROTOCOL = 0 + MAX_IP_PACKET_SIZE = 1400 + LOCAL_COST uint32 = 0 + STATIC_COST uint32 = 4294967295 // 2^32 - 1 + MaxEntries = 64 + INFINITY = 15 + SIZE_OF_RIP_ENTRY = 12 + SIZE_OF_RIP_MESSAGE = 6 + RIP_PROTOCOL = 12 + TEST_PROTOCOL = 0 ) // STRUCTS --------------------------------------------------------------------- @@ -39,12 +39,9 @@ type Interface struct { } type Neighbor struct { - Name string + Name string VipAddr netip.Addr UdpAddr netip.AddrPort - - SendSocket net.UDPConn - SocketChannel chan bool } type RIPMessage struct { @@ -55,14 +52,16 @@ type RIPMessage struct { type RIPEntry struct { address uint32 - cost uint32 - mask uint32 + cost uint32 + mask uint32 } type Hop struct { - Cost uint32 - VipAsStr string - Type string + Cost uint32 + Type string + + Interface *Interface + VIP netip.Addr } // GLOBAL VARIABLES (data structures) ------------------------------------------ @@ -70,7 +69,7 @@ var myVIP Interface var myInterfaces []*Interface var myNeighbors = make(map[string][]*Neighbor) -var myRIPNeighbors = make([]netip.Addr, 0) +var myRIPNeighbors = make([]netip.Addr, 0) type HandlerFunc func(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.IPv4Header) error @@ -80,25 +79,17 @@ var routingTable = make(map[netip.Prefix]Hop) // ************************************** INIT FUNCTIONS ********************************************************** // 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, isN bool) error { +func createUDPListener(UdpAddr netip.AddrPort, conn *net.UDPConn) error { listenString := UdpAddr.String() listenAddr, err := net.ResolveUDPAddr("udp4", listenString) if err != nil { return errors.WithMessage(err, "Error resolving address->\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 + tmpConn, err := net.ListenUDP("udp4", listenAddr) + if err != nil { + return errors.WithMessage(err, "Could not bind to UDP port->\t"+listenString) } + *conn = *tmpConn return nil } @@ -110,9 +101,8 @@ func Initialize(lnxFilePath string) error { return errors.WithMessage(err, "Error parsing config file->\t"+lnxFilePath) } - // 1) initialize the interfaces on this node here and into the routing table - static := false - interfaceToReturn := Interface{} + // 1) add each local if to the routing table, as dictated by its subnet + // static := false for _, iface := range lnxConfig.Interfaces { prefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) i := &Interface{ @@ -123,77 +113,60 @@ func Initialize(lnxFilePath string) error { SocketChannel: make(chan bool), 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, false) + err := createUDPListener(iface.UDPAddr, &i.RecvSocket) if err != nil { return errors.WithMessage(err, "Error creating UDP socket for interface->\t"+iface.Name) } - go InterfaceListenerRoutine(i.RecvSocket, i.SocketChannel) + + go InterfaceListenerRoutine(i) myInterfaces = append(myInterfaces, i) + routingTable[prefix] = Hop{STATIC_COST, "L", i, prefix.Addr()} + // TODO @ MICHAEL: The hop shouldn't be the interface name - if !static { - ifacePrefix := netip.MustParsePrefix("0.0.0.0/0") - routingTable[ifacePrefix] = Hop{STATIC_COST, iface.Name, "S"} - static = true - } + //if !static { + // ifacePrefix := netip.MustParsePrefix("0.0.0.0/0") + // routingTable[ifacePrefix] = Hop{STATIC_COST, "S", nil} + // static = true + //} } - // 2) initialize the neighbors connected to the node and into the routing table + // 2) add neighbors to if->neighbors map for _, neighbor := range lnxConfig.Neighbors { n := &Neighbor{ - Name: neighbor.InterfaceName, - VipAddr: neighbor.DestAddr, - UdpAddr: neighbor.UDPAddr, - SendSocket: net.UDPConn{}, - SocketChannel: make(chan bool), - } - - err := createUDPConn(neighbor.UDPAddr, &n.SendSocket, true) - if err != nil { - return errors.WithMessage(err, "Error creating UDP socket for neighbor->\t"+neighbor.DestAddr.String()) + Name: neighbor.InterfaceName, + VipAddr: neighbor.DestAddr, + UdpAddr: neighbor.UDPAddr, } 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, "L"} - } - - for _, route := range lnxConfig.RipNeighbors { - // add to RIP neighbors - myRIPNeighbors = append(myRIPNeighbors, route) - } - - // StaticRoutes: make(map[netip.Prefix]netip.Addr, 0), - for prefix, addr := range lnxConfig.StaticRoutes { - // add to routing table - routingTable[prefix] = Hop{STATIC_COST, addr.String(), "S"} } + // + //for _, route := range lnxConfig.RipNeighbors { + // // add to RIP neighbors + // myRIPNeighbors = append(myRIPNeighbors, route) + //} - go SendUpdates() // add protocol handlers - protocolHandlers[RIP_PROTOCOL] = handleRIP - protocolHandlers[TEST_PROTOCOL] = handleTestPackets return nil } -func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { +func InterfaceListenerRoutine(i *Interface) { + // decompose the interface + prefix := i.IpPrefix + socket := i.RecvSocket + signal := i.SocketChannel + + // booleans to control listening routine isUp := true - // testing purposes set to TRUE closed := false // go routine that hangs on the recv fmt.Println("MAKING GO ROUTINE TO LISTEN:\t", socket.LocalAddr().String()) go func() { - defer func() { // on close, set isUp to false + defer func() { fmt.Println("exiting go routine that listens on ", socket.LocalAddr().String()) }() @@ -206,7 +179,7 @@ func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { } // TODO: remove these "training wheels" time.Sleep(1 * time.Millisecond) - err := RecvIP(socket, &isUp) + err := RecvIP(prefix, socket, &isUp) if err != nil { fmt.Println("Error receiving IP packet", err) return @@ -286,41 +259,6 @@ func GetNeighborByIP(ipAddr string) (*Neighbor, error) { return nil, errors.Errorf("No interface with ip %s", ipAddr) } -func GetRouteByIP(ipAddr string) (*Neighbor, error) { - // iterate through the routing table and return the one with the same ipAddr - var defaultRoute *Neighbor - for prefix, hop := range routingTable { - // gets the default route - if hop.Type == "S" { - defaultRoute, _ = GetNeighborByIP(hop.VipAsStr) - continue - } - - // TODO @ MICHAEL: get the longest prefix match - netIP := net.ParseIP(prefix.Addr().String()) - if netIP.String() == ipAddr { - fmt.Println("found route", hop.VipAsStr) - neighbor, err := GetNeighborByIP(hop.VipAsStr) - if err != nil { - fmt.Println("Error getting neighbors to interface", err) - neighbor, err = GetRouteByIP(hop.VipAsStr) - if err != nil { - fmt.Println("Error getting route by IP", err) - return nil, err - } - // continue // fix with longest prefix matching? - } - return neighbor, nil - } - } - - if defaultRoute == nil { - return nil, errors.Errorf("No route to ip %s", ipAddr) - } - fmt.Println("returning default route", defaultRoute.VipAddr.String()) - return defaultRoute, nil -} - func GetNeighborsToInterface(ifaceName string) ([]*Neighbor, error) { if neighbors, ok := myNeighbors[ifaceName]; ok { return neighbors, nil @@ -368,11 +306,11 @@ func SprintNeighbors() { func SprintRoutingTable() { for prefix, hop := range routingTable { if hop.Type == "L" { - fmt.Printf("%s\t%s\tLOCAL:%s\t%d\n", hop.Type, prefix.String(), hop.VipAsStr, 0) + fmt.Printf("%s\t%s\tLOCAL:%s\t%d\n", hop.Type, prefix.String(), hop.Interface.Name, 0) } else if hop.Type == "S" { - fmt.Printf("%s\t%s\t%s\t%s\n", hop.Type, prefix.String(), hop.VipAsStr, "-") + fmt.Printf("%s\t%s\t%s\t%s\n", hop.Type, prefix.String(), myVIP.IpPrefix.Addr().String(), "-") } else { - fmt.Printf("%s\t%s\t%s\t%d\n", hop.Type, prefix.String(), hop.VipAsStr, hop.Cost) + fmt.Printf("%s\t%s\t%s\t%d\n", hop.Type, prefix.String(), hop.VIP, hop.Cost) } } } @@ -385,7 +323,7 @@ func DebugNeighbors() { } } -// TODO @ MICHAEL: +// TODO @ MICHAEL: // func RemoveNeighbor(neighbor Neighbor) { // // TODO: remove from routing table // myRoutes := GetRoutes() @@ -394,7 +332,7 @@ func DebugNeighbors() { // delete(myRoutes, prefix) // } // } - + // // TODO: remove from myNeighbors // myNeighbors[neighbor.VipAddr.String()] = nil @@ -422,19 +360,6 @@ func CleanUp() { } } - // go through the neighbors, pop thread & close the UDP FDs - for _, neighbor := range myNeighbors { - for _, n := range neighbor { - if n.SocketChannel != nil { - close(n.SocketChannel) - } - err := n.SendSocket.Close() - if err != nil { - continue - } - } - } - // delete all the neighbors myNeighbors = make(map[string][]*Neighbor) // delete all the interfaces @@ -446,7 +371,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, destIP string, hdr *ipv4header.IPv4Header) error { +func SendIP(src *netip.Addr, dest *Neighbor, protocolNum int, message []byte, destIP string, hdr *ipv4header.IPv4Header) error { if hdr == nil { hdr = &ipv4header.IPv4Header{ Version: 4, @@ -459,28 +384,14 @@ func SendIP(src *Interface, dest *Neighbor, protocolNum int, message []byte, des TTL: 32, Protocol: protocolNum, Checksum: 0, // Should be 0 until checksum is computed - Src: src.IpPrefix.Addr(), + Src: *src, Dst: netip.MustParseAddr(destIP), Options: []byte{}, } } else { - 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: hdr.TTL - 1, - Protocol: protocolNum, - Checksum: 0, // Should be 0 until checksum is computed - Src: src.IpPrefix.Addr(), - Dst: netip.MustParseAddr(destIP), - Options: []byte{}, - } + hdr.TTL-- } - + // Assemble the header into a byte array headerBytes, err := hdr.Marshal() if err != nil { @@ -501,17 +412,24 @@ func SendIP(src *Interface, dest *Neighbor, protocolNum int, message []byte, des bytesToSend = append(bytesToSend, headerBytes...) bytesToSend = append(bytesToSend, []byte(message)...) - bytesWritten, err := dest.SendSocket.Write(bytesToSend) + sendAddr, err := net.ResolveUDPAddr("udp4", dest.UdpAddr.String()) + tmpConn, err := net.DialUDP("udp4", nil, sendAddr) + if err != nil { + return errors.WithMessage(err, "Could not bind to UDP port->\t"+dest.UdpAddr.String()) + } + // *conn = *tmpConn + + bytesWritten, err := tmpConn.Write(bytesToSend) if err != nil { return err } - + fmt.Printf("Sent %d bytes to %s\n", bytesWritten, dest.VipAddr.String()) return nil } -func RecvIP(conn net.UDPConn, isOpen *bool) error { +func RecvIP(prefix netip.Prefix, conn net.UDPConn, isOpen *bool) error { buffer := make([]byte, MAX_IP_PACKET_SIZE) // TODO: fix wordking // Read on the UDP port @@ -559,37 +477,72 @@ func RecvIP(conn net.UDPConn, isOpen *bool) error { // drop the packet return nil } - + // check if the checksum is valid if checksumState == "FAIL" { // drop the packet return nil } - switch hdr.Protocol { - case 0: - // call the test packet handler - err = protocolHandlers[int(hdr.Protocol)](nil, nil, message, hdr) - if err != nil { - fmt.Println("Error handling test packet", err) - return err - } - case 12: - // call the RIP handler - err = protocolHandlers[int(hdr.Protocol)](nil, nil, message, hdr) - if err != nil { - fmt.Println("Error handling RIP", err) - return err + + // at this point, the packet is valid. next steps consider the forwarding of the packet + // 2) check if the message is for me, if so, sendUP (aka call the correct handler) + fmt.Println("checking if it's for me!") + if hdr.Dst == prefix.Addr() { + // see if there is a handler for this protocol + if handler, ok := protocolHandlers[hdr.Protocol]; ok { + err := handler(&myVIP, nil, message, hdr) + if err != nil { + fmt.Println(err) + } } - default: return nil - } + // 4) check forwarding table. + // if it's a local hop, send to that iface + // if it's a RIP hop, send to the neighbor with that VIP + fmt.Println("checking routing table") + hop, err := LongestPrefix(hdr.Dst) + if err == nil { // on no err, found a match + fmt.Println("found route", hop.VIP) + if hop.Type == "S" { + // default, static route + // drop in this case + return nil + } + + // local and RIP hops + if hop.Type == "L" || hop.Type == "R" { + // if it's a local route, then the name is the interface name + for _, neighbor := range myNeighbors[hop.Interface.Name] { + if neighbor.VipAddr == hdr.Dst { + err2 := SendIP(nil, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) + if err2 != nil { + return err2 + } + } + } + } + } + + // if not in table, drop packet return nil } // ************************************** Protocol Handlers ******************************************************* +func RegisterProtocolHandler(protocolNum int) bool { + if protocolNum == RIP_PROTOCOL { + protocolHandlers[protocolNum] = handleRIP + return true + } + if protocolNum == TEST_PROTOCOL { + protocolHandlers[protocolNum] = handleTestPackets + return true + } + return false +} + func handleRIP(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.IPv4Header) error { command := message[0] switch command { @@ -617,20 +570,22 @@ func handleRIP(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.I // add to routing table for _, entry := range entries { address := fmt.Sprintf("%d.%d.%d.%d", byte(entry.address>>24), byte(entry.address>>16), byte(entry.address>>8), byte(entry.address)) - mask := fmt.Sprintf("%d.%d.%d.%d", byte(entry.mask>>24), byte((entry.mask>>16)), byte((entry.mask>>8)), byte(entry.mask)) + mask := binary.LittleEndian.Uint32(message[4:8]) + prefix := netip.PrefixFrom(netip.MustParseAddr(address), int(mask)) if address == "0.0.0.0" { continue } // TODO @ MICHAEL: check if the entry is already in the routing table and update if need be - if _, ok := routingTable[netip.MustParsePrefix(address+"/24")]; ok { + if _, ok := routingTable[prefix]; ok { continue } + if entry.cost == 17 { - routingTable[netip.MustParsePrefix(address+"/24")] = Hop{0, mask, "R"} + routingTable[prefix] = Hop{0, "R", src, prefix.Addr()} } else { - routingTable[netip.MustParsePrefix(address+"/24")] = Hop{entry.cost + 1, mask, "R"} + routingTable[prefix] = Hop{entry.cost + 1, "R", src, prefix.Addr()} } } } @@ -639,96 +594,104 @@ func handleRIP(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.I } func handleTestPackets(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.IPv4Header) error { - // 2) check if the message is for me, if so, sendUP (aka call the correct handler) - - // potentially unneeded - // message = bytes.Map(func(r rune) rune { - // if unicode.IsPrint(r) { - // return r - // } - // return -1 - // }, message) - - fmt.Println("checking my interfaces") - for _, interfaces := range myInterfaces { - if hdr.Dst.String() == interfaces.IpPrefix.Addr().String() { - 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 - // DONE IN SENDIP FUNCTION - - // 3) check if message is for a neighbor, if so, sendIP there - fmt.Println("checking neighbors") - for _, neighbors := range myNeighbors { - for _, neighbor := range neighbors { - // check for matching neighbor - if hdr.Dst.String() == neighbor.VipAddr.String() { - // get the source interface and set dst to it - for _, interfaces := range myInterfaces { - if interfaces.Name == neighbor.Name { - err := SendIP(interfaces, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) - if err != nil { - fmt.Println(err) - } - break - } - } - return nil - } - } - } - // 4) check forwarding table. if so, forward to the neighbor with that VIP - fmt.Println("checking routing table") - for prefix, hop := range routingTable { - netIP := net.ParseIP(prefix.Addr().String()) - // check if the destination is in the routing table - if netIP.String() == hdr.Dst.String() { - // send the packet to next hop - // get the corresponding neighbor - neighbor, err := GetNeighborByIP(hop.VipAsStr) - if err != nil { - fmt.Println("Error getting neighbor by IP", err) - // check if there is a route to hop.VipAsStr - // this should be done recursively yeah? - neighbor, err = GetRouteByIP(hop.VipAsStr) - if err != nil { - fmt.Println("Error getting route by IP", err) - return nil - } - for _, interfaces := range myInterfaces { - if interfaces.Name == neighbor.Name { - err = SendIP(interfaces, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) - if err != nil { - fmt.Println(err) - } - break - } - } - return nil - } - // get the source interface and set dst to it - for _, interfaces := range myInterfaces { - if interfaces.Name == neighbor.Name { - err = SendIP(interfaces, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) - if err != nil { - fmt.Println(err) - } - break - } - } - return nil - } - } - - // 5) if not, drop the packet + 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 } +//func handleTestPackets(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.IPv4Header) error { +// // 2) check if the message is for me, if so, sendUP (aka call the correct handler) +// +// // potentially unneeded +// // message = bytes.Map(func(r rune) rune { +// // if unicode.IsPrint(r) { +// // return r +// // } +// // return -1 +// // }, message) +// +// fmt.Println("checking my interfaces") +// for _, interfaces := range myInterfaces { +// if hdr.Dst.String() == interfaces.IpPrefix.Addr().String() { +// 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 +// // DONE IN SENDIP FUNCTION +// +// // 3) check if message is for a neighbor, if so, sendIP there +// fmt.Println("checking neighbors") +// for _, neighbors := range myNeighbors { +// for _, neighbor := range neighbors { +// // check for matching neighbor +// if hdr.Dst.String() == neighbor.VipAddr.String() { +// // get the source interface and set dst to it +// for _, interfaces := range myInterfaces { +// if interfaces.Name == neighbor.Name { +// err := SendIP(interfaces, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) +// if err != nil { +// fmt.Println(err) +// } +// break +// } +// } +// return nil +// } +// } +// } +// // 4) check forwarding table. if so, forward to the neighbor with that VIP +// fmt.Println("checking routing table") +// for prefix, hop := range routingTable { +// netIP := net.ParseIP(prefix.Addr().String()) +// // check if the destination is in the routing table +// if netIP.String() == hdr.Dst.String() { +// // send the packet to next hop +// // get the corresponding neighbor +// neighbor, err := GetNeighborByIP(hop.VipAsStr) +// if err != nil { +// fmt.Println("Error getting neighbor by IP", err) +// // check if there is a route to hop.VipAsStr +// // this should be done recursively yeah? +// neighbor, err = GetRouteByIP(hop.VipAsStr) +// if err != nil { +// fmt.Println("Error getting route by IP", err) +// return nil +// } +// for _, interfaces := range myInterfaces { +// if interfaces.Name == neighbor.Name { +// src := interfaces.IpPrefix.Addr() +// err = SendIP(&src, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) +// if err != nil { +// fmt.Println(err) +// } +// break +// } +// } +// return nil +// } +// // get the source interface and set dst to it +// for _, interfaces := range myInterfaces { +// if interfaces.Name == neighbor.Name { +// src := interfaces.IpPrefix.Addr() +// err = SendIP(&src, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) +// if err != nil { +// fmt.Println(err) +// } +// break +// } +// } +// return nil +// } +// } +// +// // 5) if not, drop the packet +// return nil +//} + // ************************************** CHECKSUM FUNCTIONS ****************************************************** func ComputeChecksum(b []byte) uint16 { @@ -747,12 +710,24 @@ func ValidateChecksum(b []byte, fromHeader uint16) uint16 { // ************************************** RIP FUNCTIONS ********************************************************** // TODO @ MICHAEL: LONGEST PREFIX MATCHING +func LongestPrefix(src netip.Addr) (Hop, error) { + possibleBits := [5]int{32, 24, 16, 8, 0} + for _, bits := range possibleBits { + cmpPrefix := netip.PrefixFrom(src, bits) + for prefix, hop := range routingTable { + if cmpPrefix.Overlaps(prefix) { + return hop, nil + } + } + } + return Hop{}, errors.Errorf("No route to ip %s on table.", src) +} func NewRIPMessage(command uint8, entries []RIPEntry) *RIPMessage { return &RIPMessage{ - command: command, + command: command, numEntries: uint8(len(entries)), - entries: entries, + entries: entries, } } @@ -787,9 +762,9 @@ func SendRIPMessage(src Interface, dest *Neighbor, message *RIPMessage) error { bytesToSend := make([]byte, 0, len(headerBytes)+4+1+1+len(message.entries)*12) bytesToSend = append(bytesToSend, headerBytes...) - + // make the RIP message - buf := make([]byte, SIZE_OF_RIP_MESSAGE + len(message.entries)*SIZE_OF_RIP_ENTRY) + buf := make([]byte, SIZE_OF_RIP_MESSAGE+len(message.entries)*SIZE_OF_RIP_ENTRY) buf[0] = message.command buf[1] = message.numEntries @@ -804,7 +779,12 @@ func SendRIPMessage(src Interface, dest *Neighbor, message *RIPMessage) error { bytesToSend = append(bytesToSend, buf...) // send RIP message - _, err = dest.SendSocket.Write(bytesToSend) + sendAddr, err := net.ResolveUDPAddr("udp4", dest.UdpAddr.String()) + tmpConn, err := net.DialUDP("udp4", nil, sendAddr) + if err != nil { + return errors.WithMessage(err, "Could not bind to UDP port->\t"+dest.UdpAddr.String()) + } + _, err = tmpConn.Write(bytesToSend) if err != nil { return err } @@ -854,7 +834,7 @@ func RequestRip() { // // create RIP entries from its neighbors // for prefix, hop := range routingTable { - + // // convert prefix to uint32 // ipUint32 := ConvertIPToUint32(prefix.Addr().String()) @@ -866,15 +846,15 @@ func RequestRip() { // // get // for _, neighbors := range myNeighbors { // for _, neighbor := range neighbors { - + // // check if neighbor is RIP neighbor // // if not, continue // for _, ripNeighbor := range myRIPNeighbors { // if neighbor.VipAddr.String() == ripNeighbor.String() { // // TODO @ MICHAEL: using myVIP -// // BUG: this is not correct +// // BUG: this is not correct // neighborUint32 := ConvertIPToUint32(myVIP.IpPrefix.Addr().String()) - + // // Split Horizon with Poisoned Reverse // cost := hop.Cost // if hop.Type == "R" { @@ -919,15 +899,15 @@ func SendUpdates() { // hardcoded way to get cost to 0, fix if you want a better way entry := &RIPEntry{ address: ConvertIPToUint32(iface2.IpPrefix.Addr().String()), - cost: 17, - mask: ConvertIPToUint32(iface.IpPrefix.Addr().String()), + cost: 17, + mask: ConvertIPToUint32(iface.IpPrefix.Addr().String()), } entries = append(entries, *entry) entry = &RIPEntry{ address: ConvertIPToUint32(iface.IpPrefix.Addr().String()), - cost: 17, - mask: ConvertIPToUint32(iface2.IpPrefix.Addr().String()), + cost: 17, + mask: ConvertIPToUint32(iface2.IpPrefix.Addr().String()), } entries = append(entries, *entry) } @@ -944,12 +924,12 @@ func SendUpdates() { break } } - + // create RIP entry entry := &RIPEntry{ address: ipUint32, - cost: LOCAL_COST, - mask: neighborUint32, + cost: LOCAL_COST, + mask: neighborUint32, } // add to entries and create RIP message @@ -967,7 +947,7 @@ func SendUpdates() { } } - } + } } func ifaceContainsIP(iface Interface, ip netip.Addr) bool { @@ -981,10 +961,14 @@ func ifaceContainsIP(iface Interface, ip netip.Addr) bool { func ConvertIPToUint32(ip string) uint32 { netIP := net.ParseIP(ip) ipBytes := netIP.To4() - ipUint32 := uint32(ipBytes[0]) << 24 | uint32(ipBytes[1]) << 16 | uint32(ipBytes[2]) << 8 | uint32(ipBytes[3]) + ipUint32 := uint32(ipBytes[0])<<24 | uint32(ipBytes[1])<<16 | uint32(ipBytes[2])<<8 | uint32(ipBytes[3]) return ipUint32 } +func intersect(n1, n2 netip.Prefix) bool { + return n1.Overlaps(n2) +} + // TODO @ MICHEAL: Handle links going down and link recovery // func CheckAndUpdateRoutingTable() { // for { @@ -1000,4 +984,4 @@ func ConvertIPToUint32(ip string) uint32 { // } // } -// TODO @ MICHAEL: Triggered Updates and Split Horizon with Poisoned Reverse
\ No newline at end of file +// TODO @ MICHAEL: Triggered Updates and Split Horizon with Poisoned Reverse diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go index 941c4e9..5cef903 100644 --- a/pkg/ipstack/ipstack_test.go +++ b/pkg/ipstack/ipstack_test.go @@ -2,252 +2,307 @@ package ipstack import ( "fmt" - ipv4header "github.com/brown-csci1680/iptcp-headers" - "net" "net/netip" "testing" - "time" ) -func TestInitialize(t *testing.T) { - lnxFilePath := "../../doc-example/r2.lnx" - err := Initialize(lnxFilePath) - if err != nil { - t.Error(err) - } - fmt.Printf("Interfaces:\n%s\n\n", SprintInterfaces()) - fmt.Printf("Neighbors:\n%s\n", SprintNeighbors()) - fmt.Printf("RoutingTable:\n%s\n", SprintRoutingTable()) - - fmt.Println("TestInitialize successful") - t.Cleanup(func() { CleanUp() }) -} - -func TestInterfaceUpThenDown(t *testing.T) { - lnxFilePath := "../../doc-example/r2.lnx" - err := Initialize(lnxFilePath) - if err != nil { - t.Error(err) - } - - iface, err := GetInterfaceByName("if0") - if err != nil { - t.Error(err) - } - - InterfaceUp(iface) - if iface.State == false { - t.Error("iface state should be true") - } - - fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) - - time.Sleep(5 * time.Millisecond) // allow time to print - - InterfaceDown(iface) - if iface.State == true { - t.Error("iface state should be false") - } - - time.Sleep(5 * time.Millisecond) // allow time to print - - fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) - - fmt.Println("TestInterfaceUpThenDown successful") - t.Cleanup(func() { CleanUp() }) -} - -func TestInterfaceUpThenDownTwice(t *testing.T) { - lnxFilePath := "../../doc-example/r2.lnx" - err := Initialize(lnxFilePath) - if err != nil { - t.Error(err) - } - - iface, err := GetInterfaceByName("if0") - if err != nil { - t.Error(err) - } - - InterfaceUp(iface) - if iface.State == false { - t.Error("iface state should be true") - } - - fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) - - time.Sleep(5 * time.Millisecond) // allow time to print - - fmt.Println("putting interface down") - InterfaceDown(iface) - if iface.State == true { - t.Error("iface state should be false") - } - - time.Sleep(3 * time.Millisecond) - - fmt.Println("putting interface back up for 3 iterations") - InterfaceUp(iface) - if iface.State == false { - t.Error("iface state should be true") - } - time.Sleep(3 * time.Millisecond) // allow time to print - - fmt.Println("putting interface down") - InterfaceDown(iface) - if iface.State == true { - t.Error("iface state should be false") - } - - time.Sleep(5 * time.Millisecond) // allow time to print - - fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) - - 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) +//func TestInitialize(t *testing.T) { +// lnxFilePath := "../../doc-example/r2.lnx" +// err := Initialize(lnxFilePath) +// if err != nil { +// t.Error(err) +// } +// fmt.Printf("Interfaces:\n%s\n\n", SprintInterfaces()) +// fmt.Printf("Neighbors:\n%s\n", SprintNeighbors()) +// fmt.Printf("RoutingTable:\n%s\n", SprintRoutingTable()) +// +// fmt.Println("TestInitialize successful") +// t.Cleanup(func() { CleanUp() }) +//} +// +//func TestInterfaceUpThenDown(t *testing.T) { +// lnxFilePath := "../../doc-example/r2.lnx" +// err := Initialize(lnxFilePath) +// if err != nil { +// t.Error(err) +// } +// +// iface, err := GetInterfaceByName("if0") +// if err != nil { +// t.Error(err) +// } +// +// InterfaceUp(iface) +// if iface.State == false { +// t.Error("iface state should be true") +// } +// +// fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) +// +// time.Sleep(5 * time.Millisecond) // allow time to print +// +// InterfaceDown(iface) +// if iface.State == true { +// t.Error("iface state should be false") +// } +// +// time.Sleep(5 * time.Millisecond) // allow time to print +// +// fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) +// +// fmt.Println("TestInterfaceUpThenDown successful") +// t.Cleanup(func() { CleanUp() }) +//} +// +//func TestInterfaceUpThenDownTwice(t *testing.T) { +// lnxFilePath := "../../doc-example/r2.lnx" +// err := Initialize(lnxFilePath) +// if err != nil { +// t.Error(err) +// } +// +// iface, err := GetInterfaceByName("if0") +// if err != nil { +// t.Error(err) +// } +// +// InterfaceUp(iface) +// if iface.State == false { +// t.Error("iface state should be true") +// } +// +// fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) +// +// time.Sleep(5 * time.Millisecond) // allow time to print +// +// fmt.Println("putting interface down") +// InterfaceDown(iface) +// if iface.State == true { +// t.Error("iface state should be false") +// } +// +// time.Sleep(3 * time.Millisecond) +// +// fmt.Println("putting interface back up for 3 iterations") +// InterfaceUp(iface) +// if iface.State == false { +// t.Error("iface state should be true") +// } +// time.Sleep(3 * time.Millisecond) // allow time to print +// +// fmt.Println("putting interface down") +// InterfaceDown(iface) +// if iface.State == true { +// t.Error("iface state should be false") +// } +// +// time.Sleep(5 * time.Millisecond) // allow time to print +// +// fmt.Printf("Interfaces:\n%s\n", SprintInterfaces()) +// +// 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("You are my firest neighbor!")) +// 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() }) +//} + +func TestIntersect(t *testing.T) { + net1 := netip.MustParsePrefix("1.1.1.1/24") + net2 := netip.MustParsePrefix("1.1.1.2/24") + net3 := netip.MustParsePrefix("1.0.0.1/24") + net4 := netip.MustParsePrefix("0.0.0.0/0") // default route + net5 := netip.MustParsePrefix("1.1.1.1/32") + + res00 := intersect(net1, net5) + if !res00 { + t.Error("net1 and net2 should intersect") + t.Fail() } - recvSocket, err := net.ListenUDP("udp4", listenAddr) - if err != nil { - t.Error(err) + res01 := intersect(net5, net1) + if res01 { + t.Error("net1 and net2 should not intersect") + t.Fail() } - 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("You are my firest neighbor!")) - if err != nil { - t.Error(err) + res1 := intersect(net1, net2) + if !res1 { + t.Error("net1 and net2 should intersect") + t.Fail() } - - 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") + res0 := intersect(net2, net1) + if !res0 { + t.Error("net1 and net2 should intersect") 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) + res6 := intersect(net1, net3) + if res6 { + t.Error("net1 and net3 should not intersect") + t.Fail() } - - // get the first neighbor of this interface to RecvIP from - iface, err := GetInterfaceByName("if0") - if err != nil { - t.Error(err) + res2 := intersect(net2, net3) + if res2 { + t.Error("net2 and net3 should not intersect") + t.Fail() } - 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, + res3 := intersect(net1, net4) + if !res3 { + t.Error("net1 and net4 should intersect") + t.Fail() } - 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, + res4 := intersect(net2, net4) + if !res4 { + t.Error("net2 and net4 should intersect") + t.Fail() } - err = SendIP(fakeIface, ifaceAsNeighbor, 0, []byte("hello")) - if err != nil { - return + res5 := intersect(net3, net4) + if !res5 { + t.Error("net3 and net4 should intersect") + t.Fail() } - 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() }) + fmt.Println("TestIntersect successful") } |