--- zzzz-none-000/linux-4.9.276/net/bridge/br_forward.c 2021-07-20 14:21:16.000000000 +0000 +++ falcon-5530-750/linux-4.9.276/net/bridge/br_forward.c 2023-04-05 08:19:02.000000000 +0000 @@ -21,6 +21,81 @@ #include #include "br_private.h" +#include + +#if IS_ENABLED(CONFIG_MCAST_HELPER) +#if IS_ENABLED(CONFIG_MCAST_SNOOPING) +#include +#include + +void (*five_tuple_br_info_ptr)(struct sk_buff *skb) = NULL; +void (*five_tuple_br_info_hook)(struct sk_buff *skb) = NULL; +EXPORT_SYMBOL(five_tuple_br_info_ptr); +EXPORT_SYMBOL(five_tuple_br_info_hook); +atomic_t mch_br_capture_pkt = ATOMIC_INIT(0); +EXPORT_SYMBOL(mch_br_capture_pkt); + +/* Called only from multicast flood path */ +static void mcast_helper_learning_hook(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + + /* Send five tuple info to mcast helper */ + if (five_tuple_br_info_ptr && atomic_read(&mch_br_capture_pkt)) { + if ((skb->protocol == htons(ETH_P_IP) && + (ip_hdr(skb)->protocol == IPPROTO_UDP)) || + ((skb->protocol == htons(ETH_P_IPV6)) && + (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP))) { + five_tuple_br_info_ptr(skb); + } + } + + /* Hook the UDP multicast to mcast helper */ + if (!bridge_lanserver_hook) + return; + + if (five_tuple_br_info_hook == NULL) + return; + + if (((skb->protocol == htons(ETH_P_IP)) && + (ip_hdr(skb)->protocol == IPPROTO_UDP)) || + ((skb->protocol == htons(ETH_P_IPV6)) && + (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP))) { + if (!(dst && ((dst->input == ip_mr_input) || + (dst->input == ip6_mc_input)))) { + five_tuple_br_info_hook(skb); + } + } +} +#endif + +#if IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) +void (*mcast_helper_br_learning_hook)(struct sk_buff *skb, + struct net_device *mem_dev, + u8 *host_mac) = NULL; +EXPORT_SYMBOL(mcast_helper_br_learning_hook); + +/* Helper learning - called only from bridge flood path */ +static void mcast_helper_learning(struct sk_buff *skb, + struct net_device *mem_dev, u8 *host_mac) +{ + if (mcast_helper_br_learning_hook == NULL) + return; + + if ((mem_dev == NULL) || (host_mac == NULL)) + return; + + /* Send five tuple & host info to mcast helper for learning */ + if ((skb->protocol == htons(ETH_P_IP) && + (ip_hdr(skb)->protocol == IPPROTO_UDP)) || + ((skb->protocol == htons(ETH_P_IPV6)) && + (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP))) { + mcast_helper_br_learning_hook(skb, mem_dev, host_mac); + } +} +#endif +#endif + /* Don't forward packets to originating port or forwarding disabled */ static inline int should_deliver(const struct net_bridge_port *p, const struct sk_buff *skb) @@ -29,8 +104,9 @@ vg = nbp_vlan_group_rcu(p); return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && - br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING && - nbp_switchdev_allowed_egress(p, skb); + br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING && + nbp_switchdev_allowed_egress(p, skb) && + !br_skb_isolated(p, skb); } int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) @@ -165,6 +241,15 @@ if (!prev) goto out; +#if IS_ENABLED(CONFIG_MCAST_SNOOPING) + if ((bridge_igmp_snooping || bridge_mld_snooping) && + is_multicast_ether_addr(eth_hdr(skb)->h_dest) && + (br_selective_flood(prev, skb) == 0)) { + prev = p; + return p; + } +#endif + err = deliver_clone(prev, skb, local_orig); if (err) return ERR_PTR(err); @@ -181,15 +266,37 @@ struct net_bridge_port *prev = NULL; struct net_bridge_port *p; - list_for_each_entry_rcu(p, &br->port_list, list) { - /* Do not flood unicast traffic to ports that turn it off */ - if (pkt_type == BR_PKT_UNICAST && !(p->flags & BR_FLOOD)) - continue; - /* Do not flood if mc off, except for traffic we originate */ - if (pkt_type == BR_PKT_MULTICAST && - !(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) - continue; + if (!br_flood_rl(br, skb, skb->dev)) + goto out; + +#if IS_ENABLED(CONFIG_MCAST_SNOOPING) +#if IS_ENABLED(CONFIG_MCAST_HELPER) + if (is_multicast_ether_addr(eth_hdr(skb)->h_dest) && + !is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) + mcast_helper_learning_hook(skb); +#endif +#endif + avm_pa_do_not_accelerate(skb); + + list_for_each_entry_rcu(p, &br->port_list, list) { + /* Do not flood unicast traffic to ports that turn it off, nor + * other traffic if flood off, except for traffic we originate + */ + switch (pkt_type) { + case BR_PKT_UNICAST: + if (!(p->flags & BR_FLOOD)) + continue; + break; + case BR_PKT_MULTICAST: + if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) + continue; + break; + case BR_PKT_BROADCAST: + if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev) + continue; + break; + } /* Do not flood to ports that enable proxy ARP */ if (p->flags & BR_PROXYARP) continue; @@ -208,10 +315,24 @@ if (!prev) goto out; - if (local_rcv) - deliver_clone(prev, skb, local_orig); - else - __br_forward(prev, skb, local_orig); + if (local_rcv) { +#if IS_ENABLED(CONFIG_MCAST_SNOOPING) + if ((bridge_igmp_snooping || bridge_mld_snooping) && + is_multicast_ether_addr(eth_hdr(skb)->h_dest) && + (br_selective_flood(prev, skb) == 0)) { + } else +#endif + deliver_clone(prev, skb, local_orig); + } else { +#if IS_ENABLED(CONFIG_MCAST_SNOOPING) + if ((bridge_igmp_snooping || bridge_mld_snooping) && + is_multicast_ether_addr(eth_hdr(skb)->h_dest) && + (br_selective_flood(prev, skb) == 0)) + kfree_skb(skb); + else +#endif + __br_forward(prev, skb, local_orig); + } return; out: @@ -220,6 +341,31 @@ } #ifdef CONFIG_BRIDGE_IGMP_SNOOPING +static void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb, + const unsigned char *addr, bool local_orig) +{ + struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; + const unsigned char *src = eth_hdr(skb)->h_source; + + if (!should_deliver(p, skb)) + return; + + /* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */ + if (skb->dev == p->dev && ether_addr_equal(src, addr)) + return; + + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) { + dev->stats.tx_dropped++; + return; + } + + if (!is_broadcast_ether_addr(addr)) + memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN); + + __br_forward(p, skb, local_orig); +} + /* called with rcu_read_lock */ void br_multicast_flood(struct net_bridge_mdb_entry *mdst, struct sk_buff *skb, @@ -231,6 +377,9 @@ struct net_bridge_port *prev = NULL; struct net_bridge_port_group *p; struct hlist_node *rp; +#if IS_ENABLED(CONFIG_MCAST_HELPER) + u8 *host_mac = NULL; +#endif rp = rcu_dereference(hlist_first_rcu(&br->router_list)); p = mdst ? rcu_dereference(mdst->ports) : NULL; @@ -241,10 +390,27 @@ rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) : NULL; - port = (unsigned long)lport > (unsigned long)rport ? - lport : rport; + if ((unsigned long)lport > (unsigned long)rport) { + port = lport; + + if (port->flags & BR_MULTICAST_TO_UNICAST) { + maybe_deliver_addr(lport, skb, p->eth_addr, + local_orig); + goto delivered; + } + } else { + port = rport; + } + +#if IS_ENABLED(CONFIG_MCAST_HELPER) + /* For each host - learn and assign GID if deliverable */ + if (!igmp_type && prev && host_mac && + !is_zero_ether_addr(host_mac) && should_deliver(prev, skb)) + mcast_helper_learning(skb, prev->dev, host_mac); +#endif prev = maybe_deliver(prev, port, skb, local_orig); +delivered: if (IS_ERR(prev)) goto out; if (prev == port) @@ -260,6 +426,12 @@ if (!prev) goto out; +#if IS_ENABLED(CONFIG_MCAST_HELPER) + /* For each host - learn and assign GID before delivery */ + if (!igmp_type && mdst && host_mac && !is_zero_ether_addr(host_mac)) + mcast_helper_learning(skb, prev->dev, host_mac); +#endif + if (local_rcv) deliver_clone(prev, skb, local_orig); else