/* net device transmit always called with no BH (preempt_disabled) */ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); const unsigned char *dest = skb->data; struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; BR_INPUT_SKB_CB(skb)->brdev = dev; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); if (dest[0] & 1) { if (br_multicast_rcv(br, NULL, skb)) goto out; mdst = br_mdb_get(br, skb); if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) br_multicast_deliver(mdst, skb); else br_flood_deliver(br, skb); } else if ((dst = __br_fdb_get(br, dest)) != NULL) br_deliver(dst->dst, skb); else br_flood_deliver(br, skb); out: return NETDEV_TX_OK; }
/* 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 = rcu_dereference(skb->dev->br_port); struct net_bridge *br; struct net_bridge_fdb_entry *dst; int passedup = 0; if (!p || p->state == BR_STATE_DISABLED) goto drop; /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; br_fdb_update(br, p, eth_hdr(skb)->h_source); if (p->state == BR_STATE_LEARNING) goto drop; 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 (is_multicast_ether_addr(dest)) { br->statistics.multicast++; 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; drop: kfree_skb(skb); goto out; }
static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, u16 vid, struct net_bridge_port *p) { struct net_device *dev = br->dev; struct neighbour *n; struct arphdr *parp; u8 *arpptr, *sha; __be32 sip, tip; BR_INPUT_SKB_CB(skb)->proxyarp_replied = false; if (dev->flags & IFF_NOARP) return; if (!pskb_may_pull(skb, arp_hdr_len(dev))) { dev->stats.tx_dropped++; return; } parp = arp_hdr(skb); if (parp->ar_pro != htons(ETH_P_IP) || parp->ar_op != htons(ARPOP_REQUEST) || parp->ar_hln != dev->addr_len || parp->ar_pln != 4) return; arpptr = (u8 *)parp + sizeof(struct arphdr); sha = arpptr; arpptr += dev->addr_len; /* sha */ memcpy(&sip, arpptr, sizeof(sip)); arpptr += sizeof(sip); arpptr += dev->addr_len; /* tha */ memcpy(&tip, arpptr, sizeof(tip)); if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) return; n = neigh_lookup(&arp_tbl, &tip, dev); if (n) { struct net_bridge_fdb_entry *f; if (!(n->nud_state & NUD_VALID)) { neigh_release(n); return; } f = __br_fdb_get(br, n->ha, vid); if (f && ((p->flags & BR_PROXYARP) || (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)))) { arp_send(ARPOP_REPLY, ETH_P_ARP, sip, skb->dev, tip, sha, n->ha, sha); BR_INPUT_SKB_CB(skb)->proxyarp_replied = true; } neigh_release(n); } }
/* 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 = rcu_dereference(skb->dev->br_port); struct net_bridge *br; struct net_bridge_fdb_entry *dst; struct sk_buff *skb2; if (!p || p->state == BR_STATE_DISABLED) goto drop; /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; br_fdb_update(br, p, eth_hdr(skb)->h_source); if ((p->state == BR_STATE_LEARNING) && skb->protocol != htons(ETH_P_PAE)) goto drop; /* The packet skb2 goes to the local host (NULL to skip). */ skb2 = NULL; if (br->dev->flags & IFF_PROMISC) skb2 = skb; dst = NULL; if (skb->protocol == htons(ETH_P_PAE)) { skb2 = skb; /* Do not forward 802.1x/EAP frames */ skb = NULL; } else if (is_multicast_ether_addr(dest)) { br->statistics.multicast++; skb2 = skb; } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; } if (skb2 == skb) skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) br_pass_frame_up(br, skb2); if (skb) { if (dst) br_forward(dst->dst, skb); else br_flood_forward(br, skb); } out: return 0; drop: kfree_skb(skb); goto out; }
static unsigned int cs_br_nf_post_routing(unsigned int hook, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn) (struct sk_buff *)) { cs_kernel_accel_cb_t *cs_cb = CS_KERNEL_SKB_CB(skb); struct ethhdr *eth = eth_hdr(skb); const unsigned char *dest = eth->h_dest; struct net_bridge_port *p = br_port_get_rcu(skb->dev); struct net_bridge *br; struct net_bridge_fdb_entry *dst; if (!cs_cb) return NF_ACCEPT; if (cs_cb->common.tag != CS_CB_TAG) return NF_ACCEPT; /* If module mask contain L4 or PPPoE or VLAN, * it would not use L2 Flow HW acceleration * This is already judge in get_app_type_from_module_type() */ /*if (cs_cb->common.module_mask != CS_MOD_MASK_BRIDGE) return NF_ACCEPT; */ if (cs_bridge_enable() == 0) { cs_cb->common.sw_only = CS_SWONLY_STATE; return NF_ACCEPT; } if (cs_cb->common.sw_only == CS_SWONLY_STATE) { return NF_ACCEPT; } /*If there is no dst object, that will be flood packet */ if (p == NULL) return NF_ACCEPT; br = p->br; dst = __br_fdb_get(br, dest); if (dst == NULL) return NF_ACCEPT; cs_bridge_print(__func__, skb, in, out); DBG(printk("\t module_mask 0x%x ageing_time=%lu \n", cs_cb->common.module_mask, br->ageing_time)); cs_cb->common.output_dev = (struct net_device *)dst->dst->dev; cs_cb->common.sw_only = CS_SWONLY_HW_ACCEL; cs_core_logic_add_swid64(cs_cb, CS_MOD_BRIDGE_SWID); cs_core_logic_set_lifetime(cs_cb, br->ageing_time / HZ); return NF_ACCEPT; }
int br_handle_frame_finish(struct sk_buff *skb) { const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); struct net_bridge *br; struct net_bridge_fdb_entry *dst; struct sk_buff *skb2; if (!p || p->state == BR_STATE_DISABLED) goto drop; br = p->br; br_fdb_update(br, p, eth_hdr(skb)->h_source); if (p->state == BR_STATE_LEARNING) goto drop; skb2 = NULL; if (br->dev->flags & IFF_PROMISC) skb2 = skb; dst = NULL; if (is_multicast_ether_addr(dest)) { br->dev->stats.multicast++; skb2 = skb; } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { skb2 = skb; skb = NULL; } if (skb2 == skb) skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) br_pass_frame_up(br, skb2); if (skb) { if (dst) br_forward(dst->dst, skb); else br_flood_forward(br, skb); } out: return 0; drop: kfree_skb(skb); goto out; }
/* 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_update(p->br, p, eth_hdr(skb)->h_source); 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; }
/* net device transmit always called with no BH (preempt_disabled) */ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); const unsigned char *dest = skb->data; struct net_bridge_fdb_entry *dst; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); if (dest[0] & 1) br_flood_deliver(br, skb); else if ((dst = __br_fdb_get(br, dest)) != NULL) br_deliver(dst->dst, skb); else br_flood_deliver(br, skb); return 0; }
/* net device transmit always called with no BH (preempt_disabled) */ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); const unsigned char *dest = skb->data; struct net_bridge_fdb_entry *dst; #if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BLOG) blog_dev( skb, dev, DIR_TX, skb->len ); #endif dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); #if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP) if ((0x33 == dest[0]) && (0x33 == dest[1])) { if (!br_mld_mc_forward(br, skb, 0, 0)) br_flood_deliver(br, skb); } else #endif if (dest[0] & 1) { #if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_IGMP_SNOOP) if (!br_igmp_mc_forward(br, skb, 0, 0)) #endif br_flood_deliver(br, skb); } else if ((dst = __br_fdb_get(br, dest)) != NULL) br_deliver(dst->dst, skb); else br_flood_deliver(br, skb); return 0; }
/* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct sk_buff *skb) { const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_port *p = br_port_get_rcu(skb->dev); struct net_bridge *br; struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; struct sk_buff *skb2; if (!p || p->state == BR_STATE_DISABLED) goto drop; /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; br_fdb_update(br, p, eth_hdr(skb)->h_source); if (is_multicast_ether_addr(dest) && br_multicast_rcv(br, p, skb)) goto drop; if (p->state == BR_STATE_LEARNING) goto drop; 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; if (is_multicast_ether_addr(dest)) { mdst = br_mdb_get(br, skb); if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(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->dev->stats.multicast++; } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; } if (skb) { if (dst) br_forward(dst->dst, skb, skb2); else br_flood_forward(br, skb, skb2); } if (skb2) return br_pass_frame_up(skb2); out: return 0; drop: kfree_skb(skb); goto out; }
/* net device transmit always called with no BH (preempt_disabled) */ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); const unsigned char *dest = skb->data; struct net_bridge_fdb_entry *dst; #if defined (CONFIG_RTL_IGMP_SNOOPING) struct iphdr *iph=NULL; unsigned char proto=0; unsigned char reserved=0; #if defined (CONFIG_RTL_MLD_SNOOPING) struct ipv6hdr *ipv6h=NULL; #endif struct rtl_multicastDataInfo multicastDataInfo; struct rtl_multicastFwdInfo multicastFwdInfo; int ret=FAILED; #if defined (CONFIG_RTL_HARDWARE_MULTICAST) unsigned int srcPort=skb->srcPort; unsigned int srcVlanId=skb->srcVlanId; #endif #endif dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); if (dest[0] & 1) { #if defined (CONFIG_RTL_IGMP_SNOOPING) if(igmpsnoopenabled) { if(MULTICAST_MAC(dest)) { iph=(struct iphdr *)skb_network_header(skb); proto = iph->protocol; #if 0 if(( iph->daddr&0xFFFFFF00)==0xE0000000) { reserved=1; } #endif if( iph->daddr == 0xEFFFFFFA) { /*for microsoft upnp*/ reserved=1; } if(((proto ==IPPROTO_UDP) ||(proto ==IPPROTO_TCP)) && (reserved ==0)) { multicastDataInfo.ipVersion=4; multicastDataInfo.sourceIp[0]= (uint32)(iph->saddr); multicastDataInfo.groupAddr[0]= (uint32)(iph->daddr); ret= rtl_getMulticastDataFwdInfo(brIgmpModuleIndex, &multicastDataInfo, &multicastFwdInfo); if(ret==SUCCESS) { br_multicast_deliver(br, multicastFwdInfo.fwdPortMask, skb, 0); #if defined (CONFIG_RTL_HARDWARE_MULTICAST) if((srcVlanId!=0) && (srcPort!=0xFFFF)) { #if defined(CONFIG_RTK_VLAN_SUPPORT) if(rtk_vlan_support_enable == 0) { rtl865x_ipMulticastHardwareAccelerate(br, multicastFwdInfo.fwdPortMask,srcPort,srcVlanId, multicastDataInfo.sourceIp[0], multicastDataInfo.groupAddr[0]); } #else rtl865x_ipMulticastHardwareAccelerate(br, multicastFwdInfo.fwdPortMask,srcPort,srcVlanId, multicastDataInfo.sourceIp[0], multicastDataInfo.groupAddr[0]); #endif } #endif } else { br_flood_deliver(br, skb); } } else { br_flood_deliver(br, skb); } } #if defined(CONFIG_RTL_MLD_SNOOPING) else if(mldSnoopEnabled && IPV6_MULTICAST_MAC(dest)) { ipv6h=(struct ipv6hdr *)skb_network_header(skb); proto=re865x_getIpv6TransportProtocol(ipv6h); if ((proto ==IPPROTO_UDP) ||(proto ==IPPROTO_TCP)) { multicastDataInfo.ipVersion=6; memcpy(&multicastDataInfo.sourceIp, &ipv6h->saddr, sizeof(struct in6_addr)); memcpy(&multicastDataInfo.groupAddr, &ipv6h->daddr, sizeof(struct in6_addr)); ret= rtl_getMulticastDataFwdInfo(brIgmpModuleIndex, &multicastDataInfo, &multicastFwdInfo); if(ret==SUCCESS) { br_multicast_deliver(br, multicastFwdInfo.fwdPortMask, skb, 0); } else { br_flood_deliver(br, skb); } } else { br_flood_deliver(br, skb); } } #endif else { br_flood_deliver(br, skb); } } else { br_flood_deliver(br, skb); } #else br_flood_deliver(br, skb); #endif } else if ((dst = __br_fdb_get(br, dest)) != NULL) br_deliver(dst->dst, skb); else br_flood_deliver(br, skb); return 0; }
/* 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 = rcu_dereference(skb->dev->br_port); struct net_bridge *br; struct net_bridge_fdb_entry *dst; struct sk_buff *skb2; if (!p || p->state == BR_STATE_DISABLED) goto drop; /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; br_fdb_update(br, p, eth_hdr(skb)->h_source); if (p->state == BR_STATE_LEARNING) goto drop; /* The packet skb2 goes to the local host (NULL to skip). */ skb2 = NULL; if (br->dev->flags & IFF_PROMISC) skb2 = skb; dst = NULL; #if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_MLD_SNOOP) if((0x33 == dest[0]) && (0x33 == dest[1])) { br->statistics.multicast++; skb2 = skb; if (br_mld_mc_forward(br, skb, 1, 0)) { skb = NULL; } } else #endif if (is_multicast_ether_addr(dest)) { br->dev->stats.multicast++; skb2 = skb; #if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BR_IGMP_SNOOP) if (br_igmp_mc_forward(br, skb, 1, 0)) { skb = NULL; } #endif } else { dst = __br_fdb_get(br, dest); #if defined(CONFIG_MIPS_BRCM) && defined(CONFIG_BLOG) blog_br_fdb(skb, __br_fdb_get(br, eth_hdr(skb)->h_source), dst); #endif if ((dst != NULL) && dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; } } if (skb2 == skb) skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) br_pass_frame_up(br, skb2); if (skb) { if (dst) br_forward(dst->dst, skb); else br_flood_forward(br, skb); } out: return 0; drop: kfree_skb(skb); goto out; }
/* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct sk_buff *skb) { const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_port *p = br_port_get_rcu(skb->dev); struct net_bridge *br; struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; struct sk_buff *skb2; if (!p || p->state == BR_STATE_DISABLED) goto drop; /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; br_fdb_update(br, p, eth_hdr(skb)->h_source); if (is_multicast_ether_addr(dest) && br_multicast_rcv(br, p, skb)) goto drop; if (p->state == BR_STATE_LEARNING) goto drop; 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; if (is_multicast_ether_addr(dest)) { mdst = br_mdb_get(br, skb); if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) { if ((mdst && !hlist_unhashed(&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->dev->stats.multicast++; } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; } if (skb) { if (dst) br_forward(dst->dst, skb, skb2); else br_flood_forward(br, skb, skb2); } if (skb2) #ifdef CONFIG_WIRELESS_GUEST_ZONE { /* deny guest zone access UI */ if(unlikely(br->guestzone_enabled)) { //if (skb2->dev->support_guest_zone == 1) { if(unlikely(p->support_guest_zone)) { if(!cameo_check_guest_local(skb2, p)) { kfree_skb(skb2); goto out; } } } #endif return br_pass_frame_up(skb2); #ifdef CONFIG_WIRELESS_GUEST_ZONE } #endif 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) { const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_port *p = br_port_get_rcu(skb->dev); struct net_bridge *br; struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; struct sk_buff *skb2; bool unicast = true; 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; /* The packet skb2 goes to the local host (NULL to skip). */ skb2 = NULL; if (br->dev->flags & IFF_PROMISC) skb2 = skb; dst = NULL; 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; 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; 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; } if (skb) { if (dst) { dst->used = jiffies; br_forward(dst->dst, skb, skb2); } else br_flood_forward(br, skb, skb2, unicast); } if (skb2) return br_pass_frame_up(skb2); out: return 0; drop: kfree_skb(skb); goto out; }
/* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct sk_buff *skb) { const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_port *p = br_port_get_rcu(skb->dev); struct net_bridge *br; 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); if (!p || p->state == BR_STATE_DISABLED) goto drop; /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; br_fdb_update(br, p, eth_hdr(skb)->h_source); if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && br_multicast_rcv(br, p, skb)) goto drop; if ((p->state == BR_STATE_LEARNING) && skb->protocol != htons(ETH_P_PAE)) goto drop; 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; 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; 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); if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(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->dev->stats.multicast++; } else if ((pdst = __br_get(get_dst_hook, NULL, p, &skb))) { if (!skb) goto out; } else if ((p->flags & BR_ISOLATE_MODE) || ((dst = __br_fdb_get(br, dest)) && dst->is_local)) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; } if (skb) { if (dst) { dst->used = jiffies; pdst = dst->dst; } if (pdst) br_forward(pdst, skb, skb2); else br_flood_forward(br, skb, skb2); } if (skb2) return br_pass_frame_up(skb2); out: return 0; drop: kfree_skb(skb); goto out; }