-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #222 from DavideAG/packetcapture_service
Added Packetcapture service
- Loading branch information
Showing
42 changed files
with
6,921 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 139 additions & 0 deletions
139
Documentation/services/pcn-packetcapture/Packetcapture.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
Packetcapture service | ||
===================== | ||
|
||
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. | ||
|
||
An example of a client that uses the REST api of the packetcapture service is available in '*Packetcapture_Client*' directory. | ||
|
||
Features | ||
-------- | ||
- Transparent service, can be attached to any interface of any Polycube service | ||
- Support for (simple) IPv4 filters: source prefix, destination prefix, source port, destination port and layer 4 protocol. | ||
- Support partial capture of packets (i.e., snaplen) | ||
- Support localmode (store data locally) or network mode (send packets to a remote client) operations | ||
|
||
Limitations | ||
----------- | ||
- No IPv6 filtering | ||
- Traffic is returned as is, without any anonimization primitive. | ||
|
||
How to use | ||
---------- | ||
The packetcapture service is a transparent service, it can be attached to a cube port. | ||
|
||
Create the service | ||
^^^^^^^^^^^^^^^^^^ | ||
|
||
:: | ||
|
||
#create the packetcapture service | ||
polycubectl packetcapture add sniffer capture=bidirectional | ||
|
||
This service can operate in four working modes (actually, the forth mode is just to turn the capture off): | ||
|
||
- capture only incoming packets: **capture=ingress** | ||
- capture only outgoing packets: **capture=egress** | ||
- capture both incoming and outgoing packets: **capture=bidirectional** | ||
- turn packet capture off: **capture=off** | ||
|
||
*capture* option indicates the direction of the packets that the service must capture. | ||
The direction of the captured packets is independent of the operation in "nework mode" or "non network mode". | ||
|
||
In this example the service named '*mysniffer*' will work in bidirectional mode. | ||
|
||
|
||
Attach to a cube port | ||
^^^^^^^^^^^^^^^^^^^^^ | ||
:: | ||
|
||
# Attach the service to a cube port | ||
polycubectl attach mysniffer br1:toveth1 | ||
|
||
Now the packetcapture service is attached to the port *toveth1* of the bridge *br1* | ||
|
||
+----------+ | ||
veth1 ---**x**-| br1 |------ veth2 | ||
+----------+ | ||
|
||
|
||
Filters | ||
------- | ||
Traffic can be selected by means of the following filters: | ||
|
||
- source prefix | ||
- destination prefix | ||
- source port | ||
- destination port | ||
- later 4 protocol | ||
|
||
Source prefix filter | ||
^^^^^^^^^^^^^^^^^^^^ | ||
:: | ||
|
||
# Example of the source prefix filter | ||
polycubectl mysniffer filters set src=10.10.10.10/24 | ||
|
||
Destination prefix filter | ||
^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
:: | ||
# Example of the destination prefix filter | ||
polycubectl mysniffer filters set dst=10.10.10.10/24 | ||
|
||
Source port filter | ||
^^^^^^^^^^^^^^^^^^ | ||
:: | ||
# Example of the source port filter | ||
polycubectl mysniffer filters set sport=80 | ||
|
||
Destination port filter | ||
^^^^^^^^^^^^^^^^^^^^^^^ | ||
:: | ||
# Example of the destination port filter | ||
polycubectl mysniffer filters set dport=80 | ||
|
||
Layer 4 protocol filter | ||
^^^^^^^^^^^^^^^^^^^^^^^ | ||
:: | ||
# Example of the layer 4 protocol filter | ||
polycubectl mysniffer filters set l4proto=tcp | ||
|
||
Snaplen filter | ||
^^^^^^^^^^^^^^ | ||
:: | ||
# Example of the snaplen filter | ||
# In this case we capture only the first 80 bytes of each packet | ||
polycubectl mysniffer filters set snaplen=80 | ||
|
||
|
||
Filters can be viewed using the command **polycubectl mysniffer filters show** | ||
|
||
Get the capture dump | ||
-------------------- | ||
When the service is not set in *networkmode*, the dump is automatically written in a resilient way in the temporary user folder. | ||
|
||
The path of the capture file can be shown using the command: **polycubectl mysniffer show dump** | ||
|
||
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. | ||
|
||
How to use the demo client | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
:: | ||
# Start the client script | ||
python3 client.py <IPv4 address> <file destination name> | ||
|
||
|
||
Set network mode | ||
^^^^^^^^^^^^^^^^ | ||
:: | ||
# Start sniffer in network mode | ||
polycubectl mysniffer set networkmode=true | ||
|
||
# Start sniffer in local model | ||
polycubectl mysniffer set networkmode=false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Swagger Codegen Ignore | ||
# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen | ||
|
||
# Use this file to prevent files from being overwritten by the generator. | ||
|
||
.swagger-codegen-ignore | ||
|
||
src/*.cpp | ||
src/*.h | ||
|
||
!src/*Interface.h | ||
!src/*JsonObject.h | ||
!src/*JsonObject.cpp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
cmake_minimum_required (VERSION 3.2) | ||
|
||
set (CMAKE_CXX_STANDARD 11) | ||
|
||
add_subdirectory(src) |
190 changes: 190 additions & 0 deletions
190
src/services/pcn-packetcapture/Packetcapture_Client/client.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
# import modules | ||
import struct | ||
import time | ||
import binascii | ||
import sys | ||
import os | ||
import json | ||
import requests | ||
import ipaddress | ||
import logging | ||
import threading | ||
import time | ||
from requests.exceptions import HTTPError | ||
|
||
# Global Header Values | ||
PCAP_MAGICAL_NUMBER = 0 | ||
PCAP_MJ_VERN_NUMBER = 0 | ||
PCAP_MI_VERN_NUMBER = 0 | ||
PCAP_LOCAL_CORECTIN = 0 | ||
PCAP_ACCUR_TIMSTAMP = 0 | ||
PCAP_MAX_LENGTH_CAP = 0 | ||
PCAP_DATA_LINK_TYPE = 0 | ||
service_name = '' | ||
service_num = 0 | ||
Nservices = 1 | ||
f = 0 | ||
stop = False | ||
|
||
|
||
def connect(address): | ||
url = 'http://'+address+':9000/polycube/v1/cubes/' | ||
try: | ||
response = requests.get(url, timeout=8) | ||
response.raise_for_status() | ||
except HTTPError as http_err: | ||
print(f'HTTP error occurred: {http_err}') | ||
exit() | ||
except Exception as err: | ||
print(f'Other error occurred: {err}') | ||
exit() | ||
else: | ||
if response.text == "{}": | ||
print('\t=== No packetcapture services up ===') | ||
exit() | ||
print('\t===Packetcapture Client===\n\n') | ||
return json.loads(response.text) | ||
|
||
|
||
def printnames(todos): | ||
global Nservices | ||
print('services:\n') | ||
for pktcap in todos['packetcapture']: | ||
print(str(Nservices) + ' -> ' + pktcap['name']) | ||
Nservices+=1 | ||
Nservices-=1 | ||
print() | ||
|
||
|
||
def selectService(todos, address): | ||
global service_num, Nservices, service_name | ||
try: | ||
service_num = int(input('Please select the service number: ')) | ||
if service_num > Nservices or service_num < 1: | ||
exit() | ||
service_name = todos['packetcapture'][service_num-1]['name'] | ||
url = 'http://'+address+':9000/polycube/v1/packetcapture/'+service_name+'/' | ||
try: | ||
response = requests.get(url, timeout=8) | ||
response.raise_for_status() | ||
except HTTPError as http_err: | ||
print(f'HTTP error occurred: {http_err}') | ||
exit() | ||
except Exception as err: | ||
print(f'Other error occurred: {err}') | ||
exit() | ||
todos2 = json.loads(response.text) | ||
if todos2['networkmode'] is False: | ||
print('-----------------------------------') | ||
networkmode_response = input('\n\t=== The service is not in network mode ===\nDo you want to set up this service in networkmode (y / n)? ') | ||
if networkmode_response != 'y': | ||
exit() | ||
else: | ||
url = 'http://'+address+':9000/polycube/v1/'+service_name+'/networkmode/' | ||
requests.patch(url, 'true') | ||
except: | ||
print("Closing...") | ||
|
||
|
||
def showusage(): | ||
print("client.py <IPv4 address> <file destination name>") | ||
print("\t-h/--help: show the usage menu") | ||
|
||
|
||
def checkIp(param): | ||
if param == 'localhost': | ||
return True | ||
try: | ||
ret = ipaddress.ip_address(param) | ||
if isinstance(ret,ipaddress.IPv4Address) is False: | ||
print("Supports only IPv4") | ||
exit() | ||
except: | ||
print("Invalid IP address") | ||
exit() | ||
|
||
|
||
def initGlobalHeader(address): | ||
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 | ||
url = 'http://'+address+':9000/polycube/v1/packetcapture/'+service_name+'/globalheader/' | ||
try: | ||
response = requests.get(url, timeout=8) | ||
response.raise_for_status() | ||
except HTTPError as http_err: | ||
print(f'HTTP error occurred: {http_err}') | ||
exit() | ||
except Exception as err: | ||
print(f'Other error occurred: {err}') | ||
exit() | ||
todos3 = json.loads(response.text) | ||
PCAP_MAGICAL_NUMBER = todos3['magic'] | ||
PCAP_MJ_VERN_NUMBER = todos3['version_major'] | ||
PCAP_MI_VERN_NUMBER = todos3['version_minor'] | ||
PCAP_LOCAL_CORECTIN = todos3['thiszone'] | ||
PCAP_ACCUR_TIMSTAMP = todos3['sigfigs'] | ||
PCAP_MAX_LENGTH_CAP = todos3['snaplen'] | ||
PCAP_DATA_LINK_TYPE = todos3['linktype'] | ||
|
||
|
||
def writeGlobalHeader(): | ||
global f | ||
f = open(sys.argv[2] + ".pcap", "ab+") | ||
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)) | ||
|
||
|
||
def getAndWritePacket(address): | ||
global f | ||
url = 'http://'+address+':9000/polycube/v1/packetcapture/'+service_name+'/packet/' | ||
try: | ||
response = requests.get(url, timeout=8) | ||
response.raise_for_status() | ||
except HTTPError as http_err: | ||
print(f'HTTP error occurred: {http_err}') | ||
exit() | ||
except Exception as err: | ||
print(f'Other error occurred: {err}') | ||
exit() | ||
todos4 = json.loads(response.text) | ||
if todos4['rawdata'] != "": | ||
f.write(struct.pack('@ I I I I ', todos4['timestamp-seconds'], todos4['timestamp-microseconds'], todos4['capturelen'], todos4['packetlen'])) | ||
asciidata = todos4['rawdata'].encode("latin-1","strict") | ||
f.write(asciidata) | ||
else: | ||
time.sleep(0.1) | ||
|
||
|
||
def listening(): | ||
global f, stop | ||
while(1): | ||
if stop: | ||
f.close() | ||
break | ||
getAndWritePacket(sys.argv[1]) | ||
|
||
|
||
def main(): | ||
global stop | ||
|
||
#Checking arguments | ||
if len(sys.argv) != 3 or sys.argv[1] == "-h" or sys.argv[1] == "--help": | ||
showusage() | ||
exit() | ||
|
||
checkIp(sys.argv[1]) | ||
todos = connect(sys.argv[1]) | ||
printnames(todos) | ||
selectService(todos, sys.argv[1]) | ||
initGlobalHeader(sys.argv[1]) | ||
writeGlobalHeader() | ||
listener = threading.Thread(target=listening) | ||
listener.start() | ||
while(1): | ||
usr_cmd = input('write "stop" to end the capture: ') | ||
if usr_cmd == 'stop': | ||
stop = True | ||
break | ||
listener.join() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Oops, something went wrong.