Skip to content

Commit

Permalink
v6 firewall stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
JackDoanRivian committed Sep 20, 2024
1 parent 215a586 commit 8bf2f2d
Showing 1 changed file with 100 additions and 10 deletions.
110 changes: 100 additions & 10 deletions outside.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket/layers"
"golang.org/x/net/ipv6"
"net/netip"
"time"

Expand Down Expand Up @@ -297,22 +299,112 @@ func (f *Interface) handleEncrypted(ci *ConnectionState, addr netip.AddrPort, h

// newPacket validates and parses the interesting bits for the firewall out of the ip and sub protocol headers
func newPacket(data []byte, incoming bool, fp *firewall.Packet) error {
// Do we at least have an ipv4 header worth of data?
if len(data) < ipv4.HeaderLen {
return fmt.Errorf("packet is less than %v bytes", ipv4.HeaderLen)
if len(data) < 1 {
return errors.New("packet too short")
}

version := int((data[0] >> 4) & 0x0f)
switch version {
case ipv4.Version:
return parseV4(data, incoming, fp)
case ipv6.Version:
return parseV6(data, incoming, fp)
}
return fmt.Errorf("packet is an unknown ip version: %v", version)
}

func parseV6(data []byte, incoming bool, fp *firewall.Packet) error {
dataLen := len(data)
if dataLen < ipv6.HeaderLen {
return fmt.Errorf("ipv6 packet is less than %v bytes", ipv4.HeaderLen)
}

if incoming {
fp.RemoteIP, _ = netip.AddrFromSlice(data[8:24])
fp.LocalIP, _ = netip.AddrFromSlice(data[24:40])
} else {
fp.LocalIP, _ = netip.AddrFromSlice(data[8:24])
fp.RemoteIP, _ = netip.AddrFromSlice(data[24:40])
}

//TODO: whats a reasonable number of extension headers to attempt to parse?
//https://www.ietf.org/archive/id/draft-ietf-6man-eh-limits-00.html
protoAt := 6
offset := 40
for i := 0; i < 24; i++ {
if dataLen < offset {
break
}

proto := layers.IPProtocol(data[protoAt])
//fmt.Println(proto, protoAt)
switch proto {
case layers.IPProtocolICMPv6:
//TODO: we need a new protocol in config language "icmpv6"
fp.Protocol = uint8(proto)
fp.RemotePort = 0
fp.LocalPort = 0
fp.Fragment = false
return nil

case layers.IPProtocolTCP:
if dataLen < offset+4 {
return fmt.Errorf("ipv6 packet was too small")
}
fp.Protocol = uint8(proto)
fp.RemotePort = binary.BigEndian.Uint16(data[offset : offset+2])
fp.LocalPort = binary.BigEndian.Uint16(data[offset+2 : offset+4])
fp.Fragment = false
return nil

case layers.IPProtocolUDP:
if dataLen < offset+4 {
return fmt.Errorf("ipv6 packet was too small")
}
fp.Protocol = uint8(proto)
fp.RemotePort = binary.BigEndian.Uint16(data[offset : offset+2])
fp.LocalPort = binary.BigEndian.Uint16(data[offset+2 : offset+4])
fp.Fragment = false
return nil

case layers.IPProtocolIPv6Fragment:
//TODO: can we determine the protocol?
fp.RemotePort = 0
fp.LocalPort = 0
fp.Fragment = true
return nil

default:
if dataLen < offset+1 {
break
}

next := int(data[offset+1]) * 8
if next == 0 {
// each extension is at least 8 bytes
next = 8
}

protoAt = offset
offset = offset + next
}
}

// Is it an ipv4 packet?
if int((data[0]>>4)&0x0f) != 4 {
return fmt.Errorf("packet is not ipv4, type: %v", int((data[0]>>4)&0x0f))
return fmt.Errorf("could not find payload in ipv6 packet")
}

func parseV4(data []byte, incoming bool, fp *firewall.Packet) error {
// Do we at least have an ipv4 header worth of data?
if len(data) < ipv4.HeaderLen {
return fmt.Errorf("ipv4 packet is less than %v bytes", ipv4.HeaderLen)
}

// Adjust our start position based on the advertised ip header length
ihl := int(data[0]&0x0f) << 2

// Well formed ip header length?
if ihl < ipv4.HeaderLen {
return fmt.Errorf("packet had an invalid header length: %v", ihl)
return fmt.Errorf("ipv4 packet had an invalid header length: %v", ihl)
}

// Check if this is the second or further fragment of a fragmented packet.
Expand All @@ -328,12 +420,11 @@ func newPacket(data []byte, incoming bool, fp *firewall.Packet) error {
minLen += minFwPacketLen
}
if len(data) < minLen {
return fmt.Errorf("packet is less than %v bytes, ip header len: %v", minLen, ihl)
return fmt.Errorf("ipv4 packet is less than %v bytes, ip header len: %v", minLen, ihl)
}

// Firewall packets are locally oriented
if incoming {
//TODO: IPV6-WORK
fp.RemoteIP, _ = netip.AddrFromSlice(data[12:16])
fp.LocalIP, _ = netip.AddrFromSlice(data[16:20])
if fp.Fragment || fp.Protocol == firewall.ProtoICMP {
Expand All @@ -344,7 +435,6 @@ func newPacket(data []byte, incoming bool, fp *firewall.Packet) error {
fp.LocalPort = binary.BigEndian.Uint16(data[ihl+2 : ihl+4])
}
} else {
//TODO: IPV6-WORK
fp.LocalIP, _ = netip.AddrFromSlice(data[12:16])
fp.RemoteIP, _ = netip.AddrFromSlice(data[16:20])
if fp.Fragment || fp.Protocol == firewall.ProtoICMP {
Expand Down

0 comments on commit 8bf2f2d

Please sign in to comment.