def test_mapper(self):
     self.assertEqual("tapangmzseeic01",
                      mac_to_tun_name("02:11:22:33:33:01"))
     self.assertEqual("tapangmzseeicfa",
                      mac_to_tun_name("02:11:22:33:33:fa"))
     self.assertEqual("tapangmaceeia00",
                      mac_to_tun_name("00:11:22:00:33:00"))
     with self.assertRaises(AssertionError):
         mac_to_tun_name("02:11:22:33:33:QQ")
Ejemplo n.º 2
0
    def sync_ovs_from_tunnels(self,
                              mac_to_tunswitch: MACToTunnelAndSwitch) -> None:

        logger.warning(
            "negotiator tunnels are:\n%s\n%s\n%s",
            "-" * 40,
            "\n\n".join(
                f"- {mac} {mac_to_tun_name(mac)} {swi.hostname}:\n  |--{tun}\n  |--{swi}"
                for mac, (tun, swi) in sorted(mac_to_tunswitch.items())),
            "-" * 40,
        )

        tun_to_tunswitch: TUNToTunnelAndSwitch = {
            mac_to_tun_name(mac): (tunnel, switch)
            for mac, (tunnel, switch) in mac_to_tunswitch.items()
        }

        # TODO clean the dict? Eventually it might eat up a lot of memory.
        self._tun_to_dest_mac.update(
            (tun, tunnel.dst_mac)
            for tun, (tunnel, _) in tun_to_tunswitch.items())

        self._update_relay_mac_in_ovs_sync(mac_to_tunswitch)
        self._add_ports_to_bridge(tun_to_tunswitch)
        self.flow_hysteresis.update(tun_to_tunswitch)
        self._remove_extraneous_ports_in_ovs_sync(tun_to_tunswitch)

        # Create a new eventlet's green thread (akin to asyncio.ensure_future).
        hub.spawn_after(
            self.flow_hysteresis.hysteresis_seconds + 0.1,
            self._update_port_flows,
            list(tun_to_tunswitch.keys()),
        )
Ejemplo n.º 3
0
 def _update_relay_mac_in_ovs_sync(
         self, mac_to_tunswitch: MACToTunnelAndSwitch) -> None:
     relays = [
         tunnel for tunnel, switch in mac_to_tunswitch.values()
         if switch.is_relay
     ]
     assert len(relays) <= 1
     # TODO maybe drop all except one instead of failing?
     self.relay_tun = None
     if relays:
         self.relay_tun = mac_to_tun_name(relays[0].dst_mac)
Ejemplo n.º 4
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self._process_transport = None
     self.tun_dev_name = mac_to_tun_name(self._dst_mac)
    def test_packet_in_on_board(self):
        ovs_manager = self.ovs_manager
        OFPORT_RELAYER = 41

        fl = FlowsLogic(
            is_relay=False,
            ovs_manager=ovs_manager,
            tunnel_intentions_provider=self.tunnel_intentions_provider,
            topology_database=self.topology_database,
        )
        fl.flow_hysteresis.update({
            mac_to_tun_name(SECOND_MAC): (
                TunnelModel.from_dict({
                    **TUNNEL_MODEL_RELAY_DATA, "dst_mac":
                    SECOND_MAC
                }),
                SwitchEntity.from_dict({
                    **SWITCH_ENTITY_RELAY_DATA, "mac":
                    SECOND_MAC
                }),
            )
        })
        self.inc_hysteresis_clock()

        # Incoming packet from a switch
        msg = self._build_ofp_packet_in(dst_mac=LOCAL_MAC, src_mac=SECOND_MAC)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_not_called()
        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(ryu_ofproto.OFPP_LOCAL,
                         packet_out_msg.actions[0].port)
        # Flow should be installed
        self.assertEqual(ryu_ofproto.OFPP_LOCAL,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst=LOCAL_MAC), flow_add_msg.match)

        # Outgoing to a connected switch
        ovs_manager.get_ofport.side_effect = lambda tun: {
            mac_to_tun_name(SECOND_MAC): OFPORT_RELAYER
        }[tun]
        msg = self._build_ofp_packet_in(dst_mac=SECOND_MAC)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_called_once()
        ovs_manager.get_ofport.reset_mock()
        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(OFPORT_RELAYER, packet_out_msg.actions[0].port)
        # Flow should be installed
        self.assertEqual(OFPORT_RELAYER,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst=SECOND_MAC),
            flow_add_msg.match)

        # Incoming broadcast packet
        msg = self._build_ofp_packet_in(dst_mac="ff:ff:ff:ff:ff:ff",
                                        src_mac=SECOND_MAC,
                                        in_port=12399)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_not_called()
        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(ryu_ofproto.OFPP_LOCAL,
                         packet_out_msg.actions[0].port)
        self.assertEqual(12399, packet_out_msg.in_port)
        # Flow should be installed
        self.assertEqual(ryu_ofproto.OFPP_LOCAL,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst="ff:ff:ff:ff:ff:ff",
                                        in_port=12399),
            flow_add_msg.match,
        )

        # Without a connected relay - do nothing.
        ovs_manager.get_ofport.side_effect = Exception
        fl.relay_tun = None

        # Without a connected relay: Outgoing to a not connected switch
        msg = self._build_ofp_packet_in(dst_mac=SECOND_MAC)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_called_once_with(
            mac_to_tun_name(SECOND_MAC))
        ovs_manager.get_ofport.reset_mock()
        msg.datapath.send_msg.assert_not_called()
        self.tunnel_intentions_provider.ask_for_tunnel.assert_called_once_with(
            SECOND_MAC)
        self.tunnel_intentions_provider.ask_for_tunnel.reset_mock()

        # Without a connected relay: Outgoing broadcast packet
        msg = self._build_ofp_packet_in(
            dst_mac="ff:ff:ff:ff:ff:ff",
            src_mac=LOCAL_MAC,
            in_port=ryu_ofproto.OFPP_LOCAL,
        )
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_not_called()
        msg.datapath.send_msg.assert_not_called()

        # With a connected relay
        fl.relay_tun = mac_to_tun_name(SECOND_MAC)
        ovs_manager.get_ofport.side_effect = lambda tun: {
            mac_to_tun_name(SECOND_MAC): OFPORT_RELAYER
        }[tun]

        # With a connected relay: Outgoing to a not connected switch
        msg = self._build_ofp_packet_in(dst_mac=THIRD_MAC)
        fl.packet_in(msg)

        ovs_manager.get_ofport.assert_called_once_with(
            mac_to_tun_name(SECOND_MAC))
        ovs_manager.get_ofport.reset_mock()

        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(OFPORT_RELAYER, packet_out_msg.actions[0].port)
        # Flow should be installed
        self.assertEqual(OFPORT_RELAYER,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst=THIRD_MAC), flow_add_msg.match)

        self.tunnel_intentions_provider.ask_for_tunnel.assert_called_once_with(
            THIRD_MAC)
        self.tunnel_intentions_provider.ask_for_tunnel.reset_mock()

        # With a connected relay: Outgoing broadcast packet
        msg = self._build_ofp_packet_in(
            dst_mac="ff:ff:ff:ff:ff:ff",
            src_mac=LOCAL_MAC,
            in_port=ryu_ofproto.OFPP_LOCAL,
        )
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_called_once()
        ovs_manager.get_ofport.reset_mock()
        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(OFPORT_RELAYER, packet_out_msg.actions[0].port)
        self.assertEqual(ryu_ofproto.OFPP_LOCAL, packet_out_msg.in_port)
        # Flow should be installed
        self.assertEqual(OFPORT_RELAYER,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst="ff:ff:ff:ff:ff:ff",
                                        in_port=ryu_ofproto.OFPP_LOCAL),
            flow_add_msg.match,
        )

        # Outgoing to a foreign mac address
        msg = self._build_ofp_packet_in(dst_mac=UNK_MAC)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_not_called()
        msg.datapath.send_msg.assert_not_called()
        self.tunnel_intentions_provider.ask_for_tunnel.assert_not_called()
    def test_packet_in_on_relay(self):
        ovs_manager = self.ovs_manager
        OFPORT_BOARD = 41

        fl = FlowsLogic(
            is_relay=True,
            ovs_manager=ovs_manager,
            tunnel_intentions_provider=self.tunnel_intentions_provider,
            topology_database=self.topology_database,
        )
        fl.flow_hysteresis.update({
            mac_to_tun_name(SECOND_MAC): (
                TunnelModel.from_dict(TUNNEL_MODEL_BOARD_DATA),
                SwitchEntity.from_dict(SWITCH_ENTITY_BOARD_DATA),
            )
        })
        self.inc_hysteresis_clock()

        # Incoming packet from a switch
        msg = self._build_ofp_packet_in(dst_mac=LOCAL_MAC, src_mac=SECOND_MAC)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_not_called()
        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(ryu_ofproto.OFPP_LOCAL,
                         packet_out_msg.actions[0].port)
        # Flow should be installed
        self.assertEqual(ryu_ofproto.OFPP_LOCAL,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst=LOCAL_MAC), flow_add_msg.match)

        # Outgoing to a connected switch
        ovs_manager.get_ofport.side_effect = lambda tun: {
            mac_to_tun_name(SECOND_MAC): OFPORT_BOARD
        }[tun]
        msg = self._build_ofp_packet_in(dst_mac=SECOND_MAC)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_called_once()
        ovs_manager.get_ofport.reset_mock()
        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(OFPORT_BOARD, packet_out_msg.actions[0].port)
        # Flow should be installed
        self.assertEqual(OFPORT_BOARD,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst=SECOND_MAC),
            flow_add_msg.match)

        # Outgoing to a not connected switch
        ovs_manager.get_ofport.side_effect = Exception
        msg = self._build_ofp_packet_in(dst_mac=SECOND_MAC)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_called_once()
        ovs_manager.get_ofport.reset_mock()
        msg.datapath.send_msg.assert_not_called()

        # Outgoing to a foreign mac address
        ovs_manager.get_ofport.side_effect = Exception
        msg = self._build_ofp_packet_in(dst_mac=UNK_MAC)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_not_called()
        msg.datapath.send_msg.assert_not_called()

        # Incoming broadcast packet
        msg = self._build_ofp_packet_in(dst_mac="ff:ff:ff:ff:ff:ff",
                                        src_mac=SECOND_MAC,
                                        in_port=12399)
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_not_called()
        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(ryu_ofproto.OFPP_FLOOD,
                         packet_out_msg.actions[0].port)
        self.assertEqual(12399, packet_out_msg.in_port)
        # Flow should be installed
        self.assertEqual(ryu_ofproto.OFPP_FLOOD,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst="ff:ff:ff:ff:ff:ff"),
            flow_add_msg.match)

        # Outgoing broadcast packet
        msg = self._build_ofp_packet_in(
            dst_mac="ff:ff:ff:ff:ff:ff",
            src_mac=LOCAL_MAC,
            in_port=ryu_ofproto.OFPP_LOCAL,
        )
        fl.packet_in(msg)
        ovs_manager.get_ofport.assert_not_called()
        flow_add_msg, packet_out_msg = self.extract_msg_from_call_args_list(
            msg.datapath.send_msg)
        # PACKET_OUT should be sent
        self.assertEqual(ryu_ofproto.OFPP_FLOOD,
                         packet_out_msg.actions[0].port)
        self.assertEqual(ryu_ofproto.OFPP_LOCAL, packet_out_msg.in_port)
        # Flow should be installed
        self.assertEqual(ryu_ofproto.OFPP_FLOOD,
                         flow_add_msg.instructions[0].actions[0].port)
        self.assertOFPMatchEquals(
            ryu_ofproto_parser.OFPMatch(eth_dst="ff:ff:ff:ff:ff:ff"),
            flow_add_msg.match)

        # TODO !! incoming multicast packet

        self.tunnel_intentions_provider.ask_for_tunnel.assert_not_called()
    def test_tunnel_add(self, mock_add_flow):
        OFPORT_BOARD = 41
        OFPORT_RELAY = 42
        ovs_manager = self.ovs_manager

        ovs_manager.get_ports_in_bridge.return_value = []

        fl = FlowsLogic(
            is_relay=False,
            ovs_manager=ovs_manager,
            tunnel_intentions_provider=self.tunnel_intentions_provider,
            topology_database=self.topology_database,
        )
        fl.set_datapath(MagicMock()())
        self.assertIsNone(fl.relay_tun)

        # Update with one switch (w/o a relay)
        mac_to_tunswitch = {
            SWITCH_ENTITY_BOARD_DATA["mac"]: (
                TunnelModel.from_dict(TUNNEL_MODEL_BOARD_DATA),
                SwitchEntity.from_dict(SWITCH_ENTITY_BOARD_DATA),
            )
        }
        ovs_manager.get_ofport.side_effect = lambda tun: {
            mac_to_tun_name(SWITCH_ENTITY_BOARD_DATA["mac"]): OFPORT_BOARD
        }[tun]

        fl.sync_ovs_from_tunnels(mac_to_tunswitch)

        ovs_manager.add_port_to_bridge.assert_called_with(
            mac_to_tun_name(SWITCH_ENTITY_BOARD_DATA["mac"]))
        ovs_manager.add_port_to_bridge.reset_mock()
        self.assertIsNone(fl.relay_tun)
        mock_add_flow.assert_not_called()
        self.hub_spawn_after.assert_called_once()

        self.inc_hysteresis_clock()
        self.perform_last_spawn_after()
        self.hub_spawn_after.assert_called_once()
        self.hub_spawn_after.reset_mock()
        mock_add_flow.assert_called_once()
        mock_add_flow.reset_mock()

        ovs_manager.del_port_from_bridge.assert_not_called()
        ovs_manager.get_ofport.assert_called_once()
        ovs_manager.get_ofport.reset_mock()

        # Update with 2 switches (one is a relay)
        mac_to_tunswitch = {
            SWITCH_ENTITY_BOARD_DATA["mac"]: (
                TunnelModel.from_dict(TUNNEL_MODEL_BOARD_DATA),
                SwitchEntity.from_dict(SWITCH_ENTITY_BOARD_DATA),
            ),
            SWITCH_ENTITY_RELAY_DATA["mac"]: (
                TunnelModel.from_dict(TUNNEL_MODEL_RELAY_DATA),
                SwitchEntity.from_dict(SWITCH_ENTITY_RELAY_DATA),
            ),
        }
        ovs_manager.get_ofport.side_effect = lambda tun: {
            mac_to_tun_name(SWITCH_ENTITY_BOARD_DATA["mac"]): OFPORT_BOARD,
            mac_to_tun_name(SWITCH_ENTITY_RELAY_DATA["mac"]): OFPORT_RELAY,
        }[tun]

        fl.sync_ovs_from_tunnels(mac_to_tunswitch)

        self.assertEqual(fl.relay_tun,
                         mac_to_tun_name(SWITCH_ENTITY_RELAY_DATA["mac"]))
        self.assertListEqual(
            [
                tun
                for (tun, ), _ in ovs_manager.add_port_to_bridge.call_args_list
            ],
            [
                mac_to_tun_name(SWITCH_ENTITY_BOARD_DATA["mac"]),
                mac_to_tun_name(SWITCH_ENTITY_RELAY_DATA["mac"]),
            ],
        )
        ovs_manager.add_port_to_bridge.reset_mock()
        mock_add_flow.assert_not_called()
        self.hub_spawn_after.assert_called_once()

        self.inc_hysteresis_clock()
        self.perform_last_spawn_after()
        self.hub_spawn_after.assert_called_once()
        self.hub_spawn_after.reset_mock()
        self.assertEqual(2, mock_add_flow.call_count)
        mock_add_flow.reset_mock()

        ovs_manager.del_port_from_bridge.assert_not_called()
        self.assertEqual(2, ovs_manager.get_ofport.call_count)
        ovs_manager.get_ofport.reset_mock()

        # Update with empty tunnels, make sure that ports get removed
        ovs_manager.get_ports_in_bridge.return_value = [
            mac_to_tun_name(SWITCH_ENTITY_BOARD_DATA["mac"])
        ]
        mac_to_tunswitch = {}

        fl.sync_ovs_from_tunnels(mac_to_tunswitch)

        self.assertIsNone(fl.relay_tun)
        ovs_manager.add_port_to_bridge.assert_not_called()
        mock_add_flow.assert_not_called()
        ovs_manager.del_port_from_bridge.assert_called_once()
        self.hub_spawn_after.assert_called_once()

        self.inc_hysteresis_clock()
        self.perform_last_spawn_after()
        self.hub_spawn_after.assert_called_once()
        self.hub_spawn_after.reset_mock()
        mock_add_flow.assert_not_called()

        ovs_manager.del_port_from_bridge.assert_called_once()
        ovs_manager.get_ofport.assert_not_called()
        ovs_manager.get_ofport.reset_mock()
Ejemplo n.º 8
0
    def packet_in(self, msg: OFPPacketIn) -> None:
        datapath: Datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        in_port: OFPort = OFPort(msg.match["in_port"])

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]

        if eth.ethertype == ether_types.ETH_TYPE_LLDP:
            # ignore lldp packet
            return

        dst: MACAddress = eth.dst
        src: MACAddress = eth.src
        dpid: int = datapath.id  # Bridge identifier in OVS, usually derived from MAC.
        # self.mac_to_port.setdefault(dpid, {})

        logger.info(
            "packet in [%s %s %s %s] [dpid src dst in_port]",
            dpid,
            src,
            dst,
            self._ofport_to_string(in_port, ofproto),
        )

        # self.mac_to_port[dpid][src] = in_port

        is_broadcast = is_group_mac(dst)
        out_port: OFPort = OFPort(-1)
        match = parser.OFPMatch(eth_dst=dst)
        priority: OFPriority = OFPriority.DIRECT
        if dst == self.ovs_manager.bridge_mac:
            # This packet is specifically for us - capture it!
            out_port = ofproto.OFPP_LOCAL
        elif not is_broadcast:
            # This packet is for someone else - send it right away
            # if receiver is locally connected.
            tun = mac_to_tun_name(dst)
            try:
                out_port = self.flow_hysteresis.get_ofport_if_tun_is_ready(tun)
            except PortNotReadyError:
                pass
        # Either receiver is not directly reachable,
        # or it's a broadcast/multicast packet.
        if out_port < 0:
            try:
                if self.is_relay:
                    out_port, priority, match = self._packet_in_relay(
                        dst, ofproto, parser)
                else:
                    out_port, priority, match = self._packet_in_board(
                        dst, src, in_port, ofproto, parser)
            except NoSuitableOutPortError as e:
                logger.info("PACKET_IN: dropping packet: %s", str(e))
                # TODO ICMP unreachable?
                return
        assert out_port >= 0
        assert priority in (OFPriority.DIRECT, OFPriority.RELAY)
        logger.info(
            "PACKET_IN: decision: out_port %s",
            self._ofport_to_string(out_port, ofproto),
        )

        # if dst in self.mac_to_port[dpid]:
        #     out_port = self.mac_to_port[dpid][dst]

        actions = [parser.OFPActionOutput(out_port)]

        is_direct = priority == OFPriority.DIRECT
        # install a flow to avoid packet_in next time
        self.add_flow(datapath,
                      priority,
                      match,
                      actions,
                      is_direct_flow=is_direct)
        logger.info(
            "Added %s flow to %s via %s",
            "direct" if is_direct else "indirect",
            dst,
            self._ofport_to_string(out_port, ofproto),
        )

        data = None
        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
            data = msg.data

        out = parser.OFPPacketOut(
            datapath=datapath,
            buffer_id=msg.buffer_id,
            in_port=in_port,
            actions=actions,
            data=data,
        )
        datapath.send_msg(out)