Index: Makefile =================================================================== RCS file: /cvs/ports/net/kismet/Makefile,v diff -u -r1.55 Makefile --- Makefile 1 Feb 2026 11:41:34 -0000 1.55 +++ Makefile 8 May 2026 17:49:35 -0000 @@ -5,7 +5,7 @@ V= 2025-09-R1 DISTNAME= kismet-${V} PKGNAME= kismet-${V:S,-,,g} -REVISION= 0 +REVISION= 1 CATEGORIES= net security @@ -18,8 +18,9 @@ # GPLv2+ PERMIT_PACKAGE= Yes -WANTLIB += ${COMPILER_LIBCXX} c crypto m mosquitto pcap pcre2-8 +WANTLIB += ${COMPILER_LIBCXX} c crypto m mosquitto pcre2-8 WANTLIB += rtlsdr sqlite3 ssl usb-1.0 util websockets z +WANTLIB += lib/libpcap/pcap # C++20 COMPILER= base-clang ports-gcc @@ -36,7 +37,12 @@ LDFLAGS_ports-gcc= -latomic LDFLAGS= -L${X11BASE}/lib -L${LOCALBASE}/lib ${LDFLAGS_${CHOSEN_COMPILER}} +PCAP_CFLAGS != pkg-config --cflags libepcap +PCAP_LIBS != pkg-config --libs libepcap + CONFIGURE_ENV= CPPFLAGS="-I${X11BASE}/include -I${LOCALBASE}/include" \ + libepcap_CFLAGS="${PCAP_CFLAGS}" \ + libepcap_LIBS="${PCAP_LIBS}" \ LDFLAGS="${LDFLAGS}" NO_TEST= Yes @@ -47,6 +53,7 @@ LIB_DEPENDS= comms/rtl-sdr \ devel/pcre2 \ net/mosquitto \ + net/libpcap \ www/libwebsockets RUN_DEPENDS= comms/rtl_433 \ net/wireshark,-text Index: patches/patch-capture_nrf_52840_capture_nrf_52840_c =================================================================== RCS file: patches/patch-capture_nrf_52840_capture_nrf_52840_c diff -N patches/patch-capture_nrf_52840_capture_nrf_52840_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-capture_nrf_52840_capture_nrf_52840_c 8 May 2026 17:49:35 -0000 @@ -0,0 +1,31 @@ +Not yet upstreamed, waiting for positive feedback +that it actually works. + +Index: capture_nrf_52840/capture_nrf_52840.c +--- capture_nrf_52840/capture_nrf_52840.c.orig ++++ capture_nrf_52840/capture_nrf_52840.c +@@ -17,7 +17,11 @@ + + volatile int STOP=FALSE; + ++#if defined(SYS_OPENBSD) ++#define MODEMDEVICE "/dev/cuaU0" ++#else + #define MODEMDEVICE "/dev/ttyACM0" ++#endif + #define CRTSCTS 020000000000 /*should be defined but isn't with the C99*/ + + #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) +@@ -317,7 +321,12 @@ int open_callback(kis_capture_handler_t *caph, uint32_ + bzero(&localnrf->newtio, sizeof(localnrf->newtio)); /* clear struct for new port settings */ + + /* set the baud rate and flags */ ++#if defined(SYS_OPENBSD) ++ localnrf->newtio.c_cflag = CRTSCTS | CS8 | CLOCAL | CREAD; ++ cfsetspeed(&localnrf->newtio, BAUDRATE); ++#else + localnrf->newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; ++#endif + + /* ignore parity errors */ + localnrf->newtio.c_iflag = IGNPAR; Index: patches/patch-capture_ti_cc_2531_capture_ti_cc_2531_c =================================================================== RCS file: patches/patch-capture_ti_cc_2531_capture_ti_cc_2531_c diff -N patches/patch-capture_ti_cc_2531_capture_ti_cc_2531_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-capture_ti_cc_2531_capture_ti_cc_2531_c 8 May 2026 17:49:35 -0000 @@ -0,0 +1,94 @@ +address double locks, double unlocks and unlocks of not previously locked +mutexes + +address issue that OpenBSD doesn't handle libusb_detach_kernel_driver() +and use libusb_ref_device() to increse ref count on the device to keep it +usable at a later state + + +Index: capture_ti_cc_2531/capture_ti_cc_2531.c +--- capture_ti_cc_2531/capture_ti_cc_2531.c.orig ++++ capture_ti_cc_2531/capture_ti_cc_2531.c +@@ -150,6 +150,8 @@ int ticc2531_exit_promisc_mode(kis_capture_handler_t * + int ticc2531_receive_payload(kis_capture_handler_t *caph, uint8_t *rx_buf, size_t rx_max) { + local_ticc2531_t *localticc2531 = (local_ticc2531_t *) caph->userdata; + int actual_len, r; ++ ++ pthread_mutex_lock(&(localticc2531->usb_mutex)); + + r = libusb_bulk_transfer(localticc2531->ticc2531_handle, TICC2531_DATA_EP, rx_buf, + rx_max, &actual_len, TICC2531_DATA_TIMEOUT); +@@ -169,6 +171,7 @@ int ticc2531_receive_payload(kis_capture_handler_t *ca + localticc2531->soft_reset = 0; /*we got something valid so reset*/ + localticc2531->error_ctr = 0; /*we got something valid so reset*/ + ++ pthread_mutex_unlock(&(localticc2531->usb_mutex)); + return actual_len; + } + +@@ -345,7 +348,6 @@ int list_callback(kis_capture_handler_t *caph, uint32_ + } + + libusb_free_device_list(libusb_devs, 1); +- pthread_mutex_unlock(&(localticc2531->usb_mutex)); + + if (num_devs == 0) { + *interfaces = NULL; +@@ -424,7 +426,8 @@ int open_usb_device(kis_capture_handler_t *caph, char + if (libusb_kernel_driver_active(localticc2531->ticc2531_handle, 0)) { + r = libusb_detach_kernel_driver(localticc2531->ticc2531_handle, 0); + +- if (r < 0) { ++ // If it's not supported (like on OpenBSD), just keep going! ++ if (r < 0 && r != LIBUSB_ERROR_NOT_SUPPORTED) { + snprintf(errstr, STATUS_MAX, "Unable to open ticc2531 USB interface, " + "could not disconnect kernel drivers: %s", + libusb_strerror((enum libusb_error) r)); +@@ -478,7 +481,7 @@ int open_usb_device(kis_capture_handler_t *caph, char + int open_callback(kis_capture_handler_t *caph, uint32_t seqno, char *definition, + char *msg, uint32_t *dlt, char **uuid, + cf_params_interface_t **ret_interface, +- cf_params_spectrum_t **ret_spectrum) { ++ cf_params_spectrum_t **ret_spectrum) { + + char *placeholder = NULL; + int placeholder_len; +@@ -565,7 +568,7 @@ int open_callback(kis_capture_handler_t *caph, uint32_ + if (busno == libusb_get_bus_number(libusb_devs[i]) && + devno == libusb_get_device_address(libusb_devs[i])) { + matched_device = 1; +- localticc2531->matched_dev = libusb_devs[i]; ++ localticc2531->matched_dev = libusb_ref_device(libusb_devs[i]); + break; + } + } else { +@@ -573,7 +576,7 @@ int open_callback(kis_capture_handler_t *caph, uint32_ + matched_device = 1; + busno = libusb_get_bus_number(libusb_devs[i]); + devno = libusb_get_device_address(libusb_devs[i]); +- localticc2531->matched_dev = libusb_devs[i]; ++ localticc2531->matched_dev = libusb_ref_device(libusb_devs[i]); + break; + } + +@@ -666,7 +669,7 @@ int chancontrol_callback(kis_capture_handler_t *caph, + } + + if (localticc2531->ticc2531_handle == NULL) { +- pthread_mutex_unlock(&(localticc2531->usb_mutex)); ++ // pthread_mutex_unlock(&(localticc2531->usb_mutex)); + return 0; + } + +@@ -871,6 +874,11 @@ int main(int argc, char *argv[]) { + cf_handler_loop(caph); + + cf_handler_shutdown(caph); ++ ++ if (localticc2531.matched_dev) { ++ libusb_unref_device(localticc2531.matched_dev); ++ localticc2531.matched_dev = NULL; ++ } + + libusb_exit(localticc2531.libusb_ctx); + Index: patches/patch-configure_ac =================================================================== RCS file: /cvs/ports/net/kismet/patches/patch-configure_ac,v diff -u -r1.1 patch-configure_ac --- patches/patch-configure_ac 19 Jan 2026 13:00:38 -0000 1.1 +++ patches/patch-configure_ac 8 May 2026 17:49:35 -0000 @@ -1,6 +1,6 @@ - don't force -O3 - GCC's libatomic should not be pulled in just because it exists -- libstdc++ should n9t be explicitly linked (use "c++" as a linker instead) +- libstdc++ should not be explicitly linked (use "c++" as a linker instead) Index: configure.ac --- configure.ac.orig @@ -59,3 +59,32 @@ AC_SUBST(CXXLIBS) # Does the compiler handle various std::foo namespaces properly? +@@ -1187,20 +1156,20 @@ AC_SUBST(LIBWSLIBS) + AC_SUBST(LIBWSCFLAGS) + + # Look for libpcap via pkg-config +-have_libpcap=no +-PKG_CHECK_MODULES([libpcap], [libpcap], [ +- have_libpcap=yes +- AC_DEFINE(HAVE_LIBPCAP, 1, libpcap packet capture lib) ++have_libepcap=no ++PKG_CHECK_MODULES([libepcap], [libepcap], [ ++ have_libepcap=yes ++ AC_DEFINE(HAVE_LIBPCAP, 1, libepcap packet capture lib) + +- PCAPLIBS=`pkg-config --libs libpcap` +- PCAPCFLAGS=`pkg-config --cflags libpcap` ++ PCAPLIBS=`pkg-config --libs libepcap` ++ PCAPCFLAGS=`pkg-config --cflags libepcap` + pcap=yes + ], [ +- AC_MSG_WARN(No libpcap found in pkg-config, will check system paths.) ++ AC_MSG_WARN(No libepcap found in pkg-config, will check system paths.) + ]) + + # Look for pcap using our legacy mode +-if test "$have_libpcap"x != "yesx"; then ++if test "$have_libepcap"x != "yesx"; then + AC_CHECK_LIB([pcap], [pcap_open_live], + AC_DEFINE(HAVE_LIBPCAP, 1, libpcap packet capture lib) foundsyspcap=yes, + AC_MSG_ERROR(Libpcap required for proper operation)) Index: patches/patch-log_tools_kismetdb_to_pcap_cc =================================================================== RCS file: patches/patch-log_tools_kismetdb_to_pcap_cc diff -N patches/patch-log_tools_kismetdb_to_pcap_cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-log_tools_kismetdb_to_pcap_cc 8 May 2026 17:49:35 -0000 @@ -0,0 +1,226 @@ +Prevent crasher when hitting unknown DLT +Index: log_tools/kismetdb_to_pcap.cc +--- log_tools/kismetdb_to_pcap.cc.orig ++++ log_tools/kismetdb_to_pcap.cc +@@ -222,16 +222,31 @@ void write_pcap_packet(FILE *pcap_file, const std::str + hdr.incl_len = packet.size(); + hdr.orig_len = packet.size(); + +- if (fwrite(&hdr, sizeof(pcap_packet_hdr_t), 1, pcap_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcap packet: {} (errno {})", +- strerror(errno), errno)); ++ // Skip empty packets to avoid write errors and malformed pcap blocks ++ if (packet.empty()) { ++ fmt::print(stderr, "DEBUG: Skipping empty pcap packet for TS {}.{}\n", ++ ts_sec, ts_usec); ++ return; ++ } + +- if (fwrite(packet.data(), packet.size(), 1, pcap_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcap packet: {} (errno {})", +- strerror(errno), errno)); ++ // Clear errno to avoid reporting stale "No such file" errors ++ errno = 0; ++ ++ if (fwrite(&hdr, sizeof(pcap_packet_hdr_t), 1, pcap_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcap HEADER: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } ++ ++ if (fwrite(packet.data(), packet.size(), 1, pcap_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcap DATA (size {}): {} (errno {})", ++ packet.size(), errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } + } + + ++ + FILE *open_pcapng_file(const std::string& path, bool force) { + struct stat statbuf; + FILE *pcapng_file; +@@ -465,6 +480,13 @@ void write_pcapng_packet(FILE *pcapng_file, const std: + unsigned long ts_sec, unsigned long ts_usec, const std::string& tag, + unsigned int ngindex, double lat, double lon, double alt) { + ++ // Skip empty packets to avoid write errors and malformed pcapng blocks ++ if (packet.empty()) { ++ fmt::print(stderr, "DEBUG: Skipping empty pcapng packet at TS {}.{}\n", ++ ts_sec, ts_usec); ++ return; ++ } ++ + // Assemble the packet in the file in steps to avoid another memcpy + pcapng_epb_t epb; + +@@ -501,24 +523,34 @@ void write_pcapng_packet(FILE *pcapng_file, const std: + epb.captured_length = packet.size(); + epb.original_length = packet.size(); + +- if (fwrite(&epb, sizeof(pcapng_epb_t), 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcapng packet header: {} (errno {})", +- strerror(errno), errno)); ++ // Clear errno for clean reporting ++ errno = 0; + +- if (fwrite(packet.data(), packet.size(), 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcapng packet content: {} (errno {})", +- strerror(errno), errno)); ++ if (fwrite(&epb, sizeof(pcapng_epb_t), 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng EPB HEADER: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } + ++ if (fwrite(packet.data(), packet.size(), 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng DATA (size {}): {} (errno {})", ++ packet.size(), errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } ++ + // Data has to be 32bit padded + uint32_t pad = 0; + size_t pad_sz = 0; + + pad_sz = PAD_TO_32BIT(packet.size()) - packet.size(); + +- if (pad_sz > 0) +- if (fwrite(&pad, pad_sz, 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcapng packet padding: {} (errno {})", +- strerror(errno), errno)); ++ if (pad_sz > 0) { ++ if (fwrite(&pad, pad_sz, 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng PADDING: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } ++ } + + pcapng_option_t opt; + +@@ -526,20 +558,27 @@ void write_pcapng_packet(FILE *pcapng_file, const std: + opt.option_code = PCAPNG_OPT_COMMENT; + opt.option_length = tag.length(); + +- if (fwrite(&opt, sizeof(pcapng_option_t), 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcapng packet option: {} (errno {})", +- strerror(errno), errno)); ++ if (fwrite(&opt, sizeof(pcapng_option_t), 1, pcapng_file) != 1){ ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng OPTION HEADER: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown error", errsv)); ++ } + +- if (fwrite(tag.c_str(), tag.length(), 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcapng packet option: {} (errno {})", +- strerror(errno), errno)); ++ if (fwrite(tag.c_str(), tag.length(), 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng OPTION DATA (tag): {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown error", errsv)); ++ } + + pad_sz = PAD_TO_32BIT(tag.length()) - tag.length(); + +- if (pad_sz > 0) +- if (fwrite(&pad, pad_sz, 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcapng packet option: {} (errno {})", +- strerror(errno), errno)); ++ if (pad_sz > 0) { ++ if (fwrite(&pad, pad_sz, 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng OPTION PADDING: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown error", errsv)); ++ } ++ } + } + + // If we have gps data, tag the packet with a kismet custom GPS entry under the kismet PEN +@@ -590,42 +629,55 @@ void write_pcapng_packet(FILE *pcapng_file, const std: + + // Lon, lat, [alt] + u.u32 = double_to_fixed3_7(lon); +- if (fwrite(&u, sizeof(uint32_t), 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing packet gps: {} (errno {})", +- strerror(errno), errno)); ++ if (fwrite(&u, sizeof(uint32_t), 1, pcapng_file) != 1){ ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng GPS LONGITUDE: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } + + u.u32 = double_to_fixed3_7(lat); +- if (fwrite(&u, sizeof(uint32_t), 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing packet gps: {} (errno {})", +- strerror(errno), errno)); ++ if (fwrite(&u, sizeof(uint32_t), 1, pcapng_file) != 1){ ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng GPS LATITUDE: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } + + if (alt != 0) { + u.u32 = double_to_fixed6_4(alt); +- if (fwrite(&u, sizeof(uint32_t), 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing packet gps: {} (errno {})", +- strerror(errno), errno)); ++ if (fwrite(&u, sizeof(uint32_t), 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng GPS ALTITUDE: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } + } + + pad_sz = PAD_TO_32BIT(copt.option_length) - copt.option_length; + +- if (pad_sz > 0) +- if (fwrite(&pad, pad_sz, 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing pcapng packet option: {} (errno {})", +- strerror(errno), errno)); ++ if (pad_sz > 0) { ++ if (fwrite(&pad, pad_sz, 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng GPS OPTION PADDING: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } ++ } + } + + opt.option_code = PCAPNG_OPT_ENDOFOPT; + opt.option_length = 0; + +- if (fwrite(&opt, sizeof(pcapng_option_t), 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing packet end-of-options: {} (errno {})", +- strerror(errno), errno)); ++ if (fwrite(&opt, sizeof(pcapng_option_t), 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng END-OF-OPTIONS: {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } + + data_sz += 4; + +- if (fwrite(&data_sz, 4, 1, pcapng_file) != 1) +- throw std::runtime_error(fmt::format("error writing packet end-of-packet: {} (errno {})", +- strerror(errno), errno)); ++ if (fwrite(&data_sz, 4, 1, pcapng_file) != 1) { ++ int errsv = errno; ++ throw std::runtime_error(fmt::format("failed writing pcapng BLOCK TRAILER (total length): {} (errno {})", ++ errsv ? strerror(errsv) : "Unknown/Buffered Error", errsv)); ++ } + } + + +@@ -975,8 +1027,10 @@ int main(int argc, char *argv[]) { + fmt::print("Datasource #{} ({} {} {}) {} packets\n", + ifnum++, i->uuid, i->name, i->interface, i->num_packets); + for (auto d : i->dlts) { ++ const char* n = pcap_datalink_val_to_name(d); ++ const char* v = pcap_datalink_val_to_description(d); + fmt::print(" DLT {}: {} {}\n", +- d, pcap_datalink_val_to_name(d), pcap_datalink_val_to_description(d)); ++ d, n ? n : "unknown", v ? v : "unknown"); + } + + if (i->dlts.size() == 0) Index: pkg/README =================================================================== RCS file: /cvs/ports/net/kismet/pkg/README,v diff -u -r1.1 README --- pkg/README 19 Jan 2026 13:00:38 -0000 1.1 +++ pkg/README 8 May 2026 17:49:35 -0000 @@ -8,11 +8,22 @@ The following capture drivers are known to work: * openbsd_wifi (autodetected) * sdr_rtladsb (autodetected) + * planes * sdr_rtl433 (autodetected) + * meters * nrf_51822 (not autodetected) + * Bluetooth LE * use with: * -c nrf51822:type=nrf51822,device=/dev/cuaU0,name=adafruit - + * tested with Adafruit Bluefruit sniffer as well as nrf52840 + nice!Nano with Bluetooth sniffer firmware + * nrf_52840 (not autodetected) + * Zigbee + * use with: + * -c nrf52840:type=nrf52840,device=/dev/cuaU0,name=niceNano + * note: haven't seen it reporting any packets yet using a nice!Nano + * ti_cc_2531 (autodetected) + * Zigbee Many other capture drivers are enabled. They are untested, but may work as well.