Fix IPv6 connection handling and refactor packet parsing

This commit is contained in:
Safing 2020-07-28 14:06:05 +02:00 committed by Patrick Pacher
parent af8c974a85
commit 9639775ad7
No known key found for this signature in database
GPG key ID: E8CD2DA160925A6D
5 changed files with 159 additions and 88 deletions

View file

@ -69,7 +69,7 @@ func New(qid uint16, v6 bool) (*Queue, error) {
pkt.Payload = *attrs.Payload pkt.Payload = *attrs.Payload
} }
if err := pmpacket.Parse(pkt.Payload, &pkt.Base); err != nil { if err := pmpacket.Parse(pkt.Payload, pkt.Info()); err != nil {
log.Warningf("nfqexp: failed to parse payload: %s", err) log.Warningf("nfqexp: failed to parse payload: %s", err)
_ = pkt.Drop() _ = pkt.Drop()
return 0 return 0

View file

@ -12,7 +12,6 @@ import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
"net"
"os" "os"
"runtime" "runtime"
"sync" "sync"
@ -20,6 +19,7 @@ import (
"time" "time"
"unsafe" "unsafe"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/network/packet" "github.com/safing/portmaster/network/packet"
) )
@ -155,8 +155,6 @@ func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32,
qid := *qidptr qid := *qidptr
// nfq := (*NFQueue)(nfqptr) // nfq := (*NFQueue)(nfqptr)
ipVersion := packet.IPVersion(version)
ipsz := C.int(ipVersion.ByteSize())
bs := C.GoBytes(payload, (C.int)(payloadLen)) bs := C.GoBytes(payload, (C.int)(payloadLen))
verdict := make(chan uint32, 1) verdict := make(chan uint32, 1)
@ -173,19 +171,11 @@ func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32,
// Payload // Payload
pkt.Payload = bs pkt.Payload = bs
// Info if err := packet.Parse(bs, pkt.Info()); err != nil {
info := pkt.Info() log.Warningf("nfqueue: failed to parse packet: %s; dropping", err)
info.Version = ipVersion *mark = 1702
info.InTunnel = false return queues[qid].DefaultVerdict
info.Protocol = packet.IPProtocol(protocol) }
// IPs
info.Src = net.IP(C.GoBytes(saddr, ipsz))
info.Dst = net.IP(C.GoBytes(daddr, ipsz))
// Ports
info.SrcPort = sport
info.DstPort = dport
// fmt.Printf("%s queuing packet\n", time.Now().Format("060102 15:04:05.000")) // fmt.Printf("%s queuing packet\n", time.Now().Format("060102 15:04:05.000"))
// BUG: "panic: send on closed channel" when shutting down // BUG: "panic: send on closed channel" when shutting down

View file

@ -23,12 +23,13 @@ const (
InBound = true InBound = true
OutBound = false OutBound = false
ICMP = IPProtocol(1) ICMP = IPProtocol(1)
IGMP = IPProtocol(2) IGMP = IPProtocol(2)
TCP = IPProtocol(6) TCP = IPProtocol(6)
UDP = IPProtocol(17) UDP = IPProtocol(17)
ICMPv6 = IPProtocol(58) ICMPv6 = IPProtocol(58)
RAW = IPProtocol(255) UDPLite = IPProtocol(136)
RAW = IPProtocol(255)
) )
// Verdicts // Verdicts
@ -78,6 +79,8 @@ func (p IPProtocol) String() string {
return "TCP" return "TCP"
case UDP: case UDP:
return "UDP" return "UDP"
case UDPLite:
return "UDPLite"
case ICMP: case ICMP:
return "ICMP" return "ICMP"
case ICMPv6: case ICMPv6:

View file

@ -59,7 +59,7 @@ func (pkt *Base) HasPorts() bool {
switch pkt.info.Protocol { switch pkt.info.Protocol {
case TCP: case TCP:
return true return true
case UDP: case UDP, UDPLite:
return true return true
} }
return false return false

View file

@ -6,86 +6,164 @@ import (
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/safing/portbase/log"
) )
var layerType2IPProtocol map[gopacket.LayerType]IPProtocol
func genIPProtocolFromLayerType() {
layerType2IPProtocol = make(map[gopacket.LayerType]IPProtocol)
for k, v := range layers.IPProtocolMetadata {
layerType2IPProtocol[v.LayerType] = IPProtocol(k)
}
}
func parseIPv4(packet gopacket.Packet, info *Info) error {
if ipv4, ok := packet.NetworkLayer().(*layers.IPv4); ok {
info.Version = IPv4
info.Src = ipv4.SrcIP
info.Dst = ipv4.DstIP
info.Protocol = IPProtocol(ipv4.Protocol)
}
return nil
}
func parseIPv6(packet gopacket.Packet, info *Info) error {
if ipv6, ok := packet.NetworkLayer().(*layers.IPv6); ok {
info.Version = IPv6
info.Src = ipv6.SrcIP
info.Dst = ipv6.DstIP
}
return nil
}
func parseTCP(packet gopacket.Packet, info *Info) error {
if tcp, ok := packet.TransportLayer().(*layers.TCP); ok {
info.Protocol = TCP
info.SrcPort = uint16(tcp.SrcPort)
info.DstPort = uint16(tcp.DstPort)
}
return nil
}
func parseUDP(packet gopacket.Packet, info *Info) error {
if udp, ok := packet.TransportLayer().(*layers.UDP); ok {
info.Protocol = UDP
info.SrcPort = uint16(udp.SrcPort)
info.DstPort = uint16(udp.DstPort)
}
return nil
}
func parseUDPLite(packet gopacket.Packet, info *Info) error {
if udpLite, ok := packet.TransportLayer().(*layers.UDPLite); ok {
info.Protocol = UDPLite
info.SrcPort = uint16(udpLite.SrcPort)
info.DstPort = uint16(udpLite.DstPort)
}
return nil
}
func parseICMPv4(packet gopacket.Packet, info *Info) error {
if icmp, ok := packet.Layer(layers.LayerTypeICMPv4).(*layers.ICMPv4); ok {
info.Protocol = ICMP
_ = icmp
}
return nil
}
func parseICMPv6(packet gopacket.Packet, info *Info) error {
if icmp6, ok := packet.Layer(layers.LayerTypeICMPv6).(*layers.ICMPv6); ok {
info.Protocol = ICMPv6
_ = icmp6
}
return nil
}
func parseIGMP(packet gopacket.Packet, info *Info) error {
// gopacket uses LayerTypeIGMP for v1, v2 and v3 and may thus
// either return layers.IGMP or layers.IGMPv1or2
if layer := packet.Layer(layers.LayerTypeIGMP); layer != nil {
info.Protocol = IGMP
}
return nil
}
func checkError(packet gopacket.Packet, _ *Info) error {
if err := packet.ErrorLayer(); err != nil {
return err.Error()
}
return nil
}
func tryFindIPv6TransportProtocol(packet gopacket.Packet, info *Info) {
if transport := packet.TransportLayer(); transport != nil {
proto, ok := layerType2IPProtocol[transport.LayerType()]
if ok {
info.Protocol = proto
log.Tracef("packet: unsupported IPv6 protocol %02x (%d)", proto)
} else {
log.Warningf("packet: unsupported or unknown gopacket layer type: %d", transport.LayerType())
}
return
}
log.Tracef("packet: failed to get IPv6 transport protocol number")
}
// Parse parses an IP packet and saves the information in the given packet object. // Parse parses an IP packet and saves the information in the given packet object.
func Parse(packetData []byte, packet *Base) error { func Parse(packetData []byte, pktInfo *Info) error {
var parsedPacket gopacket.Packet
if len(packetData) == 0 { if len(packetData) == 0 {
return errors.New("empty packet") return errors.New("empty packet")
} }
switch packetData[0] >> 4 { ipVersion := packetData[0] >> 4
var networkLayerType gopacket.LayerType
switch ipVersion {
case 4: case 4:
parsedPacket = gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.DecodeOptions{Lazy: true, NoCopy: true}) networkLayerType = layers.LayerTypeIPv4
if ipv4Layer := parsedPacket.Layer(layers.LayerTypeIPv4); ipv4Layer != nil {
ipv4, _ := ipv4Layer.(*layers.IPv4)
packet.info.Version = IPv4
packet.info.Protocol = IPProtocol(ipv4.Protocol)
packet.info.Src = ipv4.SrcIP
packet.info.Dst = ipv4.DstIP
} else {
var err error
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil {
err = errLayer.Error()
}
return fmt.Errorf("failed to parse IPv4 packet: %s", err)
}
case 6: case 6:
parsedPacket = gopacket.NewPacket(packetData, layers.LayerTypeIPv6, gopacket.DecodeOptions{Lazy: true, NoCopy: true}) networkLayerType = layers.LayerTypeIPv6
if ipv6Layer := parsedPacket.Layer(layers.LayerTypeIPv6); ipv6Layer != nil {
ipv6, _ := ipv6Layer.(*layers.IPv6)
packet.info.Version = IPv6
packet.info.Protocol = IPProtocol(ipv6.NextHeader)
packet.info.Src = ipv6.SrcIP
packet.info.Dst = ipv6.DstIP
} else {
var err error
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil {
err = errLayer.Error()
}
return fmt.Errorf("failed to parse IPv6 packet: %s", err)
}
default: default:
return errors.New("unknown IP version") return fmt.Errorf("unknown IP version or network protocol: %02x", ipVersion)
} }
switch packet.info.Protocol { // 255 is reserved by IANA so we use it as a "failed-to-detect" marker.
case TCP: pktInfo.Protocol = 255
if tcpLayer := parsedPacket.Layer(layers.LayerTypeTCP); tcpLayer != nil {
tcp, _ := tcpLayer.(*layers.TCP) packet := gopacket.NewPacket(packetData, networkLayerType, gopacket.DecodeOptions{
packet.info.SrcPort = uint16(tcp.SrcPort) Lazy: true,
packet.info.DstPort = uint16(tcp.DstPort) NoCopy: true,
} else { })
var err error
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil { availableDecoders := []func(gopacket.Packet, *Info) error{
err = errLayer.Error() parseIPv4,
} parseIPv6,
return fmt.Errorf("could not parse TCP layer: %s", err) parseTCP,
} parseUDP,
case UDP: //parseUDPLite, // we don't yet support udplite
if udpLayer := parsedPacket.Layer(layers.LayerTypeUDP); udpLayer != nil { parseICMPv4,
udp, _ := udpLayer.(*layers.UDP) parseICMPv6,
packet.info.SrcPort = uint16(udp.SrcPort) parseIGMP,
packet.info.DstPort = uint16(udp.DstPort) checkError,
} else { }
var err error
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil { for _, dec := range availableDecoders {
err = errLayer.Error() if err := dec(packet, pktInfo); err != nil {
} return err
return fmt.Errorf("could not parse UDP layer: %s", err)
} }
} }
if appLayer := parsedPacket.ApplicationLayer(); appLayer != nil { // 255 is reserved by IANA and used as a "failed-to-detect"
packet.Payload = appLayer.Payload() // marker for IPv6 (parseIPv4 always sets the protocl field)
} if pktInfo.Protocol == 255 {
tryFindIPv6TransportProtocol(packet, pktInfo)
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil {
return errLayer.Error()
} }
return nil return nil
} }
func init() {
genIPProtocolFromLayerType()
}