参考资料:1:steven的unix网络编程
2:http://maguangzhi.bokee.com/5834192.html IPv4、TCP和UDP的校验和计算
3:http://blog.csdn.net/zfrong/archive/2008/12/31/3670863.aspx 使用rawsocket构造UDP数据包方法
4:http://blog.csdn.net/bmywindy/archive/2009/08/05/4412143.aspx Raw Socket(原始套接字)实现Sniffer(嗅探)
试验:用两台机器,一台发送,一台接收
接收端:开两个程序:1个udp包的接受,1个链路层上的接收(便于调试,有时自己构造的udp包发到了接收端,但由于自己构造的udp有些参数设置错了,导致在传输层上收不到)
发送端:
代码:
接收端:1传输层的接受代码:
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdio.h>
#define SERV_PORT 8888
void main()
{
char buf[5000];
int err=-1;
int n=0;
struct sockaddr_in remoteaddr;
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SERV_PORT);
int addlength = sizeof(struct sockaddr_in);
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
err=bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(err<0){
perror("bind failed");
close(sockfd);
exit(-1);
}
while(1){
n=recvfrom(sockfd,buf,5000,0,(struct sockaddr*)&remoteaddr,&addlength);
if(n<0){
perror("recvfrom failed");
close(sockfd);
continue;
}
printf("the receive data /n");
n=sendto(sockfd,buf,n,0,(struct sockaddr*)&remoteaddr, addlength);
if(n<0){
perror("sendto failed");
continue;
}
}
close(sockfd);
}
链路层上的接收:
/***************SimpelSniffer.c*************/
//auther:duanjigang@2006s
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#define BUFFER_MAX 2000
int main(int argc, char *argv[])
{
int fd;
int sock, n_read, proto;
char buffer[BUFFER_MAX];
char *ethhead, *iphead, *tcphead,
*udphead, *icmphead, *p;
if((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0)
{
fprintf(stdout, "create socket error/n");
exit(0);
}
while(1)
{
n_read = recvfrom(sock, buffer, BUFFER_MAX, 0, NULL, NULL);
/*
14 6(dest)+6(source)+2(type or length)
+
20 ip header
+
8 icmp,tcp or udp header
= 42
*/
if(n_read < 42)
{
fprintf(stdout, "Incomplete header, packet corrupt/n");
continue;
}
ethhead = buffer;
p = ethhead;
int n = 0XFF;
printf("MAC: %.2X:%02X:%02X:%02X:%02X:%02X==>"
"%.2X:%.2X:%.2X:%.2X:%.2X:%.2X/n",
p[6]&n, p[7]&n, p[8]&n, p[9]&n, p[10]&n, p[11]&n,
p[0]&n, p[1]&n, p[2]&n,p[3]&n, p[4]&n, p[5]&n);
iphead = ethhead + 14;
fd=open("receive",O_RDWR);
if(fd < 0)
perror("open file failed/n");
else{
write(fd,iphead,28);
}
close(fd);
p = iphead + 12;
printf("IP: %d.%d.%d.%d => %d.%d.%d.%d/n",
p[0]&0XFF, p[1]&0XFF, p[2]&0XFF, p[3]&0XFF,
p[4]&0XFF, p[5]&0XFF, p[6]&0XFF, p[7]&0XFF);
proto = (iphead + 9)[0];
p =iphead;
int a;
a=p[0]&0X0F;
printf("the iplenth is %d/n",a); //自己想在ip可选项方面做个小试验。
p = iphead+a*4;
printf("Protocol: ");
switch(proto)
{
case IPPROTO_ICMP: printf("ICMP/n");break;
case IPPROTO_IGMP: printf("IGMP/n");break;
case IPPROTO_IPIP: printf("IPIP/n");break;
case IPPROTO_TCP :
case IPPROTO_UDP :
printf("%s,", proto == IPPROTO_TCP ? "TCP": "UDP");
printf("source port: %u,",(p[0]<<8)&0XFF00 | p[1]&0XFF);
printf("dest port: %u/n", (p[2]<<8)&0XFF00 | p[3]&0XFF);
break;
case IPPROTO_RAW : printf("RAW/n");break;
default:printf("Unkown, please query in include/linux/in.h/n");
}
}
}
发送端代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#define iplength 20
#define BUFLEN 2000
#define CLIENTADDR "192.168.18.175"
#define PORT 8888
#define SERVADDR "192.168.18.174"
typedef struct ip_hdr{//ipv4头部
unsigned int ip_length:4; /*little-endian*/
unsigned int ip_version:4;
unsigned char ip_tos;
unsigned short ip_total_length;
unsigned short ip_id;
unsigned short ip_flags;
unsigned char ip_ttl;
unsigned char ip_protocol;
unsigned short ip_cksum;
unsigned int ip_source;
unsigned int ip_dest;
// unsigned int LSH;
}ip_hdr;
typedef struct udp_hdr{//udp头部
unsigned short s_port;
unsigned short d_port;
unsigned short length;
unsigned short cksum;
}udp_hdr;
typedef struct psd_header{//伪头部,用于计算校验和
unsigned int s_ip;//source ip
unsigned int d_ip;//dest ip
unsigned char mbz;//0
unsigned char proto;//proto type
unsigned short plen;//length
}psd_header;
void swap(unsigned int *a, unsigned int *b)//交换
{
*a = (*a)^(*b);
*b = (*a)^(*b);
*a = (*a)^(*b);
}
unsigned short checksum(unsigned short* buffer, int size)//校验和
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if(size)
{
cksum += *(unsigned char*)buffer;
}
cksum = (cksum>>16) + (cksum&0xffff); //将高16bit与低16bit相加
cksum += (cksum>>16); //将进位到高位的16bit与低16bit 再相加
return (unsigned short)(~cksum);
}
int main(int argc, char *argv[])
{
char buf[BUFLEN];
int sockfd = -1;
memset(buf,0,BUFLEN);
struct sockaddr_in host_addr;
if((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP))<0)
{
printf("socket() error!/n");
exit(1);
}
memset(&host_addr, 0, sizeof(host_addr));
host_addr.sin_family = AF_INET;
host_addr.sin_port = htons(PORT);
host_addr.sin_addr.s_addr = inet_addr(SERVADDR);
const int on = 1;
if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))<0)
{
printf("setsockopt() error!/n");
exit(0);
}
int addr_len = sizeof(host_addr);
// while(1)
// {
ip_hdr *iphdr;
iphdr = (ip_hdr*)buf;
udp_hdr *udphdr;
udphdr = (udp_hdr*)(iphdr+1);
iphdr->ip_length = (sizeof(iphdr)!=24)?5:6;
iphdr->ip_version = 4;
iphdr->ip_tos = 0;
iphdr->ip_total_length = htons(sizeof(buf));
iphdr->ip_id = 0;
iphdr->ip_flags = 0;
iphdr->ip_ttl = 0x40;
iphdr->ip_protocol = 0x11;
iphdr->ip_cksum = 0;
iphdr->ip_source = inet_addr(CLIENTADDR);//源地址
iphdr->ip_dest = inet_addr(SERVADDR);//目的地址
iphdr->ip_cksum = checksum((unsigned short*)buf, 20);
udphdr->s_port = htons(5000);//源端口
udphdr->d_port = htons(PORT);//目的端口
udphdr->length = htons(sizeof(buf)-iplength);
udphdr->cksum = 0;
psd_header psd;
psd.s_ip = iphdr->ip_source;
psd.d_ip = iphdr->ip_dest;
psd.mbz = 0;
psd.proto = 0x11;
psd.plen = udphdr->length;
char tmp[sizeof(psd)+ntohs(udphdr->length)];
memcpy(tmp, &psd, sizeof(psd));
memcpy(tmp+sizeof(psd), buf+iplength, sizeof(buf)-iplength);
udphdr->cksum = checksum((unsigned short*)tmp, sizeof(tmp));
int res =sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&host_addr, sizeof(host_addr));
if(res == -1)
perror("sendto failed");
sleep(1);
// }
return 0;
}
注意:用rawsocket发送数据,在以太网上最多只能发1500byte,多了就报错,我猜底层应该不会再继续进行ip分片了,