📄 mtrace.c
字号:
ifrp = (struct ifreq *)ifc.ifc_buf; ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); /* * Loop through all of the interfaces. */ for (; ifrp < ifend && !found; ifrp = (struct ifreq *)((char *)ifrp + n)) {#if BSD >= 199006 n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); if (n < sizeof(*ifrp)) n = sizeof(*ifrp);#else n = sizeof(*ifrp);#endif /* * Ignore any interface for an address family other than IP. */ if (ifrp->ifr_addr.sa_family != AF_INET) continue; if_addr = ((struct sockaddr_in *)&(ifrp->ifr_addr))->sin_addr.s_addr; if (ioctl(s, SIOCGIFFLAGS, (char *)ifrp) < 0) { fprintf(stderr, "SIOCGIFFLAGS on "); perror(ifrp->ifr_name); continue; } if ((ifrp->ifr_flags & (IFF_MULTICAST|IFF_UP|IFF_LOOPBACK)) != (IFF_MULTICAST|IFF_UP)) continue; if (*dst == 0) *dst = if_addr; if (ioctl(s, SIOCGIFNETMASK, (char *)ifrp) >= 0) { if_mask = ((struct sockaddr_in *)&(ifrp->ifr_addr))->sin_addr.s_addr; if (if_mask != 0 && (*dst & if_mask) == (if_addr & if_mask)) { retval = if_mask; if (lcl_addr == 0) lcl_addr = if_addr; /* XXX what about aliases? */ } } if (lcl_addr == if_addr) found = TRUE; } if (!found && lcl_addr != 0) { printf("Interface address is not valid\n"); exit(1); } return (retval);}/* * Try to pick a TTL that will get past all the thresholds in the path. */intget_ttl(buf) struct resp_buf *buf;{ int rno; struct tr_resp *b; u_int ttl; if (buf && (rno = buf->len) > 0) { b = buf->resps + rno - 1; ttl = b->tr_fttl; while (--rno > 0) { --b; if (ttl < b->tr_fttl) ttl = b->tr_fttl; else ++ttl; } ttl += MULTICAST_TTL_INC; if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1; if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX; return (ttl); } else return(MULTICAST_TTL1);}/* * Calculate the difference between two 32-bit NTP timestamps and return * the result in milliseconds. */intt_diff(a, b) u_long a, b;{ int d = a - b; return ((d * 125) >> 13);}/* * Swap bytes for poor little-endian machines that don't byte-swap */u_longbyteswap(v) u_long v;{ return ((v << 24) | ((v & 0xff00) << 8) | ((v >> 8) & 0xff00) | (v >> 24));}#if 0/* * XXX incomplete - need private callback data, too? * XXX since dst doesn't get passed through? */intneighbors_callback(tmo, buf, buflen, igmp, igmplen, addr, addrlen, ts) int tmo; u_char *buf; int buflen; struct igmp *igmp; int igmplen; struct sockaddr *addr; int *addrlen; struct timeval *ts;{ int len; u_int32 dst; struct ip *ip = (struct ip *)buf; if (tmo) return 0; if (igmp->igmp_code != DVMRP_NEIGHBORS2) return 0; len = igmplen; /* * Accept DVMRP_NEIGHBORS2 response if it comes from the * address queried or if that address is one of the local * addresses in the response. */ if (ip->ip_src.s_addr != dst) { u_int32 *p = (u_int32 *)(igmp + 1); u_int32 *ep = p + (len >> 2); while (p < ep) { u_int32 laddr = *p++; int n = ntohl(*p++) & 0xFF; if (laddr == dst) { ep = p + 1; /* ensure p < ep after loop */ break; } p += n; } if (p >= ep) return 0; } return buflen;}#endifintmtrace_callback(tmo, buf, buflen, igmp, igmplen, addr, addrlen, ts) int tmo; u_char *buf; int buflen; struct igmp *igmp; int igmplen; struct sockaddr *addr; int *addrlen; struct timeval *ts;{ static u_char *savbuf = NULL; static int savbuflen; static struct sockaddr *savaddr; static int savaddrlen; static struct timeval savts; int len = (igmplen - QLEN) / RLEN; struct tr_resp *r = (struct tr_resp *)((struct tr_query *)(igmp + 1) + 1); if (tmo == 1) { /* * If we timed out with a packet saved, then return that packet. * send_recv won't send this same packet to the callback again. */ if (savbuf) { bcopy(savbuf, buf, savbuflen); free(savbuf); savbuf = NULL; bcopy(savaddr, addr, savaddrlen); free(savaddr); *addrlen = savaddrlen; bcopy(&savts, ts, sizeof(savts)); return savbuflen; } return 0; } if (savbuf) { free(savbuf); savbuf = NULL; free(savaddr); } /* * Check for IOS bug described in CSCdi68628, where a router that does * not have multicast enabled responds to an mtrace request with a 1-hop * error packet. * Heuristic is: * If there is only one hop reported in the packet, * And the protocol code is 0, * And there is no previous hop, * And the forwarding information is "Not Forwarding", * And the router is not on the same subnet as the destination of the * trace, * then drop this packet. The "#if 0"'d code saves it and returns * it on timeout, but timeouts are too common (e.g. routers with * limited unicast routing tables, etc). */ if (len == 1 && r->tr_rproto == 0 && r->tr_rmtaddr == 0 && r->tr_rflags == TR_NO_FWD) { u_int32 smask; VAL_TO_MASK(smask, r->tr_smask); if ((r->tr_outaddr & smask) != (qdst & smask)) {#if 0 /* XXX should do this silently? */ fprintf(stderr, "mtrace: probably IOS-buggy packet from %s\n", inet_fmt(((struct sockaddr_in *)addr)->sin_addr.s_addr, s1)); /* Save the packet to return if a timeout occurs. */ savbuf = (u_char *)malloc(buflen); if (savbuf != NULL) { bcopy(buf, savbuf, buflen); savbuflen = buflen; savaddr = (struct sockaddr *)malloc(*addrlen); if (savaddr != NULL) { bcopy(addr, savaddr, *addrlen); savaddrlen = *addrlen; bcopy(ts, &savts, sizeof(savts)); } else { free(savbuf); savbuf = NULL; } }#endif return 0; } } return buflen;}intsend_recv(dst, type, code, tries, save, callback) u_int32 dst; int type, code, tries; struct resp_buf *save; callback_t callback;{ fd_set fds; struct timeval tq, tr, tv; struct ip *ip; struct igmp *igmp; struct tr_query *query, *rquery; struct tr_resp *r; struct sockaddr_in recvaddr; u_int32 local, group; int ipdatalen, iphdrlen, igmpdatalen; int datalen; int count, recvlen, socklen = sizeof(recvaddr); int len; int i; if (type == IGMP_MTRACE) { group = qgrp; datalen = sizeof(struct tr_query); } else { group = htonl(0xff03); datalen = 0; } if (IN_MULTICAST(ntohl(dst))) local = lcl_addr; else local = INADDR_ANY; /* * If the reply address was not explictly specified, start off * with the standard multicast reply address, or the unicast * address of this host if the unicast flag was specified. * Then, if there is no response after trying half the tries * with multicast, switch to the unicast address of this host * if the multicast flag was not specified. If the TTL was * also not specified, set a multicast TTL and increase it * for every try. */ query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); query->tr_raddr = raddr ? raddr : unicast ? lcl_addr : resp_cast; TR_SETTTL(query->tr_rttlqid, rttl ? rttl : IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL); query->tr_src = qsrc; query->tr_dst = qdst; for (i = tries ; i > 0; --i) { int oqid; if (tries == nqueries && raddr == 0) { if (i == (nqueries >> 1)) { if (multicast && unicast) { query->tr_raddr = resp_cast; if (!rttl) TR_SETTTL(query->tr_rttlqid, get_ttl(save)); } else if (!multicast) { query->tr_raddr = lcl_addr; TR_SETTTL(query->tr_rttlqid, UNICAST_TTL); } } if (i < tries && IN_MULTICAST(ntohl(query->tr_raddr)) && rttl == 0) { TR_SETTTL(query->tr_rttlqid, TR_GETTTL(query->tr_rttlqid) + MULTICAST_TTL_INC); if (TR_GETTTL(query->tr_rttlqid) > MULTICAST_TTL_MAX) TR_SETTTL(query->tr_rttlqid, MULTICAST_TTL_MAX); } } /* * Change the qid for each request sent to avoid being confused * by duplicate responses */ oqid = TR_GETQID(query->tr_rttlqid); if (staticqid) TR_SETQID(query->tr_rttlqid, staticqid); else#ifdef SYSV TR_SETQID(query->tr_rttlqid, ((u_int32)lrand48() >> 8));#else TR_SETQID(query->tr_rttlqid, ((u_int32)random() >> 8));#endif /* * Set timer to calculate delays, then send query */ gettimeofday(&tq, 0); send_igmp(local, dst, type, code, group, datalen); /* * Wait for response, discarding false alarms */ while (TRUE) { FD_ZERO(&fds); FD_SET(igmp_socket, &fds); gettimeofday(&tv, 0); tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec; tv.tv_usec = tq.tv_usec - tv.tv_usec; if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec; if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0; count = select(igmp_socket + 1, &fds, (fd_set *)0, (fd_set *)0, &tv); if (count < 0) { if (errno != EINTR) perror("select"); continue; } else if (count == 0) { /* * Timed out. Notify the callback. */ if (!callback || (recvlen = (callback)(1, recv_buf, 0, NULL, 0, (struct sockaddr *)&recvaddr, &socklen, &tr)) == 0) { printf("* "); fflush(stdout); break; } } else { /* * Data is available on the socket, so read it. */ gettimeofday(&tr, 0); recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, 0, (struct sockaddr *)&recvaddr, &socklen); } if (recvlen <= 0) { if (recvlen && errno != EINTR) perror("recvfrom"); continue; } if (recvlen < sizeof(struct ip)) { fprintf(stderr, "packet too short (%u bytes) for IP header", recvlen); continue; } ip = (struct ip *) recv_buf; if (ip->ip_p == 0) /* ignore cache creation requests */ continue; iphdrlen = ip->ip_hl << 2;#ifdef RAW_INPUT_IS_RAW ipdatalen = ntohs(ip->ip_len);#else ipdatalen = ip->ip_len;#endif if (iphdrlen + ipdatalen != recvlen) { fprintf(stderr, "packet shorter (%u bytes) than hdr+data len (%u+%u)\n", recvlen, iphdrlen, ipdatalen); continue; } igmp = (struct igmp *) (recv_buf + iphdrlen); igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { fprintf(stderr, "IP data field too short (%u bytes) for IGMP from %s\n", ipdatalen, inet_fmt(ip->ip_src.s_addr, s1)); continue; } switch (igmp->igmp_type) { case IGMP_DVMRP: if (type != IGMP_DVMRP || code != DVMRP_ASK_NEIGHBORS2) continue; if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue; len = igmpdatalen; /* * Accept DVMRP_NEIGHBORS2 response if it comes from the * address queried or if that address is one of the local * addresses in the response. */ if (ip->ip_src.s_addr != dst) { u_int32 *p = (u_int32 *)(igmp + 1); u_int32 *ep = p + (len >> 2); while (p < ep) { u_int32 laddr = *p++; int n = ntohl(*p++) & 0xFF; if (laddr == dst) { ep = p + 1; /* ensure p < ep after loop */ break; } p += n; } if (p >= ep) continue; } break; case IGMP_MTRACE: /* For backward compatibility with 3.3 */ case IGMP_MTRACE_RESP: if (type != IGMP_MTRACE) continue; if (igmpdatalen <= QLEN) continue; if ((igmpdatalen - QLEN)%RLEN) { printf("packet with incomplete responses (%d bytes)\n", igmpdatalen); continue; } /* * Ignore responses that don't match query. */ rquery = (struct tr_query *)(igmp + 1); if (rquery->tr_src != qsrc || rquery->tr_dst != qdst) continue; if (TR_GETQID(rquery->tr_rttlqid) != TR_GETQID(query->tr_rttlqid)) { if (verbose && TR_GETQID(rquery->tr_rttlqid) == oqid) printf("[D]"); continue; } len = (igmpdatalen - QLEN)/RLEN; r = (struct tr_resp *)(rquery+1) + len - 1; /* * Ignore trace queries passing through this node when * mtrace is run on an mrouter that is in the path * (needed only because IGMP_MTRACE is accepted above * for backward compatibility with multicast release 3.3). */ if (igmp->igmp_type == IGMP_MTRACE) { u_int32 smask; VAL_TO_MASK(smask, r->tr_smask); if (len < code && (r->tr_inaddr & smask) != (qsrc & smask) && r->tr_rmtaddr != 0 && !(r->tr_rflags & 0x80)) continue; } /* * Some routers will return error messages without * filling in their addresses. We fill in the address * for them. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -