def _install_uplink_tunnel_flows( self, priority: int, i_teid: int, gtp_portno: int, sid: int, ): parser = self._datapath.ofproto_parser 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=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 _install_default_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 default eth_dst to %s', self.config.virtual_iface) parser = datapath.ofproto_parser match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP, direction=Direction.OUT) actions = [ parser.NXActionRegLoad2(dst='eth_dst', value=self.config.virtual_mac), ] flows.add_resubmit_next_service_flow(datapath, self.table_num, match, actions, priority=flows.DEFAULT_PRIORITY, resubmit_table=self.next_table)
def _get_default_flow_msgs(self, datapath) -> DefaultMsgsMap: """ Gets the default flow msg that forwards to next service Args: datapath: ryu datapath struct Returns: The list of default msgs to add """ match = MagmaMatch() msg = flows.get_add_resubmit_next_service_flow_msg( datapath, self.tbl_num, match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_main_table, ) return {self.tbl_num: [msg]}
def _packet_in_handler(self, ev): msg = ev.msg pkt = packet.Packet(msg.data) pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) if pkt_ipv4 is None: return None dst = pkt_ipv4.dst if dst is None: return None # For sending notification to SMF using GRPC self._send_message_interface(dst, msg.cookie) # Add flow for paging with hard time. match = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_dst=dst) flows.add_drop_flow(self._datapath, self.tbl_num, match, [], priority=Utils.PAGING_RULE_DROP_PRIORITY, hard_timeout=self.config.paging_timeout)
def _get_default_flow_msgs(self, datapath) -> DefaultMsgsMap: """ Gets the default flow msg that forward to stats table(traffic will be dropped because stats table doesn't forward anything) Args: datapath: ryu datapath struct Returns: The list of default msgs to add """ match = MagmaMatch() msg = flows.get_add_resubmit_next_service_flow_msg( datapath, self.tbl_num, match, [], priority=flows.MINIMUM_PRIORITY, resubmit_table=self._enforcement_stats_tbl, cookie=self.DEFAULT_FLOW_COOKIE) return {self.tbl_num: [msg]}
def delete_all_flows_from_table(datapath, table, retries=3, cookie=None): """ Delete all flows from a table. Args: datapath (ryu.controller.controller.Datapath): Datapath to configure table (int): Table to clear retries (int): retry attempts on failure Raises: MagmaOFError: if the flows can't be deleted """ empty_match = MagmaMatch() cookie_match = {} if cookie is not None: cookie_match = { 'cookie': cookie, 'cookie_mask': OVS_COOKIE_MATCH_ALL, } delete_flow(datapath, table, empty_match, retries=retries, **cookie_match)
def _add_dns_passthrough_flows(self, sid, mac_addr): parser = self._datapath.ofproto_parser # Set so packet skips enforcement and send to egress action = load_passthrough(parser) # Install UDP flows for DNS ulink_match_udp = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=IPPROTO_UDP, udp_dst=53, eth_src=mac_addr) self._add_resubmit_flow(sid, ulink_match_udp, action, flows.PASSTHROUGH_PRIORITY) dlink_match_udp = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=IPPROTO_UDP, udp_src=53, eth_dst=mac_addr) self._add_resubmit_flow(sid, dlink_match_udp, action, flows.PASSTHROUGH_PRIORITY) # Install TCP flows for DNS ulink_match_tcp = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=IPPROTO_TCP, tcp_dst=53, eth_src=mac_addr) self._add_resubmit_flow(sid, ulink_match_tcp, action, flows.PASSTHROUGH_PRIORITY) dlink_match_tcp = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=IPPROTO_TCP, tcp_src=53, eth_dst=mac_addr) self._add_resubmit_flow(sid, dlink_match_tcp, action, flows.PASSTHROUGH_PRIORITY) # Install TCP flows for DNS over tls ulink_match_tcp = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=IPPROTO_TCP, tcp_dst=853, eth_src=mac_addr) self._add_resubmit_flow(sid, ulink_match_tcp, action, flows.PASSTHROUGH_PRIORITY) dlink_match_tcp = MagmaMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=IPPROTO_TCP, tcp_src=853, eth_dst=mac_addr) self._add_resubmit_flow(sid, dlink_match_tcp, action, flows.PASSTHROUGH_PRIORITY)
def _install_downlink_arp_flows( self, priority: int, in_port: int, ue_ip_adr: IPAddress, sid: 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 _remove_extra_flows(self, extra_flows): msg_list = [] for flow in extra_flows: if DIRECTION_REG in flow.match: direction = Direction(flow.match.get(DIRECTION_REG, None)) else: direction = None match = MagmaMatch(imsi=flow.match.get(IMSI_REG, None), direction=direction) self.logger.debug('Sending msg for deletion -> %s', flow.match.get('reg1', None)) msg_list.append( flows.get_delete_flow_msg( self._datapath, self.tbl_num, match, cookie=flow.cookie, cookie_mask=flows.OVS_COOKIE_MATCH_ALL)) if msg_list: chan = self._msg_hub.send(msg_list, self._datapath) self._wait_for_responses(chan, len(msg_list))
def install_drop_flows(self): """ Install flows that send test-packets to the controller instead of dropping them if no match is found """ drop_tables = set() for tables in self._service_manager \ .get_all_table_assignments() \ .values(): drop_tables.update(tables.scratch_tables + [tables.main_table]) drop_tables.discard(0) drop_tables = drop_tables.difference(self.drop_flows_installed) for table in drop_tables: self.logger.debug('Installing drop flow for %d', table) flows.add_trace_packet_output_flow(datapath=self._datapath, table=table, match=MagmaMatch(), instructions=[], priority=MINIMUM_PRIORITY) self.drop_flows_installed.add(table)
def _deactivate_flow_for_rule(self, imsi, ip_addr, rule_id): """ Deactivate a specific rule using the flow cookie for a subscriber Args: imsi (string): subscriber id rule_id (string): policy rule id """ try: num = self._rule_mapper.get_rule_num(rule_id) except KeyError: self.logger.error('Could not find rule id %s', rule_id) return cookie, mask = (num, flows.OVS_COOKIE_MATCH_ALL) match = MagmaMatch(imsi=encode_imsi(imsi)) flows.delete_flow(self._datapath, self.tbl_num, match, cookie=cookie, cookie_mask=mask) self._redirect_manager.deactivate_flow_for_rule(self._datapath, imsi, num) self._qos_mgr.remove_subscriber_qos(imsi, num) self._remove_he_flows(ip_addr, rule_id)
def _install_allow_incoming_arp_flow(self, datapath): """ Install a flow rule to allow any ARP packets coming from the UPLINK, this will be hit if arp clamping doesn't recognize the address """ parser = datapath.ofproto_parser match = MagmaMatch( eth_type=ether_types.ETH_TYPE_ARP, direction=Direction.IN, ) # Set so packet skips enforcement and send to egress actions = [load_passthrough(parser)] flows.add_resubmit_next_service_flow( datapath, self.table_num, match, actions=actions, priority=flows.UE_FLOW_PRIORITY - 1, resubmit_table=self.next_table, )
def _set_outgoing_arp_flows(self, datapath, ip_block): """ Install a flow rule to allow any ARP packets coming from the UE """ parser = datapath.ofproto_parser match = MagmaMatch( eth_type=ether_types.ETH_TYPE_ARP, direction=Direction.OUT, arp_spa=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, match, actions=actions, priority=flows.UE_FLOW_PRIORITY, resubmit_table=self.next_table, )
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 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 classify_flow(self, match, app): ryu_match = {'eth_type': ether_types.ETH_TYPE_IP} ryu_match['ipv4_dst'] = self._get_ip_tuple(match.ipv4_dst) ryu_match['ipv4_src'] = self._get_ip_tuple(match.ipv4_src) ryu_match['ip_proto'] = match.ip_proto if match.ip_proto == match.IPProto.IPPROTO_TCP: ryu_match['tcp_dst'] = match.tcp_dst ryu_match['tcp_src'] = match.tcp_src elif match.ip_proto == match.IPProto.IPPROTO_UDP: ryu_match['udp_dst'] = match.udp_dst ryu_match['udp_src'] = match.udp_src parser = self._datapath.ofproto_parser app_id = appMap.get(app, 1) # 1 is returned for unknown apps actions = [parser.NXActionRegLoad2(dst='reg3', value=app_id)] flows.add_resubmit_next_service_flow(self._datapath, self.tbl_num, MagmaMatch(**ryu_match), actions, priority=flows.DEFAULT_PRIORITY, resubmit_table=self.next_table) return True
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)
def _add_resubmit_next_service_flow( self, ue_ip_adr: IPAddress, in_port: int, priority: int, actions, ): 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, ) 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_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_if_not_installed(self, datapath, existing_flows: List[OFPFlowStats]) -> List[OFPFlowStats]: """ Install default flows(if not already installed) to forward the traffic, If no other flows are matched. Returns: The list of flows that remain after inserting default flows """ match = MagmaMatch() msg = flows.get_add_drop_flow_msg( datapath, self.tbl_num, match, priority=flows.MINIMUM_PRIORITY, cookie=self.DEFAULT_FLOW_COOKIE) msg, remaining_flows = self._msg_hub \ .filter_msgs_if_not_in_flow_list(self._datapath, [msg], existing_flows[self.tbl_num]) if msg: chan = self._msg_hub.send(msg, datapath) self._wait_for_responses(chan, 1) return {self.tbl_num: remaining_flows}
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 _install_set_vlan_id_flows(self, dp): parser = self._datapath.ofproto_parser # Load VLAN header (needs to be done before settubg vlan tag) # Can't be done through the learn action because ryu doesn't support it match = MagmaMatch( direction=Direction.IN, vlan_tag=self.LOAD_VLAN, ) actions = [ parser.OFPActionPushVlan(ether.ETH_TYPE_8021Q), parser.NXActionRegLoad2( dst=VLAN_TAG_REG, value=0, ), ] flows.add_resubmit_next_service_flow( dp, self.vlan_id_scratch, match, actions, priority=flows.UE_FLOW_PRIORITY, resubmit_table=self.vlan_id_scratch, )
def _install_default_ingress_flows(self, dp): """ Sets up the ingress table, the first step in the packet processing pipeline. This sets up flow rules to annotate packets with a metadata bit indicating the direction. Incoming packets are defined as packets originating from the LOCAL port, outgoing packets are defined as packets originating from the gtp port. All other packets bypass the pipeline. Note that the ingress rules do *not* install any flows that cause PacketIns (i.e., sends packets to the controller). Raises: MagmaOFError if any of the default flows fail to install. """ parser = dp.ofproto_parser next_table = self._service_manager.get_next_table_num(INGRESS) # set traffic direction bits # set a direction bit for outgoing (pn -> inet) traffic. match = MagmaMatch(in_port=self.config.gtp_port) actions = [load_direction(parser, Direction.OUT)] flows.add_resubmit_next_service_flow(dp, self._ingress_tbl_num, match, actions=actions, priority=flows.DEFAULT_PRIORITY, resubmit_table=next_table) # set a direction bit for incoming (internet -> UE) traffic. match = MagmaMatch(in_port=OFPP_LOCAL) actions = [load_direction(parser, Direction.IN)] flows.add_resubmit_next_service_flow(dp, self._ingress_tbl_num, match, actions=actions, priority=flows.DEFAULT_PRIORITY, resubmit_table=next_table) # set a direction bit for incoming (internet -> UE) traffic. match = MagmaMatch(in_port=self._uplink_port) actions = [load_direction(parser, Direction.IN)] flows.add_resubmit_next_service_flow(dp, self._ingress_tbl_num, match, actions=actions, priority=flows.DEFAULT_PRIORITY, resubmit_table=next_table) # Send RADIUS requests directly to li table if self._li_port: match = MagmaMatch(in_port=self._li_port) actions = [load_direction(parser, Direction.IN)] flows.add_resubmit_next_service_flow( dp, self._ingress_tbl_num, match, actions=actions, priority=flows.DEFAULT_PRIORITY, resubmit_table=self._li_table) # set a direction bit for incoming (mtr -> UE) traffic. if self._mtr_service_enabled: match = MagmaMatch(in_port=self.config.mtr_port) actions = [load_direction(parser, Direction.IN)] flows.add_resubmit_next_service_flow( dp, self._ingress_tbl_num, match, actions=actions, priority=flows.DEFAULT_PRIORITY, resubmit_table=next_table)
def test_rule_reactivation(self): """ Adds a policy to a subscriber, deletes it by incrementing the version, and add it back. Verifies that the usage stats is correctly reported, the old flows are deleted, and the new flows are installed. Assert: UPLINK policy matches 128 packets (*34 = 4352 bytes) Old flows are deleted New flows are installed No other stats are reported """ fake_controller_setup(self.enforcement_controller, self.enforcement_stats_controller) imsi = 'IMSI001010000000013' sub_ip = '192.168.128.74' num_pkts_tx_match = 128 flow_list = [ FlowDescription(match=FlowMatch(ipv4_dst='45.10.0.0/25', direction=FlowMatch.UPLINK), action=FlowDescription.PERMIT) ] policy = PolicyRule(id='rule1', priority=3, flow_list=flow_list) enf_stat_name = imsi + '|rule1' self.service_manager.session_rule_version_mapper.update_version( imsi, 'rule1') version = \ self.service_manager.session_rule_version_mapper.get_version( imsi, 'rule1') """ Setup subscriber, setup table_isolation to fwd pkts """ self._static_rule_dict[policy.id] = policy sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._main_tbl_num, self.enforcement_stats_controller).add_static_rule(policy.id) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber( sub_context.cfg).build_requests(), self.testing_controller) """ Create a packet """ pkt_sender = ScapyPacketInjector(self.IFACE) packet = IPPacketBuilder() \ .set_ip_layer('45.10.0.0/20', sub_ip) \ .set_ether_layer(self.MAC_DEST, "00:00:00:00:00:00") \ .build() # =========================== Verification =========================== rule_num = self.enforcement_stats_controller._rule_mapper \ .get_or_create_rule_num(policy.id) enf_query = FlowQuery(self._main_tbl_num, self.testing_controller, match=flow_match_to_magma_match( FlowMatch(ipv4_dst='45.10.0.0/25', direction=FlowMatch.UPLINK)), cookie=rule_num) es_old_version_query = FlowQuery(self._scratch_tbl_num, self.testing_controller, match=MagmaMatch( imsi=encode_imsi(imsi), reg2=rule_num, rule_version=version), cookie=rule_num) es_new_version_query = FlowQuery(self._scratch_tbl_num, self.testing_controller, match=MagmaMatch( imsi=encode_imsi(imsi), reg2=rule_num, rule_version=version + 1), cookie=rule_num) packet_wait = FlowVerifier([], self._wait_func([enf_stat_name])) """ Verify that flows are properly deleted """ verifier = FlowVerifier([ FlowTest(es_old_version_query, 0, flow_count=0), FlowTest(es_new_version_query, num_pkts_tx_match, flow_count=2), FlowTest(enf_query, num_pkts_tx_match, flow_count=1), ], self._wait_func([enf_stat_name])) snapshot_verifier = SnapshotVerifier(self, self.BRIDGE, self.service_manager) """ Send a packet, then deactivate and reactivate the same rule and send a packet. Wait until it is received by ovs and enf stats. """ with isolator, sub_context, verifier, snapshot_verifier: with packet_wait: self.enforcement_stats_controller._report_usage.reset_mock() pkt_sender.send(packet) self.enforcement_stats_controller._report_usage.reset_mock() self.service_manager.session_rule_version_mapper. \ update_version(imsi, 'rule1') self.enforcement_controller.deactivate_rules(imsi, [policy.id]) self.enforcement_controller.activate_rules(imsi, sub_ip, [policy.id], []) self.enforcement_stats_controller.activate_rules( imsi, sub_ip, [policy.id], []) pkt_sender.send(packet) verifier.verify() stats = get_enforcement_stats( self.enforcement_stats_controller._report_usage.call_args_list) """ Verify both packets are reported after reactivation. """ self.assertEqual(stats[enf_stat_name].sid, imsi) self.assertEqual(stats[enf_stat_name].rule_id, "rule1") self.assertEqual(stats[enf_stat_name].bytes_rx, 0) # TODO Figure out why this one fails. #self.assertEqual(stats[enf_stat_name].bytes_tx, # num_pkts_tx_match * len(packet)) self.assertEqual(len(stats), 1)
def test_passthrough_rules(self): """ Add UE MAC flows for two subscribers """ imsi_1 = 'IMSI010000000088888' other_mac = '5e:cc:cc:b1:aa:aa' cli_ip = '1.1.1.1' server_ip = '151.42.41.122' # Add subscriber with UE MAC address """ self.ue_mac_controller.add_ue_mac_flow(imsi_1, self.UE_MAC_1) # Create a set of packets pkt_sender = ScapyPacketInjector(self.BRIDGE) # Only send downlink as the pkt_sender sends pkts from in_port=LOCAL downlink_packet1 = EtherPacketBuilder() \ .set_ether_layer(self.UE_MAC_1, other_mac) \ .build() dhcp_packet = DHCPPacketBuilder() \ .set_ether_layer(self.UE_MAC_1, other_mac) \ .set_ip_layer(server_ip, cli_ip) \ .set_udp_layer(67, 68) \ .set_bootp_layer(2, cli_ip, server_ip, other_mac) \ .set_dhcp_layer([("message-type", "ack"), "end"]) \ .build() dns_packet = UDPPacketBuilder() \ .set_ether_layer(self.UE_MAC_1, other_mac) \ .set_ip_layer('151.42.41.122', '1.1.1.1') \ .set_udp_layer(53, 32795) \ .build() arp_packet = ARPPacketBuilder() \ .set_ether_layer(self.UE_MAC_1, other_mac) \ .set_arp_layer('1.1.1.1') \ .set_arp_hwdst(self.UE_MAC_1) \ .set_arp_src(other_mac, '1.1.1.12') \ .build() # Check if these flows were added (queries should return flows) flow_queries = [ FlowQuery(self._tbl_num, self.testing_controller, match=MagmaMatch(eth_dst=self.UE_MAC_1)) ] # =========================== Verification =========================== # Verify 3 flows installed for ue_mac table (3 pkts matched) # 4 flows installed for inout (3 pkts matched) # 2 flows installed (2 pkts matches) flow_verifier = FlowVerifier( [ FlowTest(FlowQuery(self._tbl_num, self.testing_controller), 4, 3), FlowTest(FlowQuery(self._ingress_tbl_num, self.testing_controller), 4, 2), FlowTest(FlowQuery(self._egress_tbl_num, self.testing_controller), 3, 2), FlowTest(flow_queries[0], 4, 1), ], lambda: wait_after_send(self.testing_controller)) snapshot_verifier = SnapshotVerifier(self, self.BRIDGE, self.service_manager) with flow_verifier, snapshot_verifier: pkt_sender.send(dhcp_packet) pkt_sender.send(downlink_packet1) pkt_sender.send(dns_packet) hub.sleep(3) pkt_sender.send(arp_packet) flow_verifier.verify()
def _remove_mirror_flows(self, imsis): for imsi in imsis: self.logger.error("Disabling LI tracking for IMSI %s", imsi) match = MagmaMatch(imsi=encode_imsi(imsi)) flows.delete_flow(self._datapath, self.tbl_num, match)
def add_tunnel_flows(self, precedence: int, i_teid: int, o_teid: int, ue_ip_adr: IPAddress, enodeb_ip_addr: str, sid: int = None) -> bool: parser = self._datapath.ofproto_parser priority = Utils.get_of_priority(precedence) # Add flow for gtp port if enodeb_ip_addr: gtp_portno = self._add_gtp_port(enodeb_ip_addr) else: gtp_portno = self.config.gtp_port # Add flow for gtp port for Uplink Tunnel actions = [] if i_teid: 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") ] 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) # Install Downlink Tunnel actions = [] if not ue_ip_adr: self.logger.error("ue_ip_address is None") return else: # Add flow for LOCAL port 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=self._uplink_port, **ip_match_out) if o_teid and enodeb_ip_addr: actions = [ parser.OFPActionSetField(tunnel_id=o_teid), parser.OFPActionSetField(tun_ipv4_dst=enodeb_ip_addr), parser.OFPActionSetField(tun_flags=TUNNEL_OAM_FLAG), parser.NXActionRegLoad2(dst=TUN_PORT_REG, value=gtp_portno) ] 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) # Add flow for mtr port match = MagmaMatch(eth_type=get_eth_type(ue_ip_adr), in_port=self.config.mtr_port, **ip_match_out) 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) # Add ARP flow for LOCAL port if ue_ip_adr.version == IPAddress.IPV4: match = MagmaMatch(eth_type=ether_types.ETH_TYPE_ARP, in_port=self._uplink_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) # Add ARP flow for mtr port if ue_ip_adr.version == IPAddress.IPV4: match = MagmaMatch(eth_type=ether_types.ETH_TYPE_ARP, in_port=self.config.mtr_port, arp_tpa=ipaddress.IPv4Address( ue_ip_adr.address.decode('utf-8'))) 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) return True
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 _delete_uplink_tunnel_flows(self, i_teid: int, gtp_portno: int): match = MagmaMatch(tunnel_id=i_teid, in_port=gtp_portno) flows.delete_flow(self._datapath, self.tbl_num, match)
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") self._install_proxy_flows(dp) 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.config.uplink_port)