diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 8eecd0ec22f2..09f26940aba5 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -61,11 +61,11 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid)) goto out; - if (is_broadcast_ether_addr(dest)) - br_flood_deliver(br, skb, false); - else if (is_multicast_ether_addr(dest)) { + if (is_broadcast_ether_addr(dest)) { + br_flood(br, skb, false, false, true); + } else if (is_multicast_ether_addr(dest)) { if (unlikely(netpoll_tx_running(dev))) { - br_flood_deliver(br, skb, false); + br_flood(br, skb, false, false, true); goto out; } if (br_multicast_rcv(br, NULL, skb, vid)) { @@ -76,14 +76,14 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) mdst = br_mdb_get(br, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && br_multicast_querier_exists(br, eth_hdr(skb))) - br_multicast_deliver(mdst, skb); + br_multicast_flood(mdst, skb, false, true); else - br_flood_deliver(br, skb, false); - } else if ((dst = __br_fdb_get(br, dest, vid)) != NULL) - br_deliver(dst->dst, skb); - else - br_flood_deliver(br, skb, true); - + br_flood(br, skb, false, false, true); + } else if ((dst = __br_fdb_get(br, dest, vid)) != NULL) { + br_forward(dst->dst, skb, false, true); + } else { + br_flood(br, skb, true, false, true); + } out: rcu_read_unlock(); return NETDEV_TX_OK; diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index d610644368b9..63a83d8d7da3 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -21,11 +21,6 @@ #include #include "br_private.h" -static int deliver_clone(const struct net_bridge_port *prev, - struct sk_buff *skb, - void (*__packet_hook)(const struct net_bridge_port *p, - struct sk_buff *skb)); - /* 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) @@ -75,41 +70,13 @@ int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(br_forward_finish); -static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) -{ - struct net_bridge_vlan_group *vg; - - vg = nbp_vlan_group_rcu(to); - skb = br_handle_vlan(to->br, vg, skb); - if (!skb) - return; - - skb->dev = to->dev; - - if (unlikely(netpoll_tx_running(to->br->dev))) { - if (!is_skb_forwardable(skb->dev, skb)) - kfree_skb(skb); - else { - skb_push(skb, ETH_HLEN); - br_netpoll_send_skb(to, skb); - } - return; - } - - NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, - dev_net(skb->dev), NULL, skb,NULL, skb->dev, - br_forward_finish); -} - -static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) +static void __br_forward(const struct net_bridge_port *to, + struct sk_buff *skb, bool local_orig) { struct net_bridge_vlan_group *vg; struct net_device *indev; - - if (skb_warn_if_lro(skb)) { - kfree_skb(skb); - return; - } + struct net *net; + int br_hook; vg = nbp_vlan_group_rcu(to); skb = br_handle_vlan(to->br, vg, skb); @@ -118,44 +85,36 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) indev = skb->dev; skb->dev = to->dev; - skb_forward_csum(skb); + if (!local_orig) { + if (skb_warn_if_lro(skb)) { + kfree_skb(skb); + return; + } + br_hook = NF_BR_FORWARD; + skb_forward_csum(skb); + net = dev_net(indev); + } else { + if (unlikely(netpoll_tx_running(to->br->dev))) { + if (!is_skb_forwardable(skb->dev, skb)) { + kfree_skb(skb); + } else { + skb_push(skb, ETH_HLEN); + br_netpoll_send_skb(to, skb); + } + return; + } + br_hook = NF_BR_LOCAL_OUT; + net = dev_net(skb->dev); + indev = NULL; + } - NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, - dev_net(indev), NULL, skb, indev, skb->dev, + NF_HOOK(NFPROTO_BRIDGE, br_hook, + net, NULL, skb, indev, skb->dev, br_forward_finish); } -/* called with rcu_read_lock */ -void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) -{ - if (to && should_deliver(to, skb)) { - __br_deliver(to, skb); - return; - } - - kfree_skb(skb); -} -EXPORT_SYMBOL_GPL(br_deliver); - -/* called with rcu_read_lock */ -void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0) -{ - if (to && should_deliver(to, skb)) { - if (skb0) - deliver_clone(to, skb, __br_forward); - else - __br_forward(to, skb); - return; - } - - if (!skb0) - kfree_skb(skb); -} - static int deliver_clone(const struct net_bridge_port *prev, - struct sk_buff *skb, - void (*__packet_hook)(const struct net_bridge_port *p, - struct sk_buff *skb)) + struct sk_buff *skb, bool local_orig) { struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; @@ -165,15 +124,38 @@ static int deliver_clone(const struct net_bridge_port *prev, return -ENOMEM; } - __packet_hook(prev, skb); + __br_forward(prev, skb, local_orig); return 0; } +/** + * br_forward - forward a packet to a specific port + * @to: destination port + * @skb: packet being forwarded + * @local_rcv: packet will be received locally after forwarding + * @local_orig: packet is locally originated + * + * Should be called with rcu_read_lock. + */ +void br_forward(const struct net_bridge_port *to, + struct sk_buff *skb, bool local_rcv, bool local_orig) +{ + if (to && should_deliver(to, skb)) { + if (local_rcv) + deliver_clone(to, skb, local_orig); + else + __br_forward(to, skb, local_orig); + return; + } + + if (!local_rcv) + kfree_skb(skb); +} +EXPORT_SYMBOL_GPL(br_forward); + static struct net_bridge_port *maybe_deliver( struct net_bridge_port *prev, struct net_bridge_port *p, - struct sk_buff *skb, - void (*__packet_hook)(const struct net_bridge_port *p, - struct sk_buff *skb)) + struct sk_buff *skb, bool local_orig) { int err; @@ -183,7 +165,7 @@ static struct net_bridge_port *maybe_deliver( if (!prev) goto out; - err = deliver_clone(prev, skb, __packet_hook); + err = deliver_clone(prev, skb, local_orig); if (err) return ERR_PTR(err); @@ -191,19 +173,14 @@ out: return p; } -/* called under bridge lock */ -static void br_flood(struct net_bridge *br, struct sk_buff *skb, - struct sk_buff *skb0, - void (*__packet_hook)(const struct net_bridge_port *p, - struct sk_buff *skb), - bool unicast) +/* called under rcu_read_lock */ +void br_flood(struct net_bridge *br, struct sk_buff *skb, + bool unicast, bool local_rcv, bool local_orig) { u8 igmp_type = br_multicast_igmp_type(skb); - struct net_bridge_port *prev; + struct net_bridge_port *prev = NULL; struct net_bridge_port *p; - prev = NULL; - list_for_each_entry_rcu(p, &br->port_list, list) { /* Do not flood unicast traffic to ports that turn it off */ if (unicast && !(p->flags & BR_FLOOD)) @@ -216,7 +193,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, BR_INPUT_SKB_CB(skb)->proxyarp_replied) continue; - prev = maybe_deliver(prev, p, skb, __packet_hook); + prev = maybe_deliver(prev, p, skb, local_orig); if (IS_ERR(prev)) goto out; if (prev == p) @@ -227,38 +204,22 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, if (!prev) goto out; - if (skb0) - deliver_clone(prev, skb, __packet_hook); + if (local_rcv) + deliver_clone(prev, skb, local_orig); else - __packet_hook(prev, skb); + __br_forward(prev, skb, local_orig); return; out: - if (!skb0) + if (!local_rcv) kfree_skb(skb); } - -/* called with rcu_read_lock */ -void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast) -{ - br_flood(br, skb, NULL, __br_deliver, unicast); -} - -/* called under bridge lock */ -void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, - struct sk_buff *skb2, bool unicast) -{ - br_flood(br, skb, skb2, __br_forward, unicast); -} - #ifdef CONFIG_BRIDGE_IGMP_SNOOPING /* called with rcu_read_lock */ -static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb, struct sk_buff *skb0, - void (*__packet_hook)( - const struct net_bridge_port *p, - struct sk_buff *skb)) +void br_multicast_flood(struct net_bridge_mdb_entry *mdst, + struct sk_buff *skb, + bool local_rcv, bool local_orig) { struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; u8 igmp_type = br_multicast_igmp_type(skb); @@ -279,7 +240,7 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, port = (unsigned long)lport > (unsigned long)rport ? lport : rport; - prev = maybe_deliver(prev, port, skb, __packet_hook); + prev = maybe_deliver(prev, port, skb, local_orig); if (IS_ERR(prev)) goto out; if (prev == port) @@ -295,28 +256,14 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, if (!prev) goto out; - if (skb0) - deliver_clone(prev, skb, __packet_hook); + if (local_rcv) + deliver_clone(prev, skb, local_orig); else - __packet_hook(prev, skb); + __br_forward(prev, skb, local_orig); return; out: - if (!skb0) + if (!local_rcv) kfree_skb(skb); } - -/* called with rcu_read_lock */ -void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb) -{ - br_multicast_flood(mdst, skb, NULL, __br_deliver); -} - -/* called with rcu_read_lock */ -void br_multicast_forward(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb, struct sk_buff *skb2) -{ - br_multicast_flood(mdst, skb, skb2, __br_forward); -} #endif diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index a7817e6f306f..8b08eec763a5 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -131,13 +131,12 @@ static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, /* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { - const unsigned char *dest = eth_hdr(skb)->h_dest; + bool local_rcv = false, mcast_hit = false, unicast = true; struct net_bridge_port *p = br_port_get_rcu(skb->dev); - struct net_bridge *br; - struct net_bridge_fdb_entry *dst; + const unsigned char *dest = eth_hdr(skb)->h_dest; + struct net_bridge_fdb_entry *dst = NULL; struct net_bridge_mdb_entry *mdst; - struct sk_buff *skb2; - bool unicast = true; + struct net_bridge *br; u16 vid = 0; if (!p || p->state == BR_STATE_DISABLED) @@ -160,53 +159,46 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb BR_INPUT_SKB_CB(skb)->brdev = br->dev; - /* The packet skb2 goes to the local host (NULL to skip). */ - skb2 = NULL; - - if (br->dev->flags & IFF_PROMISC) - skb2 = skb; - - dst = NULL; + local_rcv = !!(br->dev->flags & IFF_PROMISC); 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)) { - skb2 = skb; + local_rcv = true; unicast = false; } else if (is_multicast_ether_addr(dest)) { mdst = br_mdb_get(br, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && br_multicast_querier_exists(br, eth_hdr(skb))) { if ((mdst && mdst->mglist) || - br_multicast_is_router(br)) - skb2 = skb; - br_multicast_forward(mdst, skb, skb2); - skb = NULL; - if (!skb2) - goto out; - } else - skb2 = skb; - + br_multicast_is_router(br)) { + local_rcv = true; + br->dev->stats.multicast++; + } + mcast_hit = true; + } else { + local_rcv = true; + br->dev->stats.multicast++; + } unicast = false; - br->dev->stats.multicast++; - } else if ((dst = __br_fdb_get(br, dest, vid)) && - dst->is_local) { - skb2 = skb; + } else if ((dst = __br_fdb_get(br, dest, vid)) && dst->is_local) { /* Do not forward the packet since it's local. */ - skb = NULL; + return br_pass_frame_up(skb); } - if (skb) { - if (dst) { - dst->used = jiffies; - br_forward(dst->dst, skb, skb2); - } else - br_flood_forward(br, skb, skb2, unicast); + if (dst) { + dst->used = jiffies; + br_forward(dst->dst, skb, local_rcv, false); + } else { + if (!mcast_hit) + br_flood(br, skb, unicast, local_rcv, false); + else + br_multicast_flood(mdst, skb, local_rcv, false); } - if (skb2) - return br_pass_frame_up(skb2); + if (local_rcv) + return br_pass_frame_up(skb); out: return 0; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 40f200947ddc..b3088264f844 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -505,14 +505,12 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, u16 vid); /* br_forward.c */ -void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb); -void br_forward(const struct net_bridge_port *to, - struct sk_buff *skb, struct sk_buff *skb0); +void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, + bool local_rcv, bool local_orig); int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb); -void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast); -void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, - struct sk_buff *skb2, bool unicast); +void br_flood(struct net_bridge *br, struct sk_buff *skb, + bool unicast, bool local_rcv, bool local_orig); /* br_if.c */ void br_port_carrier_check(struct net_bridge_port *p); @@ -560,10 +558,8 @@ void br_multicast_init(struct net_bridge *br); void br_multicast_open(struct net_bridge *br); void br_multicast_stop(struct net_bridge *br); void br_multicast_dev_del(struct net_bridge *br); -void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb); -void br_multicast_forward(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb, struct sk_buff *skb2); +void br_multicast_flood(struct net_bridge_mdb_entry *mdst, + struct sk_buff *skb, bool local_rcv, bool local_orig); int br_multicast_set_router(struct net_bridge *br, unsigned long val); int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val); int br_multicast_toggle(struct net_bridge *br, unsigned long val); @@ -691,28 +687,27 @@ static inline void br_multicast_dev_del(struct net_bridge *br) { } -static inline void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb) +static inline void br_multicast_flood(struct net_bridge_mdb_entry *mdst, + struct sk_buff *skb, + bool local_rcv, bool local_orig) { } -static inline void br_multicast_forward(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb, - struct sk_buff *skb2) -{ -} static inline bool br_multicast_is_router(struct net_bridge *br) { return 0; } + static inline bool br_multicast_querier_exists(struct net_bridge *br, struct ethhdr *eth) { return false; } + static inline void br_mdb_init(void) { } + static inline void br_mdb_uninit(void) { } diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index 77f7e7a9ebe1..0b77ffbc27d6 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c @@ -72,7 +72,7 @@ static void nft_reject_br_send_v4_tcp_reset(struct net *net, nft_reject_br_push_etherhdr(oldskb, nskb); - br_deliver(br_port_get_rcu(dev), nskb); + br_forward(br_port_get_rcu(dev), nskb, false, true); } static void nft_reject_br_send_v4_unreach(struct net *net, @@ -140,7 +140,7 @@ static void nft_reject_br_send_v4_unreach(struct net *net, nft_reject_br_push_etherhdr(oldskb, nskb); - br_deliver(br_port_get_rcu(dev), nskb); + br_forward(br_port_get_rcu(dev), nskb, false, true); } static void nft_reject_br_send_v6_tcp_reset(struct net *net, @@ -174,7 +174,7 @@ static void nft_reject_br_send_v6_tcp_reset(struct net *net, nft_reject_br_push_etherhdr(oldskb, nskb); - br_deliver(br_port_get_rcu(dev), nskb); + br_forward(br_port_get_rcu(dev), nskb, false, true); } static bool reject6_br_csum_ok(struct sk_buff *skb, int hook) @@ -255,7 +255,7 @@ static void nft_reject_br_send_v6_unreach(struct net *net, nft_reject_br_push_etherhdr(oldskb, nskb); - br_deliver(br_port_get_rcu(dev), nskb); + br_forward(br_port_get_rcu(dev), nskb, false, true); } static void nft_reject_bridge_eval(const struct nft_expr *expr,