linux raw socket 编程
raw socket编程。
运行第一段代码,发送ip数据报,第二段代码接收ip数据报。需要运行第二段代码,否则将无法接收数据报。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
struct iphead{
unsigned char ip_hl:4, ip_version:4; //ip_hl,ip_version各占四个bit位。
unsigned char ip_tos;
unsigned short int ip_len;
unsigned short int ip_id;
unsigned short int ip_off;
unsigned char ip_ttl;
unsigned char ip_pro;
unsigned short int ip_sum;
unsigned int ip_src;
unsigned int ip_dst;
};
struct icmphead{ //该结构体模拟ICMP报文首部
unsigned char icmp_type;
unsigned char icmp_code;
unsigned short int icmp_sum;
unsigned short int icmp_id;
unsigned short int icmp_seq;
};
unsigned short int cksum(char buffer[], int size){ //计算校验和,具体的算法可自行百度,或查阅资料
unsigned long sum = 0;
unsigned short int answer;
unsigned short int *temp;
temp = (short int *)buffer;
for( ; temp<buffer+size; temp+=1){
sum += *temp;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
int main(){
int sockfd;
struct sockaddr_in conn;
struct iphead *ip;
struct icmphead *icmp;
unsigned char package[sizeof(struct iphead) + sizeof(struct icmphead)]; //package存储IP数据报的首部和数据
memset(package, 0, sizeof(package));
ip = (struct iphead*)package;
icmp = (struct icmphead*)(package+sizeof(struct iphead)); //IP数据报数据字段仅仅包含一个ICMP首部
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); //创建套接字
if(sockfd < 0){
printf("Create socket failed\n");
return -1;
}
conn.sin_family = AF_INET;
conn.sin_addr.s_addr = inet_addr("192.168.230.135");
int one = 1;
if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0){ //设置套接字行为,此处设置套接字不添加IP首部
printf("setsockopt failed!\n");
return -1;
}
/*设置IP首部各个字段的值*/
ip->ip_version = 4;
ip->ip_hl = 5;
ip->ip_tos = 0;
ip->ip_len = htons(sizeof(struct iphead) + sizeof(struct icmphead)); //关于htons()、htonl()的作用,可自行百度
ip->ip_id = htons(1);
ip->ip_off = htons(0x4000);
ip->ip_ttl = 10;
ip->ip_pro = IPPROTO_ICMP;
ip->ip_src = htonl(inet_addr("192.168.230.135"));
ip->ip_dst = htonl(inet_addr("192.168.230.135"));
printf("ipcksum : %d\n", cksum(package, 20));
ip->ip_sum = cksum(package, 20); // 计算校验和,应当在其他字段之后设置(实验中发现检验和会被自动添加上)
/*设置ICMP首部各字段值*/
icmp->icmp_type = 8;
icmp->icmp_code = 0;
icmp->icmp_id = 1;
icmp->icmp_seq = 0;
icmp->icmp_sum = (cksum(package+20, 8));
/*接下来发送IP数据报即可*/
if(sendto(sockfd, package, htons(ip->ip_len), 0,(struct sockaddr *)&conn, sizeof(struct sockaddr)) < 0){
printf("send failed\n");
return -1;
}
printf("send successful\n");
return 0;
}
第二段程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <linux/if_ether.h>
unsigned short int cksum(char buffer[], int size){ //校验函数
unsigned long sum = 0;
unsigned short int answer;
unsigned short int *temp;
temp = (short int *)buffer;
for( ; temp<buffer+size; temp+=1)
sum += *temp;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
int main(){
unsigned char buffer[1024];
// int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);//不知为啥,无法设置原始套接字在网络层抓IP数据报
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); //此处,利用原始套接字在数据链路层抓取MAC帧,去掉
if(sockfd < 0){ //14个字节的MAC帧首部即可
printf("create sock failed\n");
return -1;
}
int n = recvfrom(sockfd, buffer, 1024, 0, NULL, NULL); //接收MAC帧
printf("receive %d bytes\n", n);
for(int i=14; i<n; i++){ //去掉MAC帧首部,直接输出IP数据报每个字节的数据
if((i-14) % 16 == 0)
printf("\n");
printf("%d ",buffer[i]);
}
printf("\n");
printf("ipcksum: %d\n", cksum(buffer+14, 20)); //此处再次校验时,应当输出0
return 0;
}
这是之前参考
Referenced from:https://blog.csdn.net/nice_wen/article/details/53416063