mirror of https://gerrit.osmocom.org/osmo-pcap
server: Add zmq based event and data interface to the server
To allow easily extracting or streaming the data to an external analysis system, zeromq can be configured (and reconfigured). The system works as fire and forget and no loss detection is present. A simple go based client application is provided to subscribe to the publisher. Change-Id: I4f3e6d675023a81b7d2ee19bf1f44a2be0ca003c
This commit is contained in:
parent
ad29ce6f06
commit
e024869a72
|
@ -47,7 +47,7 @@ dnl checks for libraries
|
||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.2)
|
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.2)
|
||||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.2)
|
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.2)
|
||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
|
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
|
||||||
PKG_CHECK_MODULES(LIBZMQ, libzmq >= 4.0.0)
|
PKG_CHECK_MODULES(LIBZMQ, libzmq >= 3.2.2)
|
||||||
|
|
||||||
|
|
||||||
# Coverage build taken from WebKit's configure.in
|
# Coverage build taken from WebKit's configure.in
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt";
|
||||||
|
"strings";
|
||||||
|
zmq "github.com/pebbe/zmq4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
subscriber, _ := zmq.NewSocket(zmq.SUB)
|
||||||
|
defer subscriber.Close()
|
||||||
|
subscriber.Connect("tcp://localhost:6666")
|
||||||
|
|
||||||
|
subscriber.SetSubscribe("")
|
||||||
|
|
||||||
|
for {
|
||||||
|
msg, _ := subscriber.RecvMessage(0)
|
||||||
|
if (strings.HasPrefix(msg[0], "event.v1")) {
|
||||||
|
fmt.Println("Got event message.. %d", len(msg), msg)
|
||||||
|
} else if (strings.HasPrefix(msg[0], "data.v1")) {
|
||||||
|
fmt.Println("Got data message.. %d", len(msg), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
ZeroMQ data protocol v1
|
||||||
|
=======================
|
||||||
|
|
||||||
|
The osmo-pcap-server can be configured to publish PCAP data to
|
||||||
|
zero to many subscribers. The following document describes the
|
||||||
|
data format used.
|
||||||
|
|
||||||
|
Multiple clients might be connected to the osmo-pcap-server and
|
||||||
|
use different link headers depending on the underlying device
|
||||||
|
data is being captured from.
|
||||||
|
|
||||||
|
The messages published are in two categories. These are client
|
||||||
|
events and client data. Client events are generated on connect,
|
||||||
|
disconnect, link type change and client data is sent for each
|
||||||
|
frame.
|
||||||
|
|
||||||
|
Client Events
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A multi-part message with event.v1.<EVENT_NAME>.<CLIENT_NAME>
|
||||||
|
as the first part followed by textual data will be generated.
|
||||||
|
<CLIENT_NAME> is the configured name and <EVENT_NAME> can be
|
||||||
|
any of:
|
||||||
|
|
||||||
|
* connect
|
||||||
|
* disconnect
|
||||||
|
* closingtracefile
|
||||||
|
|
||||||
|
It might contain more information, such as the filename of the
|
||||||
|
tracefile that was closed. There is no guarantee for the order
|
||||||
|
and amount of connect/disconnect messages.
|
||||||
|
|
||||||
|
|
||||||
|
Client Data
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
A multi-part message with data.v1.<CLIENT_NAME> to allow to
|
||||||
|
filter for data and a specific client if wanted.
|
||||||
|
|
||||||
|
It is followed by the pcap_file_header structure as the second
|
||||||
|
part and then the data as third part.
|
|
@ -58,6 +58,7 @@ struct osmo_pcap_conn {
|
||||||
/* Remote connection */
|
/* Remote connection */
|
||||||
struct osmo_fd rem_fd;
|
struct osmo_fd rem_fd;
|
||||||
int local_fd;
|
int local_fd;
|
||||||
|
char *curr_filename;
|
||||||
|
|
||||||
/* pcap stuff */
|
/* pcap stuff */
|
||||||
struct pcap_file_header file_hdr;
|
struct pcap_file_header file_hdr;
|
||||||
|
@ -98,5 +99,6 @@ struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *ser,
|
||||||
const char *name);
|
const char *name);
|
||||||
void osmo_pcap_server_delete(struct osmo_pcap_conn *conn);
|
void osmo_pcap_server_delete(struct osmo_pcap_conn *conn);
|
||||||
void vty_server_init(struct osmo_pcap_server *server);
|
void vty_server_init(struct osmo_pcap_server *server);
|
||||||
|
void osmo_pcap_server_close_trace(struct osmo_pcap_conn *conn);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,11 +31,99 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <zmq.h>
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static void pcap_zmq_send(void *publ, const void *data, size_t len, int flags)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
zmq_msg_t msg;
|
||||||
|
|
||||||
|
rc = zmq_msg_init_size(&msg, len);
|
||||||
|
if (rc != 0) {
|
||||||
|
/* sigh.. we said SNDMORE but can't... */
|
||||||
|
LOGP(DSERVER, LOGL_ERROR, "Failed to init rc=%d errno=%d/%s\n",
|
||||||
|
rc, errno, strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(zmq_msg_data(&msg), data, len);
|
||||||
|
rc = zmq_msg_send(&msg, publ, flags);
|
||||||
|
if (rc == -1) {
|
||||||
|
/* is the zmq_msg now owned? leak??? */
|
||||||
|
LOGP(DSERVER, LOGL_ERROR, "Failed to send data rc=%d errno=%d/%s\n",
|
||||||
|
rc, errno, strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void client_event(struct osmo_pcap_conn *conn,
|
||||||
|
const char *event, const char *data)
|
||||||
|
{
|
||||||
|
char *event_name;
|
||||||
|
|
||||||
|
if (!conn->server->zmq_publ)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This multi-part support is insane... so if we lose the first
|
||||||
|
* or the last part of the multipart message stuff is going out
|
||||||
|
* of sync. *great* As we can't do anything about it right now
|
||||||
|
* just close the eyese and send it.
|
||||||
|
*/
|
||||||
|
event_name = talloc_asprintf(conn, "event.v1.%s.%s",
|
||||||
|
event, conn->name);
|
||||||
|
pcap_zmq_send(conn->server->zmq_publ,
|
||||||
|
event_name, strlen(event_name),
|
||||||
|
data ? ZMQ_SNDMORE : 0);
|
||||||
|
talloc_free(event_name);
|
||||||
|
if (data)
|
||||||
|
pcap_zmq_send(conn->server->zmq_publ, data, strlen(data), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void client_data(struct osmo_pcap_conn *conn,
|
||||||
|
struct osmo_pcap_data *data)
|
||||||
|
{
|
||||||
|
char *event_name;
|
||||||
|
|
||||||
|
if (!conn->server->zmq_publ)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This multi-part support is insane... so if we lose the first
|
||||||
|
* or the last part of the multipart message stuff is going out
|
||||||
|
* of sync. *great* As we can't do anything about it right now
|
||||||
|
* just close the eyese and send it.
|
||||||
|
*/
|
||||||
|
event_name = talloc_asprintf(conn, "data.v1.%s", conn->name);
|
||||||
|
pcap_zmq_send(conn->server->zmq_publ, event_name, strlen(event_name), ZMQ_SNDMORE);
|
||||||
|
talloc_free(event_name);
|
||||||
|
|
||||||
|
pcap_zmq_send(conn->server->zmq_publ,
|
||||||
|
&conn->file_hdr, sizeof(conn->file_hdr),
|
||||||
|
ZMQ_SNDMORE);
|
||||||
|
pcap_zmq_send(conn->server->zmq_publ,
|
||||||
|
&data->data[0], data->len,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void osmo_pcap_server_close_trace(struct osmo_pcap_conn *conn)
|
||||||
|
{
|
||||||
|
if (conn->local_fd >= 0) {
|
||||||
|
close(conn->local_fd);
|
||||||
|
conn->local_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->curr_filename) {
|
||||||
|
client_event(conn, "closingtracefile", conn->curr_filename);
|
||||||
|
talloc_free(conn->curr_filename);
|
||||||
|
conn->curr_filename = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void close_connection(struct osmo_pcap_conn *conn)
|
static void close_connection(struct osmo_pcap_conn *conn)
|
||||||
{
|
{
|
||||||
if (conn->rem_fd.fd >= 0) {
|
if (conn->rem_fd.fd >= 0) {
|
||||||
|
@ -44,44 +132,39 @@ static void close_connection(struct osmo_pcap_conn *conn)
|
||||||
osmo_fd_unregister(&conn->rem_fd);
|
osmo_fd_unregister(&conn->rem_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->local_fd >= 0) {
|
osmo_pcap_server_close_trace(conn);
|
||||||
close(conn->local_fd);
|
client_event(conn, "disconnect", NULL);
|
||||||
conn->local_fd = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void restart_pcap(struct osmo_pcap_conn *conn)
|
static void restart_pcap(struct osmo_pcap_conn *conn)
|
||||||
{
|
{
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
struct tm *tm = localtime(&now);
|
struct tm *tm = localtime(&now);
|
||||||
char *filename;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (conn->local_fd >= 0) {
|
osmo_pcap_server_close_trace(conn);
|
||||||
close(conn->local_fd);
|
|
||||||
conn->local_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* omit any storing/creation of the file */
|
/* omit any storing/creation of the file */
|
||||||
if (conn->no_store) {
|
if (conn->no_store) {
|
||||||
conn->last_write = *tm;
|
conn->last_write = *tm;
|
||||||
|
talloc_free(conn->curr_filename);
|
||||||
|
conn->curr_filename = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
filename = talloc_asprintf(conn, "%s/trace-%s-%d%.2d%.2d_%.2d%.2d%.2d.pcap",
|
conn->curr_filename = talloc_asprintf(conn, "%s/trace-%s-%d%.2d%.2d_%.2d%.2d%.2d.pcap",
|
||||||
conn->server->base_path, conn->name,
|
conn->server->base_path, conn->name,
|
||||||
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
||||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
|
|
||||||
if (!filename) {
|
if (!conn->curr_filename) {
|
||||||
LOGP(DSERVER, LOGL_ERROR, "Failed to assemble filename for %s.\n", conn->name);
|
LOGP(DSERVER, LOGL_ERROR, "Failed to assemble filename for %s.\n", conn->name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->local_fd = creat(filename, 0440);
|
conn->local_fd = creat(conn->curr_filename, 0440);
|
||||||
if (conn->local_fd < 0) {
|
if (conn->local_fd < 0) {
|
||||||
LOGP(DSERVER, LOGL_ERROR, "Failed to file: '%s'\n", filename);
|
LOGP(DSERVER, LOGL_ERROR, "Failed to file: '%s'\n", conn->curr_filename);
|
||||||
talloc_free(filename);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,12 +173,10 @@ static void restart_pcap(struct osmo_pcap_conn *conn)
|
||||||
LOGP(DSERVER, LOGL_ERROR, "Failed to write the header: %d\n", errno);
|
LOGP(DSERVER, LOGL_ERROR, "Failed to write the header: %d\n", errno);
|
||||||
close(conn->local_fd);
|
close(conn->local_fd);
|
||||||
conn->local_fd = -1;
|
conn->local_fd = -1;
|
||||||
talloc_free(filename);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->last_write = *tm;
|
conn->last_write = *tm;
|
||||||
talloc_free(filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
|
static void link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
|
||||||
|
@ -127,6 +208,8 @@ static void write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
|
||||||
struct tm *tm = localtime(&now);
|
struct tm *tm = localtime(&now);
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
client_data(conn, data);
|
||||||
|
|
||||||
if (conn->no_store) {
|
if (conn->no_store) {
|
||||||
conn->last_write = *tm;
|
conn->last_write = *tm;
|
||||||
return;
|
return;
|
||||||
|
@ -317,6 +400,7 @@ static int accept_cb(struct osmo_fd *fd, unsigned int when)
|
||||||
if (conn->remote_addr.s_addr == addr.sin_addr.s_addr) {
|
if (conn->remote_addr.s_addr == addr.sin_addr.s_addr) {
|
||||||
LOGP(DSERVER, LOGL_NOTICE,
|
LOGP(DSERVER, LOGL_NOTICE,
|
||||||
"New connection from %s\n", conn->name);
|
"New connection from %s\n", conn->name);
|
||||||
|
client_event(conn, "connect", NULL);
|
||||||
new_connection(server, conn, new_fd);
|
new_connection(server, conn, new_fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,10 +134,7 @@ DEFUN(cfg_server_client,
|
||||||
|
|
||||||
/* Checking no-store and maybe closing a pcap file */
|
/* Checking no-store and maybe closing a pcap file */
|
||||||
if (argc >= 3) {
|
if (argc >= 3) {
|
||||||
if (conn->local_fd >= 0) {
|
osmo_pcap_server_close_trace(conn);
|
||||||
close(conn->local_fd);
|
|
||||||
conn->local_fd = -1;
|
|
||||||
}
|
|
||||||
conn->no_store = 1;
|
conn->no_store = 1;
|
||||||
} else
|
} else
|
||||||
conn->no_store = 0;
|
conn->no_store = 0;
|
||||||
|
|
Loading…
Reference in New Issue