Skip to content

Commit

Permalink
Proof-of-concept for dlundquist#371
Browse files Browse the repository at this point in the history
Usage:

listen 0.0.0.0 443 {
    proto tls
    embed fd46:1::
}

If the inbound connection is IPv4, and the outbound connection is IPv6,
then the IPv4 address is embedded in the low 32 bits of the source.
  • Loading branch information
candlerb committed Mar 5, 2021
1 parent 822bb80 commit 9288de1
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ static const struct Keyword listener_stanza_grammar[] = {
.keyword="source",
.parse_arg=(int(*)(void *, const char *))accept_listener_source_address,
},
{
.keyword="embed",
.parse_arg=(int(*)(void *, const char *))accept_listener_embed_address,
},
{
.keyword="access_log",
.create=(void *(*)())new_logger_builder,
Expand Down
29 changes: 29 additions & 0 deletions src/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,35 @@ initiate_server_connect(struct Connection *con, struct ev_loop *loop) {
abort_connection(con);
return;
}
} else if (con->listener->embed_address
&& con->client.addr.ss_family == AF_INET
&& con->server.addr.ss_family == AF_INET6) {
struct sockaddr_in6 embedded_source;
#ifdef IP_FREEBIND
int on = 1;
int result = setsockopt(sockfd, SOL_IP, IP_FREEBIND, &on, sizeof(on));
if (result < 0) {
warn("setsockopt IP_FREEBIND failed: %s", strerror(errno));
// May not be necessary if user has set sysctl net.ipv6.ip_nonlocal_bind
}
#else
int result = -EPERM;
#endif

memcpy(&embedded_source,
address_sa(con->listener->embed_address),
sizeof(embedded_source));
memcpy(&(embedded_source.sin6_addr.s6_addr[12]),
&((struct sockaddr_in*)(&con->client.addr))->sin_addr, 4);
// XXX: maybe optionally the source port in bits 80-95

result = bind(sockfd, &embedded_source, sizeof(embedded_source));
if (result < 0) {
err("bind failed: %s", strerror(errno));
close(sockfd);
abort_connection(con);
return;
}
} else if (con->listener->source_address) {
int on = 1;
int result = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
Expand Down
45 changes: 45 additions & 0 deletions src/listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ listener_update(struct Listener *existing_listener, struct Listener *new_listene
existing_listener->source_address = new_listener->source_address;
new_listener->source_address = NULL;

free(existing_listener->embed_address);
existing_listener->embed_address = new_listener->embed_address;
new_listener->embed_address = NULL;

existing_listener->protocol = new_listener->protocol;

free(existing_listener->table_name);
Expand Down Expand Up @@ -212,6 +216,7 @@ new_listener() {
listener->address = NULL;
listener->fallback_address = NULL;
listener->source_address = NULL;
listener->embed_address = NULL;
listener->protocol = tls_protocol;
listener->table_name = NULL;
listener->access_log = NULL;
Expand Down Expand Up @@ -406,6 +411,40 @@ accept_listener_source_address(struct Listener *listener, const char *source) {
return 1;
}

int
accept_listener_embed_address(struct Listener *listener, const char *embed) {
if (listener->embed_address != NULL) {
err("Duplicate embed address: %s", embed);
return 0;
}

listener->embed_address = new_address(embed);
if (listener->embed_address == NULL) {
err("Unable to parse embed address: %s", embed);
return 0;
}
if (!address_is_sockaddr(listener->embed_address)) {
err("Only embed socket addresses permitted");
free(listener->embed_address);
listener->embed_address = NULL;
return 0;
}
if (address_sa(listener->embed_address)->sa_family != AF_INET6) {
err("Embed address must be IPv6 prefix");
free(listener->embed_address);
listener->embed_address = NULL;
return 0;
}
if (address_port(listener->embed_address) != 0) {
err("Port on embed address not permitted");
free(listener->embed_address);
listener->embed_address = NULL;
return 0;
}

return 1;
}

int
accept_listener_bad_request_action(struct Listener *listener, const char *action) {
if (strncmp("log", action, strlen(action)) == 0) {
Expand Down Expand Up @@ -707,6 +746,11 @@ print_listener_config(FILE *file, const struct Listener *listener) {
display_address(listener->source_address,
address, sizeof(address)));

if (listener->embed_address)
fprintf(file, "\tembed %s\n",
display_address(listener->embed_address,
address, sizeof(address)));

if (listener->reuseport)
fprintf(file, "\treuseport on\n");

Expand All @@ -732,6 +776,7 @@ free_listener(struct Listener *listener) {
free(listener->address);
free(listener->fallback_address);
free(listener->source_address);
free(listener->embed_address);
free(listener->table_name);

table_ref_put(listener->table);
Expand Down
3 changes: 2 additions & 1 deletion src/listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ SLIST_HEAD(Listener_head, Listener);

struct Listener {
/* Configuration fields */
struct Address *address, *fallback_address, *source_address;
struct Address *address, *fallback_address, *source_address, *embed_address;
const struct Protocol *protocol;
char *table_name;
struct Logger *access_log;
Expand All @@ -57,6 +57,7 @@ int accept_listener_arg(struct Listener *, const char *);
int accept_listener_table_name(struct Listener *, const char *);
int accept_listener_fallback_address(struct Listener *, const char *);
int accept_listener_source_address(struct Listener *, const char *);
int accept_listener_embed_address(struct Listener *, const char *);
int accept_listener_protocol(struct Listener *, const char *);
int accept_listener_reuseport(struct Listener *, const char *);
int accept_listener_ipv6_v6only(struct Listener *, const char *);
Expand Down

0 comments on commit 9288de1

Please sign in to comment.