dect
/
libnl
Archived
13
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
libnl/lib/netfilter/exp.c

618 lines
15 KiB
C

/*
* lib/netfilter/exp.c Conntrack Expectation
*
* 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>
* Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
*/
/**
* @ingroup nfnl
* @defgroup exp Expectation
* @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/exp.h>
static struct nl_cache_ops nfnl_exp_ops;
static struct nla_policy exp_policy[CTA_EXPECT_MAX+1] = {
[CTA_EXPECT_MASTER] = { .type = NLA_NESTED },
[CTA_EXPECT_TUPLE] = { .type = NLA_NESTED },
[CTA_EXPECT_MASK] = { .type = NLA_NESTED },
[CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 },
[CTA_EXPECT_ID] = { .type = NLA_U32 },
[CTA_EXPECT_HELP_NAME] = { .type = NLA_STRING },
[CTA_EXPECT_ZONE] = { .type = NLA_U16 },
[CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, // Added in kernel 2.6.37
[CTA_EXPECT_CLASS] = { .type = NLA_U32 }, // Added in kernel 3.5
[CTA_EXPECT_NAT] = { .type = NLA_NESTED }, // Added in kernel 3.5
[CTA_EXPECT_FN] = { .type = NLA_STRING }, // Added in kernel 3.5
};
static struct nla_policy exp_tuple_policy[CTA_TUPLE_MAX+1] = {
[CTA_TUPLE_IP] = { .type = NLA_NESTED },
[CTA_TUPLE_PROTO] = { .type = NLA_NESTED },
};
static struct nla_policy exp_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 exp_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 exp_nat_policy[CTA_EXPECT_NAT_MAX+1] = {
[CTA_EXPECT_NAT_DIR] = { .type = NLA_U8 },
[CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED },
};
static int exp_parse_ip(struct nfnl_exp *exp, int tuple, 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, exp_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_exp_set_src(exp, tuple, 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_exp_set_dst(exp, tuple, 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_exp_set_src(exp, tuple, 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_exp_set_dst(exp, tuple, addr);
nl_addr_put(addr);
if (err < 0)
goto errout;
}
return 0;
errout_enomem:
err = -NLE_NOMEM;
errout:
return err;
}
static int exp_parse_proto(struct nfnl_exp *exp, int tuple, struct nlattr *attr)
{
struct nlattr *tb[CTA_PROTO_MAX+1];
int err;
uint16_t srcport = 0, dstport = 0, icmpid = 0;
uint8_t icmptype = 0, icmpcode = 0;
err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, exp_proto_policy);
if (err < 0)
return err;
if (tb[CTA_PROTO_NUM])
nfnl_exp_set_l4protonum(exp, tuple, nla_get_u8(tb[CTA_PROTO_NUM]));
if (tb[CTA_PROTO_SRC_PORT])
srcport = ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT]));
if (tb[CTA_PROTO_DST_PORT])
dstport = ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT]));
if (tb[CTA_PROTO_SRC_PORT] || tb[CTA_PROTO_DST_PORT])
nfnl_exp_set_ports(exp, tuple, srcport, dstport);
if (tb[CTA_PROTO_ICMP_ID])
icmpid = ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID]));
if (tb[CTA_PROTO_ICMP_TYPE])
icmptype = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]);
if (tb[CTA_PROTO_ICMP_CODE])
icmpcode = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]);
if (tb[CTA_PROTO_ICMP_ID] || tb[CTA_PROTO_ICMP_TYPE] || tb[CTA_PROTO_ICMP_CODE])
nfnl_exp_set_icmp(exp, tuple, icmpid, icmptype, icmpcode);
return 0;
}
static int exp_parse_tuple(struct nfnl_exp *exp, int tuple, struct nlattr *attr)
{
struct nlattr *tb[CTA_TUPLE_MAX+1];
int err;
err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, exp_tuple_policy);
if (err < 0)
return err;
if (tb[CTA_TUPLE_IP]) {
err = exp_parse_ip(exp, tuple, tb[CTA_TUPLE_IP]);
if (err < 0)
return err;
}
if (tb[CTA_TUPLE_PROTO]) {
err = exp_parse_proto(exp, tuple, tb[CTA_TUPLE_PROTO]);
if (err < 0)
return err;
}
return 0;
}
static int exp_parse_nat(struct nfnl_exp *exp, struct nlattr *attr)
{
struct nlattr *tb[CTA_EXPECT_NAT_MAX+1];
int err;
err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_policy);
if (err < 0)
return err;
if (tb[CTA_EXPECT_NAT_DIR])
nfnl_exp_set_nat_dir(exp, nla_get_u8(tb[CTA_EXPECT_NAT_DIR]));
if (tb[CTA_EXPECT_NAT_TUPLE]) {
err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_NAT, tb[CTA_EXPECT_NAT_TUPLE]);
if (err < 0)
return err;
}
return 0;
}
int nfnlmsg_exp_group(struct nlmsghdr *nlh)
{
switch (nfnlmsg_subtype(nlh)) {
case IPCTNL_MSG_EXP_NEW:
if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL))
return NFNLGRP_CONNTRACK_EXP_NEW;
else
return NFNLGRP_CONNTRACK_EXP_UPDATE;
case IPCTNL_MSG_EXP_DELETE:
return NFNLGRP_CONNTRACK_EXP_DESTROY;
default:
return NFNLGRP_NONE;
}
}
int nfnlmsg_exp_parse(struct nlmsghdr *nlh, struct nfnl_exp **result)
{
struct nfnl_exp *exp;
struct nlattr *tb[CTA_MAX+1];
int err;
exp = nfnl_exp_alloc();
if (!exp)
return -NLE_NOMEM;
exp->ce_msgtype = nlh->nlmsg_type;
err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_EXPECT_MAX,
exp_policy);
if (err < 0)
goto errout;
nfnl_exp_set_family(exp, nfnlmsg_family(nlh));
if (tb[CTA_EXPECT_TUPLE]) {
err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_EXPECT, tb[CTA_EXPECT_TUPLE]);
if (err < 0)
goto errout;
}
if (tb[CTA_EXPECT_MASTER]) {
err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASTER, tb[CTA_EXPECT_MASTER]);
if (err < 0)
goto errout;
}
if (tb[CTA_EXPECT_MASK]) {
err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASK, tb[CTA_EXPECT_MASK]);
if (err < 0)
goto errout;
}
if (tb[CTA_EXPECT_NAT]) {
err = exp_parse_nat(exp, tb[CTA_EXPECT_MASK]);
if (err < 0)
goto errout;
}
if (tb[CTA_EXPECT_CLASS])
nfnl_exp_set_class(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_CLASS])));
if (tb[CTA_EXPECT_FN])
nfnl_exp_set_fn(exp, nla_data(tb[CTA_EXPECT_FN]));
if (tb[CTA_EXPECT_TIMEOUT])
nfnl_exp_set_timeout(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_TIMEOUT])));
if (tb[CTA_EXPECT_ID])
nfnl_exp_set_id(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_ID])));
if (tb[CTA_EXPECT_HELP_NAME])
nfnl_exp_set_helper_name(exp, nla_data(tb[CTA_EXPECT_HELP_NAME]));
if (tb[CTA_EXPECT_ZONE])
nfnl_exp_set_zone(exp, ntohs(nla_get_u16(tb[CTA_EXPECT_ZONE])));
if (tb[CTA_EXPECT_FLAGS])
nfnl_exp_set_flags(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_FLAGS])));
*result = exp;
return 0;
errout:
nfnl_exp_put(exp);
return err;
}
static int exp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
struct nlmsghdr *nlh, struct nl_parser_param *pp)
{
struct nfnl_exp *exp;
int err;
if ((err = nfnlmsg_exp_parse(nlh, &exp)) < 0)
goto errout;
err = pp->pp_cb((struct nl_object *) exp, pp);
errout:
nfnl_exp_put(exp);
return err;
}
int nfnl_exp_dump_request(struct nl_sock *sk)
{
return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET,
NLM_F_DUMP, AF_UNSPEC, 0);
}
static int exp_request_update(struct nl_cache *cache, struct nl_sock *sk)
{
return nfnl_exp_dump_request(sk);
}
static int exp_get_tuple_attr(int tuple)
{
int attr = 0;
switch (tuple) {
case CTA_EXPECT_MASTER:
attr = NFNL_EXP_TUPLE_MASTER;
break;
case CTA_EXPECT_MASK:
attr = NFNL_EXP_TUPLE_MASK;
break;
case CTA_EXPECT_NAT:
attr = NFNL_EXP_TUPLE_NAT;
break;
case CTA_EXPECT_TUPLE:
default :
attr = NFNL_EXP_TUPLE_EXPECT;
break;
}
return attr;
}
static int nfnl_exp_build_tuple(struct nl_msg *msg, const struct nfnl_exp *exp,
int cta)
{
struct nlattr *tuple, *ip, *proto;
struct nl_addr *addr;
int family;
family = nfnl_exp_get_family(exp);
int type = exp_get_tuple_attr(cta);
tuple = nla_nest_start(msg, cta);
if (!tuple)
goto nla_put_failure;
ip = nla_nest_start(msg, CTA_TUPLE_IP);
if (!ip)
goto nla_put_failure;
addr = nfnl_exp_get_src(exp, type);
if (addr)
NLA_PUT_ADDR(msg,
family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC,
addr);
addr = nfnl_exp_get_dst(exp, type);
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_exp_test_l4protonum(exp, type))
NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_exp_get_l4protonum(exp, type));
if (nfnl_exp_test_ports(exp, type)) {
NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT,
htons(nfnl_exp_get_src_port(exp, type)));
NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
htons(nfnl_exp_get_dst_port(exp, type)));
}
if (nfnl_exp_test_icmp(exp, type)) {
NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
htons(nfnl_exp_get_icmp_id(exp, type)));
NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE,
nfnl_exp_get_icmp_type(exp, type));
NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
nfnl_exp_get_icmp_code(exp, type));
}
nla_nest_end(msg, proto);
nla_nest_end(msg, tuple);
return 0;
nla_put_failure:
return -NLE_MSGSIZE;
}
static int nfnl_exp_build_nat(struct nl_msg *msg, const struct nfnl_exp *exp)
{
struct nlattr *nat;
int err;
nat = nla_nest_start(msg, CTA_EXPECT_NAT);
if (nfnl_exp_test_nat_dir(exp)) {
NLA_PUT_U8(msg, CTA_EXPECT_NAT_DIR,
nfnl_exp_get_nat_dir(exp));
}
if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_NAT_TUPLE)) < 0)
goto nla_put_failure;
nla_nest_end(msg, nat);
return 0;
nla_put_failure:
return -NLE_MSGSIZE;
}
static int nfnl_exp_build_message(const struct nfnl_exp *exp, int cmd, int flags,
struct nl_msg **result)
{
struct nl_msg *msg;
int err;
msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK_EXP, cmd, flags,
nfnl_exp_get_family(exp), 0);
if (msg == NULL)
return -NLE_NOMEM;
if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_TUPLE)) < 0)
goto err_out;
if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASTER)) < 0)
goto err_out;
if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASK)) < 0)
goto err_out;
if (nfnl_exp_test_src(exp, NFNL_EXP_TUPLE_NAT)) {
if ((err = nfnl_exp_build_nat(msg, exp)) < 0)
goto err_out;
}
if (nfnl_exp_test_class(exp))
NLA_PUT_U32(msg, CTA_EXPECT_CLASS, htonl(nfnl_exp_get_class(exp)));
if (nfnl_exp_test_fn(exp))
NLA_PUT_STRING(msg, CTA_EXPECT_FN, nfnl_exp_get_fn(exp));
if (nfnl_exp_test_id(exp))
NLA_PUT_U32(msg, CTA_EXPECT_ID, htonl(nfnl_exp_get_id(exp)));
if (nfnl_exp_test_timeout(exp))
NLA_PUT_U32(msg, CTA_EXPECT_TIMEOUT, htonl(nfnl_exp_get_timeout(exp)));
if (nfnl_exp_test_helper_name(exp))
NLA_PUT_STRING(msg, CTA_EXPECT_HELP_NAME, nfnl_exp_get_helper_name(exp));
if (nfnl_exp_test_zone(exp))
NLA_PUT_U16(msg, CTA_EXPECT_ZONE, htons(nfnl_exp_get_zone(exp)));
if (nfnl_exp_test_flags(exp))
NLA_PUT_U32(msg, CTA_EXPECT_FLAGS, htonl(nfnl_exp_get_flags(exp)));
*result = msg;
return 0;
nla_put_failure:
err_out:
nlmsg_free(msg);
return err;
}
int nfnl_exp_build_add_request(const struct nfnl_exp *exp, int flags,
struct nl_msg **result)
{
return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_NEW, flags, result);
}
int nfnl_exp_add(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
{
struct nl_msg *msg;
int err;
if ((err = nfnl_exp_build_add_request(exp, 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_exp_build_delete_request(const struct nfnl_exp *exp, int flags,
struct nl_msg **result)
{
return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_DELETE, flags, result);
}
int nfnl_exp_del(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
{
struct nl_msg *msg;
int err;
if ((err = nfnl_exp_build_delete_request(exp, 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_exp_build_query_request(const struct nfnl_exp *exp, int flags,
struct nl_msg **result)
{
return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_GET, flags, result);
}
int nfnl_exp_query(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
{
struct nl_msg *msg;
int err;
if ((err = nfnl_exp_build_query_request(exp, 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 expectation cache holding all expectations 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 expectations currently in the kernel.
*
* @return 0 on success or a negative error code.
*/
int nfnl_exp_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
{
return nl_cache_alloc_and_fill(&nfnl_exp_ops, sk, result);
}
/** @} */
/**
* @name Expectation Addition
* @{
*/
/** @} */
static struct nl_af_group exp_groups[] = {
{ AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_NEW },
{ AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_UPDATE },
{ AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_DESTROY },
{ END_OF_GROUP_LIST },
};
#define NFNLMSG_EXP_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK_EXP, (type))
static struct nl_cache_ops nfnl_exp_ops = {
.co_name = "netfilter/exp",
.co_hdrsize = NFNL_HDRLEN,
.co_msgtypes = {
{ NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_NEW), NL_ACT_NEW, "new" },
{ NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_GET), NL_ACT_GET, "get" },
{ NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_DELETE), NL_ACT_DEL, "del" },
END_OF_MSGTYPES_LIST,
},
.co_protocol = NETLINK_NETFILTER,
.co_groups = exp_groups,
.co_request_update = exp_request_update,
.co_msg_parser = exp_msg_parser,
.co_obj_ops = &exp_obj_ops,
};
static void __init exp_init(void)
{
nl_cache_mngt_register(&nfnl_exp_ops);
}
static void __exit exp_exit(void)
{
nl_cache_mngt_unregister(&nfnl_exp_ops);
}
/** @} */