162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Forwarding decision 462306a36Sopenharmony_ci * Linux ethernet bridge 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Lennert Buytenhek <buytenh@gnu.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/netdevice.h> 1462306a36Sopenharmony_ci#include <linux/netpoll.h> 1562306a36Sopenharmony_ci#include <linux/skbuff.h> 1662306a36Sopenharmony_ci#include <linux/if_vlan.h> 1762306a36Sopenharmony_ci#include <linux/netfilter_bridge.h> 1862306a36Sopenharmony_ci#include "br_private.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Don't forward packets to originating port or forwarding disabled */ 2162306a36Sopenharmony_cistatic inline int should_deliver(const struct net_bridge_port *p, 2262306a36Sopenharmony_ci const struct sk_buff *skb) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct net_bridge_vlan_group *vg; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci vg = nbp_vlan_group_rcu(p); 2762306a36Sopenharmony_ci return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && 2862306a36Sopenharmony_ci p->state == BR_STATE_FORWARDING && br_allowed_egress(vg, skb) && 2962306a36Sopenharmony_ci nbp_switchdev_allowed_egress(p, skb) && 3062306a36Sopenharmony_ci !br_skb_isolated(p, skb); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciint br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci skb_push(skb, ETH_HLEN); 3662306a36Sopenharmony_ci if (!is_skb_forwardable(skb->dev, skb)) 3762306a36Sopenharmony_ci goto drop; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci br_drop_fake_rtable(skb); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL && 4262306a36Sopenharmony_ci eth_type_vlan(skb->protocol)) { 4362306a36Sopenharmony_ci int depth; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (!vlan_get_protocol_and_depth(skb, skb->protocol, &depth)) 4662306a36Sopenharmony_ci goto drop; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci skb_set_network_header(skb, depth); 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci br_switchdev_frame_set_offload_fwd_mark(skb); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci dev_queue_xmit(skb); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cidrop: 5862306a36Sopenharmony_ci kfree_skb(skb); 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_dev_queue_push_xmit); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ciint br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci skb_clear_tstamp(skb); 6662306a36Sopenharmony_ci return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, 6762306a36Sopenharmony_ci net, sk, skb, NULL, skb->dev, 6862306a36Sopenharmony_ci br_dev_queue_push_xmit); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_forward_finish); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void __br_forward(const struct net_bridge_port *to, 7462306a36Sopenharmony_ci struct sk_buff *skb, bool local_orig) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct net_bridge_vlan_group *vg; 7762306a36Sopenharmony_ci struct net_device *indev; 7862306a36Sopenharmony_ci struct net *net; 7962306a36Sopenharmony_ci int br_hook; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Mark the skb for forwarding offload early so that br_handle_vlan() 8262306a36Sopenharmony_ci * can know whether to pop the VLAN header on egress or keep it. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci nbp_switchdev_frame_mark_tx_fwd_offload(to, skb); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci vg = nbp_vlan_group_rcu(to); 8762306a36Sopenharmony_ci skb = br_handle_vlan(to->br, to, vg, skb); 8862306a36Sopenharmony_ci if (!skb) 8962306a36Sopenharmony_ci return; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci indev = skb->dev; 9262306a36Sopenharmony_ci skb->dev = to->dev; 9362306a36Sopenharmony_ci if (!local_orig) { 9462306a36Sopenharmony_ci if (skb_warn_if_lro(skb)) { 9562306a36Sopenharmony_ci kfree_skb(skb); 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci br_hook = NF_BR_FORWARD; 9962306a36Sopenharmony_ci skb_forward_csum(skb); 10062306a36Sopenharmony_ci net = dev_net(indev); 10162306a36Sopenharmony_ci } else { 10262306a36Sopenharmony_ci if (unlikely(netpoll_tx_running(to->br->dev))) { 10362306a36Sopenharmony_ci skb_push(skb, ETH_HLEN); 10462306a36Sopenharmony_ci if (!is_skb_forwardable(skb->dev, skb)) 10562306a36Sopenharmony_ci kfree_skb(skb); 10662306a36Sopenharmony_ci else 10762306a36Sopenharmony_ci br_netpoll_send_skb(to, skb); 10862306a36Sopenharmony_ci return; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci br_hook = NF_BR_LOCAL_OUT; 11162306a36Sopenharmony_ci net = dev_net(skb->dev); 11262306a36Sopenharmony_ci indev = NULL; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci NF_HOOK(NFPROTO_BRIDGE, br_hook, 11662306a36Sopenharmony_ci net, NULL, skb, indev, skb->dev, 11762306a36Sopenharmony_ci br_forward_finish); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int deliver_clone(const struct net_bridge_port *prev, 12162306a36Sopenharmony_ci struct sk_buff *skb, bool local_orig) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci skb = skb_clone(skb, GFP_ATOMIC); 12662306a36Sopenharmony_ci if (!skb) { 12762306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_dropped); 12862306a36Sopenharmony_ci return -ENOMEM; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci __br_forward(prev, skb, local_orig); 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * br_forward - forward a packet to a specific port 13762306a36Sopenharmony_ci * @to: destination port 13862306a36Sopenharmony_ci * @skb: packet being forwarded 13962306a36Sopenharmony_ci * @local_rcv: packet will be received locally after forwarding 14062306a36Sopenharmony_ci * @local_orig: packet is locally originated 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * Should be called with rcu_read_lock. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_civoid br_forward(const struct net_bridge_port *to, 14562306a36Sopenharmony_ci struct sk_buff *skb, bool local_rcv, bool local_orig) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci if (unlikely(!to)) 14862306a36Sopenharmony_ci goto out; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* redirect to backup link if the destination port is down */ 15162306a36Sopenharmony_ci if (rcu_access_pointer(to->backup_port) && !netif_carrier_ok(to->dev)) { 15262306a36Sopenharmony_ci struct net_bridge_port *backup_port; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci backup_port = rcu_dereference(to->backup_port); 15562306a36Sopenharmony_ci if (unlikely(!backup_port)) 15662306a36Sopenharmony_ci goto out; 15762306a36Sopenharmony_ci BR_INPUT_SKB_CB(skb)->backup_nhid = READ_ONCE(to->backup_nhid); 15862306a36Sopenharmony_ci to = backup_port; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (should_deliver(to, skb)) { 16262306a36Sopenharmony_ci if (local_rcv) 16362306a36Sopenharmony_ci deliver_clone(to, skb, local_orig); 16462306a36Sopenharmony_ci else 16562306a36Sopenharmony_ci __br_forward(to, skb, local_orig); 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ciout: 17062306a36Sopenharmony_ci if (!local_rcv) 17162306a36Sopenharmony_ci kfree_skb(skb); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_forward); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic struct net_bridge_port *maybe_deliver( 17662306a36Sopenharmony_ci struct net_bridge_port *prev, struct net_bridge_port *p, 17762306a36Sopenharmony_ci struct sk_buff *skb, bool local_orig) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci u8 igmp_type = br_multicast_igmp_type(skb); 18062306a36Sopenharmony_ci int err; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!should_deliver(p, skb)) 18362306a36Sopenharmony_ci return prev; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci nbp_switchdev_frame_mark_tx_fwd_to_hwdom(p, skb); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!prev) 18862306a36Sopenharmony_ci goto out; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci err = deliver_clone(prev, skb, local_orig); 19162306a36Sopenharmony_ci if (err) 19262306a36Sopenharmony_ci return ERR_PTR(err); 19362306a36Sopenharmony_ciout: 19462306a36Sopenharmony_ci br_multicast_count(p->br, p, skb, igmp_type, BR_MCAST_DIR_TX); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return p; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* called under rcu_read_lock */ 20062306a36Sopenharmony_civoid br_flood(struct net_bridge *br, struct sk_buff *skb, 20162306a36Sopenharmony_ci enum br_pkt_type pkt_type, bool local_rcv, bool local_orig, 20262306a36Sopenharmony_ci u16 vid) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct net_bridge_port *prev = NULL; 20562306a36Sopenharmony_ci struct net_bridge_port *p; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci br_tc_skb_miss_set(skb, pkt_type != BR_PKT_BROADCAST); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci list_for_each_entry_rcu(p, &br->port_list, list) { 21062306a36Sopenharmony_ci /* Do not flood unicast traffic to ports that turn it off, nor 21162306a36Sopenharmony_ci * other traffic if flood off, except for traffic we originate 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci switch (pkt_type) { 21462306a36Sopenharmony_ci case BR_PKT_UNICAST: 21562306a36Sopenharmony_ci if (!(p->flags & BR_FLOOD)) 21662306a36Sopenharmony_ci continue; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case BR_PKT_MULTICAST: 21962306a36Sopenharmony_ci if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) 22062306a36Sopenharmony_ci continue; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci case BR_PKT_BROADCAST: 22362306a36Sopenharmony_ci if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev) 22462306a36Sopenharmony_ci continue; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Do not flood to ports that enable proxy ARP */ 22962306a36Sopenharmony_ci if (p->flags & BR_PROXYARP) 23062306a36Sopenharmony_ci continue; 23162306a36Sopenharmony_ci if (BR_INPUT_SKB_CB(skb)->proxyarp_replied && 23262306a36Sopenharmony_ci ((p->flags & BR_PROXYARP_WIFI) || 23362306a36Sopenharmony_ci br_is_neigh_suppress_enabled(p, vid))) 23462306a36Sopenharmony_ci continue; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci prev = maybe_deliver(prev, p, skb, local_orig); 23762306a36Sopenharmony_ci if (IS_ERR(prev)) 23862306a36Sopenharmony_ci goto out; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!prev) 24262306a36Sopenharmony_ci goto out; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (local_rcv) 24562306a36Sopenharmony_ci deliver_clone(prev, skb, local_orig); 24662306a36Sopenharmony_ci else 24762306a36Sopenharmony_ci __br_forward(prev, skb, local_orig); 24862306a36Sopenharmony_ci return; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciout: 25162306a36Sopenharmony_ci if (!local_rcv) 25262306a36Sopenharmony_ci kfree_skb(skb); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING 25662306a36Sopenharmony_cistatic void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb, 25762306a36Sopenharmony_ci const unsigned char *addr, bool local_orig) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; 26062306a36Sopenharmony_ci const unsigned char *src = eth_hdr(skb)->h_source; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!should_deliver(p, skb)) 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */ 26662306a36Sopenharmony_ci if (skb->dev == p->dev && ether_addr_equal(src, addr)) 26762306a36Sopenharmony_ci return; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci skb = skb_copy(skb, GFP_ATOMIC); 27062306a36Sopenharmony_ci if (!skb) { 27162306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_dropped); 27262306a36Sopenharmony_ci return; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (!is_broadcast_ether_addr(addr)) 27662306a36Sopenharmony_ci memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci __br_forward(p, skb, local_orig); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* called with rcu_read_lock */ 28262306a36Sopenharmony_civoid br_multicast_flood(struct net_bridge_mdb_entry *mdst, 28362306a36Sopenharmony_ci struct sk_buff *skb, 28462306a36Sopenharmony_ci struct net_bridge_mcast *brmctx, 28562306a36Sopenharmony_ci bool local_rcv, bool local_orig) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct net_bridge_port *prev = NULL; 28862306a36Sopenharmony_ci struct net_bridge_port_group *p; 28962306a36Sopenharmony_ci bool allow_mode_include = true; 29062306a36Sopenharmony_ci struct hlist_node *rp; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci rp = br_multicast_get_first_rport_node(brmctx, skb); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (mdst) { 29562306a36Sopenharmony_ci p = rcu_dereference(mdst->ports); 29662306a36Sopenharmony_ci if (br_multicast_should_handle_mode(brmctx, mdst->addr.proto) && 29762306a36Sopenharmony_ci br_multicast_is_star_g(&mdst->addr)) 29862306a36Sopenharmony_ci allow_mode_include = false; 29962306a36Sopenharmony_ci } else { 30062306a36Sopenharmony_ci p = NULL; 30162306a36Sopenharmony_ci br_tc_skb_miss_set(skb, true); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci while (p || rp) { 30562306a36Sopenharmony_ci struct net_bridge_port *port, *lport, *rport; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci lport = p ? p->key.port : NULL; 30862306a36Sopenharmony_ci rport = br_multicast_rport_from_node_skb(rp, skb); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if ((unsigned long)lport > (unsigned long)rport) { 31162306a36Sopenharmony_ci port = lport; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (port->flags & BR_MULTICAST_TO_UNICAST) { 31462306a36Sopenharmony_ci maybe_deliver_addr(lport, skb, p->eth_addr, 31562306a36Sopenharmony_ci local_orig); 31662306a36Sopenharmony_ci goto delivered; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci if ((!allow_mode_include && 31962306a36Sopenharmony_ci p->filter_mode == MCAST_INCLUDE) || 32062306a36Sopenharmony_ci (p->flags & MDB_PG_FLAGS_BLOCKED)) 32162306a36Sopenharmony_ci goto delivered; 32262306a36Sopenharmony_ci } else { 32362306a36Sopenharmony_ci port = rport; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci prev = maybe_deliver(prev, port, skb, local_orig); 32762306a36Sopenharmony_ci if (IS_ERR(prev)) 32862306a36Sopenharmony_ci goto out; 32962306a36Sopenharmony_cidelivered: 33062306a36Sopenharmony_ci if ((unsigned long)lport >= (unsigned long)port) 33162306a36Sopenharmony_ci p = rcu_dereference(p->next); 33262306a36Sopenharmony_ci if ((unsigned long)rport >= (unsigned long)port) 33362306a36Sopenharmony_ci rp = rcu_dereference(hlist_next_rcu(rp)); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!prev) 33762306a36Sopenharmony_ci goto out; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (local_rcv) 34062306a36Sopenharmony_ci deliver_clone(prev, skb, local_orig); 34162306a36Sopenharmony_ci else 34262306a36Sopenharmony_ci __br_forward(prev, skb, local_orig); 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ciout: 34662306a36Sopenharmony_ci if (!local_rcv) 34762306a36Sopenharmony_ci kfree_skb(skb); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci#endif 350