patch-2.1.89 linux/net/ipv6/addrconf.c

Next file: linux/net/ipv6/af_inet6.c
Previous file: linux/net/ipv4/udp.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.88/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
@@ -63,6 +63,11 @@
 #define ADBG(x)
 #endif
 
+#ifdef CONFIG_SYSCTL
+static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p);
+static void addrconf_sysctl_unregister(struct ipv6_devconf *p);
+#endif
+
 /*
  *	Configured unicast address list
  */
@@ -90,6 +95,34 @@
 static void addrconf_rs_timer(unsigned long data);
 static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
 
+struct ipv6_devconf ipv6_devconf =
+{
+	0,				/* forwarding		*/
+	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/
+	576,				/* mtu			*/
+	1,				/* accept RAs		*/
+	1,				/* accept redirects	*/
+	1,				/* autoconfiguration	*/
+	1,				/* dad transmits	*/
+	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
+	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
+	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
+};
+
+static struct ipv6_devconf ipv6_devconf_dflt =
+{
+	0,				/* forwarding		*/
+	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/
+	576,				/* mtu			*/
+	1,				/* accept RAs		*/
+	1,				/* accept redirects	*/
+	1,				/* autoconfiguration	*/
+	1,				/* dad transmits	*/
+	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
+	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
+	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
+};
+
 int ipv6_addr_type(struct in6_addr *addr)
 {
 	u32 st;
@@ -151,19 +184,26 @@
 	struct inet6_dev *ndev, **bptr, *iter;
 	int hash;
 
+	if (dev->mtu < 576)
+		return NULL;
+
 	ndev = kmalloc(sizeof(struct inet6_dev), gfp_any());
 
 	if (ndev) {
 		memset(ndev, 0, sizeof(struct inet6_dev));
 
 		ndev->dev = dev;
-		ndev->nd_parms = neigh_parms_alloc(&nd_tbl);
-		if (ndev->nd_parms == NULL)
-			ndev->nd_parms = &nd_tbl.parms;
+		memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf));
+		ndev->cnf.mtu6 = dev->mtu;
+		ndev->cnf.sysctl = NULL;
+		ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
+		if (ndev->nd_parms == NULL) {
+			kfree(ndev);
+			return NULL;
+		}
 #ifdef CONFIG_SYSCTL
-		else
-			ndev->nd_parms->sysctl_table =
-				neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+		neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+		addrconf_sysctl_register(ndev, &ndev->cnf);
 #endif
 		hash = ipv6_devindex_hash(dev->ifindex);
 		bptr = &inet6_dev_lst[hash];
@@ -192,19 +232,16 @@
 	return idev;
 }
 
-void addrconf_forwarding_on(void)
+static void addrconf_forward_change(struct inet6_dev *idev)
 {
-	struct inet6_dev *idev;
 	int i;
 
-	for (i = 0; i < IN6_ADDR_HSIZE; i++) {
-		for (idev = inet6_dev_lst[i]; idev; idev = idev->next) {
-#if ACONF_DEBUG >= 2
-			printk(KERN_DEBUG "dev %s\n", idev->dev->name);
-#endif
+	if (idev)
+		return;
 
-			idev->router = 1;
-		}
+	for (i = 0; i < IN6_ADDR_HSIZE; i++) {
+		for (idev = inet6_dev_lst[i]; idev; idev = idev->next)
+			idev->cnf.forwarding = ipv6_devconf.forwarding;
 	}
 }
 
@@ -597,6 +634,12 @@
 	__u32 prefered_lft;
 	int addr_type;
 	unsigned long rt_expires;
+	struct inet6_dev *in6_dev = ipv6_get_idev(dev);
+
+	if (in6_dev == NULL) {
+		printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
+		return;
+	}
 
 	pinfo = (struct prefix_info *) opt;
 	
@@ -661,7 +704,7 @@
 
 	/* Try to figure out our local address for this prefix */
 
-	if (pinfo->autoconf && ipv6_config.autoconf) {
+	if (pinfo->autoconf && in6_dev->cnf.autoconf) {
 		struct inet6_ifaddr * ifp;
 		struct in6_addr addr;
 		int plen;
@@ -691,12 +734,6 @@
 		ifp = ipv6_chk_addr(&addr, dev, 1);
 
 		if ((ifp == NULL || (ifp->flags&ADDR_INVALID)) && valid_lft) {
-			struct inet6_dev *in6_dev = ipv6_get_idev(dev);
-
-			if (in6_dev == NULL) {
-				printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
-				return;
-			}
 
 			if (ifp == NULL)
 				ifp = ipv6_add_addr(in6_dev, &addr, addr_type & IPV6_ADDR_SCOPE_MASK);
@@ -873,12 +910,12 @@
 	
 	if (!suser())
 		return -EPERM;
-	
+
 	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
 		return -EFAULT;
 
 	rtnl_lock();
-	err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+	err = inet6_addr_del(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
 	rtnl_unlock();
 	return err;
 }
@@ -1021,9 +1058,6 @@
 	       dev->dev_addr, dev->addr_len);
 	addrconf_add_linklocal(idev, &addr);
 #endif
-
-	if (ipv6_config.forwarding)
-		idev->router = 1;
 }
 
 static void addrconf_sit_config(struct device *dev)
@@ -1079,21 +1113,26 @@
 #endif
 		break;
 
+	case NETDEV_CHANGEMTU:
+		/* BUGGG... Should scan FIB to change pmtu on routes. --ANK */
+		if (dev->mtu >= 576)
+			break;
+
+		/* MTU falled under 576. Stop IPv6 on this interface. */
+
 	case NETDEV_DOWN:
 	case NETDEV_UNREGISTER:
 		/*
 		 *	Remove all addresses from this interface.
 		 */
-		if (addrconf_ifdown(dev, event == NETDEV_UNREGISTER) == 0) {
+		if (addrconf_ifdown(dev, event != NETDEV_DOWN) == 0) {
 #ifdef CONFIG_IPV6_NETLINK
 			rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
 #endif
 		}
 
 		break;
-	case NETDEV_CHANGEMTU:
 	case NETDEV_CHANGE:
-		/* BUGGG... Should scan FIB to change pmtu on routes. --ANK */
 		break;
 	};
 
@@ -1154,6 +1193,9 @@
 			if (idev->dev == dev) {
 				*bidev = idev->next;
 				neigh_parms_release(&nd_tbl, idev->nd_parms);
+#ifdef CONFIG_SYSCTL
+				addrconf_sysctl_unregister(&idev->cnf);
+#endif
 				kfree(idev);
 				break;
 			}
@@ -1170,7 +1212,7 @@
 
 	ifp = (struct inet6_ifaddr *) data;
 
-	if (ipv6_config.forwarding)
+	if (ifp->idev->cnf.forwarding)
 		return;
 
 	if (ifp->idev->if_flags & IF_RA_RCVD) {
@@ -1181,7 +1223,7 @@
 		return;
 	}
 
-	if (ifp->probes++ <= ipv6_config.rtr_solicits) {
+	if (ifp->probes++ <= ifp->idev->cnf.rtr_solicits) {
 		struct in6_addr all_routers;
 
 		ipv6_addr_all_routers(&all_routers);
@@ -1190,7 +1232,7 @@
 		
 		ifp->timer.function = addrconf_rs_timer;
 		ifp->timer.expires = (jiffies + 
-				      ipv6_config.rtr_solicit_interval);
+				      ifp->idev->cnf.rtr_solicit_interval);
 		add_timer(&ifp->timer);
 	} else {
 		struct in6_rtmsg rtmsg;
@@ -1236,10 +1278,10 @@
 
 	net_srandom(ifp->addr.s6_addr32[3]);
 
-	ifp->probes = ipv6_config.dad_transmits;
+	ifp->probes = ifp->idev->cnf.dad_transmits;
 	ifp->flags |= DAD_INCOMPLETE;
 
-	rand_num = net_random() % ipv6_config.rtr_solicit_delay;
+	rand_num = net_random() % ifp->idev->cnf.rtr_solicit_delay;
 
 	ifp->timer.function = addrconf_dad_timer;
 	ifp->timer.expires = jiffies + rand_num;
@@ -1278,7 +1320,7 @@
 	ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
 #endif
 
-	ifp->timer.expires = jiffies + ipv6_config.rtr_solicit_interval;
+	ifp->timer.expires = jiffies + ifp->idev->cnf.rtr_solicit_interval;
 	add_timer(&ifp->timer);
 }
 
@@ -1296,7 +1338,7 @@
 	   start sending router solicitations.
 	 */
 
-	if (ipv6_config.forwarding == 0 &&
+	if (ifp->idev->cnf.forwarding == 0 &&
 	    (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) == 0 &&
 	    (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
 		struct in6_addr all_routers;
@@ -1313,7 +1355,7 @@
 		ifp->probes = 1;
 		ifp->timer.function = addrconf_rs_timer;
 		ifp->timer.expires = (jiffies +
-				      ipv6_config.rtr_solicit_interval);
+				      ifp->idev->cnf.rtr_solicit_interval);
 		ifp->idev->if_flags |= IF_RS_SENT;
 		add_timer(&ifp->timer);
 	}
@@ -1409,20 +1451,24 @@
 }
 
 #ifdef CONFIG_RTNETLINK
+
 static int
 inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-	struct kern_ifa  *k_ifa = arg;
+	struct rtattr **rta = arg;
 	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
 	struct in6_addr *pfx;
 
 	pfx = NULL;
-	if (k_ifa->ifa_address)
-		pfx = k_ifa->ifa_address;
-	if (k_ifa->ifa_local) {
-		if (pfx && memcmp(pfx, k_ifa->ifa_local, sizeof(*pfx)))
+	if (rta[IFA_ADDRESS-1]) {
+		if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx))
 			return -EINVAL;
-		pfx = k_ifa->ifa_local;
+		pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
+	}
+	if (rta[IFA_LOCAL-1]) {
+		if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
+			return -EINVAL;
+		pfx = RTA_DATA(rta[IFA_LOCAL-1]);
 	}
 
 	return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
@@ -1431,17 +1477,20 @@
 static int
 inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-	struct kern_ifa  *k_ifa = arg;
+	struct rtattr  **rta = arg;
 	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
 	struct in6_addr *pfx;
 
 	pfx = NULL;
-	if (k_ifa->ifa_address)
-		pfx = k_ifa->ifa_address;
-	if (k_ifa->ifa_local) {
-		if (pfx && memcmp(pfx, k_ifa->ifa_local, sizeof(*pfx)))
+	if (rta[IFA_ADDRESS-1]) {
+		if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx))
+			return -EINVAL;
+		pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
+	}
+	if (rta[IFA_LOCAL-1]) {
+		if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
 			return -EINVAL;
-		pfx = k_ifa->ifa_local;
+		pfx = RTA_DATA(rta[IFA_LOCAL-1]);
 	}
 
 	return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
@@ -1485,7 +1534,7 @@
 
 nlmsg_failure:
 rtattr_failure:
-	skb_put(skb, b - skb->tail);
+	skb_trim(skb, b - skb->data);
 	return -1;
 }
 
@@ -1528,7 +1577,7 @@
 	struct sk_buff *skb;
 	int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
 
-	skb = alloc_skb(size, GFP_KERNEL);
+	skb = alloc_skb(size, GFP_ATOMIC);
 	if (!skb) {
 		netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, ENOBUFS);
 		return;
@@ -1546,7 +1595,7 @@
 {
 	{ NULL,			NULL,			},
 	{ NULL,			NULL,			},
-	{ NULL,			rtnetlink_dump_ifinfo,	},
+	{ NULL,			NULL,			},
 	{ NULL,			NULL,			},
 
 	{ inet6_rtm_newaddr,	NULL,			},
@@ -1558,16 +1607,6 @@
 	{ inet6_rtm_delroute,	NULL,			},
 	{ NULL,			inet6_dump_fib,		},
 	{ NULL,			NULL,			},
-
-	{ neigh_add,		NULL,			},
-	{ neigh_delete,		NULL,			},
-	{ NULL,			neigh_dump_info,	},
-	{ NULL,			NULL,			},
-
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
-	{ NULL,			NULL,			},
 };
 #endif
 
@@ -1590,6 +1629,144 @@
 	}
 }
 
+#ifdef CONFIG_SYSCTL
+
+static
+int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+			   void *buffer, size_t *lenp)
+{
+	int *valp = ctl->data;
+	int val = *valp;
+	int ret;
+
+	ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+	if (write && *valp != val && valp != &ipv6_devconf_dflt.forwarding) {
+		struct inet6_dev *idev = NULL;
+
+		if (valp != &ipv6_devconf.forwarding) {
+			struct device *dev = dev_get_by_index(ctl->ctl_name);
+			if (dev)
+				idev = ipv6_get_idev(dev);
+			if (idev == NULL)
+				return ret;
+		} else
+			ipv6_devconf_dflt.forwarding = ipv6_devconf.forwarding;
+
+		addrconf_forward_change(idev);
+
+		if (*valp)
+			rt6_purge_dflt_routers(0);
+	}
+
+        return ret;
+}
+
+static struct addrconf_sysctl_table
+{
+	struct ctl_table_header *sysctl_header;
+	ctl_table addrconf_vars[11];
+	ctl_table addrconf_dev[2];
+	ctl_table addrconf_conf_dir[2];
+	ctl_table addrconf_proto_dir[2];
+	ctl_table addrconf_root_dir[2];
+} addrconf_sysctl = {
+	NULL,
+        {{NET_IPV6_FORWARDING, "forwarding",
+         &ipv6_devconf.forwarding, sizeof(int), 0644, NULL,
+         &addrconf_sysctl_forward},
+
+	{NET_IPV6_HOP_LIMIT, "hop_limit",
+         &ipv6_devconf.hop_limit, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_MTU, "mtu",
+         &ipv6_devconf.mtu6, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ACCEPT_RA, "accept_ra",
+         &ipv6_devconf.accept_ra, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_ACCEPT_REDIRECTS, "accept_redirects",
+         &ipv6_devconf.accept_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_AUTOCONF, "autoconf",
+         &ipv6_devconf.autoconf, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_DAD_TRANSMITS, "dad_transmits",
+         &ipv6_devconf.dad_transmits, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_RTR_SOLICITS, "router_solicitations",
+         &ipv6_devconf.rtr_solicits, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
+	{NET_IPV6_RTR_SOLICIT_INTERVAL, "router_solicitation_interval",
+         &ipv6_devconf.rtr_solicit_interval, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+
+	{NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay",
+         &ipv6_devconf.rtr_solicit_delay, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+
+	{0}},
+
+	{{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, addrconf_sysctl.addrconf_vars},{0}},
+	{{NET_IPV6_CONF, "conf", NULL, 0, 0555, addrconf_sysctl.addrconf_dev},{0}},
+	{{NET_IPV6, "ipv6", NULL, 0, 0555, addrconf_sysctl.addrconf_conf_dir},{0}},
+	{{CTL_NET, "net", NULL, 0, 0555, addrconf_sysctl.addrconf_proto_dir},{0}}
+};
+
+static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p)
+{
+	int i;
+	struct device *dev = idev ? idev->dev : NULL;
+	struct addrconf_sysctl_table *t;
+
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	if (t == NULL)
+		return;
+	memcpy(t, &addrconf_sysctl, sizeof(*t));
+	for (i=0; i<sizeof(t->addrconf_vars)/sizeof(t->addrconf_vars[0])-1; i++) {
+		t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf;
+		t->addrconf_vars[i].de = NULL;
+	}
+	if (dev) {
+		t->addrconf_dev[0].procname = dev->name;
+		t->addrconf_dev[0].ctl_name = dev->ifindex;
+	} else {
+		t->addrconf_dev[0].procname = "default";
+		t->addrconf_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT;
+	}
+	t->addrconf_dev[0].child = t->addrconf_vars;
+	t->addrconf_dev[0].de = NULL;
+	t->addrconf_conf_dir[0].child = t->addrconf_dev;
+	t->addrconf_conf_dir[0].de = NULL;
+	t->addrconf_proto_dir[0].child = t->addrconf_conf_dir;
+	t->addrconf_proto_dir[0].de = NULL;
+	t->addrconf_root_dir[0].child = t->addrconf_proto_dir;
+	t->addrconf_root_dir[0].de = NULL;
+
+	t->sysctl_header = register_sysctl_table(t->addrconf_root_dir, 0);
+	if (t->sysctl_header == NULL)
+		kfree(t);
+}
+
+static void addrconf_sysctl_unregister(struct ipv6_devconf *p)
+{
+	if (p->sysctl) {
+		struct addrconf_sysctl_table *t = p->sysctl;
+		p->sysctl = NULL;
+		unregister_sysctl_table(t->sysctl_header);
+		kfree(t);
+	}
+}
+
+
+#endif
 
 /*
  *	Init / cleanup code
@@ -1628,6 +1805,11 @@
 #ifdef CONFIG_RTNETLINK
 	rtnetlink_links[AF_INET6] = inet6_rtnetlink_table;
 #endif
+#ifdef CONFIG_SYSCTL
+	addrconf_sysctl.sysctl_header =
+		register_sysctl_table(addrconf_sysctl.addrconf_root_dir, 0);
+	addrconf_sysctl_register(NULL, &ipv6_devconf_dflt);
+#endif
 }
 
 #ifdef MODULE
@@ -1640,7 +1822,10 @@
 #ifdef CONFIG_RTNETLINK
 	rtnetlink_links[AF_INET6] = NULL;
 #endif
-	start_bh_atomic();
+#ifdef CONFIG_SYSCTL
+	addrconf_sysctl_unregister(&ipv6_devconf_dflt);
+	addrconf_sysctl_unregister(&ipv6_devconf);
+#endif
 
 	del_timer(&addr_chk_timer);
 
@@ -1656,6 +1841,7 @@
 		}
 	}
 
+	start_bh_atomic();
 	/*
 	 *	clean addr_list
 	 */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov