diff --git a/Makefile b/Makefile index add2e715..a893ec39 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ endif GOLANGCI_LINT_VERSION = v1.61.0 BASH_VERSION = v4.2.0 -YQ_VERSION = v4.43.1 +YQ_VERSION = v4.45.1 # build a single arch target provided as argument define build_target diff --git a/README.md b/README.md index c1c68667..92720f31 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ This will write [pcapng](https://wiki.wireshark.org/Development/PcapNg) into a s Run the following command to start capturing metrics, replacing `USER`, `VERSION` and `COMMAND_ARGS` accordingly: ```bash -USER=netobserv VERSION=dev COMMAND_ARGS='--enable_pktdrop="true" --enable_dns="true" --enable_rtt="true"' make metrics +USER=netobserv VERSION=dev COMMAND_ARGS='--enable_pkt_drop --enable_dns --enable_rtt' make metrics ``` ![metrics](./img/metrics-dashboard.png) diff --git a/cmd/config.yaml b/cmd/config.yaml index c344419c..b9339b57 100644 --- a/cmd/config.yaml +++ b/cmd/config.yaml @@ -169,6 +169,14 @@ columns: default: false width: 10 feature: subnetLabels + - id: SrcNetworkName + group: Source + name: Network Name + tooltip: Network name, such as Secondary network or UDN. + field: SrcK8S_NetworkName + filter: src_network + default: false + width: 15 - id: DstK8S_Name group: Destination name: Name @@ -301,6 +309,14 @@ columns: default: false width: 10 feature: subnetLabels + - id: DstNetworkName + group: Destination + name: Network Name + tooltip: Network name, such as Secondary network or UDN. + field: DstK8S_NetworkName + filter: dst_network + default: false + width: 15 - id: K8S_Name name: Names calculated: '[SrcK8S_Name,DstK8S_Name]' @@ -346,6 +362,11 @@ columns: calculated: '[SrcK8S_HostIP,DstK8S_HostIP]' default: false width: 10 + - id: Sampling + name: Sampling + field: Sampling + default: false + width: 10 - id: K8S_HostName name: Node Name calculated: '[SrcK8S_HostName,DstK8S_HostName]' @@ -432,6 +453,14 @@ columns: filter: ifdirections default: false width: 10 + - id: Udns + name: UDN labels + tooltip: The list of User Defined Networks. + field: Udns + filter: udns + default: true + width: 15 + feature: udnMapping - id: FlowDirInts name: Interfaces and Directions tooltip: Pairs of network interface and direction of the Flow observed at the network interface observation point. @@ -563,6 +592,71 @@ columns: default: true width: 15 feature: networkEvents + - id: XlatZoneId + group: Xlat + name: Xlat zone id + field: ZoneId + filter: xlat_zone_id + default: true + width: 5 + feature: packetTranslation + - id: XlatSrcAddr + group: Xlat + name: Xlat source IP + tooltip: The packet translation source IP address. Can be either in IPv4 or IPv6 format. + field: XlatSrcAddr + filter: xlat_src_address + default: false + width: 10 + feature: packetTranslation + - id: XlatSrcPort + group: Xlat + name: Xlat source port + tooltip: The packet translation source port number. + field: XlatSrcPort + filter: xlat_src_port + default: false + width: 10 + feature: packetTranslation + - id: XlatSrcK8S_Object + group: Xlat + name: Xlat Src Kubernetes Object + calculated: kubeObject(XlatSrcK8S_Type,XlatSrcK8S_Namespace,XlatSrcK8S_Name,1) or concat(XlatSrcAddr,':',XlatSrcPort) + default: true + width: 15 + feature: packetTranslation + - id: XlatDstAddr + group: Xlat + name: Xlat destination IP + tooltip: The packet translation destination IP address. Can be either in IPv4 or IPv6 format. + field: XlatDstAddr + filter: xlat_dst_address + default: false + width: 10 + feature: packetTranslation + - id: XlatDstPort + group: Xlat + name: Xlat destination port + tooltip: The packet translation destination port number. + field: XlatDstPort + filter: xlat_dst_port + default: false + width: 10 + feature: packetTranslation + - id: XlatDstK8S_Object + group: Xlat + name: Xlat Dst Kubernetes Object + calculated: kubeObject(XlatDstK8S_Type,XlatDstK8S_Namespace,XlatDstK8S_Name,1) or concat(XlatDstAddr,':',XlatDstPort) + default: true + width: 15 + feature: packetTranslation + - id: XlatK8S_Object + group: Xlat + name: Xlat Kubernetes Objects + calculated: '[column.XlatSrcK8S_Object,column.XlatDstK8S_Object]' + default: false + width: 15 + feature: packetTranslation filters: - id: cluster_name name: Cluster @@ -822,6 +916,14 @@ filters: - Starting text like cluster, "cluster-*" - Ending text like "*-registry" - Pattern like "cluster-*-registry", "c*-*-r*y", -i*e- + - id: src_network + name: Network Name + component: text + category: source + - id: dst_network + name: Network Name + component: text + category: destination - id: protocol name: Protocol component: autocomplete @@ -874,6 +976,10 @@ filters: component: autocomplete placeholder: 'E.g: Ingress, Egress' hint: Specify the direction of the Flow observed at the network interface observation point. + - id: udns + name: User Defined Network + component: autocomplete + hint: Specify a user defined network name. - id: id name: Conversation Id component: text @@ -928,6 +1034,29 @@ filters: name: Network Events component: text hint: Specify a single network event. + - id: xlat_zone_id + name: Xlat Zone Id + component: number + - id: xlat_src_address + name: Xlat src address + component: text + category: source + hint: Specify a single IP or range. + - id: xlat_dst_address + name: Xlat dst address + component: text + category: destination + hint: Specify a single IP or range. + - id: xlat_src_port + name: Xlat src port + component: autocomplete + category: source + hint: Specify a single port number or name. + - id: xlat_dst_port + name: Xlat dst port + component: autocomplete + category: destination + hint: Specify a single port number or name. scopes: - id: cluster name: Cluster @@ -938,6 +1067,18 @@ scopes: feature: multiCluster filter: cluster_name stepInto: zone + - id: network + name: Network + shortName: Net + description: Network name, such as User Defined Network + labels: + - SrcK8S_NetworkName + - DstK8S_NetworkName + feature: udnMapping + filters: + - src_network + - dst_network + stepInto: namespace - id: zone name: Zone shortName: AZ @@ -956,13 +1097,16 @@ scopes: name: Node shortName: Node description: Node on which the resources are running - labels: + labels: - SrcK8S_HostName - DstK8S_HostName groups: - - clusters + - networks - zones + - clusters - clusters+zones + - clusters+networks + - networks+zones filters: - src_host_name - dst_host_name @@ -971,7 +1115,7 @@ scopes: name: Namespace shortName: NS description: Resource namespace - labels: + labels: - SrcK8S_Namespace - DstK8S_Namespace groups: @@ -981,6 +1125,9 @@ scopes: - zones - zones+hosts - hosts + - networks + - networks+zones + - networks+hosts filters: - src_namespace - dst_namespace @@ -989,7 +1136,7 @@ scopes: name: Owner shortName: Own description: Controller owner, such as a Deployment - labels: + labels: - SrcK8S_OwnerName - SrcK8S_OwnerType - DstK8S_OwnerName @@ -1007,6 +1154,10 @@ scopes: - hosts - hosts+namespaces - namespaces + - networks + - networks+zones + - networks+hosts + - networks+namespaces filters: - src_owner_name - dst_owner_name @@ -1015,7 +1166,7 @@ scopes: name: Resource shortName: Res description: Base resource, such as a Pod, a Service or a Node - labels: + labels: - SrcK8S_Name - SrcK8S_Type - SrcK8S_OwnerName @@ -1046,6 +1197,11 @@ scopes: - namespaces - namespaces+owners - owners + - networks + - networks+zones + - networks+hosts + - networks+namespaces + - networks+owners filters: - src_resource - dst_resource @@ -1077,6 +1233,7 @@ fields: description: Source namespace - name: SrcAddr type: string + format: IP description: Source IP address (ipv4 or ipv6) - name: SrcPort type: number @@ -1086,6 +1243,7 @@ fields: description: Source MAC address - name: SrcK8S_HostIP type: string + format: IP description: Source node IP - name: SrcK8S_HostName type: string @@ -1096,6 +1254,9 @@ fields: - name: SrcSubnetLabel type: string description: Source subnet label + - name: SrcK8S_NetworkName + type: string + description: Source network name - name: DstK8S_Name type: string description: Name of the destination Kubernetes object, such as Pod name, Service name or Node name. @@ -1113,6 +1274,7 @@ fields: description: Destination namespace - name: DstAddr type: string + format: IP description: Destination IP address (ipv4 or ipv6) - name: DstPort type: number @@ -1122,6 +1284,7 @@ fields: description: Destination MAC address - name: DstK8S_HostIP type: string + format: IP description: Destination node IP - name: DstK8S_HostName type: string @@ -1132,6 +1295,9 @@ fields: - name: DstSubnetLabel type: string description: Destination subnet label + - name: DstK8S_NetworkName + type: string + description: Destination network name - name: K8S_FlowLayer type: string description: "Flow layer: 'app' or 'infra'" @@ -1141,6 +1307,9 @@ fields: - name: Dscp type: number description: Differentiated Services Code Point (DSCP) value + - name: Sampling + type: number + description: Sampling rate used for this flow - name: IcmpType type: number description: ICMP type @@ -1158,21 +1327,24 @@ fields: - 1: Egress (outgoing traffic, from the node observation point) + - 2: Inner (with the same source and destination node) - name: IfDirections - type: number + type: number[] description: | Flow directions from the network interface observation point. Can be one of: + - 0: Ingress (interface incoming traffic) + - 1: Egress (interface outgoing traffic) - name: Interfaces - type: string + type: string[] description: Network interfaces + - name: Udns + type: string[] + description: List of User Defined Networks - name: Flags - type: string + type: string[] description: | - Logical OR combination of unique TCP flags comprised in the flow, as per RFC-9293, with additional custom flags to represent the following per-packet combinations: + - - SYN+ACK (0x100) + - - FIN+ACK (0x200) + - - RST+ACK (0x400) + List of TCP flags comprised in the flow, as per RFC-9293, with additional custom flags to represent the following per-packet combinations: + + - SYN_ACK + + - FIN_ACK + + - RST_ACK - name: Bytes type: number description: Number of bytes @@ -1215,8 +1387,31 @@ fields: type: number description: TCP Smoothed Round Trip Time (SRTT), in nanoseconds - name: NetworkEvents + type: string[] + docType: object[] + description: | + Network events, such as network policy actions, composed of nested fields: + + - Feature (such as "acl" for network policies) + + - Type (such as an "AdminNetworkPolicy") + + - Namespace (namespace where the event applies, if any) + + - Name (name of the resource that triggered the event) + + - Action (such as "allow" or "drop") + + - Direction (Ingress or Egress) + - name: ZoneId + type: number + description: packet translation zone id + - name: XlatSrcPort + type: number + description: packet translation src port + - name: XlatDstPort + type: number + description: packet translation dst port + - name: XlatSrcAddr + type: string + description: packet translation src address + - name: XlatDstAddr type: string - description: Network events flow monitoring + description: packet translation dst address - name: K8S_ClusterName type: string description: Cluster name or identifier diff --git a/cmd/flow_capture.go b/cmd/flow_capture.go index d446823b..c0f3f1a8 100644 --- a/cmd/flow_capture.go +++ b/cmd/flow_capture.go @@ -273,17 +273,24 @@ func updateTable() { ) } + // append interfaces and their directions between enrichment and features + // this is useful for pkt drop, udns etc + colIDs = append(colIDs, + "Interfaces", + "IfDirections", + ) + // standard / feature fields if display.getCurrentItem().name != standardDisplay { for _, col := range cfg.Columns { - if slices.Contains(display.getCurrentItem().ids, col.Feature) { + if col.Field != "" && slices.Contains(display.getCurrentItem().ids, col.Feature) { colIDs = append(colIDs, col.ID) } } } else { // TODO: add a new flag in the config to identify these as default feature fields colIDs = append(colIDs, - "Interfaces", + "FlowDirection", "Proto", "Dscp", "Bytes", diff --git a/cmd/flow_capture_test.go b/cmd/flow_capture_test.go index 4aeafee2..1d10b0e6 100644 --- a/cmd/flow_capture_test.go +++ b/cmd/flow_capture_test.go @@ -39,9 +39,9 @@ func TestFlowTableDefaultDisplay(t *testing.T) { rows := strings.Split(buf.String(), "\n") assert.Equal(t, 4, len(rows)) - assert.Equal(t, `End Time Src Kind Dst Kind Src Name Dst Name Src Namespace Dst Namespace Interfaces L3 Layer Protocol L3 Layer DSCP Bytes Packets `, rows[0]) - assert.Equal(t, `17:25:28.703000 Pod Pod src-pod dst-pod first-namespace second-namespace f18b970c2ce8fdd TCP Standard 456B 5 `, rows[1]) - assert.Equal(t, `--------------- ---------- ---------- --------------- --------------- --------------- --------------- ---------- ---------- ---------- ----- ----- `, rows[2]) + assert.Equal(t, `End Time Src Kind Dst Kind Src Name Dst Name Src Namespace Dst Namespace Interfaces Interface Dirs Node Dir L3 Layer Protocol L3 Layer DSCP Bytes Packets `, rows[0]) + assert.Equal(t, `17:25:28.703000 Pod Pod src-pod dst-pod first-namespace second-namespace f18b970c2ce8fdd Egress Ingress TCP Standard 456B 5 `, rows[1]) + assert.Equal(t, `--------------- ---------- ---------- --------------- --------------- --------------- --------------- ---------- ---------- ---------- ---------- ---------- ----- ----- `, rows[2]) assert.Empty(t, rows[3]) } @@ -86,16 +86,16 @@ func TestFlowTableMultipleFlows(t *testing.T) { rows := strings.Split(buf.String(), "\n") // table must display only 38 rows (35 flows + header + footer + empty line) assert.Equal(t, 38, len(rows)) - assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces L3 Layer Protocol L3 Layer DSCP Bytes Packets `, rows[0]) + assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces Interface Dirs Node Dir L3 Layer Protocol L3 Layer DSCP Bytes Packets `, rows[0]) // first flow is the 6th one that came to the display - assert.Equal(t, `00:00:06.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a 6KB 1 `, rows[1]) - assert.Equal(t, `00:00:07.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a 7KB 1 `, rows[2]) - assert.Equal(t, `00:00:08.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a 8KB 1 `, rows[3]) - assert.Equal(t, `00:00:09.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a 9KB 1 `, rows[4]) - assert.Equal(t, `00:00:10.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a 10KB 1 `, rows[5]) + assert.Equal(t, `00:00:06.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a n/a n/a 6KB 1 `, rows[1]) + assert.Equal(t, `00:00:07.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a n/a n/a 7KB 1 `, rows[2]) + assert.Equal(t, `00:00:08.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a n/a n/a 8KB 1 `, rows[3]) + assert.Equal(t, `00:00:09.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a n/a n/a 9KB 1 `, rows[4]) + assert.Equal(t, `00:00:10.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a n/a n/a 10KB 1 `, rows[5]) // last flow is the 40th one - assert.Equal(t, `00:00:40.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a 40KB 1 `, rows[35]) - assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----- ----- `, rows[36]) + assert.Equal(t, `00:00:40.000000 10.0.0.5 n/a 10.0.0.6 n/a n/a n/a n/a n/a n/a 40KB 1 `, rows[35]) + assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----- ----- `, rows[36]) assert.Empty(t, rows[37]) } @@ -140,52 +140,52 @@ func TestFlowTableAdvancedDisplay(t *testing.T) { // set display without enrichment rows := getRows(allOptions, []string{pktDropFeature, dnsFeature, rttFeature, networkEventsDisplay}, noOptions, []string{}) assert.Equal(t, 4, len(rows)) - assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Dropped Bytes Dropped Packets Drop State Drop Cause Drop Flags DNS Id DNS Latency DNS RCode DNS Error Flow RTT Network Events `, rows[0]) - assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 32B 1 TCP_INVALID_STATE SKB_DROP_REASON_TCP_INVALID_SEQUENCE 16 31319 1ms NoError 0 10µs hello `, rows[1]) - assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ----- ----- ---------- ---------- ---------- ----- ----- ----- ----- ----- --------------- `, rows[2]) + assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces Interface Dirs Dropped Bytes Dropped Packets Drop State Drop Cause Drop Flags DNS Id DNS Latency DNS RCode DNS Error Flow RTT Network Events `, rows[0]) + assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 f18b970c2ce8fdd Egress 32B 1 TCP_INVALID_STATE SKB_DROP_REASON_TCP_INVALID_SEQUENCE 16 31319 1ms NoError 0 10µs hello `, rows[1]) + assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- ----- ----- ---------- ---------- ---------- ----- ----- ----- ----- ----- --------------- `, rows[2]) assert.Empty(t, rows[3]) // set display to standard rows = getRows(standardDisplay, []string{}, noOptions, []string{}) assert.Equal(t, 4, len(rows)) - assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces L3 Layer Protocol L3 Layer DSCP Bytes Packets `, rows[0]) - assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 f18b970c2ce8fdd TCP Standard 456B 5 `, rows[1]) - assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----- ----- `, rows[2]) + assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces Interface Dirs Node Dir L3 Layer Protocol L3 Layer DSCP Bytes Packets `, rows[0]) + assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 f18b970c2ce8fdd Egress Ingress TCP Standard 456B 5 `, rows[1]) + assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----- ----- `, rows[2]) assert.Empty(t, rows[3]) // set display to pktDrop rows = getRows("Packet drops", []string{pktDropFeature}, noOptions, []string{}) assert.Equal(t, 4, len(rows)) - assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Dropped Bytes Dropped Packets Drop State Drop Cause Drop Flags `, rows[0]) - assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 32B 1 TCP_INVALID_STATE SKB_DROP_REASON_TCP_INVALID_SEQUENCE 16 `, rows[1]) - assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ----- ----- ---------- ---------- ---------- `, rows[2]) + assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces Interface Dirs Dropped Bytes Dropped Packets Drop State Drop Cause Drop Flags `, rows[0]) + assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 f18b970c2ce8fdd Egress 32B 1 TCP_INVALID_STATE SKB_DROP_REASON_TCP_INVALID_SEQUENCE 16 `, rows[1]) + assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- ----- ----- ---------- ---------- ---------- `, rows[2]) assert.Empty(t, rows[3]) // set display to DNS rows = getRows("DNS", []string{dnsFeature}, noOptions, []string{}) assert.Equal(t, 4, len(rows)) - assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port DNS Id DNS Latency DNS RCode DNS Error `, rows[0]) - assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 31319 1ms NoError 0 `, rows[1]) - assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ----- ----- ----- ----- `, rows[2]) + assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces Interface Dirs DNS Id DNS Latency DNS RCode DNS Error `, rows[0]) + assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 f18b970c2ce8fdd Egress 31319 1ms NoError 0 `, rows[1]) + assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- ----- ----- ----- ----- `, rows[2]) assert.Empty(t, rows[3]) // set display to RTT rows = getRows("RTT", []string{rttFeature}, noOptions, []string{}) assert.Equal(t, 4, len(rows)) - assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Flow RTT `, rows[0]) - assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 10µs `, rows[1]) - assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ----- `, rows[2]) + assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces Interface Dirs Flow RTT `, rows[0]) + assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 f18b970c2ce8fdd Egress 10µs `, rows[1]) + assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- ----- `, rows[2]) assert.Empty(t, rows[3]) // set display to NetworkEvents rows = getRows("Network events", []string{networkEventsDisplay}, noOptions, []string{}) assert.Equal(t, 4, len(rows)) - assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Network Events `, rows[0]) - assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 hello `, rows[1]) - assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- --------------- `, rows[2]) + assert.Equal(t, `End Time Src IP Src Port Dst IP Dst Port Interfaces Interface Dirs Network Events `, rows[0]) + assert.Equal(t, `17:25:28.703000 10.128.0.29 1234 10.129.0.26 5678 f18b970c2ce8fdd Egress hello `, rows[1]) + assert.Equal(t, `--------------- ---------- ---------- ---------- ---------- ---------- ---------- --------------- `, rows[2]) assert.Empty(t, rows[3]) } diff --git a/cmd/map_format.go b/cmd/map_format.go index 74539c1f..22bfa545 100644 --- a/cmd/map_format.go +++ b/cmd/map_format.go @@ -45,19 +45,30 @@ func toDuration(genericMap config.GenericMap, fieldName string, factor time.Dura return emptyText } +func directionValue(v float64) string { + switch v { + case 0: + return "Ingress" + case 1: + return "Egress" + case 2: + return "Inner" + default: + return fmt.Sprintf("%v", v) + } +} + func toDirection(genericMap config.GenericMap, fieldName string) string { v, ok := genericMap[fieldName] if ok { - switch v.(float64) { - case 0: - return "Ingress" - case 1: - return "Egress" - case 2: - return "Inner" - default: - return fmt.Sprintf("%v", v) + if reflect.TypeOf(v).Kind() == reflect.Slice { + arr := make([]string, len(v.([]interface{}))) + for i, v := range v.([]interface{}) { + arr[i] = directionValue(v.(float64)) + } + return strings.Join(arr, ",") } + return directionValue(v.(float64)) } return emptyText } @@ -405,8 +416,17 @@ func toValue(genericMap config.GenericMap, fieldName string) interface{} { if ok { if reflect.TypeOf(v).Kind() == reflect.Slice { arr := make([]string, len(v.([]interface{}))) - for i, v := range v.([]interface{}) { - arr[i] = v.(string) + for i, value := range v.([]interface{}) { + switch v := value.(type) { + case string: + arr[i] = v + if arr[i] == "" { + arr[i] = "None" + } + default: + arr[i] = fmt.Sprintf("%v", v) + } + } return strings.Join(arr, ",") } @@ -476,7 +496,7 @@ func ToTableRow(genericMap config.GenericMap, colIDs []string) []interface{} { row = append(row, toTimeString(genericMap, "Time")) } // special cases where autocompletes are involved - case "FlowDirection": + case "FlowDirection", "IfDirections": row = append(row, toDirection(genericMap, fieldName)) case "Proto": row = append(row, toProto(genericMap, fieldName)) diff --git a/cmd/options.go b/cmd/options.go index 18f402d7..68e10ab0 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -21,6 +21,8 @@ var ( dnsFeature = "dnsTracking" rttFeature = "flowRTT" networkEventsDisplay = "networkEvents" + pktTranslation = "packetTranslation" + udnMapping = "udnMapping" display = option{ all: []optionItem{ // exclusive displays @@ -31,8 +33,10 @@ var ( {name: "DNS", ids: []string{dnsFeature}}, {name: "RTT", ids: []string{rttFeature}}, {name: "Network events", ids: []string{networkEventsDisplay}}, + {name: "Packet translation", ids: []string{pktTranslation}}, + {name: "UDN mapping", ids: []string{udnMapping}}, // all features display - {name: allOptions, ids: []string{pktDropFeature, dnsFeature, rttFeature, networkEventsDisplay}}, + {name: allOptions, ids: []string{pktDropFeature, dnsFeature, rttFeature, networkEventsDisplay, pktTranslation, udnMapping}}, }, // standard display by default current: 1, @@ -50,7 +54,8 @@ var ( {name: "Namespace", ids: []string{"SrcK8S_Namespace", "DstK8S_Namespace"}}, {name: "Owner", ids: []string{"SrcK8S_OwnerType", "DstK8S_OwnerType", "SrcK8S_OwnerName", "DstK8S_OwnerName", "SrcK8S_Namespace", "DstK8S_Namespace"}}, {name: "Resource", ids: []string{"SrcK8S_Type", "DstK8S_Type", "SrcK8S_Name", "DstK8S_Name", "SrcK8S_Namespace", "DstK8S_Namespace"}}, - {name: "SubnetLabel", ids: []string{"SrcSubnetLabel", "DstSubnetLabel"}}, + {name: "Subnet Label", ids: []string{"SrcSubnetLabel", "DstSubnetLabel"}}, + {name: "Network Name", ids: []string{"SrcNetworkName", "DstNetworkName"}}, // all fields {name: allOptions, ids: []string{ "ClusterName", @@ -60,6 +65,7 @@ var ( "SrcK8S_OwnerType", "DstK8S_OwnerType", "SrcK8S_OwnerName", "DstK8S_OwnerName", "SrcK8S_Type", "DstK8S_Type", "SrcK8S_Name", "DstK8S_Name", "SrcSubnetLabel", "DstSubnetLabel", + "SrcNetworkName", "DstNetworkName", }}, }, // resource enrichment by default diff --git a/commands/netobserv b/commands/netobserv index 714c05ab..84961403 100755 --- a/commands/netobserv +++ b/commands/netobserv @@ -1,8 +1,10 @@ #!/usr/bin/env bash source "./scripts/functions.sh" +source "./scripts/help.sh" source "./scripts/dependencies_check.sh" set +u + # e2e skips inputs if [ -z "${isE2E+x}" ]; then isE2E=false; fi # keep capture state @@ -34,6 +36,11 @@ fi # version to display version="0.0.1" +# dependencies +supported_archs="" +required_yq_version="v0.0.0" +required_bash_version="v0.0.0" + # command to run command="" @@ -46,9 +53,22 @@ maxTime="5m" # max bytes (default: 50MB) maxBytes=50000000 -function flows() { +if [[ ! "$*" =~ ^(.*)help|version(.*) ]]; then + check_dependencies "$required_yq_version" "$supported_archs" "$required_bash_version" +fi + +case "$1" in +*help) + help + exit 0 + ;; +*version) + version "$version" + exit 0 + ;; +*flows) case "$2" in - "help") + *help) flows_usage exit 0 ;; @@ -59,11 +79,10 @@ function flows() { command="flows" ;; esac -} - -function packets() { + ;; +*packets) case "$2" in - "help") + *help) packets_usage exit 0 ;; @@ -74,11 +93,10 @@ function packets() { command="packets" ;; esac -} - -function metrics() { + ;; +*metrics) case "$2" in - "help") + *help) metrics_usage exit 0 ;; @@ -89,75 +107,58 @@ function metrics() { command="metrics" ;; esac -} - -if [[ ! "$*" =~ ^(.*)help|version(.*) ]]; then - required_yq_version="v0.0.0" - supported_archs="" - required_bash_version="v0.0.0" - check_dependencies "$required_yq_version" "$supported_archs" "$required_bash_version" -fi - -case "$1" in -"help") - # display Help - echo - echo "Netobserv allows you to capture flow, packets and metrics from your cluster." - echo "Find more information at: https://github.com/netobserv/network-observability-cli/" - echo - echo "Syntax: netobserv [flows|packets|metrics|follow|stop|copy|cleanup|version] [options]" - echo - echo "commands:" - echo " flows Capture flows information in JSON format using collector pod." - echo " Options:" - flows_usage - echo " packets Capture packets information in pcap format using collector pod." - echo " Options:" - packets_usage - echo " metrics Capture metrics information in Prometheus using a ServiceMonitor (OCP cluster only)." - echo " Options:" - metrics_usage - echo " follow Follow collector logs when running in background." - echo " stop Stop collection by removing agent daemonset." - echo " copy Copy collector generated files locally." - echo " cleanup Remove netobserv components and configurations." - echo " version Print software version." - echo - exit 0 ;; -"version") - # display version - echo "Netobserv CLI version $version" - exit 0 - ;; -"flows") - flows $* - ;; -"packets") - packets $* - ;; -"metrics") - metrics $* - ;; -"follow") - # run follow command - follow - exit 0 +*follow) + case "$2" in + *help) + follow_usage + exit 0 + ;; + *) + # run follow command + follow + exit 0 + ;; + esac ;; -"stop") - # run deleteDaemonset command - deleteDaemonset - exit 0 +*stop) + case "$2" in + *help) + stop_usage + exit 0 + ;; + *) + # run deleteDaemonset command + deleteDaemonset + exit 0 + ;; + esac ;; -"copy") - # run copy output command - copyOutput - exit 0 +*copy) + case "$2" in + *help) + copy_usage + exit 0 + ;; + *) + # run copy output command + copyOutput + exit 0 + ;; + esac ;; -"cleanup") - # run cleanup command - cleanup - exit 0 +*cleanup) + case "$2" in + *help) + cleanup_usage + exit 0 + ;; + *) + # run cleanup command + cleanup + exit 0 + ;; + esac ;; *) echo "Unknown command $1. Use 'netobserv help' to display options" diff --git a/docs/netobserv_cli.adoc b/docs/netobserv_cli.adoc index b26348b4..ef4698a1 100644 --- a/docs/netobserv_cli.adoc +++ b/docs/netobserv_cli.adoc @@ -1,9 +1,12 @@ // Automatically generated by './scripts/generate-doc.sh'. Do not edit, or make the NETOBSERV team aware of the editions. :_mod-docs-content-type: REFERENCE -[id="network-observability-cli-usage_{context}"] + +[id="network-observability-netobserv-cli-reference_{context}"] = Network Observability CLI usage -You can use the The Network Observability CLI (`oc netobserv`) to pass command line arguments to capture flow data and packet data for further analysis, enable Network Observability Operator features or pass configuration options to the eBPF agent and flowlogs-pipeline. +You can use the Network Observability CLI (`oc netobserv`) to pass command line arguments to capture flows data and packets data for further analysis, enable Network Observability Operator features or pass configuration options to the eBPF agent and `flowlogs-pipeline`. + +[id="cli-syntax_{context}"] == Syntax The basic syntax for `oc netobserv` commands: @@ -14,15 +17,24 @@ $ oc netobserv [] [] [] <1> ---- <1> Feature options can only be used with the `oc netobserv flows` command. They cannot be used with the `oc netobserv packets` command. +[id="cli-basic-commands_{context}"] == Basic commands [cols="3a,8a",options="header"] .Basic commands |=== | Command | Description | flows -| Capture flows information. For subcommands, see the "Flow capture subcommands" table. +| Capture flows information. For subcommands, see the "Flows capture options" table. | packets -| Capture packets data. For subcommands, see the "Packet capture subcommand" table. +| Capture packets data. For subcommands, see the "Packets capture options" table. +| metrics +| Capture metrics data. For subcommands, see the "Metrics capture options" table. +| follow +| Follow collector logs when running in background. +| stop +| Stop collection by removing agent daemonset. +| copy +| Copy collector generated files locally. | cleanup | Remove the Network Observability CLI components. | version @@ -31,8 +43,10 @@ $ oc netobserv [] [] [] <1> | Show help. |=== +[id="cli-reference-flows-capture-options_{context}"] == Flows capture options -Flow capture has mandatory commands as well as additional options, such as enabling extra features about packet drops, DNS latencies, Round-trip time, and filtering. +Flows capture has mandatory commands as well as additional options, such as enabling extra features about packet drops, DNS latencies, Round-trip time, and filtering. + .`oc netobserv flows` syntax [source,terminal] ---- @@ -41,47 +55,52 @@ $ oc netobserv flows [] [] [cols="1,1,1",options="header"] |=== | Option | Description | Default -|--enable_pktdrop| enable packet drop | false -|--enable_dns| enable DNS tracking | false -|--enable_rtt| enable RTT tracking | false -|--enable_network_events| enable Network events monitoring | false -|--get-subnets| get subnets informations | false -|--log-level| components logs | info -|--max-time| maximum capture time | 5m -|--max-bytes| maximum capture bytes | 50000000 = 50MB -|--background| run in background | false -|--copy| copy the output files locally | prompt -|--node-selector| capture on specific nodes | n/a -|--enable_filter| enable flow filter | false -|--direction| filter direction | n/a -|--cidr| filter CIDR | 0.0.0.0/0 -|--protocol| filter protocol | n/a -|--sport| filter source port | n/a -|--dport| filter destination port | n/a -|--port| filter port | n/a -|--sport_range| filter source port range | n/a -|--dport_range| filter destination port range | n/a -|--port_range| filter port range | n/a -|--sports| filter on either of two source ports | n/a -|--dports| filter on either of two destination ports | n/a -|--ports| filter on either of two ports | n/a -|--tcp_flags| filter TCP flags | n/a -|--action| filter action | Accept -|--icmp_type| filter ICMP type | n/a -|--icmp_code| filter ICMP code | n/a -|--peer_ip| filter peer IP | n/a -|--drops| filter flows with only dropped packets | false -|--regexes| filter flows using regex | n/a -|--interfaces| interfaces to monitor | n/a +|--enable_all| enable all eBPF features | false +|--enable_dns| enable DNS tracking | false +|--enable_network_events| enable network events monitoring | false +|--enable_pkt_translation| enable packet translation | false +|--enable_pkt_drop| enable packet drop | false +|--enable_rtt| enable RTT tracking | false +|--enable_udn_mapping| enable User Defined Network mapping | false +|--get-subnets| get subnets informations | false +|--background| run in background | false +|--copy| copy the output files locally | prompt +|--log-level| components logs | info +|--max-time| maximum capture time | 5m +|--max-bytes| maximum capture bytes | 50000000 = 50MB +|--action| filter action | Accept +|--cidr| filter CIDR | 0.0.0.0/0 +|--direction| filter direction | n/a +|--dport| filter destination port | n/a +|--dport_range| filter destination port range | n/a +|--dports| filter on either of two destination ports | n/a +|--drops| filter flows with only dropped packets | false +|--icmp_code| filter ICMP code | n/a +|--icmp_type| filter ICMP type | n/a +|--node-selector| capture on specific nodes | n/a +|--peer_ip| filter peer IP | n/a +|--peer_cidr| filter peer CIDR | n/a +|--port_range| filter port range | n/a +|--port| filter port | n/a +|--ports| filter on either of two ports | n/a +|--protocol| filter protocol | n/a +|--regexes| filter flows using regular expression | n/a +|--sport_range| filter source port range | n/a +|--sport| filter source port | n/a +|--sports| filter on either of two source ports | n/a +|--tcp_flags| filter TCP flags | n/a +|--interfaces| interfaces to monitor | n/a |=== -.Example running flow capture on TCP protocol and port 49051 with PacketDrop and RTT features enabled: +.Example running flows capture on TCP protocol and port 49051 with PacketDrop and RTT features enabled: [source,terminal] ---- -$ oc netobserv flows --enable_pktdrop=true --enable_rtt=true --enable_filter=true --action=Accept --cidr=0.0.0.0/0 --protocol=TCP --port=49051 +$ oc netobserv flows --enable_pkt_drop=true --enable_rtt=true --enable_filter=true --action=Accept --cidr=0.0.0.0/0 --protocol=TCP --port=49051 ---- +[id="cli-reference-packet-capture-options_{context}"] == Packets capture options -You can filter packet capture data as same as flow capture using the filters. However, the features are not available here. +You can filter packets capture data as same as flows capture using the filters. +Note that the features, such as packets drop, DNS, RTT, and network events, are only available for flows and metrics capture. .`oc netobserv packets` syntax [source,terminal] @@ -91,41 +110,42 @@ $ oc netobserv packets [