|
| 1 | +--TEST-- |
| 2 | +Test if socket_recvfrom() receives raw data sent by socket_sendto() via AF_PACKET |
| 3 | +--EXTENSIONS-- |
| 4 | +sockets |
| 5 | +posix |
| 6 | +--SKIPIF-- |
| 7 | +<?php |
| 8 | +if (!defined("AF_PACKET")) { |
| 9 | + die('SKIP AF_PACKET not supported on this platform.'); |
| 10 | +} |
| 11 | +if (!defined("ETH_P_ALL")) { |
| 12 | + die('SKIP ETH_P_ALL not available on this platform.'); |
| 13 | +} |
| 14 | +if (!function_exists("posix_getuid") || posix_getuid() != 0) { |
| 15 | + die('SKIP AF_PACKET requires root permissions.'); |
| 16 | +} |
| 17 | +?> |
| 18 | +--FILE-- |
| 19 | +<?php |
| 20 | + |
| 21 | +$dst_mac = "\xff\xff\xff\xff\xff\xff"; |
| 22 | +$src_mac = "\x00\x00\x00\x00\x00\x00"; |
| 23 | + |
| 24 | +// Helper to build a padded ethernet frame. |
| 25 | +function build_frame(string $dst, string $src, int $ethertype, string $payload): string { |
| 26 | + $frame = $dst . $src . pack("n", $ethertype) . $payload; |
| 27 | + return str_pad($frame, 60, "\x00"); |
| 28 | +} |
| 29 | + |
| 30 | +echo "--- ETH_P_ALL send and receive ---\n"; |
| 31 | +$s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); |
| 32 | +$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); |
| 33 | +socket_bind($s_send, 'lo'); |
| 34 | +socket_bind($s_recv, 'lo'); |
| 35 | + |
| 36 | +$frame = build_frame($dst_mac, $src_mac, 0x9000, "ETH_P_ALL test"); |
| 37 | +$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1); |
| 38 | +var_dump($sent >= 60); |
| 39 | + |
| 40 | +$bytes = socket_recvfrom($s_recv, $buf, 65536, 0, $addr); |
| 41 | +var_dump($bytes >= 60); |
| 42 | +var_dump(is_string($buf)); |
| 43 | +var_dump($addr === 'lo'); |
| 44 | +var_dump(str_contains($buf, "ETH_P_ALL test")); |
| 45 | + |
| 46 | +socket_close($s_send); |
| 47 | +socket_close($s_recv); |
| 48 | + |
| 49 | +echo "--- ETH_P_LOOP send and receive ---\n"; |
| 50 | +$s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); |
| 51 | +$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); |
| 52 | +socket_bind($s_send, 'lo'); |
| 53 | +socket_bind($s_recv, 'lo'); |
| 54 | + |
| 55 | +$frame = build_frame($dst_mac, $src_mac, ETH_P_LOOP, "loopback payload"); |
| 56 | +$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1); |
| 57 | +var_dump($sent >= 60); |
| 58 | + |
| 59 | +$bytes = socket_recvfrom($s_recv, $buf, 65536, 0, $addr); |
| 60 | +var_dump($bytes >= 60); |
| 61 | +// Verify ETH_P_LOOP ethertype at offset 12-13. |
| 62 | +var_dump(unpack("n", $buf, 12)[1] === ETH_P_LOOP); |
| 63 | +var_dump(str_contains($buf, "loopback payload")); |
| 64 | + |
| 65 | +socket_close($s_send); |
| 66 | +socket_close($s_recv); |
| 67 | + |
| 68 | +echo "--- Large payload ---\n"; |
| 69 | +$s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); |
| 70 | +$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL); |
| 71 | +socket_bind($s_send, 'lo'); |
| 72 | +socket_bind($s_recv, 'lo'); |
| 73 | + |
| 74 | +$payload = random_bytes(1024); |
| 75 | +$frame = build_frame($dst_mac, $src_mac, 0x9000, $payload); |
| 76 | +$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1); |
| 77 | +var_dump($sent === strlen($frame)); |
| 78 | + |
| 79 | +$bytes = socket_recvfrom($s_recv, $buf, 65536, 0, $addr, $port); |
| 80 | +var_dump($bytes === strlen($frame)); |
| 81 | +var_dump(is_int($port)); |
| 82 | +// Verify the payload is intact in the raw buffer. |
| 83 | +var_dump(str_contains($buf, $payload)); |
| 84 | + |
| 85 | +socket_close($s_send); |
| 86 | +socket_close($s_recv); |
| 87 | +?> |
| 88 | +--EXPECT-- |
| 89 | +--- ETH_P_ALL send and receive --- |
| 90 | +bool(true) |
| 91 | +bool(true) |
| 92 | +bool(true) |
| 93 | +bool(true) |
| 94 | +bool(true) |
| 95 | +--- ETH_P_LOOP send and receive --- |
| 96 | +bool(true) |
| 97 | +bool(true) |
| 98 | +bool(true) |
| 99 | +bool(true) |
| 100 | +--- Large payload --- |
| 101 | +bool(true) |
| 102 | +bool(true) |
| 103 | +bool(true) |
| 104 | +bool(true) |
0 commit comments