The main purpose of this script is to generate a diagram such as Wireshark do with sip/rtp captures, obviously without the need to open Wireshark and using your mouse.

43eb28c56995898e2f3fa528bdfbfbb7.png

More over, it can be used to automatically generate diagrams from a bunch of pcap or directly in a web interface.

Whis this need and the desire to discover a little more python I tried to develop a script which is by the way not (I guess) the best way to achieve, but it works and this is what I want !

How it works

It uses mutiples “default” and “not default” python libraries :

  • python3 : it’s better to run the script 😉
  • git : if you want to clone directly from github
  • tshark and pyshark : to manage packets
  • markdown and md-mermaid : for drawing the diagram

Please note that it was tested successfully on Ubuntu 21.10 (my personnal laptop) and on a fresh Debian 11 virtual machine. It’s possible that I missed up some dependencies on Ubuntu because of some already present on my laptop (it should not I think).

Setup env

Install the tools named above

apt update
apt install git tshark python3-pip --no-install-recommends
pip3 install pyshark markdown md-mermaid

Then clone the github repo

git clone https://github.com/fulljackz/DrawMyCall.git
cd DrawMyCall

Here we are, we have a folder containing this stuff

├── drawmycall.py
├── html
│   ├── mermaid.css
│   ├── mermaid.min.js
│   └── sip-rtp-g722.pcap.html
├── img
│   ├── mermaid.png
│   ├── mermaid-time.png
│   └── wireshark.png
├── pcap_samples
│   ├── sip-rtp-g722.pcap
│   ├── sip-rtp-g729a.pcap
│   ├── sip-rtp-gsm.pcap
│   ├── sip-rtp-ilbc.pcap
│   ├── sip-rtp-lpc.pcap
│   └── sip-rtp-opus.pcap
└── README.md
  • drawmycall.py is the script intersting us.
  • ./html/ is the folder contaning the .css and .js files producing the pcap.html files.
  • ./img/ is a folder for documentation images.
  • ./pcap_samples/ contains .pcap files for testing. They are directly available from wireshark wiki.

If you open the sip-rtp-g722.pcap.html file in ./html/ folder you’ll have an overview of what the script can do : c4ac7f1809fb62d767ce94f4f9c57c90.png

Code notes

I didn’t want to have to deal with arguments manually, something had to exist for my need and I was right. Not sure if it is the niceset solution (once again), but as I said before : it works !

To deal with arguments and print help I did this :

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    # Show args as required : https://bugs.python.org/issue9694
    req_grp = parser.add_argument_group(title='required arguments')
    req_grp.add_argument("-f", "--file", required=True, help="path to your pcap file")
    parser.add_argument("-t", "--time", help="Add time on diagram",
            action="store_true")
    args = parser.parse_args()

It is simple as the code. The only thing to note here is the required=True for the --file argument.

The script contains only a main function, it uses the pyshark library and parse packet per packet searching for call informations :

pcapFile = pyshark.FileCapture(capture)
# Parse .pcap 
for num in pcapFile:

If the packet in the loop has rtp attribute and length > 100 (because of bad rtp packets) or sip attribute, it set a different separator (plain or dot line for merrmaid), and it saves some informations such as source ip, destination ip, rtp payload type, sip status etc.

# check if packet has rtp attribute
if hasattr(num, 'rtp') and int(num.length) >= 100:
	diff = "-->"
	diagSeq = [num['IP'].src, diff, num['IP'].dst, diff, num['RTP'].p_type.showname]

# check if packet has sip attribute
if hasattr(num, 'sip'):
	diff = "->>"
	if hasattr(num['sip'], 'status_line'):
		status = num['sip'].status_line
	else:
		status = num['sip'].method
	diagSeq = [num['IP'].src, diff, num['IP'].dst, diff, status]

To avoid multiples identical lines on the diagram, script makes a copy of diagSeq[] in sequences[] only if diagSeq[x] is not already in sequences[]

if diagSeq not in sequences:
	sequences.append(diagSeq)
	timestamp.append(num.frame_info.time_relative)

Then, it starts from the first value and loop over sequences[] array to build the diagram structure in markdown into mermaidMarkdown[]. If -t option is set it add the time on the left. Finally, it convert mermaidMarkdown[] array to mm string converted to html using the md_mermaidextension.

y = sequences[0][0]
mermaidMarkdown.append("{} {}".format("\n~~~mermaid\n", "sequenceDiagram\n"))
for x in sequences:
	mermaidMarkdown.append("{} {} {} {} {} {}".format(x[0], x[1], x[2], ":", x[4], "\n"))
	if time:
		mermaidMarkdown.append("{} {} {} {} {}".format("Note left of ", y, ":", timestamp[i], "\n"))
	i+=1
mermaidMarkdown.append("{} {}".format("\n~~~\n", '<script src="./mermaid.min.js"></script>'))
mm = "".join(mermaidMarkdown)
html = markdown.markdown(mm, extensions=['md_mermaid'])
out.write(html)
out.close()

Give a try

The first thing we can try is the help function :

./drawmycall.py --help
usage: drawmycall.py [-h] -f FILE [-t]

optional arguments:
  -h, --help            show this help message and exit
  -t, --time            Add time on diagram

required arguments:
  -f FILE, --file FILE  path to your pcap file

Let’s generate a diagram with the ./pcap_samples/sip-rp-opus.pcap, with wireshark :

73500313b5056e4e9dbdd5bd1dc345cb.png

And with drawmycall.py :

./drawmycall.py -f ./pcap_samples/sip-rtp-opus.pcap

Open ./html/sip-rtp-opus.pcap.html

500490cfc96946519ebf22b01b7bf1d6.png

Note : To avoid multiple rtp lines (which would be a mess), there is only one line showing the voice payloads.

If you need to get the time since first packet in your capture, add -t option to the command line

./drawmycall.py -f ./pcap_samples/sip-rtp-opus.pcap -t

Which should output this html file, with time since first packet on the left

13a2b0b48421b3bba3cccc2fd5ce62ac.png

Enjoy 😉