package ipstack import ( "encoding/binary" "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" "sync" "time" // "bytes" // "unicode" ) const ( MAX_IP_PACKET_SIZE = 1400 LOCAL_COST uint32 = 0 STATIC_COST uint32 = 4294967295 // 2^32 - 1 MaxEntries = 64 INFINITY = 16 SIZE_OF_RIP_ENTRY = 12 SIZE_OF_RIP_MESSAGE = 6 RIP_PROTOCOL = 200 TEST_PROTOCOL = 0 ) // STRUCTS --------------------------------------------------------------------- type Interface struct { Name string IpPrefix netip.Prefix UdpAddr netip.AddrPort RecvSocket net.UDPConn SocketChannel chan bool State bool } type Neighbor struct { Name string VipAddr netip.Addr UdpAddr netip.AddrPort } type RIPHeader struct { command uint16 numEntries uint16 } type RIPEntry struct { prefix netip.Prefix cost uint32 } type Hop struct { Cost uint32 Type string Interface *Interface VIP netip.Addr } // GLOBAL VARIABLES (data structures) ------------------------------------------ var myVIP Interface var myInterfaces []*Interface var myNeighbors = make(map[string][]*Neighbor) var myRIPNeighbors = make(map[string]*Neighbor) type HandlerFunc func(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.IPv4Header) error var protocolHandlers = make(map[int]HandlerFunc) 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 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) } 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 } func Initialize(lnxFilePath string) error { // Parse the file lnxConfig, err := lnxconfig.ParseConfig(lnxFilePath) if err != nil { return errors.WithMessage(err, "Error parsing config file->\t"+lnxFilePath) } // 1) add each local if to the routing table, as dictated by its subnet for _, iface := range lnxConfig.Interfaces { prefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) i := &Interface{ Name: iface.Name, IpPrefix: prefix, UdpAddr: iface.UDPAddr, RecvSocket: net.UDPConn{}, SocketChannel: make(chan bool), State: true, } // Added this for printing purposes for REPL FYI, if you have a better way lmk 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) myInterfaces = append(myInterfaces, i) routingTable[prefix.Masked()] = Hop{LOCAL_COST, "L", i, prefix.Addr()} } // 2) add neighbors to if->neighbors map for _, neighbor := range lnxConfig.Neighbors { n := &Neighbor{ Name: neighbor.InterfaceName, VipAddr: neighbor.DestAddr, UdpAddr: neighbor.UDPAddr, } myNeighbors[neighbor.InterfaceName] = append(myNeighbors[neighbor.InterfaceName], n) } // 3) add RIP neighbors to RIP neighbor list for _, route := range lnxConfig.RipNeighbors { // add to RIP neighbors for _, iface := range myInterfaces { for _, neighbor := range myNeighbors[iface.Name] { if neighbor.VipAddr == route { myRIPNeighbors[neighbor.VipAddr.String()] = neighbor break } } } } // 4) add static routes to routing table for prefix, addr := range lnxConfig.StaticRoutes { // need loops to find the interface that matches the neighbor to send static to // hops needs this interface for _, iface := range myInterfaces { for _, neighbor := range myNeighbors[iface.Name] { if neighbor.VipAddr == addr { routingTable[prefix] = Hop{STATIC_COST, "S", iface, addr} break } } } } // add protocol handlers return nil } func InterfaceListenerRoutine(i *Interface) { // decompose the interface socket := i.RecvSocket signal := i.SocketChannel // booleans to control listening routine isUp := 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() { fmt.Println("exiting go routine that listens on ", socket.LocalAddr().String()) }() for { if closed { // stop this go routine if channel is closed return } //if !isUp { // don't call the listeners if interface is down // continue //} // TODO: remove these "training wheels" time.Sleep(1 * time.Millisecond) err := RecvIP(i, &isUp) if err != nil { // fmt.Println("Error receiving IP packet", err) continue } } }() for { select { case sig, ok := <-signal: if !ok { fmt.Println("channel closed, exiting") closed = true return } // fmt.Println("received isUP SIGNAL with value", sig) isUp = sig default: continue } } } // ************************************** DOWN/UP FUNCTIONS ****************************************************** func InterfaceUp(iface *Interface) { iface.State = true iface.SocketChannel <- true // if were a router, send triggered updates on up if _, ok := protocolHandlers[RIP_PROTOCOL]; ok { ripEntries := make([]RIPEntry, 0) ripEntries = append(ripEntries, RIPEntry{iface.IpPrefix.Masked(), LOCAL_COST}) sendTriggeredUpdates(ripEntries) } } func InterfaceUpREPL(ifaceName string) { iface, err := GetInterfaceByName(ifaceName) if err != nil { fmt.Println("Error getting interface by name", err) return } InterfaceUp(iface) } // 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 // if were a router, send triggered updates on down if _, ok := protocolHandlers[RIP_PROTOCOL]; ok { ripEntries := make([]RIPEntry, 0) ripEntries = append(ripEntries, RIPEntry{iface.IpPrefix.Masked(), INFINITY}) sendTriggeredUpdates(ripEntries) } } func InterfaceDownREPL(ifaceName string) { iface, err := GetInterfaceByName(ifaceName) if err != nil { fmt.Println("Error getting interface by name", err) return } InterfaceDown(iface) } // ************************************** GETTER FUNCTIONS ********************************************************** func GetInterfaceByName(ifaceName string) (*Interface, error) { // iterate through the interfaces and return the one with the same name for _, iface := range myInterfaces { if iface.Name == ifaceName { return iface, nil } } return nil, errors.Errorf("No interface with name %s", ifaceName) } func GetNeighborByIP(ipAddr string) (*Neighbor, error) { // iterate through the neighbors and return the one with the same ipAddr 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 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 GetMyVIP() Interface { return myVIP } func GetInterfaces() []*Interface { return myInterfaces } func GetNeighbors() map[string][]*Neighbor { return myNeighbors } func GetRoutes() map[netip.Prefix]Hop { return routingTable } // ************************************** PRINT FUNCTIONS ********************************************************** func SprintInterfaces() string { tmp := "" for _, iface := range myInterfaces { if iface.State { tmp += fmt.Sprintf("%s\t%s\t%s\n", iface.Name, iface.IpPrefix.String(), "UP") } else { tmp += fmt.Sprintf("%s\t%s\t%s\n", iface.Name, iface.IpPrefix.String(), "DOWN") } } return tmp } func SprintNeighbors() string { tmp := "" for _, iface := range myInterfaces { if !iface.State { continue } for _, n := range myNeighbors[iface.Name] { tmp += fmt.Sprintf("%s\t%s\t%s\n", iface.Name, n.VipAddr.String(), n.UdpAddr.String()) } } return tmp } func SprintRoutingTable() string { tmp := "" for prefix, hop := range routingTable { if hop.Type == "L" { tmp += fmt.Sprintf("%s\t%s\tLOCAL:%s\t%d\n", hop.Type, prefix.String(), hop.Interface.Name, 0) } else if hop.Type == "S" { tmp += fmt.Sprintf("%s\t%s\t%s\t%s\n", hop.Type, prefix.String(), hop.VIP.String(), "-") } else { tmp += fmt.Sprintf("%s\t%s\t%s\t%d\n", hop.Type, prefix.String(), hop.VIP.String(), hop.Cost) } } return tmp } // ************************************** BASIC FUNCTIONS ********************************************************** func CleanUp() { fmt.Print("Cleaning up...\n") // go through the interfaces, pop thread & close the UDP FDs for _, iface := range myInterfaces { if iface.SocketChannel != nil { close(iface.SocketChannel) } err := iface.RecvSocket.Close() if err != nil { continue } } // delete all the neighbors myNeighbors = make(map[string][]*Neighbor) // delete all the interfaces myInterfaces = nil // delete the routing table routingTable = make(map[netip.Prefix]Hop) time.Sleep(5 * time.Millisecond) } // TODO: have it take TTL so we can decrement it when forwarding func SendIP(src *netip.Addr, dest *Neighbor, protocolNum int, message []byte, destIP string, hdr *ipv4header.IPv4Header) (int, error) { iface, err := GetInterfaceByName(dest.Name) if !iface.State { return -1, errors.New("error: interface is down") } if hdr == nil { 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, 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, Dst: netip.MustParseAddr(destIP), Options: []byte{}, } } // Assemble the header into a byte array headerBytes, err := hdr.Marshal() if err != nil { return -1, 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) } // Combine the header and the message into a single byte array bytesToSend := make([]byte, 0, len(headerBytes)+len(message)) bytesToSend = append(bytesToSend, headerBytes...) bytesToSend = append(bytesToSend, []byte(message)...) sendAddr, err := net.ResolveUDPAddr("udp4", dest.UdpAddr.String()) // tmpConn, err := net.DialUDP("udp4", nil, sendAddr) // get the interface of this neighbor if err != nil { return -1, errors.WithMessage(err, "Could not bind to UDP port->\t"+dest.UdpAddr.String()) } // bytesWritten, err := tmpConn.Write(bytesToSend) // TODO: make this faster by removing call bytesWritten, err := iface.RecvSocket.WriteToUDP(bytesToSend, sendAddr) if err != nil { fmt.Println("Error writing to UDP socket") return -1, errors.WithMessage(err, "Error writing to UDP socket") } return bytesWritten, nil } func RecvIP(iface *Interface, isOpen *bool) error { buffer := make([]byte, MAX_IP_PACKET_SIZE) // TODO: fix wordking // Read on the UDP port // Too much printing so I commented it out // fmt.Println("wating to read from UDP socket") _, _, err := iface.RecvSocket.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 messageLen := hdr.TotalLen - hdr.Len message := buffer[headerSize : messageLen+headerSize] // 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 // fmt.Println("checksum failed, dropping packet") return nil } // 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) //if hdr.Protocol != RIP_PROTOCOL { // fmt.Println("I see a non-rip packet") //} for _, myIface := range myInterfaces { if hdr.Dst == myIface.IpPrefix.Addr() { // see if there is a handler for this protocol if handler, ok := protocolHandlers[hdr.Protocol]; ok { if hdr.Protocol != RIP_PROTOCOL { // fmt.Println("this test packet is exactly for me") } err := handler(myIface, nil, message, hdr) if err != nil { fmt.Println(err) } } 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 hop if hop.Type == "L" { // 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(&hdr.Src, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) if err2 != nil { return err2 } } } } // rip hop if hop.Type == "R" { // if it's a rip route, then the check is against the hop vip for _, neighbor := range myNeighbors[hop.Interface.Name] { if neighbor.VipAddr == hop.VIP { _, err2 := SendIP(&hdr.Src, neighbor, hdr.Protocol, message, hdr.Dst.String(), hdr) if err2 != nil { return err2 } } } } } // if not in table, drop packet return nil } // ************************************** RIP Routines ******************************************************* func makeRipMessage(command uint16, entries []RIPEntry) []byte { SIZE_OF_RIP_HEADER := 2 * 2 // 2 uint16s if command == 1 { // request message buf := make([]byte, SIZE_OF_RIP_HEADER) binary.BigEndian.PutUint16(buf[0:2], command) binary.BigEndian.PutUint16(buf[2:4], uint16(0)) return buf } // else, command == 2, response message bufLen := SIZE_OF_RIP_HEADER + // sizeof uint16 is 2, we have two of them len(entries)*SIZE_OF_RIP_ENTRY // each entry is 12 buf := make([]byte, bufLen) binary.BigEndian.PutUint16(buf[0:2], command) binary.BigEndian.PutUint16(buf[2:4], uint16(len(entries))) for i, entry := range entries { offset := 2*2 + i*SIZE_OF_RIP_ENTRY binary.BigEndian.PutUint32(buf[offset:offset+4], entry.cost) // 0-3 = 4 bytes copy(buf[offset+4:offset+8], entry.prefix.Addr().AsSlice()) // 4-7 = 4 bytes ipv4Netmask := uint32(0xffffffff) ipv4Netmask <<= 32 - entry.prefix.Bits() binary.BigEndian.PutUint32(buf[offset+8:offset+12], ipv4Netmask) } return buf } func periodicUpdateRoutine() { for { // for each periodic update, we want to send our nodes in the table for _, iface := range myInterfaces { for _, n := range myNeighbors[iface.Name] { _, in := myRIPNeighbors[n.VipAddr.String()] if !in { continue } // TODO: consider making this multithreaded and loops above more efficient // if we're here, we are sending this to a rip neighbor entries := make([]RIPEntry, 0) for prefix, hop := range routingTable { // implement split horizon + poison reverse at entry level // fmt.Println("prefix: ", prefix) var cost uint32 if hop.VIP == n.VipAddr { cost = INFINITY } else { cost = hop.Cost } entries = append(entries, RIPEntry{ prefix: prefix, cost: cost, }) } message := makeRipMessage(2, entries) addr := iface.IpPrefix.Addr() _, err := SendIP(&addr, n, RIP_PROTOCOL, message, n.VipAddr.String(), nil) if err != nil { // fmt.Printf("Error sending RIP message to %s\n", n.VipAddr.String()) continue } } } // wait 5 sec time.Sleep(5 * time.Second) } } var mu sync.Mutex var timeoutTable = make(map[netip.Prefix]int) var MAX_TIMEOUT = 12 func sendTriggeredUpdates(newEntries []RIPEntry) { for _, iface := range myInterfaces { for _, n := range myNeighbors[iface.Name] { _, in := myRIPNeighbors[n.VipAddr.String()] if !in { continue } message := makeRipMessage(2, newEntries) addr := iface.IpPrefix.Addr() _, err := SendIP(&addr, n, RIP_PROTOCOL, message, n.VipAddr.String(), nil) if err != nil { // fmt.Printf("Error sending RIP triggered update to %s\n", n.VipAddr.String()) continue } } } } func timeoutKey(prefix netip.Prefix, addr netip.Addr) string { return prefix.String() + "-" + addr.String() } func manageTimeoutsRoutine() { for { time.Sleep(time.Second) // note: waitgroup causes deadlock then crashing //wg := &sync.WaitGroup{} //wg.Add(len(timeoutTable)) //mu.Lock() //for prefix, _ := range timeoutTable { // go func(p netip.Prefix) { // timeoutTable[p]++ // if timeoutTable[p] == MAX_TIMEOUT { // delete(routingTable, p) // delete(timeoutTable, p) // // TODO: send triggered update // } // // wg.Done() // }(prefix) //} //wg.Wait() //mu.Unlock() mu.Lock() for key, _ := range timeoutTable { timeoutTable[key]++ if timeoutTable[key] == MAX_TIMEOUT { delete(timeoutTable, key) newEntries := make([]RIPEntry, 0) delete(routingTable, key) newEntries = append(newEntries, RIPEntry{key, INFINITY}) // send triggered update on timeout if len(newEntries) > 0 { sendTriggeredUpdates(newEntries) } } } // fmt.Println("timeout table: ", timeoutTable) mu.Unlock() //fmt.Println("Timeout table: ", timeoutTable) } } func startRipRoutines() { // send a request to every neighbor go func() { for _, iface := range myInterfaces { for _, neighbor := range myNeighbors[iface.Name] { _, in := myRIPNeighbors[neighbor.VipAddr.String()] if !in { continue } // send a request message := makeRipMessage(1, nil) addr := iface.IpPrefix.Addr() _, err := SendIP(&addr, neighbor, RIP_PROTOCOL, message, neighbor.VipAddr.String(), nil) if err != nil { return } } } }() go periodicUpdateRoutine() // make a "timeout" table, for each response we add to the table via rip go manageTimeoutsRoutine() // start a routine that sends updates every 10 seconds } // ************************************** Protocol Handlers ******************************************************* func RegisterProtocolHandler(protocolNum int) bool { if protocolNum == RIP_PROTOCOL { protocolHandlers[protocolNum] = handleRIP go startRipRoutines() 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 { // parse the RIP message SIZE_OF_RIP_HEADER := 2 * 2 command := int(binary.BigEndian.Uint16(message[0:2])) switch command { case 1: //fmt.Println("Received RIP command for specific info") // only send if the person asking is a RIP neighbor neighbor, in := myRIPNeighbors[hdr.Src.String()] if !in { break } // fmt.Println("he is my rip neighbor ", hdr.Src.String()) // build the entries entries := make([]RIPEntry, 0) for prefix, hop := range routingTable { // implement split horizon + poison reverse at entry level // fmt.Println("prefix: ", prefix) var cost uint32 if hop.VIP == hdr.Src { cost = INFINITY } else { cost = hop.Cost } entries = append(entries, RIPEntry{ prefix: prefix, cost: cost, }) } res := makeRipMessage(2, entries) _, err := SendIP(&hdr.Dst, neighbor, RIP_PROTOCOL, res, hdr.Src.String(), nil) if err != nil { return err } break case 2: numEntries := int(binary.BigEndian.Uint16(message[2:4])) // fmt.Println("Received RIP response with", numEntries, "entries") // parse the entries entries := make([]RIPEntry, 0) for i := 0; i < numEntries; i++ { offset := SIZE_OF_RIP_HEADER + i*SIZE_OF_RIP_ENTRY // each field is 4 bytes cost := binary.BigEndian.Uint32(message[offset : offset+4]) address, _ := netip.AddrFromSlice(message[offset+4 : offset+8]) mask := net.IPv4Mask(message[offset+8], message[offset+9], message[offset+10], message[offset+11]) // make the prefix bits, _ := mask.Size() prefix := netip.PrefixFrom(address, bits) entries = append(entries, RIPEntry{prefix, cost}) } // update the routing table triggeredEntries := make([]RIPEntry, 0) for _, entry := range entries { destination := entry.prefix.Masked() // make upperbound for cost infinity var newCost uint32 if entry.cost == INFINITY { newCost = INFINITY } else { newCost = entry.cost + 1 } hop, isin := routingTable[destination] // if prefix not in table, add it (as long as it's not infinity) if !isin { if newCost != INFINITY { // given an update to table, this is now a triggeredUpdate // triggeredEntries = append(triggeredEntries, RIPEntry{destination, entry.cost + 1}) routingTable[destination] = Hop{newCost, "R", src, hdr.Src} timeoutTable[destination] = 0 } continue } // if the entry is in the table, only two cases affect the table: // 1) the entry SRC is updating (or confirming) the hop to itself // in this case, only update if the cost is different // if it's infinity, then the route has expired. // we must set the cost to INF then delete the entry after 12 seconds // // 2) a different entry SRC reveals a shorter path to the destination // in this case, update the routing table to use this new path // // all other cases don't meaningfully change the route // first, upon an update from this prefix, reset its timeout if hop.Type == "R" { mu.Lock() _, in := timeoutTable[destination] if in { if routingTable[destination].VIP == hdr.Src { timeoutTable[destination] = 0 } } mu.Unlock() } // case 1) the entry SRC == the hop to itself if hop.VIP == hdr.Src && newCost != hop.Cost { // given an update to table, this is now a triggeredUpdate triggeredEntries = append(triggeredEntries, RIPEntry{destination, newCost}) routingTable[destination] = Hop{newCost, "R", src, hop.VIP} // if we receive infinity from the same neighbor, then delete the route after 12 sec if entry.cost == INFINITY { // remove after GC time if the COST is still INFINITY go func() { time.Sleep(12 * time.Second) if routingTable[destination].Cost == INFINITY { delete(routingTable, destination) mu.Lock() delete(timeoutTable, destination) mu.Unlock() } }() } continue } // case 2) a shorter route for this destination is revealed from a different neighbor if newCost < hop.Cost && newCost != INFINITY { triggeredEntries = append(triggeredEntries, RIPEntry{destination, entry.cost + 1}) routingTable[destination] = Hop{entry.cost + 1, "R", src, hdr.Src} continue } } // send out triggered updates if len(triggeredEntries) > 0 { sendTriggeredUpdates(triggeredEntries) } } return nil } func handleTestPackets(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.IPv4Header) error { 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 } // ************************************** CHECKSUM FUNCTIONS ****************************************************** 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 } // ************************************** RIP FUNCTIONS ********************************************************** // TODO @ MICHAEL: LONGEST PREFIX MATCHING func LongestPrefix(src netip.Addr) (Hop, error) { possibleBits := [2]int{32, 24} 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 SendRIPMessage(src Interface, dest *Neighbor, message *RIPMessage) error { // hdr := ipv4header.IPv4Header{ // Version: 4, // Len: 20, // Header length is always 20 when no IP options // TOS: 0, // TotalLen: ipv4header.HeaderLen + 4 + len(message.entries)*SIZE_OF_RIP_ENTRY, // ID: 0, // Flags: 0, // FragOff: 0, // TTL: 32, // Protocol: RIP_PROTOCOL, // Checksum: 0, // Should be 0 until checksum is computed // Src: src.IpPrefix.Addr(), // Dst: netip.MustParseAddr(dest.VipAddr.String()), // Options: []byte{}, // } // // headerBytes, err := hdr.Marshal() // if err != nil { // return err // } // // hdr.Checksum = int(ComputeChecksum(headerBytes)) // // headerBytes, err = hdr.Marshal() // if err != nil { // log.Fatalln("Error marshalling header: ", err) // } // // bytesToSend := make([]byte, 0) // bytesToSend = append(bytesToSend, headerBytes...) // // // make the RIP message // //buf := make([]byte, SIZE_OF_RIP_MESSAGE+len(message.entries)*SIZE_OF_RIP_ENTRY) // //buf[0] = message.command // //buf[1] = message.numEntries // // buf := make([]byte, 4) // binary.BigEndian.PutUint16(buf[0:2], message.command) // binary.BigEndian.PutUint16(buf[2:], message.numEntries) // // bytesToSend = append(bytesToSend, buf...) // // for _, entry := range message.entries { // // offset := SIZE_OF_RIP_MESSAGE + i*SIZE_OF_RIP_ENTRY // // each field is 4 bytes // buf := make([]byte, SIZE_OF_RIP_ENTRY) // binary.BigEndian.PutUint32(buf, entry.address) // 0-3 = 4 bytes // binary.BigEndian.PutUint32(buf[3:8], entry.mask) // 4-7 = 4 bytes // binary.BigEndian.PutUint32(buf[8:], entry.cost) // 8-11 = 4 bytes // // bytesToSend = append(bytesToSend, buf...) // } // // // send RIP message // 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()) // } // // iface, err := GetInterfaceByName(dest.Name) // //_, err = tmpConn.Write(bytesToSend) // _, err = iface.RecvSocket.WriteToUDP(bytesToSend, sendAddr) // if err != nil { // return err // } // // return nil //} //func RequestRip() { // // create RIP message // message := NewRIPMessage(1, []RIPEntry{}) // // // send RIP message to RIP neighbors // 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() { // // send RIP message // err := SendRIPMessage(myVIP, neighbor, message) // if err != nil { // continue // } // } // } // continue // } // } //} // //func BroadcastPeriodicUpdates() { // // for each periodic update, we want to send our nodes in the table // entries := make([]RIPEntry, len(routingTable)) // for prefix, hop := range routingTable { // entries = append(entries, // RIPEntry{ // address: ConvertIPToUint32(prefix.Addr().String()), // mask: uint32(prefix.Bits()), // cost: hop.Cost, // }) // } // message := NewRIPMessage(2, entries) // // // send to each neighbor // for _, iface := range myInterfaces { // for _, n := range myNeighbors[iface.Name] { // err := SendRIPMessage(*iface, n, message) // if err != nil { // fmt.Printf("Error sending RIP message to %s\n", n.VipAddr.String()) // continue // } // } // } // //} //// THIS MIGHT BE WRONG... //func SendUpdates() { // entries := make([]RIPEntry, len(routingTable)) // // create RIP entries from its interfaces to one another // for _, iface := range myInterfaces { // for _, iface2 := range myInterfaces { // if iface.Name == iface2.Name { // continue // } // // TODO @ MICHAEL: fix this // // 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()), // } // entries = append(entries, *entry) // // entry = &RIPEntry{ // address: ConvertIPToUint32(iface.IpPrefix.Addr().String()), // cost: 17, // mask: ConvertIPToUint32(iface2.IpPrefix.Addr().String()), // } // entries = append(entries, *entry) // } // } // // // create RIP entries from its neighbors // for _, neighbors := range myNeighbors { // for _, neighbor := range neighbors { // ipUint32 := ConvertIPToUint32(neighbor.VipAddr.String()) // var neighborUint32 uint32 // for _, interfaces := range myInterfaces { // if ifaceContainsIP(*interfaces, neighbor.VipAddr) { // neighborUint32 = ConvertIPToUint32(interfaces.IpPrefix.Addr().String()) // break // } // } // // // create RIP entry // entry := &RIPEntry{ // address: ipUint32, // cost: LOCAL_COST, // mask: neighborUint32, // } // // // add to entries and create RIP message // entries = append(entries, *entry) // message := NewRIPMessage(2, entries) // // // send RIP message // for _, Interfaces := range myInterfaces { // if Interfaces.Name == neighbor.Name { // err := SendRIPMessage(myVIP, neighbor, message) // if err != nil { // continue // } // } // } // // } // } //} // TODO @ MICHEAL: Handle links going down and link recovery // func CheckAndUpdateRoutingTable() { // for { // time.Sleep(12 * time.Second) // for prefix, hop := range routingTable { // // delete route if not refreshed in 12 seconds // // not sure if there is a better way to do this // if hop.Type == "R" { // delete(routingTable, prefix) // SendUpdates() // } // } // } // } // TODO @ MICHAEL: Triggered Updates and Split Horizon with Poisoned Reverse