pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-pcap/+/39288?usp=email )
Change subject: client: Introduce osmo_pcap_file to prepare support for pcapng ......................................................................
client: Introduce osmo_pcap_file to prepare support for pcapng
This patch is a preparation patch refactoring code to make it easier to add pcapng format in follow-up patch.
Different types of encodings are grouped in helper functions.
Change-Id: Ifd389a2a32897e101c62fd280ddca984d485d373 --- M include/osmo-pcap/Makefile.am M include/osmo-pcap/osmo_pcap_client.h A include/osmo-pcap/osmo_pcap_file.h M src/Makefile.am M src/osmo_client_core.c M src/osmo_client_network.c A src/osmo_pcap_file.c 7 files changed, 240 insertions(+), 78 deletions(-)
Approvals: laforge: Looks good to me, but someone else must approve pespin: Looks good to me, approved Jenkins Builder: Verified osmith: Looks good to me, but someone else must approve
diff --git a/include/osmo-pcap/Makefile.am b/include/osmo-pcap/Makefile.am index e377b6a..58c8a59 100644 --- a/include/osmo-pcap/Makefile.am +++ b/include/osmo-pcap/Makefile.am @@ -1,6 +1,7 @@ noinst_HEADERS = \ common.h \ osmo_pcap_client.h \ + osmo_pcap_file.h \ osmo_pcap_server.h \ osmo_tls.h \ wireformat.h \ diff --git a/include/osmo-pcap/osmo_pcap_client.h b/include/osmo-pcap/osmo_pcap_client.h index 442244e..470edb6 100644 --- a/include/osmo-pcap/osmo_pcap_client.h +++ b/include/osmo-pcap/osmo_pcap_client.h @@ -139,7 +139,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); diff --git a/include/osmo-pcap/osmo_pcap_file.h b/include/osmo-pcap/osmo_pcap_file.h new file mode 100644 index 0000000..50a7706 --- /dev/null +++ b/include/osmo-pcap/osmo_pcap_file.h @@ -0,0 +1,37 @@ +/* + * Procedures to operate on pcap/pcapng file format + * + * (C) 2025 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * 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/. + * + */ + +#pragma once + +#include <unistd.h> +#include <stdint.h> + +/*********************************************************** +* Libpcap File Format (.pcap) +* https://wiki.wireshark.org/Development/LibpcapFileFormat +***********************************************************/ + +#define OSMO_PCAP_FILE_MAGIC 0xa1b2c3d4 + +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); diff --git a/src/Makefile.am b/src/Makefile.am index 00c9ce9..bb70d21 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,7 @@ osmo_client_network.c \ osmo_client_stats.c \ osmo_client_vty.c \ + osmo_pcap_file.c \ osmo_tls.c \ $(NULL)
diff --git a/src/osmo_client_core.c b/src/osmo_client_core.c index a2ab894..538b68a 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; }
diff --git a/src/osmo_client_network.c b/src/osmo_client_network.c index 70fe740..e14f4ca 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 info@sysmocom.de * (C) 2011-2016 by Holger Hans Peter Freyther holger@moiji-mobile.com * (C) 2011 by On-Waves * All Rights Reserved @@ -23,7 +24,9 @@ #include <osmo-pcap/osmo_pcap_client.h> #include <osmo-pcap/common.h> #include <osmo-pcap/wireformat.h> +#include <osmo-pcap/osmo_pcap_file.h>
+#include <osmocom/core/utils.h> #include <osmocom/core/msgb.h> #include <osmocom/core/rate_ctr.h> #include <osmocom/core/select.h> @@ -160,112 +163,152 @@ } }
-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_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; - - 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); + msg = osmo_client_conn_prepare_msg_data_pcap(conn, ph, pkthdr, data); 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_client *client = conn->client; + struct osmo_pcap_data *om_hdr; + struct msgb *msg; + struct osmo_pcap_handle *ph; + int rc; + int linktype; + + ph = llist_first_entry_or_null(&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, &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!\n"); + return NULL; + } + } + + msg = msgb_alloc(sizeof(*om_hdr) + sizeof(struct pcap_file_header), "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, client->snaplen, linktype); + 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; +} + 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"); + msg = osmo_client_conn_prepare_msg_link_pcap(conn); + if (!msg) return; - } - - msg = msgb_alloc(sizeof(*om_hdr) + sizeof(*hdr), "link-data"); - if (!msg) { - LOGP(DCLIENT, LOGL_ERROR, "Failed to allocate data.\n"); - 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_pcap_file.c b/src/osmo_pcap_file.c new file mode 100644 index 0000000..eb8cc23 --- /dev/null +++ b/src/osmo_pcap_file.c @@ -0,0 +1,78 @@ +/* + * osmo-pcap-client code + * + * (C) 2025 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * (C) 2011-2016 by Holger Hans Peter Freyther holger@moiji-mobile.com + * (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_file.h> +#include <osmo-pcap/common.h> +#include <osmo-pcap/wireformat.h> + +/*********************************************************** + * Libpcap File Format (.pcap) + * https://wiki.wireshark.org/Development/LibpcapFileFormat + ***********************************************************/ + +/* 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 = OSMO_PCAP_FILE_MAGIC; + 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); +}