Example #1
0
/* 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);
}
Example #2
0
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;
}
Example #3
0
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;
}
Example #4
0
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;
}
Example #5
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;
}
Example #6
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);
}
Example #7
0
/* 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);
}
Example #8
0
/* 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);
}
Example #9
0
/* 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;
	}
Example #10
0
/* 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;
}
Example #11
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);
	}
}
Example #12
0
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;
}
Example #13
0
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;
}