Commit 2a221988 authored by Thomas Graf's avatar Thomas Graf Committed by David S. Miller

[PKT_SCHED]: tc filter extension API

The tcf_exts API abstracts extensions such as actions/policers
into a generic layer and reduces the knowledge inside classifiers
to the minimum required. It isolates the validation code into
its own function to allow classifiers to validate all input
data before making changes and thus avoids the need to undo changes
if a extension configuration request cannot be fullfilled.

Adds missing locking when adding a action/police extension to an
already existing filter. Acquiring  dev->queue_lock makes sure we
don't change the action/police in the middle of a classification.
Noted by Patrick McHardy.

As a nice side effect, using this API removes the existing
ifdef clutter.

Usage:
  The classifier holds struct tcf_exts which may be empty if no
  extensions are compiled in. It then calls tcf_exts_validate
  when a new change request was received and provides a temporary
  tcf_exts copy to store the change requests. Given it succeeded
  the classifier may change its own parameters and at the end
  call tcf_exts_change to commit the changes and replace the
  existing extension configuration with the new one. The classifier
  is responsible to destroy his temporary copy if any of its own
  validation checks fail.

  The classifier specific TLV types must be exported to the extensions
  API via tcf_ext_map.

  Destroying the extensions is as easy as calling tcf_exts_destroy.

  The extensions are executed by the classifier by calling tcf_exts_exec
  which must be done as the last thing after making sure the
  filter matches. Note: A classifier might take further actions after
  the execution to tcf_exts_exec such as correcting its own cache to
  avoid caching results which could have been influenced by the extensions.

  tcf_exts_exec returns a negative error code if the filter must be
  considered unmatched, 0 on normal execution or a positive classifier
  return code (TC_ACT_*) which must be returned to the underlying layer
  as-is.
Signed-off-by: default avatarThomas Graf <tgraf@suug.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 31c0c632
...@@ -61,6 +61,93 @@ tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r) ...@@ -61,6 +61,93 @@ tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r)
tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
} }
struct tcf_exts
{
#ifdef CONFIG_NET_CLS_ACT
struct tc_action *action;
#elif defined CONFIG_NET_CLS_POLICE
struct tcf_police *police;
#endif
};
/* Map to export classifier specific extension TLV types to the
* generic extensions API. Unsupported extensions must be set to 0.
*/
struct tcf_ext_map
{
int action;
int police;
};
/**
* tcf_exts_is_predicative - check if a predicative extension is present
* @exts: tc filter extensions handle
*
* Returns 1 if a predicative extension is present, i.e. an extension which
* might cause further actions and thus overrule the regular tcf_result.
*/
static inline int
tcf_exts_is_predicative(struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
return !!exts->action;
#elif defined CONFIG_NET_CLS_POLICE
return !!exts->police;
#else
return 0;
#endif
}
/**
* tcf_exts_is_available - check if at least one extension is present
* @exts: tc filter extensions handle
*
* Returns 1 if at least one extension is present.
*/
static inline int
tcf_exts_is_available(struct tcf_exts *exts)
{
/* All non-predicative extensions must be added here. */
return tcf_exts_is_predicative(exts);
}
/**
* tcf_exts_exec - execute tc filter extensions
* @skb: socket buffer
* @exts: tc filter extensions handle
* @res: desired result
*
* Executes all configured extensions. Returns 0 on a normal execution,
* a negative number if the filter must be considered unmatched or
* a positive action code (TC_ACT_*) which must be returned to the
* underlying layer.
*/
static inline int
tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts,
struct tcf_result *res)
{
#ifdef CONFIG_NET_CLS_ACT
if (exts->action)
return tcf_action_exec(skb, exts->action, res);
#elif defined CONFIG_NET_CLS_POLICE
if (exts->police)
return tcf_police(skb, exts->police);
#endif
return 0;
}
extern int tcf_exts_validate(struct tcf_proto *tp, struct rtattr **tb,
struct rtattr *rate_tlv, struct tcf_exts *exts,
struct tcf_ext_map *map);
extern void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts);
extern void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
struct tcf_exts *src);
extern int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts,
struct tcf_ext_map *map);
extern int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts,
struct tcf_ext_map *map);
#ifdef CONFIG_NET_CLS_ACT #ifdef CONFIG_NET_CLS_ACT
static inline int static inline int
tcf_change_act_police(struct tcf_proto *tp, struct tc_action **action, tcf_change_act_police(struct tcf_proto *tp, struct tc_action **action,
......
...@@ -439,6 +439,154 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -439,6 +439,154 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len; return skb->len;
} }
void
tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
if (exts->action) {
tcf_action_destroy(exts->action, TCA_ACT_UNBIND);
exts->action = NULL;
}
#elif defined CONFIG_NET_CLS_POLICE
if (exts->police) {
tcf_police_release(exts->police, TCA_ACT_UNBIND);
exts->police = NULL;
}
#endif
}
int
tcf_exts_validate(struct tcf_proto *tp, struct rtattr **tb,
struct rtattr *rate_tlv, struct tcf_exts *exts,
struct tcf_ext_map *map)
{
memset(exts, 0, sizeof(*exts));
#ifdef CONFIG_NET_CLS_ACT
int err;
struct tc_action *act;
if (map->police && tb[map->police-1] && rate_tlv) {
act = tcf_action_init_1(tb[map->police-1], rate_tlv, "police",
TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err);
if (act == NULL)
return err;
act->type = TCA_OLD_COMPAT;
exts->action = act;
} else if (map->action && tb[map->action-1] && rate_tlv) {
act = tcf_action_init(tb[map->action-1], rate_tlv, NULL,
TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err);
if (act == NULL)
return err;
exts->action = act;
}
#elif defined CONFIG_NET_CLS_POLICE
if (map->police && tb[map->police-1] && rate_tlv) {
struct tcf_police *p;
p = tcf_police_locate(tb[map->police-1], rate_tlv);
if (p == NULL)
return -EINVAL;
exts->police = p;
} else if (map->action && tb[map->action-1])
return -EOPNOTSUPP;
#else
if ((map->action && tb[map->action-1]) ||
(map->police && tb[map->police-1]))
return -EOPNOTSUPP;
#endif
return 0;
}
void
tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
struct tcf_exts *src)
{
#ifdef CONFIG_NET_CLS_ACT
if (src->action) {
struct tc_action *act;
tcf_tree_lock(tp);
act = xchg(&dst->action, src->action);
tcf_tree_unlock(tp);
if (act)
tcf_action_destroy(act, TCA_ACT_UNBIND);
}
#elif defined CONFIG_NET_CLS_POLICE
if (src->police) {
struct tcf_police *p;
tcf_tree_lock(tp);
p = xchg(&dst->police, src->police);
tcf_tree_unlock(tp);
if (p)
tcf_police_release(p, TCA_ACT_UNBIND);
}
#endif
}
int
tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts,
struct tcf_ext_map *map)
{
#ifdef CONFIG_NET_CLS_ACT
if (map->action && exts->action) {
/*
* again for backward compatible mode - we want
* to work with both old and new modes of entering
* tc data even if iproute2 was newer - jhs
*/
struct rtattr * p_rta = (struct rtattr*) skb->tail;
if (exts->action->type != TCA_OLD_COMPAT) {
RTA_PUT(skb, map->action, 0, NULL);
if (tcf_action_dump(skb, exts->action, 0, 0) < 0)
goto rtattr_failure;
p_rta->rta_len = skb->tail - (u8*)p_rta;
} else if (map->police) {
RTA_PUT(skb, map->police, 0, NULL);
if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0)
goto rtattr_failure;
p_rta->rta_len = skb->tail - (u8*)p_rta;
}
}
#elif defined CONFIG_NET_CLS_POLICE
if (map->police && exts->police) {
struct rtattr * p_rta = (struct rtattr*) skb->tail;
RTA_PUT(skb, map->police, 0, NULL);
if (tcf_police_dump(skb, exts->police) < 0)
goto rtattr_failure;
p_rta->rta_len = skb->tail - (u8*)p_rta;
}
#endif
return 0;
rtattr_failure: __attribute__ ((unused))
return -1;
}
int
tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts,
struct tcf_ext_map *map)
{
#ifdef CONFIG_NET_CLS_ACT
if (exts->action)
if (tcf_action_copy_stats(skb, exts->action) < 0)
goto rtattr_failure;
#elif defined CONFIG_NET_CLS_POLICE
if (exts->police)
if (tcf_police_dump_stats(skb, exts->police) < 0)
goto rtattr_failure;
#endif
return 0;
rtattr_failure: __attribute__ ((unused))
return -1;
}
static int __init tc_filter_init(void) static int __init tc_filter_init(void)
{ {
...@@ -461,3 +609,8 @@ subsys_initcall(tc_filter_init); ...@@ -461,3 +609,8 @@ subsys_initcall(tc_filter_init);
EXPORT_SYMBOL(register_tcf_proto_ops); EXPORT_SYMBOL(register_tcf_proto_ops);
EXPORT_SYMBOL(unregister_tcf_proto_ops); EXPORT_SYMBOL(unregister_tcf_proto_ops);
EXPORT_SYMBOL(tcf_exts_validate);
EXPORT_SYMBOL(tcf_exts_destroy);
EXPORT_SYMBOL(tcf_exts_change);
EXPORT_SYMBOL(tcf_exts_dump);
EXPORT_SYMBOL(tcf_exts_dump_stats);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment