diff options
author | Luigi Rizzo <rizzo@icir.org> | 2007-07-21 01:01:10 +0000 |
---|---|---|
committer | Luigi Rizzo <rizzo@icir.org> | 2007-07-21 01:01:10 +0000 |
commit | 2286afa3af24df512de10cace46eaa72bcf5ac3d (patch) | |
tree | 2ec169e930ea3829e114f67fdd65ab2d05e45917 | |
parent | 5c6d509591b8a5fceed4fa3a85d4ebf4d308b622 (diff) |
Enhance NAT support as discussed on the -dev list, i.e.:
+ extensive documentation changes both in sip.conf.sample and in the source;
+ allow "externip" and "externhost" to include a port number as well;
+ allow "bindaddr" to have a port number (making bindport unnecessary,
even though it is still present for backward compatibility);
+ introduce the new "stunaddr" parameter to specify an STUN server to
be used from the main SIP socket;
+ extend the "sip show settings" output to show all the above.
Internally:
+ change related data structures from struct in_addr to struct sockaddr_in
to store the port numbers as well;
+ reorganize ast_sip_ouraddrfor() (should also be renamed to sip_ouraddrfor()
because it is not a generic API, though it might become so if called with
a socket as an additional argument, in which case it can be moved elsewhere).
As mentioned in the documentation, media sessions still do not use STUN so the
port numbers may still be incorrect when Asterisk is behind a NAT
On passing, some of the debugging messages printing media addresses are
probably using the wrong values, but this will be checked/fixed in a
subsequent commit if needed.
Part of the following chunk in the function that handles a "sip reload" is
probably needed on previous versions as well, to avoid leaking the memory
used for the "localaddr" list:
@@ -17244,13 +17274,17 @@
/* Reset IP addresses */
memset(&bindaddr, 0, sizeof(bindaddr));
+ memset(&stunaddr, 0, sizeof(stunaddr));
+ memset(&internip, 0, sizeof(internip));
+ /* Free memory for local network address mask */
+ ---> ast_free_ha(localaddr); <-----
memset(&localaddr, 0, sizeof(localaddr));
memset(&externip, 0, sizeof(externip));
memset(&default_prefs, 0 , sizeof(default_prefs));
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@76221 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r-- | channels/chan_sip.c | 206 | ||||
-rw-r--r-- | configs/sip.conf.sample | 121 |
2 files changed, 208 insertions, 119 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index c90a00802..8adb13c9b 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1013,7 +1013,7 @@ struct sip_pvt { time_t lastrtptx; /*!< Last RTP sent */ int rtptimeout; /*!< RTP timeout time */ struct sockaddr_in recv; /*!< Received as */ - struct in_addr ourip; /*!< Our IP */ + struct sockaddr_in ourip; /*!< Our IP (as seen from the outside) */ struct ast_channel *owner; /*!< Who owns us (if we have an owner) */ struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */ int route_persistant; /*!< Is this the "real" route? */ @@ -1266,7 +1266,15 @@ static struct sip_auth *authl = NULL; */ static int sipsock = -1; -static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */ +static struct sockaddr_in bindaddr; /*!< The address we bind to */ + +/*! \brief our (internal) default address/port to put in SIP/SDP messages + * internip is initialized picking a suitable address from one of the + * interfaces, and the same port number we bind to. It is used as the + * default address/port in SIP messages, and as the default address + * (but not port) in SDP messages. + */ +static struct sockaddr_in internip; /*! \brief our external IP address/port for SIP sessions. * externip.sin_addr is only set when we know we might be behind @@ -1282,16 +1290,16 @@ static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */ * * + with "stunaddr = host[:port]" we run queries every externrefresh seconds * to the specified server, and store the result in externip. - * XXX not implemented yet. * * Other variables (externhost, externexpire, externrefresh) are used * to support the above functions. */ static struct sockaddr_in externip; /*!< External IP address if we are behind NAT */ -static char externhost[MAXHOSTNAMELEN]; /*!< External host name (possibly with dynamic DNS and DHCP */ -static time_t externexpire = 0; /*!< Expiration counter for re-resolving external host name in dynamic DNS */ +static char externhost[MAXHOSTNAMELEN]; /*!< External host name */ +static time_t externexpire; /*!< Expiration counter for re-resolving external host name in dynamic DNS */ static int externrefresh = 10; +static struct sockaddr_in stunaddr; /*!< stun server address */ /*! \brief List of local networks * We store "localnet" addresses from the config file into an access list, @@ -1301,8 +1309,6 @@ static int externrefresh = 10; */ static struct ast_ha *localaddr; /*!< List of local networks, on the same side of NAT as this Asterisk */ -static struct in_addr __ourip; -static int ourport; static struct sockaddr_in debugaddr; static struct ast_config *notify_types; /*!< The list of manual NOTIFY types we know how to send */ @@ -1539,7 +1545,7 @@ static struct sip_peer *realtime_peer(const char *peername, struct sockaddr_in * static int sip_prune_realtime(int fd, int argc, char *argv[]); /*--- Internal UA client handling (outbound registrations) */ -static void ast_sip_ouraddrfor(struct in_addr *them, struct in_addr *us); +static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us); static void sip_registry_destroy(struct sip_registry *reg); static int sip_register(char *value, int lineno); static char *regstate2str(enum sipregistrystate regstate) attribute_const; @@ -1996,42 +2002,63 @@ static void build_via(struct sip_pvt *p) /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ ast_string_field_build(p, via, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x%s", - ast_inet_ntoa(p->ourip), ourport, p->branch, rport); + ast_inet_ntoa(p->ourip.sin_addr), + ntohs(p->ourip.sin_port), p->branch, rport); } -/*! \brief NAT fix - decide which IP address to use for ASterisk server? +/*! \brief NAT fix - decide which IP address to use for Asterisk server? * * Using the localaddr structure built up with localnet statements in sip.conf * apply it to their address to see if we need to substitute our * externip or can get away with our internal bindaddr + * 'us' is always overwritten. */ -static void ast_sip_ouraddrfor(struct in_addr *them, struct in_addr *us) -{ - struct sockaddr_in theirs, ours; +static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us) +{ + struct sockaddr_in theirs; + /* Set want_remap to non-zero if we want to remap 'us' to an externally + * reachable IP address and port. This is done if: + * 1. we have a localaddr list (containing 'internal' addresses marked + * as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them, + * and AST_SENSE_ALLOW on 'external' ones); + * 2. either stunaddr or externip is set, so we know what to use as the + * externally visible address; + * 3. the remote address, 'them', is external; + * 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY + * when passed to ast_apply_ha() so it does need to be remapped. + * This fourth condition is checked later. + */ + int want_remap = localaddr && + (externip.sin_addr.s_addr || stunaddr.sin_addr.s_addr) && + ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ; - /* Get our local information */ - ast_ouraddrfor(them, us); + *us = internip; /* starting guess for the internal address */ + /* now ask the system what would it use to talk to 'them' */ + ast_ouraddrfor(them, &us->sin_addr); theirs.sin_addr = *them; - ours.sin_addr = *us; - if (localaddr && externip.sin_addr.s_addr && - (ast_apply_ha(localaddr, &theirs)) && - (!global_matchexterniplocally || !ast_apply_ha(localaddr, &ours))) { + if (want_remap && + (!global_matchexterniplocally || !ast_apply_ha(localaddr, us)) ) { + /* if we used externhost or stun, see if it is time to refresh the info */ if (externexpire && time(NULL) >= externexpire) { - struct ast_hostent ahp; - struct hostent *hp; - + if (stunaddr.sin_addr.s_addr) { + ast_stun_request(sipsock, &stunaddr, NULL, &externip); + } else { + if (ast_parse_arg(externhost, PARSE_INADDR, &externip)) + ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost); + } externexpire = time(NULL) + externrefresh; - if ((hp = ast_gethostbyname(externhost, &ahp))) { - memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr)); - } else - ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost); } - *us = externip.sin_addr; + if (externip.sin_addr.s_addr) + *us = externip; + else + ast_log(LOG_WARNING, "stun failed\n"); ast_debug(1, "Target address %s is not local, substituting externip\n", ast_inet_ntoa(*(struct in_addr *)&them->s_addr)); - } else if (bindaddr.sin_addr.s_addr) - *us = bindaddr.sin_addr; + } else if (bindaddr.sin_addr.s_addr) { + /* no remapping, but we bind to a specific address, so use it. */ + *us = bindaddr; + } } /*! \brief Append to SIP dialog history with arg list */ @@ -4650,7 +4677,7 @@ static void build_callid_pvt(struct sip_pvt *pvt) { char buf[33]; - const char *host = S_OR(pvt->fromdomain, ast_inet_ntoa(pvt->ourip)); + const char *host = S_OR(pvt->fromdomain, ast_inet_ntoa(pvt->ourip.sin_addr)); ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host); @@ -4698,11 +4725,12 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si if (intended_method != SIP_OPTIONS) /* Peerpoke has it's own system */ p->timer_t1 = SIP_TIMER_T1; /* Default SIP retransmission timer T1 (RFC 3261) */ - if (sin) { + if (!sin) + p->ourip = internip; + else { p->sa = *sin; ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); - } else - p->ourip = __ourip; + } /* Copy global flags to this PVT at setup. */ ast_copy_flags(&p->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY); @@ -6322,11 +6350,12 @@ static int transmit_response_using_temp(ast_string_field callid, struct sockaddr /* Initialize the bare minimum */ p->method = intended_method; - if (sin) { + if (!sin) + p->ourip = internip; + else { p->sa = *sin; ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); - } else - p->ourip = __ourip; + } p->branch = ast_random(); make_our_tag(p->tag, sizeof(p->tag)); @@ -6631,12 +6660,12 @@ static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p) udptldest.sin_port = p->udptlredirip.sin_port; udptldest.sin_addr = p->udptlredirip.sin_addr; } else { - udptldest.sin_addr = p->ourip; + udptldest.sin_addr = p->ourip.sin_addr; udptldest.sin_port = udptlsin.sin_port; } if (debug) - ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(udptlsin.sin_port)); + ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(udptlsin.sin_port)); /* We break with the "recommendation" and send our IP, in order that our peer doesn't have to ast_gethostbyname() us */ @@ -6726,7 +6755,7 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, struct socka dest->sin_port = p->redirip.sin_port; dest->sin_addr = p->redirip.sin_addr; } else { - dest->sin_addr = p->ourip; + dest->sin_addr = p->ourip.sin_addr; dest->sin_port = sin->sin_port; } if (needvideo) { @@ -6735,7 +6764,7 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, struct socka vdest->sin_addr = p->vredirip.sin_addr; vdest->sin_port = p->vredirip.sin_port; } else { - vdest->sin_addr = p->ourip; + vdest->sin_addr = p->ourip.sin_addr; vdest->sin_port = vsin->sin_port; } } @@ -6839,7 +6868,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest); if (debug) - ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port)); + ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port)); /* Ok, we need video. Let's add what we need for video and set codecs. Video is handled differently than audio since we can not transcode. */ @@ -6850,7 +6879,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) if (p->maxcallbitrate) snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate); if (debug) - ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port)); + ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(vsin.sin_port)); } /* Check if we need text in this call */ @@ -6873,13 +6902,13 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) tdest.sin_addr = p->tredirip.sin_addr; tdest.sin_port = p->tredirip.sin_port; } else { - tdest.sin_addr = p->ourip; + tdest.sin_addr = p->ourip.sin_addr; tdest.sin_port = tsin.sin_port; } ast_build_string(&m_text_next, &m_text_left, "m=text %d RTP/AVP", ntohs(tdest.sin_port)); - if (debug) - ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(tsin.sin_port)); + if (debug) /* XXX should I use tdest below ? */ + ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port)); } @@ -7206,10 +7235,10 @@ static void extract_uri(struct sip_pvt *p, struct sip_request *req) static void build_contact(struct sip_pvt *p) { /* Construct Contact: header */ - if (ourport != STANDARD_SIP_PORT) - ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip), ourport); + if (ntohs(p->ourip.sin_port) != STANDARD_SIP_PORT) + ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), ntohs(p->ourip.sin_port)); else - ast_string_field_build(p, our_contact, "<sip:%s%s%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip)); + ast_string_field_build(p, our_contact, "<sip:%s%s%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr)); } /*! \brief Build the Remote Party-ID & From using callingpres options */ @@ -7279,7 +7308,7 @@ static void build_rpid(struct sip_pvt *p) break; } - fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)); + fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)); snprintf(buf, sizeof(buf), "\"%s\" <sip:%s@%s>", clin, clid, fromdomain); if (send_pres_tags) @@ -7359,10 +7388,10 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho l = tmp2; } - if (ourport != STANDARD_SIP_PORT && ast_strlen_zero(p->fromdomain)) - snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)), ourport, p->tag); + if (ntohs(p->ourip.sin_port) != STANDARD_SIP_PORT && ast_strlen_zero(p->fromdomain)) + snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)), ntohs(p->ourip.sin_port), p->tag); else - snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)), p->tag); + snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)), p->tag); /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */ if (!ast_strlen_zero(p->fullcontact)) { @@ -7713,7 +7742,7 @@ static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, ast_build_string(&t, &maxbytes, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no"); ast_build_string(&t, &maxbytes, "Message-Account: sip:%s@%s\r\n", - S_OR(vmexten, default_vmexten), S_OR(p->fromdomain, ast_inet_ntoa(p->ourip))); + S_OR(vmexten, default_vmexten), S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr))); /* Cisco has a bug in the SIP stack where it can't accept the (0/0) notification. This can temporarily be disabled in sip.conf with the "buggymwi" option */ @@ -7898,7 +7927,7 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char * } else { /* Build callid for registration if we haven't registered before */ if (!r->callid_valid) { - build_callid_registry(r, __ourip, default_fromdomain); + build_callid_registry(r, internip.sin_addr, default_fromdomain); r->callid_valid = TRUE; } /* Allocate SIP packet for registration */ @@ -11208,6 +11237,8 @@ static int sip_show_settings(int fd, int argc, char *argv[]) msg = "Disabled, no localnet list"; else if (externip.sin_addr.s_addr == 0) msg = "Disabled, externip is 0.0.0.0"; + else if (stunaddr.sin_addr.s_addr != 0) + msg = "Enabled using STUN"; else if (!ast_strlen_zero(externhost)) msg = "Enabled using externhost"; else @@ -11216,7 +11247,7 @@ static int sip_show_settings(int fd, int argc, char *argv[]) ast_cli(fd, " Externhost: %s\n", S_OR(externhost, "<none>")); ast_cli(fd, " Externip: %s:%d\n", ast_inet_ntoa(externip.sin_addr), ntohs(externip.sin_port)); ast_cli(fd, " Externrefresh: %d\n", externrefresh); - ast_cli(fd, " Internal IP: %s:%d\n", ast_inet_ntoa(__ourip), ntohs(bindaddr.sin_port)); + ast_cli(fd, " Internal IP: %s:%d\n", ast_inet_ntoa(internip.sin_addr), ntohs(internip.sin_port)); { struct ast_ha *a; const char *prefix = "Localnet:"; @@ -11228,6 +11259,7 @@ static int sip_show_settings(int fd, int argc, char *argv[]) inet_ntop(AF_INET, &a->netmask, buf, sizeof(buf)) ); } } + ast_cli(fd, " STUN server: %s:%d\n", ast_inet_ntoa(stunaddr.sin_addr), ntohs(stunaddr.sin_port)); ast_cli(fd, "\nGlobal Signalling Settings:\n"); ast_cli(fd, "---------------------------\n"); @@ -11595,7 +11627,7 @@ static int sip_show_channel(int fd, int argc, char *argv[]) ast_cli(fd, " Received Address: %s:%d\n", ast_inet_ntoa(cur->recv.sin_addr), ntohs(cur->recv.sin_port)); ast_cli(fd, " SIP Transfer mode: %s\n", transfermode2str(cur->allowtransfer)); ast_cli(fd, " NAT Support: %s\n", nat2str(ast_test_flag(&cur->flags[0], SIP_NAT))); - ast_cli(fd, " Audio IP: %s %s\n", ast_inet_ntoa(cur->redirip.sin_addr.s_addr ? cur->redirip.sin_addr : cur->ourip), cur->redirip.sin_addr.s_addr ? "(Outside bridge)" : "(local)" ); + ast_cli(fd, " Audio IP: %s %s\n", ast_inet_ntoa(cur->redirip.sin_addr.s_addr ? cur->redirip.sin_addr : cur->ourip.sin_addr), cur->redirip.sin_addr.s_addr ? "(Outside bridge)" : "(local)" ); ast_cli(fd, " Our Tag: %s\n", cur->tag); ast_cli(fd, " Their Tag: %s\n", cur->theirtag); ast_cli(fd, " SIP User agent: %s\n", cur->useragent); @@ -17213,10 +17245,8 @@ static int reload_config(enum channelreloadreason reason) struct ast_variable *v; struct sip_peer *peer; struct sip_user *user; - struct ast_hostent ahp; char *cat, *stringp, *context, *oldregcontext; char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT]; - struct hostent *hp; struct ast_flags dummy[2]; int auto_sip_domains = FALSE; struct sockaddr_in old_bindaddr = bindaddr; @@ -17244,13 +17274,17 @@ static int reload_config(enum channelreloadreason reason) /* Reset IP addresses */ memset(&bindaddr, 0, sizeof(bindaddr)); + memset(&stunaddr, 0, sizeof(stunaddr)); + memset(&internip, 0, sizeof(internip)); + /* Free memory for local network address mask */ + ast_free_ha(localaddr); memset(&localaddr, 0, sizeof(localaddr)); memset(&externip, 0, sizeof(externip)); memset(&default_prefs, 0 , sizeof(default_prefs)); memset(&global_outboundproxy, 0, sizeof(struct sip_proxy)); global_outboundproxy.ip.sin_port = htons(STANDARD_SIP_PORT); global_outboundproxy.ip.sin_family = AF_INET; /* Type of address: IPv4 */ - ourport = STANDARD_SIP_PORT; + bindaddr.sin_port = htons(STANDARD_SIP_PORT); global_srvlookup = DEFAULT_SRVLOOKUP; global_tos_sip = DEFAULT_TOS_SIP; global_tos_audio = DEFAULT_TOS_AUDIO; @@ -17476,12 +17510,13 @@ static int reload_config(enum channelreloadreason reason) global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT; } else if (!strcasecmp(v->name, "registerattempts")) { global_regattempts_max = atoi(v->value); + } else if (!strcasecmp(v->name, "stunaddr")) { + stunaddr.sin_port = htons(3478); + if (ast_parse_arg(v->value, PARSE_INADDR, &stunaddr)) + ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", v->value); } else if (!strcasecmp(v->name, "bindaddr")) { - if (!(hp = ast_gethostbyname(v->value, &ahp))) { + if (ast_parse_arg(v->value, PARSE_INADDR, &bindaddr)) ast_log(LOG_WARNING, "Invalid address: %s\n", v->value); - } else { - memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr)); - } } else if (!strcasecmp(v->name, "localnet")) { struct ast_ha *na; int ha_error = 0; @@ -17493,17 +17528,13 @@ static int reload_config(enum channelreloadreason reason) if (ha_error) ast_log(LOG_ERROR, "Bad localnet configuration value line %d : %s\n", v->lineno, v->value); } else if (!strcasecmp(v->name, "externip")) { - if (!(hp = ast_gethostbyname(v->value, &ahp))) + if (ast_parse_arg(v->value, PARSE_INADDR, &externip)) ast_log(LOG_WARNING, "Invalid address for externip keyword: %s\n", v->value); - else - memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr)); externexpire = 0; } else if (!strcasecmp(v->name, "externhost")) { ast_copy_string(externhost, v->value, sizeof(externhost)); - if (!(hp = ast_gethostbyname(externhost, &ahp))) + if (ast_parse_arg(externhost, PARSE_INADDR, &externip)) ast_log(LOG_WARNING, "Invalid address for externhost keyword: %s\n", externhost); - else - memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr)); externexpire = time(NULL); } else if (!strcasecmp(v->name, "externrefresh")) { if (sscanf(v->value, "%d", &externrefresh) != 1) { @@ -17561,8 +17592,9 @@ static int reload_config(enum channelreloadreason reason) } else if (!strcasecmp(v->name, "cos_text")) { ast_str2cos(v->value, &global_cos_text); } else if (!strcasecmp(v->name, "bindport")) { - if (sscanf(v->value, "%d", &ourport) == 1) { - bindaddr.sin_port = htons(ourport); + int i; + if (sscanf(v->value, "%d", &i) == 1) { + bindaddr.sin_port = htons(i); } else { ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config); } @@ -17691,13 +17723,12 @@ static int reload_config(enum channelreloadreason reason) } } } - if (ast_find_ourip(&__ourip, bindaddr)) { + bindaddr.sin_family = AF_INET; + internip = bindaddr; + if (ast_find_ourip(&internip.sin_addr, bindaddr)) { ast_log(LOG_WARNING, "Unable to get own IP address, SIP disabled\n"); return 0; } - if (!ntohs(bindaddr.sin_port)) - bindaddr.sin_port = ntohs(STANDARD_SIP_PORT); - bindaddr.sin_family = AF_INET; ast_mutex_lock(&netlock); if ((sipsock > -1) && (memcmp(&old_bindaddr, &bindaddr, sizeof(struct sockaddr_in)))) { close(sipsock); @@ -17728,7 +17759,14 @@ static int reload_config(enum channelreloadreason reason) if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "SIP Listening on %s:%d\n", ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port)); - + if (stunaddr.sin_addr.s_addr != 0) { + ast_debug(1, "stun to %s:%d\n", + ast_inet_ntoa(stunaddr.sin_addr) , ntohs(stunaddr.sin_port)); + ast_stun_request(sipsock, &stunaddr, + NULL, &externip); + ast_debug(1, "STUN sees us at %s:%d\n", + ast_inet_ntoa(externip.sin_addr) , ntohs(externip.sin_port)); + } ast_netsock_set_qos(sipsock, global_tos_sip, global_cos_sip); } } @@ -17805,10 +17843,10 @@ static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl) memset(&p->udptlredirip, 0, sizeof(p->udptlredirip)); if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { if (!p->pendinginvite) { - ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip), udptl ? ntohs(p->udptlredirip.sin_port) : 0); + ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip.sin_addr), udptl ? ntohs(p->udptlredirip.sin_port) : 0); transmit_reinvite_with_sdp(p, TRUE); } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip), udptl ? ntohs(p->udptlredirip.sin_port) : 0); + ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip.sin_addr), udptl ? ntohs(p->udptlredirip.sin_port) : 0); ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); } } @@ -17860,13 +17898,13 @@ static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt if (flag) ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port)); else - ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip)); + ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr)); transmit_reinvite_with_sdp(p, TRUE); } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { if (flag) ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port)); else - ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip)); + ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr)); ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); } } @@ -17884,7 +17922,7 @@ static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt if (flag) ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port)); else - ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip)); + ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr)); pvt->t38.state = T38_ENABLED; p->t38.state = T38_ENABLED; ast_debug(2, "T38 changed state to %d on channel %s\n", pvt->t38.state, pvt->owner ? pvt->owner->name : "<none>"); @@ -18038,12 +18076,12 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struc if (chan->_state != AST_STATE_UP) { /* We are in early state */ if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); - ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); + ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ - ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); + ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); transmit_reinvite_with_sdp(p, FALSE); } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); + ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); /* We have a pending Invite. Send re-invite when we're done with the invite */ ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); } diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 6954843ae..842c7b513 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -293,41 +293,92 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; Default is 0 tries, continue forever ;----------------------------------------- NAT SUPPORT ------------------------ -; The externip, externhost and localnet settings are used if you use Asterisk -; behind a NAT device to communicate with services on the outside. - -;externip = 200.201.202.203 ; Address that we're going to put in outbound SIP - ; messages if we're behind a NAT - - ; The externip and localnet is used - ; when registering and communicating with other proxies - ; that we're registered with -;externhost=foo.dyndns.net ; Alternatively you can specify an - ; external host, and Asterisk will - ; perform DNS queries periodically. Not - ; recommended for production - ; environments! Use externip instead -;externrefresh=10 ; How often to refresh externhost if - ; used - ; You may add multiple local networks. A reasonable - ; set of defaults are: -;localnet=192.168.0.0/255.255.0.0; All RFC 1918 addresses are local networks -;localnet=10.0.0.0/255.0.0.0 ; Also RFC1918 -;localnet=172.16.0.0/12 ; Another RFC1918 with CIDR notation -;localnet=169.254.0.0/255.255.0.0 ;Zero conf local network - -; The nat= setting is used when Asterisk is on a public IP, communicating with -; devices hidden behind a NAT device (broadband router). If you have one-way -; audio problems, you usually have problems with your NAT configuration or your -; firewall's support of SIP+RTP ports. You configure Asterisk choice of RTP -; ports for incoming audio in rtp.conf -; -;nat=no ; Global NAT settings (Affects all peers and users) - ; yes = Always ignore info and assume NAT - ; no = Use NAT mode only according to RFC3581 (;rport) - ; never = Never attempt NAT mode or RFC3581 support - ; route = Assume NAT, don't send rport - ; (work around more UNIDEN bugs) +; +; WARNING: SIP operation behind a NAT is tricky and you really need +; to read and understand well the following section. +; +; When Asterisk is behind a NAT device, the "local" address (and port) that +; a socket is bound to has different values when seen from the inside or +; from the outside of the NATted network. Unfortunately this address must +; be communicated to the outside (e.g. in SIP and SDP messages), and in +; order to determine the correct value Asterisk needs to know: +; +; + whether it is talking to someone "inside" or "outside" of the NATted network. +; This is configured by assigning the "localnet" parameter with a list +; of network addresses that are considered "inside" of the NATted network. +; IF LOCALNET IS NOT SET, THE EXTERNAL ADDRESS WILL NOT BE SET CORRECTLY. +; Multiple entries are allowed, e.g. a reasonable set is the following: +; +; localnet=192.168.0.0/255.255.0.0 ; RFC 1918 addresses +; localnet=10.0.0.0/255.0.0.0 ; Also RFC1918 +; localnet=172.16.0.0/12 ; Another RFC1918 with CIDR notation +; localnet=169.254.0.0/255.255.0.0 ; Zero conf local network +; +; + the "externally visible" address and port number to be used when talking +; to a host outside the NAT. This information is derived by one of the +; following (mutually exclusive) config file parameters: +; +; a. "externip = hostname[:port]" specifies a static address[:port] to +; be used in SIP and SDP messages. +; The hostname is looked up only once, when [re]loading sip.conf . +; If a port number is not present, use the "bindport" value (which is +; not guaranteed to work correctly, because a NAT box might remap the +; port number as well as the address). +; This approach can be useful if you have a NAT device where you can +; configure the mapping statically. Examples: +; +; externip = 12.34.56.78 ; use this address. +; externip = 12.34.56.78:9900 ; use this address and port. +; externip = mynat.my.org:12600 ; Public address of my nat box. +; +; b. "externhost = hostname[:port]" is similar to "externip" except +; that the hostname is looked up every "externrefresh" seconds +; (default 10s). This can be useful when your NAT device lets you choose +; the port mapping, but the IP address is dynamic. +; Beware, you might suffer from service disruption when the name server +; resolution fails. Examples: +; +; externhost=foo.dyndns.net ; refreshed periodically +; externrefresh=180 ; change the refresh interval +; +; c. "stunaddr = stun.server[:port]" queries the STUN server specified +; as an argument to obtain the external address/port. +; Queries are also sent periodically every "externrefresh" seconds +; (as a side effect, sending the query also acts as a keepalive for +; the state entry on the nat box): +; +; stunaddr = foo.stun.com:3478 +; externrefresh = 15 +; +; Note that at the moment all these mechanism work only for the SIP socket. +; The IP address discovered with externip/externhost/STUN is reused for +; media sessions as well, but the port numbers are not remapped so you +; may still experience problems. +; +; NOTE 1: in some cases, NAT boxes will use different port numbers in +; the internal<->external mapping. In these cases, the "externip" and +; "externhost" might not help you configure addresses properly, and you +; really need to use STUN. +; +; NOTE 2: when using "externip" or "externhost", the address part is +; also used as the external address for media sessions. +; If you use "stunaddr", STUN queries will be sent to the same server +; also from media sockets, and this should permit a correct mapping of +; the port numbers as well. +; +; In addition to the above, Asterisk has an additional "nat" parameter to +; address NAT-related issues in incoming SIP or media sessions. +; In particular, depending on the 'nat= ' settings described below, Asterisk +; may override the address/port information specified in the SIP/SDP messages, +; and use the information (sender address) supplied by the network stack instead. +; However, this is only useful if the external traffic can reach us. +; The following settings are allowed (both globally and in individual sections): +; +; nat = no ; default. Use NAT mode only according to RFC3581 (;rport) +; nat = yes ; Always ignore info and assume NAT +; nat = never ; Never attempt NAT mode or RFC3581 support +; nat = route ; route = Assume NAT, don't send rport +; ; (work around more UNIDEN bugs) ;----------------------------------- MEDIA HANDLING -------------------------------- ; By default, Asterisk tries to re-invite the audio to an optimal path. If there's |