最近要用到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为空, 难道跟环回地址有关? 有空再研究。