This tc action allows to work with vlan tagged skbs. Two supported sub-actions are header pop and header push. Signed-off-by: Jiri Pirko <jiri@resnulli.us> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net>tirimbino
parent
93515d53b1
commit
c7e2b9689e
@ -0,0 +1,27 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us> |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation; either version 2 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __NET_TC_VLAN_H |
||||||
|
#define __NET_TC_VLAN_H |
||||||
|
|
||||||
|
#include <net/act_api.h> |
||||||
|
|
||||||
|
#define VLAN_F_POP 0x1 |
||||||
|
#define VLAN_F_PUSH 0x2 |
||||||
|
|
||||||
|
struct tcf_vlan { |
||||||
|
struct tcf_common common; |
||||||
|
int tcfv_action; |
||||||
|
__be16 tcfv_push_vid; |
||||||
|
__be16 tcfv_push_proto; |
||||||
|
}; |
||||||
|
#define to_vlan(a) \ |
||||||
|
container_of(a->priv, struct tcf_vlan, common) |
||||||
|
|
||||||
|
#endif /* __NET_TC_VLAN_H */ |
@ -0,0 +1,35 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us> |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation; either version 2 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __LINUX_TC_VLAN_H |
||||||
|
#define __LINUX_TC_VLAN_H |
||||||
|
|
||||||
|
#include <linux/pkt_cls.h> |
||||||
|
|
||||||
|
#define TCA_ACT_VLAN 12 |
||||||
|
|
||||||
|
#define TCA_VLAN_ACT_POP 1 |
||||||
|
#define TCA_VLAN_ACT_PUSH 2 |
||||||
|
|
||||||
|
struct tc_vlan { |
||||||
|
tc_gen; |
||||||
|
int v_action; |
||||||
|
}; |
||||||
|
|
||||||
|
enum { |
||||||
|
TCA_VLAN_UNSPEC, |
||||||
|
TCA_VLAN_TM, |
||||||
|
TCA_VLAN_PARMS, |
||||||
|
TCA_VLAN_PUSH_VLAN_ID, |
||||||
|
TCA_VLAN_PUSH_VLAN_PROTOCOL, |
||||||
|
__TCA_VLAN_MAX, |
||||||
|
}; |
||||||
|
#define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1) |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,207 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us> |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation; either version 2 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <linux/module.h> |
||||||
|
#include <linux/init.h> |
||||||
|
#include <linux/kernel.h> |
||||||
|
#include <linux/skbuff.h> |
||||||
|
#include <linux/rtnetlink.h> |
||||||
|
#include <linux/if_vlan.h> |
||||||
|
#include <net/netlink.h> |
||||||
|
#include <net/pkt_sched.h> |
||||||
|
|
||||||
|
#include <linux/tc_act/tc_vlan.h> |
||||||
|
#include <net/tc_act/tc_vlan.h> |
||||||
|
|
||||||
|
#define VLAN_TAB_MASK 15 |
||||||
|
|
||||||
|
static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a, |
||||||
|
struct tcf_result *res) |
||||||
|
{ |
||||||
|
struct tcf_vlan *v = a->priv; |
||||||
|
int action; |
||||||
|
int err; |
||||||
|
|
||||||
|
spin_lock(&v->tcf_lock); |
||||||
|
v->tcf_tm.lastuse = jiffies; |
||||||
|
bstats_update(&v->tcf_bstats, skb); |
||||||
|
action = v->tcf_action; |
||||||
|
|
||||||
|
switch (v->tcfv_action) { |
||||||
|
case TCA_VLAN_ACT_POP: |
||||||
|
err = skb_vlan_pop(skb); |
||||||
|
if (err) |
||||||
|
goto drop; |
||||||
|
break; |
||||||
|
case TCA_VLAN_ACT_PUSH: |
||||||
|
err = skb_vlan_push(skb, v->tcfv_push_proto, v->tcfv_push_vid); |
||||||
|
if (err) |
||||||
|
goto drop; |
||||||
|
break; |
||||||
|
default: |
||||||
|
BUG(); |
||||||
|
} |
||||||
|
|
||||||
|
goto unlock; |
||||||
|
|
||||||
|
drop: |
||||||
|
action = TC_ACT_SHOT; |
||||||
|
v->tcf_qstats.drops++; |
||||||
|
unlock: |
||||||
|
spin_unlock(&v->tcf_lock); |
||||||
|
return action; |
||||||
|
} |
||||||
|
|
||||||
|
static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = { |
||||||
|
[TCA_VLAN_PARMS] = { .len = sizeof(struct tc_vlan) }, |
||||||
|
[TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 }, |
||||||
|
[TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 }, |
||||||
|
}; |
||||||
|
|
||||||
|
static int tcf_vlan_init(struct net *net, struct nlattr *nla, |
||||||
|
struct nlattr *est, struct tc_action *a, |
||||||
|
int ovr, int bind) |
||||||
|
{ |
||||||
|
struct nlattr *tb[TCA_VLAN_MAX + 1]; |
||||||
|
struct tc_vlan *parm; |
||||||
|
struct tcf_vlan *v; |
||||||
|
int action; |
||||||
|
__be16 push_vid = 0; |
||||||
|
__be16 push_proto = 0; |
||||||
|
int ret = 0; |
||||||
|
int err; |
||||||
|
|
||||||
|
if (!nla) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
err = nla_parse_nested(tb, TCA_VLAN_MAX, nla, vlan_policy); |
||||||
|
if (err < 0) |
||||||
|
return err; |
||||||
|
|
||||||
|
if (!tb[TCA_VLAN_PARMS]) |
||||||
|
return -EINVAL; |
||||||
|
parm = nla_data(tb[TCA_VLAN_PARMS]); |
||||||
|
switch (parm->v_action) { |
||||||
|
case TCA_VLAN_ACT_POP: |
||||||
|
break; |
||||||
|
case TCA_VLAN_ACT_PUSH: |
||||||
|
if (!tb[TCA_VLAN_PUSH_VLAN_ID]) |
||||||
|
return -EINVAL; |
||||||
|
push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); |
||||||
|
if (push_vid >= VLAN_VID_MASK) |
||||||
|
return -ERANGE; |
||||||
|
|
||||||
|
if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) { |
||||||
|
push_proto = nla_get_be16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]); |
||||||
|
switch (push_proto) { |
||||||
|
case htons(ETH_P_8021Q): |
||||||
|
case htons(ETH_P_8021AD): |
||||||
|
break; |
||||||
|
default: |
||||||
|
return -EPROTONOSUPPORT; |
||||||
|
} |
||||||
|
} else { |
||||||
|
push_proto = htons(ETH_P_8021Q); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
action = parm->v_action; |
||||||
|
|
||||||
|
if (!tcf_hash_check(parm->index, a, bind)) { |
||||||
|
ret = tcf_hash_create(parm->index, est, a, sizeof(*v), bind); |
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
|
||||||
|
ret = ACT_P_CREATED; |
||||||
|
} else { |
||||||
|
if (bind) |
||||||
|
return 0; |
||||||
|
tcf_hash_release(a, bind); |
||||||
|
if (!ovr) |
||||||
|
return -EEXIST; |
||||||
|
} |
||||||
|
|
||||||
|
v = to_vlan(a); |
||||||
|
|
||||||
|
spin_lock_bh(&v->tcf_lock); |
||||||
|
|
||||||
|
v->tcfv_action = action; |
||||||
|
v->tcfv_push_vid = push_vid; |
||||||
|
v->tcfv_push_proto = push_proto; |
||||||
|
|
||||||
|
v->tcf_action = parm->action; |
||||||
|
|
||||||
|
spin_unlock_bh(&v->tcf_lock); |
||||||
|
|
||||||
|
if (ret == ACT_P_CREATED) |
||||||
|
tcf_hash_insert(a); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a, |
||||||
|
int bind, int ref) |
||||||
|
{ |
||||||
|
unsigned char *b = skb_tail_pointer(skb); |
||||||
|
struct tcf_vlan *v = a->priv; |
||||||
|
struct tc_vlan opt = { |
||||||
|
.index = v->tcf_index, |
||||||
|
.refcnt = v->tcf_refcnt - ref, |
||||||
|
.bindcnt = v->tcf_bindcnt - bind, |
||||||
|
.action = v->tcf_action, |
||||||
|
.v_action = v->tcfv_action, |
||||||
|
}; |
||||||
|
struct tcf_t t; |
||||||
|
|
||||||
|
if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt)) |
||||||
|
goto nla_put_failure; |
||||||
|
|
||||||
|
if (v->tcfv_action == TCA_VLAN_ACT_PUSH && |
||||||
|
(nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, v->tcfv_push_vid) || |
||||||
|
nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->tcfv_push_proto))) |
||||||
|
goto nla_put_failure; |
||||||
|
|
||||||
|
t.install = jiffies_to_clock_t(jiffies - v->tcf_tm.install); |
||||||
|
t.lastuse = jiffies_to_clock_t(jiffies - v->tcf_tm.lastuse); |
||||||
|
t.expires = jiffies_to_clock_t(v->tcf_tm.expires); |
||||||
|
if (nla_put(skb, TCA_VLAN_TM, sizeof(t), &t)) |
||||||
|
goto nla_put_failure; |
||||||
|
return skb->len; |
||||||
|
|
||||||
|
nla_put_failure: |
||||||
|
nlmsg_trim(skb, b); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
static struct tc_action_ops act_vlan_ops = { |
||||||
|
.kind = "vlan", |
||||||
|
.type = TCA_ACT_VLAN, |
||||||
|
.owner = THIS_MODULE, |
||||||
|
.act = tcf_vlan, |
||||||
|
.dump = tcf_vlan_dump, |
||||||
|
.init = tcf_vlan_init, |
||||||
|
}; |
||||||
|
|
||||||
|
static int __init vlan_init_module(void) |
||||||
|
{ |
||||||
|
return tcf_register_action(&act_vlan_ops, VLAN_TAB_MASK); |
||||||
|
} |
||||||
|
|
||||||
|
static void __exit vlan_cleanup_module(void) |
||||||
|
{ |
||||||
|
tcf_unregister_action(&act_vlan_ops); |
||||||
|
} |
||||||
|
|
||||||
|
module_init(vlan_init_module); |
||||||
|
module_exit(vlan_cleanup_module); |
||||||
|
|
||||||
|
MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); |
||||||
|
MODULE_DESCRIPTION("vlan manipulation actions"); |
||||||
|
MODULE_LICENSE("GPL v2"); |
Loading…
Reference in new issue