Ejemplo n.º 1
0
/*
 * Dump information about all ports, in response to GETLINK
 */
static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct net *net = sock_net(skb->sk);
	struct net_device *dev;
	int idx;

	idx = 0;
	for_each_netdev(net, dev) {
		/* not a bridge port */
		if (!br_port_exists(dev) || idx < cb->args[0])
			goto skip;

		if (br_fill_ifinfo(skb, br_port_get(dev),
				   NETLINK_CB(cb->skb).pid,
				   cb->nlh->nlmsg_seq, RTM_NEWLINK,
				   NLM_F_MULTI) < 0)
			break;
skip:
		++idx;
	}

	cb->args[0] = idx;

	return skb->len;
}
Ejemplo n.º 2
0
static size_t br_get_link_af_size(const struct net_device *dev)
{
	struct net_port_vlans *pv;

	if (br_port_exists(dev))
		pv = nbp_get_vlan_info(br_port_get_rcu(dev));
	else if (dev->priv_flags & IFF_EBRIDGE)
		pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
	else
		return 0;

	if (!pv)
		return 0;

	/* Each VLAN is returned in bridge_vlan_info along with flags */
	return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
}
Ejemplo n.º 3
0
/*
 * Change state of port (ie from forwarding to blocking etc)
 * Used by spanning tree in user space.
 */
static int br_rtm_setlink(struct sk_buff *skb,  struct nlmsghdr *nlh, void *arg)
{
	struct net *net = sock_net(skb->sk);
	struct ifinfomsg *ifm;
	struct nlattr *protinfo;
	struct net_device *dev;
	struct net_bridge_port *p;
	u8 new_state;

	if (nlmsg_len(nlh) < sizeof(*ifm))
		return -EINVAL;

	ifm = nlmsg_data(nlh);
	if (ifm->ifi_family != AF_BRIDGE)
		return -EPFNOSUPPORT;

	protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
	if (!protinfo || nla_len(protinfo) < sizeof(u8))
		return -EINVAL;

	new_state = nla_get_u8(protinfo);
	if (new_state > BR_STATE_BLOCKING)
		return -EINVAL;

	dev = __dev_get_by_index(net, ifm->ifi_index);
	if (!dev)
		return -ENODEV;

	if (!br_port_exists(dev))
		return -EINVAL;
	p = br_port_get(dev);

	/* if kernel STP is running, don't allow changes */
	if (p->br->stp_enabled == BR_KERNEL_STP)
		return -EBUSY;

	if (!netif_running(dev) ||
	    (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED))
		return -ENETDOWN;

	p->state = new_state;
	br_log_state(p);
	return 0;
}
Ejemplo n.º 4
0
static void ebt_ulog_packet(struct net *net, unsigned int hooknr,
			    const struct sk_buff *skb,
			    const struct net_device *in,
			    const struct net_device *out,
			    const struct ebt_ulog_info *uloginfo,
			    const char *prefix)
{
	ebt_ulog_packet_msg_t *pm;
	size_t size, copy_len;
	struct nlmsghdr *nlh;
	struct ebt_ulog_net *ebt = ebt_ulog_pernet(net);
	unsigned int group = uloginfo->nlgroup;
	ebt_ulog_buff_t *ub = &ebt->ulog_buffers[group];
	spinlock_t *lock = &ub->lock;
	ktime_t kt;

	if ((uloginfo->cprange == 0) ||
	    (uloginfo->cprange > skb->len + ETH_HLEN))
		copy_len = skb->len + ETH_HLEN;
	else
		copy_len = uloginfo->cprange;

	size = nlmsg_total_size(sizeof(*pm) + copy_len);
	if (size > nlbufsiz) {
		pr_debug("Size %Zd needed, but nlbufsiz=%d\n", size, nlbufsiz);
		return;
	}

	spin_lock_bh(lock);

	if (!ub->skb) {
		if (!(ub->skb = ulog_alloc_skb(size)))
			goto unlock;
	} else if (size > skb_tailroom(ub->skb)) {
		ulog_send(ebt, group);

		if (!(ub->skb = ulog_alloc_skb(size)))
			goto unlock;
	}

	nlh = nlmsg_put(ub->skb, 0, ub->qlen, 0,
			size - NLMSG_ALIGN(sizeof(*nlh)), 0);
	if (!nlh) {
		kfree_skb(ub->skb);
		ub->skb = NULL;
		goto unlock;
	}
	ub->qlen++;

	pm = nlmsg_data(nlh);
	memset(pm, 0, sizeof(*pm));

	/* Fill in the ulog data */
	pm->version = EBT_ULOG_VERSION;
	kt = ktime_get_real();
	pm->stamp = ktime_to_timeval(kt);
	if (ub->qlen == 1)
		ub->skb->tstamp = kt;
	pm->data_len = copy_len;
	pm->mark = skb->mark;
	pm->hook = hooknr;
	if (uloginfo->prefix != NULL)
		strcpy(pm->prefix, uloginfo->prefix);

	if (in) {
		strcpy(pm->physindev, in->name);
		/* If in isn't a bridge, then physindev==indev */
		if (br_port_exists(in))
			/* rcu_read_lock()ed by nf_hook_slow */
			strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name);
		else
			strcpy(pm->indev, in->name);
	}

	if (out) {
		/* If out exists, then out is a bridge port */
		strcpy(pm->physoutdev, out->name);
		/* rcu_read_lock()ed by nf_hook_slow */
		strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name);
	}

	if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)
		BUG();

	if (ub->qlen > 1)
		ub->lastnlh->nlmsg_flags |= NLM_F_MULTI;

	ub->lastnlh = nlh;

	if (ub->qlen >= uloginfo->qthreshold)
		ulog_send(ebt, group);
	else if (!timer_pending(&ub->timer)) {
		ub->timer.expires = jiffies + flushtimeout * HZ / 100;
		add_timer(&ub->timer);
	}

unlock:
	spin_unlock_bh(lock);
}
/*
 * Handle changes in state of network devices enslaved to a bridge.
 *
 * Note: don't care about up/down if bridge itself is down, because
 *     port state is checked when bridge is brought up.
 */
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
	struct net_device *dev = ptr;
	struct net_bridge_port *p = br_port_get(dev);
	struct net_bridge *br;
	bool changed_addr;
	int err;

	/* not a port of a bridge */
	if (!br_port_exists(dev))
		return NOTIFY_DONE;

	p = br_port_get(dev);
	br = p->br;

	switch (event) {
	case NETDEV_CHANGEMTU:
		dev_set_mtu(br->dev, br_min_mtu(br));
		break;

	case NETDEV_CHANGEADDR:
		spin_lock_bh(&br->lock);
		br_fdb_changeaddr(p, dev->dev_addr);
		changed_addr = br_stp_recalculate_bridge_id(br);
		spin_unlock_bh(&br->lock);

		if (changed_addr)
			call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
		break;

	case NETDEV_CHANGE:
		br_port_carrier_check(p);
		break;

	case NETDEV_FEAT_CHANGE:
		spin_lock_bh(&br->lock);
		if (netif_running(br->dev))
			br_features_recompute(br);
		spin_unlock_bh(&br->lock);
		break;

	case NETDEV_DOWN:
		spin_lock_bh(&br->lock);
		if (br->dev->flags & IFF_UP)
			br_stp_disable_port(p);
		spin_unlock_bh(&br->lock);
		break;

	case NETDEV_UP:
		if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) {
			spin_lock_bh(&br->lock);
			br_stp_enable_port(p);
			spin_unlock_bh(&br->lock);
		}
		break;

	case NETDEV_UNREGISTER:
		br_del_if(br, dev);
		break;

	case NETDEV_CHANGENAME:
		err = br_sysfs_renameif(p);
		if (err)
			return notifier_from_errno(err);
		break;

	case NETDEV_PRE_TYPE_CHANGE:
		/* Forbid underlaying device to change its type. */
		return NOTIFY_BAD;
	}

	/* Events that may cause spanning tree to refresh */
	if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
	    event == NETDEV_CHANGE || event == NETDEV_DOWN)
		br_ifinfo_notify(RTM_NEWLINK, p);

	return NOTIFY_DONE;
}