diff --git a/pkg/model/fields/fields.go b/pkg/model/fields/fields.go index 46576652b..ba2ebdd87 100644 --- a/pkg/model/fields/fields.go +++ b/pkg/model/fields/fields.go @@ -69,8 +69,7 @@ func IsNumeric(v string) bool { Packets, Proto, Bytes, - DSCP, - TCPFlags: + DSCP: return true default: return false @@ -95,7 +94,8 @@ func IsArray(v string) bool { case IfDirections, Interfaces, - NetworkEvents: + NetworkEvents, + TCPFlags: return true default: return false diff --git a/web/src/api/ipfix.ts b/web/src/api/ipfix.ts index b34f668ff..2072d946b 100644 --- a/web/src/api/ipfix.ts +++ b/web/src/api/ipfix.ts @@ -118,7 +118,7 @@ export interface Fields { /** Network Events */ NetworkEvents?: string[]; /** 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). */ - Flags?: number; + Flags?: string[]; /** Number of packets */ Packets?: number; /** In conversation tracking, A to B packets counter per conversation */ diff --git a/web/src/components/drawer/record/record-field.tsx b/web/src/components/drawer/record/record-field.tsx index aadb3b473..947494e12 100644 --- a/web/src/components/drawer/record/record-field.tsx +++ b/web/src/components/drawer/record/record-field.tsx @@ -21,7 +21,7 @@ import { import { dropCausesNames, getDropCauseDescription, getDropCauseDocUrl } from '../../../utils/pkt-drop'; import { formatPort } from '../../../utils/port'; import { formatProtocol, getProtocolDocUrl } from '../../../utils/protocol'; -import { decomposeTCPFlagsBitfield, getTCPFlagsDocUrl } from '../../../utils/tcp-flags'; +import { getFlagsList, getTCPFlagsDocUrl } from '../../../utils/tcp-flags'; import { Size } from '../../dropdowns/table-display-dropdown'; import './record-field.css'; @@ -473,9 +473,9 @@ export const RecordField: React.FC = ({ } case ColumnsId.tcpflags: { let child = emptyText(); - if (typeof value === 'number' && !isNaN(value)) { - const flags = decomposeTCPFlagsBitfield(value); - const name = flags.length > 0 ? flags.map(f => f.name).join(', ') : String(value); + if (Array.isArray(value) && value.length > 0) { + const flags = getFlagsList(value as string[]); + const joined = value.join(', '); if (detailed) { let description = `${t('Value')}: ${value}`; if (flags.length === 1) { @@ -486,9 +486,9 @@ export const RecordField: React.FC = ({ t('The flow contains packets with various flags: ') + flags.map(f => f.name + ' (' + f.description + ')').join('; '); } - child = clickableContent(name, description, getTCPFlagsDocUrl()); + child = clickableContent(joined, description, getTCPFlagsDocUrl()); } else { - child = simpleTextWithTooltip(name)!; + child = simpleTextWithTooltip(joined)!; } } return singleContainer(child); diff --git a/web/src/utils/__tests__/tcp-flags.spec.ts b/web/src/utils/__tests__/tcp-flags.spec.ts deleted file mode 100644 index f14054e55..000000000 --- a/web/src/utils/__tests__/tcp-flags.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { decomposeTCPFlagsBitfield } from '../tcp-flags'; - -describe('TCP flags', () => { - it('should decompose', () => { - const flags528 = decomposeTCPFlagsBitfield(528); - expect(flags528).toHaveLength(2); - expect(flags528.map(f => f.name)).toEqual(['ACK', 'FIN_ACK']); - - const flags256 = decomposeTCPFlagsBitfield(256); - expect(flags256).toHaveLength(1); - expect(flags256.map(f => f.name)).toEqual(['SYN_ACK']); - - const flags666 = decomposeTCPFlagsBitfield(666); - expect(flags666).toHaveLength(5); - expect(flags666.map(f => f.name)).toEqual(['SYN', 'PSH', 'ACK', 'CWR', 'FIN_ACK']); - }); -}); diff --git a/web/src/utils/filter-options.ts b/web/src/utils/filter-options.ts index 9dc15601c..7f245f1a3 100644 --- a/web/src/utils/filter-options.ts +++ b/web/src/utils/filter-options.ts @@ -174,8 +174,8 @@ export const getDSCPOptions = (value: string): Promise => { export const getTCPFlagsOptions = (value: string): Promise => { return Promise.resolve( tcpFlagsList - .filter(opt => String(opt.value).includes(value) || opt.name.toLowerCase().includes(value.toLowerCase())) - .map(v => ({ name: v.name, value: String(v.value) })) + .filter(opt => opt.name.toLowerCase().includes(value.toLowerCase())) + .map(v => ({ name: v.name, value: v.name })) ); }; diff --git a/web/src/utils/tcp-flags.ts b/web/src/utils/tcp-flags.ts index 13e86901e..66c410f57 100644 --- a/web/src/utils/tcp-flags.ts +++ b/web/src/utils/tcp-flags.ts @@ -1,29 +1,21 @@ -import { ReadOnlyValue, ReadOnlyValues } from './values'; - export const getTCPFlagsDocUrl = () => { return 'https://www.rfc-editor.org/rfc/rfc9293'; }; -export const tcpFlagsList: ReadOnlyValues = [ - { value: 1, name: 'FIN', description: 'No more data from sender' }, - { value: 2, name: 'SYN', description: 'Synchronize sequence numbers' }, - { value: 4, name: 'RST', description: 'Reset the connection' }, - { value: 8, name: 'PSH', description: 'Push function' }, - { value: 16, name: 'ACK', description: 'Acknowledgement field is significant' }, - { value: 32, name: 'URG', description: 'Urgent pointer field is significant' }, - { value: 64, name: 'ECE', description: 'ECN-Echo' }, - { value: 128, name: 'CWR', description: 'Congestion Window Reduced' }, - { value: 256, name: 'SYN_ACK', description: 'Acknowledgement of SYN (custom flag)' }, - { value: 512, name: 'FIN_ACK', description: 'Acknowledgement of FIN (custom flag)' }, - { value: 1024, name: 'RST_ACK', description: 'Acknowledgement of RST (custom flag)' } +export const tcpFlagsList = [ + { name: 'FIN', description: 'No more data from sender' }, + { name: 'SYN', description: 'Synchronize sequence numbers' }, + { name: 'RST', description: 'Reset the connection' }, + { name: 'PSH', description: 'Push function' }, + { name: 'ACK', description: 'Acknowledgement field is significant' }, + { name: 'URG', description: 'Urgent pointer field is significant' }, + { name: 'ECE', description: 'ECN-Echo' }, + { name: 'CWR', description: 'Congestion Window Reduced' }, + { name: 'SYN_ACK', description: 'Acknowledgement of SYN (custom flag)' }, + { name: 'FIN_ACK', description: 'Acknowledgement of FIN (custom flag)' }, + { name: 'RST_ACK', description: 'Acknowledgement of RST (custom flag)' } ] as const; -export const decomposeTCPFlagsBitfield = (bitfield: number): ReadOnlyValues => { - const values: ReadOnlyValue[] = []; - tcpFlagsList.forEach(flag => { - if (bitfield & flag.value) { - values.push(flag); - } - }); - return values; +export const getFlagsList = (names: string[]): { name: string; description: string }[] => { + return tcpFlagsList.filter(f => names.includes(f.name)); };