Skip to content

Commit d0386fc

Browse files
authored
Merge pull request #222 from DavideAG/packetcapture_service
Added Packetcapture service
2 parents 09aa416 + d8be778 commit d0386fc

42 files changed

Lines changed: 6921 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ The following people, in alphabetical order, have either authored or signed
22
off on commits in the Polycube repository:
33

44
Aasif Shaikh aasif@shaikh.cc
5+
Davide Antonino Giorgio davideantonino94@gmail.com
56
Francesco Messina francescomessina92@hotmail.com
67
Fulvio Risso fulvio.risso@polito.it
78
Gianluca Scopelliti gianlu.1033@gmail.com
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
Packetcapture service
2+
=====================
3+
4+
Packetcapture is a transparent service that allows to capture packets flowing through the interface it is attached to, apply (simple) filters and obtain capture in *.pcap* format. In particular, the service supports either saving captured packets in the local filesystem (e.g., useful in case of high network traffic) or it can interact and deliver packets to a remote client that stores them in the remote filesystem.
5+
6+
An example of a client that uses the REST api of the packetcapture service is available in '*Packetcapture_Client*' directory.
7+
8+
Features
9+
--------
10+
- Transparent service, can be attached to any interface of any Polycube service
11+
- Support for (simple) IPv4 filters: source prefix, destination prefix, source port, destination port and layer 4 protocol.
12+
- Support partial capture of packets (i.e., snaplen)
13+
- Support localmode (store data locally) or network mode (send packets to a remote client) operations
14+
15+
Limitations
16+
-----------
17+
- No IPv6 filtering
18+
- Traffic is returned as is, without any anonimization primitive.
19+
20+
How to use
21+
----------
22+
The packetcapture service is a transparent service, it can be attached to a cube port.
23+
24+
Create the service
25+
^^^^^^^^^^^^^^^^^^
26+
27+
::
28+
29+
#create the packetcapture service
30+
polycubectl packetcapture add sniffer capture=bidirectional
31+
32+
This service can operate in four working modes (actually, the forth mode is just to turn the capture off):
33+
34+
- capture only incoming packets: **capture=ingress**
35+
- capture only outgoing packets: **capture=egress**
36+
- capture both incoming and outgoing packets: **capture=bidirectional**
37+
- turn packet capture off: **capture=off**
38+
39+
*capture* option indicates the direction of the packets that the service must capture.
40+
The direction of the captured packets is independent of the operation in "nework mode" or "non network mode".
41+
42+
In this example the service named '*mysniffer*' will work in bidirectional mode.
43+
44+
45+
Attach to a cube port
46+
^^^^^^^^^^^^^^^^^^^^^
47+
::
48+
49+
# Attach the service to a cube port
50+
polycubectl attach mysniffer br1:toveth1
51+
52+
Now the packetcapture service is attached to the port *toveth1* of the bridge *br1*
53+
54+
+----------+
55+
veth1 ---**x**-| br1 |------ veth2
56+
+----------+
57+
58+
59+
Filters
60+
-------
61+
Traffic can be selected by means of the following filters:
62+
63+
- source prefix
64+
- destination prefix
65+
- source port
66+
- destination port
67+
- later 4 protocol
68+
69+
Source prefix filter
70+
^^^^^^^^^^^^^^^^^^^^
71+
::
72+
73+
# Example of the source prefix filter
74+
polycubectl mysniffer filters set src=10.10.10.10/24
75+
76+
Destination prefix filter
77+
^^^^^^^^^^^^^^^^^^^^^^^^^
78+
::
79+
80+
# Example of the destination prefix filter
81+
polycubectl mysniffer filters set dst=10.10.10.10/24
82+
83+
Source port filter
84+
^^^^^^^^^^^^^^^^^^
85+
::
86+
87+
# Example of the source port filter
88+
polycubectl mysniffer filters set sport=80
89+
90+
Destination port filter
91+
^^^^^^^^^^^^^^^^^^^^^^^
92+
::
93+
94+
# Example of the destination port filter
95+
polycubectl mysniffer filters set dport=80
96+
97+
Layer 4 protocol filter
98+
^^^^^^^^^^^^^^^^^^^^^^^
99+
::
100+
101+
# Example of the layer 4 protocol filter
102+
polycubectl mysniffer filters set l4proto=tcp
103+
104+
Snaplen filter
105+
^^^^^^^^^^^^^^
106+
::
107+
108+
# Example of the snaplen filter
109+
# In this case we capture only the first 80 bytes of each packet
110+
polycubectl mysniffer filters set snaplen=80
111+
112+
113+
Filters can be viewed using the command **polycubectl mysniffer filters show**
114+
115+
Get the capture dump
116+
--------------------
117+
When the service is not set in *networkmode*, the dump is automatically written in a resilient way in the temporary user folder.
118+
119+
The path of the capture file can be shown using the command: **polycubectl mysniffer show dump**
120+
121+
Otherwise, if the service is set in network mode, the capture file can be requested through the use of the provided Python client, or queried simply through the service API.
122+
123+
How to use the demo client
124+
^^^^^^^^^^^^^^^^^^^^^^^^^^
125+
::
126+
127+
# Start the client script
128+
python3 client.py <IPv4 address> <file destination name>
129+
130+
131+
Set network mode
132+
^^^^^^^^^^^^^^^^
133+
::
134+
135+
# Start sniffer in network mode
136+
polycubectl mysniffer set networkmode=true
137+
138+
# Start sniffer in local model
139+
polycubectl mysniffer set networkmode=false

src/services/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ add_service(simpleforwarder pcn-simpleforwarder)
3535
add_service(iptables pcn-iptables)
3636
add_service(transparenthelloworld pcn-transparent-helloworld)
3737
add_service(synflood pcn-synflood)
38+
add_service(packetcapture pcn-packetcapture)
3839

3940
# save string to create code that load the services
4041
SET_PROPERTY(GLOBAL PROPERTY LOAD_SERVICES_ ${LOAD_SERVICES})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Swagger Codegen Ignore
2+
# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
6+
.swagger-codegen-ignore
7+
8+
src/*.cpp
9+
src/*.h
10+
11+
!src/*Interface.h
12+
!src/*JsonObject.h
13+
!src/*JsonObject.cpp
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
cmake_minimum_required (VERSION 3.2)
2+
3+
set (CMAKE_CXX_STANDARD 11)
4+
5+
add_subdirectory(src)
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# import modules
2+
import struct
3+
import time
4+
import binascii
5+
import sys
6+
import os
7+
import json
8+
import requests
9+
import ipaddress
10+
import logging
11+
import threading
12+
import time
13+
from requests.exceptions import HTTPError
14+
15+
# Global Header Values
16+
PCAP_MAGICAL_NUMBER = 0
17+
PCAP_MJ_VERN_NUMBER = 0
18+
PCAP_MI_VERN_NUMBER = 0
19+
PCAP_LOCAL_CORECTIN = 0
20+
PCAP_ACCUR_TIMSTAMP = 0
21+
PCAP_MAX_LENGTH_CAP = 0
22+
PCAP_DATA_LINK_TYPE = 0
23+
service_name = ''
24+
service_num = 0
25+
Nservices = 1
26+
f = 0
27+
stop = False
28+
29+
30+
def connect(address):
31+
url = 'http://'+address+':9000/polycube/v1/cubes/'
32+
try:
33+
response = requests.get(url, timeout=8)
34+
response.raise_for_status()
35+
except HTTPError as http_err:
36+
print(f'HTTP error occurred: {http_err}')
37+
exit()
38+
except Exception as err:
39+
print(f'Other error occurred: {err}')
40+
exit()
41+
else:
42+
if response.text == "{}":
43+
print('\t=== No packetcapture services up ===')
44+
exit()
45+
print('\t===Packetcapture Client===\n\n')
46+
return json.loads(response.text)
47+
48+
49+
def printnames(todos):
50+
global Nservices
51+
print('services:\n')
52+
for pktcap in todos['packetcapture']:
53+
print(str(Nservices) + ' -> ' + pktcap['name'])
54+
Nservices+=1
55+
Nservices-=1
56+
print()
57+
58+
59+
def selectService(todos, address):
60+
global service_num, Nservices, service_name
61+
try:
62+
service_num = int(input('Please select the service number: '))
63+
if service_num > Nservices or service_num < 1:
64+
exit()
65+
service_name = todos['packetcapture'][service_num-1]['name']
66+
url = 'http://'+address+':9000/polycube/v1/packetcapture/'+service_name+'/'
67+
try:
68+
response = requests.get(url, timeout=8)
69+
response.raise_for_status()
70+
except HTTPError as http_err:
71+
print(f'HTTP error occurred: {http_err}')
72+
exit()
73+
except Exception as err:
74+
print(f'Other error occurred: {err}')
75+
exit()
76+
todos2 = json.loads(response.text)
77+
if todos2['networkmode'] is False:
78+
print('-----------------------------------')
79+
networkmode_response = input('\n\t=== The service is not in network mode ===\nDo you want to set up this service in networkmode (y / n)? ')
80+
if networkmode_response != 'y':
81+
exit()
82+
else:
83+
url = 'http://'+address+':9000/polycube/v1/'+service_name+'/networkmode/'
84+
requests.patch(url, 'true')
85+
except:
86+
print("Closing...")
87+
88+
89+
def showusage():
90+
print("client.py <IPv4 address> <file destination name>")
91+
print("\t-h/--help: show the usage menu")
92+
93+
94+
def checkIp(param):
95+
if param == 'localhost':
96+
return True
97+
try:
98+
ret = ipaddress.ip_address(param)
99+
if isinstance(ret,ipaddress.IPv4Address) is False:
100+
print("Supports only IPv4")
101+
exit()
102+
except:
103+
print("Invalid IP address")
104+
exit()
105+
106+
107+
def initGlobalHeader(address):
108+
global service_name, PCAP_MAGICAL_NUMBER, PCAP_MJ_VERN_NUMBER, PCAP_MI_VERN_NUMBER, PCAP_LOCAL_CORECTIN, PCAP_ACCUR_TIMSTAMP, PCAP_MAX_LENGTH_CAP, PCAP_DATA_LINK_TYPE
109+
url = 'http://'+address+':9000/polycube/v1/packetcapture/'+service_name+'/globalheader/'
110+
try:
111+
response = requests.get(url, timeout=8)
112+
response.raise_for_status()
113+
except HTTPError as http_err:
114+
print(f'HTTP error occurred: {http_err}')
115+
exit()
116+
except Exception as err:
117+
print(f'Other error occurred: {err}')
118+
exit()
119+
todos3 = json.loads(response.text)
120+
PCAP_MAGICAL_NUMBER = todos3['magic']
121+
PCAP_MJ_VERN_NUMBER = todos3['version_major']
122+
PCAP_MI_VERN_NUMBER = todos3['version_minor']
123+
PCAP_LOCAL_CORECTIN = todos3['thiszone']
124+
PCAP_ACCUR_TIMSTAMP = todos3['sigfigs']
125+
PCAP_MAX_LENGTH_CAP = todos3['snaplen']
126+
PCAP_DATA_LINK_TYPE = todos3['linktype']
127+
128+
129+
def writeGlobalHeader():
130+
global f
131+
f = open(sys.argv[2] + ".pcap", "ab+")
132+
f.write(struct.pack('@ I H H i I I I ', PCAP_MAGICAL_NUMBER, PCAP_MJ_VERN_NUMBER, PCAP_MI_VERN_NUMBER, PCAP_LOCAL_CORECTIN, PCAP_ACCUR_TIMSTAMP, PCAP_MAX_LENGTH_CAP, PCAP_DATA_LINK_TYPE))
133+
134+
135+
def getAndWritePacket(address):
136+
global f
137+
url = 'http://'+address+':9000/polycube/v1/packetcapture/'+service_name+'/packet/'
138+
try:
139+
response = requests.get(url, timeout=8)
140+
response.raise_for_status()
141+
except HTTPError as http_err:
142+
print(f'HTTP error occurred: {http_err}')
143+
exit()
144+
except Exception as err:
145+
print(f'Other error occurred: {err}')
146+
exit()
147+
todos4 = json.loads(response.text)
148+
if todos4['rawdata'] != "":
149+
f.write(struct.pack('@ I I I I ', todos4['timestamp-seconds'], todos4['timestamp-microseconds'], todos4['capturelen'], todos4['packetlen']))
150+
asciidata = todos4['rawdata'].encode("latin-1","strict")
151+
f.write(asciidata)
152+
else:
153+
time.sleep(0.1)
154+
155+
156+
def listening():
157+
global f, stop
158+
while(1):
159+
if stop:
160+
f.close()
161+
break
162+
getAndWritePacket(sys.argv[1])
163+
164+
165+
def main():
166+
global stop
167+
168+
#Checking arguments
169+
if len(sys.argv) != 3 or sys.argv[1] == "-h" or sys.argv[1] == "--help":
170+
showusage()
171+
exit()
172+
173+
checkIp(sys.argv[1])
174+
todos = connect(sys.argv[1])
175+
printnames(todos)
176+
selectService(todos, sys.argv[1])
177+
initGlobalHeader(sys.argv[1])
178+
writeGlobalHeader()
179+
listener = threading.Thread(target=listening)
180+
listener.start()
181+
while(1):
182+
usr_cmd = input('write "stop" to end the capture: ')
183+
if usr_cmd == 'stop':
184+
stop = True
185+
break
186+
listener.join()
187+
188+
189+
if __name__ == "__main__":
190+
main()

0 commit comments

Comments
 (0)