From 86075ea908655a952693a88eeb6b2c014dc5f293 Mon Sep 17 00:00:00 2001 From: Markus Gothe Date: Wed, 15 May 2024 15:02:58 +0200 Subject: [PATCH] Initial addition of ebtables-extensions package. --- ebtables-extensions/Makefile | 63 +++++++ ebtables-extensions/src/Makefile | 2 + ebtables-extensions/src/ebt_dscp2pbit.c | 172 ++++++++++++++++++ ebtables-extensions/src/ebt_dscp2pbit.h | 29 +++ ebtables-extensions/src/ebt_vlantranslation.c | 82 +++++++++ ebtables-extensions/src/ebt_vlantranslation.h | 28 +++ 6 files changed, 376 insertions(+) create mode 100644 ebtables-extensions/Makefile create mode 100644 ebtables-extensions/src/Makefile create mode 100644 ebtables-extensions/src/ebt_dscp2pbit.c create mode 100644 ebtables-extensions/src/ebt_dscp2pbit.h create mode 100644 ebtables-extensions/src/ebt_vlantranslation.c create mode 100644 ebtables-extensions/src/ebt_vlantranslation.h diff --git a/ebtables-extensions/Makefile b/ebtables-extensions/Makefile new file mode 100644 index 000000000..04000432b --- /dev/null +++ b/ebtables-extensions/Makefile @@ -0,0 +1,63 @@ +# +# Copyright (C) 2024 IOPSYS Software Solutions AB +# + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=ebtables-extensions +PKG_VERSION:=1.0.0 +PKG_LICENSE:=GPL-2.0 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/vlantranslation + SUBMENU:=Other modules + TITLE:=Kernel module for ebtables VLAN translation + FILES:=$(PKG_BUILD_DIR)/ebt_vlantranslation.ko + DEPENDS+=+kmod-ebtables +ebtables-legacy + AUTOLOAD:=$(call AutoLoad,30,ebt_vlantranslation,1) + KCONFIG:= +endef + +define KernelPackage/dscp2pbit + SUBMENU:=Other modules + TITLE:=Kernel module for DSCP-to-Pbit mapping + DEPENDS+=+kmod-ebtables +ebtables-legacy + FILES:=$(PKG_BUILD_DIR)/ebt_dscp2pbit.ko + AUTOLOAD:=$(call AutoLoad,30,ebt_dscp2pbit,1) + KCONFIG:= +endef + +define KernelPackage/vlantranslation/description + Kernel module to enable VLAN translation for ebtables +endef + +define KernelPackage/dscp2pbit/description + Kernel module to enableDSCP-to-Pbit mapping for ebtables +endef + +ifeq ($(CONFIG_TARGET_brcmbca),y) + include ../../broadcom/bcmkernel/bcm-kernel-toolchain.mk +endif + +define Build/Prepare + $(CP) -rf ./src/* $(PKG_BUILD_DIR)/ + $(CP) $(PKG_BUILD_DIR)/ebt_vlantranslation.h $(LINUX_DIR)/include/uapi/linux/netfilter_bridge/ + $(CP) $(PKG_BUILD_DIR)/ebt_dscp2pbit.h $(LINUX_DIR)/include/uapi/linux/netfilter_bridge/ +endef + +define Build/InstallDev + $(INSTALL_DIR) $(1)/include/uapi/linux/netfilter_bridge/ + $(CP) $(PKG_BUILD_DIR)/ebt_vlantranslation.h $(1)/include/uapi/linux/netfilter_bridge/ + $(CP) $(PKG_BUILD_DIR)/ebt_dscp2pbit.h $(1)/include/uapi/linux/netfilter_bridge/ +endef + +KERNEL_MAKE_FLAGS += -I$(LINUX_DIR)/include + +define Build/Compile + $(KERNEL_MAKE) M="$(PKG_BUILD_DIR)" modules +endef + +$(eval $(call KernelPackage,vlantranslation)) +$(eval $(call KernelPackage,dscp2pbit)) diff --git a/ebtables-extensions/src/Makefile b/ebtables-extensions/src/Makefile new file mode 100644 index 000000000..81b26567b --- /dev/null +++ b/ebtables-extensions/src/Makefile @@ -0,0 +1,2 @@ +obj-m += ebt_vlantranslation.o +obj-m += ebt_dscp2pbit.o diff --git a/ebtables-extensions/src/ebt_dscp2pbit.c b/ebtables-extensions/src/ebt_dscp2pbit.c new file mode 100644 index 000000000..cb1da167f --- /dev/null +++ b/ebtables-extensions/src/ebt_dscp2pbit.c @@ -0,0 +1,172 @@ +/* + * ebt_dscp2pbit + * + * Authors: + * Markus Gothe + * + * April, 2024 + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline __be16 pppoe_proto(const struct sk_buff *skb) +{ + return *((__be16 *)((void *)pppoe_hdr(skb)->tag)); +} + +static inline bool is_pppoe_ip(const struct sk_buff *skb) +{ + return pppoe_proto(skb) == htons(PPP_IP); +} + +static inline bool is_pppoe_ipv6(const struct sk_buff *skb) +{ + return pppoe_proto(skb) == htons(PPP_IPV6); +} + +static unsigned int ebt_dscp2pbit_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct ebt_dscp2pbit_info *info = par->targinfo; + const struct ipv6hdr *ipv6h = NULL; + const struct iphdr *iph = NULL; + uint8_t dscp_value = 0, pbit = NOPBITS; + unsigned int vlan_hdr_count = 0; + unsigned int ret = info->target; + __be16 protocol; + bool orig_vlan_tag_present = false; + + /* Don't just assume the skb is linear, make sure it is! */ + if (unlikely(skb_linearize(skb))) { + return EBT_DROP; + } + + protocol = skb_protocol(skb, false); +again: + switch (protocol) { + case htons(ETH_P_IP): + iph = ip_hdr(skb); + break; + case htons(ETH_P_IPV6): + ipv6h = ipv6_hdr(skb); + break; + case htons(ETH_P_PPP_SES): + if (unlikely(skb->len < sizeof(struct pppoe_hdr))) { + ret = EBT_CONTINUE; + goto out; + } + + if (is_pppoe_ipv6(skb)) { + ipv6h = (struct ipv6hdr *)(((void *)pppoe_hdr(skb)->tag) + 2); + } else if (is_pppoe_ip(skb)) { + iph = (struct iphdr *)(((void *)pppoe_hdr(skb)->tag) + 2); + } + break; + case htons(ETH_P_QINQ1): /* fall through */ + case htons(ETH_P_QINQ2): /* fall through */ + case htons(ETH_P_QINQ3): /* fall through */ + case htons(ETH_P_8021AD): /* fall through */ + case htons(ETH_P_8021Q): + if (skb_vlan_tag_present(skb) && !orig_vlan_tag_present) { + protocol = skb->protocol; + orig_vlan_tag_present = true; + } else { + struct vlan_hdr *vlan = (struct vlan_hdr *)skb->data; + + protocol = vlan->h_vlan_encapsulated_proto; + skb_pull(skb, VLAN_HLEN); + skb_reset_network_header(skb); + vlan_hdr_count++; + } + goto again; + default: + ret = EBT_CONTINUE; + goto out; + } + + if (iph != NULL && likely(skb->len >= sizeof(struct iphdr) && iph->version == 4)) { + dscp_value = ipv4_get_dsfield(iph); + } else if (ipv6h != NULL && likely(skb->len >= sizeof(struct ipv6hdr) && ipv6h->version == 6)) { + dscp_value = ipv6_get_dsfield(ipv6h); + } else { + ret = EBT_CONTINUE; + goto out; + } + + pbit = info->dscp2pbitmapping[(dscp_value >> DSCP_OFFSET)]; + if (pbit == NOPBITS) { + ret = EBT_CONTINUE; + goto out; + } + + skb_vlan_tag_get(skb) &= ~VLAN_PRIO_MASK; + skb_vlan_tag_get(skb) |= (pbit << VLAN_PRIO_SHIFT); + +out: + /* Restore the skb for the pulled VLAN tags */ + while (vlan_hdr_count--) { + skb_push(skb, VLAN_HLEN); + skb_reset_network_header(skb); + } + + return ret; +} + +static int ebt_dscp2pbit_tg_check(const struct xt_tgchk_param *par) +{ + const struct ebt_dscp2pbit_info *info = par->targinfo; + const struct ebt_entry *e = par->entryinfo; + unsigned int hook_mask; + + if (e->ethproto != htons(ETH_P_8021Q) && e->ethproto != htons(ETH_P_8021AD) && e->ethproto != htons(ETH_P_QINQ1) && e->ethproto != htons(ETH_P_QINQ2) && e->ethproto != htons(ETH_P_QINQ3)) + return -EINVAL; + + if (BASE_CHAIN && info->target == EBT_RETURN) + return -EINVAL; + + hook_mask = par->hook_mask & ~(1 << NF_BR_NUMHOOKS); + + if ((strcmp(par->table, "nat") != 0 || hook_mask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_POST_ROUTING) | (1 << NF_BR_LOCAL_OUT))) && + (strcmp(par->table, "broute") != 0 || hook_mask & ~(1 << NF_BR_BROUTING))) + return -EINVAL; + if (ebt_invalid_target(info->target)) + return -EINVAL; + return 0; +} + +static struct xt_target ebt_dscp2pbit_tg_reg __read_mostly = { + .name = "dscp2pbit", + .revision = 0, + .family = NFPROTO_BRIDGE, + .hooks = (1 << NF_BR_NUMHOOKS) | (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_POST_ROUTING) | (1 << NF_BR_LOCAL_OUT) | (1 << NF_BR_BROUTING), + .target = ebt_dscp2pbit_tg, + .checkentry = ebt_dscp2pbit_tg_check, + .targetsize = sizeof(struct ebt_dscp2pbit_info), + .me = THIS_MODULE, +}; + +static int __init ebt_dscp2pbit_init(void) +{ + return xt_register_target(&ebt_dscp2pbit_tg_reg); +} + +static void __exit ebt_dscp2pbit_fini(void) +{ + xt_unregister_target(&ebt_dscp2pbit_tg_reg); +} + +module_init(ebt_dscp2pbit_init); +module_exit(ebt_dscp2pbit_fini); +MODULE_DESCRIPTION("Ebtables: DSCP-to-Pbit manipulation"); +MODULE_LICENSE("GPL"); diff --git a/ebtables-extensions/src/ebt_dscp2pbit.h b/ebtables-extensions/src/ebt_dscp2pbit.h new file mode 100644 index 000000000..f0dcdc3b2 --- /dev/null +++ b/ebtables-extensions/src/ebt_dscp2pbit.h @@ -0,0 +1,29 @@ +/* + * ebt_dscp2pbit + * + * Authors: + * Markus Gothe + * + * April, 2024 + * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note + */ + +#ifndef __LINUX_BRIDGE_EBT_DSCP2PBIT_H +#define __LINUX_BRIDGE_EBT_DSCP2PBIT_H + +#include + +#define DSCP_BITS 0xfc +#define DSCP_OFFSET 2 +#define PBITS 0x7 +#define NOPBITS 0xff + +struct ebt_dscp2pbit_info +{ + uint8_t dscp2pbitmapping[(DSCP_BITS >> DSCP_OFFSET)+1]; /* DSCP value 0-63 -> Pbit 0-7 */ + int target; + uint8_t bitmask; + uint8_t invflags; +}; + +#endif diff --git a/ebtables-extensions/src/ebt_vlantranslation.c b/ebtables-extensions/src/ebt_vlantranslation.c new file mode 100644 index 000000000..b4bed055f --- /dev/null +++ b/ebtables-extensions/src/ebt_vlantranslation.c @@ -0,0 +1,82 @@ +/* + * ebt_vlantranslation + * + * Authors: + * Markus Gothe + * + * May, 2024 + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include + +static unsigned int ebt_vlantranslation_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct ebt_vlantranslation_info *info = par->targinfo; + + if (info->bitmask & XLATE_VLANID) { + skb_vlan_tag_get(skb) &= ~VLAN_VID_MASK; + skb_vlan_tag_get(skb) |= (VLAN_VID_MASK & info->vlanid); + } + + if (info->bitmask & XLATE_PBIT) { + skb_vlan_tag_get(skb) &= ~VLAN_PRIO_MASK; + skb_vlan_tag_get(skb) |= ((0x7 & info->pbit) << VLAN_PRIO_SHIFT); + } + + return info->target; +} + +static int ebt_vlantranslation_tg_check(const struct xt_tgchk_param *par) +{ + const struct ebt_vlantranslation_info *info = par->targinfo; + const struct ebt_entry *e = par->entryinfo; + unsigned int hook_mask; + + if (e->ethproto != htons(ETH_P_8021Q) && e->ethproto != htons(ETH_P_8021AD) && e->ethproto != htons(ETH_P_QINQ1) && e->ethproto != htons(ETH_P_QINQ2) && e->ethproto != htons(ETH_P_QINQ3)) + return -EINVAL; + + if (BASE_CHAIN && info->target == EBT_RETURN) + return -EINVAL; + + hook_mask = par->hook_mask & ~(1 << NF_BR_NUMHOOKS); + + if ((strcmp(par->table, "nat") != 0 || hook_mask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_POST_ROUTING) | (1 << NF_BR_LOCAL_OUT))) && + (strcmp(par->table, "broute") != 0 || hook_mask & ~(1 << NF_BR_BROUTING))) + return -EINVAL; + if (ebt_invalid_target(info->target)) + return -EINVAL; + return 0; +} + +static struct xt_target ebt_vlantranslation_tg_reg __read_mostly = { + .name = "vlantranslation", + .revision = 0, + .family = NFPROTO_BRIDGE, + .hooks = (1 << NF_BR_NUMHOOKS) | (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_POST_ROUTING) | (1 << NF_BR_LOCAL_OUT) | (1 << NF_BR_BROUTING), + .target = ebt_vlantranslation_tg, + .checkentry = ebt_vlantranslation_tg_check, + .targetsize = sizeof(struct ebt_vlantranslation_info), + .me = THIS_MODULE, +}; + +static int __init ebt_vlantranslation_init(void) +{ + return xt_register_target(&ebt_vlantranslation_tg_reg); +} + +static void __exit ebt_vlantranslation_fini(void) +{ + xt_unregister_target(&ebt_vlantranslation_tg_reg); +} + +module_init(ebt_vlantranslation_init); +module_exit(ebt_vlantranslation_fini); +MODULE_DESCRIPTION("Ebtables: VLAN translation"); +MODULE_LICENSE("GPL"); diff --git a/ebtables-extensions/src/ebt_vlantranslation.h b/ebtables-extensions/src/ebt_vlantranslation.h new file mode 100644 index 000000000..613a4e441 --- /dev/null +++ b/ebtables-extensions/src/ebt_vlantranslation.h @@ -0,0 +1,28 @@ +/* + * ebt_vlantranslation + * + * Authors: + * Markus Gothe + * + * May, 2024 + * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note + */ + +#ifndef __LINUX_BRIDGE_EBT_VLANTRANSLATION_H +#define __LINUX_BRIDGE_EBT_VLANTRANSLATION_H + +#include + +#define XLATE_VLANID 0x1 +#define XLATE_PBIT 0x2 + +struct ebt_vlantranslation_info +{ + unsigned int vlanid; + uint8_t pbit; + int target; + uint8_t bitmask; + uint8_t invflags; +}; + +#endif