X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/f0df874a0bce6d3adf7ccb8b6da28cb0f124e45a..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/pcap-npf.c diff --git a/pcap-npf.c b/pcap-npf.c index 60e08228..99b5981e 100644 --- a/pcap-npf.c +++ b/pcap-npf.c @@ -36,6 +36,7 @@ #endif #include +#include /* for INT_MAX */ #define PCAP_DONT_INCLUDE_PCAP_BPF_H #include #include @@ -445,6 +446,31 @@ pcap_setuserbuffer_npf(pcap_t *p, int size) return (0); } +#ifdef HAVE_NPCAP_PACKET_API +/* + * Kernel dump mode isn't supported in Npcap; calls to PacketSetDumpName(), + * PacketSetDumpLimits(), and PacketIsDumpEnded() will get compile-time + * deprecation warnings. + * + * Avoid calling them; just return errors indicating that kernel dump + * mode isn't supported in Npcap. + */ +static int +pcap_live_dump_npf(pcap_t *p, char *filename _U_, int maxsize _U_, + int maxpacks _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Npcap doesn't support kernel dump mode"); + return (-1); +} +static int +pcap_live_dump_ended_npf(pcap_t *p, int sync) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Npcap doesn't support kernel dump mode"); + return (-1); +} +#else /* HAVE_NPCAP_PACKET_API */ static int pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks) { @@ -485,6 +511,7 @@ pcap_live_dump_ended_npf(pcap_t *p, int sync) return (PacketIsDumpEnded(pw->adapter, (BOOLEAN)sync)); } +#endif /* HAVE_NPCAP_PACKET_API */ #ifdef HAVE_AIRPCAP_API static PAirpcapHandle @@ -607,6 +634,9 @@ pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) /* * Loop through each packet. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. */ #define bhp ((struct bpf_hdr *)bp) n = 0; @@ -766,6 +796,21 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) endofbuf = (char*)header + cc; + /* + * This can conceivably process more than INT_MAX packets, + * which would overflow the packet count, causing it either + * to look like a negative number, and thus cause us to + * return a value that looks like an error, or overflow + * back into positive territory, and thus cause us to + * return a too-low count. + * + * Therefore, if the packet count is unlimited, we clip + * it at INT_MAX; this routine is not expected to + * process packets indefinitely, so that's not an issue. + */ + if (PACKET_COUNT_IS_UNLIMITED(cnt)) + cnt = INT_MAX; + /* * Cycle through the packets */ @@ -856,7 +901,7 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } } - /* No underlaying filtering system. We need to filter on our own */ + /* No underlying filtering system. We need to filter on our own */ if (p->fcode.bf_insns) { if (pcap_filter(p->fcode.bf_insns, dp, packet_len, caplen) == 0) @@ -867,7 +912,7 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } } - /* Fill the header for the user suppplied callback function */ + /* Fill the header for the user supplied callback function */ pcap_header.caplen = caplen; pcap_header.len = packet_len; @@ -940,8 +985,6 @@ pcap_breakloop_npf(pcap_t *p) } /* - * Vendor-specific error codes. - * * These are NTSTATUS values: * * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/87fba13e-bf06-450e-83b1-9241dc81e781 @@ -950,15 +993,28 @@ pcap_breakloop_npf(pcap_t *p) * mapped to Windows error values in userland; they're returned by * GetLastError(). * - * Attempting to set non-promiscuous mode on a Microsoft Surface Pro's - * Mobile Broadband Adapter returns an error; that error can safely be - * ignored, as it's always in non-promiscuous mode. + * Note that "driver" here includes the Npcap NPF driver, as various + * versions would take NT status values and set the "Customer" bit + * before returning the status code. The commit message for the + * change that started doing that is + * + * Returned a customer-defined NTSTATUS in OID requests to avoid + * NTSTATUS-to-Win32 Error code translation. + * + * but I don't know why the goal was to avoid that translation. + * + * Attempting to set the hardware filter on a Microsoft Surface Pro's + * Mobile Broadband Adapter returns an error that appears to be + * NDIS_STATUS_NOT_SUPPORTED ORed with the "Customer" bit, so it's + * probably indicating that it doesn't support that. * * It is likely that there are other devices which throw spurious errors, * at which point this will need refactoring to efficiently check against - * a list, but for now we can just check this one value. + * a list, but for now we can just check this one value. Perhaps the + * right way to do this is compare against various NDIS errors with + * the "customer" bit ORed in. */ -#define NPF_SURFACE_MOBILE_NONPROMISC 0xe00000bb +#define NT_STATUS_CUSTOMER_DEFINED 0x20000000 static int pcap_activate_npf(pcap_t *p) @@ -1017,14 +1073,22 @@ pcap_activate_npf(pcap_t *p) case ERROR_BAD_UNIT: /* * There's no such device. + * There's nothing to add, so clear the error + * message. */ + p->errbuf[0] = '\0'; return (PCAP_ERROR_NO_SUCH_DEVICE); case ERROR_ACCESS_DENIED: /* * There is, but we don't have permission to * use it. + * + * XXX - we currently get ERROR_BAD_UNIT if the + * user says "no" to the UAC prompt. */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The helper program for \"Admin-only Mode\" must be allowed to make changes to your device"); return (PCAP_ERROR_PERM_DENIED); default: @@ -1240,7 +1304,13 @@ pcap_activate_npf(pcap_t *p) /* * Suppress spurious error generated by non-compiant - * MS Surface mobile adapters. + * MS Surface mobile adapters that appear to + * return NDIS_STATUS_NOT_SUPPORTED for attempts + * to set the hardware filter. + * + * It appears to be reporting NDIS_STATUS_NOT_SUPPORTED, + * but with the NT status value "Customer" bit set; + * the Npcap NPF driver sets that bit in some cases. * * If we knew that this meant "promiscuous mode * isn't supported", we could add a "promiscuous @@ -1262,8 +1332,17 @@ pcap_activate_npf(pcap_t *p) * and rejecting it with an error could disrupt * attempts to capture, as many programs (tcpdump, * *shark) default to promiscuous mode. + * + * Alternatively, we could return the "promiscuous + * mode not supported" *warning* value, so that + * correct code will either ignore it or report + * it and continue capturing. (This may require + * a pcap_init() flag to request that return + * value, so that old incorrect programs that + * assume a non-zero return from pcap_activate() + * is an error don't break.) */ - if (errcode != NPF_SURFACE_MOBILE_NONPROMISC) + if (errcode != (NDIS_STATUS_NOT_SUPPORTED|NT_STATUS_CUSTOMER_DEFINED)) { pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, errcode, @@ -1294,7 +1373,7 @@ pcap_activate_npf(pcap_t *p) * Suppress spurious error generated by non-compiant * MS Surface mobile adapters. */ - if (errcode != NPF_SURFACE_MOBILE_NONPROMISC) + if (errcode != (NDIS_STATUS_NOT_SUPPORTED|NT_STATUS_CUSTOMER_DEFINED)) { pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, errcode, @@ -1511,110 +1590,152 @@ pcap_can_set_rfmon_npf(pcap_t *p) return (PacketIsMonitorModeSupported(p->opt.device) == 1); } -pcap_t * -pcap_create_interface(const char *device _U_, char *ebuf) -{ - pcap_t *p; +/* + * Get a list of time stamp types. + */ #ifdef HAVE_PACKET_GET_TIMESTAMP_MODES - char *device_copy; - ADAPTER *adapter; +static int +get_ts_types(const char *device, pcap_t *p, char *ebuf) +{ + char *device_copy = NULL; + ADAPTER *adapter = NULL; ULONG num_ts_modes; BOOL ret; - DWORD error; - ULONG *modes; -#endif + DWORD error = ERROR_SUCCESS; + ULONG *modes = NULL; + int status = 0; - p = PCAP_CREATE_COMMON(ebuf, struct pcap_win); - if (p == NULL) - return (NULL); + do { + /* + * First, find out how many time stamp modes we have. + * To do that, we have to open the adapter. + * + * XXX - PacketOpenAdapter() takes a non-const pointer + * as an argument, so we make a copy of the argument and + * pass that to it. + */ + device_copy = strdup(device); + if (device_copy == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc"); + status = -1; + break; + } - p->activate_op = pcap_activate_npf; - p->can_set_rfmon_op = pcap_can_set_rfmon_npf; + adapter = PacketOpenAdapter(device_copy); + if (adapter == NULL) + { + error = GetLastError(); + /* + * If we can't open the device now, we won't be + * able to later, either. + * + * If the error is something that indicates + * that the device doesn't exist, or that they + * don't have permission to open the device - or + * perhaps that they don't have permission to get + * a list of devices, if PacketOpenAdapter() does + * that - the user will find that out when they try + * to activate the device; just return an empty + * list of time stamp types. + * + * Treating either of those as errors will, for + * example, cause "tcpdump -i " to fail, + * because it first tries to pass the interface + * name to pcap_create() and pcap_activate(), + * in order to handle OSes where interfaces can + * have names that are just numbers (stand up + * and say hello, Linux!), and, if pcap_activate() + * fails with a "no such device" error, checks + * whether the interface name is a valid number + * and, if so, tries to use it as an index in + * the list of interfaces. + * + * That means pcap_create() must succeed even + * for interfaces that don't exist, with the + * failure occurring at pcap_activate() time. + */ + if (error == ERROR_BAD_UNIT || + error == ERROR_ACCESS_DENIED) { + p->tstamp_type_count = 0; + p->tstamp_type_list = NULL; + status = 0; + } else { + pcap_fmt_errmsg_for_win32_err(ebuf, + PCAP_ERRBUF_SIZE, error, + "Error opening adapter"); + status = -1; + } + break; + } -#ifdef HAVE_PACKET_GET_TIMESTAMP_MODES - /* - * First, find out how many time stamp modes we have. - * To do that, we have to open the adapter. - * - * XXX - PacketOpenAdapter() takes a non-const pointer - * as an argument, so we make a copy of the argument and - * pass that to it. - */ - device_copy = strdup(device); - adapter = PacketOpenAdapter(device_copy); - error = GetLastError(); - free(device_copy); - if (adapter == NULL) - { - /* If we can't open the device now, we won't be able to later, either. */ - pcap_fmt_errmsg_for_win32_err(ebuf, PCAP_ERRBUF_SIZE, - error, "Error opening adapter"); - pcap_close(p); - return (NULL); - } - /* - * Get the total number of time stamp modes. - * - * The buffer for PacketGetTimestampModes() is - * a sequence of 1 or more ULONGs. What's - * passed to PacketGetTimestampModes() should have - * the total number of ULONGs in the first ULONG; - * what's returned *from* PacketGetTimestampModes() - * has the total number of time stamp modes in - * the first ULONG. - * - * Yes, that means if there are N time stamp - * modes, the first ULONG should be set to N+1 - * on input, and will be set to N on output. - * - * We first make a call to PacketGetTimestampModes() - * with a pointer to a single ULONG set to 1; the - * call should fail with ERROR_MORE_DATA (unless - * there are *no* modes, but that should never - * happen), and that ULONG should be set to the - * number of modes. - */ - num_ts_modes = 1; - ret = PacketGetTimestampModes(adapter, &num_ts_modes); - if (!ret) { /* - * OK, it failed. Did it fail with - * ERROR_MORE_DATA? + * Get the total number of time stamp modes. + * + * The buffer for PacketGetTimestampModes() is + * a sequence of 1 or more ULONGs. What's + * passed to PacketGetTimestampModes() should have + * the total number of ULONGs in the first ULONG; + * what's returned *from* PacketGetTimestampModes() + * has the total number of time stamp modes in + * the first ULONG. + * + * Yes, that means if there are N time stamp + * modes, the first ULONG should be set to N+1 + * on input, and will be set to N on output. + * + * We first make a call to PacketGetTimestampModes() + * with a pointer to a single ULONG set to 1; the + * call should fail with ERROR_MORE_DATA (unless + * there are *no* modes, but that should never + * happen), and that ULONG should be set to the + * number of modes. */ - error = GetLastError(); - if (error != ERROR_MORE_DATA) { + num_ts_modes = 1; + ret = PacketGetTimestampModes(adapter, &num_ts_modes); + if (!ret) { /* - * No, did it fail with ERROR_INVALID_FUNCTION? + * OK, it failed. Did it fail with + * ERROR_MORE_DATA? */ - if (error == ERROR_INVALID_FUNCTION) { + error = GetLastError(); + if (error != ERROR_MORE_DATA) { /* - * This is probably due to - * the driver with which Packet.dll - * communicates being older, or - * being a WinPcap driver, so - * that it doesn't support - * BIOCGTIMESTAMPMODES. - * - * Tell the user to try uninstalling - * Npcap - and WinPcap if installed - - * and re-installing it, to flush - * out all older drivers. + * No, did it fail with ERROR_INVALID_FUNCTION? */ - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "PacketGetTimestampModes() failed with ERROR_INVALID_FUNCTION; try uninstalling Npcap, and WinPcap if installed, and re-installing it from npcap.org"); - pcap_close(p); - return (NULL); - } + if (error == ERROR_INVALID_FUNCTION) { + /* + * This is probably due to + * the driver with which Packet.dll + * communicates being older, or + * being a WinPcap driver, so + * that it doesn't support + * BIOCGTIMESTAMPMODES. + * + * Tell the user to try uninstalling + * Npcap - and WinPcap if installed - + * and re-installing it, to flush + * out all older drivers. + */ + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "PacketGetTimestampModes() failed with ERROR_INVALID_FUNCTION; try uninstalling Npcap, and WinPcap if installed, and re-installing it from npcap.com"); + status = -1; + break; + } - /* - * No, some other error. Fail. - */ - pcap_fmt_errmsg_for_win32_err(ebuf, - PCAP_ERRBUF_SIZE, error, - "Error calling PacketGetTimestampModes"); - pcap_close(p); - return (NULL); + /* + * No, some other error. Fail. + */ + pcap_fmt_errmsg_for_win32_err(ebuf, + PCAP_ERRBUF_SIZE, error, + "Error calling PacketGetTimestampModes"); + status = -1; + break; + } } + /* else (ret == TRUE) + * Unexpected success. Let's act like we got ERROR_MORE_DATA. + * If it doesn't work, we'll hit some other error condition farther on. + */ /* If the driver reports no modes supported *and* * ERROR_MORE_DATA, something is seriously wrong. @@ -1624,8 +1745,8 @@ pcap_create_interface(const char *device _U_, char *ebuf) if (num_ts_modes == 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "PacketGetTimestampModes() reports 0 modes supported."); - pcap_close(p); - return (NULL); + status = -1; + break; } /* @@ -1639,25 +1760,23 @@ pcap_create_interface(const char *device _U_, char *ebuf) if (modes == NULL) { /* Out of memory. */ pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc"); - pcap_close(p); - return (NULL); + status = -1; + break; } modes[0] = 1 + num_ts_modes; if (!PacketGetTimestampModes(adapter, modes)) { pcap_fmt_errmsg_for_win32_err(ebuf, PCAP_ERRBUF_SIZE, GetLastError(), "Error calling PacketGetTimestampModes"); - free(modes); - pcap_close(p); - return (NULL); + status = -1; + break; } if (modes[0] != num_ts_modes) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "First PacketGetTimestampModes() call gives %lu modes, second call gives %lu modes", num_ts_modes, modes[0]); - free(modes); - pcap_close(p); - return (NULL); + status = -1; + break; } /* @@ -1668,9 +1787,8 @@ pcap_create_interface(const char *device _U_, char *ebuf) p->tstamp_type_list = malloc((1 + num_ts_modes) * sizeof(u_int)); if (p->tstamp_type_list == NULL) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc"); - free(modes); - pcap_close(p); - return (NULL); + status = -1; + break; } u_int num_ts_types = 0; p->tstamp_type_list[num_ts_types] = @@ -1719,11 +1837,48 @@ pcap_create_interface(const char *device _U_, char *ebuf) } } p->tstamp_type_count = num_ts_types; + } while (0); + + /* Clean up temporary allocations */ + if (device_copy != NULL) { + free(device_copy); + } + if (modes != NULL) { free(modes); } - PacketCloseAdapter(adapter); + if (adapter != NULL) { + PacketCloseAdapter(adapter); + } + + return status; +} +#else /* HAVE_PACKET_GET_TIMESTAMP_MODES */ +static int +get_ts_types(const char *device _U_, pcap_t *p _U_, char *ebuf _U_) +{ + /* + * Nothing to fetch, so it always "succeeds". + */ + return 0; +} #endif /* HAVE_PACKET_GET_TIMESTAMP_MODES */ +pcap_t * +pcap_create_interface(const char *device _U_, char *ebuf) +{ + pcap_t *p; + + p = PCAP_CREATE_COMMON(ebuf, struct pcap_win); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_npf; + p->can_set_rfmon_op = pcap_can_set_rfmon_npf; + + if (get_ts_types(device, p, ebuf) == -1) { + pcap_close(p); + return (NULL); + } return (p); } @@ -1930,8 +2085,8 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) #ifdef OID_GEN_PHYSICAL_MEDIUM_EX OID_GEN_PHYSICAL_MEDIUM_EX, #endif - OID_GEN_PHYSICAL_MEDIUM - }; + OID_GEN_PHYSICAL_MEDIUM + }; #define N_GEN_PHYSICAL_MEDIUM_OIDS (sizeof gen_physical_medium_oids / sizeof gen_physical_medium_oids[0]) size_t i; #endif /* OID_GEN_PHYSICAL_MEDIUM */ @@ -1984,7 +2139,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) *flags |= PCAP_IF_WIRELESS; /* - * A "network assosiation state" makes no sense for airpcap. + * A "network association state" makes no sense for airpcap. */ *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE; PacketCloseAdapter(adapter); @@ -2243,7 +2398,7 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) desc++; /* - * Found it - "desc" points to the first of the two + * Found it - "desc" points to the first of the two * nulls at the end of the list of names, so the * first byte of the list of descriptions is two bytes * after it.