Skip to content

Commit afc8a2d

Browse files
committed
Add UDNs merger logic to merge udns accross nodes for the same flow
Signed-off-by: Mohamed Mahmoud <[email protected]>
1 parent 61205c8 commit afc8a2d

File tree

6 files changed

+55
-24
lines changed

6 files changed

+55
-24
lines changed

pkg/model/fields/fields.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const (
3535
DstZone = Dst + Zone
3636
Cluster = "K8S_ClusterName"
3737
UDN = "UDN"
38+
Udns = "Udns"
3839
Layer = "K8S_FlowLayer"
3940
Packets = "Packets"
4041
Proto = "Proto"

web/src/api/ipfix.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ export interface Fields {
115115
Interfaces?: string[];
116116
/** Flow direction array from the network interface observation point */
117117
IfDirections?: IfDirection[];
118+
/** UDNs labels array */
119+
Udns?: string[];
118120
/** Network Events */
119121
NetworkEvents?: string[];
120122
/** 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) and RST+ACK (0x400). */

web/src/components/drawer/record/record-field.tsx

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import { ResourceIcon, ResourceLink } from '@openshift-console/dynamic-plugin-sdk';
2-
import { Button, Flex, FlexItem, Popover, Text, TextContent, TextVariants, Tooltip } from '@patternfly/react-core';
3-
import { FilterIcon, GlobeAmericasIcon, TimesIcon, ToggleOffIcon, ToggleOnIcon } from '@patternfly/react-icons';
1+
import {ResourceIcon, ResourceLink} from '@openshift-console/dynamic-plugin-sdk';
2+
import {Button, Flex, FlexItem, Popover, Text, TextContent, TextVariants, Tooltip} from '@patternfly/react-core';
3+
import {FilterIcon, GlobeAmericasIcon, TimesIcon, ToggleOffIcon, ToggleOnIcon} from '@patternfly/react-icons';
44
import * as React from 'react';
5-
import { useTranslation } from 'react-i18next';
6-
import { Link } from 'react-router-dom';
7-
import { FlowDirection, getDirectionDisplayString, Record } from '../../../api/ipfix';
8-
import { Column, ColumnsId, getFullColumnName, isKubeObj, KubeObj } from '../../../utils/columns';
9-
import { dateFormatter, getFormattedDate, timeMSFormatter, utcDateTimeFormatter } from '../../../utils/datetime';
10-
import { dnsCodesNames, dnsErrorsValues, getDNSErrorDescription, getDNSRcodeDescription } from '../../../utils/dns';
11-
import { getDSCPDocUrl, getDSCPServiceClassDescription, getDSCPServiceClassName } from '../../../utils/dscp';
12-
import { formatDurationAboveMillisecond, formatDurationAboveNanosecond } from '../../../utils/duration';
13-
import { getICMPCode, getICMPDocUrl, getICMPType, icmpAllTypesValues, isValidICMPProto } from '../../../utils/icmp';
14-
import { dropCausesNames, getDropCauseDescription, getDropCauseDocUrl } from '../../../utils/pkt-drop';
15-
import { formatPort } from '../../../utils/port';
16-
import { formatProtocol, getProtocolDocUrl } from '../../../utils/protocol';
17-
import { getFlagsList, getTCPFlagsDocUrl } from '../../../utils/tcp-flags';
18-
import { Size } from '../../dropdowns/table-display-dropdown';
5+
import {useTranslation} from 'react-i18next';
6+
import {Link} from 'react-router-dom';
7+
import {FlowDirection, getDirectionDisplayString, Record} from '../../../api/ipfix';
8+
import {Column, ColumnsId, getFullColumnName, isKubeObj, KubeObj} from '../../../utils/columns';
9+
import {dateFormatter, getFormattedDate, timeMSFormatter, utcDateTimeFormatter} from '../../../utils/datetime';
10+
import {dnsCodesNames, dnsErrorsValues, getDNSErrorDescription, getDNSRcodeDescription} from '../../../utils/dns';
11+
import {getDSCPDocUrl, getDSCPServiceClassDescription, getDSCPServiceClassName} from '../../../utils/dscp';
12+
import {formatDurationAboveMillisecond, formatDurationAboveNanosecond} from '../../../utils/duration';
13+
import {getICMPCode, getICMPDocUrl, getICMPType, icmpAllTypesValues, isValidICMPProto} from '../../../utils/icmp';
14+
import {dropCausesNames, getDropCauseDescription, getDropCauseDocUrl} from '../../../utils/pkt-drop';
15+
import {formatPort} from '../../../utils/port';
16+
import {formatProtocol, getProtocolDocUrl} from '../../../utils/protocol';
17+
import {getFlagsList, getTCPFlagsDocUrl} from '../../../utils/tcp-flags';
18+
import {Size} from '../../dropdowns/table-display-dropdown';
1919
import './record-field.css';
2020

2121
export type RecordFieldFilter = {
@@ -413,7 +413,8 @@ export const RecordField: React.FC<RecordFieldProps> = ({
413413
}
414414
return singleContainer(simpleTextWithTooltip(getDirectionDisplayString(String(value) as FlowDirection, t)));
415415
}
416-
case ColumnsId.interfaces: {
416+
case ColumnsId.interfaces:
417+
case ColumnsId.udns: {
417418
if (Array.isArray(value)) {
418419
return nthContainer(
419420
value.map(iName => simpleTextWithTooltip(String(iName))),

web/src/utils/__tests__/flows.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ describe('mergeFlowReporters', () => {
7676
SrcAddr: '10.0.0.1',
7777
DstAddr: '10.0.0.2',
7878
IfDirections: [IfDirection.Ingress, IfDirection.Egress],
79-
Interfaces: ['eth0', 'abcd']
79+
Interfaces: ['eth0', 'abcd'],
80+
Udns: ['udn1', 'udn2']
8081
} as Fields,
8182
labels: { FlowDirection: FlowDirection.Ingress }
8283
},
@@ -86,7 +87,8 @@ describe('mergeFlowReporters', () => {
8687
SrcAddr: '10.0.0.1',
8788
DstAddr: '10.0.0.2',
8889
IfDirections: [IfDirection.Ingress],
89-
Interfaces: ['genev']
90+
Interfaces: ['genev'],
91+
Udns: ['udn3']
9092
} as Fields,
9193
labels: { FlowDirection: FlowDirection.Egress }
9294
}
@@ -99,7 +101,8 @@ describe('mergeFlowReporters', () => {
99101
SrcAddr: '10.0.0.1',
100102
DstAddr: '10.0.0.2',
101103
IfDirections: [IfDirection.Ingress, IfDirection.Egress, IfDirection.Ingress],
102-
Interfaces: ['eth0', 'abcd', 'genev']
104+
Interfaces: ['eth0', 'abcd', 'genev'],
105+
Udns: ['udn1', 'udn2', 'udn3']
103106
} as Fields,
104107
labels: { FlowDirection: FlowDirection.Ingress }
105108
});

web/src/utils/columns.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export enum ColumnsId {
7979
interfaces = 'Interfaces',
8080
ifdirs = 'IfDirections',
8181
flowdirints = 'FlowDirInts',
82+
udns = 'Udns',
8283
recordtype = 'RecordType',
8384
bytesab = 'Bytes_AB',
8485
bytesba = 'Bytes_BA',

web/src/utils/flows.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,47 @@ const getInvolvedInterfaces = (flowsFor5Tuples: Record[]): { ifnames: string[];
3535
return { ifnames, ifdirs };
3636
};
3737

38+
const getInvolvedUdns = (flowsFor5Tuples: Record[]): string[] => {
39+
const cache = new Set();
40+
const udns: string[] = [];
41+
flowsFor5Tuples.forEach(f => {
42+
if (f.fields.Udns) {
43+
_.zip(f.fields.Udns).forEach(([udn]) => {
44+
const key = `${udn}`;
45+
if (!cache.has(key)) {
46+
cache.add(key);
47+
udns.push(udn!);
48+
}
49+
});
50+
}
51+
});
52+
return udns;
53+
};
54+
3855
export const mergeFlowReporters = (flows: Record[]): Record[] => {
3956
// The purpose of this function is to determine if, for a given 5 tuple, we'll look at INGRESS, EGRESS or INNER reporter
4057
// The assumption is that INGRESS alone, EGRESS alone or INNER alone always provide a complete visiblity
4158
// Favor whichever contains pktDrop and/or DNS responses
4259
const grouped = _.groupBy(flows, get5Tuple);
4360
const filtersIndex = _.mapValues(grouped, (records: Record[]) => electMostRelevant(records));
4461
const involvedInterfaces = _.mapValues(grouped, (records: Record[]) => getInvolvedInterfaces(records));
45-
// Filter and inject other interfaces in elected flows
46-
// An assumption is made that interfaces involved for a 5 tuples will keep being involved in the whole flows sequence
62+
const involvedUdns = _.mapValues(grouped, (records: Record[]) => getInvolvedUdns(records));
63+
// Filter and inject other interfaces and udns in elected flows
64+
// An assumption is made that interfaces and udns involved for a 5 tuples will keep being involved in the whole flows sequence
4765
// If that assumption proves wrong, we may refine by looking at time overlaps between flows
4866
return flows
4967
.filter((r: Record) => r.labels.FlowDirection === filtersIndex[get5Tuple(r)].labels.FlowDirection)
5068
.map(r => {
51-
const interfaces = involvedInterfaces[get5Tuple(r)];
69+
const key = get5Tuple(r);
70+
const interfaces = involvedInterfaces[key];
5271
if (interfaces) {
5372
r.fields.Interfaces = interfaces.ifnames;
5473
r.fields.IfDirections = interfaces.ifdirs;
5574
}
75+
const udns = involvedUdns[key];
76+
if (udns) {
77+
r.fields.Udns = udns;
78+
}
5679
return r;
5780
});
5881
};

0 commit comments

Comments
 (0)