static char default_notifymime[AST_MAX_EXTENSION]; /*!< Default MIME media type for MWI notify messages */
static char default_vmexten[AST_MAX_EXTENSION]; /*!< Default From Username on MWI updates */
static int default_qualify; /*!< Default Qualify= setting */
+static int default_keepalive; /*!< Default keepalive= setting */
static char default_mohinterpret[MAX_MUSICCLASS]; /*!< Global setting for moh class to use when put on hold */
static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting
* a bridged channel on hold */
static void sip_peer_hold(struct sip_pvt *p, int hold);
static void mwi_event_cb(const struct ast_event *, void *);
static void network_change_event_cb(const struct ast_event *, void *);
+static void sip_keepalive_all_peers(void);
/*--- Applications, functions, CLI and manager command helpers */
static const char *sip_nat_mode(const struct sip_pvt *p);
AST_SCHED_DEL_UNREF(sched, peer->expire,
sip_unref_peer(peer, "remove register expire ref"));
}
+ if (peer->keepalivesend != -1) {
+ AST_SCHED_DEL_UNREF(sched, peer->keepalivesend,
+ sip_unref_peer(peer, "remove keepalive peer ref"));
+ }
}
typedef enum {
ast_cli(fd, " Useragent : %s\n", peer->useragent);
ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact);
ast_cli(fd, " Qualify Freq : %d ms\n", peer->qualifyfreq);
+ ast_cli(fd, " Keepalive : %d ms\n", peer->keepalive * 1000);
if (peer->chanvars) {
ast_cli(fd, " Variables :\n");
for (v = peer->chanvars ; v ; v = v->next)
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, " Qualify: %d\n", default_qualify);
+ ast_cli(a->fd, " Keepalive: %d\n", default_keepalive);
ast_cli(a->fd, " Use ClientCode: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USECLIENTCODE)));
ast_cli(a->fd, " Progress inband: %s\n", (ast_test_flag(&global_flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER) ? "Never" : (AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_NO)));
ast_cli(a->fd, " Language: %s\n", default_language);
return global_st_mode;
}
+/*! \brief Send keep alive packet to peer */
+static int sip_send_keepalive(const void *data)
+{
+ struct sip_peer *peer = (struct sip_peer*) data;
+ int res = 0;
+ const char keepalive[] = "\r\n";
+
+ peer->keepalivesend = -1;
+
+ if (!peer->keepalive || ast_sockaddr_isnull(&peer->addr)) {
+ sip_unref_peer(peer, "release keepalive peer ref");
+ return 0;
+ }
+
+ /* Send the packet out using the proper method for this peer */
+ if ((peer->socket.fd != -1) && (peer->socket.type == SIP_TRANSPORT_UDP)) {
+ res = ast_sendto(peer->socket.fd, keepalive, sizeof(keepalive), 0, &peer->addr);
+ } else if ((peer->socket.type & (SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS)) &&
+ (peer->socket.tcptls_session) &&
+ (peer->socket.tcptls_session->fd != -1)) {
+ res = sip_tcptls_write(peer->socket.tcptls_session, keepalive, sizeof(keepalive));
+ } else if (peer->socket.type == SIP_TRANSPORT_UDP) {
+ res = ast_sendto(sipsock, keepalive, sizeof(keepalive), 0, &peer->addr);
+ }
+
+ if (res == -1) {
+ switch (errno) {
+ case EBADF: /* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
+ case EHOSTUNREACH: /* Host can't be reached */
+ case ENETDOWN: /* Interface down */
+ case ENETUNREACH: /* Network failure */
+ case ECONNREFUSED: /* ICMP port unreachable */
+ res = XMIT_ERROR; /* Don't bother with trying to transmit again */
+ }
+ }
+
+ if (res != sizeof(keepalive)) {
+ ast_log(LOG_WARNING, "sip_send_keepalive to %s returned %d: %s\n", ast_sockaddr_stringify(&peer->addr), res, strerror(errno));
+ }
+
+ AST_SCHED_REPLACE_UNREF(peer->keepalivesend, sched,
+ peer->keepalive * 1000, sip_send_keepalive, peer,
+ sip_unref_peer(_data, "removing keepalive peer ref"),
+ sip_unref_peer(peer, "removing keepalive peer ref"),
+ sip_ref_peer(peer, "adding keepalive peer ref"));
+
+ sip_unref_peer(peer, "release keepalive peer ref");
+
+ return 0;
+}
/*! \brief React to lack of answer to Qualify poke */
static int sip_poke_noanswer(const void *data)
*/
peer->expire = -1;
peer->pokeexpire = -1;
+ peer->keepalivesend = -1;
set_socket_transport(&peer->socket, SIP_TRANSPORT_UDP);
}
peer->type = SIP_TYPE_PEER;
peer->callgroup = 0;
peer->pickupgroup = 0;
peer->maxms = default_qualify;
+ peer->keepalive = default_keepalive;
peer->prefs = default_prefs;
ast_string_field_set(peer, zone, default_zone);
peer->stimer.st_mode_oper = global_st_mode; /* Session-Timers */
ast_log(LOG_WARNING, "Qualify is incompatible with dynamic uncached realtime. Please either turn rtcachefriends on or turn qualify off on peer '%s'\n", peer->name);
peer->maxms = 0;
}
+ } else if (!strcasecmp(v->name, "keepalive")) {
+ if (!strcasecmp(v->value, "no")) {
+ peer->keepalive = 0;
+ } else if (!strcasecmp(v->value, "yes")) {
+ peer->keepalive = DEFAULT_KEEPALIVE_INTERVAL;
+ } else if (sscanf(v->value, "%30d", &peer->keepalive) != 1) {
+ ast_log(LOG_WARNING, "Keep alive of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", peer->name, v->lineno);
+ peer->keepalive = 0;
+ }
} else if (!strcasecmp(v->name, "callcounter")) {
peer->call_limit = ast_true(v->value) ? INT_MAX : 0;
} else if (!strcasecmp(v->name, "call-limit")) {
default_fromdomain[0] = '\0';
default_fromdomainport = 0;
default_qualify = DEFAULT_QUALIFY;
+ default_keepalive = DEFAULT_KEEPALIVE;
default_zone[0] = '\0';
default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
ast_log(LOG_WARNING, "Qualification default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
default_qualify = 0;
}
+ } else if (!strcasecmp(v->name, "keepalive")) {
+ if (!strcasecmp(v->value, "no")) {
+ default_keepalive = 0;
+ } else if (!strcasecmp(v->value, "yes")) {
+ default_keepalive = DEFAULT_KEEPALIVE_INTERVAL;
+ } else if (sscanf(v->value, "%30d", &default_keepalive) != 1) {
+ ast_log(LOG_WARNING, "Keep alive default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
+ default_keepalive = 0;
+ }
} else if (!strcasecmp(v->name, "qualifyfreq")) {
int i;
if (sscanf(v->value, "%30d", &i) == 1) {
ao2_iterator_destroy(&i);
}
+/*! \brief Send a keepalive to all known peers */
+static void sip_keepalive_all_peers(void)
+{
+ struct ao2_iterator i;
+ struct sip_peer *peer;
+
+ if (!speerobjs) { /* No peers, just give up */
+ return;
+ }
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
+ ao2_lock(peer);
+ AST_SCHED_REPLACE_UNREF(peer->keepalivesend, sched, 0, sip_send_keepalive, peer,
+ sip_unref_peer(_data, "removing poke peer ref"),
+ sip_unref_peer(peer, "removing poke peer ref"),
+ sip_ref_peer(peer, "adding poke peer ref"));
+ ao2_unlock(peer);
+ sip_unref_peer(peer, "toss iterator peer ptr");
+ }
+ ao2_iterator_destroy(&i);
+}
+
/*! \brief Send all known registrations */
static void sip_send_all_registers(void)
{
/* Send qualify (OPTIONS) to all peers */
sip_poke_all_peers();
+ /* Send keepalive to all peers */
+ sip_keepalive_all_peers();
+
/* Register with all services */
sip_send_all_registers();
ast_manager_register_xml("SIPqualifypeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_qualify_peer);
ast_manager_register_xml("SIPshowregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_show_registry);
ast_manager_register_xml("SIPnotify", EVENT_FLAG_SYSTEM, manager_sipnotify);
- sip_poke_all_peers();
+ sip_poke_all_peers();
+ sip_keepalive_all_peers();
sip_send_all_registers();
sip_send_all_mwi_subscriptions();
initialize_escs();