traceroute程序剖析

    traceroute允许我们确定IP数据报从本地主机游历到某个远程主机所经过的路径。

    我们先来说明tranceroute的工作原理:是IP路由过程中对数据包TTL(Time to Live,存活时间)的处理。当路由器收到一个IP包时,会修改IP包的TTL(及由此造成的头部检测和checksum变化)。每收到一个包,检查这个的TTL是否是0或1.如果是,表明这个包还没有到达目的地,而且剩余时间不多了,肯定是到不了目的地了。这样路由器就简单地丢弃这个包,并给源主机发送ICMP通知,说明这个包已经过时了。ICMP的通知信息里包含当前路由器发送时所用的IP。

    这样就可以通过构造数据包,来间接检查到达一个主机时经过了哪些路由器。一开始发送一个TTL为1的包,这样到达第一个路由器的时侯就已经超时了,第一个路由器就发通知说包超时,这样就可以记录下所经过的第一个路由器所的IP。然后TTL加1,安全通过第一个路由器,而第二个路由器的处理与第一个相同,丢包,发通知说包超时了。这样记录下的第二个路由器IP,由此可以一直下去,直到这个数据包到达目标主机,由此打印所有经过的路由器。

    在通信中,IP层只负责数据的路由与传输,并不处理数据包的内容。例如ICMP,或TCP,UDP,这些协议是依赖IP层的传输功能来传输数据的。在通信双方的主机中,收到这些协议的数据包后,一般在通信的对应主机上,会有程序来处理这些数据。因此traceroute程序发送一个UDP包来试探。对路由器来说,UDP数据报只是IP数据报的一种,它并不关心UDP数据报的具体内容。直到这个包到达目的端口的主机,目的主机的内核会解析UDP数据报,并查找数据报中要求的端口是否已经有进程在使用。如果找到,则通知进程有数据到达。而如果找不到,则发送一个“目的端口不可达”的ICMP错误数据回到源主机。

      这样就可以完全确定下来。trcertroute建立一个UDP数据包,不断修改TTL值并发送出去,如果收到"超时错",表示刚刚到达的是路由器,而如果收到的是"端口不可达"错误,表示刚刚到达的就是目的主机。这样路由跟踪完成,程序结束。

   

下面给出部分源码(主要针对IPv4):

trace.h:

#include	"unp.h"
#include	<netinet/in_systm.h>
#include	<netinet/ip.h>
#include	<netinet/ip_icmp.h>
#include	<netinet/udp.h>

#define	BUFSIZE		1500

struct rec {					/* format of outgoing UDP data */
  u_short	rec_seq;			/* sequence number */
  u_short	rec_ttl;			/* TTL packet left with */
  struct timeval	rec_tv;		/* time packet left */
};

			/* globals */
char	 recvbuf[BUFSIZE];
char	 sendbuf[BUFSIZE];

int		 datalen;			/* # bytes of data following ICMP header */
char	*host;
u_short	 sport, dport;
int		 nsent;				/* add 1 for each sendto() */
pid_t	 pid;				/* our PID */
int		 probe, nprobes;
int		 sendfd, recvfd;	/* send on UDP sock, read on raw ICMP sock */
int		 ttl, max_ttl;
int		 verbose;

			/* function prototypes */
const char	*icmpcode_v4(int);
int		 recv_v4(int, struct timeval *);
void	 sig_alrm(int);
void	 traceloop(void);
void	 tv_sub(struct timeval *, struct timeval *);

struct proto {
  const char	*(*icmpcode)(int);
  int	 (*recv)(int, struct timeval *);
  struct sockaddr  *sasend;	/* sockaddr{} for send, from getaddrinfo */
  struct sockaddr  *sarecv;	/* sockaddr{} for receiving */
  struct sockaddr  *salast;	/* last sockaddr{} for receiving */
  struct sockaddr  *sabind;	/* sockaddr{} for binding source port */
  socklen_t   		salen;	/* length of sockaddr{}s */
  int			icmpproto;	/* IPPROTO_xxx value for ICMP */
  int	   ttllevel;		/* setsockopt() level to set TTL */
  int	   ttloptname;		/* setsockopt() name to set TTL */
} *pr;

#ifdef	IPV6

#include	<netinet/ip6.h>
#include	<netinet/icmp6.h>

#endif

main.c:

#include	"trace.h"

struct proto	proto_v4 = { icmpcode_v4, recv_v4, NULL, NULL, NULL, NULL, 0,
							 IPPROTO_ICMP, IPPROTO_IP, IP_TTL };


int		datalen = sizeof(struct rec);	/* defaults */
int		max_ttl = 30;
int		nprobes = 3;
u_short	dport = 32768 + 666;

int
main(int argc, char **argv)
{
	int				c;
	struct addrinfo	*ai;
	char *h;

	opterr = 0;		/* don't want getopt() writing to stderr */
        //对命令行参数的处理
	while ( (c = getopt(argc, argv, "m:v")) != -1) {
		switch (c) {
		case 'm':
			if ( (max_ttl = atoi(optarg)) <= 1)
				err_quit("invalid -m value");
			break;

		case 'v':
			verbose++;
			break;

		case '?':
			err_quit("unreco
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值