test: add mininet-test suite

This patch adds a test framework using mininet. The following tests
can be performed.

- Set the flow for the OVS-switch from Ryu-app, we test the actual
  packet is to be handled properly in accordance with the flow; The
  packet that generated by "mz" or replayed by "tcpreplay" is routed
  through the ovs-switch, are processed according to the flow, then
  compare test conditions and the results captured by "tshark".

- Create a packet with ryu using the packet-lib, we test the packet
  and response are correct; to compare test conditions and the results
  captured by "tshark".

  $ ./run_mnet-test.sh [OPTION] [TEST DIR or FILE]...
  $ ./run_mnet-test.sh l2 l3/icmp packet_lib/arp/ARP_gratuitous.mn
  $ ./run_mnet-test.sh --help

Requirements package:
 - mininet: git://github.com/mininet/mininet.git
 - openvswitch: git://openvswitch.org/openvswitch
 - Mausezahn 0.40: http://www.perihel.at/sec/mz/
 - TShark 1.6.2: http://www.wireshark.org/
 - tcpreplay 3.4: http://tcpreplay.synfin.net/

Signed-off-by: HIYAMA Manabu <hiyama.manabu@po.ntts.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
HIYAMA Manabu 2013-01-28 16:01:22 +09:00 committed by FUJITA Tomonori
parent 7d5b4960a8
commit 24d894bc95
18 changed files with 977 additions and 0 deletions

View File

@ -0,0 +1,6 @@
TEST_NAME=MPLS-PopMPLS
DUMP_HOST=h2
DUMP_IF=h2-eth0
RYU_APP=test_mpls
PCAP_MZ="-t tcp -M 80 -P $TEST_NAME -c 3 -r"
PCAP_FILTER="! mpls && ip.proto==TCP"

View File

@ -0,0 +1,6 @@
TEST_NAME=IP-PushMPLS
DUMP_HOST=h2
DUMP_IF=h2-eth0
RYU_APP=test_mpls
PCAP_MZ="-t tcp -P $TEST_NAME -c 3 -b 00:00:00:00:00:02"
PCAP_FILTER="mpls && ip.proto==TCP"

View File

@ -0,0 +1,6 @@
TEST_NAME=MPLS-PushMPLS
DUMP_HOST=h2
DUMP_IF=h2-eth0
RYU_APP=test_mpls
PCAP_MZ="-t tcp -M 100 -P $TEST_NAME -c 3 -r"
PCAP_FILTER="mpls.label==100 && mpls.label==200 && ip.proto==TCP"

View File

@ -0,0 +1,128 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import struct
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller import dpset
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_2
from ryu.ofproto import ether
from ryu.lib.mac import haddr_to_str
LOG = logging.getLogger(__name__)
class RunTestMininet(app_manager.RyuApp):
_CONTEXTS = {'dpset': dpset.DPSet}
OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(RunTestMininet, self).__init__(*args, **kwargs)
self.mac_to_port = {}
def _add_flow(self, dp, match, actions):
inst = [dp.ofproto_parser.OFPInstructionActions(
dp.ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = dp.ofproto_parser.OFPFlowMod(
dp, cookie=0, cookie_mask=0, table_id=0,
command=dp.ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
priority=0xff, buffer_id=0xffffffff,
out_port=dp.ofproto.OFPP_ANY, out_group=dp.ofproto.OFPG_ANY,
flags=0, match=match, instructions=inst)
dp.send_msg(mod)
def _define_flow(self, dp):
in_port = 1
out_port = 2
eth_IP = ether.ETH_TYPE_IP
eth_MPLS = ether.ETH_TYPE_MPLS
# MPLS(80) -> PopMPLS
LOG.debug("--- add_flow PopMPLS")
m_label = 80
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_MPLS)
match.set_mpls_label(m_label)
actions = [dp.ofproto_parser.OFPActionPopMpls(eth_IP),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
self._add_flow(dp, match, actions)
# IP -> PushMPLS(90)
LOG.debug("--- add_flow PushMPLS")
s_label = 90
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_IP)
f = dp.ofproto_parser.OFPMatchField.make(
dp.ofproto.OXM_OF_MPLS_LABEL, s_label)
actions = [dp.ofproto_parser.OFPActionPushMpls(eth_MPLS),
dp.ofproto_parser.OFPActionSetField(f),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
self._add_flow(dp, match, actions)
# MPLS(100) -> PushMPLS(200)
LOG.debug("--- add_flow PushMPLS")
m_label = 100
s_label = 200
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_MPLS)
match.set_mpls_label(m_label)
f = dp.ofproto_parser.OFPMatchField.make(
dp.ofproto.OXM_OF_MPLS_LABEL, s_label)
actions = [dp.ofproto_parser.OFPActionPushMpls(eth_MPLS),
dp.ofproto_parser.OFPActionSetField(f),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
self._add_flow(dp, match, actions)
# MPLS(1000):MPLS -> PopMPLS
# LOG.debug("--- add_flow PopMPLS")
# SKIP: ovs not supported
m_label = 1000
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_MPLS)
match.set_mpls_label(m_label)
actions = [dp.ofproto_parser.OFPActionPopMpls(eth_MPLS),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
# self._add_flow(dp, match, actions)
@set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER)
def handler_datapath(self, ev):
if ev.enter:
self._define_flow(ev.dp)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dst, src, eth_type = struct.unpack_from('!6s6sH', buffer(msg.data), 0)
in_port = msg.match.fields[0].value
LOG.info("----------------------------------------")
LOG.info("* PacketIn")
LOG.info("in_port=%d, eth_type: %s", in_port, hex(eth_type))
LOG.info("packet reason=%d buffer_id=%d", msg.reason, msg.buffer_id)
LOG.info("packet in datapath_id=%s src=%s dst=%s",
msg.datapath.id, haddr_to_str(src), haddr_to_str(dst))

View File

@ -0,0 +1,6 @@
TEST_NAME=VLAN-PopVLAN
DUMP_HOST=h2
DUMP_IF=h2-eth0
RYU_APP=test_vlan
PCAP_MZ="-t tcp -Q 8 -P $TEST_NAME -c 3 -r"
PCAP_FILTER="! vlan && ip.proto==TCP"

View File

@ -0,0 +1,6 @@
TEST_NAME=VLAN:VLAN-PopVLAN
DUMP_HOST=h2
DUMP_IF=h2-eth0
RYU_APP=test_vlan
PCAP_MZ="-t tcp -Q 100,99 -P $TEST_NAME -c 3 -r"
PCAP_FILTER="vlan.id!=100 && vlan.id==99 && ip.proto==TCP"

View File

@ -0,0 +1,6 @@
TEST_NAME=ICMP-PushVLAN
DUMP_HOST=h2
DUMP_IF=h2-eth0
RYU_APP=test_vlan
PCAP_MZ="-t icmp ping -P $TEST_NAME -c 3 -r -b 00:00:00:00:00:02"
PCAP_FILTER="vlan && icmp.type==8"

View File

@ -0,0 +1,131 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import struct
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller import dpset
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_2
from ryu.ofproto import ether
from ryu.ofproto import inet
from ryu.lib.mac import haddr_to_str
LOG = logging.getLogger(__name__)
class RunTestMininet(app_manager.RyuApp):
_CONTEXTS = {'dpset': dpset.DPSet}
OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(RunTestMininet, self).__init__(*args, **kwargs)
self.mac_to_port = {}
def _add_flow(self, dp, match, actions):
inst = [dp.ofproto_parser.OFPInstructionActions(
dp.ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = dp.ofproto_parser.OFPFlowMod(
dp, cookie=0, cookie_mask=0, table_id=0,
command=dp.ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
priority=0xff, buffer_id=0xffffffff,
out_port=dp.ofproto.OFPP_ANY, out_group=dp.ofproto.OFPG_ANY,
flags=0, match=match, instructions=inst)
dp.send_msg(mod)
def _define_flow(self, dp):
in_port = 1
out_port = 2
eth_IP = ether.ETH_TYPE_IP
eth_VLAN = ether.ETH_TYPE_8021Q
ip_ICMP = inet.IPPROTO_ICMP
# VLAN(8) -> PopVLAN
LOG.debug("--- add_flow VLAN(8) to PopVLAN")
m_vid = 8
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_IP)
match.set_vlan_vid(m_vid)
actions = [dp.ofproto_parser.OFPActionPopVlan(),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
self._add_flow(dp, match, actions)
# ICMP -> PushVLAN(9)
LOG.debug("--- add_flow ICMP to PushVLAN(9)")
s_vid = 9
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_IP)
match.set_ip_proto(ip_ICMP)
f = dp.ofproto_parser.OFPMatchField.make(
dp.ofproto.OXM_OF_VLAN_VID, s_vid)
actions = [dp.ofproto_parser.OFPActionPushVlan(eth_VLAN),
dp.ofproto_parser.OFPActionSetField(f),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
self._add_flow(dp, match, actions)
# VLAN(10) -> PushVLAN(20)
# LOG.debug("--- add_flow VLAN(10) to PushVLAN(100)")
# SKIP: ovs not supported
m_vid = 10
s_vid = 20
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_IP)
match.set_vlan_vid(m_vid)
f = dp.ofproto_parser.OFPMatchField.make(
dp.ofproto.OXM_OF_VLAN_VID, s_vid)
actions = [dp.ofproto_parser.OFPActionPushVlan(eth_VLAN),
dp.ofproto_parser.OFPActionSetField(f),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
# self._add_flow(dp, match, actions)
# VLAN(100):VLAN -> PopVLAN
LOG.debug("--- add_flow VLAN(100):VLAN to PopVLAN")
m_vid = 100
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_VLAN)
match.set_vlan_vid(m_vid)
actions = [dp.ofproto_parser.OFPActionPopVlan(),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
self._add_flow(dp, match, actions)
@set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER)
def handler_datapath(self, ev):
if ev.enter:
self._define_flow(ev.dp)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dst, src, eth_type = struct.unpack_from('!6s6sH', buffer(msg.data), 0)
in_port = msg.match.fields[0].value
LOG.info("----------------------------------------")
LOG.info("* PacketIn")
LOG.info("in_port=%d, eth_type: %s", in_port, hex(eth_type))
LOG.info("packet reason=%d buffer_id=%d", msg.reason, msg.buffer_id)
LOG.info("packet in datapath_id=%s src=%s dst=%s",
msg.datapath.id, haddr_to_str(src), haddr_to_str(dst))

View File

@ -0,0 +1,6 @@
TEST_NAME=ICMP-Req
DUMP_HOST=h2
DUMP_IF=h2-eth0
RYU_APP=test_icmp
PCAP_MZ="-t icmp ping -c 3 -r -b 00:00:00:00:00:00"
PCAP_FILTER="icmp.type==8"

View File

@ -0,0 +1,6 @@
TEST_NAME=ICMP-Reply
DUMP_HOST=h1
DUMP_IF=h1-eth0
RYU_APP=test_icmp
PCAP_MZ="-t icmp ping -c 3 -r -B h2"
PCAP_FILTER="icmp.type==0"

View File

@ -0,0 +1,85 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import struct
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller import dpset
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_2
from ryu.lib.mac import haddr_to_str
LOG = logging.getLogger(__name__)
class RunTestMininet(app_manager.RyuApp):
_CONTEXTS = {'dpset': dpset.DPSet}
OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(RunTestMininet, self).__init__(*args, **kwargs)
self.mac_to_port = {}
def _add_flow(self, dp, match, actions):
inst = [dp.ofproto_parser.OFPInstructionActions(
dp.ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = dp.ofproto_parser.OFPFlowMod(
dp, cookie=0, cookie_mask=0, table_id=0,
command=dp.ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
priority=0xff, buffer_id=0xffffffff,
out_port=dp.ofproto.OFPP_ANY, out_group=dp.ofproto.OFPG_ANY,
flags=0, match=match, instructions=inst)
dp.send_msg(mod)
def _define_flow(self, dp):
in_port = 1
out_port = 2
# port:1 -> port:2
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
actions = [dp.ofproto_parser.OFPActionOutput(out_port, 0)]
self._add_flow(dp, match, actions)
# port:1 -> port:2
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(out_port)
actions = [dp.ofproto_parser.OFPActionOutput(in_port, 0)]
self._add_flow(dp, match, actions)
@set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER)
def handler_datapath(self, ev):
if ev.enter:
self._define_flow(ev.dp)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dst, src, eth_type = struct.unpack_from('!6s6sH', buffer(msg.data), 0)
in_port = msg.match.fields[0].value
LOG.info("----------------------------------------")
LOG.info("* PacketIn")
LOG.info("in_port=%d, eth_type: %s", in_port, hex(eth_type))
LOG.info("packet reason=%d buffer_id=%d", msg.reason, msg.buffer_id)
LOG.info("packet in datapath_id=%s src=%s dst=%s",
msg.datapath.id, haddr_to_str(src), haddr_to_str(dst))

View File

@ -0,0 +1,6 @@
TEST_NAME=DecNwTtl
DUMP_HOST=h2
DUMP_IF=h2-eth0
RYU_APP=test_ip_ttl
PCAP_MZ="-t icmp ttl=64 -P $TEST_NAME -c 3 -b 00:00:00:00:00:02"
PCAP_FILTER="icmp && ip.ttl==63"

View File

@ -0,0 +1,85 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import struct
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller import dpset
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_2
from ryu.ofproto import ether
from ryu.lib.mac import haddr_to_str
LOG = logging.getLogger(__name__)
class RunTestMininet(app_manager.RyuApp):
_CONTEXTS = {'dpset': dpset.DPSet}
OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(RunTestMininet, self).__init__(*args, **kwargs)
self.mac_to_port = {}
def _add_flow(self, dp, match, actions):
inst = [dp.ofproto_parser.OFPInstructionActions(
dp.ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = dp.ofproto_parser.OFPFlowMod(
dp, cookie=0, cookie_mask=0, table_id=0,
command=dp.ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
priority=0xff, buffer_id=0xffffffff,
out_port=dp.ofproto.OFPP_ANY, out_group=dp.ofproto.OFPG_ANY,
flags=0, match=match, instructions=inst)
dp.send_msg(mod)
def _define_flow(self, dp):
in_port = 1
out_port = 2
eth_IP = ether.ETH_TYPE_IP
# ICMP -> DecNwTtl
LOG.debug("--- add_flow DecNwTtl")
match = dp.ofproto_parser.OFPMatch()
match.set_in_port(in_port)
match.set_dl_type(eth_IP)
actions = [dp.ofproto_parser.OFPActionDecNwTtl(),
dp.ofproto_parser.OFPActionOutput(out_port, 0)]
self._add_flow(dp, match, actions)
@set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER)
def handler_datapath(self, ev):
if ev.enter:
self._define_flow(ev.dp)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dst, src, eth_type = struct.unpack_from('!6s6sH', buffer(msg.data), 0)
in_port = msg.match.fields[0].value
LOG.info("----------------------------------------")
LOG.info("* PacketIn")
LOG.info("in_port=%d, eth_type: %s", in_port, hex(eth_type))
LOG.info("packet reason=%d buffer_id=%d", msg.reason, msg.buffer_id)
LOG.info("packet in datapath_id=%s src=%s dst=%s",
msg.datapath.id, haddr_to_str(src), haddr_to_str(dst))

View File

@ -0,0 +1,7 @@
# test-GratuitousARP-request
TEST_NAME=GARP-Request
DUMP_HOST=h1
DUMP_IF=h1-eth0
RYU_APP=test_arp
PCAP_MZ="-S"
PCAP_FILTER="arp.isgratuitous && arp.src.proto_ipv4==10.0.0.100 && arp.src.hw_mac==fe:ee:ee:ee:ee:ef"

View File

@ -0,0 +1,7 @@
# test-ARP-reply
TEST_NAME=ARP-Reply
DUMP_HOST=h1
DUMP_IF=h1-eth0
RYU_APP=test_arp
PCAP_MZ="-t arp request,targetip=10.0.0.100 -c 3 -r"
PCAP_FILTER="arp.opcode==reply && arp.src.proto_ipv4==10.0.0.100 && arp.src.hw_mac==fe:ee:ee:ee:ee:ef"

View File

@ -0,0 +1,7 @@
# test-ARP-request
TEST_NAME=ARP-Request
DUMP_HOST=h1
DUMP_IF=h1-eth0
RYU_APP=test_arp
PCAP_MZ="-t arp request,targetip=10.0.0.1"
PCAP_FILTER="arp.opcode==request && arp.src.proto_ipv4==10.0.0.100 && arp.dst.proto_ipv4==10.0.0.1 && arp.src.hw_mac==fe:ee:ee:ee:ee:ef"

View File

@ -0,0 +1,199 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import logging
import array
import netaddr
from ryu.base import app_manager
from ryu.controller import dpset
from ryu.controller import ofp_event
from ryu.controller import handler
from ryu.ofproto import ofproto_v1_2
from ryu.ofproto import ether
from ryu.ofproto import inet
from ryu.lib import mac
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import arp
from ryu.lib.packet import ipv4
from ryu.lib.packet import icmp
LOG = logging.getLogger(__name__)
class RunTestMininet(app_manager.RyuApp):
_CONTEXTS = {'dpset': dpset.DPSet}
OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
ZERO_MAC = mac.haddr_to_bin('00:00:00:00:00:00')
BROADCAST_MAC = mac.haddr_to_bin('ff:ff:ff:ff:ff:ff')
RYU_MAC = mac.haddr_to_bin('fe:ee:ee:ee:ee:ef')
HOST_MAC = mac.haddr_to_bin('00:00:00:00:00:01')
RYU_IP = int(netaddr.IPAddress('10.0.0.100'))
HOST_IP = int(netaddr.IPAddress('10.0.0.1'))
def __init__(self, *args, **kwargs):
super(RunTestMininet, self).__init__(*args, **kwargs)
def _send_msg(self, dp, data):
buffer_id = 0xffffffff
in_port = dp.ofproto.OFPP_LOCAL
actions = [dp.ofproto_parser.OFPActionOutput(1, 0)]
msg = dp.ofproto_parser.OFPPacketOut(
dp, buffer_id, in_port, actions, data)
dp.send_msg(msg)
def _add_flow(self, dp, match, actions):
inst = [dp.ofproto_parser.OFPInstructionActions(
dp.ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = dp.ofproto_parser.OFPFlowMod(
dp, cookie=0, cookie_mask=0, table_id=0,
command=dp.ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
priority=0xff, buffer_id=0xffffffff,
out_port=dp.ofproto.OFPP_ANY, out_group=dp.ofproto.OFPG_ANY,
flags=0, match=match, instructions=inst)
dp.send_msg(mod)
def _find_protocol(self, pkt, name):
for p in pkt.protocols:
if hasattr(p, 'protocol_name'):
if p.protocol_name == name:
return p
def _get_protocols(self, pkt):
protocols = {}
for p in pkt:
if hasattr(p, 'protocol_name'):
protocols[p.protocol_name] = p
else:
protocols['payload'] = p
return protocols
def _build_ether(self, ethertype, dst_mac=HOST_MAC):
e = ethernet.ethernet(dst_mac, self.RYU_MAC, ethertype)
return e
def _build_arp(self, opcode, dst_ip=HOST_IP):
if opcode == arp.ARP_REQUEST:
_eth_dst_mac = self.BROADCAST_MAC
_arp_dst_mac = self.ZERO_MAC
elif opcode == arp.ARP_REPLY:
_eth_dst_mac = self.HOST_MAC
_arp_dst_mac = self.HOST_MAC
e = self._build_ether(ether.ETH_TYPE_ARP, _eth_dst_mac)
a = arp.arp(hwtype=1, proto=ether.ETH_TYPE_IP, hlen=6, plen=4,
opcode=opcode, src_mac=self.RYU_MAC, src_ip=self.RYU_IP,
dst_mac=_arp_dst_mac, dst_ip=dst_ip)
p = packet.Packet()
p.add_protocol(e)
p.add_protocol(a)
p.serialize()
return p
def _build_echo(self, _type, echo):
e = self._build_ether(ether.ETH_TYPE_IP)
ip = ipv4.ipv4(version=4, header_length=5, tos=0, total_length=84,
identification=0, flags=0, offset=0, ttl=64,
proto=inet.IPPROTO_ICMP, csum=0,
src=self.RYU_IP, dst=self.HOST_IP)
ping = icmp.icmp(_type, code=0, csum=0, data=echo)
p = packet.Packet()
p.add_protocol(e)
p.add_protocol(ip)
p.add_protocol(ping)
p.serialize()
return p
def _garp(self):
p = self._build_arp(arp.ARP_REQUEST, self.RYU_IP)
return p.data
def _arp_request(self):
p = self._build_arp(arp.ARP_REQUEST, self.HOST_IP)
return p.data
def _arp_reply(self):
p = self._build_arp(arp.ARP_REPLY, self.HOST_IP)
return p.data
def _echo_request(self, echo):
p = self._build_echo(icmp.ICMP_ECHO_REQUEST, echo)
return p.data
def _echo_reply(self, echo):
p = self._build_echo(icmp.ICMP_ECHO_REPLY, echo)
return p.data
@handler.set_ev_cls(ofp_event.EventOFPPacketIn, handler.MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dp = msg.datapath
pkt = packet.Packet(array.array('B', msg.data))
p_arp = self._find_protocol(pkt, "arp")
p_icmp = self._find_protocol(pkt, "icmp")
p_ipv4 = self._find_protocol(pkt, "ipv4")
if p_arp:
src_ip = str(netaddr.IPAddress(p_arp.src_ip))
dst_ip = str(netaddr.IPAddress(p_arp.dst_ip))
if p_arp.opcode == arp.ARP_REQUEST:
LOG.debug("--- PacketIn: ARP_Request: %s->%s", src_ip, dst_ip)
if p_arp.dst_ip == self.RYU_IP:
LOG.debug("--- send Pkt: ARP_Reply")
data = self._arp_reply()
self._send_msg(dp, data)
elif p_arp.dst_ip == self.HOST_IP:
LOG.debug(" PacketIn: GARP")
LOG.debug("--- send Pkt: ARP_Request")
data = self._arp_request()
self._send_msg(dp, data)
elif p_arp.opcode == arp.ARP_REPLY:
LOG.debug("--- PacketIn: ARP_Reply: %s->%s", src_ip, dst_ip)
LOG.debug("--- send Pkt: Echo_Request")
echo = icmp.echo(id_=66, seq=1)
data = self._echo_request(echo)
self._send_msg(dp, data)
if p_icmp:
src = str(netaddr.IPAddress(p_ipv4.src))
dst = str(netaddr.IPAddress(p_ipv4.dst))
if p_icmp.type == icmp.ICMP_ECHO_REQUEST:
LOG.debug("--- PacketIn: Echo_Request: %s->%s", src, dst)
if p_ipv4.dst == self.RYU_IP:
LOG.debug("--- send Pkt: Echo_Reply")
echo = p_icmp.data
echo.data = bytearray(echo.data)
data = self._echo_reply(echo)
self._send_msg(dp, data)
elif p_icmp.type == icmp.ICMP_ECHO_REPLY:
LOG.debug("--- PacketIn: Echo_Reply: %s->%s", src, dst)
@handler.set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER)
def handler_datapath(self, ev):
if ev.enter:
dp = ev.dp
LOG.debug("--- send Pkt: Gratuitous ARP_Request")
data = self._garp()
self._send_msg(dp, data)

View File

@ -0,0 +1,274 @@
#!/bin/sh
RUN_DIR=`dirname $0`
CMD_NAME=`basename $0 .sh`
CMD_PATH=`readlink -f $0`
CMD_DIR=`dirname $CMD_PATH`
DUMP_SEC=10
DUMP_DELAY=2
DUMP_DIR=/tmp/test-mn/dump
TEST_LIST=
TEST_SUFFIX=.mn
MN_PRE_FILE=/tmp/test-mn/mn-pre
MN_POST_FILE=/tmp/test-mn/mn-post
PKG_LIST="tshark tcpreplay mz"
RTN=0
# usage
usage() {
echo "Usage: $0 [OPTION] [TEST DIR or FILE]..."
echo ""
echo "Run Ryu's test in mininet"
echo "ex.) $ $0 l2 l3/icmp/ICMP_ping.mn"
echo ""
echo "Options:"
echo " -h, --help show this help message and exit"
exit 0
}
# set default environment
set_env() {
POST_IF=h1-eth0
DUMP_HOST=h2
DUMP_IF=h2-eth0
TEST_NAME=
DUMP_FILE=
RYU_APP=
RYU_LOG=
PCAP_MZ=
PCAP_FILE=
PCAP_FILTER=
PCAP_COM=
CACHE_HIT=
}
# making mininet-test-pre-file
mn_pre() {
exec 3>&1
exec >$MN_PRE_FILE
echo "sh echo '----------------------------------'"
echo "sh echo '(pre) mininet topology dump.'"
echo "sh echo '----------------------------------'"
echo "dump"
echo "net"
echo "sh echo '----------------------------------'"
echo "sh echo '(pre) tshark start.'"
echo "sh echo '----------------------------------'"
echo "$DUMP_HOST tshark -i $DUMP_IF -a duration:$DUMP_SEC -w $DUMP_FILE &"
echo "sh sleep $DUMP_DELAY"
echo "sh echo '----------------------------------'"
exec 1>&3
}
# making mininet-test-post-file
mn_post() {
exec 3>&1
exec >$MN_POST_FILE
echo "sh ovs-vsctl del-controller s1"
echo "sh ovs-vsctl set bridge s1 protocols='[OpenFlow10,OpenFlow12]'"
echo "sh ovs-vsctl set-controller s1 tcp:127.0.0.1"
echo "sh echo '----------------------------------'"
echo "sh echo '(post) packet sending...'"
echo "sh echo '----------------------------------'"
echo $PCAP_COM
echo "sh sleep 1"
echo "sh echo '----------------------------------'"
echo "sh echo '(post) dump flows.'"
echo "sh echo '----------------------------------'"
echo "sh ovs-ofctl dump-flows s1"
echo "sh echo '----------------------------------'"
exec 1>&3
}
# ovs cache-hit incremental check
ovs_cache_hit() {
expr `sudo ovs-dpctl show|sed -n 's|lookups: hit:||gp'|awk '{print $1}'` - ${1:-0}
}
# starting ryu-manager
run_ryu() {
ERRSTAT=0
ERRTAG="run_ryu() :"
echo "Inf: RYU_APP=$RYU_APP"
echo "Inf: ryu-manager starting..."
ryu-manager --verbose $RYU_APP 2>$DUMP_DIR/$RYU_LOG &
PID_RYU=$!
sleep 1
[ -d /proc/$PID_RYU ] || err $ERRTAG "failed to start ryu-manager."
return $ERRSTAT
}
# starting mininet and test-script
run_mn() {
echo "Info: mininet starting..."
sudo mn --mac --test none --pre $MN_PRE_FILE --post $MN_POST_FILE \
--controller remote 127.0.0.1
}
# cleaning after mininet
clean_mn() {
wait_ryu
rm -f $MN_PRE_FILE $MN_POST_FILE
}
# check packet and chache-hit
check() {
PACKET=`tshark -r $DUMP_FILE -R "$PCAP_FILTER" 2>/dev/null`
if [ ! "$PACKET" ]; then
RESULT=NG
REASON="(unmatched packet. please check $DUMP_FILE)"
elif [ "$CACHE_HIT" ] && [ `ovs_cache_hit $CACHE_HIT` -eq 0 ]; then
RESULT=NG
REASON="(ovs cache hit miss.)"
else
RESULT=OK; REASON=
fi
echo
echo "TEST ${TEST_NAME} : $RESULT $REASON"
}
# stoping ryu-manager
wait_ryu() {
kill -2 $PID_RYU
wait $PID_RYU
}
# test-main
test_mn() {
DUMP_FILE=$DUMP_DIR/$DUMP_FILE
touch $DUMP_FILE
sudo chmod o+w $DUMP_FILE
[ "$CACHE_HIT" ] && CACHE_HIT=`ovs_cache_hit 0`
mn_pre
mn_post
run_ryu; [ $? -ne 0 ] && return 1
run_mn; [ $? -ne 0 ] && return 1
check
return 0
}
err() {
echo Error: $*
ERRSTAT=1
}
mnfile_check() {
test=`basename $1 $TEST_SUFFIX`
file=`readlink -f $1`
TEST_DIR=`dirname $file`
ERRSTAT=0
ERRTAG="mnfile_check() :"
# test-file check
if [ ! -r $file ]; then
err $ERRTAG "cannot open the file: $file"
return $ERRSTAT
fi
. $file || err $ERRTAG "failed to include $file"
# parameter check
[ "$RYU_APP" ] || err $ERRTAG: "RYU_APP is not defined"
[ "$PCAP_FILE" -o "$PCAP_MZ" ] || err $ERRTAG: "PCAP_FILE or PCAP_MZ is not defined"
[ "$PCAP_FILTER" ] || err $ERRTAG "PCAP_FILTER is not defined"
[ "$TEST_NAME" ] || TEST_NAME=$test
[ "$DUMP_FILE" ] || DUMP_FILE=$test.dump
[ "$RYU_LOG" ] || RYU_LOG=ryu-manager.$test.log
[ $ERRSTAT -ne 0 ] && return $ERRSTAT
# pcap check (pcap-file or mz-option)
if [ "$PCAP_FILE" ]; then
PCAP_FILE=$TEST_DIR/$PCAP_FILE
[ -r $PCAP_FILE ] || err $ERRTAG "PCAP_FILE[$PCAP_FILE] cannot read"
PCAP_COM="h1 tcpreplay -l 3 -i $POST_IF $PCAP_FILE"
elif [ "$PCAP_MZ" ]; then
PCAP_COM="h1 mz $POST_IF $PCAP_MZ"
fi
[ $ERRSTAT -ne 0 ] && return $ERRSTAT
# ryu-app check
[ -r $TEST_DIR/$RYU_APP -o -r $TEST_DIR/${RYU_APP}.py ] && RYU_APP=$TEST_DIR/$RYU_APP
return $ERRSTAT
}
arg_check() {
ARGLIST=
ERRTAG="argcheck() :"
case "$1" in
-h|--help) usage;;
esac
if [ $# -ne 0 ]; then
ARGLIST=$*
else
ARGLIST=`find . -type f -name "*$TEST_SUFFIX"`
fi
for arg in $ARGLIST; do
if [ -d $arg ]; then
file=`find $arg -type f -name "*$TEST_SUFFIX"`
elif [ -f $arg ]; then
file=$arg
else
err $ERRTAG "$arg is not found"
file=
fi
TEST_LIST="$TEST_LIST $file"
done
}
pkg_check() {
no_pkg=
for pkg in $PKG_LIST; do
[ ! `which $pkg` ] && no_pkg="$no_pkg $pkg"
done
for pkg in $no_pkg; do
echo "Error: Package [ $pkg ] is not found. Please install."
done
[ "$no_pkg" ] && exit 1
}
### main
[ -d $DUMP_DIR ] || mkdir -p $DUMP_DIR
pkg_check
arg_check $*
echo "\n---------- test target ----------"
for testfile in $TEST_LIST; do echo $testfile; done
count=0
for testfile in $TEST_LIST; do
echo "\n---------- test [$testfile] start ----------"
set_env
mnfile_check $testfile && test_mn
case $? in
0) msg="finished : $RESULT" ;;
*) msg="skipped with error"; RESULT="skip" ;;
esac
eval RESULT_${count}=\$RESULT
eval REASON_${count}=\$REASON
count=`expr $count + 1`
num=`eval echo \\${num_$RESULT:-0}`
eval num_${RESULT}=`expr $num + 1`
[ "$RESULT" != "OK" ] && RTN=1
clean_mn
echo "\n---------- test [$testfile] $msg ----------"
done
# output summary results
echo "\n---------- test results ----------"
count=0
for testfile in $TEST_LIST; do
eval echo \$testfile : \$RESULT_${count} \$REASON_${count}
count=`expr $count + 1`
done
echo "----------------------------------"
echo "Ran $count tests. Result: ${num_OK:+OK=}$num_OK ${num_NG:+NG=}$num_NG ${num_skip:+skip=}$num_skip"
exit $RTN