From c62173ac9558581fee03e63955a7eb8941178d05 Mon Sep 17 00:00:00 2001
From: "Mohamed S. Mahmoud" <mmahmoud@redhat.com>
Date: Mon, 20 Mar 2023 11:01:31 -0400
Subject: [PATCH] NETOBSERV-934: Add support for SCTP, ICMPv4/v6 protocols to
 ebpf agent (#103)

* Add support for SCTP, ICMP amd ICMPv6 protocols to ebpf code

Signed-off-by: Mohamed Mahmoud <mmahmoud@redhat.com>

* update GRPC protobuf definition to include icmp fields

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

* Add ICMP and ICMPv6 ipfix support

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

* Add ICMPv4/6 ebpf agent support

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

* Update unit-test cases

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

* Add verifier error check to catch JIT errors

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

* update flowlogs dump collector tool to include ICMP

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

---------

Signed-off-by: Mohamed Mahmoud <mmahmoud@redhat.com>
Signed-off-by: msherif1234 <mmahmoud@redhat.com>
---
 bpf/flow.h                                    |   3 +
 bpf/flows.c                                   | 137 +++++++++-----
 .../server/flowlogs-dump-collector.go         |   8 +-
 pkg/ebpf/bpf_bpfeb.go                         |   2 +
 pkg/ebpf/bpf_bpfeb.o                          | Bin 21176 -> 22872 bytes
 pkg/ebpf/bpf_bpfel.go                         |   2 +
 pkg/ebpf/bpf_bpfel.o                          | Bin 21704 -> 22040 bytes
 pkg/ebpf/tracer.go                            |   7 +
 pkg/exporter/ipfix.go                         |  20 +++
 pkg/exporter/kafka_proto_test.go              |   2 +
 pkg/exporter/proto.go                         |   8 +
 pkg/flow/record_test.go                       |   4 +
 pkg/grpc/grpc_test.go                         |   8 +
 pkg/pbflow/flow.pb.go                         | 169 +++++++++++++-----
 proto/flow.proto                              |   6 +
 15 files changed, 287 insertions(+), 89 deletions(-)

diff --git a/bpf/flow.h b/bpf/flow.h
index fa06e1d8..f36ce12d 100644
--- a/bpf/flow.h
+++ b/bpf/flow.h
@@ -45,6 +45,9 @@ typedef struct flow_id_t {
     u16 src_port;
     u16 dst_port;
     u8 transport_protocol;
+    // ICMP protocol
+    u8  icmp_type;
+    u8  icmp_code;
     // OS interface index
     u32 if_index;
 } __attribute__((packed)) flow_id;
diff --git a/bpf/flows.c b/bpf/flows.c
index bee299da..40ea1a1c 100644
--- a/bpf/flows.c
+++ b/bpf/flows.c
@@ -13,7 +13,6 @@
             until an entry is available.
         4) When hash collision is detected, we send the new entry to userpace via ringbuffer.
 */
-
 #include <linux/bpf.h>
 #include <linux/in.h>
 #include <linux/if_packet.h>
@@ -55,6 +54,15 @@
 #define FIN_ACK_FLAG 0x200
 #define RST_ACK_FLAG 0x400
 
+// SCTP protocol header structure, its defined here because its not
+// exported by the kernel headers like other protocols.
+struct sctphdr {
+    __be16 source;
+    __be16 dest;
+    __be32 vtag;
+    __le32 checksum;
+};
+
 // Common Ringbuffer as a conduit for ingress/egress flows to userspace
 struct {
     __uint(type, BPF_MAP_TYPE_RINGBUF);
@@ -104,71 +112,111 @@ static inline void set_flags(struct tcphdr *th, u16 *flags) {
         *flags |= CWR_FLAG;
     }
 }
-// sets flow fields from IPv4 header information
-static inline int fill_iphdr(struct iphdr *ip, void *data_end, flow_id *id, u16 *flags) {
-    if ((void *)ip + sizeof(*ip) > data_end) {
-        return DISCARD;
-    }
 
-    __builtin_memcpy(id->src_ip, ip4in6, sizeof(ip4in6));
-    __builtin_memcpy(id->dst_ip, ip4in6, sizeof(ip4in6));
-    __builtin_memcpy(id->src_ip + sizeof(ip4in6), &ip->saddr, sizeof(ip->saddr));
-    __builtin_memcpy(id->dst_ip + sizeof(ip4in6), &ip->daddr, sizeof(ip->daddr));
-    id->transport_protocol = ip->protocol;
-    id->src_port = 0;
-    id->dst_port = 0;
-    switch (ip->protocol) {
+// L4_info structure contains L4 headers parsed information.
+struct l4_info_t {
+    // TCP/UDP/SCTP source port in host byte order
+    u16 src_port;
+    // TCP/UDP/SCTP destination port in host byte order
+    u16 dst_port;
+    // ICMPv4/ICMPv6 type value
+    u8 icmp_type;
+    // ICMPv4/ICMPv6 code value
+    u8 icmp_code;
+    // TCP flags
+    u16 flags;
+};
+
+// Extract L4 info for the supported protocols
+static inline void fill_l4info(void *l4_hdr_start, void *data_end, u8 protocol,
+                               struct l4_info_t *l4_info) {
+	switch (protocol) {
     case IPPROTO_TCP: {
-        struct tcphdr *tcp = (void *)ip + sizeof(*ip);
+        struct tcphdr *tcp = l4_hdr_start;
         if ((void *)tcp + sizeof(*tcp) <= data_end) {
-            id->src_port = __bpf_ntohs(tcp->source);
-            id->dst_port = __bpf_ntohs(tcp->dest);
-            set_flags(tcp, flags);
+            l4_info->src_port = __bpf_ntohs(tcp->source);
+            l4_info->dst_port = __bpf_ntohs(tcp->dest);
+            set_flags(tcp, &l4_info->flags);
         }
     } break;
     case IPPROTO_UDP: {
-        struct udphdr *udp = (void *)ip + sizeof(*ip);
+        struct udphdr *udp = l4_hdr_start;
         if ((void *)udp + sizeof(*udp) <= data_end) {
-            id->src_port = __bpf_ntohs(udp->source);
-            id->dst_port = __bpf_ntohs(udp->dest);
+            l4_info->src_port = __bpf_ntohs(udp->source);
+            l4_info->dst_port = __bpf_ntohs(udp->dest);
+        }
+    } break;
+    case IPPROTO_SCTP: {
+        struct sctphdr *sctph = l4_hdr_start;
+        if ((void *)sctph + sizeof(*sctph) <= data_end) {
+            l4_info->src_port = __bpf_ntohs(sctph->source);
+            l4_info->dst_port = __bpf_ntohs(sctph->dest);
+        }
+    } break;
+    case IPPROTO_ICMP: {
+        struct icmphdr *icmph = l4_hdr_start;
+        if ((void *)icmph + sizeof(*icmph) <= data_end) {
+            l4_info->icmp_type = icmph->type;
+            l4_info->icmp_code = icmph->code;
+        }
+    } break;
+    case IPPROTO_ICMPV6: {
+        struct icmp6hdr *icmp6h = l4_hdr_start;
+         if ((void *)icmp6h + sizeof(*icmp6h) <= data_end) {
+            l4_info->icmp_type = icmp6h->icmp6_type;
+            l4_info->icmp_code = icmp6h->icmp6_code;
         }
     } break;
     default:
         break;
     }
+}
+
+// sets flow fields from IPv4 header information
+static inline int fill_iphdr(struct iphdr *ip, void *data_end, flow_id *id, u16 *flags) {
+    struct l4_info_t l4_info;
+    void *l4_hdr_start;
+
+    l4_hdr_start = (void *)ip + sizeof(*ip);
+    if (l4_hdr_start > data_end) {
+        return DISCARD;
+    }
+    __builtin_memset(&l4_info, 0, sizeof(l4_info));
+    __builtin_memcpy(id->src_ip, ip4in6, sizeof(ip4in6));
+    __builtin_memcpy(id->dst_ip, ip4in6, sizeof(ip4in6));
+    __builtin_memcpy(id->src_ip + sizeof(ip4in6), &ip->saddr, sizeof(ip->saddr));
+    __builtin_memcpy(id->dst_ip + sizeof(ip4in6), &ip->daddr, sizeof(ip->daddr));
+    id->transport_protocol = ip->protocol;
+    fill_l4info(l4_hdr_start, data_end, ip->protocol, &l4_info);
+    id->src_port = l4_info.src_port;
+    id->dst_port = l4_info.dst_port;
+    id->icmp_type = l4_info.icmp_type;
+    id->icmp_code = l4_info.icmp_code;
+    *flags = l4_info.flags;
+
     return SUBMIT;
 }
 
 // sets flow fields from IPv6 header information
 static inline int fill_ip6hdr(struct ipv6hdr *ip, void *data_end, flow_id *id, u16 *flags) {
-    if ((void *)ip + sizeof(*ip) > data_end) {
+    struct l4_info_t l4_info;
+    void *l4_hdr_start;
+
+    l4_hdr_start = (void *)ip + sizeof(*ip);
+    if (l4_hdr_start > data_end) {
         return DISCARD;
     }
-
+    __builtin_memset(&l4_info, 0, sizeof(l4_info));
     __builtin_memcpy(id->src_ip, ip->saddr.in6_u.u6_addr8, 16);
     __builtin_memcpy(id->dst_ip, ip->daddr.in6_u.u6_addr8, 16);
     id->transport_protocol = ip->nexthdr;
-    id->src_port = 0;
-    id->dst_port = 0;
-    switch (ip->nexthdr) {
-    case IPPROTO_TCP: {
-        struct tcphdr *tcp = (void *)ip + sizeof(*ip);
-        if ((void *)tcp + sizeof(*tcp) <= data_end) {
-            id->src_port = __bpf_ntohs(tcp->source);
-            id->dst_port = __bpf_ntohs(tcp->dest);
-            set_flags(tcp, flags);
-        }
-    } break;
-    case IPPROTO_UDP: {
-        struct udphdr *udp = (void *)ip + sizeof(*ip);
-        if ((void *)udp + sizeof(*udp) <= data_end) {
-            id->src_port = __bpf_ntohs(udp->source);
-            id->dst_port = __bpf_ntohs(udp->dest);
-        }
-    } break;
-    default:
-        break;
-    }
+    fill_l4info(l4_hdr_start, data_end, ip->nexthdr, &l4_info);
+    id->src_port = l4_info.src_port;
+    id->dst_port = l4_info.dst_port;
+    id->icmp_type = l4_info.icmp_type;
+    id->icmp_code = l4_info.icmp_code;
+    *flags = l4_info.flags;
+
     return SUBMIT;
 }
 // sets flow fields from Ethernet header information
@@ -207,6 +255,7 @@ static inline int flow_monitor(struct __sk_buff *skb, u8 direction) {
     void *data = (void *)(long)skb->data;
 
     flow_id id;
+    __builtin_memset(&id, 0, sizeof(id));
     u64 current_time = bpf_ktime_get_ns();
     struct ethhdr *eth = data;
     u16 flags = 0;
diff --git a/examples/flowlogs-dump/server/flowlogs-dump-collector.go b/examples/flowlogs-dump/server/flowlogs-dump-collector.go
index 3d2c8ab9..2728e0b3 100644
--- a/examples/flowlogs-dump/server/flowlogs-dump-collector.go
+++ b/examples/flowlogs-dump/server/flowlogs-dump-collector.go
@@ -72,7 +72,7 @@ func main() {
 	for records := range receivedRecords {
 		for _, record := range records.Entries {
 			if record.EthProtocol == ipv6 {
-				log.Printf("%s: %v %s IP %s:%d > %s:%d: protocol:%s dir:%d bytes:%d packets:%d flags:%d ends: %v\n",
+				log.Printf("%s: %v %s IP %s:%d > %s:%d: protocol:%s type: %d code: %d dir:%d bytes:%d packets:%d flags:%d ends: %v\n",
 					ipProto[record.EthProtocol],
 					record.TimeFlowStart.AsTime().Local().Format("15:04:05.000000"),
 					record.Interface,
@@ -81,6 +81,8 @@ func main() {
 					net.IP(record.Network.GetDstAddr().GetIpv6()).To16(),
 					record.Transport.DstPort,
 					protocolByNumber[record.Transport.Protocol],
+					record.Icmp.IcmpType,
+					record.Icmp.IcmpCode,
 					record.Direction,
 					record.Bytes,
 					record.Packets,
@@ -88,7 +90,7 @@ func main() {
 					record.TimeFlowEnd.AsTime().Local().Format("15:04:05.000000"),
 				)
 			} else {
-				log.Printf("%s: %v %s IP %s:%d > %s:%d: protocol:%s dir:%d bytes:%d packets:%d flags:%d ends: %v\n",
+				log.Printf("%s: %v %s IP %s:%d > %s:%d: protocol:%s type: %d code: %d dir:%d bytes:%d packets:%d flags:%d ends: %v\n",
 					ipProto[record.EthProtocol],
 					record.TimeFlowStart.AsTime().Local().Format("15:04:05.000000"),
 					record.Interface,
@@ -97,6 +99,8 @@ func main() {
 					ipIntToNetIP(record.Network.GetDstAddr().GetIpv4()).String(),
 					record.Transport.DstPort,
 					protocolByNumber[record.Transport.Protocol],
+					record.Icmp.IcmpType,
+					record.Icmp.IcmpCode,
 					record.Direction,
 					record.Bytes,
 					record.Packets,
diff --git a/pkg/ebpf/bpf_bpfeb.go b/pkg/ebpf/bpf_bpfeb.go
index c228fd42..4eb412d7 100644
--- a/pkg/ebpf/bpf_bpfeb.go
+++ b/pkg/ebpf/bpf_bpfeb.go
@@ -25,6 +25,8 @@ type BpfFlowIdT struct {
 	SrcPort           uint16
 	DstPort           uint16
 	TransportProtocol uint8
+	IcmpType          uint8
+	IcmpCode          uint8
 	IfIndex           uint32
 }
 
diff --git a/pkg/ebpf/bpf_bpfeb.o b/pkg/ebpf/bpf_bpfeb.o
index a758051fbf61c1587128bf00c17fba33dfdc0aac..047164e040edc9b3b37d4a583f3a9867b1da021b 100644
GIT binary patch
literal 22872
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

delta 8764
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
E01rB2KmY&$

diff --git a/pkg/ebpf/bpf_bpfel.go b/pkg/ebpf/bpf_bpfel.go
index 6ada0f67..4de7b8d2 100644
--- a/pkg/ebpf/bpf_bpfel.go
+++ b/pkg/ebpf/bpf_bpfel.go
@@ -25,6 +25,8 @@ type BpfFlowIdT struct {
 	SrcPort           uint16
 	DstPort           uint16
 	TransportProtocol uint8
+	IcmpType          uint8
+	IcmpCode          uint8
 	IfIndex           uint32
 }
 
diff --git a/pkg/ebpf/bpf_bpfel.o b/pkg/ebpf/bpf_bpfel.o
index 13db62889bf77cb980cc4979750485a58c059877..b33fe54eccf9a06693134fa5d8915af4b92e6a24 100644
GIT binary patch
literal 22040
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

literal 21704
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
HSg-;BJRxVw

diff --git a/pkg/ebpf/tracer.go b/pkg/ebpf/tracer.go
index e93fec38..32b2641b 100644
--- a/pkg/ebpf/tracer.go
+++ b/pkg/ebpf/tracer.go
@@ -6,6 +6,7 @@ import (
 	"io/fs"
 	"strings"
 
+	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/ringbuf"
 	"github.com/cilium/ebpf/rlimit"
 	"github.com/netobserv/netobserv-ebpf-agent/pkg/ifaces"
@@ -72,6 +73,12 @@ func NewFlowFetcher(
 		return nil, fmt.Errorf("rewriting BPF constants definition: %w", err)
 	}
 	if err := spec.LoadAndAssign(&objects, nil); err != nil {
+		var ve *ebpf.VerifierError
+		if errors.As(err, &ve) {
+			// Using %+v will print the whole verifier error, not just the last
+			// few lines.
+			log.Infof("Verifier error: %+v", ve)
+		}
 		return nil, fmt.Errorf("loading and assigning BPF objects: %w", err)
 	}
 
diff --git a/pkg/exporter/ipfix.go b/pkg/exporter/ipfix.go
index 8b88ab20..67f18adc 100644
--- a/pkg/exporter/ipfix.go
+++ b/pkg/exporter/ipfix.go
@@ -122,6 +122,14 @@ func SendTemplateRecordv4(log *logrus.Entry, exporter *ipfixExporter.ExportingPr
 	if err != nil {
 		return 0, nil, err
 	}
+	err = addElementToTemplate(log, "icmpTypeIPv4", nil, &elements)
+	if err != nil {
+		return 0, nil, err
+	}
+	err = addElementToTemplate(log, "icmpCodeIPv4", nil, &elements)
+	if err != nil {
+		return 0, nil, err
+	}
 	err = AddRecordValuesToTemplate(log, &elements)
 	if err != nil {
 		return 0, nil, err
@@ -183,6 +191,14 @@ func SendTemplateRecordv6(log *logrus.Entry, exporter *ipfixExporter.ExportingPr
 	if err != nil {
 		return 0, nil, err
 	}
+	err = addElementToTemplate(log, "icmpTypeIPv6", nil, &elements)
+	if err != nil {
+		return 0, nil, err
+	}
+	err = addElementToTemplate(log, "icmpCodeIPv6", nil, &elements)
+	if err != nil {
+		return 0, nil, err
+	}
 	err = AddRecordValuesToTemplate(log, &elements)
 	if err != nil {
 		return 0, nil, err
@@ -299,6 +315,10 @@ func setIEValue(record *flow.Record, ieValPtr *entities.InfoElementWithValue) {
 		ieVal.SetUnsigned16Value(record.Id.SrcPort)
 	case "destinationTransportPort":
 		ieVal.SetUnsigned16Value(record.Id.DstPort)
+	case "icmpTypeIPv4", "icmpTypeIPv6":
+		ieVal.SetUnsigned8Value(record.Id.IcmpType)
+	case "icmpCodeIPv4", "icmpCodeIPv6":
+		ieVal.SetUnsigned8Value(record.Id.IcmpCode)
 	}
 }
 func setEntities(record *flow.Record, elements *[]entities.InfoElementWithValue) {
diff --git a/pkg/exporter/kafka_proto_test.go b/pkg/exporter/kafka_proto_test.go
index 5d7f94a6..884bd73c 100644
--- a/pkg/exporter/kafka_proto_test.go
+++ b/pkg/exporter/kafka_proto_test.go
@@ -34,6 +34,7 @@ func TestProtoConversion(t *testing.T) {
 	record.Id.DstIp = IPAddrFromNetIP(net.ParseIP("127.3.2.1"))
 	record.Id.SrcPort = 4321
 	record.Id.DstPort = 1234
+	record.Id.IcmpType = 8
 	record.Id.TransportProtocol = 210
 	record.TimeFlowStart = time.Now().Add(-5 * time.Second)
 	record.TimeFlowEnd = time.Now()
@@ -58,6 +59,7 @@ func TestProtoConversion(t *testing.T) {
 	assert.EqualValues(t, 4321, r.Transport.SrcPort)
 	assert.EqualValues(t, 1234, r.Transport.DstPort)
 	assert.EqualValues(t, 210, r.Transport.Protocol)
+	assert.EqualValues(t, 8, r.Icmp.IcmpType)
 	assert.Equal(t, record.TimeFlowStart.UnixMilli(), r.TimeFlowStart.AsTime().UnixMilli())
 	assert.Equal(t, record.TimeFlowEnd.UnixMilli(), r.TimeFlowEnd.AsTime().UnixMilli())
 	assert.EqualValues(t, 789, r.Bytes)
diff --git a/pkg/exporter/proto.go b/pkg/exporter/proto.go
index 991863b8..94f9bcea 100644
--- a/pkg/exporter/proto.go
+++ b/pkg/exporter/proto.go
@@ -54,6 +54,10 @@ func v4FlowToPB(fr *flow.Record) *pbflow.Record {
 			SrcPort:  uint32(fr.Id.SrcPort),
 			DstPort:  uint32(fr.Id.DstPort),
 		},
+		Icmp: &pbflow.Icmp{
+			IcmpType: uint32(fr.Id.IcmpType),
+			IcmpCode: uint32(fr.Id.IcmpCode),
+		},
 		Bytes: fr.Metrics.Bytes,
 		TimeFlowStart: &timestamppb.Timestamp{
 			Seconds: fr.TimeFlowStart.Unix(),
@@ -88,6 +92,10 @@ func v6FlowToPB(fr *flow.Record) *pbflow.Record {
 			SrcPort:  uint32(fr.Id.SrcPort),
 			DstPort:  uint32(fr.Id.DstPort),
 		},
+		Icmp: &pbflow.Icmp{
+			IcmpType: uint32(fr.Id.IcmpType),
+			IcmpCode: uint32(fr.Id.IcmpCode),
+		},
 		Bytes: fr.Metrics.Bytes,
 		TimeFlowStart: &timestamppb.Timestamp{
 			Seconds: fr.TimeFlowStart.Unix(),
diff --git a/pkg/flow/record_test.go b/pkg/flow/record_test.go
index dfbf8bf1..8f1fc59b 100644
--- a/pkg/flow/record_test.go
+++ b/pkg/flow/record_test.go
@@ -23,6 +23,8 @@ func TestRecordBinaryEncoding(t *testing.T) {
 		0x0e, 0x0f, // transport: u16 src_port
 		0x10, 0x11, // transport: u16 dst_port
 		0x12,                   // transport: u8 transport_protocol
+		0x00,                   // icmp: u8 icmp_type
+		0x00,                   // icmp: u8 icmp_code
 		0x13, 0x14, 0x15, 0x16, // interface index
 		0x06, 0x07, 0x08, 0x09, // u32 packets
 		0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, // u64 bytes
@@ -45,6 +47,8 @@ func TestRecordBinaryEncoding(t *testing.T) {
 			SrcPort:           0x0f0e,
 			DstPort:           0x1110,
 			TransportProtocol: 0x12,
+			IcmpType:          0x00,
+			IcmpCode:          0x00,
 			IfIndex:           0x16151413,
 		},
 		Metrics: ebpf.BpfFlowMetrics{
diff --git a/pkg/grpc/grpc_test.go b/pkg/grpc/grpc_test.go
index faeb740e..efdd355b 100644
--- a/pkg/grpc/grpc_test.go
+++ b/pkg/grpc/grpc_test.go
@@ -162,6 +162,10 @@ func BenchmarkIPv4GRPCCommunication(b *testing.B) {
 			SrcPort:  23000,
 			DstPort:  443,
 		},
+		Icmp: &pbflow.Icmp{
+			IcmpType: 8,
+			IcmpCode: 10,
+		},
 	}
 	records := &pbflow.Records{}
 	for i := 0; i < 100; i++ {
@@ -211,6 +215,10 @@ func BenchmarkIPv6GRPCCommunication(b *testing.B) {
 			SrcPort:  23000,
 			DstPort:  443,
 		},
+		Icmp: &pbflow.Icmp{
+			IcmpType: 8,
+			IcmpCode: 10,
+		},
 	}
 	records := &pbflow.Records{}
 	for i := 0; i < 100; i++ {
diff --git a/pkg/pbflow/flow.pb.go b/pkg/pbflow/flow.pb.go
index 903e6f68..af4bee08 100644
--- a/pkg/pbflow/flow.pb.go
+++ b/pkg/pbflow/flow.pb.go
@@ -179,6 +179,7 @@ type Record struct {
 	// Agent IP address to help identifying the source of the flow
 	AgentIp *IP    `protobuf:"bytes,12,opt,name=agent_ip,json=agentIp,proto3" json:"agent_ip,omitempty"`
 	Flags   uint32 `protobuf:"varint,13,opt,name=flags,proto3" json:"flags,omitempty"`
+	Icmp    *Icmp  `protobuf:"bytes,14,opt,name=icmp,proto3" json:"icmp,omitempty"`
 }
 
 func (x *Record) Reset() {
@@ -304,6 +305,13 @@ func (x *Record) GetFlags() uint32 {
 	return 0
 }
 
+func (x *Record) GetIcmp() *Icmp {
+	if x != nil {
+		return x.Icmp
+	}
+	return nil
+}
+
 type DataLink struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -559,6 +567,61 @@ func (x *Transport) GetProtocol() uint32 {
 	return 0
 }
 
+type Icmp struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	IcmpType uint32 `protobuf:"varint,1,opt,name=icmp_type,json=icmpType,proto3" json:"icmp_type,omitempty"`
+	IcmpCode uint32 `protobuf:"varint,2,opt,name=icmp_code,json=icmpCode,proto3" json:"icmp_code,omitempty"`
+}
+
+func (x *Icmp) Reset() {
+	*x = Icmp{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_flow_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Icmp) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Icmp) ProtoMessage() {}
+
+func (x *Icmp) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_flow_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Icmp.ProtoReflect.Descriptor instead.
+func (*Icmp) Descriptor() ([]byte, []int) {
+	return file_proto_flow_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *Icmp) GetIcmpType() uint32 {
+	if x != nil {
+		return x.IcmpType
+	}
+	return 0
+}
+
+func (x *Icmp) GetIcmpCode() uint32 {
+	if x != nil {
+		return x.IcmpCode
+	}
+	return 0
+}
+
 var File_proto_flow_proto protoreflect.FileDescriptor
 
 var file_proto_flow_proto_rawDesc = []byte{
@@ -570,7 +633,7 @@ var file_proto_flow_proto_rawDesc = []byte{
 	0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72,
 	0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x66, 0x6c,
 	0x6f, 0x77, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69,
-	0x65, 0x73, 0x22, 0x94, 0x04, 0x0a, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a,
+	0x65, 0x73, 0x22, 0xb6, 0x04, 0x0a, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a,
 	0x0c, 0x65, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20,
 	0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
 	0x12, 0x2f, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
@@ -603,34 +666,40 @@ var file_proto_flow_proto_rawDesc = []byte{
 	0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a,
 	0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x50, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e,
 	0x74, 0x49, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x01,
-	0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3c, 0x0a, 0x08, 0x44, 0x61, 0x74,
-	0x61, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x5f, 0x6d, 0x61, 0x63,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x72, 0x63, 0x4d, 0x61, 0x63, 0x12, 0x17,
-	0x0a, 0x07, 0x64, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
-	0x06, 0x64, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x22, 0x57, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f,
-	0x72, 0x6b, 0x12, 0x25, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x50,
-	0x52, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x08, 0x64, 0x73, 0x74,
-	0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62,
-	0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x50, 0x52, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72,
-	0x22, 0x3d, 0x0a, 0x02, 0x49, 0x50, 0x12, 0x14, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x07, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x14, 0x0a, 0x04,
-	0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70,
-	0x76, 0x36, 0x42, 0x0b, 0x0a, 0x09, 0x69, 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22,
-	0x5d, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08,
-	0x73, 0x72, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
-	0x73, 0x72, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x70,
-	0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, 0x73, 0x74, 0x50, 0x6f,
-	0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2a, 0x24,
-	0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x49,
-	0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x47, 0x52, 0x45,
-	0x53, 0x53, 0x10, 0x01, 0x32, 0x3e, 0x0a, 0x09, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f,
-	0x72, 0x12, 0x31, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x0f, 0x2e, 0x70, 0x62, 0x66, 0x6c,
-	0x6f, 0x77, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x66,
-	0x6c, 0x6f, 0x77, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70,
-	0x6c, 0x79, 0x22, 0x00, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77,
-	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x04, 0x69, 0x63, 0x6d,
+	0x70, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77,
+	0x2e, 0x49, 0x63, 0x6d, 0x70, 0x52, 0x04, 0x69, 0x63, 0x6d, 0x70, 0x22, 0x3c, 0x0a, 0x08, 0x44,
+	0x61, 0x74, 0x61, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x5f, 0x6d,
+	0x61, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x72, 0x63, 0x4d, 0x61, 0x63,
+	0x12, 0x17, 0x0a, 0x07, 0x64, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x06, 0x64, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x22, 0x57, 0x0a, 0x07, 0x4e, 0x65, 0x74,
+	0x77, 0x6f, 0x72, 0x6b, 0x12, 0x25, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e,
+	0x49, 0x50, 0x52, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x08, 0x64,
+	0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e,
+	0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x50, 0x52, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64,
+	0x64, 0x72, 0x22, 0x3d, 0x0a, 0x02, 0x49, 0x50, 0x12, 0x14, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x07, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x14,
+	0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04,
+	0x69, 0x70, 0x76, 0x36, 0x42, 0x0b, 0x0a, 0x09, 0x69, 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c,
+	0x79, 0x22, 0x5d, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x19,
+	0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
+	0x52, 0x07, 0x73, 0x72, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74,
+	0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, 0x73, 0x74,
+	0x50, 0x6f, 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
+	0x22, 0x40, 0x0a, 0x04, 0x49, 0x63, 0x6d, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70,
+	0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d,
+	0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63, 0x6f,
+	0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43, 0x6f,
+	0x64, 0x65, 0x2a, 0x24, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+	0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
+	0x45, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01, 0x32, 0x3e, 0x0a, 0x09, 0x43, 0x6f, 0x6c, 0x6c,
+	0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x0f, 0x2e,
+	0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x16,
+	0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f,
+	0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x62,
+	0x66, 0x6c, 0x6f, 0x77, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -646,7 +715,7 @@ func file_proto_flow_proto_rawDescGZIP() []byte {
 }
 
 var file_proto_flow_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_proto_flow_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_proto_flow_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
 var file_proto_flow_proto_goTypes = []interface{}{
 	(Direction)(0),                // 0: pbflow.Direction
 	(*CollectorReply)(nil),        // 1: pbflow.CollectorReply
@@ -656,26 +725,28 @@ var file_proto_flow_proto_goTypes = []interface{}{
 	(*Network)(nil),               // 5: pbflow.Network
 	(*IP)(nil),                    // 6: pbflow.IP
 	(*Transport)(nil),             // 7: pbflow.Transport
-	(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
+	(*Icmp)(nil),                  // 8: pbflow.Icmp
+	(*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
 }
 var file_proto_flow_proto_depIdxs = []int32{
 	3,  // 0: pbflow.Records.entries:type_name -> pbflow.Record
 	0,  // 1: pbflow.Record.direction:type_name -> pbflow.Direction
-	8,  // 2: pbflow.Record.time_flow_start:type_name -> google.protobuf.Timestamp
-	8,  // 3: pbflow.Record.time_flow_end:type_name -> google.protobuf.Timestamp
+	9,  // 2: pbflow.Record.time_flow_start:type_name -> google.protobuf.Timestamp
+	9,  // 3: pbflow.Record.time_flow_end:type_name -> google.protobuf.Timestamp
 	4,  // 4: pbflow.Record.data_link:type_name -> pbflow.DataLink
 	5,  // 5: pbflow.Record.network:type_name -> pbflow.Network
 	7,  // 6: pbflow.Record.transport:type_name -> pbflow.Transport
 	6,  // 7: pbflow.Record.agent_ip:type_name -> pbflow.IP
-	6,  // 8: pbflow.Network.src_addr:type_name -> pbflow.IP
-	6,  // 9: pbflow.Network.dst_addr:type_name -> pbflow.IP
-	2,  // 10: pbflow.Collector.Send:input_type -> pbflow.Records
-	1,  // 11: pbflow.Collector.Send:output_type -> pbflow.CollectorReply
-	11, // [11:12] is the sub-list for method output_type
-	10, // [10:11] is the sub-list for method input_type
-	10, // [10:10] is the sub-list for extension type_name
-	10, // [10:10] is the sub-list for extension extendee
-	0,  // [0:10] is the sub-list for field type_name
+	8,  // 8: pbflow.Record.icmp:type_name -> pbflow.Icmp
+	6,  // 9: pbflow.Network.src_addr:type_name -> pbflow.IP
+	6,  // 10: pbflow.Network.dst_addr:type_name -> pbflow.IP
+	2,  // 11: pbflow.Collector.Send:input_type -> pbflow.Records
+	1,  // 12: pbflow.Collector.Send:output_type -> pbflow.CollectorReply
+	12, // [12:13] is the sub-list for method output_type
+	11, // [11:12] is the sub-list for method input_type
+	11, // [11:11] is the sub-list for extension type_name
+	11, // [11:11] is the sub-list for extension extendee
+	0,  // [0:11] is the sub-list for field type_name
 }
 
 func init() { file_proto_flow_proto_init() }
@@ -768,6 +839,18 @@ func file_proto_flow_proto_init() {
 				return nil
 			}
 		}
+		file_proto_flow_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Icmp); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	file_proto_flow_proto_msgTypes[5].OneofWrappers = []interface{}{
 		(*IP_Ipv4)(nil),
@@ -779,7 +862,7 @@ func file_proto_flow_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_proto_flow_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   7,
+			NumMessages:   8,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/proto/flow.proto b/proto/flow.proto
index 9e291f72..c7b459a1 100644
--- a/proto/flow.proto
+++ b/proto/flow.proto
@@ -41,6 +41,7 @@ message Record {
   // Agent IP address to help identifying the source of the flow
   IP agent_ip = 12;
   uint32 flags = 13;
+  Icmp   icmp = 14;
 }
 
 message DataLink {
@@ -68,6 +69,11 @@ message Transport {
   uint32 protocol = 3;
 }
 
+message Icmp {
+  uint32 icmp_type = 1;
+  uint32 icmp_code = 2;
+}
+
 // as defined by field 61 in
 // https://www.iana.org/assignments/ipfix/ipfix.xhtml
 enum Direction {
-- 
GitLab