2

I using the PJSIP/PJNATH port-punching library (for STUN/TURN/ICE) and want to create my own UDP-based transport over the punched-ports.

I’m unable to find the data-structure that holds the internal port of the NAT mapping. The reason I need the internal port (on both sides) is to bind the UDP socket to the internal port (instead of the OS picking a random port). Please see diagram.

I realize that the icedemo sample app is able to do send data back-and-forth on the internet (after punching the ports); so it must bind to the internal UDP port. I just need to know which data-structure holds the internal port. Internal port of the NAT mapping

The pj_ice_sess_send_data function in the in the ice_session.c file looked like a promising place to dump variables. But none contain the internal port of the NAT mapping.

//my attempt did *not* work
pj_ice_sess_cand *cand;
cand = comp->valid_check->lcand;
char addrinfo[80];
pj_sockaddr_print(&cand->base_addr, addrinfo, sizeof(addrinfo), 3);
printf("***Local address %s\n", addrinfo);

FYI, the public IP:Port is readily available.

Background
The PJNATH library implements a standards-based STUN/TURN/ICE protocol and punches UDP ports using ICE-UDP checks after it has exchanged the host/server-reflexive/relay IP:Port from both sides.

auro
  • 1,079
  • 1
  • 10
  • 22

2 Answers2

1

It looks like its the bound_addr pj_sockaddr in the pj_stun_sock_cfg struct.

According to the docs, "If the port is set to zero, the socket will bind at any port (chosen by the OS)."

  • Thank you for responding. We cannot use a zero-value for the source-port since the ICE procedure has create a mapping in the NAT; and we must use that mapping. So, we must use the local bound port, (not 0). In consumer NATs (from companies such D-Link), the internal and external ports in the NAT mapping is the same but that's not the case for all NATs. I found the answer after experimenting with a few NATs. I'll state the answer below. – auro Jul 25 '14 at 00:29
0

I found that, on ICE "complete", the lcand part of the data-structure does hold the local bound IP and port. In most consumer NATs, this was the same port number (which it indeed is) which was puzzling. After experimenting with a few enterprice NATs, I found that the port numbers are different on either side of the mapping.

You can print it with something like this...

static void cb_on_ice_complete(pj_ice_strans *ice_st,
                               pj_ice_strans_op op,
                               pj_status_t status)
{
    const char *opname =
        (op==PJ_ICE_STRANS_OP_INIT? "initialization" :
         (op==PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op"));

    if (status == PJ_SUCCESS)
    {
        PJ_LOG(3,(THIS_FILE, "ICE %s successful", opname));

        if ( op == PJ_ICE_STRANS_OP_NEGOTIATION )
        {
            const pj_ice_sess_check *check;
            check = pj_ice_strans_get_valid_pair(icedemo.icest, 1);
            if ((check != NULL) && (check->nominated == PJ_TRUE)) { //local (l) and remote(r) candidate
                pj_sockaddr_print(&check->lcand->addr, icedemo.local_ip_port,
                      sizeof(icedemo.local_ip_port), 3);
                pj_sockaddr_print(&check->rcand->addr, icedemo.remote_ip_port,
                      sizeof(icedemo.remote_ip_port), 3);

        pj_sockaddr_print(&check->lcand->base_addr, icedemo.local_bound_ip_port, sizeof(icedemo.local_bound_ip_port), 3);

            }
            else
            {
                PJ_LOG(3,(THIS_FILE, "err: unable to get valid pair for ice1 "
                          "component %d", icedemo.icest, 1));
            }


        }
    }
    else
    {
        char errmsg[PJ_ERR_MSG_SIZE];

        pj_strerror(status, errmsg, sizeof(errmsg));
        PJ_LOG(1,(THIS_FILE, "ICE %s failed: %s", opname, errmsg));
        pj_ice_strans_destroy(ice_st);
        icedemo.icest = NULL;
    }
}
auro
  • 1,079
  • 1
  • 10
  • 22