diff options
Diffstat (limited to 'src/util-ioctl.c')
-rw-r--r-- | src/util-ioctl.c | 739 |
1 files changed, 739 insertions, 0 deletions
diff --git a/src/util-ioctl.c b/src/util-ioctl.c new file mode 100644 index 0000000..f39662b --- /dev/null +++ b/src/util-ioctl.c @@ -0,0 +1,739 @@ +/* Copyright (C) 2010 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Eric Leblond <eric@regit.org> + * \author Victor Julien <victor@inliniac.net> + */ + +#include "suricata-common.h" +#include "util-ioctl.h" +#include "conf.h" +#include "decode.h" +#include "decode-sll.h" + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#ifdef HAVE_LINUX_ETHTOOL_H +#include <linux/types.h> +#include <linux/ethtool.h> +#ifdef HAVE_LINUX_SOCKIOS_H +#include <linux/sockios.h> +#else +#error "ethtool.h present but sockios.h is missing" +#endif /* HAVE_LINUX_SOCKIOS_H */ +#endif /* HAVE_LINUX_ETHTOOL_H */ + +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif + +#ifdef OS_WIN32 +#include "win32-syscall.h" +#endif + +/** + * \brief output a majorant of hardware header length + * + * \param Name of a network interface + */ +static int GetIfaceMaxHWHeaderLength(const char *dev) +{ + if ((!strcmp("eth", dev)) || (!strcmp("br", dev)) || (!strcmp("bond", dev)) || + (!strcmp("wlan", dev)) || (!strcmp("tun", dev)) || (!strcmp("tap", dev)) || + (!strcmp("lo", dev))) { + /* Add possible VLAN tag or Qing headers */ + return 8 + ETHERNET_HEADER_LEN; + } + + if (!strcmp("ppp", dev)) + return SLL_HEADER_LEN; + /* SLL_HEADER_LEN is the biggest one and + add possible VLAN tag and Qing headers */ + return 8 + SLL_HEADER_LEN; +} + + +/** + * \brief output the link MTU + * + * \param Name of link + * \retval -1 in case of error, 0 if MTU can not be found + */ +int GetIfaceMTU(const char *dev) +{ +#if defined SIOCGIFMTU + struct ifreq ifr; + int fd; + + (void)strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + return -1; + } + + if (ioctl(fd, SIOCGIFMTU, (char *)&ifr) < 0) { + SCLogWarning("Failure when trying to get MTU via ioctl for '%s': %s (%d)", dev, + strerror(errno), errno); + close(fd); + return -1; + } + close(fd); + SCLogInfo("%s: MTU %d", dev, ifr.ifr_mtu); + return ifr.ifr_mtu; +#elif defined OS_WIN32 + return GetIfaceMTUWin32(dev); +#else + /* ioctl is not defined, let's pretend returning 0 is ok */ + return 0; +#endif +} + +/** + * \brief output max packet size for a link + * + * This does a best effort to find the maximum packet size + * for the link. In case of uncertainty, it will output a + * majorant to be sure avoid the cost of dynamic allocation. + * + * \param LiveDevice object + * \retval 0 in case of error + */ +int GetIfaceMaxPacketSize(LiveDevice *ld) +{ + if (ld == NULL) + return 0; + + const char *dev = ld->dev; + if ((dev == NULL) || strlen(dev) == 0) + return 0; + + int mtu = GetIfaceMTU(dev); + switch (mtu) { + case 0: + case -1: + return 0; + } + ld->mtu = mtu; + int ll_header = GetIfaceMaxHWHeaderLength(dev); + return ll_header + mtu; +} + +#ifdef SIOCGIFFLAGS +/** + * \brief Get interface flags. + * \param ifname Interface name. + * \return Interface flags or -1 on error + */ +int GetIfaceFlags(const char *ifname) +{ + struct ifreq ifr; + + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) { + SCLogError("%s: failed to get device flags: %s", ifname, strerror(errno)); + close(fd); + return -1; + } + + close(fd); +#ifdef OS_FREEBSD + int flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); + return flags; +#else + return ifr.ifr_flags; +#endif +} +#endif + +#ifdef SIOCSIFFLAGS +/** + * \brief Set interface flags. + * \param ifname Interface name. + * \param flags Flags to set. + * \return Zero on success. + */ +int SetIfaceFlags(const char *ifname, int flags) +{ + struct ifreq ifr; + + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); +#ifdef OS_FREEBSD + ifr.ifr_flags = flags & 0xffff; + ifr.ifr_flagshigh = flags >> 16; +#else + ifr.ifr_flags = (uint16_t)flags; +#endif + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) { + SCLogError("%s: unable to set device flags: %s", ifname, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} +#endif /* SIOCGIFFLAGS */ + +#ifdef SIOCGIFCAP +int GetIfaceCaps(const char *ifname) +{ + struct ifreq ifr; + + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + if (ioctl(fd, SIOCGIFCAP, &ifr) == -1) { + SCLogError("%s: unable to get device caps: %s", ifname, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return ifr.ifr_curcap; +} +#endif +#ifdef SIOCSIFCAP +int SetIfaceCaps(const char *ifname, int caps) +{ + struct ifreq ifr; + + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + ifr.ifr_reqcap = caps; + + if (ioctl(fd, SIOCSIFCAP, &ifr) == -1) { + SCLogError("%s: unable to set caps: %s", ifname, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} +#endif + + +#if defined HAVE_LINUX_ETHTOOL_H && defined SIOCETHTOOL +static int GetEthtoolValue(const char *dev, int cmd, uint32_t *value) +{ + struct ifreq ifr; + int fd; + struct ethtool_value ethv; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + return -1; + } + (void)strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); + + ethv.cmd = cmd; + ifr.ifr_data = (void *) ðv; + if (ioctl(fd, SIOCETHTOOL, (char *)&ifr) < 0) { + SCLogWarning("%s: failed to get SIOCETHTOOL ioctl: %s", dev, strerror(errno)); + close(fd); + return -1; + } + + *value = ethv.data; + close(fd); + return 0; +} + +static int SetEthtoolValue(const char *dev, int cmd, uint32_t value) +{ + struct ifreq ifr; + int fd; + struct ethtool_value ethv; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + return -1; + } + (void)strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); + + ethv.cmd = cmd; + ethv.data = value; + ifr.ifr_data = (void *) ðv; + if (ioctl(fd, SIOCETHTOOL, (char *)&ifr) < 0) { + SCLogWarning("%s: failed to set SIOCETHTOOL ioctl: %s", dev, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + +static int GetIfaceOffloadingLinux(const char *dev, int csum, int other) +{ + int ret = 0; + uint32_t value = 0; + + if (csum) { + const char *rx = "unset", *tx = "unset"; + int csum_ret = 0; +#ifdef ETHTOOL_GRXCSUM + if (GetEthtoolValue(dev, ETHTOOL_GRXCSUM, &value) == 0 && value != 0) { + rx = "SET"; + csum_ret = 1; + } +#endif +#ifdef ETHTOOL_GTXCSUM + if (GetEthtoolValue(dev, ETHTOOL_GTXCSUM, &value) == 0 && value != 0) { + tx = "SET"; + csum_ret = 1; + } +#endif + if (csum_ret == 0) + SCLogPerf("%s: NIC offloading: RX %s TX %s", dev, rx, tx); + else { + SCLogWarning("%s: NIC offloading: RX %s TX %s. Run: ethtool -K %s rx off tx off", dev, + rx, tx, dev); + ret = 1; + } + } + + if (other) { + const char *lro = "unset", *gro = "unset", *tso = "unset", *gso = "unset"; + const char *sg = "unset"; + int other_ret = 0; +#ifdef ETHTOOL_GGRO + if (GetEthtoolValue(dev, ETHTOOL_GGRO, &value) == 0 && value != 0) { + gro = "SET"; + other_ret = 1; + } +#endif +#ifdef ETHTOOL_GTSO + if (GetEthtoolValue(dev, ETHTOOL_GTSO, &value) == 0 && value != 0) { + tso = "SET"; + other_ret = 1; + } +#endif +#ifdef ETHTOOL_GGSO + if (GetEthtoolValue(dev, ETHTOOL_GGSO, &value) == 0 && value != 0) { + gso = "SET"; + other_ret = 1; + } +#endif +#ifdef ETHTOOL_GSG + if (GetEthtoolValue(dev, ETHTOOL_GSG, &value) == 0 && value != 0) { + sg = "SET"; + other_ret = 1; + } +#endif +#ifdef ETHTOOL_GFLAGS + if (GetEthtoolValue(dev, ETHTOOL_GFLAGS, &value) == 0) { + if (value & ETH_FLAG_LRO) { + lro = "SET"; + other_ret = 1; + } + } +#endif + if (other_ret == 0) { + SCLogPerf("%s: NIC offloading: SG: %s, GRO: %s, LRO: %s, TSO: %s, GSO: %s", dev, sg, + gro, lro, tso, gso); + } else { + SCLogWarning("%s: NIC offloading: SG: %s, GRO: %s, LRO: %s, TSO: %s, GSO: %s. Run: " + "ethtool -K %s sg off gro off lro off tso off gso off", + dev, sg, gro, lro, tso, gso, dev); + ret = 1; + } + } + return ret; +} + +static int DisableIfaceOffloadingLinux(LiveDevice *ldev, int csum, int other) +{ + int ret = 0; + uint32_t value = 0; + + if (ldev == NULL) + return -1; + + const char *dev = ldev->dev; + + if (csum) { +#ifdef ETHTOOL_GRXCSUM + if (GetEthtoolValue(dev, ETHTOOL_GRXCSUM, &value) == 0 && value != 0) { + SCLogPerf("%s: disabling rxcsum offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SRXCSUM, 0); + ldev->offload_orig |= OFFLOAD_FLAG_RXCSUM; + } +#endif +#ifdef ETHTOOL_GTXCSUM + if (GetEthtoolValue(dev, ETHTOOL_GTXCSUM, &value) == 0 && value != 0) { + SCLogPerf("%s: disabling txcsum offloading", dev); + SetEthtoolValue(dev, ETHTOOL_STXCSUM, 0); + ldev->offload_orig |= OFFLOAD_FLAG_TXCSUM; + } +#endif + } + if (other) { +#ifdef ETHTOOL_GGRO + if (GetEthtoolValue(dev, ETHTOOL_GGRO, &value) == 0 && value != 0) { + SCLogPerf("%s: disabling gro offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SGRO, 0); + ldev->offload_orig |= OFFLOAD_FLAG_GRO; + } +#endif +#ifdef ETHTOOL_GTSO + if (GetEthtoolValue(dev, ETHTOOL_GTSO, &value) == 0 && value != 0) { + SCLogPerf("%s: disabling tso offloading", dev); + SetEthtoolValue(dev, ETHTOOL_STSO, 0); + ldev->offload_orig |= OFFLOAD_FLAG_TSO; + } +#endif +#ifdef ETHTOOL_GGSO + if (GetEthtoolValue(dev, ETHTOOL_GGSO, &value) == 0 && value != 0) { + SCLogPerf("%s: disabling gso offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SGSO, 0); + ldev->offload_orig |= OFFLOAD_FLAG_GSO; + } +#endif +#ifdef ETHTOOL_GSG + if (GetEthtoolValue(dev, ETHTOOL_GSG, &value) == 0 && value != 0) { + SCLogPerf("%s: disabling sg offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SSG, 0); + ldev->offload_orig |= OFFLOAD_FLAG_SG; + } +#endif +#ifdef ETHTOOL_GFLAGS + if (GetEthtoolValue(dev, ETHTOOL_GFLAGS, &value) == 0) { + if (value & ETH_FLAG_LRO) { + SCLogPerf("%s: disabling lro offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SFLAGS, value & ~ETH_FLAG_LRO); + ldev->offload_orig |= OFFLOAD_FLAG_LRO; + } + } +#endif + } + return ret; +} + +static int RestoreIfaceOffloadingLinux(LiveDevice *ldev) +{ + if (ldev == NULL) + return -1; + + const char *dev = ldev->dev; + +#ifdef ETHTOOL_GRXCSUM + if (ldev->offload_orig & OFFLOAD_FLAG_RXCSUM) { + SCLogPerf("%s: restoring rxcsum offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SRXCSUM, 1); + } +#endif +#ifdef ETHTOOL_GTXCSUM + if (ldev->offload_orig & OFFLOAD_FLAG_TXCSUM) { + SCLogPerf("%s: restoring txcsum offloading", dev); + SetEthtoolValue(dev, ETHTOOL_STXCSUM, 1); + } +#endif +#ifdef ETHTOOL_GGRO + if (ldev->offload_orig & OFFLOAD_FLAG_GRO) { + SCLogPerf("%s: restoring gro offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SGRO, 1); + } +#endif +#ifdef ETHTOOL_GTSO + if (ldev->offload_orig & OFFLOAD_FLAG_TSO) { + SCLogPerf("%s: restoring tso offloading", dev); + SetEthtoolValue(dev, ETHTOOL_STSO, 1); + } +#endif +#ifdef ETHTOOL_GGSO + if (ldev->offload_orig & OFFLOAD_FLAG_GSO) { + SCLogPerf("%s: restoring gso offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SGSO, 1); + } +#endif +#ifdef ETHTOOL_GSG + if (ldev->offload_orig & OFFLOAD_FLAG_SG) { + SCLogPerf("%s: restoring sg offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SSG, 1); + } +#endif +#ifdef ETHTOOL_GFLAGS + if (ldev->offload_orig & OFFLOAD_FLAG_LRO) { + uint32_t value = 0; + if (GetEthtoolValue(dev, ETHTOOL_GFLAGS, &value) == 0) { + SCLogPerf("%s: restoring lro offloading", dev); + SetEthtoolValue(dev, ETHTOOL_SFLAGS, value & ETH_FLAG_LRO); + } + } +#endif + return 0; +} + +#endif /* defined HAVE_LINUX_ETHTOOL_H && defined SIOCETHTOOL */ + +#ifdef SIOCGIFCAP +static int GetIfaceOffloadingBSD(const char *ifname) +{ + int ret = 0; + int if_caps = GetIfaceCaps(ifname); + if (if_caps == -1) { + return -1; + } + SCLogDebug("if_caps %X", if_caps); + + if (if_caps & IFCAP_RXCSUM) { + SCLogWarning("%s: RXCSUM activated can lead to capture problems. Run: ifconfig %s -rxcsum", + ifname, ifname); + ret = 1; + } +#ifdef IFCAP_TOE + if (if_caps & (IFCAP_TSO|IFCAP_TOE|IFCAP_LRO)) { + SCLogWarning("%s: TSO, TOE or LRO activated can lead to capture problems. Run: ifconfig %s " + "-tso -toe -lro", + ifname, ifname); + ret = 1; + } +#else + if (if_caps & (IFCAP_TSO|IFCAP_LRO)) { + SCLogWarning( + "%s: TSO or LRO activated can lead to capture problems. Run: ifconfig %s -tso -lro", + ifname, ifname); + ret = 1; + } +#endif + return ret; +} +#endif + +#ifdef SIOCSIFCAP +static int DisableIfaceOffloadingBSD(LiveDevice *ldev) +{ + int ret = 0; + + if (ldev == NULL) + return -1; + + const char *ifname = ldev->dev; + int if_caps = GetIfaceCaps(ifname); + int set_caps = if_caps; + if (if_caps == -1) { + return -1; + } + SCLogDebug("if_caps %X", if_caps); + + if (if_caps & IFCAP_RXCSUM) { + SCLogPerf("%s: disabling rxcsum offloading", ifname); + set_caps &= ~IFCAP_RXCSUM; + } + if (if_caps & IFCAP_TXCSUM) { + SCLogPerf("%s: disabling txcsum offloading", ifname); + set_caps &= ~IFCAP_TXCSUM; + } +#ifdef IFCAP_RXCSUM_IPV6 + if (if_caps & IFCAP_RXCSUM_IPV6) { + SCLogPerf("%s: disabling rxcsum6 offloading", ifname); + set_caps &= ~IFCAP_RXCSUM_IPV6; + } +#endif +#ifdef IFCAP_TXCSUM_IPV6 + if (if_caps & IFCAP_TXCSUM_IPV6) { + SCLogPerf("%s: disabling txcsum6 offloading", ifname); + set_caps &= ~IFCAP_TXCSUM_IPV6; + } +#endif +#ifdef IFCAP_TOE + if (if_caps & (IFCAP_TSO|IFCAP_TOE|IFCAP_LRO)) { + SCLogPerf("%s: disabling tso|toe|lro offloading", ifname); + set_caps &= ~(IFCAP_TSO|IFCAP_LRO); + } +#else + if (if_caps & (IFCAP_TSO|IFCAP_LRO)) { + SCLogPerf("%s: disabling tso|lro offloading", ifname); + set_caps &= ~(IFCAP_TSO|IFCAP_LRO); + } +#endif + if (set_caps != if_caps) { + if (if_caps & IFCAP_RXCSUM) + ldev->offload_orig |= OFFLOAD_FLAG_RXCSUM; + if (if_caps & IFCAP_TSO) + ldev->offload_orig |= OFFLOAD_FLAG_TSO; +#ifdef IFCAP_TOE + if (if_caps & IFCAP_TOE) + ldev->offload_orig |= OFFLOAD_FLAG_TOE; +#endif + if (if_caps & IFCAP_LRO) + ldev->offload_orig |= OFFLOAD_FLAG_LRO; + + SetIfaceCaps(ifname, set_caps); + } + return ret; +} + +static int RestoreIfaceOffloadingBSD(LiveDevice *ldev) +{ + int ret = 0; + + if (ldev == NULL) + return -1; + + const char *ifname = ldev->dev; + int if_caps = GetIfaceCaps(ifname); + int set_caps = if_caps; + if (if_caps == -1) { + return -1; + } + SCLogDebug("if_caps %X", if_caps); + + if (ldev->offload_orig & OFFLOAD_FLAG_RXCSUM) { + SCLogPerf("%s: restoring rxcsum offloading", ifname); + set_caps |= IFCAP_RXCSUM; + } + if (ldev->offload_orig & OFFLOAD_FLAG_TSO) { + SCLogPerf("%s: restoring tso offloading", ifname); + set_caps |= IFCAP_TSO; + } +#ifdef IFCAP_TOE + if (ldev->offload_orig & OFFLOAD_FLAG_TOE) { + SCLogPerf("%s: restoring toe offloading", ifname); + set_caps |= IFCAP_TOE; + } +#endif + if (ldev->offload_orig & OFFLOAD_FLAG_LRO) { + SCLogPerf("%s: restoring lro offloading", ifname); + set_caps |= IFCAP_LRO; + } + + if (set_caps != if_caps) { + SetIfaceCaps(ifname, set_caps); + } + return ret; +} +#endif + +/** + * \brief output offloading status of the link + * + * Test interface for offloading features. If one of them is + * activated then suricata mays received packets merge at reception. + * The result is oversized packets and this may cause some serious + * problem in some capture mode where the size of the packet is + * limited (AF_PACKET in V2 more for example). + * + * \param Name of link + * \param csum check if checksums are offloaded + * \param other check if other things are offloaded: TSO, GRO, etc. + * \retval -1 in case of error, 0 if none, 1 if some + */ +int GetIfaceOffloading(const char *dev, int csum, int other) +{ +#if defined HAVE_LINUX_ETHTOOL_H && defined SIOCETHTOOL + return GetIfaceOffloadingLinux(dev, csum, other); +#elif defined SIOCGIFCAP + return GetIfaceOffloadingBSD(dev); +#elif defined OS_WIN32 + return GetIfaceOffloadingWin32(dev, csum, other); +#else + return 0; +#endif +} + +int DisableIfaceOffloading(LiveDevice *dev, int csum, int other) +{ + /* already set */ + if (dev->offload_orig != 0) + return 0; +#if defined HAVE_LINUX_ETHTOOL_H && defined SIOCETHTOOL + return DisableIfaceOffloadingLinux(dev, csum, other); +#elif defined SIOCSIFCAP + return DisableIfaceOffloadingBSD(dev); +#elif defined OS_WIN32 + return DisableIfaceOffloadingWin32(dev, csum, other); +#else + return 0; +#endif + +} + +void RestoreIfaceOffloading(LiveDevice *dev) +{ + if (dev->offload_orig != 0) { +#if defined HAVE_LINUX_ETHTOOL_H && defined SIOCETHTOOL + RestoreIfaceOffloadingLinux(dev); +#elif defined SIOCSIFCAP + RestoreIfaceOffloadingBSD(dev); +#elif defined OS_WIN32 + RestoreIfaceOffloadingWin32(dev); +#endif + } +} + +int GetIfaceRSSQueuesNum(const char *dev) +{ +#if defined HAVE_LINUX_ETHTOOL_H && defined ETHTOOL_GRXRINGS + struct ifreq ifr; + struct ethtool_rxnfc nfccmd; + int fd; + + (void)strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + SCLogWarning("%s: failed to open socket for ioctl: %s", dev, strerror(errno)); + return -1; + } + + nfccmd.cmd = ETHTOOL_GRXRINGS; + ifr.ifr_data = (void*) &nfccmd; + + if (ioctl(fd, SIOCETHTOOL, (char *)&ifr) < 0) { + if (errno != ENOTSUP) { + SCLogWarning("%s: failed get number of RSS queue ioctl: %s", dev, strerror(errno)); + } + close(fd); + return 0; + } + close(fd); + SCLogInfo("%s: RX RSS queues: %d", dev, (int)nfccmd.data); + return (int)nfccmd.data; +#else + return 0; +#endif +} |