Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REFER expectations #15

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a509734
Merge branch 'release/0.7.2' into develop
benlangfeld Jul 13, 2015
4db0184
Happy New Year
bklang Dec 8, 2015
5d159a2
Install pcap spec dependency
bklang May 15, 2016
36ed072
Drop support for Ruby < 2.3
bklang Feb 28, 2017
ee68ead
Upgrade rspec to for modernization
bklang Mar 8, 2017
c7bb9ec
PacketFu 1.1.12 introduces a breaking change, removing PacketFu::UDPP…
bklang Mar 8, 2017
5b182f4
Merge pull request #90 from mojolingo/feature/travis_fixes
bklang Mar 8, 2017
6b5a5c9
Now require Ruby 2.3
bklang Mar 8, 2017
491c0b7
Use transpec to convert to new RSpec expect syntax
chewi May 11, 2017
dedfc0d
Fix non-writing of PCAP file when media is empty
chewi Jun 3, 2015
fe49676
Allow SIPp to be launched without sudo
chewi May 11, 2017
41efd37
Merge pull request #97 from yakara-ltd/feature/nosudo
bklang May 17, 2017
a797d1d
set event end flag for dtmf digits
aukeman Dec 20, 2017
fffcf0c
Merge pull request #99 from aukeman/dtmf-end-flag
bklang Dec 26, 2017
5f6d569
Merge branch 'master' into develop
bklang Dec 26, 2017
6c6288e
Update Nokogiri for security issue
bklang Dec 26, 2017
e8877b1
Merge pull request #100 from mojolingo/feature/deps_update
bklang Dec 26, 2017
b04c7af
PacketFu 1.1.12 didn't really drop PacketFu::UDPPacket
chewi Mar 16, 2018
977ccea
Merge pull request #101 from yakara-ltd/packetfu
bklang Mar 16, 2018
eb34f53
Merge pull request #83 from yakara-ltd/bugfix/nomedia
chewi Jul 11, 2018
d1b52ab
generate more robust DTMF events
aukeman Dec 13, 2018
1ced85c
Merge branch 'develop' of github.com:VHTx/sippy_cup into robust-dtmf-…
aukeman Dec 13, 2018
f778cdf
Merge pull request #103 from VHTx/robust-dtmf-packets
bklang Dec 20, 2018
4169655
[CI] Add CI builds against C-Ruby 2.4, 2.5 and 2.6
sfgeorge Apr 25, 2019
e35b29a
[CI] Add ruby-2.5.3
sfgeorge Aug 12, 2019
c22f47b
[CI] Use Xenial build and rubies that are on it
sfgeorge Sep 10, 2019
d29188f
[SPEC] Make spec a bit more lax for ruby-2.5
sfgeorge Sep 10, 2019
2dad177
Merge pull request #104 from sfgeorge/patch-1
bklang Sep 10, 2019
6ff6747
Bump Nokogiri dep to address security concern
bklang Sep 10, 2019
a1a1936
Merge pull request #105 from mojolingo/feature/nokogiri_upgrade
bklang Sep 11, 2019
cfdf674
Add support for receiving REFERs
sfgeorge Sep 3, 2013
04c7f24
[DOC] Categorize supported commands
sfgeorge Sep 3, 2013
3dba3c7
[DOC] Categorize transfer/REFER commands separately
sfgeorge Sep 3, 2013
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
dist: xenial
language: ruby
rvm:
- 1.9.3
- 2.0.0
- 2.1.0
- 2.3.8
- 2.4.5
- 2.5.3
- 2.6.3
- jruby-19mode
- rbx-19mode
- ruby-head
Expand All @@ -13,4 +15,6 @@ matrix:
- rvm: ruby-head
notifications:
irc: "irc.freenode.org#adhearsion"
sudo: false
before_install:
- rvm list
- sudo apt-get install libpcap-dev -y
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# develop
* Drop support for Ruby < 2.3.0 - Too many gem dependences require Ruby 2.3, so go along with it rather than fight it
* Require PacketFu >= 1.1.13, 1.1.12 was just broken

# [0.7.2](https://github.com/mojolingo/sippy_cup/compare/v0.7.1...v0.7.2)
* Bugfix: Logical destination for dialog formation belongs in request line
Expand Down
20 changes: 16 additions & 4 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ Sippy Cup is a tool to generate [SIPp](http://sipp.sourceforge.net/) load test p

SippyCup relies on the following to generate scenarios and the associated media PCAP files:

* Ruby 1.9.3 or later (2.1.2 recommended)
* Ruby 2.3.0 or later
* [SIPp](http://sipp.sourceforge.net/) latest master branch - Download from https://github.com/sipp/sipp - NOTE: Version SIPp version 3.4 may work, but will be missing certain new Sippy Cup features, such as rate scaling
* "root" user access via sudo: needed to run SIPp so it can bind to raw network sockets

## Installation

If you do not have Ruby 2.1.2 available (check using `ruby --version`), we recommend installing Ruby with [RVM](http://rvm.io)
If you do not have Ruby 2.3.3 available (check using `ruby --version`), we recommend installing Ruby with [RVM](http://rvm.io)

### Install via gem (production)

Expand Down Expand Up @@ -139,7 +139,7 @@ The above code can be executed as a standalone Ruby script and the resulting sce

Each command below can take [SIPp attributes](http://sipp.sourceforge.net/doc/reference.html) as optional arguments. For a full list of available steps with arguments explained, see the [API documentation](http://rubydoc.info/gems/sippy_cup/SippyCup/Scenario).

* `sleep <seconds>` Wait a specified number of seconds
#### Initiation Commands
* `invite` Send a SIP INVITE to the specified target
* `receive_invite` Wait for an INVITE to be received
* `register <username> [password]` Register the specified user to the target with an optional password
Expand All @@ -154,9 +154,21 @@ Each command below can take [SIPp attributes](http://sipp.sourceforge.net/doc/re
* `wait_for_answer` Convenient shortcut for `receive_trying; receive_ringing; receive_progress; receive_answer`, with all but the `answer` marked as optional
* `ack_answer` Send an `ACK` in response to a `200 OK`
* `receive_ack` Expect to receive an `ACK`

#### Interaction Commands
* `send_digits <string>` Send a DTMF string. May send one or many digits, including `0-9`, `*`, `#`, and `A-D`
* `sleep <seconds>` Wait a specified number of seconds
* `receive_ok` Expect to receive a `200 OK`
* `receive_message [regex]` Expect to receive a SIP MESSAGE, optionally matching a regex

#### Transfer Commands
* `receive_refer` Expect to receive a `REFER` from the target
* `ack_refer` Send an ACK in response to a `REFER`
* `notify_refer_ringing` Send a `NOTIFY` with a sipfrag of `180 Ringing`
* `notify_refer_ok` Send a `NOTIFY` with a sipfrag of `200 OK`
* `wait_for_refer` Convenient shortcut for `receive_refer; ack_refer; notify_refer_ringing; receive_answer; notify_refer_ok; receive_answer`

#### Teardown Commands
* `send_bye` Send a `BYE` (hangup request)
* `receive_bye` Expect to receive a `BYE` from the target
* `ack_bye` Send a `200 OK` response to a `BYE`
Expand Down Expand Up @@ -273,7 +285,7 @@ For more information on possible attributes, visit the [SIPp Documentation](http

## Credits

Copyright (C) 2013-2014 [Mojo Lingo LLC](https://mojolingo.com)
Copyright (C) 2013-2015 [Mojo Lingo LLC](https://mojolingo.com)

Sippy Cup is released under the [MIT license](http://opensource.org/licenses/MIT). Please see the [LICENSE](https://github.com/bklang/sippy_cup/blob/master/LICENSE) file for details.

Expand Down
16 changes: 14 additions & 2 deletions lib/sippy_cup/media.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,31 @@ def compile!
# value is the DTMF digit to send
# append that RFC2833 digit
# Assume 0.25 second duration for now
count = 250 / DTMFPayload::PTIME
count = (250 / DTMFPayload::PTIME) + 1
count.times do |i|
packet = new_packet
elapsed += DTMFPayload::PTIME
dtmf_frame = DTMFPayload.new value
dtmf_frame.rtp_marker = 1 if i == 0
dtmf_frame.index = i
dtmf_frame.rtp_timestamp = timestamp # Is this correct? This is what Blink does...
#dtmf_frame.rtp_timestamp = timestamp += dtmf_frame.timestamp_interval
dtmf_frame.rtp_sequence_num = sequence_number += 1
dtmf_frame.rtp_ssrc_id = ssrc_id
dtmf_frame.end_of_event = (count == i) # Last packet?
dtmf_frame.end_of_event = ((count-1) == i) # Last packet?
packet.headers.last.body = dtmf_frame.to_bytes
packet.recalc
@pcap_file.body << get_pcap_packet(packet, next_ts(start_time, elapsed))

# if the end-of-event, add some redundant packets in case of loss
# during transmission
if dtmf_frame.end_of_event
2.times do
dtmf_frame.rtp_sequence_num = sequence_number += 1
packet.recalc
@pcap_file.body << get_pcap_packet(packet, next_ts(start_time, elapsed))
end
end
end
# Now bump up the timestamp to cover the gap
timestamp += count * DTMFPayload::TIMESTAMP_INTERVAL
Expand Down
6 changes: 3 additions & 3 deletions lib/sippy_cup/media/dtmf_payload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class DTMFPayload < RTPPayload
TIMESTAMP_INTERVAL = 160
END_OF_EVENT = 1 << 7
DTMF = %w{0 1 2 3 4 5 6 7 8 9 * # A B C D}.freeze
attr_accessor :ptime
attr_accessor :ptime, :index

def initialize(digit, opts = {})
super RTP_PAYLOAD_ID
Expand Down Expand Up @@ -39,15 +39,15 @@ def volume(value)
end

def end_of_event
@flags & END_OF_EVENT
0 < (@flags & END_OF_EVENT)
end

def media
[@digit, @flags, timestamp_interval].pack 'CCn'
end

def timestamp_interval
TIMESTAMP_INTERVAL
TIMESTAMP_INTERVAL * (index.to_i+1)
end
end
end
Expand Down
5 changes: 3 additions & 2 deletions lib/sippy_cup/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ class Runner
# @param [Scenario, XMLScenario] scenario The scenario to execute
# @param [Hash] opts Options to modify the runner
# @option opts [optional, true, false] :full_sipp_output Whether or not to copy SIPp's stdout/stderr to the parent process. Defaults to true.
# @option opts [optional, true, false] :sudo Whether or not to invoke SIPp with sudo. Defaults to true.
# @option opts [optional, Logger] :logger A logger to use in place of the internal logger to STDOUT.
# @option opts [optional, String] :command The command to execute. This is mostly available for testing.
#
def initialize(scenario, opts = {})
@scenario = scenario
@scenario_options = @scenario.scenario_options

defaults = { full_sipp_output: true }
defaults = { full_sipp_output: true, sudo: true }
@options = defaults.merge(opts)

@command = @options[:command]
Expand Down Expand Up @@ -87,7 +88,7 @@ def wait

def command
@command ||= begin
command = "sudo $(which sipp)"
command = @options[:sudo] ? "sudo $(which sipp)" : 'sipp'
command_options.each_pair do |key, value|
command << (value ? " -#{key} #{value}" : " -#{key}")
end
Expand Down
81 changes: 78 additions & 3 deletions lib/sippy_cup/scenario.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'nokogiri'
require 'psych'
require 'active_support/core_ext/hash'
require 'active_support/core_ext/object/blank'
require 'tempfile'
require 'set'

Expand Down Expand Up @@ -659,7 +660,7 @@ def to_xml(options = {})
@media_nodes.reverse.each do |nop|
nopdup = docdup.xpath(nop.path)

if pcap_path.nil? or @media.empty?
if pcap_path.nil? or @media.blank?
nopdup.remove
else
exec = nopdup.xpath("./action/exec").first
Expand Down Expand Up @@ -694,7 +695,7 @@ def to_xml(options = {})
# scenario.compile! # Leaves files at test_scenario.xml and test_scenario.pcap
#
def compile!
unless @media.nil?
unless @media.blank?
print "Compiling media to #{@filename}.pcap..."
compile_media.to_file filename: "#{@filename}.pcap"
puts "done."
Expand All @@ -710,6 +711,80 @@ def compile!
scenario_filename
end

##
# Shortcut method that tells SIPp to receive and accept a REFER
def wait_for_refer(opts = {})
rrs = opts.delete :rrs
rrs = false if rrs.nil?
receive_refer(opts)
ack_refer(opts)
notify_refer_ringing(opts)
receive_200(opts.merge :rrs => rrs)
notify_refer_ok(opts)
receive_200(opts.merge :rrs => rrs)
end

def receive_refer(opts = {})
@scenario << new_recv(opts.merge request: 'REFER')
end

def ack_refer(opts = {})
msg = <<-ACK

SIP/2.0 202 Accepted
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
Max-Forwards: 100
Content-Length: 0
[routes]
ACK
@scenario << new_send(msg, opts)
end

def notify_refer_ringing(opts = {})
opts.merge! :body => 'SIP/2.0 180 Ringing',
:state => 'active'
notify_refer(opts)
end

def notify_refer_ok(opts = {})
opts.merge! :body => 'SIP/2.0 200 OK',
:state => 'terminated;reason=noresource'
notify_refer(opts)
end

def notify_refer(opts = {})
# @TODO The Event: field MUST contain an ID if multiple REFERs are issued
# @see http://tools.ietf.org/html/rfc3515#section-2.4.6
body = opts.delete(:body) || (raise ArgumentError,
'Each NOTIFY must include a body of type of type "message/sipfrag"')
state = opts.delete(:state) || (raise ArgumentError,
'Each NOTIFY must contain a Subscription-State')
msg = <<-HEREDOC

NOTIFY [next_url] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: "#{@from_user}" <sip:#{@from_user}@[local_ip]>;tag=[call_number]
To: <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
[last_Call-ID:]
CSeq: [cseq] NOTIFY
Event: refer
Subscription-state: #{state}
Content-Type: message/sipfrag;version=2.0
Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
Max-Forwards: 100
Content-Length: [len]
[routes]

#{body}
HEREDOC
@scenario << new_send(msg, opts)
end

#
# Write compiled Scenario XML and PCAP media (if applicable) to tempfiles.
#
Expand All @@ -720,7 +795,7 @@ def compile!
# @see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/Tempfile.html
#
def to_tmpfiles
unless @media.nil? || @media.empty?
unless @media.blank?
media_file = Tempfile.new 'media'
media_file.binmode
media_file.write compile_media.to_s
Expand Down
6 changes: 3 additions & 3 deletions sippy_cup.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ Gem::Specification.new do |s|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]

s.add_runtime_dependency 'packetfu'
s.add_runtime_dependency 'nokogiri', ["~> 1.6.0"]
s.add_runtime_dependency 'packetfu', [">= 1.1.13"]
s.add_runtime_dependency 'nokogiri', ["~> 1.10.4"]
s.add_runtime_dependency 'activesupport', [">= 3.0"]
s.add_runtime_dependency 'psych', ["~> 2.0.1"] unless RUBY_PLATFORM == 'java'

s.add_development_dependency 'guard-rspec'
s.add_development_dependency 'rspec', ["~> 2.11"]
s.add_development_dependency 'rspec', ["~> 3.4"]
s.add_development_dependency 'simplecov'
s.add_development_dependency 'simplecov-rcov'
s.add_development_dependency 'fakefs'
Expand Down
Loading