[Asterisk-code-review] Add sipp-sendfax.xml and spandspflow2pcap.py to contrib/scri... (asterisk[master])

Walter Doekes asteriskteam at digium.com
Tue Jan 5 09:21:13 CST 2016


Walter Doekes has uploaded a new change for review.

  https://gerrit.asterisk.org/1908

Change subject: Add sipp-sendfax.xml and spandspflow2pcap.py to contrib/scripts.
......................................................................

Add sipp-sendfax.xml and spandspflow2pcap.py to contrib/scripts.

The spandspflow2pcap.py creates pcap files from fax.log files, generated
through 'fax set debug on' when receiving a fax.

The sipp-sendfax.xml SIPp scenario can be used to replay that fax with a
recent version of SIPp.

ASTERISK-25660 #close

Change-Id: I4de8f28b084055b482ab8a5b28d28b605b0ed526
---
A contrib/scripts/sipp-sendfax.xml
A contrib/scripts/spandspflow2pcap.py
2 files changed, 528 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/08/1908/1

diff --git a/contrib/scripts/sipp-sendfax.xml b/contrib/scripts/sipp-sendfax.xml
new file mode 100644
index 0000000..a249808
--- /dev/null
+++ b/contrib/scripts/sipp-sendfax.xml
@@ -0,0 +1,331 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+<scenario name="INVITE (optional auth), re-INVITE to T38 and send a Fax, Walter Doekes 2012-2013">
+
+  <!--
+
+  NOTE: Creating a sipp-sendfax.pcap is as easy as:
+  - receive a fax with asterisk
+  - get the incoming side of the spansdp.log (use 'fax set debug on',
+    check your logger.conf)
+  - feed it to spandspflow2pcap.py
+
+  NOTE: sipp-sendfax.xml requires image pcap play support in SIPp. This
+  means a version above 3.5.0, or the master git branch from
+  https://github.com/SIPp/sipp.
+
+  -->
+
+  <label id="invite"/>
+
+  <send retrans="500" start_txn="invite">
+    <![CDATA[
+
+      INVITE sip:[tel]@[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: sip:[service]@[local_ip]:[local_port];tag=[pid]SIPpTag00[call_number]
+      To: sip:[tel]@[remote_ip]:[remote_port]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      Call-ID: [call_id]
+      CSeq: [cseq] INVITE
+      Max-Forwards: 70
+      Content-Type: application/sdp
+      Content-Length: [len]
+
+      v=0
+      o=- 144969 144969 IN IP[local_ip_type] [local_ip]
+      s=-
+      c=IN IP[media_ip_type] [media_ip]
+      t=0 0
+      m=audio [media_port] RTP/AVP 8 0
+      a=rtpmap:8 PCMA/8000
+      a=rtpmap:0 PCMU/8000
+
+    ]]>
+  </send>
+
+  <recv response="100" optional="true" response_txn="invite"/>
+
+  <recv response="180" optional="true" response_txn="invite"/>
+
+  <recv response="181" optional="true" response_txn="invite"/>
+
+  <recv response="183" optional="true" response_txn="invite"/>
+
+  <recv response="200" optional="true" rrs="true" response_txn="invite" next="invite-ack"/>
+
+  <recv response="401" optional="true" rrs="true" next="invite-with-auth" auth="true" rrs="true" response_txn="invite"/>
+
+  <recv response="407" auth="true" rrs="true" response_txn="invite"/>
+
+  <label id="invite-with-auth"/>
+
+  <send ack_txn="invite">
+    <![CDATA[
+
+      ACK sip:[tel]@[remote_ip]:[remote_port] SIP/2.0
+      [last_Via:]
+      [last_From:]
+      [last_To:]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      Call-ID: [call_id]
+      CSeq: [cseq] ACK
+      Max-Forwards: 70
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <send retrans="500" start_txn="invite">
+    <![CDATA[
+
+      INVITE sip:[tel]@[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: sip:[service]@[local_ip]:[local_port];tag=[pid]SIPpTag00[call_number]
+      To: sip:[tel]@[remote_ip]:[remote_port]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      [authentication]
+      Call-ID: [call_id]
+      CSeq: [cseq] INVITE
+      Max-Forwards: 70
+      Content-Type: application/sdp
+      Content-Length: [len]
+
+      v=0
+      o=- 144969 144969 IN IP[local_ip_type] [local_ip]
+      s=-
+      c=IN IP[media_ip_type] [media_ip]
+      t=0 0
+      m=audio [media_port] RTP/AVP 8 0
+      a=rtpmap:8 PCMA/8000
+      a=rtpmap:0 PCMU/8000
+
+    ]]>
+  </send>
+
+  <recv response="100" response_txn="invite"/>
+
+  <recv response="180" optional="true" response_txn="invite"/>
+
+  <recv response="181" optional="true" response_txn="invite"/>
+
+  <recv response="183" optional="true" response_txn="invite"/>
+
+  <recv response="200" rrs="true" response_txn="invite"/>
+
+  <label id="invite-ack"/>
+
+  <send ack_txn="invite">
+    <![CDATA[
+
+      ACK [next_url] SIP/2.0
+      [last_Via:]
+      [routes]
+      [last_From:]
+      [last_To:]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      Call-ID: [call_id]
+      CSeq: [cseq] ACK
+      Max-Forwards: 70
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <!-- Send a bit of noise to start the RTP. You may need to use
+       the -i MY_IP command line option. -->
+  <pause milliseconds="500"/>
+
+  <nop>
+    <action>
+      <exec play_pcap_audio="g711a.pcap"/>
+    </action>
+  </nop>
+
+  <pause milliseconds="500"/>
+
+
+  <!-- *****************************************************************
+
+  Initiate re-INVITE to T38.
+
+  ****************************************************************** -->
+
+  <send start_txn="t38invite">
+    <![CDATA[
+
+      INVITE [next_url] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      [routes]
+      [last_From:]
+      [last_To:]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      Call-ID: [call_id]
+      CSeq: [cseq] INVITE
+      Max-Forwards: 70
+      Content-Type: application/sdp
+      Content-Length: [len]
+
+      v=0
+      o=- 145280 145280 IN IP[local_ip_type] [local_ip]
+      s=-
+      c=IN IP[media_ip_type] [media_ip]
+      t=0 0
+      m=image [media_port] udptl t38
+      a=T38FaxVersion:0
+      a=T38MaxBitRate:14400
+      a=T38FaxRateManagement:transferredTCF
+      a=T38FaxMaxBuffer:200
+      a=T38FaxMaxDatagram:200
+      a=T38FaxUdpEC:t38UDPRedundancy
+
+    ]]>
+  </send>
+
+  <recv response="100" optional="true" response_txn="t38invite"/>
+
+  <recv response="488" optional="true" response_txn="t38invite" next="abort"/>
+
+  <recv response="200" response_txn="t38invite"/>
+
+  <send ack_txn="t38invite">
+    <![CDATA[
+
+      ACK [next_url] SIP/2.0
+      [last_Via:]
+      [routes]
+      [last_From:]
+      [last_To:]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      Call-ID: [call_id]
+      CSeq: [cseq] ACK
+      Max-Forwards: 70
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <!-- Enable the nop/action below to replay the FAX image. You may need to use
+       the -i MY_IP command line option. -->
+  <pause milliseconds="500"/>
+
+  <nop>
+    <action>
+      <exec play_pcap_image="sipp-sendfax.pcap"/>
+    </action>
+  </nop>
+
+
+  <!-- *****************************************************************
+
+  Wait for re-INVITE back to audio.
+
+  ****************************************************************** -->
+
+  <recv request="INVITE"/>
+
+  <send retrans="500">
+    <![CDATA[
+
+      SIP/2.0 200 OK
+      [last_Via:]
+      [last_Record-Route:]
+      [last_From:]
+      [last_To:]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      [last_Call-ID:]
+      [last_CSeq:]
+      Max-Forwards: 70
+      Content-Type: application/sdp
+      Content-Length: [len]
+
+      v=0
+      o=- 146312 146312 IN IP[local_ip_type] [local_ip]
+      s=-
+      c=IN IP[media_ip_type] [media_ip]
+      t=0 0
+      m=audio [media_port] RTP/AVP 8 0
+      a=rtpmap:8 PCMA/8000
+      a=rtpmap:0 PCMU/8000
+
+    ]]>
+  </send>
+
+  <recv request="ACK"/>
+
+  <recv request="BYE"/>
+
+  <send next="done">
+    <![CDATA[
+
+      SIP/2.0 200 OK
+      [last_Via:]
+      [last_Record-Route:]
+      [last_From:]
+      [last_To:]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      [last_Call-ID:]
+      [last_CSeq:]
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+
+  <!-- *****************************************************************
+
+  Abort the call ourselves
+
+  ****************************************************************** -->
+
+  <label id="abort"/>
+
+  <send ack_txn="t38invite">
+    <![CDATA[
+
+      ACK [next_url] SIP/2.0
+      [last_Via:]
+      [routes]
+      [last_From:]
+      [last_To:]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      Call-ID: [call_id]
+      CSeq: [cseq] ACK
+      Max-Forwards: 70
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <send start_txn="bye">
+    <![CDATA[
+
+      BYE [next_url] SIP/2.0
+      [last_Via:]
+      [routes]
+      [last_From:]
+      [last_To:]
+      Contact: sip:[service]@[local_ip]:[local_port]
+      Call-ID: [call_id]
+      CSeq: [cseq] BYE
+      Max-Forwards: 70
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" response_txn="bye" next="done"/>
+
+
+  <!-- *****************************************************************
+
+  Finalize
+
+  ****************************************************************** -->
+
+  <label id="done"/>
+
+  <!-- Keep call open to be able to retransmit stuff -->
+  <timewait milliseconds="2000"/>
+
+</scenario><!-- vim: set ts=8 sw=2 sts=2 et ai: -->
diff --git a/contrib/scripts/spandspflow2pcap.py b/contrib/scripts/spandspflow2pcap.py
new file mode 100755
index 0000000..a6546b6
--- /dev/null
+++ b/contrib/scripts/spandspflow2pcap.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+# vim: set ts=8 sw=4 sts=4 et ai tw=79:
+'''
+Usage: ./spandspflow2pcap.py SPANDSP_LOG SENDFAX_PCAP
+
+Takes a log from Asterisk with SpanDSP, extracts the "received" data
+and puts it in a pcap file. Use 'fax set debug on' and configure
+logger.conf to get fax logs.
+
+Input data should look something like this::
+
+    [2013-08-07 15:17:34] FAX[23479] res_fax.c: FLOW T.38 Rx     5: IFP c0 01 ...
+
+Output data will look like a valid pcap file ;-)
+
+This allows you to reconstruct received faxes into replayable pcaps.
+
+Replaying is expected to be done by SIPp with sipp-sendfax.xml. The
+SIPp binary used for replaying must have image (fax) support. This means
+you'll need a version higher than 3.5.0 (unreleased when writing this),
+or the git master branch: https://github.com/SIPp/sipp
+
+
+Author: Walter Doekes, OSSO B.V. (2013,2015,2016)
+License: Public Domain
+'''
+from base64 import b16decode
+from datetime import datetime, timedelta
+from re import search
+from time import mktime
+from struct import pack
+import sys
+
+
+LOSSY = False
+EMPTY_RECOVERY = False
+
+
+def n2b(text):
+    return b16decode(text.replace(' ', '').replace('\n', '').upper())
+
+
+class FaxPcap(object):
+    PCAP_PREAMBLE = n2b('d4 c3 b2 a1 02 00 04 00'
+                        '00 00 00 00 00 00 00 00'
+                        'ff ff 00 00 71 00 00 00')
+
+    def __init__(self, outfile):
+        self.outfile = outfile
+        self.date = None
+        self.dateoff = timedelta(seconds=0)
+        self.seqno = None
+        self.udpseqno = 128
+        self.prev_data = None
+
+        # Only do this if at pos 0?
+        self.outfile.write(self.PCAP_PREAMBLE)
+
+    def data2packet(self, date, udpseqno, seqno, data, prev_data):
+        sum16 = '\x43\x21'  # checksum is irrelevant for sipp sending
+
+        new_prev = data  # without seqno..
+        data = '%s%s' % (pack('>H', seqno), data)
+        if prev_data:
+            if LOSSY and (seqno % 3) == 2:
+                return '', new_prev
+            if EMPTY_RECOVERY:
+                # struct ast_frame f[16], we have room for a few
+                # packets.
+                packets = 14
+                data += '\x00%c%s%s' % (
+                    chr(packets + 1), '\x00' * packets, prev_data)
+            else:
+                # Add 1 previous packet, without the seqno.
+                data += '\x00\x01' + prev_data
+
+        kwargs = {'udpseqno': pack('>H', udpseqno), 'sum16': sum16}
+
+        kwargs['data'] = data
+        kwargs['lenb16'] = pack('>H', len(kwargs['data']) + 8)
+        udp = '\x00\x01\x00\x02%(lenb16)s%(sum16)s%(data)s' % kwargs
+
+        kwargs['data'] = udp
+        kwargs['lenb16'] = pack('>H', len(kwargs['data']) + 20)
+        ip = ('\x45\xb8%(lenb16)s%(udpseqno)s\x00\x00\xf9\x11%(sum16)s\x01'
+              '\x01\x01\x01\x02\x02\x02\x02%(data)s') % kwargs
+
+        kwargs['data'] = ip
+        frame = ('\x00\x00\x00\x01\x00\x06\x00\x30\x48\xb1\x1c\x34\x00\x00'
+                 '\x08\x00%(data)s') % kwargs
+
+        kwargs['data'] = frame
+        sec = mktime(date.timetuple())
+        msec = date.microsecond
+        datalen = len(kwargs['data'])
+        kwargs['pre'] = pack('<IIII', sec, msec, datalen, datalen)
+        packet = '%(pre)s%(data)s' % kwargs
+
+        return (packet, new_prev)
+
+    def add(self, date, seqno, data):
+        if self.seqno is None:
+            self.seqno = 0
+            for i in range(seqno):
+                # In case the first zeroes were dropped, add them.
+                self.add(date, i, '\x00')
+        assert seqno == self.seqno, '%s != %s' % (seqno, self.seqno)
+
+        # Data is prepended by len(data).
+        data = chr(len(data)) + data
+
+        # Auto-increasing dates
+        if self.date is None or date > self.date:
+            # print 'date is larger', date, self.date
+            self.date = date
+        elif (date < self.date.replace(microsecond=0)):
+            assert False, ('We increased too fast.. decrease delta: %r/%r' %
+                           (date, self.date))
+        else:
+            self.date += timedelta(microseconds=9000)
+
+        print seqno, '\t', self.date + self.dateoff
+
+        # Make packet.
+        packet, prev_data = self.data2packet(self.date + self.dateoff,
+                                             self.udpseqno, self.seqno,
+                                             data, self.prev_data)
+        self.outfile.write(packet)
+
+        # Increase values.
+        self.udpseqno += 1
+        self.seqno += 1
+        self.prev_data = prev_data
+
+    def add_garbage(self, date):
+        if self.date is None or date > self.date:
+            self.date = date
+
+        packet, ignored = self.data2packet(self.date, self.udpseqno,
+                                           0xffff, 'GARBAGE', '')
+        self.udpseqno += 1
+
+        self.outfile.write(packet)
+
+
+with open(sys.argv[1], 'r') as infile:
+    with open(sys.argv[2], 'wb') as outfile:
+        first = True
+        p = FaxPcap(outfile)
+        # p.add(datetime.now(), 0, n2b('06'))
+        # p.add(datetime.now(), 1, n2b('c0 01 80 00 00 ff'))
+
+        for lineno, line in enumerate(infile):
+            # Look for lines like:
+            # [2013-08-07 15:17:34] FAX[23479] res_fax.c: \
+            #   FLOW T.38 Rx     5: IFP c0 01 80 00 00 ff
+            if 'FLOW T.38 Rx' not in line:
+                continue
+            if 'IFP' not in line:
+                continue
+
+            match = search(r'(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)', line)
+            assert match
+            date = datetime(*[int(i) for i in match.groups()])
+
+            match = search(r'Rx\s*(\d+):', line)
+            assert match
+            seqno = int(match.groups()[0])
+
+            match = search(r': IFP ([0-9a-f ]+)', line)
+            assert match
+            data = n2b(match.groups()[0])
+
+            # Have the file start a second early.
+            if first:
+                p.add_garbage(date)
+                first = False
+
+            # Add the packets.
+            #
+            # T.38 basic format of UDPTL payload section with redundancy:
+            #
+            # UDPTL_SEQNO
+            # - 2 sequence number (big endian)
+            # UDPTL_PRIMARY_PAYLOAD (T30?)
+            # - 1 subpacket length (excluding this byte)
+            # - 1 type of message (e.g. 0xd0 for data(?))
+            # - 1 items in data field (e.g. 0x01)
+            # - 2 length of data (big endian)
+            # - N data
+            # RECOVERY (optional)
+            # - 2 count of previous seqno packets (big endian)
+            # - N UDPTL_PRIMARY_PAYLOAD of (seqno-1)
+            # - N UDPTL_PRIMARY_PAYLOAD of (seqno-2)
+            # - ...
+            #
+            p.add(date, seqno, data)

-- 
To view, visit https://gerrit.asterisk.org/1908
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I4de8f28b084055b482ab8a5b28d28b605b0ed526
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Walter Doekes <walter+asterisk at wjd.nu>



More information about the asterisk-code-review mailing list