用raw socket接收udp数据的C/C++代码实战

972 篇文章 329 订阅
148 篇文章 34 订阅

       最近要用到raw socket, 来看下。server.cpp:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <stdlib.h>
#include <arpa/inet.h>

int main()
{     
	printf("main is running\n");

    int iSock, nRead, iProtocol;        
    char buffer[4096] = {0};
    char  *ethhead, *iphead, *tcphead, *udphead, *icmphead, *p;
        
	if((iSock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0)
    {
        printf("create iSocket error, check root\n");  // 需要root权限, 最后运行的时候, 可以用sudo ./server
        return 1;
    }
	        
	while(1) 
	{
		nRead = recvfrom(iSock, buffer, 2048, 0, NULL, NULL);  
		/*
			以太网帧头 14
			ip头       20
			udp头      8
			总共42字节(最少)
		*/
		if(nRead < 42) 
		{
			printf("packet error\n");
			continue;
		}
	        	
		int n = 0XFF;
		char szVisBuf[1024] = {0};
		for(unsigned int i = 0; i < nRead; ++i)
		{
			char szTmp[3] = {0};
			sprintf(szTmp, "%02x", buffer[i]&n);
			strcat(szVisBuf, szTmp);
		}
			
		
		ethhead = buffer;
		p = ethhead;
		
		iphead = ethhead + 14;  
		p = iphead + 12;

		char szIps[128] = {0};
		snprintf(szIps, sizeof(szIps), "IP: %d.%d.%d.%d => %d.%d.%d.%d",
			p[0]&n, p[1]&n, p[2]&n, p[3]&n,
			p[4]&n, p[5]&n, p[6]&n, p[7]&n);
		iProtocol = (iphead + 9)[0];
		p = iphead + 20;
		
		
		unsigned int iDstPort = (p[2]<<8)&0xff00 | p[3]&n;
		switch(iProtocol)
		{
			case IPPROTO_UDP : 
				if(iDstPort == 8888)
				{
					printf("source port: %u,",(p[0]<<8)&0xff00 |  p[1]&n);
					printf("dest port: %u\n", iDstPort);
					
					printf("%s\n", szIps);	
					printf("%s\n", szVisBuf);
					printf("nRead is %d\n", nRead);	
					
				}
				break;
			case IPPROTO_RAW : 
				printf("raw\n");
				break;
			default:
				break;
		}
	}
}

       启动它。

 

      client.cpp:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main()
{
    struct sockaddr_in srvAddr;
    bzero(&srvAddr, sizeof(srvAddr));
    srvAddr.sin_family = AF_INET;
    srvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    srvAddr.sin_port = htons(8888);

    int iSock = socket(AF_INET, SOCK_DGRAM, 0); // udp
	int i = 0;
    while(1)
    {
		printf("press enter to send data\n");
		getchar(); // 卡住
		char szBuf[32] = {0};
        snprintf(szBuf, sizeof(szBuf), "hello %d", ++i);
        sendto(iSock, szBuf, strlen(szBuf) + 1, 0, (struct sockaddr *)&srvAddr, sizeof(srvAddr));
    }

	close(iSock);
    return 0;
}

       向server发包。

 

       抓包结果:

13:59:37.865687  In 00:00:00:00:00:00 ethertype IPv4 (0x0800), length 53: 127.0.0.1.49574 > 127.0.0.1.8888: UDP, length 9
        0x0000:  0000 0304 0006 0000 0000 0000 0000 0800  ................
        0x0010:  4500 0025 a3b1 4000 4011 9914 7f00 0001  E..%..@.@.......
        0x0020:  7f00 0001 c1a6 22b8 0011 fe24 6865 6c6c  ......"....$hell
        0x0030:  6f20 3437 00                             o.47.

       server端结果:

source port: 49574,dest port: 8888
IP: 127.0.0.1 => 127.0.0.1
000000000000000000000000080045000025a3b14000401199147f0000017f000001c1a622b80011fe2468656c6c6f20343700
nRead is 51

        仔细对比下, 一目了然。

        注意:

        1. tcpdump多了最开始的0000,它不属于以太帧

        2. server端获取的mac为空, 难道跟环回地址有关? 有空再研究。

 

 

以下是基于socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL))的接收和发送数据软件的C++代码示例: ```c++ #include <iostream> #include <cstring> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <net/ethernet.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <netinet/udp.h> #define ETH_HDRLEN 14 using namespace std; int main(int argc, char* argv[]) { int sock_fd; struct ifreq ifr; char iface[] = "eth0"; // 设备名称 char buffer[ETH_FRAME_LEN]; struct sockaddr_in source, dest; struct ethhdr* eth_hdr; struct iphdr* ip_hdr; struct tcphdr* tcp_hdr; int data_len; // 创建原始套接字 sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock_fd < 0) { perror("Error creating socket"); return 1; } // 获取设备索引号 memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); if (ioctl(sock_fd, SIOCGIFINDEX, &ifr) < 0) { perror("Error getting interface index"); return 1; } // 将socket与设备绑定 struct sockaddr_ll socket_address; memset(&socket_address, 0, sizeof(socket_address)); socket_address.sll_family = PF_PACKET; socket_address.sll_ifindex = ifr.ifr_ifindex; socket_address.sll_protocol = htons(ETH_P_ALL); bind(sock_fd, (struct sockaddr*)&socket_address, sizeof(socket_address)); // 接收数据包 while (true) { data_len = recvfrom(sock_fd, buffer, ETH_FRAME_LEN, 0, NULL, NULL); if (data_len < 0) { perror("Error receiving packet"); break; } // 解析数据包 eth_hdr = (struct ethhdr*)buffer; if (ntohs(eth_hdr->h_proto) != ETH_P_IP) { continue; } ip_hdr = (struct iphdr*)(buffer + ETH_HDRLEN); memset(&source, 0, sizeof(source)); source.sin_addr.s_addr = ip_hdr->saddr; memset(&dest, 0, sizeof(dest)); dest.sin_addr.s_addr = ip_hdr->daddr; if (ip_hdr->protocol == IPPROTO_TCP) { tcp_hdr = (struct tcphdr*)(buffer + ETH_HDRLEN + (ip_hdr->ihl * 4)); cout << inet_ntoa(source.sin_addr) << ":" << ntohs(tcp_hdr->source) << " -> " << inet_ntoa(dest.sin_addr) << ":" << ntohs(tcp_hdr->dest) << endl; } } // 发送数据包 memset(buffer, 0, sizeof(buffer)); eth_hdr = (struct ethhdr*)buffer; // 目标MAC地址 eth_hdr->h_dest[0] = 0x00; eth_hdr->h_dest[1] = 0x11; eth_hdr->h_dest[2] = 0x22; eth_hdr->h_dest[3] = 0x33; eth_hdr->h_dest[4] = 0x44; eth_hdr->h_dest[5] = 0x55; // 源MAC地址 eth_hdr->h_source[0] = 0x55; eth_hdr->h_source[1] = 0x44; eth_hdr->h_source[2] = 0x33; eth_hdr->h_source[3] = 0x22; eth_hdr->h_source[4] = 0x11; eth_hdr->h_source[5] = 0x00; // 协议类型 eth_hdr->h_proto = htons(ETH_P_IP); // IP头 ip_hdr = (struct iphdr*)(buffer + ETH_HDRLEN); ip_hdr->version = 4; ip_hdr->ihl = 5; ip_hdr->tos = 0; ip_hdr->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr)); ip_hdr->id = htons(12345); ip_hdr->frag_off = 0; ip_hdr->ttl = 255; ip_hdr->protocol = IPPROTO_TCP; ip_hdr->check = 0; ip_hdr->saddr = inet_addr("192.168.0.1"); ip_hdr->daddr = inet_addr("192.168.0.2"); // TCP头 tcp_hdr = (struct tcphdr*)(buffer + ETH_HDRLEN + (ip_hdr->ihl * 4)); tcp_hdr->source = htons(12345); tcp_hdr->dest = htons(80); tcp_hdr->seq = htonl(1); tcp_hdr->ack_seq = 0; tcp_hdr->doff = 5; tcp_hdr->urg = 0; tcp_hdr->ack = 0; tcp_hdr->psh = 0; tcp_hdr->rst = 0; tcp_hdr->syn = 1; tcp_hdr->fin = 0; tcp_hdr->window = htons(1024); tcp_hdr->check = 0; tcp_hdr->urg_ptr = 0; // 发送数据包 data_len = send(sock_fd, buffer, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr), 0); if (data_len < 0) { perror("Error sending packet"); return 1; } close(sock_fd); return 0; } ``` 该程序在基于socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL))的基础上,添加了发送数据包的功能。程序首先使用recvfrom函数接收数据包,并解析出IP、TCP头的源地址、目的地址、源端口、目的端口等信息进行输出。然后,程序构造了一个TCP SYN数据包,并使用send函数发送出去,实现了发送数据包的功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值