blob: a4505d796be0ec8c0c8f6da1ab041859c880bc8b [file] [log] [blame]
Daniele Moro80889562021-09-08 10:09:26 +02001from distutils.util import strtobool
2
3FALSE = '0'
4TRUE = '1'
5DIR_UPLINK = '1'
6DIR_DOWNLINK = '2'
7IFACE_ACCESS = '1'
8IFACE_CORE = '2'
9TUNNEL_SPORT = '2152'
10TUNNEL_TYPE_GPDU = '3'
11
12UE_PORT = 400
13PDN_PORT = 800
14GPDU_PORT = 2152
15
16
17class UP4:
18 """
19 Utility that manages interaction with UP4 via a P4RuntimeCliDriver available
20 in the cluster. Additionally, can verify connectivity by crafting GTP packets
21 via Scapy with an HostDriver component, specified via <enodeb_host>, <pdn_host>,
22 and <router_mac> parameters.
23
24 Example params file:
25 <UP4>
26 <pdn_host>Compute1</pdn_host> # Needed to verify connectivity with scapy
27 <enodeb_host>Compute3</enodeb_host> # Needed to verify connectivity with scapy
28 <router_mac>00:00:0A:4C:1C:46</router_mac> # Needed to verify connectivity with scapy
29 <s1u_address>10.32.11.126</s1u_address>
30 <enb_address>10.32.11.100</enb_address>
31 <ues>
32 <ue2>
33 <pfcp_session_id>100</pfcp_session_id>
34 <ue_address>10.240.0.2</ue_address>
35 <teid>200</teid>
36 <up_id>20</up_id>
37 <down_id>21</down_id>
38 <qfi>2</qfi>
39 <five_g>False</five_g>
40 </ue2>
41 </ues>
42 </UP4>
43 """
44
45 def __init__(self):
46 self.s1u_address = None
47 self.enb_address = None
48 self.enodeb_host = None
49 self.enodeb_interface = None
50 self.pdn_host = None
51 self.pdn_interface = None
52 self.router_mac = None
53 self.emulated_ues = []
54 self.up4_client = None
55
Daniele Moro954e2282021-09-22 17:32:03 +020056 def setup(self, p4rt_client, no_host=False):
57 """
58 Set up P4RT and scapy on eNB and PDN hosts
59 :param p4rt_client: a P4RuntimeCliDriver component
60 :param no_host: True if you don't want to start scapy on the hosts
61 :return:
62 """
Daniele Moro80889562021-09-08 10:09:26 +020063 self.s1u_address = main.params["UP4"]["s1u_address"]
64 self.enb_address = main.params["UP4"]["enb_address"]
65 self.emulated_ues = main.params["UP4"]['ues']
66 self.up4_client = p4rt_client
67
68 # Optional Parameters
Daniele Moro954e2282021-09-22 17:32:03 +020069 if not no_host:
70 if "enodeb_host" in main.params["UP4"]:
71 self.enodeb_host = getattr(main,
72 main.params["UP4"]["enodeb_host"])
73 self.enodeb_interface = self.enodeb_host.interfaces[0]
74 if "pdn_host" in main.params["UP4"]:
75 self.pdn_host = getattr(main, main.params["UP4"]["pdn_host"])
76 self.pdn_interface = self.pdn_host.interfaces[0]
77 self.router_mac = main.params["UP4"].get("router_mac", None)
Daniele Moro80889562021-09-08 10:09:26 +020078
79 # Start components
80 self.up4_client.startP4RtClient()
81 if self.enodeb_host is not None:
82 self.enodeb_host.startScapy(ifaceName=self.enodeb_interface["name"],
83 enableGtp=True)
84 if self.pdn_host is not None:
85 self.pdn_host.startScapy(ifaceName=self.pdn_interface["name"])
86
87 def teardown(self):
88 self.up4_client.stopP4RtClient()
89 if self.enodeb_host is not None:
90 self.enodeb_host.stopScapy()
91 if self.pdn_host is not None:
92 self.pdn_host.stopScapy()
93
94 def attachUes(self):
95 for ue in self.emulated_ues.values():
Daniele Moro80889562021-09-08 10:09:26 +020096 ue = UP4.__sanitizeUeData(ue)
97 self.attachUe(**ue)
98
99 def detachUes(self):
100 for ue in self.emulated_ues.values():
Daniele Moro249d6e72021-09-20 10:32:54 +0200101 ue = UP4.__sanitizeUeData(ue)
Daniele Moro80889562021-09-08 10:09:26 +0200102 self.detachUe(**ue)
103
104 def testUpstreamTraffic(self):
105 if self.enodeb_host is None or self.pdn_host is None:
106 main.log.error(
107 "Need eNodeB and PDN host params to generate scapy traffic")
108 return
109 # Scapy filter needs to start before sending traffic
110 pkt_filter_upstream = ""
111 for ue in self.emulated_ues.values():
112 if "ue_address" in ue:
113 if len(pkt_filter_upstream) != 0:
114 pkt_filter_upstream += " or "
115 pkt_filter_upstream += "src host " + ue["ue_address"]
116 pkt_filter_upstream = "ip and udp dst port %s and (%s) and dst host %s" % \
117 (PDN_PORT, pkt_filter_upstream,
118 self.pdn_interface["ips"][0])
119 main.log.info("Start listening on %s intf %s" %
120 (self.pdn_host.name, self.pdn_interface["name"]))
121 main.log.debug("BPF Filter Upstream: \n %s" % pkt_filter_upstream)
122 self.pdn_host.startFilter(ifaceName=self.pdn_interface["name"],
123 sniffCount=len(self.emulated_ues),
124 pktFilter=pkt_filter_upstream)
125
126 main.log.info(
127 "Sending %d packets from eNodeB host" % len(self.emulated_ues))
128 for ue in self.emulated_ues.values():
Daniele Morobf53dec2021-09-13 18:11:56 +0200129 UP4.buildGtpPacket(self.enodeb_host,
130 src_ip_outer=self.enb_address,
131 dst_ip_outer=self.s1u_address,
132 src_ip_inner=ue["ue_address"],
133 dst_ip_inner=self.pdn_interface["ips"][0],
134 src_udp_inner=UE_PORT,
135 dst_udp_inner=PDN_PORT,
136 teid=int(ue["teid"]))
Daniele Moro80889562021-09-08 10:09:26 +0200137
138 self.enodeb_host.sendPacket(iface=self.enodeb_interface["name"])
139
Daniele Morobf53dec2021-09-13 18:11:56 +0200140 packets = UP4.checkFilterAndGetPackets(self.pdn_host)
Daniele Moro80889562021-09-08 10:09:26 +0200141 fail = False
142 if len(self.emulated_ues) != packets.count('Ether'):
143 fail = True
144 msg = "Failed to capture packets in PDN. "
145 else:
146 msg = "Correctly captured packet in PDN. "
147 # We expect exactly 1 packet per UE
148 pktsFiltered = [packets.count("src=" + ue["ue_address"])
149 for ue in self.emulated_ues.values()]
150 if pktsFiltered.count(1) != len(pktsFiltered):
151 fail = True
152 msg += "More than one packet per UE in downstream. "
153 else:
154 msg += "One packet per UE in upstream. "
155
156 utilities.assert_equal(
157 expect=False, actual=fail, onpass=msg, onfail=msg)
158
159 def testDownstreamTraffic(self):
160 if self.enodeb_host is None or self.pdn_host is None:
161 main.log.error(
162 "Need eNodeB and PDN host params to generate scapy traffic")
163 return
164 pkt_filter_downstream = "ip and udp src port %d and udp dst port %d and dst host %s and src host %s" % (
165 GPDU_PORT, GPDU_PORT, self.enb_address, self.s1u_address)
166 main.log.info("Start listening on %s intf %s" % (
167 self.enodeb_host.name, self.enodeb_interface["name"]))
168 main.log.debug("BPF Filter Downstream: \n %s" % pkt_filter_downstream)
169 self.enodeb_host.startFilter(ifaceName=self.enodeb_interface["name"],
170 sniffCount=len(self.emulated_ues),
171 pktFilter=pkt_filter_downstream)
172
173 main.log.info(
174 "Sending %d packets from PDN host" % len(self.emulated_ues))
175 for ue in self.emulated_ues.values():
176 # From PDN we have to set dest MAC, otherwise scapy will do ARP
177 # request for the UE IP address.
Daniele Morobf53dec2021-09-13 18:11:56 +0200178 UP4.buildUdpPacket(self.pdn_host,
179 dst_eth=self.router_mac,
180 src_ip=self.pdn_interface["ips"][0],
181 dst_ip=ue["ue_address"],
182 src_udp=PDN_PORT,
183 dst_udp=UE_PORT)
Daniele Moro80889562021-09-08 10:09:26 +0200184 self.pdn_host.sendPacket(iface=self.pdn_interface["name"])
185
Daniele Morobf53dec2021-09-13 18:11:56 +0200186 packets = UP4.checkFilterAndGetPackets(self.enodeb_host)
Daniele Moro80889562021-09-08 10:09:26 +0200187
188 # The BPF filter might capture non-GTP packets because we can't filter
189 # GTP header in BPF. For this reason, check that the captured packets
190 # are from the expected tunnels.
191 # TODO: check inner UDP and IP fields as well
192 # FIXME: with newer scapy TEID becomes teid (required for Scapy 2.4.5)
193 pktsFiltered = [packets.count("TEID=" + hex(int(ue["teid"])) + "L ")
194 for ue in self.emulated_ues.values()]
195
196 fail = False
197 if len(self.emulated_ues) != sum(pktsFiltered):
198 fail = True
199 msg = "Failed to capture packets in eNodeB. "
200 else:
201 msg = "Correctly captured packets in eNodeB. "
202 # We expect exactly 1 packet per UE
203 if pktsFiltered.count(1) != len(pktsFiltered):
204 fail = True
205 msg += "More than one packet per GTP TEID in downstream. "
206 else:
207 msg += "One packet per GTP TEID in downstream. "
208
209 utilities.assert_equal(
210 expect=False, actual=fail, onpass=msg, onfail=msg)
211
Daniele Moro954e2282021-09-22 17:32:03 +0200212 def readPdrsNumber(self):
213 """
214 Read the PDRs table and return the number of entries in the PDRs table
215
216 :return: Number of entries in the PDRs table
217 """
218 tableName = 'PreQosPipe.pdrs'
219 return self.up4_client.readNumberTableEntries(tableName)
220
221 def readFarsNumber(self):
222 """
223 Read the FARs table and return the number of entries in the FARs table
224
225 :return: Number of entries in the FARs table
226 """
227 tableName = 'PreQosPipe.load_far_attributes'
228 return self.up4_client.readNumberTableEntries(tableName)
229
230 def verifyUesFlowNumberP4rt(self):
231 """
232 Verify via P4RT CLI that the number of PDRs and FARs is the expected one
233
234 :return: True if the number of PDRs and FARs is expected, False otherwise
235 """
236 nPdrs = self.readPdrsNumber()
237 nFars = self.readFarsNumber()
238 return nPdrs == nFars == len(self.emulated_ues) * 2
239
240 def verifyNoUesFlowNumberP4rt(self, preInstalledUes=0):
241 """
242 Verify via P4RT CLI that there is no PDRs and FARs installed.
243
244 :param preInstalledUes: Number of UEs whose PDRs and FARs are still programmed
245 :return:
246 """
247 return self.readPdrsNumber() == self.readFarsNumber() == preInstalledUes * 2
248
Daniele Moro6dfbfef2021-09-28 22:44:19 +0200249 def verifyNoUesFlow(self, onosCli, retries=10):
Daniele Morobf53dec2021-09-13 18:11:56 +0200250 """
251 Verify that no PDRs and FARs are installed in ONOS.
252
253 :param onosCli: An instance of a OnosCliDriver
254 :param retries: number of retries
255 :return:
256 """
257 retValue = utilities.retry(f=UP4.__verifyNoPdrsFarsOnos,
258 retValue=False,
259 args=[onosCli],
Daniele Moro6dfbfef2021-09-28 22:44:19 +0200260 sleep=5,
Daniele Morobf53dec2021-09-13 18:11:56 +0200261 attempts=retries)
262 utilities.assert_equal(expect=True,
263 actual=retValue,
264 onpass="No PDRs and FARs in ONOS",
265 onfail="Stale PDRs or FARs")
266
267 @staticmethod
268 def __verifyNoPdrsFarsOnos(onosCli):
269 """
270 Verify that no PDRs and FARs are installed in ONOS
271
272 :param onosCli: An instance of a OnosCliDriver
273 """
274 pdrs = onosCli.sendline(cmdStr="up4:read-pdrs", showResponse=True,
275 noExit=True, expectJson=False)
276 fars = onosCli.sendline(cmdStr="up4:read-fars", showResponse=True,
277 noExit=True, expectJson=False)
278 return pdrs == "" and fars == ""
279
280 def verifyUp4Flow(self, onosCli):
281 """
282 Verify PDRs and FARs installed via UP4 using the ONOS CLI.
283
284 :param onosCli: An instance of a OnosCliDriver
285 """
286 pdrs = onosCli.sendline(cmdStr="up4:read-pdrs", showResponse=True,
287 noExit=True, expectJson=False)
288 fars = onosCli.sendline(cmdStr="up4:read-fars", showResponse=True,
289 noExit=True, expectJson=False)
290 fail = False
291 failMsg = ""
292 for ue in self.emulated_ues.values():
293 if pdrs.count(self.upPdrOnosString(**ue)) != 1:
294 failMsg += self.upPdrOnosString(**ue) + "\n"
295 fail = True
296 if pdrs.count(self.downPdrOnosString(**ue)) != 1:
297 failMsg += self.downPdrOnosString(**ue) + "\n"
298 fail = True
299 if fars.count(self.upFarOnosString(**ue)) != 1:
300 failMsg += self.upFarOnosString(**ue) + "\n"
301 fail = True
302 if fars.count(self.downFarOnosString(**ue)) != 1:
303 failMsg += self.downFarOnosString(**ue) + "\n"
304 fail = True
305 utilities.assert_equal(expect=False, actual=fail,
306 onpass="Correct PDRs and FARs in ONOS",
307 onfail="Wrong PDRs and FARs in ONOS. Missing PDR/FAR:\n" + failMsg)
308
309 def upPdrOnosString(self, pfcp_session_id, teid=None, up_id=None,
310 teid_up=None, far_id_up=None, ctr_id_up=None, qfi=None,
311 **kwargs):
312 # TODO: consider that with five_g the output might be different
313 if up_id is not None:
314 far_id_up = up_id
315 ctr_id_up = up_id
316 if teid is not None:
317 teid_up = teid
318 if qfi is not None:
319 return "PDR{{Match(Dst={}, TEID={}) -> LoadParams(SEID={}, FAR={}, CtrIdx={}, QFI={})}}".format(
320 self.s1u_address, hex(int(teid_up)), hex(int(pfcp_session_id)),
Daniele Moro3e4cf3b2021-09-21 18:53:22 +0200321 far_id_up, ctr_id_up, qfi)
Daniele Morobf53dec2021-09-13 18:11:56 +0200322 return "PDR{{Match(Dst={}, TEID={}) -> LoadParams(SEID={}, FAR={}, CtrIdx={})}}".format(
323 self.s1u_address, hex(int(teid_up)), hex(int(pfcp_session_id)),
324 far_id_up, ctr_id_up)
325
326 def downPdrOnosString(self, pfcp_session_id, ue_address, down_id=None,
Daniele Moro3e4cf3b2021-09-21 18:53:22 +0200327 far_id_down=None, ctr_id_down=None, qfi=None,
328 **kwargs):
Daniele Morobf53dec2021-09-13 18:11:56 +0200329 # TODO: consider that with five_g the output might be different
330 if down_id is not None:
331 far_id_down = down_id
332 ctr_id_down = down_id
Daniele Moro3e4cf3b2021-09-21 18:53:22 +0200333 if qfi is not None:
334 return "PDR{{Match(Dst={}, !GTP) -> LoadParams(SEID={}, FAR={}, CtrIdx={}, QFI={})}}".format(
335 ue_address, hex(int(pfcp_session_id)), far_id_down, ctr_id_down,
336 qfi)
Daniele Morobf53dec2021-09-13 18:11:56 +0200337 return "PDR{{Match(Dst={}, !GTP) -> LoadParams(SEID={}, FAR={}, CtrIdx={})}}".format(
338 ue_address, hex(int(pfcp_session_id)), far_id_down, ctr_id_down)
339
340 def downFarOnosString(self, pfcp_session_id, teid=None, down_id=None,
341 teid_down=None, far_id_down=None, **kwargs):
342 if down_id is not None:
343 far_id_down = down_id
344 if teid is not None:
345 teid_down = teid
346 return "FAR{{Match(ID={}, SEID={}) -> Encap(Src={}, SPort={}, TEID={}, Dst={})}}".format(
347 far_id_down, hex(int(pfcp_session_id)), self.s1u_address, GPDU_PORT,
348 hex(int(teid_down)),
349 self.enb_address)
350
351 def upFarOnosString(self, pfcp_session_id, up_id=None, far_id_up=None,
352 **kwargs):
353 if up_id is not None:
354 far_id_up = up_id
355 return "FAR{{Match(ID={}, SEID={}) -> Forward()}}".format(
356 far_id_up, hex(int(pfcp_session_id)))
357
Daniele Moro80889562021-09-08 10:09:26 +0200358 @staticmethod
359 def __sanitizeUeData(ue):
Daniele Moro249d6e72021-09-20 10:32:54 +0200360 if "five_g" in ue and type(ue["five_g"]) != bool:
Daniele Moro80889562021-09-08 10:09:26 +0200361 ue["five_g"] = bool(strtobool(ue["five_g"]))
362 if "qfi" in ue and ue["qfi"] == "":
363 ue["qfi"] = None
364 return ue
365
366 def attachUe(self, pfcp_session_id, ue_address,
367 teid=None, up_id=None, down_id=None,
368 teid_up=None, teid_down=None,
369 pdr_id_up=None, far_id_up=None, ctr_id_up=None,
370 pdr_id_down=None, far_id_down=None, ctr_id_down=None,
371 qfi=None, five_g=False):
372 self.__programUp4Rules(pfcp_session_id,
373 ue_address,
374 teid, up_id, down_id,
375 teid_up, teid_down,
376 pdr_id_up, far_id_up, ctr_id_up,
377 pdr_id_down, far_id_down, ctr_id_down,
378 qfi, five_g, action="program")
379
380 def detachUe(self, pfcp_session_id, ue_address,
381 teid=None, up_id=None, down_id=None,
382 teid_up=None, teid_down=None,
383 pdr_id_up=None, far_id_up=None, ctr_id_up=None,
384 pdr_id_down=None, far_id_down=None, ctr_id_down=None,
385 qfi=None, five_g=False):
386 self.__programUp4Rules(pfcp_session_id,
387 ue_address,
388 teid, up_id, down_id,
389 teid_up, teid_down,
390 pdr_id_up, far_id_up, ctr_id_up,
391 pdr_id_down, far_id_down, ctr_id_down,
392 qfi, five_g, action="clear")
393
394 def __programUp4Rules(self, pfcp_session_id, ue_address,
395 teid=None, up_id=None, down_id=None,
396 teid_up=None, teid_down=None,
397 pdr_id_up=None, far_id_up=None, ctr_id_up=None,
398 pdr_id_down=None, far_id_down=None, ctr_id_down=None,
399 qfi=None, five_g=False, action="program"):
400 if up_id is not None:
401 pdr_id_up = up_id
402 far_id_up = up_id
403 ctr_id_up = up_id
404 if down_id is not None:
405 pdr_id_down = down_id
406 far_id_down = down_id
407 ctr_id_down = down_id
408 if teid is not None:
409 teid_up = teid
410 teid_down = teid
411
412 entries = []
413
414 # ========================#
415 # PDR Entries
416 # ========================#
417
418 # Uplink
419 tableName = 'PreQosPipe.pdrs'
420 actionName = ''
421 matchFields = {}
422 actionParams = {}
423 if qfi is None:
424 actionName = 'PreQosPipe.set_pdr_attributes'
425 else:
426 actionName = 'PreQosPipe.set_pdr_attributes_qos'
427 if five_g:
428 # TODO: currently QFI_MATCH is unsupported in TNA
429 matchFields['has_qfi'] = TRUE
430 matchFields["qfi"] = str(qfi)
431 actionParams['needs_qfi_push'] = FALSE
432 actionParams['qfi'] = str(qfi)
433 # Match fields
434 matchFields['src_iface'] = IFACE_ACCESS
435 matchFields['ue_addr'] = str(ue_address)
436 matchFields['teid'] = str(teid_up)
437 matchFields['tunnel_ipv4_dst'] = str(self.s1u_address)
438 # Action params
439 actionParams['id'] = str(pdr_id_up)
440 actionParams['fseid'] = str(pfcp_session_id)
441 actionParams['ctr_id'] = str(ctr_id_up)
442 actionParams['far_id'] = str(far_id_up)
443 actionParams['needs_gtpu_decap'] = TRUE
444 if not self.__add_entry(tableName, actionName, matchFields,
445 actionParams, entries, action):
446 return False
447
448 # Downlink
449 tableName = 'PreQosPipe.pdrs'
450 matchFields = {}
451 actionParams = {}
452 if qfi is None:
453 actionName = 'PreQosPipe.set_pdr_attributes'
454 else:
455 actionName = 'PreQosPipe.set_pdr_attributes_qos'
456 # TODO: currently QFI_PUSH is unsupported in TNA
457 actionParams['needs_qfi_push'] = TRUE if five_g else FALSE
458 actionParams['qfi'] = str(qfi)
459 # Match fields
460 matchFields['src_iface'] = IFACE_CORE
461 matchFields['ue_addr'] = str(ue_address)
462 # Action params
463 actionParams['id'] = str(pdr_id_down)
464 actionParams['fseid'] = str(pfcp_session_id)
465 actionParams['ctr_id'] = str(ctr_id_down)
466 actionParams['far_id'] = str(far_id_down)
467 actionParams['needs_gtpu_decap'] = FALSE
468 if not self.__add_entry(tableName, actionName, matchFields,
469 actionParams, entries, action):
470 return False
471
472 # ========================#
473 # FAR Entries
474 # ========================#
475
476 # Uplink
477 tableName = 'PreQosPipe.load_far_attributes'
478 actionName = 'PreQosPipe.load_normal_far_attributes'
479 matchFields = {}
480 actionParams = {}
481
482 # Match fields
483 matchFields['far_id'] = str(far_id_up)
484 matchFields['session_id'] = str(pfcp_session_id)
485 # Action params
486 actionParams['needs_dropping'] = FALSE
487 actionParams['notify_cp'] = FALSE
488 if not self.__add_entry(tableName, actionName, matchFields,
489 actionParams, entries, action):
490 return False
491
492 # Downlink
493 tableName = 'PreQosPipe.load_far_attributes'
494 actionName = 'PreQosPipe.load_tunnel_far_attributes'
495 matchFields = {}
496 actionParams = {}
497
498 # Match fields
499 matchFields['far_id'] = str(far_id_down)
500 matchFields['session_id'] = str(pfcp_session_id)
501 # Action params
502 actionParams['needs_dropping'] = FALSE
503 actionParams['notify_cp'] = FALSE
504 actionParams['needs_buffering'] = FALSE
505 actionParams['tunnel_type'] = TUNNEL_TYPE_GPDU
506 actionParams['src_addr'] = str(self.s1u_address)
507 actionParams['dst_addr'] = str(self.enb_address)
508 actionParams['teid'] = str(teid_down)
509 actionParams['sport'] = TUNNEL_SPORT
510 if not self.__add_entry(tableName, actionName, matchFields,
511 actionParams, entries, action):
512 return False
513 if action == "program":
514 main.log.info("All entries added successfully.")
515 elif action == "clear":
516 self.__clear_entries(entries)
517
518 def __add_entry(self, tableName, actionName, matchFields, actionParams,
519 entries, action):
520 if action == "program":
521 self.up4_client.buildP4RtTableEntry(
522 tableName=tableName, actionName=actionName,
523 actionParams=actionParams, matchFields=matchFields)
524 if self.up4_client.pushTableEntry(debug=True) == main.TRUE:
525 main.log.info("*** Entry added.")
526 else:
527 main.log.error("Error during table insertion")
528 self.__clear_entries(entries)
529 return False
530 entries.append({"tableName": tableName, "actionName": actionName,
531 "matchFields": matchFields,
532 "actionParams": actionParams})
533 return True
534
535 def __clear_entries(self, entries):
536 for i, entry in enumerate(entries):
537 self.up4_client.buildP4RtTableEntry(**entry)
538 if self.up4_client.deleteTableEntry(debug=True) == main.TRUE:
539 main.log.info(
540 "*** Entry %d of %d deleted." % (i + 1, len(entries)))
541 else:
542 main.log.error("Error during table delete")
Daniele Morobf53dec2021-09-13 18:11:56 +0200543
544 @staticmethod
545 def buildGtpPacket(host, src_ip_outer, dst_ip_outer, src_ip_inner,
546 dst_ip_inner, src_udp_inner, dst_udp_inner, teid):
547 host.buildEther()
548 host.buildIP(src=src_ip_outer, dst=dst_ip_outer)
549 host.buildUDP(ipVersion=4, dport=GPDU_PORT)
550 # FIXME: With newer scapy TEID becomes teid (required for Scapy 2.4.5)
551 host.buildGTP(gtp_type=0xFF, TEID=teid)
552 host.buildIP(overGtp=True, src=src_ip_inner, dst=dst_ip_inner)
553 host.buildUDP(ipVersion=4, overGtp=True, sport=src_udp_inner,
554 dport=dst_udp_inner)
555
556 @staticmethod
557 def buildUdpPacket(host, src_ip, dst_ip, src_udp, dst_udp, src_eth=None,
558 dst_eth=None):
559 host.buildEther(src=src_eth, dst=dst_eth)
560 host.buildIP(src=src_ip, dst=dst_ip)
561 host.buildUDP(ipVersion=4, sport=src_udp, dport=dst_udp)
562
563 @staticmethod
564 def checkFilterAndGetPackets(host):
565 finished = host.checkFilter()
566 if finished:
567 packets = host.readPackets(detailed=True)
568 for p in packets.splitlines():
569 main.log.debug(p)
570 # We care only of the last line from readPackets
571 return packets.splitlines()[-1]
572 else:
573 kill = host.killFilter()
574 main.log.debug(kill)
575 return ""