static int __br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_bridge *br; unsigned char *dest; struct net_bridge_fdb_entry *dst; br = dev->priv; br->statistics.tx_packets++; br->statistics.tx_bytes += skb->len; dest = skb->data; if (dest[0] & 1) { br_flood(br, skb, 0); return 0; } if ((dst = br_fdb_get(br, dest)) != NULL) { br_forward(dst->dst, skb); br_fdb_put(dst); return 0; } br_flood(br, skb, 0); return 0; }
void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone) { br_flood(br, skb, clone, __br_forward); }
void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone) { br_flood(br, skb, clone, __br_deliver); }
static int br_forward(void *pkt, int len, int port) /* 3.7 */ { struct fdb *f; struct ethhdr *eth; eth = (struct ethhdr*)pkt; #ifdef BR_DEBUG debug_print("br_forward be call.... \r\n"); #endif /* * flood all ports with frames destined for a group * address. If frame came from above, drop it, * otherwise it will be handled in br_receive_frame() * Multicast frames will also need to be seen * by our upper layers. */ if (eth->ether_dhost[0] & 0x01) { #ifdef BR_DEBUG debug_print("dest is group address .... \r\n"); #endif /* group address */ br_flood(pkt,len, port); /* * External groups are fed out via the normal source * This probably should be dropped since the flood will * have sent it anyway. */ if (port == 0) { /* Locally generated */ ++br_stats_cnt.local_multicast; return(0); } ++br_stats_cnt.forwarded_multicast; return(0); } else { /* unicast frame, locate port to forward to */ f = br_find_addr(eth->ether_dhost); /* * Send flood and drop. */ if (!f || !(f->flags & FDB_ENT_VALID)) { if(f && (f->port == 0)) { ++br_stats_cnt.forwarded_unicast_up_stack; #ifdef BR_DEBUG debug_print("br: port is invaild .. \r\n"); #endif return(0); } /* not found or too old; flood all ports */ ++br_stats_cnt.flood_unicast; debug_print("br_flood packet ... \r\n"); br_flood(pkt,len, port); return(0); } /* * Sending */ if (f->port!=port && port_info[f->port].state == Forwarding) { struct nic *dev; #ifdef BR_DEBUG debug_print("br: the dest in diffrent port , so forward it .. \r\n"); #endif /* Has entry expired? */ if (f->timer + fdb_aging_time < CURRENT_TIME) { #ifdef BR_DEBUG debug_print("br: port have invaild .. \r\n"); #endif /* timer expired, invalidate entry */ f->flags &= ~FDB_ENT_VALID; /* * Send flood and drop original */ ++br_stats_cnt.aged_flood_unicast; br_flood(pkt,len, port); return(0); } ++br_stats_cnt.forwarded_unicast; /* * Send the buffer out. */ dev = port_info[f->port].dev; #ifdef BR_DEBUG debug_print("br: every thing is ok , forward it .. \r\n"); #endif dev->transmit(dev,pkt,len); return(1); /* skb has been consumed */ } else { #ifdef BR_DEBUG debug_print("br: dest in same port .. \r\n"); #endif /* JRP: Needs to aged entry as well, if topology changes * the entry would not age. Got this while swapping * two cables ! * * Has entry expired? */ if (f->timer + fdb_aging_time < CURRENT_TIME) { /* timer expired, invalidate entry */ f->flags &= ~FDB_ENT_VALID; ++br_stats_cnt.drop_same_port_aged; } else ++br_stats_cnt.drop_same_port; /* * Arrived on the right port, we discard */ return(0); } } }
/* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { bool local_rcv = false, mcast_hit = false, unicast = true; struct net_bridge_port *p = br_port_get_rcu(skb->dev); const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_fdb_entry *dst = NULL; struct net_bridge_mdb_entry *mdst; struct net_bridge *br; u16 vid = 0; if (!p || p->state == BR_STATE_DISABLED) goto drop; if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid)) goto out; /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; if (p->flags & BR_LEARNING) br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false); if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && br_multicast_rcv(br, p, skb, vid)) goto drop; if (p->state == BR_STATE_LEARNING) goto drop; BR_INPUT_SKB_CB(skb)->brdev = br->dev; 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)) { 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)) { local_rcv = true; br->dev->stats.multicast++; } mcast_hit = true; } else { local_rcv = true; br->dev->stats.multicast++; } unicast = false; } else if ((dst = __br_fdb_get(br, dest, vid)) && dst->is_local) { /* Do not forward the packet since it's local. */ return br_pass_frame_up(skb); } 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 (local_rcv) return br_pass_frame_up(skb); out: return 0; drop: kfree_skb(skb); goto out; }
/* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { struct net_bridge_port *p = br_port_get_rcu(skb->dev); enum br_pkt_type pkt_type = BR_PKT_UNICAST; struct net_bridge_fdb_entry *dst = NULL; struct net_bridge_mdb_entry *mdst; bool local_rcv, mcast_hit = false; const unsigned char *dest; struct net_bridge *br; u16 vid = 0; if (!p || p->state == BR_STATE_DISABLED) goto drop; if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid)) goto out; nbp_switchdev_frame_mark(p, skb); /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; if (p->flags & BR_LEARNING) br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false); local_rcv = !!(br->dev->flags & IFF_PROMISC); dest = eth_hdr(skb)->h_dest; if (is_multicast_ether_addr(dest)) { /* by definition the broadcast is also a multicast address */ if (is_broadcast_ether_addr(dest)) { pkt_type = BR_PKT_BROADCAST; local_rcv = true; } else { pkt_type = BR_PKT_MULTICAST; if (br_multicast_rcv(br, p, skb, vid)) goto drop; } } if (p->state == BR_STATE_LEARNING) goto drop; BR_INPUT_SKB_CB(skb)->brdev = br->dev; if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP)) br_do_proxy_arp(skb, br, vid, p); switch (pkt_type) { case BR_PKT_MULTICAST: 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)) { local_rcv = true; br->dev->stats.multicast++; } mcast_hit = true; } else { local_rcv = true; br->dev->stats.multicast++; } break; case BR_PKT_UNICAST: dst = br_fdb_find_rcu(br, dest, vid); default: break; } if (dst) { unsigned long now = jiffies; if (dst->is_local) return br_pass_frame_up(skb); if (now != dst->used) dst->used = now; br_forward(dst->dst, skb, local_rcv, false); } else { if (!mcast_hit) br_flood(br, skb, pkt_type, local_rcv, false); else br_multicast_flood(mdst, skb, local_rcv, false); } if (local_rcv) return br_pass_frame_up(skb); out: return 0; drop: kfree_skb(skb); goto out; }