
The idea of a common handle is long revised and only misleading, nl_handle really represents a socket with some additional action handlers assigned to it. Alias for nl_handle is kept for backwards compatibility.
606 lines
14 KiB
C
606 lines
14 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-local.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 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,
|
|
nla_get_u16(tb[CTA_PROTO_SRC_PORT]));
|
|
if (tb[CTA_PROTO_DST_PORT])
|
|
nfnl_ct_set_dst_port(ct, repl,
|
|
nla_get_u16(tb[CTA_PROTO_DST_PORT]));
|
|
if (tb[CTA_PROTO_ICMP_ID])
|
|
nfnl_ct_set_icmp_id(ct, repl,
|
|
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]));
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
*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);
|
|
if (err < 0)
|
|
goto errout;
|
|
|
|
err = P_ACCEPT;
|
|
|
|
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,
|
|
nfnl_ct_get_src_port(ct, repl));
|
|
|
|
if (nfnl_ct_test_dst_port(ct, repl))
|
|
NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
|
|
nfnl_ct_get_dst_port(ct, repl));
|
|
|
|
if (nfnl_ct_test_icmp_id(ct, repl))
|
|
NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
|
|
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));
|
|
|
|
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 nl_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 nl_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 nl_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);
|
|
}
|
|
|
|
/** @} */
|