int br_add_if(struct net_bridge *br, struct net_device *dev) { struct net_bridge_port *p; if (dev->br_port != NULL) return -EBUSY; if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) return -EINVAL; dev_hold(dev); write_lock_bh(&br->lock); if ((p = new_nbp(br, dev)) == NULL) { write_unlock_bh(&br->lock); dev_put(dev); return -EXFULL; } dev_set_promiscuity(dev, 1); br_stp_recalculate_bridge_id(br); br_fdb_insert(br, p, dev->dev_addr, 1); if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP)) br_stp_enable_port(p); write_unlock_bh(&br->lock); return 0; }
void br_handle_frame(struct sk_buff *skb) { struct net_bridge *br; unsigned char *dest; struct net_bridge_port *p; dest = skb->mac.ethernet->h_dest; p = skb->dev->br_port; if (p == NULL) goto err_nolock; br = p->br; read_lock(&br->lock); if (skb->dev->br_port == NULL) goto err; if (!(br->dev.flags & IFF_UP) || p->state == BR_STATE_DISABLED) goto err; if (skb->mac.ethernet->h_source[0] & 1) goto err; if (p->state == BR_STATE_LEARNING || p->state == BR_STATE_FORWARDING) br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0); if (br->stp_enabled && !memcmp(dest, bridge_ula, 5) && !(dest[5] & 0xF0)) goto handle_special_frame; if (p->state == BR_STATE_FORWARDING) { NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish); read_unlock(&br->lock); return; } err: read_unlock(&br->lock); err_nolock: kfree_skb(skb); return; handle_special_frame: if (!dest[5]) { NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL, br_stp_handle_bpdu); read_unlock(&br->lock); return; } read_unlock(&br->lock); kfree_skb(skb); }
static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) { const struct net_device_ops *ops; struct net_bridge_port *p = NULL; struct net_bridge *br; struct net_device *dev; int err; if (test_bit(vid, v->vlan_bitmap)) { __vlan_add_flags(v, vid, flags); return 0; } if (vid) { if (v->port_idx) { p = v->parent.port; br = p->br; dev = p->dev; } else { br = v->parent.br; dev = br->dev; } ops = dev->netdev_ops; if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) { /* Add VLAN to the device filter if it is supported. * Stricly speaking, this is not necessary now, since * devices are made promiscuous by the bridge, but if * that ever changes this code will allow tagged * traffic to enter the bridge. */ err = ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q), vid); if (err) return err; } err = br_fdb_insert(br, p, dev->dev_addr, vid); if (err) { br_err(br, "failed insert local address into bridge " "forwarding table\n"); goto out_filt; } } set_bit(vid, v->vlan_bitmap); v->num_vlans++; __vlan_add_flags(v, vid, flags); return 0; out_filt: if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) ops->ndo_vlan_rx_kill_vid(dev, htons(ETH_P_8021Q), vid); return err; }
/* note: already called with rcu_read_lock (preempt_disabled) */ int br_handle_frame_finish(struct sk_buff *skb) { const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_port *p = skb->dev->br_port; struct net_bridge *br = p->br; struct net_bridge_fdb_entry *dst; int passedup = 0; /* insert into forwarding database after filtering to avoid spoofing */ br_fdb_insert(p->br, p, eth_hdr(skb)->h_source, 0); if (br->dev->flags & IFF_PROMISC) { struct sk_buff *skb2; skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2 != NULL) { passedup = 1; br_pass_frame_up(br, skb2); } } if (dest[0] & 1) { br_flood_forward(br, skb, !passedup); if (!passedup) br_pass_frame_up(br, skb); goto out; } dst = __br_fdb_get(br, dest); if (dst != NULL && dst->is_local) { if (!passedup) br_pass_frame_up(br, skb); else kfree_skb(skb); goto out; } if (dst != NULL) { br_forward(dst->dst, skb); goto out; } br_flood_forward(br, skb, 0); out: return 0; }
/* * Called via br_handle_frame_hook. * Return 0 if *pskb should be processed furthur * 1 if *pskb is handled * note: already called with rcu_read_lock (preempt_disabled) */ int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb) { struct sk_buff *skb = *pskb; const unsigned char *dest = eth_hdr(skb)->h_dest; if (p->state == BR_STATE_DISABLED) goto err; if (eth_hdr(skb)->h_source[0] & 1) goto err; if (p->state == BR_STATE_LEARNING || p->state == BR_STATE_FORWARDING) br_fdb_insert(p->br, p, eth_hdr(skb)->h_source, 0); if (p->br->stp_enabled && !memcmp(dest, bridge_ula, 5) && !(dest[5] & 0xF0)) { if (!dest[5]) { NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, NULL, br_stp_handle_bpdu); return 1; } } else if (p->state == BR_STATE_FORWARDING) { if (br_should_route_hook) { if (br_should_route_hook(pskb)) return 0; skb = *pskb; dest = eth_hdr(skb)->h_dest; } if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN)) skb->pkt_type = PACKET_HOST; NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish); return 1; } err: kfree_skb(skb); return 1; }
int br_add_if(struct net_bridge *br, struct net_device *dev) { struct net_bridge_port *p; if (dev->br_port != NULL) return -EBUSY; #if 0 if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) return -EINVAL; #endif if (dev->hard_start_xmit == br_dev_xmit) return -ELOOP; if (!is_valid_ether_addr(dev->dev_addr)) return -EADDRNOTAVAIL; dev_hold(dev); write_lock_bh(&br->lock); if ((p = new_nbp(br, dev)) == NULL) { write_unlock_bh(&br->lock); dev_put(dev); return -EXFULL; } dev_set_promiscuity(dev, 1); br_stp_recalculate_bridge_id(br); br_fdb_insert(br, p, dev->dev_addr, 1); if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP)) br_stp_enable_port(p); write_unlock_bh(&br->lock); return 0; }