From bd20869df5c936a17fe3ed70bf3b70076a9f7fef Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Mon, 8 Aug 2022 15:56:32 -0700 Subject: [PATCH] net/sched: act_vlan: Add {POP,PUSH}_ETH actions Implement TCA_VLAN_ACT_POP_ETH and TCA_VLAN_ACT_PUSH_ETH, to respectively pop and push a base Ethernet header at the beginning of a frame. POP_ETH is just a matter of pulling ETH_HLEN bytes. VLAN tags, if any, must be stripped before calling POP_ETH. PUSH_ETH is restricted to skbs with no mac_header, and only the MAC addresses can be configured. The Ethertype is automatically set from skb->protocol. These restrictions ensure that all skb's fields remain consistent, so that this action can't confuse other part of the networking stack (like GSO). Since openvswitch already had these actions, consolidate the code in skbuff.c (like for vlan and mpls push/pop). Change-Id: Ifce1d5fee3eb1741bdc88b22f9000226744cc56c Signed-off-by: Guillaume Nault gnault@redhat.com Signed-off-by: David S. Miller davem@davemloft.net Git-commit: 19fbcb36a39eefbe8912a13ccc02e937b1c418d6 Git-repo: https://android.googlesource.com/kernel/common/ [quic_jguidry@quicinc.com: Added CONFIG_NET_SCHED_ACT_VLAN_QGKI to sdxlemur.config] Signed-off-by: James Wyatt Guidry --- arch/arm/configs/vendor/sdxlemur.config | 1 + include/linux/skbuff.h | 5 ++ include/net/tc_act/tc_vlan.h | 4 ++ include/uapi/linux/tc_act/tc_vlan.h | 4 ++ net/core/skbuff.c | 69 +++++++++++++++++++++++++ net/sched/Kconfig | 13 +++++ net/sched/act_vlan.c | 52 +++++++++++++++++++ 7 files changed, 148 insertions(+) diff --git a/arch/arm/configs/vendor/sdxlemur.config b/arch/arm/configs/vendor/sdxlemur.config index 78d0b96c71a9..d7efcc09d7ac 100644 --- a/arch/arm/configs/vendor/sdxlemur.config +++ b/arch/arm/configs/vendor/sdxlemur.config @@ -383,3 +383,4 @@ CONFIG_SDX_EXT_IPC=y # CONFIG_SCHED_DEBUG is not set CONFIG_MACSEC=y CONFIG_SYSVIPC=y +CONFIG_NET_SCHED_ACT_VLAN_QGKI=y diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index de1426af630b..8a4644a2cd73 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3563,6 +3563,11 @@ int skb_ensure_writable(struct sk_buff *skb, int write_len); int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci); int skb_vlan_pop(struct sk_buff *skb); int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci); +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI +int skb_eth_pop(struct sk_buff *skb); +int skb_eth_push(struct sk_buff *skb, const unsigned char *dst, + const unsigned char *src); +#endif int skb_mpls_push(struct sk_buff *skb, __be32 mpls_lse, __be16 mpls_proto, int mac_len, bool ethernet); int skb_mpls_pop(struct sk_buff *skb, __be16 next_proto, int mac_len, diff --git a/include/net/tc_act/tc_vlan.h b/include/net/tc_act/tc_vlan.h index add6fb50dd33..141d82a61c9a 100644 --- a/include/net/tc_act/tc_vlan.h +++ b/include/net/tc_act/tc_vlan.h @@ -16,6 +16,10 @@ struct tcf_vlan_params { u8 tcfv_push_prio; bool tcfv_push_prio_exists; struct rcu_head rcu; +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI + unsigned char tcfv_push_dst[ETH_ALEN]; + unsigned char tcfv_push_src[ETH_ALEN]; +#endif }; struct tcf_vlan { diff --git a/include/uapi/linux/tc_act/tc_vlan.h b/include/uapi/linux/tc_act/tc_vlan.h index 168995b54a70..5b306fe815cc 100644 --- a/include/uapi/linux/tc_act/tc_vlan.h +++ b/include/uapi/linux/tc_act/tc_vlan.h @@ -16,6 +16,8 @@ #define TCA_VLAN_ACT_POP 1 #define TCA_VLAN_ACT_PUSH 2 #define TCA_VLAN_ACT_MODIFY 3 +#define TCA_VLAN_ACT_POP_ETH 4 +#define TCA_VLAN_ACT_PUSH_ETH 5 struct tc_vlan { tc_gen; @@ -30,6 +32,8 @@ enum { TCA_VLAN_PUSH_VLAN_PROTOCOL, TCA_VLAN_PAD, TCA_VLAN_PUSH_VLAN_PRIORITY, + TCA_VLAN_PUSH_ETH_DST, + TCA_VLAN_PUSH_ETH_SRC, __TCA_VLAN_MAX, }; #define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 41c832440762..f946562b6aae 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -5602,6 +5602,75 @@ int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) } EXPORT_SYMBOL(skb_vlan_push); +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI +/** + * skb_eth_pop() - Drop the Ethernet header at the head of a packet + * + * @skb: Socket buffer to modify + * + * Drop the Ethernet header of @skb. + * + * Expects that skb->data points to the mac header and that no VLAN tags are + * present. + * + * Returns 0 on success, -errno otherwise. + */ +int skb_eth_pop(struct sk_buff *skb) +{ + if (!pskb_may_pull(skb, ETH_HLEN) || skb_vlan_tagged(skb) || + skb_network_offset(skb) < ETH_HLEN) + return -EPROTO; + + skb_pull_rcsum(skb, ETH_HLEN); + skb_reset_mac_header(skb); + skb_reset_mac_len(skb); + + return 0; +} +EXPORT_SYMBOL(skb_eth_pop); + +/** + * skb_eth_push() - Add a new Ethernet header at the head of a packet + * + * @skb: Socket buffer to modify + * @dst: Destination MAC address of the new header + * @src: Source MAC address of the new header + * + * Prepend @skb with a new Ethernet header. + * + * Expects that skb->data points to the mac header, which must be empty. + * + * Returns 0 on success, -errno otherwise. + */ +int skb_eth_push(struct sk_buff *skb, const unsigned char *dst, + const unsigned char *src) +{ + struct ethhdr *eth; + int err; + + if (skb_network_offset(skb) || skb_vlan_tag_present(skb)) + return -EPROTO; + + err = skb_cow_head(skb, sizeof(*eth)); + if (err < 0) + return err; + + skb_push(skb, sizeof(*eth)); + skb_reset_mac_header(skb); + skb_reset_mac_len(skb); + + eth = eth_hdr(skb); + ether_addr_copy(eth->h_dest, dst); + ether_addr_copy(eth->h_source, src); + eth->h_proto = skb->protocol; + + skb_postpush_rcsum(skb, eth, sizeof(*eth)); + + return 0; +} +EXPORT_SYMBOL(skb_eth_push); +#endif + /* Update the ethertype of hdr and the skb csum value if required. */ static void skb_mod_eth_type(struct sk_buff *skb, struct ethhdr *hdr, __be16 ethertype) diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 2985509147a2..43bceffbe7e0 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -975,6 +975,19 @@ config NET_TC_SKB_EXT Say N here if you won't be using tc<->ovs offload or tc chains offload. +config NET_SCHED_ACT_VLAN_QGKI + bool "VLAN pop_eth/push_eth patch" + depends on NET_ACT_VLAN + depends on QGKI + help + Say Y here to support tc VLAN pop_eth/push_eth actions. Macro guards the + code against ABI breakage. When this flag is enabled, it is safe to assume + that the build is a Non GKI build. + + Say N to exclude this support. + + If unsure, say Y. + endif # NET_SCHED config NET_SCH_FIFO diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c index 7dc76c68ec52..9d9569689921 100644 --- a/net/sched/act_vlan.c +++ b/net/sched/act_vlan.c @@ -77,6 +77,18 @@ static int tcf_vlan_act(struct sk_buff *skb, const struct tc_action *a, /* put updated tci as hwaccel tag */ __vlan_hwaccel_put_tag(skb, p->tcfv_push_proto, tci); break; +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI + case TCA_VLAN_ACT_POP_ETH: + err = skb_eth_pop(skb); + if (err) + goto drop; + break; + case TCA_VLAN_ACT_PUSH_ETH: + err = skb_eth_push(skb, p->tcfv_push_dst, p->tcfv_push_src); + if (err) + goto drop; + break; +#endif default: BUG(); } @@ -93,10 +105,17 @@ drop: } static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = { +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI + [TCA_VLAN_UNSPEC] = { .strict_start_type = TCA_VLAN_PUSH_ETH_DST }, +#endif [TCA_VLAN_PARMS] = { .len = sizeof(struct tc_vlan) }, [TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 }, [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 }, [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NLA_U8 }, +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI + [TCA_VLAN_PUSH_ETH_DST] = NLA_POLICY_ETH_ADDR, + [TCA_VLAN_PUSH_ETH_SRC] = NLA_POLICY_ETH_ADDR, +#endif }; static int tcf_vlan_init(struct net *net, struct nlattr *nla, @@ -180,6 +199,19 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, if (push_prio_exists) push_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]); break; +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI + case TCA_VLAN_ACT_POP_ETH: + break; + case TCA_VLAN_ACT_PUSH_ETH: + if (!tb[TCA_VLAN_PUSH_ETH_DST] || !tb[TCA_VLAN_PUSH_ETH_SRC]) { + if (exists) + tcf_idr_release(*a, bind); + else + tcf_idr_cleanup(tn, index); + return -EINVAL; + } + break; +#endif default: if (exists) tcf_idr_release(*a, bind); @@ -221,6 +253,15 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, p->tcfv_push_prio_exists = push_prio_exists || action == TCA_VLAN_ACT_PUSH; p->tcfv_push_proto = push_proto; +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI + if (action == TCA_VLAN_ACT_PUSH_ETH) { + nla_memcpy(&p->tcfv_push_dst, tb[TCA_VLAN_PUSH_ETH_DST], + ETH_ALEN); + nla_memcpy(&p->tcfv_push_src, tb[TCA_VLAN_PUSH_ETH_SRC], + ETH_ALEN); + } +#endif + spin_lock_bh(&v->tcf_lock); goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); rcu_swap_protected(v->vlan_p, p, lockdep_is_held(&v->tcf_lock)); @@ -279,6 +320,17 @@ static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a, p->tcfv_push_prio)))) goto nla_put_failure; +#ifdef CONFIG_NET_SCHED_ACT_VLAN_QGKI + if (p->tcfv_action == TCA_VLAN_ACT_PUSH_ETH) { + if (nla_put(skb, TCA_VLAN_PUSH_ETH_DST, ETH_ALEN, + p->tcfv_push_dst)) + goto nla_put_failure; + if (nla_put(skb, TCA_VLAN_PUSH_ETH_SRC, ETH_ALEN, + p->tcfv_push_src)) + goto nla_put_failure; + } +#endif + tcf_tm_dump(&t, &v->tcf_tm); if (nla_put_64bit(skb, TCA_VLAN_TM, sizeof(t), &t, TCA_VLAN_PAD)) goto nla_put_failure;