traceroute, 也就是 trace route,跟踪路由。这个程序最早是Van Jacobson实现的。源码在网上可以找到,不过我还没有去找。是IP路由过程中对数据包TTL(Time to Live,存活时间)的处理。当路由器收到一个IP包时,会修改IP包的TTL(及由此造成的头部检验和checksum变化)。每收到一个包,检查这个 的TTL是否是0或1。如果是,表明这个包还没有到达目的地,而且剩余时间不多了,肯定是到不了目的地了。这样路由器就简单地丢弃这个包,并给源主机发送 ICMP通知,说这个包已经超时了。ICMP的通知信息里包含当前路由器发送时所用的IP。
  这样就可以通过构造数据包,来间接检查到 达一个主机时经过了哪些路由。一开始发送一个TTL为1的包,这样到达第一个路由器的时候就已经超时了,第一个路由器就发通知说包超时,这样就可以记录下 所经过的第一个路由器的IP。然后TTL加1,安全通过第一个路由器,而第二个路由器的的处理与第一个相同,丢包,发通知说包超时了,这样记录下第二个路 由器IP,由此可以一直进行下去,直到这个数据包到达目标主机,由此打印出所有经过的路由器。
#include<stdio.h>
#include<sys/time.h>
#include<errno.h>
#include<signal.h>
#include<time.h>
#include<stdlib.h>
#include<unistd.h>
#include<netdb.h>
#include<string.h>
#include<strings.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in_systm.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>
#include<netinet/udp.h>
#include<netinet/in_systm.h>
#define BUFSIZE 1500
struct rec{
u_short rec_seq;
u_short rec_ttl;
struct timeval rec_tv;
}; char recvbuf[BUFSIZE];
char sendbuf[BUFSIZE]; int datalen;
char *host;
u_short sport,dport;
int nsent;
pid_t pid;
int probe,nprobes;
int sendfd,recvfd;
int ttl,max_ttl;
int verbose; struct proto{
const char*(*icmpcode)(int);
int (*recv)(int,struct timeval*);
struct sockaddr *sasend; //dest addr, the destination
struct sockaddr *sarecv; //recv addr, store who send the message
struct sockaddr *salast;
struct sockaddr *sabind; //bind the source port
socklen_t salen;
int icmpproto;
int ttllevel;
int ttloptname;
}*pr;
int gotalarm;
const char *icmpcode_v4(int code){
static char errbuf[];
switch(code){
case :return("network unreachable");
case :return("host unreachable");
case :return("protocol unreachable");
case :return("port unreachable");
case :return("fragmentation required but DF bit set");
case :return("source route failed");
case :return("destination network unknown");
case :return("destination host unknown");
case :return("source host isolated(obsolete)");
case :return("destination network administartively prohibited");
case :return("destination host administartively prohibited");
case :return("network unreachable for TOS");
case :return("host unreachable for TOS");
case :return("communication error");
case :return("host recedenc violation");
case :return("precedence cutoff in effect");
default:sprintf(errbuf,"unknown code %d",code);
}
return errbuf;
}
void sig_alrm(int signo){
gotalarm=;
return;
}
void tv_sub(struct timeval *out,struct timeval *in){
if((out->tv_usec-=in->tv_usec)<){
--out->tv_sec;
out->tv_sec+=;
}
out->tv_sec-=in->tv_sec;
}
void traceloop(void){
int seq,code,done;
double rtt;
struct rec *rec;
struct timeval tvrecv;
if((recvfd=socket(pr->sasend->sa_family,SOCK_RAW,pr->icmpproto))<){
printf("recvfd:socket failed\n");
return;
}
setuid(getuid());
if((sendfd=socket(pr->sasend->sa_family,SOCK_DGRAM,))<){
printf("sendfd:socket failed\n");
return;
} pr->sabind->sa_family=pr->sasend->sa_family;
sport=(getpid()&0xffff) | 0x8000;
((struct sockaddr_in*)pr->sabind)->sin_port=htons(sport); if(bind(sendfd,pr->sabind,pr->salen)<){
printf("bind error\n");
return;
} sig_alrm(SIGALRM);
seq=;
done=;
for(ttl=;ttl<=max_ttl&&done==;ttl++){
setsockopt(sendfd,pr->ttllevel,pr->ttloptname,&ttl,sizeof(int));//modify ttl
bzero(pr->salast,pr->salen);
printf("%2d ",ttl);
fflush(stdout);
for(probe=;probe<nprobes;probe++){
/*
* *these sendbuf is just
* *used to exam if the received data is sended by our program
* */
rec=(struct rec*)sendbuf;
rec->rec_seq=++seq;
rec->rec_ttl=ttl; gettimeofday(&rec->rec_tv,NULL);
((struct sockaddr_in*)pr->sasend)->sin_port=htons(dport+seq);
if(sendto(sendfd,sendbuf,datalen,,pr->sasend,pr->salen)<){//send to dest with ttl added
perror("bad sendto");
continue;
} //if time_out print * else print info
if((code=(*pr->recv)(seq,&tvrecv))==-){
printf(" *");
}else{
char str[NI_MAXHOST];
if(memcmp(pr->sarecv,pr->salast,pr->salen)!=){
if(getnameinfo(pr->sarecv,pr->salen,str,sizeof(str),NULL,,)==){
printf(" %s (%s)",str,inet_ntoa(((struct sockaddr_in*)pr->sarecv)->sin_addr));
}else{
printf(" %s",inet_ntoa(((struct sockaddr_in*)pr->sarecv)->sin_addr));
}
memcpy(pr->salast,pr->sarecv,pr->salen);
}
tv_sub(&tvrecv,&rec->rec_tv);
rtt=tvrecv.tv_sec*1000.0+tvrecv.tv_usec/;
printf(" %.3f ms",rtt); if(code==-){ //reach the dest
done++;
}else if(code>){
printf(" (ICMP %s)",(*pr->icmpcode)(code));
}
}
fflush(stdout);
}
printf("\n");
}
}
int recv_v4(int seq,struct timeval *tv){
int hlen1,hlen2,icmplen,ret;
socklen_t len;
ssize_t n;
struct ip *ip,*hip;
struct icmp *icmp;
struct udphdr *udp; gotalarm=;
for(;;){
if(gotalarm){
return -;
}
len=pr->salen;
alarm();
n=recvfrom(recvfd,recvbuf,sizeof(recvbuf),,pr->sarecv,&len);//data len
if(n<){
if(errno==EINTR){
continue;
}else{
printf("recvfrom error\n");
return ;
}
}else{
//if recvfrom ok , close the alarm
alarm();
} //read data
ip=(struct ip*)recvbuf;
hlen1=ip->ip_hl<<;//ip len
icmp=(struct icmp*)(recvbuf+hlen1);
if((icmplen=n-hlen1)<){
continue;
}
if(icmp->icmp_type==ICMP_TIMXCEED&&
icmp->icmp_code==ICMP_TIMXCEED_INTRANS){
if(icmplen<+sizeof(struct ip)){
continue;
}
//get icmp data
hip=(struct ip*)(recvbuf+hlen1+);
hlen2=hip->ip_hl<<;
if(icmplen<+hlen2+){
continue;
}
udp=(struct udphdr *)(recvbuf+hlen1++hlen2);
if(hip->ip_p==IPPROTO_UDP&&
udp->source==htons(sport)&&
udp->dest==htons(dport+seq)){
ret=-;
break;
}
}else if(icmp->icmp_type==ICMP_UNREACH){
if(icmplen<+sizeof(struct ip))
continue;
hip=(struct ip*)(recvbuf+hlen1+);
hlen2=hip->ip_hl<<;
if(icmplen<+hlen2+)
continue;
udp=(struct udphdr*)(recvbuf+hlen1++hlen2);
if(hip->ip_p==IPPROTO_UDP&&
udp->source==htons(sport)&&
udp->dest==htons(dport+seq)){
if(icmp->icmp_code==ICMP_UNREACH_PORT)
ret=-; //reach the destination
else
ret=icmp->icmp_code;
break;
}
}
}
gettimeofday(tv,NULL);
return ret;
} struct proto proto_v4={icmpcode_v4,recv_v4,NULL,NULL,NULL,NULL,,IPPROTO_ICMP,IPPROTO_IP,IP_TTL}; int datalen=sizeof(struct rec);
int max_ttl=;
int nprobes=;
u_short dport=+;//hope the port of dest is not used struct addrinfo *host_serv(const char *host,const char *serv,int family,int socktype){
int n;
struct addrinfo hints,*res;
bzero(&hints,sizeof(hints));
hints.ai_flags=AI_CANONNAME;
hints.ai_family=family;
hints.ai_socktype=socktype;
if((n=getaddrinfo(host,serv,&hints,&res))!=){
return NULL;
}
return (res);
}
int main(int argc,char *argv[]){
int c;
struct addrinfo *ai;
struct sigaction s_action;
char h[]={};
while((c=getopt(argc,argv,"m:v"))!=-){
switch(c){
case 'm':
if((max_ttl=atoi(optarg))<){
printf("invalid input\n");
}
break;
case 'v':
verbose++;
break;
case '?':
printf("unrecognized\n");
return -;
}
}
if(optind!=argc-){
printf("error input\n");
return -;
}
host=argv[optind]; pid=getpid(); bzero(&s_action,sizeof(s_action));
s_action.sa_handler=sig_alrm;
s_action.sa_flags=SA_INTERRUPT;
sigaction(SIGALRM,&s_action,NULL); // signal(SIGALRM,sig_alrm);
ai=host_serv(host,NULL,,);
inet_ntop(AF_INET,&((struct sockaddr_in*)(ai->ai_addr))->sin_addr,h,sizeof(h));
printf("traceroute to %s (%s): %d hops max, %d data bytes\n",
ai->ai_canonname?ai->ai_canonname:h,h,max_ttl,datalen); if(ai->ai_family==AF_INET){
pr=&proto_v4;
}else{
printf("UNKNOW address family\n");
return -;
} pr->sasend=ai->ai_addr;
pr->sarecv=(struct sockaddr*)calloc(,ai->ai_addrlen);
pr->salast=(struct sockaddr*)calloc(,ai->ai_addrlen);
pr->sabind=(struct sockaddr*)calloc(,ai->ai_addrlen);
pr->salen=ai->ai_addrlen;
traceloop();
exit();
}

原始套接字--traceroute的更多相关文章

  1. Linux原始套接字实现分析---转

    http://blog.chinaunix.net/uid-27074062-id-3388166.html 本文从IPV4协议栈原始套接字的分类入手,详细介绍了链路层和网络层原始套接字的特点及其内核 ...

  2. Linux Socket 原始套接字编程

    对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...

  3. 005.TCP--拼接TCP头部IP头部,实现TCP三次握手的第一步(Linux,原始套接字)

    一.目的: 自己拼接IP头,TCP头,计算效验和,将生成的报文用原始套接字发送出去. 若使用tcpdump能监听有对方服务器的包回应,则证明TCP报文是正确的! 二.数据结构: TCP首部结构图: s ...

  4. 004.UDP--拼接UDP数据包,构造ip头和udp头通信(使用原始套接字)

    一.大致流程: 建立一个client端,一个server端,自己构建IP头和UDP头,写入数据(hello,world!)后通过原始套接字(SOCK_RAW)将包发出去. server端收到数据后,打 ...

  5. 002.ICMP--拼接ICMP包,实现简单Ping程序(原始套接字)

    一.大致流程: 将ICMP头和时间数据设置好后,通过创建好的原始套接字socket发出去.目的主机计算效验和后会将数据原样返回,用当前时间和返回的数据结算时间差,计算出rtt. 二.数据结构: ICM ...

  6. linux原始套接字(4)-构造IP_UDP

    一.概述                                                    同上一篇tcp一样,udp也是封装在ip报文里面.创建UDP的原始套接字如下: (soc ...

  7. linux原始套接字(3)-构造IP_TCP发送与接收

    一.概述                                                    tcp报文封装在ip报文中,创建tcp的原始套接字如下: sockfd = socket ...

  8. linux原始套接字(2)-icmp请求与接收

    一.概述                                                    上一篇arp请求使用的是链路层的原始套接字.icmp封装在ip数据报里面,所以icmp请 ...

  9. linux原始套接字(1)-arp请求与接收

    一.概述                                                   以太网的arp数据包结构: arp结构op操作参数:1为请求,2为应答. 常用的数据结构如 ...

随机推荐

  1. 创建 XXXXXXXX 的配置节处理程序时出错: 请求失败

    今天碰到这个错误,之前的程序在测试的时候都没有问题,同样的程序打包通过QQ传给其他人,在XP下测试也没有问题,我的Win7系统从QQ信箱下载压缩包,解压之后执行程序就会出问题,本来还是考虑自己程序是不 ...

  2. lasagne保存网络参数

    # Optionally, you could now dump the network weights to a file like this: # np.savez('model.npz', *l ...

  3. nginx架构分析之 模块化

    Nginx涉及到的模块分为核心模块.标准HTTP模块.可选HTTP模块.邮件服务模块以及第三方模块等五大类. 核心模块 核心模块是指Nginx服务器正常运行时必不可少的模块,它们提供了Nginx最基本 ...

  4. P2096 最佳旅游线路

    最大字段和加贪心 算长了个见识吧 #include<iostream> #include<cstdio> #include<algorithm> using nam ...

  5. 第1章-如何使用本书—零死角玩转STM32-F429系列

    第1章    如何使用本书 1.1    本书的参考资料 集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com ...

  6. poj_1995_Raising Modulo Numbers

    Description People are different. Some secretly read magazines full of interesting girls' pictures, ...

  7. 交换机基础配置之跨交换机划分vlan

    我们以上面的拓扑图来进行实验 四台pc机都在同一网段 pc1和pc2在同一台交换机上 pc3和pc4在同一台交换机上 现在我们实验的目的就是将pc1和pc3划分到同一vlan pc2和pc4划分到同一 ...

  8. HttpServletRequest cannot be resolved to a type The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path

    HttpServletRequest cannot be resolved to a type The superclass "javax.servlet.http.HttpServlet& ...

  9. Windows下如何安装composer

    相对 来说并不难直接将此文件下载安装即可 1 https://getcomposer.org/Composer-Setup.exe 文件地址由官方提供 https://getcomposer.org/ ...

  10. JDK学习---深入理解java中的HashMap、HashSet底层实现

    本文参考资料: 1.<大话数据结构> 2.http://www.cnblogs.com/dassmeta/p/5338955.html 3.http://www.cnblogs.com/d ...