From 40c1e85499ec152a26bfd07433765730e3782104 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Tue, 17 Jan 2017 09:00:30 +0100 Subject: client: Allow to bind to a specific source_ip Modify the osmo_sock_init (code clone to be integrated upstream) to allow binding to a specific source ip and source port. Allow the source ip to be configured but allow the kernel to pick a random port for us. This is necessary for systems with multiple interfaces where the default route is not necessarily the right one to connect to the pcap server. Change-Id: I84e728b0752213d28f970fcbbfd6565c441ccfeb --- src/osmo_client_network.c | 138 +++++++++++++++++++++++++++++++++++++++++++++- src/osmo_client_vty.c | 18 ++++++ 2 files changed, 155 insertions(+), 1 deletion(-) (limited to 'src') 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 #include +#include +#include #include #include +#include #include #include #include + +/* + * 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); -- cgit v1.2.3