wip: migrate to mono-repo. SPN has already been moved to spn/
This commit is contained in:
35
service/network/packet/bandwidth.go
Normal file
35
service/network/packet/bandwidth.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package packet
|
||||
|
||||
import "fmt"
|
||||
|
||||
// BandwidthUpdate holds an update to the seen bandwidth of a connection.
|
||||
type BandwidthUpdate struct {
|
||||
ConnID string
|
||||
BytesReceived uint64
|
||||
BytesSent uint64
|
||||
Method BandwidthUpdateMethod
|
||||
}
|
||||
|
||||
// BandwidthUpdateMethod defines how the bandwidth data of a bandwidth update should be interpreted.
|
||||
type BandwidthUpdateMethod uint8
|
||||
|
||||
// Bandwidth Update Methods.
|
||||
const (
|
||||
Absolute BandwidthUpdateMethod = iota
|
||||
Additive
|
||||
)
|
||||
|
||||
func (bu *BandwidthUpdate) String() string {
|
||||
return fmt.Sprintf("%s: %dB recv | %dB sent [%s]", bu.ConnID, bu.BytesReceived, bu.BytesSent, bu.Method)
|
||||
}
|
||||
|
||||
func (bum BandwidthUpdateMethod) String() string {
|
||||
switch bum {
|
||||
case Absolute:
|
||||
return "absolute"
|
||||
case Additive:
|
||||
return "additive"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
117
service/network/packet/const.go
Normal file
117
service/network/packet/const.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Basic Types.
|
||||
type (
|
||||
// IPVersion represents an IP version.
|
||||
IPVersion uint8
|
||||
// IPProtocol represents an IP protocol.
|
||||
IPProtocol uint8
|
||||
// Verdict describes the decision on a packet.
|
||||
Verdict uint8
|
||||
)
|
||||
|
||||
// Basic Constants.
|
||||
const (
|
||||
IPv4 = IPVersion(4)
|
||||
IPv6 = IPVersion(6)
|
||||
|
||||
InBound = true
|
||||
OutBound = false
|
||||
|
||||
ICMP = IPProtocol(1)
|
||||
IGMP = IPProtocol(2)
|
||||
TCP = IPProtocol(6)
|
||||
UDP = IPProtocol(17)
|
||||
ICMPv6 = IPProtocol(58)
|
||||
UDPLite = IPProtocol(136)
|
||||
RAW = IPProtocol(255)
|
||||
|
||||
AnyHostInternalProtocol61 = IPProtocol(61)
|
||||
)
|
||||
|
||||
// Verdicts.
|
||||
const (
|
||||
DROP Verdict = iota
|
||||
BLOCK
|
||||
ACCEPT
|
||||
STOLEN
|
||||
QUEUE
|
||||
REPEAT
|
||||
STOP
|
||||
)
|
||||
|
||||
// ErrFailedToLoadPayload is returned by GetPayload if it failed for an unspecified reason, or is not implemented on the current system.
|
||||
var ErrFailedToLoadPayload = errors.New("could not load packet payload")
|
||||
|
||||
// ByteSize 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
|
||||
}
|
||||
|
||||
// String returns the string representation of the IP version: "IPv4" or "IPv6".
|
||||
func (v IPVersion) String() string {
|
||||
switch v {
|
||||
case IPv4:
|
||||
return "IPv4"
|
||||
case IPv6:
|
||||
return "IPv6"
|
||||
}
|
||||
return fmt.Sprintf("<unknown ip version, %d>", uint8(v))
|
||||
}
|
||||
|
||||
// String returns the string representation (abbreviation) of the protocol.
|
||||
func (p IPProtocol) String() string {
|
||||
switch p {
|
||||
case RAW:
|
||||
return "RAW"
|
||||
case TCP:
|
||||
return "TCP"
|
||||
case UDP:
|
||||
return "UDP"
|
||||
case UDPLite:
|
||||
return "UDPLite"
|
||||
case ICMP:
|
||||
return "ICMP"
|
||||
case ICMPv6:
|
||||
return "ICMPv6"
|
||||
case IGMP:
|
||||
return "IGMP"
|
||||
case AnyHostInternalProtocol61:
|
||||
fallthrough
|
||||
default:
|
||||
return fmt.Sprintf("<unknown protocol, %d>", uint8(p))
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of the verdict.
|
||||
func (v Verdict) String() string {
|
||||
switch v {
|
||||
case DROP:
|
||||
return "DROP"
|
||||
case BLOCK:
|
||||
return "BLOCK"
|
||||
case ACCEPT:
|
||||
return "ACCEPT"
|
||||
case STOLEN:
|
||||
return "STOLEN"
|
||||
case QUEUE:
|
||||
return "QUEUE"
|
||||
case REPEAT:
|
||||
return "REPEAT"
|
||||
case STOP:
|
||||
return "STOP"
|
||||
default:
|
||||
return fmt.Sprintf("<unsupported verdict, %d>", uint8(v))
|
||||
}
|
||||
}
|
||||
77
service/network/packet/info_only.go
Normal file
77
service/network/packet/info_only.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// InfoPacket does not represent an actual packet, but only holds metadata.
|
||||
// Implements the packet.Packet interface.
|
||||
type InfoPacket struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// NewInfoPacket returns a new InfoPacket with the given info.
|
||||
func NewInfoPacket(info Info) *InfoPacket {
|
||||
return &InfoPacket{
|
||||
Base{
|
||||
info: info,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// InfoOnly returns whether the packet is informational only and does not
|
||||
// represent an actual packet.
|
||||
func (pkt *InfoPacket) InfoOnly() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// LoadPacketData does nothing on Linux, as data is always fully parsed.
|
||||
func (pkt *InfoPacket) LoadPacketData() error {
|
||||
return fmt.Errorf("%w: info-only packet", ErrFailedToLoadPayload)
|
||||
}
|
||||
|
||||
// ErrInfoOnlyPacket is returned for unsupported operations on an info-only packet.
|
||||
var ErrInfoOnlyPacket = errors.New("info-only packet")
|
||||
|
||||
// Accept does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) Accept() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// Block does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) Block() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// Drop does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) Drop() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// PermanentAccept does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) PermanentAccept() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// PermanentBlock does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) PermanentBlock() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// PermanentDrop does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) PermanentDrop() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// RerouteToNameserver does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) RerouteToNameserver() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// RerouteToTunnel does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) RerouteToTunnel() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
var _ Packet = &InfoPacket{}
|
||||
266
service/network/packet/packet.go
Normal file
266
service/network/packet/packet.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Base is a base structure for satisfying the Packet interface.
|
||||
type Base struct {
|
||||
ctx context.Context
|
||||
info Info
|
||||
connID string
|
||||
layers gopacket.Packet
|
||||
layer3Data []byte
|
||||
layer5Data []byte
|
||||
}
|
||||
|
||||
// FastTrackedByIntegration returns whether the packet has been fast-track
|
||||
// accepted by the OS integration.
|
||||
func (pkt *Base) FastTrackedByIntegration() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// InfoOnly returns whether the packet is informational only and does not
|
||||
// represent an actual packet.
|
||||
func (pkt *Base) InfoOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ExpectInfo returns whether the next packet is expected to be informational only.
|
||||
func (pkt *Base) ExpectInfo() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCtx sets the packet context.
|
||||
func (pkt *Base) SetCtx(ctx context.Context) {
|
||||
pkt.ctx = ctx
|
||||
}
|
||||
|
||||
// Ctx returns the packet context.
|
||||
func (pkt *Base) Ctx() context.Context {
|
||||
return pkt.ctx
|
||||
}
|
||||
|
||||
// Info returns the packet Info.
|
||||
func (pkt *Base) Info() *Info {
|
||||
return &pkt.info
|
||||
}
|
||||
|
||||
// SetPacketInfo sets a new packet Info. This must only used when initializing the packet structure.
|
||||
func (pkt *Base) SetPacketInfo(packetInfo Info) {
|
||||
pkt.info = packetInfo
|
||||
}
|
||||
|
||||
// SetInbound sets a the packet direction to inbound. This must only used when initializing the packet structure.
|
||||
func (pkt *Base) SetInbound() {
|
||||
pkt.info.Inbound = true
|
||||
}
|
||||
|
||||
// SetOutbound sets a the packet direction to outbound. This must only used when initializing the packet structure.
|
||||
func (pkt *Base) SetOutbound() {
|
||||
pkt.info.Inbound = false
|
||||
}
|
||||
|
||||
// IsInbound checks if the packet is inbound.
|
||||
func (pkt *Base) IsInbound() bool {
|
||||
return pkt.info.Inbound
|
||||
}
|
||||
|
||||
// IsOutbound checks if the packet is outbound.
|
||||
func (pkt *Base) IsOutbound() bool {
|
||||
return !pkt.info.Inbound
|
||||
}
|
||||
|
||||
// HasPorts checks if the packet has a protocol that uses ports.
|
||||
func (pkt *Base) HasPorts() bool {
|
||||
switch pkt.info.Protocol {
|
||||
case TCP:
|
||||
return true
|
||||
case UDP, UDPLite:
|
||||
return true
|
||||
case ICMP, ICMPv6, IGMP, RAW, AnyHostInternalProtocol61:
|
||||
fallthrough
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// LoadPacketData loads packet data from the integration, if not yet done.
|
||||
func (pkt *Base) LoadPacketData() error {
|
||||
return ErrFailedToLoadPayload
|
||||
}
|
||||
|
||||
// Layers returns the parsed layer data.
|
||||
func (pkt *Base) Layers() gopacket.Packet {
|
||||
return pkt.layers
|
||||
}
|
||||
|
||||
// Raw returns the raw Layer 3 Network Data.
|
||||
func (pkt *Base) Raw() []byte {
|
||||
return pkt.layer3Data
|
||||
}
|
||||
|
||||
// Payload returns the raw Layer 5 Network Data.
|
||||
func (pkt *Base) Payload() []byte {
|
||||
return pkt.layer5Data
|
||||
}
|
||||
|
||||
// GetConnectionID returns the link ID for this packet.
|
||||
func (pkt *Base) GetConnectionID() string {
|
||||
if pkt.connID == "" {
|
||||
pkt.connID = pkt.info.CreateConnectionID()
|
||||
}
|
||||
return pkt.connID
|
||||
}
|
||||
|
||||
// MatchesAddress checks if a the packet matches a given endpoint (remote or local) in protocol, network and port.
|
||||
//
|
||||
// Comparison matrix:
|
||||
//
|
||||
// ====== IN OUT
|
||||
//
|
||||
// Local Dst Src
|
||||
// Remote Src Dst
|
||||
// .
|
||||
func (pkt *Base) MatchesAddress(remote bool, protocol IPProtocol, network *net.IPNet, port uint16) bool {
|
||||
if pkt.info.Protocol != protocol {
|
||||
return false
|
||||
}
|
||||
if pkt.info.Inbound != remote {
|
||||
if !network.Contains(pkt.info.Src) {
|
||||
return false
|
||||
}
|
||||
if pkt.info.SrcPort != port {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !network.Contains(pkt.info.Dst) {
|
||||
return false
|
||||
}
|
||||
if pkt.info.DstPort != port {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MatchesIP checks if a the packet matches a given endpoint (remote or local) IP.
|
||||
//
|
||||
// Comparison matrix:
|
||||
//
|
||||
// ====== IN OUT
|
||||
//
|
||||
// Local Dst Src
|
||||
// Remote Src Dst
|
||||
// .
|
||||
func (pkt *Base) MatchesIP(endpoint bool, network *net.IPNet) bool {
|
||||
if pkt.info.Inbound != endpoint {
|
||||
if network.Contains(pkt.info.Src) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if network.Contains(pkt.info.Dst) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FORMATTING
|
||||
|
||||
func (pkt *Base) String() string {
|
||||
return pkt.FmtPacket()
|
||||
}
|
||||
|
||||
// FmtPacket returns the most important information about the packet as a string.
|
||||
func (pkt *Base) FmtPacket() string {
|
||||
if pkt.info.Protocol == TCP || pkt.info.Protocol == UDP {
|
||||
if pkt.info.Inbound {
|
||||
return fmt.Sprintf("IN %s %s:%d <-> %s:%d", pkt.info.Protocol, pkt.info.Dst, pkt.info.DstPort, pkt.info.Src, pkt.info.SrcPort)
|
||||
}
|
||||
return fmt.Sprintf("OUT %s %s:%d <-> %s:%d", pkt.info.Protocol, pkt.info.Src, pkt.info.SrcPort, pkt.info.Dst, pkt.info.DstPort)
|
||||
}
|
||||
if pkt.info.Inbound {
|
||||
return fmt.Sprintf("IN %s %s <-> %s", pkt.info.Protocol, pkt.info.Dst, pkt.info.Src)
|
||||
}
|
||||
return fmt.Sprintf("OUT %s %s <-> %s", pkt.info.Protocol, pkt.info.Src, pkt.info.Dst)
|
||||
}
|
||||
|
||||
// FmtProtocol returns the protocol as a string.
|
||||
func (pkt *Base) FmtProtocol() string {
|
||||
return pkt.info.Protocol.String()
|
||||
}
|
||||
|
||||
// FmtRemoteIP returns the remote IP address as a string.
|
||||
func (pkt *Base) FmtRemoteIP() string {
|
||||
if pkt.info.Inbound {
|
||||
return pkt.info.Src.String()
|
||||
}
|
||||
return pkt.info.Dst.String()
|
||||
}
|
||||
|
||||
// FmtRemotePort returns the remote port as a string.
|
||||
func (pkt *Base) FmtRemotePort() string {
|
||||
if pkt.info.SrcPort != 0 {
|
||||
if pkt.info.Inbound {
|
||||
return fmt.Sprintf("%d", pkt.info.SrcPort)
|
||||
}
|
||||
return fmt.Sprintf("%d", pkt.info.DstPort)
|
||||
}
|
||||
return "-"
|
||||
}
|
||||
|
||||
// FmtRemoteAddress returns the full remote address (protocol, IP, port) as a string.
|
||||
func (pkt *Base) FmtRemoteAddress() string {
|
||||
return fmt.Sprintf("%s:%s:%s", pkt.info.Protocol.String(), pkt.FmtRemoteIP(), pkt.FmtRemotePort())
|
||||
}
|
||||
|
||||
// Packet is an interface to a network packet to provide object behavior 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
|
||||
FastTrackedByIntegration() bool
|
||||
InfoOnly() bool
|
||||
ExpectInfo() bool
|
||||
|
||||
// Info.
|
||||
SetCtx(context.Context)
|
||||
Ctx() context.Context
|
||||
Info() *Info
|
||||
SetPacketInfo(Info)
|
||||
IsInbound() bool
|
||||
IsOutbound() bool
|
||||
SetInbound()
|
||||
SetOutbound()
|
||||
HasPorts() bool
|
||||
GetConnectionID() string
|
||||
|
||||
// Payload.
|
||||
LoadPacketData() error
|
||||
Layers() gopacket.Packet
|
||||
Raw() []byte
|
||||
Payload() []byte
|
||||
|
||||
// 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
|
||||
}
|
||||
76
service/network/packet/packetinfo.go
Normal file
76
service/network/packet/packetinfo.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Info holds IP and TCP/UDP header information.
|
||||
type Info struct {
|
||||
Inbound bool
|
||||
InTunnel bool
|
||||
|
||||
Version IPVersion
|
||||
Protocol IPProtocol
|
||||
SrcPort, DstPort uint16
|
||||
Src, Dst net.IP
|
||||
|
||||
PID int
|
||||
SeenAt time.Time
|
||||
}
|
||||
|
||||
// LocalIP returns the local IP of the packet.
|
||||
func (pi *Info) LocalIP() net.IP {
|
||||
if pi.Inbound {
|
||||
return pi.Dst
|
||||
}
|
||||
return pi.Src
|
||||
}
|
||||
|
||||
// RemoteIP returns the remote IP of the packet.
|
||||
func (pi *Info) RemoteIP() net.IP {
|
||||
if pi.Inbound {
|
||||
return pi.Src
|
||||
}
|
||||
return pi.Dst
|
||||
}
|
||||
|
||||
// LocalPort returns the local port of the packet.
|
||||
func (pi *Info) LocalPort() uint16 {
|
||||
if pi.Inbound {
|
||||
return pi.DstPort
|
||||
}
|
||||
return pi.SrcPort
|
||||
}
|
||||
|
||||
// RemotePort returns the remote port of the packet.
|
||||
func (pi *Info) RemotePort() uint16 {
|
||||
if pi.Inbound {
|
||||
return pi.SrcPort
|
||||
}
|
||||
return pi.DstPort
|
||||
}
|
||||
|
||||
// CreateConnectionID creates a connection ID.
|
||||
// In most circumstances, this method should not be used directly, but
|
||||
// packet.GetConnectionID() should be called instead.
|
||||
func (pi *Info) CreateConnectionID() string {
|
||||
return CreateConnectionID(pi.Protocol, pi.Src, pi.SrcPort, pi.Dst, pi.DstPort, pi.Inbound)
|
||||
}
|
||||
|
||||
// CreateConnectionID creates a connection ID.
|
||||
func CreateConnectionID(protocol IPProtocol, src net.IP, srcPort uint16, dst net.IP, dstPort uint16, inbound bool) string {
|
||||
// TODO: make this ID not depend on the packet direction for better support for forwarded packets.
|
||||
if protocol == TCP || protocol == UDP {
|
||||
if inbound {
|
||||
return fmt.Sprintf("%d-%s-%d-%s-%d", protocol, dst, dstPort, src, srcPort)
|
||||
}
|
||||
return fmt.Sprintf("%d-%s-%d-%s-%d", protocol, src, srcPort, dst, dstPort)
|
||||
}
|
||||
|
||||
if inbound {
|
||||
return fmt.Sprintf("%d-%s-%s", protocol, dst, src)
|
||||
}
|
||||
return fmt.Sprintf("%d-%s-%s", protocol, src, dst)
|
||||
}
|
||||
160
service/network/packet/parse.go
Normal file
160
service/network/packet/parse.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
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
|
||||
// we set Protocol to NextHeader as a fallback. If TCP or
|
||||
// UDP layers are detected (somewhere in the list of options)
|
||||
// the Protocol field is adjusted correctly.
|
||||
info.Protocol = IPProtocol(ipv6.NextHeader)
|
||||
}
|
||||
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 *Info) error {
|
||||
// Check for known unparseable before checking the error layer.
|
||||
if info.Protocol == AnyHostInternalProtocol61 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := packet.ErrorLayer(); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse parses an IP packet and saves the information in the given packet object.
|
||||
func Parse(packetData []byte, pktBase *Base) (err error) {
|
||||
if len(packetData) == 0 {
|
||||
return errors.New("empty packet")
|
||||
}
|
||||
pktBase.layer3Data = packetData
|
||||
|
||||
ipVersion := packetData[0] >> 4
|
||||
var networkLayerType gopacket.LayerType
|
||||
|
||||
switch ipVersion {
|
||||
case 4:
|
||||
networkLayerType = layers.LayerTypeIPv4
|
||||
case 6:
|
||||
networkLayerType = layers.LayerTypeIPv6
|
||||
default:
|
||||
return fmt.Errorf("unknown IP version or network protocol: %02x", ipVersion)
|
||||
}
|
||||
|
||||
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, pktBase.Info()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pktBase.layers = packet
|
||||
if transport := packet.TransportLayer(); transport != nil {
|
||||
pktBase.layer5Data = transport.LayerPayload()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
genIPProtocolFromLayerType()
|
||||
}
|
||||
Reference in New Issue
Block a user