2011-05-31 19:19:22 +00:00
|
|
|
/*
|
|
|
|
* osmo-pcap-client code
|
|
|
|
*
|
2016-08-18 16:39:53 +00:00
|
|
|
* (C) 2011-2016 by Holger Hans Peter Freyther <holger@moiji-mobile.com>
|
2011-05-31 19:19:22 +00:00
|
|
|
* (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 <osmo-pcap/osmo_pcap_client.h>
|
|
|
|
#include <osmo-pcap/common.h>
|
|
|
|
#include <osmo-pcap/wireformat.h>
|
|
|
|
|
|
|
|
#include <osmocom/core/msgb.h>
|
2016-08-18 16:39:53 +00:00
|
|
|
#include <osmocom/core/rate_ctr.h>
|
2011-05-31 19:19:22 +00:00
|
|
|
#include <osmocom/core/select.h>
|
|
|
|
#include <osmocom/core/socket.h>
|
|
|
|
|
|
|
|
#include <arpa/inet.h>
|
2016-04-24 09:09:13 +00:00
|
|
|
#include <netinet/in.h>
|
2017-01-17 08:00:30 +00:00
|
|
|
#include <netdb.h>
|
2011-05-31 19:19:22 +00:00
|
|
|
|
2017-01-17 08:00:30 +00:00
|
|
|
#include <sys/ioctl.h>
|
2011-05-31 19:19:22 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
2017-01-17 08:00:30 +00:00
|
|
|
#include <errno.h>
|
2011-05-31 19:19:22 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2017-01-17 08:00:30 +00:00
|
|
|
|
2011-05-31 19:19:22 +00:00
|
|
|
static void _osmo_client_connect(void *_data)
|
|
|
|
{
|
2016-11-08 09:33:03 +00:00
|
|
|
osmo_client_connect((struct osmo_pcap_client_conn *) _data);
|
2011-05-31 19:19:22 +00:00
|
|
|
}
|
|
|
|
|
2016-11-08 13:32:45 +00:00
|
|
|
static void lost_connection(struct osmo_pcap_client_conn *conn)
|
2011-05-31 19:19:22 +00:00
|
|
|
{
|
2016-11-08 22:50:10 +00:00
|
|
|
osmo_client_disconnect(conn);
|
2011-05-31 19:19:22 +00:00
|
|
|
|
2016-11-08 13:32:45 +00:00
|
|
|
conn->timer.cb = _osmo_client_connect;
|
|
|
|
conn->timer.data = conn;
|
|
|
|
osmo_timer_schedule(&conn->timer, 2, 0);
|
2011-05-31 19:19:22 +00:00
|
|
|
}
|
|
|
|
|
2016-11-08 09:33:03 +00:00
|
|
|
static void write_data(struct osmo_pcap_client_conn *conn, struct msgb *msg)
|
2011-05-31 19:19:22 +00:00
|
|
|
{
|
2016-11-08 09:33:03 +00:00
|
|
|
if (osmo_wqueue_enqueue(&conn->wqueue, msg) != 0) {
|
2016-11-08 21:53:32 +00:00
|
|
|
LOGP(DCLIENT, LOGL_ERROR, "Failed to enqueue conn=%s\n", conn->name);
|
2016-11-08 09:33:03 +00:00
|
|
|
rate_ctr_inc(&conn->client->ctrg->ctr[CLIENT_CTR_QERR]);
|
2011-05-31 19:19:22 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_cb(struct osmo_fd *fd)
|
|
|
|
{
|
|
|
|
char buf[4096];
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = read(fd->fd, buf, sizeof(buf));
|
|
|
|
if (rc <= 0) {
|
2016-11-08 13:32:45 +00:00
|
|
|
struct osmo_pcap_client_conn *conn = fd->data;
|
2016-11-08 21:53:32 +00:00
|
|
|
LOGP(DCLIENT, LOGL_ERROR, "Lost connection on read conn=%s\n",
|
|
|
|
conn->name);
|
2016-11-08 13:32:45 +00:00
|
|
|
lost_connection(conn);
|
2011-05-31 19:19:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int write_cb(struct osmo_fd *fd, struct msgb *msg)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = write(fd->fd, msg->data, msg->len);
|
|
|
|
if (rc < 0) {
|
2016-11-08 09:33:03 +00:00
|
|
|
struct osmo_pcap_client_conn *conn = fd->data;
|
2016-11-08 21:53:32 +00:00
|
|
|
LOGP(DCLIENT, LOGL_ERROR, "Lost connection on write to %s %s:%d.\n",
|
|
|
|
conn->name, conn->srv_ip, conn->srv_port);
|
2016-11-08 09:33:03 +00:00
|
|
|
rate_ctr_inc(&conn->client->ctrg->ctr[CLIENT_CTR_WERR]);
|
|
|
|
lost_connection(conn);
|
2011-05-31 19:19:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-25 21:07:44 +00:00
|
|
|
static void handshake_done_cb(struct osmo_tls_session *session)
|
|
|
|
{
|
2016-11-08 13:32:45 +00:00
|
|
|
struct osmo_pcap_client_conn *conn;
|
2016-08-25 21:07:44 +00:00
|
|
|
|
2016-11-08 13:32:45 +00:00
|
|
|
conn = container_of(session, struct osmo_pcap_client_conn, tls_session);
|
2016-11-08 17:48:57 +00:00
|
|
|
osmo_wqueue_clear(&conn->wqueue);
|
2016-11-08 13:32:45 +00:00
|
|
|
osmo_client_send_link(conn);
|
2016-08-25 21:07:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tls_error_cb(struct osmo_tls_session *session)
|
|
|
|
{
|
2016-11-08 13:32:45 +00:00
|
|
|
struct osmo_pcap_client_conn *conn;
|
2016-08-25 21:07:44 +00:00
|
|
|
|
2016-11-08 13:32:45 +00:00
|
|
|
conn = container_of(session, struct osmo_pcap_client_conn, tls_session);
|
|
|
|
lost_connection(conn);
|
2016-08-25 21:07:44 +00:00
|
|
|
}
|
|
|
|
|
2016-11-08 17:48:57 +00:00
|
|
|
int conn_cb(struct osmo_fd *fd, unsigned int what)
|
|
|
|
{
|
|
|
|
/* finally the socket is connected... continue */
|
|
|
|
if (what & BSC_FD_WRITE) {
|
|
|
|
struct osmo_pcap_client_conn *conn = fd->data;
|
|
|
|
/*
|
|
|
|
* The write queue needs to work differently for GNUtls. Before we can
|
|
|
|
* send data we will need to complete handshake.
|
|
|
|
*/
|
|
|
|
if (conn->tls_on) {
|
|
|
|
if (!osmo_tls_init_client_session(conn)) {
|
|
|
|
lost_connection(conn);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
conn->tls_session.handshake_done = handshake_done_cb;
|
|
|
|
conn->tls_session.error = tls_error_cb;
|
|
|
|
|
|
|
|
/* fd->data now points somewhere else, stop */
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
conn->wqueue.bfd.cb = osmo_wqueue_bfd_cb;
|
|
|
|
conn->wqueue.bfd.data = conn;
|
|
|
|
osmo_wqueue_clear(&conn->wqueue);
|
|
|
|
osmo_client_send_link(conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (what & BSC_FD_READ)
|
|
|
|
read_cb(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-21 16:12:01 +00:00
|
|
|
static int get_iphdr_offset(int dlt)
|
|
|
|
{
|
|
|
|
switch (dlt) {
|
|
|
|
case DLT_EN10MB:
|
|
|
|
return 14;
|
|
|
|
case DLT_LINUX_SLL:
|
|
|
|
return 16;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-08 09:33:03 +00:00
|
|
|
void osmo_client_send_data(struct osmo_pcap_client_conn *conn,
|
2011-05-31 19:19:22 +00:00
|
|
|
struct pcap_pkthdr *in_hdr, const uint8_t *data)
|
|
|
|
{
|
|
|
|
struct osmo_pcap_data *om_hdr;
|
2015-12-03 21:13:38 +00:00
|
|
|
struct osmo_pcap_pkthdr *hdr;
|
2011-05-31 19:19:22 +00:00
|
|
|
struct msgb *msg;
|
2017-07-21 16:12:01 +00:00
|
|
|
int offset, ip_len;
|
2011-05-31 19:19:22 +00:00
|
|
|
|
2015-12-03 19:16:37 +00:00
|
|
|
if (in_hdr->caplen > 9000) {
|
|
|
|
LOGP(DCLIENT, LOGL_ERROR,
|
|
|
|
"Capture len too big %zu\n", in_hdr->caplen);
|
2016-11-08 09:33:03 +00:00
|
|
|
rate_ctr_inc(&conn->client->ctrg->ctr[CLIENT_CTR_2BIG]);
|
2015-12-03 19:16:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = msgb_alloc(9000 + sizeof(*om_hdr) + sizeof(*hdr), "data-data");
|
2011-05-31 19:19:22 +00:00
|
|
|
if (!msg) {
|
|
|
|
LOGP(DCLIENT, LOGL_ERROR, "Failed to allocate.\n");
|
2016-11-08 09:33:03 +00:00
|
|
|
rate_ctr_inc(&conn->client->ctrg->ctr[CLIENT_CTR_NOMEM]);
|
2011-05-31 19:19:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-21 16:12:01 +00:00
|
|
|
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_add(&conn->client->ctrg->ctr[CLIENT_CTR_BYTES], hdr->caplen);
|
|
|
|
rate_ctr_inc(&conn->client->ctrg->ctr[CLIENT_CTR_PKTS]);
|
|
|
|
break;
|
|
|
|
case PROTOCOL_IPIP:
|
|
|
|
offset = get_iphdr_offset(pcap_datalink(conn->client->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);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
2011-05-31 19:19:22 +00:00
|
|
|
|
2016-11-08 09:33:03 +00:00
|
|
|
write_data(conn, msg);
|
2011-05-31 19:19:22 +00:00
|
|
|
}
|
|
|
|
|
2016-11-08 09:33:03 +00:00
|
|
|
void osmo_client_send_link(struct osmo_pcap_client_conn *conn)
|
2011-05-31 19:19:22 +00:00
|
|
|
{
|
|
|
|
struct pcap_file_header *hdr;
|
|
|
|
struct osmo_pcap_data *om_hdr;
|
2015-12-03 19:16:37 +00:00
|
|
|
struct msgb *msg;
|
2011-05-31 19:19:22 +00:00
|
|
|
|
2017-07-21 16:12:01 +00:00
|
|
|
/* IPIP encapsulation has no linktype header */
|
|
|
|
if (conn->protocol == PROTOCOL_IPIP)
|
|
|
|
return;
|
|
|
|
|
2016-11-09 00:45:53 +00:00
|
|
|
if (!conn->client->handle) {
|
|
|
|
LOGP(DCLIENT, LOGL_ERROR,
|
|
|
|
"No pcap_handle not sending link info to conn=%s\n", conn->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-12-03 19:16:37 +00:00
|
|
|
msg = msgb_alloc(9000 + sizeof(*om_hdr) + sizeof(*hdr), "link-data");
|
2011-05-31 19:19:22 +00:00
|
|
|
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;
|
2011-06-01 15:29:15 +00:00
|
|
|
om_hdr->len = htons(sizeof(*hdr));
|
2011-05-31 19:19:22 +00:00
|
|
|
|
|
|
|
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 = UINT_MAX;
|
2016-11-08 09:33:03 +00:00
|
|
|
hdr->linktype = pcap_datalink(conn->client->handle);
|
2011-05-31 19:19:22 +00:00
|
|
|
|
2016-11-08 09:33:03 +00:00
|
|
|
write_data(conn, msg);
|
2011-05-31 19:19:22 +00:00
|
|
|
}
|
|
|
|
|
2016-11-08 09:33:03 +00:00
|
|
|
void osmo_client_connect(struct osmo_pcap_client_conn *conn)
|
2011-05-31 19:19:22 +00:00
|
|
|
{
|
2017-07-21 14:57:12 +00:00
|
|
|
int rc;
|
2017-07-21 16:12:01 +00:00
|
|
|
uint16_t srv_port;
|
|
|
|
int sock_type, sock_proto;
|
2011-05-31 19:19:22 +00:00
|
|
|
|
2016-11-08 23:13:21 +00:00
|
|
|
osmo_client_disconnect(conn);
|
|
|
|
|
2016-11-08 09:33:03 +00:00
|
|
|
conn->wqueue.read_cb = read_cb;
|
|
|
|
conn->wqueue.write_cb = write_cb;
|
|
|
|
osmo_wqueue_clear(&conn->wqueue);
|
2011-05-31 19:19:22 +00:00
|
|
|
|
2017-07-21 16:12:01 +00:00
|
|
|
switch (conn->protocol) {
|
|
|
|
case PROTOCOL_OSMOPCAP:
|
|
|
|
srv_port = conn->srv_port;
|
|
|
|
sock_type = SOCK_STREAM;
|
|
|
|
sock_proto = IPPROTO_TCP;
|
|
|
|
break;
|
|
|
|
case PROTOCOL_IPIP:
|
|
|
|
srv_port = 0;
|
|
|
|
sock_type = SOCK_RAW;
|
|
|
|
sock_proto = IPPROTO_IPIP;
|
|
|
|
conn->wqueue.bfd.when = BSC_FD_WRITE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = osmo_sock_init2_ofd(&conn->wqueue.bfd, AF_INET, sock_type, sock_proto,
|
|
|
|
conn->source_ip, 0, conn->srv_ip, srv_port,
|
2017-07-21 14:55:19 +00:00
|
|
|
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT | OSMO_SOCK_F_NONBLOCK);
|
2017-07-21 14:57:12 +00:00
|
|
|
if (rc < 0) {
|
2011-05-31 19:19:22 +00:00
|
|
|
LOGP(DCLIENT, LOGL_ERROR,
|
2016-11-08 21:53:32 +00:00
|
|
|
"Failed to connect conn=%s to %s:%d\n",
|
|
|
|
conn->name, conn->srv_ip, conn->srv_port);
|
2016-11-08 09:33:03 +00:00
|
|
|
lost_connection(conn);
|
2011-05-31 19:19:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-08 09:33:03 +00:00
|
|
|
rate_ctr_inc(&conn->client->ctrg->ctr[CLIENT_CTR_CONNECT]);
|
2016-11-08 17:48:57 +00:00
|
|
|
conn->wqueue.bfd.cb = conn_cb;
|
|
|
|
conn->wqueue.bfd.data = conn;
|
2017-07-21 16:12:01 +00:00
|
|
|
if (conn->protocol == PROTOCOL_IPIP)
|
|
|
|
conn->wqueue.bfd.when = BSC_FD_WRITE;
|
|
|
|
else
|
|
|
|
conn->wqueue.bfd.when = BSC_FD_READ | BSC_FD_WRITE;
|
2011-05-31 19:19:22 +00:00
|
|
|
}
|
2016-08-25 17:55:36 +00:00
|
|
|
|
2016-11-08 13:32:45 +00:00
|
|
|
void osmo_client_reconnect(struct osmo_pcap_client_conn *conn)
|
2016-08-25 17:55:36 +00:00
|
|
|
{
|
2016-11-08 13:32:45 +00:00
|
|
|
lost_connection(conn);
|
2016-08-25 17:55:36 +00:00
|
|
|
}
|
2016-11-08 22:50:10 +00:00
|
|
|
|
|
|
|
void osmo_client_disconnect(struct osmo_pcap_client_conn *conn)
|
|
|
|
{
|
|
|
|
if (conn->wqueue.bfd.fd >= 0) {
|
|
|
|
osmo_tls_release(&conn->tls_session);
|
|
|
|
osmo_fd_unregister(&conn->wqueue.bfd);
|
|
|
|
close(conn->wqueue.bfd.fd);
|
|
|
|
conn->wqueue.bfd.fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
osmo_timer_del(&conn->timer);
|
|
|
|
}
|