/** * calculates wether each node in the tree is a client, server, or unknown for each node in the tree */ void tree_calculate(tcpr_data_tree_t *treeroot) { tcpr_tree_t *node; tcpprep_opt_t *options = tcpprep->options; dbg(1, "Running tree_calculate()"); RB_FOREACH(node, tcpr_data_tree_s, treeroot) { dbgx(4, "Processing %s", get_addr2name4(node->u.ip, RESOLVE)); if ((node->server_cnt > 0) || (node->client_cnt > 0)) { /* type based on: server >= (client*ratio) */ if ((double)node->server_cnt >= (double)node->client_cnt * options->ratio) { node->type = DIR_SERVER; dbgx(3, "Setting %s to server", get_addr2name4(node->u.ip, RESOLVE)); } else { node->type = DIR_CLIENT; dbgx(3, "Setting %s to client", get_addr2name4(node->u.ip, RESOLVE)); } } else { /* IP had no client or server connections */ node->type = DIR_UNKNOWN; dbgx(3, "Setting %s to unknown", get_addr2name4(node->u.ip, RESOLVE)); } }
/* * return the length of the L2 header of the current packet */ int dlt_jnpr_ether_l2len(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen) { uint16_t len; jnpr_ether_config_t *config; assert(ctx); assert(packet); assert(pktlen); config = (jnpr_ether_config_t *)ctx->encoder->config; /* first get the Juniper header length */ memcpy(&len, &packet[JUNIPER_ETHER_EXTLEN_OFFSET], 2); len = ntohs(len) + JUNIPER_ETHER_HEADER_LEN; dbgx(3, "juniper header len: %u", len); /* add the 802.3 length */ len += tcpedit_dlt_l2len(config->subctx, DLT_EN10MB, (packet + len), (pktlen - len)); dbgx(3, "total l2len: %u", len); /* and return that */ return len; }
/** * returns the next payload or header of the current extention header * returns NULL for none/ESP. */ void * get_ipv6_next(struct tcpr_ipv6_ext_hdr_base *exthdr, const int len) { int extlen = 0; int maxlen; void *ptr; assert(exthdr); maxlen = (int)((u_char *)exthdr + len); dbgx(3, "Jumping to next IPv6 header. Processing 0x%02x", exthdr->ip_nh); switch (exthdr->ip_nh) { /* no further processing */ case TCPR_IPV6_NH_NO_NEXT: case TCPR_IPV6_NH_ESP: dbg(3, "No-Next or ESP... can't go any further..."); return NULL; break; /* * fragment header is fixed size * FIXME: Frag header has further ext headers (has a ip_nh field) * but I don't support it because there's never a full L4 + payload beyond. */ case TCPR_IPV6_NH_FRAGMENT: dbg(3, "Looks like were a fragment header. Returning some frag'd data."); ptr = (void *)((u_char *)exthdr + sizeof(struct tcpr_ipv6_frag_hdr)); if ((int)ptr > maxlen) return NULL; return ptr; break; /* all the rest require us to go deeper using the ip_len field */ case TCPR_IPV6_NH_IPV6: case TCPR_IPV6_NH_ROUTING: case TCPR_IPV6_NH_DESTOPTS: case TCPR_IPV6_NH_HBH: case TCPR_IPV6_NH_AH: extlen = IPV6_EXTLEN_TO_BYTES(exthdr->ip_len); dbgx(3, "Looks like we're an ext header (0x%hhx). Jumping %u bytes" " to the next", exthdr->ip_nh, extlen); ptr = (void *)((u_char *)exthdr + extlen); if ((int)ptr > maxlen) return NULL; return ptr; break; default: dbg(3, "Must not be a v6 extension header... returning self"); return (void *)exthdr; break; } }
/** * \brief Build TX ring buffer request structure * * This builds a ring buffer request structure making sure * that we have buffers big enough so that a frame which * is the size of the MTU doesn't get truncated. We also * need to structure things with minimum memory wastage */ void txring_mkreq(struct tpacket_req* treq, unsigned int mtu) { unsigned int pg,bs; unsigned int s; unsigned int mult = 1; unsigned nr_blocks = 1000; bs = pg = getpagesize(); s = mtu + TPACKET_HDRLEN; memset(treq, 0, sizeof(struct tpacket_req)); if (bs <= s) { while(bs < s) { bs += pg; mult++; } treq->tp_block_size = bs; treq->tp_frame_size = bs / mult; treq->tp_block_nr = nr_blocks; treq->tp_frame_nr = mult * nr_blocks; } else { while ((s * (mult + 1)) <=pg) { mult++; } treq->tp_block_size = pg; treq->tp_frame_size = pg / mult; treq->tp_block_nr = nr_blocks; treq->tp_frame_nr = mult * nr_blocks; } dbgx(1, "txring: block_size=%d block_nr=%d frame_size=%d frame_nr=%d", treq->tp_block_size, treq->tp_block_nr, treq->tp_frame_size, treq->tp_frame_nr); }
/* * Function to decode the layer 2 header in the packet. * You need to fill out: * - ctx->l2len * - ctx->srcaddr * - ctx->dstaddr * - ctx->proto * - ctx->decoded_extra * Returns: TCPEDIT_ERROR | TCPEDIT_OK | TCPEDIT_WARN */ int dlt_ieee80211_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen) { assert(ctx); assert(packet); assert(pktlen >= dlt_ieee80211_l2len(ctx, packet, pktlen)); dbgx(3, "Decoding 802.11 packet " COUNTER_SPEC, ctx->tcpedit->runtime.packetnum); if (! ieee80211_is_data(ctx, packet, pktlen)) { tcpedit_seterr(ctx->tcpedit, "Packet " COUNTER_SPEC " is not a normal 802.11 data frame", ctx->tcpedit->runtime.packetnum); return TCPEDIT_SOFT_ERROR; } if (ieee80211_is_encrypted(ctx, packet, pktlen)) { tcpedit_seterr(ctx->tcpedit, "Packet " COUNTER_SPEC " is encrypted. Unable to decode frame.", ctx->tcpedit->runtime.packetnum); return TCPEDIT_SOFT_ERROR; } ctx->l2len = dlt_ieee80211_l2len(ctx, packet, pktlen); memcpy(&(ctx->srcaddr), ieee80211_get_src((ieee80211_hdr_t *)packet), ETHER_ADDR_LEN); memcpy(&(ctx->dstaddr), ieee80211_get_dst((ieee80211_hdr_t *)packet), ETHER_ADDR_LEN); ctx->proto = dlt_ieee80211_proto(ctx, packet, pktlen); return TCPEDIT_OK; /* success */ }
/** * This task will call send() procedure */ void * txring_send(void *arg) { int ec_send; static int total = 0; int fd_socket = (int)arg; do { /* send all buffers with TP_STATUS_SEND_REQUEST */ ec_send=sendto(fd_socket, NULL, 0, MSG_DONTWAIT, (struct sockaddr *)NULL, sizeof(struct sockaddr_ll)); if (ec_send > 0) { total += ec_send; dbgx(2, "Sent %d bytes (+%d bytes)", total, ec_send); } else { /* nothing to do => schedule : useful if no SMP */ usleep(100); } } while (!shutdown_flag); //if(blocking) printf("end of task send()\n"); //printf("end of task send(ec=%x)\n", ec_send); return (void*) ec_send; }
/** * Closes connection to Quick TX module */ static void sendpacket_close_quick_tx(sendpacket_t *sp) { struct quick_tx* dev; assert(sp); dev = sp->qtx_dev; if (dev) { int i; for (i = (dev->data->num_mem_blocks - 1); i >= 0; i--) { struct quick_tx_mem_block_entry* mem_block = &dev->data->mem_blocks[i]; if (munmap (mem_block->user_addr, mem_block->length) == -1) dbgx(1, "[quick_tx] error while calling munmap for block %d\n", i); } if (munmap ((void*)dev->data, dev->map_length) == -1) dbg(1, "[quick_tx] error while calling munmap\n"); free(dev); } close(sp->handle.fd); }
/* * return the length of the L2 header of the current packet * based on: http://www.tcpdump.org/lists/workers/2004/07/msg00121.html */ int dlt_ieee80211_l2len(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen) { u_int16_t *frame_control, fc; struct tcpr_802_2snap_hdr *hdr; int hdrlen = 0; assert(ctx); assert(packet); assert(pktlen); dbgx(2, "packet = %p\t\tplen = %d", packet, pktlen); frame_control = (u_int16_t *)packet; fc = ntohs(*frame_control); if (ieee80211_USE_4(fc)) { hdrlen = sizeof(ieee80211_addr4_hdr_t); } else { hdrlen = sizeof(ieee80211_hdr_t); } /* if Data/QoS, then L2 len is + 2 bytes */ if ((fc & ieee80211_FC_SUBTYPE_QOS) == ieee80211_FC_SUBTYPE_QOS) { dbgx(2, "total header length (fc %04x) (802.11 + QoS data): %d", fc, hdrlen + 2); hdrlen += 2; } if (pktlen >= (hdrlen + (int)sizeof(struct tcpr_802_2snap_hdr))) { hdr = (struct tcpr_802_2snap_hdr *)&packet[hdrlen]; /* verify the header is 802.2SNAP (8 bytes) not 802.2 (3 bytes) */ if (hdr->snap_dsap == 0xAA && hdr->snap_ssap == 0xAA) { hdrlen += (int)sizeof(struct tcpr_802_2snap_hdr); dbgx(2, "total header length (802.11 + 802.2SNAP): %d", hdrlen); } else { hdrlen += (int)sizeof(struct tcpr_802_2_hdr); dbgx(2, "total header length (802.11 + 802.2): %d (%02x/%02x)", hdrlen, hdr->snap_dsap, hdr->snap_ssap); } } dbgx(2, "header length: %d", hdrlen); return hdrlen; }
/* * Function to decode the layer 2 header in the packet. * You need to fill out: * - ctx->l2len * - ctx->srcaddr * - ctx->dstaddr * - ctx->proto * - ctx->decoded_extra * Returns: TCPEDIT_ERROR | TCPEDIT_OK | TCPEDIT_WARN */ int dlt_jnpr_ether_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen) { int jnpr_header_len = 0; const u_char *ethernet = NULL; jnpr_ether_config_t *config; assert(ctx); assert(packet); assert(pktlen > JUNIPER_ETHER_HEADER_LEN); /* MAGIC + Static fields + Extension Length */ config = (jnpr_ether_config_t *)ctx->encoder->config; /* first, verify magic */ if (memcmp(packet, JUNIPER_ETHER_MAGIC, JUNIPER_ETHER_MAGIC_LEN) != 0) { tcpedit_seterr(ctx->tcpedit, "Invalid magic 0x%02X%02X%02X", packet[0], packet[1], packet[2]); return TCPEDIT_ERROR; } /* next make sure the L2 header is present */ if ((packet[JUNIPER_ETHER_OPTIONS_OFFSET] & JUNIPER_ETHER_L2PRESENT) != JUNIPER_ETHER_L2PRESENT) { tcpedit_seterr(ctx->tcpedit, "Frame is missing L2 Header: %x", packet[JUNIPER_ETHER_OPTIONS_OFFSET]); return TCPEDIT_ERROR; } /* then get the Juniper header length */ memcpy(&jnpr_header_len, &packet[JUNIPER_ETHER_EXTLEN_OFFSET], 2); jnpr_header_len = ntohs(jnpr_header_len) + JUNIPER_ETHER_HEADER_LEN; dbgx(1, "jnpr header len: %d", jnpr_header_len); /* make sure the packet is big enough to find the Ethernet Header */ if (pktlen < jnpr_header_len + TCPR_ETH_H) { tcpedit_seterr(ctx->tcpedit, "Frame is too short! %d < %d", pktlen, (jnpr_header_len + TCPR_ETH_H)); return TCPEDIT_ERROR; } ctx->l2len = jnpr_header_len; /* jump to the appropriate offset */ ethernet = packet + jnpr_header_len; /* let the en10mb plugin decode the rest */ if (tcpedit_dlt_decode(config->subctx, ethernet, (pktlen - jnpr_header_len)) == TCPEDIT_ERROR) return TCPEDIT_ERROR; /* copy the subdecoder state to our encoder state */ if (tcpedit_dlt_copy_decoder_state(ctx, config->subctx) == TCPEDIT_ERROR) return TCPEDIT_ERROR; return TCPEDIT_OK; }
/** * returns the protocol of the actual layer4 header by processing through * the extension headers */ uint8_t get_ipv6_l4proto(const ipv6_hdr_t *ip6_hdr, const int len) { u_char *ptr = (u_char *)ip6_hdr + TCPR_IPV6_H; /* jump to the end of the IPv6 header */ uint8_t proto; struct tcpr_ipv6_ext_hdr_base *exthdr = NULL; proto = ip6_hdr->ip_nh; assert(ip6_hdr); while (TRUE) { dbgx(3, "Processing next proto 0x%02X", proto); switch (proto) { /* no further processing for IPV6 types with nothing beyond them */ case TCPR_IPV6_NH_FRAGMENT: case TCPR_IPV6_NH_ESP: dbg(3, "No-Next or ESP... can't go any further..."); return proto; break; /* recurse */ case TCPR_IPV6_NH_IPV6: dbg(3, "Recursing due to v6 in v6"); return get_ipv6_l4proto((ipv6_hdr_t *)ptr, len); break; /* loop again */ case TCPR_IPV6_NH_AH: case TCPR_IPV6_NH_ROUTING: case TCPR_IPV6_NH_DESTOPTS: case TCPR_IPV6_NH_HBH: dbgx(3, "Jumping to next extension header (0x%hhx)", proto); exthdr = get_ipv6_next((struct tcpr_ipv6_ext_hdr_base *)ptr, len); proto = exthdr->ip_nh; ptr = (u_char *)exthdr; break; /* should be TCP, UDP or the like */ default: dbgx(3, "Selecting next L4 Proto as: 0x%02x", proto); return proto; } /* switch */ } /* while */ }
static void add_tree_node(tcpr_tree_t *newnode) { tcpr_tree_t *node; /* try to find a simular entry in the tree */ node = RB_FIND(tcpr_data_tree_s, &treeroot, newnode); dbgx(3, "%s", tree_printnode("add_tree", node)); /* new entry required */ if (node == NULL) { /* increment counters */ if (newnode->type == DIR_SERVER) { newnode->server_cnt++; } else if (newnode->type == DIR_CLIENT) { newnode->client_cnt++; } /* insert it in */ RB_INSERT(tcpr_data_tree_s, &treeroot, newnode); } else { /* we found something, so update it */ dbgx(2, " node: %p\nnewnode: %p", node, newnode); dbgx(3, "%s", tree_printnode("update node", node)); /* increment counter */ if (newnode->type == DIR_SERVER) { node->server_cnt++; } else if (newnode->type == DIR_CLIENT) { /* temp debug code */ node->client_cnt++; } /* didn't insert it, so free it */ safe_free(newnode); } dbg(2, "------- START NEXT -------"); dbgx(3, "%s", tree_print(&treeroot)); }
/** * copy the string of args (*opt) to the vector (**opt_vec) * for a max of opt_len. Returns the number of options * in the vector */ static int tcpdump_fill_in_options(char *opt) { char options[256]; char *arg, *newarg; int i = 1, arglen; char *token = NULL; /* zero out our options_vec for execv() */ memset(options_vec, '\0', sizeof(options_vec)); /* first arg should be the binary (by convention) */ options_vec[0] = TCPDUMP_BINARY; /* prep args */ memset(options, '\0', 256); if (opt != NULL) { strlcat(options, opt, sizeof(options)); } strlcat(options, TCPDUMP_ARGS, sizeof(options)); dbgx(2, "[child] Will execute: tcpdump %s", options); /* process args */ /* process the first argument */ arg = strtok_r(options, OPT_DELIM, &token); arglen = strlen(arg) + 2; /* -{arg}\0 */ newarg = (char *)safe_malloc(arglen); strlcat(newarg, "-", arglen); strlcat(newarg, arg, arglen); options_vec[i++] = newarg; /* process the remaining args * note that i < OPTIONS_VEC_SIZE - 1 * because: a) we need to add '-' as an option to the end * b) because the array has to be null terminated */ while (((arg = strtok_r(NULL, OPT_DELIM, &token)) != NULL) && (i < OPTIONS_VEC_SIZE - 1)) { arglen = strlen(arg) + 2; newarg = (char *)safe_malloc(arglen); strlcat(newarg, "-", arglen); strlcat(newarg, arg, arglen); options_vec[i++] = newarg; } /* tell -r to read from stdin */ options_vec[i] = "-"; return i; }
/** * Put data in TX ring buffer and rotate it if necessary */ int txring_put(txring_t *txp, const void * data, size_t length) { struct tpacket_hdr *ps_header; char * to_data; int loop = 1; int first_loop = 1; unsigned int start_index = txp->tx_index; do { ps_header = ((struct tpacket_hdr *)((void *)txp->tx_head + (txp->treq->tp_frame_size * txp->tx_index))); to_data = ((void*) ps_header) + tdata_offset; switch ((volatile uint32_t)ps_header->tp_status) { case TP_STATUS_WRONG_FORMAT: warnx("TP_STATUS_WRONG_FORMAT occuries O_o. Frame %d, pkt len %d\n", txp->tx_index, length); break; case TP_STATUS_AVAILABLE: if (length > txp->treq->tp_frame_size) { //TODO Fragment packet warnx("[!] %d bytes from %d packet truncated\n", length-txp->treq->tp_frame_size, length); length = txp->treq->tp_frame_size; } memcpy(to_data, data, length); ps_header->tp_len = length; ps_header->tp_status = TP_STATUS_SEND_REQUEST; loop = 0; break; default: dbgx(2,"TPACKET status %u at frame %d with length %d\n", ps_header->tp_status, txp->tx_index, ps_header->tp_len); usleep(0); break; } txp->tx_index++; if (txp->tx_index >= txp->treq->tp_frame_nr) { txp->tx_index = 0; first_loop = 0; } /* check if we've ran over all ring */ if ((txp->tx_index == start_index) && !first_loop) { errno = ENOBUFS; return -1; } } while(loop == 1); return ps_header->tp_len; }
/** * Main entry point to bridging. Does some initial setup and then calls the * correct loop (unidirectional or bidirectional) */ void do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit) { /* do we apply a bpf filter? */ if (options->bpf.filter != NULL) { /* compile filter */ dbgx(2, "Try to compile pcap bpf filter: %s", options->bpf.filter); if (pcap_compile(options->pcap1, &options->bpf.program, options->bpf.filter, options->bpf.optimize, 0) != 0) { errx(-1, "Error compiling BPF filter: %s", pcap_geterr(options->pcap1)); } /* apply filter */ pcap_setfilter(options->pcap1, &options->bpf.program); /* same for other interface if applicable */ if (options->unidir == 0) { /* compile filter */ dbgx(2, "Try to compile pcap bpf filter: %s", options->bpf.filter); if (pcap_compile(options->pcap2, &options->bpf.program, options->bpf.filter, options->bpf.optimize, 0) != 0) { errx(-1, "Error compiling BPF filter: %s", pcap_geterr(options->pcap2)); } /* apply filter */ pcap_setfilter(options->pcap2, &options->bpf.program); } } /* register signals */ didsig = 0; (void)signal(SIGINT, signal_catcher); if (options->unidir == 1) { do_bridge_unidirectional(options, tcpedit); } else { do_bridge_bidirectional(options, tcpedit); } if (gettimeofday(&stats.end_time, NULL) < 0) errx(-1, "gettimeofday() failed: %s", strerror(errno)); packet_stats(&stats); }
/** * Method takes an open "/dev/netmap" file descriptor and returns * the netmap version. * * Returns -1 on error */ int get_netmap_version() { u_int32_t netmap_version = -1; nmreq_t nmr; int fd; if ((fd = open ("/dev/netmap", O_RDWR)) < 0) return -1; /* netmap version discovery */ bzero (&nmr, sizeof(nmr)); nmr.nr_version = NETMAP_API; /* attempt using the netmap API version that this was compiled under */ if(ioctl(fd, NIOCGINFO, &nmr) == 0) { netmap_version = nmr.nr_version; dbgx(1, "netmap detected API version %d which matches compiled version\n", netmap_version); } else { /* failed. * * Try other versions in an attempt to find the version * that matches this system. */ int x; for (x = 0; x < 50; ++x) { bzero (&nmr, sizeof(nmr)); nmr.nr_version = x; if(ioctl(fd, NIOCGINFO, &nmr) == 0) { netmap_version = nmr.nr_version; dbgx(1, "netmap detected API version %d which doesn't match compiled version %d\n", netmap_version, NETMAP_API); break; } } } close(fd); return netmap_version; }
/** * Figures out if a MAC is listed in a comma delimited * string of MAC addresses. * returns TCPR_DIR_C2S if listed * returns TCPR_DIR_S2C if not listed */ tcpr_dir_t macinstring(const char *macstring, const u_char *mac) { char *tok, *tempstr, *ourstring; u_char tempmac[6]; int len = 6, ret = TCPR_DIR_S2C; ourstring = safe_strdup(macstring); tempstr = strtok_r(ourstring, ",", &tok); if (strlen(tempstr)) { mac2hex(tempstr, tempmac, len); if (memcmp(mac, tempmac, len) == 0) { dbgx(3, "Packet matches: " MAC_FORMAT " sending out primary.\n", MAC_STR(tempmac)); ret = TCPR_DIR_C2S; goto EXIT_MACINSTRING; } } else { goto EXIT_MACINSTRING; } while ((tempstr = strtok_r(NULL, ",", &tok)) != NULL) { mac2hex(tempstr, tempmac, len); if (memcmp(mac, tempmac, len) == 0) { ret = TCPR_DIR_C2S; dbgx(3, "Packet matches: " MAC_FORMAT " sending out primary.\n", MAC_STR(tempmac)); goto EXIT_MACINSTRING; } } EXIT_MACINSTRING: safe_free(ourstring); #ifdef DEBUG if (ret == TCPR_DIR_S2C) dbg(3, "Packet doesn't match any MAC addresses sending out secondary.\n"); #endif return ret; }
/* * Function returns a pointer to the layer 3 protocol header or NULL on error */ u_char * dlt_ieee80211_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen) { int l2len; assert(ctx); assert(packet); l2len = dlt_ieee80211_l2len(ctx, packet, pktlen); assert(pktlen >= l2len); dbgx(1, "Getting data for packet " COUNTER_SPEC " from offset: %d", ctx->tcpedit->runtime.packetnum, l2len); return tcpedit_dlt_l3data_copy(ctx, packet, pktlen, l2len); }
/** * processes the tree using rbwalk / tree2cidr to generate a CIDR * used for 2nd pass, router mode * * returns > 0 for success (the mask len), 0 for fail */ int process_tree(void) { int mymask = 0; tcpr_buildcidr_t *bcdata; tcpprep_opt_t *options = tcpprep->options; dbg(1, "Running: process_tree()"); bcdata = (tcpr_buildcidr_t *)safe_malloc(sizeof(tcpr_buildcidr_t)); for (mymask = options->max_mask; mymask <= options->min_mask; mymask++) { dbgx(1, "Current mask: %u", mymask); /* set starting vals */ bcdata->type = DIR_SERVER; bcdata->masklen = mymask; /* build cidrdata with servers */ tree_buildcidr(&treeroot, bcdata); /* calculate types of all IP's */ tree_calculate(&treeroot); /* try to find clients in cidrdata */ bcdata->type = DIR_CLIENT; if (! tree_checkincidr(&treeroot, bcdata)) { /* didn't find any clients in cidrdata */ safe_free(bcdata); return (mymask); /* success! */ } else { destroy_cidr(options->cidrdata); /* clean up after our mess */ options->cidrdata = NULL; } } safe_free(bcdata); /* we failed to find a valid cidr list */ notice("Unable to determine any IP addresses as a clients."); notice("Perhaps you should change the --ratio, --minmask/maxmask settings, or try another mode?"); return (0); }
/** * adds an entry to the tree (phase 1 of auto mode). We add each host * to the tree if it doesn't yet exist. We go through and track: * - number of times each host acts as a client or server * - the way the host acted the first time we saw it (client or server) */ void add_tree_ipv4(const unsigned long ip, const u_char * data) { tcpr_tree_t *newnode = NULL; assert(data); newnode = packet2tree(data); assert(ip == newnode->u.ip); if (newnode->type == DIR_UNKNOWN) { /* couldn't figure out if packet was client or server */ dbgx(2, "%s (%lu) unknown client/server", get_addr2name4(newnode->u.ip, RESOLVE), newnode->u.ip); } add_tree_node(newnode); }
void add_tree_ipv6(const struct tcpr_in6_addr * addr, const u_char * data) { tcpr_tree_t *newnode = NULL; assert(data); newnode = packet2tree(data); assert(ipv6_cmp(addr, &newnode->u.ip6) == 0); if (newnode->type == DIR_UNKNOWN) { /* couldn't figure out if packet was client or server */ dbgx(2, "%s unknown client/server", get_addr2name6(&newnode->u.ip6, RESOLVE)); } add_tree_node(newnode); }
/** * shutdown tcpdump */ void tcpdump_close(tcpdump_t *tcpdump) { if (! tcpdump) return; if (tcpdump->pid <= 0) return; dbgx(2, "[parent] killing tcpdump pid: %d", tcpdump->pid); kill(tcpdump->pid, SIGKILL); close(PARENT_READ_FD); close(PARENT_WRITE_FD); if (waitpid(tcpdump->pid, NULL, 0) != tcpdump->pid) errx(-1, "[parent] Error in waitpid: %s", strerror(errno)); tcpdump->pid = 0; PARENT_READ_FD = 0; PARENT_WRITE_FD = 0; }
static sendpacket_t * sendpacket_open_quick_tx(const char *device, char *errbuf) { sendpacket_t *sp; struct quick_tx* dev; const char *ifname; char full_dev_name[256]; unsigned int map_size = QTX_MASTER_PAGE_NUM * PAGE_SIZE; unsigned int *map; int blocks; int i, fd; assert(device); assert(errbuf); if (strlen(device) > MAX_IFNAMELEN - 8) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Interface name is to long: %s\n", device); goto ifacename_invalid; } /* * Sort out interface names */ if (!strncmp(device, "qtx:", 4)) ifname = &device[4]; else ifname = device; /* open device name */ snprintf(full_dev_name, sizeof(full_dev_name), "%s%s", QTX_FULL_PATH_PREFIX, ifname); if ((fd = open(full_dev_name, O_RDWR | O_SYNC)) < 0) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "[quick_tx] error while opening device\n" "Check that the QuickTX module is loaded and the interface name is correct \n"); goto open_failed; } /* create a memory map to Quick TX kernel module */ map = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "[quick_tx] error while attempting to map memory from device"); goto map_create_failed; } /* create device */ dev = safe_malloc(sizeof(struct quick_tx)); dev->map_length = map_size; dev->fd = fd; dev->data = (struct quick_tx_shared_data*)map; dev->stop_auto_mapping = false; if (quick_tx_mmap_mem_block(dev) < 0) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "[quick_tx] error while mapping block"); goto map_failed; } if ((blocks = quick_tx_alloc_mem_space(dev, QTX_QUEUE_SIZE)) < 0) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "[quick_tx] error while allocating memory"); goto map_alloc_failed; } dbgx(1, "[quick_tx] mapped %d blocks of memory\n", blocks); /* store results */ sp = (sendpacket_t *)safe_malloc(sizeof(sendpacket_t)); strncpy(sp->device, full_dev_name, sizeof(sp->device)); sp->handle.fd = fd; sp->qtx_dev = dev; sp->handle_type = SP_TYPE_QUICK_TX; return sp; map_alloc_failed: for (i = (dev->data->num_mem_blocks - 1); i >= 0; i--) { struct quick_tx_mem_block_entry* mem_block = &dev->data->mem_blocks[i]; if (munmap (mem_block->user_addr, mem_block->length) == -1) { dbgx(1, "[quick_tx] error while calling munmap for block %d\n", i); } } munmap ((void*)dev->data, dev->map_length); map_failed: free(dev); map_create_failed: close(fd); open_failed: ifacename_invalid: return NULL; }
/** * ioctl support for netmap */ static int nm_do_ioctl (sendpacket_t *sp, u_long what, int subcmd) { struct ifreq ifr; int error; int fd; #ifdef linux struct ethtool_value eval; #endif assert(sp); fd = socket (AF_INET, SOCK_DGRAM, 0); if (fd < 0) { dbg(1, "ioctl error: cannot get device control socket.\n"); return -1; } bzero (&ifr, sizeof(ifr)); strncpy (ifr.ifr_name, sp->device, sizeof(ifr.ifr_name)); switch (what) { case SIOCSIFFLAGS: ifr.ifr_flags = sp->if_flags >> 16; ifr.ifr_flags = sp->if_flags & 0xffff; break; #ifdef linux case SIOCETHTOOL: eval.cmd = subcmd; eval.data = sp->data; ifr.ifr_data = (caddr_t)&eval; dbgx(1, "ioctl SIOCETHTOOL subcmd=%d data=%u", subcmd, eval.data); break; #endif } error = ioctl (fd, what, &ifr); if (error) goto done; switch (what) { case SIOCGIFFLAGS: sp->if_flags = (ifr.ifr_flags << 16) | (0xffff & ifr.ifr_flags); dbgx(1, "SIOCGIFFLAGS flags are 0x%x", sp->if_flags); break; #ifdef linux case SIOCETHTOOL: switch (subcmd) { case ETHTOOL_GGSO: sp->gso = eval.data; dbgx(1, "ioctl SIOCETHTOOL ETHTOOL_GGSO=%u", eval.data); break; case ETHTOOL_GTSO: sp->tso = eval.data; dbgx(1, "ioctl SIOCETHTOOL ETHTOOL_GTSO=%u", eval.data); break; case ETHTOOL_GRXCSUM: sp->rxcsum = eval.data; dbgx(1, "ioctl SIOCETHTOOL ETHTOOL_GRXCSUM=%u", eval.data); break; case ETHTOOL_GTXCSUM: sp->txcsum = eval.data; dbgx(1, "ioctl SIOCETHTOOL ETHTOOL_GTXCSUM=%u", eval.data); break; } break; #endif } done: close (fd); if (error) warnx("ioctl error %d %lu:%d", error, what, subcmd); return error; }
int sendpacket_send_netmap(void *p, const u_char *data, size_t len) { sendpacket_t *sp = p; struct netmap_ring *txring; struct netmap_slot *slot; char *pkt; uint32_t cur, avail; if (sp->abort) return 0; txring = NETMAP_TXRING(sp->nm_if, sp->cur_tx_ring); while ((avail = nm_ring_space(txring)) == 0) { /* out of space on current TX queue - go to next */ ++sp->cur_tx_ring; if (sp->cur_tx_ring > sp->last_tx_ring) { /* * out of space on all queues * * we have looped through all configured TX queues * so we have to reset to the first queue and * wait for available space */ sp->cur_tx_ring = sp->first_tx_ring; /* send TX interrupt signal * * On Linux this makes one slot free on the * ring, which increases speed by about 10Mbps. * * But it will never free up all the slots. For * that we must poll and call again. */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* loop again */ return -2; } txring = NETMAP_TXRING(sp->nm_if, sp->cur_tx_ring); } /* * send */ cur = txring->cur; slot = &txring->slot[cur]; slot->flags = 0; pkt = NETMAP_BUF(txring, slot->buf_idx); memcpy(pkt, data, min(len, txring->nr_buf_size)); slot->len = len; if (avail <= 1) slot->flags = NS_REPORT; dbgx(3, "netmap cur=%d slot index=%d flags=0x%x empty=%d avail=%u bufsize=%d\n", cur, slot->buf_idx, slot->flags, NETMAP_TX_RING_EMPTY(txring), nm_ring_space(txring), txring->nr_buf_size); /* let kernel know that packet is available */ cur = NETMAP_RING_NEXT(txring, cur); #ifdef HAVE_NETMAP_RING_HEAD_TAIL txring->head = cur; #else txring->avail--; #endif txring->cur = cur; return len; }
/** * Inner sendpacket_open() method for using netmap */ void * sendpacket_open_netmap(const char *device, char *errbuf, void *arg) { tcpreplay_t *ctx = (tcpreplay_t*)arg; sendpacket_t *sp = NULL; nmreq_t nmr; char ifname_buf[MAX_IFNAMELEN]; const char *ifname; const char *port = NULL; size_t namelen; u_int32_t nr_ringid = 0; u_int32_t nr_flags = NR_REG_DEFAULT; int is_default = 0; assert(device); assert(errbuf); dbg(1, "sendpacket_open_netmap: using netmap"); bzero(&nmr, sizeof(nmr)); /* prep & return our sp handle */ sp = (sendpacket_t *)safe_malloc(sizeof(sendpacket_t)); if (strlen(device) > MAX_IFNAMELEN - 8) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Interface name is to long: %s\n", device); goto IFACENAME_INVALID; } /* get the version of the netmap driver. If < 0, driver is not installed */ sp->netmap_version = get_netmap_version(); if (sp->netmap_version < 0) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Unable to determine the running netmap version.\n" "See INSTALL document for details on installing or upgrading netmap."); goto NETMAP_NOT_INSTALLED; } /* * Sort out interface names * * ifname (foo, netmap:foo or vale:foo) is the port name * foo bind to a single NIC hardware queue * netmap:foo bind to a single NIC hardware queue * vale:foo bind to the Vale virtual interface * * for netmap version 10+ a suffix can indicate the following: * netmap:foo! bind to all NIC hardware queues (may cause TX reordering) * netmap:foo^ bind to the host (sw) ring pair * netmap:foo* bind to the host (sw) and NIC ring pairs (transparent) * netmap:foo-NN bind to the individual NIC ring pair (queue) where NN = the ring number * netmap:foo{NN bind to the master side of pipe NN * netmap:foo}NN bind to the slave side of pipe NN */ if (strncmp(device, "netmap:", 7) && strncmp(device, "vale", 4)) { snprintf(ifname_buf, sizeof(ifname_buf), "netmap:%s", device); ifname = ifname_buf; } else { ifname = device; } if (!strncmp("vale", device, 4)) sp->is_vale = 1; if (ifname[0] == 'n') ifname += 7; /* scan for a separator */ for (port = ifname; *port && !index("!-*^{}", *port); port++) ; namelen = port - ifname; if (namelen > sizeof(nmr.nr_name)) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Interface name is to long: %s\n", device); goto IFACENAME_INVALID; } /* * Open the netmap device to fetch the number of queues of our * interface. * * The first NIOCREGIF also detaches the card from the * protocol stack and may cause a reset of the card, * which in turn may take some time for the PHY to * reconfigure. */ if ((sp->handle.fd = open("/dev/netmap", O_RDWR)) < 0) { dbg(1, "sendpacket_open_netmap: Unable to access netmap"); snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Unable to access netmap.\n" "See INSTALL to learn which NICs are supported and\n" "how to set up netmap-capable network drivers."); goto OPEN_FAILED; } /* * The nmreq structure must have the NETMAP_API version for the running machine. * However the binary may have been compiled on a different machine than the * running machine. Discover the true netmap API version, and be careful to call * fuctions that are available on all netmap versions. */ if (sp->netmap_version >= 10) { switch (*port) { case '-': /* one NIC */ nr_flags = NR_REG_ONE_NIC; nr_ringid = atoi(port + 1); break; case '*': /* NIC and SW, ignore port */ nr_flags = NR_REG_NIC_SW; if (port[1]) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "invalid netmap port for nic+sw"); goto NETMAP_IF_PARSE_FAIL; } break; case '^': /* only sw ring */ nr_flags = NR_REG_SW; if (port[1]) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "invalid port for sw ring"); goto NETMAP_IF_PARSE_FAIL; } break; case '{': nr_flags = NR_REG_PIPE_MASTER; nr_ringid = atoi(port + 1); break; case '}': nr_flags = NR_REG_PIPE_SLAVE; nr_ringid = atoi(port + 1); break; case '!': nr_flags = NR_REG_ALL_NIC; break; default: /* '\0', no suffix */ nr_flags = NR_REG_ALL_NIC; is_default = 1; break; } if (nr_ringid >= NETMAP_RING_MASK) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "invalid ringid"); goto NETMAP_IF_PARSE_FAIL; } nmr.nr_ringid = nr_ringid; nmr.nr_flags = nr_flags; } nmr.nr_version = sp->netmap_version; memcpy(nmr.nr_name, ifname, namelen); nmr.nr_name[namelen] = '\0'; strlcpy(sp->device, nmr.nr_name, sizeof(sp->device)); /* * Register the interface on the netmap device: from now on, * we can operate on the network interface without any * interference from the legacy network stack. * * Cards take a long time to reset the PHY. */ fprintf(stderr, "Switching network driver for %s to netmap bypass mode... ", sp->device); fflush(NULL); sleep(1); /* ensure message prints when user is connected via ssh */ if (ioctl (sp->handle.fd, NIOCREGIF, &nmr)) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Failure accessing netmap.\n" "\tRequest for netmap version %u failed.\n\tCompiled netmap driver is version %u.\n\tError=%s\n", sp->netmap_version, NETMAP_API, strerror(errno)); goto NETMAP_IF_FAILED; } if (!nmr.nr_memsize) { snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Netmap interface '%s' not configured.\n", device); goto NETMAP_IF_FAILED; } sp->mmap_size = nmr.nr_memsize; sp->mmap_addr = (struct netmap_d *)mmap (0, sp->mmap_size, PROT_WRITE | PROT_READ, MAP_SHARED, sp->handle.fd, 0); if (!sp->mmap_addr || sp->mmap_addr == MAP_FAILED) { snprintf (errbuf, SENDPACKET_ERRBUF_SIZE, "mmap: %s", strerror (errno)); goto MMAP_FAILED; } dbgx(1, "sendpacket_open_netmap: mapping %d Kbytes queues=%d", sp->mmap_size >> 10, nmr.nr_tx_rings); sp->nm_if = NETMAP_IF(sp->mmap_addr, nmr.nr_offset); sp->nmr = nmr; sp->handle_type = SP_TYPE_NETMAP; /* set up ring IDs */ sp->cur_tx_ring = 0; switch(nr_flags) { case NR_REG_DEFAULT: /* only use one queue to prevent TX reordering */ sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = 0; break; case NR_REG_ALL_NIC: if (is_default) { sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = 0; } else { sp->first_tx_ring = sp->cur_tx_ring = 0; sp->last_tx_ring = nmr.nr_tx_rings - 1; } break; case NR_REG_SW: sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = nmr.nr_tx_rings; break; case NR_REG_NIC_SW: sp->first_tx_ring = sp->cur_tx_ring = 0; sp->last_tx_ring = nmr.nr_tx_rings; break; case NR_REG_ONE_NIC: sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = nr_ringid; break; default: sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = 0; } { /* debugging code */ int i; dbgx(1, "%s tx first=%d last=%d num=%d", ifname, sp->first_tx_ring, sp->last_tx_ring, sp->nmr.nr_tx_rings); for (i = 0; i <= sp->nmr.nr_tx_rings; i++) { #ifdef HAVE_NETMAP_RING_HEAD_TAIL dbgx(1, "TX%d 0x%p head=%d cur=%d tail=%d", i, NETMAP_TXRING(sp->nm_if, i), (NETMAP_TXRING(sp->nm_if, i))->head, (NETMAP_TXRING(sp->nm_if, i))->cur, (NETMAP_TXRING(sp->nm_if, i))->tail); #else dbgx(1, "TX%d 0x%p cur=%d avail=%d", i, NETMAP_TXRING(sp->nm_if, i), (NETMAP_TXRING(sp->nm_if, i))->cur, (NETMAP_TXRING(sp->nm_if, i))->avail); #endif } } dbgx(2, "Waiting %d seconds for phy reset...", ctx->options->netmap_delay); sleep(ctx->options->netmap_delay); dbg(2, "Ready!"); if (!sp->is_vale) { if (nm_do_ioctl(sp, SIOCGIFFLAGS, 0) < 0) goto NM_DO_IOCTL_FAILED; if ((sp->if_flags & IFF_RUNNING) == 0) { dbgx(1, "sendpacket_open_netmap: %s is not running", sp->device); snprintf (errbuf, SENDPACKET_ERRBUF_SIZE, "interface %s is not running - check cables\n", sp->device); goto NETMAP_IF_NOT_RUNNING; } if ((sp->if_flags & IFF_UP) == 0) { dbgx(1, "%s is down, bringing up...", sp->device); sp->if_flags |= IFF_UP; } /* set promiscuous mode */ sp->if_flags |= IFF_PROMISC; if (nm_do_ioctl(sp, SIOCSIFFLAGS, 0) < 0) goto NM_DO_IOCTL_FAILED; #ifdef linux /* disable: * - generic-segmentation-offload * - tcp-segmentation-offload * - rx-checksumming * - tx-checksumming */ if (nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_GGSO) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_GTSO) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_GRXCSUM) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_GTXCSUM) < 0) goto NM_DO_IOCTL_FAILED; sp->data = 0; if (nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_SGSO) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_STSO) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_SRXCSUM) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_STXCSUM)) goto NM_DO_IOCTL_FAILED; #endif } if(sp->abort) goto NETMAP_ABORT; notice("done!"); return sp; NM_DO_IOCTL_FAILED: snprintf (errbuf, SENDPACKET_ERRBUF_SIZE, "nm_do_ioctl: %s", strerror (errno)); NETMAP_IF_NOT_RUNNING: notice("failed!"); NETMAP_ABORT: fprintf(stderr, " Switching network driver for %s to normal mode... ", sp->device); fflush(NULL); munmap(sp->mmap_addr, sp->mmap_size); MMAP_FAILED: #if NETMAP_API < 10 ioctl(sp->handle.fd, NIOCUNREGIF, NULL); #endif NETMAP_IF_FAILED: NETMAP_IF_PARSE_FAIL: close (sp->handle.fd); OPEN_FAILED: safe_free(sp); IFACENAME_INVALID: NETMAP_NOT_INSTALLED: return NULL; }
/** * returns the include_exclude_mode on success placing the CIDR or LIST in mybuf * but on failure, returns xXError */ int parse_xX_str(tcpr_xX_t *xX, char *str, tcpr_bpf_t *bpf) { int out = 0; dbgx(1, "Parsing string: %s", str); dbgx(1, "Switching on: %c", str[0]); switch (str[0]) { case 'B': /* both ip's */ str = str + 2; out = xXBoth; if (!parse_cidr(&(xX->cidr), str, ",")) return xXError; break; case 'D': /* dst ip */ str = str + 2; out = xXDest; if (!parse_cidr(&(xX->cidr), str, ",")) return xXError; break; case 'E': /* either ip */ str = str + 2; out = xXEither; if (!parse_cidr(&(xX->cidr), str, ",")) return xXError; break; case 'F': /* bpf filter */ str = str + 2; out = xXBPF; bpf->filter = safe_strdup(str); /* * note: it's temping to compile the BPF here, but we don't * yet know what the link type is for the file, so we have * to compile the BPF once we open the pcap file */ break; case 'P': /* packet id */ str = str + 2; out = xXPacket; if (!parse_list(&(xX->list), str)) return xXError; break; case 'S': /* source ip */ str = str + 2; out = xXSource; if (!parse_cidr(&(xX->cidr), str, ",")) return xXError; break; default: errx(-1, "Invalid -%c option: %c", xX->mode, *str); break; } if (xX->mode == 'X') { /* run in exclude mode */ out += xXExclude; if (bpf->filter != NULL) err(-1, "Using a BPF filter with -X doesn't work.\n" "Try using -xF:\"not <filter>\" instead"); } xX->mode = out; return xX->mode; }
int main(int argc, char *argv[]) { int optct, rcode; pcap_t *dlt_pcap; #ifdef ENABLE_FRAGROUTE char ebuf[FRAGROUTE_ERRBUF_LEN]; #endif tcprewrite_init(); /* call autoopts to process arguments */ optct = optionProcess(&tcprewriteOptions, argc, argv); argc -= optct; argv += optct; /* parse the tcprewrite args */ post_args(argc, argv); /* init tcpedit context */ if (tcpedit_init(&tcpedit, pcap_datalink(options.pin)) < 0) { errx(-1, "Error initializing tcpedit: %s", tcpedit_geterr(tcpedit)); } /* parse the tcpedit args */ rcode = tcpedit_post_args(&tcpedit); if (rcode < 0) { errx(-1, "Unable to parse args: %s", tcpedit_geterr(tcpedit)); } else if (rcode == 1) { warnx("%s", tcpedit_geterr(tcpedit)); } if (tcpedit_validate(tcpedit) < 0) { errx(-1, "Unable to edit packets given options:\n%s", tcpedit_geterr(tcpedit)); } /* open up the output file */ options.outfile = safe_strdup(OPT_ARG(OUTFILE)); dbgx(1, "Rewriting DLT to %s", pcap_datalink_val_to_name(tcpedit_get_output_dlt(tcpedit))); if ((dlt_pcap = pcap_open_dead(tcpedit_get_output_dlt(tcpedit), 65535)) == NULL) err(-1, "Unable to open dead pcap handle."); dbgx(1, "DLT of dlt_pcap is %s", pcap_datalink_val_to_name(pcap_datalink(dlt_pcap))); #ifdef ENABLE_FRAGROUTE if (options.fragroute_args) { if ((options.frag_ctx = fragroute_init(65535, pcap_datalink(dlt_pcap), options.fragroute_args, ebuf)) == NULL) errx(-1, "%s", ebuf); } #endif #ifdef ENABLE_VERBOSE if (options.verbose) { tcpdump_open(&tcpdump, dlt_pcap); } #endif if ((options.pout = pcap_dump_open(dlt_pcap, options.outfile)) == NULL) errx(-1, "Unable to open output pcap file: %s", pcap_geterr(dlt_pcap)); pcap_close(dlt_pcap); /* rewrite packets */ if (rewrite_packets(tcpedit, options.pin, options.pout) != 0) errx(-1, "Error rewriting packets: %s", tcpedit_geterr(tcpedit)); /* clean up after ourselves */ pcap_dump_close(options.pout); pcap_close(options.pin); #ifdef ENABLE_VERBOSE tcpdump_close(&tcpdump); #endif #ifdef ENABLE_DMALLOC dmalloc_shutdown(); #endif return 0; }
/** * Main loop to rewrite packets */ int rewrite_packets(tcpedit_t *tcpedit, pcap_t *pin, pcap_dumper_t *pout) { tcpr_dir_t cache_result = TCPR_DIR_C2S; /* default to primary */ struct pcap_pkthdr pkthdr, *pkthdr_ptr; /* packet header */ const u_char *pktconst = NULL; /* packet from libpcap */ u_char **pktdata = NULL; static u_char *pktdata_buff; static char *frag = NULL; COUNTER packetnum = 0; int rcode, frag_len, i, proto; pkthdr_ptr = &pkthdr; if (pktdata_buff == NULL) pktdata_buff = (u_char *)safe_malloc(MAXPACKET); pktdata = &pktdata_buff; if (frag == NULL) frag = (char *)safe_malloc(MAXPACKET); /* MAIN LOOP * Keep sending while we have packets or until * we've sent enough packets */ while ((pktconst = pcap_next(pin, pkthdr_ptr)) != NULL) { packetnum++; dbgx(2, "packet " COUNTER_SPEC " caplen %d", packetnum, pkthdr.caplen); /* * copy over the packet so we can pad it out if necessary and * because pcap_next() returns a const ptr */ memcpy(*pktdata, pktconst, pkthdr.caplen); #ifdef ENABLE_VERBOSE if (options.verbose) tcpdump_print(&tcpdump, pkthdr_ptr, *pktdata); #endif /* Dual nic processing? */ if (options.cachedata != NULL) { cache_result = check_cache(options.cachedata, packetnum); } /* sometimes we should not send the packet, in such cases * no point in editing this packet at all, just write it to the * output file (note, we can't just remove it, or the tcpprep cache * file will loose it's indexing */ if (cache_result == TCPR_DIR_NOSEND) goto WRITE_PACKET; /* still need to write it so cache stays in sync */ if ((rcode = tcpedit_packet(tcpedit, &pkthdr_ptr, pktdata, cache_result)) == TCPEDIT_ERROR) { return -1; } else if ((rcode == TCPEDIT_SOFT_ERROR) && HAVE_OPT(SKIP_SOFT_ERRORS)) { /* don't write packet */ dbgx(1, "Packet " COUNTER_SPEC " is suppressed from being written due to soft errors", packetnum); continue; } WRITE_PACKET: #ifdef ENABLE_FRAGROUTE if (options.frag_ctx == NULL) { /* write the packet when there's no fragrouting to be done */ pcap_dump((u_char *)pout, pkthdr_ptr, *pktdata); } else { /* get the L3 protocol of the packet */ proto = tcpedit_l3proto(tcpedit, AFTER_PROCESS, *pktdata, pkthdr_ptr->caplen); /* packet is IPv4/IPv6 AND needs to be fragmented */ if ((proto == ETHERTYPE_IP || proto == ETHERTYPE_IP6) && ((options.fragroute_dir == FRAGROUTE_DIR_BOTH) || (cache_result == TCPR_DIR_C2S && options.fragroute_dir == FRAGROUTE_DIR_C2S) || (cache_result == TCPR_DIR_S2C && options.fragroute_dir == FRAGROUTE_DIR_S2C))) { if (fragroute_process(options.frag_ctx, *pktdata, pkthdr_ptr->caplen) < 0) errx(-1, "Error processing packet via fragroute: %s", options.frag_ctx->errbuf); i = 0; while ((frag_len = fragroute_getfragment(options.frag_ctx, &frag)) > 0) { /* frags get the same timestamp as the original packet */ dbgx(1, "processing packet " COUNTER_SPEC " frag: %u (%d)", packetnum, i++, frag_len); pkthdr_ptr->caplen = frag_len; pkthdr_ptr->len = frag_len; pcap_dump((u_char *)pout, pkthdr_ptr, (u_char *)frag); } } else { /* write the packet without fragroute */ pcap_dump((u_char *)pout, pkthdr_ptr, *pktdata); } } #else /* write the packet when there's no fragrouting to be done */ pcap_dump((u_char *)pout, pkthdr_ptr, *pktdata); #endif } /* while() */ return 0; }
/** * Get all available interfaces as an interface_list * */ interface_list_t * get_interface_list(void) { interface_list_t *list_head, *list_ptr; char ebuf[PCAP_ERRBUF_SIZE], testnic[255]; pcap_if_t *pcap_if, *pcap_if_ptr; int i = 0; DIR *dir; struct dirent *dirdata; #ifdef HAVE_QUICK_TX char buf[MAXNAMLEN]; #endif #ifdef HAVE_NETMAP int fd = -1; nmreq_t nmr; #endif #if defined HAVE_LIBPCAP_NETMAP || defined HAVE_NETMAP u_int32_t netmap_version = -1; #endif #ifndef HAVE_WIN32 /* Unix just has a warning about being root */ if (geteuid() != 0) warn("May need to run as root to get access to all network interfaces."); #endif #ifdef HAVE_NETMAP netmap_version = get_netmap_version(); #endif if (pcap_findalldevs(&pcap_if, ebuf) < 0) errx(-1, "Error: %s", ebuf); pcap_if_ptr = pcap_if; list_head = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_head; while (pcap_if_ptr != NULL) { if (i > 0) { list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; } strlcpy(list_ptr->name, pcap_if_ptr->name, sizeof(list_ptr->name)); dbgx(3, "Adding %s to interface list", list_ptr->name); /* description is usually null under Unix */ if (pcap_if_ptr->description != NULL) strlcpy(list_ptr->description, pcap_if_ptr->description, sizeof(list_ptr->description)); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; #ifdef HAVE_QUICK_TX snprintf(buf, sizeof(buf), "%s%s", QTX_FULL_PATH_PREFIX, pcap_if_ptr->name); if (!access(buf, R_OK | W_OK)) { list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; snprintf(list_ptr->name, sizeof(list_ptr->name), "qtx:%s", pcap_if_ptr->name); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; } #endif #ifdef HAVE_LIBPCAP_NETMAP /* * add the syntaxes supported by netmap-libpcap * * available at http://code.google.com/p/netmap-libpcap/ */ if (!(pcap_if_ptr->flags & PCAP_IF_LOOPBACK) && strcmp("any", pcap_if_ptr->name)) { #endif #ifdef HAVE_NETMAP if (netmap_version != -1 && (fd = open ("/dev/netmap", O_RDWR)) < 0) continue; bzero(&nmr, sizeof(nmr)); strncpy(nmr.nr_name, pcap_if_ptr->name, sizeof(nmr.nr_name)); nmr.nr_version = netmap_version; if (ioctl(fd, NIOCGINFO, &nmr) == 0) { int x; #endif /* HAVE_NETMAP */ #if defined HAVE_LIBPCAP_NETMAP || defined HAVE_NETMAP list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; snprintf(list_ptr->name, sizeof(list_ptr->name), "vale:%s", pcap_if_ptr->name); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; snprintf(list_ptr->name, sizeof(list_ptr->name), "netmap:%s", pcap_if_ptr->name); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; if (netmap_version >= 10) { list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; snprintf(list_ptr->name, sizeof(list_ptr->name), "netmap:%s!", pcap_if_ptr->name); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; snprintf(list_ptr->name, sizeof(list_ptr->name), "netmap:%s*", pcap_if_ptr->name); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; snprintf(list_ptr->name, sizeof(list_ptr->name), "netmap:%s^", pcap_if_ptr->name); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; } #endif /* HAVE_LIBPCAP_NETMAP || HAVE_NETMAP */ #ifdef HAVE_NETMAP if (netmap_version >= 10) { for (x = 0; x < nmr.nr_rx_rings; ++x) { list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; snprintf(list_ptr->name, sizeof(list_ptr->name), "netmap:%s-%d", pcap_if_ptr->name, x); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; } } } close(fd); #endif /* HAVE_NETMAP */ #ifdef HAVE_LIBPCAP_NETMAP } #endif /* HAVE_LIBPCAP_NETMAP */ #ifdef HAVE_PF_RING_PCAP list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; snprintf(list_ptr->name, sizeof(list_ptr->name), "zc:%s", pcap_if_ptr->name); sprintf(list_ptr->alias, "%%%d", i++); list_ptr->flags = pcap_if_ptr->flags; #endif pcap_if_ptr = pcap_if_ptr->next; } pcap_freealldevs(pcap_if); /* look for khial device: https://github.com/boundary/khial */ if ((dir = opendir("/dev/char")) != NULL) { while ((dirdata = readdir(dir)) != NULL) { if (strncmp(dirdata->d_name, "testpackets", strlen("testpackets")) == 0) { if (i > 0) { list_ptr->next = (interface_list_t *)safe_malloc(sizeof(interface_list_t)); list_ptr = list_ptr->next; } dbgx(3, "Adding %s to interface list", dirdata->d_name); snprintf(testnic, 254, "/dev/char/%s", dirdata->d_name); strlcpy(list_ptr->name, testnic, 255); snprintf(testnic, 255, "khial pseudo-nic: %s", dirdata->d_name); strlcpy(list_ptr->description, testnic, 255); strlcpy(list_ptr->alias, dirdata->d_name, 255); i += 1; } } } dbg(1, "xxx get_interface_list end"); return(list_head); }
int sendpacket_send_netmap(void *p, const u_char *data, size_t len) { int retcode = 0; sendpacket_t *sp = p; struct netmap_ring *txring; struct netmap_slot *slot; char *pkt; uint32_t cur, avail; if (sp->abort) return retcode; txring = NETMAP_TXRING(sp->nm_if, sp->cur_tx_ring); while ((avail = nm_ring_space(txring)) == 0) { /* out of space on current TX queue - go to next */ ++sp->cur_tx_ring; if (sp->cur_tx_ring > sp->last_tx_ring) { /* * out of space on all queues * * we have looped through all configured TX queues * so we have to reset to the first queue and * wait for available space */ struct pollfd pfd; sp->cur_tx_ring = sp->first_tx_ring; /* send TX interrupt signal * * On Linux this makes one slot free on the * ring, which increases speed by about 10Mbps. * * But it will never free up all the slots. For * that we must poll and call again. */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); pfd.fd = sp->handle.fd; pfd.events = POLLOUT; pfd.revents = 0; if (poll(&pfd, 1, 1000) <= 0) { if (++sp->tx_timeouts == NETMAP_TX_TIMEOUT_SEC) { return -1; } return -2; } sp->tx_timeouts = 0; /* * Do not remove this even though it looks redundant. * Overall performance is increased with this restart * of the TX queue. * * This call increases the number of available slots from * 1 to all that are truly available. */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); } txring = NETMAP_TXRING(sp->nm_if, sp->cur_tx_ring); } /* * send */ cur = txring->cur; slot = &txring->slot[cur]; slot->flags = 0; pkt = NETMAP_BUF(txring, slot->buf_idx); memcpy(pkt, data, min(len, txring->nr_buf_size)); slot->len = len; if (avail <= 1) slot->flags = NS_REPORT; dbgx(3, "netmap cur=%d slot index=%d flags=0x%x empty=%d avail=%u bufsize=%d\n", cur, slot->buf_idx, slot->flags, NETMAP_TX_RING_EMPTY(txring), nm_ring_space(txring), txring->nr_buf_size); /* let kernel know that packet is available */ cur = NETMAP_RING_NEXT(txring, cur); #ifdef HAVE_NETMAP_RING_HEAD_TAIL txring->head = cur; #else txring->avail--; #endif txring->cur = cur; retcode = len; return retcode; }