libnl/lib/netfilter/ct.c
Holger Eitzenberger 18152ca916 ct: add ICMPv6 type,code and ID
Add ICMPv6 type, code and ID (if set) by using the already available
conntrack atttributes.

Currently the ICMPv6 conntrack objects in libnl are without type, code
and ID.  This e. g. is the output of nl_object_dump() without the patch:

  ipv6-icmp ::1 <-> ::1
  id 0xdd0871f0 family inet6 timeout 30s <NOREPLY,SNAT_INIT,DNAT_INIT>

The attached patch tries to solve that.  It then looks like

  ipv6-icmp ::1 <-> ::1 icmp type 128 code 0 id 28253
  id 0xdf3a11f0 family inet6 timeout 30s <SNAT_INIT,DNAT_INIT>

It is the 'small' approach, because it reuses the existing ICMP
attributes of the conntrack object (currently only used for IPv4).
This way I can avoid to add new _icmp6_get_, _icmp6_set_ and
_icmp6_test_ functions.

Signed-off-by: Holger Eitzenberger <holger@eitzenberger.org>
Signed-off-by: Thomas Graf <tgraf@suug.ch>
2013-04-02 11:42:59 +02:00

628 lines
15 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 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;
}
}
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);
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);
}
/** @} */