netmap: redo config parsing

Normally we parse the config per interface only. But to properly
setup the bridge, netmap also needs the config of it's peering
interface. Instead of using a complicated peering scheme like in
afpacket, simply parse the peers config too.
pull/2160/head
Victor Julien 10 years ago
parent 0e9134930d
commit 6c7bf006b7

@ -93,183 +93,178 @@ static void NetmapDerefConfig(void *conf)
} }
} }
/** static int ParseNetmapSettings(NetmapIfaceSettings *ns, const char *iface,
* \brief extract information from config file ConfNode *if_root, ConfNode *if_default)
*
* The returned structure will be freed by the thread init function.
* This is thus necessary to or copy the structure before giving it
* to thread or to reparse the file for each thread (and thus have
* new structure.
*
* \return a NetmapIfaceConfig corresponding to the interface name
*/
static void *ParseNetmapConfig(const char *iface_name)
{ {
char *threadsstr = NULL; ns->threads = 0;
ConfNode *if_root; ns->promisc = 1;
ConfNode *if_default = NULL; ns->checksum_mode = CHECKSUM_VALIDATION_AUTO;
ConfNode *netmap_node; ns->copy_mode = NETMAP_COPY_MODE_NONE;
char *tmpctype;
char *copymodestr; strlcpy(ns->iface, iface, sizeof(ns->iface));
int boolval; if (ns->iface[0]) {
char *bpf_filter = NULL; size_t len = strlen(ns->iface);
char *out_iface = NULL; if (ns->iface[len-1] == '+') {
ns->iface[len-1] = '\0';
if (iface_name == NULL) { ns->sw_ring = 1;
return NULL;
}
NetmapIfaceConfig *aconf = SCMalloc(sizeof(*aconf));
if (unlikely(aconf == NULL)) {
return NULL;
}
memset(aconf, 0, sizeof(*aconf));
aconf->DerefFunc = NetmapDerefConfig;
aconf->threads = 0;
aconf->promisc = 1;
aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
aconf->copy_mode = NETMAP_COPY_MODE_NONE;
strlcpy(aconf->iface_name, iface_name, sizeof(aconf->iface_name));
SC_ATOMIC_INIT(aconf->ref);
(void) SC_ATOMIC_ADD(aconf->ref, 1);
strlcpy(aconf->iface, aconf->iface_name, sizeof(aconf->iface));
if (aconf->iface[0]) {
size_t len = strlen(aconf->iface);
if (aconf->iface[len-1] == '+') {
aconf->iface[len-1] = '\0';
aconf->iface_sw = 1;
} }
} }
char *bpf_filter = NULL;
if (ConfGet("bpf-filter", &bpf_filter) == 1) { if (ConfGet("bpf-filter", &bpf_filter) == 1) {
if (strlen(bpf_filter) > 0) { if (strlen(bpf_filter) > 0) {
aconf->bpf_filter = bpf_filter; ns->bpf_filter = bpf_filter;
SCLogInfo("Going to use command-line provided bpf filter '%s'", SCLogInfo("Going to use command-line provided bpf filter '%s'",
aconf->bpf_filter); ns->bpf_filter);
} }
} }
/* Find initial node */
netmap_node = ConfGetNode("netmap");
if (netmap_node == NULL) {
SCLogInfo("Unable to find netmap config using default value");
goto finalize;
}
if_root = ConfFindDeviceConfig(netmap_node, aconf->iface_name);
if_default = ConfFindDeviceConfig(netmap_node, "default");
if (if_root == NULL && if_default == NULL) { if (if_root == NULL && if_default == NULL) {
SCLogInfo("Unable to find netmap config for " SCLogInfo("Unable to find netmap config for "
"interface \"%s\" or \"default\", using default value", "interface \"%s\" or \"default\", using default values",
aconf->iface_name); iface);
goto finalize; goto finalize;
}
/* If there is no setting for current interface use default one as main iface */ /* If there is no setting for current interface use default one as main iface */
if (if_root == NULL) { } else if (if_root == NULL) {
if_root = if_default; if_root = if_default;
if_default = NULL; if_default = NULL;
} }
char *threadsstr = NULL;
if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) { if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
aconf->threads = 0; ns->threads = 0;
} else { } else {
if (strcmp(threadsstr, "auto") == 0) { if (strcmp(threadsstr, "auto") == 0) {
aconf->threads = 0; ns->threads = 0;
} else {
aconf->threads = (uint8_t)atoi(threadsstr);
}
}
if (ConfGetChildValueWithDefault(if_root, if_default, "copy-iface", &out_iface) == 1) {
if (strlen(out_iface) > 0) {
aconf->out_iface_name = out_iface;
}
}
if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
if (aconf->out_iface_name == NULL) {
SCLogInfo("Copy mode activated but no destination"
" iface. Disabling feature");
} else if (strlen(copymodestr) <= 0) {
aconf->out_iface_name = NULL;
} else if (strcmp(copymodestr, "ips") == 0) {
SCLogInfo("Netmap IPS mode activated %s->%s",
aconf->iface_name,
aconf->out_iface_name);
aconf->copy_mode = NETMAP_COPY_MODE_IPS;
} else if (strcmp(copymodestr, "tap") == 0) {
SCLogInfo("Netmap TAP mode activated %s->%s",
aconf->iface_name,
aconf->out_iface_name);
aconf->copy_mode = NETMAP_COPY_MODE_TAP;
} else { } else {
SCLogInfo("Invalid mode (not in tap, ips)"); ns->threads = (uint8_t)atoi(threadsstr);
}
}
if (aconf->out_iface_name && aconf->out_iface_name[0]) {
strlcpy(aconf->out_iface, aconf->out_iface_name,
sizeof(aconf->out_iface));
size_t len = strlen(aconf->out_iface);
if (aconf->out_iface[len-1] == '+') {
aconf->out_iface[len-1] = '\0';
aconf->out_iface_sw = 1;
} }
} }
/* load netmap bpf filter */ /* load netmap bpf filter */
/* command line value has precedence */ /* command line value has precedence */
if (ConfGet("bpf-filter", &bpf_filter) != 1) { if (ns->bpf_filter == NULL) {
if (ConfGetChildValueWithDefault(if_root, if_default, "bpf-filter", &bpf_filter) == 1) { if (ConfGetChildValueWithDefault(if_root, if_default, "bpf-filter", &bpf_filter) == 1) {
if (strlen(bpf_filter) > 0) { if (strlen(bpf_filter) > 0) {
aconf->bpf_filter = bpf_filter; ns->bpf_filter = bpf_filter;
SCLogInfo("Going to use bpf filter %s", aconf->bpf_filter); SCLogInfo("Going to use bpf filter %s", ns->bpf_filter);
} }
} }
} }
int boolval = 0;
(void)ConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", (int *)&boolval); (void)ConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", (int *)&boolval);
if (boolval) { if (boolval) {
SCLogInfo("Disabling promiscuous mode on iface %s", aconf->iface); SCLogInfo("Disabling promiscuous mode on iface %s", ns->iface);
aconf->promisc = 0; ns->promisc = 0;
} }
if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) { char *tmpctype;
if (ConfGetChildValueWithDefault(if_root, if_default,
"checksum-checks", &tmpctype) == 1)
{
if (strcmp(tmpctype, "auto") == 0) { if (strcmp(tmpctype, "auto") == 0) {
aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO; ns->checksum_mode = CHECKSUM_VALIDATION_AUTO;
} else if (ConfValIsTrue(tmpctype)) { } else if (ConfValIsTrue(tmpctype)) {
aconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE; ns->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
} else if (ConfValIsFalse(tmpctype)) { } else if (ConfValIsFalse(tmpctype)) {
aconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE; ns->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
} else { } else {
SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid value for checksum-checks for %s", aconf->iface_name); SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid value for "
"checksum-checks for %s", iface);
}
}
char *copymodestr;
if (ConfGetChildValueWithDefault(if_root, if_default,
"copy-mode", &copymodestr) == 1)
{
if (strcmp(copymodestr, "ips") == 0) {
ns->copy_mode = NETMAP_COPY_MODE_IPS;
} else if (strcmp(copymodestr, "tap") == 0) {
ns->copy_mode = NETMAP_COPY_MODE_TAP;
} else {
SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid copy-mode "
"(valid are tap, ips)");
} }
} }
finalize: finalize:
if (aconf->iface_sw) { if (ns->sw_ring) {
/* just one thread per interface supported */ /* just one thread per interface supported */
aconf->threads = 1; ns->threads = 1;
} else if (aconf->threads == 0) { } else if (ns->threads == 0) {
/* As NetmapGetRSSCount is broken on Linux, first run /* As NetmapGetRSSCount is broken on Linux, first run
* GetIfaceRSSQueuesNum. If that fails, run NetmapGetRSSCount */ * GetIfaceRSSQueuesNum. If that fails, run NetmapGetRSSCount */
aconf->threads = GetIfaceRSSQueuesNum(aconf->iface); ns->threads = GetIfaceRSSQueuesNum(ns->iface);
if (aconf->threads == 0) { if (ns->threads == 0) {
aconf->threads = NetmapGetRSSCount(aconf->iface); ns->threads = NetmapGetRSSCount(ns->iface);
} }
} }
if (aconf->threads <= 0) { if (ns->threads <= 0) {
aconf->threads = 1; ns->threads = 1;
} }
return 0;
}
/**
* \brief extract information from config file
*
* The returned structure will be freed by the thread init function.
* This is thus necessary to or copy the structure before giving it
* to thread or to reparse the file for each thread (and thus have
* new structure.
*
* \return a NetmapIfaceConfig corresponding to the interface name
*/
static void *ParseNetmapConfig(const char *iface_name)
{
ConfNode *if_root = NULL;
ConfNode *if_default = NULL;
ConfNode *netmap_node;
char *out_iface = NULL;
if (iface_name == NULL) {
return NULL;
}
NetmapIfaceConfig *aconf = SCMalloc(sizeof(*aconf));
if (unlikely(aconf == NULL)) {
return NULL;
}
memset(aconf, 0, sizeof(*aconf));
aconf->DerefFunc = NetmapDerefConfig;
strlcpy(aconf->iface_name, iface_name, sizeof(aconf->iface_name));
SC_ATOMIC_INIT(aconf->ref);
(void) SC_ATOMIC_ADD(aconf->ref, 1);
/* Find initial node */
netmap_node = ConfGetNode("netmap");
if (netmap_node == NULL) {
SCLogInfo("Unable to find netmap config using default value");
} else {
if_root = ConfFindDeviceConfig(netmap_node, aconf->iface_name);
if_default = ConfFindDeviceConfig(netmap_node, "default");
}
/* parse settings for capture iface */
ParseNetmapSettings(&aconf->in, aconf->iface_name, if_root, if_default);
/* if we have a copy iface, parse that as well */
if (ConfGetChildValueWithDefault(if_root, if_default, "copy-iface", &out_iface) == 1) {
if (strlen(out_iface) > 0) {
if_root = ConfFindDeviceConfig(netmap_node, out_iface);
ParseNetmapSettings(&aconf->out, out_iface, if_root, if_default);
}
}
SC_ATOMIC_RESET(aconf->ref); SC_ATOMIC_RESET(aconf->ref);
(void) SC_ATOMIC_ADD(aconf->ref, aconf->threads); (void) SC_ATOMIC_ADD(aconf->ref, aconf->in.threads);
SCLogPerf("Using %d threads for interface %s", aconf->threads, SCLogPerf("Using %d threads for interface %s", aconf->in.threads,
aconf->iface_name); aconf->iface_name);
return aconf; return aconf;
@ -278,7 +273,7 @@ finalize:
static int NetmapConfigGeThreadsCount(void *conf) static int NetmapConfigGeThreadsCount(void *conf)
{ {
NetmapIfaceConfig *aconf = (NetmapIfaceConfig *)conf; NetmapIfaceConfig *aconf = (NetmapIfaceConfig *)conf;
return aconf->threads; return aconf->in.threads;
} }
int NetmapRunModeIsIPS() int NetmapRunModeIsIPS()

@ -622,8 +622,8 @@ static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **da
memset(ntv, 0, sizeof(*ntv)); memset(ntv, 0, sizeof(*ntv));
ntv->tv = tv; ntv->tv = tv;
ntv->checksum_mode = aconf->checksum_mode; ntv->checksum_mode = aconf->in.checksum_mode;
ntv->copy_mode = aconf->copy_mode; ntv->copy_mode = aconf->in.copy_mode;
ntv->livedev = LiveGetDevice(aconf->iface_name); ntv->livedev = LiveGetDevice(aconf->iface_name);
if (ntv->livedev == NULL) { if (ntv->livedev == NULL) {
@ -631,32 +631,32 @@ static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **da
goto error_ntv; goto error_ntv;
} }
if (NetmapOpen(aconf->iface, aconf->promisc, &ntv->ifsrc, 1) != 0) { if (NetmapOpen(aconf->in.iface, aconf->in.promisc, &ntv->ifsrc, 1) != 0) {
goto error_ntv; goto error_ntv;
} }
if (unlikely(!aconf->iface_sw && !ntv->ifsrc->rx_rings_cnt)) { if (unlikely(!aconf->in.sw_ring && !ntv->ifsrc->rx_rings_cnt)) {
SCLogError(SC_ERR_NETMAP_CREATE, SCLogError(SC_ERR_NETMAP_CREATE,
"Input interface '%s' does not have Rx rings", "Input interface '%s' does not have Rx rings",
aconf->iface_name); aconf->iface_name);
goto error_src; goto error_src;
} }
if (unlikely(aconf->iface_sw && aconf->threads > 1)) { if (unlikely(aconf->in.sw_ring && aconf->in.threads > 1)) {
SCLogError(SC_ERR_INVALID_VALUE, SCLogError(SC_ERR_INVALID_VALUE,
"Interface '%s+'. " "Interface '%s+'. "
"Thread count can't be greater than 1 for SW ring.", "Thread count can't be greater than 1 for SW ring.",
aconf->iface_name); aconf->iface_name);
goto error_src; goto error_src;
} else if (unlikely(aconf->threads > ntv->ifsrc->rx_rings_cnt)) { } else if (unlikely(aconf->in.threads > ntv->ifsrc->rx_rings_cnt)) {
SCLogError(SC_ERR_INVALID_VALUE, SCLogError(SC_ERR_INVALID_VALUE,
"Thread count can't be greater than Rx ring count. " "Thread count can't be greater than Rx ring count. "
"Configured %d threads for interface '%s' with %d Rx rings.", "Configured %d threads for interface '%s' with %d Rx rings.",
aconf->threads, aconf->iface_name, ntv->ifsrc->rx_rings_cnt); aconf->in.threads, aconf->iface_name, ntv->ifsrc->rx_rings_cnt);
goto error_src; goto error_src;
} }
if (aconf->iface_sw) { if (aconf->in.sw_ring) {
ntv->thread_idx = 0; ntv->thread_idx = 0;
} else { } else {
do { do {
@ -665,35 +665,35 @@ static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **da
} }
/* calculate thread rings binding */ /* calculate thread rings binding */
if (aconf->iface_sw) { if (aconf->in.sw_ring) {
ntv->src_ring_from = ntv->src_ring_to = ntv->ifsrc->rings_cnt; ntv->src_ring_from = ntv->src_ring_to = ntv->ifsrc->rings_cnt;
} else { } else {
int tmp = (ntv->ifsrc->rx_rings_cnt + 1) / aconf->threads; int tmp = (ntv->ifsrc->rx_rings_cnt + 1) / aconf->in.threads;
ntv->src_ring_from = ntv->thread_idx * tmp; ntv->src_ring_from = ntv->thread_idx * tmp;
ntv->src_ring_to = ntv->src_ring_from + tmp - 1; ntv->src_ring_to = ntv->src_ring_from + tmp - 1;
if (ntv->thread_idx == (aconf->threads - 1)) { if (ntv->thread_idx == (aconf->in.threads - 1)) {
ntv->src_ring_to = ntv->ifsrc->rx_rings_cnt - 1; ntv->src_ring_to = ntv->ifsrc->rx_rings_cnt - 1;
} }
} }
SCLogDebug("netmap: %s thread:%d rings:%d-%d", aconf->iface_name, SCLogDebug("netmap: %s thread:%d rings:%d-%d", aconf->iface_name,
ntv->thread_idx, ntv->src_ring_from, ntv->src_ring_to); ntv->thread_idx, ntv->src_ring_from, ntv->src_ring_to);
if (aconf->copy_mode != NETMAP_COPY_MODE_NONE) { if (aconf->in.copy_mode != NETMAP_COPY_MODE_NONE) {
if (NetmapOpen(aconf->out_iface, 0, &ntv->ifdst, 1) != 0) { if (NetmapOpen(aconf->out.iface, aconf->out.promisc, &ntv->ifdst, 1) != 0) {
goto error_src; goto error_src;
} }
if (unlikely(!aconf->out_iface_sw && !ntv->ifdst->tx_rings_cnt)) { if (unlikely(!aconf->out.sw_ring && !ntv->ifdst->tx_rings_cnt)) {
SCLogError(SC_ERR_NETMAP_CREATE, SCLogError(SC_ERR_NETMAP_CREATE,
"Output interface '%s' does not have Tx rings", "Output interface '%s' does not have Tx rings",
aconf->out_iface_name); aconf->out.iface);
goto error_dst; goto error_dst;
} }
/* calculate dst rings bindings */ /* calculate dst rings bindings */
for (int i = ntv->src_ring_from; i <= ntv->src_ring_to; i++) { for (int i = ntv->src_ring_from; i <= ntv->src_ring_to; i++) {
NetmapRing *ring = &ntv->ifsrc->rings[i]; NetmapRing *ring = &ntv->ifsrc->rings[i];
if (aconf->out_iface_sw) { if (aconf->out.sw_ring) {
ring->dst_ring_from = ring->dst_ring_to = ntv->ifdst->rings_cnt; ring->dst_ring_from = ring->dst_ring_to = ntv->ifdst->rings_cnt;
} else if (ntv->ifdst->tx_rings_cnt > ntv->ifsrc->rx_rings_cnt) { } else if (ntv->ifdst->tx_rings_cnt > ntv->ifsrc->rx_rings_cnt) {
int tmp = (ntv->ifdst->tx_rings_cnt + 1) / ntv->ifsrc->rx_rings_cnt; int tmp = (ntv->ifdst->tx_rings_cnt + 1) / ntv->ifsrc->rx_rings_cnt;
@ -709,7 +709,7 @@ static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **da
ring->dst_next_ring = ring->dst_ring_from; ring->dst_next_ring = ring->dst_ring_from;
SCLogDebug("netmap: %s(%d)->%s(%d-%d)", SCLogDebug("netmap: %s(%d)->%s(%d-%d)",
aconf->iface_name, i, aconf->out_iface_name, aconf->in.iface, i, aconf->out.iface,
ring->dst_ring_from, ring->dst_ring_to); ring->dst_ring_from, ring->dst_ring_to);
} }
} }
@ -722,11 +722,11 @@ static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **da
/* enable zero-copy mode for workers runmode */ /* enable zero-copy mode for workers runmode */
char const *active_runmode = RunmodeGetActive(); char const *active_runmode = RunmodeGetActive();
if ((aconf->copy_mode != NETMAP_COPY_MODE_NONE) && active_runmode if ((aconf->in.copy_mode != NETMAP_COPY_MODE_NONE) && active_runmode &&
&& !strcmp("workers", active_runmode)) { strcmp("workers", active_runmode) == 0) {
ntv->flags |= NETMAP_FLAG_ZERO_COPY; ntv->flags |= NETMAP_FLAG_ZERO_COPY;
SCLogPerf("Enabling zero copy mode for %s->%s", SCLogPerf("Enabling zero copy mode for %s->%s",
aconf->iface_name, aconf->out_iface_name); aconf->in.iface, aconf->out.iface);
} else { } else {
uint16_t ring_size = ntv->ifsrc->rings[0].rx->num_slots; uint16_t ring_size = ntv->ifsrc->rings[0].rx->num_slots;
if (ring_size > max_pending_packets) { if (ring_size > max_pending_packets) {
@ -738,16 +738,17 @@ static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **da
} }
} }
if (aconf->bpf_filter) { if (aconf->in.bpf_filter) {
SCLogConfig("Using BPF '%s' on iface '%s'", SCLogConfig("Using BPF '%s' on iface '%s'",
aconf->bpf_filter, ntv->ifsrc->ifname); aconf->in.bpf_filter, ntv->ifsrc->ifname);
if (pcap_compile_nopcap(default_packet_size, /* snaplen_arg */ if (pcap_compile_nopcap(default_packet_size, /* snaplen_arg */
LINKTYPE_ETHERNET, /* linktype_arg */ LINKTYPE_ETHERNET, /* linktype_arg */
&ntv->bpf_prog, /* program */ &ntv->bpf_prog, /* program */
aconf->bpf_filter, /* const char *buf */ aconf->in.bpf_filter, /* const char *buf */
1, /* optimize */ 1, /* optimize */
PCAP_NETMASK_UNKNOWN /* mask */ PCAP_NETMASK_UNKNOWN /* mask */
) == -1) { ) == -1)
{
SCLogError(SC_ERR_NETMAP_CREATE, "Filter compilation failed."); SCLogError(SC_ERR_NETMAP_CREATE, "Filter compilation failed.");
goto error_dst; goto error_dst;
} }
@ -758,7 +759,7 @@ static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **da
SCReturnInt(TM_ECODE_OK); SCReturnInt(TM_ECODE_OK);
error_dst: error_dst:
if (aconf->copy_mode != NETMAP_COPY_MODE_NONE) { if (aconf->in.copy_mode != NETMAP_COPY_MODE_NONE) {
NetmapClose(ntv->ifdst); NetmapClose(ntv->ifdst);
} }
error_src: error_src:

@ -35,25 +35,34 @@ enum {
#define NETMAP_IFACE_NAME_LENGTH 48 #define NETMAP_IFACE_NAME_LENGTH 48
typedef struct NetmapIfaceConfig_ typedef struct NetmapIfaceSettings_
{ {
/* semantic interface name */
char iface_name[NETMAP_IFACE_NAME_LENGTH];
/* real inner interface name */ /* real inner interface name */
char iface[NETMAP_IFACE_NAME_LENGTH]; char iface[NETMAP_IFACE_NAME_LENGTH];
/* sw ring flag for iface */
int iface_sw;
int threads; int threads;
/* sw ring flag for out_iface */
int sw_ring;
int promisc; int promisc;
int copy_mode; int copy_mode;
ChecksumValidationMode checksum_mode; ChecksumValidationMode checksum_mode;
char *bpf_filter; char *bpf_filter;
} NetmapIfaceSettings;
typedef struct NetmapIfaceConfig_
{
/* semantic interface name */
char iface_name[NETMAP_IFACE_NAME_LENGTH];
/* settings for out capture device*/
NetmapIfaceSettings in;
/* semantic interface name */ /* semantic interface name */
char *out_iface_name; char *out_iface_name;
/* real inner interface name */
char out_iface[NETMAP_IFACE_NAME_LENGTH]; /* settings for outgoing iface for IPS/TAP */
/* sw ring flag for out_iface */ NetmapIfaceSettings out;
int out_iface_sw;
SC_ATOMIC_DECLARE(unsigned int, ref); SC_ATOMIC_DECLARE(unsigned int, ref);
void (*DerefFunc)(void *); void (*DerefFunc)(void *);
} NetmapIfaceConfig; } NetmapIfaceConfig;

Loading…
Cancel
Save