diff --git a/regress/bin/of_kmod_setup.pl b/regress/bin/of_kmod_setup.pl index 5571dff..fc3ef4b 100755 --- a/regress/bin/of_kmod_setup.pl +++ b/regress/bin/of_kmod_setup.pl @@ -9,6 +9,7 @@ my $mapFile, $controller; # Process command line options unless ( GetOptions( "map=s" => \$mapFile, + "emerg" => \$emerg, "controller=s", \$controller) ) { print "unrecognized option\n"; exit 1; @@ -18,4 +19,4 @@ if ( defined($mapFile) ) { nftest_process_iface_map($mapFile); } -setup_kmod($controller); +setup_kmod($controller, $emerg); diff --git a/regress/bin/of_kmod_veth_setup.pl b/regress/bin/of_kmod_veth_setup.pl index 2270a3a..79a3773 100755 --- a/regress/bin/of_kmod_veth_setup.pl +++ b/regress/bin/of_kmod_veth_setup.pl @@ -9,6 +9,7 @@ my ($mapFile, $controller); # Process command line options unless ( GetOptions( "map=s" => \$mapFile, + "emerg" => \$emerg, "controller=s", \$controller) ) { print "unrecognized option\n"; exit 1; @@ -22,4 +23,4 @@ else { nftest_process_iface_map("$ENV{'OFT_ROOT'}/bin/veth.map"); } -setup_kmod($controller); +setup_kmod($controller, $emerg); diff --git a/regress/bin/of_nf2_setup.pl b/regress/bin/of_nf2_setup.pl index 1363d72..34fe335 100755 --- a/regress/bin/of_nf2_setup.pl +++ b/regress/bin/of_nf2_setup.pl @@ -13,6 +13,7 @@ print Dumper(@ARGV) . "\n"; # Process command line options unless ( GetOptions( "map=s" => \$mapFile, + "emerg" => \$emerg, "controller=s", \$controller) ) { print "unrecognized option\n"; exit 1; @@ -22,4 +23,4 @@ if ( defined($mapFile) ) { nftest_process_iface_map($mapFile); } -setup_NF2($controller); +setup_NF2($controller, $emerg); diff --git a/regress/bin/of_user_setup.pl b/regress/bin/of_user_setup.pl index 896a821..5ff8603 100755 --- a/regress/bin/of_user_setup.pl +++ b/regress/bin/of_user_setup.pl @@ -9,6 +9,7 @@ my ($mapFile, $controller); # Process command line options unless ( GetOptions( "map=s" => \$mapFile, + "emerg" => \$emerg, "controller=s", \$controller) ) { print "unrecognized option\n"; exit 1; @@ -18,4 +19,4 @@ if ( defined($mapFile) ) { nftest_process_iface_map($mapFile); } -setup_user($controller); +setup_user($controller, $emerg); diff --git a/regress/bin/of_user_veth_setup.pl b/regress/bin/of_user_veth_setup.pl index be8fafd..d5ae5ed 100755 --- a/regress/bin/of_user_veth_setup.pl +++ b/regress/bin/of_user_veth_setup.pl @@ -9,6 +9,7 @@ my ($mapFile, $controller); # Process command line options unless ( GetOptions( "map=s" => \$mapFile, + "emerg" => \$emerg, "controller=s", \$controller) ) { print "unrecognized option\n"; exit 1; @@ -22,4 +23,4 @@ else { nftest_process_iface_map("$ENV{'OFT_ROOT'}/bin/veth.map"); } -setup_user($controller); +setup_user($controller, $emerg); diff --git a/regress/lib/Perl5/OF/OFUtil.pm b/regress/lib/Perl5/OF/OFUtil.pm index 35ed533..e6cdea2 100644 --- a/regress/lib/Perl5/OF/OFUtil.pm +++ b/regress/lib/Perl5/OF/OFUtil.pm @@ -78,6 +78,9 @@ use Time::HiRes qw(sleep gettimeofday tv_interval usleep); &create_flow_mod_from_icmp_action &create_flow_mod_from_icmp &wait_for_two_flow_expired + &get_dpinst + &wait_for_echo_request + &del_flows ); my $nf2_kernel_module_path = 'datapath/linux-2.6'; @@ -172,13 +175,16 @@ sub setup_pcap_interfaces { sub start_ofprotocol { - my ( $dpinst, $controller ) = @_; - if ( !$controller) { $controller = nftest_default_controllers(); } - - my $cmd = "${openflow_dir}/secchan/ofprotocol $dpinst $controller --inactivity-probe=999999 &"; - - print "about to run $cmd\n"; - system($cmd); + my ( $dpinst, $controller, $emerg ) = @_; + if ( !$controller) { $controller = nftest_default_controllers(); } + my $cmd; + if (defined $emerg) { + $cmd = "${openflow_dir}/secchan/ofprotocol $dpinst $controller --emerg-flow --inactivity-probe=10 &"; + } else { + $cmd = "${openflow_dir}/secchan/ofprotocol $dpinst $controller --inactivity-probe=999999 &"; + } + print "about to run $cmd\n"; + system($cmd); } sub setup_kmod { @@ -1056,7 +1062,7 @@ sub create_flow_mod_from_udp_action { flags => $flags, priority => 0, buffer_id => -1, - out_port => $enums{'OFPP_NONE'}, + out_port => $enums{'OFPP_NONE'} }; my $flow_mod = $ofp->pack( 'ofp_flow_mod', $flow_mod_args ); my $flow_mod_pkt = combine_args($flow_mod, $mod_type, $out_port, $chg_field, $chg_val); @@ -1916,5 +1922,62 @@ sub wait_for_two_flow_expired { sleep 3; } +sub wait_for_echo_request { + + my ( $ofp, $sock, $options_ref, $read_size_ ) = @_; + my $read_size; + + if ( defined $read_size_ ) { + $read_size = $read_size_; + } else { + $read_size = 1512; + } + + my $recvd_mesg; + sysread( $sock, $recvd_mesg, $read_size ) + || die "Failed to receive ofp_echo_request message: $!"; + + #print HexDump ($recvd_mesg); + + # Inspect message + my $msg_size = length($recvd_mesg); + my $expected_size = $ofp->sizeof('ofp_header'); + compare( "ofp_echo_reply msg size", length($recvd_mesg), '==', $expected_size ); + + my $msg = $ofp->unpack( 'ofp_header', $recvd_mesg ); + + #print Dumper($msg); + # Verify fields + compare( "header version", $$msg{'version'}, '==', $of_ver ); + compare( "header type", $$msg{'type'}, '==', $enums{'OFPT_ECHO_REQUEST'} ); + compare( "header length", $$msg{'length'}, '==', $msg_size ); + + return $$msg{'xid'}; +} + +sub get_dpinst { + my ($options_ref) = @_; + + my $platform = $$options_ref{'common-st-args'}; + my $kmod_dpinst = "nl:0"; + my $user_dpinst = "unix:/var/run/test"; + my $dpinst; + + if (($platform eq 'user') or ($platform eq 'user_veth')) { + $dpinst = $user_dpinst; + } else { + $dpinst = $kmod_dpinst; + } + + return $dpinst; +} + +sub del_flows { + my ($options_ref) = @_; + + my $dpinst = get_dpinst($options_ref); + `dpctl del-flows $dpinst`; +} + # Always end library in 1 1; diff --git a/regress/lib/Perl5/Test/RegressTest.pm b/regress/lib/Perl5/Test/RegressTest.pm index c4c3293..ee53c5f 100644 --- a/regress/lib/Perl5/Test/RegressTest.pm +++ b/regress/lib/Perl5/Test/RegressTest.pm @@ -527,6 +527,10 @@ sub runTest { $args .= " --less_ports"; } + if ( defined($commonSTArgs) ) { + $args .= " --common-st-args=$commonSTArgs"; + } + if ( -d "$_ROOT_DIR/$projectRoot/$project/$regressRoot/$test" ) { return runScript( $project, $test, $run, REQUIRED, $args ); } diff --git a/regress/lib/Perl5/Test/TestLib.pm b/regress/lib/Perl5/Test/TestLib.pm index 60811a7..54c8f6b 100644 --- a/regress/lib/Perl5/Test/TestLib.pm +++ b/regress/lib/Perl5/Test/TestLib.pm @@ -229,6 +229,7 @@ sub nftest_init { "base_idle=i", "ignore_byte_count", "num_ports=i", + "common-st-args=s", "less_ports" ) ) diff --git a/regress/projects/controller_disconnect/regress/common/setup b/regress/projects/controller_disconnect/regress/common/setup new file mode 100755 index 0000000..f796718 --- /dev/null +++ b/regress/projects/controller_disconnect/regress/common/setup @@ -0,0 +1,49 @@ +#!/usr/bin/perl -w + +use Getopt::Long; + +use Test::TestLib; +use OF::OFUtil; + +my $mapFile; +my $platform; +my $args; + +# Process command line options +unless ( + GetOptions ( + "map=s" => \$mapFile, + "common-st-args=s" => \$platform, + ) +) +{ + print "invalid command format\n"; + exit(1); +} + +if (defined($mapFile)) { + $args = "--map=$mapFile"; +} + +if (!defined($platform)) { + print "no platform defined\n"; + exit(1); +} +else { + print "platform = $platform\n"; +} + +$args .= " --emerg"; + +my $filename = "of_${platform}_setup.pl"; + +# exit if of_${platform}_setup.pl not in path +if (-e "$ENV{'OFT_ROOT'}/bin/$filename") { + #system("$filename " . $args . " 2> /dev/null > /dev/null"); + system("$ENV{'OFT_ROOT'}/bin/$filename " . $args ); + #setup_kmod(); + exit (0); +} else { + print "couldn't find setup file $filename\n"; + exit (1); +} diff --git a/regress/projects/controller_disconnect/regress/common/teardown b/regress/projects/controller_disconnect/regress/common/teardown new file mode 100755 index 0000000..6f1f839 --- /dev/null +++ b/regress/projects/controller_disconnect/regress/common/teardown @@ -0,0 +1,47 @@ +#!/usr/bin/perl -w + +use Getopt::Long; + +use Test::TestLib; +use OF::OFUtil; + +my $mapFile; +my $platform; +my $args; + +# Process command line options +unless ( + GetOptions ( + "map=s" => \$mapFile, + "common-st-args=s" => \$platform, + ) +) +{ + print "invalid command format\n"; + exit(1); +} + +if (defined($mapFile)) { + $args = "--map=$mapFile"; +} + +if (!defined($platform)) { + print "no platform defined\n"; + exit(1); +} +else { + print "platform = $platform\n"; +} + +my $filename = "of_${platform}_teardown.pl"; + +# exit if of_${platform}_setup.pl not in path +if (-e "$ENV{'OFT_ROOT'}/bin/$filename") { + #system("$filename " . $args . " 2> /dev/null > /dev/null"); + system("$ENV{'OFT_ROOT'}/bin/$filename " . $args ); + #teardown_kmod(); + exit (0); +} else { + print "couldn't find setup file $filename\n"; + exit (1); +} diff --git a/regress/projects/controller_disconnect/regress/test_emergency_table/run.pl b/regress/projects/controller_disconnect/regress/test_emergency_table/run.pl new file mode 100755 index 0000000..081ae05 --- /dev/null +++ b/regress/projects/controller_disconnect/regress/test_emergency_table/run.pl @@ -0,0 +1,105 @@ +#!/usr/bin/perl -w +# test_emergency_table + +use strict; +use OF::Includes; +use OF::OFUtil; + +sub test_emergency_cache_first { + my ( $ofp, $sock, $options_ref, $i, $j, $wildcards ) = @_; + + my $max_idle = $$options_ref{'max_idle'}; + my $pkt_len = $$options_ref{'pkt_len'}; + my $in_port = $i + $$options_ref{'port_base'}; + my $out_port = $j + $$options_ref{'port_base'}; + my $test_pkt_args = { + DA => "00:00:00:00:00:0" . ( $out_port ), + SA => "00:00:00:00:00:0" . ( $in_port ), + src_ip => "192.168.200." . ( $in_port ), + dst_ip => "192.168.201." . ( $out_port ), + ttl => 64, + len => $pkt_len, + src_port => 70, + dst_port => 80 + }; + my $test_pkt = new NF2::UDP_pkt(%$test_pkt_args); + + print "Set both normal and emergency flow table. Normal key must win\n"; + + # 1st flow entry -- exact match, normal flow table + my $max_idle_no_expire = 0; + my $normal_wildcards = 0x0; # exact match + my $normal_flags = $enums{'OFPFF_SEND_FLOW_EXP'}; # want flow expiry + my $flow_mod_normal_pkt = + create_flow_mod_from_udp( $ofp, $test_pkt, $in_port, $out_port, $max_idle_no_expire, $normal_flags, $normal_wildcards ); + + # 2nd flow entry -- wildcard match all, emergency flow table + my $emergency_wildcards = $enums{'OFPFW_ALL'}; # wildcard match all to the all ports + my $emergency_flags = $enums{'OFPFF_EMERG'}; + my $flow_mod_emergency_pkt = + create_flow_mod_from_udp( $ofp, $test_pkt, $in_port, $enums{'OFPP_ALL'}, $max_idle, $emergency_flags, $emergency_wildcards ); + + #print HexDump($flow_mod_normal_pkt); + #print HexDump($flow_mod_emergency_pkt); + + # Send 'flow_mod' message + print $sock $flow_mod_normal_pkt; + print "sent flow_mod message (normal table)\n"; + + # Give OF switch time to process the flow mod + usleep($$options_ref{'send_delay'}); + + # Send 2nd 'flow_mod' message + print $sock $flow_mod_emergency_pkt; + print "sent flow_mod message (emergency table)\n"; + + # Give OF switch time to process the flow mod + usleep($$options_ref{'send_delay'}); + + # Send a packet - ensure packet comes out desired port + print "Verify packets are forwarded correctly\n"; + nftest_send( "eth" . ( $i + 1 ), $test_pkt->packed ); + nftest_expect( "eth" . ( $j + 1 ), $test_pkt->packed ); + + # Wait for ECHO_REQUEST but don't reply so that ofprotocol notices disconnection. + wait_for_echo_request ( $ofp, $sock, $options_ref, $ofp->sizeof('ofp_header')); + return $test_pkt; +} + +sub test_emergency_cache_second { + my ( $test_pkt, $options_ref, $i, $j ) = @_; + + print "sending from $i to $j, but expect the packet from all ports\n"; + nftest_send( "eth" . ( $i + 1 ), $test_pkt->packed ); + + # expect packets on all other interfaces + print "expect multiple packets\n"; + + for ( my $k = 0 ; $k < $$options_ref{'num_ports'} ; $k++ ) { + if ( $k != $i ) { + nftest_expect( "eth" . ( $k + 1), $test_pkt->packed ); + } + } +} + +sub my_test { + my ($sock, $options_ref) = @_; + + #This test uses two ports + my $inport = 0; + my $outport = 1; + my $wildcards = 0; #exact match + + # Wait until switch notices disconnection. it depends on implementation + my $wait_timer = 20; + + my $test_pkt = test_emergency_cache_first($ofp, $sock, $options_ref, $inport, $outport, $wildcards); + + # Wait until ofprotocol notices that connection is broken + sleep $wait_timer; + + # chek if the emergency table has become active + test_emergency_cache_second($test_pkt, $options_ref, $inport, $outport); +} + +run_black_box_test( \&my_test, \@ARGV ); diff --git a/regress/projects/controller_disconnect/regress/tests.txt b/regress/projects/controller_disconnect/regress/tests.txt new file mode 100644 index 0000000..0d34d9b --- /dev/null +++ b/regress/projects/controller_disconnect/regress/tests.txt @@ -0,0 +1,2 @@ +test_emergency_table/run.pl +#test_reconnect/run.pl diff --git a/regress/projects/regress.txt b/regress/projects/regress.txt index 702ffd3..9339cd3 100644 --- a/regress/projects/regress.txt +++ b/regress/projects/regress.txt @@ -1,2 +1,3 @@ black_box +controller_disconnect learning_switch