def _install_default_flows(self, datapath): """ For each direction set the default flows to just forward to next table. The policies for each subscriber would be added when the IP session is created, by reaching out to the controller/PCRF. 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) if self._dpi_enabled: actions = [parser.OFPActionOutput(self._mon_port_number)] else: actions = [] 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)
def _add_tunnel_ip_flow_dl( self, i_teid: int, ip_flow_dl: IPFlowDL, gtp_port: int, o_teid: int, enodeb_ip_addr: str, sid: int = None, ): priority = Utils.get_of_priority(ip_flow_dl.precedence) parser = self._datapath.ofproto_parser match = self._get_ip_flow_dl_match(ip_flow_dl, self._uplink_port) actions = [ parser.OFPActionSetField(tunnel_id=o_teid), parser.OFPActionSetField(tun_ipv4_dst=enodeb_ip_addr), parser.NXActionRegLoad2(dst=TUN_PORT_REG, value=gtp_port), ] if i_teid: actions.append(parser.NXActionRegLoad2(dst=INGRESS_TUN_ID_REG, value=i_teid)) if sid: actions.append(parser.OFPActionSetField(metadata=sid)) flows.add_resubmit_next_service_flow( self._datapath, self.tbl_num, match, actions=actions, priority=priority, reset_default_register=False, resubmit_table=self.next_table, ) match = self._get_ip_flow_dl_match(ip_flow_dl, self.config.mtr_port) flows.add_resubmit_next_service_flow( self._datapath, self.tbl_num, match, actions=actions, priority=priority, reset_default_register=False, resubmit_table=self.next_table, )
def _install_default_flows(self, datapath: Datapath): """ Set the default flows to just forward to next app. Args: datapath: ryu datapath struct """ # Default flows for non matched traffic inbound_match = MagmaMatch(direction=Direction.IN) outbound_match = MagmaMatch(direction=Direction.OUT) flows.add_resubmit_next_service_flow( datapath, self.tbl_num, inbound_match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_main_table, ) flows.add_resubmit_next_service_flow( datapath, self.tbl_num, outbound_match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_main_table, )
def _install_uplink_s8_tunnel_flows( self, priority: int, i_teid: int, o_teid: int, pgw_ip_addr: str, gtp_portno: int, sid: int, pgw_gtp_port: int, ): parser = self._datapath.ofproto_parser match = MagmaMatch(tunnel_id=i_teid, in_port=gtp_portno) gtp_port = pgw_gtp_port if pgw_gtp_port == 0: gtp_port = self.config.gtp_port actions = [ parser.OFPActionSetField(tunnel_id=o_teid), parser.OFPActionSetField(tun_ipv4_dst=pgw_ip_addr), parser.NXActionRegLoad2(dst=TUN_PORT_REG, value=gtp_port), parser.OFPActionSetField(eth_dst="ff:ff:ff:ff:ff:ff"), ] if sid: actions.append(parser.OFPActionSetField(metadata=sid)) flows.add_resubmit_next_service_flow( self._datapath, self.tbl_num, match, actions=actions, priority=priority, reset_default_register=False, resubmit_table=self.next_table, )
def _install_default_flows(self, datapath): """ For each direction set the default flows to just forward to next app. The enforcement flows for each subscriber would be added when the IP session is created, by reaching out to the controller/PCRF. Args: datapath: ryu datapath struct """ 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) flows.add_resubmit_next_service_flow( datapath, self.tbl_num, inbound_match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_main_table) flows.add_resubmit_next_service_flow( datapath, self.tbl_num, outbound_match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_main_table)
def _install_uplink_tunnel_flows( self, priority: int, i_teid: int, gtp_portno: int, sid: Optional[int], o_teid: int, ng_flag: bool, session_qfi: QCI, ): parser = self._datapath.ofproto_parser if ng_flag and session_qfi: match = MagmaMatch(tunnel_id=i_teid, qfi=session_qfi, in_port=gtp_portno) else: match = MagmaMatch(tunnel_id=i_teid, in_port=gtp_portno) actions = [ parser.OFPActionSetField(eth_src=GTP_PORT_MAC), parser.OFPActionSetField(eth_dst="ff:ff:ff:ff:ff:ff"), parser.NXActionRegLoad2(dst=INGRESS_TUN_ID_REG, value=o_teid), ] if sid: actions.append(parser.OFPActionSetField(metadata=sid)) flows.add_resubmit_next_service_flow( self._datapath, self.tbl_num, match, actions=actions, priority=priority, reset_default_register=False, resubmit_table=self.next_table, )
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. """ next_tbl = self._service_manager.get_next_table_num(PHYSICAL_TO_LOGICAL) # Allow passthrough pkts(skip enforcement and send to egress table) ps_match = MagmaMatch(passthrough=PASSTHROUGH_REG_VAL) flows.add_resubmit_next_service_flow(dp, self._midle_tbl_num, ps_match, actions=[], priority=flows.PASSTHROUGH_PRIORITY, resubmit_table=self._egress_tbl_num) match = MagmaMatch() flows.add_resubmit_next_service_flow(dp, self._midle_tbl_num, match, actions=[], priority=flows.DEFAULT_PRIORITY, resubmit_table=next_tbl) if self._mtr_service_enabled: _install_vlan_egress_flows(dp, self._midle_tbl_num, self.config.mtr_ip, self.config.mtr_port, priority=flows.UE_FLOW_PRIORITY, direction=Direction.OUT)
def set_incoming_arp_flows_res( self, datapath, ip_block, flow_priority: int = flows.UE_FLOW_PRIORITY, ): parser = datapath.ofproto_parser 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, )
def _install_local_eth_dst_flow(self, datapath): """ Add lower-pri flow rule to set `eth_dst` on outgoing packets to the specified MAC address. """ self.logger.info( 'Setting local eth_dst to %s for ip %s', self.config.virtual_iface, self.config.mtr_ip, ) parser = datapath.ofproto_parser match = MagmaMatch( eth_type=ether_types.ETH_TYPE_IP, ipv4_dst=self.config.mtr_ip, direction=Direction.OUT, ) actions = [ parser.NXActionRegLoad2(dst='eth_dst', value=self.config.mtr_mac), ] flows.add_resubmit_next_service_flow( datapath, self.table_num, match, actions, priority=flows.UE_FLOW_PRIORITY, resubmit_table=self.next_table, )
def _install_default_flows(self): """ Install default flows """ # Allows arp packets from uplink(no eth dst set) to go to the arp table self._add_uplink_arp_allow_flow() self._add_dhcp_passthrough_flows() self._add_dns_passthrough_flows() self._add_resubmit_flow( None, MagmaMatch(), priority=flows.MINIMUM_PRIORITY, tbl_num=self._passthrough_set_tbl, ) if self._service_manager.is_app_enabled(IPFIXController.APP_NAME): self._add_resubmit_flow( None, MagmaMatch(in_port=self._dpi_port), priority=flows.PASSTHROUGH_PRIORITY, next_table=self._app_set_tbl_num, ) if self._li_port: match = MagmaMatch(in_port=self._li_port) flows.add_resubmit_next_service_flow( self._datapath, self.tbl_num, match, actions=[], priority=flows.DEFAULT_PRIORITY, resubmit_table=self.next_table, )
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, [], priority=flows.DEFAULT_PRIORITY, resubmit_table=logical_table)
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_not_processed_flows(self, datapath, imsi, rule, rule_num, priority): """ Redirect all traffic to the scratch table to only allow redirected http traffic to go through, the rest will be dropped. reg0 is used as a boolean to know whether the drop rule was processed. """ parser = datapath.ofproto_parser of_note = parser.NXActionNote(list(rule.id.encode())) match = MagmaMatch(imsi=encode_imsi(imsi), direction=Direction.OUT, reg0=self.REDIRECT_NOT_PROCESSED, eth_type=ether_types.ETH_TYPE_IP) action = [of_note] flows.add_resubmit_current_service_flow( datapath, self.main_tbl_num, match, action, priority=priority, cookie=rule_num, hard_timeout=rule.hard_timeout, resubmit_table=self._scratch_tbl_num) match = MagmaMatch(imsi=encode_imsi(imsi), direction=Direction.OUT, reg0=self.REDIRECT_PROCESSED) action = [of_note] flows.add_resubmit_next_service_flow( datapath, self.main_tbl_num, match, action, priority=priority, cookie=rule_num, hard_timeout=rule.hard_timeout, resubmit_table=self.next_table)
def _install_ipv4_bypass_flows(self, datapath, imsi, rule, rule_num, priority, ips): """ Installs flows for traffic that is allowed to pass through for subscriber who has redirection enabled. Allow access to all passed ips. Allow UDP traffic(for DNS queries), traffic to/from redirection address """ parser = datapath.ofproto_parser of_note = parser.NXActionNote(list(rule.id.encode())) actions = [ of_note, ] actions += self._load_rule_actions(parser, rule_num, imsi, rule.id) matches = [] for ip in ips: matches.append(MagmaMatch( eth_type=ether_types.ETH_TYPE_IP, direction=Direction.OUT, ipv4_dst=ip, imsi=encode_imsi(imsi) )) matches.append(MagmaMatch( eth_type=ether_types.ETH_TYPE_IP, direction=Direction.IN, ipv4_src=ip, imsi=encode_imsi(imsi) )) for match in matches: flows.add_resubmit_next_service_flow( datapath, self.main_tbl_num, match, actions, priority=priority + 1, cookie=rule_num, hard_timeout=rule.hard_timeout, resubmit_table=self.next_table)
def _install_downlink_arp_flows( self, priority: int, in_port: int, ue_ip_adr: IPAddress, sid: Optional[int], ): parser = self._datapath.ofproto_parser match = MagmaMatch( eth_type=ether_types.ETH_TYPE_ARP, in_port=in_port, arp_tpa=ipaddress.IPv4Address(ue_ip_adr.address.decode('utf-8')), ) actions = [] if sid: actions = [parser.OFPActionSetField(metadata=sid)] flows.add_resubmit_next_service_flow( self._datapath, self.tbl_num, match, actions=actions, priority=priority, reset_default_register=False, resubmit_table=self.next_table, )
def _add_resubmit_flow(self, sid, match, action=None, priority=flows.DEFAULT_PRIORITY, next_table=None): parser = self._datapath.ofproto_parser if action is None: actions = [] else: actions = [action] if next_table is None: next_table = self.next_table # Add IMSI metadata actions.append( parser.NXActionRegLoad2(dst=IMSI_REG, value=encode_imsi(sid))) flows.add_resubmit_next_service_flow(self._datapath, self.tbl_num, match, actions=actions, priority=priority, resubmit_table=next_table)
def _install_downlink_tunnel_flows(self, priority: int, i_teid: int, o_teid: int, in_port: int, ue_ip_adr: IPAddress, enodeb_ip_addr: str, gtp_portno: int, sid: int, ng_flag: bool): parser = self._datapath.ofproto_parser ip_match_out = get_ue_ip_match_args(ue_ip_adr, Direction.IN) match = MagmaMatch(eth_type=get_eth_type(ue_ip_adr), in_port=in_port, **ip_match_out) actions = [ parser.OFPActionSetField(tunnel_id=o_teid), parser.OFPActionSetField(tun_ipv4_dst=enodeb_ip_addr), parser.NXActionRegLoad2(dst=TUN_PORT_REG, value=gtp_portno) ] if ng_flag: actions.append(parser.OFPActionSetField(tun_flags=TUNNEL_OAM_FLAG)) if i_teid: actions.append( parser.NXActionRegLoad2(dst=INGRESS_TUN_ID_REG, value=i_teid)) if sid: actions.append(parser.OFPActionSetField(metadata=sid)) flows.add_resubmit_next_service_flow(self._datapath, self.tbl_num, match, actions=actions, priority=priority, reset_default_register=False, resubmit_table=self.next_table)
def add_ue_sample_flow( self, imsi: str, msisdn: str, apn_mac_addr: str, apn_name: str, pdp_start_time: int, ) -> None: """ Install a flow to sample packets for IPFIX for specific imsi Args: imsi (string): subscriber to install rule for msisdn (string): msisdn string apn_mac_addr (string): AP mac address string apn_name (string): AP name """ if self._datapath is None: self.logger.error('Datapath not initialized for adding flows') return if not self.ipfix_config.enabled: # TODO logging higher than debug here will provide too much noise # possible fix is making ipfix a dynamic service enabled from orc8r self.logger.debug('IPFIX export dst not setup for adding flows') return parser = self._datapath.ofproto_parser if not apn_mac_addr or '-' not in apn_mac_addr: apn_mac_bytes = [0, 0, 0, 0, 0, 0] else: apn_mac_bytes = [int(a, 16) for a in apn_mac_addr.split('-')] if not msisdn: msisdn = 'no_msisdn' actions = [ parser.NXActionSample2( probability=self.ipfix_config.probability, collector_set_id=self.ipfix_config.collector_set_id, obs_domain_id=self.ipfix_config.obs_domain_id, obs_point_id=self.ipfix_config.obs_point_id, apn_mac_addr=apn_mac_bytes, msisdn=msisdn.encode('ascii'), apn_name=apn_name.encode('ascii'), pdp_start_epoch=pdp_start_time.to_bytes(8, byteorder='little'), sampling_port=self.ipfix_config.sampling_port, ), ] match = MagmaMatch(imsi=encode_imsi(imsi)) if self._dpi_enabled or self._conntrackd_enabled: flows.add_drop_flow( self._datapath, self._ipfix_sample_tbl_num, match, actions, priority=flows.UE_FLOW_PRIORITY, ) else: flows.add_resubmit_next_service_flow( self._datapath, self._ipfix_sample_tbl_num, match, actions, priority=flows.UE_FLOW_PRIORITY, resubmit_table=self.next_main_table, )
def _install_forward_flow(self, datapath): """ Set a simple forward flow for when metering is disabled """ match = MagmaMatch() flows.add_resubmit_next_service_flow(datapath, self.tbl_num, match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_table)
def _install_default_tunnel_flows(self): match = MagmaMatch() flows.add_resubmit_next_service_flow(self._datapath, self.tbl_num, match, priority=flows.MINIMUM_PRIORITY, reset_default_register=False, resubmit_table=self.next_table)
def _install_internal_conntrack_flow(self): match = MagmaMatch(in_port=self.config.internal_conntrack_port) flows.add_resubmit_next_service_flow( self._datapath, self.tbl_num, match, [], priority=flows.MINIMUM_PRIORITY, reset_default_register=False, resubmit_table=self.config.internal_conntrack_fwd_tbl, )
def _add_uplink_arp_allow_flow(self): arp_match = MagmaMatch(eth_type=ether_types.ETH_TYPE_ARP) flows.add_resubmit_next_service_flow(self._datapath, self.tbl_num, arp_match, actions=[], priority=flows.DEFAULT_PRIORITY, resubmit_table=self.next_table)
def _install_default_flows(self, datapath): """ For each direction set the default flows to just forward to next table. The policies for each subscriber would be added when the IP session is created, by reaching out to the controller/PCRF. Args: datapath: ryu datapath struct """ parser = self._datapath.ofproto_parser # Setup flows to classify & mirror to sampling port match = MagmaMatch() actions = [ parser.NXActionResubmitTable(table_id=self._classify_app_tbl_num), ] if self._dpi_enabled: actions.append(parser.OFPActionOutput(self._mon_port_number)) flows.add_resubmit_next_service_flow( datapath, self.tbl_num, match, actions, priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_table, ) # Setup flows for internal IPFIX sampling actions = [ parser.NXActionResubmitTable(table_id=self._classify_app_tbl_num), ] flows.add_resubmit_next_service_flow( self._datapath, self._app_set_tbl_num, MagmaMatch(), actions, priority=flows.MINIMUM_PRIORITY, cookie=self.tbl_num, resubmit_table=self._imsi_set_tbl_num, ) # Setup flows for the application reg classifier tbl actions = [ parser.NXActionRegLoad2( dst=DPI_REG, value=UNCLASSIFIED_PROTO_ID, ), ] flows.add_flow( datapath, self._classify_app_tbl_num, MagmaMatch(), actions, priority=flows.MINIMUM_PRIORITY, )
def _install_default_flows(self, dp): match = MagmaMatch(in_port=self.config.he_proxy_port) flows.add_drop_flow(dp, self.tbl_num, match, priority=flows.MINIMUM_PRIORITY + 1) match = MagmaMatch() flows.add_resubmit_next_service_flow(dp, self.tbl_num, match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_table)
def initialize_on_connect(self, datapath): self._datapath = datapath self.logger.debug('Tracer connected (dp.id): %d', datapath.id) flows.delete_all_flows_from_table(datapath, self.tbl_num) flows.add_resubmit_next_service_flow(self._datapath, self.tbl_num, match=MagmaMatch(), priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_table)
def _install_default_flows(self, datapath): """ Default flow is to forward to next table. """ flows.add_resubmit_next_service_flow(datapath, self.tbl_num, MagmaMatch(), [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_table)
def _install_default_flows(self, datapath): """ If no flows are matched, simply forward the traffic. """ match = MagmaMatch() flows.add_resubmit_next_service_flow(datapath, self.tbl_num, match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_table, cookie=self.DEFAULT_FLOW_COOKIE)
def _install_passthrough_flow(self, datapath): flows.add_resubmit_next_service_flow( datapath, self.tbl_num, MagmaMatch(), actions=[], priority=flows.DEFAULT_PRIORITY, resubmit_table=self._service_manager.get_table_num(INGRESS), )
def _install_mirror_flows(self, imsis): parser = self._datapath.ofproto_parser for imsi in imsis: self.logger.debug("Enabling LI tracking for IMSI %s", imsi) match = MagmaMatch(imsi=encode_imsi(imsi)) actions = [parser.OFPActionOutput(self._li_dst_port_num)] flows.add_resubmit_next_service_flow(self._datapath, self.tbl_num, match, actions, priority=flows.DEFAULT_PRIORITY, resubmit_table=self.next_table)
def _install_default_forward_flow(self, datapath): """ Set a default 0-priority flow to forward to the next table. """ match = MagmaMatch() flows.add_resubmit_next_service_flow(datapath, self.table_num, match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_table)