Example #1
0
    def __init__(self, *args, **kwargs):
        self.ip_prefix = CONF.ip_prefix
        super(Inception, self).__init__(*args, **kwargs)
        # dpset: management of all connected switches
        self.dpset = kwargs['dpset']
        # network: port information of switches
        self.network = kwargs['network']

        ## Data Structures
        # dpid -> IP address:
        #
        # Records the "IP address" of rVM where a switch ("dpid") resides.
        self.dpid_to_ip = {}

        # dpid -> [(IP address -> port)]:
        #
        # Records the neighboring relations of each switch.
        # "IP address": address of remote VMs
        # "port": port number of dpid connecting IP address
        self.dpid_to_conns = defaultdict(dict)

        # MAC => (dpid, port):
        #
        # Records the switch ("dpid") to which a local "mac" connects,
        # as well as the "port" of the connection.
        self.mac_to_dpid_port = {}

        # [(dpid, mac)]:
        # "dpid" installed a rule forwarding packets to a "mac"
        self.unicast_rules = []

        ## Modules
        # ARP
        self.inception_arp = InceptionArp(self)
Example #2
0
class Inception(app_manager.RyuApp):
    """
    Inception Cloud SDN controller
    """
    _CONTEXTS = {
        'network': network.Network,
        'dpset': dpset.DPSet
    }
    OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]

    MAX_LEN = 65535

    def __init__(self, *args, **kwargs):
        self.ip_prefix = CONF.ip_prefix
        super(Inception, self).__init__(*args, **kwargs)
        # dpset: management of all connected switches
        self.dpset = kwargs['dpset']
        # network: port information of switches
        self.network = kwargs['network']

        ## Data Structures
        # dpid -> IP address:
        #
        # Records the "IP address" of rVM where a switch ("dpid") resides.
        self.dpid_to_ip = {}

        # dpid -> [(IP address -> port)]:
        #
        # Records the neighboring relations of each switch.
        # "IP address": address of remote VMs
        # "port": port number of dpid connecting IP address
        self.dpid_to_conns = defaultdict(dict)

        # MAC => (dpid, port):
        #
        # Records the switch ("dpid") to which a local "mac" connects,
        # as well as the "port" of the connection.
        self.mac_to_dpid_port = {}

        # [(dpid, mac)]:
        # "dpid" installed a rule forwarding packets to a "mac"
        self.unicast_rules = []

        ## Modules
        # ARP
        self.inception_arp = InceptionArp(self)
        # DHCP
        # self.inception_dhcp = InceptionDhcp(self)

    @handler.set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER)
    def switch_connection_handler(self, event):
        """
        Handle when a switch event is received
        """
        datapath = event.dp
        dpid = datapath.id
        ofproto = datapath.ofproto
        ofproto_parser = datapath.ofproto_parser

        # A new switch connects
        if event.enter is True:
            socket = datapath.socket
            ip, port = socket.getpeername()
            host_ports = []
            all_ports = []

            # Set the miss_send_len parameter of switch
#            datapath.send_msg(ofproto_parser.OFPSetConfig(
#                            datapath,
#                            ofproto.OFPC_FRAG_NORMAL,
#                            Inception.miss_send_len))

            # If the entry corresponding to the MAC already exists
            if dpid in self.dpid_to_ip:
                LOGGER.info("switch=%s already updated", dpid_to_str(dpid))
                return

            self.dpid_to_ip[dpid] = ip
            LOGGER.info("Add: switch=%s -> ip=%s", dpid_to_str(dpid), ip)

            # Collect port information.  Sift out ports connecting peer
            # switches and store them in dpid_to_conns
            for port in event.ports:
                # TODO(changbl): Parse the port name to get the IP
                # address of remote rVM to which the bridge builds a
                # VXLAN. E.g., obr1_184-53 => ip_prefix.184.53. Only
                # store the port connecting remote rVM.
                port_no = port.port_no
                all_ports.append(port_no)
                if port.name.startswith('obr') and '_' in port.name:
                    _, ip_suffix = port.name.split('_')
                    ip_suffix = ip_suffix.replace('-', '.')
                    peer_ip = '.'.join((self.ip_prefix, ip_suffix))
                    self.dpid_to_conns[dpid][peer_ip] = port_no
                    LOGGER.info("Add: (switch=%s, peer_ip=%s) -> port=%s",
                                dpid_to_str(dpid), peer_ip, port_no)
                elif port.name == 'eth_dhcp':
                    self.inception_dhcp.update_server(dpid, port_no)
                    LOGGER.info("DHCP server is found!")
                else:
                    # Store the port connecting local hosts
                    host_ports.append(port_no)

            # Intercepts all ARP packets and send them to the controller
            actions_arp = [ofproto_parser.OFPActionOutput(
                    ofproto.OFPP_CONTROLLER, Inception.MAX_LEN)]
            instruction_arp = [datapath.ofproto_parser.OFPInstructionActions(
                    ofproto.OFPIT_APPLY_ACTIONS, actions_arp)]
            datapath.send_msg(ofproto_parser.OFPFlowMod(
                datapath=datapath,
                match=ofproto_parser.OFPMatch(
                    eth_type=ether.ETH_TYPE_ARP,
                ),
                priority=priority.ARP,
                flags=ofproto.OFPFF_SEND_FLOW_REM,
                cookie=0, command=ofproto.OFPFC_ADD,
                instructions=instruction_arp
            ))

            # Intercepts DHCP packets and send them to the controller
            instruction_dhcp = instruction_arp
            datapath.send_msg(ofproto_parser.OFPFlowMod(
                datapath=datapath,
                match=ofproto_parser.OFPMatch(
                    eth_type=ether.ETH_TYPE_IP,
                    ip_proto=inet.IPPROTO_UDP,
                    tcp_src=68,
                ),
                priority=priority.DHCP,
                flags=ofproto.OFPFF_SEND_FLOW_REM,
                cookie=0, command=ofproto.OFPFC_ADD,
                instructions=instruction_dhcp
            ))

            # Set up flow at the currently connected switch On
            # receiving a broadcast message, the switch forwards it to
            # all non-vxlan ports.
            # TODO(chenche): need to setup more flows for new hosts in the
            # future
            actions_bcast_in = [ofproto_parser.OFPActionOutput(
                port=port_no) for port_no in host_ports]
            instruction_bcast_in = [datapath.ofproto_parser.OFPInstructionActions(
                    ofproto.OFPIT_APPLY_ACTIONS, actions_bcast_in)]
            datapath.send_msg(ofproto_parser.OFPFlowMod(
                datapath=datapath,
                match=ofproto_parser.OFPMatch(
                    eth_dst=mac.BROADCAST_STR,
                ),

                priority=priority.SWITCH_BCAST,
                flags=ofproto.OFPFF_SEND_FLOW_REM,
                cookie=0, command=ofproto.OFPFC_ADD,
                instructions=instruction_bcast_in
            ))

            # Default flows: Process via normal L2/L3 legacy switch
            # configuration
            actions_norm = [ofproto_parser.
                                    OFPActionOutput(ofproto.OFPP_NORMAL)]
            instruction_norm = [datapath.ofproto_parser.OFPInstructionActions(
                    ofproto.OFPIT_APPLY_ACTIONS, actions_norm)]
            datapath.send_msg(ofproto_parser.OFPFlowMod(
                datapath=datapath,
                match=ofproto_parser.OFPMatch(),
                priority=priority.NORMAL,
                flags=ofproto.OFPFF_SEND_FLOW_REM,
                cookie=0, command=ofproto.OFPFC_ADD,
                instructions=instruction_norm
            ))
        # A switch disconnects
        else:
            LOGGER.info("Del: switch=%s -> ip=%s", dpid_to_str(dpid),
                        self.dpid_to_ip[dpid])
            # Delete switch's mapping from switch dpid to remote IP address
            del self.dpid_to_ip[dpid]

            # Delete the switch's connection info
            del self.dpid_to_conns[dpid]

            # Delete all connected hosts
            for mac_addr in self.mac_to_dpid_port.keys():
                local_dpid, _ = self.mac_to_dpid_port[mac_addr]
                if local_dpid == dpid:
                    del self.mac_to_dpid_port[mac_addr]

            # Delete all rules tracking

    @handler.set_ev_cls(ofp_event.EventOFPPacketIn, handler.MAIN_DISPATCHER)
    def packet_in_handler(self, event):
        """
        Handle when a packet is received
        """
        LOGGER.debug("Packet received")
        # do source learning
        self._do_source_learning(event)
        # handle ARP packet if it is
        self.inception_arp.handle(event)
        # handle DHCP packet if it is
        # self.inception_dhcp.handle(event)

    def _do_source_learning(self, event):
        """
        Learn MAC => (switch dpid, switch port) mapping from a packet,
        update self.mac_to_dpid_port table. Also set up flow table for
        forwarding broadcast message
        """
        msg = event.msg
        datapath = msg.datapath
        dpid = datapath.id
        ofproto_parser = datapath.ofproto_parser
        ofproto = datapath.ofproto
        in_port = msg.match['in_port']

        whole_packet = packet.Packet(msg.data)
        ethernet_header = whole_packet.get_protocol(ethernet.ethernet)
        ethernet_src = ethernet_header.src
        if ethernet_src not in self.mac_to_dpid_port:
            self.mac_to_dpid_port[ethernet_src] = (dpid, in_port)
            LOGGER.info("Add: (Mac %s) => (port %s)", ethernet_src, in_port)
            # Set unicast flow to ethernet_src
            actions_unicast = [ofproto_parser.OFPActionOutput(in_port)]
            instructions_unicast = [datapath.ofproto_parser.
                    OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                          actions_unicast)]
            datapath.send_msg(ofproto_parser.OFPFlowMod(
                    datapath=datapath,
                    match=ofproto_parser.OFPMatch(eth_dst=ethernet_src),
                    cookie=0, command=ofproto.OFPFC_ADD,
                    priority=priority.DATA_FWD,
                    flags=ofproto.OFPFF_SEND_FLOW_REM,
                    instructions=instructions_unicast
                    ))
            self.unicast_rules.append((dpid, ethernet_src))
            # Set up broadcast flow when local hosts are sources
            # Note(changbl): where are "broadcast_ports"?
            actions_bcast_out = [ofproto_parser.OFPActionOutput(
                                                    ofproto.OFPP_ALL)]
            instructions_bcast_out = [datapath.ofproto_parser.
                    OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                          actions_bcast_out)]
            datapath.send_msg(ofproto_parser.OFPFlowMod(
                datapath=datapath,
                match=ofproto_parser.OFPMatch(
                    eth_src=ethernet_src,
                    eth_dst=mac.BROADCAST_STR,
                ),
                priority=priority.HOST_BCAST,
                flags=ofproto.OFPFF_SEND_FLOW_REM,
                cookie=0, command=ofproto.OFPFC_ADD,
                instructions=instructions_bcast_out
                ))
        else:
            (dpid_record, in_port_record) = self.mac_to_dpid_port[ethernet_src]
            if dpid_record != dpid:
                # The server has been migrated to other switches
                datapath_record = self.dpset.get(dpid_record)
                ip_datapath = self.dpid_to_ip[dpid]

                self.mac_to_dpid_port[ethernet_src] = (dpid, in_port)
                LOGGER.info("Update: (Mac %s) => (port %s)",
                                    ethernet_src, in_port)
                # Set up new broadcast flow
                actions_bcast_out = [ofproto_parser.OFPActionOutput(
                                                    ofproto.OFPP_ALL)]
                instructions_bcast_out = [datapath.ofproto_parser.
                    OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                          actions_bcast_out)]
                datapath.send_msg(ofproto_parser.OFPFlowMod(
                datapath=datapath,
                match=ofproto_parser.OFPMatch(
                    eth_src=ethernet_src,
                    eth_dst=mac.BROADCAST_STR,
                ),
                priority=priority.HOST_BCAST,
                flags=ofproto.OFPFF_SEND_FLOW_REM,
                cookie=0, command=ofproto.OFPFC_ADD,
                instructions=instructions_bcast_out))
                # Delete old broadcast flow
                datapath_record.send_msg(ofproto_parser.OFPFlowMod(
                    datapath=datapath_record,
                    match=ofproto_parser.OFPMatch(
                        eth_src=ethernet_src,
                        eth_dst=mac.BROADCAST_STR,
                        ),
                    priority=priority.HOST_BCAST,
                    flags=ofproto.OFPFF_SEND_FLOW_REM,
                    cookie=0, command=ofproto.OFPFC_DELETE_STRICT,
                    instructions=instructions_bcast_out))
                # Add flow on new datapath towards ethernet_src
                flow_command = ofproto.OFPFC_ADD
                if (dpid, ethernet_src) in self.unicast_rules:
                    flow_command = ofproto.OFPFC_MODIFY_STRICT
                else:
                    self.unicast_rules.append((dpid, ethernet_src))
                actions_inport = [ofproto_parser.OFPActionOutput(in_port)]
                instructions_inport = [datapath.ofproto_parser.
                    OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                          actions_inport)]
                datapath.send_msg(ofproto_parser.OFPFlowMod(
                    datapath=datapath,
                    match=ofproto_parser.OFPMatch(eth_dst=ethernet_src),
                    cookie=0, command=flow_command,
                    priority=priority.DATA_FWD,
                    flags=ofproto.OFPFF_SEND_FLOW_REM,
                    instructions=instructions_inport
                    ))
                # Mofidy flows on all other datapaths contacting ethernet_src
                for (remote_dpid, dst_mac) in self.unicast_rules:
                    if remote_dpid == dpid or dst_mac != ethernet_src:
                        continue

                    remote_datapath = self.dpset.get(remote_dpid)
                    remote_fwd_port = (self.dpid_to_conns[remote_dpid]
                                                                [ip_datapath])
                    actions_remote = [ofproto_parser.OFPActionOutput(
                                                        remote_fwd_port)]
                    instructions_remote = [datapath.ofproto_parser.
                            OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                          actions_remote)]

                    remote_datapath.send_msg(ofproto_parser.OFPFlowMod(
                    datapath=remote_datapath,
                    match=ofproto_parser.OFPMatch(eth_dst=ethernet_src),
                    cookie=0, command=ofproto.OFPFC_MODIFY_STRICT,
                    priority=priority.DATA_FWD,
                    flags=ofproto.OFPFF_SEND_FLOW_REM,
                    instructions=instructions_remote
                    ))