Skip to content

Commit 45ec9c0

Browse files
authored
feat(conntrack): added labels to the response (#118)
1 parent b2e374b commit 45ec9c0

File tree

2 files changed

+33
-84
lines changed

2 files changed

+33
-84
lines changed

src/nethsec/conntrack/__init__.py

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,6 @@
1313
from xml.etree import ElementTree
1414
from xml.etree.ElementTree import Element
1515

16-
def __parse_meta_connection_tag(meta: Element) -> dict:
17-
"""
18-
From a meta tag, extract the connection information.
19-
20-
Args:
21-
- meta: ElementTree.Element with the meta tag.
22-
23-
Returns:
24-
dictionary with the connection information.
25-
"""
26-
result = {'src': '', 'dest': '', 'protocol': '', 'packets': '0', 'bytes': '0'}
27-
layer3 = meta.find('layer3')
28-
result['src'] = layer3.find('src').text
29-
result['dest'] = layer3.find('dst').text
30-
layer4 = meta.find('layer4')
31-
result['protocol'] = layer4.get('protoname')
32-
# start port and end port might not be present for some protocols, like ICMP.
33-
if layer4.find('sport') is not None:
34-
result['start_port'] = layer4.find('sport').text
35-
if layer4.find('dport') is not None:
36-
result['end_port'] = layer4.find('dport').text
37-
counters = meta.find('counters')
38-
if counters is not None:
39-
result['packets'] = counters.find('packets').text
40-
result['bytes'] = counters.find('bytes').text
41-
return result
42-
4316

4417
def __parse_connection_info(flow: Element) -> dict:
4518
"""
@@ -54,35 +27,40 @@ def __parse_connection_info(flow: Element) -> dict:
5427
result = {}
5528
# expand meta tags
5629
for child in flow.findall('meta'):
57-
# parse the meta tag using __parse_meta_connection_tag function
58-
if child.get('direction') == 'original' or child.get('direction') == 'reply':
59-
connection_info = __parse_meta_connection_tag(child)
60-
if child.get('direction') == 'original':
61-
result['source'] = connection_info['src']
62-
result['destination'] = connection_info['dest']
63-
result['protocol'] = connection_info['protocol']
64-
if 'start_port' in connection_info:
65-
result['source_port'] = connection_info['start_port']
66-
if 'end_port' in connection_info:
67-
result['destination_port'] = connection_info['end_port']
30+
if child.get('direction') == 'original':
31+
layer3 = child.find('layer3')
32+
result['source'] = layer3.find('src').text
33+
result['destination'] = layer3.find('dst').text
34+
layer4 = child.find('layer4')
35+
result['protocol'] = layer4.get('protoname')
36+
if layer4.find('sport') is not None:
37+
result['source_port'] = layer4.find('sport').text
38+
if layer4.find('dport') is not None:
39+
result['destination_port'] = layer4.find('dport').text
40+
counters = child.find('counters')
41+
if counters is not None:
6842
result['source_stats'] = {
69-
'packets': connection_info['packets'],
70-
'bytes': connection_info['bytes']
43+
'packets': int(counters.find('packets').text),
44+
'bytes': int(counters.find('bytes').text)
7145
}
72-
else:
46+
if child.get('direction') == 'reply':
47+
counters = child.find('counters')
48+
if counters is not None:
7349
result['destination_stats'] = {
74-
'packets': connection_info['packets'],
75-
'bytes': connection_info['bytes']
50+
'packets': int(counters.find('packets').text),
51+
'bytes': int(counters.find('bytes').text)
7652
}
77-
# not easily parsable, just add the values
78-
else:
53+
if child.get('direction') == 'independent':
7954
result['id'] = child.find('id').text
8055
if child.find('timeout') is not None:
8156
result['timeout'] = child.find('timeout').text
8257
if child.find('unreplied') is not None:
8358
result['unreplied'] = True
8459
if child.find('state') is not None:
8560
result['state'] = child.find('state').text
61+
result['labels'] = []
62+
for label in child.findall('labels/label'):
63+
result['labels'].append(label.text)
8664

8765
return result
8866

@@ -94,13 +72,10 @@ def list_connections():
9472
Returns:
9573
dict of applications and their connections.
9674
"""
97-
result = subprocess.run(["conntrack", "-L", "-o", "xml"], capture_output=True, text=True)
75+
result = subprocess.run(["conntrack", "-L", "-o", "labels,xml"], capture_output=True, text=True)
9876
root = ElementTree.fromstring(result.stdout)
9977
result = []
10078
for flow in root.findall('flow'):
101-
# download
102-
# upload
103-
# wan
10479
result.append(__parse_connection_info(flow))
10580

10681
return result

tests/test_conntrack.py

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -261,47 +261,21 @@
261261
"""
262262

263263

264-
def test_parse_meta_tag():
265-
# get first meta tag in first flow
266-
meta_tag = ElementTree.fromstring(conntrack_response).find('flow/meta')
267-
result = conntrack.__parse_meta_connection_tag(meta_tag)
268-
assert result['src'] == '192.168.122.234'
269-
assert result['dest'] == '31.14.133.122'
270-
assert result['protocol'] == 'udp'
271-
assert result['start_port'] == '41692'
272-
assert result['end_port'] == '123'
273-
assert result['packets'] == '1'
274-
assert result['bytes'] == '76'
275-
276-
277-
def test_parse_meta_tag_without_ports():
278-
# get the meta tag with the ICMP protocol
279-
meta_tag = ElementTree.fromstring(conntrack_response).findall('flow')[1][1]
280-
result = conntrack.__parse_meta_connection_tag(meta_tag)
281-
assert result['src'] == '192.168.122.155'
282-
assert result['dest'] == '192.168.122.1'
283-
assert result['protocol'] == 'icmp'
284-
assert 'start_port' not in result
285-
assert 'end_port' not in result
286-
assert result['packets'] == '2'
287-
assert result['bytes'] == '168'
288-
289-
290264
def test_connection_info():
291265
# get the first flow
292266
flow = ElementTree.fromstring(conntrack_response).findall('flow')[0]
293267
result = conntrack.__parse_connection_info(flow)
294268
original = flow[0]
295269
reply = flow[1]
296-
assert result['source'] == conntrack.__parse_meta_connection_tag(original)['src']
297-
assert result['destination'] == conntrack.__parse_meta_connection_tag(original)['dest']
298-
assert result['protocol'] == conntrack.__parse_meta_connection_tag(original)['protocol']
299-
assert result['source_port'] == conntrack.__parse_meta_connection_tag(original)['start_port']
300-
assert result['destination_port'] == conntrack.__parse_meta_connection_tag(original)['end_port']
301-
assert result['source_stats']['packets'] == conntrack.__parse_meta_connection_tag(original)['packets']
302-
assert result['source_stats']['bytes'] == conntrack.__parse_meta_connection_tag(original)['bytes']
303-
assert result['destination_stats']['packets'] == conntrack.__parse_meta_connection_tag(reply)['packets']
304-
assert result['destination_stats']['bytes'] == conntrack.__parse_meta_connection_tag(reply)['bytes']
270+
assert result['source'] == '192.168.122.234'
271+
assert result['destination'] == '31.14.133.122'
272+
assert result['protocol'] == 'udp'
273+
assert result['source_port'] == '41692'
274+
assert result['destination_port'] == '123'
275+
assert result['source_stats']['packets'] == 1
276+
assert result['source_stats']['bytes'] == 76
277+
assert result['destination_stats']['packets'] == 1
278+
assert result['destination_stats']['bytes'] == 76
305279
assert result['timeout'] == '47'
306280
assert result['id'] == '1905826093'
307281
flow = ElementTree.fromstring(conntrack_response).findall('flow')[1]

0 commit comments

Comments
 (0)