47#define ENABLE_STRNATPMPERR 1
52#include <pcp-client/pcp.h>
61#define DEFAULT_ALLOCATION_TIMEOUT_S 1800
62#define DEFAULT_NAT_PMP_TIMEOUT 5
63#define MOD_NAME "[NAT] "
64#define PREALLOCATE_S 30
78 pcp_flow_t *audio_flow;
79 pcp_flow_t *video_flow;
110static int check_flow_info(pcp_flow_t* f)
113 pcp_flow_info_t *info_buf = NULL;
114 pcp_flow_info_t *ret = pcp_flow_get_info(f,&cnt);
117 for (; cnt>0; cnt--, ret++) {
120 case pcp_state_succeeded:
121 printf(
"\nFlow signaling succeeded.\n");
124 case pcp_state_short_lifetime_error:
125 printf(
"\nFlow signaling failed. Short lifetime error received.\n");
128 case pcp_state_failed:
129 printf(
"\nFlow signaling failed.\n");
132 case pcp_state_processing:
133 printf(
"\nFlow signaling processing.\n");
135 case pcp_state_partial_result:
136 printf(
"\nFlow signaling partial result.\n");
149static const char* decode_fresult(pcp_fstate_e s)
152 case pcp_state_short_lifetime_error:
154 case pcp_state_succeeded:
156 case pcp_state_failed:
164static char *ctime_r(
const time_t *timep,
char *buf)
166 ctime_s(buf, 26, timep);
171static void print_ext_addr(pcp_flow_t* f)
174 pcp_flow_info_t *info_buf = NULL;
175 pcp_flow_info_t *ret = pcp_flow_get_info(f,&cnt);
178 printf(
"%-20s %-4s %-20s %5s %-20s %5s %-20s %5s %3s %5s %s\n",
184 "Res",
"State",
"Ends");
185 for (; cnt>0; cnt--, ret++) {
186 char ntop_buffs[4][INET6_ADDRSTRLEN];
189 printf(
"%-20s %-4s %-20s %5hu %-20s %5hu %-20s %5hu %3d %5s %s",
190 inet_ntop(AF_INET6, &ret->pcp_server_ip, ntop_buffs[0],
191 sizeof(ntop_buffs[0])),
192 ret->protocol == IPPROTO_TCP ?
"TCP" : (
193 ret->protocol == IPPROTO_UDP ?
"UDP" :
"UNK"),
194 inet_ntop(AF_INET6, &ret->int_ip, ntop_buffs[1],
195 sizeof(ntop_buffs[1])),
196 ntohs(ret->int_port),
197 inet_ntop(AF_INET6, &ret->dst_ip, ntop_buffs[2],
198 sizeof(ntop_buffs[2])),
199 ntohs(ret->dst_port),
200 inet_ntop(AF_INET6, &ret->ext_ip, ntop_buffs[3],
201 sizeof(ntop_buffs[3])),
202 ntohs(ret->ext_port),
203 ret->pcp_result_code,
204 decode_fresult(ret->result),
205 ret->recv_lifetime_end == 0 ?
" -\n" :
206 ctime_r(&ret->recv_lifetime_end, timebuf));
217 struct pcp_state *s = &
state->nat_state.pcp_state;
218 pcp_terminate(s->ctx, 1);
224#define PCP_WAIT_MS 500
226#define NAT_HANDLE_ERROR(msg) { if (fd != -1) CLOSESOCKET(fd); socket_error(msg); return false; }
227#define NAT_ASSERT_EQ(expr, val) { int rc = expr; if (rc != (val)) NAT_HANDLE_ERROR(#expr) }
228#define NAT_ASSERT_NEQ(expr, val) { int rc = expr; if (rc == (val)) NAT_HANDLE_ERROR(#expr) }
230 struct sockaddr_in dst = { 0 };
231 socklen_t src_len =
sizeof *out;
233 dst.sin_family = AF_INET;
234 dst.sin_port = htons(80);
236 NAT_ASSERT_EQ(inet_pton(AF_INET,
"93.184.216.34", &dst.sin_addr.s_addr), 1);
237 fd = socket(AF_INET, SOCK_DGRAM, 0);
239 NAT_ASSERT_EQ(connect(fd, (
struct sockaddr *) &dst,
sizeof dst), 0);
240 NAT_ASSERT_EQ(getsockname(fd, (
struct sockaddr *) out, &src_len), 0);
249 struct pcp_state *s = &
state->nat_state.pcp_state;
250 struct sockaddr_in src = { 0 };
252 s->ctx = pcp_init(ENABLE_AUTODISCOVERY, NULL);
263 src.sin_port = htons(video_rx_port);
264 s->video_flow = pcp_new_flow(s->ctx, (
struct sockaddr*) &src, NULL, NULL, IPPROTO_UDP, lifetime, NULL);
265 if (s->video_flow == NULL) {
270 src.sin_port = htons(audio_rx_port);
271 s->audio_flow = pcp_new_flow(s->ctx, (
struct sockaddr*) &src, NULL, NULL, IPPROTO_UDP, lifetime, NULL);
272 if (s->audio_flow == NULL) {
281 if (video_rx_port != 0) {
283 ret = ret && check_flow_info(s->video_flow) == 0;
285 print_ext_addr(s->video_flow);
290 if (audio_rx_port != 0) {
292 ret = ret && check_flow_info(s->audio_flow) == 0;
294 print_ext_addr(s->audio_flow);
316static bool nat_pmp_add_mapping(natpmp_t *natpmp,
int privateport,
int publicport,
int lifetime)
318 if (privateport == 0 && publicport == 0) {
324 r = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_UDP,
325 privateport, publicport,
328 "NAT PMP - sendnewportmappingrequest returned %d (%s)\n",
329 r, r == 12 ?
"SUCCESS" :
"FAILED");
335 time_t t0 = time(NULL);
338 struct timeval timeout = { 0 };
340 FD_SET((
fd_t) natpmp->s, &fds);
341 r = getnatpmprequesttimeout(natpmp, &timeout);
347 select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
348 r = readnatpmpresponseorretry(natpmp, &
response);
350 r, r == 0 ?
"OK" : (r == NATPMP_TRYAGAIN ?
"TRY AGAIN" :
"FAILED"));
360 response.pnu.newportmapping.mappedpublicport,
361 response.type == NATPMP_RESPTYPE_UDPPORTMAPPING ?
"UDP" :
362 (
response.type == NATPMP_RESPTYPE_TCPPORTMAPPING ?
"TCP" :
364 response.pnu.newportmapping.privateport,
365 response.pnu.newportmapping.lifetime);
376 struct in_addr gateway_in_use = { 0 };
379 r = initnatpmp(&natpmp, 0, 0);
381 r ?
"FAILED" :
"SUCCESS");
385 gateway_in_use.s_addr = natpmp.gateway;
389 r = sendpublicaddressrequest(&natpmp);
391 r, r == 2 ?
"SUCCESS" :
"FAILED");
399 struct timeval timeout;
401 FD_SET((
fd_t) natpmp.s, &fds);
402 getnatpmprequesttimeout(&natpmp, &timeout);
403 r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
408 r = readnatpmpresponseorretry(&natpmp, &
response);
409 int sav_errno = errno;
411 r, r == 0 ?
"OK" : ( r== NATPMP_TRYAGAIN ?
"TRY AGAIN" :
"FAILED"));
412 if (r < 0 && r != NATPMP_TRYAGAIN) {
416 sav_errno, strerror(sav_errno));
418 }
while(r==NATPMP_TRYAGAIN);
427 if (!nat_pmp_add_mapping(&natpmp, video_rx_port, video_rx_port, lifetime) ||
428 !nat_pmp_add_mapping(&natpmp, audio_rx_port, audio_rx_port, lifetime)) {
432 r = closenatpmp(&natpmp);
464 if (rc != ETIMEDOUT) {
474 timeout.tv_sec = time(NULL) + 5;
489 if (config == NULL) {
491 struct sockaddr_in out;
500 assert(video_rx_port >= 0 && video_rx_port <= 65535 && audio_rx_port >= 0 && audio_rx_port <= 65535);
502 if (strcmp(config,
"help") == 0) {
507 for (
int i = UG_NAT_TRAVERSE_FIRST; i <= UG_NAT_TRAVERSE_LAST; ++i) {
520 bool not_found =
true;
521 char protocol[strlen(config) + 1];
522 strcpy(protocol, config);
523 if (strchr(protocol,
':') != NULL) {
530 *strchr(protocol,
':') =
'\0';
533 if (strlen(protocol) > 0 && strcmp(
nat_traverse_info[i].name_short, protocol) != 0) {
static af_result_code init(struct module *parent, const char *cfg, void **state)
int color_printf(const char *format,...)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_VERBOSE
#define LOG_LEVEL_WARNING
less severe errors
static bool setup_nat_pmp(struct ug_nat_traverse *state, int video_rx_port, int audio_rx_port, int lifetime)
void stop_nat_traverse(struct ug_nat_traverse *s)
static const struct nat_traverse_info_t nat_traverse_info[]
struct ug_nat_traverse * start_nat_traverse(const char *config, const char *remote_host, int video_rx_port, int audio_rx_port)
static bool get_outbound_ip(struct sockaddr_in *out)
#define DEFAULT_ALLOCATION_TIMEOUT_S
#define NAT_ASSERT_EQ(expr, val)
#define NAT_ASSERT_NEQ(expr, val)
static void * nat_traverse_keepalive(void *state)
static bool setup_pcp(struct ug_nat_traverse *state, int video_rx_port, int audio_rx_port, int lifetime)
static void done_nat_pmp(struct ug_nat_traverse *state)
#define DEFAULT_NAT_PMP_TIMEOUT
#define PREALLOCATE_S
number of seconds that repeated allocation is performed before timeout
static void done_pcp(struct ug_nat_traverse *state)
bool is_addr_private(struct sockaddr *sa)
bool is_host_private(const char *hostname)
bool is_host_loopback(const char *hostname)
const char * name_long
for output
bool(* init)(struct ug_nat_traverse *state, int video_rx_port, int audio_rx_port, int lifetime)
const char * name_short
for command-line specifiction
void(* done)(struct ug_nat_traverse *state)
pthread_cond_t keepalive_cv
pthread_t keepalive_thread
bool keepalive_should_exit
enum ug_nat_traverse::traverse_t traverse
@ UG_NAT_TRAVERSE_NAT_PMP
pthread_mutex_t keepalive_mutex
bool initialized
whether the state was initialized
union ug_nat_traverse::@62 nat_state