1 /*
2  * Copyright (c) 2006-2016 Sippy Software, Inc., http://www.sippysoft.com
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/param.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <errno.h>
32 #include <inttypes.h>
33 #include <fcntl.h>
34 #include <limits.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #include "rtpp_ssrc.h"
42 #include "rtpa_stats.h"
43 #include "rtpp_monotime.h"
44 #include "rtpp_types.h"
45 #include "rtpp_analyzer.h"
46 #include "rtpp_pcount.h"
47 #include "rtpp_pcnt_strm.h"
48 #include "rtpp_acct_pipe.h"
49 #include "rtpp_acct.h"
50 #include "rtpp_module.h"
51 #include "rtpp_netaddr.h"
52 #include "rtpp_network.h"
53 #include "rtpp_cfg_stable.h"
54 
55 #define SSRC_STRLEN 11
56 
57 struct rtpp_mod_acct_face {
58    char rtp_adr[MAX_AP_STRBUF];
59    char rtcp_adr[MAX_AP_STRBUF];
60    char ssrc[SSRC_STRLEN];
61 };
62 
63 struct rtpp_module_priv {
64    int fd;
65    pid_t pid;
66    struct stat stt;
67    char fname[MAXPATHLEN + 1];
68    double next_hupd_ts;
69    char node_id[_POSIX_HOST_NAME_MAX + 1];
70    struct rtpp_mod_acct_face o;
71    struct rtpp_mod_acct_face a;
72 };
73 
74 /* Bump this when some changes are made */
75 #define RTPP_METRICS_VERSION	"1.2"
76 
77 #define HNAME_REFRESH_IVAL	1.0
78 
79 static struct rtpp_module_priv *rtpp_acct_csv_ctor(struct rtpp_cfg_stable *);
80 static void rtpp_acct_csv_dtor(struct rtpp_module_priv *);
81 static void rtpp_acct_csv_do(struct rtpp_module_priv *, struct rtpp_acct *);
82 static off_t rtpp_acct_csv_lockf(int);
83 static void rtpp_acct_csv_unlockf(int, off_t);
84 
85 #define API_FUNC(fname, asize) {.func = (fname), .argsize = (asize)}
86 
87 struct rtpp_minfo rtpp_module = {
88     .name = "acct_csv",
89     .ver = MI_VER_INIT(),
90     .ctor = rtpp_acct_csv_ctor,
91     .dtor = rtpp_acct_csv_dtor,
92     .on_session_end = API_FUNC(rtpp_acct_csv_do, rtpp_acct_OSIZE())
93 };
94 
95 #if 0
96 /* Quick hack to check and see if periodic updates work as expected */
97 static int
98 gethostname_test(char *name, size_t namelen)
99 {
100     static int i = 0;
101 
102     if (i < 2) {
103         strcpy(name, "foo.bar.com");
104         i++;
105         return 0;
106     }
107     return (gethostname(name, namelen));
108 }
109 
110 #define gethostname gethostname_test
111 #endif
112 
113 static const char *
rtpp_acct_get_nid(struct rtpp_module_priv * pvt,struct rtpp_acct * ap)114 rtpp_acct_get_nid(struct rtpp_module_priv *pvt, struct rtpp_acct *ap)
115 {
116 
117     if (pvt->next_hupd_ts == 0.0 || pvt->next_hupd_ts < ap->destroy_ts) {
118         if (gethostname(pvt->node_id, sizeof(pvt->node_id)) == 0) {
119             pvt->next_hupd_ts = ap->destroy_ts + HNAME_REFRESH_IVAL;
120         }
121     }
122     return (pvt->node_id);
123 }
124 
125 #define SFX_INO "_ino"
126 #define SFX_INA "_ina"
127 
128 #define SFX_O   "_o"
129 #define SFX_A   "_a"
130 
131 #define PFX_GEN "rtpp_"
132 
133 
134 #define RVER_NM     "rec_ver"
135 #define NID_NM      PFX_GEN "node_id"
136 #define PID_NM      PFX_GEN "pid"
137 #define SID_NM      "sess_uid"
138 #define CID_NM      "call_id"
139 #define PT_NAME     "rtpa_pt_last"
140 #define PT_NM_O     PT_NAME SFX_INO
141 #define PT_NM_A     PT_NAME SFX_INA
142 #define PFX_RTP     "rtp_"
143 #define PFX_RTCP    "rtcp_"
144 #define RM_IP_NM "rmt_ip"
145 #define RM_PT_NM "rmt_pt"
146 #define R_RM_NM_O PFX_GEN PFX_RTP RM_IP_NM SFX_O
147 #define R_RM_NM_A PFX_GEN PFX_RTP RM_IP_NM SFX_A
148 #define C_RM_NM_O PFX_GEN PFX_RTCP RM_IP_NM SFX_O
149 #define C_RM_NM_A PFX_GEN PFX_RTCP RM_IP_NM SFX_A
150 #define R_RM_PT_NM_O PFX_GEN PFX_RTP RM_PT_NM SFX_O
151 #define R_RM_PT_NM_A PFX_GEN PFX_RTP RM_PT_NM SFX_A
152 #define C_RM_PT_NM_O PFX_GEN PFX_RTCP RM_PT_NM SFX_O
153 #define C_RM_PT_NM_A PFX_GEN PFX_RTCP RM_PT_NM SFX_A
154 
155 #define HLD_CNT_NM   "hld_cnt"
156 #define HLD_STS_NM   "hld_sts"
157 #define HLD_CNT_NM_O PFX_GEN HLD_CNT_NM SFX_O
158 #define HLD_CNT_NM_A PFX_GEN HLD_CNT_NM SFX_A
159 #define HLD_STS_NM_O PFX_GEN HLD_STS_NM SFX_O
160 #define HLD_STS_NM_A PFX_GEN HLD_STS_NM SFX_A
161 
162 #define RVER_FMT    "%s"
163 #define NID_FMT     "%s"
164 #define PID_FMT     "%d"
165 #define SID_FMT     "%" PRId64
166 #define PT_FMT      "%d"
167 #define LSSRC_FMT   "%s"
168 #define SNCHG_FMT   "%lu"
169 #define RM_FMT      "%s"
170 #define SEP         ","
171 #define HLD_STS_FMT "%s"
172 #define HLD_CNT_FMT "%d"
173 
174 static int
rtpp_acct_csv_open(struct rtpp_module_priv * pvt)175 rtpp_acct_csv_open(struct rtpp_module_priv *pvt)
176 {
177     char *buf;
178     int len, pos;
179 
180     if (pvt->fd != -1) {
181         close(pvt->fd);
182     }
183     pvt->fd = open(pvt->fname, O_WRONLY | O_APPEND | O_CREAT, DEFFILEMODE);
184     if (pvt->fd == -1) {
185         goto e0;
186     }
187     pos = rtpp_acct_csv_lockf(pvt->fd);
188     if (pos < 0) {
189         goto e1;
190     }
191     if (fstat(pvt->fd, &pvt->stt) < 0) {
192         goto e2;
193     }
194     if (pvt->stt.st_size == 0) {
195         buf = NULL;
196         len = mod_asprintf(&buf, RVER_NM SEP NID_NM SEP PID_NM SEP SID_NM
197           SEP CID_NM SEP
198           "from_tag,setup_ts,teardown_ts,first_rtp_ts_ino,last_rtp_ts_ino,"
199           "first_rtp_ts_ina,last_rtp_ts_ina,rtp_npkts_ina,rtp_npkts_ino,"
200           "rtp_nrelayed,rtp_ndropped,rtcp_npkts_ina,rtcp_npkts_ino,"
201           "rtcp_nrelayed,rtcp_ndropped,rtpa_nsent_ino,rtpa_nrcvd_ino,"
202           "rtpa_ndups_ino,rtpa_nlost_ino,rtpa_perrs_ino,"
203           "rtpa_ssrc_last_ino,rtpa_ssrc_cnt_ino" SEP PT_NM_O SEP
204           "rtpa_nsent_ina,rtpa_nrcvd_ina,rtpa_ndups_ina,rtpa_nlost_ina,"
205           "rtpa_perrs_ina,rtpa_ssrc_last_ina,rtpa_ssrc_cnt_ina" SEP PT_NM_A SEP
206           "rtpa_jitter_last_ino,rtpa_jitter_max_ino,rtpa_jitter_avg_ino,"
207           "rtpa_jitter_last_ina,rtpa_jitter_max_ina,rtpa_jitter_avg_ina" SEP
208           R_RM_NM_O SEP R_RM_PT_NM_O SEP R_RM_NM_A SEP R_RM_PT_NM_A SEP
209           C_RM_NM_O SEP C_RM_PT_NM_O SEP C_RM_NM_A SEP C_RM_PT_NM_A SEP
210           HLD_STS_NM_O SEP HLD_STS_NM_A SEP HLD_CNT_NM_O SEP HLD_CNT_NM_A "\n");
211         if (len <= 0) {
212             if (len == 0 && buf != NULL) {
213                 goto e3;
214             }
215             goto e2;
216         }
217         write(pvt->fd, buf, len);
218         mod_free(buf);
219     }
220     rtpp_acct_csv_unlockf(pvt->fd, pos);
221     return (0);
222 
223 e3:
224     mod_free(buf);
225 e2:
226     rtpp_acct_csv_unlockf(pvt->fd, pos);
227 e1:
228     close(pvt->fd);
229 e0:
230     return (-1);
231 }
232 
233 static struct rtpp_module_priv *
rtpp_acct_csv_ctor(struct rtpp_cfg_stable * cfsp)234 rtpp_acct_csv_ctor(struct rtpp_cfg_stable *cfsp)
235 {
236     struct rtpp_module_priv *pvt;
237 
238     pvt = mod_zmalloc(sizeof(struct rtpp_module_priv));
239     if (pvt == NULL) {
240         goto e0;
241     }
242     pvt->pid = getpid();
243     if (cfsp->cwd_orig == NULL) {
244         snprintf(pvt->fname, sizeof(pvt->fname), "%s", "rtpproxy_acct.csv");
245     } else {
246         snprintf(pvt->fname, sizeof(pvt->fname), "%s/%s", cfsp->cwd_orig,
247           "rtpproxy_acct.csv");
248     }
249     if (gethostname(pvt->node_id, sizeof(pvt->node_id)) != 0) {
250         strcpy(pvt->node_id, "UNKNOWN");
251     }
252     pvt->fd = -1;
253     if (rtpp_acct_csv_open(pvt) == -1) {
254         goto e1;
255     }
256     return (pvt);
257 
258 e1:
259     mod_free(pvt);
260 e0:
261     return (NULL);
262 }
263 
264 static void
rtpp_acct_csv_dtor(struct rtpp_module_priv * pvt)265 rtpp_acct_csv_dtor(struct rtpp_module_priv *pvt)
266 {
267 
268     close(pvt->fd);
269     mod_free(pvt);
270     return;
271 }
272 
273 #define ES_IF_NULL(s) ((s) == NULL ? "" : s)
274 #define MT2RT_NZ(mt) ((mt) == 0.0 ? 0.0 : dtime2rtime(mt))
275 
276 static void
format_ssrc(struct rtpp_ssrc * sp,char * sbuf,size_t sblen)277 format_ssrc(struct rtpp_ssrc *sp, char *sbuf, size_t sblen)
278 {
279 
280     if (sp->inited) {
281         snprintf(sbuf, sblen, SSRC_FMT, sp->val);
282     } else {
283         sbuf[0] = '\0';
284     }
285 }
286 
287 static void
format_netaddr(struct rtpp_netaddr * nap_rtp,struct rtpp_netaddr * nap_rtcp,struct rtpp_mod_acct_face * afp)288 format_netaddr(struct rtpp_netaddr *nap_rtp, struct rtpp_netaddr *nap_rtcp,
289   struct rtpp_mod_acct_face *afp)
290 {
291 
292     if (CALL_SMETHOD(nap_rtp, isempty)) {
293         sprintf(afp->rtp_adr, ",");
294     } else {
295         CALL_SMETHOD(nap_rtp, sip_print, afp->rtp_adr, sizeof(afp->rtp_adr),
296           ',');
297     }
298     if (CALL_SMETHOD(nap_rtcp, isempty)) {
299         sprintf(afp->rtcp_adr, ",");
300     } else {
301         CALL_SMETHOD(nap_rtcp, sip_print, afp->rtcp_adr, sizeof(afp->rtcp_adr),
302           ',');
303     }
304 }
305 
306 #define FMT_BOOL(x) ((x == 0) ? "f" : "t")
307 
308 static void
rtpp_acct_csv_do(struct rtpp_module_priv * pvt,struct rtpp_acct * acct)309 rtpp_acct_csv_do(struct rtpp_module_priv *pvt, struct rtpp_acct *acct)
310 {
311     char *buf;
312     int len, pos, rval;
313     struct stat stt;
314 
315     buf = NULL;
316     rval = stat(pvt->fname, &stt);
317     if (rval != -1) {
318         if (stt.st_dev != pvt->stt.st_dev || stt.st_ino != pvt->stt.st_ino) {
319             rtpp_acct_csv_open(pvt);
320         }
321     } else if (rval == -1 && errno == ENOENT) {
322         rtpp_acct_csv_open(pvt);
323     }
324     pos = rtpp_acct_csv_lockf(pvt->fd);
325     if (pos < 0) {
326         return;
327     }
328 
329     format_ssrc(&acct->rasta->last_ssrc, pvt->a.ssrc, sizeof(pvt->a.ssrc));
330     format_ssrc(&acct->rasto->last_ssrc, pvt->o.ssrc, sizeof(pvt->o.ssrc));
331     format_netaddr(acct->rtp.a.rem_addr, acct->rtcp.a.rem_addr, &pvt->a);
332     format_netaddr(acct->rtp.o.rem_addr, acct->rtcp.o.rem_addr, &pvt->o);
333     len = mod_asprintf(&buf, RVER_FMT SEP NID_FMT SEP PID_FMT SEP SID_FMT SEP
334       "%s,%s,%f,%f,%f,%f,%f,%f,%lu,%lu,"
335       "%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu" SEP LSSRC_FMT SEP SNCHG_FMT SEP
336       PT_FMT SEP "%lu,%lu,%lu,%lu,%lu" SEP LSSRC_FMT SEP SNCHG_FMT SEP PT_FMT SEP
337       "%f,%f,%f,%f,%f,%f" SEP RM_FMT SEP RM_FMT SEP RM_FMT SEP RM_FMT SEP
338       HLD_STS_FMT SEP HLD_STS_FMT SEP HLD_CNT_FMT SEP HLD_CNT_FMT "\n",
339       RTPP_METRICS_VERSION, rtpp_acct_get_nid(pvt, acct),
340       pvt->pid, acct->seuid, ES_IF_NULL(acct->call_id), ES_IF_NULL(acct->from_tag),
341       MT2RT_NZ(acct->init_ts), MT2RT_NZ(acct->destroy_ts), MT2RT_NZ(acct->rtp.o.ps->first_pkt_rcv),
342       MT2RT_NZ(acct->rtp.o.ps->last_pkt_rcv), MT2RT_NZ(acct->rtp.a.ps->first_pkt_rcv),
343       MT2RT_NZ(acct->rtp.a.ps->last_pkt_rcv), acct->rtp.a.ps->npkts_in, acct->rtp.o.ps->npkts_in,
344       acct->rtp.pcnts->nrelayed, acct->rtp.pcnts->ndropped, acct->rtcp.a.ps->npkts_in,
345       acct->rtcp.o.ps->npkts_in, acct->rtcp.pcnts->nrelayed, acct->rtcp.pcnts->ndropped,
346       acct->rasto->psent, acct->rasto->precvd, acct->rasto->pdups, acct->rasto->plost,
347       acct->rasto->pecount, pvt->o.ssrc, acct->rasto->ssrc_changes, acct->rasto->last_pt,
348       acct->rasta->psent, acct->rasta->precvd, acct->rasta->pdups, acct->rasta->plost,
349       acct->rasta->pecount, pvt->a.ssrc, acct->rasta->ssrc_changes, acct->rasta->last_pt,
350       acct->jrasto->jlast, acct->jrasto->jmax, acct->jrasto->javg,
351       acct->jrasta->jlast, acct->jrasta->jmax, acct->jrasta->javg,
352       pvt->o.rtp_adr, pvt->a.rtp_adr, pvt->o.rtcp_adr, pvt->a.rtcp_adr,
353       FMT_BOOL(acct->rtp.o.hld_stat.status), FMT_BOOL(acct->rtp.a.hld_stat.status),
354       acct->rtp.o.hld_stat.cnt, acct->rtp.a.hld_stat.cnt);
355     if (len <= 0) {
356         if (len == 0 && buf != NULL) {
357             mod_free(buf);
358         }
359         return;
360     }
361     write(pvt->fd, buf, len);
362     rtpp_acct_csv_unlockf(pvt->fd, pos);
363     mod_free(buf);
364 }
365 
366 static off_t
rtpp_acct_csv_lockf(int fd)367 rtpp_acct_csv_lockf(int fd)
368 {
369     struct flock l;
370     int rval;
371 
372     memset(&l, '\0', sizeof(l));
373     l.l_whence = SEEK_CUR;
374     l.l_type = F_WRLCK;
375     do {
376         rval = fcntl(fd, F_SETLKW, &l);
377     } while (rval == -1 && errno == EINTR);
378     if (rval == -1) {
379         return (-1);
380     }
381     return lseek(fd, 0, SEEK_CUR);
382 }
383 
384 static void
rtpp_acct_csv_unlockf(int fd,off_t offset)385 rtpp_acct_csv_unlockf(int fd, off_t offset)
386 {
387     struct flock l;
388     int rval;
389 
390     memset(&l, '\0', sizeof(l));
391     l.l_whence = SEEK_SET;
392     l.l_start = offset;
393     l.l_type = F_UNLCK;
394     do {
395         rval = fcntl(fd, F_SETLKW, &l);
396     } while (rval == -1 && errno == EINTR);
397 }
398