Commit 3c1ea2a5 authored by Shirley Ma's avatar Shirley Ma Committed by David S. Miller

[IPV6]: Implement MIB:ipv6InterfaceTable

parent 395950f8
...@@ -558,9 +558,18 @@ enum ...@@ -558,9 +558,18 @@ enum
IFLA_INET6_CONF, /* sysctl parameters */ IFLA_INET6_CONF, /* sysctl parameters */
IFLA_INET6_STATS, /* statistics */ IFLA_INET6_STATS, /* statistics */
IFLA_INET6_MCAST, /* MC things. What of them? */ IFLA_INET6_MCAST, /* MC things. What of them? */
IFLA_INET6_CACHEINFO, /* time values and max reasm size */
}; };
#define IFLA_INET6_MAX IFLA_INET6_MCAST struct ifla_cacheinfo
{
__u32 max_reasm_len;
__u32 tstamp; /* ipv6InterfaceTable updated timestamp */
__u32 reachable_time;
__u32 retrans_time;
};
#define IFLA_INET6_MAX IFLA_INET6_CACHEINFO
/***************************************************************** /*****************************************************************
* Traffic control messages. * Traffic control messages.
...@@ -611,6 +620,7 @@ enum ...@@ -611,6 +620,7 @@ enum
#define RTMGRP_IPV6_IFADDR 0x100 #define RTMGRP_IPV6_IFADDR 0x100
#define RTMGRP_IPV6_MROUTE 0x200 #define RTMGRP_IPV6_MROUTE 0x200
#define RTMGRP_IPV6_ROUTE 0x400 #define RTMGRP_IPV6_ROUTE 0x400
#define RTMGRP_IPV6_IFINFO 0x800
#define RTMGRP_DECnet_IFADDR 0x1000 #define RTMGRP_DECnet_IFADDR 0x1000
#define RTMGRP_DECnet_ROUTE 0x4000 #define RTMGRP_DECnet_ROUTE 0x4000
......
...@@ -183,6 +183,7 @@ struct inet6_dev ...@@ -183,6 +183,7 @@ struct inet6_dev
struct inet6_dev *next; struct inet6_dev *next;
struct ipv6_devconf cnf; struct ipv6_devconf cnf;
struct ipv6_devstat stats; struct ipv6_devstat stats;
unsigned long tstamp; /* ipv6InterfaceTable update timestamp */
}; };
extern struct ipv6_devconf ipv6_devconf; extern struct ipv6_devconf ipv6_devconf;
......
...@@ -98,6 +98,17 @@ extern int igmp6_event_report(struct sk_buff *skb); ...@@ -98,6 +98,17 @@ extern int igmp6_event_report(struct sk_buff *skb);
extern void igmp6_cleanup(void); extern void igmp6_cleanup(void);
#ifdef CONFIG_SYSCTL
extern int ndisc_ifinfo_sysctl_change(ctl_table *ctl,
int write,
struct file * filp,
void __user *buffer,
size_t *lenp);
#endif
extern void inet6_ifinfo_notify(int event,
struct inet6_dev *idev);
static inline struct neighbour * ndisc_get_neigh(struct net_device *dev, struct in6_addr *addr) static inline struct neighbour * ndisc_get_neigh(struct net_device *dev, struct in6_addr *addr)
{ {
......
...@@ -47,6 +47,9 @@ ...@@ -47,6 +47,9 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/err.h> #include <linux/err.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
#define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE) #define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE)
#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) #define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
...@@ -206,8 +209,11 @@ extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); ...@@ -206,8 +209,11 @@ extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
extern void neigh_app_ns(struct neighbour *n); extern void neigh_app_ns(struct neighbour *n);
extern int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, extern int neigh_sysctl_register(struct net_device *dev,
int p_id, int pdev_id, char *p_name); struct neigh_parms *p,
int p_id, int pdev_id,
char *p_name,
proc_handler *proc_handler);
extern void neigh_sysctl_unregister(struct neigh_parms *p); extern void neigh_sysctl_unregister(struct neigh_parms *p);
/* /*
......
...@@ -1629,7 +1629,8 @@ struct neigh_sysctl_table { ...@@ -1629,7 +1629,8 @@ struct neigh_sysctl_table {
}; };
int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
int p_id, int pdev_id, char *p_name) int p_id, int pdev_id, char *p_name,
proc_handler *handler)
{ {
struct neigh_sysctl_table *t = kmalloc(sizeof(*t), GFP_KERNEL); struct neigh_sysctl_table *t = kmalloc(sizeof(*t), GFP_KERNEL);
const char *dev_name_source = NULL; const char *dev_name_source = NULL;
...@@ -1643,6 +1644,10 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, ...@@ -1643,6 +1644,10 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
t->neigh_vars[1].data = &p->ucast_probes; t->neigh_vars[1].data = &p->ucast_probes;
t->neigh_vars[2].data = &p->app_probes; t->neigh_vars[2].data = &p->app_probes;
t->neigh_vars[3].data = &p->retrans_time; t->neigh_vars[3].data = &p->retrans_time;
if (handler) {
t->neigh_vars[3].proc_handler = handler;
t->neigh_vars[3].extra1 = dev;
}
t->neigh_vars[4].data = &p->base_reachable_time; t->neigh_vars[4].data = &p->base_reachable_time;
t->neigh_vars[5].data = &p->delay_probe_time; t->neigh_vars[5].data = &p->delay_probe_time;
t->neigh_vars[6].data = &p->gc_staletime; t->neigh_vars[6].data = &p->gc_staletime;
......
...@@ -1122,7 +1122,7 @@ void __init arp_init(void) ...@@ -1122,7 +1122,7 @@ void __init arp_init(void)
arp_proc_init(); arp_proc_init();
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4,
NET_IPV4_NEIGH, "ipv4"); NET_IPV4_NEIGH, "ipv4", NULL);
#endif #endif
register_netdevice_notifier(&arp_netdev_notifier); register_netdevice_notifier(&arp_netdev_notifier);
} }
......
...@@ -155,7 +155,7 @@ struct in_device *inetdev_init(struct net_device *dev) ...@@ -155,7 +155,7 @@ struct in_device *inetdev_init(struct net_device *dev)
dev_hold(dev); dev_hold(dev);
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4,
NET_IPV4_NEIGH, "ipv4"); NET_IPV4_NEIGH, "ipv4", NULL);
#endif #endif
write_lock_bh(&inetdev_lock); write_lock_bh(&inetdev_lock);
dev->ip_ptr = in_dev; dev->ip_ptr = in_dev;
...@@ -910,7 +910,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, ...@@ -910,7 +910,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
devinet_sysctl_unregister(&in_dev->cnf); devinet_sysctl_unregister(&in_dev->cnf);
neigh_sysctl_unregister(in_dev->arp_parms); neigh_sysctl_unregister(in_dev->arp_parms);
neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4,
NET_IPV4_NEIGH, "ipv4"); NET_IPV4_NEIGH, "ipv4", NULL);
devinet_sysctl_register(in_dev, &in_dev->cnf); devinet_sysctl_register(in_dev, &in_dev->cnf);
#endif #endif
break; break;
......
...@@ -373,9 +373,10 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) ...@@ -373,9 +373,10 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
write_unlock_bh(&addrconf_lock); write_unlock_bh(&addrconf_lock);
ipv6_mc_init_dev(ndev); ipv6_mc_init_dev(ndev);
ndev->tstamp = jiffies;
#ifdef CONFIG_SYSCTL #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); addrconf_sysctl_register(ndev, &ndev->cnf);
#endif #endif
} }
...@@ -1890,6 +1891,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, ...@@ -1890,6 +1891,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
rt6_mtu_change(dev, dev->mtu); rt6_mtu_change(dev, dev->mtu);
idev->cnf.mtu6 = 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 /* If the changed mtu during down is lower than IPV6_MIN_MTU
stop IPv6 on this interface. stop IPv6 on this interface.
*/ */
...@@ -1921,7 +1924,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, ...@@ -1921,7 +1924,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
if (idev) { if (idev) {
addrconf_sysctl_unregister(&idev->cnf); addrconf_sysctl_unregister(&idev->cnf);
neigh_sysctl_unregister(idev->nd_parms); 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); addrconf_sysctl_register(idev, &idev->cnf);
} }
#endif #endif
...@@ -2031,6 +2034,10 @@ static int addrconf_ifdown(struct net_device *dev, int how) ...@@ -2031,6 +2034,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
else else
ipv6_mc_down(idev); 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) */ /* Shot the device (if unregistered) */
if (how == 1) { if (how == 1) {
...@@ -2732,17 +2739,19 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf, ...@@ -2732,17 +2739,19 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
#endif #endif
} }
static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
struct inet6_dev *idev, u32 pid, u32 seq, int event)
int type, u32 pid, u32 seq)
{ {
struct net_device *dev = idev->dev;
__s32 *array = NULL; __s32 *array = NULL;
struct ifinfomsg *r; struct ifinfomsg *r;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
unsigned char *b = skb->tail; unsigned char *b = skb->tail;
struct rtattr *subattr; 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; if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
r = NLMSG_DATA(nlh); r = NLMSG_DATA(nlh);
r->ifi_family = AF_INET6; r->ifi_family = AF_INET6;
...@@ -2757,6 +2766,13 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, ...@@ -2757,6 +2766,13 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name); 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; subattr = (struct rtattr*)skb->tail;
RTA_PUT(skb, IFLA_PROTINFO, 0, NULL); RTA_PUT(skb, IFLA_PROTINFO, 0, NULL);
...@@ -2764,6 +2780,14 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, ...@@ -2764,6 +2780,14 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
/* return the device flags */ /* return the device flags */
RTA_PUT(skb, IFLA_INET6_FLAGS, sizeof(__u32), &idev->if_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 */ /* return the device sysctl params */
if ((array = kmalloc(DEVCONF_MAX * sizeof(*array), GFP_ATOMIC)) == NULL) if ((array = kmalloc(DEVCONF_MAX * sizeof(*array), GFP_ATOMIC)) == NULL)
goto rtattr_failure; goto rtattr_failure;
...@@ -2798,8 +2822,8 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2798,8 +2822,8 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
continue; continue;
if ((idev = in6_dev_get(dev)) == NULL) if ((idev = in6_dev_get(dev)) == NULL)
continue; continue;
err = inet6_fill_ifinfo(skb, dev, idev, RTM_NEWLINK, err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid,
NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq); cb->nlh->nlmsg_seq, RTM_NEWLINK);
in6_dev_put(idev); in6_dev_put(idev);
if (err <= 0) if (err <= 0)
break; break;
...@@ -2810,6 +2834,26 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2810,6 +2834,26 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len; 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] = { static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = {
[RTM_GETLINK - RTM_BASE] = { .dumpit = inet6_dump_ifinfo, }, [RTM_GETLINK - RTM_BASE] = { .dumpit = inet6_dump_ifinfo, },
[RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, }, [RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, },
......
...@@ -1115,6 +1115,8 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1115,6 +1115,8 @@ static void ndisc_router_discovery(struct sk_buff *skb)
if (rtime < HZ/10) if (rtime < HZ/10)
rtime = HZ/10; rtime = HZ/10;
in6_dev->nd_parms->retrans_time = rtime; in6_dev->nd_parms->retrans_time = rtime;
in6_dev->tstamp = jiffies;
inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
} }
rtime = ntohl(ra_msg->reachable_time); rtime = ntohl(ra_msg->reachable_time);
...@@ -1128,6 +1130,8 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1128,6 +1130,8 @@ static void ndisc_router_discovery(struct sk_buff *skb)
in6_dev->nd_parms->base_reachable_time = rtime; in6_dev->nd_parms->base_reachable_time = rtime;
in6_dev->nd_parms->gc_staletime = 3 * rtime; in6_dev->nd_parms->gc_staletime = 3 * rtime;
in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime); in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
in6_dev->tstamp = jiffies;
inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
} }
} }
} }
...@@ -1492,6 +1496,21 @@ struct notifier_block ndisc_netdev_notifier = { ...@@ -1492,6 +1496,21 @@ struct notifier_block ndisc_netdev_notifier = {
.notifier_call = ndisc_netdev_event, .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) int __init ndisc_init(struct net_proto_family *ops)
{ {
struct ipv6_pinfo *np; struct ipv6_pinfo *np;
...@@ -1522,7 +1541,8 @@ int __init ndisc_init(struct net_proto_family *ops) ...@@ -1522,7 +1541,8 @@ int __init ndisc_init(struct net_proto_family *ops)
neigh_table_init(&nd_tbl); neigh_table_init(&nd_tbl);
#ifdef CONFIG_SYSCTL #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 #endif
register_netdevice_notifier(&ndisc_netdev_notifier); register_netdevice_notifier(&ndisc_netdev_notifier);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment