blob: c3a836a42adb4151fbd3bd6031ee8361ef46ae22 [file] [log] [blame]
/*
* (C) 2005-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "internal/internal.h"
/*
* XML output sample:
*
* <flow>
* <meta direction="original">
* <layer3 protonum="2" protoname="IPv4">
* <src>192.168.0.1</src>
* <dst>192.168.0.2</dst>
* </layer3>
* <layer4 protonum="16" protoname"udp">
* <sport>80</sport>
* <dport>56665</dport>
* </layer4>
* <counters>
* <bytes>10</bytes>
* <packets>1</packets>
* </counters>
* </meta>
* <meta direction="reply">
* <layer3 protonum="2" protoname="IPv4">
* <src>192.168.0.2</src>
* <dst>192.168.0.1</dst>
* </layer3>
* <layer4 protonum="16" protoname="udp">
* <sport>80</sport>
* <dport>56665</dport>
* </layer4>
* <counters>
* <bytes>5029</bytes>
* <packets>12</packets>
* </counters>
* </meta>
* <meta direction="independent">
* <state>ESTABLISHED</state>
* <timeout>100</timeout>
* <mark>1</mark>
* <secmark>0</secmark>
* <id>453281439</id>
* <use>1</use>
* <assured/>
* </meta>
* </flow>
*/
const char *__proto2str(uint8_t protonum)
{
return proto2str[protonum] ? proto2str[protonum] : "unknown";
}
const char *__l3proto2str(uint8_t protonum)
{
return l3proto2str[protonum] ? l3proto2str[protonum] : "unknown";
}
static int __snprintf_ipv4_xml(char *buf,
unsigned int len,
const struct __nfct_tuple *tuple,
unsigned int type)
{
struct in_addr addr = {
.s_addr = (type == __ADDR_SRC) ? tuple->src.v4 : tuple->dst.v4,
};
return snprintf(buf, len, "%s", inet_ntoa(addr));
}
static int __snprintf_ipv6_xml(char *buf,
unsigned int len,
const struct __nfct_tuple *tuple,
unsigned int type)
{
struct in6_addr addr;
static char tmp[INET6_ADDRSTRLEN];
const void *p = (type == __ADDR_SRC) ? &tuple->src.v6 : &tuple->dst.v6;
memcpy(&addr, p, sizeof(struct in6_addr));
if (!inet_ntop(AF_INET6, &addr, tmp, sizeof(tmp)))
return -1;
return snprintf(buf, len, "%s", tmp);
}
int __snprintf_addr_xml(char *buf, unsigned int len,
const struct __nfct_tuple *tuple,
enum __nfct_addr type)
{
int ret;
unsigned int size = 0, offset = 0;
const char *tag = (type == __ADDR_SRC) ? "src" : "dst";
ret = snprintf(buf, len, "<%s>", tag);
BUFFER_SIZE(ret, size, len, offset);
switch (tuple->l3protonum) {
case AF_INET:
ret = __snprintf_ipv4_xml(buf+offset, len, tuple, type);
BUFFER_SIZE(ret, size, len, offset);
break;
case AF_INET6:
ret = __snprintf_ipv6_xml(buf+offset, len, tuple, type);
BUFFER_SIZE(ret, size, len, offset);
break;
}
ret = snprintf(buf+offset, len, "</%s>", tag);
BUFFER_SIZE(ret, size, len, offset);
return size;
}
int __snprintf_proto_xml(char *buf, unsigned int len,
const struct __nfct_tuple *tuple,
enum __nfct_addr type)
{
int ret = 0;
unsigned int size = 0, offset = 0;
switch(tuple->protonum) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
case IPPROTO_SCTP:
case IPPROTO_DCCP:
if (type == __ADDR_SRC) {
ret = snprintf(buf, len, "<sport>%u</sport>",
ntohs(tuple->l4src.tcp.port));
BUFFER_SIZE(ret, size, len, offset);
} else {
ret = snprintf(buf, len, "<dport>%u</dport>",
ntohs(tuple->l4dst.tcp.port));
BUFFER_SIZE(ret, size, len, offset);
}
break;
case IPPROTO_GRE:
if (type == __ADDR_SRC) {
ret = snprintf(buf, len, "<srckey>0x%x</srckey>",
ntohs(tuple->l4src.all));
BUFFER_SIZE(ret, size, len, offset);
} else {
ret = snprintf(buf, len, "<dstkey>0x%x</dstkey>",
ntohs(tuple->l4dst.all));
BUFFER_SIZE(ret, size, len, offset);
}
break;
}
return ret;
}
static int __snprintf_counters_xml(char *buf,
unsigned int len,
const struct nf_conntrack *ct,
unsigned int type)
{
int ret;
unsigned int size = 0, offset = 0;
ret = snprintf(buf, len, "<packets>%llu</packets>",
(unsigned long long)ct->counters[type].packets);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "<bytes>%llu</bytes>",
(unsigned long long)ct->counters[type].bytes);
BUFFER_SIZE(ret, size, len, offset);
return size;
}
static int
__snprintf_timestamp_start(char *buf, unsigned int len,
const struct nf_conntrack *ct)
{
int ret;
unsigned int size = 0, offset = 0;
ret = snprintf(buf, len, "<start>%llu</start>",
(unsigned long long)ct->timestamp.start);
BUFFER_SIZE(ret, size, len, offset);
return size;
}
static int
__snprintf_timestamp_stop(char *buf, unsigned int len,
const struct nf_conntrack *ct)
{
int ret;
unsigned int size = 0, offset = 0;
ret = snprintf(buf, len, "<stop>%llu</stop>",
(unsigned long long)ct->timestamp.stop);
BUFFER_SIZE(ret, size, len, offset);
return size;
}
static int
__snprintf_deltatime_now(char *buf, unsigned int len,
const struct nf_conntrack *ct)
{
int ret;
unsigned int size = 0, offset = 0;
time_t now, delta_time;
time(&now);
delta_time = now - (time_t)(ct->timestamp.start / NSEC_PER_SEC);
ret = snprintf(buf+offset, len, "<deltatime>%llu</deltatime>",
(unsigned long long)delta_time);
BUFFER_SIZE(ret, size, len, offset);
return size;
}
static int
__snprintf_deltatime(char *buf, unsigned int len, const struct nf_conntrack *ct)
{
int ret;
unsigned int size = 0, offset = 0;
time_t delta_time = (time_t)((ct->timestamp.stop -
ct->timestamp.start) / NSEC_PER_SEC);
ret = snprintf(buf+offset, len, "<deltatime>%llu</deltatime>",
(unsigned long long)delta_time);
BUFFER_SIZE(ret, size, len, offset);
return size;
}
static int
__snprintf_helper_name(char *buf, unsigned int len, const struct nf_conntrack *ct)
{
int ret;
unsigned int size = 0, offset = 0;
ret = snprintf(buf+offset, len, "<helper>%s</helper>", ct->helper_name);
BUFFER_SIZE(ret, size, len, offset);
return size;
}
int
__snprintf_localtime_xml(char *buf, unsigned int len, const struct tm *tm)
{
int ret = 0;
unsigned int size = 0, offset = 0;
ret = snprintf(buf+offset, len, "<hour>%d</hour>", tm->tm_hour);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "<min>%02d</min>", tm->tm_min);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "<sec>%02d</sec>", tm->tm_sec);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "<wday>%d</wday>", tm->tm_wday + 1);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "<day>%d</day>", tm->tm_mday);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "<month>%d</month>", tm->tm_mon + 1);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "<year>%d</year>", 1900 + tm->tm_year);
BUFFER_SIZE(ret, size, len, offset);
return size;
}
static int __snprintf_tuple_xml(char *buf,
unsigned int len,
const struct nf_conntrack *ct,
unsigned int dir, bool zone_incl)
{
int ret;
unsigned int size = 0, offset = 0;
const struct __nfct_tuple *tuple = NULL;
switch(dir) {
case __DIR_ORIG:
tuple = &ct->head.orig;
break;
case __DIR_REPL:
tuple = &ct->repl;
break;
}
ret = snprintf(buf, len, "<meta direction=\"%s\">",
dir == __DIR_ORIG ? "original" : "reply");
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len,
"<layer3 protonum=\"%d\" protoname=\"%s\">",
tuple->l3protonum, __l3proto2str(tuple->l3protonum));
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_addr_xml(buf+offset, len, tuple, __ADDR_SRC);
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_addr_xml(buf+offset, len, tuple, __ADDR_DST);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "</layer3>");
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len,
"<layer4 protonum=\"%d\" protoname=\"%s\">",
tuple->protonum, __proto2str(tuple->protonum));
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_proto_xml(buf+offset, len, tuple, __DIR_ORIG);
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_proto_xml(buf+offset, len, tuple, __DIR_REPL);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "</layer4>");
BUFFER_SIZE(ret, size, len, offset);
if (zone_incl) {
ret = snprintf(buf+offset, len, "<zone>%u</zone>", tuple->zone);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_ORIG_COUNTER_PACKETS, ct->head.set) &&
test_bit(ATTR_ORIG_COUNTER_BYTES, ct->head.set)) {
ret = snprintf(buf+offset, len, "<counters>");
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_counters_xml(buf+offset, len, ct, dir);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "</counters>");
BUFFER_SIZE(ret, size, len, offset);
}
ret = snprintf(buf+offset, len, "</meta>");
BUFFER_SIZE(ret, size, len, offset);
return size;
}
static int
__snprintf_clabels_xml(char *buf, unsigned int len,
const struct nf_conntrack *ct, struct nfct_labelmap *map)
{
const struct nfct_bitmask *b = nfct_get_attr(ct, ATTR_CONNLABELS);
int ret, size = 0, offset = 0;
if (!b)
return 0;
ret = snprintf(buf, len, "<labels>");
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_connlabels(buf + offset, len, map, b, "<label>%s</label>");
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf + offset, len, "</labels>");
BUFFER_SIZE(ret, size, len, offset);
return size;
}
int __snprintf_conntrack_xml(char *buf,
unsigned int len,
const struct nf_conntrack *ct,
const unsigned int msg_type,
const unsigned int flags,
struct nfct_labelmap *map)
{
int ret = 0;
unsigned int size = 0, offset = 0;
switch(msg_type) {
case NFCT_T_NEW:
ret = snprintf(buf, len, "<flow type=\"new\">");
break;
case NFCT_T_UPDATE:
ret = snprintf(buf, len, "<flow type=\"update\">");
break;
case NFCT_T_DESTROY:
ret = snprintf(buf, len, "<flow type=\"destroy\">");
break;
default:
ret = snprintf(buf, len, "<flow>");
break;
}
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_tuple_xml(buf+offset, len, ct, __DIR_ORIG,
test_bit(ATTR_ORIG_ZONE, ct->head.set));
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_tuple_xml(buf+offset, len, ct, __DIR_REPL,
test_bit(ATTR_REPL_ZONE, ct->head.set));
BUFFER_SIZE(ret, size, len, offset);
if (test_bit(ATTR_TCP_STATE, ct->head.set) ||
test_bit(ATTR_SCTP_STATE, ct->head.set) ||
test_bit(ATTR_DCCP_STATE, ct->head.set) ||
test_bit(ATTR_TIMEOUT, ct->head.set) ||
test_bit(ATTR_MARK, ct->head.set) ||
test_bit(ATTR_SECMARK, ct->head.set) ||
test_bit(ATTR_ZONE, ct->head.set) ||
test_bit(ATTR_USE, ct->head.set) ||
test_bit(ATTR_STATUS, ct->head.set) ||
test_bit(ATTR_ID, ct->head.set) ||
test_bit(ATTR_CONNLABELS, ct->head.set) ||
test_bit(ATTR_TIMESTAMP_START, ct->head.set) ||
test_bit(ATTR_TIMESTAMP_STOP, ct->head.set)) {
ret = snprintf(buf+offset, len,
"<meta direction=\"independent\">");
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_TCP_STATE, ct->head.set)) {
ret = snprintf(buf+offset, len, "<state>%s</state>",
ct->protoinfo.tcp.state < TCP_CONNTRACK_MAX ?
states[ct->protoinfo.tcp.state] :
states[TCP_CONNTRACK_NONE]);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_SCTP_STATE, ct->head.set)) {
ret = snprintf(buf+offset, len, "<state>%s</state>",
ct->protoinfo.sctp.state < SCTP_CONNTRACK_MAX ?
states[ct->protoinfo.sctp.state] :
states[SCTP_CONNTRACK_NONE]);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_DCCP_STATE, ct->head.set)) {
ret = snprintf(buf+offset, len, "<state>%s</state>",
ct->protoinfo.sctp.state < DCCP_CONNTRACK_MAX ?
states[ct->protoinfo.dccp.state] :
states[DCCP_CONNTRACK_NONE]);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_TIMEOUT, ct->head.set)) {
ret = snprintf(buf+offset, len,
"<timeout>%u</timeout>", ct->timeout);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_MARK, ct->head.set)) {
ret = snprintf(buf+offset, len, "<mark>%u</mark>", ct->mark);
BUFFER_SIZE(ret, size, len, offset);
}
if (map && test_bit(ATTR_CONNLABELS, ct->head.set)) {
ret = __snprintf_clabels_xml(buf+offset, len, ct, map);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_SECMARK, ct->head.set)) {
ret = snprintf(buf+offset, len,
"<secmark>%u</secmark>", ct->secmark);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_SECCTX, ct->head.set)) {
ret = snprintf(buf+offset, len,
"<secctx>%s</secctx>", ct->secctx);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_ZONE, ct->head.set)) {
ret = snprintf(buf+offset, len, "<zone>%u</zone>", ct->zone);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_USE, ct->head.set)) {
ret = snprintf(buf+offset, len, "<use>%u</use>", ct->use);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_ID, ct->head.set)) {
ret = snprintf(buf+offset, len, "<id>%u</id>", ct->id);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_STATUS, ct->head.set)
&& ct->status & IPS_ASSURED) {
ret = snprintf(buf+offset, len, "<assured/>");
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_STATUS, ct->head.set)
&& !(ct->status & IPS_SEEN_REPLY)) {
ret = snprintf(buf+offset, len, "<unreplied/>");
BUFFER_SIZE(ret, size, len, offset);
}
if (flags & NFCT_OF_TIMESTAMP) {
if (test_bit(ATTR_TIMESTAMP_START, ct->head.set) ||
test_bit(ATTR_TIMESTAMP_STOP, ct->head.set)) {
ret = snprintf(buf+offset, len, "<timestamp>");
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_TIMESTAMP_START, ct->head.set)) {
ret = __snprintf_timestamp_start(buf+offset, len, ct);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_TIMESTAMP_STOP, ct->head.set)) {
ret = __snprintf_timestamp_stop(buf+offset, len, ct);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_TIMESTAMP_START, ct->head.set) ||
test_bit(ATTR_TIMESTAMP_STOP, ct->head.set)) {
ret = snprintf(buf+offset, len, "</timestamp>");
BUFFER_SIZE(ret, size, len, offset);
}
}
if (test_bit(ATTR_TIMESTAMP_START, ct->head.set) &&
test_bit(ATTR_TIMESTAMP_STOP, ct->head.set)) {
ret = __snprintf_deltatime(buf+offset, len, ct);
BUFFER_SIZE(ret, size, len, offset);
} else if (test_bit(ATTR_TIMESTAMP_START, ct->head.set)) {
ret = __snprintf_deltatime_now(buf+offset, len, ct);
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_TCP_STATE, ct->head.set) ||
test_bit(ATTR_SCTP_STATE, ct->head.set) ||
test_bit(ATTR_DCCP_STATE, ct->head.set) ||
test_bit(ATTR_TIMEOUT, ct->head.set) ||
test_bit(ATTR_MARK, ct->head.set) ||
test_bit(ATTR_SECMARK, ct->head.set) ||
test_bit(ATTR_ZONE, ct->head.set) ||
test_bit(ATTR_USE, ct->head.set) ||
test_bit(ATTR_STATUS, ct->head.set) ||
test_bit(ATTR_ID, ct->head.set) ||
test_bit(ATTR_CONNLABELS, ct->head.set) ||
test_bit(ATTR_TIMESTAMP_START, ct->head.set) ||
test_bit(ATTR_TIMESTAMP_STOP, ct->head.set)) {
ret = snprintf(buf+offset, len, "</meta>");
BUFFER_SIZE(ret, size, len, offset);
}
if (flags & NFCT_OF_TIME) {
time_t t;
struct tm tm;
t = time(NULL);
if (localtime_r(&t, &tm) == NULL)
goto err_out;
ret = snprintf(buf+offset, len, "<when>");
BUFFER_SIZE(ret, size, len, offset);
ret = __snprintf_localtime_xml(buf+offset, len, &tm);
BUFFER_SIZE(ret, size, len, offset);
ret = snprintf(buf+offset, len, "</when>");
BUFFER_SIZE(ret, size, len, offset);
}
if (test_bit(ATTR_HELPER_NAME, ct->head.set)) {
ret = __snprintf_helper_name(buf+offset, len, ct);
BUFFER_SIZE(ret, size, len, offset);
}
err_out:
ret = snprintf(buf+offset, len, "</flow>");
BUFFER_SIZE(ret, size, len, offset);
return size;
}