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 == "not-mine": 84 # Modify the ICMP Identifier field 85 oicmp.id += 1 86 87 if icmp.type in icmp_id_seq_types: 88 pkt = ip / icmp / load 89 else: 90 del ip.options 91 pkt = ip / icmp / oip / oicmp / load 92 return pkt 93 94 95def generate_ip_options(opts): 96 if not opts: 97 return [] 98 99 routers = [ 100 "192.0.2.10", 101 "192.0.2.20", 102 "192.0.2.30", 103 "192.0.2.40", 104 "192.0.2.50", 105 "192.0.2.60", 106 "192.0.2.70", 107 "192.0.2.80", 108 "192.0.2.90", 109 ] 110 routers_zero = [0, 0, 0, 0, 0, 0, 0, 0, 0] 111 if opts == "EOL": 112 options = sc.IPOption_EOL() 113 elif opts == "NOP": 114 options = sc.IPOption_NOP() 115 elif opts == "NOP-40": 116 options = sc.IPOption_NOP() * 40 117 elif opts == "RR": 118 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 119 options = sc.IPOption_RR(pointer=40, routers=routers) 120 elif opts == "RR-same": 121 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 122 options = sc.IPOption_RR(pointer=3, routers=routers_zero) 123 elif opts == "RR-trunc": 124 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 125 options = sc.IPOption_RR(length=7, routers=routers_zero) 126 elif opts == "LSRR": 127 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 128 options = sc.IPOption_LSRR(routers=routers) 129 elif opts == "LSRR-trunc": 130 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 131 options = sc.IPOption_LSRR(length=3, routers=routers_zero) 132 elif opts == "SSRR": 133 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 134 options = sc.IPOption_SSRR(routers=routers) 135 elif opts == "SSRR-trunc": 136 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 137 options = sc.IPOption_SSRR(length=3, routers=routers_zero) 138 elif opts == "unk": 139 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 140 options = b"\x9f" 141 elif opts == "unk-40": 142 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 143 options = b"\x9f" * 40 144 else: 145 options = [] 146 return options 147 148 149def pinger( 150 # Required arguments 151 # Avoid setting defaults on these arguments, 152 # as we want to set them explicitly in the tests 153 iface: str, 154 /, 155 src: sc.scapy.fields.SourceIPField, 156 dst: sc.scapy.layers.inet.DestIPField, 157 icmp_type: sc.scapy.fields.ByteEnumField, 158 icmp_code: sc.scapy.fields.MultiEnumField, 159 # IP arguments 160 ihl: Optional[sc.scapy.fields.BitField] = None, 161 flags: Optional[sc.scapy.fields.FlagsField] = 0, 162 opts: Optional[str] = None, 163 oip_ihl: Optional[sc.scapy.fields.BitField] = None, 164 special: Optional[str] = None, 165 # ICMP arguments 166 # Match names with <netinet/ip_icmp.h> 167 icmp_pptr: sc.scapy.fields.ByteField = 0, 168 icmp_gwaddr: sc.scapy.fields.IPField = "0.0.0.0", 169 icmp_nextmtu: sc.scapy.fields.ShortField = 0, 170 icmp_otime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 171 icmp_rtime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 172 icmp_ttime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 173 icmp_mask: sc.scapy.fields.IPField = "0.0.0.0", 174 request: Optional[str] = None, 175 # Miscellaneous arguments 176 count: int = 1, 177 dup: bool = False, 178 verbose: bool = True, 179) -> subprocess.CompletedProcess: 180 """P I N G E R 181 182 Echo reply faker 183 184 :param str iface: Interface to send packet to 185 :keyword src: Source packet IP 186 :type src: class:`scapy.fields.SourceIPField` 187 :keyword dst: Destination packet IP 188 :type dst: class:`scapy.layers.inet.DestIPField` 189 :keyword icmp_type: ICMP type 190 :type icmp_type: class:`scapy.fields.ByteEnumField` 191 :keyword icmp_code: ICMP code 192 :type icmp_code: class:`scapy.fields.MultiEnumField` 193 194 :keyword ihl: Internet Header Length, defaults to None 195 :type ihl: class:`scapy.fields.BitField`, optional 196 :keyword flags: IP flags - one of `DF`, `MF` or `evil`, defaults to 0 197 :type flags: class:`scapy.fields.FlagsField`, optional 198 :keyword opts: Include IP options - one of `EOL`, `NOP`, `NOP-40`, `unk`, 199 `unk-40`, `RR`, `RR-same`, `RR-trunc`, `LSRR`, `LSRR-trunc`, `SSRR` or 200 `SSRR-trunc`, defaults to None 201 :type opts: str, optional 202 :keyword oip_ihl: Inner packet's Internet Header Length, defaults to None 203 :type oip_ihl: class:`scapy.fields.BitField`, optional 204 :keyword special: Send a special packet - one of `no-payload`, `not-mine`, 205 `tcp`, `udp`, `wrong` or `warp`, defaults to None 206 :type special: str, optional 207 :keyword icmp_pptr: ICMP pointer, defaults to 0 208 :type icmp_pptr: class:`scapy.fields.ByteField` 209 :keyword icmp_gwaddr: ICMP gateway IP address, defaults to "0.0.0.0" 210 :type icmp_gwaddr: class:`scapy.fields.IPField` 211 :keyword icmp_nextmtu: ICMP next MTU, defaults to 0 212 :type icmp_nextmtu: class:`scapy.fields.ShortField` 213 :keyword icmp_otime: ICMP originate timestamp, defaults to 0 214 :type icmp_otime: class:`scapy.layers.inet.ICMPTimeStampField` 215 :keyword icmp_rtime: ICMP receive timestamp, defaults to 0 216 :type icmp_rtime: class:`scapy.layers.inet.ICMPTimeStampField` 217 :keyword icmp_ttime: ICMP transmit timestamp, defaults to 0 218 :type icmp_ttime: class:`scapy.layers.inet.ICMPTimeStampField` 219 :keyword icmp_mask: ICMP address mask, defaults to "0.0.0.0" 220 :type icmp_mask: class:`scapy.fields.IPField` 221 :keyword request: Request type - one of `mask` or `timestamp`, 222 defaults to None 223 :type request: str, optional 224 :keyword count: Number of packets to send, defaults to 1 225 :type count: int 226 :keyword dup: Duplicate packets, defaults to `False` 227 :type dup: bool 228 :keyword verbose: Turn on/off verbosity, defaults to `True` 229 :type verbose: bool 230 231 :return: A class:`subprocess.CompletedProcess` with the output from the 232 ping utility 233 :rtype: class:`subprocess.CompletedProcess` 234 """ 235 tun = sc.TunTapInterface(iface) 236 subprocess.run(["ifconfig", tun.iface, "up"], check=True) 237 subprocess.run(["ifconfig", tun.iface, src, dst], check=True) 238 ip_opts = generate_ip_options(opts) 239 ip = sc.IP(ihl=ihl, flags=flags, src=dst, dst=src, options=ip_opts) 240 command = [ 241 "/sbin/ping", 242 "-c", 243 str(count), 244 "-t", 245 str(count), 246 ] 247 if verbose: 248 command += ["-v"] 249 if request == "mask": 250 command += ["-Mm"] 251 if request == "timestamp": 252 command += ["-Mt"] 253 if special: 254 command += ["-p1"] 255 if opts in [ 256 "RR", 257 "RR-same", 258 "RR-trunc", 259 "LSRR", 260 "LSRR-trunc", 261 "SSRR", 262 "SSRR-trunc", 263 ]: 264 command += ["-R"] 265 command += [dst] 266 with subprocess.Popen( 267 args=command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True 268 ) as ping: 269 for dummy in range(count): 270 echo = tun.recv() 271 icmp = sc.ICMP( 272 type=icmp_type, 273 code=icmp_code, 274 id=echo[sc.ICMP].id, 275 seq=echo[sc.ICMP].seq, 276 ts_ori=icmp_otime, 277 ts_rx=icmp_rtime, 278 ts_tx=icmp_ttime, 279 gw=icmp_gwaddr, 280 ptr=icmp_pptr, 281 addr_mask=icmp_mask, 282 nexthopmtu=icmp_nextmtu, 283 ) 284 pkt = build_response_packet(echo, ip, icmp, oip_ihl, special) 285 tun.send(pkt) 286 if dup is True: 287 tun.send(pkt) 288 stdout, stderr = ping.communicate() 289 return subprocess.CompletedProcess( 290 ping.args, ping.returncode, stdout, stderr 291 ) 292 293 294def redact(output): 295 """Redact some elements of ping's output""" 296 pattern_replacements = [ 297 (r"localhost \([0-9]{1,3}(\.[0-9]{1,3}){3}\)", "localhost"), 298 (r"from [0-9]{1,3}(\.[0-9]{1,3}){3}", "from"), 299 ("hlim=[0-9]*", "hlim="), 300 ("ttl=[0-9]*", "ttl="), 301 ("time=[0-9.-]*", "time="), 302 ("cp: .*", "cp: xx xx xx xx xx xx xx xx"), 303 ("dp: .*", "dp: xx xx xx xx xx xx xx xx"), 304 (r"\(-[0-9\.]+[0-9]+ ms\)", "(- ms)"), 305 (r"[0-9\.]+/[0-9.]+", "/"), 306 ] 307 for pattern, repl in pattern_replacements: 308 output = re.sub(pattern, repl, output) 309 return output 310 311 312class TestPing(SingleVnetTestTemplate): 313 IPV6_PREFIXES: List[str] = ["2001:db8::1/64"] 314 IPV4_PREFIXES: List[str] = ["192.0.2.1/24"] 315 316 # Each param in testdata contains a dictionary with the command, 317 # and the expected outcome (returncode, redacted stdout, and stderr) 318 testdata = [ 319 pytest.param( 320 { 321 "args": "ping -4 -c1 -s56 -t1 localhost", 322 "returncode": 0, 323 "stdout": """\ 324PING localhost: 56 data bytes 32564 bytes from: icmp_seq=0 ttl= time= ms 326 327--- localhost ping statistics --- 3281 packets transmitted, 1 packets received, 0.0% packet loss 329round-trip min/avg/max/stddev = /// ms 330""", 331 "stderr": "", 332 }, 333 id="_4_c1_s56_t1_localhost", 334 ), 335 pytest.param( 336 { 337 "args": "ping -6 -c1 -s8 -t1 localhost", 338 "returncode": 0, 339 "stdout": """\ 340PING(56=40+8+8 bytes) ::1 --> ::1 34116 bytes from ::1, icmp_seq=0 hlim= time= ms 342 343--- localhost ping statistics --- 3441 packets transmitted, 1 packets received, 0.0% packet loss 345round-trip min/avg/max/stddev = /// ms 346""", 347 "stderr": "", 348 }, 349 id="_6_c1_s8_t1_localhost", 350 ), 351 pytest.param( 352 { 353 "args": "ping -A -c1 192.0.2.1", 354 "returncode": 0, 355 "stdout": """\ 356PING 192.0.2.1 (192.0.2.1): 56 data bytes 35764 bytes from: icmp_seq=0 ttl= time= ms 358 359--- 192.0.2.1 ping statistics --- 3601 packets transmitted, 1 packets received, 0.0% packet loss 361round-trip min/avg/max/stddev = /// ms 362""", 363 "stderr": "", 364 }, 365 id="_A_c1_192_0_2_1", 366 ), 367 pytest.param( 368 { 369 "args": "ping -A -c1 192.0.2.2", 370 "returncode": 2, 371 "stdout": """\ 372PING 192.0.2.2 (192.0.2.2): 56 data bytes 373 374--- 192.0.2.2 ping statistics --- 3751 packets transmitted, 0 packets received, 100.0% packet loss 376""", 377 "stderr": "", 378 }, 379 id="_A_c1_192_0_2_2", 380 ), 381 pytest.param( 382 { 383 "args": "ping -A -c1 2001:db8::1", 384 "returncode": 0, 385 "stdout": """\ 386PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 38716 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 388 389--- 2001:db8::1 ping statistics --- 3901 packets transmitted, 1 packets received, 0.0% packet loss 391round-trip min/avg/max/stddev = /// ms 392""", 393 "stderr": "", 394 }, 395 id="_A_c1_2001_db8__1", 396 ), 397 pytest.param( 398 { 399 "args": "ping -A -c1 2001:db8::2", 400 "returncode": 2, 401 "stdout": """\ 402PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 403 404--- 2001:db8::2 ping statistics --- 4051 packets transmitted, 0 packets received, 100.0% packet loss 406""", 407 "stderr": "", 408 }, 409 id="_A_c1_2001_db8__2", 410 ), 411 pytest.param( 412 { 413 "args": "ping -A -c3 192.0.2.1", 414 "returncode": 0, 415 "stdout": """\ 416PING 192.0.2.1 (192.0.2.1): 56 data bytes 41764 bytes from: icmp_seq=0 ttl= time= ms 41864 bytes from: icmp_seq=1 ttl= time= ms 41964 bytes from: icmp_seq=2 ttl= time= ms 420 421--- 192.0.2.1 ping statistics --- 4223 packets transmitted, 3 packets received, 0.0% packet loss 423round-trip min/avg/max/stddev = /// ms 424""", 425 "stderr": "", 426 }, 427 id="_A_3_192_0.2.1", 428 ), 429 pytest.param( 430 { 431 "args": "ping -A -c3 192.0.2.2", 432 "returncode": 2, 433 "stdout": """\ 434\x07\x07PING 192.0.2.2 (192.0.2.2): 56 data bytes 435 436--- 192.0.2.2 ping statistics --- 4373 packets transmitted, 0 packets received, 100.0% packet loss 438""", 439 "stderr": "", 440 }, 441 id="_A_c3_192_0_2_2", 442 ), 443 pytest.param( 444 { 445 "args": "ping -A -c3 2001:db8::1", 446 "returncode": 0, 447 "stdout": """\ 448PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 44916 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 45016 bytes from 2001:db8::1, icmp_seq=1 hlim= time= ms 45116 bytes from 2001:db8::1, icmp_seq=2 hlim= time= ms 452 453--- 2001:db8::1 ping statistics --- 4543 packets transmitted, 3 packets received, 0.0% packet loss 455round-trip min/avg/max/stddev = /// ms 456""", 457 "stderr": "", 458 }, 459 id="_A_c3_2001_db8__1", 460 ), 461 pytest.param( 462 { 463 "args": "ping -A -c3 2001:db8::2", 464 "returncode": 2, 465 "stdout": """\ 466\x07\x07PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 467 468--- 2001:db8::2 ping statistics --- 4693 packets transmitted, 0 packets received, 100.0% packet loss 470""", 471 "stderr": "", 472 }, 473 id="_A_c3_2001_db8__2", 474 ), 475 pytest.param( 476 { 477 "args": "ping -c1 192.0.2.1", 478 "returncode": 0, 479 "stdout": """\ 480PING 192.0.2.1 (192.0.2.1): 56 data bytes 48164 bytes from: icmp_seq=0 ttl= time= ms 482 483--- 192.0.2.1 ping statistics --- 4841 packets transmitted, 1 packets received, 0.0% packet loss 485round-trip min/avg/max/stddev = /// ms 486""", 487 "stderr": "", 488 }, 489 id="_c1_192_0_2_1", 490 ), 491 pytest.param( 492 { 493 "args": "ping -c1 192.0.2.2", 494 "returncode": 2, 495 "stdout": """\ 496PING 192.0.2.2 (192.0.2.2): 56 data bytes 497 498--- 192.0.2.2 ping statistics --- 4991 packets transmitted, 0 packets received, 100.0% packet loss 500""", 501 "stderr": "", 502 }, 503 id="_c1_192_0_2_2", 504 ), 505 pytest.param( 506 { 507 "args": "ping -c1 2001:db8::1", 508 "returncode": 0, 509 "stdout": """\ 510PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 51116 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 512 513--- 2001:db8::1 ping statistics --- 5141 packets transmitted, 1 packets received, 0.0% packet loss 515round-trip min/avg/max/stddev = /// ms 516""", 517 "stderr": "", 518 }, 519 id="_c1_2001_db8__1", 520 ), 521 pytest.param( 522 { 523 "args": "ping -c1 2001:db8::2", 524 "returncode": 2, 525 "stdout": """\ 526PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 527 528--- 2001:db8::2 ping statistics --- 5291 packets transmitted, 0 packets received, 100.0% packet loss 530""", 531 "stderr": "", 532 }, 533 id="_c1_2001_db8__2", 534 ), 535 pytest.param( 536 { 537 "args": "ping -c1 -S127.0.0.1 -s56 -t1 localhost", 538 "returncode": 0, 539 "stdout": """\ 540PING localhost from: 56 data bytes 54164 bytes from: icmp_seq=0 ttl= time= ms 542 543--- localhost ping statistics --- 5441 packets transmitted, 1 packets received, 0.0% packet loss 545round-trip min/avg/max/stddev = /// ms 546""", 547 "stderr": "", 548 }, 549 id="_c1_S127_0_0_1_s56_t1_localhost", 550 ), 551 pytest.param( 552 { 553 "args": "ping -c1 -S::1 -s8 -t1 localhost", 554 "returncode": 0, 555 "stdout": """\ 556PING(56=40+8+8 bytes) ::1 --> ::1 55716 bytes from ::1, icmp_seq=0 hlim= time= ms 558 559--- localhost ping statistics --- 5601 packets transmitted, 1 packets received, 0.0% packet loss 561round-trip min/avg/max/stddev = /// ms 562""", 563 "stderr": "", 564 }, 565 id="_c1_S__1_s8_t1_localhost", 566 ), 567 pytest.param( 568 { 569 "args": "ping -c3 192.0.2.1", 570 "returncode": 0, 571 "stdout": """\ 572PING 192.0.2.1 (192.0.2.1): 56 data bytes 57364 bytes from: icmp_seq=0 ttl= time= ms 57464 bytes from: icmp_seq=1 ttl= time= ms 57564 bytes from: icmp_seq=2 ttl= time= ms 576 577--- 192.0.2.1 ping statistics --- 5783 packets transmitted, 3 packets received, 0.0% packet loss 579round-trip min/avg/max/stddev = /// ms 580""", 581 "stderr": "", 582 }, 583 id="_c3_192_0_2_1", 584 ), 585 pytest.param( 586 { 587 "args": "ping -c3 192.0.2.2", 588 "returncode": 2, 589 "stdout": """\ 590PING 192.0.2.2 (192.0.2.2): 56 data bytes 591 592--- 192.0.2.2 ping statistics --- 5933 packets transmitted, 0 packets received, 100.0% packet loss 594""", 595 "stderr": "", 596 }, 597 id="_c3_192_0_2_2", 598 ), 599 pytest.param( 600 { 601 "args": "ping -c3 2001:db8::1", 602 "returncode": 0, 603 "stdout": """\ 604PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 60516 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 60616 bytes from 2001:db8::1, icmp_seq=1 hlim= time= ms 60716 bytes from 2001:db8::1, icmp_seq=2 hlim= time= ms 608 609--- 2001:db8::1 ping statistics --- 6103 packets transmitted, 3 packets received, 0.0% packet loss 611round-trip min/avg/max/stddev = /// ms 612""", 613 "stderr": "", 614 }, 615 id="_c3_2001_db8__1", 616 ), 617 pytest.param( 618 { 619 "args": "ping -c3 2001:db8::2", 620 "returncode": 2, 621 "stdout": """\ 622PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 623 624--- 2001:db8::2 ping statistics --- 6253 packets transmitted, 0 packets received, 100.0% packet loss 626""", 627 "stderr": "", 628 }, 629 id="_c3_2001_db8__2", 630 ), 631 pytest.param( 632 { 633 "args": "ping -q -c1 192.0.2.1", 634 "returncode": 0, 635 "stdout": """\ 636PING 192.0.2.1 (192.0.2.1): 56 data bytes 637 638--- 192.0.2.1 ping statistics --- 6391 packets transmitted, 1 packets received, 0.0% packet loss 640round-trip min/avg/max/stddev = /// ms 641""", 642 "stderr": "", 643 }, 644 id="_q_c1_192_0_2_1", 645 ), 646 pytest.param( 647 { 648 "args": "ping -q -c1 192.0.2.2", 649 "returncode": 2, 650 "stdout": """\ 651PING 192.0.2.2 (192.0.2.2): 56 data bytes 652 653--- 192.0.2.2 ping statistics --- 6541 packets transmitted, 0 packets received, 100.0% packet loss 655""", 656 "stderr": "", 657 }, 658 id="_q_c1_192_0_2_2", 659 ), 660 pytest.param( 661 { 662 "args": "ping -q -c1 2001:db8::1", 663 "returncode": 0, 664 "stdout": """\ 665PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 666 667--- 2001:db8::1 ping statistics --- 6681 packets transmitted, 1 packets received, 0.0% packet loss 669round-trip min/avg/max/stddev = /// ms 670""", 671 "stderr": "", 672 }, 673 id="_q_c1_2001_db8__1", 674 ), 675 pytest.param( 676 { 677 "args": "ping -q -c1 2001:db8::2", 678 "returncode": 2, 679 "stdout": """\ 680PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 681 682--- 2001:db8::2 ping statistics --- 6831 packets transmitted, 0 packets received, 100.0% packet loss 684""", 685 "stderr": "", 686 }, 687 id="_q_c1_2001_db8__2", 688 ), 689 pytest.param( 690 { 691 "args": "ping -q -c3 192.0.2.1", 692 "returncode": 0, 693 "stdout": """\ 694PING 192.0.2.1 (192.0.2.1): 56 data bytes 695 696--- 192.0.2.1 ping statistics --- 6973 packets transmitted, 3 packets received, 0.0% packet loss 698round-trip min/avg/max/stddev = /// ms 699""", 700 "stderr": "", 701 }, 702 id="_q_c3_192_0_2_1", 703 ), 704 pytest.param( 705 { 706 "args": "ping -q -c3 192.0.2.2", 707 "returncode": 2, 708 "stdout": """\ 709PING 192.0.2.2 (192.0.2.2): 56 data bytes 710 711--- 192.0.2.2 ping statistics --- 7123 packets transmitted, 0 packets received, 100.0% packet loss 713""", 714 "stderr": "", 715 }, 716 id="_q_c3_192_0_2_2", 717 ), 718 pytest.param( 719 { 720 "args": "ping -q -c3 2001:db8::1", 721 "returncode": 0, 722 "stdout": """\ 723PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 724 725--- 2001:db8::1 ping statistics --- 7263 packets transmitted, 3 packets received, 0.0% packet loss 727round-trip min/avg/max/stddev = /// ms 728""", 729 "stderr": "", 730 }, 731 id="_q_c3_2001_db8__1", 732 ), 733 pytest.param( 734 { 735 "args": "ping -q -c3 2001:db8::2", 736 "returncode": 2, 737 "stdout": """\ 738PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 739 740--- 2001:db8::2 ping statistics --- 7413 packets transmitted, 0 packets received, 100.0% packet loss 742""", 743 "stderr": "", 744 }, 745 id="_q_c3_2001_db8__2", 746 ), 747 ] 748 749 @pytest.mark.parametrize("expected", testdata) 750 @pytest.mark.require_user("root") 751 @pytest.mark.require_user("unprivileged") 752 def test_ping(self, expected): 753 """Test ping""" 754 ping = subprocess.run( 755 expected["args"].split(), 756 capture_output=True, 757 timeout=15, 758 text=True, 759 ) 760 assert ping.returncode == expected["returncode"] 761 assert redact(ping.stdout) == expected["stdout"] 762 assert ping.stderr == expected["stderr"] 763 764 # Each param in ping46_testdata contains a dictionary with the arguments 765 # and the expected outcome (returncode, redacted stdout, and stderr) 766 # common to `ping -4` and `ping -6` 767 ping46_testdata = [ 768 pytest.param( 769 { 770 "args": "-Wx localhost", 771 "returncode": os.EX_USAGE, 772 "stdout": "", 773 "stderr": "ping: invalid timing interval: `x'\n", 774 }, 775 id="_Wx_localhost", 776 ), 777 ] 778 779 @pytest.mark.parametrize("expected", ping46_testdata) 780 @pytest.mark.require_user("root") 781 @pytest.mark.require_user("unprivileged") 782 def test_ping_46(self, expected): 783 """Test ping -4/ping -6""" 784 for version in [4, 6]: 785 ping = subprocess.run( 786 ["ping", f"-{version}"] + expected["args"].split(), 787 capture_output=True, 788 timeout=15, 789 text=True, 790 ) 791 assert ping.returncode == expected["returncode"] 792 assert redact(ping.stdout) == expected["stdout"] 793 assert ping.stderr == expected["stderr"] 794 795 # Each param in pinger_testdata contains a dictionary with the keywords to 796 # `pinger()` and a dictionary with the expected outcome (returncode, 797 # stdout, stderr, and if ping's output is redacted) 798 pinger_testdata = [ 799 pytest.param( 800 { 801 "src": "192.0.2.1", 802 "dst": "192.0.2.2", 803 "icmp_type": 0, 804 "icmp_code": 0, 805 }, 806 { 807 "returncode": 0, 808 "stdout": """\ 809PING 192.0.2.2 (192.0.2.2): 56 data bytes 81064 bytes from: icmp_seq=0 ttl= time= ms 811 812--- 192.0.2.2 ping statistics --- 8131 packets transmitted, 1 packets received, 0.0% packet loss 814round-trip min/avg/max/stddev = /// ms 815""", 816 "stderr": "", 817 "redacted": True, 818 }, 819 id="_0_0", 820 ), 821 pytest.param( 822 { 823 "src": "192.0.2.1", 824 "dst": "192.0.2.2", 825 "icmp_type": 0, 826 "icmp_code": 0, 827 "opts": "EOL", 828 }, 829 { 830 "returncode": 0, 831 "stdout": """\ 832PING 192.0.2.2 (192.0.2.2): 56 data bytes 83364 bytes from: icmp_seq=0 ttl= time= ms 834wrong total length 88 instead of 84 835 836--- 192.0.2.2 ping statistics --- 8371 packets transmitted, 1 packets received, 0.0% packet loss 838round-trip min/avg/max/stddev = /// ms 839""", 840 "stderr": "", 841 "redacted": True, 842 }, 843 id="_0_0_opts_EOL", 844 ), 845 pytest.param( 846 { 847 "src": "192.0.2.1", 848 "dst": "192.0.2.2", 849 "icmp_type": 0, 850 "icmp_code": 0, 851 "opts": "LSRR", 852 }, 853 { 854 "returncode": 0, 855 "stdout": """\ 856PING 192.0.2.2 (192.0.2.2): 56 data bytes 85764 bytes from: icmp_seq=0 ttl= time= ms 858LSRR: 192.0.2.10 859 192.0.2.20 860 192.0.2.30 861 192.0.2.40 862 192.0.2.50 863 192.0.2.60 864 192.0.2.70 865 192.0.2.80 866 192.0.2.90 867 868--- 192.0.2.2 ping statistics --- 8691 packets transmitted, 1 packets received, 0.0% packet loss 870round-trip min/avg/max/stddev = /// ms 871""", 872 "stderr": "", 873 "redacted": True, 874 }, 875 id="_0_0_opts_LSRR", 876 ), 877 pytest.param( 878 { 879 "src": "192.0.2.1", 880 "dst": "192.0.2.2", 881 "icmp_type": 0, 882 "icmp_code": 0, 883 "opts": "LSRR-trunc", 884 }, 885 { 886 "returncode": 0, 887 "stdout": """\ 888PING 192.0.2.2 (192.0.2.2): 56 data bytes 88964 bytes from: icmp_seq=0 ttl= time= ms 890LSRR: (truncated route) 891 892--- 192.0.2.2 ping statistics --- 8931 packets transmitted, 1 packets received, 0.0% packet loss 894round-trip min/avg/max/stddev = /// ms 895""", 896 "stderr": "", 897 "redacted": True, 898 }, 899 id="_0_0_opts_LSRR_trunc", 900 ), 901 pytest.param( 902 { 903 "src": "192.0.2.1", 904 "dst": "192.0.2.2", 905 "icmp_type": 0, 906 "icmp_code": 0, 907 "opts": "SSRR", 908 }, 909 { 910 "returncode": 0, 911 "stdout": """\ 912PING 192.0.2.2 (192.0.2.2): 56 data bytes 91364 bytes from: icmp_seq=0 ttl= time= ms 914SSRR: 192.0.2.10 915 192.0.2.20 916 192.0.2.30 917 192.0.2.40 918 192.0.2.50 919 192.0.2.60 920 192.0.2.70 921 192.0.2.80 922 192.0.2.90 923 924--- 192.0.2.2 ping statistics --- 9251 packets transmitted, 1 packets received, 0.0% packet loss 926round-trip min/avg/max/stddev = /// ms 927""", 928 "stderr": "", 929 "redacted": True, 930 }, 931 id="_0_0_opts_SSRR", 932 ), 933 pytest.param( 934 { 935 "src": "192.0.2.1", 936 "dst": "192.0.2.2", 937 "icmp_type": 0, 938 "icmp_code": 0, 939 "opts": "SSRR-trunc", 940 }, 941 { 942 "returncode": 0, 943 "stdout": """\ 944PING 192.0.2.2 (192.0.2.2): 56 data bytes 94564 bytes from: icmp_seq=0 ttl= time= ms 946SSRR: (truncated route) 947 948--- 192.0.2.2 ping statistics --- 9491 packets transmitted, 1 packets received, 0.0% packet loss 950round-trip min/avg/max/stddev = /// ms 951""", 952 "stderr": "", 953 "redacted": True, 954 }, 955 id="_0_0_opts_SSRR_trunc", 956 ), 957 pytest.param( 958 { 959 "src": "192.0.2.1", 960 "dst": "192.0.2.2", 961 "icmp_type": 0, 962 "icmp_code": 0, 963 "opts": "RR", 964 }, 965 { 966 "returncode": 0, 967 "stdout": """\ 968PING 192.0.2.2 (192.0.2.2): 56 data bytes 96964 bytes from: icmp_seq=0 ttl= time= ms 970RR: 192.0.2.10 971 192.0.2.20 972 192.0.2.30 973 192.0.2.40 974 192.0.2.50 975 192.0.2.60 976 192.0.2.70 977 192.0.2.80 978 192.0.2.90 979 980--- 192.0.2.2 ping statistics --- 9811 packets transmitted, 1 packets received, 0.0% packet loss 982round-trip min/avg/max/stddev = /// ms 983""", 984 "stderr": "", 985 "redacted": True, 986 }, 987 id="_0_0_opts_RR", 988 ), 989 pytest.param( 990 { 991 "src": "192.0.2.1", 992 "dst": "192.0.2.2", 993 "icmp_type": 0, 994 "icmp_code": 0, 995 "opts": "RR-same", 996 }, 997 { 998 "returncode": 0, 999 "stdout": """\ 1000PING 192.0.2.2 (192.0.2.2): 56 data bytes 100164 bytes from: icmp_seq=0 ttl= time= ms (same route) 1002 1003--- 192.0.2.2 ping statistics --- 10041 packets transmitted, 1 packets received, 0.0% packet loss 1005round-trip min/avg/max/stddev = /// ms 1006""", 1007 "stderr": "", 1008 "redacted": True, 1009 }, 1010 id="_0_0_opts_RR_same", 1011 ), 1012 pytest.param( 1013 { 1014 "src": "192.0.2.1", 1015 "dst": "192.0.2.2", 1016 "icmp_type": 0, 1017 "icmp_code": 0, 1018 "opts": "RR-trunc", 1019 }, 1020 { 1021 "returncode": 0, 1022 "stdout": """\ 1023PING 192.0.2.2 (192.0.2.2): 56 data bytes 102464 bytes from: icmp_seq=0 ttl= time= ms 1025RR: (truncated route) 1026 1027--- 192.0.2.2 ping statistics --- 10281 packets transmitted, 1 packets received, 0.0% packet loss 1029round-trip min/avg/max/stddev = /// ms 1030""", 1031 "stderr": "", 1032 "redacted": True, 1033 }, 1034 id="_0_0_opts_RR_trunc", 1035 ), 1036 pytest.param( 1037 { 1038 "src": "192.0.2.1", 1039 "dst": "192.0.2.2", 1040 "icmp_type": 0, 1041 "icmp_code": 0, 1042 "opts": "NOP", 1043 }, 1044 { 1045 "returncode": 0, 1046 "stdout": """\ 1047PING 192.0.2.2 (192.0.2.2): 56 data bytes 104864 bytes from: icmp_seq=0 ttl= time= ms 1049wrong total length 88 instead of 84 1050NOP 1051 1052--- 192.0.2.2 ping statistics --- 10531 packets transmitted, 1 packets received, 0.0% packet loss 1054round-trip min/avg/max/stddev = /// ms 1055""", 1056 "stderr": "", 1057 "redacted": True, 1058 }, 1059 id="_0_0_opts_NOP", 1060 ), 1061 pytest.param( 1062 { 1063 "src": "192.0.2.1", 1064 "dst": "192.0.2.2", 1065 "icmp_type": 3, 1066 "icmp_code": 1, 1067 "ihl": 0x4, 1068 }, 1069 { 1070 "returncode": 2, 1071 "stdout": """\ 1072PING 192.0.2.2 (192.0.2.2): 56 data bytes 1073 1074--- 192.0.2.2 ping statistics --- 10751 packets transmitted, 0 packets received, 100.0% packet loss 1076""", 1077 "stderr": "", # "IHL too short" message not shown 1078 "redacted": False, 1079 }, 1080 id="_IHL_too_short", 1081 ), 1082 pytest.param( 1083 { 1084 "src": "192.0.2.1", 1085 "dst": "192.0.2.2", 1086 "icmp_type": 3, 1087 "icmp_code": 1, 1088 "special": "no-payload", 1089 }, 1090 { 1091 "returncode": 2, 1092 "stdout": """\ 1093PATTERN: 0x01 1094PING 192.0.2.2 (192.0.2.2): 56 data bytes 1095 1096--- 192.0.2.2 ping statistics --- 10971 packets transmitted, 0 packets received, 100.0% packet loss 1098""", 1099 "stderr": """\ 1100ping: quoted data too short (28 bytes) from 192.0.2.2 1101""", 1102 "redacted": False, 1103 }, 1104 id="_quoted_data_too_short", 1105 ), 1106 pytest.param( 1107 { 1108 "src": "192.0.2.1", 1109 "dst": "192.0.2.2", 1110 "icmp_type": 3, 1111 "icmp_code": 1, 1112 "oip_ihl": 0x4, 1113 }, 1114 { 1115 "returncode": 2, 1116 "stdout": """\ 1117PING 192.0.2.2 (192.0.2.2): 56 data bytes 1118 1119--- 192.0.2.2 ping statistics --- 11201 packets transmitted, 0 packets received, 100.0% packet loss 1121""", 1122 "stderr": "", # "inner IHL too short" message not shown 1123 "redacted": False, 1124 }, 1125 id="_inner_IHL_too_short", 1126 ), 1127 pytest.param( 1128 { 1129 "src": "192.0.2.1", 1130 "dst": "192.0.2.2", 1131 "icmp_type": 3, 1132 "icmp_code": 1, 1133 "oip_ihl": 0xF, 1134 }, 1135 { 1136 "returncode": 2, 1137 "stdout": """\ 1138PING 192.0.2.2 (192.0.2.2): 56 data bytes 1139 1140--- 192.0.2.2 ping statistics --- 11411 packets transmitted, 0 packets received, 100.0% packet loss 1142""", 1143 "stderr": """\ 1144ping: inner packet too short (84 bytes) from 192.0.2.2 1145""", 1146 "redacted": False, 1147 }, 1148 id="_inner_packet_too_short", 1149 ), 1150 pytest.param( 1151 { 1152 "src": "192.0.2.1", 1153 "dst": "192.0.2.2", 1154 "icmp_type": 3, 1155 "icmp_code": 1, 1156 "oip_ihl": 0xF, 1157 "special": "no-payload", 1158 }, 1159 { 1160 "returncode": 2, 1161 "stdout": """\ 1162PATTERN: 0x01 1163PING 192.0.2.2 (192.0.2.2): 56 data bytes 1164 1165--- 192.0.2.2 ping statistics --- 11661 packets transmitted, 0 packets received, 100.0% packet loss 1167""", 1168 "stderr": "", 1169 "redacted": False, 1170 }, 1171 id="_max_inner_packet_ihl_without_payload", 1172 ), 1173 pytest.param( 1174 { 1175 "src": "192.0.2.1", 1176 "dst": "192.0.2.2", 1177 "icmp_type": 0, 1178 "icmp_code": 0, 1179 "opts": "NOP-40", 1180 }, 1181 { 1182 "returncode": 0, 1183 "stdout": """\ 1184PING 192.0.2.2 (192.0.2.2): 56 data bytes 118564 bytes from: icmp_seq=0 ttl= time= ms 1186wrong total length 124 instead of 84 1187NOP 1188NOP 1189NOP 1190NOP 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 1227 1228--- 192.0.2.2 ping statistics --- 12291 packets transmitted, 1 packets received, 0.0% packet loss 1230round-trip min/avg/max/stddev = /// ms 1231""", 1232 "stderr": "", 1233 "redacted": True, 1234 }, 1235 id="_0_0_opts_NOP_40", 1236 ), 1237 pytest.param( 1238 { 1239 "src": "192.0.2.1", 1240 "dst": "192.0.2.2", 1241 "icmp_type": 0, 1242 "icmp_code": 0, 1243 "opts": "unk", 1244 }, 1245 { 1246 "returncode": 0, 1247 "stdout": """\ 1248PING 192.0.2.2 (192.0.2.2): 56 data bytes 124964 bytes from: icmp_seq=0 ttl= time= ms 1250wrong total length 88 instead of 84 1251unknown option 9f 1252 1253--- 192.0.2.2 ping statistics --- 12541 packets transmitted, 1 packets received, 0.0% packet loss 1255round-trip min/avg/max/stddev = /// ms 1256""", 1257 "stderr": "", 1258 "redacted": True, 1259 }, 1260 id="_0_0_opts_unk", 1261 ), 1262 pytest.param( 1263 { 1264 "src": "192.0.2.1", 1265 "dst": "192.0.2.2", 1266 "icmp_type": 3, 1267 "icmp_code": 1, 1268 "opts": "NOP-40", 1269 }, 1270 { 1271 "returncode": 2, 1272 "stdout": """\ 1273PING 192.0.2.2 (192.0.2.2): 56 data bytes 1274132 bytes from 192.0.2.2: Destination Host Unreachable 1275Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Opts 1276 4 f 00 007c 0001 0 0000 40 01 d868 192.0.2.1 192.0.2.2 01010101010101010101010101010101010101010101010101010101010101010101010101010101 1277 1278 1279--- 192.0.2.2 ping statistics --- 12801 packets transmitted, 0 packets received, 100.0% packet loss 1281""", 1282 "stderr": "", 1283 "redacted": False, 1284 }, 1285 id="_3_1_opts_NOP_40", 1286 ), 1287 pytest.param( 1288 { 1289 "src": "192.0.2.1", 1290 "dst": "192.0.2.2", 1291 "icmp_type": 3, 1292 "icmp_code": 1, 1293 "flags": "DF", 1294 }, 1295 { 1296 "returncode": 2, 1297 "stdout": """\ 1298PING 192.0.2.2 (192.0.2.2): 56 data bytes 129992 bytes from 192.0.2.2: Destination Host Unreachable 1300Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 1301 4 5 00 0054 0001 2 0000 40 01 b6a4 192.0.2.1 192.0.2.2 1302 1303 1304--- 192.0.2.2 ping statistics --- 13051 packets transmitted, 0 packets received, 100.0% packet loss 1306""", 1307 "stderr": "", 1308 "redacted": False, 1309 }, 1310 id="_3_1_flags_DF", 1311 ), 1312 pytest.param( 1313 { 1314 "src": "192.0.2.1", 1315 "dst": "192.0.2.2", 1316 "icmp_type": 3, 1317 "icmp_code": 1, 1318 "special": "tcp", 1319 }, 1320 { 1321 "returncode": 2, 1322 "stdout": """\ 1323PATTERN: 0x01 1324PING 192.0.2.2 (192.0.2.2): 56 data bytes 1325 1326--- 192.0.2.2 ping statistics --- 13271 packets transmitted, 0 packets received, 100.0% packet loss 1328""", 1329 "stderr": """\ 1330ping: quoted data too short (40 bytes) from 192.0.2.2 1331""", 1332 "redacted": False, 1333 }, 1334 id="_3_1_special_tcp", 1335 ), 1336 pytest.param( 1337 { 1338 "src": "192.0.2.1", 1339 "dst": "192.0.2.2", 1340 "icmp_type": 3, 1341 "icmp_code": 1, 1342 "special": "udp", 1343 }, 1344 { 1345 "returncode": 2, 1346 "stdout": """\ 1347PATTERN: 0x01 1348PING 192.0.2.2 (192.0.2.2): 56 data bytes 1349 1350--- 192.0.2.2 ping statistics --- 13511 packets transmitted, 0 packets received, 100.0% packet loss 1352""", 1353 "stderr": """\ 1354ping: quoted data too short (28 bytes) from 192.0.2.2 1355""", 1356 "redacted": False, 1357 }, 1358 id="_3_1_special_udp", 1359 ), 1360 pytest.param( 1361 { 1362 "src": "192.0.2.1", 1363 "dst": "192.0.2.2", 1364 "icmp_type": 3, 1365 "icmp_code": 1, 1366 "verbose": False, 1367 }, 1368 { 1369 "returncode": 2, 1370 "stdout": """\ 1371PING 192.0.2.2 (192.0.2.2): 56 data bytes 137292 bytes from 192.0.2.2: Destination Host Unreachable 1373Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 1374 4 5 00 0054 0001 0 0000 40 01 f6a4 192.0.2.1 192.0.2.2 1375 1376 1377--- 192.0.2.2 ping statistics --- 13781 packets transmitted, 0 packets received, 100.0% packet loss 1379""", 1380 "stderr": "", 1381 "redacted": False, 1382 }, 1383 id="_3_1_verbose_false", 1384 ), 1385 pytest.param( 1386 { 1387 "src": "192.0.2.1", 1388 "dst": "192.0.2.2", 1389 "icmp_type": 3, 1390 "icmp_code": 1, 1391 "special": "not-mine", 1392 "verbose": False, 1393 }, 1394 { 1395 "returncode": 2, 1396 "stdout": """\ 1397PATTERN: 0x01 1398PING 192.0.2.2 (192.0.2.2): 56 data bytes 1399 1400--- 192.0.2.2 ping statistics --- 14011 packets transmitted, 0 packets received, 100.0% packet loss 1402""", 1403 "stderr": "", 1404 "redacted": False, 1405 }, 1406 id="_3_1_special_not_mine_verbose_false", 1407 ), 1408 pytest.param( 1409 { 1410 "src": "192.0.2.1", 1411 "dst": "192.0.2.2", 1412 "icmp_type": 0, 1413 "icmp_code": 0, 1414 "special": "warp", 1415 }, 1416 { 1417 "returncode": 0, 1418 "stdout": """\ 1419PATTERN: 0x01 1420PING 192.0.2.2 (192.0.2.2): 56 data bytes 142164 bytes from: icmp_seq=0 ttl= time= ms 1422 1423--- 192.0.2.2 ping statistics --- 14241 packets transmitted, 1 packets received, 0.0% packet loss 1425round-trip min/avg/max/stddev = /// ms 1426""", 1427 "stderr": """\ 1428ping: time of day goes back (- ms), clamping time to 0 1429""", 1430 "redacted": True, 1431 }, 1432 id="_0_0_special_warp", 1433 ), 1434 pytest.param( 1435 { 1436 "src": "192.0.2.1", 1437 "dst": "192.0.2.2", 1438 "icmp_type": 0, 1439 "icmp_code": 0, 1440 "special": "wrong", 1441 }, 1442 { 1443 "returncode": 0, 1444 "stdout": """\ 1445PATTERN: 0x01 1446PING 192.0.2.2 (192.0.2.2): 56 data bytes 144764 bytes from: icmp_seq=0 ttl= time= ms 1448wrong data byte #55 should be 0x1 but was 0x0 1449cp: xx xx xx xx xx xx xx xx 1450 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1451 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1452 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1453dp: 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 1 1457 1458--- 192.0.2.2 ping statistics --- 14591 packets transmitted, 1 packets received, 0.0% packet loss 1460round-trip min/avg/max/stddev = /// ms 1461""", 1462 "stderr": "", 1463 "redacted": True, 1464 }, 1465 id="_0_0_special_wrong", 1466 ), 1467 ] 1468 1469 @pytest.mark.parametrize("pinger_kargs, expected", pinger_testdata) 1470 @pytest.mark.require_progs(["scapy"]) 1471 @pytest.mark.require_user("root") 1472 def test_pinger(self, pinger_kargs, expected): 1473 """Test ping using pinger(), a reply faker""" 1474 iface = IfaceFactory().create_iface("", "tun")[0].name 1475 ping = pinger(iface, **pinger_kargs) 1476 assert ping.returncode == expected["returncode"] 1477 if expected["redacted"]: 1478 assert redact(ping.stdout) == expected["stdout"] 1479 assert redact(ping.stderr) == expected["stderr"] 1480 else: 1481 assert ping.stdout == expected["stdout"] 1482 assert ping.stderr == expected["stderr"] 1483