Пример #1
0
        def vlan_load_reg():
            if vlan_removed:
                of_actions.append(nx.nx_reg_load(dst=vlan_reg,
                                                 value=0, nbits=16))
            else:
                """The load/unload operations are complicated to simplify the intermediate
                masked write operations. This is really helpful with multiple
                stages: it's easier to do a single masked write per stage; there
                are more stages which do the former than load/unload.

                Basically what is happening is the following mapping of
                different parts of the VLAN_TCI field into the register
                (typically NXM_NX_REG3) as follows:

                REG3:        CFI PCP2 PCP1 PCP0 ID11 ID10 ... ID1 ID0
                VLAN_TCI:    PCP2 PCP1 PCP0 CFI ID11 ID10 ... ID1 ID0

                This enables masked writes considering the VLAN as one
                *contiguous* 15 bit field, instead of breaking the masked write
                into two writes, one for the ID part and one for the PCP part
                (yuck!)
                """
                of_actions.append(nx.nx_reg_move(dst=vlan_reg,
                                                 src=nx.NXM_OF_VLAN_TCI,
                                                 dst_ofs=0, src_ofs=0,
                                                 nbits=12))
                of_actions.append(nx.nx_reg_move(dst=vlan_reg,
                                                 src=nx.NXM_OF_VLAN_TCI,
                                                 dst_ofs=12, src_ofs=13,
                                                 nbits=3))
                of_actions.append(nx.nx_reg_move(dst=vlan_reg,
                                                 src=nx.NXM_OF_VLAN_TCI,
                                                 dst_ofs=15, src_ofs=12,
                                                 nbits=1))
Пример #2
0
    def _add_arp_entry(self, ip_or_arp, eth=None):
        """
    Creates an entry in the switch ARP table

    You can either pass an ARP packet or an IP and Ethernet address
    """
        if not self._conn: return
        if eth is None:
            assert isinstance(ip_or_arp, pkt.arp)
            ip = ip_or_arp.protosrc
            eth = ip_or_arp.hwsrc
        else:
            ip = ip_or_arp
        self.log.debug("Populating ARP table with %s -> %s", ip, eth)

        fm = ovs.ofp_flow_mod_table_id()
        fm.xid = 0
        fm.table_id = ARP_TABLE
        fm.idle_timeout = ARP_IDLE_TIMEOUT
        fm.hard_timeout = ARP_HARD_TIMEOUT
        fm.match.dl_type = pkt.ethernet.IP_TYPE
        fm.match.nw_dst = ip
        fm.actions.append(of.ofp_action_dl_addr.set_dst(eth))
        fm.actions.append(
            ovs.nx_reg_move(src=DST_IP_REGISTER, dst=ovs.NXM_OF_IP_DST))
        fm.actions.append(ovs.nx_output_reg(reg=OUT_PORT_REGISTER))
        self._conn.send(fm)
Пример #3
0
 def vlan_write_back():
     """ The write back operation is slightly complicated for reasons
     described under `vlan_load_reg()`. """
     if debug:
         print "pox_client: build_nx_actions: Writing back VLAN. The actions are:"
         print actions
     if table_id > 0:
         of_actions.append(nx.nx_reg_move(src=vlan_reg,
                                          dst=nx.NXM_OF_VLAN_TCI,
                                          src_ofs=0, dst_ofs=0,
                                          nbits=12))
         of_actions.append(nx.nx_reg_move(src=vlan_reg,
                                          dst=nx.NXM_OF_VLAN_TCI,
                                          src_ofs=12, dst_ofs=13,
                                          nbits=3))
         of_actions.append(nx.nx_reg_move(src=vlan_reg,
                                          dst=nx.NXM_OF_VLAN_TCI,
                                          src_ofs=15, dst_ofs=12,
                                          nbits=1))
Пример #4
0
    def build_nx_actions(self,inport,action_list,table_id,pipeline):
        ### BUILD NX ACTIONS
        of_actions = []
        ctlr_outport = False # there is a controller outport action
        phys_outports = list() # list of physical outports to forward out of
        possibly_resubmit_next_table = False # should packet be passed on to next table?
        atleast_one_action = False

        for actions in action_list:
            atleast_one_action = True
            if 'srcmac' in actions:
                of_actions.append(of.ofp_action_dl_addr.set_src(actions['srcmac']))
            if 'dstmac' in actions:
                of_actions.append(of.ofp_action_dl_addr.set_dst(actions['dstmac']))
            if 'srcip' in actions:
                of_actions.append(of.ofp_action_nw_addr.set_src(actions['srcip']))
            if 'dstip' in actions:
                of_actions.append(of.ofp_action_nw_addr.set_dst(actions['dstip']))
            if 'srcport' in actions:
                of_actions.append(of.ofp_action_tp_port.set_src(actions['srcport']))
            if 'dstport' in actions:
                of_actions.append(of.ofp_action_tp_port.set_dst(actions['dstport']))
            if 'vlan_id' in actions:
                if actions['vlan_id'] is None:
                    of_actions.append(of.ofp_action_strip_vlan())
                else:
                    of_actions.append(of.ofp_action_vlan_vid(vlan_vid=actions['vlan_id']))
            if 'vlan_pcp' in actions:
                if actions['vlan_pcp'] is None:
                    if not actions['vlan_id'] is None:
                        raise RuntimeError("vlan_id and vlan_pcp must be set together!")
                    pass
                else:
                    of_actions.append(of.ofp_action_vlan_pcp(vlan_pcp=actions['vlan_pcp']))

            assert 'port' in actions
            outport = actions['port']

            if outport == of.OFPP_CONTROLLER:
                ctlr_outport = True
            else:
                """ There is either a physical output action (i.e., on a
                non-controller port), or a "send to next table" action."""
                possibly_resubmit_next_table = True
                if outport != CUSTOM_NEXT_TABLE_PORT:
                    phys_outports.append(outport)
                """ Otherwise there are no physical outports; just a possibility
                of resubmitting to the next table. Pass. """

        """In general, actual packet forwarding may have to wait until the final table
        in the pipeline. This means we must determine if there is a "next" table
        that processes the packet from here, or if this is the last one.

        But first, the easy part. There are exactly three cases where a
        forwarding table *will* in fact "immediately forward" a packet according
        to the current rule (and all previous table stages that processed the
        packet), without waiting for any other further processing:

        (1) if the packet is dropped by the current rule,
        (2) if the packet is forwarded to the controller port, or
        (3) if this is the last stage of the pipeline.

        In the case of (1) and (2), packet forwarding may happen immediately and
        only depend on the current rule. But in (3), the forwarding decision
        must take the current rule as well as previous port changes into
        account, as follows:

        (a) if the current rule specifies an output port, forward the packet out
        of that port.

        (b) if the current rule does not specify an outport, then forward the
        packet out of the port using the value stored in the dedicated
        per-packet port register.

        If neither of (1)-(3) above is true, then we take the following
        approach:

        (a) if there is an outport set by this rule, write that value into the
        dedicated per-packet register that contains the current port the
        packet is in.

        (b) if there is no outport set by this rule, and if this is table id 0,
        move the value of the inport into the dedicated per-packet port
        register. This denotes that the packet is currently still on its inport.

        (c) resubmit the packet to the "next" table (according to the pipeline).
        """
        exists_next_table = table_id in pipeline.edges

        # Decide first on "immediate forwarding" conditions:
        immediately_fwd = True
        if not atleast_one_action: # (1) drop
            of_actions = []
        elif ctlr_outport: # (2) controller
            of_actions = []
            of_actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        elif possibly_resubmit_next_table and (not exists_next_table):
            # (3) last stage of pipeline
            if len(phys_outports) > 0: # fwd out of latest assigned ports
                for p in phys_outports:
                    of_actions.append(of.ofp_action_output(port=p))
            else:
                # fwd out of stored port value
                of_actions.append(nx.nx_output_reg(reg=nx.NXM_NX_REG2,
                                                   nbits=16))
        elif (not exists_next_table) and (not possibly_resubmit_next_table):
            raise RuntimeError("Unexpected condition in multi-stage processing")
        else: # must resubmit packet to subsequent tables for processing
            immediately_fwd = False

        if immediately_fwd:
            return of_actions

        # Act on packet with knowledge that subsequent tables must process it
        assert (possibly_resubmit_next_table and exists_next_table and
                (not immediately_fwd))
        next_table = pipeline.edges[table_id]
        if len(phys_outports) > 0:
            # move port register to latest assigned port values
            for p in phys_outports:
                of_actions.append(nx.nx_reg_load(dst=nx.NXM_NX_REG2,
                                                 value=p, nbits=16))
                of_actions.append(nx.nx_action_resubmit.resubmit_table(
                    table=next_table))
        elif table_id == 0:
            # move the inport value to reg2.
            of_actions.append(nx.nx_reg_move(src=nx.NXM_OF_IN_PORT,
                                             dst=nx.NXM_NX_REG2,
                                             nbits=16))
            of_actions.append(nx.nx_action_resubmit.resubmit_table(
                table=next_table))
        else:
            of_actions.append(nx.nx_action_resubmit.resubmit_table(
                table=next_table))
        return of_actions
Пример #5
0
    def _init_ingress_table(self):
        self._clear_table(INGRESS_TABLE)

        # INGRESS_TABLE: Send RIP to controller
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.cookie = RIP_PACKET_COOKIE
        fm.match.dl_type = pkt.ethernet.IP_TYPE
        fm.match.dl_dst = RIP.RIP2_ADDRESS.multicast_ethernet_address
        fm.match.nw_dst = RIP.RIP2_ADDRESS
        fm.match.nw_proto = pkt.ipv4.UDP_PROTOCOL
        fm.match.tp_dst = RIP.RIP_PORT
        fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        self._conn.send(fm)

        #TODO: Add RIP entry for unicast advertisements?  Or be liberal here
        #      and validate on the controller side?

        # INGRESS_TABLE: Send ARP requests for router to controller
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.cookie = ARP_REQUEST_COOKIE
        fm.match.dl_type = pkt.ethernet.ARP_TYPE
        fm.match.nw_proto = pkt.arp.REQUEST
        fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        for portno, portobj in self._ports.iteritems():
            if portno not in self._conn.ports: continue
            fm.match.in_port = portno
            for ip in portobj.ips:
                fm.match.nw_dst = ip
                self._conn.send(fm)

        # INGRESS_TABLE: Send ARP replies send to router to controller
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.cookie = ARP_REPLY_COOKIE
        fm.match.dl_type = pkt.ethernet.ARP_TYPE
        fm.match.nw_proto = pkt.arp.REPLY
        fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        for portno, portobj in self._ports.iteritems():
            if portno not in self._conn.ports: continue
            fm.match.in_port = portno
            fm.match.dl_dst = self._conn.ports[portno].hw_addr
            self._conn.send(fm)

        # INGRESS_TABLE: Send ICMP to controller
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.cookie = PING_COOKIE
        fm.match.dl_type = pkt.ethernet.IP_TYPE
        fm.match.nw_proto = pkt.ipv4.ICMP_PROTOCOL
        fm.match.tp_src = pkt.ICMP.TYPE_ECHO_REQUEST  # Type
        fm.match.tp_dst = 0  # Code
        fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        for portno, portobj in self._ports.iteritems():
            if portno not in self._conn.ports: continue
            fm.match.in_port = portno
            fm.match.dl_dst = self._conn.ports[portno].hw_addr
            for ip in self.all_ips:
                fm.match.nw_dst = ip
                self._conn.send(fm)

        if core.hasComponent("DHCPD"):
            # INGRESS_TABLE: Send DHCP to controller
            fm = ovs.ofp_flow_mod_table_id()
            fm.table_id = INGRESS_TABLE
            fm.cookie = DHCP_COOKIE
            fm.match.dl_type = pkt.ethernet.IP_TYPE
            fm.match.nw_proto = pkt.ipv4.UDP_PROTOCOL
            fm.match.tp_src = pkt.dhcp.CLIENT_PORT
            fm.match.tp_dst = pkt.dhcp.SERVER_PORT
            fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
            for portno, dhcpd in core.DHCPD.get_ports_for_dpid(self.dpid):
                if portno not in self._conn.ports: continue
                if dhcpd._install_flow:
                    self.log.warn(
                        "Turning off DHCP server table entry installation.")
                    self.log.warn(
                        "You probably want to configure it with no_flow.")
                    dhcpd._install_flow = False
                fm.match.in_port = portno
                fm.match.dl_dst = pkt.ETHERNET.ETHER_BROADCAST
                fm.match.nw_dst = pkt.IPV4.IP_BROADCAST
                self._conn.send(fm)

                fm.match.dl_dst = self._conn.ports[portno].hw_addr
                fm.match.nw_dst = dhcpd.ip_addr
                self._conn.send(fm)

        # INGRESS_TABLE: IP packets (lower priority)
        fm = ovs.ofp_flow_mod_table_id()
        fm.table_id = INGRESS_TABLE
        fm.priority -= 1
        fm.match.dl_type = pkt.ethernet.IP_TYPE
        fm.actions.append(
            ovs.nx_reg_move(dst=DST_IP_REGISTER, src=ovs.NXM_OF_IP_DST))
        fm.actions.append(ovs.nx_action_dec_ttl())
        fm.actions.append(ovs.nx_action_resubmit.resubmit_table(RIP_NET_TABLE))
        self._conn.send(fm)
Пример #6
0
    def build_nx_actions(self,inport,action_list,table_id,pipeline,debug=False):
        ### BUILD NX ACTIONS
        of_actions = []
        ctlr_outport = False # there is a controller outport action
        phys_outports = list() # list of physical outports to forward out of
        possibly_resubmit_next_table = False # should packet be passed on to next table?
        atleast_one_action = False

        # vlan handling flags
        vlan_removed = False
        vlan_written = {}

        if debug:
            print "pox_client: build_nx_actions: Received actions:"
            print action_list

        for actions in action_list:
            atleast_one_action = True
            if 'srcmac' in actions:
                of_actions.append(of.ofp_action_dl_addr.set_src(actions['srcmac']))
            if 'dstmac' in actions:
                of_actions.append(of.ofp_action_dl_addr.set_dst(actions['dstmac']))
            if 'srcip' in actions:
                of_actions.append(of.ofp_action_nw_addr.set_src(actions['srcip']))
            if 'dstip' in actions:
                of_actions.append(of.ofp_action_nw_addr.set_dst(actions['dstip']))
            if 'srcport' in actions:
                of_actions.append(of.ofp_action_tp_port.set_src(actions['srcport']))
            if 'dstport' in actions:
                of_actions.append(of.ofp_action_tp_port.set_dst(actions['dstport']))
            if 'vlan_id' in actions:
                if actions['vlan_id'] is None:
                    vlan_removed = True
                else:
                    assert 'vlan_pcp' in actions
                    assert 'vlan_offset' in actions
                    assert 'vlan_nbits' in actions
                    vlan_written = {k: actions['vlan_' + k] for k in
                                      ['pcp', 'offset', 'nbits', 'id']}
            if 'vlan_pcp' in actions:
                assert 'vlan_id' in actions, "vlan_id and vlan_pcp must be set together"
            assert 'port' in actions
            outport = actions['port']

            if outport == of.OFPP_CONTROLLER:
                ctlr_outport = True
            else:
                """ There is either a physical output action (i.e., on a
                non-controller port), or a "send to next table" action."""
                possibly_resubmit_next_table = True
                if outport != CUSTOM_NEXT_TABLE_PORT:
                    phys_outports.append(outport)
                """ Otherwise there are no physical outports; just a possibility
                of resubmitting to the next table. Pass. """

        """Construct routines to move packet VLAN into the register, do a masked write
        into the register, and to write back the register into the packet.
        """
        vlan_reg = nx.NXM_NX_REG3
        def vlan_load_reg():
            if vlan_removed:
                of_actions.append(nx.nx_reg_load(dst=vlan_reg,
                                                 value=0, nbits=16))
            else:
                """The load/unload operations are complicated to simplify the intermediate
                masked write operations. This is really helpful with multiple
                stages: it's easier to do a single masked write per stage; there
                are more stages which do the former than load/unload.

                Basically what is happening is the following mapping of
                different parts of the VLAN_TCI field into the register
                (typically NXM_NX_REG3) as follows:

                REG3:        CFI PCP2 PCP1 PCP0 ID11 ID10 ... ID1 ID0
                VLAN_TCI:    PCP2 PCP1 PCP0 CFI ID11 ID10 ... ID1 ID0

                This enables masked writes considering the VLAN as one
                *contiguous* 15 bit field, instead of breaking the masked write
                into two writes, one for the ID part and one for the PCP part
                (yuck!)
                """
                of_actions.append(nx.nx_reg_move(dst=vlan_reg,
                                                 src=nx.NXM_OF_VLAN_TCI,
                                                 dst_ofs=0, src_ofs=0,
                                                 nbits=12))
                of_actions.append(nx.nx_reg_move(dst=vlan_reg,
                                                 src=nx.NXM_OF_VLAN_TCI,
                                                 dst_ofs=12, src_ofs=13,
                                                 nbits=3))
                of_actions.append(nx.nx_reg_move(dst=vlan_reg,
                                                 src=nx.NXM_OF_VLAN_TCI,
                                                 dst_ofs=15, src_ofs=12,
                                                 nbits=1))

        def vlan_masked_write():
            if debug:
                print "pox_client: build_nx_actions: in rewrite function:"
                print vlan_removed, vlan_written
            if vlan_removed:
                if debug:
                    print "pox_client: build_nx_actions: VLAN was removed"
                of_actions.append(nx.nx_reg_load(dst=vlan_reg,
                                                 value=0, nbits=16))
            elif vlan_written:
                vlan_16bit = ((int(vlan_written['pcp']) << 12) |
                              (int(vlan_written['id'])))
                load_value = ((vlan_16bit >> vlan_written['offset']) &
                              ((1 << vlan_written['nbits'])-1))
                of_actions.append(nx.nx_reg_load(dst=vlan_reg,
                                                 value=load_value,
                                                 offset=vlan_written['offset'],
                                                 nbits=vlan_written['nbits']))
                of_actions.append(nx.nx_reg_load(dst=vlan_reg,
                                                 value=1,
                                                 offset=15,
                                                 nbits=1))

        def vlan_write_back():
            """ The write back operation is slightly complicated for reasons
            described under `vlan_load_reg()`. """
            if debug:
                print "pox_client: build_nx_actions: Writing back VLAN. The actions are:"
                print actions
            if table_id > 0:
                of_actions.append(nx.nx_reg_move(src=vlan_reg,
                                                 dst=nx.NXM_OF_VLAN_TCI,
                                                 src_ofs=0, dst_ofs=0,
                                                 nbits=12))
                of_actions.append(nx.nx_reg_move(src=vlan_reg,
                                                 dst=nx.NXM_OF_VLAN_TCI,
                                                 src_ofs=12, dst_ofs=13,
                                                 nbits=3))
                of_actions.append(nx.nx_reg_move(src=vlan_reg,
                                                 dst=nx.NXM_OF_VLAN_TCI,
                                                 src_ofs=15, dst_ofs=12,
                                                 nbits=1))

        """In general, actual packet forwarding may have to wait until the final table
        in the pipeline. This means we must determine if there is a "next" table
        that processes the packet from here, or if this is the last one.

        But first, the easy part. There are exactly three cases where a
        forwarding table *will* in fact "immediately forward" a packet according
        to the current rule (and all previous table stages that processed the
        packet), without waiting for any other further processing:

        (1) if the packet is dropped by the current rule,
        (2) if the packet is forwarded to the controller port, or
        (3) if this is the last stage of the pipeline.

        In the case of (1) and (2), packet forwarding may happen immediately and
        only depend on the current rule. But in (3), the forwarding decision
        must take the current rule as well as previous port changes into
        account, as follows:

        (a) if the current rule specifies an output port, forward the packet out
        of that port.

        (b) if the current rule does not specify an outport, then forward the
        packet out of the port using the value stored in the dedicated
        per-packet port register.

        If neither of (1)-(3) above is true, then we take the following
        approach:

        (a) if there is an outport set by this rule, write that value into the
        dedicated per-packet register that contains the current port the
        packet is in.

        (b) if there is no outport set by this rule, and if this is table id 0,
        move the value of the inport into the dedicated per-packet port
        register. This denotes that the packet is currently still on its inport.

        (c) resubmit the packet to the "next" table (according to the pipeline).
        """
        exists_next_table = table_id in pipeline.edges

        # Decide first on "immediate forwarding" conditions:
        immediately_fwd = True
        if not atleast_one_action: # (1) drop
            of_actions = []
        elif ctlr_outport: # (2) controller
            of_actions = []
            vlan_write_back()
            of_actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER))
        elif possibly_resubmit_next_table and (not exists_next_table):
            # (3) last stage of pipeline
            vlan_masked_write()
            vlan_write_back()
            if len(phys_outports) > 0: # fwd out of latest assigned ports
                for p in phys_outports:
                    of_actions.append(of.ofp_action_output(port=p))
            else:
                # fwd out of stored port value
                of_actions.append(nx.nx_output_reg(reg=nx.NXM_NX_REG2,
                                                   nbits=16))
        elif (not exists_next_table) and (not possibly_resubmit_next_table):
            raise RuntimeError("Unexpected condition in multi-stage processing")
        else: # must resubmit packet to subsequent tables for processing
            immediately_fwd = False

        if immediately_fwd:
            return of_actions

        if debug:
            print "pox_client: build_nx_actions: Not the last stage."

        # Act on packet with knowledge that subsequent tables must process it
        assert (possibly_resubmit_next_table and exists_next_table and
                (not immediately_fwd))

        # 1. Handle VLAN writing for resubmitted packets
        if table_id == 0:
            vlan_load_reg()
        vlan_masked_write()

        # 2. Handle packet forwarding for resubmitted packets
        next_table = pipeline.edges[table_id]
        if len(phys_outports) > 0:
            # move port register to latest assigned port values
            for p in phys_outports:
                of_actions.append(nx.nx_reg_load(dst=nx.NXM_NX_REG2,
                                                 value=p, nbits=16))
                of_actions.append(nx.nx_action_resubmit.resubmit_table(
                    table=next_table))
        elif table_id == 0:
            # move the inport value to reg2.
            of_actions.append(nx.nx_reg_move(src=nx.NXM_OF_IN_PORT,
                                             dst=nx.NXM_NX_REG2,
                                             nbits=16))
            of_actions.append(nx.nx_action_resubmit.resubmit_table(
                table=next_table))
        else:
            of_actions.append(nx.nx_action_resubmit.resubmit_table(
                table=next_table))
        return of_actions