diff --git a/src/gtp_apn.c b/src/gtp_apn.c index ffbae29..b3f9cb4 100644 --- a/src/gtp_apn.c +++ b/src/gtp_apn.c @@ -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", @@ -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 @@ -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); diff --git a/src/gtp_resolv.c b/src/gtp_resolv.c index deb503e..24b9050 100644 --- a/src/gtp_resolv.c +++ b/src/gtp_resolv.c @@ -103,6 +103,80 @@ 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) { @@ -110,6 +184,7 @@ ns_res_nquery_retry(gtp_resolv_ctx_t *ctx, int class, int type) 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); @@ -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) { @@ -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) { diff --git a/src/gtp_server.c b/src/gtp_server.c index 7b998fd..59fb560 100644 --- a/src/gtp_server.c +++ b/src/gtp_server.c @@ -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 * diff --git a/src/include/gtp_apn.h b/src/include/gtp_apn.h index 11e9583..acba305 100644 --- a/src/include/gtp_apn.h +++ b/src/include/gtp_apn.h @@ -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; diff --git a/src/include/gtp_resolv.h b/src/include/gtp_resolv.h index eff541d..26f5c4b 100644 --- a/src/include/gtp_resolv.h +++ b/src/include/gtp_resolv.h @@ -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; @@ -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;