diff options
author | sotech117 <michael_foiani@brown.edu> | 2023-10-23 19:02:01 -0400 |
---|---|---|
committer | sotech117 <michael_foiani@brown.edu> | 2023-10-23 19:02:01 -0400 |
commit | 0798d95168446770e8a22f67483d046aa7f2feb0 (patch) | |
tree | 6609899a12f2ac08c41ea9df577f8796089d1e46 | |
parent | 78de9f539dfbcc7eaf400601c80e24ba7230b910 (diff) |
merge with sections of code
-rw-r--r-- | pkg/ipstack/ipstack.go | 121 | ||||
-rwxr-xr-x | vhost | bin | 0 -> 3095650 bytes | |||
-rwxr-xr-x | vrouter | bin | 0 -> 3095650 bytes |
3 files changed, 81 insertions, 40 deletions
diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 29a3cb9..c843bee 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -1,4 +1,5 @@ package ipstack + // code begins on line 97 after imports, constants, and structs definitions // This class is divided as follows: // 1) INIT FUNCTIONS @@ -7,11 +8,10 @@ package ipstack // 4) PRINT FUNCTIONS // 5) ROUTE FUNCTIONS // 6) RIP FUNCTIONS -// 7) PROTOCOL HANDLERS -// 9) CHECKSUM FUNCTIONS +// 7) PROTOCOL HANDLERS +// 8) CHECKSUM FUNCTIONS // 9) HELPERS - import ( "encoding/binary" "fmt" @@ -30,7 +30,6 @@ 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 RIP_PROTOCOL = 200 @@ -76,7 +75,6 @@ type Hop struct { // GLOBAL VARIABLES (data structures) ------------------------------------------ var myInterfaces []*Interface - var myNeighbors = make(map[string][]*Neighbor) var myRIPNeighbors = make(map[string]*Neighbor) @@ -87,13 +85,14 @@ var protocolHandlers = make(map[int]HandlerFunc) var routingTable = make(map[netip.Prefix]Hop) -var mu sync.Mutex +var timeoutTableMu sync.Mutex var timeoutTable = make(map[netip.Prefix]int) // ************************************** INIT FUNCTIONS ********************************************************** // reference: https://github.com/brown-csci1680/lecture-examples/blob/main/ip-demo/cmd/udp-ip-recv/main.go -// createUDPListener creates a UDP listener. +// createUDPListener creates a UDP listener on the given UDP address. +// It SETS the conn parameter to the created UDP socket. func createUDPListener(UdpAddr netip.AddrPort, conn *net.UDPConn) error { listenString := UdpAddr.String() listenAddr, err := net.ResolveUDPAddr("udp4", listenString) @@ -109,7 +108,15 @@ func createUDPListener(UdpAddr netip.AddrPort, conn *net.UDPConn) error { return nil } -// initialize parse the lnxfile and initializes the data structures and listener routines +// Initialize initializes the data structures and creates the UDP sockets. +// +// It will return an error if the lnx file is not valid or if a socket fails to be created. +// +// After parsing the lnx file, it does the following: +// 1. adds each local interface to the routing table, as dictated by its subnet +// 2. adds neighbors to interface->neighbors[] map +// 3. adds RIP neighbors to RIP neighbor list +// 4. adds static routes to routing table func Initialize(lnxFilePath string) error { // Parse the file lnxConfig, err := lnxconfig.ParseConfig(lnxFilePath) @@ -186,7 +193,13 @@ func Initialize(lnxFilePath string) error { return nil } -// defines the go routine that listens on the UDP socket +// InterfaceListenerRoutine is a go routine for interfaces to listen on a UDP port. +// +// It is composed two go routines: +// 1. a go routine that hangs on the recv and calls RecvIP() when a packet is received +// 2. a go routine that listens on the channel for a signal to start/stop listening +// +// TODO: (performance) remove isUp and use the interface's value instead func InterfaceListenerRoutine(i *Interface) { // decompose the interface socket := i.Socket @@ -208,9 +221,6 @@ func InterfaceListenerRoutine(i *Interface) { if closed { // stop this go routine if channel is closed return } - //if !isUp { // don't call the listeners if interface is down - // continue - //} err := RecvIP(i, &isUp) if err != nil { continue @@ -238,7 +248,12 @@ func InterfaceListenerRoutine(i *Interface) { // ************************************** DOWN/UP FUNCTIONS ****************************************************** -// sets the interface to be up and sends a triggered update +// InterfaceUp brings up the link layer +// +// It does the following: +// 1. tells the listener (through a channel) to start listening +// 2. updates the interface state to up +// 3. sends RIP request to all neighbors of this iface to quickly update the routing table func InterfaceUp(iface *Interface) { // set the state to up and send the signal iface.State = true @@ -273,7 +288,12 @@ func InterfaceUpREPL(ifaceName string) { InterfaceUp(iface) } -// sets the interface to be down and sends a triggered update +// InterfaceDown cuts off the link layer. +// +// It does the following: +// 1. tells the listener (through a channel) to stop listening +// 2. updates the interface state to down +// 3. updates the routing table by removing the routes those neighbors connected to, sending triggered updates. func InterfaceDown(iface *Interface) { // set the state to down and send the signal iface.SocketChannel <- false @@ -322,7 +342,7 @@ func GetRoutes() map[netip.Prefix]Hop { // ************************************** PRINT FUNCTIONS ********************************************************** -// returns a string representation of the myInterfaces data structure +// SprintInterfaces returns a string representation of the interfaces data structure func SprintInterfaces() string { tmp := "" for _, iface := range myInterfaces { @@ -337,7 +357,7 @@ func SprintInterfaces() string { return tmp } -// returns a string representation of the myNeighbors data structure +// SprintNeighbors returns a string representation of the neighbors data structure func SprintNeighbors() string { tmp := "" for _, iface := range myInterfaces { @@ -352,7 +372,7 @@ func SprintNeighbors() string { return tmp } -// returns a string representation of the routingTable data structure +// SprintRoutingTable returns a string representation of the routing table func SprintRoutingTable() string { tmp := "" for prefix, hop := range routingTable { @@ -369,9 +389,9 @@ func SprintRoutingTable() string { return tmp } -// ************************************** ROUTE FUNCTIONS ********************************************************** +// ************************************** BASIC FUNCTIONS ********************************************************** -// cleans up the data structures and closes the UDP sockets +// CleanUp cleans up the data structures and closes the UDP sockets func CleanUp() { fmt.Print("Cleaning up...\n") @@ -398,7 +418,14 @@ func CleanUp() { time.Sleep(5 * time.Millisecond) } +// ************************************** ROUTE FUNCTIONS ********************************************************** + // SendIP sends an IP packet to a destination +// +// If the header is nil, then a new header is created +// If the header is not nil, then it will use that header after decrementing TTL & recomputing checksum +// +// TODO: (performance) have this take in an interface instead of src for performance func SendIP(src *netip.Addr, dest *Neighbor, protocolNum int, message []byte, destIP string, hdr *ipv4header.IPv4Header) (int, error) { // check if the interface is up iface, err := GetInterfaceByName(dest.Name) @@ -466,7 +493,6 @@ func SendIP(src *netip.Addr, dest *Neighbor, protocolNum int, message []byte, de return -1, errors.WithMessage(err, "Could not bind to UDP port->\t"+dest.UdpAddr.String()) } - // TODO: make this faster by removing call // send the packet bytesWritten, err := iface.Socket.WriteToUDP(bytesToSend, sendAddr) if err != nil { @@ -477,9 +503,15 @@ func SendIP(src *netip.Addr, dest *Neighbor, protocolNum int, message []byte, de return bytesWritten, nil } -// RecvIP receives an IP packet from a source +// RecvIP receives an IP packet from the interface +// To be called by the listener routine, representing one interface +// Upon receiving a packet, this function: +// 1. determines if packet is valid (checksum, TTL) +// 2. determines if the packet is for me. if so, SENDUP (call correct handler) +// 3. the packet is not SENTUP, then checks the routing table +// 4. if there is no route in the routing table, then prints an error and DROPS the packet func RecvIP(iface *Interface, isOpen *bool) error { - buffer := make([]byte, MAX_IP_PACKET_SIZE) // TODO: fix wording + buffer := make([]byte, MAX_IP_PACKET_SIZE) // Read on the UDP port // fmt.Println("wating to read from UDP socket") @@ -554,9 +586,9 @@ func RecvIP(iface *Interface, isOpen *bool) error { } } - // 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 + // 3) 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 := Route(hdr.Dst) if err == nil { // on no err, found a match @@ -567,7 +599,7 @@ func RecvIP(iface *Interface, isOpen *bool) error { return nil } - // local hop + // - 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] { @@ -580,7 +612,7 @@ func RecvIP(iface *Interface, isOpen *bool) error { } } - // rip hop + // - 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] { @@ -600,7 +632,7 @@ func RecvIP(iface *Interface, isOpen *bool) error { // ************************************** RIP FUNCTIONS ******************************************************* -// creates a byte array that represents a RIP message +// MakeRipMessage returns the byte array to be used in SendIp for a RIP packet func MakeRipMessage(command uint16, entries []RIPEntry) []byte { if command == 1 { // request message buf := make([]byte, SIZE_OF_RIP_HEADER) @@ -636,7 +668,8 @@ func MakeRipMessage(command uint16, entries []RIPEntry) []byte { return buf } -// PeriodicUpdateRoutine sends updates to neighbors every 5 seconds +// PeriodicUpdateRoutine sends RIP updates to neighbors every 5 seconds +// TODO: (performace) consider making this multithreaded and loops above more efficient func PeriodicUpdateRoutine() { for { // for each periodic update, we want to send our nodes in the table @@ -647,7 +680,6 @@ func PeriodicUpdateRoutine() { if !in { continue } - // TODO: consider making this multithreaded and loops above more efficient // Sending to a rip neighbor // create the entries @@ -683,7 +715,7 @@ func PeriodicUpdateRoutine() { } } -// SendTriggeredUpdates sends updates to ALL neighbors +// SendTriggeredUpdates sends the entries consumed to ALL neighbors func SendTriggeredUpdates(newEntries []RIPEntry) { for _, iface := range myInterfaces { for _, n := range myNeighbors[iface.Name] { @@ -705,12 +737,13 @@ func SendTriggeredUpdates(newEntries []RIPEntry) { } } -// manages the timeout table go routine +// ManageTimeoutsRoutine manages the timeout table by incrementing the timeouts every second. +// If a timeout reaches MAX_TIMEOUT, then the entry is deleted from the routing table and a triggered update is sent. func ManageTimeoutsRoutine() { for { time.Sleep(time.Second) - mu.Lock() + timeoutTableMu.Lock() // check if any timeouts have occurred for key, _ := range timeoutTable { timeoutTable[key]++ @@ -728,12 +761,15 @@ func ManageTimeoutsRoutine() { } } } - mu.Unlock() + timeoutTableMu.Unlock() //fmt.Println("Timeout table: ", timeoutTable) } } -// StartRipRoutines routine to send rip requests to neighbors +// StartRipRoutines handles all the routines for RIP +// 1. sends a RIP request to every neighbor +// 2. starts the routine that sends periodic updates every 5 seconds +// 3. starts the routine that manages the timeout table func StartRipRoutines() { // send a request to every neighbor go func() { @@ -764,7 +800,7 @@ func StartRipRoutines() { // ************************************** PROTOCOL HANDLERS ******************************************************* -// registers a protocol handler +// RegisterProtocolHandler registers a protocol handler for a given protocol number func RegisterProtocolHandler(protocolNum int) bool { if protocolNum == RIP_PROTOCOL { protocolHandlers[protocolNum] = HandleRIP @@ -778,6 +814,10 @@ func RegisterProtocolHandler(protocolNum int) bool { return false } +// HandleRIP handles incoming RIP packets in the following way: +// 1. if the command is a request, send a RIP response only to that requestor +// 2. if the command is a response, parse the entries, update the routing table from them, +// and send applicable triggered updates (see implementation for how to update) func HandleRIP(src *Interface, message []byte, hdr *ipv4header.IPv4Header) error { // parse the RIP message command := int(binary.BigEndian.Uint16(message[0:2])) @@ -876,14 +916,14 @@ func HandleRIP(src *Interface, message []byte, hdr *ipv4header.IPv4Header) error // first, upon an update from this prefix, reset its timeout if hop.Type == "R" { - mu.Lock() + timeoutTableMu.Lock() _, in := timeoutTable[destination] if in { if routingTable[destination].VIP == hdr.Src { timeoutTable[destination] = 0 } } - mu.Unlock() + timeoutTableMu.Unlock() } // case 1) the entry SRC == the hop to itself @@ -900,9 +940,9 @@ func HandleRIP(src *Interface, message []byte, hdr *ipv4header.IPv4Header) error time.Sleep(time.Second * time.Duration(MAX_TIMEOUT)) if routingTable[destination].Cost == INFINITY { delete(routingTable, destination) - mu.Lock() + timeoutTableMu.Lock() delete(timeoutTable, destination) - mu.Unlock() + timeoutTableMu.Unlock() } }() } @@ -951,6 +991,7 @@ func ValidateChecksum(b []byte, fromHeader uint16) uint16 { // *********************************************** HELPERS ********************************************************** // Route returns the next HOP, based on longest prefix match for a given ip +// TODO: revisit how to do this at the bit level, not hardcoded for 32 & 24 func Route(src netip.Addr) (Hop, error) { possibleBits := [2]int{32, 24} for _, bits := range possibleBits { Binary files differBinary files differ |