diff options
-rw-r--r-- | pkg/ipstack/ipstack.go | 86 | ||||
-rw-r--r-- | pkg/ipstack/ipstack_test.go | 45 |
2 files changed, 101 insertions, 30 deletions
diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index fb18161..7b67d08 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -20,7 +20,7 @@ type Interface struct { Name string IpPrefix netip.Prefix - RecvSocket net.Conn + RecvSocket net.UDPConn SocketChannel chan bool State bool } @@ -29,7 +29,7 @@ type Neighbor struct { VipAddr netip.Addr UdpAddr netip.AddrPort - SendSocket net.Conn + SendSocket net.UDPConn SocketChannel chan bool } @@ -63,7 +63,7 @@ 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.Conn) error { +func createUDPConn(UdpAddr netip.AddrPort, conn *net.UDPConn) error { listenString := UdpAddr.String() listenAddr, err := net.ResolveUDPAddr("udp4", listenString) if err != nil { @@ -73,7 +73,7 @@ func createUDPConn(UdpAddr netip.AddrPort, conn *net.Conn) error { if err != nil { return errors.WithMessage(err, "Could not bind to UDP port->\t"+listenString) } - *conn = tmpConn + *conn = *tmpConn return nil } @@ -98,14 +98,16 @@ func Initialize(lnxFilePath string) error { i := &Interface{ Name: iface.Name, IpPrefix: prefix, - RecvSocket: nil, - SocketChannel: nil, + RecvSocket: net.UDPConn{}, + SocketChannel: make(chan bool), State: false, } + err := createUDPConn(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) myInterfaces = append(myInterfaces, i) // TODO: (FOR HOSTS ONLY) @@ -120,14 +122,18 @@ func Initialize(lnxFilePath string) error { // 2) initialize the neighbors connected to the node and into the routing table for _, neighbor := range lnxConfig.Neighbors { n := &Neighbor{ - VipAddr: neighbor.DestAddr, - UdpAddr: neighbor.UDPAddr, - SendSocket: nil, + VipAddr: neighbor.DestAddr, + UdpAddr: neighbor.UDPAddr, + SendSocket: net.UDPConn{}, + SocketChannel: make(chan bool), } + err := createUDPConn(neighbor.UDPAddr, &n.SendSocket) if err != nil { return errors.WithMessage(err, "Error creating UDP socket for neighbor->\t"+neighbor.DestAddr.String()) } + go InterfaceListenerRoutine(n.SendSocket, n.SocketChannel) + myNeighbors[neighbor.InterfaceName] = append(myNeighbors[neighbor.InterfaceName], n) // add to routing table @@ -138,41 +144,63 @@ func Initialize(lnxFilePath string) error { return nil } -func InterfaceListenerRoutine(iface Interface, signal <-chan bool) { - isDown := false +//func InitInterfaceListener(iface *Interface) { +// // TODO: cleanup syntax +// iface.State = false +// go func() { +// InterfaceListenerRoutine(iface.RecvSocket, iface.SocketChannel) +// }() +//} + +// TODO: differentiate between SEND AND RECV +func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { + isUp := false + 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 + 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 + } + + fmt.Println("no activity, actively listening on ", socket.LocalAddr().String()) + // TODO: remove these training wheels, call the listener function + time.Sleep(1 * time.Millisecond) + } + }() + for { select { case open, sig := <-signal: if !open { fmt.Println("channel closed, exiting") + closed = true return } - fmt.Println("received SIGNAL with value", sig) - if sig { - isDown = <-signal - } + fmt.Println("received isUP SIGNAL with value", sig) + isUp = sig default: - if isDown { - continue - } - fmt.Println("no activity, actively listening on", iface.Name) - // TODO: remove these training wheels - time.Sleep(1 * time.Millisecond) } } } // When an interface goes up, we need to start it's go routine that listens func InterfaceUp(iface *Interface) { - iface.SocketChannel = make(chan bool) iface.State = true - go func() { - InterfaceListenerRoutine(*iface, iface.SocketChannel) - }() + iface.SocketChannel <- true } func InterfaceDown(iface *Interface) { - iface.SocketChannel <- true + iface.SocketChannel <- false iface.State = false } @@ -335,7 +363,7 @@ func SprintRoutingTable() string { func DebugNeighbors() { for ifaceName, neighbor := range myNeighbors { for _, n := range neighbor { - fmt.Printf("%s\t%s\t%s\t%s\n", ifaceName, n.UdpAddr.String(), n.VipAddr.String(), n.SendSocket) + fmt.Printf("%s\t%s\t%s\n", ifaceName, n.UdpAddr.String(), n.VipAddr.String()) } } } @@ -344,9 +372,7 @@ 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) - } + close(iface.SocketChannel) iface.RecvSocket.Close() } diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go index d5b755a..97c4947 100644 --- a/pkg/ipstack/ipstack_test.go +++ b/pkg/ipstack/ipstack_test.go @@ -53,3 +53,48 @@ func TestInterfaceUpThenDown(t *testing.T) { 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 + + InterfaceDown(iface) + if iface.State == true { + t.Error("iface state should be false") + } + + InterfaceUp(iface) + if iface.State == false { + t.Error("iface state should be true") + } + time.Sleep(3 * 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("TestInterfaceUpThenDownTwice successful") + t.Cleanup(func() { CleanUp() }) +} |