2

I currently have two hosts which run a client and server Python program which send TLS traffic to one another - I have tested this outside of mininet to confirm it works (and it does!).

However, the goal here is to use tcpdump/tshark/wireshark to capture the TLS traffic between these two hosts. I have tried things such as using quietRun or subprocess.Popen to call tcpdump -i any -w capture.pcap however these do not seem to capture the traffic for my hosts, or they stall until I ctrl+c and/or go straight to the CLI(net).

For reference; this is all using mininet CLI - the aim is to do this programmatically

Below is the current code:

#!/usr/bin/python
# -*- coding: utf-8 -*-

# from sys import exit  # pylint: disable=redefined-builtin
import sys
import os
import subprocess
import time
from functools import partial

from mininet.node import Host, UserSwitch, OVSKernelSwitch, Controller, Switch
from mininet.topo import Topo, SingleSwitchTopo
from mininet.util import quietRun, pmonitor
from mininet.log import error, lg, info, setLogLevel
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.link import TCLink


class ExperimentTopology(Topo):

    """Custom mininet topology for robot-controller experiments"""

    def __init__(self):
        """Create custom topology"""

        # Initialize topology

        Topo.__init__(self)

        # Add hosts and switches
        switch = self.addSwitch("s1")
        h1= self.addHost("h1")
        h2= self.addHost("h2")

        # Set link parameters (delay, etc.)
        # bw = Bandwidth in Mbps
        # delay = Link delay (s, ms, us)
        # loss = Percentage packet loss
        # max_queue_size = Maximum queue size
        # use_htb = Use the Hierarchical Token Bucket rate limiter and netem delay/loss emulator?
        # linkopts = dict(bw=10, delay="5ms", loss=10) #max_queue_size=1000

        # Add links
        self.addLink(switch, h1)  # to use params, add ", **linkopts"
        self.addLink(switch, h2)


def main():
    lg.setLogLevel("info")

    # quietRun('tcpdump -i any -w capture.pcap')

    net = Mininet(topo=ExperimentTopology(), waitConnected=True)
    net.start()

    h1= net.get('h1')
    h1p= robot.popen('python3 tls_server.py -i %s -p %d &' % (str(h1.IP()), 443))

    # time.sleep(10)

    h2 = net.get('h2')
    h2.cmd('python3 tls_client.py -i %s -p %d -m %s -d %s -s %s' % (str(h2.IP()), 443, 'x', 1, '12.5'))

    # net.popen('tcpdump -i any -w capture.pcap') # _process = subprocess.Popen(['sudo', 'tcpdump', '-i', 'any', '-w', 'capture.pcap'])

    s1 = net.get('s1')
    s1.cmd(os.system('sudo tshark -w $HOME/captures/capture.pcap'))

    CLI(net)
    h1p.terminate()
    net.stop()
    # _process.terminate()

if __name__ == '__main__':
    main()

** EDIT: TLS client and server files:: **

tls_client.py:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
import ssl
import optparse
import time

from scapy.all import *

load_layer("usb")

parser = optparse.OptionParser()
parser.add_option('-i', dest='ip', default='127.0.0.1')
parser.add_option('-p', dest='port', type='int', default=12345)
parser.add_option('-m', dest='movement', default='x')
parser.add_option('-d', dest='distance', type='int', default=1)
parser.add_option('-s', dest='speed', default='12.5')
(options, args) = parser.parse_args()

hostname = options.ip  # '127.0.0.1'
port = options.port  # 443
context = ssl.SSLContext()

# Confirm these min + max values
MIN_X = 150
MAX_X = 300
MIN_Y = -230
MAX_Y = 230
MIN_Z = -50
MAX_Z = 150

MIN_SPEED = 12.5 # 12.5, 25, 50, 100
MAX_SPEED = 100.0
NUM_RUNS = 5

with socket.create_connection((hostname, port)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        print(ssock.version())

        # Load pcap file
        # x_packets = rdpcap('pcaps/operation_move_x.pcapng')

        # Now we have handshake and socket open, lets send messages
        # Get data from wireshark dump, and use ssock.sendall(bytes)

        # TODO: Set these (x,y,z) to the default starting values for robot
        i = 0
        g = 0
        x = 0.00
        y = 0.00
        z = 0.00
        f = options.speed # 12.5

        if options.movement == 'x':
            for j in range(NUM_RUNS):
                i = 0
                for k in [k for k in range(MIN_X, MAX_X+1, int(options.distance))]:
                    x = k
                    payload = '#' + str(i) + ' G' + str(g) + ' X' + str(x) + ' Y' + str(y) + ' Z' + str(z) + ' F' + str(f)
                    ssock.sendall(bytes(payload, encoding='utf-8'))
                    time.sleep(1)
                    i += 1
        elif options.movement == 'y':
            for j in range(NUM_RUNS):
                i = 0
                for k in [k for k in range(MIN_X, MAX_X+1, int(options.distance))]:
                    y = k
                    payload = '#' + str(i) + ' G' + str(g) + ' X' + str(x) + ' Y' + str(y) + ' Z' + str(z) + ' F' + str(f)
                    ssock.sendall(bytes(payload, encoding='utf-8'))
                    time.sleep(1)
                    i += 1
        elif options.movement == 'z':
            for j in range(NUM_RUNS):
                i = 0
                for k in [k for k in range(MIN_X, MAX_X+1, int(options.distance))]:
                    z = k
                    payload = '#' + str(i) + ' G' + str(g) + ' X' + str(x) + ' Y' + str(y) + ' Z' + str(z) + ' F' + str(f)
                    ssock.sendall(bytes(payload, encoding='utf-8'))
                    time.sleep(1)
                    i += 1
        elif options.movement == 'xy':
            for i in range(NUM_RUNS):
                i = 0
                for k in [k for k in range(MIN_X, MAX_X+1, int(options.distance))]: # y pos will be x-120 (too keep in Y range)
                    x = k
                    y = k-120
                    payload = '#' + str(i) + ' G' + str(g) + ' X' + str(x) + ' Y' + str(y) + ' Z' + str(z) + ' F' + str(f)
                    ssock.sendall(bytes(payload, encoding='utf-8'))
                    time.sleep(1)
                    i += 1
        elif options.movement == 'xz':
            for i in range(NUM_RUNS):
                i = 0
                z = 0
                for k in [k for k in range(MIN_X, MAX_X+1, int(options.distance))]: # z pos will be (x/10)+5 (too keep in Z range)
                    x = k
                    if z == MAX_Z:
                        z = MAX_Z
                    else:
                        z += 1
                    payload = '#' + str(i) + ' G' + str(g) + ' X' + str(x) + ' Y' + str(y) + ' Z' + str(z) + ' F' + str(f)
                    ssock.sendall(bytes(payload, encoding='utf-8'))
                    time.sleep(1)
                    i += 1
        elif options.movement == 'yz':
            for i in range(NUM_RUNS):
                i = 0
                z = 0
                for k in [k for k in range(MIN_Y, MAX_Y+1, int(options.distance))]:
                    y = k
                    if z >= MAX_Z:
                        z = 0
                    else:
                        z += 1
                    payload = '#' + str(i) + ' G' + str(g) + ' X' + str(x) + ' Y' + str(y) + ' Z' + str(z) + ' F' + str(f)
                    ssock.sendall(bytes(payload, encoding='utf-8'))
                    time.sleep(1)
                    i += 1
        elif options.movement == 'xyz':
            for i in range(NUM_RUNS):
                i = 0
                y = MIN_Y
                z = MIN_Z
                for k in [k for k in range(MIN_X, MAX_X+1)]:
                    x = k
                    if y >= MAX_Y:
                        y = 0
                    else:
                        y += 1

                    if z >= MAX_Z:
                        z = 0
                    else:
                        z += 1
                    payload = '#' + str(i) + ' G' + str(g) + ' X' + str(x) + ' Y' + str(y) + ' Z' + str(z) + ' F' + str(f)
                    ssock.sendall(bytes(payload, encoding='utf-8'))
                    time.sleep(1)
                    i += 1

** tls_server.py **

#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
import ssl
import optparse

parser = optparse.OptionParser()
parser.add_option('-i', dest='ip', default='')
parser.add_option('-p', dest='port', type='int', default=12345)
(options, args) = parser.parse_args()

hostname = options.ip  # '127.0.0.1'
port = options.port  # 443

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('robot.cert', 'robot.key')

print('Loaded certificate and key..')

with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
    print('Socket starting..')
    sock.bind((hostname, port))
    sock.listen(25)
    with context.wrap_socket(sock, server_side=True) as ssock:
        print('Socket connection established!')
        (conn, addr) = ssock.accept()

        f = open('server_output.txt', 'w')

        while True:
            message = conn.recv()
            if not message:
                break

            # message = message.decode()

            f.write('%s: %s\n' % (addr, message))
            f.flush()

            # print(message)
rshah
  • 675
  • 2
  • 12
  • 32
  • My answer would be to run a sniffer from your switch, you can do something like: `s1 = net.get("s1") ` `s1.cmd(os.system("tshark -w $YOUR_PATH/%sSniffLog" % str(this_switch)))` (this is two lines but you can't see it in the comment format) Your switch will make a sweet little pcap file for you. – WildWilyWilly Oct 05 '21 at 13:20
  • @WildWilyWilly Thanks for the comment so far! I've updated the OP to include the current code, however it generates the capture.pcap file with two problems - (a) it cannot be opened (no permission) and (b) the program stalls until I ctrl+c out to end the network. Any idea how to get around this so it only captures until the hosts stop interacting? – rshah Oct 05 '21 at 15:57
  • @WildWilyWilly I tried adding `&` to the end of the tshark command, however this runs it in background (which I have to kill via pid after even mininet has stopped running) and is not capturing the packets between my two hosts. – rshah Oct 08 '21 at 08:56
  • How is `sudo` working for you? Is it asking a password? How about adding your user to wireshark group and stop using sudo? Also, trivial but possible, is there network activity between server and client? Tshark starts after the client so the network activity might have stopped by then. – LMC Oct 09 '21 at 16:23
  • 1
    @LMC no `sudo` doesnt ask for a password. There should be network activity between both as I have tested this out of mininet with wireshark.. I've moved the net to before client starts to capture beforehand. Might try adding to wireshark group to stop sudo. – rshah Oct 09 '21 at 16:37
  • tshark will have to run in background or it will hang there. Try it then see how to fix the background issue. – LMC Oct 09 '21 at 16:39
  • @LMC yeah so running in background means using `&` correct? I've added to wireshark group but it still doesnt seem to be capturing the traffic between the two hosts. Perhaps the traffic is not flowing over the switch? I've edited the OP to see my server and client python scripts. – rshah Oct 09 '21 at 17:27
  • yes, adding & at the end to run it in background. – LMC Oct 09 '21 at 17:34
  • @LMC okay, well the & did not do much but it does allow for the capturing to be run at the same time as the network. I've updated the OP to show the client+server code as this may help find the issue. Perhaps nothing is flowing over the switch? – rshah Oct 09 '21 at 18:09
  • That's how far I can help so far unfortunately. Tried your code but fails with `Error setting s1-eth1 up: bash`. Never used mininet before – LMC Oct 09 '21 at 18:23
  • Can you reduce your code to a Minimum Reproducible Example (https://stackoverflow.com/help/minimal-reproducible-example)? It looks like there's a bunch of extraneous code. (reducing the code to the minimum required to reproduce will also help you to identify your issue) – Ross Jacobs Oct 13 '21 at 03:56

1 Answers1

1

I have done this in the past by using the node's popen method to start the pcap and then the terminate to close the process and force it to record the pcap.

Say you have a node called h1. Then you can do

h1_pcap = h1.popen('tcpdump -w h1_dump.pcap')

# Do stuff here
# ...

h1_pcap.terminate()

This should record all traffic on h1 into h1_dump.pcap once the script is executed.

Misho Janev
  • 512
  • 5
  • 13
  • Thanks for the answer! Will having two popens on the same host cause any issues? – rshah Nov 12 '21 at 19:29
  • I do not see why they should. Do you want to run tcpdump twice on the same host for some reason? – Misho Janev Nov 12 '21 at 23:34
  • No, I wish to run a client-server program where h1 would run the client as well as the tcpdump to capture the traffic between both h1 and h2 – rshah Nov 25 '21 at 15:57