From fa4435e65c7bd1abb6758062249d3b55bfccf018 Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Sun, 22 Aug 2010 17:36:27 -0700 Subject: [PATCH] Add support for setting the time stamp type for a capture. Based on a patch from Scott Mcmillan . (Second part of the commit.) --- .gitignore | 3 + Makefile.in | 15 ++- config.h.in | 3 + configure | 154 ++++++++++++++++++++- configure.in | 19 ++- pcap-int.h | 3 + pcap-linux.c | 125 +++++++++++++++-- pcap.c | 318 ++++++++++++++++++++++++++++++++------------ pcap/pcap.h | 53 ++++++++ pcap_activate.3pcap | 6 +- 10 files changed, 596 insertions(+), 103 deletions(-) diff --git a/.gitignore b/.gitignore index 7d3f3a6..b8d7f0f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,13 +19,16 @@ pcap-config pcap-filter.manmisc pcap-linktype.manmisc pcap-savefile.manfile +pcap-tstamp.manmisc pcap.3pcap pcap_compile.3pcap pcap_datalink.3pcap pcap_dump_open.3pcap pcap_list_datalinks.3pcap +pcap_list_tstamp_types.3pcap pcap_open_dead.3pcap pcap_open_offline.3pcap +pcap_set_tstamp_type.3pcap scanner.c scanner.h selpolltest diff --git a/Makefile.in b/Makefile.in index dc51f90..129b18e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -157,8 +157,10 @@ MAN3PCAP_EXPAND = \ pcap_datalink.3pcap.in \ pcap_dump_open.3pcap.in \ pcap_list_datalinks.3pcap.in \ + pcap_list_tstamp_types.3pcap.in \ pcap_open_dead.3pcap.in \ - pcap_open_offline.3pcap.in + pcap_open_offline.3pcap.in \ + pcap_set_tstamp_type.3pcap.in MAN3PCAP_NOEXPAND = \ pcap_activate.3pcap \ @@ -201,7 +203,9 @@ MAN3PCAP_NOEXPAND = \ pcap_snapshot.3pcap \ pcap_stats.3pcap \ pcap_statustostr.3pcap \ - pcap_strerror.3pcap + pcap_strerror.3pcap \ + pcap_tstamp_type_name_to_val.3pcap \ + pcap_tstamp_type_val_to_name.3pcap MAN3PCAP = $(MAN3PCAP_NOEXPAND) $(MAN3PCAP_EXPAND:.in=) @@ -210,7 +214,8 @@ MANFILE = \ MANMISC = \ pcap-filter.manmisc.in \ - pcap-linktype.manmisc.in + pcap-linktype.manmisc.in \ + pcap-tstamp.manmisc.in EXTRA_DIST = \ $(TESTS_SRC) \ @@ -561,6 +566,9 @@ install: install-shared install-archive pcap-config rm -f $(DESTDIR)$(mandir)/man3/pcap_free_datalinks.3pcap ln $(DESTDIR)$(mandir)/man3/pcap_list_datalinks.3pcap \ $(DESTDIR)$(mandir)/man3/pcap_free_datalinks.3pcap + rm -f $(DESTDIR)$(mandir)/man3/pcap_free_tstamp_types.3pcap + ln $(DESTDIR)$(mandir)/man3/pcap_list_tstamp_types.3pcap \ + $(DESTDIR)$(mandir)/man3/pcap_free_tstamp_types.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_dispatch.3pcap ln $(DESTDIR)$(mandir)/man3/pcap_loop.3pcap \ $(DESTDIR)$(mandir)/man3/pcap_dispatch.3pcap @@ -648,6 +656,7 @@ uninstall: uninstall-shared rm -f $(DESTDIR)$(mandir)/man3/pcap_perror.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_sendpacket.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_free_datalinks.3pcap + rm -f $(DESTDIR)$(mandir)/man3/pcap_free_tstamp_types.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_dispatch.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_minor_version.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_next.3pcap diff --git a/config.h.in b/config.h.in index 55e13da..f988e8f 100644 --- a/config.h.in +++ b/config.h.in @@ -55,6 +55,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_NET_TSTAMP_H + /* if tp_vlan_tci exists */ #undef HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI diff --git a/configure b/configure index ed7fb6d..e85d87e 100755 --- a/configure +++ b/configure @@ -10787,6 +10787,155 @@ echo "$as_me: no CAN sniffing support implemented for $host_os" >&6;} fi +case "$host_os" in +linux*) + +for ac_header in linux/net_tstamp.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: no hardware timestamp support implemented for $host_os" >&5 +echo "$as_me: no hardware timestamp support implemented for $host_os" >&6;} + ;; +esac + # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: @@ -10871,7 +11020,7 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' ac_config_headers="$ac_config_headers config.h" -ac_config_files="$ac_config_files Makefile pcap-filter.manmisc pcap-linktype.manmisc pcap-savefile.manfile pcap.3pcap pcap_compile.3pcap pcap_datalink.3pcap pcap_dump_open.3pcap pcap_list_datalinks.3pcap pcap_open_dead.3pcap pcap_open_offline.3pcap" +ac_config_files="$ac_config_files Makefile pcap-filter.manmisc pcap-linktype.manmisc pcap-tstamp.manmisc pcap-savefile.manfile pcap.3pcap pcap_compile.3pcap pcap_datalink.3pcap pcap_dump_open.3pcap pcap_list_datalinks.3pcap pcap_list_tstamp_types.3pcap pcap_open_dead.3pcap pcap_open_offline.3pcap pcap_set_tstamp_type.3pcap" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -11430,14 +11579,17 @@ do "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "pcap-filter.manmisc") CONFIG_FILES="$CONFIG_FILES pcap-filter.manmisc" ;; "pcap-linktype.manmisc") CONFIG_FILES="$CONFIG_FILES pcap-linktype.manmisc" ;; + "pcap-tstamp.manmisc") CONFIG_FILES="$CONFIG_FILES pcap-tstamp.manmisc" ;; "pcap-savefile.manfile") CONFIG_FILES="$CONFIG_FILES pcap-savefile.manfile" ;; "pcap.3pcap") CONFIG_FILES="$CONFIG_FILES pcap.3pcap" ;; "pcap_compile.3pcap") CONFIG_FILES="$CONFIG_FILES pcap_compile.3pcap" ;; "pcap_datalink.3pcap") CONFIG_FILES="$CONFIG_FILES pcap_datalink.3pcap" ;; "pcap_dump_open.3pcap") CONFIG_FILES="$CONFIG_FILES pcap_dump_open.3pcap" ;; "pcap_list_datalinks.3pcap") CONFIG_FILES="$CONFIG_FILES pcap_list_datalinks.3pcap" ;; + "pcap_list_tstamp_types.3pcap") CONFIG_FILES="$CONFIG_FILES pcap_list_tstamp_types.3pcap" ;; "pcap_open_dead.3pcap") CONFIG_FILES="$CONFIG_FILES pcap_open_dead.3pcap" ;; "pcap_open_offline.3pcap") CONFIG_FILES="$CONFIG_FILES pcap_open_offline.3pcap" ;; + "pcap_set_tstamp_type.3pcap") CONFIG_FILES="$CONFIG_FILES pcap_set_tstamp_type.3pcap" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} diff --git a/configure.in b/configure.in index 43e8f18..d71d3fd 100644 --- a/configure.in +++ b/configure.in @@ -1447,15 +1447,26 @@ if test "x$enable_can" != "xno" ; then AC_SUBST(CAN_SRC) fi +dnl check for hardware timestamp support +case "$host_os" in +linux*) + AC_CHECK_HEADERS([linux/net_tstamp.h]) + ;; +*) + AC_MSG_NOTICE(no hardware timestamp support implemented for $host_os) + ;; +esac + AC_PROG_INSTALL AC_CONFIG_HEADER(config.h) AC_OUTPUT(Makefile pcap-filter.manmisc pcap-linktype.manmisc - pcap-savefile.manfile pcap.3pcap pcap_compile.3pcap - pcap_datalink.3pcap pcap_dump_open.3pcap - pcap_list_datalinks.3pcap pcap_open_dead.3pcap - pcap_open_offline.3pcap) + pcap-tstamp.manmisc pcap-savefile.manfile pcap.3pcap + pcap_compile.3pcap pcap_datalink.3pcap pcap_dump_open.3pcap + pcap_list_datalinks.3pcap pcap_list_tstamp_types.3pcap + pcap_open_dead.3pcap pcap_open_offline.3pcap + pcap_set_tstamp_type.3pcap) if test -f .devel ; then make depend diff --git a/pcap-int.h b/pcap-int.h index c3afbad..8444e62 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -209,6 +209,7 @@ struct pcap_opt { char *source; int promisc; int rfmon; + int tstamp_type; }; /* @@ -331,6 +332,8 @@ struct pcap { char errbuf[PCAP_ERRBUF_SIZE + 1]; int dlt_count; u_int *dlt_list; + int tstamp_type_count; + u_int *tstamp_type_list; struct pcap_pkthdr pcap_header; /* This is needed for the pcap_next_ex() to work */ }; diff --git a/pcap-linux.c b/pcap-linux.c index 81bc8d3..024b177 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -138,6 +138,11 @@ static const char rcsid[] _U_ = #include #include +#ifdef HAVE_LINUX_NET_TSTAMP_H +#include +#include +#endif + /* * Got Wireless Extensions? */ @@ -411,6 +416,28 @@ pcap_create(const char *device, char *ebuf) handle->activate_op = pcap_activate_linux; handle->can_set_rfmon_op = pcap_can_set_rfmon_linux; +#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) + /* + * We claim that we support: + * + * software time stamps, with no details about their precision; + * hardware time stamps, synced to the host time; + * hardware time stamps, not synced to the host time. + * + * XXX - we can't ask a device whether it supports + * hardware time stamps, so we just claim all devices do. + */ + handle->tstamp_type_count = 3; + handle->tstamp_type_list = malloc(3 * sizeof(u_int)); + if (handle->tstamp_type_list == NULL) { + free(handle); + return NULL; + } + handle->tstamp_type_list[0] = PCAP_TSTAMP_HOST; + handle->tstamp_type_list[1] = PCAP_TSTAMP_ADAPTER; + handle->tstamp_type_list[2] = PCAP_TSTAMP_ADAPTER_UNSYNC; +#endif + return handle; } @@ -1172,7 +1199,8 @@ pcap_activate_linux(pcap_t *handle) * Success. * Try to use memory-mapped access. */ - switch (activate_mmap(handle)) { + status = activate_mmap(handle); + switch (status) { case 1: /* we succeeded; nothing more to do */ @@ -1183,17 +1211,16 @@ pcap_activate_linux(pcap_t *handle) * Kernel doesn't support it - just continue * with non-memory-mapped access. */ - status = 0; break; - case -1: + default: /* * We failed to set up to use it, or kernel * supports it, but we failed to enable it; - * return an error. handle->errbuf contains - * an error message. + * the return value is the error status to + * return and, if it's PCAP_ERROR, handle->errbuf + * contains the error message. */ - status = PCAP_ERROR; goto fail; } } @@ -3083,6 +3110,86 @@ create_ring(pcap_t *handle) frames_per_block = req.tp_block_size/req.tp_frame_size; + /* + * PACKET_TIMESTAMP was added after linux/net_tstamp.h was, + * so we check for PACKET_TIMESTAMP. We check for + * linux/net_tstamp.h just in case a system somehow has + * PACKET_TIMESTAMP but not linux/net_tstamp.h; that might + * be unnecessary. + * + * SIOCSHWTSTAMP was introduced in the patch that introduced + * linux/net_tstamp.h, so we don't bother checking whether + * SIOCSHWTSTAMP is defined (if your Linux system has + * linux/net_tstamp.h but doesn't define SIOCSHWTSTAMP, your + * Linux system is badly broken). + */ +#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) + /* + * If we were told to do so, ask the kernel and the driver + * to use hardware timestamps. + * + * Hardware timestamps are only supported with mmapped + * captures. + */ + if (handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER || + handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER_UNSYNCED) { + struct hwtstamp_config hwconfig; + struct ifreq ifr; + int timesource; + + /* + * Ask for hardware time stamps on all packets, + * including transmitted packets. + */ + memset(&hwconfig, 0, sizeof(hwconfig)); + hwconfig.tx_type = HWTSTAMP_TX_ON; + hwconfig.rx_filter = HWTSTAMP_FILTER_ALL; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, handle->opt.source); + ifr.ifr_data = (void *)&hwconfig; + + if (ioctl(handle->fd, SIOCSHWTSTAMP, &ifr) < 0) { + switch (errno) { + + case EPERM: + return PCAP_ERROR_PERM_DENIED; + + case EOPNOTSUPP: + return PCAP_ERROR_TSTAMP_TYPE_NOTSUP: + + default: + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "SIOCSHWTSTAMP failed: %s", + pcap_strerror(errno)); + return PCAP_ERROR; + } + } + + if (handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER) { + /* + * Hardware timestamp, synchronized + * with the system clock. + */ + timesource = SOF_TIMESTAMPING_SYS_HARDWARE; + } else { + /* + * PCAP_TSTAMP_ADAPTER_UNSYNCED - hardware + * timestamp, not synchronized with the + * system clock. + */ + timesource = SOF_TIMESTAMPING_RAW_HARDWARE; + } + if (setsockopt(handle->fd, SOL_PACKET, PACKET_TIMESTAMP, + (void *)×ource, sizeof(timesource))) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't set PACKET_TIMESTAMP: %s", + pcap_strerror(errno)); + return PCAP_ERROR; + } + } +#endif /* HAVE_LINUX_NET_TSTAMP_H && PACKET_TIMESTAMP */ + /* ask the kernel to create the ring */ retry: req.tp_block_nr = req.tp_frame_nr / frames_per_block; @@ -3117,7 +3224,7 @@ retry: snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't create rx ring on packet socket: %s", pcap_strerror(errno)); - return -1; + return PCAP_ERROR; } /* memory map the rx ring */ @@ -3130,7 +3237,7 @@ retry: /* clear the allocated ring on error*/ destroy_ring(handle); - return -1; + return PCAP_ERROR; } /* allocate a ring for each frame header pointer*/ @@ -3142,7 +3249,7 @@ retry: pcap_strerror(errno)); destroy_ring(handle); - return -1; + return PCAP_ERROR; } /* fill the header ring with proper frame ptr*/ diff --git a/pcap.c b/pcap.c index 7c94f8b..9cb481e 100644 --- a/pcap.c +++ b/pcap.c @@ -104,6 +104,56 @@ pcap_cant_set_rfmon(pcap_t *p _U_) return (0); } +/* + * Sets *tstamp_typesp to point to an array 1 or more supported time stamp + * types; the return value is the number of supported time stamp types. + * The list should be freed by a call to pcap_free_tstamp_types() when + * you're done with it. + * + * A return value of 0 means "you don't get a choice of time stamp type", + * in which case *tstamp_typesp is set to null. + * + * PCAP_ERROR is returned on error. + */ +int +pcap_list_tstamp_types(pcap_t *p, int **tstamp_typesp) +{ + if (p->tstamp_type_count == 0) { + /* + * We don't support multiple time stamp types. + */ + *tstamp_typesp = NULL; + } else { + *tstamp_typesp = (int*)calloc(sizeof(**tstamp_typesp), + p->tstamp_type_count); + if (*tstamp_typesp == NULL) { + (void)snprintf(p->errbuf, sizeof(p->errbuf), + "malloc: %s", pcap_strerror(errno)); + return (PCAP_ERROR); + } + (void)memcpy(*tstamp_typesp, p->tstamp_type_list, + sizeof(**tstamp_typesp) * p->tstamp_type_count); + } + return (p->tstamp_type_count); +} + +/* + * In Windows, you might have a library built with one version of the + * C runtime library and an application built with another version of + * the C runtime library, which means that the library might use one + * version of malloc() and free() and the application might use another + * version of malloc() and free(). If so, that means something + * allocated by the library cannot be freed by the application, so we + * need to have a pcap_free_tstamp_types() routine to free up the list + * allocated by pcap_list_tstamp_types(), even though it's just a wrapper + * around free(). + */ +void +pcap_free_tstamp_types(int *tstamp_type_list) +{ + free(tstamp_type_list); +} + /* * Default one-shot callback; overridden for capture types where the * packet data cannot be guaranteed to be available after the callback @@ -258,6 +308,7 @@ pcap_create_common(const char *source, char *ebuf) pcap_set_snaplen(p, 65535); /* max packet size */ p->opt.promisc = 0; p->opt.buffer_size = 0; + p->opt.tstamp_type = -1; /* default to not setting time stamp type */ return (p); } @@ -308,6 +359,41 @@ pcap_set_timeout(pcap_t *p, int timeout_ms) return 0; } +int +pcap_set_tstamp_type(pcap_t *p, int tstamp_type) +{ + int i; + + if (pcap_check_activated(p)) + return PCAP_ERROR_ACTIVATED; + + /* + * If p->tstamp_type_count is 0, we don't support setting + * the time stamp type at all. + */ + if (p->tstamp_type_count == 0) + return PCAP_ERROR_CANTSET_TSTAMP_TYPE; + + /* + * Check whether we claim to support this type of time stamp. + */ + for (i = 0; i < p->tstamp_type_count; i++) { + if (p->tstamp_type_list[i] == tstamp_type) { + /* + * Yes. + */ + p->opt.tstamp_type = tstamp_type; + return 0; + } + } + + /* + * No. We support setting the time stamp type, but not to this + * particular value. + */ + return PCAP_ERROR_TSTAMP_TYPE_NOTSUP; +} + int pcap_set_buffer_size(pcap_t *p, int buffer_size) { @@ -571,6 +657,91 @@ unsupported: return (-1); } +/* + * This array is designed for mapping upper and lower case letter + * together for a case independent comparison. The mappings are + * based upon ascii character sequences. + */ +static const u_char charmap[] = { + (u_char)'\000', (u_char)'\001', (u_char)'\002', (u_char)'\003', + (u_char)'\004', (u_char)'\005', (u_char)'\006', (u_char)'\007', + (u_char)'\010', (u_char)'\011', (u_char)'\012', (u_char)'\013', + (u_char)'\014', (u_char)'\015', (u_char)'\016', (u_char)'\017', + (u_char)'\020', (u_char)'\021', (u_char)'\022', (u_char)'\023', + (u_char)'\024', (u_char)'\025', (u_char)'\026', (u_char)'\027', + (u_char)'\030', (u_char)'\031', (u_char)'\032', (u_char)'\033', + (u_char)'\034', (u_char)'\035', (u_char)'\036', (u_char)'\037', + (u_char)'\040', (u_char)'\041', (u_char)'\042', (u_char)'\043', + (u_char)'\044', (u_char)'\045', (u_char)'\046', (u_char)'\047', + (u_char)'\050', (u_char)'\051', (u_char)'\052', (u_char)'\053', + (u_char)'\054', (u_char)'\055', (u_char)'\056', (u_char)'\057', + (u_char)'\060', (u_char)'\061', (u_char)'\062', (u_char)'\063', + (u_char)'\064', (u_char)'\065', (u_char)'\066', (u_char)'\067', + (u_char)'\070', (u_char)'\071', (u_char)'\072', (u_char)'\073', + (u_char)'\074', (u_char)'\075', (u_char)'\076', (u_char)'\077', + (u_char)'\100', (u_char)'\141', (u_char)'\142', (u_char)'\143', + (u_char)'\144', (u_char)'\145', (u_char)'\146', (u_char)'\147', + (u_char)'\150', (u_char)'\151', (u_char)'\152', (u_char)'\153', + (u_char)'\154', (u_char)'\155', (u_char)'\156', (u_char)'\157', + (u_char)'\160', (u_char)'\161', (u_char)'\162', (u_char)'\163', + (u_char)'\164', (u_char)'\165', (u_char)'\166', (u_char)'\167', + (u_char)'\170', (u_char)'\171', (u_char)'\172', (u_char)'\133', + (u_char)'\134', (u_char)'\135', (u_char)'\136', (u_char)'\137', + (u_char)'\140', (u_char)'\141', (u_char)'\142', (u_char)'\143', + (u_char)'\144', (u_char)'\145', (u_char)'\146', (u_char)'\147', + (u_char)'\150', (u_char)'\151', (u_char)'\152', (u_char)'\153', + (u_char)'\154', (u_char)'\155', (u_char)'\156', (u_char)'\157', + (u_char)'\160', (u_char)'\161', (u_char)'\162', (u_char)'\163', + (u_char)'\164', (u_char)'\165', (u_char)'\166', (u_char)'\167', + (u_char)'\170', (u_char)'\171', (u_char)'\172', (u_char)'\173', + (u_char)'\174', (u_char)'\175', (u_char)'\176', (u_char)'\177', + (u_char)'\200', (u_char)'\201', (u_char)'\202', (u_char)'\203', + (u_char)'\204', (u_char)'\205', (u_char)'\206', (u_char)'\207', + (u_char)'\210', (u_char)'\211', (u_char)'\212', (u_char)'\213', + (u_char)'\214', (u_char)'\215', (u_char)'\216', (u_char)'\217', + (u_char)'\220', (u_char)'\221', (u_char)'\222', (u_char)'\223', + (u_char)'\224', (u_char)'\225', (u_char)'\226', (u_char)'\227', + (u_char)'\230', (u_char)'\231', (u_char)'\232', (u_char)'\233', + (u_char)'\234', (u_char)'\235', (u_char)'\236', (u_char)'\237', + (u_char)'\240', (u_char)'\241', (u_char)'\242', (u_char)'\243', + (u_char)'\244', (u_char)'\245', (u_char)'\246', (u_char)'\247', + (u_char)'\250', (u_char)'\251', (u_char)'\252', (u_char)'\253', + (u_char)'\254', (u_char)'\255', (u_char)'\256', (u_char)'\257', + (u_char)'\260', (u_char)'\261', (u_char)'\262', (u_char)'\263', + (u_char)'\264', (u_char)'\265', (u_char)'\266', (u_char)'\267', + (u_char)'\270', (u_char)'\271', (u_char)'\272', (u_char)'\273', + (u_char)'\274', (u_char)'\275', (u_char)'\276', (u_char)'\277', + (u_char)'\300', (u_char)'\341', (u_char)'\342', (u_char)'\343', + (u_char)'\344', (u_char)'\345', (u_char)'\346', (u_char)'\347', + (u_char)'\350', (u_char)'\351', (u_char)'\352', (u_char)'\353', + (u_char)'\354', (u_char)'\355', (u_char)'\356', (u_char)'\357', + (u_char)'\360', (u_char)'\361', (u_char)'\362', (u_char)'\363', + (u_char)'\364', (u_char)'\365', (u_char)'\366', (u_char)'\367', + (u_char)'\370', (u_char)'\371', (u_char)'\372', (u_char)'\333', + (u_char)'\334', (u_char)'\335', (u_char)'\336', (u_char)'\337', + (u_char)'\340', (u_char)'\341', (u_char)'\342', (u_char)'\343', + (u_char)'\344', (u_char)'\345', (u_char)'\346', (u_char)'\347', + (u_char)'\350', (u_char)'\351', (u_char)'\352', (u_char)'\353', + (u_char)'\354', (u_char)'\355', (u_char)'\356', (u_char)'\357', + (u_char)'\360', (u_char)'\361', (u_char)'\362', (u_char)'\363', + (u_char)'\364', (u_char)'\365', (u_char)'\366', (u_char)'\367', + (u_char)'\370', (u_char)'\371', (u_char)'\372', (u_char)'\373', + (u_char)'\374', (u_char)'\375', (u_char)'\376', (u_char)'\377', +}; + +int +pcap_strcasecmp(const char *s1, const char *s2) +{ + register const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + while (cm[*us1] == cm[*us2++]) + if (*us1++ == '\0') + return(0); + return (cm[*us1] - cm[*--us2]); +} + struct dlt_choice { const char *name; const char *description; @@ -677,91 +848,6 @@ static struct dlt_choice dlt_choices[] = { DLT_CHOICE_SENTINEL }; -/* - * This array is designed for mapping upper and lower case letter - * together for a case independent comparison. The mappings are - * based upon ascii character sequences. - */ -static const u_char charmap[] = { - (u_char)'\000', (u_char)'\001', (u_char)'\002', (u_char)'\003', - (u_char)'\004', (u_char)'\005', (u_char)'\006', (u_char)'\007', - (u_char)'\010', (u_char)'\011', (u_char)'\012', (u_char)'\013', - (u_char)'\014', (u_char)'\015', (u_char)'\016', (u_char)'\017', - (u_char)'\020', (u_char)'\021', (u_char)'\022', (u_char)'\023', - (u_char)'\024', (u_char)'\025', (u_char)'\026', (u_char)'\027', - (u_char)'\030', (u_char)'\031', (u_char)'\032', (u_char)'\033', - (u_char)'\034', (u_char)'\035', (u_char)'\036', (u_char)'\037', - (u_char)'\040', (u_char)'\041', (u_char)'\042', (u_char)'\043', - (u_char)'\044', (u_char)'\045', (u_char)'\046', (u_char)'\047', - (u_char)'\050', (u_char)'\051', (u_char)'\052', (u_char)'\053', - (u_char)'\054', (u_char)'\055', (u_char)'\056', (u_char)'\057', - (u_char)'\060', (u_char)'\061', (u_char)'\062', (u_char)'\063', - (u_char)'\064', (u_char)'\065', (u_char)'\066', (u_char)'\067', - (u_char)'\070', (u_char)'\071', (u_char)'\072', (u_char)'\073', - (u_char)'\074', (u_char)'\075', (u_char)'\076', (u_char)'\077', - (u_char)'\100', (u_char)'\141', (u_char)'\142', (u_char)'\143', - (u_char)'\144', (u_char)'\145', (u_char)'\146', (u_char)'\147', - (u_char)'\150', (u_char)'\151', (u_char)'\152', (u_char)'\153', - (u_char)'\154', (u_char)'\155', (u_char)'\156', (u_char)'\157', - (u_char)'\160', (u_char)'\161', (u_char)'\162', (u_char)'\163', - (u_char)'\164', (u_char)'\165', (u_char)'\166', (u_char)'\167', - (u_char)'\170', (u_char)'\171', (u_char)'\172', (u_char)'\133', - (u_char)'\134', (u_char)'\135', (u_char)'\136', (u_char)'\137', - (u_char)'\140', (u_char)'\141', (u_char)'\142', (u_char)'\143', - (u_char)'\144', (u_char)'\145', (u_char)'\146', (u_char)'\147', - (u_char)'\150', (u_char)'\151', (u_char)'\152', (u_char)'\153', - (u_char)'\154', (u_char)'\155', (u_char)'\156', (u_char)'\157', - (u_char)'\160', (u_char)'\161', (u_char)'\162', (u_char)'\163', - (u_char)'\164', (u_char)'\165', (u_char)'\166', (u_char)'\167', - (u_char)'\170', (u_char)'\171', (u_char)'\172', (u_char)'\173', - (u_char)'\174', (u_char)'\175', (u_char)'\176', (u_char)'\177', - (u_char)'\200', (u_char)'\201', (u_char)'\202', (u_char)'\203', - (u_char)'\204', (u_char)'\205', (u_char)'\206', (u_char)'\207', - (u_char)'\210', (u_char)'\211', (u_char)'\212', (u_char)'\213', - (u_char)'\214', (u_char)'\215', (u_char)'\216', (u_char)'\217', - (u_char)'\220', (u_char)'\221', (u_char)'\222', (u_char)'\223', - (u_char)'\224', (u_char)'\225', (u_char)'\226', (u_char)'\227', - (u_char)'\230', (u_char)'\231', (u_char)'\232', (u_char)'\233', - (u_char)'\234', (u_char)'\235', (u_char)'\236', (u_char)'\237', - (u_char)'\240', (u_char)'\241', (u_char)'\242', (u_char)'\243', - (u_char)'\244', (u_char)'\245', (u_char)'\246', (u_char)'\247', - (u_char)'\250', (u_char)'\251', (u_char)'\252', (u_char)'\253', - (u_char)'\254', (u_char)'\255', (u_char)'\256', (u_char)'\257', - (u_char)'\260', (u_char)'\261', (u_char)'\262', (u_char)'\263', - (u_char)'\264', (u_char)'\265', (u_char)'\266', (u_char)'\267', - (u_char)'\270', (u_char)'\271', (u_char)'\272', (u_char)'\273', - (u_char)'\274', (u_char)'\275', (u_char)'\276', (u_char)'\277', - (u_char)'\300', (u_char)'\341', (u_char)'\342', (u_char)'\343', - (u_char)'\344', (u_char)'\345', (u_char)'\346', (u_char)'\347', - (u_char)'\350', (u_char)'\351', (u_char)'\352', (u_char)'\353', - (u_char)'\354', (u_char)'\355', (u_char)'\356', (u_char)'\357', - (u_char)'\360', (u_char)'\361', (u_char)'\362', (u_char)'\363', - (u_char)'\364', (u_char)'\365', (u_char)'\366', (u_char)'\367', - (u_char)'\370', (u_char)'\371', (u_char)'\372', (u_char)'\333', - (u_char)'\334', (u_char)'\335', (u_char)'\336', (u_char)'\337', - (u_char)'\340', (u_char)'\341', (u_char)'\342', (u_char)'\343', - (u_char)'\344', (u_char)'\345', (u_char)'\346', (u_char)'\347', - (u_char)'\350', (u_char)'\351', (u_char)'\352', (u_char)'\353', - (u_char)'\354', (u_char)'\355', (u_char)'\356', (u_char)'\357', - (u_char)'\360', (u_char)'\361', (u_char)'\362', (u_char)'\363', - (u_char)'\364', (u_char)'\365', (u_char)'\366', (u_char)'\367', - (u_char)'\370', (u_char)'\371', (u_char)'\372', (u_char)'\373', - (u_char)'\374', (u_char)'\375', (u_char)'\376', (u_char)'\377', -}; - -int -pcap_strcasecmp(const char *s1, const char *s2) -{ - register const u_char *cm = charmap, - *us1 = (const u_char *)s1, - *us2 = (const u_char *)s2; - - while (cm[*us1] == cm[*us2++]) - if (*us1++ == '\0') - return(0); - return (cm[*us1] - cm[*--us2]); -} - int pcap_datalink_name_to_val(const char *name) { @@ -799,6 +885,57 @@ pcap_datalink_val_to_description(int dlt) return (NULL); } +struct tstamp_type_choice { + const char *name; + const char *description; + int type; +}; + +static struct tstamp_type_choice tstamp_type_choices[] = { + { "host", "Host", PCAP_TSTAMP_HOST }, + { "host_lowprec", "Host, low precision", PCAP_TSTAMP_HOST_LOWPREC }, + { "host_hiprec", "Host, high precision", PCAP_TSTAMP_HOST_HIPREC }, + { "adapter", "Adapter", PCAP_TSTAMP_ADAPTER }, + { "adapter_unsynced", "Adapter, not synced with system time", PCAP_TSTAMP_ADAPTER_UNSYNCED }, + { NULL, NULL, 0 } +}; + +int +pcap_tstamp_type_name_to_val(const char *name) +{ + int i; + + for (i = 0; tstamp_type_choices[i].name != NULL; i++) { + if (pcap_strcasecmp(tstamp_type_choices[i].name, name) == 0) + return (tstamp_type_choices[i].type); + } + return (PCAP_ERROR); +} + +const char * +pcap_tstamp_type_val_to_name(int tstamp_type) +{ + int i; + + for (i = 0; tstamp_type_choices[i].name != NULL; i++) { + if (tstamp_type_choices[i].type == tstamp_type) + return (tstamp_type_choices[i].name); + } + return (NULL); +} + +const char * +pcap_tstamp_type_val_to_description(int tstamp_type) +{ + int i; + + for (i = 0; tstamp_type_choices[i].name != NULL; i++) { + if (tstamp_type_choices[i].type == tstamp_type) + return (tstamp_type_choices[i].description); + } + return (NULL); +} + int pcap_snapshot(pcap_t *p) { @@ -1006,6 +1143,12 @@ pcap_statustostr(int errnum) case PCAP_ERROR_IFACE_NOT_UP: return ("That device is not up"); + + case PCAP_ERROR_CANTSET_TSTAMP_TYPE: + return ("That device doesn't support setting the time stamp type"); + + case PCAP_ERROR_TSTAMP_TYPE_NOTSUP: + return ("That type of time stamp is not supported by that device"); } (void)snprintf(ebuf, sizeof ebuf, "Unknown error: %d", errnum); return(ebuf); @@ -1213,6 +1356,11 @@ pcap_cleanup_live_common(pcap_t *p) p->dlt_list = NULL; p->dlt_count = 0; } + if (p->tstamp_type_list != NULL) { + free(p->tstamp_type_list); + p->tstamp_type_list = NULL; + p->tstamp_type_count = 0; + } pcap_freecode(&p->fcode); #if !defined(WIN32) && !defined(MSDOS) if (p->fd >= 0) { diff --git a/pcap/pcap.h b/pcap/pcap.h index 05ba31f..8fcca5a 100644 --- a/pcap/pcap.h +++ b/pcap/pcap.h @@ -251,6 +251,8 @@ typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, #define PCAP_ERROR_NOT_RFMON -7 /* operation supported only in monitor mode */ #define PCAP_ERROR_PERM_DENIED -8 /* no permission to open the device */ #define PCAP_ERROR_IFACE_NOT_UP -9 /* interface isn't up */ +#define PCAP_ERROR_CANTSET_TSTAMP_TYPE -10 /* this device doesn't support setting the time stamp type */ +#define PCAP_ERROR_TSTAMP_TYPE_NOTSUP -11 /* the requested time stamp type is not supported */ /* * Warning codes for the pcap API. @@ -275,9 +277,60 @@ int pcap_set_promisc(pcap_t *, int); int pcap_can_set_rfmon(pcap_t *); int pcap_set_rfmon(pcap_t *, int); int pcap_set_timeout(pcap_t *, int); +int pcap_set_tstamp_type(pcap_t *, int); int pcap_set_buffer_size(pcap_t *, int); int pcap_activate(pcap_t *); +int pcap_list_tstamp_types(pcap_t *, int **); +void pcap_free_tstamp_types(int *); +int pcap_tstamp_type_name_to_val(const char *); +const char *pcap_tstamp_type_val_to_name(int); +const char *pcap_tstamp_type_val_to_description(int); + +/* + * Time stamp types. + * Not all systems and interfaces will necessarily support all of these. + * + * A system that supports PCAP_TSTAMP_HOST is offering time stamps + * provided by the host machine, rather than by the capture device, + * but not committing to any characteristics of the time stamp; + * it will not offer any of the PCAP_TSTAMP_HOST_ subtypes. + * + * PCAP_TSTAMP_HOST_LOWPREC is a time stamp, provided by the host machine, + * that's low-precision but relatively cheap to fetch; it's normally done + * using the system clock, so it's normally synchronized with times you'd + * fetch from system calls. + * + * PCAP_TSTAMP_HOST_HIPREC is a time stamp, provided by the host machine, + * that's high-precision; it might be more expensive to fetch. It might + * or might not be synchronized with the system clock, and might have + * problems with time stamps for packets received on different CPUs, + * depending on the platform. + * + * PCAP_TSTAMP_ADAPTER is a high-precision time stamp supplied by the + * capture device; it's synchronized with the system clock. + * + * PCAP_TSTAMP_ADAPTER_UNSYNC is a high-precision time stamp supplied by + * the capture device; it's not synchronized with the system clock. + * + * Note that time stamps synchronized with the system clock can go + * backwards, as the system clock can go backwards. If a clock is + * not in sync with the system clock, that could be because the + * system clock isn't keeping accurate time, because the other + * clock isn't keeping accurate time, or both. + * + * Note that host-provided time stamps generally correspond to the + * time when the time-stamping code sees the packet; this could + * be some unknown amount of time after the first or last bit of + * the packet is received by the network adapter, due to batching + * of interrupts for packet arrival, queueing delays, etc.. + */ +#define PCAP_TSTAMP_HOST 0 /* host-provided, unknown characteristics */ +#define PCAP_TSTAMP_HOST_LOWPREC 1 /* host-provided, low precision */ +#define PCAP_TSTAMP_HOST_HIPREC 2 /* host-provided, high precision */ +#define PCAP_TSTAMP_ADAPTER 3 /* device-provided, synced with the system clock */ +#define PCAP_TSTAMP_ADAPTER_UNSYNCED 4 /* device-provided, not synced with the system clock */ + pcap_t *pcap_open_live(const char *, int, int, int, char *); pcap_t *pcap_open_dead(int, int); pcap_t *pcap_open_offline(const char *, char *); diff --git a/pcap_activate.3pcap b/pcap_activate.3pcap index b33fa53..219e450 100644 --- a/pcap_activate.3pcap +++ b/pcap_activate.3pcap @@ -56,7 +56,11 @@ if the process doesn't have permission to open the capture source, if monitor mode was specified but the capture source doesn't support monitor mode, .B PCAP_ERROR_IFACE_NOT_UP -if the capture source is not up, and +if the capture source is not up, +.B PCAP_ERROR_TSTAMP_TYPE_NOTSUP +if the time stamp specified in a previous +.B pcap_set_tstamp_type() +call isn't supported by the capture source, and .B PCAP_ERROR if another error occurred. If