aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmo-pcap/osmo_pcap_client.h1
-rw-r--r--src/osmo_client_network.c138
-rw-r--r--src/osmo_client_vty.c18
3 files changed, 156 insertions, 1 deletions
diff --git a/include/osmo-pcap/osmo_pcap_client.h b/include/osmo-pcap/osmo_pcap_client.h
index 68a648d..b4dda78 100644
--- a/include/osmo-pcap/osmo_pcap_client.h
+++ b/include/osmo-pcap/osmo_pcap_client.h
@@ -52,6 +52,7 @@ struct osmo_pcap_client_conn {
char *srv_ip;
int srv_port;
+ char *source_ip;
struct osmo_wqueue wqueue;
struct osmo_timer_list timer;
diff --git a/src/osmo_client_network.c b/src/osmo_client_network.c
index 2667809..937caa0 100644
--- a/src/osmo_client_network.c
+++ b/src/osmo_client_network.c
@@ -31,14 +31,149 @@
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <errno.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
+
+/*
+ * Move to libosmocore... if the api makes source
+ */
+static int sock_src_init(uint16_t family, uint16_t type, uint8_t proto,
+ const char *src, uint16_t src_port,
+ const char *host, uint16_t port, unsigned int flags)
+{
+ struct addrinfo hints, *result, *rp;
+ struct addrinfo *src_result, *src_rp = NULL;
+ int sfd, rc, on = 1;
+ char portbuf[16];
+ char src_portbuf[16];
+
+ sprintf(portbuf, "%u", port);
+ sprintf(src_portbuf, "%u", src_port);
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = family;
+ if (type == SOCK_RAW) {
+ /* Workaround for glibc, that returns EAI_SERVICE (-8) if
+ * SOCK_RAW and IPPROTO_GRE is used.
+ */
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ } else {
+ hints.ai_socktype = type;
+ hints.ai_protocol = proto;
+ }
+
+ rc = getaddrinfo(host, portbuf, &hints, &result);
+ if (rc != 0) {
+ fprintf(stderr, "getaddrinfo returned NULL: %s:%u: %s\n",
+ host, port, strerror(errno));
+ return -EINVAL;
+ }
+
+ if (src) {
+ rc = getaddrinfo(src, src_portbuf, &hints, &src_result);
+ if (rc != 0) {
+ fprintf(stderr, "getaddrinfo returned NULL: %s:%u: %s\n",
+ src, src_port, strerror(errno));
+ freeaddrinfo(result);
+ return -EINVAL;
+ }
+
+ /* select an address */
+ for (src_rp = src_result; src_rp != NULL; src_rp = src_rp->ai_next) {
+ /* Workaround for glibc again */
+ if (type == SOCK_RAW) {
+ src_rp->ai_socktype = SOCK_RAW;
+ src_rp->ai_protocol = proto;
+ }
+ break;
+ }
+
+ if (!src_rp) {
+ fprintf(stderr, "Failed to get src: %s:%u %s\n",
+ src, src_port, strerror(errno));
+ freeaddrinfo(result);
+ freeaddrinfo(src_result);
+ return -EINVAL;
+ }
+ }
+
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ /* Workaround for glibc again */
+ if (type == SOCK_RAW) {
+ rp->ai_socktype = SOCK_RAW;
+ rp->ai_protocol = proto;
+ }
+
+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (sfd == -1)
+ continue;
+ if (flags & OSMO_SOCK_F_NONBLOCK) {
+ if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
+ fprintf(stderr,
+ "cannot set this socket unblocking:"
+ " %s:%u: %s\n",
+ host, port, strerror(errno));
+ close(sfd);
+ freeaddrinfo(result);
+ return -EINVAL;
+ }
+ }
+
+
+ if (src_rp) {
+ rc = bind(sfd, src_rp->ai_addr, src_rp->ai_addrlen);
+ if (rc != 0) {
+ fprintf(stderr,
+ "cannot bind socket:"
+ " %s:%u: %s\n",
+ src, src_port, strerror(errno));
+ close(sfd);
+ continue;
+ }
+ }
+
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
+ if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
+ break;
+ } else {
+ rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on));
+ if (rc < 0) {
+ fprintf(stderr,
+ "cannot setsockopt socket:"
+ " %s:%u: %s\n",
+ host, port, strerror(errno));
+ break;
+ }
+ if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
+ break;
+ }
+ close(sfd);
+ }
+ freeaddrinfo(result);
+ freeaddrinfo(src_result);
+
+ if (rp == NULL) {
+ fprintf(stderr, "unable to connect/bind socket: %s:%u: %s\n",
+ host, port, strerror(errno));
+ return -ENODEV;
+ }
+
+ setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ return sfd;
+}
+
static void _osmo_client_connect(void *_data)
{
osmo_client_connect((struct osmo_pcap_client_conn *) _data);
@@ -233,7 +368,8 @@ void osmo_client_connect(struct osmo_pcap_client_conn *conn)
conn->wqueue.bfd.when = BSC_FD_READ;
osmo_wqueue_clear(&conn->wqueue);
- fd = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+ fd = sock_src_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+ conn->source_ip, 0,
conn->srv_ip, conn->srv_port,
OSMO_SOCK_F_CONNECT | OSMO_SOCK_F_NONBLOCK);
if (fd < 0) {
diff --git a/src/osmo_client_vty.c b/src/osmo_client_vty.c
index 5cf3aa1..4cd2908 100644
--- a/src/osmo_client_vty.c
+++ b/src/osmo_client_vty.c
@@ -91,6 +91,9 @@ static void write_client_conn_data(
if (conn->srv_port > 0)
vty_out(vty, "%s server port %d%s", indent,
conn->srv_port, VTY_NEWLINE);
+ if (conn->source_ip)
+ vty_out(vty, "%s source ip %s%s", indent,
+ conn->source_ip, VTY_NEWLINE);
}
static int config_write_server(struct vty *vty)
@@ -393,6 +396,19 @@ DEFUN(cfg_server_port,
return CMD_SUCCESS;
}
+DEFUN(cfg_source_ip,
+ cfg_source_ip_cmd,
+ "source ip A.B.C.D",
+ SERVER_STRING "Source IP Address\n" "IP\n")
+{
+ struct osmo_pcap_client_conn *conn = get_conn(vty);
+
+ talloc_free(conn->source_ip);
+ conn->source_ip = talloc_strdup(pcap_client, argv[0]);
+ return CMD_SUCCESS;
+}
+
+
DEFUN(cfg_pcap_store,
cfg_pcap_store_cmd,
"pcap-store-connection .NAME",
@@ -465,6 +481,7 @@ int vty_client_init(struct osmo_pcap_client *pcap)
install_element(CLIENT_NODE, &cfg_server_ip_cmd);
install_element(CLIENT_NODE, &cfg_server_port_cmd);
+ install_element(CLIENT_NODE, &cfg_source_ip_cmd);
install_element(CLIENT_NODE, &cfg_enable_tls_cmd);
install_element(CLIENT_NODE, &cfg_disable_tls_cmd);
@@ -491,6 +508,7 @@ int vty_client_init(struct osmo_pcap_client *pcap)
install_element(CLIENT_NODE, &cfg_no_pcap_store_cmd);
install_element(CLIENT_SERVER_NODE, &cfg_server_ip_cmd);
install_element(CLIENT_SERVER_NODE, &cfg_server_port_cmd);
+ install_element(CLIENT_SERVER_NODE, &cfg_source_ip_cmd);
install_element(CLIENT_SERVER_NODE, &cfg_enable_tls_cmd);
install_element(CLIENT_SERVER_NODE, &cfg_disable_tls_cmd);
install_element(CLIENT_SERVER_NODE, &cfg_tls_hostname_cmd);