mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
Fix IPv6 connection handling and refactor packet parsing
This commit is contained in:
parent
af8c974a85
commit
9639775ad7
5 changed files with 159 additions and 88 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue