
Recent kernels support conntrack time stamping, which is a helpful feature to determine the duration of a flow without building a flow cache in your user space application, just to keep the 'start' time of your flow. Timestamps are recorded with nanosecond resolution once this feature is enabled. This patch adds optional support for the CTA_TIMESTAMP, then modifies the dump routine to write that info in a format similar to /proc/net/nf_conntrack. This is an example output when using NL_DUMP_LINE: udp 10.128.128.28:56836 <-> 10.128.129.255:8612 delta-time 30 Signed-off-by: Holger Eitzenberger <holger@eitzenberger.org> Signed-off-by: Thomas Graf <tgraf@suug.ch>
657 lines
16 KiB
C
657 lines
16 KiB
C
/*
|
|
* lib/netfilter/ct.c Conntrack
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation version 2.1
|
|
* of the License.
|
|
*
|
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
|
* Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
|
|
* Copyright (c) 2007 Secure Computing Corporation
|
|
* Copyright (c= 2008 Patrick McHardy <kaber@trash.net>
|
|
*/
|
|
|
|
/**
|
|
* @ingroup nfnl
|
|
* @defgroup ct Conntrack
|
|
* @brief
|
|
* @{
|
|
*/
|
|
|
|
#include <byteswap.h>
|
|
#include <sys/types.h>
|
|
#include <linux/netfilter/nfnetlink_conntrack.h>
|
|
|
|
#include <netlink-private/netlink.h>
|
|
#include <netlink/attr.h>
|
|
#include <netlink/netfilter/nfnl.h>
|
|
#include <netlink/netfilter/ct.h>
|
|
|
|
static struct nl_cache_ops nfnl_ct_ops;
|
|
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
static uint64_t ntohll(uint64_t x)
|
|
{
|
|
return x;
|
|
}
|
|
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
|
static uint64_t ntohll(uint64_t x)
|
|
{
|
|
return bswap_64(x);
|
|
}
|
|
#endif
|
|
|
|
static struct nla_policy ct_policy[CTA_MAX+1] = {
|
|
[CTA_TUPLE_ORIG] = { .type = NLA_NESTED },
|
|
[CTA_TUPLE_REPLY] = { .type = NLA_NESTED },
|
|
[CTA_STATUS] = { .type = NLA_U32 },
|
|
[CTA_PROTOINFO] = { .type = NLA_NESTED },
|
|
//[CTA_HELP]
|
|
//[CTA_NAT_SRC]
|
|
[CTA_TIMEOUT] = { .type = NLA_U32 },
|
|
[CTA_MARK] = { .type = NLA_U32 },
|
|
[CTA_COUNTERS_ORIG] = { .type = NLA_NESTED },
|
|
[CTA_COUNTERS_REPLY] = { .type = NLA_NESTED },
|
|
[CTA_USE] = { .type = NLA_U32 },
|
|
[CTA_ID] = { .type = NLA_U32 },
|
|
//[CTA_NAT_DST]
|
|
};
|
|
|
|
static struct nla_policy ct_tuple_policy[CTA_TUPLE_MAX+1] = {
|
|
[CTA_TUPLE_IP] = { .type = NLA_NESTED },
|
|
[CTA_TUPLE_PROTO] = { .type = NLA_NESTED },
|
|
};
|
|
|
|
static struct nla_policy ct_ip_policy[CTA_IP_MAX+1] = {
|
|
[CTA_IP_V4_SRC] = { .type = NLA_U32 },
|
|
[CTA_IP_V4_DST] = { .type = NLA_U32 },
|
|
[CTA_IP_V6_SRC] = { .minlen = 16 },
|
|
[CTA_IP_V6_DST] = { .minlen = 16 },
|
|
};
|
|
|
|
static struct nla_policy ct_proto_policy[CTA_PROTO_MAX+1] = {
|
|
[CTA_PROTO_NUM] = { .type = NLA_U8 },
|
|
[CTA_PROTO_SRC_PORT] = { .type = NLA_U16 },
|
|
[CTA_PROTO_DST_PORT] = { .type = NLA_U16 },
|
|
[CTA_PROTO_ICMP_ID] = { .type = NLA_U16 },
|
|
[CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 },
|
|
[CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 },
|
|
[CTA_PROTO_ICMPV6_ID] = { .type = NLA_U16 },
|
|
[CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 },
|
|
[CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 },
|
|
};
|
|
|
|
static struct nla_policy ct_protoinfo_policy[CTA_PROTOINFO_MAX+1] = {
|
|
[CTA_PROTOINFO_TCP] = { .type = NLA_NESTED },
|
|
};
|
|
|
|
static struct nla_policy ct_protoinfo_tcp_policy[CTA_PROTOINFO_TCP_MAX+1] = {
|
|
[CTA_PROTOINFO_TCP_STATE] = { .type = NLA_U8 },
|
|
[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NLA_U8 },
|
|
[CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NLA_U8 },
|
|
[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .minlen = 2 },
|
|
[CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .minlen = 2 },
|
|
|
|
};
|
|
|
|
static struct nla_policy ct_counters_policy[CTA_COUNTERS_MAX+1] = {
|
|
[CTA_COUNTERS_PACKETS] = { .type = NLA_U64 },
|
|
[CTA_COUNTERS_BYTES] = { .type = NLA_U64 },
|
|
[CTA_COUNTERS32_PACKETS]= { .type = NLA_U32 },
|
|
[CTA_COUNTERS32_BYTES] = { .type = NLA_U32 },
|
|
};
|
|
|
|
static struct nla_policy ct_timestamp_policy[CTA_TIMESTAMP_MAX + 1] = {
|
|
[CTA_TIMESTAMP_START] = { .type = NLA_U64 },
|
|
[CTA_TIMESTAMP_STOP] = { .type = NLA_U64 },
|
|
};
|
|
|
|
static int ct_parse_ip(struct nfnl_ct *ct, int repl, struct nlattr *attr)
|
|
{
|
|
struct nlattr *tb[CTA_IP_MAX+1];
|
|
struct nl_addr *addr;
|
|
int err;
|
|
|
|
err = nla_parse_nested(tb, CTA_IP_MAX, attr, ct_ip_policy);
|
|
if (err < 0)
|
|
goto errout;
|
|
|
|
if (tb[CTA_IP_V4_SRC]) {
|
|
addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET);
|
|
if (addr == NULL)
|
|
goto errout_enomem;
|
|
err = nfnl_ct_set_src(ct, repl, addr);
|
|
nl_addr_put(addr);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
if (tb[CTA_IP_V4_DST]) {
|
|
addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET);
|
|
if (addr == NULL)
|
|
goto errout_enomem;
|
|
err = nfnl_ct_set_dst(ct, repl, addr);
|
|
nl_addr_put(addr);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
if (tb[CTA_IP_V6_SRC]) {
|
|
addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6);
|
|
if (addr == NULL)
|
|
goto errout_enomem;
|
|
err = nfnl_ct_set_src(ct, repl, addr);
|
|
nl_addr_put(addr);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
if (tb[CTA_IP_V6_DST]) {
|
|
addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6);
|
|
if (addr == NULL)
|
|
goto errout_enomem;
|
|
err = nfnl_ct_set_dst(ct, repl, addr);
|
|
nl_addr_put(addr);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
|
|
return 0;
|
|
|
|
errout_enomem:
|
|
err = -NLE_NOMEM;
|
|
errout:
|
|
return err;
|
|
}
|
|
|
|
static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr)
|
|
{
|
|
struct nlattr *tb[CTA_PROTO_MAX+1];
|
|
int err;
|
|
|
|
err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, ct_proto_policy);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (!repl && tb[CTA_PROTO_NUM])
|
|
nfnl_ct_set_proto(ct, nla_get_u8(tb[CTA_PROTO_NUM]));
|
|
if (tb[CTA_PROTO_SRC_PORT])
|
|
nfnl_ct_set_src_port(ct, repl,
|
|
ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT])));
|
|
if (tb[CTA_PROTO_DST_PORT])
|
|
nfnl_ct_set_dst_port(ct, repl,
|
|
ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])));
|
|
|
|
if (ct->ct_family == AF_INET) {
|
|
if (tb[CTA_PROTO_ICMP_ID])
|
|
nfnl_ct_set_icmp_id(ct, repl,
|
|
ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])));
|
|
if (tb[CTA_PROTO_ICMP_TYPE])
|
|
nfnl_ct_set_icmp_type(ct, repl,
|
|
nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]));
|
|
if (tb[CTA_PROTO_ICMP_CODE])
|
|
nfnl_ct_set_icmp_code(ct, repl,
|
|
nla_get_u8(tb[CTA_PROTO_ICMP_CODE]));
|
|
} else if (ct->ct_family == AF_INET6) {
|
|
if (tb[CTA_PROTO_ICMPV6_ID])
|
|
nfnl_ct_set_icmp_id(ct, repl,
|
|
ntohs(nla_get_u16(tb[CTA_PROTO_ICMPV6_ID])));
|
|
if (tb[CTA_PROTO_ICMPV6_TYPE])
|
|
nfnl_ct_set_icmp_type(ct, repl,
|
|
nla_get_u8(tb[CTA_PROTO_ICMPV6_TYPE]));
|
|
if (tb[CTA_PROTO_ICMPV6_CODE])
|
|
nfnl_ct_set_icmp_code(ct, repl,
|
|
nla_get_u8(tb[CTA_PROTO_ICMPV6_CODE]));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ct_parse_tuple(struct nfnl_ct *ct, int repl, struct nlattr *attr)
|
|
{
|
|
struct nlattr *tb[CTA_TUPLE_MAX+1];
|
|
int err;
|
|
|
|
err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, ct_tuple_policy);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (tb[CTA_TUPLE_IP]) {
|
|
err = ct_parse_ip(ct, repl, tb[CTA_TUPLE_IP]);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
if (tb[CTA_TUPLE_PROTO]) {
|
|
err = ct_parse_proto(ct, repl, tb[CTA_TUPLE_PROTO]);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ct_parse_protoinfo_tcp(struct nfnl_ct *ct, struct nlattr *attr)
|
|
{
|
|
struct nlattr *tb[CTA_PROTOINFO_TCP_MAX+1];
|
|
int err;
|
|
|
|
err = nla_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr,
|
|
ct_protoinfo_tcp_policy);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (tb[CTA_PROTOINFO_TCP_STATE])
|
|
nfnl_ct_set_tcp_state(ct,
|
|
nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ct_parse_protoinfo(struct nfnl_ct *ct, struct nlattr *attr)
|
|
{
|
|
struct nlattr *tb[CTA_PROTOINFO_MAX+1];
|
|
int err;
|
|
|
|
err = nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr,
|
|
ct_protoinfo_policy);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (tb[CTA_PROTOINFO_TCP]) {
|
|
err = ct_parse_protoinfo_tcp(ct, tb[CTA_PROTOINFO_TCP]);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ct_parse_counters(struct nfnl_ct *ct, int repl, struct nlattr *attr)
|
|
{
|
|
struct nlattr *tb[CTA_COUNTERS_MAX+1];
|
|
int err;
|
|
|
|
err = nla_parse_nested(tb, CTA_COUNTERS_MAX, attr, ct_counters_policy);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (tb[CTA_COUNTERS_PACKETS])
|
|
nfnl_ct_set_packets(ct, repl,
|
|
ntohll(nla_get_u64(tb[CTA_COUNTERS_PACKETS])));
|
|
if (tb[CTA_COUNTERS32_PACKETS])
|
|
nfnl_ct_set_packets(ct, repl,
|
|
ntohl(nla_get_u32(tb[CTA_COUNTERS32_PACKETS])));
|
|
if (tb[CTA_COUNTERS_BYTES])
|
|
nfnl_ct_set_bytes(ct, repl,
|
|
ntohll(nla_get_u64(tb[CTA_COUNTERS_BYTES])));
|
|
if (tb[CTA_COUNTERS32_BYTES])
|
|
nfnl_ct_set_bytes(ct, repl,
|
|
ntohl(nla_get_u32(tb[CTA_COUNTERS32_BYTES])));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nfnlmsg_ct_group(struct nlmsghdr *nlh)
|
|
{
|
|
switch (nfnlmsg_subtype(nlh)) {
|
|
case IPCTNL_MSG_CT_NEW:
|
|
if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL))
|
|
return NFNLGRP_CONNTRACK_NEW;
|
|
else
|
|
return NFNLGRP_CONNTRACK_UPDATE;
|
|
case IPCTNL_MSG_CT_DELETE:
|
|
return NFNLGRP_CONNTRACK_DESTROY;
|
|
default:
|
|
return NFNLGRP_NONE;
|
|
}
|
|
}
|
|
|
|
static int ct_parse_timestamp(struct nfnl_ct *ct, struct nlattr *attr)
|
|
{
|
|
struct nlattr *tb[CTA_TIMESTAMP_MAX + 1];
|
|
int err;
|
|
|
|
err = nla_parse_nested(tb, CTA_TIMESTAMP_MAX, attr,
|
|
ct_timestamp_policy);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (tb[CTA_TIMESTAMP_START] && tb[CTA_TIMESTAMP_STOP])
|
|
nfnl_ct_set_timestamp(ct,
|
|
ntohll(nla_get_u64(tb[CTA_TIMESTAMP_START])),
|
|
ntohll(nla_get_u64(tb[CTA_TIMESTAMP_STOP])));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nfnlmsg_ct_parse(struct nlmsghdr *nlh, struct nfnl_ct **result)
|
|
{
|
|
struct nfnl_ct *ct;
|
|
struct nlattr *tb[CTA_MAX+1];
|
|
int err;
|
|
|
|
ct = nfnl_ct_alloc();
|
|
if (!ct)
|
|
return -NLE_NOMEM;
|
|
|
|
ct->ce_msgtype = nlh->nlmsg_type;
|
|
|
|
err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_MAX,
|
|
ct_policy);
|
|
if (err < 0)
|
|
goto errout;
|
|
|
|
nfnl_ct_set_family(ct, nfnlmsg_family(nlh));
|
|
|
|
if (tb[CTA_TUPLE_ORIG]) {
|
|
err = ct_parse_tuple(ct, 0, tb[CTA_TUPLE_ORIG]);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
if (tb[CTA_TUPLE_REPLY]) {
|
|
err = ct_parse_tuple(ct, 1, tb[CTA_TUPLE_REPLY]);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
|
|
if (tb[CTA_PROTOINFO]) {
|
|
err = ct_parse_protoinfo(ct, tb[CTA_PROTOINFO]);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
|
|
if (tb[CTA_STATUS])
|
|
nfnl_ct_set_status(ct, ntohl(nla_get_u32(tb[CTA_STATUS])));
|
|
if (tb[CTA_TIMEOUT])
|
|
nfnl_ct_set_timeout(ct, ntohl(nla_get_u32(tb[CTA_TIMEOUT])));
|
|
if (tb[CTA_MARK])
|
|
nfnl_ct_set_mark(ct, ntohl(nla_get_u32(tb[CTA_MARK])));
|
|
if (tb[CTA_USE])
|
|
nfnl_ct_set_use(ct, ntohl(nla_get_u32(tb[CTA_USE])));
|
|
if (tb[CTA_ID])
|
|
nfnl_ct_set_id(ct, ntohl(nla_get_u32(tb[CTA_ID])));
|
|
|
|
if (tb[CTA_COUNTERS_ORIG]) {
|
|
err = ct_parse_counters(ct, 0, tb[CTA_COUNTERS_ORIG]);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
|
|
if (tb[CTA_COUNTERS_REPLY]) {
|
|
err = ct_parse_counters(ct, 1, tb[CTA_COUNTERS_REPLY]);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
|
|
if (tb[CTA_TIMESTAMP]) {
|
|
err = ct_parse_timestamp(ct, tb[CTA_TIMESTAMP]);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
|
|
*result = ct;
|
|
return 0;
|
|
|
|
errout:
|
|
nfnl_ct_put(ct);
|
|
return err;
|
|
}
|
|
|
|
static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
|
|
struct nlmsghdr *nlh, struct nl_parser_param *pp)
|
|
{
|
|
struct nfnl_ct *ct;
|
|
int err;
|
|
|
|
if ((err = nfnlmsg_ct_parse(nlh, &ct)) < 0)
|
|
goto errout;
|
|
|
|
err = pp->pp_cb((struct nl_object *) ct, pp);
|
|
errout:
|
|
nfnl_ct_put(ct);
|
|
return err;
|
|
}
|
|
|
|
int nfnl_ct_dump_request(struct nl_sock *sk)
|
|
{
|
|
return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET,
|
|
NLM_F_DUMP, AF_UNSPEC, 0);
|
|
}
|
|
|
|
static int ct_request_update(struct nl_cache *cache, struct nl_sock *sk)
|
|
{
|
|
return nfnl_ct_dump_request(sk);
|
|
}
|
|
|
|
static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct,
|
|
int repl)
|
|
{
|
|
struct nlattr *tuple, *ip, *proto;
|
|
struct nl_addr *addr;
|
|
int family;
|
|
|
|
family = nfnl_ct_get_family(ct);
|
|
|
|
tuple = nla_nest_start(msg, repl ? CTA_TUPLE_REPLY : CTA_TUPLE_ORIG);
|
|
if (!tuple)
|
|
goto nla_put_failure;
|
|
|
|
ip = nla_nest_start(msg, CTA_TUPLE_IP);
|
|
if (!ip)
|
|
goto nla_put_failure;
|
|
|
|
addr = nfnl_ct_get_src(ct, repl);
|
|
if (addr)
|
|
NLA_PUT_ADDR(msg,
|
|
family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC,
|
|
addr);
|
|
|
|
addr = nfnl_ct_get_dst(ct, repl);
|
|
if (addr)
|
|
NLA_PUT_ADDR(msg,
|
|
family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST,
|
|
addr);
|
|
|
|
nla_nest_end(msg, ip);
|
|
|
|
proto = nla_nest_start(msg, CTA_TUPLE_PROTO);
|
|
if (!proto)
|
|
goto nla_put_failure;
|
|
|
|
if (nfnl_ct_test_proto(ct))
|
|
NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_ct_get_proto(ct));
|
|
|
|
if (nfnl_ct_test_src_port(ct, repl))
|
|
NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT,
|
|
htons(nfnl_ct_get_src_port(ct, repl)));
|
|
|
|
if (nfnl_ct_test_dst_port(ct, repl))
|
|
NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
|
|
htons(nfnl_ct_get_dst_port(ct, repl)));
|
|
|
|
if (family == AF_INET) {
|
|
if (nfnl_ct_test_icmp_id(ct, repl))
|
|
NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
|
|
htons(nfnl_ct_get_icmp_id(ct, repl)));
|
|
|
|
if (nfnl_ct_test_icmp_type(ct, repl))
|
|
NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE,
|
|
nfnl_ct_get_icmp_type(ct, repl));
|
|
|
|
if (nfnl_ct_test_icmp_code(ct, repl))
|
|
NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
|
|
nfnl_ct_get_icmp_code(ct, repl));
|
|
} else if (family == AF_INET6) {
|
|
if (nfnl_ct_test_icmp_id(ct, repl))
|
|
NLA_PUT_U16(msg, CTA_PROTO_ICMPV6_ID,
|
|
htons(nfnl_ct_get_icmp_id(ct, repl)));
|
|
|
|
if (nfnl_ct_test_icmp_type(ct, repl))
|
|
NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_TYPE,
|
|
nfnl_ct_get_icmp_type(ct, repl));
|
|
|
|
if (nfnl_ct_test_icmp_code(ct, repl))
|
|
NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_CODE,
|
|
nfnl_ct_get_icmp_code(ct, repl));
|
|
}
|
|
|
|
nla_nest_end(msg, proto);
|
|
|
|
nla_nest_end(msg, tuple);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
return -NLE_MSGSIZE;
|
|
}
|
|
|
|
static int nfnl_ct_build_message(const struct nfnl_ct *ct, int cmd, int flags,
|
|
struct nl_msg **result)
|
|
{
|
|
struct nl_msg *msg;
|
|
int err;
|
|
|
|
msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK, cmd, flags,
|
|
nfnl_ct_get_family(ct), 0);
|
|
if (msg == NULL)
|
|
return -NLE_NOMEM;
|
|
|
|
if ((err = nfnl_ct_build_tuple(msg, ct, 0)) < 0)
|
|
goto err_out;
|
|
|
|
*result = msg;
|
|
return 0;
|
|
|
|
err_out:
|
|
nlmsg_free(msg);
|
|
return err;
|
|
}
|
|
|
|
int nfnl_ct_build_add_request(const struct nfnl_ct *ct, int flags,
|
|
struct nl_msg **result)
|
|
{
|
|
return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_NEW, flags, result);
|
|
}
|
|
|
|
int nfnl_ct_add(struct nl_sock *sk, const struct nfnl_ct *ct, int flags)
|
|
{
|
|
struct nl_msg *msg;
|
|
int err;
|
|
|
|
if ((err = nfnl_ct_build_add_request(ct, flags, &msg)) < 0)
|
|
return err;
|
|
|
|
err = nl_send_auto_complete(sk, msg);
|
|
nlmsg_free(msg);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return wait_for_ack(sk);
|
|
}
|
|
|
|
int nfnl_ct_build_delete_request(const struct nfnl_ct *ct, int flags,
|
|
struct nl_msg **result)
|
|
{
|
|
return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_DELETE, flags, result);
|
|
}
|
|
|
|
int nfnl_ct_del(struct nl_sock *sk, const struct nfnl_ct *ct, int flags)
|
|
{
|
|
struct nl_msg *msg;
|
|
int err;
|
|
|
|
if ((err = nfnl_ct_build_delete_request(ct, flags, &msg)) < 0)
|
|
return err;
|
|
|
|
err = nl_send_auto_complete(sk, msg);
|
|
nlmsg_free(msg);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return wait_for_ack(sk);
|
|
}
|
|
|
|
int nfnl_ct_build_query_request(const struct nfnl_ct *ct, int flags,
|
|
struct nl_msg **result)
|
|
{
|
|
return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_GET, flags, result);
|
|
}
|
|
|
|
int nfnl_ct_query(struct nl_sock *sk, const struct nfnl_ct *ct, int flags)
|
|
{
|
|
struct nl_msg *msg;
|
|
int err;
|
|
|
|
if ((err = nfnl_ct_build_query_request(ct, flags, &msg)) < 0)
|
|
return err;
|
|
|
|
err = nl_send_auto_complete(sk, msg);
|
|
nlmsg_free(msg);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return wait_for_ack(sk);
|
|
}
|
|
|
|
/**
|
|
* @name Cache Management
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Build a conntrack cache holding all conntrack currently in the kernel
|
|
* @arg sk Netlink socket.
|
|
* @arg result Pointer to store resulting cache.
|
|
*
|
|
* Allocates a new cache, initializes it properly and updates it to
|
|
* contain all conntracks currently in the kernel.
|
|
*
|
|
* @return 0 on success or a negative error code.
|
|
*/
|
|
int nfnl_ct_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
|
|
{
|
|
return nl_cache_alloc_and_fill(&nfnl_ct_ops, sk, result);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Conntrack Addition
|
|
* @{
|
|
*/
|
|
|
|
/** @} */
|
|
|
|
static struct nl_af_group ct_groups[] = {
|
|
{ AF_UNSPEC, NFNLGRP_CONNTRACK_NEW },
|
|
{ AF_UNSPEC, NFNLGRP_CONNTRACK_UPDATE },
|
|
{ AF_UNSPEC, NFNLGRP_CONNTRACK_DESTROY },
|
|
{ END_OF_GROUP_LIST },
|
|
};
|
|
|
|
#define NFNLMSG_CT_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK, (type))
|
|
static struct nl_cache_ops nfnl_ct_ops = {
|
|
.co_name = "netfilter/ct",
|
|
.co_hdrsize = NFNL_HDRLEN,
|
|
.co_msgtypes = {
|
|
{ NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_NEW), NL_ACT_NEW, "new" },
|
|
{ NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_GET), NL_ACT_GET, "get" },
|
|
{ NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_DELETE), NL_ACT_DEL, "del" },
|
|
END_OF_MSGTYPES_LIST,
|
|
},
|
|
.co_protocol = NETLINK_NETFILTER,
|
|
.co_groups = ct_groups,
|
|
.co_request_update = ct_request_update,
|
|
.co_msg_parser = ct_msg_parser,
|
|
.co_obj_ops = &ct_obj_ops,
|
|
};
|
|
|
|
static void __init ct_init(void)
|
|
{
|
|
nl_cache_mngt_register(&nfnl_ct_ops);
|
|
}
|
|
|
|
static void __exit ct_exit(void)
|
|
{
|
|
nl_cache_mngt_unregister(&nfnl_ct_ops);
|
|
}
|
|
|
|
/** @} */
|