pespin has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-pcap/+/39265?usp=email )


Change subject: WIP: Support generation and storing of pcapng format
......................................................................

WIP: Support generation and storing of pcapng format

TODO:
* client: timestamp in EPB
* server: detect pcap vs pcapng using magic field in link_hdr
** server: Do size checks based on pcap/pcapng knowledge
** server: Change saved file suffix based on pcap/pcapng knowledge.

Change-Id: I3c80518a1e53a1f77e1aca8dfa83f683f9516ad6
---
M include/osmo-pcap/osmo_pcap_client.h
M include/osmo-pcap/osmo_pcap_server.h
M src/Makefile.am
M src/osmo_client_core.c
M src/osmo_client_network.c
A src/osmo_client_pcap.c
M src/osmo_client_vty.c
M src/osmo_server_network.c
8 files changed, 704 insertions(+), 86 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-pcap refs/changes/65/39265/1

diff --git a/include/osmo-pcap/osmo_pcap_client.h 
b/include/osmo-pcap/osmo_pcap_client.h
index 250a41f..83621bc 100644
--- a/include/osmo-pcap/osmo_pcap_client.h
+++ b/include/osmo-pcap/osmo_pcap_client.h
@@ -61,6 +61,12 @@
        PROTOCOL_IPIP,
 };

+enum osmo_pcap_fmt {
+       OSMO_PCAP_FMT_PCAP = 0,
+       OSMO_PCAP_FMT_PCAPNG,
+};
+
+
 struct osmo_pcap_client_conn {
        struct llist_head entry;
        const char *name;
@@ -114,6 +120,7 @@
        int filter_itself;
        int gprs_filtering;
        int snaplen;
+       enum osmo_pcap_fmt pcap_fmt;

        struct osmo_pcap_client_conn *conn;
        struct llist_head conns;
@@ -138,7 +145,9 @@
 struct osmo_pcap_client_conn *osmo_client_conn_alloc(struct osmo_pcap_client 
*client, const char *name);
 void osmo_client_conn_free(struct osmo_pcap_client_conn *conn);
 void osmo_client_conn_send_data(struct osmo_pcap_client_conn *conn,
-                               struct pcap_pkthdr *hdr, const uint8_t *data);
+                               const struct osmo_pcap_handle *ph,
+                               const struct pcap_pkthdr *pkthdr,
+                               const uint8_t *data);
 void osmo_client_conn_send_link(struct osmo_pcap_client_conn *conn);
 void osmo_client_conn_connect(struct osmo_pcap_client_conn *conn);
 void osmo_client_conn_disconnect(struct osmo_pcap_client_conn *conn);
@@ -149,6 +158,47 @@
 void osmo_pcap_handle_free(struct osmo_pcap_handle *ph);
 int osmo_pcap_handle_start_capture(struct osmo_pcap_handle *ph);

+unsigned int osmo_pcap_file_global_header_size(void);
+int osmo_pcap_file_msgb_append_global_header(struct msgb *msg, uint32_t 
snaplen, uint32_t linktype);
+unsigned int osmo_pcap_file_record_size(const struct pcap_pkthdr *in_hdr);
+int osmo_pcap_file_msgb_append_record(struct msgb *msg, const struct 
pcap_pkthdr *in_hdr, const uint8_t *data);
+
+struct osmo_pcapng_file_shb_pars {
+       const char *hardware;
+       const char *os;
+       const char *userappl;
+};
+unsigned int osmo_pcapng_file_shb_size(const struct osmo_pcapng_file_shb_pars 
*pars);
+int osmo_pcapng_file_msgb_append_shb(struct msgb *msg, const struct 
osmo_pcapng_file_shb_pars *pars);
+
+struct osmo_pcapng_file_idb_pars {
+       //char *comment;
+       const char *name;
+       //char *descr;
+       //char *filter;
+       //char *os;
+       //char *hardware;
+       int link_type;
+       int snap_len;
+       //uint64_t *bytes_written;
+       //uint64_t if_speed;
+       //uint8_t tsresol;
+};
+unsigned int osmo_pcapng_file_idb_size(const struct osmo_pcapng_file_idb_pars 
*pars);
+int osmo_pcapng_file_msgb_append_idb(struct msgb *msg, const struct 
osmo_pcapng_file_idb_pars *pars);
+
+struct osmo_pcapng_file_epb_pars {
+       uint32_t interface_id;
+       uint32_t timestamp_high;
+       uint32_t timestamp_low;
+       const uint8_t *captured_data;
+       uint32_t captured_len;
+       uint32_t packet_len;
+};
+unsigned int osmo_pcapng_file_epb_size(const struct osmo_pcapng_file_epb_pars 
*pars);
+int osmo_pcapng_file_msgb_append_epb(struct msgb *msg, const struct 
osmo_pcapng_file_epb_pars *pars);
+
+
 #define LOGCONN(conn, lvl, fmt, args...) \
        LOGP(DCLIENT, lvl, "CONN(%s,%s:%d) " fmt, (conn)->name, (conn)->srv_ip, 
(conn)->srv_port, ## args)

diff --git a/include/osmo-pcap/osmo_pcap_server.h 
b/include/osmo-pcap/osmo_pcap_server.h
index 614ceca..b3188a8 100644
--- a/include/osmo-pcap/osmo_pcap_server.h
+++ b/include/osmo-pcap/osmo_pcap_server.h
@@ -30,6 +30,7 @@
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/write_queue.h>

+#include <stdint.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -91,7 +92,8 @@
        char *curr_filename;

        /* pcap stuff */
-       struct pcap_file_header file_hdr;
+       uint8_t *file_hdr;
+       uint32_t file_hdr_len;

        /* last time */
        struct tm last_write;
diff --git a/src/Makefile.am b/src/Makefile.am
index b35227c..47f7795 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,9 +17,10 @@
        osmo_client_main.c \
        osmo_common.c \
        osmo_client_core.c \
-       osmo_client_vty.c \
+       osmo_client_pcap.c \
        osmo_client_network.c \
        osmo_client_stats.c \
+       osmo_client_vty.c \
        osmo_tls.c \
        $(NULL)

diff --git a/src/osmo_client_core.c b/src/osmo_client_core.c
index a2ab894..dd16878 100644
--- a/src/osmo_client_core.c
+++ b/src/osmo_client_core.c
@@ -172,7 +172,7 @@
                return 0;

        llist_for_each_entry(conn, &client->conns, entry)
-               osmo_client_conn_send_data(conn, &hdr, data);
+               osmo_client_conn_send_data(conn, ph, &hdr, data);
        return 0;
 }

@@ -303,6 +303,7 @@
                return NULL;

        client->snaplen = DEFAULT_SNAPLEN;
+       client->pcap_fmt = OSMO_PCAP_FMT_PCAP;
        INIT_LLIST_HEAD(&client->handles);
        INIT_LLIST_HEAD(&client->conns);

diff --git a/src/osmo_client_network.c b/src/osmo_client_network.c
index 70fe740..a9282e4 100644
--- a/src/osmo_client_network.c
+++ b/src/osmo_client_network.c
@@ -1,6 +1,7 @@
 /*
  * osmo-pcap-client code
  *
+ * (C) 2025 by sysmocom - s.f.m.c. GmbH <[email protected]>
  * (C) 2011-2016 by Holger Hans Peter Freyther <[email protected]>
  * (C) 2011 by On-Waves
  * All Rights Reserved
@@ -24,6 +25,7 @@
 #include <osmo-pcap/common.h>
 #include <osmo-pcap/wireformat.h>

+#include <osmocom/core/utils.h>
 #include <osmocom/core/msgb.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/select.h>
@@ -160,112 +162,287 @@
        }
 }

-void osmo_client_conn_send_data(struct osmo_pcap_client_conn *conn,
-                               struct pcap_pkthdr *in_hdr, const uint8_t *data)
+static struct msgb *osmo_client_conn_prepare_msg_data_pcap(struct 
osmo_pcap_client_conn *conn,
+                                                          const struct 
osmo_pcap_handle *ph,
+                                                          const struct 
pcap_pkthdr *pkthdr,
+                                                          const uint8_t *data)
 {
        struct osmo_pcap_data *om_hdr;
-       struct osmo_pcap_pkthdr *hdr;
        struct msgb *msg;
-       int offset, ip_len;
-       struct osmo_pcap_handle *ph;
+       unsigned int record_size = osmo_pcap_file_record_size(pkthdr);

-       if (in_hdr->len > in_hdr->caplen) {
-               LOGCONN(conn, LOGL_ERROR, "Recording truncated packet, len %zu 
> snaplen %zu\n",
-                       (size_t) in_hdr->len, (size_t) in_hdr->caplen);
-               rate_ctr_inc2(conn->client->ctrg, CLIENT_CTR_2BIG);
-       }
-
-       msg = msgb_alloc(in_hdr->caplen + sizeof(*om_hdr) + sizeof(*hdr), 
"data-data");
+       msg = msgb_alloc(sizeof(*om_hdr) + record_size, "pcap-data");
        if (!msg) {
                LOGCONN(conn, LOGL_ERROR, "Failed to allocate\n");
                rate_ctr_inc2(conn->client->ctrg, CLIENT_CTR_NOMEM);
-               return;
+               return NULL;
+       }
+
+       om_hdr = (struct osmo_pcap_data *) msgb_put(msg, sizeof(*om_hdr));
+       om_hdr->type = PKT_LINK_DATA;
+       om_hdr->len = htons(record_size);
+       osmo_pcap_file_msgb_append_record(msg, pkthdr, data);
+
+       return msg;
+}
+
+static struct msgb *osmo_client_conn_prepare_msg_data_pcapng(struct 
osmo_pcap_client_conn *conn,
+                                                            const struct 
osmo_pcap_handle *ph,
+                                                            const struct 
pcap_pkthdr *pkthdr,
+                                                            const uint8_t 
*data)
+{
+       struct osmo_pcap_data *om_hdr;
+       struct msgb *msg;
+       struct osmo_pcapng_file_epb_pars epb_pars;
+       unsigned int record_size;
+       int rc;
+
+       epb_pars = (struct osmo_pcapng_file_epb_pars){
+               .interface_id = ph->idx,
+               .timestamp_high = 0, /* TODO: fill this properly */
+               .timestamp_low = 0, /* TODO: fill this properly */
+               .captured_data = data,
+               .captured_len = pkthdr->caplen,
+               .packet_len = pkthdr->len,
+       };
+
+       record_size = osmo_pcapng_file_epb_size(&epb_pars);
+
+       msg = msgb_alloc(sizeof(*om_hdr) + record_size, "pcap-data");
+       if (!msg) {
+               LOGCONN(conn, LOGL_ERROR, "Failed to allocate\n");
+               rate_ctr_inc2(conn->client->ctrg, CLIENT_CTR_NOMEM);
+               return NULL;
+       }
+
+       om_hdr = (struct osmo_pcap_data *) msgb_put(msg, sizeof(*om_hdr));
+       om_hdr->type = PKT_LINK_DATA;
+       om_hdr->len = htons(record_size);
+       rc = osmo_pcapng_file_msgb_append_epb(msg, &epb_pars);
+       if (rc < 0) {
+               msgb_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+static struct msgb *osmo_client_conn_prepare_msg_ipip(struct 
osmo_pcap_client_conn *conn,
+                                                     const struct 
osmo_pcap_handle *ph,
+                                                     const struct pcap_pkthdr 
*pkthdr,
+                                                     const uint8_t *data)
+{
+       struct msgb *msg;
+       int offset, ip_len;
+
+       offset = get_iphdr_offset(pcap_datalink(ph->handle));
+       if (offset < 0)
+               return NULL;
+
+       ip_len = pkthdr->caplen - offset;
+       if (ip_len < 0)
+               return NULL;
+
+
+       msg = msgb_alloc(ip_len, "ipip_msg");
+       if (!msg) {
+               LOGP(DCLIENT, LOGL_ERROR, "Failed to allocate data.\n");
+               rate_ctr_inc2(conn->client->ctrg, CLIENT_CTR_NOMEM);
+               return NULL;
+       }
+       msg->l2h = msgb_put(msg, ip_len);
+       memcpy(msg->l2h, data+offset, ip_len);
+       return msg;
+}
+
+void osmo_client_conn_send_data(struct osmo_pcap_client_conn *conn,
+                               const struct osmo_pcap_handle *ph,
+                               const struct pcap_pkthdr *pkthdr,
+                               const uint8_t *data)
+{
+       struct osmo_pcap_client *client = conn->client;
+       struct msgb *msg;
+
+       if (pkthdr->len > pkthdr->caplen) {
+               LOGCONN(conn, LOGL_ERROR, "Recording truncated packet, len %zu 
> snaplen %zu\n",
+                       (size_t) pkthdr->len, (size_t) pkthdr->caplen);
+               rate_ctr_inc2(client->ctrg, CLIENT_CTR_2BIG);
        }

        switch (conn->protocol) {
        case PROTOCOL_OSMOPCAP:
-               om_hdr = (struct osmo_pcap_data *) msgb_put(msg, 
sizeof(*om_hdr));
-               om_hdr->type = PKT_LINK_DATA;
+               switch (client->pcap_fmt) {
+               case OSMO_PCAP_FMT_PCAP:
+                       msg = osmo_client_conn_prepare_msg_data_pcap(conn, ph, 
pkthdr, data);
+                       break;
+               case OSMO_PCAP_FMT_PCAPNG:
+                       msg = osmo_client_conn_prepare_msg_data_pcapng(conn, 
ph, pkthdr, data);
+                       break;
+               default:
+                       OSMO_ASSERT(0);
+               }

-               msg->l2h = msgb_put(msg, sizeof(*hdr));
-               hdr = (struct osmo_pcap_pkthdr *) msg->l2h;
-               hdr->ts_sec = in_hdr->ts.tv_sec;
-               hdr->ts_usec = in_hdr->ts.tv_usec;
-               hdr->caplen = in_hdr->caplen;
-               hdr->len = in_hdr->len;
-
-               msg->l3h = msgb_put(msg, in_hdr->caplen);
-               memcpy(msg->l3h, data, in_hdr->caplen);
-
-               om_hdr->len = htons(msgb_l2len(msg));
-               rate_ctr_add2(conn->client->ctrg, CLIENT_CTR_BYTES, 
hdr->caplen);
-               rate_ctr_inc2(conn->client->ctrg, CLIENT_CTR_PKTS);
                break;
        case PROTOCOL_IPIP:
-               /* TODO: support capturing from multiple interfaces here: */
-               ph = llist_first_entry_or_null(&conn->client->handles, struct 
osmo_pcap_handle, entry);
-               if (!ph) {
-                       msgb_free(msg);
-                       return;
-               }
-               offset = get_iphdr_offset(pcap_datalink(ph->handle));
-               if (offset < 0) {
-                       msgb_free(msg);
-                       return;
-               }
-               ip_len = in_hdr->caplen - offset;
-               if (ip_len < 0) {
-                       msgb_free(msg);
-                       return;
-               }
-               msg->l2h = msgb_put(msg, ip_len);
-               memcpy(msg->l2h, data+offset, ip_len);
+               msg = osmo_client_conn_prepare_msg_ipip(conn, ph, pkthdr, data);
                break;
        default:
                OSMO_ASSERT(0);
        }

+       if (!msg)
+               return;
+
+       rate_ctr_add2(conn->client->ctrg, CLIENT_CTR_BYTES, pkthdr->caplen);
+       rate_ctr_inc2(conn->client->ctrg, CLIENT_CTR_PKTS);
+
        write_data(conn, msg);
 }

+static struct msgb *osmo_client_conn_prepare_msg_link_pcap(struct 
osmo_pcap_client_conn *conn)
+{
+       struct osmo_pcap_data *om_hdr;
+       struct msgb *msg;
+       struct osmo_pcap_handle *ph;
+       int rc;
+       int linktype;
+
+       ph = llist_first_entry_or_null(&conn->client->handles, struct 
osmo_pcap_handle, entry);
+       if (!ph || !ph->handle) {
+               LOGCONN(conn, LOGL_ERROR, "No pcap_handle not sending link 
info\n");
+               return NULL;
+       }
+       linktype = pcap_datalink(ph->handle);
+
+       /* Make sure others have same linktype, .pcap doesn't support different
+        * linktypes since traffic from all ifaces goes mixed together. */
+       llist_for_each_entry(ph, &conn->client->handles, entry) {
+               if (linktype != pcap_datalink(ph->handle)) {
+                       LOGCONN(conn, LOGL_ERROR,
+                               "File format 'pcap' doesn't support recording 
from multiple ifaces "
+                               "with different link types! Use VTY config 
'pcap file-format pcapng'.\n");
+                       return NULL;
+               }
+       }
+
+       msg = msgb_alloc(sizeof(*om_hdr) + osmo_pcap_file_global_header_size(), 
"link-data");
+       if (!msg) {
+               LOGP(DCLIENT, LOGL_ERROR, "Failed to allocate data.\n");
+               return NULL;
+       }
+
+
+       om_hdr = (struct osmo_pcap_data *)msgb_put(msg, sizeof(*om_hdr));
+       om_hdr->type = PKT_LINK_HDR;
+
+       rc = osmo_pcap_file_msgb_append_global_header(msg, 
conn->client->snaplen, pcap_datalink(ph->handle));
+       if (rc < 0) {
+               LOGP(DCLIENT, LOGL_ERROR, "Failed to create pcap file global 
header.\n");
+               msgb_free(msg);
+               return NULL;
+       }
+
+       /* Update payload length: */
+       om_hdr->len = htons(rc);
+       return msg;
+}
+
+struct osmo_pcapng_file_shb_pars shb_pars = {
+       .hardware = "osmo-pcap hw",
+       .os = "osmo-pcap os",
+       .userappl = "osmo-pcap userappl",
+};
+
+static int pcap_handle_prepare_pcapng_idb_pars(const struct osmo_pcap_handle 
*ph,
+                                              struct osmo_pcapng_file_idb_pars 
*pars)
+{
+       struct osmo_pcap_client *client = ph->client;
+       memset(pars, 0, sizeof(*pars));
+
+       pars->name = ph->devname;
+       //pars->filter = client->filter_string;
+       pars->link_type = pcap_datalink(ph->handle);
+       pars->snap_len = client->snaplen;
+       return 0;
+}
+
+static struct msgb *osmo_client_conn_prepare_msg_link_pcapng(struct 
osmo_pcap_client_conn *conn)
+{
+       struct osmo_pcap_data *om_hdr;
+       struct msgb *msg;
+       struct osmo_pcap_handle *ph;
+       int rc;
+       uint32_t file_hdr_size = osmo_pcapng_file_shb_size(&shb_pars);
+
+       /* Calculate size: */
+       llist_for_each_entry(ph, &conn->client->handles, entry) {
+               struct osmo_pcapng_file_idb_pars idb_pars;
+               if (pcap_handle_prepare_pcapng_idb_pars(ph, &idb_pars) < 0) {
+                       LOGPH(ph, LOGL_ERROR, "Failed preparing pcapng IDB from 
handle\n");
+                       return NULL;
+               }
+               file_hdr_size += osmo_pcapng_file_idb_size(&idb_pars);
+       }
+
+       msg = msgb_alloc(sizeof(*om_hdr) + file_hdr_size, "link-data");
+       if (!msg) {
+               LOGCONN(conn, LOGL_ERROR, "Failed to allocate data.\n");
+               return NULL;
+       }
+
+       om_hdr = (struct osmo_pcap_data *)msgb_put(msg, sizeof(*om_hdr));
+       om_hdr->type = PKT_LINK_HDR;
+
+       rc = osmo_pcapng_file_msgb_append_shb(msg, &shb_pars);
+       if (rc < 0) {
+               LOGCONN(conn, LOGL_ERROR, "Failed to create pcapng SHB\n");
+               msgb_free(msg);
+               return NULL;
+       }
+       om_hdr->len = rc;
+
+       llist_for_each_entry(ph, &conn->client->handles, entry) {
+               struct osmo_pcapng_file_idb_pars idb_pars;
+               if (pcap_handle_prepare_pcapng_idb_pars(ph, &idb_pars) < 0) {
+                       LOGPH(ph, LOGL_ERROR, "Failed preparing pcapng IDB from 
handle\n");
+                       msgb_free(msg);
+                       return NULL;
+               }
+               rc = osmo_pcapng_file_msgb_append_idb(msg, &idb_pars);
+               if (rc < 0) {
+                       LOGPH(ph, LOGL_ERROR, "Failed to append pcapng IDB to 
msgb\n");
+                       msgb_free(msg);
+                       return NULL;
+               }
+               om_hdr->len += rc;
+       }
+
+       OSMO_ASSERT(om_hdr->len == file_hdr_size);
+       om_hdr->len = htons(om_hdr->len);
+       return msg;
+}
+
 void osmo_client_conn_send_link(struct osmo_pcap_client_conn *conn)
 {
-       struct osmo_pcap_handle *ph;
-       struct pcap_file_header *hdr;
-       struct osmo_pcap_data *om_hdr;
        struct msgb *msg;

        /* IPIP encapsulation has no linktype header */
        if (conn->protocol == PROTOCOL_IPIP)
                return;

-       /* TODO: support capturing from multiple interfaces here: */
-       ph = llist_first_entry_or_null(&conn->client->handles, struct 
osmo_pcap_handle, entry);
-       if (!ph || !ph->handle) {
-               LOGCONN(conn, LOGL_ERROR, "No pcap_handle not sending link 
info\n");
-               return;
+       switch (conn->client->pcap_fmt) {
+       case OSMO_PCAP_FMT_PCAP:
+               msg = osmo_client_conn_prepare_msg_link_pcap(conn);
+               break;
+       case OSMO_PCAP_FMT_PCAPNG:
+               msg = osmo_client_conn_prepare_msg_link_pcapng(conn);
+               break;
+       default:
+               OSMO_ASSERT(0);
        }

-       msg = msgb_alloc(sizeof(*om_hdr) + sizeof(*hdr), "link-data");
-       if (!msg) {
-               LOGP(DCLIENT, LOGL_ERROR, "Failed to allocate data.\n");
+       if (!msg)
                return;
-       }
-
-
-       om_hdr = (struct osmo_pcap_data *) msgb_put(msg, sizeof(*om_hdr));
-       om_hdr->type = PKT_LINK_HDR;
-       om_hdr->len = htons(sizeof(*hdr));
-
-       hdr = (struct pcap_file_header *) msgb_put(msg, sizeof(*hdr));
-       hdr->magic = 0xa1b2c3d4;
-       hdr->version_major = 2;
-       hdr->version_minor = 4;
-       hdr->thiszone = 0;
-       hdr->sigfigs = 0;
-       hdr->snaplen = conn->client->snaplen;
-       hdr->linktype = pcap_datalink(ph->handle);
-
        write_data(conn, msg);
 }

diff --git a/src/osmo_client_pcap.c b/src/osmo_client_pcap.c
new file mode 100644
index 0000000..e7b987e
--- /dev/null
+++ b/src/osmo_client_pcap.c
@@ -0,0 +1,355 @@
+/*
+ * osmo-pcap-client code
+ *
+ * (C) 2025 by sysmocom - s.f.m.c. GmbH <[email protected]>
+ * (C) 2011-2016 by Holger Hans Peter Freyther <[email protected]>
+ * (C) 2011 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <pcap/pcap.h>
+
+#include <osmocom/core/msgb.h>
+
+#include <osmo-pcap/osmo_pcap_client.h>
+#include <osmo-pcap/common.h>
+#include <osmo-pcap/wireformat.h>
+
+/***********************************************************
+ * Libpcap File Format (.pcap)
+ * https://wiki.wireshark.org/Development/LibpcapFileFormat
+ ***********************************************************/
+
+/* Get required length to store a Global Header */
+unsigned int osmo_pcap_file_global_header_size(void)
+{
+       return sizeof(struct pcap_file_header);
+}
+
+/* Appends a Global Header to msg
+ * returns number of bytes appended on success, negative on error */
+int osmo_pcap_file_msgb_append_global_header(struct msgb *msg, uint32_t 
snaplen, uint32_t linktype)
+{
+       struct pcap_file_header *hdr;
+
+       hdr = (struct pcap_file_header *) msgb_put(msg, sizeof(*hdr));
+       hdr->magic = 0xa1b2c3d4;
+       hdr->version_major = 2;
+       hdr->version_minor = 4;
+       hdr->thiszone = 0;
+       hdr->sigfigs = 0;
+       hdr->snaplen = snaplen;
+       hdr->linktype = linktype;
+
+       return sizeof(*hdr);
+}
+
+/* Get required length to store a given record (packet) */
+unsigned int osmo_pcap_file_record_size(const struct pcap_pkthdr *in_hdr)
+{
+       return sizeof(struct osmo_pcap_pkthdr) + in_hdr->caplen;
+}
+
+/* Appends a Record (Packet) Header to msg
+ * returns number of bytes appended on success, negative on error */
+int osmo_pcap_file_msgb_append_record(struct msgb *msg, const struct 
pcap_pkthdr *in_hdr, const uint8_t *data)
+{
+       struct osmo_pcap_pkthdr *hdr;
+       uint8_t *pkt_payload;
+
+       hdr = (struct osmo_pcap_pkthdr *) msgb_put(msg, sizeof(*hdr));
+       hdr->ts_sec = in_hdr->ts.tv_sec;
+       hdr->ts_usec = in_hdr->ts.tv_usec;
+       hdr->caplen = in_hdr->caplen;
+       hdr->len = in_hdr->len;
+
+       pkt_payload = msgb_put(msg, in_hdr->caplen);
+       memcpy(pkt_payload, data, in_hdr->caplen);
+
+       return osmo_pcap_file_record_size(in_hdr);
+}
+
+
+/***********************************************************
+ * PCAP Next Generation (pcapng) Capture File Format
+ * https://wiki.wireshark.org/Development/PcapNg
+ * 
https://ietf-opsawg-wg.github.io/draft-ietf-opsawg-pcap/draft-ietf-opsawg-pcapng.html
+ * wireshark.git: wiretap/pcapng.h, wiretap/pcapng.c, wiretap/pcapng_module.h
+ ***********************************************************/
+
+#define BLOCK_TYPE_SHB 0x0A0D0D0A /* Section Header Block */
+#define BLOCK_TYPE_IDB 0x00000001 /* Interface Description Block */
+#define BLOCK_TYPE_PB  0x00000002 /* Packet Block (obsolete) */
+#define BLOCK_TYPE_SPB 0x00000003 /* Simple Packet Block */
+#define BLOCK_TYPE_EPB 0x00000006 /* Enhanced Packet Block */
+
+/* Options for all blocks */
+#define OPT_EOFOPT             0
+#define OPT_COMMENT            1
+/* Section Header block (SHB) */
+#define OPT_SHB_HARDWARE       2
+#define OPT_SHB_OS             3
+#define OPT_SHB_USERAPPL       4
+
+/* Interface Description block (IDB) */
+#define OPT_IDB_NAME           2
+#define OPT_IDB_DESCRIPTION    3
+#define OPT_IDB_IP4ADDR                4
+#define OPT_IDB_IP6ADDR                5
+#define OPT_IDB_MACADDR                6
+#define OPT_IDB_FILTER         11
+
+/* pcapng: common block header file encoding for every block type */
+struct pcapng_block_header {
+       uint32_t block_type;
+       uint32_t block_total_length;
+       /* x bytes block_body */
+       /* uint32_t block_total_length */
+} __attribute__((packed));
+
+struct pcapng_option_header {
+       uint16_t type;
+       uint16_t value_length;
+} __attribute__((packed));
+
+/* pcapng: section header block file encoding */
+struct pcapng_section_header_block {
+       /* pcapng_block_header_t */
+       uint32_t magic;
+       uint16_t version_major;
+       uint16_t version_minor;
+       uint64_t section_length; /* might be -1 for unknown */
+       /* ... Options ... */
+} __attribute__((packed));
+
+/* pcapng: interface description block file encoding */
+struct pcapng_iface_descr_block {
+       uint16_t linktype;
+       uint16_t reserved;
+       uint32_t snaplen;
+       /* ... Options ... */
+} __attribute__((packed));
+
+/* pcapng: enhanced packet block file encoding */
+struct pcapng_enhanced_packet_block {
+       uint32_t interface_id;
+       uint32_t timestamp_high;
+       uint32_t timestamp_low;
+       uint32_t captured_len;
+       uint32_t packet_len;
+       /* ... Packet Data ... */
+       /* ... Padding ... */
+       /* ... Options ... */
+} __attribute__((packed));
+
+/*
+ * Minimum IDB size = minimum block size + size of fixed length portion of IDB.
+ */
+#define MIN_IDB_SIZE    ((uint32_t)(MIN_BLOCK_SIZE + 
sizeof(pcapng_interface_description_block_t)))
+
+/* Get required length to store a given record (packet) */
+static unsigned int osmo_pcapng_file_opt_string_size(const char *str)
+{
+       size_t str_len = str ? strlen(str) : 0;
+       uint8_t pad = str_len % 4;
+       /* Each option is padded to 4 bytes: */
+       if (pad)
+               pad = 4 - pad;
+       return sizeof(struct pcapng_option_header) + str_len + pad;
+}
+
+static int osmo_pcapng_file_msgb_append_opt_string(struct msgb *msg, uint16_t 
type, const char *str)
+{
+       struct pcapng_option_header *opth;
+       size_t str_len = str ? strlen(str) : 0;
+
+       opth = (struct pcapng_option_header *)msgb_put(msg, sizeof(*opth));
+       opth->type = type;
+       opth->value_length = str_len;
+       if (str_len > 0)
+               memcpy(msgb_put(msg, str_len), str, str_len);
+
+       /* Each option is padded to 4 bytes: */
+       uint8_t pad = str_len % 4;
+       if (pad) {
+               pad = 4 - pad;
+               uint8_t *buf = (uint8_t *)msgb_put(msg, pad);
+               memset(buf, 0, pad);
+       }
+       return sizeof(*opth) + opth->value_length + pad;
+}
+
+/* Get required length to store a given record (packet) */
+static unsigned int osmo_pcapng_file_opt_eofopt_size(void)
+{
+       return sizeof(struct pcapng_option_header);
+}
+
+static int osmo_pcapng_file_msgb_append_opt_eofopt(struct msgb *msg)
+{
+       struct pcapng_option_header *opth;
+
+       opth = (struct pcapng_option_header *)msgb_put(msg, sizeof(*opth));
+       opth->type = OPT_EOFOPT;
+       opth->value_length = 0;
+
+       return sizeof(*opth);
+}
+
+/* Get required length to store a given record (packet) */
+unsigned int osmo_pcapng_file_shb_size(const struct osmo_pcapng_file_shb_pars 
*pars)
+{
+       uint32_t block_total_len = sizeof(struct pcapng_block_header) +
+                                  sizeof(struct pcapng_section_header_block) +
+                                  sizeof(uint32_t);
+       block_total_len += osmo_pcapng_file_opt_string_size(pars->hardware);
+       block_total_len += osmo_pcapng_file_opt_string_size(pars->os);
+       block_total_len += osmo_pcapng_file_opt_string_size(pars->userappl);
+       block_total_len += osmo_pcapng_file_opt_eofopt_size();
+       return block_total_len;
+}
+
+/* Appends a Section Header Block (SHB) to msg
+ * returns number of bytes appended on success, negative on error */
+int osmo_pcapng_file_msgb_append_shb(struct msgb *msg, const struct 
osmo_pcapng_file_shb_pars *pars)
+{
+       struct pcapng_block_header *bh;
+       struct pcapng_section_header_block *shb;
+       uint8_t *footer_len;
+       uint32_t block_total_len = osmo_pcapng_file_shb_size(pars);
+
+       bh = (struct pcapng_block_header *)msgb_put(msg, sizeof(*bh));
+       bh->block_type = BLOCK_TYPE_SHB;
+       bh->block_total_length = block_total_len;
+
+       /* write block fixed content */
+       shb = (struct pcapng_section_header_block *)msgb_put(msg, sizeof(*shb));
+       shb->magic = 0x1A2B3C4D;
+       shb->version_major = 1;
+       shb->version_minor = 0;
+       shb->section_length = -1;
+
+       /* Options (variable) */
+       osmo_pcapng_file_msgb_append_opt_string(msg, OPT_SHB_HARDWARE, 
pars->hardware);
+       osmo_pcapng_file_msgb_append_opt_string(msg, OPT_SHB_OS, pars->os);
+       osmo_pcapng_file_msgb_append_opt_string(msg, OPT_SHB_USERAPPL, 
pars->userappl);
+       osmo_pcapng_file_msgb_append_opt_eofopt(msg);
+
+       /* SHB Block Total Length */
+       footer_len = (uint8_t *)msgb_put(msg, sizeof(uint32_t));
+       memcpy(footer_len, &block_total_len, sizeof(uint32_t));
+
+       return block_total_len;
+}
+
+unsigned int osmo_pcapng_file_idb_size(const struct osmo_pcapng_file_idb_pars 
*pars)
+{
+       uint32_t block_total_len = sizeof(struct pcapng_block_header) +
+                                  sizeof(struct pcapng_iface_descr_block) +
+                                  sizeof(uint32_t);
+       block_total_len += osmo_pcapng_file_opt_string_size(pars->name);
+       //block_total_len += osmo_pcapng_file_opt_string_size(pars->filter);
+       block_total_len += osmo_pcapng_file_opt_eofopt_size();
+       return block_total_len;
+}
+
+int osmo_pcapng_file_msgb_append_idb(struct msgb *msg, const struct 
osmo_pcapng_file_idb_pars *pars)
+{
+       struct pcapng_block_header *bh;
+       struct pcapng_iface_descr_block *idb;
+       uint8_t *footer_len;
+       uint32_t block_total_len = osmo_pcapng_file_idb_size(pars);
+
+       bh = (struct pcapng_block_header *)msgb_put(msg, sizeof(*bh));
+       bh->block_type = BLOCK_TYPE_IDB;
+       bh->block_total_length = block_total_len;
+
+       /* write block fixed content */
+       idb = (struct pcapng_iface_descr_block *)msgb_put(msg, sizeof(*idb));
+       idb->linktype = pars->link_type;
+       idb->reserved = 0;
+       idb->snaplen = pars->snap_len;
+
+       /* Options (variable) */
+       osmo_pcapng_file_msgb_append_opt_string(msg, OPT_IDB_NAME, pars->name);
+       // TODO: filter is not a string opt, it has an extra prepended byte....
+       //osmo_pcapng_file_msgb_append_opt_string(msg, OPT_IDB_FILTER, 
pars->filter);
+       osmo_pcapng_file_msgb_append_opt_eofopt(msg);
+
+       /* IDB Block Total Length */
+       footer_len = (uint8_t *)msgb_put(msg, sizeof(uint32_t));
+       memcpy(footer_len, &block_total_len, sizeof(uint32_t));
+
+       return block_total_len;
+}
+
+unsigned int osmo_pcapng_file_epb_size(const struct osmo_pcapng_file_epb_pars 
*pars)
+{
+       uint32_t block_total_len = sizeof(struct pcapng_block_header) +
+                                  sizeof(struct pcapng_enhanced_packet_block) +
+                                  pars->captured_len +
+                                  sizeof(uint32_t);
+       /* Packet data is padded to 4 bytes: */
+       uint8_t pad = pars->captured_len % 4;
+       if (pad)
+               block_total_len += (4 - pad);
+
+       /* TODO: other Options */
+       block_total_len += osmo_pcapng_file_opt_eofopt_size();
+       return block_total_len;
+}
+
+int osmo_pcapng_file_msgb_append_epb(struct msgb *msg, const struct 
osmo_pcapng_file_epb_pars *pars)
+{
+       struct pcapng_block_header *bh;
+       struct pcapng_enhanced_packet_block *epb;
+       uint8_t *footer_len;
+       uint32_t block_total_len = osmo_pcapng_file_epb_size(pars);
+
+       bh = (struct pcapng_block_header *)msgb_put(msg, sizeof(*bh));
+       bh->block_type = BLOCK_TYPE_EPB;
+       bh->block_total_length = block_total_len;
+
+       /* write block fixed content */
+       epb = (struct pcapng_enhanced_packet_block *)msgb_put(msg, 
sizeof(*epb));
+       epb->interface_id = pars->interface_id;
+       epb->timestamp_high = 0; /* TODO: apply... */
+       epb->timestamp_low = 0; /* TODO: apply... */
+       epb->captured_len = pars->captured_len;
+       epb->packet_len = pars->packet_len;
+
+       /* Packet Data */
+       if (pars->captured_len > 0)
+               memcpy(msgb_put(msg, pars->captured_len), pars->captured_data, 
pars->captured_len);
+
+       /* Each option is padded to 4 bytes: */
+       uint8_t pad = pars->captured_len % 4;
+       if (pad) {
+               pad = 4 - pad;
+               uint8_t *buf = (uint8_t *)msgb_put(msg, pad);
+               memset(buf, 0, pad);
+       }
+
+       /* Options (variable) */
+       osmo_pcapng_file_msgb_append_opt_eofopt(msg);
+
+       /* EPB Block Total Length */
+       footer_len = (uint8_t *)msgb_put(msg, sizeof(uint32_t));
+       memcpy(footer_len, &block_total_len, sizeof(uint32_t));
+
+       return block_total_len;
+}
diff --git a/src/osmo_client_vty.c b/src/osmo_client_vty.c
index 009c735..1824324 100644
--- a/src/osmo_client_vty.c
+++ b/src/osmo_client_vty.c
@@ -1,6 +1,7 @@
 /*
  * osmo-pcap-client code
  *
+ * (C) 2025 by sysmocom - s.f.m.c. GmbH <[email protected]>
  * (C) 2011-2016 by Holger Hans Peter Freyther <[email protected]>
  * (C) 2011 by On-Waves
  * All Rights Reserved
@@ -130,6 +131,8 @@

        vty_out(vty, "client%s", VTY_NEWLINE);

+       if (pcap_client->pcap_fmt != OSMO_PCAP_FMT_PCAP)
+               vty_out(vty, " pcap file-format pcapng%s", VTY_NEWLINE);
        llist_for_each_entry(ph, &pcap_client->handles, entry) {
                vty_out(vty, " pcap device %s%s",
                        ph->devname, VTY_NEWLINE);
@@ -150,6 +153,22 @@
        return CMD_SUCCESS;
 }

+DEFUN(cfg_client_pcap_file_format,
+      cfg_client_pcap_file_format_cmd,
+      "pcap file-format (pcap|pcapng)",
+      PCAP_STRING "The pcap file format to use\n"
+      "Libpcap Capture File Format (.pcap)\n"
+      "PCAP Next Generation Capture File Format (.pcapng)\n")
+{
+       if (strcmp(argv[0], "pcap") == 0)
+               pcap_client->pcap_fmt = OSMO_PCAP_FMT_PCAP;
+       else if (strcmp(argv[0], "pcapng") == 0)
+               pcap_client->pcap_fmt = OSMO_PCAP_FMT_PCAPNG;
+       else
+               return CMD_WARNING;
+       return CMD_SUCCESS;
+}
+
 DEFUN(cfg_client_no_device,
       cfg_client_no_device_cmd,
       "no pcap device NAME",
@@ -559,6 +578,7 @@

        install_node(&server_node, config_write_server);

+       install_element(CLIENT_NODE, &cfg_client_pcap_file_format_cmd);
        install_element(CLIENT_NODE, &cfg_client_no_device_cmd);
        install_element(CLIENT_NODE, &cfg_client_device_cmd);
        install_element(CLIENT_NODE, &cfg_client_snaplen_cmd);
diff --git a/src/osmo_server_network.c b/src/osmo_server_network.c
index 9750170..86afc90 100644
--- a/src/osmo_server_network.c
+++ b/src/osmo_server_network.c
@@ -108,7 +108,7 @@
        talloc_free(event_name);

        pcap_zmq_send(conn->server->zmq_publ,
-                       &conn->file_hdr, sizeof(conn->file_hdr),
+                       conn->file_hdr, conn->file_hdr_len,
                        ZMQ_SNDMORE);
        pcap_zmq_send(conn->server->zmq_publ,
                        &data->data[0], data->len,
@@ -285,8 +285,8 @@
                return;
        }

-       rc = write(conn->local_fd, &conn->file_hdr, sizeof(conn->file_hdr));
-       if (rc != sizeof(conn->file_hdr)) {
+       rc = write(conn->local_fd, conn->file_hdr, conn->file_hdr_len);
+       if (rc != conn->file_hdr_len) {
                LOGP(DSERVER, LOGL_ERROR, "Failed to write the header: %d\n", 
errno);
                close(conn->local_fd);
                conn->local_fd = -1;
@@ -298,6 +298,8 @@

 static int link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
 {
+#if 0
+       /* TODO: discover pcap vs pcapng based on "magic" field. Check minimum 
size. */
        struct pcap_file_header *hdr;

        hdr = (struct pcap_file_header *) &data->data[0];
@@ -308,12 +310,20 @@
                     (size_t) hdr->snaplen, (size_t) conn->server->max_snaplen);
                return -1;
        }
+#endif

        if (!conn->no_store && conn->local_fd < 0) {
-               conn->file_hdr = *hdr;
+               TALLOC_FREE(conn->file_hdr);
+               conn->file_hdr = talloc_size(conn, data->len);
+               memcpy(conn->file_hdr, data->data, data->len);
+               conn->file_hdr_len = data->len;
                restart_pcap(conn);
-       } else if (memcmp(&conn->file_hdr, hdr, sizeof(*hdr)) != 0) {
-               conn->file_hdr = *hdr;
+       } else if (conn->file_hdr_len != data->len ||
+                  memcmp(&conn->file_hdr, data->data, data->len) != 0) {
+               TALLOC_FREE(conn->file_hdr);
+               conn->file_hdr = talloc_size(conn, data->len);
+               memcpy(conn->file_hdr, data->data, data->len);
+               conn->file_hdr_len = data->len;
                restart_pcap(conn);
        }

@@ -583,9 +593,10 @@
        unsigned int min_len, max_len;
        switch ((enum OsmoPcapDataType) conn->data->type) {
        case PKT_LINK_HDR:
-               if (conn->data->len != sizeof(struct pcap_file_header)) {
+               /* TODO: discover pcap vs pcapng based on "magic" field. */
+               if (conn->data->len < sizeof(struct pcap_file_header)) {
                        LOGP(DSERVER, LOGL_ERROR,
-                            "Implausible llink_hdr length: %u != %zu\n",
+                            "Implausible llink_hdr length: %u < %zu\n",
                             conn->data->len, sizeof(struct osmo_pcap_pkthdr));
                        return false;
                }
@@ -747,7 +758,8 @@
 {
        close_connection(client);

-       memset(&client->file_hdr, 0, sizeof(client->file_hdr));
+       TALLOC_FREE(client->file_hdr);
+       client->file_hdr_len = 0;
        client->rem_wq.bfd.fd = new_fd;
        if (osmo_fd_register(&client->rem_wq.bfd) != 0) {
                LOGP(DSERVER, LOGL_ERROR, "Failed to register fd.\n");

--
To view, visit https://gerrit.osmocom.org/c/osmo-pcap/+/39265?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: osmo-pcap
Gerrit-Branch: master
Gerrit-Change-Id: I3c80518a1e53a1f77e1aca8dfa83f683f9516ad6
Gerrit-Change-Number: 39265
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <[email protected]>

Reply via email to