summaryrefslogtreecommitdiff
path: root/pcap-linux.c
diff options
context:
space:
mode:
authorJoseph Mingrone <jrm@FreeBSD.org>2026-01-07 12:56:48 -0400
committerJoseph Mingrone <jrm@FreeBSD.org>2026-01-07 12:56:48 -0400
commit0a1fbf4c244d5cfb242d3d7b146fea2ee676d46d (patch)
tree603f2cb82ae91e7dfc1174391a1f096db62827bf /pcap-linux.c
parent025be3f592b920ff0a3e602e5aa8b60a34e5e617 (diff)
Diffstat (limited to 'pcap-linux.c')
-rw-r--r--pcap-linux.c636
1 files changed, 550 insertions, 86 deletions
diff --git a/pcap-linux.c b/pcap-linux.c
index e0fe1d3e0237..20802e4301f7 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -91,6 +91,7 @@
#include <linux/ethtool.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
+#include <linux/netlink.h>
#include <linux/if_arp.h>
#include <poll.h>
#include <dirent.h>
@@ -213,8 +214,7 @@ struct pcap_linux {
/*
* Stuff to do when we close.
*/
-#define MUST_CLEAR_RFMON 0x00000001 /* clear rfmon (monitor) mode */
-#define MUST_DELETE_MONIF 0x00000002 /* delete monitor-mode interface */
+#define MUST_DELETE_MONIF 0x00000001 /* delete monitor-mode interface */
/*
* Prototypes for internal functions and methods.
@@ -457,9 +457,57 @@ get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path,
}
bytes_read = readlink(pathstr, phydev_path, phydev_max_pathlen);
if (bytes_read == -1) {
- if (errno == ENOENT || errno == EINVAL) {
+ if (errno == ENOENT) {
/*
- * Doesn't exist, or not a symlink; assume that
+ * This either means that the directory
+ * /sys/class/net/{device} exists but doesn't
+ * have anything named "phy80211" in it,
+ * in which case it's not a mac80211 device,
+ * or that the directory doesn't exist,
+ * in which case the device doesn't exist.
+ *
+ * Directly check whether the directory
+ * exists.
+ */
+ struct stat statb;
+
+ free(pathstr);
+ if (asprintf(&pathstr, "/sys/class/net/%s", device) == -1) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: Can't generate path name string for /sys/class/net device",
+ device);
+ return PCAP_ERROR;
+ }
+ if (stat(pathstr, &statb) == -1) {
+ if (errno == ENOENT) {
+ /*
+ * No such device.
+ */
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: %s doesn't exist",
+ device, pathstr);
+ free(pathstr);
+ return PCAP_ERROR_NO_SUCH_DEVICE;
+ }
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: Can't stat %s: %s",
+ device, pathstr, strerror(errno));
+ free(pathstr);
+ return PCAP_ERROR;
+ }
+
+ /*
+ * Path to the directory that would contain
+ * "phy80211" exists, but "phy80211" doesn't
+ * exist; that means it's not a mac80211
+ * device.
+ */
+ free(pathstr);
+ return 0;
+ }
+ if (errno == EINVAL) {
+ /*
+ * Exists, but it's not a symlink; assume that
* means it's not a mac80211 device.
*/
free(pathstr);
@@ -536,10 +584,34 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
const char *device, const char *mondevice);
static int
-add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
- const char *device, const char *mondevice)
+if_type_cb(struct nl_msg *msg, void* arg)
+{
+ struct nlmsghdr* ret_hdr = nlmsg_hdr(msg);
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ int *type = (int*)arg;
+
+ struct genlmsghdr *gnlh = (struct genlmsghdr*) nlmsg_data(ret_hdr);
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ /*
+ * We sent a message asking for info about a single index.
+ * To be really paranoid, we could check if the index matched
+ * by examining nla_get_u32(tb_msg[NL80211_ATTR_IFINDEX]).
+ */
+
+ if (tb_msg[NL80211_ATTR_IFTYPE]) {
+ *type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]);
+ }
+
+ return NL_SKIP;
+}
+
+static int
+get_if_type(pcap_t *handle, int sock_fd, struct nl80211_state *state,
+ const char *device, int *type)
{
- struct pcap_linux *handlep = handle->priv;
int ifindex;
struct nl_msg *msg;
int err;
@@ -555,15 +627,13 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
return PCAP_ERROR;
}
- genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
- 0, NL80211_CMD_NEW_INTERFACE, 0);
+ genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ,
+ genl_family_get_id(state->nl80211), 0,
+ 0, NL80211_CMD_GET_INTERFACE, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
-DIAG_OFF_NARROWING
- NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, mondevice);
-DIAG_ON_NARROWING
- NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
- err = nl_send_auto_complete(state->nl_sock, msg);
+ err = nl_send_auto(state->nl_sock, msg);
+ nlmsg_free(msg);
if (err < 0) {
if (err == -NLE_FAILURE) {
/*
@@ -573,7 +643,6 @@ DIAG_ON_NARROWING
* to that, but there's not much we can do
* about that.)
*/
- nlmsg_free(msg);
return 0;
} else {
/*
@@ -581,15 +650,87 @@ DIAG_ON_NARROWING
* available.
*/
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_send_auto_complete failed adding %s interface: %s",
- device, mondevice, nl_geterror(-err));
- nlmsg_free(msg);
+ "%s: nl_send_auto failed getting interface type: %s",
+ device, nl_geterror(-err));
return PCAP_ERROR;
}
}
+
+ struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, if_type_cb, (void*)type);
+ err = nl_recvmsgs(state->nl_sock, cb);
+ nl_cb_put(cb);
+
+ if (err < 0) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: nl_recvmsgs failed getting interface type: %s",
+ device, nl_geterror(-err));
+ return PCAP_ERROR;
+ }
+
+ /*
+ * If this is a mac80211 device not in monitor mode, nl_sock will be
+ * reused for add_mon_if. So we must wait for the ACK here so that
+ * add_mon_if does not receive it instead and incorrectly interpret
+ * the ACK as its NEW_INTERFACE command succeeding, even when it fails.
+ */
err = nl_wait_for_ack(state->nl_sock);
if (err < 0) {
- if (err == -NLE_FAILURE) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: nl_wait_for_ack failed getting interface type: %s",
+ device, nl_geterror(-err));
+ return PCAP_ERROR;
+ }
+
+ /*
+ * Success.
+ */
+ return 1;
+
+nla_put_failure:
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: nl_put failed getting interface type",
+ device);
+ nlmsg_free(msg);
+ // Do not call nl_cb_put(): nl_cb_alloc() has not been called.
+ return PCAP_ERROR;
+}
+
+static int
+add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
+ const char *device, const char *mondevice)
+{
+ struct pcap_linux *handlep = handle->priv;
+ int ifindex;
+ struct nl_msg *msg;
+ int err;
+
+ ifindex = iface_get_id(sock_fd, device, handle->errbuf);
+ if (ifindex == -1)
+ return PCAP_ERROR;
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: failed to allocate netlink msg", device);
+ return PCAP_ERROR;
+ }
+
+ genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ,
+ genl_family_get_id(state->nl80211), 0,
+ 0, NL80211_CMD_NEW_INTERFACE, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+DIAG_OFF_NARROWING
+ NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, mondevice);
+DIAG_ON_NARROWING
+ NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
+
+ err = nl_send_sync(state->nl_sock, msg); // calls nlmsg_free()
+ if (err < 0) {
+ switch (err) {
+
+ case -NLE_FAILURE:
+ case -NLE_AGAIN:
/*
* Device not available; our caller should just
* keep trying. (libnl 2.x maps ENFILE to
@@ -597,17 +738,25 @@ DIAG_ON_NARROWING
* to that, but there's not much we can do
* about that.)
*/
- nlmsg_free(msg);
return 0;
- } else {
+
+ case -NLE_OPNOTSUPP:
+ /*
+ * Device is a mac80211 device but adding it as a
+ * monitor mode device isn't supported. Report our
+ * error.
+ */
+ return PCAP_ERROR_RFMON_NOTSUP;
+
+ default:
/*
* Real failure, not just "that device is not
- * available.
+ * available." Report a generic error, using the
+ * error message from libnl.
*/
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_wait_for_ack failed adding %s interface: %s",
+ "%s: nl_send_sync failed adding %s interface: %s",
device, mondevice, nl_geterror(-err));
- nlmsg_free(msg);
return PCAP_ERROR;
}
}
@@ -615,7 +764,6 @@ DIAG_ON_NARROWING
/*
* Success.
*/
- nlmsg_free(msg);
/*
* Try to remember the monitor device.
@@ -659,31 +807,22 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
return PCAP_ERROR;
}
- genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
+ genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ,
+ genl_family_get_id(state->nl80211), 0,
0, NL80211_CMD_DEL_INTERFACE, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
- err = nl_send_auto_complete(state->nl_sock, msg);
+ err = nl_send_sync(state->nl_sock, msg); // calls nlmsg_free()
if (err < 0) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_send_auto_complete failed deleting %s interface: %s",
+ "%s: nl_send_sync failed deleting %s interface: %s",
device, mondevice, nl_geterror(-err));
- nlmsg_free(msg);
- return PCAP_ERROR;
- }
- err = nl_wait_for_ack(state->nl_sock);
- if (err < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_wait_for_ack failed adding %s interface: %s",
- device, mondevice, nl_geterror(-err));
- nlmsg_free(msg);
return PCAP_ERROR;
}
/*
* Success.
*/
- nlmsg_free(msg);
return 1;
nla_put_failure:
@@ -1231,19 +1370,17 @@ linux_check_direction(const pcap_t *handle, const struct sockaddr_ll *sll)
return 0;
/*
- * If this is an outgoing CAN or CAN FD frame, and
- * the user doesn't only want outgoing packets,
- * reject it; CAN devices and drivers, and the CAN
- * stack, always arrange to loop back transmitted
- * packets, so they also appear as incoming packets.
- * We don't want duplicate packets, and we can't
- * easily distinguish packets looped back by the CAN
- * layer than those received by the CAN layer, so we
- * eliminate this packet instead.
+ * If this is an outgoing CAN frame, and the user doesn't
+ * want only outgoing packets, reject it; CAN devices
+ * and drivers, and the CAN stack, always arrange to
+ * loop back transmitted packets, so they also appear
+ * as incoming packets. We don't want duplicate packets,
+ * and we can't easily distinguish packets looped back
+ * by the CAN layer than those received by the CAN layer,
+ * so we eliminate this packet instead.
*
- * We check whether this is a CAN or CAN FD frame
- * by checking whether the device's hardware type
- * is ARPHRD_CAN.
+ * We check whether this is a CAN frame by checking whether
+ * the device's hardware type is ARPHRD_CAN.
*/
if (sll->sll_hatype == ARPHRD_CAN &&
handle->direction != PCAP_D_OUT)
@@ -1925,8 +2062,8 @@ static int map_arphrd_to_dlt(pcap_t *handle, int arptype,
if (ret == 1) {
/*
* This is a DSA master/management network
- * device linktype is already set by
- * iface_dsa_get_proto_info() set an
+ * device, linktype is already set by
+ * iface_dsa_get_proto_info(), set an
* appropriate offset here.
*/
handle->offset = 2;
@@ -2218,7 +2355,7 @@ static int map_arphrd_to_dlt(pcap_t *handle, int arptype,
*
* https://github.com/mcr/libpcap/pull/29
*
- * There doesn't seem to be any network drivers that uses
+ * There don't seem to be any network drivers that uses
* any of the ARPHRD_FC* values for IP-over-FC, and
* it's not exactly clear what the "Dummy types for non
* ARP hardware" are supposed to mean (link-layer
@@ -2374,9 +2511,9 @@ setup_socket(pcap_t *handle, int is_any_device)
* Other error.
*/
status = PCAP_ERROR;
+ pcapint_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno, "socket");
}
- pcapint_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
- errno, "socket");
return status;
}
@@ -2564,6 +2701,7 @@ setup_socket(pcap_t *handle, int is_any_device)
if (handle->dlt_list == NULL) {
pcapint_fmt_errmsg_for_errno(handle->errbuf,
PCAP_ERRBUF_SIZE, errno, "malloc");
+ close(sock_fd);
return (PCAP_ERROR);
}
handle->dlt_list[0] = DLT_LINUX_SLL;
@@ -4810,18 +4948,40 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
if (ret == 0)
return 0; /* no error, but not mac80211 device */
+ ret = nl80211_init(handle, &nlstate, device);
+ if (ret != 0)
+ return ret;
+
/*
- * XXX - is this already a monN device?
+ * Is this already a monN device?
* If so, we're done.
*/
+ int type;
+ ret = get_if_type(handle, sock_fd, &nlstate, device, &type);
+ if (ret <= 0) {
+ /*
+ * < 0 is a Hard failure. Just return ret; handle->errbuf
+ * has already been set.
+ *
+ * 0 is "device not available"; the caller should retry later.
+ */
+ nl80211_cleanup(&nlstate);
+ return ret;
+ }
+ if (type == NL80211_IFTYPE_MONITOR) {
+ /*
+ * OK, it's already a monitor mode device; just use it.
+ * There's no point in creating another monitor device
+ * that will have to be cleaned up.
+ */
+ nl80211_cleanup(&nlstate);
+ return ret;
+ }
/*
- * OK, it's apparently a mac80211 device.
+ * OK, it's apparently a mac80211 device but not a monitor device.
* Try to find an unused monN device for it.
*/
- ret = nl80211_init(handle, &nlstate, device);
- if (ret != 0)
- return ret;
for (n = 0; n < UINT_MAX; n++) {
/*
* Try mon{n}.
@@ -5267,22 +5427,294 @@ iface_get_offload(pcap_t *handle _U_)
}
#endif /* SIOCETHTOOL */
+/*
+ * As per
+ *
+ * https://www.kernel.org/doc/html/latest/networking/dsa/dsa.html#switch-tagging-protocols
+ *
+ * Type 1 means that the tag is prepended to the Ethernet packet.
+ *
+ * Type 2 means that the tag is inserted into the Ethernet header
+ * after the source address and before the type/length field.
+ *
+ * Type 3 means that tag is a packet trailer.
+ *
+ * Every element in the array below uses a DLT. Because a DSA-tagged frame is
+ * not a standard IEEE 802.3 Ethernet frame, the array elements must not use
+ * DLT_EN10MB. It is safe, albeit only barely useful, to use DLT_DEBUG_ONLY,
+ * which is also the implicit default for any DSA tag that is not present in
+ * the array. To implement proper support for a particular DSA tag of
+ * interest, please do as much of the following as is reasonably practicable:
+ *
+ * 1. Using recent versions of tcpdump and libpcap on a Linux host with a
+ * network interface that implements the required DSA tag, capture packets
+ * on the interface and study the hex dumps.
+ * 2. Using the hex dumps and any other available supporting materials, produce
+ * a sufficiently detailed description of the DSA tag structure, complete
+ * with a full comment indicating whether it's type 1, 2, or 3, and, for
+ * type 2, indicating whether it has an Ethertype and, if so, what that type
+ * is, and whether it's registered with the IEEE or not. Refer to the
+ * specification(s), existing implementation(s), or any other relevant
+ * resources.
+ * 3. Using the description, request and obtain a new DLT for the DSA tag.
+ * 4. Associate the new DLT with the DSA tag in the array below.
+ * 5. Using the updated libpcap, capture packets again, produce a .pcap file
+ * and confirm it uses the new DLT.
+ * 6. Using the .pcap file as a test, prepare additional changes to tcpdump to
+ * enable decoding of packets for the new DLT.
+ * 7. Using the .pcap file as a test, prepare additional changes to libpcap to
+ * enable filtering of packets for the new DLT.
+ *
+ * For working examples of such support, see the existing DLTs other than
+ * DLT_DEBUG_ONLY in the array below.
+ */
static struct dsa_proto {
const char *name;
bpf_u_int32 linktype;
} dsa_protos[] = {
/*
- * None is special and indicates that the interface does not have
- * any tagging protocol configured, and is therefore a standard
- * Ethernet interface.
+ * Type 1. See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_ar9331.c
+ */
+ { "ar9331", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, without an EtherType at the beginning.
*/
- { "none", DLT_EN10MB },
{ "brcm", DLT_DSA_TAG_BRCM },
+
+ /*
+ * Type 2, with EtherType 0x8874, assigned to Broadcom.
+ */
+ { "brcm-legacy", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 1.
+ */
{ "brcm-prepend", DLT_DSA_TAG_BRCM_PREPEND },
+
+ /*
+ * Type 2, without an EtherType at the beginning.
+ */
{ "dsa", DLT_DSA_TAG_DSA },
+
+ /*
+ * Type 2, with an Ethertype field, but without
+ * an assigned EtherType value that can be relied
+ * on.
+ */
{ "edsa", DLT_DSA_TAG_EDSA },
+
+ /*
+ * Type 1, with different transmit and receive headers,
+ * so can't really be handled well with the current
+ * libpcap API and with pcap files.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_gswip.c
+ */
+ { "gswip", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 3. See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_hellcreek.c
+ */
+ { "hellcreek", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 3, with different transmit and receive headers,
+ * so can't really be handled well with the current
+ * libpcap API and with pcap files.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_ksz.c#L102
+ */
+ { "ksz8795", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 3, with different transmit and receive headers,
+ * so can't really be handled well with the current
+ * libpcap API and with pcap files.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_ksz.c#L160
+ */
+ { "ksz9477", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 3, with different transmit and receive headers,
+ * so can't really be handled well with the current
+ * libpcap API and with pcap files.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_ksz.c#L341
+ */
+ { "ksz9893", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 3, with different transmit and receive headers,
+ * so can't really be handled well with the current
+ * libpcap API and with pcap files.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_ksz.c#L386
+ */
+ { "lan937x", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, with EtherType 0x8100; the VID can be interpreted
+ * as per
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_lan9303.c#L24
+ */
+ { "lan9303", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, without an EtherType at the beginning.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_mtk.c#L15
+ *
+ * Linux kernel implements this tag so that it does not indicate the frame
+ * encoding reliably. The matter is, some drivers use METADATA_HW_PORT_MUX,
+ * which (for the switch->CPU direction only, at the time of this writing)
+ * means that the frame does not have a DSA tag, the frame metadata is stored
+ * elsewhere and libpcap receives the frame only. Specifically, this is the
+ * case for drivers/net/ethernet/mediatek/mtk_eth_soc.c, but the tag visible
+ * in sysfs is still "mtk" even though the wire encoding is different.
+ */
+ { "mtk", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 1.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_ocelot.c
+ */
+ { "ocelot", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 1.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_ocelot.c
+ */
+ { "seville", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, with EtherType 0x8100; the VID can be interpreted
+ * as per
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_8021q.c#L15
+ */
+ { "ocelot-8021q", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, without an EtherType at the beginning.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_qca.c
+ */
+ { "qca", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, with EtherType 0x8899, assigned to Realtek;
+ * they use it for several on-the-Ethernet protocols
+ * as well, but there are fields that allow the two
+ * tag formats, and all the protocols in question,
+ * to be distinguiished from one another.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_rtl4_a.c
+ *
+ * http://realtek.info/pdf/rtl8306sd%28m%29_datasheet_1.1.pdf
+ *
+ * and various pages in tcpdump's print-realtek.c and Wireshark's
+ * epan/dissectors/packet-realtek.c for the other protocols.
+ */
+ { "rtl4a", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, with EtherType 0x8899, assigned to Realtek;
+ * see above.
+ */
+ { "rtl8_4", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 3, with the same tag format as rtl8_4.
+ */
+ { "rtl8_4t", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, with EtherType 0xe001; that's probably
+ * self-assigned.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_rzn1_a5psw.c
+ */
+ { "a5psw", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, with EtherType 0x8100 or the self-assigned
+ * 0xdadb, so this really should have its own
+ * LINKTYPE_/DLT_ value; that would also allow the
+ * VID of the tag to be dissected as per
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_8021q.c#L15
+ */
+ { "sja1105", DLT_DEBUG_ONLY },
+
+ /*
+ * Type "none of the above", with both a header and trailer,
+ * with different transmit and receive tags. Has
+ * EtherType 0xdadc, which is probably self-assigned.
+ */
+ { "sja1110", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 3, as the name suggests.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_trailer.c
+ */
+ { "trailer", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 2, with EtherType 0x8100; the VID can be interpreted
+ * as per
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_8021q.c#L15
+ */
+ { "vsc73xx-8021q", DLT_DEBUG_ONLY },
+
+ /*
+ * Type 3.
+ *
+ * See
+ *
+ * https://elixir.bootlin.com/linux/v6.13.2/source/net/dsa/tag_xrs700x.c
+ */
+ { "xrs700x", DLT_DEBUG_ONLY },
};
+/*
+ * Return 1 if the interface uses DSA tagging, 0 if the interface does not use
+ * DSA tagging, or PCAP_ERROR on error.
+ */
static int
iface_dsa_get_proto_info(const char *device, pcap_t *handle)
{
@@ -5309,10 +5741,18 @@ iface_dsa_get_proto_info(const char *device, pcap_t *handle)
fd = open(pathstr, O_RDONLY);
free(pathstr);
/*
- * This is not fatal, kernel >= 4.20 *might* expose this attribute
+ * This could be not fatal: kernel >= 4.20 *might* expose this
+ * attribute. However, if it exposes the attribute, but the read has
+ * failed due to another reason (ENFILE, EMFILE, ENOMEM...), propagate
+ * the failure.
*/
- if (fd < 0)
- return 0;
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return 0;
+ pcapint_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "open");
+ return PCAP_ERROR;
+ }
r = read(fd, buf, sizeof(buf) - 1);
if (r <= 0) {
@@ -5330,23 +5770,41 @@ iface_dsa_get_proto_info(const char *device, pcap_t *handle)
r--;
buf[r] = '\0';
+ /*
+ * The string "none" indicates that the interface does not have
+ * any tagging protocol configured, and is therefore a standard
+ * Ethernet interface.
+ */
+ if (strcmp(buf, "none") == 0)
+ return 0;
+
+ /*
+ * Every element in the array stands for a DSA-tagged interface. Using
+ * DLT_EN10MB (the standard IEEE 802.3 Ethernet) for such an interface
+ * may seem a good idea at first, but doing so would certainly cause
+ * major problems in areas that are already complicated and depend on
+ * DLT_EN10MB meaning the standard IEEE 802.3 Ethernet only, namely:
+ *
+ * - live capturing of packets on Linux, and
+ * - live kernel filtering of packets on Linux, and
+ * - live userspace filtering of packets on Linux, and
+ * - offline filtering of packets on all supported OSes, and
+ * - identification of savefiles on all OSes.
+ *
+ * Therefore use a default DLT value that does not block capturing and
+ * hexdumping of unsupported DSA encodings (in case the tag is not in
+ * the array) and enforce the non-use of DLT_EN10MB (in case the tag is
+ * in the array, but is incorrectly declared).
+ */
+ handle->linktype = DLT_DEBUG_ONLY;
for (i = 0; i < sizeof(dsa_protos) / sizeof(dsa_protos[0]); i++) {
- if (strlen(dsa_protos[i].name) == (size_t)r &&
- strcmp(buf, dsa_protos[i].name) == 0) {
- handle->linktype = dsa_protos[i].linktype;
- switch (dsa_protos[i].linktype) {
- case DLT_EN10MB:
- return 0;
- default:
- return 1;
- }
+ if (strcmp(buf, dsa_protos[i].name) == 0) {
+ if (dsa_protos[i].linktype != DLT_EN10MB)
+ handle->linktype = dsa_protos[i].linktype;
+ break;
}
}
-
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "unsupported DSA tag: %s", buf);
-
- return PCAP_ERROR;
+ return 1;
}
/*
@@ -5724,12 +6182,18 @@ pcap_set_protocol_linux(pcap_t *p, int protocol)
/*
* Libpcap version string.
*/
+#if defined(HAVE_TPACKET3) && defined(PCAP_SUPPORT_NETMAP)
+ #define ADDITIONAL_INFO_STRING "with TPACKET_V3 and netmap"
+#elif defined(HAVE_TPACKET3)
+ #define ADDITIONAL_INFO_STRING "with TPACKET_V3"
+#elif defined(PCAP_SUPPORT_NETMAP)
+ #define ADDITIONAL_INFO_STRING "with TPACKET_V2 and netmap"
+#else
+ #define ADDITIONAL_INFO_STRING "with TPACKET_V2"
+#endif
+
const char *
pcap_lib_version(void)
{
-#if defined(HAVE_TPACKET3)
- return (PCAP_VERSION_STRING " (with TPACKET_V3)");
-#else
- return (PCAP_VERSION_STRING " (with TPACKET_V2)");
-#endif
+ return (PCAP_VERSION_STRING_WITH_ADDITIONAL_INFO(ADDITIONAL_INFO_STRING));
}