环境:内网众多机器会发出ping,希望在网关上进行修改,使得所有ping 包在网关被截获,并返回ping reply。
最简单的解决方法就是iptables 转发的网关内部地址,由其负责回应ping。但这样就会使得ping 值非常小,作假也看着不像了。当时想写个程序控制回应ping 包,但这样回应就会有两个,一个网卡自身回应的ping 包,和程序回应的ping 包,为了拦截网卡本省发出的ping 包,可以采用内核模块编程,hook 住ICMP 包,这样有点超过我的范围了,调了一天多,放弃了。最后使用了一个折中的办法,iptables 将内网ping 包转发到外网ip 地址,运行ping 回应程序,最后iptables 拦截所有外网地址的ICMP 包,这样网关程序既可以收到ping 包回应,而外网卡的ping 被REJECT ,而内网卡的ping 不被REJECT。有个小小的问题就是client 收到ping 的回应都带有(DUP!),可能是因为报文的内容的原因,有知道原因的请告诉我哦~~~
下面三张图帮助了解iptables 机制:
DNAT 修改目的地址在进行路由之前进行
SNAT 修改源地址在进行路由之后进行
INPUT 控制所有进入网关处理的包(比如在DNAT 将–to-destination 改为网关地址)
OUTPUT 控制所有从网关发出的包
下面是转发流程:
下面是网关处理后转发流程:
inet_addr
函数作用:将“xxx.xxx.xxx.xxx”字符串格式地址转换为结构格式,返回地址
函数申明:in_addr_t inet_addr(const char *cp);
头文件:<arpa/inet.h>
inet_ntoa
函数作用:将地址转换为“xxx.xxx.xxx.xxx”字符串格式,返回地址
函数申明:char *inet_ntoa (struct in_addr);
头文件:<arpa/inet.h>
recvfrom
函数作用:经socket接收数据
函数申明:ssize_t recvfrom(int sockfd,void *buf,int len,unsigned int flags, struct sockaddr *from,socket_t *fromlen); 六个参数分别是一个已连接套接口的描述字、接收数据缓冲区、缓冲区长度、调用操作方式、装有源地址的缓冲区和缓冲区长度值。
头文件:<arpa/inet.h>
1: #include <;unistd.h>
2: #include <;fcntl.h>
3: #include <;stdlib.h>
4: #include <;stdio.h>
5: #include <;string.h>
6: #include <;errno.h>
7: #include <;sys/ioctl.h>
8: #include <;sys/socket.h>
9: #include <;linux/if.h>
10: #include <;linux/if_tun.h>
11: #include <;sys/types.h>
12: #include <;netinet/in.h>
13: #include <;netinet/ip.h>
14: #include <;netinet/ip_icmp.h>
15: #include <;arpa/inet.h>
16: #include <;syswait.h>
17:
18: int id;
19:
20: int get_icmp(int sock,
21: unsigned char * buff,
22: int buff_len,
23: struct sockaddr_in * addr,
24: socklen_t * len){
25: struct ip * ip;
26: static u_char outpack[4096];
27: struct icmp * icmp=(struct icmp *)outpack;
28: int ret,iphl;
29:
30: printf("waiting for your ping ...\n");
31: ret = recvfrom(sock,buff,buff_len,0,(struct sockaddr *)addr,len);
32: if (ret){
33: ip = (struct ip *) buff;
34: iphl = ip->ip_hl << 2;
35: printf(">>>>>ip length: %d\n",iphl);
36: printf(">>>>>ip address: %s\n",inet_ntoa(addr->sin_addr));
37: icmp = (struct icmp *) (buff + iphl);
38: id=icmp->icmp_id;
39: printf(">>>>>id= %d\n",id);
40: memmove(buff,buff+(iphl+8),ret-(iphl+8));
41: return(ret-(iphl+8));
42: }
43: }
44:
45: int send_icmp(int sock,
46: unsigned char * buff,
47: int buff_len,
48: struct sockaddr_in * addr,
49: socklen_t len){
50: int ret,cc;
51: static u_char outpack[4096];
52: struct icmp * icmp = (struct icmp *) outpack;
53:
54: icmp->icmp_type = ICMP_ECHOREPLY;
55: icmp->;icmp_code = 0;
56: icmp->;icmp_cksum = 0;
57: icmp->;icmp_seq = 0;
58: icmp->;icmp_id = id;
59: cc = 8 + buff_len;
60: memcpy(&outpack[8],buff,buff_len);
61: printf("echo your ping now!\n");
62: printf(">>>>>ip address: %s\n",inet_ntoa(addr->sin_addr));
63: if ((ret = sendto(sock, outpack, cc, 0,(struct sockaddr *) addr, len)) <; 0){
64: perror("icmp sendto");
65: exit(1);
66: }
67: return(ret);
68: }
69:
70: unsigned short get_delay(char* ip_p){
71: unsigned short delay=10;
72:
73: delay=20;
74: return delay;
75: }
76:
77: void wait_time(struct sockaddr_in *addr){
78: char ip_str[16];
79: unsigned short delay;
80: char * ip_p;
81:
82: ip_p=ip_str;
83: sprintf(ip_p,"%s",inet_ntoa(addr->;sin_addr));
84: delay=get_delay(ip_p);
85: usleep(delay*1000);
86: }
87:
88: int main(void){
89: unsigned char buff[4096];
90: fd_set rset;
91: struct sockaddr_in addr;
92: struct sockaddr_in addr_recv;
93: int sock;
94: int ret;
95: socklen_t len = sizeof(struct sockaddr_in);
96: socklen_t len_recv = sizeof(struct sockaddr_in);
97: int tunfd;
98:
99: if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0){
100: perror("error: socket\n");
101: exit(1);
102: }
103: memset(&;addr,0,sizeof(struct sockaddr_in));
104: addr.sin_family = AF_INET;
105: addr.sin_port = 0;
106: while(1){
107: FD_ZERO(&;rset);
108: FD_SET(sock,&;rset);
109: if (FD_ISSET(sock,&;rset)){
110: printf(">>>ready to test\n");
111: if ((ret = get_icmp(sock,buff,sizeof(buff),&addr_recv,&len_recv)) > 0){
112: printf(">>>ping received\n");
113: }
114: }
115: memcpy((char *)&;addr.sin_addr,(char *)&addr_recv.sin_addr,sizeof(addr.sin_addr));
116: wait_time(&;addr);
117: printf(">>>ready to echo\n");
118: send_icmp(sock,buff,ret,&addr,len);
119: printf(">>>echo sent!\n");
120: }
121: }
参考文献:
- 用C 实现ping 程序功能
- recvfrom 函数
- FS_SET()
- struct sockeaddr_in
- iptables 的升级宝典
- ping.c 内核源码