aboutsummaryrefslogtreecommitdiff
path: root/pkg/ipstack
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/ipstack')
-rw-r--r--pkg/ipstack/ipstack.go314
1 files changed, 57 insertions, 257 deletions
diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go
index 030b9c7..e3d9ca2 100644
--- a/pkg/ipstack/ipstack.go
+++ b/pkg/ipstack/ipstack.go
@@ -12,8 +12,6 @@ import (
"net/netip"
"sync"
"time"
- // "bytes"
- // "unicode"
)
const (
@@ -23,9 +21,9 @@ const (
MaxEntries = 64
INFINITY = 16
SIZE_OF_RIP_ENTRY = 12
- SIZE_OF_RIP_MESSAGE = 6
RIP_PROTOCOL = 200
TEST_PROTOCOL = 0
+ SIZE_OF_RIP_HEADER = 4
)
// STRUCTS ---------------------------------------------------------------------
@@ -64,8 +62,8 @@ type Hop struct {
}
// GLOBAL VARIABLES (data structures) ------------------------------------------
-var myVIP Interface
var myInterfaces []*Interface
+
var myNeighbors = make(map[string][]*Neighbor)
var myRIPNeighbors = make(map[string]*Neighbor)
@@ -78,6 +76,8 @@ 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
+
+// createUDPListener creates a UDP listener.
func createUDPListener(UdpAddr netip.AddrPort, conn *net.UDPConn) error {
listenString := UdpAddr.String()
listenAddr, err := net.ResolveUDPAddr("udp4", listenString)
@@ -93,6 +93,7 @@ func createUDPListener(UdpAddr netip.AddrPort, conn *net.UDPConn) error {
return nil
}
+// initialize parse the lnxfile and initializes the data structures and listener routines
func Initialize(lnxFilePath string) error {
// Parse the file
lnxConfig, err := lnxconfig.ParseConfig(lnxFilePath)
@@ -100,7 +101,7 @@ func Initialize(lnxFilePath string) error {
return errors.WithMessage(err, "Error parsing config file->\t"+lnxFilePath)
}
- // 1) add each local if to the routing table, as dictated by its subnet
+ // 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{
@@ -111,16 +112,20 @@ func Initialize(lnxFilePath string) error {
SocketChannel: make(chan bool),
State: true,
}
- // Added this for printing purposes for REPL FYI, if you have a better way lmk
+ // create the UDP listener
err := createUDPListener(iface.UDPAddr, &i.RecvSocket)
if err != nil {
return errors.WithMessage(err, "Error creating UDP socket for interface->\t"+iface.Name)
}
+ // start the listener routine
go InterfaceListenerRoutine(i)
+
+ // add to the list of interfaces
myInterfaces = append(myInterfaces, i)
+ // add to the routing table
routingTable[prefix.Masked()] = Hop{LOCAL_COST, "L", i, prefix.Addr()}
}
@@ -162,10 +167,10 @@ func Initialize(lnxFilePath string) error {
}
}
- // add protocol handlers
return nil
}
+// defines the go routine that listens on the UDP socket
func InterfaceListenerRoutine(i *Interface) {
// decompose the interface
socket := i.RecvSocket
@@ -175,8 +180,9 @@ func InterfaceListenerRoutine(i *Interface) {
isUp := true
closed := false
- // go routine that hangs on the recv
// fmt.Println("MAKING GO ROUTINE TO LISTEN:\t", socket.LocalAddr().String())
+
+ // go routine that hangs on the recv
go func() {
defer func() {
fmt.Println("exiting go routine that listens on ", socket.LocalAddr().String())
@@ -193,7 +199,6 @@ func InterfaceListenerRoutine(i *Interface) {
// time.Sleep(1 * time.Millisecond)
err := RecvIP(i, &isUp)
if err != nil {
- // fmt.Println("Error receiving IP packet", err)
continue
}
}
@@ -201,6 +206,7 @@ func InterfaceListenerRoutine(i *Interface) {
for {
select {
+ // if the channel is closed, exit
case sig, ok := <-signal:
if !ok {
fmt.Println("channel closed, exiting")
@@ -209,6 +215,7 @@ func InterfaceListenerRoutine(i *Interface) {
}
// fmt.Println("received isUP SIGNAL with value", sig)
isUp = sig
+ // if the channel is not closed, continue
default:
continue
}
@@ -217,7 +224,9 @@ func InterfaceListenerRoutine(i *Interface) {
// ************************************** DOWN/UP FUNCTIONS ******************************************************
+// sets the interface to be up and sends a triggered update
func InterfaceUp(iface *Interface) {
+ // set the state to up and send the signal
iface.State = true
iface.SocketChannel <- true
@@ -246,12 +255,13 @@ func InterfaceUpREPL(ifaceName string) {
fmt.Println("Error getting interface by name", err)
return
}
+ // set the state to up and send the signal
InterfaceUp(iface)
}
-// we could do either of these but the REPL results in less work done in router and host
-
+// sets the interface to be down and sends a triggered update
func InterfaceDown(iface *Interface) {
+ // set the state to down and send the signal
iface.SocketChannel <- false
iface.State = false
@@ -269,6 +279,7 @@ func InterfaceDownREPL(ifaceName string) {
fmt.Println("Error getting interface by name", err)
return
}
+ // set the state to down and send the signal
InterfaceDown(iface)
}
@@ -283,31 +294,6 @@ func GetInterfaceByName(ifaceName string) (*Interface, error) {
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
}
@@ -322,22 +308,27 @@ func GetRoutes() map[netip.Prefix]Hop {
// ************************************** PRINT FUNCTIONS **********************************************************
+// returns a string representation of the myInterfaces data structure
func SprintInterfaces() string {
tmp := ""
for _, iface := range myInterfaces {
if iface.State {
+ // if the state is up, print UP
tmp += fmt.Sprintf("%s\t%s\t%s\n", iface.Name, iface.IpPrefix.String(), "UP")
} else {
+ // if the state is down, print DOWN
tmp += fmt.Sprintf("%s\t%s\t%s\n", iface.Name, iface.IpPrefix.String(), "DOWN")
}
}
return tmp
}
+// returns a string representation of the myNeighbors data structure
func SprintNeighbors() string {
tmp := ""
for _, iface := range myInterfaces {
if !iface.State {
+ // if the interface is down, skip it
continue
}
for _, n := range myNeighbors[iface.Name] {
@@ -347,12 +338,15 @@ func SprintNeighbors() string {
return tmp
}
+// returns a string representation of the routingTable data structure
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)
+ // if the hop is local, print LOCAL
+ tmp += fmt.Sprintf("%s\t%s\tLOCAL:%s\t%d\n", hop.Type, prefix.String(), hop.Interface.Name, hop.Cost)
} else if hop.Type == "S" {
+ // if the hop is static, don't print the cost
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)
@@ -363,13 +357,17 @@ func SprintRoutingTable() string {
// ************************************** BASIC FUNCTIONS **********************************************************
+// cleans up the data structures and closes the UDP sockets
func CleanUp() {
fmt.Print("Cleaning up...\n")
+
// go through the interfaces, pop thread & close the UDP FDs
for _, iface := range myInterfaces {
+ // close the channel
if iface.SocketChannel != nil {
close(iface.SocketChannel)
}
+ // close the UDP FD
err := iface.RecvSocket.Close()
if err != nil {
continue
@@ -386,13 +384,14 @@ func CleanUp() {
time.Sleep(5 * time.Millisecond)
}
-// TODO: have it take TTL so we can decrement it when forwarding
+// SendIP sends an IP packet to a destination
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)
if !iface.State {
return -1, errors.Errorf("error SEND: %s is down", iface.Name)
}
-
+ // if the header is nil, create a new one
if hdr == nil {
hdr = &ipv4header.IPv4Header{
Version: 4,
@@ -410,6 +409,7 @@ func SendIP(src *netip.Addr, dest *Neighbor, protocolNum int, message []byte, de
Options: []byte{},
}
} else {
+ // if the header is not nil, decrement the TTL
hdr = &ipv4header.IPv4Header{
Version: 4,
Len: 20, // Header length is always 20 when no IP options
@@ -448,13 +448,10 @@ func SendIP(src *netip.Addr, dest *Neighbor, protocolNum int, message []byte, de
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 {
@@ -465,33 +462,31 @@ func SendIP(src *netip.Addr, dest *Neighbor, protocolNum int, message []byte, de
return bytesWritten, nil
}
+
+// RecvIP receives an IP packet from a source
func RecvIP(iface *Interface, isOpen *bool) error {
- buffer := make([]byte, MAX_IP_PACKET_SIZE) // TODO: fix wordking
+ buffer := make([]byte, MAX_IP_PACKET_SIZE) // TODO: fix wording
// 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
}
+ // check if the interface is up
if !*isOpen {
return errors.Errorf("error RECV: %s is down", iface.Name)
}
// 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
}
+ // checksum validation
headerSize := hdr.Len
headerBytes := buffer[:headerSize]
checksumFromHeader := uint16(hdr.Checksum)
@@ -522,11 +517,13 @@ func RecvIP(iface *Interface, isOpen *bool) error {
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")
//}
+
+ // 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)
for _, myIface := range myInterfaces {
if hdr.Dst == myIface.IpPrefix.Addr() {
// see if there is a handler for this protocol
@@ -589,8 +586,8 @@ func RecvIP(iface *Interface, isOpen *bool) error {
// ************************************** RIP Routines *******************************************************
+// creates a byte array that represents a RIP message
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)
@@ -599,19 +596,23 @@ func makeRipMessage(command uint16, entries []RIPEntry) []byte {
}
// else, command == 2, response message
+ // create the buffer
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)
+ // fill in the header
binary.BigEndian.PutUint16(buf[0:2], command)
binary.BigEndian.PutUint16(buf[2:4], uint16(len(entries)))
+ // fill in the entries
for i, entry := range entries {
- offset := 2*2 + i*SIZE_OF_RIP_ENTRY
+ offset := SIZE_OF_RIP_HEADER + 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
+ // convert the prefix to a uint32
ipv4Netmask := uint32(0xffffffff)
ipv4Netmask <<= 32 - entry.prefix.Bits()
binary.BigEndian.PutUint32(buf[offset+8:offset+12], ipv4Netmask)
@@ -620,6 +621,7 @@ func makeRipMessage(command uint16, entries []RIPEntry) []byte {
return buf
}
+
func periodicUpdateRoutine() {
for {
// for each periodic update, we want to send our nodes in the table
@@ -781,7 +783,7 @@ func RegisterProtocolHandler(protocolNum int) bool {
func handleRIP(src *Interface, dest *Neighbor, message []byte, hdr *ipv4header.IPv4Header) error {
// parse the RIP message
- SIZE_OF_RIP_HEADER := 2 * 2
+ // SIZE_OF_RIP_HEADER := 2 * 2
command := int(binary.BigEndian.Uint16(message[0:2]))
switch command {
case 1:
@@ -962,206 +964,4 @@ func LongestPrefix(src netip.Addr) (Hop, error) {
}
}
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
+} \ No newline at end of file