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. windows 平台使用 VS2017 编译 libevent 源码

    一 依赖库编译 先要将其依赖的库编译好,其中openssl需要编译到libevent中,编译成libevent_openssl.lib库,zlib在新版本中只有示例用到. 1)windows 平台使用 ...

  2. P1242 新汉诺塔(hanio)

    这道题加深了hanio的理解 如果我们要移动第n个盘子.那么就是说,n+1以后(包括n+1)的盘子都已经到位了 #include<iostream> #include<cstdio& ...

  3. MySQL自增锁等待问题解决

    有网友再群里问:在做基准测试时候,批量插入数据时,有很多自增锁等待,我告诉他解决办法: 1.innodb_autoinc_lock_mode=2 2.innodb_autoextend_increme ...

  4. MySQL5.6基于GTID的主从复制配置

    全局事务标示符(Global Transactions Identifier)是MySQL 5.6复制的一个新特性. GTID实际上是由UUID+TID组成的.其中UUID是一个MySQL实例的唯一标 ...

  5. 自定义扩展Compare比较方法

    public static int Compare<T, V>(this T x, T y, Func<T, V> func) { return Comparer<V&g ...

  6. 统计寄存器AX中1 的个数

    ;==================================== ; 统计寄存器AX中1 的个数 DATAS segment DATAS ends CODES segment START: ...

  7. axios进行ajax请求得不到数据,cookie无法携带问题

    这个坑也是很早之前踩过,今天做项目的时候居然忘了,怎么都拿不到数据,果然好记性不如烂笔头,决定写篇博客来祭奠下我的猪脑子: 原因可能就是你发送请求的时候,需要设置cookie,然而你的cookie并没 ...

  8. 【Effective C++ 读书笔记】导读 Introduction

    学习程序语言根本大法是一回事,学习如何以某种语言设计并实现高效程序则是另一回事. 一组明智选择并精心设计的classes.functions.templates可使程序编写容易.直观.高效.并且远离错 ...

  9. 【PHP基础】序列化serialize()与反序列化unserialize()

    序列化serialize()与反序列化unserialize(): 序列化serialize():就是将一个变量所代表的 “内存数据”转换为“字符串”的形式,并持久保存在硬盘(写入文件中保存)上的一种 ...

  10. 【yii2】 yii框架如果控制器和方法都是多个单词组成应该怎样写请求链接

    最近的一个项目碰到这种问题,摸索出了解决办法,故此记录下 example 如果控制器为 ShopCollect 方法为 UserList solution 请求的链接应该为 doman.com/sho ...