/* called under bridge lock */ static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr) { unsigned char oldaddr[6]; struct net_bridge_port *p; int wasroot; wasroot = br_is_root_bridge(br); memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN); memcpy(br->bridge_id.addr, addr, ETH_ALEN); memcpy(br->dev.dev_addr, addr, ETH_ALEN); p = br->port_list; while (p != NULL) { if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN)) memcpy(p->designated_bridge.addr, addr, ETH_ALEN); if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN)) memcpy(p->designated_root.addr, addr, ETH_ALEN); p = p->next; } br_configuration_update(br); br_port_state_selection(br); if (br_is_root_bridge(br) && !wasroot) br_become_root_bridge(br); }
static void set_hello_time(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); br->hello_time = t; if (br_is_root_bridge(br)) br->bridge_hello_time = t; }
static void set_forward_delay(struct net_bridge *br, unsigned long val) { unsigned long delay = clock_t_to_jiffies(val); br->forward_delay = delay; if (br_is_root_bridge(br)) br->bridge_forward_delay = delay; }
static int set_max_age(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); br->max_age = t; if (br_is_root_bridge(br)) br->bridge_max_age = t; return 0; }
static int set_hello_time(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); if (t < HZ) return -EINVAL; br->hello_time = t; if (br_is_root_bridge(br)) br->bridge_hello_time = t; return 0; }
/* lock-safe */ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) { struct net_bridge *br; int was_root; if (p->state == BR_STATE_DISABLED) return; br = p->br; read_lock(&br->lock); was_root = br_is_root_bridge(br); if (br_supersedes_port_info(p, bpdu)) { br_record_config_information(p, bpdu); br_configuration_update(br); br_port_state_selection(br); if (!br_is_root_bridge(br) && was_root) { br_timer_clear(&br->hello_timer); if (br->topology_change_detected) { br_timer_clear(&br->topology_change_timer); br_transmit_tcn(br); br_timer_set(&br->tcn_timer, jiffies); } } if (p->port_no == br->root_port) { br_record_config_timeout_values(br, bpdu); br_config_bpdu_generation(br); if (bpdu->topology_change_ack) br_topology_change_acknowledged(br); } } else if (br_is_designated_port(p)) { br_reply(p); } read_unlock(&br->lock); }
/* called under bridge lock */ void br_stp_disable_port(struct net_bridge_port *p) { struct net_bridge *br; int wasroot; br = p->br; printk(KERN_INFO "%s: port %i(%s) entering %s state\n", br->dev.name, p->port_no, p->dev->name, "disabled"); wasroot = br_is_root_bridge(br); br_become_designated_port(p); p->state = BR_STATE_DISABLED; p->topology_change_ack = 0; p->config_pending = 0; br_timer_clear(&p->message_age_timer); br_timer_clear(&p->forward_delay_timer); br_timer_clear(&p->hold_timer); br_configuration_update(br); br_port_state_selection(br); if (br_is_root_bridge(br) && !wasroot) br_become_root_bridge(br); }
/* called under bridge lock */ static void br_message_age_timer_expired(struct net_bridge_port *p) { struct net_bridge *br; int was_root; br = p->br; printk(KERN_INFO "%s: ", br->dev.name); printk("neighbour "); dump_bridge_id(&p->designated_bridge); printk(" lost on port %i(%s)\n", p->port_no, p->dev->name); /* * According to the spec, the message age timer cannot be * running when we are the root bridge. So.. this was_root * check is redundant. I'm leaving it in for now, though. */ was_root = br_is_root_bridge(br); br_become_designated_port(p); br_configuration_update(br); br_port_state_selection(br); if (br_is_root_bridge(br) && !was_root) br_become_root_bridge(br); }
/* called under bridge lock */ void br_stp_set_bridge_priority(struct net_bridge *br, int newprio) { struct net_bridge_port *p; int wasroot; wasroot = br_is_root_bridge(br); p = br->port_list; while (p != NULL) { if (p->state != BR_STATE_DISABLED && br_is_designated_port(p)) { p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF; p->designated_bridge.prio[1] = newprio & 0xFF; } p = p->next; }
/* called under bridge lock */ void br_topology_change_detection(struct net_bridge *br) { // printk(KERN_INFO "%s: topology change detected", br->dev.name); if (br_is_root_bridge(br)) { // printk(", propagating"); br->topology_change = 1; br_timer_set(&br->topology_change_timer, jiffies); } else if (!br->topology_change_detected) { // printk(", sending tcn bpdu"); br_transmit_tcn(br); br_timer_set(&br->tcn_timer, jiffies); } // printk("\n"); br->topology_change_detected = 1; }
/* 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; }
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; }