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 del 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_EOL() 89 elif opts == "NOP": 90 options = sc.IPOption_NOP() 91 elif opts == "NOP-40": 92 options = sc.IPOption_NOP() * 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 = 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] = 0, 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 0 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 (r"localhost \([0-9]{1,3}(\.[0-9]{1,3}){3}\)", "localhost"), 274 (r"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 (r"\(-[0-9\.]+[0-9]+ ms\)", "(- ms)"), 281 (r"[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 @pytest.mark.require_user("unprivileged") 728 def test_ping(self, expected): 729 """Test ping""" 730 ping = subprocess.run( 731 expected["args"].split(), 732 capture_output=True, 733 timeout=15, 734 text=True, 735 ) 736 assert ping.returncode == expected["returncode"] 737 assert redact(ping.stdout) == expected["stdout"] 738 assert ping.stderr == expected["stderr"] 739 740 # Each param in ping46_testdata contains a dictionary with the arguments 741 # and the expected outcome (returncode, redacted stdout, and stderr) 742 # common to `ping -4` and `ping -6` 743 ping46_testdata = [ 744 pytest.param( 745 { 746 "args": "-Wx localhost", 747 "returncode": os.EX_USAGE, 748 "stdout": "", 749 "stderr": "ping: invalid timing interval: `x'\n", 750 }, 751 id="_Wx_localhost", 752 ), 753 ] 754 755 @pytest.mark.parametrize("expected", ping46_testdata) 756 @pytest.mark.require_user("root") 757 @pytest.mark.require_user("unprivileged") 758 def test_ping_46(self, expected): 759 """Test ping -4/ping -6""" 760 for version in [4, 6]: 761 ping = subprocess.run( 762 ["ping", f"-{version}"] + expected["args"].split(), 763 capture_output=True, 764 timeout=15, 765 text=True, 766 ) 767 assert ping.returncode == expected["returncode"] 768 assert redact(ping.stdout) == expected["stdout"] 769 assert ping.stderr == expected["stderr"] 770 771 # Each param in pinger_testdata contains a dictionary with the keywords to 772 # `pinger()` and a dictionary with the expected outcome (returncode, 773 # stdout, stderr, and if ping's output is redacted) 774 pinger_testdata = [ 775 pytest.param( 776 { 777 "src": "192.0.2.1", 778 "dst": "192.0.2.2", 779 "icmp_type": 0, 780 "icmp_code": 0, 781 }, 782 { 783 "returncode": 0, 784 "stdout": """\ 785PING 192.0.2.2 (192.0.2.2): 56 data bytes 78664 bytes from: icmp_seq=0 ttl= time= ms 787 788--- 192.0.2.2 ping statistics --- 7891 packets transmitted, 1 packets received, 0.0% packet loss 790round-trip min/avg/max/stddev = /// ms 791""", 792 "stderr": "", 793 "redacted": True, 794 }, 795 id="_0_0", 796 ), 797 pytest.param( 798 { 799 "src": "192.0.2.1", 800 "dst": "192.0.2.2", 801 "icmp_type": 0, 802 "icmp_code": 0, 803 "opts": "EOL", 804 }, 805 { 806 "returncode": 0, 807 "stdout": """\ 808PING 192.0.2.2 (192.0.2.2): 56 data bytes 80964 bytes from: icmp_seq=0 ttl= time= ms 810wrong total length 88 instead of 84 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_opts_EOL", 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": "LSRR", 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 834LSRR: 192.0.2.10 835 192.0.2.20 836 192.0.2.30 837 192.0.2.40 838 192.0.2.50 839 192.0.2.60 840 192.0.2.70 841 192.0.2.80 842 192.0.2.90 843 844--- 192.0.2.2 ping statistics --- 8451 packets transmitted, 1 packets received, 0.0% packet loss 846round-trip min/avg/max/stddev = /// ms 847""", 848 "stderr": "", 849 "redacted": True, 850 }, 851 id="_0_0_opts_LSRR", 852 ), 853 pytest.param( 854 { 855 "src": "192.0.2.1", 856 "dst": "192.0.2.2", 857 "icmp_type": 0, 858 "icmp_code": 0, 859 "opts": "LSRR-trunc", 860 }, 861 { 862 "returncode": 0, 863 "stdout": """\ 864PING 192.0.2.2 (192.0.2.2): 56 data bytes 86564 bytes from: icmp_seq=0 ttl= time= ms 866LSRR: (truncated route) 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_trunc", 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": "SSRR", 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 890SSRR: 192.0.2.10 891 192.0.2.20 892 192.0.2.30 893 192.0.2.40 894 192.0.2.50 895 192.0.2.60 896 192.0.2.70 897 192.0.2.80 898 192.0.2.90 899 900--- 192.0.2.2 ping statistics --- 9011 packets transmitted, 1 packets received, 0.0% packet loss 902round-trip min/avg/max/stddev = /// ms 903""", 904 "stderr": "", 905 "redacted": True, 906 }, 907 id="_0_0_opts_SSRR", 908 ), 909 pytest.param( 910 { 911 "src": "192.0.2.1", 912 "dst": "192.0.2.2", 913 "icmp_type": 0, 914 "icmp_code": 0, 915 "opts": "SSRR-trunc", 916 }, 917 { 918 "returncode": 0, 919 "stdout": """\ 920PING 192.0.2.2 (192.0.2.2): 56 data bytes 92164 bytes from: icmp_seq=0 ttl= time= ms 922SSRR: (truncated route) 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_trunc", 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": "RR", 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 946RR: 192.0.2.10 947 192.0.2.20 948 192.0.2.30 949 192.0.2.40 950 192.0.2.50 951 192.0.2.60 952 192.0.2.70 953 192.0.2.80 954 192.0.2.90 955 956--- 192.0.2.2 ping statistics --- 9571 packets transmitted, 1 packets received, 0.0% packet loss 958round-trip min/avg/max/stddev = /// ms 959""", 960 "stderr": "", 961 "redacted": True, 962 }, 963 id="_0_0_opts_RR", 964 ), 965 pytest.param( 966 { 967 "src": "192.0.2.1", 968 "dst": "192.0.2.2", 969 "icmp_type": 0, 970 "icmp_code": 0, 971 "opts": "RR-same", 972 }, 973 { 974 "returncode": 0, 975 "stdout": """\ 976PING 192.0.2.2 (192.0.2.2): 56 data bytes 97764 bytes from: icmp_seq=0 ttl= time= ms (same route) 978 979--- 192.0.2.2 ping statistics --- 9801 packets transmitted, 1 packets received, 0.0% packet loss 981round-trip min/avg/max/stddev = /// ms 982""", 983 "stderr": "", 984 "redacted": True, 985 }, 986 id="_0_0_opts_RR_same", 987 ), 988 pytest.param( 989 { 990 "src": "192.0.2.1", 991 "dst": "192.0.2.2", 992 "icmp_type": 0, 993 "icmp_code": 0, 994 "opts": "RR-trunc", 995 }, 996 { 997 "returncode": 0, 998 "stdout": """\ 999PING 192.0.2.2 (192.0.2.2): 56 data bytes 100064 bytes from: icmp_seq=0 ttl= time= ms 1001RR: (truncated 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_trunc", 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": "NOP", 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 1025wrong total length 88 instead of 84 1026NOP 1027 1028--- 192.0.2.2 ping statistics --- 10291 packets transmitted, 1 packets received, 0.0% packet loss 1030round-trip min/avg/max/stddev = /// ms 1031""", 1032 "stderr": "", 1033 "redacted": True, 1034 }, 1035 id="_0_0_opts_NOP", 1036 ), 1037 pytest.param( 1038 { 1039 "src": "192.0.2.1", 1040 "dst": "192.0.2.2", 1041 "icmp_type": 3, 1042 "icmp_code": 1, 1043 "ihl": 0x4, 1044 }, 1045 { 1046 "returncode": 2, 1047 "stdout": """\ 1048PING 192.0.2.2 (192.0.2.2): 56 data bytes 1049 1050--- 192.0.2.2 ping statistics --- 10511 packets transmitted, 0 packets received, 100.0% packet loss 1052""", 1053 "stderr": "", # "IHL too short" message not shown 1054 "redacted": False, 1055 }, 1056 id="_IHL_too_short", 1057 ), 1058 pytest.param( 1059 { 1060 "src": "192.0.2.1", 1061 "dst": "192.0.2.2", 1062 "icmp_type": 3, 1063 "icmp_code": 1, 1064 "special": "no-payload", 1065 }, 1066 { 1067 "returncode": 2, 1068 "stdout": """\ 1069PATTERN: 0x01 1070PING 192.0.2.2 (192.0.2.2): 56 data bytes 1071 1072--- 192.0.2.2 ping statistics --- 10731 packets transmitted, 0 packets received, 100.0% packet loss 1074""", 1075 "stderr": """\ 1076ping: quoted data too short (28 bytes) from 192.0.2.2 1077""", 1078 "redacted": False, 1079 }, 1080 id="_quoted_data_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 "oip_ihl": 0x4, 1089 }, 1090 { 1091 "returncode": 2, 1092 "stdout": """\ 1093PING 192.0.2.2 (192.0.2.2): 56 data bytes 1094 1095--- 192.0.2.2 ping statistics --- 10961 packets transmitted, 0 packets received, 100.0% packet loss 1097""", 1098 "stderr": "", # "inner IHL too short" message not shown 1099 "redacted": False, 1100 }, 1101 id="_inner_IHL_too_short", 1102 ), 1103 pytest.param( 1104 { 1105 "src": "192.0.2.1", 1106 "dst": "192.0.2.2", 1107 "icmp_type": 3, 1108 "icmp_code": 1, 1109 "oip_ihl": 0xF, 1110 }, 1111 { 1112 "returncode": 2, 1113 "stdout": """\ 1114PING 192.0.2.2 (192.0.2.2): 56 data bytes 1115 1116--- 192.0.2.2 ping statistics --- 11171 packets transmitted, 0 packets received, 100.0% packet loss 1118""", 1119 "stderr": """\ 1120ping: inner packet too short (84 bytes) from 192.0.2.2 1121""", 1122 "redacted": False, 1123 }, 1124 id="_inner_packet_too_short", 1125 ), 1126 pytest.param( 1127 { 1128 "src": "192.0.2.1", 1129 "dst": "192.0.2.2", 1130 "icmp_type": 3, 1131 "icmp_code": 1, 1132 "oip_ihl": 0xF, 1133 "special": "no-payload", 1134 }, 1135 { 1136 "returncode": 2, 1137 "stdout": """\ 1138PATTERN: 0x01 1139PING 192.0.2.2 (192.0.2.2): 56 data bytes 1140 1141--- 192.0.2.2 ping statistics --- 11421 packets transmitted, 0 packets received, 100.0% packet loss 1143""", 1144 "stderr": "", 1145 "redacted": False, 1146 }, 1147 id="_max_inner_packet_ihl_without_payload", 1148 ), 1149 pytest.param( 1150 { 1151 "src": "192.0.2.1", 1152 "dst": "192.0.2.2", 1153 "icmp_type": 0, 1154 "icmp_code": 0, 1155 "opts": "NOP-40", 1156 }, 1157 { 1158 "returncode": 0, 1159 "stdout": """\ 1160PING 192.0.2.2 (192.0.2.2): 56 data bytes 116164 bytes from: icmp_seq=0 ttl= time= ms 1162wrong total length 124 instead of 84 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 1201NOP 1202NOP 1203 1204--- 192.0.2.2 ping statistics --- 12051 packets transmitted, 1 packets received, 0.0% packet loss 1206round-trip min/avg/max/stddev = /// ms 1207""", 1208 "stderr": "", 1209 "redacted": True, 1210 }, 1211 id="_0_0_opts_NOP_40", 1212 ), 1213 pytest.param( 1214 { 1215 "src": "192.0.2.1", 1216 "dst": "192.0.2.2", 1217 "icmp_type": 0, 1218 "icmp_code": 0, 1219 "opts": "unk", 1220 }, 1221 { 1222 "returncode": 0, 1223 "stdout": """\ 1224PING 192.0.2.2 (192.0.2.2): 56 data bytes 122564 bytes from: icmp_seq=0 ttl= time= ms 1226wrong total length 88 instead of 84 1227unknown option 9f 1228 1229--- 192.0.2.2 ping statistics --- 12301 packets transmitted, 1 packets received, 0.0% packet loss 1231round-trip min/avg/max/stddev = /// ms 1232""", 1233 "stderr": "", 1234 "redacted": True, 1235 }, 1236 id="_0_0_opts_unk", 1237 ), 1238 pytest.param( 1239 { 1240 "src": "192.0.2.1", 1241 "dst": "192.0.2.2", 1242 "icmp_type": 3, 1243 "icmp_code": 1, 1244 "opts": "NOP-40", 1245 }, 1246 { 1247 "returncode": 2, 1248 "stdout": """\ 1249PING 192.0.2.2 (192.0.2.2): 56 data bytes 1250132 bytes from 192.0.2.2: Destination Host Unreachable 1251Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Opts 1252 4 f 00 007c 0001 0 0000 40 01 d868 192.0.2.1 192.0.2.2 01010101010101010101010101010101010101010101010101010101010101010101010101010101 1253 1254 1255--- 192.0.2.2 ping statistics --- 12561 packets transmitted, 0 packets received, 100.0% packet loss 1257""", 1258 "stderr": "", 1259 "redacted": False, 1260 }, 1261 id="_3_1_opts_NOP_40", 1262 ), 1263 pytest.param( 1264 { 1265 "src": "192.0.2.1", 1266 "dst": "192.0.2.2", 1267 "icmp_type": 3, 1268 "icmp_code": 1, 1269 "flags": "DF", 1270 }, 1271 { 1272 "returncode": 2, 1273 "stdout": """\ 1274PING 192.0.2.2 (192.0.2.2): 56 data bytes 127592 bytes from 192.0.2.2: Destination Host Unreachable 1276Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 1277 4 5 00 0054 0001 2 0000 40 01 b6a4 192.0.2.1 192.0.2.2 1278 1279 1280--- 192.0.2.2 ping statistics --- 12811 packets transmitted, 0 packets received, 100.0% packet loss 1282""", 1283 "stderr": "", 1284 "redacted": False, 1285 }, 1286 id="_3_1_flags_DF", 1287 ), 1288 pytest.param( 1289 { 1290 "src": "192.0.2.1", 1291 "dst": "192.0.2.2", 1292 "icmp_type": 3, 1293 "icmp_code": 1, 1294 "special": "tcp", 1295 }, 1296 { 1297 "returncode": 2, 1298 "stdout": """\ 1299PATTERN: 0x01 1300PING 192.0.2.2 (192.0.2.2): 56 data bytes 1301 1302--- 192.0.2.2 ping statistics --- 13031 packets transmitted, 0 packets received, 100.0% packet loss 1304""", 1305 "stderr": """\ 1306ping: quoted data too short (40 bytes) from 192.0.2.2 1307""", 1308 "redacted": False, 1309 }, 1310 id="_3_1_special_tcp", 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": "udp", 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 (28 bytes) from 192.0.2.2 1331""", 1332 "redacted": False, 1333 }, 1334 id="_3_1_special_udp", 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 "verbose": False, 1343 }, 1344 { 1345 "returncode": 2, 1346 "stdout": """\ 1347PING 192.0.2.2 (192.0.2.2): 56 data bytes 134892 bytes from 192.0.2.2: Destination Host Unreachable 1349Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 1350 4 5 00 0054 0001 0 0000 40 01 f6a4 192.0.2.1 192.0.2.2 1351 1352 1353--- 192.0.2.2 ping statistics --- 13541 packets transmitted, 0 packets received, 100.0% packet loss 1355""", 1356 "stderr": "", 1357 "redacted": False, 1358 }, 1359 id="_3_1_verbose_false", 1360 ), 1361 pytest.param( 1362 { 1363 "src": "192.0.2.1", 1364 "dst": "192.0.2.2", 1365 "icmp_type": 3, 1366 "icmp_code": 1, 1367 "special": "not-mine", 1368 "verbose": False, 1369 }, 1370 { 1371 "returncode": 2, 1372 "stdout": """\ 1373PATTERN: 0x01 1374PING 192.0.2.2 (192.0.2.2): 56 data bytes 1375 1376--- 192.0.2.2 ping statistics --- 13771 packets transmitted, 0 packets received, 100.0% packet loss 1378""", 1379 "stderr": "", 1380 "redacted": False, 1381 }, 1382 id="_3_1_special_not_mine_verbose_false", 1383 ), 1384 pytest.param( 1385 { 1386 "src": "192.0.2.1", 1387 "dst": "192.0.2.2", 1388 "icmp_type": 0, 1389 "icmp_code": 0, 1390 "special": "warp", 1391 }, 1392 { 1393 "returncode": 0, 1394 "stdout": """\ 1395PATTERN: 0x01 1396PING 192.0.2.2 (192.0.2.2): 56 data bytes 139764 bytes from: icmp_seq=0 ttl= time= ms 1398 1399--- 192.0.2.2 ping statistics --- 14001 packets transmitted, 1 packets received, 0.0% packet loss 1401round-trip min/avg/max/stddev = /// ms 1402""", 1403 "stderr": """\ 1404ping: time of day goes back (- ms), clamping time to 0 1405""", 1406 "redacted": True, 1407 }, 1408 id="_0_0_special_warp", 1409 ), 1410 pytest.param( 1411 { 1412 "src": "192.0.2.1", 1413 "dst": "192.0.2.2", 1414 "icmp_type": 0, 1415 "icmp_code": 0, 1416 "special": "wrong", 1417 }, 1418 { 1419 "returncode": 0, 1420 "stdout": """\ 1421PATTERN: 0x01 1422PING 192.0.2.2 (192.0.2.2): 56 data bytes 142364 bytes from: icmp_seq=0 ttl= time= ms 1424wrong data byte #55 should be 0x1 but was 0x0 1425cp: xx xx xx xx xx xx xx xx 1426 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1427 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1428 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1429dp: xx xx xx xx xx xx xx xx 1430 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1431 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1432 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1433 1434--- 192.0.2.2 ping statistics --- 14351 packets transmitted, 1 packets received, 0.0% packet loss 1436round-trip min/avg/max/stddev = /// ms 1437""", 1438 "stderr": "", 1439 "redacted": True, 1440 }, 1441 id="_0_0_special_wrong", 1442 ), 1443 ] 1444 1445 @pytest.mark.parametrize("pinger_kargs, expected", pinger_testdata) 1446 @pytest.mark.require_progs(["scapy"]) 1447 @pytest.mark.require_user("root") 1448 def test_pinger(self, pinger_kargs, expected): 1449 """Test ping using pinger(), a reply faker""" 1450 iface = IfaceFactory().create_iface("", "tun")[0].name 1451 ping = pinger(iface, **pinger_kargs) 1452 assert ping.returncode == expected["returncode"] 1453 if expected["redacted"]: 1454 assert redact(ping.stdout) == expected["stdout"] 1455 assert redact(ping.stderr) == expected["stderr"] 1456 else: 1457 assert ping.stdout == expected["stdout"] 1458 assert ping.stderr == expected["stderr"] 1459