第一回分析源码
程序流程一览图
感觉有点简单吧,ping的核心就是上面的图了,而其它都是对选项的处理,不仅仅是ping源码,其它开源代码一般都是一堆选项,如果一开始就被那些对分析程序核心没有帮助的选项所困扰,那么分析一个程序需要的时间和效率就可想而知。当然ping的很多选项都是值得分析的。
这个ping源码控制发包的时间间隔是通过main_loop()比较复杂的时间计算控制,因为它要控制诸如-l这样的选项,如果纯粹是控制时间,那么注册个SIGALRM信号处理函数,再加个1秒的定时器alarm(1)或者就直接sleep(1)就可以很轻松的实现。-w选项就是通过alarm完成的,SIGALRM信号处理函数是在main()->setup()中设置的,具体请看setup函数分析。
1,准备工作,获得ping源码并学会编译
会编译了看代码其实就是后续的活,网上找的方法,挺好用……
[root@xxx study_2]# type ping
ping is /bin/ping
[root@xxx study_2]# rpm -qf /bin/ping
iputils-20020927-46.el5
从而得知ping属于iputils包,去下iputils源码包即可,其它linux 命令源码获取方法类似。
[root@xxx iputils]# make
Please, set correct KERNEL_INCLUDE
make: *** [check-kernel] 错误 1
反正是一堆错误,改了这个来了那个,看了INSTALL说是被定制过的linux头文件可能会不支持,反正只要能编出ping就行,改了下Makefile,把有关KERNEL_INCLUDE的都屏蔽了,编译成功。
2,源码分析
只分析实现最简单的不带选项的ping程序。也花了一个星期的业余时间。
112main(int argc, char **argv)
113{
114 struct hostent *hp;
115 int ch, hold, packlen;
116 int socket_errno;
117 u_char *packet;
118 char *target, hnamebuf[MAXHOSTNAMELEN];
119 char rspace[3 + 4 * NROUTES + 1]; /* record route space */
120#ifdef DO_IPSEC
121 char *policy_string = NULL;
122#endif
123
124 icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); //创建icmp套接字
125 socket_errno = errno;
126
127 uid = getuid();//设置进程有效用户ID为实际用户uid,访问权限方面的知识。
128 setuid(uid);
129
130 source.sin_family = AF_INET;
131
132 preload = 1;//这个参数默认为1,可由-l选项控制,一次发多个包而不管对方是否回应,其本质还是通过计算时间来实现,具体可看main_loop().没兴趣不用管。
133 while ((ch = getopt(argc, argv, COMMON_OPTSTR "bRT:P:")) !=EOF) {
134 switch(ch) {//getopt得好好学会。
213 default:
214 usage();
215 }
216 }
217 argc -= optind;//可参考GETOPT(3) ,是个全局变量
218 argv += optind;
219
//这里对选项的处理,不管,option为0。
220 printf("options :%d\n", options );
221 if (argc == 0)
222 usage();
223 if (argc > 1) {
224 if (options & F_RROUTE)
225 usage();
226 else if (options & F_TIMESTAMP) {
227 if (ts_type != IPOPT_TS_PRESPEC)
228 usage();
229 if (argc > 5)
230 usage();
231 } else {
232 if (argc > 10)
233 usage();
234 options |= F_SOURCEROUTE;
235 }
236 }
//处理传给ping的参数
237 while (argc > 0) {
//我只传一个目标IP,所以argc=1,这个循环只做一回,把IP地址给了target
238 target = *argv;
239 printf("target:%s\n", target);
240
241 bzero((char *)&whereto, sizeof(whereto));
242 whereto.sin_family = AF_INET;
243 if (inet_aton(target, &whereto.sin_addr) == 1) {//如果是IP地址
244 hostname = target;
245 if (argc == 1)
246 options |= F_NUMERIC;
247 } else {//如果是其它域名
248 hp = gethostbyname(target);
249 if (!hp) {//如果解析域名不成功,返回
250 fprintf(stderr, "ping:unknown host %s\n", target);
251 exit(2);
252 }
253 memcpy(&whereto.sin_addr, hp->h_addr, 4);
254 strncpy(hnamebuf, hp->h_name,sizeof(hnamebuf) - 1);
255 hnamebuf[sizeof(hnamebuf) - 1] = 0;
256 hostname = hnamebuf;
257 }
258 if (argc > 1)
259 route[nroute++] =