Skip to content

Commit cb0a7a6

Browse files
committed
Add AF_PACKET sendto/recvfrom tests.
Cover ETH_P_ALL, ETH_P_LOOP, large payloads, bogus and invalid ethertypes, truncated IP/IPv6 headers, undersized frames, small receive buffers, optional port argument and error cases.
1 parent c4c415d commit cb0a7a6

4 files changed

Lines changed: 402 additions & 0 deletions
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
AF_PACKET socket_recvfrom() without optional port argument
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+
$s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
21+
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
22+
23+
socket_bind($s_send, 'lo');
24+
socket_bind($s_recv, 'lo');
25+
26+
$dst_mac = "\xff\xff\xff\xff\xff\xff";
27+
$src_mac = "\x00\x00\x00\x00\x00\x00";
28+
$ethertype = pack("n", 0x9000);
29+
$payload = "no port test";
30+
$frame = str_pad($dst_mac . $src_mac . $ethertype . $payload, 60, "\x00");
31+
32+
socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1);
33+
34+
// recvfrom without the optional 6th argument (port/ifindex).
35+
$bytes = socket_recvfrom($s_recv, $buf, 65536, 0, $addr);
36+
var_dump($bytes >= 60);
37+
var_dump($addr === 'lo');
38+
39+
socket_close($s_send);
40+
socket_close($s_recv);
41+
?>
42+
--EXPECT--
43+
bool(true)
44+
bool(true)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
AF_PACKET socket_sendto() and socket_recvfrom() error cases
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+
echo "--- sendto without port (ifindex) ---\n";
22+
$s = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
23+
socket_bind($s, 'lo');
24+
25+
try {
26+
socket_sendto($s, str_repeat("\x00", 60), 60, 0, "lo");
27+
} catch (ValueError $e) {
28+
echo $e->getMessage(), PHP_EOL;
29+
}
30+
socket_close($s);
31+
32+
echo "--- sendto with invalid interface name ---\n";
33+
$s = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
34+
socket_bind($s, 'lo');
35+
36+
$ret = @socket_sendto($s, str_repeat("\x00", 60), 60, 0, "lo", 999999);
37+
var_dump($ret === false);
38+
socket_close($s);
39+
40+
echo "--- recvfrom on non-blocking socket with no data ---\n";
41+
$s = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
42+
socket_bind($s, 'lo');
43+
socket_set_nonblock($s);
44+
45+
$ret = @socket_recvfrom($s, $buf, 65536, 0, $addr);
46+
var_dump($ret === false);
47+
socket_close($s);
48+
49+
?>
50+
--EXPECT--
51+
--- sendto without port (ifindex) ---
52+
socket_sendto(): Argument #6 ($port) cannot be null when the socket type is AF_PACKET
53+
--- sendto with invalid interface name ---
54+
bool(true)
55+
--- recvfrom on non-blocking socket with no data ---
56+
bool(true)

0 commit comments

Comments
 (0)