/* * Copyright (c) 2000, Ping Pan (Columbia University/Bell Labs) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The author acknowledges Bell Labs for providing time. Please forward * bug fixes, enhancements and questions to pingpan@cs.columbia.edu. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _KERNEL #define KERNEL #include #include #include extern int optind; extern int opterr; extern char *optarg; void sock_init(int); void sockwriter(int); int reg_sockread(int, int); void logbad(int dump, char *p, ...); void usage(); void stopit(int); void finish(); void print_raw(u_char *, int, int); void print_rr(int, int); void print_ts(int, int); void print_sec(int, int); void print_lsrr(int, int); void print_ssrr(int, int); void print_ra(int, int); void print_rsvp(int, int); void print_rtcp(int, int); /* the type of message that we are trying to capture: * the first 8-bit is the actual option value; the second 8-bit is * option-specific. */ #define IPOPT_ANY -1 #define IPOPT_RRANY (IPOPT_RR << 8) #define IPOPT_TSANY (IPOPT_TS << 8) #define IPOPT_SECANY (IPOPT_SECURITY << 8) #define IPOPT_LSRRANY (IPOPT_LSRR << 8) #define IPOPT_SSRRANY (IPOPT_SSRR << 8) #define IPOPT_RAANY (IPOPT_RA << 8) #define IPOPT_RARSVP ((IPOPT_RA << 8) | IPOPT_RECVRSVP) #define IPOPT_RARTCP ((IPOPT_RA << 8) | IPOPT_RECVRTCP) #define DEFAULT_SNAPLEN 128 /* default snap packet length */ /* max. control info size... plus some safe guard space */ #define MAXCTRLSIZE \ (sizeof(struct cmsghdr) + sizeof(struct sockaddr_dl) + \ sizeof(struct cmsghdr) + sizeof(int) + 32) #define MAXPKTSIZE 9126 /* don't ask */ #define CMSG_IFINDEX(cmsg) \ (((struct sockaddr_dl*)(cmsg + 1))->sdl_index) \ #define CMSG_LOCAL(cmsg) (*(int *)(cmsg + 1)) struct sockread { int opt; /* option type */ int sock; /* socket num */ void (*readfunc)(int, int); /* printer func */ }; static struct sockread sockreader[] = { { IPOPT_RRANY, -1, print_rr, }, { IPOPT_TSANY, -1, print_ts, }, { IPOPT_SECANY, -1, print_sec, }, { IPOPT_LSRRANY, -1, print_lsrr, }, { IPOPT_SSRRANY, -1, print_ssrr, }, { IPOPT_RAANY, -1, print_ra, }, { IPOPT_RARSVP, -1, print_rsvp, }, { IPOPT_RARTCP, -1, print_rtcp, }, }; #define sizeof_sockread_tab \ (sizeof (sockreader) / sizeof sockreader[0]) fd_set opt_fdset; /* option fd's */ int sock_max; /* max. sockets per process */ int eflag; /* error flag */ int iflag; /* include-interface-index flag */ int xflag; /* print-in-hex flag */ int packetype; /* capture pkt type */ int snaplen; /* snap length */ int tx_sock; /* transmit socket */ volatile sig_atomic_t finish_up; /* nonzero if told to finish up */ u_char *packet; /* pointer to the recv pkt */ int ncapture; /* number of captured packets */ int ntransmit; /* number of transmitted packets */ int nra; /* number of router-alert pkts */ int nrsvp; /* number of router-alert rsvp pkts */ int nrtcp; /* number of router-alert rtcp pkts */ int nrr; /* number of route-record pkts */ int nts; /* number of time-stamp pkts */ int nsec; /* number of security pkts */ int nlsrr; /* number of loose-source-routing */ int nssrr; /* number of strict-source-routing */ char *program_name; /* what do you think ? */ int main(int argc, char *argv[]) { register int uid, n, cnt; struct sigaction si_sa; struct msghdr msg; struct sockaddr_in from; struct iovec iov; struct cmsghdr *cmsg; char *ctrl, *cp; int on=1; setlinebuf(stdout); setuid(getuid()); if ((uid = getuid())) { perror("need to be a superuser"); exit(1); } /* init. all parameters */ packetype = IPOPT_ANY; snaplen = DEFAULT_SNAPLEN; eflag = 0; iflag = 0; xflag = 0; cnt = -1; ncapture = 0; nra = 0; nrsvp = 0; nrtcp = 0; nrr = 0; nts = 0; nsec = 0; nlsrr = 0; nssrr = 0; if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else program_name = argv[0]; while ((n = getopt(argc, argv, "c:ixT:s:")) != EOF) { switch (n) { case 'c': cnt = atoi(optarg); if (cnt < 0) { fprintf(stderr, "bad packet count %s", optarg); eflag++; } break; case 'i': iflag++; break; case 'x': xflag++; break; case 'T': if (strcasecmp(optarg, "rr") == 0) packetype = IPOPT_RRANY; else if (strcasecmp(optarg, "ts") == 0) packetype = IPOPT_TSANY; else if (strcasecmp(optarg, "sec") == 0) packetype = IPOPT_SECANY; else if (strcasecmp(optarg, "lsrr") == 0) packetype = IPOPT_LSRRANY; else if (strcasecmp(optarg, "ssrr") == 0) packetype = IPOPT_SSRRANY; else if (strcasecmp(optarg, "ra") == 0) packetype = IPOPT_RAANY; else if (strcasecmp(optarg, "rsvp") == 0) packetype = IPOPT_RARSVP; else if (strcasecmp(optarg, "rtcp") == 0) packetype = IPOPT_RARTCP; else { fprintf(stderr, "invalid type %s", optarg); eflag++; } break; case 's': snaplen = atoi(optarg); if (snaplen <= 0) { fprintf(stderr, "bad snap length %s", optarg); eflag++; } break; default: usage(); } } if (eflag) usage(); /* socket and buffer setup: */ sock_max = getdtablesize(); FD_ZERO(&opt_fdset); if (packetype == IPOPT_ANY) { sock_init(IPOPT_RRANY); sock_init(IPOPT_TSANY); sock_init(IPOPT_SECANY); sock_init(IPOPT_LSRRANY); sock_init(IPOPT_SSRRANY); sock_init(IPOPT_RAANY); sock_init(IPOPT_RARSVP); sock_init(IPOPT_RARTCP); } else sock_init(packetype); if (!(packet = (char *)malloc(MAXPKTSIZE))) logbad(0, "cannot get buff space"); /* setup tx. socket */ if ((tx_sock = socket(PF_INET, SOCK_RAW, 0)) < 0) logbad(0, "tx_sock: socket() < 0"); if (setsockopt(tx_sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) logbad(0, "setsockopt() for IP_HDRINCL < 0"); /* signal setup */ sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; si_sa.sa_handler = stopit; if (sigaction(SIGINT, &si_sa, 0) == -1) logbad(0, "sigaction SIGINT"); si_sa.sa_handler = stopit; if (sigaction(SIGTERM, &si_sa, 0) == -1) logbad(0, "sigaction SIGTERM"); /* read from sockets: */ if (!(ctrl = (char *)malloc(MAXCTRLSIZE))) logbad(0, "cannot get control data space"); bzero(&msg, sizeof(msg)); msg.msg_name = (caddr_t)&from; msg.msg_namelen = sizeof(from); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = ctrl; msg.msg_controllen = MAXCTRLSIZE; iov.iov_base = (char *)packet; iov.iov_len = MAXPKTSIZE; while (!finish_up) { struct timeval timeout; fd_set curr_fdset; int s, cc, if_index, local; if_index = -1; local = 0; timeout.tv_sec = 1; timeout.tv_usec = 0; curr_fdset = opt_fdset; n = select(sock_max, &curr_fdset, 0, 0, &timeout); if (n <= 0) { if (n < 0 && errno != EINTR && errno != EAGAIN) logbad(0, "bad select"); continue; } for (n=0; n < (int)sizeof_sockread_tab; n++) { if ((s = sockreader[n].sock) == -1) continue; if (!(FD_ISSET(s, &curr_fdset))) continue; for (;;) { if ((cc = recvmsg(s, &msg, 0)) < 0) break; if (cc > MAXPKTSIZE) { fprintf(stderr, "pkt size %d", cc); continue; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_type == IP_RECVIF) if_index = CMSG_IFINDEX(cmsg); if (cmsg->cmsg_type == IPOPT_RECVLOCAL) local = CMSG_LOCAL(cmsg); } ncapture++; if ((cnt > 0) && (ncapture > cnt)) finish_up = 1; if (xflag) print_raw(packet, cc, if_index); else sockreader[n].readfunc(cc, if_index); /* continue to forward the message */ if (!local) { sockwriter(cc); local = 0; } } } } finish(); exit(0); } /* init a socket for option type : create and set socket; * register the socket to the reader table. */ void sock_init(int packetype) { int sock, on=1; if ((sock = socket(PF_IPOPTION, SOCK_RAW, (packetype >> 8))) < 0) { fprintf(stderr, "Need to install PF_IPOPTION kernel option\n"); logbad(0, "PF_IPOPTION error: socket() < 0"); } if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) logbad(0, "fcntl failed to O_NONBLOCK: %s", errno); if (packetype == IPOPT_RARSVP) { if (setsockopt(sock, IPPROTO_IP, IPOPT_RECVRSVP, &on, sizeof(on)) < 0) logbad(0, "bad setsockopt() on RSVP"); } else if (packetype == IPOPT_RARTCP) { if (setsockopt(sock, IPPROTO_IP, IPOPT_RECVRTCP, &on, sizeof(on)) < 0) logbad(0, "bad setsockopt() on RTCP"); } else if (packetype == IPOPT_RAANY) { if (setsockopt(sock, IPPROTO_IP, IPOPT_RECVRA, &on, sizeof(on)) < 0) logbad(0, "bad setsockopt() on RECVRA"); } if (iflag) { /* include the ingrss if-index */ if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0) logbad(0, "bad setsockopt() on RECVIF < 0"); } /* add the socket number into the readsock table */ if (!reg_sockread(sock, packetype)) logbad(0, "cannot register the socket"); FD_SET(sock, &opt_fdset); return; } /* inject the captued packet back to the network * Note: IPOPTION sockets are in front of INET sockets in the ip_input(). * So if the captured packet is doing or has the same source and * destination addresses for some reason, the same packet will be captued * and transmitted forever. So we'd better do some checking before it hits * the fan. */ void sockwriter(int len) { struct sockaddr whereto; struct sockaddr_in *to; struct ip *ip; struct in_addr *dst, *src; ip = (struct ip *)packet; src = (struct in_addr *)&(ip->ip_src); dst = (struct in_addr *)&(ip->ip_dst); /* loopback already ? */ if (src->s_addr == dst->s_addr) return; ntransmit++; to = (struct sockaddr_in *)&whereto; to->sin_len = sizeof *to; to->sin_family = PF_INET; to->sin_addr.s_addr = dst->s_addr; if (sendto(tx_sock, packet, len, 0, &whereto, sizeof(whereto)) < 0) logbad(0, "cannot transmit a captured message, oops!"); return; } /* insert the socket number 'sock' that is meant for option 'packetype' * into lookup table, sockreader[]. */ int reg_sockread(int sock, int packetype) { int i; for (i=0; i < (int)sizeof_sockread_tab; i++) { if (packetype == sockreader[i].opt) { sockreader[i].sock = sock; return -1; } } return 0; } /* display the bad news on console and quit */ void logbad(int dump, char *p, ...) { va_list args; (void)fprintf(stderr, "%s: ", program_name); va_start(args, p); (void)vfprintf(stderr, p, args); va_end(args); if (*p) { p += strlen(p); if (p[-1] != 'n') (void)fputc('\n', stderr); } if (dump) abort(); exit(-1); } void stopit(int sig) { finish_up = 1; } void finish() { (void)signal(SIGINT, SIG_IGN); (void)signal(SIGTERM, SIG_IGN); (void)putchar('\n'); (void)fflush(stdout); printf("--- %s statistics ---\n", program_name); printf("%d received packets.\n", ncapture); printf("%d transmited packets.\n", ntransmit); printf("%d received Route Record packets.\n", nrr); printf("%d received Timestamp packets.\n", nts); printf("%d received Security packets.\n", nsec); printf("%d received Loose-Sourced Routing packets.\n", nlsrr); printf("%d received Struct-Sourced Routing packets.\n", nssrr); printf("%d received Router Alert packets.\n", nra); printf("%d received RSVP (raw mode) packets.\n", nrsvp); printf("%d received RTCP packets.\n", nrtcp); exit(0); } void usage() { fprintf(stderr, "%s [-c count] [-ix] [-T type] [-s snaplen]\n", program_name); exit(-1); } #define BYTES_PER_LINE 16 void print_raw(u_char *msg, int len, int if_index) { int i, j, line_cnt; if (len > snaplen) len = snaplen; if (if_index != -1) printf("\nReceived from if-index %d\n", if_index); line_cnt = (len / BYTES_PER_LINE) + (len % BYTES_PER_LINE ? 1 : 0); printf("\r\n"); for (i=0; i < line_cnt; i++) { j = i * BYTES_PER_LINE; for (; ((j < (i+1) * BYTES_PER_LINE) && (j < len)); j++) printf("%02X:", msg[j]); printf("\r\n"); } } /* my version of inet_aton */ char *myinet_aton(struct in_addr addr) { char *host; struct hostent *hp; host = inet_ntoa(addr); /* get from DNS */ hp = gethostbyaddr((char *)&addr, sizeof(struct in_addr), AF_INET); if (hp == NULL) /* unknown host */ return host; if (hp->h_addrtype != AF_INET || hp->h_length != 4) return host; /* bad name */ return (hp->h_name); } void print_ip_hdr(u_char *msg, int len) { struct { char *name; int ptype; } ptype_map[] = { { "TCP", IPPROTO_TCP }, { "UDP", IPPROTO_UDP }, { "ICMP", IPPROTO_ICMP }, { "IGMP", IPPROTO_IGRP }, { "RSVP", IPPROTO_RSVP }, }; struct ip *ip; int hlen, max, i; ip = (struct ip *)msg; hlen = ip->ip_hl * 4; max = sizeof(ptype_map) / sizeof(ptype_map[0]); if (hlen > len) { print_raw(msg, len, -1); return; } printf("\r\n"); printf("IP: "); printf("[len %d] ", ip->ip_len); if (ip->ip_tos) printf("[tos 0x%x] ", (int)ip->ip_tos); printf("[ttl %d] ", (int)ip->ip_ttl); for (i=0; i < max; i++) { if (ip->ip_p == ptype_map[i].ptype) { printf("[%s] ", ptype_map[i].name); break; } } if (i == max) printf("[ptype %d] ", ip->ip_p); printf("%s -> ", myinet_aton(ip->ip_src)); printf("%s", myinet_aton(ip->ip_dst)); printf("\r\n"); return; } void print_rr(int len, int if_index) { nrr++; if (len > snaplen) len = snaplen; if (if_index != -1) printf("\nIngress if-index: %d\n", if_index); print_ip_hdr(packet, len); printf("IP Record Route option"); printf("\r\n"); return; } void print_ts(int len, int if_index) { nts++; if (len > snaplen) len = snaplen; if (if_index != -1) printf("\nIngress if-index: %d\n", if_index); print_ip_hdr(packet, len); return; } void print_sec(int len, int if_index) { nsec++; if (len > snaplen) len = snaplen; if (if_index != -1) printf("\nIngress if-index: %d\n", if_index); print_ip_hdr(packet, len); return; } void print_lsrr(int len, int if_index) { nlsrr++; if (len > snaplen) len = snaplen; if (if_index != -1) printf("\nIngress if-index: %d\n", if_index); print_ip_hdr(packet, len); return; } void print_ssrr(int len, int if_index) { nssrr++; if (len > snaplen) len = snaplen; if (if_index != -1) printf("\nIngress if-index: %d\n", if_index); print_ip_hdr(packet, len); return; } void print_ra(int len, int if_index) { struct ip *ip; struct ip_routeralert *alert; ip = (struct ip *)packet; alert = (struct ip_routeralert *)(ip + 1); if (len > snaplen) len = snaplen; if (ntohs(alert->ipa_val) == IPOPT_RA_RTCP) { print_rtcp(len, if_index); return; } else if (ntohs(alert->ipa_val) == IPOPT_RA_EXAM && ip->ip_p == IPPROTO_RSVP) { print_rsvp(len, if_index); return; } nra++; if (if_index != -1) printf("\nReceiving interface index: %d\n", if_index); if(((ip->ip_hl * 4) - sizeof(struct ip)) != alert->ipa_len) { fprintf(stderr, "bad alert message length\n"); return; } printf("IP Router-Alert: "); printf("[value %d] ", ntohs(alert->ipa_val)); printf("\r\n"); return; } struct rsvphdr { u_char ver; u_char type; u_short chksum; u_char ttl; u_char rsvd; u_short len; /* * a blub number of objects */ }; #define RSVP_PATH 1 #define RSVP_RESV 2 #define RSVP_PERR 3 #define RSVP_RERR 4 #define RSVP_PTEAR 5 #define RSVP_RTEAR 6 #define RSVP_RCONF 7 void print_rsvp(int len, int if_index) { struct rsvphdr *rhdr; struct ip *ip; int hlen; nrsvp++; if (len > snaplen) len = snaplen; if (if_index != -1) printf("\nIngress if-index: %d\n", if_index); print_ip_hdr(packet, len); /* print RSVP header */ printf("RSVP: "); ip = (struct ip *)packet; hlen = ip->ip_hl * 4; rhdr = (struct rsvphdr *)((char *)ip + hlen); switch (rhdr->type) { case RSVP_PATH: printf("[PATH] "); break; case RSVP_RESV: printf("[RESV] "); break; case RSVP_PERR: printf("[PATH ERR] "); break; case RSVP_RERR: printf("[RESV ERR] "); break; case RSVP_PTEAR: printf("[PATH TEAR] "); break; case RSVP_RTEAR: printf("[RESV TEAR] "); break; case RSVP_RCONF: printf("[RESV CONF] "); break; default: printf("[type %d] ", rhdr->type); break; } printf("[ttl %d] ", (int)rhdr->ttl); printf("[len %d] ", ntohs(rhdr->len)); /* well... will add the detailed parsing stuff here later * For impatient people, copy rsvp_map_packet() from the ISI * RSVP release and display all the objects one by one, */ printf("\r\n"); return; } void print_rtcp(int len, int if_index) { struct ip *ip; struct ip_routeralert *alert; nra++; nrtcp++; ip = (struct ip *)packet; alert = (struct ip_routeralert *)(ip + 1); if (len > snaplen) len = snaplen; if (if_index != -1) printf("\nIngress if-index: %d\n", if_index); print_ip_hdr(packet, len); printf("IP Router-Alert: [RTCP] "); if(((ip->ip_hl * 4) - sizeof(struct ip)) != alert->ipa_len) { fprintf(stderr, "bad alert message length\n"); return; } if (ntohs(alert->ipa_val) != IPOPT_RA_RTCP) { fprintf(stderr, "not RTCP code, %d\n", alert->ipa_val); return; } printf("\r\n"); return; }