From 71957db8f560f1da53b4ea9e43ac7501640b5ec6 Mon Sep 17 00:00:00 2001 From: wanglongjie Date: Tue, 14 Oct 2025 19:42:08 +0800 Subject: [PATCH] embsys: macvlan broadcast protocol filter Implement broadcast packet filtering support for network MACVLAN devices, enabling MACVLAN devices to set broadcast filtering conditions based on protocol types. This filters out unwanted broadcast packets at the driver device level, reducing unnecessary processing overhead and improving network efficiency. Users can configure filtering conditions using the ip command, adding, deleting, and displaying filtering rules with the ip command. Signed-off-by: wanglongjie Signed-off-by: Wenya Zhang Reviewed-by: Huang Jian Link: https://gitee.com/anolis/embedded-kernel/pulls/1038 --- drivers/net/macvlan.c | 121 ++++++++++++++++++ include/linux/if_macvlan.h | 3 + include/linux/netlink.h | 4 + include/uapi/linux/if_link.h | 6 + .../macvlan-bc-proto-filter/Kconfig | 13 ++ 5 files changed, 147 insertions(+) create mode 100644 nos/extend_features/macvlan-bc-proto-filter/Kconfig diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 948cd95d9487..fc605769e655 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -279,6 +279,20 @@ static void macvlan_broadcast(struct sk_buff *skb, return; hash_for_each_rcu(port->vlan_hash, i, vlan, hlist) { +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER + struct packet_type *pt; + bool match = false; + + list_for_each_entry_rcu(pt, + &vlan->bc_proto_filter, list) { + if (pt && pt->type == skb->protocol) { + match = true; + break; + } + } + if (match) + continue; +#endif if (vlan->dev == src || !(vlan->mode & mode)) continue; @@ -1466,6 +1480,26 @@ static int macvlan_changelink_sources(struct macvlan_dev *vlan, u32 mode, return 0; } +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER +static DEFINE_SPINLOCK(ptype_mc_filter_lock); +int bc_proto_filter_init(struct macvlan_dev *vlan) +{ + struct packet_type *pt_new; + struct list_head *head = &vlan->bc_proto_filter; + + INIT_LIST_HEAD(head); + pt_new = kmalloc(sizeof(*pt_new), GFP_KERNEL); + if (!pt_new) { + netdev_warn(vlan->dev, "Macvlan bc-proto-filter failed to add the initial TIPC!"); + return -ENOMEM; + } + pt_new->type = htons(ETH_P_TIPC); + list_add_rcu(&pt_new->list, head); + + return 0; +} +#endif + int macvlan_common_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) @@ -1525,6 +1559,12 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, vlan->set_features = MACVLAN_FEATURES; vlan->mode = MACVLAN_MODE_VEPA; +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER + err = bc_proto_filter_init(vlan); + if (err < 0) + return err; +#endif + if (data && data[IFLA_MACVLAN_MODE]) vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); @@ -1669,6 +1709,54 @@ static int macvlan_changelink(struct net_device *dev, if (ret) return ret; } + +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER + if (data && data[IFLA_MACVLAN_BC_PROTO_FILTER_ADD]) { + struct list_head *head = &vlan->bc_proto_filter; + u32 proto = nla_get_u32(data[IFLA_MACVLAN_BC_PROTO_FILTER_ADD]); + struct packet_type *pt, *pt_new; + + list_for_each_entry_rcu(pt, head, list) { + /* Network endianness vs. network endianness */ + if (pt && pt->type == proto) + return 0; + } + + pt_new = kmalloc(sizeof(*pt_new), GFP_KERNEL); + if (!pt_new) + return -ENOMEM; + pt_new->type = proto; + + spin_lock(&ptype_mc_filter_lock); + list_add_rcu(&pt_new->list, head); + spin_unlock(&ptype_mc_filter_lock); + + return 0; + } + + if (data && data[IFLA_MACVLAN_BC_PROTO_FILTER_DEL]) { + struct list_head *head = &vlan->bc_proto_filter; + struct packet_type *pt; + u16 proto = nla_get_u16(data[IFLA_MACVLAN_BC_PROTO_FILTER_DEL]); + + list_for_each_entry_rcu(pt, head, list) { + if (!pt) + break; + /* Network endianness vs. network endianness */ + if (pt->type != proto) + continue; + + spin_lock(&ptype_mc_filter_lock); + list_del_rcu(&pt->list); + spin_unlock(&ptype_mc_filter_lock); + + kfree(pt); + return 0; + } + return -EINVAL; + } +#endif + return 0; } @@ -1691,6 +1779,13 @@ static size_t macvlan_get_size(const struct net_device *dev) + macvlan_get_size_mac(vlan) /* IFLA_MACVLAN_MACADDR */ + nla_total_size(4) /* IFLA_MACVLAN_BC_QUEUE_LEN */ + nla_total_size(4) /* IFLA_MACVLAN_BC_QUEUE_LEN_USED */ +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER +#define BC_PROTO_FILTER_SHOW_SIZ 64 + + nla_total_size(4) /* IFLA_MACVLAN_BC_PROTO_FILTER_ADD */ + + nla_total_size(4) /* IFLA_MACVLAN_BC_PROTO_FILTER_DEL */ + + nla_total_size(BC_PROTO_FILTER_SHOW_SIZ) + /* IFLA_MACVLAN_BC_PROTO_FILTER_SHOW */ +#endif ); } @@ -1717,6 +1812,27 @@ static int macvlan_fill_info(struct sk_buff *skb, struct macvlan_port *port = vlan->port; int i; struct nlattr *nest; +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER + struct list_head *head = &vlan->bc_proto_filter; + struct packet_type *pt = NULL; + char protocols[BC_PROTO_FILTER_SHOW_SIZ] = ""; + + list_for_each_entry_rcu(pt, head, list) { + char *p_type; + + p_type = pt->type == htons(ETH_P_IP) ? "IP " + : pt->type == htons(ETH_P_ARP) ? "ARP " + : pt->type == htons(ETH_P_IPV6) ? "IPV6 " + : pt->type == htons(ETH_P_TIPC) ? "TIPC " + : "unknown "; + if (strlen(protocols) < BC_PROTO_FILTER_SHOW_SIZ) + strncat(protocols, p_type, BC_PROTO_FILTER_SHOW_SIZ - + strlen(protocols) - 1); + } + protocols[BC_PROTO_FILTER_SHOW_SIZ - 1] = '\0'; + if (nla_put_string(skb, IFLA_MACVLAN_BC_PROTO_FILTER_SHOW, protocols)) + goto nla_put_failure; +#endif if (nla_put_u32(skb, IFLA_MACVLAN_MODE, vlan->mode)) goto nla_put_failure; @@ -1758,6 +1874,11 @@ static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = { [IFLA_MACVLAN_BC_QUEUE_LEN] = { .type = NLA_U32 }, [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NLA_REJECT }, [IFLA_MACVLAN_BC_CUTOFF] = { .type = NLA_S32 }, +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER + [IFLA_MACVLAN_BC_PROTO_FILTER_ADD] = { .type = NLA_U32 }, + [IFLA_MACVLAN_BC_PROTO_FILTER_DEL] = { .type = NLA_U32 }, + [IFLA_MACVLAN_BC_PROTO_FILTER_SHOW] = { .type = NLA_NUL_STRING }, +#endif }; int macvlan_link_register(struct rtnl_link_ops *ops) diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 47fa58ffa167..35636eba843e 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -34,6 +34,9 @@ struct macvlan_dev { u32 bc_queue_len_req; #ifdef CONFIG_NET_POLL_CONTROLLER struct netpoll *netpoll; +#endif +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER + struct list_head bc_proto_filter; #endif CK_KABI_RESERVE(1) CK_KABI_RESERVE(2) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 6dbf2bef936d..3fbbd9c59285 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -9,6 +9,10 @@ #include #include +#ifdef CONFIG_MACVLAN_BC_PROTO_FILTER +#define MACVLAN_BC_PROTO_FILTER +#endif + struct net; void do_trace_netlink_extack(const char *msg); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index ce3117df9cec..94a91be1bdcf 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -638,6 +638,12 @@ enum { IFLA_MACVLAN_BC_QUEUE_LEN, IFLA_MACVLAN_BC_QUEUE_LEN_USED, IFLA_MACVLAN_BC_CUTOFF, +#ifdef MACVLAN_BC_PROTO_FILTER + /* To be compatible with higher versions. */ + IFLA_MACVLAN_BC_PROTO_FILTER_ADD = 10, + IFLA_MACVLAN_BC_PROTO_FILTER_DEL, + IFLA_MACVLAN_BC_PROTO_FILTER_SHOW, +#endif __IFLA_MACVLAN_MAX, }; diff --git a/nos/extend_features/macvlan-bc-proto-filter/Kconfig b/nos/extend_features/macvlan-bc-proto-filter/Kconfig new file mode 100644 index 000000000000..f1b944eae44f --- /dev/null +++ b/nos/extend_features/macvlan-bc-proto-filter/Kconfig @@ -0,0 +1,13 @@ +config MACVLAN_BC_PROTO_FILTER + bool "MACVLAN support filter broadcasts by protocol" + depends on MACVLAN + default n + help + This feature allows you to configure the MACVLAN to allow broadcast + packets of a specified protocol to not pass. + + "ip link set dev type macvlan bc_proto_filter [ add | del ] [ PROTO ]" + "ip -d addr show dev " + + If unsure, say N. + -- Gitee