diff --git a/communityid.go b/communityid.go index f75dc9f..785ed2d 100644 --- a/communityid.go +++ b/communityid.go @@ -5,6 +5,8 @@ import ( "hash" ) +// CommunityID is an interface defining the supported operations +// on a component calculating a specific community ID version. type CommunityID interface { Calc(FlowTuple) []byte CalcHex(FlowTuple) string @@ -15,6 +17,9 @@ type CommunityID interface { RenderBase64(hash.Hash) string } +// GetCommunityIDByVersion returns, for a given version number and seed, an +// object implementing the CommunityID interface for the specified version. +// This will be preconfigured with the given seed. func GetCommunityIDByVersion(version uint, seed uint16) (CommunityID, error) { switch version { case 1: diff --git a/communityid_v1.go b/communityid_v1.go index f87b07e..91c7f23 100644 --- a/communityid_v1.go +++ b/communityid_v1.go @@ -8,25 +8,35 @@ import ( "hash" ) +// CommunityIDv1 encapsulates the calculation code for version 1 of the +// Community ID flow hashing algorithm. type CommunityIDv1 struct { Seed uint16 } +// Calc returns the community id value for a given FlowTuple, as an +// unformatted byte slice. func (cid CommunityIDv1) Calc(ft FlowTuple) []byte { ft = ft.InOrder() return cid.Render(cid.Hash(ft)) } +// CalcHex returns the community id value for a given FlowTuple, as an +// hex-encoded string. func (cid CommunityIDv1) CalcHex(ft FlowTuple) string { ft = ft.InOrder() return cid.RenderHex(cid.Hash(ft)) } +// CalcBase64 returns the community id value for a given FlowTuple, as an +// Base64-encoded string. func (cid CommunityIDv1) CalcBase64(ft FlowTuple) string { ft = ft.InOrder() return cid.RenderBase64(cid.Hash(ft)) } +// Hash returns a hash.Hash instance (SHA1) in a state corresponding to all +// input value already dealt with in the hash. func (cid CommunityIDv1) Hash(ft FlowTuple) hash.Hash { h := sha1.New() binary.Write(h, binary.BigEndian, cid.Seed) @@ -46,14 +56,17 @@ func (cid CommunityIDv1) Hash(ft FlowTuple) hash.Hash { return h } +// Render returns the value of the given hash, as an unformatted byte slice. func (cid CommunityIDv1) Render(h hash.Hash) []byte { return h.Sum(nil) } +// RenderBase64 returns the value of the given hash, as Base64-encoded string. func (cid CommunityIDv1) RenderBase64(h hash.Hash) string { return "1:" + base64.StdEncoding.EncodeToString(cid.Render(h)) } +// RenderHex returns the value of the given hash, as hex-encoded string. func (cid CommunityIDv1) RenderHex(h hash.Hash) string { return fmt.Sprintf("1:%x", cid.Render(h)) } diff --git a/flowtuple.go b/flowtuple.go index a56d522..cf6b2dc 100644 --- a/flowtuple.go +++ b/flowtuple.go @@ -5,6 +5,7 @@ import ( "net" ) +// FlowTuple is a collection of all values required for ID calculation. type FlowTuple struct { Srcip net.IP Dstip net.IP @@ -14,6 +15,9 @@ type FlowTuple struct { IsOneWay bool } +// MakeFlowTuple returns a FlowTuple for the given set of communication +// details: protocol, IPs (source, destination) and ports (source, +// destination). func MakeFlowTuple(srcip, dstip net.IP, srcport, dstport uint16, proto uint8) FlowTuple { var isOneWay bool if proto == ProtoICMP { @@ -32,22 +36,27 @@ func MakeFlowTuple(srcip, dstip net.IP, srcport, dstport uint16, proto uint8) Fl return v } +// MakeFlowTupleTCP returns a FlowTuple with the TCP protocol preconfigured. func MakeFlowTupleTCP(srcip, dstip net.IP, srcport, dstport uint16) FlowTuple { return MakeFlowTuple(srcip, dstip, srcport, dstport, ProtoTCP) } +// MakeFlowTupleUDP returns a FlowTuple with the UDP protocol preconfigured. func MakeFlowTupleUDP(srcip, dstip net.IP, srcport, dstport uint16) FlowTuple { return MakeFlowTuple(srcip, dstip, srcport, dstport, ProtoUDP) } +// MakeFlowTupleSCTP returns a FlowTuple with the SCTP protocol preconfigured. func MakeFlowTupleSCTP(srcip, dstip net.IP, srcport, dstport uint16) FlowTuple { return MakeFlowTuple(srcip, dstip, srcport, dstport, ProtoSCTP) } +// MakeFlowTupleICMP returns a FlowTuple with the ICMPv4 protocol preconfigured. func MakeFlowTupleICMP(srcip, dstip net.IP, srcport, dstport uint16) FlowTuple { return MakeFlowTuple(srcip, dstip, srcport, dstport, ProtoICMP) } +// MakeFlowTupleICMP6 returns a FlowTuple with the ICMPv6 protocol preconfigured. func MakeFlowTupleICMP6(srcip, dstip net.IP, srcport, dstport uint16) FlowTuple { return MakeFlowTuple(srcip, dstip, srcport, dstport, ProtoICMP6) } @@ -56,10 +65,13 @@ func flowTupleOrdered(addr1, addr2 []byte, port1, port2 uint16) bool { return bytes.Compare(addr1, addr2) == -1 || (bytes.Equal(addr1, addr2) && port1 < port2) } +// IsOrdered returns true if the flow tuple direction is ordered. func (ft FlowTuple) IsOrdered() bool { return ft.IsOneWay || flowTupleOrdered(ft.Srcip, ft.Dstip, ft.Srcport, ft.Dstport) } +// InOrder returns a new copy of the flow tuple, with guaranteed IsOrdered() +// property. func (ft FlowTuple) InOrder() FlowTuple { if ft.IsOrdered() { return FlowTuple{ diff --git a/icmp_helper.go b/icmp_helper.go index c28f69d..665c2cb 100644 --- a/icmp_helper.go +++ b/icmp_helper.go @@ -4,7 +4,7 @@ import ( "github.com/google/gopacket/layers" ) -var ICMPv4PortEquivalents = map[uint8]uint8{ +var icmpv4PortEquivalents = map[uint8]uint8{ layers.ICMPv4TypeEchoRequest: layers.ICMPv4TypeEchoReply, layers.ICMPv4TypeEchoReply: layers.ICMPv4TypeEchoRequest, layers.ICMPv4TypeTimestampRequest: layers.ICMPv4TypeTimestampReply, @@ -17,7 +17,7 @@ var ICMPv4PortEquivalents = map[uint8]uint8{ layers.ICMPv4TypeAddressMaskReply: layers.ICMPv4TypeAddressMaskRequest, } -var ICMPv6PortEquivalents = map[uint8]uint8{ +var icmpv6PortEquivalents = map[uint8]uint8{ layers.ICMPv6TypeEchoRequest: layers.ICMPv6TypeEchoReply, layers.ICMPv6TypeEchoReply: layers.ICMPv6TypeEchoRequest, layers.ICMPv6TypeRouterSolicitation: layers.ICMPv6TypeRouterAdvertisement, @@ -30,15 +30,19 @@ var ICMPv6PortEquivalents = map[uint8]uint8{ 145: 144, } +// GetICMPv4PortEquivalents returns ICMPv4 codes mapped back to pseudo port +// numbers, as well as a bool indicating whether a communication is one-way. func GetICMPv4PortEquivalents(p1, p2 uint8) (uint16, uint16, bool) { - if val, ok := ICMPv4PortEquivalents[p1]; ok { + if val, ok := icmpv4PortEquivalents[p1]; ok { return uint16(p1), uint16(val), false } return uint16(p1), uint16(p2), true } +// GetICMPv6PortEquivalents returns ICMPv6 codes mapped back to pseudo port +// numbers, as well as a bool indicating whether a communication is one-way. func GetICMPv6PortEquivalents(p1, p2 uint8) (uint16, uint16, bool) { - if val, ok := ICMPv6PortEquivalents[p1]; ok { + if val, ok := icmpv6PortEquivalents[p1]; ok { return uint16(p1), uint16(val), false } return uint16(p1), uint16(p2), true diff --git a/pcap_flowtuple_source.go b/pcap_flowtuple_source.go index 061cfc4..40f59f1 100644 --- a/pcap_flowtuple_source.go +++ b/pcap_flowtuple_source.go @@ -8,11 +8,17 @@ import ( "github.com/google/gopacket/pcap" ) +// PcapFlowTuple represents a pair of the FlowTuple for a packet as +// well as its packet metadata (e.g. timestamp). type PcapFlowTuple struct { FlowTuple FlowTuple Metadata *gopacket.PacketMetadata } +// PcapFlowTupleSource returns, for a given pcap file name, a channel +// delivering PcapFlowTuples for each packet in the file. If the file +// cannot be read for some reason, an error is returned as well +// accordingly. func PcapFlowTupleSource(file string) (<-chan PcapFlowTuple, error) { outChan := make(chan PcapFlowTuple) handle, err := pcap.OpenOffline(file) diff --git a/protocols.go b/protocols.go index f8c4f0d..6ec476f 100644 --- a/protocols.go +++ b/protocols.go @@ -1,5 +1,6 @@ package gommunityid +// Define protocol number constants. const ( ProtoICMP = 1 ProtoTCP = 6