write_unlock_bh(&addrconf_lock);
ipv6_mc_init_dev(ndev);
-
+ ndev->tstamp = jiffies;
#ifdef CONFIG_SYSCTL
- 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", &ndisc_ifinfo_sysctl_change);
addrconf_sysctl_register(ndev, &ndev->cnf);
#endif
}
rt6_mtu_change(dev, dev->mtu);
idev->cnf.mtu6 = dev->mtu;
}
+ idev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, idev);
/* If the changed mtu during down is lower than IPV6_MIN_MTU
stop IPv6 on this interface.
*/
if (idev) {
addrconf_sysctl_unregister(&idev->cnf);
neigh_sysctl_unregister(idev->nd_parms);
- neigh_sysctl_register(dev, idev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+ neigh_sysctl_register(dev, idev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change);
addrconf_sysctl_register(idev, &idev->cnf);
}
#endif
else
ipv6_mc_down(idev);
+ /* Step 5: netlink notification of this interface */
+ idev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, idev);
+
/* Shot the device (if unregistered) */
if (how == 1) {
#endif
}
-static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
- struct inet6_dev *idev,
- int type, u32 pid, u32 seq)
+static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
+ u32 pid, u32 seq, int event)
{
+ struct net_device *dev = idev->dev;
__s32 *array = NULL;
struct ifinfomsg *r;
struct nlmsghdr *nlh;
unsigned char *b = skb->tail;
struct rtattr *subattr;
+ __u32 mtu = dev->mtu;
+ struct ifla_cacheinfo ci;
- nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
r = NLMSG_DATA(nlh);
r->ifi_family = AF_INET6;
RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
+ if (dev->addr_len)
+ RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
+
+ RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);
+ if (dev->ifindex != dev->iflink)
+ RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink);
+
subattr = (struct rtattr*)skb->tail;
RTA_PUT(skb, IFLA_PROTINFO, 0, NULL);
/* return the device flags */
RTA_PUT(skb, IFLA_INET6_FLAGS, sizeof(__u32), &idev->if_flags);
+ /* return interface cacheinfo */
+ ci.max_reasm_len = IPV6_MAXPLEN;
+ ci.tstamp = (__u32)(TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) / HZ * 100
+ + TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) % HZ * 100 / HZ);
+ ci.reachable_time = idev->nd_parms->reachable_time;
+ ci.retrans_time = idev->nd_parms->retrans_time;
+ RTA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci);
+
/* return the device sysctl params */
if ((array = kmalloc(DEVCONF_MAX * sizeof(*array), GFP_ATOMIC)) == NULL)
goto rtattr_failure;
continue;
if ((idev = in6_dev_get(dev)) == NULL)
continue;
- err = inet6_fill_ifinfo(skb, dev, idev, RTM_NEWLINK,
- NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq);
+ err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWLINK);
in6_dev_put(idev);
if (err <= 0)
break;
return skb->len;
}
+void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
+{
+ struct sk_buff *skb;
+ /* 128 bytes ?? */
+ int size = NLMSG_SPACE(sizeof(struct ifinfomsg)+128);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFINFO, ENOBUFS);
+ return;
+ }
+ if (inet6_fill_ifinfo(skb, idev, 0, 0, event) < 0) {
+ kfree_skb(skb);
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFINFO, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_IFINFO;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFINFO, GFP_ATOMIC);
+}
+
static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = {
[RTM_GETLINK - RTM_BASE] = { .dumpit = inet6_dump_ifinfo, },
[RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, },
if (rtime < HZ/10)
rtime = HZ/10;
in6_dev->nd_parms->retrans_time = rtime;
+ in6_dev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
}
rtime = ntohl(ra_msg->reachable_time);
in6_dev->nd_parms->base_reachable_time = rtime;
in6_dev->nd_parms->gc_staletime = 3 * rtime;
in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
+ in6_dev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
}
}
}
.notifier_call = ndisc_netdev_event,
};
+#ifdef CONFIG_SYSCTL
+int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp)
+{
+ struct net_device *dev = ctl->extra1;
+ struct inet6_dev *idev;
+
+ if (write && dev && (idev = in6_dev_get(dev)) != NULL) {
+ idev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, idev);
+ in6_dev_put(idev);
+ }
+ return proc_dointvec(ctl, write, filp, buffer, lenp);
+}
+#endif
+
int __init ndisc_init(struct net_proto_family *ops)
{
struct ipv6_pinfo *np;
neigh_table_init(&nd_tbl);
#ifdef CONFIG_SYSCTL
- neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+ neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH,
+ "ipv6", &ndisc_ifinfo_sysctl_change);
#endif
register_netdevice_notifier(&ndisc_netdev_notifier);