Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can Npcap use KeQuerySystemTimePrecise() if running on Windows 8 or later? #71

Closed
guyharris opened this issue Dec 8, 2018 · 10 comments
Closed

Comments

@guyharris
Copy link
Contributor

Somebody complained about packet time stamps drifting from system time with WinPcap on Windows 10.

WinPcap has, at least on 32-bit Windows, a registry parameter HKLM\System\CurrentControlSet\Services\NPF\TimestampMode that controls how time stamps are generated:

  • 0 (default) -> Timestamps generated through KeQueryPerformanceCounter, less reliable on SMP/HyperThreading machines, precision = some microseconds
  • 2 -> Timestamps generated through KeQuerySystemTime, more reliable on SMP/HyperThreading machines, precision = scheduling quantum (10/15 ms)
  • 3 -> Timestamps generated through the i386 instruction RDTSC, less reliable on SMP/HyperThreading/SpeedStep machines, precision = some microseconds

There also appears to be a setting of 1, the symbol for which in time_calls.h is TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_WITH_FIXUP, and a setting of 99, the symbol for which is TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_NO_FIXUP.

It looks as if, on 64-bit Windows, the RDTSC version isn't supported.

From a quick look at the Npcap driver, it seems to behave similarly, although the NT GET_TIME() code went from

#ifdef _X86_
	if ( TimestampMode == TIMESTAMPMODE_RDTSC )
    	{
		GetTimeRDTSC(dst,data);
	}
	else
#endif // _X86_
	if ( TimestampMode == TIMESTAMPMODE_QUERYSYSTEMTIME )
	{
		GetTimeQST(dst,data);
	}
	else
	{
		GetTimeKQPC(dst,data);
	}

in WinPcap to

#ifdef _X86_
	if (g_TimestampMode == TIMESTAMPMODE_RDTSC)
	{
		GetTimeRDTSC(dst, data);
	}
	else
		#endif // _X86_
	if (g_TimestampMode == TIMESTAMPMODE_QUERYSYSTEMTIME)
	{
		GetTimeQST(dst, data);
	}
	else
	{
		GetTimeKQPC(dst, data);
	}

on Npcap - the mis-indentation of the #endif makes it less clear that modes 0 and 2 are still supported.

If a mode were supported in which KeQuerySystemTimePrecise() were used, at least on Windows 8 (and its server variant) and later, that would provide a mode that 1) gives high-resolution time stamps and 2) doesn't drift from the system clock, although it might be more costly than using KeQueryPerformanceCounter(). (And if it's not more costly, it's probably the best mode on 64-bit Windows, given that RDTSC isn't being used.)

Furthermore, given that there's now a pcap_set_tstamp_type() in libpcap, having the time stamp type be per-pcap_t, rather than system-wide, might be useful. You'd offer a choice between:

  • PCAP_TSTAMP_HOST - this would use KeQuerySystemTime() prior to Windows 8 and KeQuerySystemTimePrecise() on Windows 8 and later, and would always be available (it's the default in libpcap);
  • PCAP_TSTAMP_HOST_LOWPREC - this would always use KeQuerySystemTime(), and would be always be available;
  • PCAP_TSTAMP_HOST_HIPREC - this would always use KeQuerySystemTimePrecise(), and would only be available on Windows 8 and later.

If the other time stamp types are useful, we could add new PCAP_TSTAMP_ types for them, and make them available only on Windows (the PCAP_TSTAMP_ADAPTER types are available only on Linux - and they have a number of problems, not the least of which is that they give time as seconds since January 1, 1970, 00:00:00 TAI with at least some adapters; don't get me started on UN*X time stamps and leap seconds...).

@Nazardo
Copy link

Nazardo commented Oct 30, 2019

I am very much interested in this feature as I use npcap to sniff and timestamp traffic that will need to be correlated with other traffic captured on different hosts. NTP is used to keep system clocks synchronized.
Default timestamping mode based on KeQueryPerformanceCounter() causes a drift from the system clock.
KeQuerySystemTime() does not give me the precision I want, so I really look forward to the possibility of using KeQuerySystemTimePrecise().

Can I submit a pull request with this change?

@guyharris
Copy link
Contributor Author

Can I submit a pull request with this change?

As long as either

  1. it somehow arranges to avoid using KeQuerySystemTimePrecise() on Windows Vista and Windows 7, whether that can be done with a single driver that checks the version of the kernel in which it's running or must be done by building two versions of the driver and installing the appropriate driver based on the OS version on which it's being installed (I suspect the answer is that two versions will be required)

or

  1. the Npcap developers are willing to drop support for Windows Vista and Windows 7.

@Nazardo
Copy link

Nazardo commented Oct 30, 2019

a single driver that checks the version of the kernel in which it's running

I think it may be possible using a combination of RtlIsNtDdiVersionAvailable() and MmGetSystemRoutineAddress(). It is worth a try.

@Nazardo
Copy link

Nazardo commented Nov 11, 2019

I am posting a little update here to answer the question in the title: yes, it is possible to use KeQuerySystemTimePrecise() when running on Windows 8 or later, instead of the imprecise KeQuerySystemTime().

In the thread of #19 comments, @guyharris and I talked about the possibility to use the new precise time-of-day API as a single alternative to current time modes (mainly QPC and QST).
After some thinking, I would like to propose to merge #19. It is a single call replacement that will not break existing application using npcap time mode settings (whether they are officially documented or not) and will provide greater precision on Windows 8 and later.
Microseconds precision and coherency with a wall clock is a much-needed requirement for distributed monitoring.

Further changes to timestamping can be maybe discussed in a separate issue or into #46.
What do you think? 😉

@guyharris
Copy link
Contributor Author

So the first thing to do here might be to remove some undocumented time stamp types that might have been failed experiments at better supporting multiple CPUs with KeQueryPerformanceCounter(); given that Nmap is either Vista-and-later or Windows 7-and-later, where I think KeQueryPerformanceCounter() ensures cross-CPU consistence, those modes may no longer be necessary. I've filed nmap/nmap#1829 on that.

I have some cleanup to do on libpcap's timestamp types:

  • add PCAP_TSTAMP_HOST_HIPREC_UNSYNCED and use that for TIMESTAMPMODE_SINGLE_SYNCHRONIZATION, which isn't synchronized with the host OS's clock;
  • change the definition of PCAP_TSTAMP_HOST_LOWPREC to be synchronized with the host OS's clock, and use that for TIMESTAMPMODE_QUERYSYSTEMTIME
  • change the definition of PCAP_TSTAMP_HOST_HIPREC to be synchronized with the host OS's clock, so that we can use it for a new TIMESTAMPMODE_QUERYSYSTEMTIMEPRECISE mode.

I'm also working on making the time stamp modes per-instance; I'll submit a pull request once that's done. (I'll probably wait until nmap/nmap#1829 is resolved; there's extra crud I won't have to worry about if it's resolved as "kill those old modes".)

dmiller-nmap referenced this issue Mar 16, 2020
This mode uses KeQuerySystemTimePrecise if available. On Win7/2008R2, it
falls back to KeQuerySystemTime, equivalent to TimestampMode 2. While
this is not as precise as TIMESTAMPMODE_SINGLE_SYNCHRONIZATION, it at
least does not drift from wall clock time in the same way. I believe the
behavior (monotonic vs synchronized) is a more important distinction to
preserve than the precision.

Once we have per-capture-handle timestamp modes, we can probably have a
feedback mechanism to inform the caller whether a timestamp mode is
supported on the current platform.

See nmap/nmap#1407
@guyharris
Copy link
Contributor Author

So should we have the default time stamp type, if the HKLM\System\CurrentControlSet\Services\NPF\TimestampMode registry key is absent, be:

  • TIMESTAMPMODE_SINGLE_SYNCHRONIZATION if KeQuerySystemTimePrecise() isn't available;

  • TIMESTAMPMODE_QUERYSYSTEMTIMEPRECISE if KeQuerySystemTimePrecise() isn't available is available?

That way, if the registry key isn't set, Windows 8 and later will default to a time stamp type that is 1) high resolution and 2) synchronized with the system time.

If so, then, unless there's a compelling reason to use TIMESTAMPMODE_SINGLE_SYNCHRONIZATION or TIMESTAMPMODE_QUERYSYSTEMTIMErather thanTIMESTAMPMODE_QUERYSYSTEMTIMEPRECISE, perhaps on Windows 8 and later there's no need to offer a choice of time stamp types through the pcap API. Most if not all UN\*Xes currently do the equivalent of TIMESTAMPMODE_QUERYSYSTEMTIMEPRECISEwith kernel APIs that provide high-resolution time stamps - and that are used forgettimeofday()`, so they are inherently synchronized with the system time.

The only compelling reason I can think of would be performance, i.e. if KeQuerySystemTimePrecise() is sufficiently more expensive than KeQuerySystemTime() or KeQueryPerformanceCounter() somebody might want to be able to use one of the latter, if, in the first case, they didn't care about high precision or, in the latter case, they didn't care about being synchronized with the system clock.

On Windows 7, we could offer a choice between TIMESTAMPMODE_SINGLE_SYNCHRONIZATION and TIMESTAMPMODE_QUERYSYSTEMTIME. Windows 7 is out of support, though, so I'm not sure to what extent supporting that in the pcap API, rather than at a system-wide level via the registry key, would be worth the effort. It might have the advantage of reducing the number of systems having the registry key set; that would mean that, with my proposal above, those systems would automatically switch to TIMESTAMPMODE_QUERYSYSTEMTIMEPRECISE if upgraded to Windows 10.

@fyodor fyodor transferred this issue from nmap/nmap May 20, 2020
@guyharris
Copy link
Contributor Author

So should we have the default time stamp type, if the HKLM\System\CurrentControlSet\Services\NPF\TimestampMode registry key is absent, be:

  • TIMESTAMPMODE_SINGLE_SYNCHRONIZATION if KeQuerySystemTimePrecise() isn't available;
  • TIMESTAMPMODE_QUERYSYSTEMTIMEPRECISE if KeQuerySystemTimePrecise() isn't available is available?

That way, if the registry key isn't set, Windows 8 and later will default to a time stamp type that is 1) high resolution and 2) synchronized with the system time.

This would let Wireshark and tcpdump get high-precision times on Windows 8 and later without any source changes.

@guyharris
Copy link
Contributor Author

OK, I've checked in the libpcap changes I used to test the new PacketGetTimestampModes() code as the-tcpdump-group/libpcap@b75571b. Backport those to Npcap's libpcap code and give that a try on Windows 10, using tcpdump.

@matman13
Copy link

matman13 commented Feb 16, 2021

For now, we are having to run npcap in WinPcap-compatibility mode to support our legacy software. To use the TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE timestamping mode, I would need to set the HKLM\System\CurrentControlSet\Services\NPF\TimestampMode key or the HKLM\System\CurrentControlSet\Services\npcap\TimestampMode key? And do I set the value to 2 or 4? FWIW, I'm using Npcap OEM 1.10. Is it defaulted to the TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE value?

@fyodor
Copy link
Member

fyodor commented Mar 7, 2022

Thanks everyone. I believe this was implemented a while back and so this issue can be closed. It's documented in the Npcap timestamp guide (which comes from libpcap). So it's available on a per-handle basis rather than being systemwide like with the old Winpcap registry entries. Some comments from @dmiller-nmap: The default is HIPREC_UNSYNCED, which uses KeQuerySystemTimePrecise once at the beginning of the capture, then uses KeQueryPerformanceCounter to stamp later packets based on CPU ticks. We call that TIMESTAMPMODE_SINGLE_SYNCHRONIZATION. The TIMESTAMPMODE_QUERYSYSTEMTIME calls KeQuerySystemTime for every packet, and was supported (registry entry for whole system) by WinPcap. Libpcap calls that PCAP_TSTAMP_HOST_LOWPREC. The new mode is TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE, which libpcap calls PCAP_TSTAMP_HOST_HIPREC, which calls KeQuerySystemTimePrecise for every packet. Both those last 2 do not drift from wall clock time, but may run forwards or backwards according to leap seconds, time zones, system time changes, etc. But the default is a monotonic (always increasing) clock. Both are needed in different situations.

@fyodor fyodor closed this as completed Mar 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants