Skip to content

Commit

Permalink
Merge pull request #222 from DavideAG/packetcapture_service
Browse files Browse the repository at this point in the history
Added Packetcapture service
  • Loading branch information
frisso committed Nov 16, 2019
2 parents 09aa416 + d8be778 commit d0386fc
Show file tree
Hide file tree
Showing 42 changed files with 6,921 additions and 0 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ The following people, in alphabetical order, have either authored or signed
off on commits in the Polycube repository:

Aasif Shaikh aasif@shaikh.cc
Davide Antonino Giorgio davideantonino94@gmail.com
Francesco Messina francescomessina92@hotmail.com
Fulvio Risso fulvio.risso@polito.it
Gianluca Scopelliti gianlu.1033@gmail.com
Expand Down
139 changes: 139 additions & 0 deletions Documentation/services/pcn-packetcapture/Packetcapture.rst
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
1 change: 1 addition & 0 deletions src/services/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ add_service(simpleforwarder pcn-simpleforwarder)
add_service(iptables pcn-iptables)
add_service(transparenthelloworld pcn-transparent-helloworld)
add_service(synflood pcn-synflood)
add_service(packetcapture pcn-packetcapture)

# save string to create code that load the services
SET_PROPERTY(GLOBAL PROPERTY LOAD_SERVICES_ ${LOAD_SERVICES})
Expand Down
13 changes: 13 additions & 0 deletions src/services/pcn-packetcapture/.swagger-codegen-ignore
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
5 changes: 5 additions & 0 deletions src/services/pcn-packetcapture/CMakeLists.txt
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 src/services/pcn-packetcapture/Packetcapture_Client/client.py
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()

0 comments on commit d0386fc

Please sign in to comment.