/* Allocate and initialize a VI. */ static int init(const char* intf, int vi_i) { struct vi* vi = &vis[vi_i]; int i; unsigned vi_flags = EF_VI_FLAGS_DEFAULT; TRY(ef_driver_open(&vi->dh)); /* check that RX merge is supported */ if( cfg_rx_merge ) { unsigned long value; int ifindex = if_nametoindex(intf); TEST(ifindex > 0); int rc = ef_vi_capabilities_get(vi->dh, ifindex, EF_VI_CAP_RX_MERGE, &value); if( rc < 0 || ! value ) { fprintf(stderr, "WARNING: RX merge not supported on %s. Use '-c' " "option instead.\n", intf); exit(EXIT_FAILURE); } else { vi_flags |= EF_VI_RX_EVENT_MERGE; } } TRY(ef_pd_alloc_by_name(&vi->pd, vi->dh, intf, EF_PD_DEFAULT)); TRY(ef_vi_alloc_from_pd(&vi->vi, vi->dh, &vi->pd, vi->dh, -1, RX_RING_SIZE, TX_RING_SIZE, NULL, -1, vi_flags)); /* Memory for pkt buffers has already been allocated. Map it into * the VI. */ TRY(ef_memreg_alloc(&vi->memreg, vi->dh, &vi->pd, vi->dh, pbs.mem, pbs.mem_size)); for( i = 0; i < pbs.num; ++i ) { struct pkt_buf* pkt_buf = pkt_buf_from_id(i); pkt_buf->rx_ef_addr[vi_i] = ef_memreg_dma_addr(&vi->memreg, i * PKT_BUF_SIZE) + RX_DMA_OFF + addr_offset_from_id(i); pkt_buf->tx_ef_addr[vi_i] = ef_memreg_dma_addr(&vi->memreg, i * PKT_BUF_SIZE) + RX_DMA_OFF + ef_vi_receive_prefix_len(&vi->vi) + addr_offset_from_id(i); } /* Our pkt buffer allocation function makes assumptions on queue sizes */ assert(ef_vi_receive_capacity(&vi->vi) == RX_RING_SIZE - 1); assert(ef_vi_transmit_capacity(&vi->vi) == TX_RING_SIZE - 1); if( cfg_unidirectional && vi_i == 1 ) return 0; /* only need filter and RX fill for ingress VI */ while( ef_vi_receive_space(&vi->vi) > REFILL_BATCH_SIZE ) vi_refill_rx_ring(vi_i); ef_filter_spec fs; ef_filter_spec_init(&fs, EF_FILTER_FLAG_NONE); TRY(ef_filter_spec_set_unicast_all(&fs)); TRY(ef_vi_filter_add(&vi->vi, vi->dh, &fs, NULL)); ef_filter_spec_init(&fs, EF_FILTER_FLAG_NONE); TRY(ef_filter_spec_set_multicast_all(&fs)); TRY(ef_vi_filter_add(&vi->vi, vi->dh, &fs, NULL)); return 0; }
static int install_filters(void) { ef_filter_spec fs; ef_filter_spec_init(&fs, EF_FILTER_FLAG_NONE); TRY(ef_filter_spec_set_unicast_all(&fs)); TRY(ef_vi_set_filter_add(&vi_set, dh, &fs, NULL)); ef_filter_spec_init(&fs, cfg_loopback ? EF_FILTER_FLAG_MCAST_LOOP_RECEIVE : EF_FILTER_FLAG_NONE); TRY(ef_filter_spec_set_multicast_all(&fs)); TRY(ef_vi_set_filter_add(&vi_set, dh, &fs, NULL)); return 0; }
static int sc_stream_add(struct sc_stream* s, void* vi_or_set, ef_driver_handle dh, ef_filter_cookie *cookie_out) { ef_filter_spec spec; ef_filter_spec_init(&spec, EF_FILTER_FLAG_NONE); switch( s->fields ) { case SC_SF_ALL: TRY(ef_filter_spec_set_unicast_all(&spec)); TRY(ef_vi_set_filter_add(vi_or_set, dh, &spec, cookie_out)); ef_filter_spec_init(&spec, EF_FILTER_FLAG_NONE); TRY(ef_filter_spec_set_multicast_all(&spec)); TRY(ef_vi_set_filter_add(vi_or_set, dh, &spec, cookie_out)); break; case SC_SF_SNIFF: TRY(ef_filter_spec_set_port_sniff(&spec, s->promiscuous)); TRY(ef_vi_set_filter_add(vi_or_set, dh, &spec, cookie_out)); break; case SC_SF_ETH_DHOST: TRY(ef_filter_spec_set_eth_local(&spec, EF_FILTER_VLAN_ID_ANY, s->eth_dhost)); TRY(ef_vi_set_filter_add(vi_or_set, dh, &spec, cookie_out)); break; case SC_SF_ETH_DHOST | SC_SF_ETH_VLAN_ID: TRY(ef_filter_spec_set_eth_local(&spec, s->eth_vlan_id, s->eth_dhost)); TRY(ef_vi_set_filter_add(vi_or_set, dh, &spec, cookie_out)); break; case SC_SF_ETH_TYPE | SC_SF_IP4_PROTOCOL | SC_SF_IP4_DEST_ADDR | SC_SF_IP4_DEST_PORT: TRY(ef_filter_spec_set_ip4_local(&spec, s->ip4_protocol, s->ip4_dest_addr, s->ip4_dest_port)); TRY(ef_vi_set_filter_add(vi_or_set, dh, &spec, cookie_out)); break; case SC_SF_ETH_TYPE | SC_SF_IP4_PROTOCOL | SC_SF_IP4_DEST_ADDR | SC_SF_IP4_DEST_PORT | SC_SF_IP4_SOURCE_ADDR | SC_SF_IP4_SOURCE_PORT: TRY(ef_filter_spec_set_ip4_full(&spec, s->ip4_protocol, s->ip4_dest_addr, s->ip4_dest_port, s->ip4_source_addr, s->ip4_source_port)); TRY(ef_vi_set_filter_add(vi_or_set, dh, &spec, cookie_out)); break; default: fprintf(stderr, "ERROR: sc_vi[_set]_add_stream_string: " "unsupported combination of fields (0x%x)\n", s->fields); return -EINVAL; } return 0; }
/* Allocate and initialize a VI. */ static int init(const char* intf, int vi_i) { struct vi* vi = &vis[vi_i]; int i; TRY(ef_driver_open(&vi->dh)); TRY(ef_pd_alloc_by_name(&vi->pd, vi->dh, intf, EF_PD_DEFAULT)); TRY(ef_vi_alloc_from_pd(&vi->vi, vi->dh, &vi->pd, vi->dh, -1, -1, -1, NULL, -1, EF_VI_FLAGS_DEFAULT)); /* Memory for pkt buffers has already been allocated. Map it into * the VI. */ TRY(ef_memreg_alloc(&vi->memreg, vi->dh, &vi->pd, vi->dh, pbs.mem, pbs.mem_size)); for( i = 0; i < pbs.num; ++i ) { struct pkt_buf* pkt_buf = pkt_buf_from_id(i); pkt_buf->rx_ef_addr[vi_i] = ef_memreg_dma_addr(&vi->memreg, i * PKT_BUF_SIZE) + RX_DMA_OFF; pkt_buf->tx_ef_addr[vi_i] = ef_memreg_dma_addr(&vi->memreg, i * PKT_BUF_SIZE) + RX_DMA_OFF + ef_vi_receive_prefix_len(&vi->vi); pkt_buf->rx_ptr[vi_i] = (char*) pkt_buf + RX_DMA_OFF + ef_vi_receive_prefix_len(&vi->vi); } /* Our pkt buffer allocation function makes assumptions on queue sizes */ assert(ef_vi_receive_capacity(&vi->vi) == 511); assert(ef_vi_transmit_capacity(&vi->vi) == 511); while( ef_vi_receive_space(&vi->vi) > REFILL_BATCH_SIZE ) vi_refill_rx_ring(vi_i); ef_filter_spec fs; ef_filter_spec_init(&fs, EF_FILTER_FLAG_NONE); TRY(ef_filter_spec_set_unicast_all(&fs)); TRY(ef_vi_filter_add(&vi->vi, vi->dh, &fs, NULL)); ef_filter_spec_init(&fs, EF_FILTER_FLAG_NONE); TRY(ef_filter_spec_set_multicast_all(&fs)); TRY(ef_vi_filter_add(&vi->vi, vi->dh, &fs, NULL)); return 0; }
int filter_parse(ef_filter_spec* fs, const char* s_in) { struct sockaddr_in lsin, rsin; const char* type; const char* hostport; char* vlan; char* remainder; char *s; int rc = -EINVAL; int protocol; int i; ef_filter_spec_init(fs, EF_FILTER_FLAG_NONE); s = strdup(s_in); if( (type = strtok(s, ":")) == NULL ) goto out; if( ! strcmp("udp", type) || ! strcmp("tcp", type) ) { protocol = strcasecmp(type, "tcp") ? IPPROTO_UDP : IPPROTO_TCP; remainder = strtok(NULL, ""); if( ! strncmp("mcastloop-rx,", remainder, strlen("mcastloop-rx,")) ) { ef_filter_spec_init(fs, EF_FILTER_FLAG_MCAST_LOOP_RECEIVE); strtok(remainder, ","); remainder = strtok(NULL, ""); } if( ! strncmp("vid=", remainder, strlen("vid=")) ) { vlan = strtok(remainder, ","); remainder = strtok(NULL, ""); if( ! vlan ) goto out; vlan = strchr(vlan, '='); ++vlan; TRY(ef_filter_spec_set_vlan(fs, atoi(vlan))); } if( strchr(remainder, ',') ) { hostport = strtok(remainder, ","); remainder = strtok(NULL, ""); TRY(hostport_parse(&lsin, hostport)); TRY(hostport_parse(&rsin, remainder)); TRY(ef_filter_spec_set_ip4_full(fs, protocol, lsin.sin_addr.s_addr, lsin.sin_port, rsin.sin_addr.s_addr, rsin.sin_port)); rc = 0; } else { TRY(hostport_parse(&lsin, strtok(remainder, ","))); TRY(ef_filter_spec_set_ip4_local(fs, protocol, lsin.sin_addr.s_addr, lsin.sin_port)); rc = 0; } } else if( ! strcmp("eth", type) ) { uint8_t mac[6]; int vlan_id = EF_FILTER_VLAN_ID_ANY; vlan = strtok(NULL, ","); remainder = strtok(NULL, ""); if( remainder == '\0' ) /* No vlan */ remainder = vlan; else { if( strncmp("vid=", vlan, strlen("vid=")) ) goto out; vlan = strchr(vlan, '='); ++vlan; vlan_id = atoi(vlan); } for( i = 0; i < 6; ++i ) { mac[i] = strtol(remainder, &remainder, 16); if( i != 5 ) { if( *remainder != ':' ) goto out; ++remainder; if( ! strlen(remainder) ) goto out; } } if( strlen(remainder) ) goto out; TRY(ef_filter_spec_set_eth_local(fs, vlan_id, mac)); rc = 0; } else if( ! strcmp("multicast-all", type) ) { if( strlen(type) != strlen(s_in) ) goto out; TRY(ef_filter_spec_set_multicast_all(fs)); rc = 0; } else if( ! strcmp("unicast-all", type) ) { if( strlen(type) != strlen(s_in) ) goto out; TRY(ef_filter_spec_set_unicast_all(fs)); rc = 0; } else if( ! strcmp("multicast-mis", type) ) { TRY(ef_filter_spec_set_multicast_mismatch(fs)); if( strlen(type) != strlen(s_in) ) { remainder = strtok(NULL, ""); if( ! (vlan = strchr(remainder, '=')) ) goto out; ++vlan; TRY(ef_filter_spec_set_vlan(fs, atoi(vlan))); } rc = 0; } else if( ! strcmp("unicast-mis", type) ) { TRY(ef_filter_spec_set_unicast_mismatch(fs)); if( strlen(type) != strlen(s_in) ) { remainder = strtok(NULL, ""); if( ! (vlan = strchr(remainder, '=')) ) goto out; ++vlan; TRY(ef_filter_spec_set_vlan(fs, atoi(vlan))); } rc = 0; } else if( ! strcmp("sniff", type) ) { if( strlen(type) == strlen(s_in) ) { TRY(ef_filter_spec_set_port_sniff(fs, 1)); } else { remainder = strtok(NULL, ""); if( ! strcmp("promisc", remainder) ) TRY(ef_filter_spec_set_port_sniff(fs, 1)); else if( ! strcmp("no-promisc", remainder) ) TRY(ef_filter_spec_set_port_sniff(fs, 0)); else TRY(-EINVAL); } rc = 0; } else if( ! strcmp("tx-sniff", type) ) { TRY(ef_filter_spec_set_tx_port_sniff(fs)); rc = 0; } else if( ! strcmp("block-kernel", type) ) { TRY(ef_filter_spec_set_block_kernel(fs)); rc = 0; } else if( ! strcmp("block-kernel-unicast", type) ) { TRY(ef_filter_spec_set_block_kernel_unicast(fs)); rc = 0; } else if( ! strcmp("block-kernel-multicast", type) ) { TRY(ef_filter_spec_set_block_kernel_multicast(fs)); rc = 0; } out: free(s); return rc; }