--- zzzz-none-000/linux-4.4.60/net/bridge/br_input.c 2017-04-08 07:53:53.000000000 +0000 +++ wasp-540e-714/linux-4.4.60/net/bridge/br_input.c 2019-07-03 09:21:34.000000000 +0000 @@ -33,7 +33,15 @@ return netif_receive_skb(skb); } -static int br_pass_frame_up(struct sk_buff *skb) +/* Hook for external Multicast handler */ +br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; +EXPORT_SYMBOL_GPL(br_multicast_handle_hook); + +/* Hook for external forwarding logic */ +br_get_dst_hook_t __rcu *br_get_dst_hook __read_mostly; +EXPORT_SYMBOL_GPL(br_get_dst_hook); + +int br_pass_frame_up(struct sk_buff *skb) { struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; struct net_bridge *br = netdev_priv(brdev); @@ -62,10 +70,11 @@ if (!skb) return NET_RX_DROP; - return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, + return BR_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, dev_net(indev), NULL, skb, indev, NULL, br_netif_receive_skb); } +EXPORT_SYMBOL_GPL(br_pass_frame_up); static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, u16 vid, struct net_bridge_port *p) @@ -135,6 +144,8 @@ struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; struct sk_buff *skb2; + struct net_bridge_port *pdst = NULL; + br_get_dst_hook_t *get_dst_hook = rcu_dereference(br_get_dst_hook); bool unicast = true; u16 vid = 0; @@ -153,7 +164,7 @@ br_multicast_rcv(br, p, skb, vid)) goto drop; - if (p->state == BR_STATE_LEARNING) + if ((p->state == BR_STATE_LEARNING) && skb->protocol != htons(ETH_P_PAE)) goto drop; BR_INPUT_SKB_CB(skb)->brdev = br->dev; @@ -169,10 +180,19 @@ if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP)) br_do_proxy_arp(skb, br, vid, p); - if (is_broadcast_ether_addr(dest)) { + if (skb->protocol == htons(ETH_P_PAE)) { + skb2 = skb; + /* Do not forward 802.1x/EAP frames */ + skb = NULL; + } else if (is_broadcast_ether_addr(dest)) { skb2 = skb; unicast = false; } else if (is_multicast_ether_addr(dest)) { + br_multicast_handle_hook_t *multicast_handle_hook = + rcu_dereference(br_multicast_handle_hook); + if (!__br_get(multicast_handle_hook, true, p, skb)) + goto out; + mdst = br_mdb_get(br, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && br_multicast_querier_exists(br, eth_hdr(skb))) { @@ -188,18 +208,31 @@ unicast = false; br->dev->stats.multicast++; - } else if ((dst = __br_fdb_get(br, dest, vid)) && - dst->is_local) { - skb2 = skb; - /* Do not forward the packet since it's local. */ - skb = NULL; + } else { + pdst = __br_get(get_dst_hook, NULL, p, &skb); + if (pdst) { + if (!skb) + goto out; + } else { + dst = __br_fdb_get(br, dest, vid); + if ((p->flags & BR_ISOLATE_MODE) || + (dst && dst->is_local)) { + skb2 = skb; + /* Do not forward the packet since it's local.*/ + skb = NULL; + } + } } if (skb) { if (dst) { dst->used = jiffies; - br_forward(dst->dst, skb, skb2); - } else + pdst = dst->dst; + } + + if (pdst) + br_forward(pdst, skb, skb2); + else br_flood_forward(br, skb, skb2, unicast); } @@ -218,11 +251,13 @@ static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { struct net_bridge_port *p = br_port_get_rcu(skb->dev); - u16 vid = 0; + if (p->state != BR_STATE_DISABLED) { + u16 vid = 0; - /* check if vlan is allowed, to avoid spoofing */ - if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid)) - br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false); + /* check if vlan is allowed, to avoid spoofing */ + if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid)) + br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false); + } return 0; /* process further */ } @@ -285,7 +320,7 @@ } /* Deliver packet to local host only */ - if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, + if (BR_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, dev_net(skb->dev), NULL, skb, skb->dev, NULL, br_handle_local_finish)) { return RX_HANDLER_CONSUMED; /* consumed by filter */ @@ -297,6 +332,21 @@ forward: switch (p->state) { + case BR_STATE_DISABLED: + if (skb->protocol == htons(ETH_P_PAE)) { + if (ether_addr_equal(p->br->dev->dev_addr, dest)) + skb->pkt_type = PACKET_HOST; + + if (BR_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, dev_net(skb->dev), NULL, + skb, skb->dev, NULL, br_handle_local_finish)) + break; + + BR_INPUT_SKB_CB(skb)->brdev = p->br->dev; + br_pass_frame_up(skb); + break; + } + goto drop; + case BR_STATE_FORWARDING: rhook = rcu_dereference(br_should_route_hook); if (rhook) { @@ -311,7 +361,7 @@ if (ether_addr_equal(p->br->dev->dev_addr, dest)) skb->pkt_type = PACKET_HOST; - NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, + BR_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, dev_net(skb->dev), NULL, skb, skb->dev, NULL, br_handle_frame_finish); break;