/* Define to 1 if you have the <linux/compiler.h> header file. */
#undef HAVE_LINUX_COMPILER_H
+/* Define to 1 if you have the <linux/ethtool.h> header file. */
+#undef HAVE_LINUX_ETHTOOL_H
+
/* Define to 1 if you have the <linux/net_tstamp.h> header file. */
#undef HAVE_LINUX_NET_TSTAMP_H
fi
+
+for ac_header in linux/ethtool.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
{ echo "$as_me:$LINENO: checking if if_packet.h has tpacket_stats defined" >&5
echo $ECHO_N "checking if if_packet.h has tpacket_stats defined... $ECHO_C" >&6; }
if test "${ac_cv_lbl_tpacket_stats+set}" = set; then
])
fi
+ AC_CHECK_HEADERS(linux/ethtool.h)
AC_LBL_TPACKET_STATS
AC_LBL_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI
;;
#include <netlink/attr.h>
#endif /* HAVE_LIBNL */
+/*
+ * Got ethtool support?
+ */
+#ifdef HAVE_LINUX_ETHTOOL_H
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+#endif /* HAVE_LINUX_ETHTOOL_H */
+
#include "pcap-int.h"
#include "pcap/sll.h"
#include "pcap/vlan.h"
*/
#ifdef HAVE_PF_PACKET_SOCKETS
static int iface_get_id(int fd, const char *device, char *ebuf);
-#endif
+#endif /* HAVE_PF_PACKET_SOCKETS */
static int iface_get_mtu(int fd, const char *device, char *ebuf);
static int iface_get_arptype(int fd, const char *device, char *ebuf);
#ifdef HAVE_PF_PACKET_SOCKETS
static int enter_rfmon_mode(pcap_t *handle, int sock_fd,
const char *device);
#endif /* HAVE_PF_PACKET_SOCKETS */
+static int iface_get_offload(pcap_t *handle);
static int iface_bind_old(int fd, const char *device, char *ebuf);
#ifdef SO_ATTACH_FILTER
= BPF_STMT(BPF_RET | BPF_K, 0);
static struct sock_fprog total_fcode
= { 1, &total_insn };
-#endif
+#endif /* SO_ATTACH_FILTER */
pcap_t *
pcap_create(const char *device, char *ebuf)
* interface is just being used for passive snooping, the driver
* might set the size of buffers in the receive ring based on
* the MTU, so that the MTU limits the maximum size of packets
- * that we can receive.) */
+ * that we can receive.)
+ *
+ * We don't do that if segmentation/fragmentation or receive
+ * offload are enabled, so we don't get rudely surprised by
+ * "packets" bigger than the MTU. */
frame_size = handle->snapshot;
if (handle->linktype == DLT_EN10MB) {
int mtu;
-
- mtu = iface_get_mtu(handle->fd, handle->opt.source,
- handle->errbuf);
- if (mtu == -1) {
+ int offload;
+
+ offload = iface_get_offload(handle);
+ if (offload == -1) {
*status = PCAP_ERROR;
return -1;
}
- if (frame_size > mtu + 18)
- frame_size = mtu + 18;
+ if (!offload) {
+ mtu = iface_get_mtu(handle->fd, handle->opt.source,
+ handle->errbuf);
+ if (mtu == -1) {
+ *status = PCAP_ERROR;
+ return -1;
+ }
+ if (frame_size > mtu + 18)
+ frame_size = mtu + 18;
+ }
}
/* NOTE: calculus matching those in tpacket_rcv()
return 0;
}
+/*
+ * Find out if we have any form of fragmentation/reassembly offloading.
+ */
+#ifdef SIOCETHTOOL
+static int
+iface_ethtool_ioctl(pcap_t *handle, int cmd)
+{
+ struct ifreq ifr;
+ struct ethtool_value eval;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name));
+ eval.cmd = cmd;
+ ifr.ifr_data = (caddr_t)&eval;
+ if (ioctl(handle->fd, SIOCETHTOOL, &ifr) == -1) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: 0x%08x ioctl failed: %s", handle->opt.source,
+ cmd, strerror(errno));
+ return -1;
+ }
+ return eval.data;
+}
+
+static int
+iface_get_offload(pcap_t *handle)
+{
+ int ret;
+
+ ret = iface_ethtool_ioctl(handle, ETHTOOL_GTSO);
+ if (ret == -1)
+ return -1;
+ if (ret)
+ return 1; /* TCP segmentation offloading on */
+
+ ret = iface_ethtool_ioctl(handle, ETHTOOL_GUFO);
+ if (ret == -1)
+ return -1;
+ if (ret)
+ return 1; /* UDP fragmentation offloading on */
+
+ /*
+ * XXX - will this cause large unsegmented packets to be
+ * handed to PF_PACKET sockets on transmission? If not,
+ * this need not be checked.
+ */
+ ret = iface_ethtool_ioctl(handle, ETHTOOL_GGSO);
+ if (ret == -1)
+ return -1;
+ if (ret)
+ return 1; /* generic segmentation offloading on */
+
+ ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS);
+ if (ret == -1)
+ return -1;
+ if (ret & ETH_FLAG_LRO)
+ return 1; /* large receive offloading on */
+
+ /*
+ * XXX - will this cause large reassembled packets to be
+ * handed to PF_PACKET sockets on receipt? If not,
+ * this need not be checked.
+ */
+ ret = iface_ethtool_ioctl(handle, ETHTOOL_GGRO);
+ if (ret == -1)
+ return -1;
+ if (ret)
+ return 1; /* generic (large) receive offloading on */
+
+ return 0;
+}
+#else /* SIOCETHTOOL */
+static int
+iface_get_offload(pcap_t *handle _U_)
+{
+ /*
+ * XXX - do we need to get this information if we don't
+ * have the ethtool ioctls? If so, how do we do that?
+ */
+ return 0;
+}
+#endif /* SIOCETHTOOL */
+
#endif /* HAVE_PF_PACKET_SOCKETS */
/* ===== Functions to interface to the older kernels ================== */