aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pkg/ipstack/ipstack.go270
-rw-r--r--pkg/ipstack/ipstack_test.go148
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() })
+}