Add auto_force_rport and auto_comedia NAT options

This patch adds the auto_force_rport and auto_comedia NAT options. It
also converts the nat= setting to a list of comma-separated combinable
options: no, force_rport, comedia, auto_force_rport, and auto_comedia.
nat=yes remains as an undocumented option equal to
"force_rport,comedia". The first instance of 'yes' or 'no' in the list
stops parsing and overrides any previously set options. If an auto_*
option is specified with its non-auto_ counterpart, the auto setting
takes precedence.

This patch builds upon the patch posted to ASTERISK-17860 by JIRA user
pedro-garcia.

(closes issue ASTERISK-17860)
Review: https://reviewboard.asterisk.org/r/1698/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@354597 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Terry Wilson
2012-02-09 18:14:39 +00:00
parent 8f5c33f95a
commit e5c51ee44c
8 changed files with 286 additions and 30 deletions

12
CHANGES
View File

@@ -59,6 +59,12 @@ SIP Changes
callbackextension options, incoming requests that are matched by address callbackextension options, incoming requests that are matched by address
will be matched to the peer with the matching callbackextension if it is will be matched to the peer with the matching callbackextension if it is
available. available.
* NAT settings are now a combinable list of options. The equivalent of the
deprecated nat=yes is nat=force_rport,comedia. nat=no behaves as before.
* Two new NAT options, auto_force_rport and auto_comedia, have been added
which set the force_rport and comedia options automatically if Asterisk
detects that an incoming SIP request crossed a NAT after being sent by
the remote endpoint.
Chan_local changes Chan_local changes
------------------ ------------------
@@ -125,6 +131,12 @@ AMI (Asterisk Manager Interface) changes
if the i(variable) option is used. StopMixMonitor will accept MixMonitorID as if the i(variable) option is used. StopMixMonitor will accept MixMonitorID as
on option to close specific MixMonitors. on option to close specific MixMonitors.
* The SIPshowpeer manager action response field "SIP-Forcerport" has been updated
to include information about peers configured with nat=auto_force_rport by
returning "A" if auto_force_rport is set and nat is detected, and "a" if it is
set and nat is not detected. "Y" and "N" are still returned if auto_force_rport
is not enabled.
FAX changes FAX changes
----------- -----------
* FAXOPT(faxdetect) will enable a generic fax detect framehook for dialplan * FAXOPT(faxdetect) will enable a generic fax detect framehook for dialplan

View File

@@ -15276,6 +15276,21 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name);
res = AUTH_PEER_NOT_DYNAMIC; res = AUTH_PEER_NOT_DYNAMIC;
} else { } else {
if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
if (p->natdetected) {
ast_set_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT);
} else {
ast_clear_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT);
}
}
if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
if (p->natdetected) {
ast_set_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP);
} else {
ast_clear_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP);
}
}
ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT_FORCE_RPORT); ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT_FORCE_RPORT);
if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri2, XMIT_UNRELIABLE, req->ignore))) { if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri2, XMIT_UNRELIABLE, req->ignore))) {
if (sip_cancel_destroy(p)) if (sip_cancel_destroy(p))
@@ -16354,6 +16369,30 @@ static void check_via(struct sip_pvt *p, struct sip_request *req)
ast_sockaddr_set_port(&p->sa, ast_sockaddr_set_port(&p->sa,
port != 0 ? port : STANDARD_SIP_PORT); port != 0 ? port : STANDARD_SIP_PORT);
/* Check and see if the requesting UA is likely to be behind a NAT. If they are, set the
* natdetected flag so that later, peers with nat=auto_* can use the value. Also
* set the flags so that Asterisk responds identically whether or not a peer exists
* so as not to leak peer name information. */
if (ast_sockaddr_cmp(&tmp, &p->recv)) {
char *tmp_str = ast_strdupa(ast_sockaddr_stringify(&tmp));
ast_debug(3, "NAT detected for %s / %s\n", tmp_str, ast_sockaddr_stringify(&p->recv));
p->natdetected = 1;
if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
ast_set_flag(&p->flags[0], SIP_NAT_FORCE_RPORT);
}
if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
ast_set_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
}
} else {
p->natdetected = 0;
if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
ast_clear_flag(&p->flags[0], SIP_NAT_FORCE_RPORT);
}
if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
ast_clear_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
}
}
if (sip_debug_test_pvt(p)) { if (sip_debug_test_pvt(p)) {
ast_verbose("Sending to %s (%s)\n", ast_verbose("Sending to %s (%s)\n",
ast_sockaddr_stringify(sip_real_dst(p)), ast_sockaddr_stringify(sip_real_dst(p)),
@@ -17437,7 +17476,9 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
snprintf(srch, sizeof(srch), FORMAT2, name, snprintf(srch, sizeof(srch), FORMAT2, name,
tmp_host, tmp_host,
peer->host_dynamic ? " D " : " ", /* Dynamic or not? */ peer->host_dynamic ? " D " : " ", /* Dynamic or not? */
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " N " : " ", /* NAT=yes? */ ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " A " : " a " :
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " N " : " ", /* NAT=yes? */
peer->ha ? " A " : " ", /* permit/deny */ peer->ha ? " A " : " ", /* permit/deny */
tmp_port, status, tmp_port, status,
peer->description ? peer->description : "", peer->description ? peer->description : "",
@@ -17447,7 +17488,9 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
ast_cli(fd, FORMAT2, name, ast_cli(fd, FORMAT2, name,
tmp_host, tmp_host,
peer->host_dynamic ? " D " : " ", /* Dynamic or not? */ peer->host_dynamic ? " D " : " ", /* Dynamic or not? */
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " N " : " ", /* NAT=yes? */ ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " A " : " a " :
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " N " : " ", /* NAT=yes? */
peer->ha ? " A " : " ", /* permit/deny */ peer->ha ? " A " : " ", /* permit/deny */
tmp_port, status, tmp_port, status,
peer->description ? peer->description : "", peer->description ? peer->description : "",
@@ -17462,7 +17505,10 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
"IPaddress: %s\r\n" "IPaddress: %s\r\n"
"IPport: %s\r\n" "IPport: %s\r\n"
"Dynamic: %s\r\n" "Dynamic: %s\r\n"
"AutoForcerport: %s\r\n"
"Forcerport: %s\r\n" "Forcerport: %s\r\n"
"AutoComedia: %s\r\n"
"Comedia: %s\r\n"
"VideoSupport: %s\r\n" "VideoSupport: %s\r\n"
"TextSupport: %s\r\n" "TextSupport: %s\r\n"
"ACL: %s\r\n" "ACL: %s\r\n"
@@ -17474,7 +17520,10 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
ast_sockaddr_isnull(&peer->addr) ? "-none-" : tmp_host, ast_sockaddr_isnull(&peer->addr) ? "-none-" : tmp_host,
ast_sockaddr_isnull(&peer->addr) ? "0" : tmp_port, ast_sockaddr_isnull(&peer->addr) ? "0" : tmp_port,
peer->host_dynamic ? "yes" : "no", /* Dynamic or not? */ peer->host_dynamic ? "yes" : "no", /* Dynamic or not? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ? "yes" : "no",
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "yes" : "no", /* NAT=yes? */ ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "yes" : "no", /* NAT=yes? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) ? "yes" : "no",
ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "yes" : "no",
ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */ ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */
ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */ ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */
peer->ha ? "yes" : "no", /* permit/deny */ peer->ha ? "yes" : "no", /* permit/deny */
@@ -18142,7 +18191,8 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
ast_cli(fd, " MaxCallBR : %d kbps\n", peer->maxcallbitrate); ast_cli(fd, " MaxCallBR : %d kbps\n", peer->maxcallbitrate);
ast_cli(fd, " Expire : %ld\n", ast_sched_when(sched, peer->expire)); ast_cli(fd, " Expire : %ld\n", ast_sched_when(sched, peer->expire));
ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE))); ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
ast_cli(fd, " Force rport : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT))); ast_cli(fd, " Force rport : %s\n", force_rport_string(peer->flags));
ast_cli(fd, " Symmetric RTP: %s\n", comedia_string(peer->flags));
ast_cli(fd, " ACL : %s\n", AST_CLI_YESNO(peer->ha != NULL)); ast_cli(fd, " ACL : %s\n", AST_CLI_YESNO(peer->ha != NULL));
ast_cli(fd, " DirectMedACL : %s\n", AST_CLI_YESNO(peer->directmediaha != NULL)); ast_cli(fd, " DirectMedACL : %s\n", AST_CLI_YESNO(peer->directmediaha != NULL));
ast_cli(fd, " T.38 support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT))); ast_cli(fd, " T.38 support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
@@ -18254,7 +18304,12 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "")); astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, ""));
astman_append(s, "RegExpire: %ld seconds\r\n", ast_sched_when(sched, peer->expire)); astman_append(s, "RegExpire: %ld seconds\r\n", ast_sched_when(sched, peer->expire));
astman_append(s, "SIP-AuthInsecure: %s\r\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE))); astman_append(s, "SIP-AuthInsecure: %s\r\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
astman_append(s, "SIP-Forcerport: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT)?"Y":"N")); astman_append(s, "SIP-Forcerport: %s\r\n", ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
(ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "A" : "a") :
(ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "Y" : "N"));
astman_append(s, "SIP-Comedia: %s\r\n", ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) ?
(ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "A" : "a") :
(ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "Y" : "N"));
astman_append(s, "ACL: %s\r\n", (peer->ha?"Y":"N")); astman_append(s, "ACL: %s\r\n", (peer->ha?"Y":"N"));
astman_append(s, "SIP-CanReinvite: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N")); astman_append(s, "SIP-CanReinvite: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N"));
astman_append(s, "SIP-DirectMedia: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N")); astman_append(s, "SIP-DirectMedia: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N"));
@@ -18866,7 +18921,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
ast_cli(a->fd, " Context: %s\n", sip_cfg.default_context); ast_cli(a->fd, " Context: %s\n", sip_cfg.default_context);
ast_cli(a->fd, " Record on feature: %s\n", sip_cfg.default_record_on_feature); ast_cli(a->fd, " Record on feature: %s\n", sip_cfg.default_record_on_feature);
ast_cli(a->fd, " Record off feature: %s\n", sip_cfg.default_record_off_feature); ast_cli(a->fd, " Record off feature: %s\n", sip_cfg.default_record_off_feature);
ast_cli(a->fd, " Force rport: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_NAT_FORCE_RPORT))); ast_cli(a->fd, " Force rport: %s\n", force_rport_string(global_flags));
ast_cli(a->fd, " DTMF: %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF))); ast_cli(a->fd, " DTMF: %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF)));
ast_cli(a->fd, " Qualify: %d\n", default_qualify); ast_cli(a->fd, " Qualify: %d\n", default_qualify);
ast_cli(a->fd, " Use ClientCode: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USECLIENTCODE))); ast_cli(a->fd, " Use ClientCode: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USECLIENTCODE)));
@@ -19237,7 +19292,7 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a
ast_cli(a->fd, " Theoretical Address: %s\n", ast_sockaddr_stringify(&cur->sa)); ast_cli(a->fd, " Theoretical Address: %s\n", ast_sockaddr_stringify(&cur->sa));
ast_cli(a->fd, " Received Address: %s\n", ast_sockaddr_stringify(&cur->recv)); ast_cli(a->fd, " Received Address: %s\n", ast_sockaddr_stringify(&cur->recv));
ast_cli(a->fd, " SIP Transfer mode: %s\n", transfermode2str(cur->allowtransfer)); ast_cli(a->fd, " SIP Transfer mode: %s\n", transfermode2str(cur->allowtransfer));
ast_cli(a->fd, " Force rport: %s\n", AST_CLI_YESNO(ast_test_flag(&cur->flags[0], SIP_NAT_FORCE_RPORT))); ast_cli(a->fd, " Force rport: %s\n", force_rport_string(cur->flags));
if (ast_sockaddr_isnull(&cur->redirip)) { if (ast_sockaddr_isnull(&cur->redirip)) {
ast_cli(a->fd, ast_cli(a->fd,
" Audio IP: %s (local)\n", " Audio IP: %s (local)\n",
@@ -27692,8 +27747,8 @@ static int handle_t38_options(struct ast_flags *flags, struct ast_flags *mask, s
/*! /*!
\brief Handle flag-type options common to configuration of devices - peers \brief Handle flag-type options common to configuration of devices - peers
\param flags array of two struct ast_flags \param flags array of three struct ast_flags
\param mask array of two struct ast_flags \param mask array of three struct ast_flags
\param v linked list of config variables to process \param v linked list of config variables to process
\returns non-zero if any config options were handled, zero otherwise \returns non-zero if any config options were handled, zero otherwise
*/ */
@@ -27743,19 +27798,7 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
ast_set_flag(&flags[0], SIP_DTMF_RFC2833); ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
} }
} else if (!strcasecmp(v->name, "nat")) { } else if (!strcasecmp(v->name, "nat")) {
ast_set_flag(&mask[0], SIP_NAT_FORCE_RPORT); sip_parse_nat_option(v->value, mask, flags);
ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT); /* Default to "force_rport" */
if (!strcasecmp(v->value, "no")) {
ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
} else if (!strcasecmp(v->value, "yes")) {
/* We've already defaulted to force_rport */
ast_set_flag(&mask[1], SIP_PAGE2_SYMMETRICRTP);
ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
} else if (!strcasecmp(v->value, "comedia")) {
ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
ast_set_flag(&mask[1], SIP_PAGE2_SYMMETRICRTP);
ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
}
} else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) { } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
ast_set_flag(&mask[0], SIP_REINVITE); ast_set_flag(&mask[0], SIP_REINVITE);
ast_clear_flag(&flags[0], SIP_REINVITE); ast_clear_flag(&flags[0], SIP_REINVITE);
@@ -28969,7 +29012,7 @@ static int reload_config(enum channelreloadreason reason)
struct sip_peer *peer; struct sip_peer *peer;
char *cat, *stringp, *context, *oldregcontext; char *cat, *stringp, *context, *oldregcontext;
char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT]; char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT];
struct ast_flags dummy[2]; struct ast_flags dummy[3];
struct ast_flags config_flags = { reason == CHANNEL_MODULE_LOAD ? 0 : ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) ? 0 : CONFIG_FLAG_FILEUNCHANGED }; struct ast_flags config_flags = { reason == CHANNEL_MODULE_LOAD ? 0 : ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) ? 0 : CONFIG_FLAG_FILEUNCHANGED };
int auto_sip_domains = FALSE; int auto_sip_domains = FALSE;
struct ast_sockaddr old_bindaddr = bindaddr; struct ast_sockaddr old_bindaddr = bindaddr;
@@ -29164,7 +29207,7 @@ static int reload_config(enum channelreloadreason reason)
ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten)); ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten));
ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */ ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */
ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA); /*!< Allow re-invites */ ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA); /*!< Allow re-invites */
ast_set_flag(&global_flags[0], SIP_NAT_FORCE_RPORT); /*!< Default to nat=force_rport */ ast_set_flag(&global_flags[2], SIP_PAGE3_NAT_AUTO_RPORT); /*!< Default to nat=auto_force_rport */
ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine)); ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine));
ast_copy_string(default_parkinglot, DEFAULT_PARKINGLOT, sizeof(default_parkinglot)); ast_copy_string(default_parkinglot, DEFAULT_PARKINGLOT, sizeof(default_parkinglot));

View File

@@ -765,11 +765,128 @@ AST_TEST_DEFINE(sip_parse_host_line_test)
} }
/*! \brief Parse the comma-separated nat= option values */
void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_flags *flags)
{
char *parse, *this;
if (!(parse = ast_strdupa(value))) {
return;
}
/* Since we need to completely override the general settings if we are being called
* later for a peer, always set the flags for all options on the mask */
ast_set_flag(&mask[0], SIP_NAT_FORCE_RPORT);
ast_set_flag(&mask[1], SIP_PAGE2_SYMMETRICRTP);
ast_set_flag(&mask[2], SIP_PAGE3_NAT_AUTO_RPORT);
ast_set_flag(&mask[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
while ((this = strsep(&parse, ","))) {
if (ast_false(this)) {
ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
ast_clear_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
break; /* It doesn't make sense to have no + something else */
} else if (!strcasecmp(this, "yes")) {
ast_log(LOG_WARNING, "nat=yes is deprecated, use nat=force_rport,comedia instead\n");
ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
break; /* It doesn't make sense to have yes + something else */
} else if (!strcasecmp(this, "force_rport") && !ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
} else if (!strcasecmp(this, "comedia") && !ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
} else if (!strcasecmp(this, "auto_force_rport")) {
ast_set_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
/* In case someone did something dumb like nat=force_rport,auto_force_rport */
ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
} else if (!strcasecmp(this, "auto_comedia")) {
ast_set_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
/* In case someone did something dumb like nat=comedia,auto_comedia*/
ast_clear_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
}
}
}
#define TEST_FORCE_RPORT 1 << 0
#define TEST_COMEDIA 1 << 1
#define TEST_AUTO_FORCE_RPORT 1 << 2
#define TEST_AUTO_COMEDIA 1 << 3
static int match_nat_options(int val, struct ast_flags *flags)
{
if ((!ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT)) != !(val & TEST_FORCE_RPORT)) {
return 0;
}
if (!ast_test_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP) != !(val & TEST_COMEDIA)) {
return 0;
}
if (!ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT) != !(val & TEST_AUTO_FORCE_RPORT)) {
return 0;
}
if (!ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) != !(val & TEST_AUTO_COMEDIA)) {
return 0;
}
return 1;
}
AST_TEST_DEFINE(sip_parse_nat_test)
{
int i, res = AST_TEST_PASS;
struct ast_flags mask[3] = {{0}}, flags[3] = {{0}};
struct {
const char *str;
int i;
} options[] = {
{ "yes", TEST_FORCE_RPORT | TEST_COMEDIA },
{ "no", 0 },
{ "force_rport", TEST_FORCE_RPORT },
{ "comedia", TEST_COMEDIA },
{ "auto_force_rport", TEST_AUTO_FORCE_RPORT },
{ "auto_comedia", TEST_AUTO_COMEDIA },
{ "force_rport,auto_force_rport", TEST_AUTO_FORCE_RPORT },
{ "auto_force_rport,force_rport", TEST_AUTO_FORCE_RPORT },
{ "comedia,auto_comedia", TEST_AUTO_COMEDIA },
{ "auto_comedia,comedia", TEST_AUTO_COMEDIA },
{ "force_rport,comedia", TEST_FORCE_RPORT | TEST_COMEDIA },
{ "force_rport,auto_comedia", TEST_FORCE_RPORT | TEST_AUTO_COMEDIA },
{ "force_rport,yes,no", TEST_FORCE_RPORT | TEST_COMEDIA },
{ "auto_comedia,no,yes", 0 },
};
switch (cmd) {
case TEST_INIT:
info->name = "sip_parse_nat_test";
info->category = "/channels/chan_sip/";
info->summary = "tests sip.conf nat line parsing";
info->description =
"Tests parsing of various nat line configurations. "
"Verifies output matches expected behavior.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
for (i = 0; i < ARRAY_LEN(options); i++) {
sip_parse_nat_option(options[i].str, mask, flags);
if (!match_nat_options(options[i].i, flags)) {
ast_test_status_update(test, "Failed nat=%s\n", options[i].str);
res = AST_TEST_FAIL;
}
memset(flags, 0, sizeof(flags));
memset(mask, 0, sizeof(mask));
}
return res;
}
/*! \brief SIP test registration */ /*! \brief SIP test registration */
void sip_config_parser_register_tests(void) void sip_config_parser_register_tests(void)
{ {
AST_TEST_REGISTER(sip_parse_register_line_test); AST_TEST_REGISTER(sip_parse_register_line_test);
AST_TEST_REGISTER(sip_parse_host_line_test); AST_TEST_REGISTER(sip_parse_host_line_test);
AST_TEST_REGISTER(sip_parse_nat_test);
} }
/*! \brief SIP test registration */ /*! \brief SIP test registration */
@@ -777,5 +894,6 @@ void sip_config_parser_unregister_tests(void)
{ {
AST_TEST_UNREGISTER(sip_parse_register_line_test); AST_TEST_UNREGISTER(sip_parse_register_line_test);
AST_TEST_UNREGISTER(sip_parse_host_line_test); AST_TEST_UNREGISTER(sip_parse_host_line_test);
AST_TEST_UNREGISTER(sip_parse_nat_test);
} }

View File

@@ -43,6 +43,18 @@ int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const
*/ */
int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum sip_transport *transport); int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum sip_transport *transport);
/*! \brief Parse the comma-separated nat= option values
* \param value The comma-separated value
* \param mask An array of ast_flags that will be set by this function
* and used as a mask for copying the flags later
* \param flags An array of ast_flags that will be set by this function
*
* \note The nat-related values in both mask and flags are assumed to empty. This function
* will treat the first "yes" or "no" value in a list of values as overiding all other values
* and will stop parsing. Auto values will override their non-auto counterparts.
*/
void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_flags *flags);
/*! /*!
* \brief register config parsing tests * \brief register config parsing tests
*/ */

View File

@@ -363,9 +363,11 @@
#define SIP_PAGE3_SNOM_AOC (1 << 0) /*!< DPG: Allow snom aoc messages */ #define SIP_PAGE3_SNOM_AOC (1 << 0) /*!< DPG: Allow snom aoc messages */
#define SIP_PAGE3_SRTP_TAG_32 (1 << 1) /*!< DP: Use a 32bit auth tag in INVITE not 80bit */ #define SIP_PAGE3_SRTP_TAG_32 (1 << 1) /*!< DP: Use a 32bit auth tag in INVITE not 80bit */
#define SIP_PAGE3_NAT_AUTO_RPORT (1 << 2) /*!< DGP: Set SIP_NAT_FORCE_RPORT when NAT is detected */
#define SIP_PAGE3_NAT_AUTO_COMEDIA (1 << 3) /*!< DGP: Set SIP_PAGE2_SYMMETRICRTP when NAT is detected */
#define SIP_PAGE3_FLAGS_TO_COPY \ #define SIP_PAGE3_FLAGS_TO_COPY \
(SIP_PAGE3_SNOM_AOC | SIP_PAGE3_SRTP_TAG_32) (SIP_PAGE3_SNOM_AOC | SIP_PAGE3_SRTP_TAG_32 | SIP_PAGE3_NAT_AUTO_RPORT | SIP_PAGE3_NAT_AUTO_COMEDIA)
#define CHECK_AUTH_BUF_INITLEN 256 #define CHECK_AUTH_BUF_INITLEN 256
@@ -1062,6 +1064,7 @@ struct sip_pvt {
* for incoming calls * for incoming calls
*/ */
unsigned short req_secure_signaling:1;/*!< Whether we are required to have secure signaling or not */ unsigned short req_secure_signaling:1;/*!< Whether we are required to have secure signaling or not */
unsigned short natdetected:1; /*!< Whether we detected a NAT when processing the Via */
char tag[11]; /*!< Our tag for this session */ char tag[11]; /*!< Our tag for this session */
int timer_t1; /*!< SIP timer T1, ms rtt */ int timer_t1; /*!< SIP timer T1, ms rtt */
int timer_b; /*!< SIP timer B, ms */ int timer_b; /*!< SIP timer B, ms */

View File

@@ -80,4 +80,10 @@ int hangup_sip2cause(int cause);
*/ */
const char *hangup_cause2sip(int cause); const char *hangup_cause2sip(int cause);
/*! \brief Return a string describing the force_rport value for the given flags */
const char *force_rport_string(struct ast_flags *flags);
/*! \brief Return a string describing the comedia value for the given flags */
const char *comedia_string(struct ast_flags *flags);
#endif #endif

45
channels/sip/utils.c Normal file
View File

@@ -0,0 +1,45 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2012, Digium, Inc.
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief Utility functions for chan_sip
*
* \author Terry Wilson <twilson@digium.com>
*/
#include "asterisk.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "include/sip.h"
#include "include/sip_utils.h"
const char *force_rport_string(struct ast_flags *flags)
{
if (ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
return ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT) ? "Auto (Yes)" : "Auto (No)";
}
return AST_CLI_YESNO(ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT));
}
const char *comedia_string(struct ast_flags *flags)
{
if (ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
return ast_test_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP) ? "Auto (Yes)" : "Auto (No)";
}
return AST_CLI_YESNO(ast_test_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP));
}

View File

@@ -831,17 +831,34 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; However, this is only useful if the external traffic can reach us. ; However, this is only useful if the external traffic can reach us.
; The following settings are allowed (both globally and in individual sections): ; The following settings are allowed (both globally and in individual sections):
; ;
; nat = no ; Use rport if the remote side says to use it. ; nat = no ; Do no special NAT handling other than RFC3581
; nat = force_rport ; Force rport to always be on. (default) ; nat = force_rport ; Pretend there was an rport parameter even if there wasn't
; nat = yes ; Force rport to always be on and perform comedia RTP handling. ; nat = comedia ; Send media to the port Asterisk received it from regardless
; nat = comedia ; Use rport if the remote side says to use it and perform comedia RTP handling. ; ; of where the SDP says to send it.
; nat = auto_force_rport ; Set the force_rport option if Asterisk detects NAT (default)
; nat = auto_comedia ; Set the comedia option if Asterisk detects NAT
;
; The nat settings can be combined. For example, to set both force_rport and comedia
; one would set nat=force_rport,comedia. If any of the comma-separated options is 'no',
; Asterisk will ignore any other settings and set nat=no. If one of the "auto" settings
; is used in conjunction with its non-auto counterpart (nat=comedia,auto_comedia), then
; the non-auto option will be ignored.
;
; The RFC 3581-defined 'rport' parameter allows a client to request that Asterisk send
; SIP responses to it via the source IP and port from which the request originated
; instead of the address/port listed in the top-most Via header. This is useful if a
; client knows that it is behind a NAT and therefore cannot guess from what address/port
; its request will be sent. Asterisk will always honor the 'rport' parameter if it is
; sent. The force_rport setting causes Asterisk to always send responses back to the
; address/port from which it received requests; even if the other side doesn't support
; adding the 'rport' parameter.
; ;
; 'comedia RTP handling' refers to the technique of sending RTP to the port that the ; 'comedia RTP handling' refers to the technique of sending RTP to the port that the
; the other endpoint's RTP arrived from, and means 'connection-oriented media'. This is ; the other endpoint's RTP arrived from, and means 'connection-oriented media'. This is
; only partially related to RFC 4145 which was referred to as COMEDIA while it was in ; only partially related to RFC 4145 which was referred to as COMEDIA while it was in
; draft form. This method is used to accomodate endpoints that may be located behind ; draft form. This method is used to accomodate endpoints that may be located behind
; NAT devices, and as such the port number they tell Asterisk to send RTP packets to ; NAT devices, and as such the address/port they tell Asterisk to send RTP packets to
; for their media streams is not actual port number that will be used on the nearer ; for their media streams is not the actual address/port that will be used on the nearer
; side of the NAT. ; side of the NAT.
; ;
; IT IS IMPORTANT TO NOTE that if the nat setting in the general section differs from ; IT IS IMPORTANT TO NOTE that if the nat setting in the general section differs from