123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676 |
- /* $Id: do_packets.c 882 2004-11-07 04:16:26Z aturner $ */
- /*
- * Copyright (c) 2001-2004 Aaron Turner, Matt Bing.
- * 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.
- * 3. Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``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 AUTHORS OR COPYRIGHT HOLDERS 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.
- */
- #include "config.h"
- #include <libnet.h>
- #ifdef HAVE_PCAPNAV
- #include <pcapnav.h>
- #else
- #include "fakepcapnav.h"
- #endif
- #include <sys/time.h>
- #include <sys/types.h>
- #include <signal.h>
- #include <string.h>
- #include <netinet/in.h>
- #include <time.h>
- #ifdef HAVE_SYS_POLL_H
- #include <sys/poll.h>
- #elif HAVE_POLL_H
- #include <poll.h>
- #else
- #include "fakepoll.h"
- #endif
- #include "tcpreplay.h"
- #include "tcpdump.h"
- #include "cidr.h"
- #include "portmap.h"
- #include "cache.h"
- #include "err.h"
- #include "do_packets.h"
- #include "edit_packet.h"
- #include "timer.h"
- #include "list.h"
- #include "xX.h"
- extern struct options options;
- extern char *cachedata, *intf, *intf2;
- extern CIDR *cidrdata;
- extern PORTMAP *portmap_data;
- extern struct timeval begin, end;
- extern u_int64_t bytes_sent, failed, pkts_sent;
- extern u_int64_t cache_packets;
- extern volatile int didsig;
- extern int l2len, maxpacket;
- extern int include_exclude_mode;
- extern CIDR *xX_cidr;
- extern LIST *xX_list;
- extern tcpdump_t tcpdump;
- #ifdef DEBUG
- extern int debug;
- #endif
- void packet_stats(); /* from tcpreplay.c */
- /*
- * we've got a race condition, this is our workaround
- */
- void
- catcher(int signo)
- {
- /* stdio in signal handlers cause a race, instead we set a flag */
- if (signo == SIGINT)
- didsig = 1;
- }
- /*
- * when we're sending only one packet at a time via <ENTER>
- * then there's no race and we can quit now
- * also called when didsig is set
- */
- void
- break_now(int signo)
- {
- if (signo == SIGINT || didsig) {
- printf("\n");
- /* kill tcpdump child if required */
- if (tcpdump.pid)
- if (kill(tcpdump.pid, SIGTERM) != 0)
- kill(tcpdump.pid, SIGKILL);
- packet_stats();
- exit(1);
- }
- }
- /*
- * the main loop function. This is where we figure out
- * what to do with each packet
- */
- void
- do_packets(pcapnav_t * pcapnav, pcap_t * pcap, u_int32_t linktype,
- int l2enabled, char *l2data, int l2len)
- {
- eth_hdr_t *eth_hdr = NULL;
- ip_hdr_t *ip_hdr = NULL;
- arp_hdr_t *arp_hdr = NULL;
- libnet_t *l = NULL;
- struct pcap_pkthdr pkthdr; /* libpcap packet info */
- const u_char *nextpkt = NULL; /* packet buffer from libpcap */
- u_char *pktdata = NULL; /* full packet buffer */
- #ifdef FORCE_ALIGN
- u_char *ipbuff = NULL; /* IP header and above buffer */
- #endif
- struct timeval last;
- static int firsttime = 1;
- int ret, newl2len;
- u_int64_t packetnum = 0;
- #ifdef HAVE_PCAPNAV
- pcapnav_result_t pcapnav_result = 0;
- #endif
- char datadumpbuff[MAXPACKET]; /* data dumper buffer */
- int datalen = 0; /* data dumper length */
- int newchar = 0;
- int needtorecalc = 0; /* did the packet change? if so, checksum */
- struct pollfd poller[1]; /* use poll to read from the keyboard */
- /* create packet buffers */
- if ((pktdata = (u_char *) malloc(maxpacket)) == NULL)
- errx(1, "Unable to malloc pktdata buffer");
- #ifdef FORCE_ALIGN
- if ((ipbuff = (u_char *) malloc(maxpacket)) == NULL)
- errx(1, "Unable to malloc ipbuff buffer");
- #endif
- /* register signals */
- didsig = 0;
- if (!options.one_at_a_time) {
- (void)signal(SIGINT, catcher);
- }
- else {
- (void)signal(SIGINT, break_now);
- }
- if (firsttime) {
- timerclear(&last);
- firsttime = 0;
- }
- #ifdef HAVE_PCAPNAV
- /* only support jumping w/ files */
- if ((pcapnav != NULL) && (options.offset)) {
- /* jump to the next packet >= the offset */
- if (pcapnav_goto_offset(pcapnav, (off_t)options.offset, PCAPNAV_CMP_GEQ)
- != PCAPNAV_DEFINITELY)
- warnx("Unable to get a definate jump offset "
- "pcapnav_goto_offset(): %d\n", pcapnav_result);
- }
- #endif
- /* get the pcap handler for the main loop */
- pcap = pcapnav_pcap(pcapnav);
- /* MAIN LOOP
- * Keep sending while we have packets or until
- * we've sent enough packets
- */
- while (((nextpkt = pcap_next(pcap, &pkthdr)) != NULL) &&
- (options.limit_send != pkts_sent)) {
- /* die? */
- if (didsig)
- break_now(0);
- dbg(2, "packets sent %llu", pkts_sent);
- packetnum++;
- dbg(2, "packet %llu caplen %d", packetnum, pkthdr.caplen);
- /* zero out the old packet info */
- memset(pktdata, '\0', maxpacket);
- needtorecalc = 0;
- /* Rewrite any Layer 2 data */
- if ((newl2len = rewrite_l2(&pkthdr, pktdata, nextpkt,
- linktype, l2enabled, l2data, l2len)) == 0)
- continue;
- l2len = newl2len;
- /* look for include or exclude LIST match */
- if (xX_list != NULL) {
- if (include_exclude_mode < xXExclude) {
- if (!check_list(xX_list, (packetnum))) {
- continue;
- }
- }
- else if (check_list(xX_list, (packetnum))) {
- continue;
- }
- }
- eth_hdr = (eth_hdr_t *) pktdata;
- /* does packet have an IP header? if so set our pointer to it */
- if (ntohs(eth_hdr->ether_type) == ETHERTYPE_IP) {
- #ifdef FORCE_ALIGN
- /*
- * copy layer 3 and up to our temp packet buffer
- * for now on, we have to edit the packetbuff because
- * just before we send the packet, we copy the packetbuff
- * back onto the pkt.data + l2len buffer
- * we do all this work to prevent byte alignment issues
- */
- ip_hdr = (ip_hdr_t *) ipbuff;
- memcpy(ip_hdr, (&pktdata[l2len]), pkthdr.caplen - l2len);
- #else
- /*
- * on non-strict byte align systems, don't need to memcpy(),
- * just point to 14 bytes into the existing buffer
- */
- ip_hdr = (ip_hdr_t *) (&pktdata[l2len]);
- #endif
- /* look for include or exclude CIDR match */
- if (xX_cidr != NULL) {
- if (!process_xX_by_cidr(include_exclude_mode, xX_cidr, ip_hdr)) {
- continue;
- }
- }
- }
- else {
- /* non-IP packets have a NULL ip_hdr struct */
- ip_hdr = NULL;
- }
- /* check for martians? */
- if (options.no_martians && (ip_hdr != NULL)) {
- switch ((ntohl(ip_hdr->ip_dst.s_addr) & 0xff000000) >> 24) {
- case 0:
- case 127:
- case 255:
- dbg(1, "Skipping martian. Packet #%llu", packetnum);
- /* then skip the packet */
- continue;
- default:
- /* continue processing */
- break;
- }
- }
- /* Dual nic processing */
- if (options.intf2 != NULL) {
- if (cachedata != NULL) {
- l = (LIBNET *) cache_mode(cachedata, packetnum, eth_hdr);
- }
- else if (options.cidr) {
- l = (LIBNET *) cidr_mode(eth_hdr, ip_hdr);
- }
- else {
- errx(1, "do_packets(): Strange, we should of never of gotten here");
- }
- }
- else {
- /* normal single nic operation */
- l = options.intf1;
- /* check for destination MAC rewriting */
- if (memcmp(options.intf1_mac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_dhost, options.intf1_mac, ETHER_ADDR_LEN);
- }
- if (memcmp(options.intf1_smac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_shost, options.intf1_smac, ETHER_ADDR_LEN);
- }
- }
- /* sometimes we should not send the packet */
- if (l == CACHE_NOSEND)
- continue;
- /* rewrite IP addresses */
- if (options.rewriteip) {
- /* IP packets */
- if (ip_hdr != NULL) {
- needtorecalc += rewrite_ipl3(ip_hdr, l);
- }
- /* ARP packets */
- else if (ntohs(eth_hdr->ether_type) == ETHERTYPE_ARP) {
- arp_hdr = (arp_hdr_t *)(&pktdata[l2len]);
- /* unlike, rewrite_ipl3, we don't care if the packet changed
- * because we never need to recalc the checksums for an ARP
- * packet. So ignore the return value
- */
- rewrite_iparp(arp_hdr, l);
- }
- }
- /* rewrite ports */
- if (options.rewriteports && (ip_hdr != NULL)) {
- needtorecalc += rewrite_ports(portmap_data, &ip_hdr);
- }
- /* Untruncate packet? Only for IP packets */
- if ((options.trunc) && (ip_hdr != NULL)) {
- needtorecalc += untrunc_packet(&pkthdr, pktdata, ip_hdr, l, l2len);
- }
- /* do we need to spoof the src/dst IP address? */
- if ((options.seed) && (ip_hdr != NULL)) {
- needtorecalc += randomize_ips(&pkthdr, pktdata, ip_hdr, l, l2len);
- }
- /* do we need to force fixing checksums? */
- if ((options.fixchecksums || needtorecalc) && (ip_hdr != NULL)) {
- fix_checksums(&pkthdr, ip_hdr, l);
- }
- #ifdef STRICT_ALIGN
- /*
- * put back the layer 3 and above back in the pkt.data buffer
- * we can't edit the packet at layer 3 or above beyond this point
- */
- memcpy(&pktdata[l2len], ip_hdr, pkthdr.caplen - l2len);
- #endif
- /* do we need to print the packet via tcpdump? */
- if (options.verbose)
- tcpdump_print(&tcpdump, &pkthdr, pktdata);
- if ((!options.topspeed) && (!options.one_at_a_time)) {
- /* we have to cast the ts, since OpenBSD sucks
- * had to be special and use bpf_timeval
- */
- do_sleep((struct timeval *)&pkthdr.ts, &last, pkthdr.caplen);
- }
- else if (options.one_at_a_time) {
- printf("**** Press <ENTER> to send the next packet out %s\n",
- l == options.intf1 ? intf : intf2);
- poller[0].fd = STDIN_FILENO;
- poller[0].events = POLLIN;
- poller[0].revents = 0;
- /* wait for the input */
- if (poll(poller, 1, -1) < 0)
- errx(1, "do_packets(): Error reading from stdin: %s", strerror(errno));
- /* read to the end of the line */
- do {
- newchar = getc(stdin);
- } while (newchar != '\n');
- }
- /* in one output mode always use primary nic/file */
- if (options.one_output)
- l = options.intf1;
- /* Physically send the packet or write to file */
- if (options.savepcap != NULL || options.datadump_mode) {
- /* figure out the correct offsets/data len */
- if (options.datadump_mode) {
- memset(datadumpbuff, '\0', MAXPACKET);
- datalen =
- extract_data(pktdata, pkthdr.caplen, l2len, &datadumpbuff);
- }
- /* interface 1 */
- if (l == options.intf1) {
- if (options.datadump_mode) { /* data only? */
- if (datalen) {
- if (write(options.datadumpfile, datadumpbuff, datalen)
- == -1)
- warnx("error writing data to primary dump file: %s",
- strerror(errno));
- }
- }
- else { /* full packet */
- pcap_dump((u_char *) options.savedumper, &pkthdr, pktdata);
- }
- }
- /* interface 2 */
- else {
- if (options.datadump_mode) { /* data only? */
- if (datalen) {
- if (write(options.datadumpfile2, datadumpbuff, datalen)
- == -1)
- warnx
- ("error writing data to secondary dump file: %s",
- strerror(errno));
- }
- }
- else { /* full packet */
- pcap_dump((u_char *) options.savedumper2, &pkthdr, pktdata);
- }
- }
- }
- else {
- /* write packet out on network */
- do {
- ret = libnet_adv_write_link(l, pktdata, pkthdr.caplen);
- if (ret == -1) {
- /* Make note of failed writes due to full buffers */
- if (errno == ENOBUFS) {
- failed++;
- }
- else {
- errx(1, "libnet_adv_write_link(): %s", strerror(errno));
- }
- }
- /* keep trying if fail, unless user Ctrl-C's */
- } while (ret == -1 && !didsig);
- }
- bytes_sent += pkthdr.caplen;
- pkts_sent++;
- /* again, OpenBSD is special, so use memcpy() rather then a
- * straight assignment
- */
- memcpy(&last, &pkthdr.ts, sizeof(struct timeval));
- } /* while() */
- /* free buffers */
- free(pktdata);
- #ifdef FORCE_ALIGN
- free(ipbuff);
- #endif
- /*
- * if we exited our while() loop, we need to exit
- * gracefully
- */
- if (options.limit_send == pkts_sent) {
- packet_stats();
- exit(1);
- }
- }
- /*
- * determines based upon the cachedata which interface the given packet
- * should go out. Also rewrites any layer 2 data we might need to adjust.
- * Returns a void cased pointer to the options.intfX of the corresponding
- * interface.
- */
- void *
- cache_mode(char *cachedata, u_int64_t packet_num, eth_hdr_t * eth_hdr)
- {
- void *l = NULL;
- int result;
- if (packet_num > cache_packets)
- errx(1, "Exceeded number of packets in cache file.");
- result = check_cache(cachedata, packet_num);
- if (result == CACHE_NOSEND) {
- dbg(2, "Cache: Not sending packet %d.", packet_num);
- return NULL;
- }
- else if (result == CACHE_PRIMARY) {
- dbg(2, "Cache: Sending packet %d out primary interface.", packet_num);
- l = options.intf1;
- /* check for dest/src MAC rewriting */
- if (memcmp(options.intf1_mac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_dhost, options.intf1_mac, ETHER_ADDR_LEN);
- }
- if (memcmp(options.intf1_smac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_shost, options.intf1_smac, ETHER_ADDR_LEN);
- }
- }
- else if (result == CACHE_SECONDARY) {
- dbg(2, "Cache: Sending packet %d out secondary interface.", packet_num);
- l = options.intf2;
- /* check for dest/src MAC rewriting */
- if (memcmp(options.intf2_mac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_dhost, options.intf2_mac, ETHER_ADDR_LEN);
- }
- if (memcmp(options.intf2_smac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_shost, options.intf2_smac, ETHER_ADDR_LEN);
- }
- }
- else {
- errx(1, "check_cache() returned an error. Aborting...");
- }
- return l;
- }
- /*
- * determines based upon the cidrdata which interface the given packet
- * should go out. Also rewrites any layer 2 data we might need to adjust.
- * Returns a void cased pointer to the options.intfX of the corresponding
- * interface.
- */
- void *
- cidr_mode(eth_hdr_t * eth_hdr, ip_hdr_t * ip_hdr)
- {
- void *l = NULL;
- if (ip_hdr == NULL) {
- /* non IP packets go out intf1 */
- l = options.intf1;
- /* check for dest/src MAC rewriting */
- if (memcmp(options.intf1_mac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_dhost, options.intf1_mac, ETHER_ADDR_LEN);
- }
- if (memcmp(options.intf1_smac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_shost, options.intf1_smac, ETHER_ADDR_LEN);
- }
- }
- else if (check_ip_CIDR(cidrdata, ip_hdr->ip_src.s_addr)) {
- /* set interface to send out packet */
- l = options.intf1;
- /* check for dest/src MAC rewriting */
- if (memcmp(options.intf1_mac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_dhost, options.intf1_mac, ETHER_ADDR_LEN);
- }
- if (memcmp(options.intf1_smac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_shost, options.intf1_smac, ETHER_ADDR_LEN);
- }
- }
- else {
- /* override interface to send out packet */
- l = options.intf2;
- /* check for dest/src MAC rewriting */
- if (memcmp(options.intf2_mac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_dhost, options.intf2_mac, ETHER_ADDR_LEN);
- }
- if (memcmp(options.intf2_smac, NULL_MAC, ETHER_ADDR_LEN) != 0) {
- memcpy(eth_hdr->ether_shost, options.intf2_smac, ETHER_ADDR_LEN);
- }
- }
- return l;
- }
- /*
- * Given the timestamp on the current packet and the last packet sent,
- * calculate the appropriate amount of time to sleep and do so.
- */
- void
- do_sleep(struct timeval *time, struct timeval *last, int len)
- {
- static struct timeval didsleep = { 0, 0 };
- static struct timeval start = { 0, 0 };
- struct timeval nap, now, delta;
- struct timespec ignore, sleep;
- float n;
- if (gettimeofday(&now, NULL) < 0) {
- err(1, "gettimeofday");
- }
- /* First time through for this file */
- if (!timerisset(last)) {
- start = now;
- timerclear(&delta);
- timerclear(&didsleep);
- }
- else {
- timersub(&now, &start, &delta);
- }
- if (options.mult) {
- /*
- * Replay packets a factor of the time they were originally sent.
- */
- if (timerisset(last) && timercmp(time, last, >)) {
- timersub(time, last, &nap);
- }
- else {
- /*
- * Don't sleep if this is our first packet, or if the
- * this packet appears to have been sent before the
- * last packet.
- */
- timerclear(&nap);
- }
- timerdiv(&nap, options.mult);
- }
- else if (options.rate) {
- /*
- * Ignore the time supplied by the capture file and send data at
- * a constant 'rate' (bytes per second).
- */
- if (timerisset(last)) {
- n = (float)len / (float)options.rate;
- nap.tv_sec = n;
- nap.tv_usec = (n - nap.tv_sec) * 1000000;
- }
- else {
- timerclear(&nap);
- }
- }
- else if (options.packetrate) {
- float pr;
- pr = 1 / options.packetrate;
- nap.tv_sec = pr;
- pr -= nap.tv_sec;
- nap.tv_usec = pr * 1000000;
- }
- timeradd(&didsleep, &nap, &didsleep);
- if (timercmp(&didsleep, &delta, >)) {
- timersub(&didsleep, &delta, &nap);
- sleep.tv_sec = nap.tv_sec;
- sleep.tv_nsec = nap.tv_usec * 1000; /* convert ms to ns */
- if (nanosleep(&sleep, &ignore) == -1) {
- warnx("nanosleep error: %s", strerror(errno));
- }
- }
- }
|