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) 15import scapy.all as sc 16 17 18def build_response_packet(echo, ip, icmp, oip_ihl, special): 19 icmp_id_seq_types = [0, 8, 13, 14, 15, 16, 17, 18, 37, 38] 20 oip = echo[sc.IP] 21 oicmp = echo[sc.ICMP] 22 load = echo[sc.ICMP].payload 23 oip[sc.IP].remove_payload() 24 oicmp[sc.ICMP].remove_payload() 25 oicmp.type = 8 26 27 # As if the original IP packet had these set 28 oip.ihl = None 29 oip.len = None 30 oip.id = 1 31 oip.flags = ip.flags 32 oip.chksum = None 33 oip.options = ip.options 34 35 # Inner packet (oip) options 36 if oip_ihl: 37 oip.ihl = oip_ihl 38 39 # Special options 40 if special == "no-payload": 41 load = "" 42 if special == "tcp": 43 oip.proto = "tcp" 44 tcp = sc.TCP(sport=1234, dport=5678) 45 return ip / icmp / oip / tcp 46 if special == "udp": 47 oip.proto = "udp" 48 udp = sc.UDP(sport=1234, dport=5678) 49 return ip / icmp / oip / udp 50 if special == "warp": 51 # Build a package with a timestamp of INT_MAX 52 # (time-warped package) 53 payload_no_timestamp = sc.bytes_hex(load)[16:] 54 load = b"\x7f" + (b"\xff" * 7) + sc.hex_bytes(payload_no_timestamp) 55 if special == "wrong": 56 # Build a package with a wrong last byte 57 payload_no_last_byte = sc.bytes_hex(load)[:-2] 58 load = (sc.hex_bytes(payload_no_last_byte)) + b"\x00" 59 if special == "not-mine": 60 # Modify the ICMP Identifier field 61 oicmp.id += 1 62 63 if icmp.type in icmp_id_seq_types: 64 pkt = ip / icmp / load 65 else: 66 ip.options = "" 67 pkt = ip / icmp / oip / oicmp / load 68 return pkt 69 70 71def generate_ip_options(opts): 72 if not opts: 73 return "" 74 75 routers = [ 76 "192.0.2.10", 77 "192.0.2.20", 78 "192.0.2.30", 79 "192.0.2.40", 80 "192.0.2.50", 81 "192.0.2.60", 82 "192.0.2.70", 83 "192.0.2.80", 84 "192.0.2.90", 85 ] 86 routers_zero = [0, 0, 0, 0, 0, 0, 0, 0, 0] 87 if opts == "EOL": 88 options = sc.IPOption(b"\x00") 89 elif opts == "NOP": 90 options = sc.IPOption(b"\x01") 91 elif opts == "NOP-40": 92 options = sc.IPOption(b"\x01" * 40) 93 elif opts == "RR": 94 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 95 options = sc.IPOption_RR(pointer=40, routers=routers) 96 elif opts == "RR-same": 97 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 98 options = sc.IPOption_RR(pointer=3, routers=routers_zero) 99 elif opts == "RR-trunc": 100 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 101 options = sc.IPOption_RR(length=7, routers=routers_zero) 102 elif opts == "LSRR": 103 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 104 options = sc.IPOption_LSRR(routers=routers) 105 elif opts == "LSRR-trunc": 106 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 107 options = sc.IPOption_LSRR(length=3, routers=routers_zero) 108 elif opts == "SSRR": 109 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 110 options = sc.IPOption_SSRR(routers=routers) 111 elif opts == "SSRR-trunc": 112 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 113 options = sc.IPOption_SSRR(length=3, routers=routers_zero) 114 elif opts == "unk": 115 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 116 options = b"\x9f" 117 elif opts == "unk-40": 118 ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) 119 options = sc.IPOption(b"\x9f" * 40) 120 else: 121 options = "" 122 return options 123 124 125def pinger( 126 # Required arguments 127 # Avoid setting defaults on these arguments, 128 # as we want to set them explicitly in the tests 129 iface: str, 130 /, 131 src: sc.scapy.fields.SourceIPField, 132 dst: sc.scapy.layers.inet.DestIPField, 133 icmp_type: sc.scapy.fields.ByteEnumField, 134 icmp_code: sc.scapy.fields.MultiEnumField, 135 # IP arguments 136 ihl: Optional[sc.scapy.fields.BitField] = None, 137 flags: Optional[sc.scapy.fields.FlagsField] = None, 138 opts: Optional[str] = None, 139 oip_ihl: Optional[sc.scapy.fields.BitField] = None, 140 special: Optional[str] = None, 141 # ICMP arguments 142 # Match names with <netinet/ip_icmp.h> 143 icmp_pptr: sc.scapy.fields.ByteField = 0, 144 icmp_gwaddr: sc.scapy.fields.IPField = "0.0.0.0", 145 icmp_nextmtu: sc.scapy.fields.ShortField = 0, 146 icmp_otime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 147 icmp_rtime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 148 icmp_ttime: sc.scapy.layers.inet.ICMPTimeStampField = 0, 149 icmp_mask: sc.scapy.fields.IPField = "0.0.0.0", 150 request: Optional[str] = None, 151 # Miscellaneous arguments 152 count: int = 1, 153 dup: bool = False, 154 verbose: bool = True, 155) -> subprocess.CompletedProcess: 156 """P I N G E R 157 158 Echo reply faker 159 160 :param str iface: Interface to send packet to 161 :keyword src: Source packet IP 162 :type src: class:`scapy.fields.SourceIPField` 163 :keyword dst: Destination packet IP 164 :type dst: class:`scapy.layers.inet.DestIPField` 165 :keyword icmp_type: ICMP type 166 :type icmp_type: class:`scapy.fields.ByteEnumField` 167 :keyword icmp_code: ICMP code 168 :type icmp_code: class:`scapy.fields.MultiEnumField` 169 170 :keyword ihl: Internet Header Length, defaults to None 171 :type ihl: class:`scapy.fields.BitField`, optional 172 :keyword flags: IP flags - one of `DF`, `MF` or `evil`, defaults to None 173 :type flags: class:`scapy.fields.FlagsField`, optional 174 :keyword opts: Include IP options - one of `EOL`, `NOP`, `NOP-40`, `unk`, 175 `unk-40`, `RR`, `RR-same`, `RR-trunc`, `LSRR`, `LSRR-trunc`, `SSRR` or 176 `SSRR-trunc`, defaults to None 177 :type opts: str, optional 178 :keyword oip_ihl: Inner packet's Internet Header Length, defaults to None 179 :type oip_ihl: class:`scapy.fields.BitField`, optional 180 :keyword special: Send a special packet - one of `no-payload`, `not-mine`, 181 `tcp`, `udp`, `wrong` or `warp`, defaults to None 182 :type special: str, optional 183 :keyword icmp_pptr: ICMP pointer, defaults to 0 184 :type icmp_pptr: class:`scapy.fields.ByteField` 185 :keyword icmp_gwaddr: ICMP gateway IP address, defaults to "0.0.0.0" 186 :type icmp_gwaddr: class:`scapy.fields.IPField` 187 :keyword icmp_nextmtu: ICMP next MTU, defaults to 0 188 :type icmp_nextmtu: class:`scapy.fields.ShortField` 189 :keyword icmp_otime: ICMP originate timestamp, defaults to 0 190 :type icmp_otime: class:`scapy.layers.inet.ICMPTimeStampField` 191 :keyword icmp_rtime: ICMP receive timestamp, defaults to 0 192 :type icmp_rtime: class:`scapy.layers.inet.ICMPTimeStampField` 193 :keyword icmp_ttime: ICMP transmit timestamp, defaults to 0 194 :type icmp_ttime: class:`scapy.layers.inet.ICMPTimeStampField` 195 :keyword icmp_mask: ICMP address mask, defaults to "0.0.0.0" 196 :type icmp_mask: class:`scapy.fields.IPField` 197 :keyword request: Request type - one of `mask` or `timestamp`, 198 defaults to None 199 :type request: str, optional 200 :keyword count: Number of packets to send, defaults to 1 201 :type count: int 202 :keyword dup: Duplicate packets, defaults to `False` 203 :type dup: bool 204 :keyword verbose: Turn on/off verbosity, defaults to `True` 205 :type verbose: bool 206 207 :return: A class:`subprocess.CompletedProcess` with the output from the 208 ping utility 209 :rtype: class:`subprocess.CompletedProcess` 210 """ 211 tun = sc.TunTapInterface(iface) 212 subprocess.run(["ifconfig", tun.iface, "up"], check=True) 213 subprocess.run(["ifconfig", tun.iface, src, dst], check=True) 214 ip_opts = generate_ip_options(opts) 215 ip = sc.IP(ihl=ihl, flags=flags, src=dst, dst=src, options=ip_opts) 216 command = [ 217 "/sbin/ping", 218 "-c", 219 str(count), 220 "-t", 221 str(count), 222 ] 223 if verbose: 224 command += ["-v"] 225 if request == "mask": 226 command += ["-Mm"] 227 if request == "timestamp": 228 command += ["-Mt"] 229 if special: 230 command += ["-p1"] 231 if opts in [ 232 "RR", 233 "RR-same", 234 "RR-trunc", 235 "LSRR", 236 "LSRR-trunc", 237 "SSRR", 238 "SSRR-trunc", 239 ]: 240 command += ["-R"] 241 command += [dst] 242 with subprocess.Popen( 243 args=command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True 244 ) as ping: 245 for dummy in range(count): 246 echo = tun.recv() 247 icmp = sc.ICMP( 248 type=icmp_type, 249 code=icmp_code, 250 id=echo[sc.ICMP].id, 251 seq=echo[sc.ICMP].seq, 252 ts_ori=icmp_otime, 253 ts_rx=icmp_rtime, 254 ts_tx=icmp_ttime, 255 gw=icmp_gwaddr, 256 ptr=icmp_pptr, 257 addr_mask=icmp_mask, 258 nexthopmtu=icmp_nextmtu, 259 ) 260 pkt = build_response_packet(echo, ip, icmp, oip_ihl, special) 261 tun.send(pkt) 262 if dup is True: 263 tun.send(pkt) 264 stdout, stderr = ping.communicate() 265 return subprocess.CompletedProcess( 266 ping.args, ping.returncode, stdout, stderr 267 ) 268 269 270def redact(output): 271 """Redact some elements of ping's output""" 272 pattern_replacements = [ 273 ("localhost \([0-9]{1,3}(\.[0-9]{1,3}){3}\)", "localhost"), 274 ("from [0-9]{1,3}(\.[0-9]{1,3}){3}", "from"), 275 ("hlim=[0-9]*", "hlim="), 276 ("ttl=[0-9]*", "ttl="), 277 ("time=[0-9.-]*", "time="), 278 ("cp: .*", "cp: xx xx xx xx xx xx xx xx"), 279 ("dp: .*", "dp: xx xx xx xx xx xx xx xx"), 280 ("\(-[0-9\.]+[0-9]+ ms\)", "(- ms)"), 281 ("[0-9\.]+/[0-9.]+", "/"), 282 ] 283 for pattern, repl in pattern_replacements: 284 output = re.sub(pattern, repl, output) 285 return output 286 287 288class TestPing(SingleVnetTestTemplate): 289 IPV6_PREFIXES: List[str] = ["2001:db8::1/64"] 290 IPV4_PREFIXES: List[str] = ["192.0.2.1/24"] 291 292 # Each param in testdata contains a dictionary with the command, 293 # and the expected outcome (returncode, redacted stdout, and stderr) 294 testdata = [ 295 pytest.param( 296 { 297 "args": "ping -4 -c1 -s56 -t1 localhost", 298 "returncode": 0, 299 "stdout": """\ 300PING localhost: 56 data bytes 30164 bytes from: icmp_seq=0 ttl= time= ms 302 303--- localhost ping statistics --- 3041 packets transmitted, 1 packets received, 0.0% packet loss 305round-trip min/avg/max/stddev = /// ms 306""", 307 "stderr": "", 308 }, 309 id="_4_c1_s56_t1_localhost", 310 ), 311 pytest.param( 312 { 313 "args": "ping -6 -c1 -s8 -t1 localhost", 314 "returncode": 0, 315 "stdout": """\ 316PING(56=40+8+8 bytes) ::1 --> ::1 31716 bytes from ::1, icmp_seq=0 hlim= time= ms 318 319--- localhost ping statistics --- 3201 packets transmitted, 1 packets received, 0.0% packet loss 321round-trip min/avg/max/stddev = /// ms 322""", 323 "stderr": "", 324 }, 325 id="_6_c1_s8_t1_localhost", 326 ), 327 pytest.param( 328 { 329 "args": "ping -A -c1 192.0.2.1", 330 "returncode": 0, 331 "stdout": """\ 332PING 192.0.2.1 (192.0.2.1): 56 data bytes 33364 bytes from: icmp_seq=0 ttl= time= ms 334 335--- 192.0.2.1 ping statistics --- 3361 packets transmitted, 1 packets received, 0.0% packet loss 337round-trip min/avg/max/stddev = /// ms 338""", 339 "stderr": "", 340 }, 341 id="_A_c1_192_0_2_1", 342 ), 343 pytest.param( 344 { 345 "args": "ping -A -c1 192.0.2.2", 346 "returncode": 2, 347 "stdout": """\ 348PING 192.0.2.2 (192.0.2.2): 56 data bytes 349 350--- 192.0.2.2 ping statistics --- 3511 packets transmitted, 0 packets received, 100.0% packet loss 352""", 353 "stderr": "", 354 }, 355 id="_A_c1_192_0_2_2", 356 ), 357 pytest.param( 358 { 359 "args": "ping -A -c1 2001:db8::1", 360 "returncode": 0, 361 "stdout": """\ 362PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 36316 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 364 365--- 2001:db8::1 ping statistics --- 3661 packets transmitted, 1 packets received, 0.0% packet loss 367round-trip min/avg/max/stddev = /// ms 368""", 369 "stderr": "", 370 }, 371 id="_A_c1_2001_db8__1", 372 ), 373 pytest.param( 374 { 375 "args": "ping -A -c1 2001:db8::2", 376 "returncode": 2, 377 "stdout": """\ 378PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 379 380--- 2001:db8::2 ping statistics --- 3811 packets transmitted, 0 packets received, 100.0% packet loss 382""", 383 "stderr": "", 384 }, 385 id="_A_c1_2001_db8__2", 386 ), 387 pytest.param( 388 { 389 "args": "ping -A -c3 192.0.2.1", 390 "returncode": 0, 391 "stdout": """\ 392PING 192.0.2.1 (192.0.2.1): 56 data bytes 39364 bytes from: icmp_seq=0 ttl= time= ms 39464 bytes from: icmp_seq=1 ttl= time= ms 39564 bytes from: icmp_seq=2 ttl= time= ms 396 397--- 192.0.2.1 ping statistics --- 3983 packets transmitted, 3 packets received, 0.0% packet loss 399round-trip min/avg/max/stddev = /// ms 400""", 401 "stderr": "", 402 }, 403 id="_A_3_192_0.2.1", 404 ), 405 pytest.param( 406 { 407 "args": "ping -A -c3 192.0.2.2", 408 "returncode": 2, 409 "stdout": """\ 410\x07\x07PING 192.0.2.2 (192.0.2.2): 56 data bytes 411 412--- 192.0.2.2 ping statistics --- 4133 packets transmitted, 0 packets received, 100.0% packet loss 414""", 415 "stderr": "", 416 }, 417 id="_A_c3_192_0_2_2", 418 ), 419 pytest.param( 420 { 421 "args": "ping -A -c3 2001:db8::1", 422 "returncode": 0, 423 "stdout": """\ 424PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 42516 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 42616 bytes from 2001:db8::1, icmp_seq=1 hlim= time= ms 42716 bytes from 2001:db8::1, icmp_seq=2 hlim= time= ms 428 429--- 2001:db8::1 ping statistics --- 4303 packets transmitted, 3 packets received, 0.0% packet loss 431round-trip min/avg/max/stddev = /// ms 432""", 433 "stderr": "", 434 }, 435 id="_A_c3_2001_db8__1", 436 ), 437 pytest.param( 438 { 439 "args": "ping -A -c3 2001:db8::2", 440 "returncode": 2, 441 "stdout": """\ 442\x07\x07PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 443 444--- 2001:db8::2 ping statistics --- 4453 packets transmitted, 0 packets received, 100.0% packet loss 446""", 447 "stderr": "", 448 }, 449 id="_A_c3_2001_db8__2", 450 ), 451 pytest.param( 452 { 453 "args": "ping -c1 192.0.2.1", 454 "returncode": 0, 455 "stdout": """\ 456PING 192.0.2.1 (192.0.2.1): 56 data bytes 45764 bytes from: icmp_seq=0 ttl= time= ms 458 459--- 192.0.2.1 ping statistics --- 4601 packets transmitted, 1 packets received, 0.0% packet loss 461round-trip min/avg/max/stddev = /// ms 462""", 463 "stderr": "", 464 }, 465 id="_c1_192_0_2_1", 466 ), 467 pytest.param( 468 { 469 "args": "ping -c1 192.0.2.2", 470 "returncode": 2, 471 "stdout": """\ 472PING 192.0.2.2 (192.0.2.2): 56 data bytes 473 474--- 192.0.2.2 ping statistics --- 4751 packets transmitted, 0 packets received, 100.0% packet loss 476""", 477 "stderr": "", 478 }, 479 id="_c1_192_0_2_2", 480 ), 481 pytest.param( 482 { 483 "args": "ping -c1 2001:db8::1", 484 "returncode": 0, 485 "stdout": """\ 486PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 48716 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 488 489--- 2001:db8::1 ping statistics --- 4901 packets transmitted, 1 packets received, 0.0% packet loss 491round-trip min/avg/max/stddev = /// ms 492""", 493 "stderr": "", 494 }, 495 id="_c1_2001_db8__1", 496 ), 497 pytest.param( 498 { 499 "args": "ping -c1 2001:db8::2", 500 "returncode": 2, 501 "stdout": """\ 502PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 503 504--- 2001:db8::2 ping statistics --- 5051 packets transmitted, 0 packets received, 100.0% packet loss 506""", 507 "stderr": "", 508 }, 509 id="_c1_2001_db8__2", 510 ), 511 pytest.param( 512 { 513 "args": "ping -c1 -S127.0.0.1 -s56 -t1 localhost", 514 "returncode": 0, 515 "stdout": """\ 516PING localhost from: 56 data bytes 51764 bytes from: icmp_seq=0 ttl= time= ms 518 519--- localhost ping statistics --- 5201 packets transmitted, 1 packets received, 0.0% packet loss 521round-trip min/avg/max/stddev = /// ms 522""", 523 "stderr": "", 524 }, 525 id="_c1_S127_0_0_1_s56_t1_localhost", 526 ), 527 pytest.param( 528 { 529 "args": "ping -c1 -S::1 -s8 -t1 localhost", 530 "returncode": 0, 531 "stdout": """\ 532PING(56=40+8+8 bytes) ::1 --> ::1 53316 bytes from ::1, icmp_seq=0 hlim= time= ms 534 535--- localhost ping statistics --- 5361 packets transmitted, 1 packets received, 0.0% packet loss 537round-trip min/avg/max/stddev = /// ms 538""", 539 "stderr": "", 540 }, 541 id="_c1_S__1_s8_t1_localhost", 542 ), 543 pytest.param( 544 { 545 "args": "ping -c3 192.0.2.1", 546 "returncode": 0, 547 "stdout": """\ 548PING 192.0.2.1 (192.0.2.1): 56 data bytes 54964 bytes from: icmp_seq=0 ttl= time= ms 55064 bytes from: icmp_seq=1 ttl= time= ms 55164 bytes from: icmp_seq=2 ttl= time= ms 552 553--- 192.0.2.1 ping statistics --- 5543 packets transmitted, 3 packets received, 0.0% packet loss 555round-trip min/avg/max/stddev = /// ms 556""", 557 "stderr": "", 558 }, 559 id="_c3_192_0_2_1", 560 ), 561 pytest.param( 562 { 563 "args": "ping -c3 192.0.2.2", 564 "returncode": 2, 565 "stdout": """\ 566PING 192.0.2.2 (192.0.2.2): 56 data bytes 567 568--- 192.0.2.2 ping statistics --- 5693 packets transmitted, 0 packets received, 100.0% packet loss 570""", 571 "stderr": "", 572 }, 573 id="_c3_192_0_2_2", 574 ), 575 pytest.param( 576 { 577 "args": "ping -c3 2001:db8::1", 578 "returncode": 0, 579 "stdout": """\ 580PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 58116 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 58216 bytes from 2001:db8::1, icmp_seq=1 hlim= time= ms 58316 bytes from 2001:db8::1, icmp_seq=2 hlim= time= ms 584 585--- 2001:db8::1 ping statistics --- 5863 packets transmitted, 3 packets received, 0.0% packet loss 587round-trip min/avg/max/stddev = /// ms 588""", 589 "stderr": "", 590 }, 591 id="_c3_2001_db8__1", 592 ), 593 pytest.param( 594 { 595 "args": "ping -c3 2001:db8::2", 596 "returncode": 2, 597 "stdout": """\ 598PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 599 600--- 2001:db8::2 ping statistics --- 6013 packets transmitted, 0 packets received, 100.0% packet loss 602""", 603 "stderr": "", 604 }, 605 id="_c3_2001_db8__2", 606 ), 607 pytest.param( 608 { 609 "args": "ping -q -c1 192.0.2.1", 610 "returncode": 0, 611 "stdout": """\ 612PING 192.0.2.1 (192.0.2.1): 56 data bytes 613 614--- 192.0.2.1 ping statistics --- 6151 packets transmitted, 1 packets received, 0.0% packet loss 616round-trip min/avg/max/stddev = /// ms 617""", 618 "stderr": "", 619 }, 620 id="_q_c1_192_0_2_1", 621 ), 622 pytest.param( 623 { 624 "args": "ping -q -c1 192.0.2.2", 625 "returncode": 2, 626 "stdout": """\ 627PING 192.0.2.2 (192.0.2.2): 56 data bytes 628 629--- 192.0.2.2 ping statistics --- 6301 packets transmitted, 0 packets received, 100.0% packet loss 631""", 632 "stderr": "", 633 }, 634 id="_q_c1_192_0_2_2", 635 ), 636 pytest.param( 637 { 638 "args": "ping -q -c1 2001:db8::1", 639 "returncode": 0, 640 "stdout": """\ 641PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 642 643--- 2001:db8::1 ping statistics --- 6441 packets transmitted, 1 packets received, 0.0% packet loss 645round-trip min/avg/max/stddev = /// ms 646""", 647 "stderr": "", 648 }, 649 id="_q_c1_2001_db8__1", 650 ), 651 pytest.param( 652 { 653 "args": "ping -q -c1 2001:db8::2", 654 "returncode": 2, 655 "stdout": """\ 656PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 657 658--- 2001:db8::2 ping statistics --- 6591 packets transmitted, 0 packets received, 100.0% packet loss 660""", 661 "stderr": "", 662 }, 663 id="_q_c1_2001_db8__2", 664 ), 665 pytest.param( 666 { 667 "args": "ping -q -c3 192.0.2.1", 668 "returncode": 0, 669 "stdout": """\ 670PING 192.0.2.1 (192.0.2.1): 56 data bytes 671 672--- 192.0.2.1 ping statistics --- 6733 packets transmitted, 3 packets received, 0.0% packet loss 674round-trip min/avg/max/stddev = /// ms 675""", 676 "stderr": "", 677 }, 678 id="_q_c3_192_0_2_1", 679 ), 680 pytest.param( 681 { 682 "args": "ping -q -c3 192.0.2.2", 683 "returncode": 2, 684 "stdout": """\ 685PING 192.0.2.2 (192.0.2.2): 56 data bytes 686 687--- 192.0.2.2 ping statistics --- 6883 packets transmitted, 0 packets received, 100.0% packet loss 689""", 690 "stderr": "", 691 }, 692 id="_q_c3_192_0_2_2", 693 ), 694 pytest.param( 695 { 696 "args": "ping -q -c3 2001:db8::1", 697 "returncode": 0, 698 "stdout": """\ 699PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 700 701--- 2001:db8::1 ping statistics --- 7023 packets transmitted, 3 packets received, 0.0% packet loss 703round-trip min/avg/max/stddev = /// ms 704""", 705 "stderr": "", 706 }, 707 id="_q_c3_2001_db8__1", 708 ), 709 pytest.param( 710 { 711 "args": "ping -q -c3 2001:db8::2", 712 "returncode": 2, 713 "stdout": """\ 714PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 715 716--- 2001:db8::2 ping statistics --- 7173 packets transmitted, 0 packets received, 100.0% packet loss 718""", 719 "stderr": "", 720 }, 721 id="_q_c3_2001_db8__2", 722 ), 723 ] 724 725 @pytest.mark.parametrize("expected", testdata) 726 @pytest.mark.require_user("root") 727 def test_ping(self, expected): 728 """Test ping""" 729 ping = subprocess.run( 730 expected["args"].split(), 731 capture_output=True, 732 timeout=15, 733 text=True, 734 ) 735 assert ping.returncode == expected["returncode"] 736 assert redact(ping.stdout) == expected["stdout"] 737 assert ping.stderr == expected["stderr"] 738 739 # Each param in ping46_testdata contains a dictionary with the arguments 740 # and the expected outcome (returncode, redacted stdout, and stderr) 741 # common to `ping -4` and `ping -6` 742 ping46_testdata = [ 743 pytest.param( 744 { 745 "args": "-Wx localhost", 746 "returncode": os.EX_USAGE, 747 "stdout": "", 748 "stderr": "ping: invalid timing interval: `x'\n", 749 }, 750 id="_Wx_localhost", 751 ), 752 ] 753 754 @pytest.mark.parametrize("expected", ping46_testdata) 755 @pytest.mark.require_user("root") 756 def test_ping_46(self, expected): 757 """Test ping -4/ping -6""" 758 for version in [4, 6]: 759 ping = subprocess.run( 760 ["ping", f"-{version}"] + expected["args"].split(), 761 capture_output=True, 762 timeout=15, 763 text=True, 764 ) 765 assert ping.returncode == expected["returncode"] 766 assert redact(ping.stdout) == expected["stdout"] 767 assert ping.stderr == expected["stderr"] 768 769 # Each param in pinger_testdata contains a dictionary with the keywords to 770 # `pinger()` and a dictionary with the expected outcome (returncode, 771 # stdout, stderr, and if ping's output is redacted) 772 pinger_testdata = [ 773 pytest.param( 774 { 775 "src": "192.0.2.1", 776 "dst": "192.0.2.2", 777 "icmp_type": 0, 778 "icmp_code": 0, 779 }, 780 { 781 "returncode": 0, 782 "stdout": """\ 783PING 192.0.2.2 (192.0.2.2): 56 data bytes 78464 bytes from: icmp_seq=0 ttl= time= ms 785 786--- 192.0.2.2 ping statistics --- 7871 packets transmitted, 1 packets received, 0.0% packet loss 788round-trip min/avg/max/stddev = /// ms 789""", 790 "stderr": "", 791 "redacted": True, 792 }, 793 id="_0_0", 794 ), 795 pytest.param( 796 { 797 "src": "192.0.2.1", 798 "dst": "192.0.2.2", 799 "icmp_type": 0, 800 "icmp_code": 0, 801 "opts": "EOL", 802 }, 803 { 804 "returncode": 0, 805 "stdout": """\ 806PING 192.0.2.2 (192.0.2.2): 56 data bytes 80764 bytes from: icmp_seq=0 ttl= time= ms 808wrong total length 88 instead of 84 809 810--- 192.0.2.2 ping statistics --- 8111 packets transmitted, 1 packets received, 0.0% packet loss 812round-trip min/avg/max/stddev = /// ms 813""", 814 "stderr": "", 815 "redacted": True, 816 }, 817 id="_0_0_opts_EOL", 818 ), 819 pytest.param( 820 { 821 "src": "192.0.2.1", 822 "dst": "192.0.2.2", 823 "icmp_type": 0, 824 "icmp_code": 0, 825 "opts": "LSRR", 826 }, 827 { 828 "returncode": 0, 829 "stdout": """\ 830PING 192.0.2.2 (192.0.2.2): 56 data bytes 83164 bytes from: icmp_seq=0 ttl= time= ms 832LSRR: 192.0.2.10 833 192.0.2.20 834 192.0.2.30 835 192.0.2.40 836 192.0.2.50 837 192.0.2.60 838 192.0.2.70 839 192.0.2.80 840 192.0.2.90 841 842--- 192.0.2.2 ping statistics --- 8431 packets transmitted, 1 packets received, 0.0% packet loss 844round-trip min/avg/max/stddev = /// ms 845""", 846 "stderr": "", 847 "redacted": True, 848 }, 849 id="_0_0_opts_LSRR", 850 ), 851 pytest.param( 852 { 853 "src": "192.0.2.1", 854 "dst": "192.0.2.2", 855 "icmp_type": 0, 856 "icmp_code": 0, 857 "opts": "LSRR-trunc", 858 }, 859 { 860 "returncode": 0, 861 "stdout": """\ 862PING 192.0.2.2 (192.0.2.2): 56 data bytes 86364 bytes from: icmp_seq=0 ttl= time= ms 864LSRR: (truncated route) 865 866--- 192.0.2.2 ping statistics --- 8671 packets transmitted, 1 packets received, 0.0% packet loss 868round-trip min/avg/max/stddev = /// ms 869""", 870 "stderr": "", 871 "redacted": True, 872 }, 873 id="_0_0_opts_LSRR_trunc", 874 ), 875 pytest.param( 876 { 877 "src": "192.0.2.1", 878 "dst": "192.0.2.2", 879 "icmp_type": 0, 880 "icmp_code": 0, 881 "opts": "SSRR", 882 }, 883 { 884 "returncode": 0, 885 "stdout": """\ 886PING 192.0.2.2 (192.0.2.2): 56 data bytes 88764 bytes from: icmp_seq=0 ttl= time= ms 888SSRR: 192.0.2.10 889 192.0.2.20 890 192.0.2.30 891 192.0.2.40 892 192.0.2.50 893 192.0.2.60 894 192.0.2.70 895 192.0.2.80 896 192.0.2.90 897 898--- 192.0.2.2 ping statistics --- 8991 packets transmitted, 1 packets received, 0.0% packet loss 900round-trip min/avg/max/stddev = /// ms 901""", 902 "stderr": "", 903 "redacted": True, 904 }, 905 id="_0_0_opts_SSRR", 906 ), 907 pytest.param( 908 { 909 "src": "192.0.2.1", 910 "dst": "192.0.2.2", 911 "icmp_type": 0, 912 "icmp_code": 0, 913 "opts": "SSRR-trunc", 914 }, 915 { 916 "returncode": 0, 917 "stdout": """\ 918PING 192.0.2.2 (192.0.2.2): 56 data bytes 91964 bytes from: icmp_seq=0 ttl= time= ms 920SSRR: (truncated route) 921 922--- 192.0.2.2 ping statistics --- 9231 packets transmitted, 1 packets received, 0.0% packet loss 924round-trip min/avg/max/stddev = /// ms 925""", 926 "stderr": "", 927 "redacted": True, 928 }, 929 id="_0_0_opts_SSRR_trunc", 930 ), 931 pytest.param( 932 { 933 "src": "192.0.2.1", 934 "dst": "192.0.2.2", 935 "icmp_type": 0, 936 "icmp_code": 0, 937 "opts": "RR", 938 }, 939 { 940 "returncode": 0, 941 "stdout": """\ 942PING 192.0.2.2 (192.0.2.2): 56 data bytes 94364 bytes from: icmp_seq=0 ttl= time= ms 944RR: 192.0.2.10 945 192.0.2.20 946 192.0.2.30 947 192.0.2.40 948 192.0.2.50 949 192.0.2.60 950 192.0.2.70 951 192.0.2.80 952 192.0.2.90 953 954--- 192.0.2.2 ping statistics --- 9551 packets transmitted, 1 packets received, 0.0% packet loss 956round-trip min/avg/max/stddev = /// ms 957""", 958 "stderr": "", 959 "redacted": True, 960 }, 961 id="_0_0_opts_RR", 962 ), 963 pytest.param( 964 { 965 "src": "192.0.2.1", 966 "dst": "192.0.2.2", 967 "icmp_type": 0, 968 "icmp_code": 0, 969 "opts": "RR-same", 970 }, 971 { 972 "returncode": 0, 973 "stdout": """\ 974PING 192.0.2.2 (192.0.2.2): 56 data bytes 97564 bytes from: icmp_seq=0 ttl= time= ms (same route) 976 977--- 192.0.2.2 ping statistics --- 9781 packets transmitted, 1 packets received, 0.0% packet loss 979round-trip min/avg/max/stddev = /// ms 980""", 981 "stderr": "", 982 "redacted": True, 983 }, 984 id="_0_0_opts_RR_same", 985 ), 986 pytest.param( 987 { 988 "src": "192.0.2.1", 989 "dst": "192.0.2.2", 990 "icmp_type": 0, 991 "icmp_code": 0, 992 "opts": "RR-trunc", 993 }, 994 { 995 "returncode": 0, 996 "stdout": """\ 997PING 192.0.2.2 (192.0.2.2): 56 data bytes 99864 bytes from: icmp_seq=0 ttl= time= ms 999RR: (truncated route) 1000 1001--- 192.0.2.2 ping statistics --- 10021 packets transmitted, 1 packets received, 0.0% packet loss 1003round-trip min/avg/max/stddev = /// ms 1004""", 1005 "stderr": "", 1006 "redacted": True, 1007 }, 1008 id="_0_0_opts_RR_trunc", 1009 ), 1010 pytest.param( 1011 { 1012 "src": "192.0.2.1", 1013 "dst": "192.0.2.2", 1014 "icmp_type": 0, 1015 "icmp_code": 0, 1016 "opts": "NOP", 1017 }, 1018 { 1019 "returncode": 0, 1020 "stdout": """\ 1021PING 192.0.2.2 (192.0.2.2): 56 data bytes 102264 bytes from: icmp_seq=0 ttl= time= ms 1023wrong total length 88 instead of 84 1024NOP 1025 1026--- 192.0.2.2 ping statistics --- 10271 packets transmitted, 1 packets received, 0.0% packet loss 1028round-trip min/avg/max/stddev = /// ms 1029""", 1030 "stderr": "", 1031 "redacted": True, 1032 }, 1033 id="_0_0_opts_NOP", 1034 ), 1035 pytest.param( 1036 { 1037 "src": "192.0.2.1", 1038 "dst": "192.0.2.2", 1039 "icmp_type": 3, 1040 "icmp_code": 1, 1041 "ihl": 0x4, 1042 }, 1043 { 1044 "returncode": 2, 1045 "stdout": """\ 1046PING 192.0.2.2 (192.0.2.2): 56 data bytes 1047 1048--- 192.0.2.2 ping statistics --- 10491 packets transmitted, 0 packets received, 100.0% packet loss 1050""", 1051 "stderr": "", # "IHL too short" message not shown 1052 "redacted": False, 1053 }, 1054 id="_IHL_too_short", 1055 ), 1056 pytest.param( 1057 { 1058 "src": "192.0.2.1", 1059 "dst": "192.0.2.2", 1060 "icmp_type": 3, 1061 "icmp_code": 1, 1062 "special": "no-payload", 1063 }, 1064 { 1065 "returncode": 2, 1066 "stdout": """\ 1067PATTERN: 0x01 1068PING 192.0.2.2 (192.0.2.2): 56 data bytes 1069 1070--- 192.0.2.2 ping statistics --- 10711 packets transmitted, 0 packets received, 100.0% packet loss 1072""", 1073 "stderr": """\ 1074ping: quoted data too short (28 bytes) from 192.0.2.2 1075""", 1076 "redacted": False, 1077 }, 1078 id="_quoted_data_too_short", 1079 ), 1080 pytest.param( 1081 { 1082 "src": "192.0.2.1", 1083 "dst": "192.0.2.2", 1084 "icmp_type": 3, 1085 "icmp_code": 1, 1086 "oip_ihl": 0x4, 1087 }, 1088 { 1089 "returncode": 2, 1090 "stdout": """\ 1091PING 192.0.2.2 (192.0.2.2): 56 data bytes 1092 1093--- 192.0.2.2 ping statistics --- 10941 packets transmitted, 0 packets received, 100.0% packet loss 1095""", 1096 "stderr": "", # "inner IHL too short" message not shown 1097 "redacted": False, 1098 }, 1099 id="_inner_IHL_too_short", 1100 ), 1101 pytest.param( 1102 { 1103 "src": "192.0.2.1", 1104 "dst": "192.0.2.2", 1105 "icmp_type": 3, 1106 "icmp_code": 1, 1107 "oip_ihl": 0xF, 1108 }, 1109 { 1110 "returncode": 2, 1111 "stdout": """\ 1112PING 192.0.2.2 (192.0.2.2): 56 data bytes 1113 1114--- 192.0.2.2 ping statistics --- 11151 packets transmitted, 0 packets received, 100.0% packet loss 1116""", 1117 "stderr": """\ 1118ping: inner packet too short (84 bytes) from 192.0.2.2 1119""", 1120 "redacted": False, 1121 }, 1122 id="_inner_packet_too_short", 1123 ), 1124 pytest.param( 1125 { 1126 "src": "192.0.2.1", 1127 "dst": "192.0.2.2", 1128 "icmp_type": 3, 1129 "icmp_code": 1, 1130 "oip_ihl": 0xF, 1131 "special": "no-payload", 1132 }, 1133 { 1134 "returncode": 2, 1135 "stdout": """\ 1136PATTERN: 0x01 1137PING 192.0.2.2 (192.0.2.2): 56 data bytes 1138 1139--- 192.0.2.2 ping statistics --- 11401 packets transmitted, 0 packets received, 100.0% packet loss 1141""", 1142 "stderr": "", 1143 "redacted": False, 1144 }, 1145 id="_max_inner_packet_ihl_without_payload", 1146 ), 1147 pytest.param( 1148 { 1149 "src": "192.0.2.1", 1150 "dst": "192.0.2.2", 1151 "icmp_type": 0, 1152 "icmp_code": 0, 1153 "opts": "NOP-40", 1154 }, 1155 { 1156 "returncode": 0, 1157 "stdout": """\ 1158PING 192.0.2.2 (192.0.2.2): 56 data bytes 115964 bytes from: icmp_seq=0 ttl= time= ms 1160wrong total length 124 instead of 84 1161NOP 1162NOP 1163NOP 1164NOP 1165NOP 1166NOP 1167NOP 1168NOP 1169NOP 1170NOP 1171NOP 1172NOP 1173NOP 1174NOP 1175NOP 1176NOP 1177NOP 1178NOP 1179NOP 1180NOP 1181NOP 1182NOP 1183NOP 1184NOP 1185NOP 1186NOP 1187NOP 1188NOP 1189NOP 1190NOP 1191NOP 1192NOP 1193NOP 1194NOP 1195NOP 1196NOP 1197NOP 1198NOP 1199NOP 1200NOP 1201 1202--- 192.0.2.2 ping statistics --- 12031 packets transmitted, 1 packets received, 0.0% packet loss 1204round-trip min/avg/max/stddev = /// ms 1205""", 1206 "stderr": "", 1207 "redacted": True, 1208 }, 1209 id="_0_0_opts_NOP_40", 1210 ), 1211 pytest.param( 1212 { 1213 "src": "192.0.2.1", 1214 "dst": "192.0.2.2", 1215 "icmp_type": 0, 1216 "icmp_code": 0, 1217 "opts": "unk", 1218 }, 1219 { 1220 "returncode": 0, 1221 "stdout": """\ 1222PING 192.0.2.2 (192.0.2.2): 56 data bytes 122364 bytes from: icmp_seq=0 ttl= time= ms 1224wrong total length 88 instead of 84 1225unknown option 9f 1226 1227--- 192.0.2.2 ping statistics --- 12281 packets transmitted, 1 packets received, 0.0% packet loss 1229round-trip min/avg/max/stddev = /// ms 1230""", 1231 "stderr": "", 1232 "redacted": True, 1233 }, 1234 id="_0_0_opts_unk", 1235 ), 1236 pytest.param( 1237 { 1238 "src": "192.0.2.1", 1239 "dst": "192.0.2.2", 1240 "icmp_type": 3, 1241 "icmp_code": 1, 1242 "opts": "NOP-40", 1243 }, 1244 { 1245 "returncode": 2, 1246 "stdout": """\ 1247PING 192.0.2.2 (192.0.2.2): 56 data bytes 1248132 bytes from 192.0.2.2: Destination Host Unreachable 1249Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Opts 1250 4 f 00 007c 0001 0 0000 40 01 d868 192.0.2.1 192.0.2.2 01010101010101010101010101010101010101010101010101010101010101010101010101010101 1251 1252 1253--- 192.0.2.2 ping statistics --- 12541 packets transmitted, 0 packets received, 100.0% packet loss 1255""", 1256 "stderr": "", 1257 "redacted": False, 1258 }, 1259 id="_3_1_opts_NOP_40", 1260 ), 1261 pytest.param( 1262 { 1263 "src": "192.0.2.1", 1264 "dst": "192.0.2.2", 1265 "icmp_type": 3, 1266 "icmp_code": 1, 1267 "flags": "DF", 1268 }, 1269 { 1270 "returncode": 2, 1271 "stdout": """\ 1272PING 192.0.2.2 (192.0.2.2): 56 data bytes 127392 bytes from 192.0.2.2: Destination Host Unreachable 1274Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 1275 4 5 00 0054 0001 2 0000 40 01 b6a4 192.0.2.1 192.0.2.2 1276 1277 1278--- 192.0.2.2 ping statistics --- 12791 packets transmitted, 0 packets received, 100.0% packet loss 1280""", 1281 "stderr": "", 1282 "redacted": False, 1283 }, 1284 id="_3_1_flags_DF", 1285 ), 1286 pytest.param( 1287 { 1288 "src": "192.0.2.1", 1289 "dst": "192.0.2.2", 1290 "icmp_type": 3, 1291 "icmp_code": 1, 1292 "special": "tcp", 1293 }, 1294 { 1295 "returncode": 2, 1296 "stdout": """\ 1297PATTERN: 0x01 1298PING 192.0.2.2 (192.0.2.2): 56 data bytes 1299 1300--- 192.0.2.2 ping statistics --- 13011 packets transmitted, 0 packets received, 100.0% packet loss 1302""", 1303 "stderr": """\ 1304ping: quoted data too short (40 bytes) from 192.0.2.2 1305""", 1306 "redacted": False, 1307 }, 1308 id="_3_1_special_tcp", 1309 ), 1310 pytest.param( 1311 { 1312 "src": "192.0.2.1", 1313 "dst": "192.0.2.2", 1314 "icmp_type": 3, 1315 "icmp_code": 1, 1316 "special": "udp", 1317 }, 1318 { 1319 "returncode": 2, 1320 "stdout": """\ 1321PATTERN: 0x01 1322PING 192.0.2.2 (192.0.2.2): 56 data bytes 1323 1324--- 192.0.2.2 ping statistics --- 13251 packets transmitted, 0 packets received, 100.0% packet loss 1326""", 1327 "stderr": """\ 1328ping: quoted data too short (28 bytes) from 192.0.2.2 1329""", 1330 "redacted": False, 1331 }, 1332 id="_3_1_special_udp", 1333 ), 1334 pytest.param( 1335 { 1336 "src": "192.0.2.1", 1337 "dst": "192.0.2.2", 1338 "icmp_type": 3, 1339 "icmp_code": 1, 1340 "verbose": False, 1341 }, 1342 { 1343 "returncode": 2, 1344 "stdout": """\ 1345PING 192.0.2.2 (192.0.2.2): 56 data bytes 134692 bytes from 192.0.2.2: Destination Host Unreachable 1347Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 1348 4 5 00 0054 0001 0 0000 40 01 f6a4 192.0.2.1 192.0.2.2 1349 1350 1351--- 192.0.2.2 ping statistics --- 13521 packets transmitted, 0 packets received, 100.0% packet loss 1353""", 1354 "stderr": "", 1355 "redacted": False, 1356 }, 1357 id="_3_1_verbose_false", 1358 ), 1359 pytest.param( 1360 { 1361 "src": "192.0.2.1", 1362 "dst": "192.0.2.2", 1363 "icmp_type": 3, 1364 "icmp_code": 1, 1365 "special": "not-mine", 1366 "verbose": False, 1367 }, 1368 { 1369 "returncode": 2, 1370 "stdout": """\ 1371PATTERN: 0x01 1372PING 192.0.2.2 (192.0.2.2): 56 data bytes 1373 1374--- 192.0.2.2 ping statistics --- 13751 packets transmitted, 0 packets received, 100.0% packet loss 1376""", 1377 "stderr": "", 1378 "redacted": False, 1379 }, 1380 id="_3_1_special_not_mine_verbose_false", 1381 ), 1382 pytest.param( 1383 { 1384 "src": "192.0.2.1", 1385 "dst": "192.0.2.2", 1386 "icmp_type": 0, 1387 "icmp_code": 0, 1388 "special": "warp", 1389 }, 1390 { 1391 "returncode": 0, 1392 "stdout": """\ 1393PATTERN: 0x01 1394PING 192.0.2.2 (192.0.2.2): 56 data bytes 139564 bytes from: icmp_seq=0 ttl= time= ms 1396 1397--- 192.0.2.2 ping statistics --- 13981 packets transmitted, 1 packets received, 0.0% packet loss 1399round-trip min/avg/max/stddev = /// ms 1400""", 1401 "stderr": """\ 1402ping: time of day goes back (- ms), clamping time to 0 1403""", 1404 "redacted": True, 1405 }, 1406 id="_0_0_special_warp", 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": "wrong", 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 1422wrong data byte #55 should be 0x1 but was 0x0 1423cp: xx xx xx xx xx xx xx xx 1424 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1425 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1426 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1427dp: xx xx xx xx xx xx xx xx 1428 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1429 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1430 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1431 1432--- 192.0.2.2 ping statistics --- 14331 packets transmitted, 1 packets received, 0.0% packet loss 1434round-trip min/avg/max/stddev = /// ms 1435""", 1436 "stderr": "", 1437 "redacted": True, 1438 }, 1439 id="_0_0_special_wrong", 1440 ), 1441 ] 1442 1443 @pytest.mark.parametrize("pinger_kargs, expected", pinger_testdata) 1444 @pytest.mark.require_progs(["scapy"]) 1445 @pytest.mark.require_user("root") 1446 def test_pinger(self, pinger_kargs, expected): 1447 """Test ping using pinger(), a reply faker""" 1448 iface = IfaceFactory().create_iface("", "tun")[0].name 1449 ping = pinger(iface, **pinger_kargs) 1450 assert ping.returncode == expected["returncode"] 1451 if expected["redacted"]: 1452 assert redact(ping.stdout) == expected["stdout"] 1453 assert redact(ping.stderr) == expected["stderr"] 1454 else: 1455 assert ping.stdout == expected["stdout"] 1456 assert ping.stderr == expected["stderr"] 1457