]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
ctdb-common: Add packet type detection to pcap-based capture
authorMartin Schwenke <martin@meltin.net>
Sun, 14 Aug 2022 23:43:58 +0000 (09:43 +1000)
committerJule Anger <janger@samba.org>
Tue, 29 Aug 2023 09:35:11 +0000 (09:35 +0000)
The current code will almost certainly generate ENOMSG for
non-ethernet packets, even for ethernet packets when the "any"
interface is used.

pcap_datalink(3PCAP) says:

  Do NOT assume that the packets for a given capture or ``savefile``
  will have any given link-layer header type, such as DLT_EN10MB for
  Ethernet.  For example, the "any" device on Linux will have a
  link-layer header type of DLT_LINUX_SLL or DLT_LINUX_SLL2 even if
  all devices on the sys‐ tem at the time the "any" device is opened
  have some other data link type, such as DLT_EN10MB for Ethernet.

So, pcap_datalink() must be used.

Detect pcap packet types that are supported (currently only ethernet)
in the open code. There is no use continuing if the read code can't
parse packets.  The pattern of using switch statements supports future
addition of other packet types.

Signed-off-by: Martin Schwenke <martin@meltin.net>
Reviewed-by: Amitay Isaacs <amitay@gmail.com>
(cherry picked from commit 3bf20300ac5962e71069be3998ef7f0502045d24)

ctdb/common/system_socket.c

index e8ccd301f6ca817ddb96a82d4a1bc2301f6b70c6..51f5d03090650f2c9a2c22be424e6e32cff227b7 100644 (file)
@@ -971,6 +971,8 @@ int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
 {
        char errbuf[PCAP_ERRBUF_SIZE];
        pcap_t *pt;
+       int pcap_packet_type;
+       const char *t;
        int fd;
 
        pt = pcap_open_live(iface, 100, 0, 0, errbuf);
@@ -981,9 +983,22 @@ int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
                return -1;
        }
        *((pcap_t **)private_data) = pt;
-       fd = pcap_get_selectable_fd(pt);
 
-       DBG_DEBUG("Opened pcap capture for TCP tickle capture (fd=%d)\n", fd);
+       pcap_packet_type = pcap_datalink(pt);
+       switch (pcap_packet_type) {
+       case DLT_EN10MB:
+               t = "DLT_EN10MB";
+               break;
+       default:
+               DBG_ERR("Unknown pcap packet type %d\n", pcap_packet_type);
+               pcap_close(pt);
+               return -1;
+       }
+
+       fd = pcap_get_selectable_fd(pt);
+       DBG_DEBUG("Opened pcap capture for TCP tickle (type=%s, fd=%d)\n",
+                 t,
+                 fd);
 
        return fd;
 }
@@ -1005,10 +1020,12 @@ int ctdb_sys_read_tcp_packet(int s,
                             uint16_t *window)
 {
        int ret;
-       const struct ether_header *eth;
        struct pcap_pkthdr pkthdr;
        const u_char *buffer;
        pcap_t *pt = (pcap_t *)private_data;
+       int pcap_packet_type;
+       uint16_t ether_type;
+       size_t ll_hdr_len;
 
        buffer=pcap_next(pt, &pkthdr);
        if (buffer==NULL) {
@@ -1018,36 +1035,50 @@ int ctdb_sys_read_tcp_packet(int s,
        ZERO_STRUCTP(src);
        ZERO_STRUCTP(dst);
 
-       /* Ethernet */
-       eth = (const struct ether_header *)buffer;
+       pcap_packet_type = pcap_datalink(pt);
+       switch (pcap_packet_type) {
+       case DLT_EN10MB: {
+               const struct ether_header *eth =
+                       (const struct ether_header *)buffer;
+               ether_type = ntohs(eth->ether_type);
+               ll_hdr_len = sizeof(struct ether_header);
+               break;
+       }
+       default:
+               DBG_DEBUG("Unknown pcap packet type %d\n", pcap_packet_type);
+               return EPROTONOSUPPORT;
+       }
 
-       /* we want either IPv4 or IPv6 */
-       if (eth->ether_type == htons(ETHERTYPE_IP)) {
-               ret = tcp4_extract(buffer + sizeof(struct ether_header),
-                                  (size_t)(pkthdr.caplen -
-                                           sizeof(struct ether_header)),
+       switch (ether_type) {
+       case ETHERTYPE_IP:
+               ret = tcp4_extract(buffer + ll_hdr_len,
+                                  (size_t)pkthdr.caplen - ll_hdr_len,
                                   &src->ip,
                                   &dst->ip,
                                   ack_seq,
                                   seq,
                                   rst,
                                   window);
-               return ret;
-
-       } else if (eth->ether_type == htons(ETHERTYPE_IP6)) {
-               ret = tcp6_extract(buffer + sizeof(struct ether_header),
-                                  (size_t)(pkthdr.caplen -
-                                           sizeof(struct ether_header)),
+               break;
+       case ETHERTYPE_IP6:
+               ret = tcp6_extract(buffer + ll_hdr_len,
+                                  (size_t)pkthdr.caplen - ll_hdr_len,
                                   &src->ip6,
                                   &dst->ip6,
                                   ack_seq,
                                   seq,
                                   rst,
                                   window);
-               return ret;
+               break;
+       case ETHERTYPE_ARP:
+               /* Silently ignore ARP packets */
+               return EPROTO;
+       default:
+               DBG_DEBUG("Unknown ether type %"PRIu16"\n", ether_type);
+               return EPROTO;
        }
 
-       return ENOMSG;
+       return ret;
 }
 
 #endif /* defined(HAVE_AF_PACKET) && !defined(ENABLE_PCAP) */