[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

(usagi-users 02792) [PATCH] Configuration support for addrselect_label_table in 2.6



Hi,
this patch provides support for configuration of addrselect_label_table
in 2.6 kernel.

Cheers.

-- 
EuropeSwPatentFree - http://EuropeSwPatentFree.hispalinux.es
diff -urN -X ../dontdiff usagi.0119/kernel/linux26/include/linux/in6.h usagi/kernel/linux26/include/linux/in6.h
--- usagi.0119/kernel/linux26/include/linux/in6.h	2003-10-10 15:28:04.000000000 +0200
+++ usagi/kernel/linux26/include/linux/in6.h	2004-01-19 12:27:19.000000000 +0100
@@ -183,5 +183,8 @@
 #define IPV6_IPSEC_POLICY	34
 #define IPV6_XFRM_POLICY	35
 
+/* device controls */
+#define SIOCSDADDRLABEL	(SIOCPROTOPRIVATE + 2)
+#define SIOCDDADDRLABEL	(SIOCPROTOPRIVATE + 3)
 
 #endif
diff -urN -X ../dontdiff usagi.0119/kernel/linux26/include/linux/ipv6.h usagi/kernel/linux26/include/linux/ipv6.h
--- usagi.0119/kernel/linux26/include/linux/ipv6.h	2004-01-11 09:19:25.000000000 +0100
+++ usagi/kernel/linux26/include/linux/ipv6.h	2004-01-19 12:27:19.000000000 +0100
@@ -28,6 +28,14 @@
 	int		ifr6_ifindex; 
 };
 
+struct in6_addrlabelreq {
+	struct in6_addr		addr;
+	__u16			plen;
+	__u16			__reserved;
+	__u32			ifindex;
+	__u32			label;
+};
+
 #define IPV6_SRCRT_STRICT	0x01	/* this hop must be a neighbor	*/
 #define IPV6_SRCRT_TYPE_0	0	/* IPv6 type 0 Routing Header	*/
 
diff -urN -X ../dontdiff usagi.0119/kernel/linux26/include/net/addrconf.h usagi/kernel/linux26/include/net/addrconf.h
--- usagi.0119/kernel/linux26/include/net/addrconf.h	2004-01-11 09:18:16.000000000 +0100
+++ usagi/kernel/linux26/include/net/addrconf.h	2004-01-22 14:49:02.000000000 +0100
@@ -77,6 +77,7 @@
 					struct in6_addr *addr);
 extern void			addrconf_leave_solict(struct net_device *dev,
 					struct in6_addr *addr);
+extern int			addrconf_label_ioctl(unsigned int cmd, void *arg);
 
 /*
  *	multicast prototypes (mcast.c)
diff -urN -X ../dontdiff usagi.0119/kernel/linux26/net/ipv6/addrconf.c usagi/kernel/linux26/net/ipv6/addrconf.c
--- usagi.0119/kernel/linux26/net/ipv6/addrconf.c	2004-01-11 09:18:16.000000000 +0100
+++ usagi/kernel/linux26/net/ipv6/addrconf.c	2004-01-22 14:46:18.000000000 +0100
@@ -38,6 +38,8 @@
  *	YOSHIFUJI Hideaki @USAGI	:	improved source address
  *						selection; consider scope,
  *						status etc.
+ *	Antonio Tapiador		:	Label table user level
+ *	<atapiador@xxxxxxxxxx>			configuration
  */
 
 #include <linux/config.h>
@@ -145,6 +147,18 @@
 
 static u32 ipv6_addrselect_label_lookup(const struct in6_addr *addr, int ifindex);
 
+struct ipv6_addrselect_label
+{
+	struct ipv6_addrselect_label *next;
+	struct in6_addr addr;
+	u16	plen;
+	u32	ifindex;
+	u32	label;
+};
+
+struct ipv6_addrselect_label	*ipv6_addrselect_label_table;
+rwlock_t	addrselect_label_lock = RW_LOCK_UNLOCKED;
+
 struct ipv6_devconf ipv6_devconf = {
 	.forwarding		= 0,
 	.hop_limit		= IPV6_DEFAULT_HOPLIMIT,
@@ -1062,47 +1076,96 @@
 }
 
 /* address selection: default policy label */
-/* XXX: user level configuration */
-static struct ipv6_addrselect_label {
-	struct in6_addr addr;
-	u16	plen;
-	u32	ifindex;
-	u32	label;
-} ipv6_addrselect_label_table[] = {
-	/* ::1/128, label = 0 */
-	{
-		.addr = IN6ADDR_LOOPBACK_INIT,
-		.plen = 128,
-		.label = 0,
-	},
-	/* ::/0, label = 1 */
-	{
-		.addr = IN6ADDR_ANY_INIT,
-		.plen = 0,
-		.label = 1,
-	},
-	/* 2002::/16, label = 2 */
-	{
-		.addr = {{{ 0x20, 0x02 }}},
-		.plen = 16,
-		.label = 2,
-	},
-	/* ::/96, label = 3 */
-	{
-		.plen = 96,
-		.label = 3,
-	},
-	/* ::ffff:0:0/96, label = 4 */
-	{
-		.addr = {{{ [10] = 0xff, [11] = 0xff }}},
-		.plen = 96,
-		.label = 4,
-	},
-	/* sentinel */
-	{
-		.label = 0xffffffff,
+int ipv6_addrselect_add_label(int ifindex, struct in6_addr *addr, int plen, int label)
+{
+	struct ipv6_addrselect_label *p, *p_last;
+
+	/* Update label on duplicate addresses */
+	write_lock_bh(&addrselect_label_lock);
+	p_last = ipv6_addrselect_label_table;
+	for (p = ipv6_addrselect_label_table;
+	     p;
+	     p = p->next) {
+		if (ipv6_addr_diff(addr, &p->addr) >= plen && plen == p->plen) {
+			p->ifindex = ifindex;
+			p->label = label;
+			write_unlock_bh(&addrselect_label_lock);
+			return 0;
+		}
+		p_last = p;
 	}
-};
+
+	p = kmalloc(sizeof(struct ipv6_addrselect_label), GFP_KERNEL);
+	if (p == NULL) {
+		ADBG((KERN_WARNING
+			"ipv6_addrselect_add_label: malloc failed\n"));
+		write_unlock_bh(&addrselect_label_lock);
+		return -ENOMEM;
+	}
+
+	memset(p, 0, sizeof(struct ipv6_addrselect_label));
+
+	if (!ipv6_addrselect_label_table) 
+		ipv6_addrselect_label_table = p;
+	else 
+		p_last->next = p;
+	p->addr = *addr;
+	p->plen = plen;
+	p->ifindex = ifindex;
+	p->label = label;
+	write_unlock_bh(&addrselect_label_lock);
+	return 0;
+}
+
+int ipv6_addrselect_del_label(struct in6_addr *addr, int plen)
+{
+	struct ipv6_addrselect_label *p, *p_last;
+
+	write_lock_bh(&addrselect_label_lock);
+	p_last = ipv6_addrselect_label_table;
+	for (p = ipv6_addrselect_label_table;
+	     p;
+	     p = p->next) {
+		if (ipv6_addr_diff(addr, &p->addr) >= plen && plen == p->plen) {
+			if (ipv6_addrselect_label_table == p) {
+				ipv6_addrselect_label_table = p->next;
+			} else {
+				p_last->next = p->next;
+			}
+			kfree(p);
+			write_unlock_bh(&addrselect_label_lock);
+			return 0;
+		}
+		p_last = p;
+	}
+	write_unlock_bh(&addrselect_label_lock);
+	return -ENOENT;
+}
+
+int addrconf_label_ioctl(unsigned int cmd, void *arg)
+{
+	struct in6_addrlabelreq req;
+	int err = -EINVAL;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+	switch(cmd) {
+	case SIOCSDADDRLABEL:
+		err = copy_from_user(&req, arg, sizeof(struct in6_addrlabelreq));
+		if (err)
+			return -EFAULT;
+		err = ipv6_addrselect_add_label(req.ifindex, &req.addr, req.plen, req.label);
+		break;
+
+	case SIOCDDADDRLABEL:
+		err = copy_from_user(&req, arg, sizeof(struct in6_addrlabelreq));
+		if (err)
+			return -EFAULT;
+		err = ipv6_addrselect_del_label(&req.addr, req.plen);
+		break;
+	}
+	return err;
+}
 
 static u32 ipv6_addrselect_label_lookup(const struct in6_addr *addr, 
 					int ifindex)
@@ -1111,9 +1174,10 @@
 	int plen, matchlen = -1;
 	u32 label = 0xffffffff;
 
+	read_lock_bh(&addrselect_label_lock);
 	for (p = ipv6_addrselect_label_table;
-	     p->label != 0xffffffff;
-	     p++) {
+	     p;
+	     p = p->next) {
 		if (ifindex && p->ifindex && ifindex != p->ifindex)
 			continue;
 		plen = ipv6_addr_diff(addr, &p->addr);
@@ -1122,9 +1186,177 @@
 		matchlen = plen;
 		label = p->label;
 	}
+	read_unlock_bh(&addrselect_label_lock);
 	return label;
 }
 
+static struct ipv6_addrselect_label ipv6_addrselect_label_init_table[] = {
+	/* prefix = ::1/128, label = 0 */
+	{
+		addr:   IN6ADDR_LOOPBACK_INIT,
+		plen:   128,
+		label:  0,
+	},
+	/* prefix = ::/0, label = 1 */
+	{
+		label:  1,
+	},
+	/* prefix = 2002::/16, label = 2 */
+	{
+		addr:   {{{ 0x20,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }}},
+		plen:   16,
+		label:  2,
+	},
+	/* prefix = ::/96, label = 3 */
+	{
+		plen:   96,
+		label:  3,
+	},
+	/* prefix = ::ffff:0:0/96, label = 4 */
+	{
+		addr:   {{{ 0,0,0,0,0,0,0,0,0,0xff,0xff,0,0,0,0 }}},
+		plen:   96,
+		label:  4,
+	},
+	/* sentinel */
+	{
+		label:  0xffffffff,
+	}
+};
+
+void ipv6_addrselect_init(void)
+{
+	struct ipv6_addrselect_label **pnext, *p0;
+
+	for (p0 = ipv6_addrselect_label_init_table,
+	     pnext = &ipv6_addrselect_label_table;
+	     p0->label != 0xffffffff;
+	     p0++,
+	     pnext = &(*pnext)->next) {
+		*pnext = kmalloc(sizeof(**pnext), GFP_ATOMIC);
+		if (*pnext == NULL) {
+			printk(KERN_WARNING
+				"%s(): failed to allocate memory\n",
+				__FUNCTION__);
+			break;
+		}
+		memcpy(*pnext, p0, sizeof(**pnext));
+	}
+};
+
+void ipv6_addrselect_cleanup(void)
+{
+	struct ipv6_addrselect_label *p, *pnext;
+
+	write_lock_bh(&addrselect_label_lock);	
+	if ((pnext = ipv6_addrselect_label_table)) {
+		ipv6_addrselect_label_table = NULL;
+		do {
+			p = pnext;
+			pnext = p->next;
+			kfree(p);
+		} while (pnext);
+	}
+	write_unlock_bh(&addrselect_label_lock);	
+}
+	     
+
+#ifdef CONFIG_PROC_FS
+
+static struct ipv6_addrselect_label *addrsel_pt_get_first(struct seq_file *seq)
+{
+	return ipv6_addrselect_label_table;
+}
+
+static struct ipv6_addrselect_label *addrsel_pt_get_next(struct seq_file *seq, struct ipv6_addrselect_label *p)
+{
+	p = p->next;
+	return p;
+}
+
+static struct ipv6_addrselect_label *addrsel_pt_get_idx(struct seq_file *seq, loff_t pos)
+{
+	struct ipv6_addrselect_label *p = addrsel_pt_get_first(seq);
+
+	if (p)
+		while(pos && (p = addrsel_pt_get_next(seq, p)) != NULL)
+			--pos;
+	return pos ? NULL : p;
+}
+
+static void *addrsel_pt_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	read_lock_bh(&addrselect_label_lock);
+	return addrsel_pt_get_idx(seq, *pos);
+}
+
+static void *addrsel_pt_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct ipv6_addrselect_label *p;
+
+	p = addrsel_pt_get_next(seq, v);
+	++*pos;
+	return p;
+}
+
+static void addrsel_pt_seq_stop(struct seq_file *seq, void *v)
+{
+	read_unlock_bh(&addrselect_label_lock);
+}
+
+static int addrsel_pt_seq_show(struct seq_file *seq, void *v)
+{
+	struct ipv6_addrselect_label *p = (struct ipv6_addrselect_label *)v;
+	seq_printf(seq,
+		   "%04x%04x%04x%04x%04x%04x%04x%04x %02x %02x %02x\n",
+		   NIP6(p->addr),
+		   p->plen,
+		   p->ifindex,
+		   p->label);
+	return 0;
+}
+
+static struct seq_operations addrsel_pt_seq_ops = {
+	.start	= addrsel_pt_seq_start,
+	.next	= addrsel_pt_seq_next,
+	.show	= addrsel_pt_seq_show,
+	.stop	= addrsel_pt_seq_stop,
+};
+
+static int addrsel_pt_seq_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	int rc = -ENOMEM;
+
+	rc = seq_open(file, &addrsel_pt_seq_ops);
+	if (!rc)
+		seq = file->private_data;
+
+	return rc;
+}
+
+static struct file_operations addrsel_pt_fops = {
+	.owner		= THIS_MODULE,
+	.open		= addrsel_pt_seq_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release_private,
+};
+
+int __init addrsel_pt_proc_init(void)
+{
+	if (!proc_net_fops_create("addrsel_pt", S_IRUGO, &addrsel_pt_fops))
+		return -ENOMEM;
+	return 0;
+}
+
+void addrsel_pt_proc_exit(void)
+{
+	proc_net_remove("addrsel_pt");
+}
+
+#endif  /* CONFIG_PROC_FS */
+
 int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
 {
 	const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr;
@@ -3425,6 +3657,7 @@
 		register_sysctl_table(addrconf_sysctl.addrconf_root_dir, 0);
 	addrconf_sysctl_register(NULL, &ipv6_devconf_dflt);
 #endif
+	ipv6_addrselect_init();
 }
 
 #ifdef MODULE
@@ -3488,8 +3721,11 @@
 		crypto_free_tfm(tfm);
 #endif
 
+	ipv6_addrselect_cleanup();
+
 #ifdef CONFIG_PROC_FS
 	proc_net_remove("if_inet6");
+	proc_net_remove("addrsel_pt");
 #endif
 }
 #endif	/* MODULE */
diff -urN -X ../dontdiff usagi.0119/kernel/linux26/net/ipv6/af_inet6.c usagi/kernel/linux26/net/ipv6/af_inet6.c
--- usagi.0119/kernel/linux26/net/ipv6/af_inet6.c	2003-10-10 15:28:04.000000000 +0200
+++ usagi/kernel/linux26/net/ipv6/af_inet6.c	2004-01-19 12:27:19.000000000 +0100
@@ -88,6 +88,8 @@
 extern void ac6_proc_exit(void);
 extern int if6_proc_init(void);
 extern void if6_proc_exit(void);
+extern int addrsel_pt_proc_init(void);
+extern void addrsel_pt_proc_exit(void);
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -488,6 +490,9 @@
 		return addrconf_del_ifaddr((void *) arg);
 	case SIOCSIFDSTADDR:
 		return addrconf_set_dstaddr((void *) arg);
+	case SIOCSDADDRLABEL:
+	case SIOCDDADDRLABEL:
+		return (addrconf_label_ioctl(cmd, (void *) arg));
 	default:
 		if (!sk->sk_prot->ioctl ||
 		    (err = sk->sk_prot->ioctl(sk, cmd, arg)) == -ENOIOCTLCMD)
@@ -801,6 +806,8 @@
 		goto proc_anycast6_fail;
 	if (if6_proc_init())
 		goto proc_if6_fail;
+	if (addrsel_pt_proc_init())
+		goto proc_addrsel_pt_fail;
 #endif
 	ipv6_netdev_notif_init();
 	ipv6_packet_init();
@@ -822,6 +829,8 @@
 	return 0;
 
 #ifdef CONFIG_PROC_FS
+proc_addrsel_pt_fail:
+	if6_proc_exit();
 proc_if6_fail:
 	ac6_proc_exit();
 proc_anycast6_fail:
@@ -860,6 +869,7 @@
 	/* First of all disallow new sockets creation. */
 	sock_unregister(PF_INET6);
 #ifdef CONFIG_PROC_FS
+	addrsel_pt_proc_exit();
 	if6_proc_exit();
 	ac6_proc_exit();
  	ipv6_misc_proc_exit();