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)
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)
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)
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)
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)
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)
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)
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
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)
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)
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)
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)
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)
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, )
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)
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)
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)
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)
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, )
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)
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, )
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)
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)