the-tcpdump-group / libpcap

the LIBpcap interface to various kernel packet capture mechanism
https://www.tcpdump.org/
Other
2.7k stars 851 forks source link

[Patch] allow cooked mode on linux ethernet devices #246

Open guyharris opened 11 years ago

guyharris commented 11 years ago

Converted from SourceForge issue 3078313, submitted by stu-42

hi,

i have a use case where i would like to capture packets from a ethernet interface on linux using the DLT_LINUX_SLL link type, but i don't want to necessarily capture on the "any" pseudointerface. specifically, i need access to the DLT_LINUX_SLL "packet type" field so that i can determine if the packet was sent or received by the host. i'd like to perform UDP checksum verification on captured UDP packets, but if UDP checksum offload is enabled on the interface i need to selectively verify only the packets that were received by the host, which is not possible with the DLT_EN10MB link type, hence this patch. since my code will be run on hosts outside my control, disabling UDP checksum offload is unfortunately not an option for me.

this patch is fairly trivial -- it basically just alters the pcap-linux.c:activate_new() logic slightly, but it does introduce a new pcap_set_cooked() entry point. it might need to be modified slightly so that pcap_set_cooked() fails on non-linux platforms.

also, if this is merged to libpcap mainline, can you please update my entry in the CREDITS file to use my git author address (edmonds@debian.org) rather than my sourceforge address?

thanks.

infrastation commented 11 years ago

Suggested patch 1:

From d8daa5c91213dfd22f3016f6188378d43d0ada37 Mon Sep 17 00:00:00 2001
From: Robert S. Edmonds <edmonds@debian.org>
Date: Sat, 24 Jul 2010 10:20:50 -0400
Subject: [PATCH 1/2] add function pcap_set_cooked() for explicitly requesting cooked mode on linux

---
 pcap-int.h   |    1 +
 pcap-linux.c |   12 +++++++-----
 pcap.c       |    9 +++++++++
 pcap/pcap.h  |    1 +
 4 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/pcap-int.h b/pcap-int.h
index c3afbad..0d7481f 100644
--- a/pcap-int.h
+++ b/pcap-int.h
@@ -209,6 +209,7 @@ struct pcap_opt {
    char    *source;
    int promisc;
    int rfmon;
+   int cooked;
 };

 /*
diff --git a/pcap-linux.c b/pcap-linux.c
index af12543..b9e694d 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -2683,9 +2683,10 @@ activate_new(pcap_t *handle)
     * socket for the cooked interface, otherwise we first
     * try a SOCK_RAW socket for the raw interface.
     */
-   sock_fd = is_any_device ?
-       socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) :
-       socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+   if (is_any_device || handle->opt.cooked)
+       sock_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
+   else
+       sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

    if (sock_fd == -1) {
        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
@@ -2720,7 +2721,7 @@ activate_new(pcap_t *handle)
     * to cooked mode if we have an unknown interface type
     * or a type we know doesn't work well in raw mode.
     */
-   if (!is_any_device) {
+   if (!(is_any_device || handle->opt.cooked)) {
        /* Assume for now we don't need cooked mode. */
        handle->md.cooked = 0;

@@ -2844,7 +2845,8 @@ activate_new(pcap_t *handle)
        }
    } else {
        /*
-        * The "any" device.
+        * The "any" device, or the user has explicitly requested
+        * cooked mode.
         */
        if (handle->opt.rfmon) {
            /*
diff --git a/pcap.c b/pcap.c
index 8c5959f..3be5ff1 100644
--- a/pcap.c
+++ b/pcap.c
@@ -318,6 +318,15 @@ pcap_set_buffer_size(pcap_t *p, int buffer_size)
 }

 int
+pcap_set_cooked(pcap_t *p, int cooked)
+{
+   if (pcap_check_activated(p))
+       return PCAP_ERROR_ACTIVATED;
+   p->opt.cooked = 1;
+   return 0;
+}
+
+int
 pcap_activate(pcap_t *p)
 {
    int status;
diff --git a/pcap/pcap.h b/pcap/pcap.h
index 05ba31f..c608f8b 100644
--- a/pcap/pcap.h
+++ b/pcap/pcap.h
@@ -276,6 +276,7 @@ int pcap_can_set_rfmon(pcap_t *);
 int    pcap_set_rfmon(pcap_t *, int);
 int    pcap_set_timeout(pcap_t *, int);
 int    pcap_set_buffer_size(pcap_t *, int);
+int    pcap_set_cooked(pcap_t *, int);
 int    pcap_activate(pcap_t *);

 pcap_t *pcap_open_live(const char *, int, int, int, char *);
-- 
1.7.1

Suggested patch 2:

From af6d6ff83f687769a444f32283c96a7d13883423 Mon Sep 17 00:00:00 2001
From: Robert S. Edmonds <edmonds@debian.org>
Date: Sat, 24 Jul 2010 10:26:12 -0400
Subject: [PATCH 2/2] add cooked option to opentest

---
 opentest.c |   17 ++++++++++++++---
 1 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/opentest.c b/opentest.c
index 0c91531..02275df 100644
--- a/opentest.c
+++ b/opentest.c
@@ -51,7 +51,7 @@ main(int argc, char **argv)
 {
    register int op;
    register char *cp, *device;
-   int dorfmon, dopromisc, snaplen, useactivate, bufsize;
+   int dorfmon, dopromisc, snaplen, useactivate, bufsize, cooked;
    char ebuf[PCAP_ERRBUF_SIZE];
    pcap_t *pd;
    int status = 0;
@@ -62,13 +62,14 @@ main(int argc, char **argv)
    snaplen = MAXIMUM_SNAPLEN;
    bufsize = 0;
    useactivate = 0;
+   cooked = 0;
    if ((cp = strrchr(argv[0], '/')) != NULL)
        program_name = cp + 1;
    else
        program_name = argv[0];

    opterr = 0;
-   while ((op = getopt(argc, argv, "i:Ips:aB:")) != -1) {
+   while ((op = getopt(argc, argv, "i:Ips:aB:c")) != -1) {
        switch (op) {

        case 'i':
@@ -107,6 +108,10 @@ main(int argc, char **argv)
            useactivate = 1;
            break;

+       case 'c':
+           cooked = 1;
+           break;
+
        default:
            usage();
            /* NOTREACHED */
@@ -143,6 +148,12 @@ main(int argc, char **argv)
                error("%s: pcap_set_buffer_size failed: %s",
                    device, pcap_statustostr(status));
        }
+       if (cooked != 0) {
+           status = pcap_set_cooked(pd, 1);
+           if (status != 0)
+               error("%s: pcap_set_cooked failed: %s",
+                   device, pcap_statustostr(status));
+       }
        status = pcap_activate(pd);
        if (status < 0) {
            /*
@@ -174,7 +185,7 @@ static void
 usage(void)
 {
    (void)fprintf(stderr,
-       "Usage: %s [ -Ipa ] [ -i interface ] [ -s snaplen ] [ -B bufsize ]\n",
+       "Usage: %s [ -Ipac ] [ -i interface ] [ -s snaplen ] [ -B bufsize ]\n",
        program_name);
    exit(1);
 }
-- 
1.7.1
infrastation commented 1 month ago

Originally requested by @edmonds on 2010-09-29. The e-mail has been updated as requested.

infrastation commented 1 month ago

This is a sound problem. However, from the API point of view it would be wrong to introduce a new Linux-specific function to override the DLT quietly whilst continuing to return the original DLT from pcap_datalink(3PCAP). A more consistent solution would need to:

With this in mind, I had a look at pcap-linux.c and it is obvious the consistent solution would require more work. The matter is, pcap_set_datalink() requires the handle to be activated. I do not yet understand why because pcap_set_protocol_linux(3PCAP), pcap_set_promisc(3PCAP) and pcap_set_rfmon(3PCAP) all require the handle not to be activated.

Anyway, a practical consequence of the present design is that in the more consistent solution pcap_set_datalink() would be called after pcap_activate_linux(). The latter and its helper functions setup_socket() and create_ring() together comprise about 900 lines of code, some of which is conditional on pcap_linux.cooked. Then during the packet capture pcap_handle_packet_mmap(), which is about 400 lines long, checks the same to act consistently with the setup. So the difficulty here is in the amount of coordination required between these parts.

fxlb commented 1 month ago

i need to selectively verify only the packets that were received by the host, which is not possible with the DLT_EN10MB link type,

-Q in doesn't work?

It seems it works only on live capture. Reason unknown.

(But documented in the man page This operation is not supported if a ''savefile'' is being read.)

infrastation commented 1 month ago

That could be a solution if the only problem is to select the packets by direction at the capture time. -Q is a direct interface to pcap_setdirection(3PCAP), which is implemented for Linux at the userspace end of the capture buffer. It cannot reliably have an effect on savefiles because most formats do not record the packet direction. If the problem is to capture all packets and to see which packets went in which direction, this would not solve it.

guyharris commented 1 month ago

The matter is, pcap_set_datalink() requires the handle to be activated. I do not yet understand why because pcap_set_protocol_linux(3PCAP), pcap_set_promisc(3PCAP) and pcap_set_rfmon(3PCAP) all require the handle not to be activated.

pcap_set_datalink() was introduced in 0.8, before there were pcap_create() and pcap_activate(), so there was no such thing as an un-activated pcap_t.

It should probably be changed to work before activation - as should pcap_list_datalinks(), so that, on at least some platforms, you can get the data link types available in monitor mode without having to put the interface in monitor mode. The most work on that would need to be done on the *BSDs - the original platform for which the API was created - where the ioctls to get the link-layer types and set the link-layer type can only be done if you have a BPF device bound to the interface, so, to get the link-layer types, it'd have to do a temporary open and ioctl and, to set the link-layer type, either do the same to get the types and check whether the requested type is in the list and fail if it isn't, or just save it and defer the failure to pcap_activate() time.

guyharris commented 1 month ago

-Q in doesn't work?

It seems it works only on live capture. Reason unknown.

It could work on savefiles if either 1) it's a pcap file with a cooked link-layer type or 2) it's a pcapng file, although most pcapng files probably don't have direction information in the Enhanced Packet Blocks, so it wouldn't work there.

edmonds commented 1 month ago

Hi,

I don't think I remember writing this patch 14 years ago. I'm not entirely sure exactly what I wanted to use this for, but whatever it was I'm not doing it any more. It does sound kind of nifty, though.

Thanks!