2 * Usrloc module - keepalive
4 * Copyright (C) 2020 Asipto.com
6 * This file is part of Kamailio, a free SIP server.
8 * Kamailio is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version
13 * Kamailio is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "../../core/dprint.h"
31 #include "../../core/ut.h"
32 #include "../../core/resolve.h"
33 #include "../../core/forward.h"
34 #include "../../core/globals.h"
35 #include "../../core/pvar.h"
36 #include "../../core/parser/parse_uri.h"
37 #include "../../core/parser/parse_from.h"
38 #include "../../core/parser/parse_to.h"
39 #include "../../core/parser/parse_rr.h"
41 #include "ul_keepalive.h"
43 extern int ul_keepalive_timeout;
45 static int ul_ka_send(str *kamsg, dest_info_t *kadst);
49 _KAMETHOD_ _URI_ SIP/2.0\r\n
50 Via: SIP/2.0/_PROTO_ _IP_:_PORT_;branch=z9hG4bKx._GCNT_._BCNT_.0\r\n
52 From: <_KAFROM_>;tag=_RUID_-_AORHASH_-_TIMESEC_-_TIMEUSEC_-_GCNT_._BCNT_\r\n
54 Call-ID: _KACALLID_\r\n
55 CSeq: 1 _KAMETHOD_\r\n
56 Content-Length: 0\r\n\r\n"
60 #define ULKA_CALLID_PREFIX "ksrulka-"
61 #define ULKA_CALLID_PREFIX_LEN (sizeof(ULKA_CALLID_PREFIX) - 1)
63 #define ULKA_MSG "%.*s %.*s SIP/2.0\r\n" \
64 "Via: SIP/2.0/%.*s %.*s:%.*s;branch=z9hG4bKx.%u.%u.0\r\n" \
66 "From: <%.*s>;tag=%.*s-%x-%lx-%lx-%x.%x\r\n" \
67 "To: <sip:%.*s%s%.*s>\r\n" \
68 "Call-ID: " ULKA_CALLID_PREFIX "%u.%u\r\n" \
70 "Content-Length: 0\r\n\r\n"
72 extern str ul_ka_from;
73 extern str ul_ka_domain;
74 extern str ul_ka_method;
75 extern int ul_ka_mode;
76 extern int ul_ka_filter;
77 extern int ul_ka_loglevel;
78 extern pv_elem_t *ul_ka_logfmt;
80 extern unsigned int ul_nat_bflag;
82 static unsigned int _ul_ka_counter = 0;
87 int ul_ka_urecord(urecord_t *ur)
90 #define ULKA_BUF_SIZE 2048
91 char kabuf[ULKA_BUF_SIZE];
97 str sproto = STR_NULL;
101 socket_info_t *ssock;
103 unsigned int bcnt = 0;
109 if (ul_ka_mode == ULKA_NONE) {
112 LM_DBG("keepalive for aor: %.*s\n", ur->aor.len, ur->aor.s);
115 for(i=0; i<ur->aor.len; i++) {
116 if(ur->aor.s[i] == '@') {
122 for (uc = ur->contacts; uc != NULL; uc = uc->next) {
123 if (uc->c.len <= 0) {
126 if((ul_ka_filter&GAU_OPT_SERVER_ID) && (uc->server_id != server_id)) {
129 if(ul_keepalive_timeout>0 && uc->last_keepalive>0) {
130 if(uc->last_keepalive+ul_keepalive_timeout < tnow) {
131 /* set contact as expired in 10s */
132 LM_DBG("set expired contact on keepalive - aor: %.*s c: %.*s\n",
133 ur->aor.len, ur->aor.s, uc->c.len, uc->c.s);
134 if(uc->expires > tnow + 10) {
135 uc->expires = tnow + 10;
141 if(ul_ka_mode & ULKA_NAT) {
142 /* keepalive for natted contacts only */
143 if (ul_nat_bflag == 0) {
146 if ((uc->cflags & ul_nat_bflag) != ul_nat_bflag) {
150 if(uc->received.len > 0) {
153 if (uc->path.len > 0) {
154 if(get_path_dst_uri(&uc->path, &sdst) < 0) {
155 LM_ERR("failed to get first uri for path\n");
162 if(parse_uri(sdst.s, sdst.len, &duri) < 0) {
163 LM_ERR("cannot parse next hop uri\n");
167 if(duri.port_no == 0) {
168 duri.port_no = SIP_PORT;
171 he = sip_resolvehost(&duri.host, &duri.port_no, &dproto);
173 LM_ERR("cannot resolve destination\n");
176 if(ul_ka_mode & ULKA_UDP) {
177 if(dproto != PROTO_UDP) {
178 LM_DBG("skipping non-udp contact - proto %d\n", (int)dproto);
182 init_dest_info(&idst);
183 hostent2su(&idst.to, he, 0, duri.port_no);
186 ssock = get_send_socket(0, &idst.to, dproto);
189 LM_ERR("cannot get sending socket\n");
193 idst.send_sock = ssock;
195 if(ssock->useinfo.name.len > 0) {
196 vaddr = ssock->useinfo.name;
198 vaddr = ssock->address_str;
200 if(ssock->useinfo.port_no > 0) {
201 vport = ssock->useinfo.port_no_str;
203 vport = ssock->port_no_str;
205 get_valid_proto_string(dproto, 1, 1, &sproto);
208 gettimeofday(&tv, NULL);
209 kabuf_len = snprintf(kabuf, ULKA_BUF_SIZE - 1, ULKA_MSG,
210 ul_ka_method.len, ul_ka_method.s,
212 sproto.len, sproto.s,
215 _ul_ka_counter, bcnt,
216 (uc->path.len>0)?"Route: ":"",
217 (uc->path.len>0)?uc->path.len:0,
218 (uc->path.len>0)?uc->path.s:"",
219 (uc->path.len>0)?2:0,
220 (uc->path.len>0)?"\r\n":"",
221 ul_ka_from.len, ul_ka_from.s,
222 uc->ruid.len, uc->ruid.s, ur->aorhash,
223 (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec,
224 _ul_ka_counter, bcnt,
225 ur->aor.len, ur->aor.s,
227 (aortype==1)?0:ul_ka_domain.len, (aortype==1)?"":ul_ka_domain.s,
228 _ul_ka_counter, bcnt,
229 ul_ka_method.len, ul_ka_method.s);
230 if(kabuf_len<=0 || kabuf_len>=ULKA_BUF_SIZE) {
231 LM_ERR("failed to print the keepalive request\n");
233 LM_DBG("keepalive request (len: %d) [[\n%.*s]]\n",
234 kabuf_len, kabuf_len, kabuf);
236 kamsg.len = kabuf_len;
237 ul_ka_send(&kamsg, &idst);
247 static int ul_ka_send(str *kamsg, dest_info_t *kadst)
249 if (kadst->proto == PROTO_UDP) {
250 return udp_send(kadst, kamsg->s, kamsg->len);
254 else if(kadst->proto == PROTO_TCP) {
257 return tcp_send(kadst, 0, kamsg->s, kamsg->len);
261 else if(kadst->proto == PROTO_TLS) {
264 return tcp_send(kadst, 0, kamsg->s, kamsg->len);
268 else if(kadst->proto == PROTO_SCTP) {
270 return sctp_core_msg_send(kadst, kamsg->s, kamsg->len);
274 LM_ERR("unknown proto [%d] for sending keepalive\n",
283 unsigned long ul_ka_fromhex(str *shex, int *err)
289 for (i=0; i<shex->len; i++) {
291 if (b >= '0' && b <= '9') b = b - '0';
292 else if (b >= 'a' && b <='f') b = b - 'a' + 10;
293 else if (b >= 'A' && b <='F') b = b - 'A' + 10;
294 else { *err = 1; return 0; };
295 v = (v << 4) | (b & 0xF);
303 int ul_ka_reply_received(sip_msg_t *msg)
309 unsigned int aorhash;
315 if(msg->cseq == NULL) {
316 if((parse_headers(msg, HDR_CSEQ_F, 0) == -1) || (msg->cseq == NULL)) {
317 LM_ERR("invalid CSeq header\n");
322 if(get_cseq(msg)->method.len != ul_ka_method.len) {
325 if(strncmp(get_cseq(msg)->method.s, ul_ka_method.s, ul_ka_method.len) != 0) {
329 /* there must be no second via */
330 if(!(parse_headers(msg, HDR_VIA2_F, 0) == -1 || (msg->via2 == 0)
331 || (msg->via2->error != PARSE_OK))) {
336 if((parse_from_header(msg)) < 0) {
337 LM_ERR("cannot parse From header\n");
342 if(fb->uri.len != ul_ka_from.len
343 || strncmp(fb->uri.s, ul_ka_from.s, ul_ka_from.len) != 0) {
347 /* from-tag is: ruid-aorhash-tmsec-tmusec-counter */
348 if(fb->tag_value.len <= 8) {
352 LM_DBG("checking keepalive reply [%.*s]\n", fb->tag_value.len,
355 /* todo: macro/function to tokenize */
357 p = q_memrchr(fb->tag_value.s, '-', fb->tag_value.len);
359 LM_DBG("from tag format mismatch [%.*s]\n", fb->tag_value.len,
365 tok.len = p - fb->tag_value.s;
366 p = q_memrchr(fb->tag_value.s, '-', tok.len);
368 LM_DBG("from tag format mismatch [%.*s]\n", fb->tag_value.len,
373 tok.len = fb->tag_value.s + tok.len - tok.s;
375 LM_DBG("empty token\n");
378 LM_DBG("tv usec string is [%.*s] (%d)\n", tok.len, tok.s, tok.len);
379 tvm.tv_usec = ul_ka_fromhex(&tok, &err);
381 LM_DBG("invalid tv usec value\n");
384 LM_DBG("tv usec is [%lu]\n", (unsigned long)tvm.tv_usec);
387 tok.len = p - fb->tag_value.s;
388 p = q_memrchr(fb->tag_value.s, '-', tok.len);
390 LM_DBG("from tag format mismatch [%.*s]\n", fb->tag_value.len,
395 tok.len = fb->tag_value.s + tok.len - tok.s;
397 LM_DBG("empty token\n");
400 LM_DBG("tv sec string is [%.*s] (%d)\n", tok.len, tok.s, tok.len);
401 tvm.tv_sec = ul_ka_fromhex(&tok, &err);
403 LM_DBG("invalid tv sec value\n");
406 LM_DBG("tv sec is [%lu]\n", (unsigned long)tvm.tv_sec);
409 tok.len = p - fb->tag_value.s;
410 p = q_memrchr(fb->tag_value.s, '-', tok.len);
412 LM_DBG("from tag format mismatch [%.*s]\n", fb->tag_value.len,
417 tok.len = fb->tag_value.s + tok.len - tok.s;
419 LM_DBG("empty token\n");
422 LM_DBG("aor hash string is [%.*s] (%d)\n", tok.len, tok.s, tok.len);
423 aorhash = ul_ka_fromhex(&tok, &err);
425 LM_DBG("invalid aor hash value\n");
428 LM_DBG("aor hash is [%u]\n", aorhash);
430 ruid.s = fb->tag_value.s;
431 ruid.len = tok.s - ruid.s - 1;
434 LM_DBG("cannot get ruid in [%.*s]\n", fb->tag_value.len,
439 gettimeofday(&tvn, NULL);
440 tvdiff = (tvn.tv_sec - tvm.tv_sec) * 1000000
441 + (tvn.tv_usec - tvm.tv_usec);
442 ul_update_keepalive(aorhash, &ruid, tvn.tv_sec, tvdiff);
444 if(ul_ka_loglevel != 255 && ul_ka_logfmt != NULL) {
445 if (pv_printf_s(msg, ul_ka_logfmt, &tok) == 0) {
446 LOG(ul_ka_loglevel, "keepalive roundtrip: %u.%06u sec - ruid [%.*s]%.*s\n",
447 tvdiff/1000000, tvdiff%1000000, ruid.len, ruid.s,
453 LM_DBG("response of keepalive for ruid [%.*s] aorhash [%u] roundtrip: %u.%06u secs\n",
454 ruid.len, ruid.s, aorhash, tvdiff/1000000, tvdiff%1000000);