RAW socket

  • 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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L7256

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值