Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 300aaeea authored by YOSHIFUJI Hideaki's avatar YOSHIFUJI Hideaki
Browse files

[IPV6] SIT: Add SIOCGETPRL ioctl to get/dump PRL.

parent 0009ae1f
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#define SIOCADDTUNNEL   (SIOCDEVPRIVATE + 1)
#define SIOCDELTUNNEL   (SIOCDEVPRIVATE + 2)
#define SIOCCHGTUNNEL   (SIOCDEVPRIVATE + 3)
#define SIOCGETPRL      (SIOCDEVPRIVATE + 4)
#define SIOCADDPRL      (SIOCDEVPRIVATE + 5)
#define SIOCDELPRL      (SIOCDEVPRIVATE + 6)
#define SIOCCHGPRL      (SIOCDEVPRIVATE + 7)
@@ -38,6 +39,9 @@ struct ip_tunnel_prl {
	__be32			addr;
	__u16			flags;
	__u16			__reserved;
	__u32			datalen;
	__u32			__reserved2;
	void __user		*data;
};

/* PRL flags */
+4 −1
Original line number Diff line number Diff line
@@ -24,13 +24,16 @@ struct ip_tunnel
	int			mlink;

	struct ip_tunnel_parm	parms;

	struct ip_tunnel_prl_entry	*prl;		/* potential router list */
	unsigned int			prl_count;	/* # of entries in PRL */
};

struct ip_tunnel_prl_entry
{
	struct ip_tunnel_prl_entry	*next;
	struct ip_tunnel_prl		entry;
	__be32				addr;
	u16				flags;
};

#define IPTUNNEL_XMIT() do {						\
+87 −9
Original line number Diff line number Diff line
@@ -203,12 +203,73 @@ __ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr)
	struct ip_tunnel_prl_entry *p = (struct ip_tunnel_prl_entry *)NULL;

	for (p = t->prl; p; p = p->next)
		if (p->entry.addr == addr)
		if (p->addr == addr)
			break;
	return p;

}

static int ipip6_tunnel_get_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
{
	struct ip_tunnel_prl *kp;
	struct ip_tunnel_prl_entry *prl;
	unsigned int cmax, c = 0, ca, len;
	int ret = 0;

	cmax = a->datalen / sizeof(*a);
	if (cmax > 1 && a->addr != htonl(INADDR_ANY))
		cmax = 1;

	/* For simple GET or for root users,
	 * we try harder to allocate.
	 */
	kp = (cmax <= 1 || capable(CAP_NET_ADMIN)) ?
		kcalloc(cmax, sizeof(*kp), GFP_KERNEL) :
		NULL;

	read_lock(&ipip6_lock);

	ca = t->prl_count < cmax ? t->prl_count : cmax;

	if (!kp) {
		/* We don't try hard to allocate much memory for
		 * non-root users.
		 * For root users, retry allocating enough memory for
		 * the answer.
		 */
		kp = kcalloc(ca, sizeof(*kp), GFP_ATOMIC);
		if (!kp) {
			ret = -ENOMEM;
			goto out;
		}
	}

	c = 0;
	for (prl = t->prl; prl; prl = prl->next) {
		if (c > cmax)
			break;
		if (a->addr != htonl(INADDR_ANY) && prl->addr != a->addr)
			continue;
		kp[c].addr = prl->addr;
		kp[c].flags = prl->flags;
		c++;
		if (a->addr != htonl(INADDR_ANY))
			break;
	}
out:
	read_unlock(&ipip6_lock);

	len = sizeof(*kp) * c;
	ret = len ? copy_to_user(a->data, kp, len) : 0;

	kfree(kp);
	if (ret)
		return -EFAULT;

	a->datalen = len;
	return 0;
}

static int
ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
{
@@ -221,7 +282,7 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
	write_lock(&ipip6_lock);

	for (p = t->prl; p; p = p->next) {
		if (p->entry.addr == a->addr) {
		if (p->addr == a->addr) {
			if (chg)
				goto update;
			err = -EEXIST;
@@ -242,8 +303,10 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)

	p->next = t->prl;
	t->prl = p;
	t->prl_count++;
update:
	p->entry = *a;
	p->addr = a->addr;
	p->flags = a->flags;
out:
	write_unlock(&ipip6_lock);
	return err;
@@ -259,10 +322,11 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)

	if (a && a->addr != htonl(INADDR_ANY)) {
		for (p = &t->prl; *p; p = &(*p)->next) {
			if ((*p)->entry.addr == a->addr) {
			if ((*p)->addr == a->addr) {
				x = *p;
				*p = x->next;
				kfree(x);
				t->prl_count--;
				goto out;
			}
		}
@@ -272,6 +336,7 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
			x = t->prl;
			t->prl = t->prl->next;
			kfree(x);
			t->prl_count--;
		}
	}
out:
@@ -313,7 +378,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
	read_lock(&ipip6_lock);
	p = __ipip6_tunnel_locate_prl(t, iph->saddr);
	if (p) {
		if (p->entry.flags & PRL_DEFAULT)
		if (p->flags & PRL_DEFAULT)
			skb->ndisc_nodetype = NDISC_NODETYPE_DEFAULT;
		else
			skb->ndisc_nodetype = NDISC_NODETYPE_NODEFAULT;
@@ -899,11 +964,12 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
		err = 0;
		break;

	case SIOCGETPRL:
	case SIOCADDPRL:
	case SIOCDELPRL:
	case SIOCCHGPRL:
		err = -EPERM;
		if (!capable(CAP_NET_ADMIN))
		if (cmd != SIOCGETPRL && !capable(CAP_NET_ADMIN))
			goto done;
		err = -EINVAL;
		if (dev == ipip6_fb_tunnel_dev)
@@ -915,10 +981,22 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
		if (!(t = netdev_priv(dev)))
			goto done;

		if (cmd == SIOCDELPRL)
		switch (cmd) {
		case SIOCGETPRL:
			err = ipip6_tunnel_get_prl(t, &prl);
			if (!err && copy_to_user(ifr->ifr_ifru.ifru_data,
						 &prl, sizeof(prl)))
				err = -EFAULT;
			break;
		case SIOCDELPRL:
			err = ipip6_tunnel_del_prl(t, &prl);
		else
			break;
		case SIOCADDPRL:
		case SIOCCHGPRL:
			err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL);
			break;
		}
		if (cmd != SIOCGETPRL)
			netdev_state_change(dev);
		break;