| /* |
| * (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" |
| |
| static int __snprintf_l3protocol(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "%-8s %u ", |
| l3proto2str[ct->head.orig.l3protonum] == NULL ? |
| "unknown" : l3proto2str[ct->head.orig.l3protonum], |
| ct->head.orig.l3protonum)); |
| } |
| |
| int __snprintf_protocol(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "%-8s %u ", |
| proto2str[ct->head.orig.protonum] == NULL ? |
| "unknown" : proto2str[ct->head.orig.protonum], |
| ct->head.orig.protonum)); |
| } |
| |
| static int __snprintf_timeout(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| return snprintf(buf, len, "%u ", ct->timeout); |
| } |
| |
| static int __snprintf_protoinfo(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| return snprintf(buf, len, "%s ", |
| ct->protoinfo.tcp.state < TCP_CONNTRACK_MAX ? |
| states[ct->protoinfo.tcp.state] : |
| states[TCP_CONNTRACK_NONE]); |
| } |
| |
| static int __snprintf_protoinfo_sctp(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| return snprintf(buf, len, "%s ", |
| ct->protoinfo.sctp.state < SCTP_CONNTRACK_MAX ? |
| sctp_states[ct->protoinfo.sctp.state] : |
| sctp_states[SCTP_CONNTRACK_NONE]); |
| } |
| |
| static int __snprintf_protoinfo_dccp(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| return snprintf(buf, len, "%s ", |
| ct->protoinfo.dccp.state < DCCP_CONNTRACK_MAX ? |
| sctp_states[ct->protoinfo.dccp.state] : |
| sctp_states[DCCP_CONNTRACK_NONE]); |
| } |
| |
| static int __snprintf_address_ipv4(char *buf, |
| unsigned int len, |
| const struct __nfct_tuple *tuple, |
| const char *src_tag, |
| const char *dst_tag) |
| { |
| int ret, size = 0, offset = 0; |
| struct in_addr src = { .s_addr = tuple->src.v4 }; |
| struct in_addr dst = { .s_addr = tuple->dst.v4 }; |
| |
| ret = snprintf(buf, len, "%s=%s ", src_tag, inet_ntoa(src)); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| ret = snprintf(buf+offset, len, "%s=%s ", dst_tag, inet_ntoa(dst)); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| return size; |
| } |
| |
| static int __snprintf_address_ipv6(char *buf, |
| unsigned int len, |
| const struct __nfct_tuple *tuple, |
| const char *src_tag, |
| const char *dst_tag) |
| { |
| int ret, size = 0, offset = 0; |
| struct in6_addr src; |
| struct in6_addr dst; |
| char tmp[INET6_ADDRSTRLEN]; |
| |
| memcpy(&src, &tuple->src.v6, sizeof(struct in6_addr)); |
| memcpy(&dst, &tuple->dst.v6, sizeof(struct in6_addr)); |
| |
| if (!inet_ntop(AF_INET6, &src, tmp, sizeof(tmp))) |
| return -1; |
| |
| ret = snprintf(buf, len, "%s=%s ", src_tag, tmp); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| if (!inet_ntop(AF_INET6, &dst, tmp, sizeof(tmp))) |
| return -1; |
| |
| ret = snprintf(buf+offset, len-size, "%s=%s ", dst_tag, tmp); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| return size; |
| } |
| |
| int __snprintf_address(char *buf, |
| unsigned int len, |
| const struct __nfct_tuple *tuple, |
| const char *src_tag, |
| const char *dst_tag) |
| { |
| int size = 0; |
| |
| switch (tuple->l3protonum) { |
| case AF_INET: |
| size = __snprintf_address_ipv4(buf, len, tuple, |
| src_tag, dst_tag); |
| break; |
| case AF_INET6: |
| size = __snprintf_address_ipv6(buf, len, tuple, |
| src_tag, dst_tag); |
| break; |
| } |
| |
| return size; |
| } |
| |
| int __snprintf_proto(char *buf, |
| unsigned int len, |
| const struct __nfct_tuple *tuple) |
| { |
| int size = 0; |
| |
| switch(tuple->protonum) { |
| case IPPROTO_TCP: |
| case IPPROTO_UDP: |
| case IPPROTO_UDPLITE: |
| case IPPROTO_SCTP: |
| case IPPROTO_DCCP: |
| return snprintf(buf, len, "sport=%u dport=%u ", |
| ntohs(tuple->l4src.tcp.port), |
| ntohs(tuple->l4dst.tcp.port)); |
| break; |
| case IPPROTO_GRE: |
| return snprintf(buf, len, "srckey=0x%x dstkey=0x%x ", |
| ntohs(tuple->l4src.all), |
| ntohs(tuple->l4dst.all)); |
| break; |
| case IPPROTO_ICMP: |
| case IPPROTO_ICMPV6: |
| /* The ID only makes sense some ICMP messages but we want to |
| * display the same output that /proc/net/ip_conntrack does */ |
| return (snprintf(buf, len, "type=%d code=%d id=%d ", |
| tuple->l4dst.icmp.type, |
| tuple->l4dst.icmp.code, |
| ntohs(tuple->l4src.icmp.id))); |
| break; |
| } |
| |
| return size; |
| } |
| |
| static int |
| __snprintf_tuple_zone(char *buf, unsigned int len, const char *pfx, |
| const struct __nfct_tuple *tuple) |
| { |
| return (snprintf(buf, len, "zone-%s=%u ", pfx, tuple->zone)); |
| } |
| |
| static int __snprintf_status_assured(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| int size = 0; |
| |
| if (ct->status & IPS_ASSURED) |
| size = snprintf(buf, len, "[ASSURED] "); |
| |
| return size; |
| } |
| |
| static int __snprintf_status_not_seen_reply(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| int size = 0; |
| |
| if (!(ct->status & IPS_SEEN_REPLY)) |
| size = snprintf(buf, len, "[UNREPLIED] "); |
| |
| return size; |
| } |
| |
| static int __snprintf_counters(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct, |
| int dir) |
| { |
| return (snprintf(buf, len, "packets=%llu bytes=%llu ", |
| (unsigned long long) ct->counters[dir].packets, |
| (unsigned long long) ct->counters[dir].bytes)); |
| } |
| |
| static int |
| __snprintf_mark(char *buf, unsigned int len, const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "mark=%u ", ct->mark)); |
| } |
| |
| static int |
| __snprintf_secmark(char *buf, unsigned int len, const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "secmark=%u ", ct->secmark)); |
| } |
| |
| static int |
| __snprintf_use(char *buf, unsigned int len, const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "use=%u ", ct->use)); |
| } |
| |
| static int |
| __snprintf_id(char *buf, unsigned int len, const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "id=%u ", ct->id)); |
| } |
| |
| static int |
| __snprintf_zone(char *buf, unsigned int len, const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "zone=%u ", ct->zone)); |
| } |
| |
| static int |
| __snprintf_secctx(char *buf, unsigned int len, const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "secctx=%s ", ct->secctx)); |
| } |
| |
| static int |
| __snprintf_timestamp_start(char *buf, unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| time_t start = (time_t)(ct->timestamp.start / NSEC_PER_SEC); |
| char *tmp = ctime(&start); |
| |
| /* overwrite \n in the ctime() output. */ |
| tmp[strlen(tmp)-1] = '\0'; |
| return (snprintf(buf, len, "[start=%s] ", tmp)); |
| } |
| |
| static int |
| __snprintf_timestamp_stop(char *buf, unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| time_t stop = (time_t)(ct->timestamp.stop / NSEC_PER_SEC); |
| char *tmp = ctime(&stop); |
| |
| /* overwrite \n in the ctime() output. */ |
| tmp[strlen(tmp)-1] = '\0'; |
| return (snprintf(buf, len, "[stop=%s] ", tmp)); |
| } |
| |
| static int |
| __snprintf_timestamp_delta(char *buf, unsigned int len, |
| const struct nf_conntrack *ct) |
| { |
| time_t delta_time, stop; |
| |
| if (ct->timestamp.stop == 0) |
| time(&stop); |
| else |
| stop = (time_t)(ct->timestamp.stop / NSEC_PER_SEC); |
| |
| delta_time = stop - (time_t)(ct->timestamp.start / NSEC_PER_SEC); |
| |
| return (snprintf(buf, len, "delta-time=%llu ", |
| (unsigned long long)delta_time)); |
| } |
| |
| static int |
| __snprintf_helper_name(char *buf, unsigned int len, const struct nf_conntrack *ct) |
| { |
| return (snprintf(buf, len, "helper=%s ", ct->helper_name)); |
| } |
| |
| int |
| __snprintf_connlabels(char *buf, unsigned int len, |
| struct nfct_labelmap *map, |
| const struct nfct_bitmask *b, const char *fmt) |
| { |
| unsigned int i, max; |
| int ret, size = 0, offset = 0; |
| |
| max = nfct_bitmask_maxbit(b); |
| for (i = 0; i <= max && len; i++) { |
| const char *name; |
| if (!nfct_bitmask_test_bit(b, i)) |
| continue; |
| name = nfct_labelmap_get_name(map, i); |
| if (!name || strcmp(name, "") == 0) |
| continue; |
| |
| ret = snprintf(buf + offset, len, fmt, name); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| return size; |
| } |
| |
| static int |
| __snprintf_clabels(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, "%s,"); |
| |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| offset--; /* remove last , */ |
| size--; |
| ret = snprintf(buf + offset, len, " "); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| return size; |
| } |
| |
| int __snprintf_conntrack_default(char *buf, |
| unsigned int len, |
| const struct nf_conntrack *ct, |
| unsigned int msg_type, |
| unsigned int flags, |
| struct nfct_labelmap *map) |
| { |
| int ret = 0, size = 0, offset = 0; |
| |
| switch(msg_type) { |
| case NFCT_T_NEW: |
| ret = snprintf(buf, len, "%9s ", "[NEW]"); |
| break; |
| case NFCT_T_UPDATE: |
| ret = snprintf(buf, len, "%9s ", "[UPDATE]"); |
| break; |
| case NFCT_T_DESTROY: |
| ret = snprintf(buf, len, "%9s ", "[DESTROY]"); |
| break; |
| default: |
| break; |
| } |
| |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| if (flags & NFCT_OF_SHOW_LAYER3) { |
| ret = __snprintf_l3protocol(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| ret = __snprintf_protocol(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| if (test_bit(ATTR_TIMEOUT, ct->head.set)) { |
| ret = __snprintf_timeout(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_TCP_STATE, ct->head.set)) { |
| ret = __snprintf_protoinfo(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_SCTP_STATE, ct->head.set)) { |
| ret = __snprintf_protoinfo_sctp(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_DCCP_STATE, ct->head.set)) { |
| ret = __snprintf_protoinfo_dccp(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| ret = __snprintf_address(buf+offset, len, &ct->head.orig, |
| "src", "dst"); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| ret = __snprintf_proto(buf+offset, len, &ct->head.orig); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| if (test_bit(ATTR_ORIG_ZONE, ct->head.set)) { |
| ret = __snprintf_tuple_zone(buf+offset, len, "orig", &ct->head.orig); |
| 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_counters(buf+offset, len, ct, __DIR_ORIG); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_STATUS, ct->head.set)) { |
| ret = __snprintf_status_not_seen_reply(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| ret = __snprintf_address(buf+offset, len, &ct->repl, |
| "src", "dst"); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| ret = __snprintf_proto(buf+offset, len, &ct->repl); |
| BUFFER_SIZE(ret, size, len, offset); |
| |
| if (test_bit(ATTR_REPL_ZONE, ct->head.set)) { |
| ret = __snprintf_tuple_zone(buf+offset, len, "reply", &ct->repl); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_REPL_COUNTER_PACKETS, ct->head.set) && |
| test_bit(ATTR_REPL_COUNTER_BYTES, ct->head.set)) { |
| ret = __snprintf_counters(buf+offset, len, ct, __DIR_REPL); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_STATUS, ct->head.set)) { |
| ret = __snprintf_status_assured(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_MARK, ct->head.set)) { |
| ret = __snprintf_mark(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_SECMARK, ct->head.set)) { |
| ret = __snprintf_secmark(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_SECCTX, ct->head.set)) { |
| ret = __snprintf_secctx(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_ZONE, ct->head.set)) { |
| ret = __snprintf_zone(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_TIMESTAMP_START, ct->head.set)) { |
| ret = __snprintf_timestamp_delta(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| if (flags & NFCT_OF_TIMESTAMP) { |
| 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_HELPER_NAME, ct->head.set)) { |
| ret = __snprintf_helper_name(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (test_bit(ATTR_USE, ct->head.set)) { |
| ret = __snprintf_use(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (flags & NFCT_OF_ID && test_bit(ATTR_ID, ct->head.set)) { |
| ret = __snprintf_id(buf+offset, len, ct); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| if (map && test_bit(ATTR_CONNLABELS, ct->head.set)) { |
| ret = __snprintf_clabels(buf+offset, len, ct, map); |
| BUFFER_SIZE(ret, size, len, offset); |
| } |
| |
| /* Delete the last blank space */ |
| size--; |
| |
| return size; |
| } |