Daniele Moro | 790cc10 | 2021-08-30 18:27:30 +0200 | [diff] [blame^] | 1 | class UP4: |
| 2 | |
| 3 | def __init__(self): |
| 4 | self.default = '' |
| 5 | |
| 6 | # TODO: add test case that checks entries are being inserted and deleted from ONOS correclty |
| 7 | def CASE1(self, main): |
| 8 | """ |
| 9 | Attach UE |
| 10 | Generate traffic from UE to PDN |
| 11 | Verify traffic received from PDN |
| 12 | Generate traffic from PDN to UE |
| 13 | Verify traffic received from UE |
| 14 | Detach UE |
| 15 | """ |
| 16 | UE_PORT = 400 |
| 17 | PDN_PORT = 800 |
| 18 | GPDU_PORT = 2152 |
| 19 | try: |
| 20 | from tests.USECASE.SegmentRouting.dependencies.up4libcli import \ |
| 21 | Up4LibCli |
| 22 | from tests.USECASE.SegmentRouting.dependencies.Testcaselib import \ |
| 23 | Testcaselib as run |
| 24 | from distutils.util import strtobool |
| 25 | except ImportError as e: |
| 26 | main.log.error("Import not found. Exiting the test") |
| 27 | main.log.error(e) |
| 28 | main.cleanAndExit() |
| 29 | |
| 30 | # TODO: Move to a setup script |
| 31 | run.initTest(main) |
| 32 | main.log.info(main.Cluster.numCtrls) |
| 33 | main.Cluster.setRunningNode(3) |
| 34 | run.installOnos(main, skipPackage=True, cliSleep=5) |
| 35 | |
| 36 | # Get the P4RT client connected to UP4 in the first available ONOS instance |
| 37 | up4Client = main.Cluster.active(0).p4rtUp4 |
| 38 | |
| 39 | s1u_address = main.params["UP4"]["s1u_address"] |
| 40 | enb_address = main.params["UP4"]["enb_address"] |
| 41 | router_mac = main.params["UP4"]["router_mac"] |
| 42 | |
| 43 | pdn_host = getattr(main, main.params["UP4"]["pdn_host"]) |
| 44 | pdn_interface = pdn_host.interfaces[0] |
| 45 | |
| 46 | enodeb_host = getattr(main, main.params["UP4"]["enodeb_host"]) |
| 47 | enodeb_interface = enodeb_host.interfaces[0] |
| 48 | |
| 49 | emulated_ues = main.params["UP4"]['ues'] |
| 50 | n_ues = len(emulated_ues) |
| 51 | |
| 52 | main.step("Start scapy and p4rt client") |
| 53 | pdn_host.startScapy(ifaceName=pdn_interface["name"]) |
| 54 | enodeb_host.startScapy(ifaceName=enodeb_interface["name"], |
| 55 | enableGtp=True) |
| 56 | up4Client.startP4RtClient() |
| 57 | |
| 58 | # TODO: move to library in dependencies |
| 59 | main.step("Attach UEs") |
| 60 | for ue in emulated_ues.values(): |
| 61 | # Sanitize values coming from the params file |
| 62 | if "five_g" in ue: |
| 63 | ue["five_g"] = bool(strtobool(ue["five_g"])) |
| 64 | if "qfi" in ue and ue["qfi"] == "": |
| 65 | ue["qfi"] = None |
| 66 | Up4LibCli.attachUe(up4Client, s1u_address=s1u_address, |
| 67 | enb_address=enb_address, |
| 68 | **ue) |
| 69 | |
| 70 | # ----------------- Test Upstream traffic (enb->pdn) |
| 71 | main.step("Test upstream traffic") |
| 72 | # Scapy filter needs to start before sending traffic |
| 73 | pkt_filter_upstream = "" |
| 74 | for ue in emulated_ues.values(): |
| 75 | if "ue_address" in ue: |
| 76 | if len(pkt_filter_upstream) != 0: |
| 77 | pkt_filter_upstream += " or " |
| 78 | pkt_filter_upstream += "src host " + ue["ue_address"] |
| 79 | pkt_filter_upstream = "ip and udp dst port %s and (%s) and dst host %s" % \ |
| 80 | (PDN_PORT, pkt_filter_upstream, |
| 81 | pdn_interface["ips"][0]) |
| 82 | main.log.info("Start listening on %s intf %s" % |
| 83 | (main.params["UP4"]["pdn_host"], pdn_interface["name"])) |
| 84 | main.log.debug("BPF Filter Upstream: \n %s" % pkt_filter_upstream) |
| 85 | pdn_host.startFilter(ifaceName=pdn_interface["name"], |
| 86 | sniffCount=n_ues, |
| 87 | pktFilter=pkt_filter_upstream) |
| 88 | |
| 89 | main.log.info("Sending %d packets from eNodeB host" % len(emulated_ues)) |
| 90 | for ue in emulated_ues.values(): |
| 91 | enodeb_host.buildEther() |
| 92 | enodeb_host.buildIP(src=enb_address, dst=s1u_address) |
| 93 | enodeb_host.buildUDP(ipVersion=4, dport=GPDU_PORT) |
| 94 | # FIXME: With newer scapy TEID becomes teid (required for Scapy 2.4.5) |
| 95 | enodeb_host.buildGTP(gtp_type=0xFF, TEID=int(ue["teid"])) |
| 96 | enodeb_host.buildIP(overGtp=True, src=ue["ue_address"], |
| 97 | dst=pdn_interface["ips"][0]) |
| 98 | enodeb_host.buildUDP(ipVersion=4, overGtp=True, sport=UE_PORT, |
| 99 | dport=PDN_PORT) |
| 100 | |
| 101 | enodeb_host.sendPacket(iface=enodeb_interface["name"]) |
| 102 | |
| 103 | finished = pdn_host.checkFilter() |
| 104 | packets = "" |
| 105 | if finished: |
| 106 | packets = pdn_host.readPackets(detailed=True) |
| 107 | for p in packets.splitlines(): |
| 108 | main.log.debug(p) |
| 109 | # We care only of the last line from readPackets |
| 110 | packets = packets.splitlines()[-1] |
| 111 | else: |
| 112 | kill = pdn_host.killFilter() |
| 113 | main.log.debug(kill) |
| 114 | |
| 115 | fail = False |
| 116 | if len(emulated_ues) != packets.count('Ether'): |
| 117 | fail = True |
| 118 | msg = "Failed to capture packets in PDN. " |
| 119 | else: |
| 120 | msg = "Correctly captured packet in PDN. " |
| 121 | # We expect exactly 1 packet per UE |
| 122 | pktsFiltered = [packets.count("src=" + ue["ue_address"]) |
| 123 | for ue in emulated_ues.values()] |
| 124 | if pktsFiltered.count(1) != len(pktsFiltered): |
| 125 | fail = True |
| 126 | msg += "More than one packet per UE in downstream. " |
| 127 | else: |
| 128 | msg += "One packet per UE in upstream. " |
| 129 | |
| 130 | utilities.assert_equal( |
| 131 | expect=False, actual=fail, onpass=msg, onfail=msg) |
| 132 | |
| 133 | # --------------- Test Downstream traffic (pdn->enb) |
| 134 | main.step("Test downstream traffic") |
| 135 | pkt_filter_downstream = "ip and udp src port %d and udp dst port %d and dst host %s and src host %s" % ( |
| 136 | GPDU_PORT, GPDU_PORT, enb_address, s1u_address) |
| 137 | main.log.info("Start listening on %s intf %s" % ( |
| 138 | main.params["UP4"]["enodeb_host"], enodeb_interface["name"])) |
| 139 | main.log.debug("BPF Filter Downstream: \n %s" % pkt_filter_downstream) |
| 140 | enodeb_host.startFilter(ifaceName=enodeb_interface["name"], |
| 141 | sniffCount=len(emulated_ues), |
| 142 | pktFilter=pkt_filter_downstream) |
| 143 | |
| 144 | main.log.info("Sending %d packets from PDN host" % len(emulated_ues)) |
| 145 | for ue in emulated_ues.values(): |
| 146 | # From PDN we have to set dest MAC, otherwise scapy will do ARP |
| 147 | # request for the UE IP address. |
| 148 | pdn_host.buildEther(dst=router_mac) |
| 149 | pdn_host.buildIP(src=pdn_interface["ips"][0], |
| 150 | dst=ue["ue_address"]) |
| 151 | pdn_host.buildUDP(ipVersion=4, sport=PDN_PORT, dport=UE_PORT) |
| 152 | pdn_host.sendPacket(iface=pdn_interface["name"]) |
| 153 | |
| 154 | finished = enodeb_host.checkFilter() |
| 155 | packets = "" |
| 156 | if finished: |
| 157 | packets = enodeb_host.readPackets(detailed=True) |
| 158 | for p in packets.splitlines(): |
| 159 | main.log.debug(p) |
| 160 | # We care only of the last line from readPackets |
| 161 | packets = packets.splitlines()[-1] |
| 162 | else: |
| 163 | kill = enodeb_host.killFilter() |
| 164 | main.log.debug(kill) |
| 165 | |
| 166 | # The BPF filter might capture non-GTP packets because we can't filter |
| 167 | # GTP header in BPF. For this reason, check that the captured packets |
| 168 | # are from the expected tunnels. |
| 169 | # TODO: check inner UDP and IP fields as well |
| 170 | # FIXME: with newer scapy TEID becomes teid (required for Scapy 2.4.5) |
| 171 | pktsFiltered = [packets.count("TEID=" + hex(int(ue["teid"])) + "L ") |
| 172 | for ue in emulated_ues.values()] |
| 173 | |
| 174 | fail = False |
| 175 | if len(emulated_ues) != sum(pktsFiltered): |
| 176 | fail = True |
| 177 | msg = "Failed to capture packets in eNodeB. " |
| 178 | else: |
| 179 | msg = "Correctly captured packets in eNodeB. " |
| 180 | # We expect exactly 1 packet per UE |
| 181 | if pktsFiltered.count(1) != len(pktsFiltered): |
| 182 | fail = True |
| 183 | msg += "More than one packet per GTP TEID in downstream. " |
| 184 | else: |
| 185 | msg += "One packet per GTP TEID in downstream. " |
| 186 | |
| 187 | utilities.assert_equal( |
| 188 | expect=False, actual=fail, onpass=msg, onfail=msg) |
| 189 | |
| 190 | # Detach UEs |
| 191 | main.step("Detach UEs") |
| 192 | for ue in emulated_ues.values(): |
| 193 | # No need to sanitize values, already sanitized during attachment |
| 194 | Up4LibCli.detachUe(up4Client, s1u_address=s1u_address, |
| 195 | enb_address=enb_address, |
| 196 | **ue) |
| 197 | |
| 198 | # Teardown |
| 199 | main.step("Stop scapy and p4rt client") |
| 200 | enodeb_host.stopScapy() |
| 201 | pdn_host.stopScapy() |
| 202 | up4Client.stopP4RtClient() |
| 203 | run.cleanup(main) |