Skip to content

Commit

Permalink
netfilter: cttimeout: allow to set/get default protocol timeouts
Browse files Browse the repository at this point in the history
Default timeouts are currently set via proc/sysctl interface, the
typical pattern is a file name like:

/proc/sys/net/netfilter/nf_conntrack_PROTOCOL_timeout_STATE

This results in one entry per default protocol state timeout.
This patch simplifies this by allowing to set default protocol
timeouts via cttimeout netlink interface.

This should allow us to get rid of the existing proc/sysctl code
in the midterm.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Pablo Neira Ayuso committed Oct 1, 2013
1 parent 180cf72 commit 91cb498
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 8 deletions.
2 changes: 2 additions & 0 deletions include/uapi/linux/netfilter/nfnetlink_cttimeout.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ enum ctnl_timeout_msg_types {
IPCTNL_MSG_TIMEOUT_NEW,
IPCTNL_MSG_TIMEOUT_GET,
IPCTNL_MSG_TIMEOUT_DELETE,
IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
IPCTNL_MSG_TIMEOUT_DEFAULT_GET,

IPCTNL_MSG_TIMEOUT_MAX
};
Expand Down
161 changes: 153 additions & 8 deletions net/netfilter/nfnetlink_cttimeout.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
};

static int
ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
struct nf_conntrack_l4proto *l4proto,
struct net *net,
const struct nlattr *attr)
ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto,
struct net *net, const struct nlattr *attr)
{
int ret = 0;

Expand All @@ -64,8 +62,7 @@ ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
if (ret < 0)
return ret;

ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net,
&timeout->data);
ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeouts);
}
return ret;
}
Expand Down Expand Up @@ -123,7 +120,8 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
goto err_proto_put;
}

ret = ctnl_timeout_parse_policy(matching, l4proto, net,
ret = ctnl_timeout_parse_policy(&matching->data,
l4proto, net,
cda[CTA_TIMEOUT_DATA]);
return ret;
}
Expand All @@ -138,7 +136,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
goto err_proto_put;
}

ret = ctnl_timeout_parse_policy(timeout, l4proto, net,
ret = ctnl_timeout_parse_policy(&timeout->data, l4proto, net,
cda[CTA_TIMEOUT_DATA]);
if (ret < 0)
goto err;
Expand Down Expand Up @@ -342,6 +340,147 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
return ret;
}

static int
cttimeout_default_set(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
struct net *net = sock_net(skb->sk);
unsigned int *timeouts;
int ret;

if (!cda[CTA_TIMEOUT_L3PROTO] ||
!cda[CTA_TIMEOUT_L4PROTO] ||
!cda[CTA_TIMEOUT_DATA])
return -EINVAL;

l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
l4proto = nf_ct_l4proto_find_get(l3num, l4num);

/* This protocol is not supported, skip. */
if (l4proto->l4proto != l4num) {
ret = -EOPNOTSUPP;
goto err;
}

timeouts = l4proto->get_timeouts(net);

ret = ctnl_timeout_parse_policy(timeouts, l4proto, net,
cda[CTA_TIMEOUT_DATA]);
if (ret < 0)
goto err;

nf_ct_l4proto_put(l4proto);
return 0;
err:
nf_ct_l4proto_put(l4proto);
return ret;
}

static int
cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid,
u32 seq, u32 type, int event,
struct nf_conntrack_l4proto *l4proto)
{
struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg;
unsigned int flags = portid ? NLM_F_MULTI : 0;

event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
if (nlh == NULL)
goto nlmsg_failure;

nfmsg = nlmsg_data(nlh);
nfmsg->nfgen_family = AF_UNSPEC;
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = 0;

if (nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(l4proto->l3proto)) ||
nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto))
goto nla_put_failure;

if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) {
struct nlattr *nest_parms;
unsigned int *timeouts = l4proto->get_timeouts(net);
int ret;

nest_parms = nla_nest_start(skb,
CTA_TIMEOUT_DATA | NLA_F_NESTED);
if (!nest_parms)
goto nla_put_failure;

ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, timeouts);
if (ret < 0)
goto nla_put_failure;

nla_nest_end(skb, nest_parms);
}

nlmsg_end(skb, nlh);
return skb->len;

nlmsg_failure:
nla_put_failure:
nlmsg_cancel(skb, nlh);
return -1;
}

static int cttimeout_default_get(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
struct net *net = sock_net(skb->sk);
struct sk_buff *skb2;
int ret, err;

if (!cda[CTA_TIMEOUT_L3PROTO] || !cda[CTA_TIMEOUT_L4PROTO])
return -EINVAL;

l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
l4proto = nf_ct_l4proto_find_get(l3num, l4num);

/* This protocol is not supported, skip. */
if (l4proto->l4proto != l4num) {
err = -EOPNOTSUPP;
goto err;
}

skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (skb2 == NULL) {
err = -ENOMEM;
goto err;
}

ret = cttimeout_default_fill_info(net, skb2, NETLINK_CB(skb).portid,
nlh->nlmsg_seq,
NFNL_MSG_TYPE(nlh->nlmsg_type),
IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
l4proto);
if (ret <= 0) {
kfree_skb(skb2);
err = -ENOMEM;
goto err;
}
ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
if (ret > 0)
ret = 0;

/* this avoids a loop in nfnetlink. */
return ret == -EAGAIN ? -ENOBUFS : ret;
err:
nf_ct_l4proto_put(l4proto);
return err;
}

#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
{
Expand Down Expand Up @@ -384,6 +523,12 @@ static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
[IPCTNL_MSG_TIMEOUT_DELETE] = { .call = cttimeout_del_timeout,
.attr_count = CTA_TIMEOUT_MAX,
.policy = cttimeout_nla_policy },
[IPCTNL_MSG_TIMEOUT_DEFAULT_SET]= { .call = cttimeout_default_set,
.attr_count = CTA_TIMEOUT_MAX,
.policy = cttimeout_nla_policy },
[IPCTNL_MSG_TIMEOUT_DEFAULT_GET]= { .call = cttimeout_default_get,
.attr_count = CTA_TIMEOUT_MAX,
.policy = cttimeout_nla_policy },
};

static const struct nfnetlink_subsystem cttimeout_subsys = {
Expand Down

0 comments on commit 91cb498

Please sign in to comment.