/** * returns number of bytes sent on success or -1 on error * Note: it is theoretically possible to get a return code >0 and < len * which for most people would be considered an error (the packet wasn't fully sent) * so you may want to test for recode != len too. * * Most socket API's have two interesting errors: ENOBUFS & EAGAIN. ENOBUFS * is usually due to the kernel buffers being full. EAGAIN happens when you * try to send traffic faster then the PHY allows. */ int sendpacket(sendpacket_t *sp, const u_char *data, size_t len, struct pcap_pkthdr *pkthdr) { int retcode = 0, val; static u_char buffer[10000]; /* 10K bytes, enough for jumbo frames + pkthdr * larger than page size so made static to * prevent page misses on stack */ static const size_t buffer_payload_size = sizeof(buffer) + sizeof(struct pcap_pkthdr); assert(sp); assert(data); if (len <= 0) return -1; TRY_SEND_AGAIN: sp->attempt ++; switch (sp->handle_type) { case SP_TYPE_KHIAL: memcpy(buffer, pkthdr, sizeof(struct pcap_pkthdr)); memcpy(buffer + sizeof(struct pcap_pkthdr), data, min(len, buffer_payload_size)); /* tell the kernel module which direction the traffic is going */ if (sp->cache_dir == TCPR_DIR_C2S) { /* aka PRIMARY */ val = KHIAL_DIRECTION_RX; if (ioctl(sp->handle.fd, KHIAL_SET_DIRECTION, (void *)&val) < 0) { sendpacket_seterr(sp, "Error setting direction on %s: %s (%d)", sp->device, strerror(errno), errno); return -1; } } else if (sp->cache_dir == TCPR_DIR_S2C) { val = KHIAL_DIRECTION_TX; if (ioctl(sp->handle.fd, KHIAL_SET_DIRECTION, (void *)&val) < 0) { sendpacket_seterr(sp, "Error setting direction on %s: %s (%d)", sp->device, strerror(errno), errno); return -1; } } /* write the pkthdr + packet data all at once */ retcode = write(sp->handle.fd, (void *)buffer, sizeof(struct pcap_pkthdr) + len); retcode -= sizeof(struct pcap_pkthdr); /* only record packet bytes we sent, not pcap data too */ if (retcode < 0 && !sp->abort) { switch(errno) { case EAGAIN: sp->retry_eagain ++; goto TRY_SEND_AGAIN; break; case ENOBUFS: sp->retry_enobufs ++; goto TRY_SEND_AGAIN; break; default: sendpacket_seterr(sp, "Error with %s [" COUNTER_SPEC "]: %s (errno = %d)", "khial", sp->sent + sp->failed + 1, strerror(errno), errno); } break; } break; case SP_TYPE_TUNTAP: retcode = write(sp->handle.fd, (void *)data, len); break; /* Linux PF_PACKET and TX_RING */ case SP_TYPE_PF_PACKET: case SP_TYPE_TX_RING: #if defined HAVE_PF_PACKET #ifdef HAVE_TX_RING retcode = (int)txring_put(sp->tx_ring, data, len); #else retcode = (int)send(sp->handle.fd, (void *)data, len, 0); #endif /* out of buffers, or hit max PHY speed, silently retry * as long as we're not told to abort */ if (retcode < 0 && !sp->abort) { switch (errno) { case EAGAIN: sp->retry_eagain ++; goto TRY_SEND_AGAIN; break; case ENOBUFS: sp->retry_enobufs ++; goto TRY_SEND_AGAIN; break; default: sendpacket_seterr(sp, "Error with %s [" COUNTER_SPEC "]: %s (errno = %d)", INJECT_METHOD, sp->sent + sp->failed + 1, strerror(errno), errno); } } #endif /* HAVE_PF_PACKET */ break; /* BPF */ case SP_TYPE_BPF: #if defined HAVE_BPF retcode = write(sp->handle.fd, (void *)data, len); /* out of buffers, or hit max PHY speed, silently retry */ if (retcode < 0 && !sp->abort) { switch (errno) { case EAGAIN: sp->retry_eagain ++; goto TRY_SEND_AGAIN; break; case ENOBUFS: sp->retry_enobufs ++; goto TRY_SEND_AGAIN; break; default: sendpacket_seterr(sp, "Error with %s [" COUNTER_SPEC "]: %s (errno = %d)", INJECT_METHOD, sp->sent + sp->failed + 1, strerror(errno), errno); } } #endif break; /* Libdnet */ case SP_TYPE_LIBDNET: #if defined HAVE_LIBDNET retcode = eth_send(sp->handle.ldnet, (void*)data, (size_t)len); /* out of buffers, or hit max PHY speed, silently retry */ if (retcode < 0 && !sp->abort) { switch (errno) { case EAGAIN: sp->retry_eagain ++; goto TRY_SEND_AGAIN; break; case ENOBUFS: sp->retry_enobufs ++; goto TRY_SEND_AGAIN; break; default: sendpacket_seterr(sp, "Error with %s [" COUNTER_SPEC "]: %s (errno = %d)", INJECT_METHOD, sp->sent + sp->failed + 1, strerror(errno), errno); } } #endif break; case SP_TYPE_LIBPCAP: #if (defined HAVE_PCAP_INJECT || defined HAVE_PCAP_SENDPACKET) #if defined HAVE_PCAP_INJECT /* * pcap methods don't seem to support ENOBUFS, so we just straight fail * is there a better way??? */ retcode = pcap_inject(sp->handle.pcap, (void*)data, len); #elif defined HAVE_PCAP_SENDPACKET retcode = pcap_sendpacket(sp->handle.pcap, data, (int)len); #endif /* out of buffers, or hit max PHY speed, silently retry */ if (retcode < 0 && !sp->abort) { switch (errno) { case EAGAIN: sp->retry_eagain ++; goto TRY_SEND_AGAIN; break; case ENOBUFS: sp->retry_enobufs ++; goto TRY_SEND_AGAIN; break; default: sendpacket_seterr(sp, "Error with %s [" COUNTER_SPEC "]: %s (errno = %d)", INJECT_METHOD, sp->sent + sp->failed + 1, pcap_geterr(sp->handle.pcap), errno); } } #if defined HAVE_PCAP_SENDPACKET /* * pcap_sendpacket returns 0 on success, not the packet length! * hence, we have to fix retcode to be more standard on success */ if (retcode == 0) retcode = len; #endif /* HAVE_PCAP_SENDPACKET */ #endif /* HAVE_PCAP_INJECT || HAVE_PCAP_SENDPACKET */ break; case SP_TYPE_QUICK_TX: #ifdef HAVE_QUICK_TX retcode = quick_tx_send_packet(sp->qtx_dev, data, len); if (retcode < 0) sendpacket_seterr(sp, "Quick TX send failure"); #endif break; case SP_TYPE_NETMAP: #ifdef HAVE_NETMAP retcode = sendpacket_send_netmap(sp, data, len); if (retcode == -1) { sendpacket_seterr(sp, "interface hung!!"); } else if (retcode == -2) { /* this indicates that a retry was requested - this is not a failure */ retcode = 0; goto TRY_SEND_AGAIN; } #endif /* HAVE_NETMAP */ break; default: errx(-1, "Unsupported sp->handle_type = %d", sp->handle_type); } /* end case */ if (retcode < 0) { sp->failed ++; } else if (retcode != (int)len) { sendpacket_seterr(sp, "Only able to write %d bytes out of %u bytes total", retcode, len); sp->trunc_packets ++; } else { sp->bytes_sent += len; sp->sent ++; } return retcode; }
int main (int argc, char* argv[]) { int i; void* buffer; long length; int loops; __u64 packets_sent = 0; __u64 packet_bytes = 0; struct timeval tv_start; struct pcap_pkthdr* pcap_hdr; struct quick_tx qtx; if (argc != 3 && argc != 4) { printf("Usage: ./pcapsend <path-to-pcap> <interface> [loops]\n"); exit(-1); } if (!read_pcap_file(argv[1], &buffer, &length)) { perror("Failed to read file! "); exit(-1); } if (argc == 4) { loops = atoi(argv[3]); } else { loops = 1; } memset(&qtx, 0, sizeof(qtx)); int ret = quick_tx_open(argv[2], &qtx); if (ret == 0) { int blocks = quick_tx_alloc_mem_space(&qtx, length * loops); if (blocks >= 0) { printf("quick_tx mapped %d blocks of memory\n", blocks); } else { printf("quick_tx_alloc_mem_space failure\n"); exit(-1); } } else { exit(-1); } gettimeofday(&tv_start,NULL); for (i = 0; i < loops; i++) { void* offset = buffer + sizeof(struct pcap_file_header); while(offset < buffer + length) { pcap_hdr = (struct pcap_pkthdr*) offset; offset += sizeof(struct pcap_pkthdr); if ((quick_tx_send_packet(&qtx, (const void*)offset, pcap_hdr->caplen)) < 0) { printf("An error occurred while trying to send a packet\n"); goto quick_tx_error; } offset += pcap_hdr->caplen; packets_sent++; packet_bytes+= pcap_hdr->caplen; } } printf("Done, closing everything!\n"); printf("\n"); quick_tx_error: quick_tx_close(&qtx); free(buffer); return 0; }