diff -urN v2.4.20/linux/include/linux/if_bridge.h linux/include/linux/if_bridge.h --- v2.4.20/linux/include/linux/if_bridge.h Fri Jun 2 10:27:43 2000 +++ linux/include/linux/if_bridge.h Fri Nov 29 03:40:47 2002 @@ -38,6 +38,7 @@ #define BRCTL_SET_PORT_PRIORITY 16 #define BRCTL_SET_PATH_COST 17 #define BRCTL_GET_FDB_ENTRIES 18 +#define BRCTL_SET_BRIDGE_IP_MODE 19 #define BR_STATE_DISABLED 0 #define BR_STATE_LISTENING 1 @@ -66,6 +67,7 @@ __u32 tcn_timer_value; __u32 topology_change_timer_value; __u32 gc_timer_value; + __u32 ip_mode; }; struct __port_info @@ -102,7 +104,7 @@ struct net_bridge_port; extern int (*br_ioctl_hook)(unsigned long arg); -extern void (*br_handle_frame_hook)(struct sk_buff *skb); +extern int (*br_handle_frame_hook)(struct sk_buff *skb); #endif diff -urN v2.4.20/linux/net/bridge/br_input.c linux/net/bridge/br_input.c --- v2.4.20/linux/net/bridge/br_input.c Sat Aug 3 12:13:52 2002 +++ linux/net/bridge/br_input.c Fri Nov 29 03:40:47 2002 @@ -29,7 +29,8 @@ return 0; } -static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) +static void +br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb, int force_local) { struct net_device *indev; @@ -41,18 +42,53 @@ skb->pkt_type = PACKET_HOST; skb_push(skb, ETH_HLEN); skb->protocol = eth_type_trans(skb, &br->dev); + if (skb->pkt_type == PACKET_OTHERHOST && force_local) + skb->pkt_type = PACKET_HOST; NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, br_pass_frame_up_finish); } +#define frame_up(br, skb, clone, force_local) { \ + if (ip_mode) { \ + if (htons(ETH_P_ARP) == skb->mac.ethernet->h_proto) { \ + struct sk_buff *skb2; \ + \ + if ((skb2 = skb_clone(skb, GFP_ATOMIC))) \ + br_pass_frame_up(br, skb2, 1); \ + } \ + passedup = 1; \ + } else if (clone) { \ + struct sk_buff *skb2; \ + \ + skb2 = skb_clone(skb, GFP_ATOMIC); \ + if (skb2) { \ + passedup = 1; \ + br_pass_frame_up(br, skb2, \ + force_local); \ + } \ + } else { \ + br_pass_frame_up(br, skb, force_local); \ + } \ +} + +#define handle_frame(br, p, skb) \ + if (ip_mode && passedup) { \ + ret = 1; \ + if (PACKET_OTHERHOST != skb->pkt_type) \ + goto out; \ + memcpy(dest, p->dev->dev_addr, p->dev->addr_len); \ + skb->pkt_type = PACKET_HOST; \ + goto out; \ + } + static int br_handle_frame_finish(struct sk_buff *skb) { struct net_bridge *br; unsigned char *dest; struct net_bridge_fdb_entry *dst; struct net_bridge_port *p; - int passedup; + int passedup, ip_mode, ret; dest = skb->mac.ethernet->h_dest; @@ -66,33 +102,43 @@ goto err; passedup = 0; + ret = 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); - } + ip_mode = 0; + frame_up(br, skb, 1, 0); + } else { + ip_mode = (br->ip_mode != 0); } if (dest[0] & 1) { - br_flood_forward(br, skb, !passedup); + br_flood_forward(br, skb, !passedup || ip_mode); if (!passedup) - br_pass_frame_up(br, skb); + frame_up(br, skb, 0, 1); + handle_frame(br, p, 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); + if (dst != NULL && + (dst->is_local || + (ip_mode && dst->dst->dev != skb->dev && + htons(ETH_P_IP) == skb->mac.ethernet->h_proto))) { br_fdb_put(dst); + if (!passedup) { + frame_up(br, skb, 0, 1); + handle_frame(br, p, skb); + } else { + handle_frame(br, p, skb); + kfree_skb(skb); + } goto out; } + if (ip_mode && passedup) { + ip_mode = 0; + frame_up(br, skb, 1, 0); + } + if (dst != NULL) { br_forward(dst->dst, skb); br_fdb_put(dst); @@ -103,7 +149,7 @@ out: read_unlock(&br->lock); - return 0; + return ret; err: read_unlock(&br->lock); @@ -112,7 +158,7 @@ return 0; } -void br_handle_frame(struct sk_buff *skb) +int br_handle_frame(struct sk_buff *skb) { struct net_bridge *br; unsigned char *dest; @@ -146,25 +192,31 @@ 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); + int ret = 0; + + if (!br->ip_mode || br->dev.flags & IFF_PROMISC) + NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, + skb, skb->dev, NULL, br_handle_frame_finish); + else + ret = br_handle_frame_finish(skb); read_unlock(&br->lock); - return; + return ret; } err: read_unlock(&br->lock); err_nolock: kfree_skb(skb); - return; + return 0; handle_special_frame: if (!dest[5]) { br_stp_handle_bpdu(skb); read_unlock(&br->lock); - return; + return 0; } read_unlock(&br->lock); kfree_skb(skb); + return 0; } diff -urN v2.4.20/linux/net/bridge/br_ioctl.c linux/net/bridge/br_ioctl.c --- v2.4.20/linux/net/bridge/br_ioctl.c Mon Nov 20 22:52:50 2000 +++ linux/net/bridge/br_ioctl.c Fri Nov 29 03:40:47 2002 @@ -73,6 +73,7 @@ b.tcn_timer_value = br_timer_get_residue(&br->tcn_timer); b.topology_change_timer_value = br_timer_get_residue(&br->topology_change_timer); b.gc_timer_value = br_timer_get_residue(&br->gc_timer); + b.ip_mode = br->ip_mode; if (copy_to_user((void *)arg0, &b, sizeof(b))) return -EFAULT; @@ -179,6 +180,10 @@ case BRCTL_GET_FDB_ENTRIES: return br_fdb_get_entries(br, (void *)arg0, arg1, arg2); + + case BRCTL_SET_BRIDGE_IP_MODE: + br->ip_mode = arg0?1:0; + return 0; } return -EOPNOTSUPP; diff -urN v2.4.20/linux/net/bridge/br_private.h linux/net/bridge/br_private.h --- v2.4.20/linux/net/bridge/br_private.h Sun Mar 31 06:19:04 2002 +++ linux/net/bridge/br_private.h Fri Nov 29 03:40:47 2002 @@ -102,6 +102,7 @@ unsigned stp_enabled:1; unsigned topology_change:1; unsigned topology_change_detected:1; + unsigned ip_mode:1; struct br_timer hello_timer; struct br_timer tcn_timer; @@ -166,7 +167,7 @@ int *ifindices); /* br_input.c */ -extern void br_handle_frame(struct sk_buff *skb); +extern int br_handle_frame(struct sk_buff *skb); /* br_ioctl.c */ extern void br_call_ioctl_atomic(void (*fn)(void)); diff -urN v2.4.20/linux/net/core/dev.c linux/net/core/dev.c --- v2.4.20/linux/net/core/dev.c Fri Nov 29 03:00:18 2002 +++ linux/net/core/dev.c Fri Nov 29 03:56:53 2002 @@ -1380,7 +1380,7 @@ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) -void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; +int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; #endif static __inline__ int handle_bridge(struct sk_buff *skb, @@ -1397,7 +1397,6 @@ } } - br_handle_frame_hook(skb); return ret; } @@ -1457,7 +1456,12 @@ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) if (skb->dev->br_port != NULL && br_handle_frame_hook != NULL) { - return handle_bridge(skb, pt_prev); + int ret; + + ret = handle_bridge(skb, pt_prev); + if (br_handle_frame_hook(skb) == 0) + return ret; + pt_prev = NULL; } #endif