libc::recv return docker
libc::recv does not return on docker
我在 macOS 托管的 docker 上用 Rust 编写了一个简单的数据包捕获。
但是,libc::recv 不会永远 return。
src/main.rs
use std::io;
fn main() -> io::Result<()> {
let sock = unsafe {
match libc::socket(
libc::AF_PACKET,
libc::SOCK_RAW,
libc::ETH_P_ALL.to_be() as _,
) {
-1 => Err(io::Error::last_os_error()),
fd => Ok(fd),
}
}?;
println!("sock: {}", sock);
let mut buf = [0u8; 1024];
loop {
let len = unsafe {
libc::recv(
sock,
(&mut buf[..]).as_mut_ptr() as *mut libc::c_void,
buf.len(),
0,
)
};
println!("len: {}", len);
}
}
Docker 文件
RUN apt-get update && apt-get install -y tcpdump
WORKDIR /app
COPY . .
ENTRYPOINT ["cargo", "run"]
运行 命令
$ docker build . -t rust_cap && docker run -p 127.0.0.1:15006:15006/udp -it --rm --name="rust_cap" rust_cap
我尝试检查是否通过 tcpdump 将任何数据包发送到容器中,并检查是否通过 strace 调用系统调用,它们似乎是正确的。
其次,我在 C 中编写了与上面相同的代码,例如:
main.c
#include <stdio.h>
#include <sys/socket.h>
#include <net/ethernet.h>
int main(int argc, char **argv) {
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
perror("socket");
exit(1);
}
printf("sock: %d\n", sock);
u_char buf[1024];
while (1) {
int len = recv(sock, buf, sizeof(buf), 0);
printf("len: %d\n", len);
}
}
Docker 文件
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y build-essential
COPY . .
RUN cc -o main main.c
ENTRYPOINT ["./main"]
运行 命令
$ docker build . -t c_cap && docker run -p 127.0.0.1:15007:15007/udp -it --rm --name="c_cap" c_cap
此 C 代码 运行 正确。 (我可以在 stdout 上看到我发送的每条消息 len: xxx
)
为什么 recv
在 Rust 代码中没有 return?
我错过了什么?
参考。跟踪输出
生锈
socket(AF_PACKET, SOCK_RAW, htons(0 /* ETH_P_??? */)) = 3
write(1, "sock: 3\n", 8sock: 3
) = 8
recvfrom(3,
in C (省略部分接收缓冲区)
socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) = 3
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
brk(NULL) = 0x5616f5ab4000
brk(0x5616f5ad5000) = 0x5616f5ad5000
write(1, "sock: 3\n", 8sock: 3
) = 8
recvfrom(3,
仅供参考,我 运行 通过以下方式跟踪。
$ docker run -it --cap-add sys_ptrace --entrypoint="/bin/bash" $IMAGE
$ cargo build && strace /app/target/debug/xxx
# or strace /main
我自己解决了这个问题。
传递给 libc::socket
的 libc::ETH_P_ALL
类型是 c_int
aliasing to i32
。
将 libc::ETH_P_ALL 作为 i32
转换为大端是不正确的 libc::socket
参数。
相反,它必须将 libc::ETH_P_ALL
转换为 u16
到大端。
use std::io;
fn main() -> io::Result<()> {
let sock = unsafe {
match libc::socket(
libc::AF_PACKET,
libc::SOCK_RAW,
- libc::ETH_P_ALL.to_be() as _,
+ (libc::ETH_P_ALL as u16).to_be() as _,
) {
-1 => Err(io::Error::last_os_error()),
fd => Ok(fd),
}
}?;
println!("sock: {}", sock);
let mut buf = [0u8; 1024];
loop {
let len = unsafe {
libc::recv(
sock,
(&mut buf[..]).as_mut_ptr() as *mut libc::c_void,
buf.len(),
0,
)
};
println!("len: {}", len);
}
}
仅供参考,
use libc;
fn main() {
assert_eq!(0x0003, libc::ETH_P_ALL);
assert_eq!(0x3000000, libc::ETH_P_ALL.to_be()); // <- incorrect in the question
assert_eq!(0x0003, libc::ETH_P_ALL as u16);
assert_eq!(0x300, (libc::ETH_P_ALL as u16).to_be()); // <- correct
}
我在 macOS 托管的 docker 上用 Rust 编写了一个简单的数据包捕获。 但是,libc::recv 不会永远 return。
src/main.rs
use std::io;
fn main() -> io::Result<()> {
let sock = unsafe {
match libc::socket(
libc::AF_PACKET,
libc::SOCK_RAW,
libc::ETH_P_ALL.to_be() as _,
) {
-1 => Err(io::Error::last_os_error()),
fd => Ok(fd),
}
}?;
println!("sock: {}", sock);
let mut buf = [0u8; 1024];
loop {
let len = unsafe {
libc::recv(
sock,
(&mut buf[..]).as_mut_ptr() as *mut libc::c_void,
buf.len(),
0,
)
};
println!("len: {}", len);
}
}
Docker 文件
RUN apt-get update && apt-get install -y tcpdump
WORKDIR /app
COPY . .
ENTRYPOINT ["cargo", "run"]
运行 命令
$ docker build . -t rust_cap && docker run -p 127.0.0.1:15006:15006/udp -it --rm --name="rust_cap" rust_cap
我尝试检查是否通过 tcpdump 将任何数据包发送到容器中,并检查是否通过 strace 调用系统调用,它们似乎是正确的。
其次,我在 C 中编写了与上面相同的代码,例如:
main.c
#include <stdio.h>
#include <sys/socket.h>
#include <net/ethernet.h>
int main(int argc, char **argv) {
int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
perror("socket");
exit(1);
}
printf("sock: %d\n", sock);
u_char buf[1024];
while (1) {
int len = recv(sock, buf, sizeof(buf), 0);
printf("len: %d\n", len);
}
}
Docker 文件
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y build-essential
COPY . .
RUN cc -o main main.c
ENTRYPOINT ["./main"]
运行 命令
$ docker build . -t c_cap && docker run -p 127.0.0.1:15007:15007/udp -it --rm --name="c_cap" c_cap
此 C 代码 运行 正确。 (我可以在 stdout 上看到我发送的每条消息 len: xxx
)
为什么 recv
在 Rust 代码中没有 return?
我错过了什么?
参考。跟踪输出
生锈
socket(AF_PACKET, SOCK_RAW, htons(0 /* ETH_P_??? */)) = 3
write(1, "sock: 3\n", 8sock: 3
) = 8
recvfrom(3,
in C (省略部分接收缓冲区)
socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) = 3
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
brk(NULL) = 0x5616f5ab4000
brk(0x5616f5ad5000) = 0x5616f5ad5000
write(1, "sock: 3\n", 8sock: 3
) = 8
recvfrom(3,
仅供参考,我 运行 通过以下方式跟踪。
$ docker run -it --cap-add sys_ptrace --entrypoint="/bin/bash" $IMAGE
$ cargo build && strace /app/target/debug/xxx
# or strace /main
我自己解决了这个问题。
传递给 libc::socket
的 libc::ETH_P_ALL
类型是 c_int
aliasing to i32
。
将 libc::ETH_P_ALL 作为 i32
转换为大端是不正确的 libc::socket
参数。
相反,它必须将 libc::ETH_P_ALL
转换为 u16
到大端。
use std::io;
fn main() -> io::Result<()> {
let sock = unsafe {
match libc::socket(
libc::AF_PACKET,
libc::SOCK_RAW,
- libc::ETH_P_ALL.to_be() as _,
+ (libc::ETH_P_ALL as u16).to_be() as _,
) {
-1 => Err(io::Error::last_os_error()),
fd => Ok(fd),
}
}?;
println!("sock: {}", sock);
let mut buf = [0u8; 1024];
loop {
let len = unsafe {
libc::recv(
sock,
(&mut buf[..]).as_mut_ptr() as *mut libc::c_void,
buf.len(),
0,
)
};
println!("len: {}", len);
}
}
仅供参考,
use libc;
fn main() {
assert_eq!(0x0003, libc::ETH_P_ALL);
assert_eq!(0x3000000, libc::ETH_P_ALL.to_be()); // <- incorrect in the question
assert_eq!(0x0003, libc::ETH_P_ALL as u16);
assert_eq!(0x300, (libc::ETH_P_ALL as u16).to_be()); // <- correct
}