diff --git a/sniproxy.conf b/sniproxy.conf index a45b22f4..f9100b35 100644 --- a/sniproxy.conf +++ b/sniproxy.conf @@ -74,6 +74,12 @@ listen 80 { # reduce the maximum number of simultaneous connections possible. source 192.0.2.10 + # Bind device for outgoing connections + # + # This is not available for all platforms. + # Root is required for SO_BINDTODEVICE + #device eth1 + # Log the content of bad requests #bad_requests log diff --git a/src/config.c b/src/config.c index fd27d56f..39e6a95a 100644 --- a/src/config.c +++ b/src/config.c @@ -132,6 +132,14 @@ struct Keyword listener_stanza_grammar[] = { (int(*)(void *, char *))accept_listener_source_address, NULL, NULL}, + +#ifdef SO_BINDTODEVICE + { "device", + NULL, + (int(*)(void *, char *))accept_listener_device_name, + NULL, + NULL}, +#endif { "access_log", (void *(*)())new_logger_builder, (int(*)(void *, char *))accept_logger_filename, diff --git a/src/connection.c b/src/connection.c index 56c8da35..48ca385f 100644 --- a/src/connection.c +++ b/src/connection.c @@ -32,6 +32,7 @@ #include #include #include +#include #include /* getaddrinfo */ #include /* close */ #include @@ -221,6 +222,7 @@ server_socket_open(const struct Connection *con) { static void connection_cb(struct ev_loop *loop, struct ev_io *w, int revents) { struct Connection *con = (struct Connection *)w->data; + int is_client = &con->client.watcher == w; struct Buffer *input_buffer = is_client ? con->client.buffer : con->server.buffer; @@ -463,6 +465,8 @@ resolv_cb(struct Address *result, void *data) { struct Connection *con = cb_data->connection; struct ev_loop *loop = cb_data->loop; + debug("[DEBUG] %s(): Resolve '%s'", __FUNCTION__, address_hostname(cb_data->address)); + if (con->state != RESOLVING) { info("resolv_cb() called for connection not in RESOLVING state"); return; @@ -484,6 +488,13 @@ resolv_cb(struct Address *result, void *data) { con->state = RESOLVED; + char tmp1[INET6_ADDRSTRLEN + 8]; + char tmp2[INET6_ADDRSTRLEN + 8]; + debug("[DEBUG] %s(): Resolved. server='%s', client='%s'", __FUNCTION__, + display_sockaddr(&con->server.addr, tmp1, sizeof(tmp1)), + display_sockaddr(&con->client.addr, tmp2, sizeof(tmp2)) + ); + initiate_server_connect(con, loop); } @@ -499,6 +510,15 @@ free_resolv_cb_data(struct resolv_cb_data *cb_data) { static void initiate_server_connect(struct Connection *con, struct ev_loop *loop) { + char tmp1[INET6_ADDRSTRLEN + 8]; + char tmp2[INET6_ADDRSTRLEN + 8]; + + debug("[DEBUG] %s(): Connecting to '%s', client='%s'", + __FUNCTION__, + display_sockaddr(&con->server.addr, tmp1, sizeof(tmp1)), + display_sockaddr(&con->client.addr, tmp2, sizeof(tmp2)) + ); + #ifdef HAVE_ACCEPT4 int sockfd = socket(con->server.addr.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0); #else @@ -513,6 +533,7 @@ initiate_server_connect(struct Connection *con, struct ev_loop *loop) { return; } + #ifndef HAVE_ACCEPT4 int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); @@ -553,6 +574,11 @@ initiate_server_connect(struct Connection *con, struct ev_loop *loop) { return; } + debug("[DEBUG] %s(): Use source address '%s'", + __FUNCTION__, + inet_ntoa(((struct sockaddr_in *) address_sa(con->listener->source_address))->sin_addr)); + + int result = 0; int tries = 5; do { result = bind(sockfd, @@ -570,6 +596,27 @@ initiate_server_connect(struct Connection *con, struct ev_loop *loop) { } } +#ifdef SO_BINDTODEVICE + if (con->listener->device_name) { + debug("[DEBUG] %s(): Bind to interface '%s'", __FUNCTION__, con->listener->device_name); + + struct ifreq ifr; + + memcpy(&ifr.ifr_name, con->listener->device_name, sizeof(ifr.ifr_name)); + + if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + (void *)&ifr, sizeof(ifr)) < 0) { + + err("bind failed: %s", strerror(errno)); + close(sockfd); + abort_connection(con); + return; + } + + } +#endif + + debug("[DEBUG] %s(): Connect", __FUNCTION__); int result = connect(sockfd, (struct sockaddr *)&con->server.addr, con->server.addr_len); diff --git a/src/listener.c b/src/listener.c index 2c43ec2c..716d01d1 100644 --- a/src/listener.c +++ b/src/listener.c @@ -166,6 +166,7 @@ listeners_reload(struct Listener_head *existing_listeners, */ static void listener_update(struct Listener *existing_listener, struct Listener *new_listener, const struct Table_head *tables) { + debug("[DEBUG] %s(): ", __FUNCTION__); assert(existing_listener != NULL); assert(new_listener != NULL); assert(address_compare(existing_listener->address, new_listener->address) == 0); @@ -178,6 +179,12 @@ listener_update(struct Listener *existing_listener, struct Listener *new_listene existing_listener->source_address = new_listener->source_address; new_listener->source_address = NULL; +#ifdef SO_BINDTODEVICE + free(existing_listener->device_name); + existing_listener->device_name = new_listener->device_name; + new_listener->device_name = NULL; +#endif + existing_listener->protocol = new_listener->protocol; free(existing_listener->table_name); @@ -211,6 +218,9 @@ new_listener() { return NULL; } +#ifdef SO_BINDTODEVICE + listener->device_name = NULL; +#endif listener->address = NULL; listener->fallback_address = NULL; listener->source_address = NULL; @@ -329,6 +339,8 @@ accept_listener_ipv6_v6only(struct Listener *listener, char *ipv6_v6only) { int accept_listener_fallback_address(struct Listener *listener, char *fallback) { + debug("[DEBUG] %s(): load fallback address from config: '%s'", __FUNCTION__, fallback); + if (listener->fallback_address != NULL) { err("Duplicate fallback address: %s", fallback); return 0; @@ -366,10 +378,21 @@ accept_listener_fallback_address(struct Listener *listener, char *fallback) { int accept_listener_source_address(struct Listener *listener, char *source) { + debug("[DEBUG] %s(): load source address from config: %s", __FUNCTION__, source); + if (listener->source_address != NULL || listener->transparent_proxy) { err("Duplicate source address: %s", source); return 0; } +#ifdef SO_BINDTODEVICE + if (listener->device_name != NULL) { + err("Config Error: Cannot set source address '%s' with bind device '%s'", + listener->source_address, + listener->device_name + ); + return 0; + } +#endif if (strcasecmp("client", source) == 0) { #ifdef IP_TRANSPARENT @@ -399,8 +422,40 @@ accept_listener_source_address(struct Listener *listener, char *source) { display_address(listener->address, address, sizeof(address))); } + debug("[DEBUG] %s(): source address '%s' set.", __FUNCTION__, source); + return 1; +} + + +#ifdef SO_BINDTODEVICE +int +accept_listener_device_name(struct Listener *listener, char *device_name) { + debug("[DEBUG] %s(): load device name from config: %s", __FUNCTION__, device_name); + + if (getuid() != 0) { + err("Binding device '%s' requires ROOT access.", device_name); + return 0; + } + + if (listener->device_name != NULL) { + err("Duplicate device name: %s", device_name); + return 0; + } + + if (listener->device_name != NULL) { + err("Config Error: Cannot set bind device '%s' with source address '%s'", + listener->device_name, + listener->source_address + ); + return 0; + } + + listener->device_name = strdup(device_name); + + debug("[DEBUG] %s(): bind device '%s' set.", __FUNCTION__, listener->device_name); return 1; } +#endif int accept_listener_bad_request_action(struct Listener *listener, char *action) { @@ -667,6 +722,11 @@ print_listener_config(FILE *file, const struct Listener *listener) { display_address(listener->source_address, address, sizeof(address))); +#ifdef SO_BINDTODEVICE + if (listener->device_name) + fprintf(file, "\tdevice %s\n", listener->device_name); +#endif + if (listener->reuseport) fprintf(file, "\treuseport on\n"); @@ -692,6 +752,11 @@ free_listener(struct Listener *listener) { free(listener->address); free(listener->fallback_address); free(listener->source_address); + +#ifdef SO_BINDTODEVICE + listener->device_name = NULL; +#endif + free(listener->table_name); table_ref_put(listener->table); diff --git a/src/listener.h b/src/listener.h index fbd777a5..fcf2c64f 100644 --- a/src/listener.h +++ b/src/listener.h @@ -41,6 +41,10 @@ struct Listener { struct Logger *access_log; int log_bad_requests, reuseport, transparent_proxy, ipv6_v6only; +#ifdef SO_BINDTODEVICE + char *device_name; +#endif + /* Runtime fields */ int reference_count; struct ev_io watcher; @@ -55,6 +59,9 @@ int accept_listener_arg(struct Listener *, char *); int accept_listener_table_name(struct Listener *, char *); int accept_listener_fallback_address(struct Listener *, char *); int accept_listener_source_address(struct Listener *, char *); +#ifdef SO_BINDTODEVICE +int accept_listener_device_name(struct Listener *, char *); +#endif int accept_listener_protocol(struct Listener *, char *); int accept_listener_reuseport(struct Listener *, char *); int accept_listener_ipv6_v6only(struct Listener *, char *);