dragonflow/dragonflow/tests/unit/_test_l3.py

212 lines
8.1 KiB
Python

# 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 copy
import mock
from ryu.controller import ofp_event
from ryu.lib.packet import ethernet
from ryu.lib.packet import icmp
from ryu.lib.packet import in_proto
from ryu.lib.packet import ipv4
from ryu.lib.packet import packet
from ryu.lib.packet import udp
from ryu.ofproto import ofproto_v1_3_parser as ofproto_parser
from dragonflow.controller.common import constants as const
from dragonflow.db.models import l2
from dragonflow.db.models import l3
from dragonflow.tests.unit import test_app_base
class L3AppTestCaseMixin(object):
def _add_another_router_interface(self):
router_port1 = l3.LogicalRouterPort(network="20.0.0.1/24",
lswitch="fake_switch2",
topic="fake_tenant1",
mac="fa:16:3e:50:96:fe",
unique_key=15,
id="fake_router_port2")
self.router.add_router_port(router_port1)
def test_n_icmp_responder_for_n_router_interface(self):
self._add_another_router_interface()
dst_router_port = self.router.ports[0]
with mock.patch("dragonflow.controller.common"
".icmp_responder.ICMPResponder") as icmp:
self.app._add_new_router_port(self.router, dst_router_port)
self.assertEqual(1, icmp.call_count)
def test_n_route_for_n_router_interface(self):
self._add_another_router_interface()
dst_router_port = self.router.ports[0]
with mock.patch.object(self.app,
"_add_subnet_send_to_route") as method:
self.app._add_new_router_port(self.router, dst_router_port)
self.assertEqual(1, method.call_count)
def test_del_add_router(self):
self.app.mod_flow.reset_mock()
# delete router
self.controller.delete(self.router)
# 5 mod flows, l2 -> l3, arp, icmp, router interface and route.
self.assertEqual(5, self.app.mod_flow.call_count)
# add router
self.app.mod_flow.reset_mock()
self.controller.update(self.router)
# 5 mod flows, l2 -> l3, arp, icmp, router interface and route.
self.assertEqual(5, self.app.mod_flow.call_count)
args, kwargs = self.app.mod_flow.call_args
self.assertEqual(const.L3_LOOKUP_TABLE, kwargs['table_id'])
def test_reply_ttl_invalid_message_with_rate_limit(self):
pkt = packet.Packet()
pkt.add_protocol(ethernet.ethernet(dst='aa:bb:cc:dd:ee:ff'))
pkt.add_protocol(ipv4.ipv4(proto=in_proto.IPPROTO_UDP))
pkt.add_protocol(udp.udp())
pkt.serialize()
lswitch = l2.LogicalSwitch(
id='lswitch1',
topic='topic1',
unique_key=9,
version=1,
)
self.app.db_store.update(lswitch)
lrouter = l3.LogicalRouter(
id='lrouter1',
topic='topic1',
version=1,
unique_key=22,
ports=[
l3.LogicalRouterPort(
id='lrouter1-port1',
unique_key=55,
topic='topic1',
mac='aa:bb:cc:dd:ee:ff',
network='10.0.0.1/24',
lswitch='lswitch1',
),
],
)
self.app.db_store.update(lrouter)
event = ofp_event.EventOFPMsgBase(
msg=ofproto_parser.OFPPacketIn(
datapath=mock.Mock(),
reason=self.app.ofproto.OFPR_INVALID_TTL,
match=ofproto_parser.OFPMatch(
metadata=lswitch.unique_key,
reg5=lrouter.unique_key,
),
data=pkt.data,
)
)
with mock.patch("dragonflow.controller.common."
"icmp_error_generator.generate") as icmp_error:
for _ in range(self.app.conf.router_ttl_invalid_max_rate * 2):
self.app.packet_in_handler(event)
self.assertEqual(self.app.conf.router_ttl_invalid_max_rate,
icmp_error.call_count)
icmp_error.assert_called_with(icmp.ICMP_TIME_EXCEEDED,
icmp.ICMP_TTL_EXPIRED_CODE,
mock.ANY, "10.0.0.1", mock.ANY)
def test_reply_icmp_unreachable_with_rate_limit(self):
pkt = packet.Packet()
pkt.add_protocol(ethernet.ethernet(dst='aa:bb:cc:dd:ee:ff'))
pkt.add_protocol(ipv4.ipv4(dst='10.0.0.1', proto=in_proto.IPPROTO_UDP))
pkt.add_protocol(udp.udp())
pkt.serialize()
lrouter = l3.LogicalRouter(
id='lrouter1',
topic='topic1',
version=1,
unique_key=22,
ports=[
l3.LogicalRouterPort(
id='lrouter1-port1',
unique_key=55,
topic='topic1',
mac='aa:bb:cc:dd:ee:ff',
network='10.0.0.1/24',
),
],
)
self.app.db_store.update(lrouter)
event = ofp_event.EventOFPMsgBase(
msg=ofproto_parser.OFPPacketIn(
datapath=mock.Mock(),
reason=self.app.ofproto.OFPR_PACKET_IN,
match=ofproto_parser.OFPMatch(
reg7=lrouter.ports[0].unique_key,
),
data=pkt.data,
)
)
with mock.patch("dragonflow.controller.common."
"icmp_error_generator.generate") as icmp_error:
for _ in range(self.app.conf.router_port_unreach_max_rate * 2):
self.app.packet_in_handler(event)
self.assertEqual(
self.app.conf.router_port_unreach_max_rate,
icmp_error.call_count)
icmp_error.assert_called_with(icmp.ICMP_DEST_UNREACH,
icmp.ICMP_PORT_UNREACH_CODE,
pkt.data, pkt=mock.ANY)
def test_add_del_router_route_after_lport(self):
self.controller.update(test_app_base.fake_local_port1)
self.app.mod_flow.reset_mock()
# add route
routes = [{"destination": "10.100.0.0/16",
"nexthop": "10.0.0.6"},
{"destination": "10.101.0.0/16",
"nexthop": "10.0.0.6"}]
# Use another object here to differentiate the one in cache
router_with_route = copy.deepcopy(self.router)
router_with_route.routes = routes
router_with_route.version += 1
self.controller.update(router_with_route)
# 2 routes, 2 mod_flow
self.assertEqual(2, self.app.mod_flow.call_count)
# delete route
self.app.mod_flow.reset_mock()
self.router.routes = []
self.router.version += 2
self.controller.update(self.router)
self.assertEqual(2, self.app.mod_flow.call_count)
def test_no_route_if_no_match_lport(self):
# add route
routes = [{"destination": "10.100.0.0/16",
"nexthop": "10.0.0.106"},
{"destination": "10.101.0.0/16",
"nexthop": "10.0.0.106"}]
self.controller.update(test_app_base.fake_local_port1)
self.app.mod_flow.reset_mock()
router_with_route = copy.deepcopy(self.router)
router_with_route.routes = routes
router_with_route.version += 1
self.controller.update(router_with_route)
self.assertFalse(self.app.mod_flow.called)