-
raw socket分为2种
- 网络层数据帧
socket(AF_INET, SOCK_RAW, IPPROTO_XXX) // 发送、接收网络层IP数据包 网络层
- 数据链路帧
socket(PF_PACKET, SOCK_RAW, htons(ETH_P_XXX)) // 发送、接收数据链路层
【以ptp报文帧为例】
raw socket通过帧报文中的DMAC地址知道发送给谁
要注意的是send/recv函数使用MSG_WAITALL的时候,sockfd 必须是处于阻塞模式下,否则WAITALL不能起作用。
-
raw socket如何过滤
- 用raw socket会收到很多的以太网帧
- solve:告诉底层 raw socket, 哪些包是自己想要的,直接在底层被过滤掉,即bpf
- 在终端输入命令获取过滤参数: sudo tcpdump -dd -i eth0 tcp port 9090 -s 0
- 在将过滤参数写入代码
raw socket 使用 BPF 过滤报文_confirmwz的博客-CSDN博客_bpf socket
#include <stdlib.h> #include <stdio.h> #include <sys/socket.h> #include <net/if_arp.h> #include <net/if.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/ethernet.h> #include <netpacket/packet.h> #include <linux/filter.h> unsigned char buffer[102]; void main() { int fd, n, i; struct sock_fprog filter; struct sock_filter code[] = { { 0x28, 0, 0, 0x0000000c }, { 0x15, 0, 6, 0x000086dd }, { 0x30, 0, 0, 0x00000014 }, { 0x15, 0, 15, 0x00000006 }, { 0x28, 0, 0, 0x00000036 }, { 0x15, 12, 0, 0x00002382 }, { 0x28, 0, 0, 0x00000038 }, { 0x15, 10, 11, 0x00002382 }, { 0x15, 0, 10, 0x00000800 }, { 0x30, 0, 0, 0x00000017 }, { 0x15, 0, 8, 0x00000006 }, { 0x28, 0, 0, 0x00000014 }, { 0x45, 6, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x48, 0, 0, 0x0000000e }, { 0x15, 2, 0, 0x00002382 }, { 0x48, 0, 0, 0x00000010 }, { 0x15, 0, 1, 0x00002382 }, { 0x6, 0, 0, 0x00040000 }, { 0x6, 0, 0, 0x00000000 }, }; filter.len = sizeof(code)/sizeof(struct sock_filter); filter.filter = code; fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (fd < 0 ) { perror("socket fail\n"); exit(1); } //设置 sk_filter if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0) { perror("setsockopt fail\n"); exit(1); } /*block*/ n = recvfrom(fd, buffer, sizeof(buffer), 0, NULL, NULL); if (n < 0) { perror("recvfrom none\n"); exit(1); } for (i = 0; i < n ;i++) { printf("%02x ", buffer[i]); } }
- 过滤指定mac地址的mac帧
sudo tcpdump ether host 01:1b:19:00:00:00 -vvv -xx
- 过滤指定的以太网帧类型为0x88f7的包
root@pc123:/usrdata# tcpdump -dd ether proto 0x88F7
[ 2073.317491] device eth0 entered promiscuous mode
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 1, 0x000088f7 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
[ 2073.357828] device eth0 left promiscuous mode
-
混杂模式:
在调试过程中,用tcpdump发现可以抓到包,但应用程序拿不到这个包。
查资料发现:默认情况下,网卡接收一个包,网卡只把发给本机的包(包括广播包)传到上层的APP
混杂模式就是指网卡能接受所有通过它的数据流,无论是什么模式、什么地址的。当网卡处于这种“混杂”模式时,它对所有遇到的每一个数据帧都产生一个硬件中断,以提醒操作系统处理流经该物理媒体上的每一个报文包。
- 设置网卡eth0为混杂模式
[root@localhost test]# ifconfig eth0 promisc
- 取消网卡混杂模式
[root@localhost test]# ifconfig enp0s3 -promisc
- 查看方式: ifconfig 看网卡信息是否有PROMISC字段
-
一个用raw socket的client/server例子
- 编译: g++ main.cpp -lpthread
- 运行: ./a.out master 或者 ./a.out slave
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <ctype.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <linux/if_packet.h> #include <netinet/in.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <net/if.h> #include <string.h> #include <netinet/in.h> #include <linux/if_ether.h> #include <pthread.h> #include <sys/ioctl.h> #include <linux/filter.h> enum role { master, slave, }; enum role mTmprole; int fd_raw; void master_process_msg() { uint8_t tmp_buffer[300] = {'0'}; ssize_t size = recvfrom(fd_raw, tmp_buffer, sizeof(tmp_buffer), 0, NULL, NULL); if (size > 14) { printf("master recv msg size [%ld]\n", size); } } void slave_process_msg() { uint8_t tmp_buffer[300] = {'0'}; ssize_t size = recvfrom(fd_raw, tmp_buffer, sizeof(tmp_buffer), 0, NULL, NULL); if (size > 14) { printf(" recv msg size [%ld] \n", size); } } void process_msg() { if (mTmprole == master) { master_process_msg(); } else if (mTmprole == slave) { slave_process_msg(); } else { printf("please selecet role\n"); exit(0); } } void *thread_recv(void* arg) { printf("thread is running!!!!\n"); while(1) { process_msg(); } printf("thread recv over!!!!!!\n"); } int start_recv_msg_thread() { int res; pthread_t a_thread; // void *thread_result; res = pthread_create(&a_thread, NULL, thread_recv, NULL); if(res != 0){ printf("error create thread\n"); } pthread_detach(a_thread); return 0; } void add_raw_filter() { // sudo tcpdump -dd ether host 01:1b:19:00:00:00 -vvv -xx /*struct sock_filter code[] = { { 0x20, 0, 0, 0x00000008 }, { 0x15, 0, 2, 0x19000000 }, { 0x28, 0, 0, 0x00000006 }, { 0x15, 4, 0, 0x0000011b }, { 0x20, 0, 0, 0x00000002 }, { 0x15, 0, 3, 0x19000000 }, { 0x28, 0, 0, 0x00000000 }, { 0x15, 0, 1, 0x0000011b }, { 0x6, 0, 0, 0x00040000 }, { 0x6, 0, 0, 0x00000000 }, };*/ struct sock_fprog filter; filter.len = sizeof(code)/sizeof(struct sock_filter); filter.filter = code; if (setsockopt(fd_raw, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0) { perror("setsockopt fail\n"); exit(1); } } int open_raw_socket(const char* interface) { fd_raw = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); //收发数据链路层的数据包 if (fd_raw == -1) { printf("socket failed: [%d] [%s]\n", fd_raw, strerror(errno)); return -1; } else { printf("socket [%d] \n", fd_raw); } // add_raw_filter(); ioctl(fd_raw, FIONBIO, 0); //1:非阻塞 0:阻塞 struct sockaddr_ll addr; memset(&addr, 0, sizeof(addr)); addr.sll_ifindex = if_nametoindex(interface); addr.sll_family = AF_PACKET; addr.sll_protocol = htons(ETH_P_ALL); int err = bind(fd_raw, (struct sockaddr *) &addr, sizeof(addr)); if (err != 0) { printf("raw socket bind failed: [%d] [%s]\n", err, strerror(errno)); return -1; } else { printf("raw socket bind sucess [%s] [%d]\n", interface, fd_raw); } // 开启接收线程 start_recv_msg_thread(); return 0; } void enter_promis_mode(char* mac_name) { // 进入混杂模式 char cmd[50] ={'0'}; // ifconfig %s promisc snprintf(cmd, 50, "ifconfig %s promisc", mac_name); printf("[%s]\n", cmd); system(cmd); } int main(int argc, char** argv) { if (argc >= 2) { printf("argv = %s\n", argv[1]); if (strcmp(argv[1], "master")) { mTmprole = master; } else if (strcmp(argv[1], "slave")) { mTmprole = slave; } } else { exit(0); } enter_promis_mode("eth0"); open_raw_socket("eth0"); while(1) { sleep(3); } exit(0); }