/* called under bridge lock */ static void br_root_selection(struct net_bridge *br) { struct net_bridge_port *p; int root_port; root_port = 0; p = br->port_list; while (p != NULL) { if (br_should_become_root_port(p, root_port)) root_port = p->port_no; p = p->next; } br->root_port = root_port; if (!root_port) { br->designated_root = br->bridge_id; br->root_path_cost = 0; } else { p = br_get_port(br, root_port); br->designated_root = p->designated_root; br->root_path_cost = p->designated_cost + p->path_cost; } }
/* called under bridge lock */ static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device *dev) { int i; struct net_bridge_port *p; p = kmalloc(sizeof(*p), GFP_KERNEL); if (p == NULL) return p; memset(p, 0, sizeof(*p)); p->br = br; p->dev = dev; p->path_cost = br_initial_port_cost(dev); p->priority = 0x80; dev->br_port = p; for (i=1;i<255;i++) if (br_get_port(br, i) == NULL) break; if (i == 255) { kfree(p); return NULL; } p->port_no = i; br_init_port(p); p->state = BR_STATE_DISABLED; p->next = br->port_list; br->port_list = p; return p; }
/* called under bridge lock */ static int br_should_become_root_port(struct net_bridge_port *p, int root_port) { struct net_bridge *br; struct net_bridge_port *rp; int t; br = p->br; if (p->state == BR_STATE_DISABLED || br_is_designated_port(p)) return 0; if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0) return 0; if (!root_port) return 1; rp = br_get_port(br, root_port); t = memcmp(&p->designated_root, &rp->designated_root, 8); if (t < 0) return 1; else if (t > 0) return 0; if (p->designated_cost + p->path_cost < rp->designated_cost + rp->path_cost) return 1; else if (p->designated_cost + p->path_cost > rp->designated_cost + rp->path_cost) return 0; t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8); if (t < 0) return 1; else if (t > 0) return 0; if (p->designated_port < rp->designated_port) return 1; else if (p->designated_port > rp->designated_port) return 0; if (p->port_id < rp->port_id) return 1; return 0; }
/* called under bridge lock */ void br_transmit_config(struct net_bridge_port *p) { struct br_config_bpdu bpdu; struct net_bridge *br; if (br_timer_is_running(&p->hold_timer)) { p->config_pending = 1; return; } br = p->br; bpdu.topology_change = br->topology_change; bpdu.topology_change_ack = p->topology_change_ack; bpdu.root = br->designated_root; bpdu.root_path_cost = br->root_path_cost; bpdu.bridge_id = br->bridge_id; bpdu.port_id = p->port_id; if (br_is_root_bridge(br)) bpdu.message_age = 0; else { struct net_bridge_port *root; root = br_get_port(br, br->root_port); bpdu.message_age = br_timer_get_residue(&root->message_age_timer) + MESSAGE_AGE_INCR; } bpdu.max_age = br->max_age; bpdu.hello_time = br->hello_time; bpdu.forward_delay = br->forward_delay; if (bpdu.message_age < br->max_age) { br_send_config_bpdu(p, &bpdu); p->topology_change_ack = 0; p->config_pending = 0; br_timer_set(&p->hold_timer, jiffies); } }
static int br_ioctl_device(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2) { if (br == NULL) return -EINVAL; switch (cmd) { case BRCTL_ADD_IF: case BRCTL_DEL_IF: { struct net_device *dev; int ret; dev = dev_get_by_index(arg0); if (dev == NULL) return -EINVAL; if (cmd == BRCTL_ADD_IF) ret = br_add_if(br, dev); else ret = br_del_if(br, dev); dev_put(dev); return ret; } case BRCTL_GET_BRIDGE_INFO: { struct __bridge_info b; memset(&b, 0, sizeof(struct __bridge_info)); memcpy(&b.designated_root, &br->designated_root, 8); memcpy(&b.bridge_id, &br->bridge_id, 8); b.root_path_cost = br->root_path_cost; b.max_age = br->max_age; b.hello_time = br->hello_time; b.forward_delay = br->forward_delay; b.bridge_max_age = br->bridge_max_age; b.bridge_hello_time = br->bridge_hello_time; b.bridge_forward_delay = br->bridge_forward_delay; b.topology_change = br->topology_change; b.topology_change_detected = br->topology_change_detected; b.root_port = br->root_port; b.stp_enabled = br->stp_enabled; b.ageing_time = br->ageing_time; b.gc_interval = br->gc_interval; b.hello_timer_value = br_timer_get_residue(&br->hello_timer); 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); if (copy_to_user((void *)arg0, &b, sizeof(b))) return -EFAULT; return 0; } case BRCTL_GET_PORT_LIST: { int i; int indices[256]; for (i=0;i<256;i++) indices[i] = 0; br_get_port_ifindices(br, indices); if (copy_to_user((void *)arg0, indices, 256*sizeof(int))) return -EFAULT; return 0; } case BRCTL_SET_BRIDGE_FORWARD_DELAY: br->bridge_forward_delay = arg0; if (br_is_root_bridge(br)) br->forward_delay = arg0; return 0; case BRCTL_SET_BRIDGE_HELLO_TIME: br->bridge_hello_time = arg0; if (br_is_root_bridge(br)) br->hello_time = arg0; return 0; case BRCTL_SET_BRIDGE_MAX_AGE: br->bridge_max_age = arg0; if (br_is_root_bridge(br)) br->max_age = arg0; return 0; case BRCTL_SET_AGEING_TIME: br->ageing_time = arg0; return 0; case BRCTL_SET_GC_INTERVAL: br->gc_interval = arg0; return 0; case BRCTL_GET_PORT_INFO: { struct __port_info p; struct net_bridge_port *pt; if ((pt = br_get_port(br, arg1)) == NULL) return -EINVAL; memset(&p, 0, sizeof(struct __port_info)); memcpy(&p.designated_root, &pt->designated_root, 8); memcpy(&p.designated_bridge, &pt->designated_bridge, 8); p.port_id = pt->port_id; p.designated_port = pt->designated_port; p.path_cost = pt->path_cost; p.designated_cost = pt->designated_cost; p.state = pt->state; p.top_change_ack = pt->topology_change_ack; p.config_pending = pt->config_pending; p.message_age_timer_value = br_timer_get_residue(&pt->message_age_timer); p.forward_delay_timer_value = br_timer_get_residue(&pt->forward_delay_timer); p.hold_timer_value = br_timer_get_residue(&pt->hold_timer); if (copy_to_user((void *)arg0, &p, sizeof(p))) return -EFAULT; return 0; } case BRCTL_SET_BRIDGE_STP_STATE: br->stp_enabled = arg0?1:0; return 0; case BRCTL_SET_BRIDGE_PRIORITY: br_stp_set_bridge_priority(br, arg0); return 0; case BRCTL_SET_PORT_PRIORITY: { struct net_bridge_port *p; if ((p = br_get_port(br, arg0)) == NULL) return -EINVAL; br_stp_set_port_priority(p, arg1); return 0; } case BRCTL_SET_PATH_COST: { struct net_bridge_port *p; if ((p = br_get_port(br, arg0)) == NULL) return -EINVAL; br_stp_set_path_cost(p, arg1); return 0; } case BRCTL_GET_FDB_ENTRIES: #ifdef CONFIG_RTK_GUEST_ZONE return br_fdb_get_entries(br, (void *)arg0, arg1, arg2, 0); #else return br_fdb_get_entries(br, (void *)arg0, arg1, arg2); #endif #ifdef MULTICAST_FILTER case 101: printk(KERN_INFO "%s: clear port list of multicast filter\n", br->dev.name); br->fltr_portlist_num = 0; return 0; case 102: { int i; if (br->fltr_portlist_num == MLCST_FLTR_ENTRY) { printk(KERN_INFO "%s: set port num of multicast filter, entries full!\n", br->dev.name); return 0; } for (i=0; i<br->fltr_portlist_num; i++) if (br->fltr_portlist[i] == (unsigned short)arg0) return 0; printk(KERN_INFO "%s: set port num [%d] of multicast filter\n", br->dev.name, (unsigned short)arg0); br->fltr_portlist[br->fltr_portlist_num] = (unsigned short)arg0; br->fltr_portlist_num++; return 0; } #endif #ifdef MULTICAST_BWCTRL case 103: { struct net_bridge_port *p; if ((p = br_get_port(br, arg0)) == NULL) return -EINVAL; if (arg1 == 0) { p->bandwidth = 0; printk(KERN_INFO "%s: port %i(%s) multicast bandwidth all\n", p->br->dev.name, p->port_no, p->dev->name); } else { p->bandwidth = arg1 * 1000 / 8; printk(KERN_INFO "%s: port %i(%s) multicast bandwidth %dkbps\n", p->br->dev.name, p->port_no, p->dev->name, (unsigned int)arg1); } return 0; } #endif #ifdef RTL_BRIDGE_MAC_CLONE case 104: // MAC Clone enable/disable { struct net_bridge_port *p; unsigned char nullmac[] = {0, 0, 0, 0, 0, 0}; if ((p = br_get_port(br, arg0)) == NULL) return -EINVAL; if ((p->macCloneTargetPort = br_get_port(br, arg1)) == NULL) return -EINVAL; p->enable_mac_clone = 1; p->mac_clone_completed = 0; if (clone_pair.port != p->macCloneTargetPort) { TRACE("clone_pair.port [%x] != p->macCloneTargetPort [%x], don't clone\n", (unsigned int)clone_pair.port, (unsigned int)p->macCloneTargetPort); clone_pair.port = p->macCloneTargetPort; TRACE("clone_pair.port = %x\n", (unsigned int)clone_pair.port); memset(clone_pair.mac.addr, 0, ETH_ALEN); } else { if(!memcmp(clone_pair.mac.addr, nullmac, ETH_ALEN)) { TRACE("clone_pair.mac.addr == nullmac, don't clone\n"); } else { TRACE("Clone MAC from previous one\n"); br_mac_clone(p->macCloneTargetPort, clone_pair.mac.addr); } } TRACE("device %s, Enable MAC Clone to device %s\n", p->dev->name, p->macCloneTargetPort->dev->name); return 0; } #endif #ifdef CONFIG_RTK_GUEST_ZONE case 105: // set zone { struct net_bridge_port *p; if ((p = br_get_port(br, arg0)) == NULL) return -EINVAL; p->is_guest_zone = arg1; #ifdef DEBUG_GUEST_ZONE panic_printk("set device=%s is_guest_zone=%d\n", p->dev->name, p->is_guest_zone); #endif return 0; } case 106: // set zone isolation br->is_zone_isolated = arg0; #ifdef DEBUG_GUEST_ZONE panic_printk("set zone isolation=%d\n", br->is_zone_isolated); #endif return 0; case 107: // set guest isolation br->is_guest_isolated = arg0; #ifdef DEBUG_GUEST_ZONE panic_printk("set guest isolation=%d\n", br->is_guest_isolated); #endif return 0; case 108: // set lock mac list { unsigned char mac[6]; int i; if (copy_from_user(mac, (unsigned long*)arg0, 6)) return -EFAULT; #ifdef DEBUG_GUEST_ZONE panic_printk("set lock client list=%02x:%02x:%02x:%02x:%02x:%02x\n", mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); #endif if (!memcmp(mac, "\x0\x0\x0\x0\x0\x0", 6)) { // reset list #ifdef DEBUG_GUEST_ZONE panic_printk("reset lock list!\n"); #endif br->lock_client_num = 0; return 0; } for (i=0; i<br->lock_client_num; i++) { if (!memcmp(mac, br->lock_client_list[i], 6)) { #ifdef DEBUG_GUEST_ZONE panic_printk("duplicated lock entry!\n"); #endif return 0; } } if (br->lock_client_num >= MAX_LOCK_CLIENT) { #ifdef DEBUG_GUEST_ZONE panic_printk("Add failed, lock list table full!\n"); #endif return 0; } memcpy(br->lock_client_list[br->lock_client_num], mac, 6); br->lock_client_num++; return 0; } case 109: // show guest info { int i; panic_printk("\n"); panic_printk(" zone isolation: %d\n", br->is_zone_isolated); panic_printk(" guest isolation: %d\n", br->is_guest_isolated); i = 1; while (1) { struct net_bridge_port *p; if ((p = br_get_port(br, i++)) == NULL) break; panic_printk(" %s: %s\n", p->dev->name, (p->is_guest_zone ? "guest" : "host")); } panic_printk(" locked client no: %d\n", br->lock_client_num); for (i=0; i< br->lock_client_num; i++) { unsigned char *mac; mac = br->lock_client_list[i]; panic_printk(" mac=%02x:%02x:%02x:%02x:%02x:%02x\n", mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); } panic_printk("\n"); return 0; } case 110: return br_fdb_get_entries(br, (void *)arg0, arg1, arg2, 1); #endif // CONFIG_RTK_GUEST_ZONE } return -EOPNOTSUPP; }
/* called under bridge lock */ void br_transmit_tcn(struct net_bridge *br) { br_send_tcn_bpdu(br_get_port(br, br->root_port)); }
static int br_ioctl_device(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2) { if (br == NULL) return -EINVAL; switch (cmd) { case BRCTL_ADD_IF: case BRCTL_DEL_IF: { struct net_device *dev; int ret; dev = dev_get_by_index(arg0); if (dev == NULL) return -EINVAL; if (cmd == BRCTL_ADD_IF) ret = br_add_if(br, dev); else ret = br_del_if(br, dev); dev_put(dev); return ret; } case BRCTL_GET_BRIDGE_INFO: { struct __bridge_info b; memset(&b, 0, sizeof(struct __bridge_info)); memcpy(&b.designated_root, &br->designated_root, 8); memcpy(&b.bridge_id, &br->bridge_id, 8); b.root_path_cost = br->root_path_cost; b.max_age = br->max_age; b.hello_time = br->hello_time; b.forward_delay = br->forward_delay; b.bridge_max_age = br->bridge_max_age; b.bridge_hello_time = br->bridge_hello_time; b.bridge_forward_delay = br->bridge_forward_delay; b.topology_change = br->topology_change; b.topology_change_detected = br->topology_change_detected; b.root_port = br->root_port; b.stp_enabled = br->stp_enabled; b.ageing_time = br->ageing_time; b.gc_interval = br->gc_interval; b.hello_timer_value = br_timer_get_residue(&br->hello_timer); 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); if (copy_to_user((void *)arg0, &b, sizeof(b))) return -EFAULT; return 0; } case BRCTL_GET_PORT_LIST: { int i; int indices[256]; for (i=0;i<256;i++) indices[i] = 0; br_get_port_ifindices(br, indices); if (copy_to_user((void *)arg0, indices, 256*sizeof(int))) return -EFAULT; return 0; } case BRCTL_SET_BRIDGE_FORWARD_DELAY: br->bridge_forward_delay = arg0; if (br_is_root_bridge(br)) br->forward_delay = arg0; return 0; case BRCTL_SET_BRIDGE_HELLO_TIME: br->bridge_hello_time = arg0; if (br_is_root_bridge(br)) br->hello_time = arg0; return 0; case BRCTL_SET_BRIDGE_MAX_AGE: br->bridge_max_age = arg0; if (br_is_root_bridge(br)) br->max_age = arg0; return 0; case BRCTL_SET_AGEING_TIME: br->ageing_time = arg0; return 0; case BRCTL_SET_GC_INTERVAL: br->gc_interval = arg0; return 0; case BRCTL_GET_PORT_INFO: { struct __port_info p; struct net_bridge_port *pt; if ((pt = br_get_port(br, arg1)) == NULL) return -EINVAL; memset(&p, 0, sizeof(struct __port_info)); memcpy(&p.designated_root, &pt->designated_root, 8); memcpy(&p.designated_bridge, &pt->designated_bridge, 8); p.port_id = pt->port_id; p.designated_port = pt->designated_port; p.path_cost = pt->path_cost; p.designated_cost = pt->designated_cost; p.state = pt->state; p.top_change_ack = pt->topology_change_ack; p.config_pending = pt->config_pending; p.message_age_timer_value = br_timer_get_residue(&pt->message_age_timer); p.forward_delay_timer_value = br_timer_get_residue(&pt->forward_delay_timer); p.hold_timer_value = br_timer_get_residue(&pt->hold_timer); if (copy_to_user((void *)arg0, &p, sizeof(p))) return -EFAULT; return 0; } case BRCTL_SET_BRIDGE_STP_STATE: #ifdef CONFIG_BRIDGESTEP br->stp_enabled = arg0?1:0; return 0; #else br->stp_enabled = 0; return 0; #endif case BRCTL_SET_BRIDGE_PRIORITY: br_stp_set_bridge_priority(br, arg0); return 0; case BRCTL_SET_PORT_PRIORITY: { struct net_bridge_port *p; if ((p = br_get_port(br, arg0)) == NULL) return -EINVAL; br_stp_set_port_priority(p, arg1); return 0; } case BRCTL_SET_PATH_COST: { struct net_bridge_port *p; if ((p = br_get_port(br, arg0)) == NULL) return -EINVAL; br_stp_set_path_cost(p, arg1); return 0; } case BRCTL_GET_FDB_ENTRIES: return br_fdb_get_entries(br, (void *)arg0, arg1, arg2); #ifdef CONFIG_EMUSWITCH_MODULE #define EMUSWITCH_DEBUG(X) printk("EMUSWITCH: " X) case BRCTL_SET_PORT_EMUSWITCH: { printk("Emuswitch set port\n"); struct net_bridge_port *p; struct __emuswitch_info info; if ((p = br_get_port(br, arg0)) == NULL) { printk("Emuswitch Could not get port %d\n", arg0); return -EINVAL; } if(copy_from_user(&info, (void*)arg1, sizeof(info))) { EMUSWITCH_DEBUG("Could not copy in info\n"); return -EFAULT; } return br_port_set_emuswitch(p, &info); } case BRCTL_GET_PORT_EMUSWITCH: { printk("Emuswitch get port\n"); struct net_bridge_port *p; struct __emuswitch_info info; if(copy_from_user(&info, (void*)arg1, sizeof(info))) { EMUSWITCH_DEBUG("Could not copy in info\n"); return -EFAULT; } if ((p = br_get_port(br, arg0)) == NULL) { printk("EMUSWITCH: Could not get port %d\n", arg0); return -EINVAL; } br_port_get_emuswitch(p, &info); if(copy_to_user((void*)arg1, &info, sizeof(info))) { EMUSWITCH_DEBUG("Could not copy out info\n"); return -EFAULT; } return 0; } #undef EMUSWITCH_DEBUG #endif // CONFIG_EMUSWITCH_MODULE } return -EOPNOTSUPP; }