1import pytest 2 3import logging 4import os 5import re 6import subprocess 7 8from atf_python.sys.net.vnet import IfaceFactory 9from atf_python.sys.net.vnet import SingleVnetTestTemplate 10from atf_python.sys.net.tools import ToolsHelper 11from typing import List 12from typing import Optional 13 14logging.getLogger("scapy").setLevel(logging.CRITICAL) 15try: 16 import scapy.all as sc 17except ImportError as e: 18 # Fake scapy well enough to be able to list test cases 19 from types import SimpleNamespace 20 sc = SimpleNamespace( 21 scapy=SimpleNamespace( 22 fields=SimpleNamespace( 23 SourceIPField=0, 24 ByteEnumField=0, 25 MultiEnumField=0, 26 BitField=0, 27 FlagsField=0, 28 ByteField=0, 29 IPField=0, 30 ShortField=0, 31 ), 32 layers=SimpleNamespace( 33 inet=SimpleNamespace( 34 DestIPField=0, 35 ICMPTimeStampField=0, 36 ) 37 ) 38 ) 39 ) 40 41 42def build_response_packet(echo, ip, icmp, oip_ihl, special): 43 icmp_id_seq_types = [0, 8, 13, 14, 15, 16, 17, 18, 37, 38] 44 oip = echo[sc.IP] 45 oicmp = echo[sc.ICMP] 46 load = echo[sc.ICMP].payload 47 oip[sc.IP].remove_payload() 48 oicmp[sc.ICMP].remove_payload() 49 oicmp.type = 8 50 51 # As if the original IP packet had these set 52 oip.ihl = None 53 oip.len = None 54 oip.id = 1 55 oip.flags = ip.flags 56 oip.chksum = None 57 oip.options = ip.options 58 59 # Inner packet (oip) options 60 if oip_ihl: 61 oip.ihl = oip_ihl 62 63 # Special options 64 if special == "no-payload": 65 load = "" 66 if special == "tcp": 67 oip.proto = "tcp" 68 tcp = sc.TCP(sport=1234, dport=5678) 69 return ip / icmp / oip / tcp 70 if special == "udp": 71 oip.proto = "udp" 72 udp = sc.UDP(sport=1234, dport=5678) 73 return ip / icmp / oip / udp 74 if special == "warp": 75 # Build a package with a timestamp of INT_MAX 76 # (time-warped package) 77 payload_no_timestamp = sc.bytes_hex(load)[16:] 78 load = b"\x7f" + (b"\xff" * 7) + sc.hex_bytes(payload_no_timestamp) 79 if special == "wrong": 80 # Build a package with a wrong last byte 81 payload_no_last_byte = sc.bytes_hex(load)[:-2] 82 load = (sc.hex_bytes(payload_no_last_byte)) + b"\x00" 83 if special == "short-wrong": 84 # Build a short package with a wrong last byte 85 payload_no_last_byte = sc.bytes_hex(load)[:-4] 86 load = (sc.hex_bytes(payload_no_last_byte)) + b"\x00" 87 if special == "not-mine": 88 # Modify the ICMP Identifier field 89 oicmp.id += 1 90 91 if icmp.type in icmp_id_seq_types: 92 pkt = ip / icmp / load 93 else: 94 del ip.options 95 pkt = ip / icmp / oip / oicmp / load 96 return pkt 97 98 99def generate_ip_options(opts): 100 if not opts: 101 return [] 102 103 routers = [ 104 "192.0.2.10", 105 "192.0.2.20", 106 "192.0.2.30", 107 "192.0.2.40", 108 "192.0.2.50", 109 "192.0.2.60", 110 "192.0.2.70", 111 "192.0.2.80", 112 "192.0.2.90", 113 ] 114 routers_zero = [0, 0, 0, 0, 0, 0, 0, 0, 0] 115 if opts == "EOL": 116 options = sc.IPOption_EOL() 117 elif opts == "NOP": 118 options = sc.IPOption_NOP() 119 elif opts == "NOP-40": 120 options = sc.IPOption_NOP() * 40 121 elif opts == "RR": 122 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 123 options = sc.IPOption_RR(pointer=40, routers=routers) 124 elif opts == "RR-same": 125 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 126 options = sc.IPOption_RR(pointer=3, routers=routers_zero) 127 elif opts == "RR-trunc": 128 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 129 options = sc.IPOption_RR(length=7, routers=routers_zero) 130 elif opts == "LSRR": 131 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 132 options = sc.IPOption_LSRR(routers=routers) 133 elif opts == "LSRR-trunc": 134 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 135 options = sc.IPOption_LSRR(length=3, routers=routers_zero) 136 elif opts == "SSRR": 137 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 138 options = sc.IPOption_SSRR(routers=routers) 139 elif opts == "SSRR-trunc": 140 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 141 options = sc.IPOption_SSRR(length=3, routers=routers_zero) 142 elif opts == "unk": 143 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 144 options = b"\x9f" 145 elif opts == "unk-40": 146 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 147 options = b"\x9f" * 40 148 else: 149 options = [] 150 return options 151 152 153def pinger( 154 # Required arguments 155 # Avoid setting defaults on these arguments, 156 # as we want to set them explicitly in the tests 157 iface: str, 158 /, 159 src: sc.scapy.fields.SourceIPField, 160 dst: sc.scapy.layers.inet.DestIPField, 161 icmp_type: sc.scapy.fields.ByteEnumField, 162 icmp_code: sc.scapy.fields.MultiEnumField, 163 # IP arguments 164 ihl: Optional[sc.scapy.fields.BitField] = None, 165 flags: Optional[sc.scapy.fields.FlagsField] = 0, 166 opts: Optional[str] = None, 167 oip_ihl: Optional[sc.scapy.fields.BitField] = None, 168 special: Optional[str] = None, 169 # ICMP arguments 170 # Match names with <netinet/ip_icmp.h> 171 icmp_pptr: sc.scapy.fields.ByteField = 0, 172 icmp_gwaddr: sc.scapy.fields.IPField = "0.0.0.0", 173 icmp_nextmtu: sc.scapy.fields.ShortField = 0, 174 icmp_otime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 175 icmp_rtime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 176 icmp_ttime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 177 icmp_mask: sc.scapy.fields.IPField = "0.0.0.0", 178 request: Optional[str] = None, 179 # Miscellaneous arguments 180 count: int = 1, 181 dup: bool = False, 182 verbose: bool = True, 183) -> subprocess.CompletedProcess: 184 """P I N G E R 185 186 Echo reply faker 187 188 :param str iface: Interface to send packet to 189 :keyword src: Source packet IP 190 :type src: class:`scapy.fields.SourceIPField` 191 :keyword dst: Destination packet IP 192 :type dst: class:`scapy.layers.inet.DestIPField` 193 :keyword icmp_type: ICMP type 194 :type icmp_type: class:`scapy.fields.ByteEnumField` 195 :keyword icmp_code: ICMP code 196 :type icmp_code: class:`scapy.fields.MultiEnumField` 197 198 :keyword ihl: Internet Header Length, defaults to None 199 :type ihl: class:`scapy.fields.BitField`, optional 200 :keyword flags: IP flags - one of `DF`, `MF` or `evil`, defaults to 0 201 :type flags: class:`scapy.fields.FlagsField`, optional 202 :keyword opts: Include IP options - one of `EOL`, `NOP`, `NOP-40`, `unk`, 203 `unk-40`, `RR`, `RR-same`, `RR-trunc`, `LSRR`, `LSRR-trunc`, `SSRR` or 204 `SSRR-trunc`, defaults to None 205 :type opts: str, optional 206 :keyword oip_ihl: Inner packet's Internet Header Length, defaults to None 207 :type oip_ihl: class:`scapy.fields.BitField`, optional 208 :keyword special: Send a special packet - one of `no-payload`, `not-mine`, 209 `short-wrong`, `tcp`, `udp`, `wrong` or `warp`, defaults to None 210 :type special: str, optional 211 :keyword icmp_pptr: ICMP pointer, defaults to 0 212 :type icmp_pptr: class:`scapy.fields.ByteField` 213 :keyword icmp_gwaddr: ICMP gateway IP address, defaults to "0.0.0.0" 214 :type icmp_gwaddr: class:`scapy.fields.IPField` 215 :keyword icmp_nextmtu: ICMP next MTU, defaults to 0 216 :type icmp_nextmtu: class:`scapy.fields.ShortField` 217 :keyword icmp_otime: ICMP originate timestamp, defaults to 0 218 :type icmp_otime: class:`scapy.layers.inet.ICMPTimeStampField` 219 :keyword icmp_rtime: ICMP receive timestamp, defaults to 0 220 :type icmp_rtime: class:`scapy.layers.inet.ICMPTimeStampField` 221 :keyword icmp_ttime: ICMP transmit timestamp, defaults to 0 222 :type icmp_ttime: class:`scapy.layers.inet.ICMPTimeStampField` 223 :keyword icmp_mask: ICMP address mask, defaults to "0.0.0.0" 224 :type icmp_mask: class:`scapy.fields.IPField` 225 :keyword request: Request type - one of `mask` or `timestamp`, 226 defaults to None 227 :type request: str, optional 228 :keyword count: Number of packets to send, defaults to 1 229 :type count: int 230 :keyword dup: Duplicate packets, defaults to `False` 231 :type dup: bool 232 :keyword verbose: Turn on/off verbosity, defaults to `True` 233 :type verbose: bool 234 235 :return: A class:`subprocess.CompletedProcess` with the output from the 236 ping utility 237 :rtype: class:`subprocess.CompletedProcess` 238 """ 239 tun = sc.TunTapInterface(iface) 240 subprocess.run(["ifconfig", tun.iface, "up"], check=True) 241 subprocess.run(["ifconfig", tun.iface, src, dst], check=True) 242 ip_opts = generate_ip_options(opts) 243 ip = sc.IP(ihl=ihl, flags=flags, src=dst, dst=src, options=ip_opts) 244 command = [ 245 "/sbin/ping", 246 "-c", 247 str(count), 248 "-t", 249 str(count), 250 ] 251 if verbose: 252 command += ["-v"] 253 if request == "mask": 254 command += ["-Mm"] 255 if request == "timestamp": 256 command += ["-Mt"] 257 if special: 258 command += ["-p1"] 259 if opts in [ 260 "RR", 261 "RR-same", 262 "RR-trunc", 263 "LSRR", 264 "LSRR-trunc", 265 "SSRR", 266 "SSRR-trunc", 267 ]: 268 command += ["-R"] 269 command += [dst] 270 with subprocess.Popen( 271 args=command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True 272 ) as ping: 273 for dummy in range(count): 274 echo = tun.recv() 275 icmp = sc.ICMP( 276 type=icmp_type, 277 code=icmp_code, 278 id=echo[sc.ICMP].id, 279 seq=echo[sc.ICMP].seq, 280 ts_ori=icmp_otime, 281 ts_rx=icmp_rtime, 282 ts_tx=icmp_ttime, 283 gw=icmp_gwaddr, 284 ptr=icmp_pptr, 285 addr_mask=icmp_mask, 286 nexthopmtu=icmp_nextmtu, 287 ) 288 pkt = build_response_packet(echo, ip, icmp, oip_ihl, special) 289 tun.send(pkt) 290 if dup is True: 291 tun.send(pkt) 292 stdout, stderr = ping.communicate() 293 return subprocess.CompletedProcess( 294 ping.args, ping.returncode, stdout, stderr 295 ) 296 297 298def redact(output): 299 """Redact some elements of ping's output""" 300 pattern_replacements = [ 301 (r"localhost \([0-9]{1,3}(\.[0-9]{1,3}){3}\)", "localhost"), 302 (r"from [0-9]{1,3}(\.[0-9]{1,3}){3}", "from"), 303 ("hlim=[0-9]*", "hlim="), 304 ("ttl=[0-9]*", "ttl="), 305 ("time=[0-9.-]*", "time="), 306 ("cp: .*", "cp: xx xx xx xx xx xx xx xx"), 307 ("dp: .*", "dp: xx xx xx xx xx xx xx xx"), 308 (r"\(-[0-9\.]+[0-9]+ ms\)", "(- ms)"), 309 (r"[0-9\.]+/[0-9.]+", "/"), 310 ] 311 for pattern, repl in pattern_replacements: 312 output = re.sub(pattern, repl, output) 313 return output 314 315 316class TestPing(SingleVnetTestTemplate): 317 IPV6_PREFIXES: List[str] = ["2001:db8::1/64"] 318 IPV4_PREFIXES: List[str] = ["192.0.2.1/24"] 319 320 # Each param in testdata contains a dictionary with the command, 321 # and the expected outcome (returncode, redacted stdout, and stderr) 322 testdata = [ 323 pytest.param( 324 { 325 "args": "ping -4 -c1 -s56 -t1 localhost", 326 "returncode": 0, 327 "stdout": """\ 328PING localhost: 56 data bytes 32964 bytes from: icmp_seq=0 ttl= time= ms 330 331--- localhost ping statistics --- 3321 packets transmitted, 1 packets received, 0.0% packet loss 333round-trip min/avg/max/stddev = /// ms 334""", 335 "stderr": "", 336 }, 337 id="_4_c1_s56_t1_localhost", 338 ), 339 pytest.param( 340 { 341 "args": "ping -6 -c1 -s8 -t1 localhost", 342 "returncode": 0, 343 "stdout": """\ 344PING(56=40+8+8 bytes) ::1 --> ::1 34516 bytes from ::1, icmp_seq=0 hlim= time= ms 346 347--- localhost ping statistics --- 3481 packets transmitted, 1 packets received, 0.0% packet loss 349round-trip min/avg/max/stddev = /// ms 350""", 351 "stderr": "", 352 }, 353 id="_6_c1_s8_t1_localhost", 354 ), 355 pytest.param( 356 { 357 "args": "ping -A -c1 192.0.2.1", 358 "returncode": 0, 359 "stdout": """\ 360PING 192.0.2.1 (192.0.2.1): 56 data bytes 36164 bytes from: icmp_seq=0 ttl= time= ms 362 363--- 192.0.2.1 ping statistics --- 3641 packets transmitted, 1 packets received, 0.0% packet loss 365round-trip min/avg/max/stddev = /// ms 366""", 367 "stderr": "", 368 }, 369 id="_A_c1_192_0_2_1", 370 ), 371 pytest.param( 372 { 373 "args": "ping -A -c1 192.0.2.2", 374 "returncode": 2, 375 "stdout": """\ 376PING 192.0.2.2 (192.0.2.2): 56 data bytes 377 378--- 192.0.2.2 ping statistics --- 3791 packets transmitted, 0 packets received, 100.0% packet loss 380""", 381 "stderr": "", 382 }, 383 id="_A_c1_192_0_2_2", 384 ), 385 pytest.param( 386 { 387 "args": "ping -A -c1 2001:db8::1", 388 "returncode": 0, 389 "stdout": """\ 390PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 39116 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 392 393--- 2001:db8::1 ping statistics --- 3941 packets transmitted, 1 packets received, 0.0% packet loss 395round-trip min/avg/max/stddev = /// ms 396""", 397 "stderr": "", 398 }, 399 id="_A_c1_2001_db8__1", 400 ), 401 pytest.param( 402 { 403 "args": "ping -A -c1 2001:db8::2", 404 "returncode": 2, 405 "stdout": """\ 406PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 407 408--- 2001:db8::2 ping statistics --- 4091 packets transmitted, 0 packets received, 100.0% packet loss 410""", 411 "stderr": "", 412 }, 413 id="_A_c1_2001_db8__2", 414 ), 415 pytest.param( 416 { 417 "args": "ping -A -c3 192.0.2.1", 418 "returncode": 0, 419 "stdout": """\ 420PING 192.0.2.1 (192.0.2.1): 56 data bytes 42164 bytes from: icmp_seq=0 ttl= time= ms 42264 bytes from: icmp_seq=1 ttl= time= ms 42364 bytes from: icmp_seq=2 ttl= time= ms 424 425--- 192.0.2.1 ping statistics --- 4263 packets transmitted, 3 packets received, 0.0% packet loss 427round-trip min/avg/max/stddev = /// ms 428""", 429 "stderr": "", 430 }, 431 id="_A_3_192_0.2.1", 432 ), 433 pytest.param( 434 { 435 "args": "ping -A -c3 192.0.2.2", 436 "returncode": 2, 437 "stdout": """\ 438PING 192.0.2.2 (192.0.2.2): 56 data bytes 439\x07\x07 440--- 192.0.2.2 ping statistics --- 4413 packets transmitted, 0 packets received, 100.0% packet loss 442""", 443 "stderr": "", 444 }, 445 id="_A_c3_192_0_2_2", 446 ), 447 pytest.param( 448 { 449 "args": "ping -A -c3 2001:db8::1", 450 "returncode": 0, 451 "stdout": """\ 452PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 45316 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 45416 bytes from 2001:db8::1, icmp_seq=1 hlim= time= ms 45516 bytes from 2001:db8::1, icmp_seq=2 hlim= time= ms 456 457--- 2001:db8::1 ping statistics --- 4583 packets transmitted, 3 packets received, 0.0% packet loss 459round-trip min/avg/max/stddev = /// ms 460""", 461 "stderr": "", 462 }, 463 id="_A_c3_2001_db8__1", 464 ), 465 pytest.param( 466 { 467 "args": "ping -A -c3 2001:db8::2", 468 "returncode": 2, 469 "stdout": """\ 470PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 471\x07\x07 472--- 2001:db8::2 ping statistics --- 4733 packets transmitted, 0 packets received, 100.0% packet loss 474""", 475 "stderr": "", 476 }, 477 id="_A_c3_2001_db8__2", 478 ), 479 pytest.param( 480 { 481 "args": "ping -c1 192.0.2.1", 482 "returncode": 0, 483 "stdout": """\ 484PING 192.0.2.1 (192.0.2.1): 56 data bytes 48564 bytes from: icmp_seq=0 ttl= time= ms 486 487--- 192.0.2.1 ping statistics --- 4881 packets transmitted, 1 packets received, 0.0% packet loss 489round-trip min/avg/max/stddev = /// ms 490""", 491 "stderr": "", 492 }, 493 id="_c1_192_0_2_1", 494 ), 495 pytest.param( 496 { 497 "args": "ping -c1 192.0.2.2", 498 "returncode": 2, 499 "stdout": """\ 500PING 192.0.2.2 (192.0.2.2): 56 data bytes 501 502--- 192.0.2.2 ping statistics --- 5031 packets transmitted, 0 packets received, 100.0% packet loss 504""", 505 "stderr": "", 506 }, 507 id="_c1_192_0_2_2", 508 ), 509 pytest.param( 510 { 511 "args": "ping -c1 2001:db8::1", 512 "returncode": 0, 513 "stdout": """\ 514PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 51516 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 516 517--- 2001:db8::1 ping statistics --- 5181 packets transmitted, 1 packets received, 0.0% packet loss 519round-trip min/avg/max/stddev = /// ms 520""", 521 "stderr": "", 522 }, 523 id="_c1_2001_db8__1", 524 ), 525 pytest.param( 526 { 527 "args": "ping -c1 2001:db8::2", 528 "returncode": 2, 529 "stdout": """\ 530PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 531 532--- 2001:db8::2 ping statistics --- 5331 packets transmitted, 0 packets received, 100.0% packet loss 534""", 535 "stderr": "", 536 }, 537 id="_c1_2001_db8__2", 538 ), 539 pytest.param( 540 { 541 "args": "ping -c1 -S127.0.0.1 -s56 -t1 localhost", 542 "returncode": 0, 543 "stdout": """\ 544PING localhost from: 56 data bytes 54564 bytes from: icmp_seq=0 ttl= time= ms 546 547--- localhost ping statistics --- 5481 packets transmitted, 1 packets received, 0.0% packet loss 549round-trip min/avg/max/stddev = /// ms 550""", 551 "stderr": "", 552 }, 553 id="_c1_S127_0_0_1_s56_t1_localhost", 554 ), 555 pytest.param( 556 { 557 "args": "ping -c1 -S::1 -s8 -t1 localhost", 558 "returncode": 0, 559 "stdout": """\ 560PING(56=40+8+8 bytes) ::1 --> ::1 56116 bytes from ::1, icmp_seq=0 hlim= time= ms 562 563--- localhost ping statistics --- 5641 packets transmitted, 1 packets received, 0.0% packet loss 565round-trip min/avg/max/stddev = /// ms 566""", 567 "stderr": "", 568 }, 569 id="_c1_S__1_s8_t1_localhost", 570 ), 571 pytest.param( 572 { 573 "args": "ping -c3 192.0.2.1", 574 "returncode": 0, 575 "stdout": """\ 576PING 192.0.2.1 (192.0.2.1): 56 data bytes 57764 bytes from: icmp_seq=0 ttl= time= ms 57864 bytes from: icmp_seq=1 ttl= time= ms 57964 bytes from: icmp_seq=2 ttl= time= ms 580 581--- 192.0.2.1 ping statistics --- 5823 packets transmitted, 3 packets received, 0.0% packet loss 583round-trip min/avg/max/stddev = /// ms 584""", 585 "stderr": "", 586 }, 587 id="_c3_192_0_2_1", 588 ), 589 pytest.param( 590 { 591 "args": "ping -c3 192.0.2.2", 592 "returncode": 2, 593 "stdout": """\ 594PING 192.0.2.2 (192.0.2.2): 56 data bytes 595 596--- 192.0.2.2 ping statistics --- 5973 packets transmitted, 0 packets received, 100.0% packet loss 598""", 599 "stderr": "", 600 }, 601 id="_c3_192_0_2_2", 602 ), 603 pytest.param( 604 { 605 "args": "ping -c3 2001:db8::1", 606 "returncode": 0, 607 "stdout": """\ 608PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 60916 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 61016 bytes from 2001:db8::1, icmp_seq=1 hlim= time= ms 61116 bytes from 2001:db8::1, icmp_seq=2 hlim= time= ms 612 613--- 2001:db8::1 ping statistics --- 6143 packets transmitted, 3 packets received, 0.0% packet loss 615round-trip min/avg/max/stddev = /// ms 616""", 617 "stderr": "", 618 }, 619 id="_c3_2001_db8__1", 620 ), 621 pytest.param( 622 { 623 "args": "ping -c3 2001:db8::2", 624 "returncode": 2, 625 "stdout": """\ 626PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 627 628--- 2001:db8::2 ping statistics --- 6293 packets transmitted, 0 packets received, 100.0% packet loss 630""", 631 "stderr": "", 632 }, 633 id="_c3_2001_db8__2", 634 ), 635 pytest.param( 636 { 637 "args": "ping -q -c1 192.0.2.1", 638 "returncode": 0, 639 "stdout": """\ 640PING 192.0.2.1 (192.0.2.1): 56 data bytes 641 642--- 192.0.2.1 ping statistics --- 6431 packets transmitted, 1 packets received, 0.0% packet loss 644round-trip min/avg/max/stddev = /// ms 645""", 646 "stderr": "", 647 }, 648 id="_q_c1_192_0_2_1", 649 ), 650 pytest.param( 651 { 652 "args": "ping -q -c1 192.0.2.2", 653 "returncode": 2, 654 "stdout": """\ 655PING 192.0.2.2 (192.0.2.2): 56 data bytes 656 657--- 192.0.2.2 ping statistics --- 6581 packets transmitted, 0 packets received, 100.0% packet loss 659""", 660 "stderr": "", 661 }, 662 id="_q_c1_192_0_2_2", 663 ), 664 pytest.param( 665 { 666 "args": "ping -q -c1 2001:db8::1", 667 "returncode": 0, 668 "stdout": """\ 669PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 670 671--- 2001:db8::1 ping statistics --- 6721 packets transmitted, 1 packets received, 0.0% packet loss 673round-trip min/avg/max/stddev = /// ms 674""", 675 "stderr": "", 676 }, 677 id="_q_c1_2001_db8__1", 678 ), 679 pytest.param( 680 { 681 "args": "ping -q -c1 2001:db8::2", 682 "returncode": 2, 683 "stdout": """\ 684PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 685 686--- 2001:db8::2 ping statistics --- 6871 packets transmitted, 0 packets received, 100.0% packet loss 688""", 689 "stderr": "", 690 }, 691 id="_q_c1_2001_db8__2", 692 ), 693 pytest.param( 694 { 695 "args": "ping -q -c3 192.0.2.1", 696 "returncode": 0, 697 "stdout": """\ 698PING 192.0.2.1 (192.0.2.1): 56 data bytes 699 700--- 192.0.2.1 ping statistics --- 7013 packets transmitted, 3 packets received, 0.0% packet loss 702round-trip min/avg/max/stddev = /// ms 703""", 704 "stderr": "", 705 }, 706 id="_q_c3_192_0_2_1", 707 ), 708 pytest.param( 709 { 710 "args": "ping -q -c3 192.0.2.2", 711 "returncode": 2, 712 "stdout": """\ 713PING 192.0.2.2 (192.0.2.2): 56 data bytes 714 715--- 192.0.2.2 ping statistics --- 7163 packets transmitted, 0 packets received, 100.0% packet loss 717""", 718 "stderr": "", 719 }, 720 id="_q_c3_192_0_2_2", 721 ), 722 pytest.param( 723 { 724 "args": "ping -q -c3 2001:db8::1", 725 "returncode": 0, 726 "stdout": """\ 727PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 728 729--- 2001:db8::1 ping statistics --- 7303 packets transmitted, 3 packets received, 0.0% packet loss 731round-trip min/avg/max/stddev = /// ms 732""", 733 "stderr": "", 734 }, 735 id="_q_c3_2001_db8__1", 736 ), 737 pytest.param( 738 { 739 "args": "ping -q -c3 2001:db8::2", 740 "returncode": 2, 741 "stdout": """\ 742PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 743 744--- 2001:db8::2 ping statistics --- 7453 packets transmitted, 0 packets received, 100.0% packet loss 746""", 747 "stderr": "", 748 }, 749 id="_q_c3_2001_db8__2", 750 ), 751 ] 752 753 @pytest.mark.parametrize("expected", testdata) 754 @pytest.mark.require_user("root") 755 @pytest.mark.require_user("unprivileged") 756 def test_ping(self, expected): 757 """Test ping""" 758 ping = subprocess.run( 759 expected["args"].split(), 760 capture_output=True, 761 timeout=15, 762 text=True, 763 ) 764 assert ping.returncode == expected["returncode"] 765 assert redact(ping.stdout) == expected["stdout"] 766 assert ping.stderr == expected["stderr"] 767 768 # Each param in ping46_testdata contains a dictionary with the arguments 769 # and the expected outcome (returncode, redacted stdout, and stderr) 770 # common to `ping -4` and `ping -6` 771 ping46_testdata = [ 772 pytest.param( 773 { 774 "args": "-Wx localhost", 775 "returncode": os.EX_USAGE, 776 "stdout": "", 777 "stderr": "ping: invalid timing interval: `x'\n", 778 }, 779 id="_Wx_localhost", 780 ), 781 ] 782 783 @pytest.mark.parametrize("expected", ping46_testdata) 784 @pytest.mark.require_user("root") 785 @pytest.mark.require_user("unprivileged") 786 def test_ping_46(self, expected): 787 """Test ping -4/ping -6""" 788 for version in [4, 6]: 789 ping = subprocess.run( 790 ["ping", f"-{version}"] + expected["args"].split(), 791 capture_output=True, 792 timeout=15, 793 text=True, 794 ) 795 assert ping.returncode == expected["returncode"] 796 assert redact(ping.stdout) == expected["stdout"] 797 assert ping.stderr == expected["stderr"] 798 799 # Each param in pinger_testdata contains a dictionary with the keywords to 800 # `pinger()` and a dictionary with the expected outcome (returncode, 801 # stdout, stderr, and if ping's output is redacted) 802 pinger_testdata = [ 803 pytest.param( 804 { 805 "src": "192.0.2.1", 806 "dst": "192.0.2.2", 807 "icmp_type": 0, 808 "icmp_code": 0, 809 }, 810 { 811 "returncode": 0, 812 "stdout": """\ 813PING 192.0.2.2 (192.0.2.2): 56 data bytes 81464 bytes from: icmp_seq=0 ttl= time= ms 815 816--- 192.0.2.2 ping statistics --- 8171 packets transmitted, 1 packets received, 0.0% packet loss 818round-trip min/avg/max/stddev = /// ms 819""", 820 "stderr": "", 821 "redacted": True, 822 }, 823 id="_0_0", 824 ), 825 pytest.param( 826 { 827 "src": "192.0.2.1", 828 "dst": "192.0.2.2", 829 "icmp_type": 0, 830 "icmp_code": 0, 831 "opts": "EOL", 832 }, 833 { 834 "returncode": 0, 835 "stdout": """\ 836PING 192.0.2.2 (192.0.2.2): 56 data bytes 83764 bytes from: icmp_seq=0 ttl= time= ms 838wrong total length 88 instead of 84 839 840--- 192.0.2.2 ping statistics --- 8411 packets transmitted, 1 packets received, 0.0% packet loss 842round-trip min/avg/max/stddev = /// ms 843""", 844 "stderr": "", 845 "redacted": True, 846 }, 847 id="_0_0_opts_EOL", 848 ), 849 pytest.param( 850 { 851 "src": "192.0.2.1", 852 "dst": "192.0.2.2", 853 "icmp_type": 0, 854 "icmp_code": 0, 855 "opts": "LSRR", 856 }, 857 { 858 "returncode": 0, 859 "stdout": """\ 860PING 192.0.2.2 (192.0.2.2): 56 data bytes 86164 bytes from: icmp_seq=0 ttl= time= ms 862LSRR: 192.0.2.10 863 192.0.2.20 864 192.0.2.30 865 192.0.2.40 866 192.0.2.50 867 192.0.2.60 868 192.0.2.70 869 192.0.2.80 870 192.0.2.90 871 872--- 192.0.2.2 ping statistics --- 8731 packets transmitted, 1 packets received, 0.0% packet loss 874round-trip min/avg/max/stddev = /// ms 875""", 876 "stderr": "", 877 "redacted": True, 878 }, 879 id="_0_0_opts_LSRR", 880 ), 881 pytest.param( 882 { 883 "src": "192.0.2.1", 884 "dst": "192.0.2.2", 885 "icmp_type": 0, 886 "icmp_code": 0, 887 "opts": "LSRR-trunc", 888 }, 889 { 890 "returncode": 0, 891 "stdout": """\ 892PING 192.0.2.2 (192.0.2.2): 56 data bytes 89364 bytes from: icmp_seq=0 ttl= time= ms 894LSRR: (truncated route) 895 896--- 192.0.2.2 ping statistics --- 8971 packets transmitted, 1 packets received, 0.0% packet loss 898round-trip min/avg/max/stddev = /// ms 899""", 900 "stderr": "", 901 "redacted": True, 902 }, 903 id="_0_0_opts_LSRR_trunc", 904 ), 905 pytest.param( 906 { 907 "src": "192.0.2.1", 908 "dst": "192.0.2.2", 909 "icmp_type": 0, 910 "icmp_code": 0, 911 "opts": "SSRR", 912 }, 913 { 914 "returncode": 0, 915 "stdout": """\ 916PING 192.0.2.2 (192.0.2.2): 56 data bytes 91764 bytes from: icmp_seq=0 ttl= time= ms 918SSRR: 192.0.2.10 919 192.0.2.20 920 192.0.2.30 921 192.0.2.40 922 192.0.2.50 923 192.0.2.60 924 192.0.2.70 925 192.0.2.80 926 192.0.2.90 927 928--- 192.0.2.2 ping statistics --- 9291 packets transmitted, 1 packets received, 0.0% packet loss 930round-trip min/avg/max/stddev = /// ms 931""", 932 "stderr": "", 933 "redacted": True, 934 }, 935 id="_0_0_opts_SSRR", 936 ), 937 pytest.param( 938 { 939 "src": "192.0.2.1", 940 "dst": "192.0.2.2", 941 "icmp_type": 0, 942 "icmp_code": 0, 943 "opts": "SSRR-trunc", 944 }, 945 { 946 "returncode": 0, 947 "stdout": """\ 948PING 192.0.2.2 (192.0.2.2): 56 data bytes 94964 bytes from: icmp_seq=0 ttl= time= ms 950SSRR: (truncated route) 951 952--- 192.0.2.2 ping statistics --- 9531 packets transmitted, 1 packets received, 0.0% packet loss 954round-trip min/avg/max/stddev = /// ms 955""", 956 "stderr": "", 957 "redacted": True, 958 }, 959 id="_0_0_opts_SSRR_trunc", 960 ), 961 pytest.param( 962 { 963 "src": "192.0.2.1", 964 "dst": "192.0.2.2", 965 "icmp_type": 0, 966 "icmp_code": 0, 967 "opts": "RR", 968 }, 969 { 970 "returncode": 0, 971 "stdout": """\ 972PING 192.0.2.2 (192.0.2.2): 56 data bytes 97364 bytes from: icmp_seq=0 ttl= time= ms 974RR: 192.0.2.10 975 192.0.2.20 976 192.0.2.30 977 192.0.2.40 978 192.0.2.50 979 192.0.2.60 980 192.0.2.70 981 192.0.2.80 982 192.0.2.90 983 984--- 192.0.2.2 ping statistics --- 9851 packets transmitted, 1 packets received, 0.0% packet loss 986round-trip min/avg/max/stddev = /// ms 987""", 988 "stderr": "", 989 "redacted": True, 990 }, 991 id="_0_0_opts_RR", 992 ), 993 pytest.param( 994 { 995 "src": "192.0.2.1", 996 "dst": "192.0.2.2", 997 "icmp_type": 0, 998 "icmp_code": 0, 999 "opts": "RR-same", 1000 }, 1001 { 1002 "returncode": 0, 1003 "stdout": """\ 1004PING 192.0.2.2 (192.0.2.2): 56 data bytes 100564 bytes from: icmp_seq=0 ttl= time= ms (same route) 1006 1007--- 192.0.2.2 ping statistics --- 10081 packets transmitted, 1 packets received, 0.0% packet loss 1009round-trip min/avg/max/stddev = /// ms 1010""", 1011 "stderr": "", 1012 "redacted": True, 1013 }, 1014 id="_0_0_opts_RR_same", 1015 ), 1016 pytest.param( 1017 { 1018 "src": "192.0.2.1", 1019 "dst": "192.0.2.2", 1020 "icmp_type": 0, 1021 "icmp_code": 0, 1022 "opts": "RR-trunc", 1023 }, 1024 { 1025 "returncode": 0, 1026 "stdout": """\ 1027PING 192.0.2.2 (192.0.2.2): 56 data bytes 102864 bytes from: icmp_seq=0 ttl= time= ms 1029RR: (truncated route) 1030 1031--- 192.0.2.2 ping statistics --- 10321 packets transmitted, 1 packets received, 0.0% packet loss 1033round-trip min/avg/max/stddev = /// ms 1034""", 1035 "stderr": "", 1036 "redacted": True, 1037 }, 1038 id="_0_0_opts_RR_trunc", 1039 ), 1040 pytest.param( 1041 { 1042 "src": "192.0.2.1", 1043 "dst": "192.0.2.2", 1044 "icmp_type": 0, 1045 "icmp_code": 0, 1046 "opts": "NOP", 1047 }, 1048 { 1049 "returncode": 0, 1050 "stdout": """\ 1051PING 192.0.2.2 (192.0.2.2): 56 data bytes 105264 bytes from: icmp_seq=0 ttl= time= ms 1053wrong total length 88 instead of 84 1054NOP 1055 1056--- 192.0.2.2 ping statistics --- 10571 packets transmitted, 1 packets received, 0.0% packet loss 1058round-trip min/avg/max/stddev = /// ms 1059""", 1060 "stderr": "", 1061 "redacted": True, 1062 }, 1063 id="_0_0_opts_NOP", 1064 ), 1065 pytest.param( 1066 { 1067 "src": "192.0.2.1", 1068 "dst": "192.0.2.2", 1069 "icmp_type": 3, 1070 "icmp_code": 1, 1071 "ihl": 0x4, 1072 }, 1073 { 1074 "returncode": 2, 1075 "stdout": """\ 1076PING 192.0.2.2 (192.0.2.2): 56 data bytes 1077 1078--- 192.0.2.2 ping statistics --- 10791 packets transmitted, 0 packets received, 100.0% packet loss 1080""", 1081 "stderr": "", # "IHL too short" message not shown 1082 "redacted": False, 1083 }, 1084 id="_IHL_too_short", 1085 ), 1086 pytest.param( 1087 { 1088 "src": "192.0.2.1", 1089 "dst": "192.0.2.2", 1090 "icmp_type": 3, 1091 "icmp_code": 1, 1092 "special": "no-payload", 1093 }, 1094 { 1095 "returncode": 2, 1096 "stdout": """\ 1097PATTERN: 0x01 1098PING 192.0.2.2 (192.0.2.2): 56 data bytes 1099 1100--- 192.0.2.2 ping statistics --- 11011 packets transmitted, 0 packets received, 100.0% packet loss 1102""", 1103 "stderr": """\ 1104ping: quoted data too short (28 bytes) from 192.0.2.2 1105""", 1106 "redacted": False, 1107 }, 1108 id="_quoted_data_too_short", 1109 ), 1110 pytest.param( 1111 { 1112 "src": "192.0.2.1", 1113 "dst": "192.0.2.2", 1114 "icmp_type": 3, 1115 "icmp_code": 1, 1116 "oip_ihl": 0x4, 1117 }, 1118 { 1119 "returncode": 2, 1120 "stdout": """\ 1121PING 192.0.2.2 (192.0.2.2): 56 data bytes 1122 1123--- 192.0.2.2 ping statistics --- 11241 packets transmitted, 0 packets received, 100.0% packet loss 1125""", 1126 "stderr": "", # "inner IHL too short" message not shown 1127 "redacted": False, 1128 }, 1129 id="_inner_IHL_too_short", 1130 ), 1131 pytest.param( 1132 { 1133 "src": "192.0.2.1", 1134 "dst": "192.0.2.2", 1135 "icmp_type": 3, 1136 "icmp_code": 1, 1137 "oip_ihl": 0xF, 1138 }, 1139 { 1140 "returncode": 2, 1141 "stdout": """\ 1142PING 192.0.2.2 (192.0.2.2): 56 data bytes 1143 1144--- 192.0.2.2 ping statistics --- 11451 packets transmitted, 0 packets received, 100.0% packet loss 1146""", 1147 "stderr": """\ 1148ping: inner packet too short (84 bytes) from 192.0.2.2 1149""", 1150 "redacted": False, 1151 }, 1152 id="_inner_packet_too_short", 1153 ), 1154 pytest.param( 1155 { 1156 "src": "192.0.2.1", 1157 "dst": "192.0.2.2", 1158 "icmp_type": 3, 1159 "icmp_code": 1, 1160 "oip_ihl": 0xF, 1161 "special": "no-payload", 1162 }, 1163 { 1164 "returncode": 2, 1165 "stdout": """\ 1166PATTERN: 0x01 1167PING 192.0.2.2 (192.0.2.2): 56 data bytes 1168 1169--- 192.0.2.2 ping statistics --- 11701 packets transmitted, 0 packets received, 100.0% packet loss 1171""", 1172 "stderr": "", 1173 "redacted": False, 1174 }, 1175 id="_max_inner_packet_ihl_without_payload", 1176 ), 1177 pytest.param( 1178 { 1179 "src": "192.0.2.1", 1180 "dst": "192.0.2.2", 1181 "icmp_type": 0, 1182 "icmp_code": 0, 1183 "opts": "NOP-40", 1184 }, 1185 { 1186 "returncode": 0, 1187 "stdout": """\ 1188PING 192.0.2.2 (192.0.2.2): 56 data bytes 118964 bytes from: icmp_seq=0 ttl= time= ms 1190wrong total length 124 instead of 84 1191NOP 1192NOP 1193NOP 1194NOP 1195NOP 1196NOP 1197NOP 1198NOP 1199NOP 1200NOP 1201NOP 1202NOP 1203NOP 1204NOP 1205NOP 1206NOP 1207NOP 1208NOP 1209NOP 1210NOP 1211NOP 1212NOP 1213NOP 1214NOP 1215NOP 1216NOP 1217NOP 1218NOP 1219NOP 1220NOP 1221NOP 1222NOP 1223NOP 1224NOP 1225NOP 1226NOP 1227NOP 1228NOP 1229NOP 1230NOP 1231 1232--- 192.0.2.2 ping statistics --- 12331 packets transmitted, 1 packets received, 0.0% packet loss 1234round-trip min/avg/max/stddev = /// ms 1235""", 1236 "stderr": "", 1237 "redacted": True, 1238 }, 1239 id="_0_0_opts_NOP_40", 1240 ), 1241 pytest.param( 1242 { 1243 "src": "192.0.2.1", 1244 "dst": "192.0.2.2", 1245 "icmp_type": 0, 1246 "icmp_code": 0, 1247 "opts": "unk", 1248 }, 1249 { 1250 "returncode": 0, 1251 "stdout": """\ 1252PING 192.0.2.2 (192.0.2.2): 56 data bytes 125364 bytes from: icmp_seq=0 ttl= time= ms 1254wrong total length 88 instead of 84 1255unknown option 9f 1256 1257--- 192.0.2.2 ping statistics --- 12581 packets transmitted, 1 packets received, 0.0% packet loss 1259round-trip min/avg/max/stddev = /// ms 1260""", 1261 "stderr": "", 1262 "redacted": True, 1263 }, 1264 id="_0_0_opts_unk", 1265 ), 1266 pytest.param( 1267 { 1268 "src": "192.0.2.1", 1269 "dst": "192.0.2.2", 1270 "icmp_type": 3, 1271 "icmp_code": 1, 1272 "opts": "NOP-40", 1273 }, 1274 { 1275 "returncode": 2, 1276 "stdout": """\ 1277PING 192.0.2.2 (192.0.2.2): 56 data bytes 1278132 bytes from 192.0.2.2: Destination Host Unreachable 1279Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Opts 1280 4 f 00 007c 0001 0 0000 40 01 d868 192.0.2.1 192.0.2.2 01010101010101010101010101010101010101010101010101010101010101010101010101010101 1281 1282 1283--- 192.0.2.2 ping statistics --- 12841 packets transmitted, 0 packets received, 100.0% packet loss 1285""", 1286 "stderr": "", 1287 "redacted": False, 1288 }, 1289 id="_3_1_opts_NOP_40", 1290 ), 1291 pytest.param( 1292 { 1293 "src": "192.0.2.1", 1294 "dst": "192.0.2.2", 1295 "icmp_type": 3, 1296 "icmp_code": 1, 1297 "flags": "DF", 1298 }, 1299 { 1300 "returncode": 2, 1301 "stdout": """\ 1302PING 192.0.2.2 (192.0.2.2): 56 data bytes 130392 bytes from 192.0.2.2: Destination Host Unreachable 1304Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 1305 4 5 00 0054 0001 2 0000 40 01 b6a4 192.0.2.1 192.0.2.2 1306 1307 1308--- 192.0.2.2 ping statistics --- 13091 packets transmitted, 0 packets received, 100.0% packet loss 1310""", 1311 "stderr": "", 1312 "redacted": False, 1313 }, 1314 id="_3_1_flags_DF", 1315 ), 1316 pytest.param( 1317 { 1318 "src": "192.0.2.1", 1319 "dst": "192.0.2.2", 1320 "icmp_type": 3, 1321 "icmp_code": 1, 1322 "special": "tcp", 1323 }, 1324 { 1325 "returncode": 2, 1326 "stdout": """\ 1327PATTERN: 0x01 1328PING 192.0.2.2 (192.0.2.2): 56 data bytes 1329 1330--- 192.0.2.2 ping statistics --- 13311 packets transmitted, 0 packets received, 100.0% packet loss 1332""", 1333 "stderr": """\ 1334ping: quoted data too short (40 bytes) from 192.0.2.2 1335""", 1336 "redacted": False, 1337 }, 1338 id="_3_1_special_tcp", 1339 ), 1340 pytest.param( 1341 { 1342 "src": "192.0.2.1", 1343 "dst": "192.0.2.2", 1344 "icmp_type": 3, 1345 "icmp_code": 1, 1346 "special": "udp", 1347 }, 1348 { 1349 "returncode": 2, 1350 "stdout": """\ 1351PATTERN: 0x01 1352PING 192.0.2.2 (192.0.2.2): 56 data bytes 1353 1354--- 192.0.2.2 ping statistics --- 13551 packets transmitted, 0 packets received, 100.0% packet loss 1356""", 1357 "stderr": """\ 1358ping: quoted data too short (28 bytes) from 192.0.2.2 1359""", 1360 "redacted": False, 1361 }, 1362 id="_3_1_special_udp", 1363 ), 1364 pytest.param( 1365 { 1366 "src": "192.0.2.1", 1367 "dst": "192.0.2.2", 1368 "icmp_type": 3, 1369 "icmp_code": 1, 1370 "verbose": False, 1371 }, 1372 { 1373 "returncode": 2, 1374 "stdout": """\ 1375PING 192.0.2.2 (192.0.2.2): 56 data bytes 137692 bytes from 192.0.2.2: Destination Host Unreachable 1377Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 1378 4 5 00 0054 0001 0 0000 40 01 f6a4 192.0.2.1 192.0.2.2 1379 1380 1381--- 192.0.2.2 ping statistics --- 13821 packets transmitted, 0 packets received, 100.0% packet loss 1383""", 1384 "stderr": "", 1385 "redacted": False, 1386 }, 1387 id="_3_1_verbose_false", 1388 ), 1389 pytest.param( 1390 { 1391 "src": "192.0.2.1", 1392 "dst": "192.0.2.2", 1393 "icmp_type": 3, 1394 "icmp_code": 1, 1395 "special": "not-mine", 1396 "verbose": False, 1397 }, 1398 { 1399 "returncode": 2, 1400 "stdout": """\ 1401PATTERN: 0x01 1402PING 192.0.2.2 (192.0.2.2): 56 data bytes 1403 1404--- 192.0.2.2 ping statistics --- 14051 packets transmitted, 0 packets received, 100.0% packet loss 1406""", 1407 "stderr": "", 1408 "redacted": False, 1409 }, 1410 id="_3_1_special_not_mine_verbose_false", 1411 ), 1412 pytest.param( 1413 { 1414 "src": "192.0.2.1", 1415 "dst": "192.0.2.2", 1416 "icmp_type": 0, 1417 "icmp_code": 0, 1418 "special": "warp", 1419 }, 1420 { 1421 "returncode": 0, 1422 "stdout": """\ 1423PATTERN: 0x01 1424PING 192.0.2.2 (192.0.2.2): 56 data bytes 142564 bytes from: icmp_seq=0 ttl= time= ms 1426 1427--- 192.0.2.2 ping statistics --- 14281 packets transmitted, 1 packets received, 0.0% packet loss 1429round-trip min/avg/max/stddev = /// ms 1430""", 1431 "stderr": """\ 1432ping: time of day goes back (- ms), clamping time to 0 1433""", 1434 "redacted": True, 1435 }, 1436 id="_0_0_special_warp", 1437 ), 1438 pytest.param( 1439 { 1440 "src": "192.0.2.1", 1441 "dst": "192.0.2.2", 1442 "icmp_type": 0, 1443 "icmp_code": 0, 1444 "special": "wrong", 1445 }, 1446 { 1447 "returncode": 0, 1448 "stdout": """\ 1449PATTERN: 0x01 1450PING 192.0.2.2 (192.0.2.2): 56 data bytes 145164 bytes from: icmp_seq=0 ttl= time= ms 1452wrong data byte #55 should be 0x1 but was 0x0 1453cp: xx xx xx xx xx xx xx xx 1454 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1455 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1456 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1457dp: xx xx xx xx xx xx xx xx 1458 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1459 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1460 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1461 1462--- 192.0.2.2 ping statistics --- 14631 packets transmitted, 1 packets received, 0.0% packet loss 1464round-trip min/avg/max/stddev = /// ms 1465""", 1466 "stderr": "", 1467 "redacted": True, 1468 }, 1469 id="_0_0_special_wrong", 1470 ), 1471 pytest.param( 1472 { 1473 "src": "192.0.2.1", 1474 "dst": "192.0.2.2", 1475 "icmp_type": 0, 1476 "icmp_code": 0, 1477 "special": "short-wrong", 1478 }, 1479 { 1480 "returncode": 0, 1481 "stdout": """\ 1482PATTERN: 0x01 1483PING 192.0.2.2 (192.0.2.2): 56 data bytes 148463 bytes from: icmp_seq=0 ttl= time= ms 1485wrong total length 83 instead of 84 1486wrong data byte #54 should be 0x1 but was 0x0 1487cp: xx xx xx xx xx xx xx xx 1488 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1489 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1490 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1491dp: xx xx xx xx xx xx xx xx 1492 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1493 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1494 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1495 1496--- 192.0.2.2 ping statistics --- 14971 packets transmitted, 1 packets received, 0.0% packet loss 1498round-trip min/avg/max/stddev = /// ms 1499""", 1500 "stderr": "", 1501 "redacted": True, 1502 }, 1503 id="_0_0_special_short_wrong", 1504 ), 1505 ] 1506 1507 @pytest.mark.parametrize("pinger_kargs, expected", pinger_testdata) 1508 @pytest.mark.require_progs(["scapy"]) 1509 @pytest.mark.require_user("root") 1510 def test_pinger(self, pinger_kargs, expected): 1511 """Test ping using pinger(), a reply faker""" 1512 iface = IfaceFactory().create_iface("", "tun")[0].name 1513 ping = pinger(iface, **pinger_kargs) 1514 assert ping.returncode == expected["returncode"] 1515 if expected["redacted"]: 1516 assert redact(ping.stdout) == expected["stdout"] 1517 assert redact(ping.stderr) == expected["stderr"] 1518 else: 1519 assert ping.stdout == expected["stdout"] 1520 assert ping.stderr == expected["stderr"] 1521