Пример #1
0
    def _install_default_flows(self, datapath):
        """
        For each direction set the default flows to just forward to next table.
        If mirror flag set, copy all packets to li mirror port.

        Match traffic from local LI port and redirect it to dst li port

        Args:
            datapath: ryu datapath struct
        """
        parser = datapath.ofproto_parser
        inbound_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                                   direction=Direction.IN)
        outbound_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                                    direction=Direction.OUT)
        actions = []
        if self._mirror_all and self._li_dst_port_num:
            self.logger.warning("Mirroring all traffic to LI")
            actions = [parser.OFPActionOutput(self._li_dst_port_num)]
        flows.add_resubmit_next_service_flow(datapath, self.tbl_num,
                                             inbound_match, actions,
                                             priority=flows.MINIMUM_PRIORITY,
                                             resubmit_table=self.next_table)
        flows.add_resubmit_next_service_flow(datapath, self.tbl_num,
                                             outbound_match, actions,
                                             priority=flows.MINIMUM_PRIORITY,
                                             resubmit_table=self.next_table)
        if self._li_dst_port_num:
            li_match = MagmaMatch(in_port=self._li_local_port_num)
            flows.add_output_flow(datapath, self.tbl_num, li_match, [],
                                  output_port=self._li_dst_port_num)
Пример #2
0
    def _install_default_egress_flows(self, dp, mac_addr: str = ""):
        """
        Egress table is the last table that a packet touches in the pipeline.
        Output downlink traffic to gtp port, uplink trafic to LOCAL

        Raises:
            MagmaOFError if any of the default flows fail to install.
        """
        downlink_match = MagmaMatch(direction=Direction.IN)
        flows.add_output_flow(dp,
                              self._egress_tbl_num,
                              downlink_match, [],
                              output_port=self.config.gtp_port)

        uplink_match = MagmaMatch(direction=Direction.OUT)
        actions = []
        # avoid resetting mac address on switch connect event.
        if mac_addr == "":
            mac_addr = self._current_upstream_mac

        if mac_addr != "":
            parser = dp.ofproto_parser
            actions.append(
                parser.NXActionRegLoad2(dst='eth_dst', value=mac_addr))

            self.logger.info("Using GW: %s actions: %s", mac_addr,
                             str(actions))
            self._current_upstream_mac = mac_addr

        flows.add_output_flow(dp,
                              self._egress_tbl_num,
                              uplink_match,
                              actions=actions,
                              output_port=self._uplink_port)
Пример #3
0
    def _install_proxy_flows(self, dp):
        """
        Install egress flows
        Args:
            dp datapath
            table_no table to install flow
            out_port specify egress port, if None reg value is used
            priority flow priority
            direction packet direction.
        """
        if self.config.he_proxy_port <= 0:
            return

        self.logger.info("Header Enrichment : %s", self.config.he_proxy_port)
        parser = dp.ofproto_parser

        match = MagmaMatch(proxy_tag=PROXY_TAG_TO_PROXY)
        actions = [
            parser.NXActionRegLoad2(dst='eth_dst',
                                    value=self.config.he_proxy_eth_mac)
        ]
        flows.add_output_flow(dp,
                              self._egress_tbl_num,
                              match,
                              priority=flows.UE_FLOW_PRIORITY,
                              actions=actions,
                              output_port=self.config.he_proxy_port)
Пример #4
0
    def _install_default_middle_flows(self, dp):
        """
        Egress table is the last table that a packet touches in the pipeline.
        Output downlink traffic to gtp port, uplink trafic to LOCAL

        Raises:
            MagmaOFError if any of the default flows fail to install.
        """
        tbl_num = self._service_manager.get_table_num(PHYSICAL_TO_LOGICAL)
        logical_table = \
            self._service_manager.get_next_table_num(PHYSICAL_TO_LOGICAL)
        egress = self._service_manager.get_table_num(EGRESS)

        # Allow passthrough pkts(skip enforcement and send to egress table)
        ps_match = MagmaMatch(passthrough=PASSTHROUGH_REG_VAL)
        flows.add_resubmit_next_service_flow(dp, tbl_num, ps_match,
            actions=[], priority=flows.PASSTHROUGH_PRIORITY,
            resubmit_table=egress)

        match = MagmaMatch()
        flows.add_resubmit_next_service_flow(dp,
            self._service_manager.get_table_num(PHYSICAL_TO_LOGICAL), match,
            actions=[], priority=flows.DEFAULT_PRIORITY,
            resubmit_table=logical_table)

        if self._mtr_service_enabled:
            match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                               ipv4_dst=self.config.mtr_ip)
            flows.add_output_flow(dp,
                self._service_manager.get_table_num(PHYSICAL_TO_LOGICAL), match,
                [], priority=flows.UE_FLOW_PRIORITY,
                output_port=self.config.mtr_port)
Пример #5
0
def _install_vlan_egress_flows(dp,
                               table_no,
                               out_port,
                               ip,
                               priority=0,
                               direction=Direction.IN):
    # Pass non vlan packet as it is.
    match = MagmaMatch(direction=direction,
                       eth_type=ether_types.ETH_TYPE_IP,
                       vlan_vid=(0x0000, 0x1000),
                       ipv4_dst=ip)
    flows.add_output_flow(dp,
                          table_no,
                          match, [],
                          priority=priority,
                          output_port=out_port)

    # remove vlan header for out_port.
    match = MagmaMatch(direction=direction,
                       eth_type=ether_types.ETH_TYPE_IP,
                       vlan_vid=(0x1000, 0x1000),
                       ipv4_dst=ip)
    actions_vlan_pop = [dp.ofproto_parser.OFPActionPopVlan()]
    flows.add_output_flow(dp,
                          table_no,
                          match,
                          actions_vlan_pop,
                          priority=priority,
                          output_port=out_port)
Пример #6
0
    def _add_dhcp_passthrough_flows(self, sid, mac_addr):
        ofproto, parser = self._datapath.ofproto, self._datapath.ofproto_parser

        # Set so inout knows to skip tables and send to egress
        action = load_direction(parser, Direction.PASSTHROUGH)
        uplink_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                                  ip_proto=IPPROTO_UDP,
                                  udp_src=68,
                                  udp_dst=67,
                                  eth_src=mac_addr)
        self._add_resubmit_flow(sid, uplink_match, action,
                                flows.PASSTHROUGH_PRIORITY)

        downlink_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                                    ip_proto=IPPROTO_UDP,
                                    udp_src=67,
                                    udp_dst=68,
                                    eth_dst=mac_addr)
        # Set so triggers packetin and we can learn the ip to do arp response
        self._add_resubmit_flow(sid,
                                downlink_match,
                                action,
                                flows.PASSTHROUGH_PRIORITY,
                                next_table=self._dhcp_learn_scratch)

        # Install default flow for dhcp learn scratch
        imsi_match = MagmaMatch(imsi=encode_imsi(sid))
        flows.add_output_flow(self._datapath,
                              self._dhcp_learn_scratch,
                              match=imsi_match,
                              actions=[],
                              priority=flows.PASSTHROUGH_PRIORITY,
                              output_port=ofproto.OFPP_CONTROLLER,
                              copy_table=self.next_table,
                              max_len=ofproto.OFPCML_NO_BUFFER)
Пример #7
0
 def _install_default_flows(self, datapath):
     """
     For every UE IP block, this adds a pair of  0-priority flow-miss rules
     for incoming and outgoing traffic which trigger PACKET IN.
     """
     ofproto = datapath.ofproto
     imsi_match = (0x1, 0x1)  # match on the last bit set
     inbound_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                                direction=Direction.IN,
                                imsi=imsi_match)
     outbound_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                                 direction=Direction.OUT,
                                 imsi=imsi_match)
     flows.add_output_flow(datapath,
                           self.tbl_num,
                           inbound_match, [],
                           priority=flows.MINIMUM_PRIORITY,
                           cookie=self.DEFAULT_FLOW_COOKIE,
                           output_port=ofproto.OFPP_CONTROLLER,
                           max_len=ofproto.OFPCML_NO_BUFFER)
     flows.add_output_flow(datapath,
                           self.tbl_num,
                           outbound_match, [],
                           priority=flows.MINIMUM_PRIORITY,
                           cookie=self.DEFAULT_FLOW_COOKIE,
                           output_port=ofproto.OFPP_CONTROLLER,
                           max_len=ofproto.OFPCML_NO_BUFFER)
Пример #8
0
    def install_paging_flow(self,
                            ue_ip_addr: IPAddress,
                            local_f_teid: int,
                            ng_flag: bool = True):

        ofproto = self._datapath.ofproto
        parser = self._datapath.ofproto_parser
        ip_match_out = get_ue_ip_match_args(ue_ip_addr, Direction.IN)
        match = MagmaMatch(eth_type=get_eth_type(ue_ip_addr), **ip_match_out)

        # Pass Controller ID value as a ACTION
        classifier_controller_id = 0
        if ng_flag:
            classifier_controller_id = self.config.classifier_controller_id

        actions = [
            parser.NXActionController(0, classifier_controller_id,
                                      ofproto.OFPR_ACTION_SET)
        ]

        flows.add_output_flow(self._datapath,
                              self.tbl_num,
                              match=match,
                              actions=actions,
                              priority=Utils.PAGING_RULE_PRIORITY,
                              cookie=local_f_teid,
                              output_port=ofproto.OFPP_CONTROLLER,
                              max_len=ofproto.OFPCML_NO_BUFFER)
        return True
Пример #9
0
    def _add_dhcp_passthrough_flows(self):
        ofproto, parser = self._datapath.ofproto, self._datapath.ofproto_parser

        # Set so packet skips enforcement controller
        action = load_passthrough(parser)
        uplink_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                                  ip_proto=IPPROTO_UDP,
                                  udp_src=68,
                                  udp_dst=67)
        self._add_resubmit_flow(None, uplink_match, action,
                                flows.PASSTHROUGH_PRIORITY,
                                tbl_num=self._passthrough_set_tbl)

        downlink_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP,
                                    ip_proto=IPPROTO_UDP,
                                    udp_src=67,
                                    udp_dst=68)
        # Set so triggers packetin and we can learn the ip to do arp response
        self._add_resubmit_flow(None, downlink_match, action,
              flows.PASSTHROUGH_PRIORITY, next_table=self._dhcp_learn_scratch,
              tbl_num=self._passthrough_set_tbl)

        # Install default flow for dhcp learn scratch
        flows.add_output_flow(self._datapath, self._dhcp_learn_scratch,
                              match=MagmaMatch(), actions=[],
                              priority=flows.PASSTHROUGH_PRIORITY,
                              output_port=ofproto.OFPP_CONTROLLER,
                              copy_table=self.next_table,
                              max_len=ofproto.OFPCML_NO_BUFFER)
Пример #10
0
    def _install_default_egress_flows(self, dp, mac_addr: str = "", vlan: str = ""):
        """
        Egress table is the last table that a packet touches in the pipeline.
        Output downlink traffic to gtp port, uplink trafic to LOCAL
        Args:
            mac_addr: In Non NAT mode, this is upstream internet GW mac address
            vlan: in multi APN this is vlan_id of the upstream network.

        Raises:
            MagmaOFError if any of the default flows fail to install.
        """
        if self.config.setup_type == 'LTE':
            _install_vlan_egress_flows(dp,
                                       self._egress_tbl_num,
                                       "0.0.0.0/0")
        else:
            # Use regular match for Non LTE setup.
            downlink_match = MagmaMatch(direction=Direction.IN)
            flows.add_output_flow(dp, self._egress_tbl_num, downlink_match, [],
                                  output_port=self.config.gtp_port)

        if vlan != "":
            vid = 0x1000 | int(vlan)
            uplink_match = MagmaMatch(direction=Direction.OUT,
                                      vlan_vid=(vid, vid))
        else:
            uplink_match = MagmaMatch(direction=Direction.OUT)

        actions = []
        # avoid resetting mac address on switch connect event.
        if mac_addr == "":
            mac_addr = self._current_upstream_mac_map.get(vlan, "")
        if mac_addr == "" and self.config.enable_nat is False and \
            self.config.setup_type == 'LTE':
            mac_addr = self.config.uplink_gw_mac

        if mac_addr != "":
            parser = dp.ofproto_parser
            actions.append(parser.NXActionRegLoad2(dst='eth_dst',
                                                   value=mac_addr))
            if self._current_upstream_mac_map.get(vlan, "") != mac_addr:
                self.logger.info("Using GW: mac: %s match %s actions: %s",
                                 mac_addr,
                                 str(uplink_match.ryu_match),
                                 str(actions))

                self._current_upstream_mac_map[vlan] = mac_addr

        if vlan != "":
            priority = flows.UE_FLOW_PRIORITY
        elif mac_addr != "":
            priority = flows.DEFAULT_PRIORITY
        else:
            priority = flows.MINIMUM_PRIORITY

        flows.add_output_flow(dp, self._egress_tbl_num, uplink_match,
                              priority=priority,
                              actions=actions,
                              output_port=self._uplink_port)
Пример #11
0
    def _set_incoming_arp_flows(self, datapath):
        """
        Install a flow rule to respond in incoming ARP requests for UE IPs.
        Drop all other incoming ARPs.
        """
        parser = datapath.ofproto_parser
        ofproto = datapath.ofproto

        # Set up ARP responder using flow rules. Add a rule with the following
        # actions for each UE IP block:
        # 1. eth_dst becomes eth_src (back to sender)
        # 2. eth_src becomes the bridge MAC
        # 3. Set ARP op field to reply
        # 4. Target MAC becomes source MAC
        # 5. Source MAC becomes bridge MAC
        # 6. Swap target and source IPs using register 0 as a buffer
        # 7. Send back to the port the packet came on
        for ip_block in self.config.ue_ip_blocks:
            match = MagmaMatch(eth_type=ether_types.ETH_TYPE_ARP,
                               direction=Direction.IN,
                               arp_tpa=ip_block)
            actions = [
                parser.NXActionRegMove(src_field='eth_src',
                                       dst_field='eth_dst',
                                       n_bits=48),
                parser.OFPActionSetField(eth_src=self.config.virtual_mac),
                parser.OFPActionSetField(arp_op=arp.ARP_REPLY),
                parser.NXActionRegMove(src_field='arp_sha',
                                       dst_field='arp_tha',
                                       n_bits=48),
                parser.OFPActionSetField(arp_sha=self.config.virtual_mac),
                parser.NXActionRegMove(src_field='arp_tpa',
                                       dst_field='reg0',
                                       n_bits=32),
                parser.NXActionRegMove(src_field='arp_spa',
                                       dst_field='arp_tpa',
                                       n_bits=32),
                parser.NXActionRegMove(src_field='reg0',
                                       dst_field='arp_spa',
                                       n_bits=32),
            ]
            flows.add_output_flow(datapath,
                                  self.table_num,
                                  match,
                                  actions,
                                  priority=flows.DEFAULT_PRIORITY,
                                  output_port=ofproto.OFPP_IN_PORT)

        # Drop all other ARPs
        match = MagmaMatch(eth_type=ether_types.ETH_TYPE_ARP,
                           direction=Direction.IN)
        flows.add_drop_flow(datapath,
                            self.table_num,
                            match, [],
                            priority=flows.MINIMUM_PRIORITY)
Пример #12
0
    def set_incoming_arp_flows(self, datapath, ip_block, src_mac,
                               flow_priority: int = flows.UE_FLOW_PRIORITY):
        """
        Install flow rules for incoming ARPs(to UE):
            - For ARP request: respond to incoming ARP requests.
            - For ARP response: pass to next table.
        """
        parser = datapath.ofproto_parser
        ofproto = datapath.ofproto

        arp_resp_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_ARP,
                                    direction=Direction.IN,
                                    arp_op=arp.ARP_REPLY, arp_tpa=ip_block)
        # Set so packet skips enforcement and send to egress
        actions = [load_passthrough(parser)]

        flows.add_resubmit_next_service_flow(datapath, self.table_num,
                                             arp_resp_match, actions=actions,
                                             priority=flow_priority,
                                             resubmit_table=self.next_table)

        # Set up ARP responder using flow rules. Add a rule with the following
        # 1. eth_dst becomes eth_src (back to sender)
        # 2. eth_src becomes the bridge MAC
        # 3. Set ARP op field to reply
        # 4. Target MAC becomes source MAC
        # 5. Source MAC becomes bridge MAC
        # 6. Swap target and source IPs using register 0 as a buffer
        # 7. Send back to the port the packet came on
        arp_req_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_ARP,
                                   direction=Direction.IN,
                                   arp_op=arp.ARP_REQUEST, arp_tpa=ip_block)
        actions = [
            parser.NXActionRegMove(src_field='eth_src',
                                   dst_field='eth_dst',
                                   n_bits=48),
            parser.OFPActionSetField(eth_src=src_mac),
            parser.OFPActionSetField(arp_op=arp.ARP_REPLY),
            parser.NXActionRegMove(src_field='arp_sha',
                                   dst_field='arp_tha',
                                   n_bits=48),
            parser.OFPActionSetField(arp_sha=src_mac),
            parser.NXActionRegMove(src_field='arp_tpa',
                                   dst_field='reg0',
                                   n_bits=32),
            parser.NXActionRegMove(src_field='arp_spa',
                                   dst_field='arp_tpa',
                                   n_bits=32),
            parser.NXActionRegMove(src_field='reg0',
                                   dst_field='arp_spa',
                                   n_bits=32),
        ]
        flows.add_output_flow(datapath, self.table_num, arp_req_match, actions,
                              priority=flow_priority,
                              output_port=ofproto.OFPP_IN_PORT)
Пример #13
0
    def _install_default_egress_flows(self, dp):
        """
        Egress table is the last table that a packet touches in the pipeline.
        Output downlink traffic to gtp port, uplink trafic to LOCAL

        Raises:
            MagmaOFError if any of the default flows fail to install.
        """
        downlink_match = MagmaMatch(direction=Direction.IN)
        flows.add_output_flow(dp, self._egress_tbl_num, downlink_match, [],
                              output_port=self.config.gtp_port)

        uplink_match = MagmaMatch(direction=Direction.OUT)
        flows.add_output_flow(dp, self._egress_tbl_num, uplink_match, [],
                              output_port=self._uplink_port)
Пример #14
0
    def _add_output_flow(
        self, local_f_teid: int, ue_ip_addr: IPAddress,
        output_port: int, max_len: int, actions,
    ):

        ip_match_out = get_ue_ip_match_args(ue_ip_addr, Direction.IN)
        match = MagmaMatch(eth_type=get_eth_type(ue_ip_addr), **ip_match_out)

        flows.add_output_flow(
            self._datapath, self.tbl_num,
            match=match, actions=actions,
            priority=Utils.PAGING_RULE_PRIORITY,
            cookie=local_f_teid,
            output_port=output_port,
            max_len=max_len,
        )
Пример #15
0
    def _add_subscriber_flow(self, imsi: str, ue_mac: str, has_quota: bool):
        """
        Redirect the UE flow to the dedicated flask server.
        On return traffic rewrite the IP/port so the redirection is seamless.
        """
        parser = self._datapath.ofproto_parser
        if has_quota:
            tcp_dst = self.config.has_quota_port
        else:
            tcp_dst = self.config.no_quota_port
        match = MagmaMatch(imsi=encode_imsi(imsi),
                           eth_type=ether_types.ETH_TYPE_IP,
                           ip_proto=IPPROTO_TCP,
                           direction=Direction.OUT,
                           vlan_vid=(0x1000, 0x1000),
                           ipv4_dst=self.config.quota_check_ip)
        actions = [
            parser.OFPActionSetField(ipv4_dst=self.config.bridge_ip),
            parser.OFPActionSetField(eth_dst=self.config.cwf_bridge_mac),
            parser.OFPActionSetField(tcp_dst=tcp_dst),
            parser.OFPActionPopVlan()
        ]
        flows.add_output_flow(self._datapath,
                              self.tbl_num,
                              match,
                              actions,
                              priority=flows.UE_FLOW_PRIORITY,
                              output_port=OFPP_LOCAL)

        # For traffic from the check quota server rewrite src ip and port
        match = MagmaMatch(imsi=encode_imsi(imsi),
                           eth_type=ether_types.ETH_TYPE_IP,
                           ip_proto=IPPROTO_TCP,
                           direction=Direction.IN,
                           ipv4_src=self.config.bridge_ip)
        actions = [
            parser.OFPActionSetField(ipv4_src=self.config.quota_check_ip),
            parser.OFPActionSetField(eth_dst=ue_mac),
            parser.OFPActionSetField(tcp_src=80)
        ]
        flows.add_resubmit_next_service_flow(
            self._datapath,
            self.tbl_num,
            match,
            actions,
            priority=flows.DEFAULT_PRIORITY,
            resubmit_table=self.next_main_table)
Пример #16
0
def _install_vlan_egress_flows(dp,
                               table_no,
                               ip,
                               out_port=None,
                               priority=0,
                               direction=Direction.IN):
    """
    Install egress flows
    Args:
        dp datapath
        table_no table to install flow
        out_port specify egress port, if None reg value is used
        priority flow priority
        direction packet direction.
    """

    if out_port:
        output_reg = None
    else:
        output_reg = TUN_PORT_REG

    # Pass non vlan packet as it is.
    match = MagmaMatch(direction=direction,
                       eth_type=ether_types.ETH_TYPE_IP,
                       vlan_vid=(0x0000, 0x1000),
                       ipv4_dst=ip)
    flows.add_output_flow(dp,
                          table_no,
                          match, [],
                          priority=priority,
                          output_reg=output_reg,
                          output_port=out_port)

    # remove vlan header for out_port.
    match = MagmaMatch(direction=direction,
                       eth_type=ether_types.ETH_TYPE_IP,
                       vlan_vid=(0x1000, 0x1000),
                       ipv4_dst=ip)
    actions_vlan_pop = [dp.ofproto_parser.OFPActionPopVlan()]
    flows.add_output_flow(dp,
                          table_no,
                          match,
                          actions_vlan_pop,
                          priority=priority,
                          output_reg=output_reg,
                          output_port=out_port)
Пример #17
0
    def _install_default_egress_flows(self, dp):
        """
        Egress table is the last table that a packet touches in the pipeline.
        Output downlink traffic to gtp port, uplink trafic to LOCAL

        Raises:
            MagmaOFError if any of the default flows fail to install.
        """
        downlink_match = MagmaMatch(direction=Direction.IN)
        flows.add_output_flow(dp, self._service_manager.get_table_num(EGRESS),
                              downlink_match, [],
                              output_port=self.config.gtp_port)

        uplink_match = MagmaMatch(direction=Direction.OUT)
        flows.add_output_flow(dp, self._service_manager.get_table_num(EGRESS),
                              uplink_match, [],
                              output_port=dp.ofproto.OFPP_LOCAL)
Пример #18
0
    def _install_default_egress_flows(self, dp, mac_addr: str = None):
        """
        Egress table is the last table that a packet touches in the pipeline.
        Output downlink traffic to gtp port, uplink trafic to LOCAL

        Raises:
            MagmaOFError if any of the default flows fail to install.
        """
        downlink_match = MagmaMatch(direction=Direction.IN)
        flows.add_output_flow(dp, self._egress_tbl_num, downlink_match, [],
                              output_port=self.config.gtp_port)

        uplink_match = MagmaMatch(direction=Direction.OUT)
        actions = []
        if mac_addr is not None:
            parser = dp.ofproto_parser
            actions.append(parser.OFPActionSetField(eth_dst=mac_addr))
            self.logger.info("Using GW: %s actions: %s", mac_addr, str(actions))

        flows.add_output_flow(dp, self._egress_tbl_num, uplink_match, actions,
                              output_port=self._uplink_port)
Пример #19
0
    def _install_default_ipv6_flows(self, datapath):
        """
        Install flows that match on RS/NS and trigger packet in message, that
        will respond with RA/NA.
        """
        ofproto = datapath.ofproto

        match_rs = MagmaMatch(
            eth_type=ether_types.ETH_TYPE_IPV6,
            ipv6_src=self.LINK_LOCAL_PREFIX,
            ip_proto=IPPROTO_ICMPV6,
            icmpv6_type=icmpv6.ND_ROUTER_SOLICIT,
            direction=Direction.OUT,
        )

        flows.add_output_flow(
            datapath,
            self.tbl_num,
            match=match_rs,
            actions=[],
            priority=flows.DEFAULT_PRIORITY,
            output_port=ofproto.OFPP_CONTROLLER,
            max_len=ofproto.OFPCML_NO_BUFFER,
        )

        match_ns_ue = MagmaMatch(
            eth_type=ether_types.ETH_TYPE_IPV6,
            ipv6_src=self.LINK_LOCAL_PREFIX,
            ip_proto=IPPROTO_ICMPV6,
            icmpv6_type=icmpv6.ND_NEIGHBOR_SOLICIT,
            direction=Direction.OUT,
        )

        flows.add_output_flow(
            datapath,
            self.tbl_num,
            match=match_ns_ue,
            actions=[],
            priority=flows.DEFAULT_PRIORITY,
            output_port=ofproto.OFPP_CONTROLLER,
            max_len=ofproto.OFPCML_NO_BUFFER,
        )

        match_ns_sgi = MagmaMatch(
            eth_type=ether_types.ETH_TYPE_IPV6,
            ipv6_dst=self.SOLICITED_NODE_MULTICAST,
            ip_proto=IPPROTO_ICMPV6,
            icmpv6_type=icmpv6.ND_NEIGHBOR_SOLICIT,
            direction=Direction.IN,
        )

        flows.add_output_flow(
            datapath,
            self.tbl_num,
            match=match_ns_sgi,
            actions=[],
            priority=flows.DEFAULT_PRIORITY,
            output_port=ofproto.OFPP_CONTROLLER,
            max_len=ofproto.OFPCML_NO_BUFFER,
        )
Пример #20
0
    def set_incoming_arp_flows_req(self, datapath, ip_block, src_mac,
                                   flow_priority: int = flows.UE_FLOW_PRIORITY):
        parser = datapath.ofproto_parser
        ofproto = datapath.ofproto

        # Set up ARP responder using flow rules. Add a rule with the following
        # 1. eth_dst becomes eth_src (back to sender)
        # 2. eth_src becomes the bridge MAC
        # 3. Set ARP op field to reply
        # 4. Target MAC becomes source MAC
        # 5. Source MAC becomes bridge MAC
        # 6. Swap target and source IPs using register 0 as a buffer
        # 7. Send back to the port the packet came on
        arp_req_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_ARP,
                                   direction=Direction.IN,
                                   arp_op=arp.ARP_REQUEST, arp_tpa=ip_block)
        actions = [
            parser.NXActionRegMove(src_field='eth_src',
                                   dst_field='eth_dst',
                                   n_bits=48),
            parser.OFPActionSetField(eth_src=src_mac),
            parser.OFPActionSetField(arp_op=arp.ARP_REPLY),
            parser.NXActionRegMove(src_field='arp_sha',
                                   dst_field='arp_tha',
                                   n_bits=48),
            parser.OFPActionSetField(arp_sha=src_mac),
            parser.NXActionRegMove(src_field='arp_tpa',
                                   dst_field='reg0',
                                   n_bits=32),
            parser.NXActionRegMove(src_field='arp_spa',
                                   dst_field='arp_tpa',
                                   n_bits=32),
            parser.NXActionRegMove(src_field='reg0',
                                   dst_field='arp_spa',
                                   n_bits=32),
        ]
        flows.add_output_flow(datapath, self.table_num, arp_req_match, actions,
                              priority=flow_priority,
                              output_port=ofproto.OFPP_IN_PORT)
Пример #21
0
    def _install_default_ipv6_flows(self, datapath):
        """
        Install flows that match on RS/NS and trigger packet in message, that
        will respond with RA/NA.
        """
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        match_rs = MagmaMatch(
            eth_type=ether_types.ETH_TYPE_IPV6,
            ipv6_src=self.LINK_LOCAL_PREFIX,
            ip_proto=IPPROTO_ICMPV6,
            icmpv6_type=icmpv6.ND_ROUTER_SOLICIT,
            direction=Direction.OUT,
        )

        if self.config.ng_service_enabled:
            action = [
                parser.NXActionController(
                    0,
                    self.config.classifier_controller_id,
                    ofproto.OFPR_ACTION_SET,
                ),
            ]
        else:
            action = []
        flows.add_output_flow(
            datapath,
            self.tbl_num,
            match=match_rs,
            actions=action,
            priority=flows.DEFAULT_PRIORITY,
            output_port=ofproto.OFPP_CONTROLLER,
            max_len=ofproto.OFPCML_NO_BUFFER,
        )

        match_ns_ue = MagmaMatch(
            eth_type=ether_types.ETH_TYPE_IPV6,
            ipv6_src=self.LINK_LOCAL_PREFIX,
            ip_proto=IPPROTO_ICMPV6,
            icmpv6_type=icmpv6.ND_NEIGHBOR_SOLICIT,
            direction=Direction.OUT,
        )

        flows.add_output_flow(
            datapath,
            self.tbl_num,
            match=match_ns_ue,
            actions=action,
            priority=flows.DEFAULT_PRIORITY,
            output_port=ofproto.OFPP_CONTROLLER,
            max_len=ofproto.OFPCML_NO_BUFFER,
        )

        match_ns_sgi = MagmaMatch(
            eth_type=ether_types.ETH_TYPE_IPV6,
            ipv6_dst=self.SOLICITED_NODE_MULTICAST,
            ip_proto=IPPROTO_ICMPV6,
            icmpv6_type=icmpv6.ND_NEIGHBOR_SOLICIT,
            direction=Direction.IN,
        )

        flows.add_output_flow(
            datapath,
            self.tbl_num,
            match=match_ns_sgi,
            actions=action,
            priority=flows.DEFAULT_PRIORITY,
            output_port=ofproto.OFPP_CONTROLLER,
            max_len=ofproto.OFPCML_NO_BUFFER,
        )
Пример #22
0
    def _add_subscriber_flow(self, imsi: str, ue_mac: str, has_quota: bool):
        """
        Redirect the UE flow to the dedicated flask server.
        On return traffic rewrite the IP/port so the redirection is seamless.

        Match incoming user traffic:
            1. Rewrite ip src to be in same subnet as check quota server
            2. Rewrite ip dst to check quota server
            3. Rewrite eth dst to check quota server
            4. Rewrite tcp dst port to either quota/non quota

            5. LEARN action
                This will rewrite the ip src and dst and tcp port for traffic
                coming back to the UE

            6. ARP controller arp clamp
                Sets the ARP clamping(for ARPs from the check quota server)
                for the fake IP we used to reach the check quota server

        """
        parser = self._datapath.ofproto_parser
        fake_ip = self._next_fake_ip()

        if has_quota:
            tcp_dst = self.config.has_quota_port
        else:
            tcp_dst = self.config.no_quota_port
        match = MagmaMatch(imsi=encode_imsi(imsi),
                           eth_type=ether_types.ETH_TYPE_IP,
                           ip_proto=IPPROTO_TCP,
                           direction=Direction.OUT,
                           vlan_vid=(0x1000, 0x1000),
                           ipv4_dst=self.config.quota_check_ip)
        actions = [
            parser.NXActionLearn(
                table_id=self.ip_rewrite_scratch,
                priority=flows.UE_FLOW_PRIORITY,
                specs=[
                    parser.NXFlowSpecMatch(src=ether_types.ETH_TYPE_IP,
                                           dst=('eth_type_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecMatch(src=IPPROTO_TCP,
                                           dst=('ip_proto_nxm', 0),
                                           n_bits=8),
                    parser.NXFlowSpecMatch(src=Direction.IN,
                                           dst=(DIRECTION_REG, 0),
                                           n_bits=32),
                    parser.NXFlowSpecMatch(src=int(
                        ipaddress.IPv4Address(self.config.bridge_ip)),
                                           dst=('ipv4_src_nxm', 0),
                                           n_bits=32),
                    parser.NXFlowSpecMatch(src=int(fake_ip),
                                           dst=('ipv4_dst_nxm', 0),
                                           n_bits=32),
                    parser.NXFlowSpecMatch(src=('tcp_src_nxm', 0),
                                           dst=('tcp_dst_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecMatch(src=tcp_dst,
                                           dst=('tcp_src_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecMatch(src=encode_imsi(imsi),
                                           dst=(IMSI_REG, 0),
                                           n_bits=64),
                    parser.NXFlowSpecLoad(src=('ipv4_src_nxm', 0),
                                          dst=('ipv4_dst_nxm', 0),
                                          n_bits=32),
                    parser.NXFlowSpecLoad(src=int(
                        ipaddress.IPv4Address(self.config.quota_check_ip)),
                                          dst=('ipv4_src_nxm', 0),
                                          n_bits=32),
                    parser.NXFlowSpecLoad(src=80,
                                          dst=('tcp_src_nxm', 0),
                                          n_bits=16),
                ]),
            parser.NXActionLearn(
                table_id=self.mac_rewrite_scratch,
                priority=flows.UE_FLOW_PRIORITY,
                specs=[
                    parser.NXFlowSpecMatch(src=ether_types.ETH_TYPE_IP,
                                           dst=('eth_type_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecMatch(src=IPPROTO_TCP,
                                           dst=('ip_proto_nxm', 0),
                                           n_bits=8),
                    parser.NXFlowSpecMatch(src=int(
                        ipaddress.IPv4Address(self.config.bridge_ip)),
                                           dst=('ipv4_src_nxm', 0),
                                           n_bits=32),
                    parser.NXFlowSpecMatch(src=int(fake_ip),
                                           dst=('ipv4_dst_nxm', 0),
                                           n_bits=32),
                    parser.NXFlowSpecMatch(src=('tcp_src_nxm', 0),
                                           dst=('tcp_dst_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecMatch(src=tcp_dst,
                                           dst=('tcp_src_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecLoad(src=('eth_src_nxm', 0),
                                          dst=('eth_dst_nxm', 0),
                                          n_bits=48),
                    parser.NXFlowSpecLoad(src=encode_imsi(imsi),
                                          dst=(IMSI_REG, 0),
                                          n_bits=64),
                ]),
            parser.OFPActionSetField(ipv4_src=str(fake_ip)),
            parser.OFPActionSetField(ipv4_dst=self.config.bridge_ip),
            parser.OFPActionSetField(eth_dst=self.config.cwf_bridge_mac),
            parser.OFPActionSetField(tcp_dst=tcp_dst),
            parser.OFPActionPopVlan()
        ]
        flows.add_output_flow(self._datapath,
                              self.tbl_num,
                              match,
                              actions,
                              priority=flows.UE_FLOW_PRIORITY,
                              output_port=OFPP_LOCAL)

        ue_tbl = self._service_manager.get_table_num(
            UEMacAddressController.APP_NAME)
        ue_next_tbl = self._service_manager.get_table_num(INGRESS)

        # Allows traffic back from the check quota server
        match = MagmaMatch(in_port=OFPP_LOCAL)
        actions = [
            parser.NXActionResubmitTable(table_id=self.mac_rewrite_scratch)
        ]
        flows.add_resubmit_next_service_flow(self._datapath,
                                             ue_tbl,
                                             match,
                                             actions=actions,
                                             priority=flows.DEFAULT_PRIORITY,
                                             resubmit_table=ue_next_tbl)

        # For traffic from the check quota server rewrite src ip and port
        match = MagmaMatch(imsi=encode_imsi(imsi),
                           eth_type=ether_types.ETH_TYPE_IP,
                           ip_proto=IPPROTO_TCP,
                           direction=Direction.IN,
                           ipv4_src=self.config.bridge_ip)
        actions = [
            parser.NXActionResubmitTable(table_id=self.ip_rewrite_scratch)
        ]
        flows.add_resubmit_next_service_flow(self._datapath,
                                             self.tbl_num,
                                             match,
                                             actions,
                                             priority=flows.DEFAULT_PRIORITY,
                                             resubmit_table=self.egress_table)

        self.logger.debug(
            "Setting up fake arp for for subscriber %s(%s),"
            "with fake ip %s", imsi, ue_mac, fake_ip)

        if self.arp_contoller or self.arpd_controller_fut.done():
            if not self.arp_contoller:
                self.arp_contoller = self.arpd_controller_fut.result()
            self.arp_contoller.set_incoming_arp_flows(self._datapath, fake_ip,
                                                      ue_mac)
Пример #23
0
    def setup_cwf_redirect(self, datapath, loop, redirect_request):
        """
        Add flows to forward traffic to the redirection server for cwf networks

        1) Intercept tcp traffic to the web to the redirection server, which
            completes the tcp handshake. Also overwrite UE src ip to match the
            subnet of the redirection server.
            This is done by assigning an internal IP per each subscriber.
            Add an OVS flow with a learn action (flow catches inbound tcp http
            packets, while learn action creates another flow that rewrites
            packet back to send to ue)

        2) Add flows to allow UDP traffic so DNS queries can go through.
            Add flows with a higher priority that allow traffic to and
            from the address provided in redirect rule.

        TODO we might want to track stats for these rules and report to sessiond
        """
        if not self._cwf_args_set:
            raise RedirectException("Can't install cwf redirection, missing"
                                    "cwf specific args, call set_cwf_args()")
        imsi = redirect_request.imsi
        rule = redirect_request.rule
        rule_num = redirect_request.rule_num
        rule_version = redirect_request.rule_version
        priority = redirect_request.priority
        if rule.redirect.address_type == rule.redirect.URL:
            self._install_url_bypass_flows(datapath, loop, imsi, rule,
                                           rule_num, rule_version, priority)
        elif rule.redirect.address_type == rule.redirect.IPv4:
            self._install_ipv4_bypass_flows(datapath, imsi, rule, rule_num,
                                            rule_version, priority,
                                            [rule.redirect.server_address])

        parser = datapath.ofproto_parser
        # TODO use subscriber ip_addr to generate internal IP and release
        # internal IP when subscriber disconnects or redirection flow is removed
        internal_ip = self._internal_ip_allocator.next_ip()

        self._save_redirect_entry(internal_ip, rule.redirect)
        #TODO check if we actually need this, dns might already be allowed
        self._install_dns_flows(datapath, imsi, rule, rule_num, rule_version,
                                priority)

        match_tcp_80 = MagmaMatch(imsi=encode_imsi(imsi),
                                  eth_type=ether_types.ETH_TYPE_IP,
                                  ip_proto=IPPROTO_TCP,
                                  direction=Direction.OUT,
                                  tcp_dst=80)
        match_tcp_8008 = MagmaMatch(imsi=encode_imsi(imsi),
                                    eth_type=ether_types.ETH_TYPE_IP,
                                    ip_proto=IPPROTO_TCP,
                                    direction=Direction.OUT,
                                    tcp_dst=8080)
        match_tcp_8080 = MagmaMatch(imsi=encode_imsi(imsi),
                                    eth_type=ether_types.ETH_TYPE_IP,
                                    ip_proto=IPPROTO_TCP,
                                    direction=Direction.OUT,
                                    tcp_dst=8008)
        actions = [
            parser.NXActionLearn(
                table_id=self._mac_rewrite_scratch,
                priority=flows.UE_FLOW_PRIORITY,
                cookie=rule_num,
                specs=[
                    parser.NXFlowSpecMatch(src=ether_types.ETH_TYPE_IP,
                                           dst=('eth_type_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecMatch(src=IPPROTO_TCP,
                                           dst=('ip_proto_nxm', 0),
                                           n_bits=8),
                    parser.NXFlowSpecMatch(src=int(
                        ipaddress.IPv4Address(self._bridge_ip)),
                                           dst=('ipv4_src_nxm', 0),
                                           n_bits=32),
                    parser.NXFlowSpecMatch(src=int(internal_ip),
                                           dst=('ipv4_dst_nxm', 0),
                                           n_bits=32),
                    parser.NXFlowSpecMatch(src=('tcp_src_nxm', 0),
                                           dst=('tcp_dst_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecMatch(src=self._redirect_port,
                                           dst=('tcp_src_nxm', 0),
                                           n_bits=16),
                    parser.NXFlowSpecLoad(src=('eth_src_nxm', 0),
                                          dst=('eth_dst_nxm', 0),
                                          n_bits=48),
                    parser.NXFlowSpecLoad(src=encode_imsi(imsi),
                                          dst=(IMSI_REG, 0),
                                          n_bits=64),
                    parser.NXFlowSpecLoad(src=('ipv4_src_nxm', 0),
                                          dst=('ipv4_dst_nxm', 0),
                                          n_bits=32),
                    parser.NXFlowSpecLoad(src=('ipv4_dst_nxm', 0),
                                          dst=('ipv4_src_nxm', 0),
                                          n_bits=32),
                    parser.NXFlowSpecLoad(src=('tcp_dst_nxm', 0),
                                          dst=('tcp_src_nxm', 0),
                                          n_bits=16),
                ]),
            parser.OFPActionSetField(ipv4_src=str(internal_ip)),
            parser.OFPActionSetField(ipv4_dst=self._bridge_ip),
            parser.OFPActionSetField(eth_dst=self._bridge_mac),
            parser.OFPActionSetField(tcp_dst=self._redirect_port),
        ]
        flows.add_output_flow(datapath,
                              self.main_tbl_num,
                              match_tcp_80,
                              actions,
                              priority=flows.UE_FLOW_PRIORITY,
                              cookie=rule_num,
                              output_port=OFPP_LOCAL)
        flows.add_output_flow(datapath,
                              self.main_tbl_num,
                              match_tcp_8008,
                              actions,
                              priority=flows.UE_FLOW_PRIORITY,
                              cookie=rule_num,
                              output_port=OFPP_LOCAL)
        flows.add_output_flow(datapath,
                              self.main_tbl_num,
                              match_tcp_8080,
                              actions,
                              priority=flows.UE_FLOW_PRIORITY,
                              cookie=rule_num,
                              output_port=OFPP_LOCAL)

        # Add flows for vlan traffic too (we need to pop vlan for flask server)
        # In ryu vlan_vid=(0x1000, 0x1000) matches all vlans
        match_tcp_80_vlan = MagmaMatch(imsi=encode_imsi(imsi),
                                       eth_type=ether_types.ETH_TYPE_IP,
                                       ip_proto=IPPROTO_TCP,
                                       direction=Direction.OUT,
                                       tcp_dst=80,
                                       vlan_vid=(0x1000, 0x1000))
        match_tcp_8008_vlan = MagmaMatch(imsi=encode_imsi(imsi),
                                         eth_type=ether_types.ETH_TYPE_IP,
                                         ip_proto=IPPROTO_TCP,
                                         direction=Direction.OUT,
                                         tcp_dst=8080,
                                         vlan_vid=(0x1000, 0x1000))
        match_tcp_8080_vlan = MagmaMatch(imsi=encode_imsi(imsi),
                                         eth_type=ether_types.ETH_TYPE_IP,
                                         ip_proto=IPPROTO_TCP,
                                         direction=Direction.OUT,
                                         tcp_dst=8008,
                                         vlan_vid=(0x1000, 0x1000))
        actions.append(parser.OFPActionPopVlan())
        flows.add_output_flow(datapath,
                              self.main_tbl_num,
                              match_tcp_80_vlan,
                              actions,
                              priority=flows.UE_FLOW_PRIORITY + 1,
                              cookie=rule_num,
                              output_port=OFPP_LOCAL)
        flows.add_output_flow(datapath,
                              self.main_tbl_num,
                              match_tcp_8008_vlan,
                              actions,
                              priority=flows.UE_FLOW_PRIORITY + 1,
                              cookie=rule_num,
                              output_port=OFPP_LOCAL)
        flows.add_output_flow(datapath,
                              self.main_tbl_num,
                              match_tcp_8080_vlan,
                              actions,
                              priority=flows.UE_FLOW_PRIORITY + 1,
                              cookie=rule_num,
                              output_port=OFPP_LOCAL)

        # TODO cleanup, make this a default rule in the ue_mac table
        ue_tbl = 0
        ue_next_tbl = 1
        # Allows traffic back from the flask server
        match = MagmaMatch(in_port=OFPP_LOCAL)
        actions = [
            parser.NXActionResubmitTable(table_id=self._mac_rewrite_scratch)
        ]
        flows.add_resubmit_next_service_flow(datapath,
                                             ue_tbl,
                                             match,
                                             actions=actions,
                                             priority=flows.DEFAULT_PRIORITY,
                                             resubmit_table=ue_next_tbl)
        match = MagmaMatch(imsi=encode_imsi(imsi),
                           eth_type=ether_types.ETH_TYPE_IP,
                           ip_proto=IPPROTO_TCP,
                           direction=Direction.IN,
                           in_port=OFPP_LOCAL)
        flows.add_resubmit_next_service_flow(datapath,
                                             self.main_tbl_num,
                                             match, [],
                                             priority=flows.DEFAULT_PRIORITY,
                                             cookie=rule_num,
                                             resubmit_table=self._egress_table)

        # Mac doesn't matter as we rewrite it anwyays
        mac_addr = '01:02:03:04:05:06'
        if self._arp_contoller or self._arpd_controller_fut.done():
            if not self._arp_contoller:
                self._arp_contoller = self._arpd_controller_fut.result()
            self._arp_contoller.set_incoming_arp_flows(datapath, internal_ip,
                                                       mac_addr)

        # Drop all other traffic that doesn't match
        match = MagmaMatch(imsi=encode_imsi(imsi))
        flows.add_drop_flow(datapath,
                            self.main_tbl_num,
                            match, [],
                            priority=flows.MINIMUM_PRIORITY + 1,
                            cookie=rule_num)