diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 948cd95d9487c90ed7dbbd2af7fc7450da80bc84..fc605769e6557ba8207856e3c190075461de2a3f 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 47fa58ffa167e382c8f8e0df5fae3081e09cf6f0..35636eba843e90be0c75ef48d9b4331beaf6ab5a 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 6dbf2bef936d637b3e7fbde404de9857ad447853..3fbbd9c592855b0b5286a809ad4c429dac89a9e3 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 ce3117df9cec2948203319c64f62c0af99d58c01..94a91be1bdcf18ccbb075a6f8876614e12ac3df4 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 0000000000000000000000000000000000000000..f1b944eae44faa08263366987abe99c7d186a9c4 --- /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. +