2 * A Simple SIP Logging Plugin for ulogd
4 * (c) 2008 Martin Mares <mj@ucw.cz>
14 #include <ulogd/ulogd.h>
15 #include <ulogd/conffile.h>
17 typedef unsigned char byte;
18 typedef unsigned int uns;
20 static inline int is_white(int c)
22 return (c == ' ' || c == '\t');
25 /*** Configuration ***/
27 static config_entry_t sip_cf_log = {
30 .type = CONFIG_TYPE_STRING,
31 .options = CONFIG_OPT_NONE,
32 .u.string = "/var/log/ulog/sip.log"
35 /*** Unaligned access ***/
37 static uns get_u16(byte *p)
39 return (p[0] << 8) | p[1];
42 static uns get_u32(byte *p)
44 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
55 { "oob.time.sec", 0 },
56 { "oob.time.usec", 0 },
68 static int init_keys(void)
70 for (uns i=0; i < sizeof(keys)/sizeof(keys[0]); i++)
71 if (!(keys[i].id = keyh_getid(keys[i].name)))
73 ulogd_log(ULOGD_ERROR, "SIP: Unable to resolve key %s\n", keys[i].name);
79 #define KEY_H(i) ulogd_keyh[keys[i].id].interp->result[ulogd_keyh[keys[i].id].offset]
80 #define KEY_VAL(i) KEY_H(i).value
81 #define KEY_FLAGS(i) KEY_H(i).flags
82 #define KEY_TYPE(i) KEY_H(i).type
83 #define KEY_OK(i,t) ((KEY_FLAGS(i) & ULOGD_RETF_VALID) && KEY_TYPE(i) == ULOGD_RET_##t)
85 /*** Representation of packets ***/
88 time_t time_sec; // Arrival time
91 byte *raw; // Raw packet
94 byte *ip; // Inside IP
96 uns src_addr, dst_addr;
97 byte *udp; // Inside UDP
99 uns src_port, dst_port;
100 char *sip_rr; // Decoded SIP
112 static int verbose = 1;
114 static void debug(char *fmt, ...)
121 vfprintf(siplog, fmt, args);
126 static int error(struct pkt *pkt, char *fmt, ...)
130 fprintf(siplog, "%s ", pkt->time);
131 vfprintf(siplog, fmt, args);
137 static int parse_rr(struct pkt *pkt)
139 char *rr = pkt->sip_rr;
140 if (!strncmp(rr, "SIP/", 4))
143 while (*rr && !is_white(*rr))
145 while (is_white(*rr))
155 static int parse_sip(struct pkt *pkt)
157 char *p = (char*) pkt->udp;
158 char *end = p + pkt->udp_len;
161 // Parse the request/reply line first
163 while (p < end && *p != '\r' && *p != '\n')
166 return error(pkt, "Truncated 1st line");
169 if (p < end && *p == '\n')
171 debug(">>> %s", pkt->sip_rr);
173 // Slurp the other header lines
181 return error(pkt, "Truncated SIP headers");
187 if (p < end && is_white(*p))
200 // Got a line, parse it
201 char *sep = strchr(start, ':');
204 error(pkt, "Malformed header line");
208 for (char *p=sep; p>start && is_white(p[-1]);)
210 while (++sep < end && is_white(*sep))
212 debug("\t%s: %s", start, sep);
213 struct hdr *h = alloca(sizeof(*h));
214 h->next = pkt->sip_hdr;
220 return parse_rr(pkt);
223 static int parse_udp(struct pkt *pkt)
227 return error(pkt, "Truncated UDP header");
228 pkt->src_port = get_u16(p);
229 pkt->dst_port = get_u16(p+2);
230 uns ulen = get_u16(p+4);
232 return error(pkt, "Invalid UDP length (%d)", ulen);
233 if (ulen > pkt->ip_len)
234 return error(pkt, "Truncated UDP data (have %d, want %d)", pkt->ip_len, ulen);
236 pkt->udp_len = ulen - 8;
237 debug("%08x:%d -> %08x:%d (%d bytes, out_p=%d)", pkt->src_addr, pkt->src_port, pkt->dst_addr, pkt->dst_port, pkt->out_p);
238 return parse_sip(pkt);
241 static int parse_ip(struct pkt *pkt)
244 if (pkt->raw_len < 20)
245 return error(pkt, "Very truncated IP header");
246 uns ipver = p[0] >> 4;
248 return error(pkt, "We don't speak IPv%d", ipver);
249 uns hlen = (p[0] & 0x0f)*4;
250 if (pkt->raw_len < hlen)
251 return error(pkt, "Truncated IP header (want %d)", hlen);
252 uns ilen = get_u16(p+2);
254 return error(pkt, "Invalid IP length (have %d, want at least %d for header)", ilen, hlen);
255 if (ilen > pkt->raw_len)
256 return error(pkt, "Truncated (have %d, want %d)", pkt->raw_len, ilen);
258 return error(pkt, "Not UDP (protocol %02x)", p[9]);
259 if (get_u16(p+6) & 0x3fff)
260 return error(pkt, "Fragmented");
262 pkt->ip_len = ilen - hlen;
263 pkt->src_addr = get_u32(p+12);
264 pkt->dst_addr = get_u32(p+16);
265 return parse_udp(pkt);
268 /*** Interface to ulogd ***/
270 static int sip_output(ulog_iret_t *res __attribute__((unused)))
274 char *prefix = (KEY_OK(K_OOB_PREFIX, STRING) ? KEY_VAL(K_OOB_PREFIX).ptr : "?");
275 if (!strcasecmp(prefix, "phone-in"))
277 else if (!strcasecmp(prefix, "phone-out"))
282 fprintf(siplog, "(non-phone packet received, prefix=%s)\n", prefix);
286 if (!KEY_OK(K_RAW_PKT, RAW) ||
287 !KEY_OK(K_RAW_PKTLEN, UINT32) ||
288 !KEY_OK(K_OOB_TIME_SEC, UINT32) ||
289 !KEY_OK(K_OOB_TIME_USEC, UINT32))
291 ulogd_log(ULOGD_ERROR, "SIP: Mandatory keys missing\n");
294 pkt.raw = KEY_VAL(K_RAW_PKT).ptr;
295 pkt.raw_len = KEY_VAL(K_RAW_PKTLEN).ui32;
296 pkt.time_sec = KEY_VAL(K_OOB_TIME_SEC).ui32;
297 pkt.time_usec = KEY_VAL(K_OOB_TIME_USEC).ui32;
298 struct tm *tm = localtime(&pkt.time_sec);
300 sprintf(pkt.time, "<#%d.%06d> ", (int)pkt.time_sec, pkt.time_usec);
302 sprintf(pkt.time, "%04d-%02d-%02d %02d:%02d:%02d.%06d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
303 tm->tm_hour, tm->tm_min, tm->tm_sec, pkt.time_usec);
305 fprintf(siplog, "\t%s\n", pkt.time);
312 /*** Housekeeping ***/
314 static int sip_init(void)
316 if (config_parse_file("SIP", &sip_cf_log))
318 ulogd_log(ULOGD_ERROR, "SIP: Parsing of configuration file failed\n");
321 ulogd_log(ULOGD_INFO, "Initializing SIP plugin, logging to %s\n", sip_cf_log.u.string);
322 siplog = fopen(sip_cf_log.u.string, "a");
325 ulogd_log(ULOGD_ERROR, "Cannot open %s: %s", sip_cf_log.u.string, strerror(errno));
331 static void sip_fini(void)
333 // Logging does not work here
334 // ulogd_log(ULOGD_INFO, "siplog fini\n");
338 static void sip_signal(int sig)
342 // FIXME: Should reopen the log file here
346 static ulog_output_t sip_op = {
350 .output = sip_output,
354 static void __attribute__((constructor)) constr(void)
356 // All logging goes to stderr instead of the log file
357 // ulogd_log(ULOGD_INFO, "Registered siplog plugin\n");
359 register_output(&sip_op);