Skip to content

Commit

Permalink
add DNS error number
Browse files Browse the repository at this point in the history
  • Loading branch information
jpinsonneau committed Oct 31, 2023
1 parent e854027 commit bcc9de7
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 28 deletions.
8 changes: 8 additions & 0 deletions pkg/loki/flow_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,14 @@ func (q *FlowQueryBuilder) appendDNSFilter(sb *strings.Builder) {
sb.WriteString("`")
}

func (q *FlowQueryBuilder) appendDNSLatencyFilter(sb *strings.Builder) {
// ensure DnsLatencyMs field is specified
// |~`"DnsLatencyMs`
sb.WriteString("|~`")
sb.WriteString(`"DnsLatencyMs`)
sb.WriteString("`")
}

func (q *FlowQueryBuilder) appendDNSRCodeFilter(sb *strings.Builder) {
// ensure DnsFlagsResponseCode field is specified with valid error
// |~`"DnsFlagsResponseCode"`
Expand Down
52 changes: 28 additions & 24 deletions pkg/loki/topology_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@ const (
)

type Topology struct {
limit string
rateInterval string
step string
function string
dataField string
fields string
skipEmptyDropState bool
skipEmptyDropCause bool
skipNonDNS bool
skipEmptyDNSRCode bool
skipEmptyRTT bool
factor string
limit string
rateInterval string
step string
function string
dataField string
fields string
skipEmptyDropState bool
skipEmptyDropCause bool
skipNonDNS bool
skipEmptyDNSLatency bool
skipEmptyDNSRCode bool
skipEmptyRTT bool
factor string
}

type TopologyQueryBuilder struct {
Expand Down Expand Up @@ -79,18 +80,19 @@ func NewTopologyQuery(cfg *Config, start, end, limit, rateInterval, step string,
return &TopologyQueryBuilder{
FlowQueryBuilder: NewFlowQueryBuilder(cfg, start, end, limit, d, rt, packetLoss),
topology: &Topology{
rateInterval: rateInterval,
step: step,
limit: l,
function: f,
dataField: t,
fields: fields,
skipEmptyDropState: aggregate == "droppedState",
skipEmptyDropCause: aggregate == "droppedCause",
skipNonDNS: metricType == constants.MetricTypeDNSLatencies || metricType == constants.MetricTypeCountDNS,
skipEmptyDNSRCode: aggregate == "dnsRCode",
skipEmptyRTT: metricType == constants.MetricTypeFlowRTT,
factor: factor,
rateInterval: rateInterval,
step: step,
limit: l,
function: f,
dataField: t,
fields: fields,
skipEmptyDropState: aggregate == "droppedState",
skipEmptyDropCause: aggregate == "droppedCause",
skipNonDNS: metricType == constants.MetricTypeCountDNS,
skipEmptyDNSLatency: metricType == constants.MetricTypeDNSLatencies,
skipEmptyDNSRCode: aggregate == "dnsRCode",
skipEmptyRTT: metricType == constants.MetricTypeFlowRTT,
factor: factor,
},
}, nil
}
Expand Down Expand Up @@ -177,6 +179,8 @@ func (q *TopologyQueryBuilder) Build() string {

if q.topology.skipEmptyDNSRCode {
q.appendDNSRCodeFilter(sb)
} else if q.topology.skipEmptyDNSLatency {
q.appendDNSLatencyFilter(sb)
} else if q.topology.skipNonDNS {
q.appendDNSFilter(sb)
}
Expand Down
3 changes: 3 additions & 0 deletions web/locales/en/plugin__netobserv-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@
"Time elapsed between DNS request and response.": "Time elapsed between DNS request and response.",
"DNS Response Code": "DNS Response Code",
"DNS RCODE name from response header.": "DNS RCODE name from response header.",
"DNS Error": "DNS Error",
"DNS error number returned by bpf_skb_load_bytes function.": "DNS error number returned by bpf_skb_load_bytes function.",
"Start Time": "Start Time",
"Time of the first packet observed. Unlike End Time, it is not used in queries to select records in an interval.": "Time of the first packet observed. Unlike End Time, it is not used in queries to select records in an interval.",
"End Time": "End Time",
Expand Down Expand Up @@ -483,6 +485,7 @@
"Specify a single DNS RCODE name like:": "Specify a single DNS RCODE name like:",
"A IANA RCODE number like 0, 3, 9": "A IANA RCODE number like 0, 3, 9",
"A IANA RCODE name like NoError, NXDomain, NotAuth": "A IANA RCODE name like NoError, NXDomain, NotAuth",
"Specify a single DNS error number.": "Specify a single DNS error number.",
"Specify a TCP handshake Round Trip Time in nanoseconds.": "Specify a TCP handshake Round Trip Time in nanoseconds.",
"P": "P",
"Pps": "Pps",
Expand Down
2 changes: 2 additions & 0 deletions web/src/api/ipfix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ export interface Fields {
DnsFlagsResponseCode?: string;
/** Calculated time between response and request, in milliseconds */
DnsLatencyMs?: number;
/** Error number returned by bpf_skb_load_bytes */
DnsErrno?: number;
/** Start timestamp of this flow, in milliseconds */
TimeFlowStartMs: number;
/** End timestamp of this flow, in milliseconds */
Expand Down
10 changes: 9 additions & 1 deletion web/src/components/netflow-record/record-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Link } from 'react-router-dom';
import { FlowDirection, Record } from '../../api/ipfix';
import { Column, ColumnsId, getFullColumnName } from '../../utils/columns';
import { dateFormatter, getFormattedDate, timeMSFormatter, utcDateTimeFormatter } from '../../utils/datetime';
import { DNS_CODE_NAMES, getDNSRcodeDescription } from '../../utils/dns';
import { DNS_CODE_NAMES, DNS_ERRORS_VALUES, getDNSErrorDescription, getDNSRcodeDescription } from '../../utils/dns';
import {
getICMPCode,
getICMPDocUrl,
Expand Down Expand Up @@ -470,6 +470,14 @@ export const RecordField: React.FC<{
? simpleTextWithTooltip(detailed ? `${value}: ${getDNSRcodeDescription(value as DNS_CODE_NAMES)}` : value)
: emptyText()
);
case ColumnsId.dnserrno:
return singleContainer(
typeof value === 'number' && !isNaN(value)
? simpleTextWithTooltip(
detailed ? `${value}: ${getDNSErrorDescription(value as DNS_ERRORS_VALUES)}` : String(value)
)
: emptyText()
);
default:
if (Array.isArray(value) && value.length) {
return doubleContainer(simpleTextWithTooltip(String(value[0])), simpleTextWithTooltip(String(value[1])));
Expand Down
3 changes: 2 additions & 1 deletion web/src/components/netflow-traffic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ export const NetflowTraffic: React.FC<{
return (isSidePanel ? getDefaultColumns(t, false, false) : columns).filter(
col =>
(isConnectionTracking() || ![ColumnsId.recordtype, ColumnsId.hashid].includes(col.id)) &&
(isDNSTracking() || ![ColumnsId.dnsid, ColumnsId.dnslatency, ColumnsId.dnsresponsecode].includes(col.id)) &&
(isDNSTracking() ||
![ColumnsId.dnsid, ColumnsId.dnslatency, ColumnsId.dnsresponsecode, ColumnsId.dnserrno].includes(col.id)) &&
(isFlowRTT() || ![ColumnsId.rttTime].includes(col.id))
);
},
Expand Down
1 change: 1 addition & 0 deletions web/src/model/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export type FilterId =
| 'dns_id'
| 'dns_latency'
| 'dns_flag_response_code'
| 'dns_errno'
| 'time_flow_rtt';

export interface FilterDefinition {
Expand Down
13 changes: 13 additions & 0 deletions web/src/utils/columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export enum ColumnsId {
dnsid = 'DNSId',
dnslatency = 'DNSLatency',
dnsresponsecode = 'DNSResponseCode',
dnserrno = 'DNSErrNo',
hostaddr = 'K8S_HostIP',
srchostaddr = 'SrcK8S_HostIP',
dsthostaddr = 'DstK8S_HostIP',
Expand Down Expand Up @@ -848,6 +849,18 @@ export const getExtraColumns = (t: TFunction): Column[] => {
value: f => f.fields.DnsFlagsResponseCode || '',
sort: (a, b, col) => compareNumbers(col.value(a) as number, col.value(b) as number),
width: 5
},
{
id: ColumnsId.dnserrno,
group: t('DNS'),
name: t('DNS Error'),
tooltip: t('DNS error number returned by bpf_skb_load_bytes function.'),
fieldName: 'DnsErrno',
quickFilter: 'dns_errno',
isSelected: false,
value: f => (f.fields.DnsErrno === undefined ? Number.NaN : f.fields.DnsErrno),
sort: (a, b, col) => compareNumbers(col.value(a) as number, col.value(b) as number),
width: 5
}
];
};
Expand Down
48 changes: 48 additions & 0 deletions web/src/utils/dns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,51 @@ export type DNS_CODE_NAMES = typeof dnsRcodesNames[number];
export const getDNSRcodeDescription = (name: DNS_CODE_NAMES): string => {
return DNS_RCODES.find(v => v.name === name)?.description || 'Unassigned';
};

// https://elixir.bootlin.com/linux/v4.7/source/include/uapi/asm-generic/errno-base.h
export const DNS_ERRORS: ReadOnlyValues = [
{ value: 1, name: 'EPERM', description: 'Operation not permitted' },
{ value: 2, name: 'ENOENT', description: 'No such file or directory' },
{ value: 3, name: 'ESRCH', description: 'No such process' },
{ value: 4, name: 'EINTR', description: 'Interrupted system call' },
{ value: 5, name: 'EIO', description: 'I/O error' },
{ value: 6, name: 'ENXIO', description: 'No such device or address' },
{ value: 7, name: 'E2BIG', description: 'Argument list too long' },
{ value: 8, name: 'ENOEXEC', description: 'Exec format error' },
{ value: 9, name: 'EBADF', description: 'Bad file number' },
{ value: 10, name: 'ECHILD', description: 'No child processes' },
{ value: 11, name: 'EAGAIN', description: 'Try again' },
{ value: 12, name: 'ENOMEM', description: 'Out of memory' },
{ value: 13, name: 'EACCES', description: 'Permission denied' },
{ value: 14, name: 'EFAULT', description: 'Bad address' },
{ value: 15, name: 'ENOTBLK', description: 'Block device required' },
{ value: 16, name: 'EBUSY', description: 'Device or resource busy' },
{ value: 17, name: 'EEXIST', description: 'File exists' },
{ value: 18, name: 'EXDEV', description: 'Cross-device link' },
{ value: 19, name: 'ENODEV', description: 'No such device' },
{ value: 20, name: 'ENOTDIR', description: 'Not a directory' },
{ value: 21, name: 'EISDIR', description: 'Is a directory' },
{ value: 22, name: 'EINVAL', description: 'Invalid argument' },
{ value: 23, name: 'ENFILE', description: 'File table overflow' },
{ value: 24, name: 'EMFILE', description: 'Too many open files' },
{ value: 25, name: 'ENOTTY', description: 'Not a typewriter' },
{ value: 26, name: 'ETXBSY', description: 'Text file busy' },
{ value: 27, name: 'EFBIG', description: 'File too large' },
{ value: 28, name: 'ENOSPC', description: 'No space left on device' },
{ value: 29, name: 'ESPIPE', description: 'Illegal seek' },
{ value: 30, name: 'EROFS', description: 'Read-only file system' },
{ value: 31, name: 'EMLINK', description: 'Too many links' },
{ value: 32, name: 'EPIPE', description: 'Broken pipe' },
{ value: 33, name: 'EDOM', description: 'Math argument out of domain of func' },
{ value: 34, name: 'ERANGE', description: 'Math result not representable' }
] as const;

const dnsErrorsValues = DNS_ERRORS.map(v => v.value);
export type DNS_ERRORS_VALUES = typeof dnsErrorsValues[number];

const dnsErrorsNames = DNS_ERRORS.map(v => v.name);
export type DNS_ERRORS_NAMES = typeof dnsErrorsNames[number];

export const getDNSErrorDescription = (value: DNS_ERRORS_VALUES): string => {
return DNS_ERRORS.find(v => v.value === value)?.description || '';
};
13 changes: 12 additions & 1 deletion web/src/utils/filter-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import {
getDropStateOptions,
getDropCauseOptions,
getDirectionOptionsAsync,
findDirectionOption
findDirectionOption,
getDnsErrorCodeOptions
} from './filter-options';

// Convenience string to filter by undefined field values
Expand Down Expand Up @@ -538,6 +539,16 @@ export const getFilterDefinitions = (
docUrl: 'https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6',
encoder: simpleFiltersEncoder('DnsFlagsResponseCode')
},
{
id: 'dns_errno',
name: t('DNS Error'),
category: FilterCategory.None,
component: FilterComponent.Autocomplete,
getOptions: cap10(getDnsErrorCodeOptions),
validate: rejectEmptyValue,
hint: t('Specify a single DNS error number.'),
encoder: simpleFiltersEncoder('DnsErrno')
},
{
id: 'time_flow_rtt',
name: t('Flow RTT'),
Expand Down
10 changes: 9 additions & 1 deletion web/src/utils/filter-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FlowDirection } from '../api/ipfix';
import { FilterOption } from '../model/filters';
import { splitResource, SplitStage } from '../model/resource';
import { autoCompleteCache } from './autocomplete-cache';
import { DNS_RCODES } from './dns';
import { DNS_ERRORS, DNS_RCODES } from './dns';
import { getPort, getService } from './port';
import { DROP_CAUSES, DROP_STATES } from './pkt-drop';
import { TFunction } from 'i18next';
Expand Down Expand Up @@ -125,6 +125,14 @@ export const getDnsResponseCodeOptions = (value: string): Promise<FilterOption[]
);
};

export const getDnsErrorCodeOptions = (value: string): Promise<FilterOption[]> => {
return Promise.resolve(
DNS_ERRORS.filter(
opt => String(opt.value).includes(value) || opt.name.toLowerCase().includes(value.toLowerCase())
).map(v => ({ name: v.name, value: String(v.value) }))
);
};

export const findProtocolOption = (nameOrVal: string) => {
return protocolOptions.find(p => p.name.toLowerCase() === nameOrVal.toLowerCase() || p.value === nameOrVal);
};
Expand Down

0 comments on commit bcc9de7

Please sign in to comment.