From 4966c054b3f462f6eaf24591c4ab1a945e72bb6f Mon Sep 17 00:00:00 2001 From: sotech117 Date: Sun, 8 Oct 2023 23:05:53 -0400 Subject: fix heiarchy. rewrite routing table code --- .idea/.gitignore | 8 + .idea/iptcp-jailpt2.iml | 9 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + cmd/example/main.go | 28 +++ go.mod | 2 +- pkg/ipstack/ipstack.go | 298 +++++++++++++++++++++++++++++++ pkg/lnxconfig.go | 365 -------------------------------------- pkg/lnxconfig/lnxconfig.go | 366 +++++++++++++++++++++++++++++++++++++++ pkg/protocol.go | 299 -------------------------------- pkg/routingTable.go | 71 -------- pkg/routingtable/routingtable.go | 81 +++++++++ 12 files changed, 805 insertions(+), 736 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/iptcp-jailpt2.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 cmd/example/main.go create mode 100644 pkg/ipstack/ipstack.go delete mode 100644 pkg/lnxconfig.go create mode 100644 pkg/lnxconfig/lnxconfig.go delete mode 100644 pkg/protocol.go delete mode 100644 pkg/routingTable.go create mode 100644 pkg/routingtable/routingtable.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/iptcp-jailpt2.iml b/.idea/iptcp-jailpt2.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/iptcp-jailpt2.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6ae4065 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cmd/example/main.go b/cmd/example/main.go new file mode 100644 index 0000000..5b6ae61 --- /dev/null +++ b/cmd/example/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "iptcp-jailpt2/pkg/lnxconfig" + "net/netip" + "os" +) + +func main() { + if len(os.Args) != 2 { + fmt.Printf("Usage: %s \n", os.Args[0]) + os.Exit(1) + } + fileName := os.Args[1] + + // Parse the file + lnxConfig, err := lnxconfig.ParseConfig(fileName) + if err != nil { + panic(err) + } + + // Demo: print out the IP for each interface in this config + for _, iface := range lnxConfig.Interfaces { + prefixForm := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) + fmt.Printf("%s has IP %s\n", iface.Name, prefixForm.String()) + } +} diff --git a/go.mod b/go.mod index 9654e0e..b7e6cbe 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module golang-sockets +module iptcp go 1.20 diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go new file mode 100644 index 0000000..770fd17 --- /dev/null +++ b/pkg/ipstack/ipstack.go @@ -0,0 +1,298 @@ +package ipstack + +import ( + "fmt" + ipv4header "github.com/brown-csci1680/iptcp-headers" + "github.com/google/netstack/tcpip/header" + "github.com/pkg/errors" + "../../pkg/lnxconfig" + "log" + "net" + "net/netip" + "os" +) + +const ( + MAX_IP_PACKET_SIZE = 1400 +) + +type Interface struct { + Name string + AssignedIP netip.Addr + AssignedPrefix netip.Prefix + + UDPAddr netip.AddrPort + State bool + neighbors map[netip.AddrPort]netip.AddrPort +} + +// type Host struct { +// Interface Interface +// Neighbors []Neighbor +// } + +// type Router struct { +// Interfaces []Interface +// Neighbors []Neighbor +// RIPNeighbors []Neighbor +// } + + +type Neighbor struct{ + DestAddr netip.Addr + UDPAddr netip.AddrPort + + InterfaceName string +} + +type RIPMessage struct { + command uint8_t; + numEntries uint8_t; + entries []RIPEntry; +} + +type RIPEntry struct { + addr netip.Addr; + cost uint16_t; + mask netip.Prefix; +} + +myInterfaces := make([]Interface); +myNeighbors := make(map[string]Neighbor) +myRIPNeighbors := make(map[string]Neighbor) +protocolHandlers := make(map[uint16]HandlerFunc) +// routingTable := make(map[Address]Routing) + +func Initialize(config IpConfig) (error) { + if len(os.Args) != 2 { + fmt.Printf("Usage: %s \n", os.Args[0]) + os.Exit(1) + } + fileName := os.Args[1] + + // Parse the file + lnxConfig, err := lnxconfig.ParseConfig(fileName) + if err != nil { + panic(err) + } + + // populate routing table??? + for _, iface := range lnxConfig.Interfaces { + myInterfaces = append(myInterfaces, Interface{iface.Name, iface.AssignedIP, iface.AssignedPrefix, iface.UDPAddr, 0}) + } + + for _, neighbor := range lnxConfig.Neighbors { + myNeighbors[neighbor.DestAddr.String()] = Neighbor{neighbor.DestAddr, neighbor.UDPAddr, neighbor.InterfaceName} + } + + // add RIP neighbors + for _, neighbor := range lnxConfig.RipNeighbors { + myRIPNeighbors[neighbor.DestAddr.String()] = Neighbor{neighbor.DestAddr, neighbor.UDPAddr, neighbor.InterfaceName} + } +} + +func ListerToInterfaces() { + for _, iface := range myInterfaces { + go RecvIp(iface) + } +} + +func RecvIp(iface Interface) (error) { + for { + buffer := make([]byte, MAX_IP_PACKET_SIZE) + _, sourceAddr, err := iface.udp.ReadFrom(buffer) + if err != nil { + log.Panicln("Error reading from UDP socket ", err) + } + + hdr, err := ipv4header.ParseHeader(buffer) + + if err != nil { + fmt.Println("Error parsing header", err) + continue + } + + 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" + continue + } + + // check ttl + ttl := data[8] + if ttl == 0 { + fmt.Println("TTL is 0") + continue + } + + + destAddr := netip.AddrFrom(data[16:20]) + protocolNum := data[9] + + if destAddr == iface.addr { + // send to handler + protocolHandlers[protocolNum](data) + // message := buffer[headerSize:] + + // fmt.Printf("Received IP packet from %s\nHeader: %v\nChecksum: %s\nMessage: %s\n", + // sourceAddr.String(), hdr, checksumState, string(message)) + } else { + // decrement ttl and update checksum + data[8] = ttl - 1 + data[10] = 0 + data[11] = 0 + newChecksum := int(ComputeChecksum(data[:headerSize])) + data[10] = newChecksum >> 8 + data[11] = newChecksum & 0xff + + // check neighbors + for _, neighbor := range iface.neighbors { + if neighbor == destAddr { + // send to neighbor + // SendIp(destAddr, protocolNum, data) + } + } + + // check forwarding table + + } + + } +} + +func ValidateChecksum(b []byte, fromHeader uint16) uint16 { + checksum := header.Checksum(b, fromHeader) + + return checksum +} + +func SendIp(dst netip.Addr, port uint16, protocolNum uint16, data []byte, iface Interface) (error) { + bindLocalAddr, err := net.ResolveUDPAddr("udp4", iface.UDPAddr.String()) + if err != nil { + log.Panicln("Error resolving address: ", err) + } + + addrString := fmt.Sprintf("%s:%s", dst, port) + remoteAddr, err := net.ResolveUDPAddr("udp4", addrString) + if err != nil { + log.Panicln("Error resolving address: ", err) + } + + fmt.Printf("Sending to %s:%d\n", + remoteAddr.IP.String(), remoteAddr.Port) + + // Bind on the local UDP port: this sets the source port + // and creates a conn + conn, err := net.ListenUDP("udp4", bindLocalAddr) + if err != nil { + log.Panicln("Dial: ", err) + } + + // Start filling in the header + message := data[20:] + hdr := ipv4header.IPv4Header{ + Version: data[0] >> 4, + Len: 20, // Header length is always 20 when no IP options + TOS: data[1], + TotalLen: ipv4header.HeaderLen + len(message), + ID: data[4], + Flags: data[6] >> 5, + FragOff: data[6] & 0x1f, + TTL: data[8], + Protocol: data[9], + Checksum: 0, // Should be 0 until checksum is computed + Src: netip.MustParseAddr(iface.addr.String()), + Dst: netip.MustParseAddr(dst.String()), + Options: []byte{}, + } + + // Assemble the header into a byte array + headerBytes, err := hdr.Marshal() + if err != nil { + log.Fatalln("Error marshalling header: ", 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) + } + + bytesToSend := make([]byte, 0, len(headerBytes)+len(message)) + bytesToSend = append(bytesToSend, headerBytes...) + bytesToSend = append(bytesToSend, []byte(message)...) + + // Send the message to the "link-layer" addr:port on UDP + bytesWritten, err := conn.WriteToUDP(bytesToSend, remoteAddr) + if err != nil { + log.Panicln("Error writing to socket: ", err) + } + fmt.Printf("Sent %d bytes\n", bytesWritten) +} + +func ComputeChecksum(b []byte) uint16 { + checksum := header.Checksum(b, 0) + checksumInv := checksum ^ 0xffff + + return checksumInv +} + +func ForwardIP(data []byte) (error) { +} + +type HandlerFunc = func help(*Packet, []interface{}) (error) { + + // do smth with packet +} + +func AddRecvHandler(protocolNum uint8, callbackFunc HandlerFunc) (error) { + if protocolHandlers[protocolNum] != nil { + fmt.Printf("Warning: Handler for protocol %d already exists", protocolNum) + } + protocolHandlers[protocolNum] = callbackFunc + return nil +} + +func RemoveRecvHandler(protocolNum uint8) (error) { + // consider error + if protocolHandlers[protocolNum] == nil { + return errors.Errorf("No handler for protocol %d", protocolNum) + } + delete(protocolHandlers, protocolNum) + return nil +} + +// func routeRip(data []byte) (error) { +// // deconstruct packet +// newRIPMessage := RIPMessage{} +// newRIPMessage.command = data[0] +// newRIPMessage.numEntries = data[1] +// newRIPMessage.entries = make([]RIPEntry, newRIPMessage.numEntries) +// } + +func PrintNeighbors() { + for _, iface := range myNeighbors { + fmt.Printf("%s\n", iface.addr.String()) + } +} + +func PrintInterfaces() { + for _, iface := range myInterfaces { + fmt.Printf("%s\n", iface.addr.String()) + } +} +func GetNeighbors() ([]netip.Addr) { + return myNeighbors +} + diff --git a/pkg/lnxconfig.go b/pkg/lnxconfig.go deleted file mode 100644 index 36b1b56..0000000 --- a/pkg/lnxconfig.go +++ /dev/null @@ -1,365 +0,0 @@ -package lnxconfig - -import ( - "bufio" - "fmt" - "net/netip" - "os" - "strings" -) - -type RoutingMode int - -const ( - RoutingTypeNone RoutingMode = 0 - RoutingTypeStatic RoutingMode = 1 - RoutingTypeRIP RoutingMode = 2 -) - -/* - * NOTE: These data structures only represent structure of a - * configuration file. In your implementation, you will still need to - * build your own data structures that store relevant information - * about your links, interfaces, etc. at runtime. - * - * These structs only represent the things in the config file--you - * will probably only parse these at startup in order to set up your own - * data structures. - * - */ -type IPConfig struct { - Interfaces []InterfaceConfig - Neighbors []NeighborConfig - - OriginatingPrefixes []netip.Prefix // Unused in F23, ignore. - - RoutingMode RoutingMode - - // ROUTERS ONLY: Neighbors to send RIP packets - RipNeighbors []netip.Addr - - // Manually-added routes ("route" directive, usually just for default on hosts) - StaticRoutes map[netip.Prefix]netip.Addr -} - -type InterfaceConfig struct { - Name string - AssignedIP netip.Addr - AssignedPrefix netip.Prefix - - UDPAddr netip.AddrPort -} - -type NeighborConfig struct { - DestAddr netip.Addr - UDPAddr netip.AddrPort - - InterfaceName string -} - -// Static config for testing -var LnxConfig = IPConfig{ - Interfaces: []InterfaceConfig{ - { - Name: "if0", - AssignedIP: netip.MustParseAddr("10.1.0.1"), - AssignedPrefix: netip.MustParsePrefix("10.1.0.1/24"), - UDPAddr: netip.MustParseAddrPort("127.0.0.1:5000"), - }, - { - Name: "if1", - AssignedIP: netip.MustParseAddr("10.10.1.1"), - AssignedPrefix: netip.MustParsePrefix("10.10.1.1/24"), - UDPAddr: netip.MustParseAddrPort("127.0.0.1:5001"), - }, - }, - - Neighbors: []NeighborConfig{ - { - DestAddr: netip.MustParseAddr("10.1.0.10"), - UDPAddr: netip.MustParseAddrPort("127.0.0.1:6001"), - InterfaceName: "if0", - }, - { - DestAddr: netip.MustParseAddr("10.10.1.2"), - UDPAddr: netip.MustParseAddrPort("127.0.0.1:5100"), - InterfaceName: "if1", - }, - }, - - OriginatingPrefixes: []netip.Prefix{ - netip.MustParsePrefix("10.1.0.1/24"), - }, - - RoutingMode: RoutingTypeStatic, - - RipNeighbors: []netip.Addr{ - netip.MustParseAddr("10.10.1.2"), - }, -} - -// ******************** END PUBLIC INTERFACE ********************************************* -// (You shouldn't need to worry about what's below, unless you want to modify the parser.) - -type ParseFunc func(int, string, *IPConfig) error - -var parseCommands = map[string]ParseFunc{ - "interface": parseInterface, - "neighbor": parseNeighbor, - "routing": parseRouting, - "route": parseRoute, - "rip": parseRip, -} - -func parseRip(ln int, line string, config *IPConfig) error { - tokens := strings.Split(line, " ") - - if len(tokens) < 2 { - return newErrString(ln, "Usage: rip [cmd] ...") - } - cmd := tokens[1] - ripTokens := tokens[2:] - - switch cmd { - case "originate": - if len(ripTokens) < 2 && ripTokens[0] != "prefix" { - return newErrString(ln, "Usage: rip originate prefix ") - } - ripPrefix, err := netip.ParsePrefix(ripTokens[1]) - if err != nil { - return newErr(ln, err) - } - ripPrefix = ripPrefix.Masked() - - // Check if prefix is in config - err = addOriginatingPrefix(config, ripPrefix) - if err != nil { - return err - } - case "advertise-to": - if len(ripTokens) < 1 { - return newErrString(ln, "Usage: rip advertise-to ") - } - addr, err := netip.ParseAddr(ripTokens[0]) - if err != nil { - return newErr(ln, err) - } - err = addRipNeighbor(config, addr) - if err != nil { - return err - } - default: - return newErrString(ln, "Unrecognized RIP command %s", cmd) - } - - return nil -} - -func addOriginatingPrefix(config *IPConfig, prefix netip.Prefix) error { - for _, iface := range config.Interfaces { - if iface.AssignedPrefix == prefix { - config.OriginatingPrefixes = append(config.OriginatingPrefixes, prefix) - return nil - } - } - - return errors.Errorf("No matching prefix %s in config", prefix.String()) -} - -func addRipNeighbor(config *IPConfig, neighbor netip.Addr) error { - for _, iface := range config.Neighbors { - if iface.DestAddr == neighbor { - config.RipNeighbors = append(config.RipNeighbors, neighbor) - return nil - } - } - - return errors.Errorf("RIP neighbor %s is not a neighbor IP", neighbor.String()) -} - -func parseRouting(ln int, line string, config *IPConfig) error { - tokens := strings.Split(line, " ") - - if len(tokens) < 2 { - return newErrString(ln, "routing directive must have format: routing ") - } - rt := tokens[1] - - switch rt { - case "static": - config.RoutingMode = RoutingTypeStatic - case "rip": - config.RoutingMode = RoutingTypeRIP - default: - return newErrString(ln, "Invalid routing type: %s", rt) - } - - return nil -} - -func parseRoute(ln int, line string, config *IPConfig) error { - var sPrefix, sAddr string - - format := "route via " - r := strings.NewReader(line) - n, err := fmt.Fscanf(r, "route %s via %s", &sPrefix, &sAddr) - - if err != nil { - return err - } - - if n != 2 { - return newErrString(ln, "route directive must have format %s", format) - } - - prefix, err := netip.ParsePrefix(sPrefix) - if err != nil { - return err - } - - addr, err := netip.ParseAddr(sAddr) - if err != nil { - return err - } - - config.StaticRoutes[prefix] = addr - return nil -} - -func parseInterface(ln int, line string, config *IPConfig) error { - var sName, sPrefix, sBindAddr string - - format := "interface " - - r := strings.NewReader(line) - n, err := fmt.Fscanf(r, "interface %s %s %s", - &sName, &sPrefix, &sBindAddr) - - if err != nil { - return err - } - - if n != 3 { - return newErrString(ln, "interface directive must have format: %s", format) - } - - // Check prefix format first - prefix, err := netip.ParsePrefix(sPrefix) - if err != nil { - return err - } - - addr := prefix.Addr() // Get addr part - prefix = prefix.Masked() // Clear add bits for prefix - - addrPort, err := netip.ParseAddrPort(sBindAddr) - if err != nil { - return err - } - - iface := InterfaceConfig{ - Name: sName, - AssignedIP: addr, - AssignedPrefix: prefix, - UDPAddr: addrPort, - } - - config.Interfaces = append(config.Interfaces, iface) - return nil -} - -func parseNeighbor(ln int, line string, config *IPConfig) error { - var sDestAddr, sUDPAddr, sIfName string - - format := "neighbor at via " - - r := strings.NewReader(line) - n, err := fmt.Fscanf(r, "neighbor %s at %s via %s", - &sDestAddr, &sUDPAddr, &sIfName) - - if err != nil { - return err - } - - if n != 3 { - newErrString(ln, "neighbor directive must have format: %s", format) - } - - destAddr, err := netip.ParseAddr(sDestAddr) - if err != nil { - return err - } - - udpAddr, err := netip.ParseAddrPort(sUDPAddr) - if err != nil { - return err - } - - neighbor := NeighborConfig{ - DestAddr: destAddr, - UDPAddr: udpAddr, - InterfaceName: sIfName, - } - - config.Neighbors = append(config.Neighbors, neighbor) - - return nil -} - -func newErrString(line int, msg string, args ...any) error { - _msg := fmt.Sprintf(msg, args...) - return errors.New(fmt.Sprintf("Parse error on line %d: %s", line, _msg)) -} - -func newErr(line int, err error) error { - return errors.New(fmt.Sprintf("Parse error on line %d: %s", line, err.Error())) - -} - -// Parse a configuration file -func ParseConfig(configFile string) (*IPConfig, error) { - fd, err := os.Open(configFile) - if err != nil { - return nil, errors.New("Unable to open file") - } - defer fd.Close() - - config := &IPConfig{ - Interfaces: make([]InterfaceConfig, 0, 1), - Neighbors: make([]NeighborConfig, 0, 1), - OriginatingPrefixes: make([]netip.Prefix, 0, 1), - - RipNeighbors: make([]netip.Addr, 0), - StaticRoutes: make(map[netip.Prefix]netip.Addr, 0), - } - - scanner := bufio.NewScanner(fd) - ln := 0 - for scanner.Scan() { - ln++ - - line := scanner.Text() - tokens := strings.Split(line, " ") - - if len(tokens) == 0 { - continue - } - - // Skip comments - head := tokens[0] - if len(head) == 0 || head == "#" || head[0] == '#' { - continue - } - - pf, found := parseCommands[head] - if !found { - return nil, newErrString(ln, "Unrecognized token %s", head) - } - err = pf(ln, line, config) - if err != nil { - return nil, newErr(ln, err) - } - } - - return config, nil -} \ No newline at end of file diff --git a/pkg/lnxconfig/lnxconfig.go b/pkg/lnxconfig/lnxconfig.go new file mode 100644 index 0000000..d0699f9 --- /dev/null +++ b/pkg/lnxconfig/lnxconfig.go @@ -0,0 +1,366 @@ +package lnxconfig + +import ( + "bufio" + "fmt" + "github.com/pkg/errors" + "net/netip" + "os" + "strings" +) + +type RoutingMode int + +const ( + RoutingTypeNone RoutingMode = 0 + RoutingTypeStatic RoutingMode = 1 + RoutingTypeRIP RoutingMode = 2 +) + +/* + * NOTE: These data structures only represent structure of a + * configuration file. In your implementation, you will still need to + * build your own data structures that store relevant information + * about your links, interfaces, etc. at runtime. + * + * These structs only represent the things in the config file--you + * will probably only parse these at startup in order to set up your own + * data structures. + * + */ +type IPConfig struct { + Interfaces []InterfaceConfig + Neighbors []NeighborConfig + + OriginatingPrefixes []netip.Prefix // Unused in F23, ignore. + + RoutingMode RoutingMode + + // ROUTERS ONLY: Neighbors to send RIP packets + RipNeighbors []netip.Addr + + // Manually-added routes ("route" directive, usually just for default on hosts) + StaticRoutes map[netip.Prefix]netip.Addr +} + +type InterfaceConfig struct { + Name string + AssignedIP netip.Addr + AssignedPrefix netip.Prefix + + UDPAddr netip.AddrPort +} + +type NeighborConfig struct { + DestAddr netip.Addr + UDPAddr netip.AddrPort + + InterfaceName string +} + +// Static config for testing +var LnxConfig = IPConfig{ + Interfaces: []InterfaceConfig{ + { + Name: "if0", + AssignedIP: netip.MustParseAddr("10.1.0.1"), + AssignedPrefix: netip.MustParsePrefix("10.1.0.1/24"), + UDPAddr: netip.MustParseAddrPort("127.0.0.1:5000"), + }, + { + Name: "if1", + AssignedIP: netip.MustParseAddr("10.10.1.1"), + AssignedPrefix: netip.MustParsePrefix("10.10.1.1/24"), + UDPAddr: netip.MustParseAddrPort("127.0.0.1:5001"), + }, + }, + + Neighbors: []NeighborConfig{ + { + DestAddr: netip.MustParseAddr("10.1.0.10"), + UDPAddr: netip.MustParseAddrPort("127.0.0.1:6001"), + InterfaceName: "if0", + }, + { + DestAddr: netip.MustParseAddr("10.10.1.2"), + UDPAddr: netip.MustParseAddrPort("127.0.0.1:5100"), + InterfaceName: "if1", + }, + }, + + OriginatingPrefixes: []netip.Prefix{ + netip.MustParsePrefix("10.1.0.1/24"), + }, + + RoutingMode: RoutingTypeStatic, + + RipNeighbors: []netip.Addr{ + netip.MustParseAddr("10.10.1.2"), + }, +} + +// ******************** END PUBLIC INTERFACE ********************************************* +// (You shouldn't need to worry about what's below, unless you want to modify the parser.) + +type ParseFunc func(int, string, *IPConfig) error + +var parseCommands = map[string]ParseFunc{ + "interface": parseInterface, + "neighbor": parseNeighbor, + "routing": parseRouting, + "route": parseRoute, + "rip": parseRip, +} + +func parseRip(ln int, line string, config *IPConfig) error { + tokens := strings.Split(line, " ") + + if len(tokens) < 2 { + return newErrString(ln, "Usage: rip [cmd] ...") + } + cmd := tokens[1] + ripTokens := tokens[2:] + + switch cmd { + case "originate": + if len(ripTokens) < 2 && ripTokens[0] != "prefix" { + return newErrString(ln, "Usage: rip originate prefix ") + } + ripPrefix, err := netip.ParsePrefix(ripTokens[1]) + if err != nil { + return newErr(ln, err) + } + ripPrefix = ripPrefix.Masked() + + // Check if prefix is in config + err = addOriginatingPrefix(config, ripPrefix) + if err != nil { + return err + } + case "advertise-to": + if len(ripTokens) < 1 { + return newErrString(ln, "Usage: rip advertise-to ") + } + addr, err := netip.ParseAddr(ripTokens[0]) + if err != nil { + return newErr(ln, err) + } + err = addRipNeighbor(config, addr) + if err != nil { + return err + } + default: + return newErrString(ln, "Unrecognized RIP command %s", cmd) + } + + return nil +} + +func addOriginatingPrefix(config *IPConfig, prefix netip.Prefix) error { + for _, iface := range config.Interfaces { + if iface.AssignedPrefix == prefix { + config.OriginatingPrefixes = append(config.OriginatingPrefixes, prefix) + return nil + } + } + + return errors.Errorf("No matching prefix %s in config", prefix.String()) +} + +func addRipNeighbor(config *IPConfig, neighbor netip.Addr) error { + for _, iface := range config.Neighbors { + if iface.DestAddr == neighbor { + config.RipNeighbors = append(config.RipNeighbors, neighbor) + return nil + } + } + + return errors.Errorf("RIP neighbor %s is not a neighbor IP", neighbor.String()) +} + +func parseRouting(ln int, line string, config *IPConfig) error { + tokens := strings.Split(line, " ") + + if len(tokens) < 2 { + return newErrString(ln, "routing directive must have format: routing ") + } + rt := tokens[1] + + switch rt { + case "static": + config.RoutingMode = RoutingTypeStatic + case "rip": + config.RoutingMode = RoutingTypeRIP + default: + return newErrString(ln, "Invalid routing type: %s", rt) + } + + return nil +} + +func parseRoute(ln int, line string, config *IPConfig) error { + var sPrefix, sAddr string + + format := "route via " + r := strings.NewReader(line) + n, err := fmt.Fscanf(r, "route %s via %s", &sPrefix, &sAddr) + + if err != nil { + return err + } + + if n != 2 { + return newErrString(ln, "route directive must have format %s", format) + } + + prefix, err := netip.ParsePrefix(sPrefix) + if err != nil { + return err + } + + addr, err := netip.ParseAddr(sAddr) + if err != nil { + return err + } + + config.StaticRoutes[prefix] = addr + return nil +} + +func parseInterface(ln int, line string, config *IPConfig) error { + var sName, sPrefix, sBindAddr string + + format := "interface " + + r := strings.NewReader(line) + n, err := fmt.Fscanf(r, "interface %s %s %s", + &sName, &sPrefix, &sBindAddr) + + if err != nil { + return err + } + + if n != 3 { + return newErrString(ln, "interface directive must have format: %s", format) + } + + // Check prefix format first + prefix, err := netip.ParsePrefix(sPrefix) + if err != nil { + return err + } + + addr := prefix.Addr() // Get addr part + prefix = prefix.Masked() // Clear add bits for prefix + + addrPort, err := netip.ParseAddrPort(sBindAddr) + if err != nil { + return err + } + + iface := InterfaceConfig{ + Name: sName, + AssignedIP: addr, + AssignedPrefix: prefix, + UDPAddr: addrPort, + } + + config.Interfaces = append(config.Interfaces, iface) + return nil +} + +func parseNeighbor(ln int, line string, config *IPConfig) error { + var sDestAddr, sUDPAddr, sIfName string + + format := "neighbor at via " + + r := strings.NewReader(line) + n, err := fmt.Fscanf(r, "neighbor %s at %s via %s", + &sDestAddr, &sUDPAddr, &sIfName) + + if err != nil { + return err + } + + if n != 3 { + newErrString(ln, "neighbor directive must have format: %s", format) + } + + destAddr, err := netip.ParseAddr(sDestAddr) + if err != nil { + return err + } + + udpAddr, err := netip.ParseAddrPort(sUDPAddr) + if err != nil { + return err + } + + neighbor := NeighborConfig{ + DestAddr: destAddr, + UDPAddr: udpAddr, + InterfaceName: sIfName, + } + + config.Neighbors = append(config.Neighbors, neighbor) + + return nil +} + +func newErrString(line int, msg string, args ...any) error { + _msg := fmt.Sprintf(msg, args...) + return errors.New(fmt.Sprintf("Parse error on line %d: %s", line, _msg)) +} + +func newErr(line int, err error) error { + return errors.New(fmt.Sprintf("Parse error on line %d: %s", line, err.Error())) + +} + +// Parse a configuration file +func ParseConfig(configFile string) (*IPConfig, error) { + fd, err := os.Open(configFile) + if err != nil { + return nil, errors.New("Unable to open file") + } + defer fd.Close() + + config := &IPConfig{ + Interfaces: make([]InterfaceConfig, 0, 1), + Neighbors: make([]NeighborConfig, 0, 1), + OriginatingPrefixes: make([]netip.Prefix, 0, 1), + + RipNeighbors: make([]netip.Addr, 0), + StaticRoutes: make(map[netip.Prefix]netip.Addr, 0), + } + + scanner := bufio.NewScanner(fd) + ln := 0 + for scanner.Scan() { + ln++ + + line := scanner.Text() + tokens := strings.Split(line, " ") + + if len(tokens) == 0 { + continue + } + + // Skip comments + head := tokens[0] + if len(head) == 0 || head == "#" || head[0] == '#' { + continue + } + + pf, found := parseCommands[head] + if !found { + return nil, newErrString(ln, "Unrecognized token %s", head) + } + err = pf(ln, line, config) + if err != nil { + return nil, newErr(ln, err) + } + } + + return config, nil +} diff --git a/pkg/protocol.go b/pkg/protocol.go deleted file mode 100644 index 9358099..0000000 --- a/pkg/protocol.go +++ /dev/null @@ -1,299 +0,0 @@ -package protocol - -import ( - "net" - "net/netip" - "fmt" - "os" - "bufio" - "time" - "github.com/pkg/errors" - ipv4header "github.com/brown-csci1680/iptcp-headers" - "github.com/google/netstack/tcpip/header" - "github.com/brown-csci1680/ipstack-utils" -) - -const ( - MAX_IP_PACKET_SIZE = 1400 -) - -type Interface struct { - Name string - AssignedIP netip.Addr - AssignedPrefix netip.Prefix - - UDPAddr netip.AddrPort - State uint8_t - neighbors map[netip.AddrPort]netip.AddrPort -} - -// type Host struct { -// Interface Interface -// Neighbors []Neighbor -// } - -// type Router struct { -// Interfaces []Interface -// Neighbors []Neighbor -// RIPNeighbors []Neighbor -// } - - -type Neighbor struct{ - DestAddr netip.Addr - UDPAddr netip.AddrPort - - InterfaceName string -} - -type RIPMessage struct { - command uint8_t; - numEntries uint8_t; - entries []RIPEntry; -} - -type RIPEntry struct { - addr netip.Addr; - cost uint16_t; - mask netip.Prefix; -} - -myInterfaces := make([]Interface); -myNeighbors := make(map[string]Neighbor) -myRIPNeighbors := make(map[string]Neighbor) -protocolHandlers := make(map[uint16]HandlerFunc) -// routingTable := make(map[Address]Routing) - -func Initialize(config IpConfig) (error) { - if len(os.Args) != 2 { - fmt.Printf("Usage: %s \n", os.Args[0]) - os.Exit(1) - } - fileName := os.Args[1] - - // Parse the file - lnxConfig, err := lnxconfig.ParseConfig(fileName) - if err != nil { - panic(err) - } - - // populate routing table??? - for _, iface := range lnxConfig.Interfaces { - myInterfaces = append(myInterfaces, Interface{iface.Name, iface.AssignedIP, iface.AssignedPrefix, iface.UDPAddr, 0}) - } - - for _, neighbor := range lnxConfig.Neighbors { - myNeighbors[neighbor.DestAddr.String()] = Neighbor{neighbor.DestAddr, neighbor.UDPAddr, neighbor.InterfaceName} - } - - // add RIP neighbors - for _, neighbor := range lnxConfig.RipNeighbors { - myRIPNeighbors[neighbor.DestAddr.String()] = Neighbor{neighbor.DestAddr, neighbor.UDPAddr, neighbor.InterfaceName} - } -} - -func ListerToInterfaces() { - for _, iface := range myInterfaces { - go RecvIp(iface) - } -} - -func RecvIp(iface Interface) (error) { - for { - buffer := make([]byte, MAX_IP_PACKET_SIZE) - _, sourceAddr, err := iface.udp.ReadFrom(buffer) - if err != nil { - log.Panicln("Error reading from UDP socket ", err) - } - - hdr, err := ipv4header.ParseHeader(buffer) - - if err != nil { - fmt.Println("Error parsing header", err) - continue - } - - 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" - continue - } - - // check ttl - ttl := data[8] - if ttl == 0 { - fmt.Println("TTL is 0") - continue - } - - - destAddr := netip.AddrFrom(data[16:20]) - protocolNum := data[9] - - if destAddr == iface.addr { - // send to handler - protocolHandlers[protocolNum](data) - // message := buffer[headerSize:] - - // fmt.Printf("Received IP packet from %s\nHeader: %v\nChecksum: %s\nMessage: %s\n", - // sourceAddr.String(), hdr, checksumState, string(message)) - } else { - // decrement ttl and update checksum - data[8] = ttl - 1 - data[10] = 0 - data[11] = 0 - newChecksum := int(ComputeChecksum(data[:headerSize])) - data[10] = newChecksum >> 8 - data[11] = newChecksum & 0xff - - // check neighbors - for _, neighbor := range iface.neighbors { - if neighbor == destAddr { - // send to neighbor - // SendIp(destAddr, protocolNum, data) - } - } - - // check forwarding table - - } - - } -} - -func ValidateChecksum(b []byte, fromHeader uint16) uint16 { - checksum := header.Checksum(b, fromHeader) - - return checksum -} - -func SendIp(dst netip.Addr, port uint16, protocolNum uint16, data []byte, iface Interface) (error) { - bindLocalAddr, err := net.ResolveUDPAddr("udp4", iface.UDPAddr.String()) - if err != nil { - log.Panicln("Error resolving address: ", err) - } - - addrString := fmt.Sprintf("%s:%s", dst, port) - remoteAddr, err := net.ResolveUDPAddr("udp4", addrString) - if err != nil { - log.Panicln("Error resolving address: ", err) - } - - fmt.Printf("Sending to %s:%d\n", - remoteAddr.IP.String(), remoteAddr.Port) - - // Bind on the local UDP port: this sets the source port - // and creates a conn - conn, err := net.ListenUDP("udp4", bindLocalAddr) - if err != nil { - log.Panicln("Dial: ", err) - } - - // Start filling in the header - message := data[20:] - hdr := ipv4header.IPv4Header{ - Version: data[0] >> 4, - Len: 20, // Header length is always 20 when no IP options - TOS: data[1], - TotalLen: ipv4header.HeaderLen + len(message), - ID: data[4], - Flags: data[6] >> 5, - FragOff: data[6] & 0x1f, - TTL: data[8], - Protocol: data[9], - Checksum: 0, // Should be 0 until checksum is computed - Src: netip.MustParseAddr(iface.addr.String()), - Dst: netip.MustParseAddr(dst.String()), - Options: []byte{}, - } - - // Assemble the header into a byte array - headerBytes, err := hdr.Marshal() - if err != nil { - log.Fatalln("Error marshalling header: ", 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) - } - - bytesToSend := make([]byte, 0, len(headerBytes)+len(message)) - bytesToSend = append(bytesToSend, headerBytes...) - bytesToSend = append(bytesToSend, []byte(message)...) - - // Send the message to the "link-layer" addr:port on UDP - bytesWritten, err := conn.WriteToUDP(bytesToSend, remoteAddr) - if err != nil { - log.Panicln("Error writing to socket: ", err) - } - fmt.Printf("Sent %d bytes\n", bytesWritten) -} - -func ComputeChecksum(b []byte) uint16 { - checksum := header.Checksum(b, 0) - checksumInv := checksum ^ 0xffff - - return checksumInv -} - -func ForwardIP(data []byte) (error) { -} - -type HandlerFunc = func help(*Packet, []interface{}) (error) { - - // do smth with packet -} - -func AddRecvHandler(protocolNum uint8, callbackFunc HandlerFunc) (error) { - if protocolHandlers[protocolNum] != nil { - fmt.Printf("Warning: Handler for protocol %d already exists", protocolNum) - } - protocolHandlers[protocolNum] = callbackFunc - return nil -} - -func RemoveRecvHandler(protocolNum uint8) (error) { - // consider error - if protocolHandlers[protocolNum] == nil { - return errors.Errorf("No handler for protocol %d", protocolNum) - } - delete(protocolHandlers, protocolNum) - return nil -} - -// func routeRip(data []byte) (error) { -// // deconstruct packet -// newRIPMessage := RIPMessage{} -// newRIPMessage.command = data[0] -// newRIPMessage.numEntries = data[1] -// newRIPMessage.entries = make([]RIPEntry, newRIPMessage.numEntries) -// } - -func PrintNeighbors() { - for _, iface := range myNeighbors { - fmt.Printf("%s\n", iface.addr.String()) - } -} - -func PrintInterfaces() { - for _, iface := range myInterfaces { - fmt.Printf("%s\n", iface.addr.String()) - } -} -func GetNeighbors() ([]netip.Addr) { - return myNeighbors -} - diff --git a/pkg/routingTable.go b/pkg/routingTable.go deleted file mode 100644 index bda4524..0000000 --- a/pkg/routingTable.go +++ /dev/null @@ -1,71 +0,0 @@ -package routingTable - -import ( - "fmt" - "net" - "net/netip" - "os" - "bufio" -) - -type Address struct { - addr netip.Addr; - prefix netip.Prefix; -} - -type Routing struct { - dest Address; - cost uint16_t; - mask netip.Prefix; -} - -routingTable := make(map[Address]Routing) - -func Initialize(config IpConfig) (error) { - if len(os.Args) != 2 { - fmt.Printf("Usage: %s \n", os.Args[0]) - os.Exit(1) - } - fileName := os.Args[1] - - lnxConfig, err := lnxconfig.ParseConfig(fileName) - if err != nil { - panic(err) - } - - // populate routing table - for _, iface := range lnxConfig.Interfaces { - routingTable[Address{iface.AssignedIP, iface.AssignedPrefix}] = Routing{Address{iface.AssignedIP, iface.AssignedPrefix}, 0, iface.AssignedPrefix} - } - -} - -func AddRoute(dest Address, cost uint16_t, mask netip.Prefix) (error) { - if _, ok := routingTable[dest]; ok { - return newErrString(ln, "Route already exists") - } - routingTable[dest] = Routing{dest, cost, mask} -} - -func RemoveRoute(dest Address) (error) { - if _, ok := routingTable[dest]; !ok { - return newErrString(ln, "Route does not exist") - } - delete(routingTable, dest) -} - -func GetRoute(dest Address) (Routing, error) { - // get the most specific route - for key, value := range routingTable { - if key.prefix.Contains(dest.addr) { - return value, nil - } - } - return nil, newErrString(ln, "Route does not exist") -} - -func PrintRoutingTable() { - for key, value := range routingTable { - fmt.Printf("%s/%d\t%d\n", key.addr, key.prefix.Bits(), value.cost) - } -} \ No newline at end of file diff --git a/pkg/routingtable/routingtable.go b/pkg/routingtable/routingtable.go new file mode 100644 index 0000000..7f7e2b2 --- /dev/null +++ b/pkg/routingtable/routingtable.go @@ -0,0 +1,81 @@ +package routingtable + +import ( + "fmt" + "github.com/pkg/errors" + "net/netip" +) + +type Address struct { + addr netip.Addr + prefix netip.Prefix +} + +type Route struct { + dest Address + cost uint32 + mask netip.Prefix +} + +var table map[Address]Route + +//func Initialize(config lnxconfig.IPConfig) error { +// if len(os.Args) != 2 { +// fmt.Printf("Usage: %s \n", os.Args[0]) +// os.Exit(1) +// } +// fileName := os.Args[1] +// +// lnxConfig, err := lnxconfig.ParseConfig(fileName) +// if err != nil { +// panic(err) +// } +// +// // make and populate routing table +// table = make(map[Address]Route) +// for _, iface := range lnxConfig.Interfaces { +// var address = Address{iface.AssignedIP, iface.AssignedPrefix} +// var route = Route{Address{iface.AssignedIP, iface.AssignedPrefix}, 0, iface.AssignedPrefix} +// table[address] = route +// } +// +// +//} + +func AddRoute(dest Address, cost uint32, mask netip.Prefix) error { + if _, ok := table[dest]; ok { + return errors.New("Route already exists") + } + + table[dest] = Route{dest, cost, mask} + return nil +} + +func RemoveRoute(dest Address) error { + if _, ok := table[dest]; !ok { + return errors.New("Route doesn't exist") + } + + delete(table, dest) + return nil +} + +// TODO: implement this with most specific prefix matching +func GetRoute(dest Address) (Route, error) { + // get the most specific route + for key, value := range table { + if key.prefix.Contains(dest.addr) { + return value, nil + } + } + return Route{}, errors.New("Route doesn't exist") +} + +func SprintRoutingTable() string { + message := "" + for address, route := range table { + message += fmt.Sprintf("%s/%d\t%d\n", address.addr, address.prefix, route.cost) + } + + return message +} -- cgit v1.2.3-70-g09d2 From 077d576becae10b35b84782d4070fbf2f5c0b7c8 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 06:00:08 +0000 Subject: good progress, fixing almost all of the red. in the process of ensuring the data structures are initialized correctly. --- cmd/example/main.go | 5 +- doc-example/binaries.example.json | 17 ++++ doc-example/nodes.json | 7 ++ pkg/ipstack/ipstack.go | 193 ++++++++++++++++++++++++-------------- pkg/ipstack/ipstack_test.go | 20 ++++ pkg/lnxconfig/lnxconfig.go | 11 --- pkg/routingtable/routingtable.go | 51 +++++----- 7 files changed, 199 insertions(+), 105 deletions(-) create mode 100644 doc-example/binaries.example.json create mode 100644 doc-example/nodes.json create mode 100644 pkg/ipstack/ipstack_test.go diff --git a/cmd/example/main.go b/cmd/example/main.go index 5b6ae61..383e490 100644 --- a/cmd/example/main.go +++ b/cmd/example/main.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "iptcp-jailpt2/pkg/lnxconfig" + "iptcp/pkg/lnxconfig" "net/netip" "os" ) @@ -24,5 +24,8 @@ func main() { for _, iface := range lnxConfig.Interfaces { prefixForm := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) fmt.Printf("%s has IP %s\n", iface.Name, prefixForm.String()) + + fmt.Printf(iface.UDPAddr.String() + "\n") + fmt.Printf(iface.AssignedIP.String() + "\n") } } diff --git a/doc-example/binaries.example.json b/doc-example/binaries.example.json new file mode 100644 index 0000000..b6ff6d9 --- /dev/null +++ b/doc-example/binaries.example.json @@ -0,0 +1,17 @@ +{ + "h1": { + "binary_path": "./vhost" + }, + "h2": { + "binary_path": "./vhost" + }, + "h3": { + "binary_path": "./vhost" + }, + "r1": { + "binary_path": "./vrouter" + }, + "r2": { + "binary_path": "./vrouter" + } +} \ No newline at end of file diff --git a/doc-example/nodes.json b/doc-example/nodes.json new file mode 100644 index 0000000..7b91355 --- /dev/null +++ b/doc-example/nodes.json @@ -0,0 +1,7 @@ +{ + "h1": "host", + "h2": "host", + "h3": "host", + "r1": "router", + "r2": "router" +} \ No newline at end of file diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 770fd17..abaa09f 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -2,102 +2,147 @@ package ipstack import ( "fmt" - ipv4header "github.com/brown-csci1680/iptcp-headers" - "github.com/google/netstack/tcpip/header" "github.com/pkg/errors" - "../../pkg/lnxconfig" - "log" + "iptcp/pkg/lnxconfig" "net" "net/netip" - "os" ) const ( - MAX_IP_PACKET_SIZE = 1400 + MAX_IP_PACKET_SIZE = 1400 + LOCAL_COST uint32 = 4294967295 // 2^32 - 1 + STATIC_COST uint32 = 1 ) +// STRUCTS --------------------------------------------------------------------- type Interface struct { - Name string - AssignedIP netip.Addr - AssignedPrefix netip.Prefix + Name string + IpAddress netip.Addr + IpPrefix netip.Prefix - UDPAddr netip.AddrPort - State bool - neighbors map[netip.AddrPort]netip.AddrPort + RecvSocket net.Conn + SocketChannel chan<- bool + State bool } -// type Host struct { -// Interface Interface -// Neighbors []Neighbor -// } - -// type Router struct { -// Interfaces []Interface -// Neighbors []Neighbor -// RIPNeighbors []Neighbor -// } +type Neighbor struct { + VipAddr netip.Addr + UdpAddr netip.AddrPort - -type Neighbor struct{ - DestAddr netip.Addr - UDPAddr netip.AddrPort - - InterfaceName string + SendSocket net.Conn + SocketChannel chan<- bool } type RIPMessage struct { - command uint8_t; - numEntries uint8_t; - entries []RIPEntry; + command uint8 + numEntries uint8 + entries []RIPEntry } type RIPEntry struct { - addr netip.Addr; - cost uint16_t; - mask netip.Prefix; + addr netip.Addr + cost uint32 + mask netip.Prefix } -myInterfaces := make([]Interface); -myNeighbors := make(map[string]Neighbor) -myRIPNeighbors := make(map[string]Neighbor) -protocolHandlers := make(map[uint16]HandlerFunc) -// routingTable := make(map[Address]Routing) +type Hop struct { + Cost uint32 + VipAsStr string +} -func Initialize(config IpConfig) (error) { - if len(os.Args) != 2 { - fmt.Printf("Usage: %s \n", os.Args[0]) - os.Exit(1) +// GLOBAL VARIABLES (data structures) ------------------------------------------ +var myInterfaces []Interface +var myNeighbors = make(map[string]Neighbor) + +// var myRIPNeighbors = make(map[string]Neighbor) +type HandlerFunc func(int, string, *[]byte) error + +var protocolHandlers = make(map[uint16]HandlerFunc) + +// var routingTable = routingtable.New() +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 { + listenString := UdpAddr.String() + listenAddr, err := net.ResolveUDPAddr("udp4", listenString) + if err != nil { + return errors.WithMessage(err, "Error resolving address->\t"+listenString) } - fileName := os.Args[1] + 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 { + //if len(os.Args) != 2 { + // fmt.Printf("Usage: %s \n", os.Args[0]) + // os.Exit(1) + //} + //lnxFilePath := os.Args[1] // Parse the file - lnxConfig, err := lnxconfig.ParseConfig(fileName) + lnxConfig, err := lnxconfig.ParseConfig(lnxFilePath) if err != nil { - panic(err) + return errors.WithMessage(err, "Error parsing config file->\t"+lnxFilePath) } - // populate routing table??? + // 1) initialize the interfaces on this node here and into the routing table for _, iface := range lnxConfig.Interfaces { - myInterfaces = append(myInterfaces, Interface{iface.Name, iface.AssignedIP, iface.AssignedPrefix, iface.UDPAddr, 0}) + i := &Interface{ + Name: iface.Name, + IpAddress: iface.AssignedIP, + IpPrefix: iface.AssignedPrefix, + RecvSocket: nil, + SocketChannel: nil, + State: false, + } + err := createUDPConn(iface.UDPAddr, &i.RecvSocket) + if err != nil { + return errors.WithMessage(err, "Error creating UDP socket for interface->\t"+iface.Name) + } + myInterfaces = append(myInterfaces, *i) + + // add to routing table + ifacePrefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) + routingTable[ifacePrefix] = Hop{STATIC_COST, iface.Name} } + // 2) initialize the neighbors connected to the node for _, neighbor := range lnxConfig.Neighbors { - myNeighbors[neighbor.DestAddr.String()] = Neighbor{neighbor.DestAddr, neighbor.UDPAddr, neighbor.InterfaceName} - } + n := &Neighbor{ + VipAddr: neighbor.DestAddr, + UdpAddr: neighbor.UDPAddr, + SendSocket: nil, + } + err := createUDPConn(neighbor.UDPAddr, &n.SendSocket) + if err != nil { + return errors.WithMessage(err, "Error creating UDP socket for neighbor->\t"+neighbor.DestAddr.String()) + } + // TODO: make a hash map + myNeighbors[neighbor.InterfaceName] = *n - // add RIP neighbors - for _, neighbor := range lnxConfig.RipNeighbors { - myRIPNeighbors[neighbor.DestAddr.String()] = Neighbor{neighbor.DestAddr, neighbor.UDPAddr, neighbor.InterfaceName} + // add to routing table + neighborPrefix := netip.PrefixFrom(neighbor.DestAddr, 24) + routingTable[neighborPrefix] = Hop{LOCAL_COST, neighbor.InterfaceName} } + + return nil } +/* + func ListerToInterfaces() { for _, iface := range myInterfaces { go RecvIp(iface) } } -func RecvIp(iface Interface) (error) { +func RecvIp(iface Interface) error { for { buffer := make([]byte, MAX_IP_PACKET_SIZE) _, sourceAddr, err := iface.udp.ReadFrom(buffer) @@ -125,7 +170,7 @@ func RecvIp(iface Interface) (error) { checksumState = "FAIL" continue } - + // check ttl ttl := data[8] if ttl == 0 { @@ -133,7 +178,6 @@ func RecvIp(iface Interface) (error) { continue } - destAddr := netip.AddrFrom(data[16:20]) protocolNum := data[9] @@ -174,7 +218,7 @@ func ValidateChecksum(b []byte, fromHeader uint16) uint16 { return checksum } -func SendIp(dst netip.Addr, port uint16, protocolNum uint16, data []byte, iface Interface) (error) { +func SendIp(dst netip.Addr, port uint16, protocolNum uint16, data []byte, iface Interface) error { bindLocalAddr, err := net.ResolveUDPAddr("udp4", iface.UDPAddr.String()) if err != nil { log.Panicln("Error resolving address: ", err) @@ -248,15 +292,10 @@ func ComputeChecksum(b []byte) uint16 { return checksumInv } -func ForwardIP(data []byte) (error) { -} - -type HandlerFunc = func help(*Packet, []interface{}) (error) { - - // do smth with packet +func ForwardIP(data []byte) error { } -func AddRecvHandler(protocolNum uint8, callbackFunc HandlerFunc) (error) { +func AddRecvHandler(protocolNum uint8, callbackFunc HandlerFunc) error { if protocolHandlers[protocolNum] != nil { fmt.Printf("Warning: Handler for protocol %d already exists", protocolNum) } @@ -264,7 +303,7 @@ func AddRecvHandler(protocolNum uint8, callbackFunc HandlerFunc) (error) { return nil } -func RemoveRecvHandler(protocolNum uint8) (error) { +func RemoveRecvHandler(protocolNum uint8) error { // consider error if protocolHandlers[protocolNum] == nil { return errors.Errorf("No handler for protocol %d", protocolNum) @@ -281,18 +320,28 @@ func RemoveRecvHandler(protocolNum uint8) (error) { // newRIPMessage.entries = make([]RIPEntry, newRIPMessage.numEntries) // } -func PrintNeighbors() { - for _, iface := range myNeighbors { - fmt.Printf("%s\n", iface.addr.String()) - } +func GetNeighbors() []netip.Addr { + return myNeighbors } +*/ func PrintInterfaces() { for _, iface := range myInterfaces { - fmt.Printf("%s\n", iface.addr.String()) + fmt.Printf("%s\t%s\t%t\n", iface.Name, iface.IpPrefix.String(), iface.State) } } -func GetNeighbors() ([]netip.Addr) { - return myNeighbors + +func PrintNeighbors() { + for ifaceName, neighbor := range myNeighbors { + fmt.Printf("%s\t%s\t%s\n", ifaceName, neighbor.IpAddress.String(), neighbor.UdpAddr.String()) + } } +func SprintRoutingTable() string { + message := "" + for prefix, hop := range routingTable { + message += fmt.Sprintf("%s\t%s\t%d\n", prefix.String(), hop.VipAsStr, hop.Cost) + } + + return message +} diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go new file mode 100644 index 0000000..5530b9d --- /dev/null +++ b/pkg/ipstack/ipstack_test.go @@ -0,0 +1,20 @@ +package ipstack + +import ( + "fmt" + "testing" +) + +func TestInitialize(t *testing.T) { + lnxFilePath := "../../doc-example/r2.lnx" + err := Initialize(lnxFilePath) + if err != nil { + t.Error(err) + } + fmt.Println("TestInitialize successful") + PrintInterfaces() + fmt.Println("Interfaces^^") + PrintNeighbors() + fmt.Println("Neighbors^^") + fmt.Println(SprintRoutingTable()) +} diff --git a/pkg/lnxconfig/lnxconfig.go b/pkg/lnxconfig/lnxconfig.go index d0699f9..8e43613 100644 --- a/pkg/lnxconfig/lnxconfig.go +++ b/pkg/lnxconfig/lnxconfig.go @@ -17,17 +17,6 @@ const ( RoutingTypeRIP RoutingMode = 2 ) -/* - * NOTE: These data structures only represent structure of a - * configuration file. In your implementation, you will still need to - * build your own data structures that store relevant information - * about your links, interfaces, etc. at runtime. - * - * These structs only represent the things in the config file--you - * will probably only parse these at startup in order to set up your own - * data structures. - * - */ type IPConfig struct { Interfaces []InterfaceConfig Neighbors []NeighborConfig diff --git a/pkg/routingtable/routingtable.go b/pkg/routingtable/routingtable.go index 7f7e2b2..90b64ae 100644 --- a/pkg/routingtable/routingtable.go +++ b/pkg/routingtable/routingtable.go @@ -7,17 +7,26 @@ import ( ) type Address struct { - addr netip.Addr - prefix netip.Prefix + Addr netip.Addr + Prefix netip.Prefix } -type Route struct { - dest Address - cost uint32 - mask netip.Prefix +type Hop struct { + Cost uint32 + VipAsStr string } -var table map[Address]Route +type RoutingTable map[Address]Hop + +const ( + STATIC_COST uint32 = 4294967295 // 2^32 - 1 +) + +// TODO: consider making this take in arguments, such as a config file +func New() *RoutingTable { + var table = make(RoutingTable) + return &table +} //func Initialize(config lnxconfig.IPConfig) error { // if len(os.Args) != 2 { @@ -42,39 +51,39 @@ var table map[Address]Route // //} -func AddRoute(dest Address, cost uint32, mask netip.Prefix) error { - if _, ok := table[dest]; ok { +func AddRoute(srcAddr Address, cost uint32, addrAsStr string, tableReference *RoutingTable) error { + if _, ok := (*tableReference)[srcAddr]; ok { return errors.New("Route already exists") } - table[dest] = Route{dest, cost, mask} + (*tableReference)[srcAddr] = Hop{cost, addrAsStr} return nil } -func RemoveRoute(dest Address) error { - if _, ok := table[dest]; !ok { +func RemoveRoute(dest Address, table *RoutingTable) error { + if _, ok := (*table)[dest]; !ok { return errors.New("Route doesn't exist") } - delete(table, dest) + delete(*table, dest) return nil } // TODO: implement this with most specific prefix matching -func GetRoute(dest Address) (Route, error) { +func Route(dest Address, table *RoutingTable) (Hop, error) { // get the most specific route - for key, value := range table { - if key.prefix.Contains(dest.addr) { - return value, nil + for address, route := range *table { + if address.Prefix.Contains(dest.Addr) { + return route, nil } } - return Route{}, errors.New("Route doesn't exist") + return Hop{}, errors.New("Route doesn't exist") } -func SprintRoutingTable() string { +func SprintRoutingTable(table *RoutingTable) string { message := "" - for address, route := range table { - message += fmt.Sprintf("%s/%d\t%d\n", address.addr, address.prefix, route.cost) + for address, route := range *table { + message += fmt.Sprintf("%s/%d\t%d\n", address.Addr, address.Prefix, route.Cost) } return message -- cgit v1.2.3-70-g09d2 From 41a675f0f260b7436713a63f6cf2f1db7f8d827f Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 10:00:06 -0400 Subject: fix the bugs with filling data structures --- pkg/ipstack/ipstack.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index abaa09f..e4d1651 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -10,15 +10,14 @@ import ( const ( MAX_IP_PACKET_SIZE = 1400 - LOCAL_COST uint32 = 4294967295 // 2^32 - 1 - STATIC_COST uint32 = 1 + LOCAL_COST uint32 = 0 + STATIC_COST uint32 = 4294967295 // 2^32 - 1 ) // STRUCTS --------------------------------------------------------------------- type Interface struct { - Name string - IpAddress netip.Addr - IpPrefix netip.Prefix + Name string + IpPrefix netip.Prefix RecvSocket net.Conn SocketChannel chan<- bool @@ -52,7 +51,7 @@ type Hop struct { // GLOBAL VARIABLES (data structures) ------------------------------------------ var myInterfaces []Interface -var myNeighbors = make(map[string]Neighbor) +var myNeighbors = make(map[string][]Neighbor) // var myRIPNeighbors = make(map[string]Neighbor) type HandlerFunc func(int, string, *[]byte) error @@ -93,10 +92,10 @@ func Initialize(lnxFilePath string) error { // 1) initialize the interfaces on this node here and into the routing table for _, iface := range lnxConfig.Interfaces { + prefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) i := &Interface{ Name: iface.Name, - IpAddress: iface.AssignedIP, - IpPrefix: iface.AssignedPrefix, + IpPrefix: prefix, RecvSocket: nil, SocketChannel: nil, State: false, @@ -108,8 +107,8 @@ func Initialize(lnxFilePath string) error { myInterfaces = append(myInterfaces, *i) // add to routing table - ifacePrefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) - routingTable[ifacePrefix] = Hop{STATIC_COST, iface.Name} + //ifacePrefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) + //routingTable[ifacePrefix] = Hop{STATIC_COST, iface.Name} } // 2) initialize the neighbors connected to the node @@ -123,8 +122,7 @@ func Initialize(lnxFilePath string) error { if err != nil { return errors.WithMessage(err, "Error creating UDP socket for neighbor->\t"+neighbor.DestAddr.String()) } - // TODO: make a hash map - myNeighbors[neighbor.InterfaceName] = *n + myNeighbors[neighbor.InterfaceName] = append(myNeighbors[neighbor.InterfaceName], *n) // add to routing table neighborPrefix := netip.PrefixFrom(neighbor.DestAddr, 24) @@ -333,7 +331,9 @@ func PrintInterfaces() { func PrintNeighbors() { for ifaceName, neighbor := range myNeighbors { - fmt.Printf("%s\t%s\t%s\n", ifaceName, neighbor.IpAddress.String(), neighbor.UdpAddr.String()) + for _, n := range neighbor { + fmt.Printf("%s\t%s\t%s\n", ifaceName, n.UdpAddr.String(), n.VipAddr.String()) + } } } -- cgit v1.2.3-70-g09d2 From 05b4acd8843805230484bb2a35846fe06566084f Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 12:00:00 -0400 Subject: robustly implement how channels will interact with threads --- pkg/ipstack/ipstack.go | 191 ++++++++++++++++++++++++-------------------- pkg/ipstack/ipstack_test.go | 45 +++++++++-- 2 files changed, 143 insertions(+), 93 deletions(-) diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index e4d1651..fb18161 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -6,6 +6,7 @@ import ( "iptcp/pkg/lnxconfig" "net" "net/netip" + "time" ) const ( @@ -20,7 +21,7 @@ type Interface struct { IpPrefix netip.Prefix RecvSocket net.Conn - SocketChannel chan<- bool + SocketChannel chan bool State bool } @@ -29,7 +30,7 @@ type Neighbor struct { UdpAddr netip.AddrPort SendSocket net.Conn - SocketChannel chan<- bool + SocketChannel chan bool } type RIPMessage struct { @@ -50,8 +51,8 @@ type Hop struct { } // GLOBAL VARIABLES (data structures) ------------------------------------------ -var myInterfaces []Interface -var myNeighbors = make(map[string][]Neighbor) +var myInterfaces []*Interface +var myNeighbors = make(map[string][]*Neighbor) // var myRIPNeighbors = make(map[string]Neighbor) type HandlerFunc func(int, string, *[]byte) error @@ -91,6 +92,7 @@ func Initialize(lnxFilePath string) error { } // 1) initialize the interfaces on this node here and into the routing table + static := false for _, iface := range lnxConfig.Interfaces { prefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) i := &Interface{ @@ -104,14 +106,18 @@ func Initialize(lnxFilePath string) error { if err != nil { return errors.WithMessage(err, "Error creating UDP socket for interface->\t"+iface.Name) } - myInterfaces = append(myInterfaces, *i) - - // add to routing table - //ifacePrefix := netip.PrefixFrom(iface.AssignedIP, iface.AssignedPrefix.Bits()) - //routingTable[ifacePrefix] = Hop{STATIC_COST, iface.Name} + myInterfaces = append(myInterfaces, i) + + // TODO: (FOR HOSTS ONLY) + // add STATIC to routing table + if !static { + ifacePrefix := netip.MustParsePrefix("0.0.0.0/0") + routingTable[ifacePrefix] = Hop{STATIC_COST, iface.Name} + static = true + } } - // 2) initialize the neighbors connected to the node + // 2) initialize the neighbors connected to the node and into the routing table for _, neighbor := range lnxConfig.Neighbors { n := &Neighbor{ VipAddr: neighbor.DestAddr, @@ -122,7 +128,7 @@ func Initialize(lnxFilePath string) error { if err != nil { return errors.WithMessage(err, "Error creating UDP socket for neighbor->\t"+neighbor.DestAddr.String()) } - myNeighbors[neighbor.InterfaceName] = append(myNeighbors[neighbor.InterfaceName], *n) + myNeighbors[neighbor.InterfaceName] = append(myNeighbors[neighbor.InterfaceName], n) // add to routing table neighborPrefix := netip.PrefixFrom(neighbor.DestAddr, 24) @@ -132,84 +138,51 @@ func Initialize(lnxFilePath string) error { return nil } -/* - -func ListerToInterfaces() { - for _, iface := range myInterfaces { - go RecvIp(iface) - } -} - -func RecvIp(iface Interface) error { +func InterfaceListenerRoutine(iface Interface, signal <-chan bool) { + isDown := false for { - buffer := make([]byte, MAX_IP_PACKET_SIZE) - _, sourceAddr, err := iface.udp.ReadFrom(buffer) - if err != nil { - log.Panicln("Error reading from UDP socket ", err) - } - - hdr, err := ipv4header.ParseHeader(buffer) - - if err != nil { - fmt.Println("Error parsing header", err) - continue - } - - 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" - continue - } - - // check ttl - ttl := data[8] - if ttl == 0 { - fmt.Println("TTL is 0") - continue + select { + case open, sig := <-signal: + if !open { + fmt.Println("channel closed, exiting") + return + } + fmt.Println("received SIGNAL with value", sig) + if sig { + isDown = <-signal + } + default: + if isDown { + continue + } + fmt.Println("no activity, actively listening on", iface.Name) + // TODO: remove these training wheels + time.Sleep(1 * time.Millisecond) } + } +} - destAddr := netip.AddrFrom(data[16:20]) - protocolNum := data[9] - - if destAddr == iface.addr { - // send to handler - protocolHandlers[protocolNum](data) - // message := buffer[headerSize:] - - // fmt.Printf("Received IP packet from %s\nHeader: %v\nChecksum: %s\nMessage: %s\n", - // sourceAddr.String(), hdr, checksumState, string(message)) - } else { - // decrement ttl and update checksum - data[8] = ttl - 1 - data[10] = 0 - data[11] = 0 - newChecksum := int(ComputeChecksum(data[:headerSize])) - data[10] = newChecksum >> 8 - data[11] = newChecksum & 0xff - - // check neighbors - for _, neighbor := range iface.neighbors { - if neighbor == destAddr { - // send to neighbor - // SendIp(destAddr, protocolNum, data) - } - } +// 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) + }() +} - // check forwarding table +func InterfaceDown(iface *Interface) { + iface.SocketChannel <- true + iface.State = false +} - } +/* +func ListerToInterfaces() { + for _, iface := range myInterfaces { + go RecvIp(iface) } } - func ValidateChecksum(b []byte, fromHeader uint16) uint16 { checksum := header.Checksum(b, fromHeader) @@ -323,25 +296,67 @@ func GetNeighbors() []netip.Addr { } */ -func PrintInterfaces() { +func GetInterfaceByName(ifaceName string) (*Interface, error) { for _, iface := range myInterfaces { - fmt.Printf("%s\t%s\t%t\n", iface.Name, iface.IpPrefix.String(), iface.State) + if iface.Name == ifaceName { + return iface, nil + } } + + return nil, errors.Errorf("No interface with name %s", ifaceName) } -func PrintNeighbors() { +func SprintInterfaces() string { + buf := "" + for _, iface := range myInterfaces { + buf += fmt.Sprintf("%s\t%s\t%t\n", iface.Name, iface.IpPrefix.String(), iface.State) + } + return buf +} + +func SprintNeighbors() string { + buf := "" for ifaceName, neighbor := range myNeighbors { for _, n := range neighbor { - fmt.Printf("%s\t%s\t%s\n", ifaceName, n.UdpAddr.String(), n.VipAddr.String()) + buf += fmt.Sprintf("%s\t%s\t%s\n", ifaceName, n.UdpAddr.String(), n.VipAddr.String()) } } + return buf } func SprintRoutingTable() string { - message := "" + buf := "" for prefix, hop := range routingTable { - message += fmt.Sprintf("%s\t%s\t%d\n", prefix.String(), hop.VipAsStr, hop.Cost) + buf += fmt.Sprintf("%s\t%s\t%d\n", prefix.String(), hop.VipAsStr, hop.Cost) + } + return buf +} + +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) + } + } +} + +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) + } + iface.RecvSocket.Close() } - return message + // 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) + } + n.SendSocket.Close() + } + } } diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go index 5530b9d..d5b755a 100644 --- a/pkg/ipstack/ipstack_test.go +++ b/pkg/ipstack/ipstack_test.go @@ -3,6 +3,7 @@ package ipstack import ( "fmt" "testing" + "time" ) func TestInitialize(t *testing.T) { @@ -11,10 +12,44 @@ func TestInitialize(t *testing.T) { 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") - PrintInterfaces() - fmt.Println("Interfaces^^") - PrintNeighbors() - fmt.Println("Neighbors^^") - fmt.Println(SprintRoutingTable()) + 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() }) } -- cgit v1.2.3-70-g09d2 From a00f93e745b588d8bb55a7af7b8b94a2ff5adca0 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 12:47:09 -0400 Subject: simply system of threads that abstracts the Link layer --- pkg/ipstack/ipstack.go | 86 +++++++++++++++++++++++++++++---------------- 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() }) +} -- cgit v1.2.3-70-g09d2 From 365548a29f23f15632305d65070236cfd270589c Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 12:53:12 -0400 Subject: fix small bug --- pkg/ipstack/ipstack.go | 7 ++++--- pkg/ipstack/ipstack_test.go | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 7b67d08..c993a7d 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -132,7 +132,7 @@ func Initialize(lnxFilePath string) error { if err != nil { return errors.WithMessage(err, "Error creating UDP socket for neighbor->\t"+neighbor.DestAddr.String()) } - go InterfaceListenerRoutine(n.SendSocket, n.SocketChannel) + // go InterfaceListenerRoutine(n.SendSocket, n.SocketChannel) myNeighbors[neighbor.InterfaceName] = append(myNeighbors[neighbor.InterfaceName], n) @@ -180,8 +180,8 @@ func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { for { select { - case open, sig := <-signal: - if !open { + case sig, ok := <-signal: + if !ok { fmt.Println("channel closed, exiting") closed = true return @@ -189,6 +189,7 @@ func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { fmt.Println("received isUP SIGNAL with value", sig) isUp = sig default: + continue } } } diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go index 97c4947..ddb5453 100644 --- a/pkg/ipstack/ipstack_test.go +++ b/pkg/ipstack/ipstack_test.go @@ -75,17 +75,22 @@ func TestInterfaceUpThenDownTwice(t *testing.T) { 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") + 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") -- cgit v1.2.3-70-g09d2 From f81bfd8b8e1c663d40bcc0f2ce9fb114a204f8d7 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 12:54:59 -0400 Subject: small changes --- pkg/ipstack/ipstack.go | 4 +++- pkg/ipstack/ipstack_test.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index c993a7d..446bbff 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -373,7 +373,9 @@ func CleanUp() { fmt.Print("Cleaning up...\n") // go through the interfaces, pop thread & close the UDP FDs for _, iface := range myInterfaces { - close(iface.SocketChannel) + if iface.SocketChannel != nil { + close(iface.SocketChannel) + } iface.RecvSocket.Close() } diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go index ddb5453..ae71bba 100644 --- a/pkg/ipstack/ipstack_test.go +++ b/pkg/ipstack/ipstack_test.go @@ -82,8 +82,8 @@ func TestInterfaceUpThenDownTwice(t *testing.T) { } time.Sleep(3 * time.Millisecond) - fmt.Println("putting interface back up") + fmt.Println("putting interface back up for 3 iterations") InterfaceUp(iface) if iface.State == false { t.Error("iface state should be true") -- cgit v1.2.3-70-g09d2 From 1253398ac33d51559573972149384d1019c3ec56 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 12:59:13 -0400 Subject: fix tests --- pkg/ipstack/ipstack.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 446bbff..5546e32 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -388,4 +388,13 @@ func CleanUp() { n.SendSocket.Close() } } + + // delete all the neighbors + myNeighbors = make(map[string][]*Neighbor) + // delete tall the interfaces + myInterfaces = nil + // delete the routing table + routingTable = make(map[netip.Prefix]Hop) + + time.Sleep(5 * time.Millisecond) } -- cgit v1.2.3-70-g09d2 From 3932d9b5135c0f1831ed2cb714083559a251ff20 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 18:05:30 -0400 Subject: implement recvip and sendip, with tests to know it's working --- pkg/ipstack/ipstack.go | 270 +++++++++++++++++++++++--------------------- pkg/ipstack/ipstack_test.go | 148 ++++++++++++++++++++++++ 2 files changed, 290 insertions(+), 128 deletions(-) diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 5546e32..2063c67 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -2,8 +2,11 @@ package ipstack import ( "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" "time" @@ -17,9 +20,9 @@ const ( // STRUCTS --------------------------------------------------------------------- type Interface struct { - Name string - IpPrefix netip.Prefix - + Name string + IpPrefix netip.Prefix + UdpAddr netip.AddrPort RecvSocket net.UDPConn SocketChannel chan bool State bool @@ -51,6 +54,7 @@ type Hop struct { } // GLOBAL VARIABLES (data structures) ------------------------------------------ +var myVIP netip.Addr var myInterfaces []*Interface var myNeighbors = make(map[string][]*Neighbor) @@ -98,6 +102,7 @@ func Initialize(lnxFilePath string) error { i := &Interface{ Name: iface.Name, IpPrefix: prefix, + UdpAddr: iface.UDPAddr, RecvSocket: net.UDPConn{}, SocketChannel: make(chan bool), State: false, @@ -137,6 +142,7 @@ func Initialize(lnxFilePath string) error { 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} } @@ -171,10 +177,13 @@ func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { 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 + // TODO: remove these "training wheels" time.Sleep(1 * time.Millisecond) + err := RecvIP(socket, &isUp) + if err != nil { + fmt.Println("Error receiving IP packet", err) + return + } } }() @@ -205,126 +214,6 @@ func InterfaceDown(iface *Interface) { iface.State = false } -/* - -func ListerToInterfaces() { - for _, iface := range myInterfaces { - go RecvIp(iface) - } -} -func ValidateChecksum(b []byte, fromHeader uint16) uint16 { - checksum := header.Checksum(b, fromHeader) - - return checksum -} - -func SendIp(dst netip.Addr, port uint16, protocolNum uint16, data []byte, iface Interface) error { - bindLocalAddr, err := net.ResolveUDPAddr("udp4", iface.UDPAddr.String()) - if err != nil { - log.Panicln("Error resolving address: ", err) - } - - addrString := fmt.Sprintf("%s:%s", dst, port) - remoteAddr, err := net.ResolveUDPAddr("udp4", addrString) - if err != nil { - log.Panicln("Error resolving address: ", err) - } - - fmt.Printf("Sending to %s:%d\n", - remoteAddr.IP.String(), remoteAddr.Port) - - // Bind on the local UDP port: this sets the source port - // and creates a conn - conn, err := net.ListenUDP("udp4", bindLocalAddr) - if err != nil { - log.Panicln("Dial: ", err) - } - - // Start filling in the header - message := data[20:] - hdr := ipv4header.IPv4Header{ - Version: data[0] >> 4, - Len: 20, // Header length is always 20 when no IP options - TOS: data[1], - TotalLen: ipv4header.HeaderLen + len(message), - ID: data[4], - Flags: data[6] >> 5, - FragOff: data[6] & 0x1f, - TTL: data[8], - Protocol: data[9], - Checksum: 0, // Should be 0 until checksum is computed - Src: netip.MustParseAddr(iface.addr.String()), - Dst: netip.MustParseAddr(dst.String()), - Options: []byte{}, - } - - // Assemble the header into a byte array - headerBytes, err := hdr.Marshal() - if err != nil { - log.Fatalln("Error marshalling header: ", 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) - } - - bytesToSend := make([]byte, 0, len(headerBytes)+len(message)) - bytesToSend = append(bytesToSend, headerBytes...) - bytesToSend = append(bytesToSend, []byte(message)...) - - // Send the message to the "link-layer" addr:port on UDP - bytesWritten, err := conn.WriteToUDP(bytesToSend, remoteAddr) - if err != nil { - log.Panicln("Error writing to socket: ", err) - } - fmt.Printf("Sent %d bytes\n", bytesWritten) -} - -func ComputeChecksum(b []byte) uint16 { - checksum := header.Checksum(b, 0) - checksumInv := checksum ^ 0xffff - - return checksumInv -} - -func ForwardIP(data []byte) error { -} - -func AddRecvHandler(protocolNum uint8, callbackFunc HandlerFunc) error { - if protocolHandlers[protocolNum] != nil { - fmt.Printf("Warning: Handler for protocol %d already exists", protocolNum) - } - protocolHandlers[protocolNum] = callbackFunc - return nil -} - -func RemoveRecvHandler(protocolNum uint8) error { - // consider error - if protocolHandlers[protocolNum] == nil { - return errors.Errorf("No handler for protocol %d", protocolNum) - } - delete(protocolHandlers, protocolNum) - return nil -} - -// func routeRip(data []byte) (error) { -// // deconstruct packet -// newRIPMessage := RIPMessage{} -// newRIPMessage.command = data[0] -// newRIPMessage.numEntries = data[1] -// newRIPMessage.entries = make([]RIPEntry, newRIPMessage.numEntries) -// } - -func GetNeighbors() []netip.Addr { - return myNeighbors -} -*/ - func GetInterfaceByName(ifaceName string) (*Interface, error) { for _, iface := range myInterfaces { if iface.Name == ifaceName { @@ -335,6 +224,14 @@ func GetInterfaceByName(ifaceName string) (*Interface, error) { return nil, errors.Errorf("No interface with name %s", ifaceName) } +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 SprintInterfaces() string { buf := "" for _, iface := range myInterfaces { @@ -376,7 +273,10 @@ func CleanUp() { if iface.SocketChannel != nil { close(iface.SocketChannel) } - iface.RecvSocket.Close() + err := iface.RecvSocket.Close() + if err != nil { + continue + } } // go through the neighbors, pop thread & close the UDP FDs @@ -385,7 +285,10 @@ func CleanUp() { if n.SocketChannel != nil { close(n.SocketChannel) } - n.SendSocket.Close() + err := n.SendSocket.Close() + if err != nil { + continue + } } } @@ -398,3 +301,114 @@ func CleanUp() { time.Sleep(5 * time.Millisecond) } + +func SendIP(src Interface, dest Neighbor, protocolNum int, message []byte) error { + 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.IpPrefix.Addr(), + Dst: dest.VipAddr, + Options: []byte{}, + } + + // Assemble the header into a byte array + headerBytes, err := hdr.Marshal() + if err != nil { + return 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) + } + + bytesToSend := make([]byte, 0, len(headerBytes)+len(message)) + bytesToSend = append(bytesToSend, headerBytes...) + bytesToSend = append(bytesToSend, []byte(message)...) + + // Send the message to the "link-layer" addr:port on UDP + listenAddr, err := net.ResolveUDPAddr("udp4", dest.UdpAddr.String()) + if err != nil { + return err + } + bytesWritten, err := dest.SendSocket.WriteToUDP(bytesToSend, listenAddr) + if err != nil { + return err + } + fmt.Printf("Sent %d bytes to %s\n", bytesWritten, listenAddr.String()) + + return nil +} + +func RecvIP(conn net.UDPConn, isOpen *bool) error { + buffer := make([]byte, MAX_IP_PACKET_SIZE) // TODO: fix wordking + + // Read on the UDP port + fmt.Println("wating to read from UDP socket") + _, sourceAddr, err := conn.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 + message := buffer[headerSize:] + + // Finally, print everything out + fmt.Printf("Received IP packet from %s\nHeader: %v\nChecksum: %s\nMessage: %s\n", + sourceAddr.String(), hdr, checksumState, string(message)) + + return nil +} + +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 +} diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go index ae71bba..c6103ee 100644 --- a/pkg/ipstack/ipstack_test.go +++ b/pkg/ipstack/ipstack_test.go @@ -2,6 +2,9 @@ package ipstack import ( "fmt" + ipv4header "github.com/brown-csci1680/iptcp-headers" + "net" + "net/netip" "testing" "time" ) @@ -103,3 +106,148 @@ func TestInterfaceUpThenDownTwice(t *testing.T) { 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("hello")) + 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() }) +} -- cgit v1.2.3-70-g09d2 From 1fbd2a1dbb09b75afbb1435be46670ee61942bcf Mon Sep 17 00:00:00 2001 From: sotech117 Date: Mon, 9 Oct 2023 18:07:54 -0400 Subject: small changes --- pkg/ipstack/ipstack.go | 2 +- pkg/ipstack/ipstack_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 2063c67..91cc6f9 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -294,7 +294,7 @@ func CleanUp() { // delete all the neighbors myNeighbors = make(map[string][]*Neighbor) - // delete tall the interfaces + // delete all the interfaces myInterfaces = nil // delete the routing table routingTable = make(map[netip.Prefix]Hop) diff --git a/pkg/ipstack/ipstack_test.go b/pkg/ipstack/ipstack_test.go index c6103ee..941c4e9 100644 --- a/pkg/ipstack/ipstack_test.go +++ b/pkg/ipstack/ipstack_test.go @@ -185,7 +185,7 @@ func TestSendIPToNeighbor(t *testing.T) { // send a message to the neighbor fmt.Printf("sending message to neighbor\t%t\n", sent) - err = SendIP(*iface, *testNeighbor, 0, []byte("hello")) + err = SendIP(*iface, *testNeighbor, 0, []byte("You are my firest neighbor!")) if err != nil { t.Error(err) } -- cgit v1.2.3-70-g09d2 From ce42396f99e1d99d7e3b3016acabd9380627a297 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Tue, 10 Oct 2023 00:43:27 -0400 Subject: add TODOS --- pkg/ipstack/ipstack.go | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/pkg/ipstack/ipstack.go b/pkg/ipstack/ipstack.go index 91cc6f9..be8bc1e 100644 --- a/pkg/ipstack/ipstack.go +++ b/pkg/ipstack/ipstack.go @@ -20,9 +20,10 @@ const ( // STRUCTS --------------------------------------------------------------------- type Interface struct { - Name string - IpPrefix netip.Prefix - UdpAddr netip.AddrPort + Name string + IpPrefix netip.Prefix + UdpAddr netip.AddrPort + RecvSocket net.UDPConn SocketChannel chan bool State bool @@ -137,7 +138,6 @@ func Initialize(lnxFilePath string) error { 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) @@ -150,15 +150,6 @@ func Initialize(lnxFilePath string) error { return nil } -//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 @@ -203,7 +194,6 @@ func InterfaceListenerRoutine(socket net.UDPConn, signal <-chan bool) { } } -// When an interface goes up, we need to start it's go routine that listens func InterfaceUp(iface *Interface) { iface.State = true iface.SocketChannel <- true @@ -302,6 +292,7 @@ func CleanUp() { time.Sleep(5 * time.Millisecond) } +// TODO: have it take TTL so we can decrement it when forwarding func SendIP(src Interface, dest Neighbor, protocolNum int, message []byte) error { hdr := ipv4header.IPv4Header{ Version: 4, @@ -397,6 +388,14 @@ func RecvIP(conn net.UDPConn, isOpen *bool) error { fmt.Printf("Received IP packet from %s\nHeader: %v\nChecksum: %s\nMessage: %s\n", sourceAddr.String(), hdr, checksumState, string(message)) + // TODO: handle the message + // 1) check if the TTL & checksum is valid + // 2) check if the message is for me, if so, sendUP (aka call the correct handler) + // if not, need to forward the packer to a neighbor or check the table + // after decrementing TTL and updating checksum + // 3) check if message is for a neighbor, if so, sendIP there + // 4) check if message is for a neighbor, if so, forward to the neighbor with that VIP + return nil } -- cgit v1.2.3-70-g09d2