aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Doan <daviddoan@Davids-MacBook-Pro-70.local>2023-10-05 23:32:06 -0400
committerDavid Doan <daviddoan@Davids-MacBook-Pro-70.local>2023-10-05 23:32:06 -0400
commit770a08b747233942fb19da529763f61987a804d6 (patch)
tree3c822fde32750780fe38f46b6f25e9642c1cec0f
parent7bb2015a1f2de7a31c3f2b3092a574602a2a3d53 (diff)
file importing
-rw-r--r--.vscode/settings.json5
-rw-r--r--cmd/pkg/protocol.go66
-rw-r--r--cmd/vrouter/main.go28
-rw-r--r--go.mod4
-rw-r--r--go.sum2
-rw-r--r--pkg/lnxconfig.go365
-rw-r--r--pkg/protocol.go183
-rw-r--r--pkg/routingTable.go71
8 files changed, 657 insertions, 67 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..0d84b99
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "cSpell.words": [
+ "lnxconfig"
+ ]
+} \ No newline at end of file
diff --git a/cmd/pkg/protocol.go b/cmd/pkg/protocol.go
deleted file mode 100644
index 84444c3..0000000
--- a/cmd/pkg/protocol.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package pkg
-
-import (
- "net"
- // "netip"
-)
-
-const (
- MAX_IP_PACKET_SIZE = 1400
-)
-
-func Initialize(config IpConfig) (error) {
- // ip config from go parser
-
- // initialize ip table
-
- // error check
-
- // different for router and host??
- // host
- // create node interfaces?
-}
-
-func ipRecv(data []byte) (error) {
- // parse ip header
-
- // check ip checksum
-
- // check ip version
-
- // check ip length
-
- // check ip ttl
-
- // check ip protocol
-
- // check ip destination
-
- // check ip source
-
- // check forwarding table
-}
-
-func ipForwarding(dst netip.Addr, protocolNum uint16, data []byte) (error) {
- // send test packest to dst
-
- // lookup forwarding table
-
- // locally
-
- // not locally
-}
-
-type HandlerFunc = func(*Packet, []interface{}) (error) {
-
- // do smth with packet
-}
-
-func RegisterRecvHandler(protocolNum uint8, callbackFunc HandlerFunc) (error) {
-}
-
-func routeRip() (error) {
- // communicate with other routers
-
- // update forwarding table
-}
diff --git a/cmd/vrouter/main.go b/cmd/vrouter/main.go
index 82b8195..158016a 100644
--- a/cmd/vrouter/main.go
+++ b/cmd/vrouter/main.go
@@ -4,9 +4,37 @@ import (
"bufio"
"fmt"
"os"
+ "time"
)
+func SendUpdates() {
+ for _, iface := range myInterfaces {
+ // send RIP updates to all neighbors
+ for _, neighbor := range myNeighbors {
+ iface.udp.Write(neighbor, data)
+ // wait for response for 12 seconds
+ response := make([]byte, 512)
+ iface.udp.Read(response)
+ time.Sleep(12 * time.Second)
+ if len(response) == 0 {
+ RemoveNeighbor(neighbor)
+ }
+ }
+ }
+ time.Sleep(5 * time.Second)
+}
+
func main() {
+ if len(os.Args) != 2 {
+ fmt.Printf("Usage: %s <configFile>\n", os.Args[0])
+ os.Exit(1)
+ }
+
+ fileName := os.Args[1]
+
+ initialize(fileName)
+ go SendUpdates()
+
scanner := bufio.NewScanner(os.Stdin)
diff --git a/go.mod b/go.mod
index 032a459..73b8244 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,5 @@
module golang-sockets
-go 1.18 \ No newline at end of file
+go 1.20
+
+require github.com/pkg/errors v0.9.1 // indirect \ No newline at end of file
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..7c401c3
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
diff --git a/pkg/lnxconfig.go b/pkg/lnxconfig.go
new file mode 100644
index 0000000..36b1b56
--- /dev/null
+++ b/pkg/lnxconfig.go
@@ -0,0 +1,365 @@
+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 <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 <neighbor IP>")
+ }
+ 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 <type>")
+ }
+ 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 <prefix> via <addr>"
+ 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 <name> <prefix> <bindAddr>"
+
+ 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 <vip> at <bindAddr> via <interface name>"
+
+ 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/protocol.go b/pkg/protocol.go
new file mode 100644
index 0000000..10cf058
--- /dev/null
+++ b/pkg/protocol.go
@@ -0,0 +1,183 @@
+package protocol
+
+import (
+ "net"
+ "net/netip"
+ "fmt"
+ "os"
+ "bufio"
+ "time"
+ "github.com/brown-cs1680-f23/iptcp-jailpt2/pkg/lnxconfig"
+)
+
+const (
+ MAX_IP_PACKET_SIZE = 1400
+)
+
+type Interface struct {
+ Name string
+ AssignedIP netip.Addr
+ AssignedPrefix netip.Prefix
+
+ UDPAddr netip.AddrPort
+ State uint8_t
+}
+
+
+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 <configFile>\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 RecvIp(data []byte) (error) {
+ // deconstruct packet
+
+ // check ip checksum
+ checksum := netip.Checksum(data)
+ if checksum != 0 {
+ return errors.Errorf("Checksum failed: %d", checksum)
+ }
+
+ // check ttl
+ ttl := data[8]
+ if ttl == 0 {
+ return errors.Errorf("TTL is 0")
+ }
+
+ destAddr := netip.AddrFrom(data[16:20])
+ protocolNum := data[9]
+
+ // decrement ttl and update checksum
+ data[8] = ttl - 1
+ data[10] = 0
+ data[11] = 0
+ newChecksum := netip.Checksum(data)
+ data[10] = newChecksum >> 8
+ data[11] = newChecksum & 0xff
+
+ SendIp(destAddr, protocolNum, data)
+}
+
+func SendIp(dst netip.Addr, protocolNum uint16, data []byte) (error) {
+ // check if dst is local
+ islocal := false
+ for _, iface := range myNeighbors {
+ if iface.addr == dst {
+ islocal = true
+ break
+ }
+ }
+
+ if islocal {
+ // send to local handler
+ protocolHandlers[protocolNum](data)
+ } else {
+ // send to forwarding table
+ // lookup forwarding table
+ // toHop, err := lookupForwardingTable(dst)
+ // if err != nil {
+ // routeRip()
+ // }
+
+ // // send to toHop interface
+ // for _, iface := range myInterfaces {
+ // if iface.addr == toHop {
+ // iface.udp.Write(data)
+ // break
+ // }
+ // }
+}
+
+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
new file mode 100644
index 0000000..bda4524
--- /dev/null
+++ b/pkg/routingTable.go
@@ -0,0 +1,71 @@
+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 <configFile>\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