mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
316 lines
6.4 KiB
Go
316 lines
6.4 KiB
Go
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
|
|
|
package packet
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
)
|
|
|
|
type (
|
|
IPVersion uint8
|
|
IPProtocol uint8
|
|
Verdict uint8
|
|
Endpoint bool
|
|
)
|
|
|
|
const (
|
|
IPv4 = IPVersion(4)
|
|
IPv6 = IPVersion(6)
|
|
|
|
InBound = true
|
|
OutBound = false
|
|
|
|
Local = true
|
|
Remote = false
|
|
|
|
// convenience
|
|
IGMP = IPProtocol(2)
|
|
RAW = IPProtocol(255)
|
|
TCP = IPProtocol(6)
|
|
UDP = IPProtocol(17)
|
|
ICMP = IPProtocol(1)
|
|
ICMPv6 = IPProtocol(58)
|
|
)
|
|
|
|
const (
|
|
DROP Verdict = iota
|
|
BLOCK
|
|
ACCEPT
|
|
STOLEN
|
|
QUEUE
|
|
REPEAT
|
|
STOP
|
|
)
|
|
|
|
// Returns the byte size of the ip, IPv4 = 4 bytes, IPv6 = 16
|
|
func (v IPVersion) ByteSize() int {
|
|
switch v {
|
|
case IPv4:
|
|
return 4
|
|
case IPv6:
|
|
return 16
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (v IPVersion) String() string {
|
|
switch v {
|
|
case IPv4:
|
|
return "IPv4"
|
|
case IPv6:
|
|
return "IPv6"
|
|
}
|
|
return fmt.Sprintf("<unknown ip version, %d>", uint8(v))
|
|
}
|
|
|
|
func (p IPProtocol) String() string {
|
|
switch p {
|
|
case RAW:
|
|
return "RAW"
|
|
case TCP:
|
|
return "TCP"
|
|
case UDP:
|
|
return "UDP"
|
|
case ICMP:
|
|
return "ICMP"
|
|
case ICMPv6:
|
|
return "ICMPv6"
|
|
case IGMP:
|
|
return "IGMP"
|
|
}
|
|
return fmt.Sprintf("<unknown protocol, %d>", uint8(p))
|
|
}
|
|
|
|
func (v Verdict) String() string {
|
|
switch v {
|
|
case DROP:
|
|
return "DROP"
|
|
case ACCEPT:
|
|
return "ACCEPT"
|
|
}
|
|
return fmt.Sprintf("<unsupported verdict, %d>", uint8(v))
|
|
}
|
|
|
|
type IPHeader struct {
|
|
Version IPVersion
|
|
|
|
Tos, TTL uint8
|
|
Protocol IPProtocol
|
|
Src, Dst net.IP
|
|
}
|
|
|
|
type TCPUDPHeader struct {
|
|
SrcPort, DstPort uint16
|
|
Checksum uint16 //not implemented
|
|
}
|
|
|
|
type PacketBase struct {
|
|
connectionID string
|
|
Direction bool
|
|
InTunnel bool
|
|
Payload []byte
|
|
*IPHeader
|
|
*TCPUDPHeader
|
|
}
|
|
|
|
func (pkt *PacketBase) GetIPHeader() *IPHeader {
|
|
return pkt.IPHeader
|
|
}
|
|
|
|
func (pkt *PacketBase) GetTCPUDPHeader() *TCPUDPHeader {
|
|
return pkt.TCPUDPHeader
|
|
}
|
|
|
|
func (pkt *PacketBase) GetPayload() []byte {
|
|
return pkt.Payload
|
|
}
|
|
|
|
func (pkt *PacketBase) SetInbound() {
|
|
pkt.Direction = true
|
|
}
|
|
|
|
func (pkt *PacketBase) SetOutbound() {
|
|
pkt.Direction = false
|
|
}
|
|
|
|
func (pkt *PacketBase) IsInbound() bool {
|
|
return pkt.Direction
|
|
}
|
|
|
|
func (pkt *PacketBase) IsOutbound() bool {
|
|
return !pkt.Direction
|
|
}
|
|
|
|
func (pkt *PacketBase) IPVersion() IPVersion {
|
|
return pkt.Version
|
|
}
|
|
|
|
func (pkt *PacketBase) GetConnectionID() string {
|
|
if pkt.connectionID == "" {
|
|
pkt.createConnectionID()
|
|
}
|
|
return pkt.connectionID
|
|
}
|
|
|
|
func (pkt *PacketBase) createConnectionID() {
|
|
if pkt.IPHeader.Protocol == TCP || pkt.IPHeader.Protocol == UDP {
|
|
if pkt.Direction {
|
|
pkt.connectionID = fmt.Sprintf("%d-%s-%d-%s-%d", pkt.Protocol, pkt.Dst, pkt.DstPort, pkt.Src, pkt.SrcPort)
|
|
} else {
|
|
pkt.connectionID = fmt.Sprintf("%d-%s-%d-%s-%d", pkt.Protocol, pkt.Src, pkt.SrcPort, pkt.Dst, pkt.DstPort)
|
|
}
|
|
} else {
|
|
if pkt.Direction {
|
|
pkt.connectionID = fmt.Sprintf("%d-%s-%s", pkt.Protocol, pkt.Dst, pkt.Src)
|
|
} else {
|
|
pkt.connectionID = fmt.Sprintf("%d-%s-%s", pkt.Protocol, pkt.Src, pkt.Dst)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Matches checks if a the packet matches a given endpoint (remote or local) in protocol, network and port.
|
|
//
|
|
// IN OUT
|
|
// Local Dst Src
|
|
// Remote Src Dst
|
|
//
|
|
func (pkt *PacketBase) MatchesAddress(endpoint bool, protocol IPProtocol, network *net.IPNet, port uint16) bool {
|
|
if pkt.Protocol != protocol {
|
|
return false
|
|
}
|
|
if pkt.Direction != endpoint {
|
|
if !network.Contains(pkt.Src) {
|
|
return false
|
|
}
|
|
if port != 0 && pkt.TCPUDPHeader != nil {
|
|
if pkt.SrcPort != port {
|
|
return false
|
|
}
|
|
}
|
|
} else {
|
|
if !network.Contains(pkt.Dst) {
|
|
return false
|
|
}
|
|
if port != 0 && pkt.TCPUDPHeader != nil {
|
|
if pkt.DstPort != port {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (pkt *PacketBase) MatchesIP(endpoint bool, network *net.IPNet) bool {
|
|
if pkt.Direction != endpoint {
|
|
if network.Contains(pkt.Src) {
|
|
return true
|
|
}
|
|
} else {
|
|
if network.Contains(pkt.Dst) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// func (pkt *PacketBase) Accept() error {
|
|
// return nil
|
|
// }
|
|
//
|
|
// func (pkt *PacketBase) Drop() error {
|
|
// return nil
|
|
// }
|
|
//
|
|
// func (pkt *PacketBase) Block() error {
|
|
// return nil
|
|
// }
|
|
//
|
|
// func (pkt *PacketBase) Verdict(verdict Verdict) error {
|
|
// return nil
|
|
// }
|
|
|
|
// FORMATTING
|
|
|
|
func (pkt *PacketBase) String() string {
|
|
return pkt.FmtPacket()
|
|
}
|
|
|
|
// FmtPacket returns the most important information about the packet as a string
|
|
func (pkt *PacketBase) FmtPacket() string {
|
|
if pkt.IPHeader.Protocol == TCP || pkt.IPHeader.Protocol == UDP {
|
|
if pkt.Direction {
|
|
return fmt.Sprintf("IN %s %s:%d <-> %s:%d", pkt.Protocol, pkt.Dst, pkt.DstPort, pkt.Src, pkt.SrcPort)
|
|
}
|
|
return fmt.Sprintf("OUT %s %s:%d <-> %s:%d", pkt.Protocol, pkt.Src, pkt.SrcPort, pkt.Dst, pkt.DstPort)
|
|
}
|
|
if pkt.Direction {
|
|
return fmt.Sprintf("IN %s %s <-> %s", pkt.Protocol, pkt.Dst, pkt.Src)
|
|
}
|
|
return fmt.Sprintf("OUT %s %s <-> %s", pkt.Protocol, pkt.Src, pkt.Dst)
|
|
}
|
|
|
|
// FmtProtocol returns the protocol as a string
|
|
func (pkt *PacketBase) FmtProtocol() string {
|
|
return pkt.IPHeader.Protocol.String()
|
|
}
|
|
|
|
// FmtRemoteIP returns the remote IP address as a string
|
|
func (pkt *PacketBase) FmtRemoteIP() string {
|
|
if pkt.Direction {
|
|
return pkt.IPHeader.Src.String()
|
|
}
|
|
return pkt.IPHeader.Dst.String()
|
|
}
|
|
|
|
// FmtRemotePort returns the remote port as a string
|
|
func (pkt *PacketBase) FmtRemotePort() string {
|
|
if pkt.TCPUDPHeader != nil {
|
|
if pkt.Direction {
|
|
return fmt.Sprintf("%d", pkt.TCPUDPHeader.SrcPort)
|
|
}
|
|
return fmt.Sprintf("%d", pkt.TCPUDPHeader.DstPort)
|
|
}
|
|
return "-"
|
|
}
|
|
|
|
// FmtRemoteAddress returns the full remote address (protocol, IP, port) as a string
|
|
func (pkt *PacketBase) FmtRemoteAddress() string {
|
|
return fmt.Sprintf("%s:%s:%s", pkt.IPHeader.Protocol.String(), pkt.FmtRemoteIP(), pkt.FmtRemotePort())
|
|
}
|
|
|
|
// Packet is an interface to a network packet to provide object behaviour the same across all systems
|
|
type Packet interface {
|
|
// VERDICTS
|
|
Accept() error
|
|
Block() error
|
|
Drop() error
|
|
PermanentAccept() error
|
|
PermanentBlock() error
|
|
PermanentDrop() error
|
|
RerouteToNameserver() error
|
|
RerouteToTunnel() error
|
|
|
|
// INFO
|
|
GetIPHeader() *IPHeader
|
|
GetTCPUDPHeader() *TCPUDPHeader
|
|
GetPayload() []byte
|
|
IsInbound() bool
|
|
IsOutbound() bool
|
|
SetInbound()
|
|
SetOutbound()
|
|
GetConnectionID() string
|
|
IPVersion() IPVersion
|
|
|
|
// MATCHING
|
|
MatchesAddress(bool, IPProtocol, *net.IPNet, uint16) bool
|
|
MatchesIP(bool, *net.IPNet) bool
|
|
|
|
// FORMATTING
|
|
String() string
|
|
FmtPacket() string
|
|
FmtProtocol() string
|
|
FmtRemoteIP() string
|
|
FmtRemotePort() string
|
|
FmtRemoteAddress() string
|
|
}
|