blob: 39621e2285317b30ceccacf9244be7c17317ef60 [file] [log] [blame]
Daniele Moro80889562021-09-08 10:09:26 +02001from distutils.util import strtobool
Daniele Moro522023c2021-10-15 17:30:33 +02002import copy
Daniele Moro80889562021-09-08 10:09:26 +02003
4FALSE = '0'
5TRUE = '1'
6DIR_UPLINK = '1'
7DIR_DOWNLINK = '2'
8IFACE_ACCESS = '1'
9IFACE_CORE = '2'
10TUNNEL_SPORT = '2152'
11TUNNEL_TYPE_GPDU = '3'
12
13UE_PORT = 400
Daniele Morobef0c7e2022-02-16 17:47:13 -080014DEFAULT_PDN_PORT = 800
Daniele Moro80889562021-09-08 10:09:26 +020015GPDU_PORT = 2152
16
Daniele Moroa1975192022-01-21 18:01:19 +010017DEFAULT_APP_ID = 0
Daniele Moro4650ffb2022-02-15 22:44:18 +010018DEFAULT_SESSION_METER_IDX = 0
19DEFAULT_APP_METER_IDX = 0
20
Daniele Moro80889562021-09-08 10:09:26 +020021
22class UP4:
23 """
24 Utility that manages interaction with UP4 via a P4RuntimeCliDriver available
25 in the cluster. Additionally, can verify connectivity by crafting GTP packets
Daniele Moro522023c2021-10-15 17:30:33 +020026 via Scapy with an HostDriver component, specified via <enodebs>, <pdn_host>,
Daniele Moro80889562021-09-08 10:09:26 +020027 and <router_mac> parameters.
28
29 Example params file:
30 <UP4>
31 <pdn_host>Compute1</pdn_host> # Needed to verify connectivity with scapy
Daniele Moro522023c2021-10-15 17:30:33 +020032 <enodebs> # List of emulated eNodeBs
33 <enode_1>
34 <host>Compute1</host> # Host that emulates this eNodeB
35 <interface>eno3</interface> # Name of the linux interface to use on the host, if not specified take the default
36 <enb_address>10.32.11.122</enb_address> # IP address of the eNodeB
37 <ues>ue3</ues> # Emulated ues connected to this eNB
38 </enode_1>
39 <enodeb_2>
40 <host>Compute3</host>
41 <enb_address>10.32.11.194</enb_address>
42 <ues>ue1,ue2</ues>
43 </enodeb_2>
44 </enodebs>
45 <enodeb_host>Compute3</enodeb_host>
Daniele Moro80889562021-09-08 10:09:26 +020046 <router_mac>00:00:0A:4C:1C:46</router_mac> # Needed to verify connectivity with scapy
47 <s1u_address>10.32.11.126</s1u_address>
Daniele Morocc4ecda2022-02-25 23:27:25 +010048 <slice_id>1</slice_id> # Mobile SLICE ID, used when pushing application filtering entries
Daniele Moro80889562021-09-08 10:09:26 +020049 <ues>
50 <ue2>
Daniele Moro80889562021-09-08 10:09:26 +020051 <ue_address>10.240.0.2</ue_address>
52 <teid>200</teid>
53 <up_id>20</up_id>
54 <down_id>21</down_id>
Carmelo Cascone848d1f52022-01-27 18:15:58 -080055 <!-- TC 0 means BEST EFFORT -->
56 <tc>2</tc>
Daniele Moro80889562021-09-08 10:09:26 +020057 <five_g>False</five_g>
58 </ue2>
59 </ues>
Daniele Moro522023c2021-10-15 17:30:33 +020060 <switch_to_kill>Leaf2</switch_to_kill> # Component name of the switch to kill in CASE 5
61 <enodebs_fail>enodeb_1</enodebs_fail> # List of eNodeBs that should fail traffic forwarding in CASE 5
Daniele Moro80889562021-09-08 10:09:26 +020062 </UP4>
63 """
64
65 def __init__(self):
66 self.s1u_address = None
Daniele Moro522023c2021-10-15 17:30:33 +020067 self.enodebs = None
Daniele Moro80889562021-09-08 10:09:26 +020068 self.pdn_host = None
69 self.pdn_interface = None
70 self.router_mac = None
Daniele Moro522023c2021-10-15 17:30:33 +020071 self.emulated_ues = {}
Daniele Morobef0c7e2022-02-16 17:47:13 -080072 self.app_filters = {}
Daniele Moro80889562021-09-08 10:09:26 +020073 self.up4_client = None
Daniele Moro522023c2021-10-15 17:30:33 +020074 self.no_host = False
Daniele Morocc4ecda2022-02-25 23:27:25 +010075 self.slice_id = None
Daniele Moro80889562021-09-08 10:09:26 +020076
Daniele Moro954e2282021-09-22 17:32:03 +020077 def setup(self, p4rt_client, no_host=False):
78 """
79 Set up P4RT and scapy on eNB and PDN hosts
80 :param p4rt_client: a P4RuntimeCliDriver component
81 :param no_host: True if you don't want to start scapy on the hosts
82 :return:
83 """
Daniele Moro80889562021-09-08 10:09:26 +020084 self.s1u_address = main.params["UP4"]["s1u_address"]
Daniele Moro80889562021-09-08 10:09:26 +020085 self.emulated_ues = main.params["UP4"]['ues']
Daniele Morobef0c7e2022-02-16 17:47:13 -080086 self.app_filters = main.params["UP4"]['app_filters']
Daniele Morocc4ecda2022-02-25 23:27:25 +010087 self.slice_id = main.params["UP4"]['slice_id']
Daniele Moro80889562021-09-08 10:09:26 +020088 self.up4_client = p4rt_client
Daniele Moro522023c2021-10-15 17:30:33 +020089 self.no_host = no_host
Daniele Moro80889562021-09-08 10:09:26 +020090
91 # Optional Parameters
Daniele Moro522023c2021-10-15 17:30:33 +020092
93 self.enodebs = copy.deepcopy((main.params["UP4"]["enodebs"]))
94 for enb in self.enodebs.values():
95 enb["ues"] = enb["ues"].split(",")
96 enb["host"] = getattr(main, enb["host"])
97 # If interface not provided by the params, use the default in the host
98 if "interface" not in enb.keys():
99 enb["interface"] = enb["host"].interfaces[0]["name"]
100 if "pdn_host" in main.params["UP4"]:
101 self.pdn_host = getattr(main, main.params["UP4"]["pdn_host"])
102 self.pdn_interface = self.pdn_host.interfaces[0]
103 self.router_mac = main.params["UP4"].get("router_mac", None)
Daniele Moro80889562021-09-08 10:09:26 +0200104
Jon Halla601dde2022-02-28 14:22:07 -0800105 for app_filter in self.app_filters.values():
106 if app_filter.get('slice_id', None) is None:
107 app_filter['slice_id'] = self.slice_id
Daniele Moro80889562021-09-08 10:09:26 +0200108 # Start components
109 self.up4_client.startP4RtClient()
Daniele Moro522023c2021-10-15 17:30:33 +0200110 if not self.no_host:
111 if self.enodebs is not None:
112 for enb in self.enodebs.values():
113 enb["host"].startScapy(ifaceName=enb["interface"],
Daniele Moro49a843c2022-01-05 14:36:32 +0100114 enableGtp=True)
Daniele Moro522023c2021-10-15 17:30:33 +0200115 if self.pdn_host is not None:
116 self.pdn_host.startScapy(ifaceName=self.pdn_interface["name"])
Daniele Morocc4ecda2022-02-25 23:27:25 +0100117 # TODO: configure interfaces table. Currently, we rely on netcfg or
118 # PFCP agent to push interface entries, but we should explicitly push
119 # them here
Daniele Moro80889562021-09-08 10:09:26 +0200120
121 def teardown(self):
122 self.up4_client.stopP4RtClient()
Daniele Moro522023c2021-10-15 17:30:33 +0200123 if not self.no_host:
124 if self.enodebs is not None:
125 for enb in self.enodebs.values():
126 enb["host"].stopScapy()
127 if self.pdn_host is not None:
128 self.pdn_host.stopScapy()
Daniele Moro80889562021-09-08 10:09:26 +0200129
130 def attachUes(self):
Daniele Morobef0c7e2022-02-16 17:47:13 -0800131 for app_filter in self.app_filters.values():
132 self.insertAppFilter(**app_filter)
Daniele Moro522023c2021-10-15 17:30:33 +0200133 for (name, ue) in self.emulated_ues.items():
Daniele Moro80889562021-09-08 10:09:26 +0200134 ue = UP4.__sanitizeUeData(ue)
Daniele Moro522023c2021-10-15 17:30:33 +0200135 self.attachUe(name, **ue)
Daniele Moro80889562021-09-08 10:09:26 +0200136
137 def detachUes(self):
Daniele Morobef0c7e2022-02-16 17:47:13 -0800138 for app_filter in self.app_filters.values():
139 self.removeAppFilter(**app_filter)
Daniele Moro522023c2021-10-15 17:30:33 +0200140 for (name, ue) in self.emulated_ues.items():
Daniele Moro249d6e72021-09-20 10:32:54 +0200141 ue = UP4.__sanitizeUeData(ue)
Daniele Moro522023c2021-10-15 17:30:33 +0200142 self.detachUe(name, **ue)
Daniele Moro80889562021-09-08 10:09:26 +0200143
Daniele Morobef0c7e2022-02-16 17:47:13 -0800144 def __pickPortFromRange(self, range):
145 if range is None or len(range) == 0:
146 return DEFAULT_PDN_PORT
147 # First port in range
148 return int(range.split('..')[0])
149
150 def testUpstreamTraffic(self, enb_names=None, app_filter_name=None, shouldFail=False):
Daniele Moro522023c2021-10-15 17:30:33 +0200151 if self.enodebs is None or self.pdn_host is None:
Daniele Moro80889562021-09-08 10:09:26 +0200152 main.log.error(
153 "Need eNodeB and PDN host params to generate scapy traffic")
154 return
Daniele Moro522023c2021-10-15 17:30:33 +0200155 if enb_names is None or enb_names == []:
156 enodebs = self.enodebs.values()
157 else:
158 enodebs = [self.enodebs[enb] for enb in enb_names]
Daniele Morobef0c7e2022-02-16 17:47:13 -0800159
160 app_filter = self.app_filters[app_filter_name]
161 pdn_port = self.__pickPortFromRange(app_filter.get("port_range", None))
162 app_filter_should_drop = app_filter["action"] != "allow"
163
Daniele Moro80889562021-09-08 10:09:26 +0200164 pkt_filter_upstream = ""
Daniele Moro522023c2021-10-15 17:30:33 +0200165 ues = []
166 for enb in enodebs:
167 for ue_name in enb["ues"]:
168 ue = self.emulated_ues[ue_name]
169 if "ue_address" in ue:
170 ues.append(ue)
171 if len(pkt_filter_upstream) != 0:
172 pkt_filter_upstream += " or "
173 pkt_filter_upstream += "src host " + ue["ue_address"]
Daniele Moro80889562021-09-08 10:09:26 +0200174 pkt_filter_upstream = "ip and udp dst port %s and (%s) and dst host %s" % \
Daniele Morobef0c7e2022-02-16 17:47:13 -0800175 (pdn_port, pkt_filter_upstream,
Daniele Moro80889562021-09-08 10:09:26 +0200176 self.pdn_interface["ips"][0])
177 main.log.info("Start listening on %s intf %s" %
178 (self.pdn_host.name, self.pdn_interface["name"]))
179 main.log.debug("BPF Filter Upstream: \n %s" % pkt_filter_upstream)
180 self.pdn_host.startFilter(ifaceName=self.pdn_interface["name"],
Daniele Moro522023c2021-10-15 17:30:33 +0200181 sniffCount=len(ues),
Daniele Moro80889562021-09-08 10:09:26 +0200182 pktFilter=pkt_filter_upstream)
183
184 main.log.info(
Daniele Moro522023c2021-10-15 17:30:33 +0200185 "Sending %d packets from eNodeB host" % len(ues))
186 for enb in enodebs:
187 for ue_name in enb["ues"]:
188 main.log.info(ue_name)
189 ue = self.emulated_ues[ue_name]
190 main.log.info(str(ue))
191 UP4.buildGtpPacket(enb["host"],
192 src_ip_outer=enb["enb_address"],
193 dst_ip_outer=self.s1u_address,
194 src_ip_inner=ue["ue_address"],
195 dst_ip_inner=self.pdn_interface["ips"][0],
196 src_udp_inner=UE_PORT,
Daniele Morobef0c7e2022-02-16 17:47:13 -0800197 dst_udp_inner=pdn_port,
Daniele Moro522023c2021-10-15 17:30:33 +0200198 teid=int(ue["teid"]))
199 enb["host"].sendPacket(iface=enb["interface"])
Daniele Moro80889562021-09-08 10:09:26 +0200200
Daniele Morobf53dec2021-09-13 18:11:56 +0200201 packets = UP4.checkFilterAndGetPackets(self.pdn_host)
Daniele Morobef0c7e2022-02-16 17:47:13 -0800202 if app_filter_should_drop:
203 expected_pkt_count = 0
204 else:
205 # We expect exactly 1 packet per UE.
206 expected_pkt_count = len(ues)
207 actual_pkt_count = packets.count('Ether')
Daniele Moro80889562021-09-08 10:09:26 +0200208 fail = False
Daniele Morobef0c7e2022-02-16 17:47:13 -0800209 if expected_pkt_count != actual_pkt_count:
Daniele Moro80889562021-09-08 10:09:26 +0200210 fail = True
Daniele Morobef0c7e2022-02-16 17:47:13 -0800211 msg = "Received %d packets (expected %d)\n%s\n" % (
212 actual_pkt_count, expected_pkt_count, str(packets)
213 )
Daniele Moro80889562021-09-08 10:09:26 +0200214 else:
Daniele Morobef0c7e2022-02-16 17:47:13 -0800215 msg = "Received %d packets (expected %d)\n" % (
216 actual_pkt_count, expected_pkt_count
217 )
Daniele Moro80889562021-09-08 10:09:26 +0200218
Daniele Morobef0c7e2022-02-16 17:47:13 -0800219 if expected_pkt_count > 0:
220 # Make sure the captured packets are from the expected UE addresses.
221 for ue in ues:
222 ue_pkt_count = packets.count("src=" + ue["ue_address"])
223 if ue_pkt_count != 1:
224 fail = True
225 msg += "Received %d packet(s) from UE %s (expected 1)\n" % (
226 ue_pkt_count, ue["ue_address"]
227 )
Daniele Moro80889562021-09-08 10:09:26 +0200228 utilities.assert_equal(
Daniele Morobef0c7e2022-02-16 17:47:13 -0800229 expect=shouldFail, actual=fail, onpass=msg, onfail=msg
230 )
Daniele Moro80889562021-09-08 10:09:26 +0200231
Daniele Morobef0c7e2022-02-16 17:47:13 -0800232 def testDownstreamTraffic(self, enb_names=None, app_filter_name=None, shouldFail=False):
Daniele Moro522023c2021-10-15 17:30:33 +0200233 if self.enodebs is None or self.pdn_host is None:
Daniele Moro80889562021-09-08 10:09:26 +0200234 main.log.error(
235 "Need eNodeB and PDN host params to generate scapy traffic")
236 return
Daniele Moro522023c2021-10-15 17:30:33 +0200237 if enb_names is None or enb_names == []:
238 enodebs = self.enodebs.values()
239 else:
240 enodebs = [self.enodebs[enb] for enb in enb_names]
Daniele Morobef0c7e2022-02-16 17:47:13 -0800241
242 app_filter = self.app_filters[app_filter_name]
243 pdn_port = self.__pickPortFromRange(app_filter.get("port_range", None))
244 app_filter_should_drop = app_filter["action"] != "allow"
245
Daniele Moro522023c2021-10-15 17:30:33 +0200246 pkt_filter_downstream = "ip and udp src port %d and udp dst port %d and src host %s" % (
247 GPDU_PORT, GPDU_PORT, self.s1u_address)
248 ues = []
249 for enb in enodebs:
Daniele Moro49a843c2022-01-05 14:36:32 +0100250 filter_down = pkt_filter_downstream + " and dst host %s" % enb[
251 "enb_address"]
Daniele Moro522023c2021-10-15 17:30:33 +0200252 main.log.info("Start listening on %s intf %s" % (
253 enb["host"], enb["interface"]))
254 main.log.debug("BPF Filter Downstream: \n %s" % filter_down)
255 enb["host"].startFilter(ifaceName=enb["interface"],
256 sniffCount=len(enb["ues"]),
257 pktFilter=filter_down)
258 ues.extend([self.emulated_ues[ue_name] for ue_name in enb["ues"]])
Daniele Moro80889562021-09-08 10:09:26 +0200259
260 main.log.info(
Daniele Moro522023c2021-10-15 17:30:33 +0200261 "Sending %d packets from PDN host" % len(ues))
262 for ue in ues:
Daniele Moro80889562021-09-08 10:09:26 +0200263 # From PDN we have to set dest MAC, otherwise scapy will do ARP
264 # request for the UE IP address.
Daniele Morobf53dec2021-09-13 18:11:56 +0200265 UP4.buildUdpPacket(self.pdn_host,
266 dst_eth=self.router_mac,
267 src_ip=self.pdn_interface["ips"][0],
268 dst_ip=ue["ue_address"],
Daniele Morobef0c7e2022-02-16 17:47:13 -0800269 src_udp=pdn_port,
Daniele Morobf53dec2021-09-13 18:11:56 +0200270 dst_udp=UE_PORT)
Daniele Moro80889562021-09-08 10:09:26 +0200271 self.pdn_host.sendPacket(iface=self.pdn_interface["name"])
Daniele Moro522023c2021-10-15 17:30:33 +0200272 packets = ""
273 for enb in enodebs:
274 pkt = UP4.checkFilterAndGetPackets(enb["host"])
275 packets += pkt
Daniele Moro80889562021-09-08 10:09:26 +0200276 # The BPF filter might capture non-GTP packets because we can't filter
277 # GTP header in BPF. For this reason, check that the captured packets
278 # are from the expected tunnels.
Daniele Morobef0c7e2022-02-16 17:47:13 -0800279 # TODO: check inner IP fields as well
Daniele Moro80889562021-09-08 10:09:26 +0200280 # FIXME: with newer scapy TEID becomes teid (required for Scapy 2.4.5)
Daniele Morobef0c7e2022-02-16 17:47:13 -0800281 downlink_teids = [int(ue["teid"]) + 1 for ue in ues]
282 # Number of GTP packets from expected TEID per UEs
283 gtp_pkts = [
284 packets.count("TEID=" + hex(teid) + "L ") for teid in downlink_teids
285 ]
286 # Number of packets from the expected PDN port
287 app_pkts = packets.count("UDP sport=" + str(pdn_port))
288 if app_filter_should_drop:
289 expected_pkt_count = 0
290 else:
291 # We expect exactly 1 packet per UE.
292 expected_pkt_count = len(ues)
293
Daniele Moro80889562021-09-08 10:09:26 +0200294 fail = False
Daniele Morobef0c7e2022-02-16 17:47:13 -0800295 if expected_pkt_count != sum(gtp_pkts):
Daniele Moro80889562021-09-08 10:09:26 +0200296 fail = True
Daniele Morobef0c7e2022-02-16 17:47:13 -0800297 msg = "Received %d packets (expected %d) from TEIDs %s\n%s\n" % (
298 sum(gtp_pkts), expected_pkt_count, downlink_teids, str(packets)
299 )
Daniele Moro80889562021-09-08 10:09:26 +0200300 else:
Daniele Morobef0c7e2022-02-16 17:47:13 -0800301 msg = "Received %d packets (expected %d) from TEIDs %s\n" % (
302 sum(gtp_pkts), expected_pkt_count, downlink_teids
303 )
304 if expected_pkt_count != app_pkts:
Daniele Moro80889562021-09-08 10:09:26 +0200305 fail = True
Daniele Morobef0c7e2022-02-16 17:47:13 -0800306 msg += "Received %d packets (expected %d) from PDN port %s\n%s\n" % (
307 sum(gtp_pkts), expected_pkt_count, pdn_port, str(packets)
308 )
Daniele Moro80889562021-09-08 10:09:26 +0200309 else:
Daniele Morobef0c7e2022-02-16 17:47:13 -0800310 msg += "Received %d packets (expected %d) from PDN port %s\n" % (
311 sum(gtp_pkts), expected_pkt_count, pdn_port
312 )
313 if expected_pkt_count > 0:
314 if gtp_pkts.count(1) != len(gtp_pkts):
315 fail = True
316 msg += "Received %s packet(s) per UE (expected %s)\n%s\n" % (
317 gtp_pkts, [1] * len(gtp_pkts), packets
318 )
319 else:
320 msg += "Received %s packet(s) per UE (expected %s)\n" % (
321 gtp_pkts, [1] * len(gtp_pkts)
322 )
Daniele Moro80889562021-09-08 10:09:26 +0200323
324 utilities.assert_equal(
Daniele Morobef0c7e2022-02-16 17:47:13 -0800325 expect=shouldFail, actual=fail, onpass=msg, onfail=msg
326 )
Daniele Moro80889562021-09-08 10:09:26 +0200327
Daniele Moro49a843c2022-01-05 14:36:32 +0100328 def readUeSessionsNumber(self):
Daniele Moro954e2282021-09-22 17:32:03 +0200329 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100330 Read the UE session tables and return the number of entries
Daniele Moro954e2282021-09-22 17:32:03 +0200331
Daniele Moro49a843c2022-01-05 14:36:32 +0100332 :return: Number of entries in the UE session tables
Daniele Moro954e2282021-09-22 17:32:03 +0200333 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100334 tableName = 'PreQosPipe.sessions_uplink'
335 nUeSess = self.up4_client.readNumberTableEntries(tableName)
336 tableName = 'PreQosPipe.sessions_downlink'
337 nUeSess += self.up4_client.readNumberTableEntries(tableName)
338 return nUeSess
Daniele Moro954e2282021-09-22 17:32:03 +0200339
Daniele Moro49a843c2022-01-05 14:36:32 +0100340 def readTerminationsNumber(self):
Daniele Moro954e2282021-09-22 17:32:03 +0200341 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100342 Read the terminations and return the number of entities
Daniele Moro954e2282021-09-22 17:32:03 +0200343
Daniele Moro49a843c2022-01-05 14:36:32 +0100344 :return: Number of terminations entities
Daniele Moro954e2282021-09-22 17:32:03 +0200345 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100346 tableName = 'PreQosPipe.terminations_uplink'
347 nTerm = self.up4_client.readNumberTableEntries(tableName)
348 tableName = 'PreQosPipe.terminations_downlink'
349 nTerm += self.up4_client.readNumberTableEntries(tableName)
350 return nTerm
Daniele Moro954e2282021-09-22 17:32:03 +0200351
352 def verifyUesFlowNumberP4rt(self):
353 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100354 Verify via P4RT CLI that the number of UE sessions and terminations
355 is the expected one
Daniele Moro954e2282021-09-22 17:32:03 +0200356
Daniele Moro49a843c2022-01-05 14:36:32 +0100357 :return: True if the number of UE sessions and terminations is expected,
358 False otherwise
Daniele Moro954e2282021-09-22 17:32:03 +0200359 """
Daniele Morobef0c7e2022-02-16 17:47:13 -0800360 return self.readUeSessionsNumber() == len(self.emulated_ues) * 2 and \
361 self.readTerminationsNumber() == len(self.emulated_ues) * 2 * len(self.app_filters)
Daniele Moro954e2282021-09-22 17:32:03 +0200362
363 def verifyNoUesFlowNumberP4rt(self, preInstalledUes=0):
364 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100365 Verify via P4RT CLI that there is no UE sessions and terminations installed.
Daniele Moro954e2282021-09-22 17:32:03 +0200366
Daniele Moro49a843c2022-01-05 14:36:32 +0100367 :param preInstalledUes: Number of UEs whose UE sessions and terminations
368 are still programmed
Daniele Moro954e2282021-09-22 17:32:03 +0200369 :return:
370 """
Daniele Morobef0c7e2022-02-16 17:47:13 -0800371 return self.readUeSessionsNumber() == preInstalledUes * 2 and \
372 self.readTerminationsNumber() == preInstalledUes * 2 * len(self.app_filters)
Daniele Moro954e2282021-09-22 17:32:03 +0200373
Daniele Moro6dfbfef2021-09-28 22:44:19 +0200374 def verifyNoUesFlow(self, onosCli, retries=10):
Daniele Morobf53dec2021-09-13 18:11:56 +0200375 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100376 Verify that no UE session, terminations are installed in ONOS.
Daniele Morobf53dec2021-09-13 18:11:56 +0200377
378 :param onosCli: An instance of a OnosCliDriver
379 :param retries: number of retries
380 :return:
381 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100382 retValue = utilities.retry(f=UP4.__verifyNoUeSessionAndTerminationOnos,
Daniele Morobf53dec2021-09-13 18:11:56 +0200383 retValue=False,
384 args=[onosCli],
Daniele Moro6dfbfef2021-09-28 22:44:19 +0200385 sleep=5,
Daniele Morobf53dec2021-09-13 18:11:56 +0200386 attempts=retries)
387 utilities.assert_equal(expect=True,
388 actual=retValue,
Daniele Moro49a843c2022-01-05 14:36:32 +0100389 onpass="No UE session and terminations in ONOS",
390 onfail="Stale UE session or terminations")
Daniele Morobf53dec2021-09-13 18:11:56 +0200391
392 @staticmethod
Daniele Moro49a843c2022-01-05 14:36:32 +0100393 def __verifyNoUeSessionAndTerminationOnos(onosCli):
Daniele Morobf53dec2021-09-13 18:11:56 +0200394 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100395 Verify that no UE session, terminations are installed in ONOS
Daniele Morobf53dec2021-09-13 18:11:56 +0200396
397 :param onosCli: An instance of a OnosCliDriver
398 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100399 sessions = onosCli.sendline(cmdStr="up4:read-entities -s",
400 showResponse=True,
401 noExit=True, expectJson=False)
402 terminations = onosCli.sendline(cmdStr="up4:read-entities -t",
403 showResponse=True,
404 noExit=True, expectJson=False)
405 return sessions == "" and terminations == ""
Daniele Morobf53dec2021-09-13 18:11:56 +0200406
Daniele Moroc6811a82021-10-12 11:29:41 +0200407 def verifyUp4Flow(self, onosCli, retries=10):
Daniele Morobf53dec2021-09-13 18:11:56 +0200408 """
Daniele Moro49a843c2022-01-05 14:36:32 +0100409 Verify UE session, terminations and GTP tunnel peers installed via UP4
410 using the ONOS CLI.
Daniele Morobf53dec2021-09-13 18:11:56 +0200411
412 :param onosCli: An instance of a OnosCliDriver
Daniele Moroc6811a82021-10-12 11:29:41 +0200413 :param retries: Number of retries
Daniele Morobf53dec2021-09-13 18:11:56 +0200414 """
Daniele Morobef0c7e2022-02-16 17:47:13 -0800415 failString = []
Daniele Moroc6811a82021-10-12 11:29:41 +0200416 retValue = utilities.retry(f=self.__internalVerifyUp4Flow,
417 retValue=False,
418 args=[onosCli, failString],
419 sleep=5,
420 attempts=retries)
421 utilities.assert_equal(
422 expect=True,
423 actual=retValue,
Daniele Moro49a843c2022-01-05 14:36:32 +0100424 onpass="Correct UE session, terminations and GTP tunnel peers in ONOS",
Jon Halla601dde2022-02-28 14:22:07 -0800425 onfail="Wrong Application Filters, UE sessions, terminations and/or GTP tunnel peers in ONOS. " +
Daniele Morobef0c7e2022-02-16 17:47:13 -0800426 "Missing:\n" + '\n'.join(failString)
Daniele Moroc6811a82021-10-12 11:29:41 +0200427 )
428
Daniele Morobef0c7e2022-02-16 17:47:13 -0800429 def __internalVerifyUp4Flow(self, onosCli, failMsg=[]):
430 # Need to pass a list, so it's an object and we can use failMsg to
431 # return a string values from this method.
432
433 # Cleanup failMsg if any remaining from previous runs
434 del failMsg[:]
435 applications = onosCli.sendline(cmdStr="up4:read-entities -f",
436 showResponse=True,
437 noExit=True, expectJson=False)
Daniele Moro49a843c2022-01-05 14:36:32 +0100438 sessions = onosCli.sendline(cmdStr="up4:read-entities -s",
439 showResponse=True,
440 noExit=True, expectJson=False)
441 terminations = onosCli.sendline(cmdStr="up4:read-entities -t",
442 showResponse=True,
443 noExit=True, expectJson=False)
444 tunn_peer = onosCli.sendline(cmdStr="up4:read-entities -g",
445 showResponse=True,
446 noExit=True, expectJson=False)
Daniele Morobf53dec2021-09-13 18:11:56 +0200447 fail = False
Daniele Morobef0c7e2022-02-16 17:47:13 -0800448 for app_filter in self.app_filters.values():
449 if not UP4.__defaultApp(**app_filter):
450 if applications.count(self.appFilterOnosString(**app_filter)) != 1:
451 failMsg.append(self.appFilterOnosString(**app_filter))
452 fail = True
Daniele Moro522023c2021-10-15 17:30:33 +0200453 for (ue_name, ue) in self.emulated_ues.items():
Daniele Moro49a843c2022-01-05 14:36:32 +0100454 if sessions.count(self.upUeSessionOnosString(**ue)) != 1:
Daniele Morobef0c7e2022-02-16 17:47:13 -0800455 failMsg.append(self.upUeSessionOnosString(**ue))
Daniele Morobf53dec2021-09-13 18:11:56 +0200456 fail = True
Daniele Moro49a843c2022-01-05 14:36:32 +0100457 if sessions.count(self.downUeSessionOnosString(**ue)) != 1:
Daniele Morobef0c7e2022-02-16 17:47:13 -0800458 failMsg.append(self.downUeSessionOnosString(**ue))
Daniele Morobf53dec2021-09-13 18:11:56 +0200459 fail = True
Daniele Morobef0c7e2022-02-16 17:47:13 -0800460 for app_filter in self.app_filters.values():
461 if terminations.count(self.upTerminationOnosString(app_filter=app_filter, **ue)) != 1:
462 failMsg.append(self.upTerminationOnosString(app_filter=app_filter, **ue))
463 fail = True
464 if terminations.count(self.downTerminationOnosString(app_filter=app_filter, **ue)) != 1:
465 failMsg.append(self.downTerminationOnosString(app_filter=app_filter, **ue))
466 fail = True
Daniele Moro49a843c2022-01-05 14:36:32 +0100467 if tunn_peer.count(self.gtpTunnelPeerOnosString(ue_name, **ue)) != 1:
Daniele Morobef0c7e2022-02-16 17:47:13 -0800468 failMsg.append(self.gtpTunnelPeerOnosString(ue_name, **ue))
Daniele Morobf53dec2021-09-13 18:11:56 +0200469 fail = True
Daniele Moroc6811a82021-10-12 11:29:41 +0200470 return not fail
Daniele Morobf53dec2021-09-13 18:11:56 +0200471
Jon Halla601dde2022-02-28 14:22:07 -0800472 def appFilterOnosString(self, app_id, priority, slice_id=None, ip_proto=None, ip_prefix=None, port_range=None, **kwargs):
473 if slice_id is None:
474 slice_id = self.slice_id
475 matches = []
476 if ip_prefix:
477 matches.append("ip_prefix=%s" % ip_prefix)
478 if port_range:
479 matches.append("l4_port_range=[%s]" % port_range)
480 if ip_proto:
481 matches.append("ip_proto=%s" % ip_proto)
482 matches.append("slice_id=%s" % slice_id)
483
484 return "UpfApplication(priority=%s, Match(%s) -> Action(app_id=%s))" % (
Daniele Morobef0c7e2022-02-16 17:47:13 -0800485 priority,
Jon Halla601dde2022-02-28 14:22:07 -0800486 ", ".join(matches),
Daniele Morobef0c7e2022-02-16 17:47:13 -0800487 app_id
488 )
489
Daniele Moro4650ffb2022-02-15 22:44:18 +0100490 def upUeSessionOnosString(self, teid=None, teid_up=None, sess_meter_idx=DEFAULT_SESSION_METER_IDX, **kwargs):
Daniele Morobf53dec2021-09-13 18:11:56 +0200491 if teid is not None:
492 teid_up = teid
Daniele Moro4650ffb2022-02-15 22:44:18 +0100493 return "UpfSessionUL(Match(tun_dst_addr={}, teid={}) -> Action(FWD, session_meter_idx={}))".format(
494 self.s1u_address, teid_up, sess_meter_idx)
Daniele Morobf53dec2021-09-13 18:11:56 +0200495
Daniele Moro49a843c2022-01-05 14:36:32 +0100496 def downUeSessionOnosString(self, ue_address, down_id=None,
Daniele Morobef0c7e2022-02-16 17:47:13 -0800497 tunn_peer_id=None, sess_meter_idx=DEFAULT_SESSION_METER_IDX,
Daniele Moro49a843c2022-01-05 14:36:32 +0100498 **kwargs):
Daniele Morobf53dec2021-09-13 18:11:56 +0200499 if down_id is not None:
Daniele Moro49a843c2022-01-05 14:36:32 +0100500 tunn_peer_id = down_id
Daniele Moro4650ffb2022-02-15 22:44:18 +0100501 return "UpfSessionDL(Match(ue_addr={}) -> Action(FWD, tun_peer={}, session_meter_idx={}))".format(
502 ue_address, tunn_peer_id, sess_meter_idx)
Daniele Moro49a843c2022-01-05 14:36:32 +0100503
Daniele Morobef0c7e2022-02-16 17:47:13 -0800504 def upTerminationOnosString(self, ue_address, app_filter, up_id=None,
Daniele Moro4650ffb2022-02-15 22:44:18 +0100505 ctr_id_up=None, tc=None, app_meter_idx=DEFAULT_APP_METER_IDX, **kwargs):
Daniele Moro49a843c2022-01-05 14:36:32 +0100506 if up_id is not None:
507 ctr_id_up = up_id
Daniele Morobef0c7e2022-02-16 17:47:13 -0800508 if app_filter["action"] == "allow":
509 return "UpfTerminationUL(Match(ue_addr={}, app_id={}) -> Action(FWD, ctr_id={}, tc={}, app_meter_idx={}))".format(
510 ue_address, app_filter["app_id"], ctr_id_up, tc, app_meter_idx)
511 else:
512 return "UpfTerminationUL(Match(ue_addr={}, app_id={}) -> Action(DROP, ctr_id={}, tc=null, app_meter_idx=0))".format(
513 ue_address, app_filter["app_id"], ctr_id_up)
Daniele Moro49a843c2022-01-05 14:36:32 +0100514
Daniele Morobef0c7e2022-02-16 17:47:13 -0800515 def downTerminationOnosString(self, ue_address, app_filter, teid=None,
Daniele Moro1f7ca6f2022-01-27 11:58:50 +0100516 down_id=None, ctr_id_down=None, teid_down=None,
Daniele Moro4650ffb2022-02-15 22:44:18 +0100517 tc=None, app_meter_idx=DEFAULT_APP_METER_IDX,
518 **kwargs):
Daniele Moro49a843c2022-01-05 14:36:32 +0100519 if down_id is not None:
Daniele Morobf53dec2021-09-13 18:11:56 +0200520 ctr_id_down = down_id
Daniele Morobf53dec2021-09-13 18:11:56 +0200521 if teid is not None:
Daniele Morobef0c7e2022-02-16 17:47:13 -0800522 teid_down = int(teid) + 1
523 if tc is None:
524 tc="null"
525 if app_filter["action"] == "allow":
526 return "UpfTerminationDL(Match(ue_addr={}, app_id={}) -> Action(FWD, teid={}, ctr_id={}, qfi={}, tc={}, app_meter_idx={}))".format(
527 ue_address, app_filter["app_id"], teid_down, ctr_id_down, tc, tc, app_meter_idx)
528 else:
529 return "UpfTerminationDL(Match(ue_addr={}, app_id={}) -> Action(DROP, teid=null, ctr_id={}, qfi=null, tc=null, app_meter_idx=0))".format(
530 ue_address, app_filter["app_id"], ctr_id_down)
Daniele Morobf53dec2021-09-13 18:11:56 +0200531
Daniele Moro49a843c2022-01-05 14:36:32 +0100532 def gtpTunnelPeerOnosString(self, ue_name, down_id=None, tunn_peer_id=None,
533 **kwargs):
534 if down_id is not None:
535 tunn_peer_id = down_id
536 enb_address = self.__getEnbAddress(ue_name)
Daniele Moro1f7ca6f2022-01-27 11:58:50 +0100537 return "UpfGtpTunnelPeer(tunn_peer_id={} -> src={}, dst={} src_port={})".format(
Daniele Moro49a843c2022-01-05 14:36:32 +0100538 tunn_peer_id, self.s1u_address, enb_address, GPDU_PORT)
Daniele Morobf53dec2021-09-13 18:11:56 +0200539
Daniele Moro80889562021-09-08 10:09:26 +0200540 @staticmethod
541 def __sanitizeUeData(ue):
Daniele Moro249d6e72021-09-20 10:32:54 +0200542 if "five_g" in ue and type(ue["five_g"]) != bool:
Daniele Moro80889562021-09-08 10:09:26 +0200543 ue["five_g"] = bool(strtobool(ue["five_g"]))
Carmelo Cascone848d1f52022-01-27 18:15:58 -0800544 if "tc" in ue and ue["tc"] == "":
545 ue["tc"] = 0
Daniele Moro80889562021-09-08 10:09:26 +0200546 return ue
547
Daniele Morobef0c7e2022-02-16 17:47:13 -0800548 def insertAppFilter(self, **kwargs):
549 if not UP4.__defaultApp(**kwargs):
550 self.__programAppFilter(op="program", **kwargs)
551
552 def removeAppFilter(self, **kwargs):
553 if not UP4.__defaultApp(**kwargs):
554 self.__programAppFilter(op="clear", **kwargs)
555
Daniele Moro49a843c2022-01-05 14:36:32 +0100556 def attachUe(self, ue_name, ue_address,
Daniele Moro80889562021-09-08 10:09:26 +0200557 teid=None, up_id=None, down_id=None,
558 teid_up=None, teid_down=None,
Daniele Moro49a843c2022-01-05 14:36:32 +0100559 ctr_id_up=None, ctr_id_down=None,
560 tunn_peer_id=None,
Carmelo Cascone848d1f52022-01-27 18:15:58 -0800561 tc=None, five_g=False):
Daniele Morobef0c7e2022-02-16 17:47:13 -0800562 self.__programUeRules(ue_name,
563 ue_address,
564 teid, up_id, down_id,
565 teid_up, teid_down,
566 ctr_id_up, ctr_id_down,
567 tunn_peer_id,
568 tc, five_g, op="program")
Daniele Moro80889562021-09-08 10:09:26 +0200569
Daniele Moro49a843c2022-01-05 14:36:32 +0100570 def detachUe(self, ue_name, ue_address,
Daniele Moro80889562021-09-08 10:09:26 +0200571 teid=None, up_id=None, down_id=None,
572 teid_up=None, teid_down=None,
Daniele Moro49a843c2022-01-05 14:36:32 +0100573 ctr_id_up=None, ctr_id_down=None,
574 tunn_peer_id=None,
Carmelo Cascone848d1f52022-01-27 18:15:58 -0800575 tc=None, five_g=False):
Daniele Morobef0c7e2022-02-16 17:47:13 -0800576 self.__programUeRules(ue_name,
577 ue_address,
578 teid, up_id, down_id,
579 teid_up, teid_down,
580 ctr_id_up, ctr_id_down,
581 tunn_peer_id,
582 tc, five_g, op="clear")
Daniele Moro80889562021-09-08 10:09:26 +0200583
Jon Halla601dde2022-02-28 14:22:07 -0800584 def __programAppFilter(self, app_id, slice_id, ip_prefix=None, ip_proto=None,
Daniele Morobef0c7e2022-02-16 17:47:13 -0800585 port_range=None, priority=0, op="program", **kwargs):
586
587 entries = []
588
589 tableName = 'PreQosPipe.applications'
590 actionName = 'PreQosPipe.set_app_id'
591 actionParams = {'app_id': str(app_id)}
Daniele Morocc4ecda2022-02-25 23:27:25 +0100592 matchFields = {
Jon Halla601dde2022-02-28 14:22:07 -0800593 'slice_id': str(slice_id)
Daniele Morocc4ecda2022-02-25 23:27:25 +0100594 }
Daniele Morobef0c7e2022-02-16 17:47:13 -0800595 if ip_prefix:
596 matchFields['app_ip_addr'] = str(ip_prefix)
597 if ip_proto:
598 matchFields['app_ip_proto'] = str(ip_proto)
599 if port_range:
600 matchFields['app_l4_port'] = str(port_range)
601
602 if not self.__add_entry(tableName, actionName, matchFields,
603 actionParams, entries, op, priority):
604 return False
605
606 if op == "program":
607 main.log.info("Application entry added successfully.")
608 elif op == "clear":
609 self.__clear_entries(entries)
610
611 def __programUeRules(self, ue_name, ue_address,
612 teid=None, up_id=None, down_id=None,
613 teid_up=None, teid_down=None, ctr_id_up=None,
614 ctr_id_down=None, tunn_peer_id=None,
615 tc=0, five_g=False,
616 op="program"):
Daniele Moro80889562021-09-08 10:09:26 +0200617 if up_id is not None:
Daniele Moro80889562021-09-08 10:09:26 +0200618 ctr_id_up = up_id
619 if down_id is not None:
Daniele Moro49a843c2022-01-05 14:36:32 +0100620 tunn_peer_id = down_id
Daniele Moro80889562021-09-08 10:09:26 +0200621 ctr_id_down = down_id
622 if teid is not None:
623 teid_up = teid
Daniele Morobef0c7e2022-02-16 17:47:13 -0800624 teid_down = int(teid) + 1
Daniele Moro80889562021-09-08 10:09:26 +0200625
626 entries = []
627
Daniele Moro522023c2021-10-15 17:30:33 +0200628 # Retrieve eNobeB address from eNodeB list
629 enb_address = self.__getEnbAddress(ue_name)
630
Daniele Moro80889562021-09-08 10:09:26 +0200631 # ========================#
Daniele Moro49a843c2022-01-05 14:36:32 +0100632 # UE Session Entries
Daniele Moro80889562021-09-08 10:09:26 +0200633 # ========================#
634
635 # Uplink
Daniele Moro49a843c2022-01-05 14:36:32 +0100636 tableName = 'PreQosPipe.sessions_uplink'
637 actionName = 'PreQosPipe.set_session_uplink'
Daniele Moro80889562021-09-08 10:09:26 +0200638 matchFields = {}
Daniele Morobef0c7e2022-02-16 17:47:13 -0800639 actionParams = {}
Daniele Moro80889562021-09-08 10:09:26 +0200640 # Match fields
Daniele Moro49a843c2022-01-05 14:36:32 +0100641 matchFields['n3_address'] = str(self.s1u_address)
Daniele Moro80889562021-09-08 10:09:26 +0200642 matchFields['teid'] = str(teid_up)
Daniele Moro4650ffb2022-02-15 22:44:18 +0100643 # Action params
644 actionParams["session_meter_idx"] = str(DEFAULT_SESSION_METER_IDX)
Daniele Moro49a843c2022-01-05 14:36:32 +0100645 if five_g:
646 # TODO: currently QFI match is unsupported in TNA
647 main.log.warn("Matching on QFI is currently unsupported in TNA")
Daniele Moro80889562021-09-08 10:09:26 +0200648 if not self.__add_entry(tableName, actionName, matchFields,
Daniele Morobef0c7e2022-02-16 17:47:13 -0800649 actionParams, entries, op):
Daniele Moro80889562021-09-08 10:09:26 +0200650 return False
651
652 # Downlink
Daniele Moro49a843c2022-01-05 14:36:32 +0100653 tableName = 'PreQosPipe.sessions_downlink'
654 actionName = 'PreQosPipe.set_session_downlink'
Daniele Moro80889562021-09-08 10:09:26 +0200655 matchFields = {}
656 actionParams = {}
Daniele Moro80889562021-09-08 10:09:26 +0200657 # Match fields
Daniele Moro49a843c2022-01-05 14:36:32 +0100658 matchFields['ue_address'] = str(ue_address)
Daniele Moro80889562021-09-08 10:09:26 +0200659 # Action params
Daniele Moro49a843c2022-01-05 14:36:32 +0100660 actionParams['tunnel_peer_id'] = str(tunn_peer_id)
Daniele Moro4650ffb2022-02-15 22:44:18 +0100661 actionParams["session_meter_idx"] = str(DEFAULT_SESSION_METER_IDX)
Daniele Moro80889562021-09-08 10:09:26 +0200662 if not self.__add_entry(tableName, actionName, matchFields,
Daniele Morobef0c7e2022-02-16 17:47:13 -0800663 actionParams, entries, op):
Daniele Moro80889562021-09-08 10:09:26 +0200664 return False
665
666 # ========================#
Daniele Moro49a843c2022-01-05 14:36:32 +0100667 # Terminations Entries
Daniele Moro80889562021-09-08 10:09:26 +0200668 # ========================#
669
Daniele Morobef0c7e2022-02-16 17:47:13 -0800670 # Insert one termination entry per app filtering rule,
Daniele Moro80889562021-09-08 10:09:26 +0200671
Daniele Morobef0c7e2022-02-16 17:47:13 -0800672 # Uplink
673 for f in self.app_filters.values():
674 tableName = 'PreQosPipe.terminations_uplink'
675 matchFields = {}
676 actionParams = {}
677
678 # Match fields
679 matchFields['ue_address'] = str(ue_address)
680 matchFields['app_id'] = str(f["app_id"])
681
682 # Action params
683 if f['action'] == 'allow':
684 actionName = 'PreQosPipe.uplink_term_fwd'
685 actionParams['app_meter_idx'] = str(DEFAULT_APP_METER_IDX)
686 actionParams['tc'] = str(tc)
687 else:
688 actionName = 'PreQosPipe.uplink_term_drop'
689 actionParams['ctr_idx'] = str(ctr_id_up)
690 if not self.__add_entry(
691 tableName, actionName, matchFields, actionParams, entries, op
692 ):
693 return False
Daniele Moro80889562021-09-08 10:09:26 +0200694
695 # Downlink
Daniele Morobef0c7e2022-02-16 17:47:13 -0800696 for f in self.app_filters.values():
697 tableName = 'PreQosPipe.terminations_downlink'
698 matchFields = {}
699 actionParams = {}
Daniele Moro80889562021-09-08 10:09:26 +0200700
Daniele Morobef0c7e2022-02-16 17:47:13 -0800701 # Match fields
702 matchFields['ue_address'] = str(ue_address)
703 matchFields['app_id'] = str(f["app_id"])
704
705 # Action params
706 if f['action'] == 'allow':
707 actionName = 'PreQosPipe.downlink_term_fwd'
708 actionParams['teid'] = str(teid_down)
709 # 1-1 mapping between QFI and TC
710 actionParams['tc'] = str(tc)
711 actionParams['qfi'] = str(tc)
712 actionParams['app_meter_idx'] = str(DEFAULT_APP_METER_IDX)
713 else:
714 actionName = 'PreQosPipe.downlink_term_drop'
715 actionParams['ctr_idx'] = str(ctr_id_down)
716
717 if not self.__add_entry(tableName, actionName, matchFields,
718 actionParams, entries, op):
719 return False
Daniele Moro49a843c2022-01-05 14:36:32 +0100720
721 # ========================#
722 # Tunnel Peer Entry
723 # ========================#
724 tableName = 'PreQosPipe.tunnel_peers'
725 actionName = 'PreQosPipe.load_tunnel_param'
726 matchFields = {}
727 actionParams = {}
728 # Match fields
729 matchFields['tunnel_peer_id'] = str(tunn_peer_id)
730 # Action params
Daniele Moro80889562021-09-08 10:09:26 +0200731 actionParams['src_addr'] = str(self.s1u_address)
Daniele Moro522023c2021-10-15 17:30:33 +0200732 actionParams['dst_addr'] = str(enb_address)
Daniele Moro80889562021-09-08 10:09:26 +0200733 actionParams['sport'] = TUNNEL_SPORT
734 if not self.__add_entry(tableName, actionName, matchFields,
Daniele Morobef0c7e2022-02-16 17:47:13 -0800735 actionParams, entries, op):
Daniele Moro80889562021-09-08 10:09:26 +0200736 return False
Daniele Morobef0c7e2022-02-16 17:47:13 -0800737 if op == "program":
Daniele Moro80889562021-09-08 10:09:26 +0200738 main.log.info("All entries added successfully.")
Daniele Morobef0c7e2022-02-16 17:47:13 -0800739 elif op == "clear":
Daniele Moro80889562021-09-08 10:09:26 +0200740 self.__clear_entries(entries)
741
742 def __add_entry(self, tableName, actionName, matchFields, actionParams,
Daniele Morobef0c7e2022-02-16 17:47:13 -0800743 entries, op, priority=0):
744 if op == "program":
Daniele Moro80889562021-09-08 10:09:26 +0200745 self.up4_client.buildP4RtTableEntry(
746 tableName=tableName, actionName=actionName,
Daniele Morobef0c7e2022-02-16 17:47:13 -0800747 actionParams=actionParams, matchFields=matchFields, priority=priority)
Daniele Moro80889562021-09-08 10:09:26 +0200748 if self.up4_client.pushTableEntry(debug=True) == main.TRUE:
749 main.log.info("*** Entry added.")
750 else:
751 main.log.error("Error during table insertion")
752 self.__clear_entries(entries)
753 return False
Daniele Morobef0c7e2022-02-16 17:47:13 -0800754 entries.append({
755 "tableName": tableName, "actionName": actionName,
756 "matchFields": matchFields,
757 "actionParams": actionParams,
758 "priority": priority
759 })
Daniele Moro80889562021-09-08 10:09:26 +0200760 return True
761
762 def __clear_entries(self, entries):
763 for i, entry in enumerate(entries):
764 self.up4_client.buildP4RtTableEntry(**entry)
765 if self.up4_client.deleteTableEntry(debug=True) == main.TRUE:
766 main.log.info(
767 "*** Entry %d of %d deleted." % (i + 1, len(entries)))
768 else:
769 main.log.error("Error during table delete")
Daniele Morobf53dec2021-09-13 18:11:56 +0200770
Daniele Moro522023c2021-10-15 17:30:33 +0200771 def __getEnbAddress(self, ue_name):
772 for enb in self.enodebs.values():
773 if ue_name in enb["ues"]:
774 return enb["enb_address"]
775 main.log.error("Missing eNodeB address!")
776 return ""
777
Daniele Morobf53dec2021-09-13 18:11:56 +0200778 @staticmethod
779 def buildGtpPacket(host, src_ip_outer, dst_ip_outer, src_ip_inner,
780 dst_ip_inner, src_udp_inner, dst_udp_inner, teid):
781 host.buildEther()
782 host.buildIP(src=src_ip_outer, dst=dst_ip_outer)
783 host.buildUDP(ipVersion=4, dport=GPDU_PORT)
784 # FIXME: With newer scapy TEID becomes teid (required for Scapy 2.4.5)
785 host.buildGTP(gtp_type=0xFF, TEID=teid)
786 host.buildIP(overGtp=True, src=src_ip_inner, dst=dst_ip_inner)
787 host.buildUDP(ipVersion=4, overGtp=True, sport=src_udp_inner,
788 dport=dst_udp_inner)
789
790 @staticmethod
791 def buildUdpPacket(host, src_ip, dst_ip, src_udp, dst_udp, src_eth=None,
792 dst_eth=None):
793 host.buildEther(src=src_eth, dst=dst_eth)
794 host.buildIP(src=src_ip, dst=dst_ip)
795 host.buildUDP(ipVersion=4, sport=src_udp, dport=dst_udp)
796
797 @staticmethod
798 def checkFilterAndGetPackets(host):
799 finished = host.checkFilter()
800 if finished:
801 packets = host.readPackets(detailed=True)
802 for p in packets.splitlines():
803 main.log.debug(p)
804 # We care only of the last line from readPackets
805 return packets.splitlines()[-1]
806 else:
807 kill = host.killFilter()
808 main.log.debug(kill)
809 return ""
Daniele Morobef0c7e2022-02-16 17:47:13 -0800810
811 @staticmethod
812 def __defaultApp(ip_prefix=None, ip_proto=None, port_range=None, **kwargs):
813 if ip_prefix is None and ip_proto is None and port_range is None:
814 return True
815 return False
816