diff -ur v2.6.0-test1/linux/include/linux/if_bridge.h linux/include/linux/if_bridge.h --- v2.6.0-test1/linux/include/linux/if_bridge.h Sat Jul 26 16:38:22 2003 +++ linux/include/linux/if_bridge.h Sun Jul 27 16:09:05 2003 @@ -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 diff -ur v2.6.0-test1/linux/net/bridge/br_input.c linux/net/bridge/br_input.c --- v2.6.0-test1/linux/net/bridge/br_input.c Sat Jul 26 16:38:39 2003 +++ linux/net/bridge/br_input.c Sun Jul 27 17:29:21 2003 @@ -32,7 +32,7 @@ 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 +41,53 @@ indev = skb->dev; skb->dev = 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; \ + } + 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 = 0; dest = skb->mac.ethernet->h_dest; @@ -68,32 +103,41 @@ br = p->br; passedup = 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); @@ -104,7 +148,7 @@ out: rcu_read_unlock(); - return 0; + return ret; } int br_handle_frame(struct sk_buff *skb) @@ -138,6 +182,8 @@ } else if (p->state == BR_STATE_FORWARDING) { + int ret = 0; + if (br_should_route_hook && br_should_route_hook(&skb)) { rcu_read_unlock(); return -1; @@ -146,10 +192,13 @@ 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); + if (!p->br->ip_mode || p->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); rcu_read_unlock(); - return 0; + return ret; } err: diff -ur v2.6.0-test1/linux/net/bridge/br_ioctl.c linux/net/bridge/br_ioctl.c --- v2.6.0-test1/linux/net/bridge/br_ioctl.c Sat Jul 26 16:38:39 2003 +++ linux/net/bridge/br_ioctl.c Sun Jul 27 16:22:53 2003 @@ -94,6 +94,7 @@ b.tcn_timer_value = timer_residue(&br->tcn_timer); b.topology_change_timer_value = timer_residue(&br->topology_change_timer); b.gc_timer_value = timer_residue(&br->gc_timer); + b.ip_mode = br->ip_mode; rcu_read_unlock(); if (copy_to_user((void *)arg0, &b, sizeof(b))) @@ -224,6 +225,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 -ur v2.6.0-test1/linux/net/bridge/br_private.h linux/net/bridge/br_private.h --- v2.6.0-test1/linux/net/bridge/br_private.h Sat Jul 26 16:38:39 2003 +++ linux/net/bridge/br_private.h Sun Jul 27 16:23:44 2003 @@ -102,6 +102,7 @@ unsigned topology_change:1; unsigned topology_change_detected:1; + unsigned ip_mode:1; struct timer_list hello_timer; struct timer_list tcn_timer; struct timer_list topology_change_timer;