From 9639775ad7e52d60ace50e1a6ab60b37d0f190f4 Mon Sep 17 00:00:00 2001 From: Safing Date: Tue, 28 Jul 2020 14:06:05 +0200 Subject: [PATCH] Fix IPv6 connection handling and refactor packet parsing --- firewall/interception/nfqexp/nfqexp.go | 2 +- firewall/interception/nfqueue/nfqueue.go | 22 +-- network/packet/const.go | 15 +- network/packet/packet.go | 2 +- network/packet/parse.go | 206 ++++++++++++++++------- 5 files changed, 159 insertions(+), 88 deletions(-) diff --git a/firewall/interception/nfqexp/nfqexp.go b/firewall/interception/nfqexp/nfqexp.go index 89eced06..45c358e6 100644 --- a/firewall/interception/nfqexp/nfqexp.go +++ b/firewall/interception/nfqexp/nfqexp.go @@ -69,7 +69,7 @@ func New(qid uint16, v6 bool) (*Queue, error) { 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) _ = pkt.Drop() return 0 diff --git a/firewall/interception/nfqueue/nfqueue.go b/firewall/interception/nfqueue/nfqueue.go index 41c05346..545bcf2f 100644 --- a/firewall/interception/nfqueue/nfqueue.go +++ b/firewall/interception/nfqueue/nfqueue.go @@ -12,7 +12,6 @@ import "C" import ( "errors" "fmt" - "net" "os" "runtime" "sync" @@ -20,6 +19,7 @@ import ( "time" "unsafe" + "github.com/safing/portbase/log" "github.com/safing/portmaster/network/packet" ) @@ -155,8 +155,6 @@ func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32, qid := *qidptr // nfq := (*NFQueue)(nfqptr) - ipVersion := packet.IPVersion(version) - ipsz := C.int(ipVersion.ByteSize()) bs := C.GoBytes(payload, (C.int)(payloadLen)) verdict := make(chan uint32, 1) @@ -173,19 +171,11 @@ func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32, // Payload pkt.Payload = bs - // Info - info := pkt.Info() - info.Version = ipVersion - info.InTunnel = false - 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 + if err := packet.Parse(bs, pkt.Info()); err != nil { + log.Warningf("nfqueue: failed to parse packet: %s; dropping", err) + *mark = 1702 + return queues[qid].DefaultVerdict + } // fmt.Printf("%s queuing packet\n", time.Now().Format("060102 15:04:05.000")) // BUG: "panic: send on closed channel" when shutting down diff --git a/network/packet/const.go b/network/packet/const.go index 6107d2e7..374ef57a 100644 --- a/network/packet/const.go +++ b/network/packet/const.go @@ -23,12 +23,13 @@ const ( InBound = true OutBound = false - ICMP = IPProtocol(1) - IGMP = IPProtocol(2) - TCP = IPProtocol(6) - UDP = IPProtocol(17) - ICMPv6 = IPProtocol(58) - RAW = IPProtocol(255) + ICMP = IPProtocol(1) + IGMP = IPProtocol(2) + TCP = IPProtocol(6) + UDP = IPProtocol(17) + ICMPv6 = IPProtocol(58) + UDPLite = IPProtocol(136) + RAW = IPProtocol(255) ) // Verdicts @@ -78,6 +79,8 @@ func (p IPProtocol) String() string { return "TCP" case UDP: return "UDP" + case UDPLite: + return "UDPLite" case ICMP: return "ICMP" case ICMPv6: diff --git a/network/packet/packet.go b/network/packet/packet.go index 8076ed69..315292ca 100644 --- a/network/packet/packet.go +++ b/network/packet/packet.go @@ -59,7 +59,7 @@ func (pkt *Base) HasPorts() bool { switch pkt.info.Protocol { case TCP: return true - case UDP: + case UDP, UDPLite: return true } return false diff --git a/network/packet/parse.go b/network/packet/parse.go index 28815620..05212977 100644 --- a/network/packet/parse.go +++ b/network/packet/parse.go @@ -6,86 +6,164 @@ import ( "github.com/google/gopacket" "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. -func Parse(packetData []byte, packet *Base) error { - - var parsedPacket gopacket.Packet - +func Parse(packetData []byte, pktInfo *Info) error { if len(packetData) == 0 { return errors.New("empty packet") } - switch packetData[0] >> 4 { + ipVersion := packetData[0] >> 4 + var networkLayerType gopacket.LayerType + + switch ipVersion { case 4: - parsedPacket = gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.DecodeOptions{Lazy: true, NoCopy: true}) - 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) - } + networkLayerType = layers.LayerTypeIPv4 case 6: - parsedPacket = gopacket.NewPacket(packetData, layers.LayerTypeIPv6, gopacket.DecodeOptions{Lazy: true, NoCopy: true}) - 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) - } + networkLayerType = layers.LayerTypeIPv6 default: - return errors.New("unknown IP version") + return fmt.Errorf("unknown IP version or network protocol: %02x", ipVersion) } - switch packet.info.Protocol { - case TCP: - if tcpLayer := parsedPacket.Layer(layers.LayerTypeTCP); tcpLayer != nil { - tcp, _ := tcpLayer.(*layers.TCP) - packet.info.SrcPort = uint16(tcp.SrcPort) - packet.info.DstPort = uint16(tcp.DstPort) - } else { - var err error - if errLayer := parsedPacket.ErrorLayer(); errLayer != nil { - err = errLayer.Error() - } - return fmt.Errorf("could not parse TCP layer: %s", err) - } - case UDP: - if udpLayer := parsedPacket.Layer(layers.LayerTypeUDP); udpLayer != nil { - udp, _ := udpLayer.(*layers.UDP) - packet.info.SrcPort = uint16(udp.SrcPort) - packet.info.DstPort = uint16(udp.DstPort) - } else { - var err error - if errLayer := parsedPacket.ErrorLayer(); errLayer != nil { - err = errLayer.Error() - } - return fmt.Errorf("could not parse UDP layer: %s", err) + // 255 is reserved by IANA so we use it as a "failed-to-detect" marker. + pktInfo.Protocol = 255 + + packet := gopacket.NewPacket(packetData, networkLayerType, gopacket.DecodeOptions{ + Lazy: true, + NoCopy: true, + }) + + availableDecoders := []func(gopacket.Packet, *Info) error{ + parseIPv4, + parseIPv6, + parseTCP, + parseUDP, + //parseUDPLite, // we don't yet support udplite + parseICMPv4, + parseICMPv6, + parseIGMP, + checkError, + } + + for _, dec := range availableDecoders { + if err := dec(packet, pktInfo); err != nil { + return err } } - if appLayer := parsedPacket.ApplicationLayer(); appLayer != nil { - packet.Payload = appLayer.Payload() - } - - if errLayer := parsedPacket.ErrorLayer(); errLayer != nil { - return errLayer.Error() + // 255 is reserved by IANA and used as a "failed-to-detect" + // marker for IPv6 (parseIPv4 always sets the protocl field) + if pktInfo.Protocol == 255 { + tryFindIPv6TransportProtocol(packet, pktInfo) } return nil } + +func init() { + genIPProtocolFromLayerType() +}