Skip to content

Commit

Permalink
resolv: Add support to local bind while connecting remote nameserver.
Browse files Browse the repository at this point in the history
This feature is available via following VTY command in apn conf :
"nameserver-bind (A.B.C.D|X:X:X:X) port <1024-65535>"

This feature is useful when your local networking configuration
is based on loopback interfaces (dummy). In that situation
connected interface and service interface are different so you
need to bind to the proper interface for service.

glibc resolver interface (not documented), is providing context
structure in order to set remote nameserver and connection to
use. We are then, creating our nameserver resolv socket directly,
bind to the proper Address & Port, and pass it to glibc as
pre-init.
  • Loading branch information
acassen committed Nov 21, 2024
1 parent f1b13dc commit d6b4254
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 7 deletions.
37 changes: 37 additions & 0 deletions src/gtp_apn.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,37 @@ DEFUN(apn_nameserver,
return CMD_SUCCESS;
}

DEFUN(apn_nameserver_bind,
apn_nameserver_bind_cmd,
"nameserver-bind (A.B.C.D|X:X:X:X) port <1024-65535>",
"Set Global PDN nameserver binding Address\n"
"IP Address\n"
"IPv4 Address\n"
"IPv6 Address\n"
"UDP Port\n"
"Number\n")
{
gtp_apn_t *apn = vty->index;
struct sockaddr_storage *addr = &apn->nameserver_bind;
int ret, port;

if (argc < 2) {
vty_out(vty, "%% missing arguments%s", VTY_NEWLINE);
return CMD_WARNING;
}

VTY_GET_INTEGER_RANGE("UDP Port", port, argv[1], 1024, 65535);

ret = inet_stosockaddr(argv[0], port, addr);
if (ret < 0) {
vty_out(vty, "%% malformed IP address %s%s", argv[0], VTY_NEWLINE);
memset(addr, 0, sizeof(struct sockaddr_storage));
return CMD_WARNING;
}

return CMD_SUCCESS;
}

DEFUN(apn_nameserver_timeout,
apn_nameserver_timeout_cmd,
"nameserver-timeout INTEGER",
Expand Down Expand Up @@ -1250,6 +1281,11 @@ apn_config_write(vty_t *vty)
vty_out(vty, " nameserver %s%s"
, inet_sockaddrtos(&apn->nameserver)
, VTY_NEWLINE);
if (apn->nameserver_bind.ss_family)
vty_out(vty, " nameserver-bind %s port %d%s"
, inet_sockaddrtos(&apn->nameserver_bind)
, ntohs(inet_sockaddrport(&apn->nameserver_bind))
, VTY_NEWLINE);
if (apn->nameserver_timeout)
vty_out(vty, " nameserver-timeout %d%s"
, apn->nameserver_timeout
Expand Down Expand Up @@ -1310,6 +1346,7 @@ gtp_apn_vty_init(void)

install_default(APN_NODE);
install_element(APN_NODE, &apn_nameserver_cmd);
install_element(APN_NODE, &apn_nameserver_bind_cmd);
install_element(APN_NODE, &apn_nameserver_timeout_cmd);
install_element(APN_NODE, &apn_resolv_max_retry_cmd);
install_element(APN_NODE, &apn_resolv_cache_update_cmd);
Expand Down
77 changes: 77 additions & 0 deletions src/gtp_resolv.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,88 @@ ns_log_error(const char *dn, int error)
}
}

static int
ns_bind_connect(gtp_apn_t *apn)
{
struct sockaddr_storage *addr = &apn->nameserver_bind;
socklen_t addrlen;
int fd, err;

if (!apn->nameserver_bind.ss_family)
return -1;

/* Create UDP Client socket */
fd = socket(addr->ss_family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
fd = (fd < 0) ? fd : if_setsockopt_reuseaddr(fd, 1);
fd = (fd < 0) ? fd : if_setsockopt_rcvtimeo(fd, 2000);
fd = (fd < 0) ? fd : if_setsockopt_sndtimeo(fd, 2000);
if (fd < 0) {
log_message(LOG_INFO, "%s(): error creating UDP [%s]:%d socket"
, __FUNCTION__
, inet_sockaddrtos(addr)
, ntohs(inet_sockaddrport(addr)));
return -1;
}

/* Bind listening channel */
addrlen = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6);
err = bind(fd, (struct sockaddr *) addr, addrlen);
if (err) {
log_message(LOG_INFO, "%s(): Error binding to [%s]:%d (%m)"
, __FUNCTION__
, inet_sockaddrtos(addr)
, ntohs(inet_sockaddrport(addr)));
close(fd);
return -1;
}

err = connect(fd, (struct sockaddr *) &apn->nameserver, addrlen);
if (err) {
log_message(LOG_INFO, "%s(): Error connecting to [%s]:%d (%m)"
, __FUNCTION__
, inet_sockaddrtos(&apn->nameserver)
, ntohs(inet_sockaddrport(&apn->nameserver)));
close(fd);
return -1;
}

return fd;
}

static int
ns_ctx_init(gtp_resolv_ctx_t *ctx)
{
gtp_apn_t *apn = ctx->apn;
struct sockaddr_storage *addr;
int fd;

fd = ns_bind_connect(apn);
if (fd < 0)
return -1;

addr = (apn->nameserver.ss_family) ? &apn->nameserver : &daemon_data->nameserver;

/* glibc resolver is providing extension to set remote nameserver.
* We are using this facility to set pre-allocated/pre-initialized
* socket connection to remote nameserver. Specially useful when you
* want to bind the connection to a local IP Address. */
ctx->ns_rs._u._ext.nssocks[0] = fd;
ctx->ns_rs._u._ext.nsaddrs[0] = MALLOC(sizeof(struct sockaddr_in6));
*ctx->ns_rs._u._ext.nsaddrs[0] = *((struct sockaddr_in6 *) addr);
ctx->ns_rs._u._ext.nscount = 1;
return 0;
}


static int
ns_res_nquery_retry(gtp_resolv_ctx_t *ctx, int class, int type)
{
int retry_count = 0;
int ret;

retry:
ns_ctx_init(ctx);
ret = res_nquery(&ctx->ns_rs, ctx->nsdisp, class, type, ctx->nsbuffer, GTP_RESOLV_BUFFER_LEN);
if (ret < 0) {
ns_log_error(ctx->nsdisp, h_errno);
Expand Down Expand Up @@ -375,6 +450,7 @@ gtp_resolv_naptr(gtp_resolv_ctx_t *ctx, list_head_t *l, const char *format, ...)
return 0;
}


gtp_resolv_ctx_t *
gtp_resolv_ctx_alloc(gtp_apn_t *apn)
{
Expand All @@ -385,6 +461,7 @@ gtp_resolv_ctx_alloc(gtp_apn_t *apn)
if (!ctx)
return NULL;

ctx->apn = apn;
ctx->max_retry = apn->resolv_max_retry;
addr = (apn->nameserver.ss_family) ? &apn->nameserver : &daemon_data->nameserver;
if (!addr->ss_family) {
Expand Down
10 changes: 4 additions & 6 deletions src/gtp_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,25 +90,23 @@ gtp_server_udp_init(gtp_server_t *srv)
, __FUNCTION__
, inet_sockaddrtos(addr)
, ntohs(inet_sockaddrport(addr)));
goto socket_error;
return -1;
}

/* Bind listening channel */
addrlen = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6);
err = bind(fd, (struct sockaddr *) addr, addrlen);
if (err < 0) {
if (err) {
log_message(LOG_INFO, "%s(): Error binding to [%s]:%d (%m)"
, __FUNCTION__
, inet_sockaddrtos(addr)
, ntohs(inet_sockaddrport(addr)));
goto socket_error;
close(fd);
return -1;
}

return fd;

socket_error:
return -1;
}

static void *
Expand Down
1 change: 1 addition & 0 deletions src/include/gtp_apn.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ typedef struct _gtp_apn {
char name[GTP_APN_MAX_LEN];
char realm[GTP_REALM_LEN];
struct sockaddr_storage nameserver;
struct sockaddr_storage nameserver_bind;
int nameserver_timeout;
uint8_t resolv_max_retry;
int resolv_cache_update;
Expand Down
3 changes: 2 additions & 1 deletion src/include/gtp_resolv.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ typedef struct _gtp_pgw {
uint16_t priority;
uint16_t weight;
char srv_name[GTP_DISPLAY_SRV_LEN];
struct _gtp_naptr *naptr; /*Back-pointer */
struct _gtp_naptr *naptr; /* Back-pointer */
struct sockaddr_storage addr;
uint64_t cnt;
time_t last_resp;
Expand Down Expand Up @@ -71,6 +71,7 @@ typedef struct _gtp_service {
} gtp_service_t;

typedef struct _gtp_resolv_ctx {
gtp_apn_t *apn; /* Back-pointer */
char *realm;
struct __res_state ns_rs;
ns_msg msg;
Expand Down

0 comments on commit d6b4254

Please sign in to comment.