From 3c1ea2a57f835a46d08b20d72b2c3c8bc4e9c2a0 Mon Sep 17 00:00:00 2001 From: Shirley Ma <mashirle@us.ibm.com> Date: Wed, 14 Jan 2004 08:58:20 -0800 Subject: [PATCH] [IPV6]: Implement MIB:ipv6InterfaceTable --- include/linux/rtnetlink.h | 12 +++++++- include/net/if_inet6.h | 1 + include/net/ndisc.h | 11 +++++++ include/net/neighbour.h | 10 +++++-- net/core/neighbour.c | 7 ++++- net/ipv4/arp.c | 2 +- net/ipv4/devinet.c | 4 +-- net/ipv6/addrconf.c | 62 +++++++++++++++++++++++++++++++++------ net/ipv6/ndisc.c | 22 +++++++++++++- 9 files changed, 114 insertions(+), 17 deletions(-) diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 4a26f207b5d7..db1966e24462 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -558,9 +558,18 @@ enum IFLA_INET6_CONF, /* sysctl parameters */ IFLA_INET6_STATS, /* statistics */ 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. @@ -611,6 +620,7 @@ enum #define RTMGRP_IPV6_IFADDR 0x100 #define RTMGRP_IPV6_MROUTE 0x200 #define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 #define RTMGRP_DECnet_IFADDR 0x1000 #define RTMGRP_DECnet_ROUTE 0x4000 diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 6dd6ebda5e6f..4ef8526494d2 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -183,6 +183,7 @@ struct inet6_dev struct inet6_dev *next; struct ipv6_devconf cnf; struct ipv6_devstat stats; + unsigned long tstamp; /* ipv6InterfaceTable update timestamp */ }; extern struct ipv6_devconf ipv6_devconf; diff --git a/include/net/ndisc.h b/include/net/ndisc.h index d364fc636912..95684d3363c1 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -98,6 +98,17 @@ extern int igmp6_event_report(struct sk_buff *skb); 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) { diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 24bee28fd7fb..e016389694a8 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -47,6 +47,9 @@ #include <linux/skbuff.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_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); extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); extern void neigh_app_ns(struct neighbour *n); -extern int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, - int p_id, int pdev_id, char *p_name); +extern int neigh_sysctl_register(struct net_device *dev, + 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); /* diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 4b194b6be05a..c1b4a4496309 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1629,7 +1629,8 @@ struct neigh_sysctl_table { }; 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); const char *dev_name_source = NULL; @@ -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[2].data = &p->app_probes; 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[5].data = &p->delay_probe_time; t->neigh_vars[6].data = &p->gc_staletime; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index dc026cfacd3d..5c03f63502bc 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1122,7 +1122,7 @@ void __init arp_init(void) arp_proc_init(); #ifdef CONFIG_SYSCTL neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, - NET_IPV4_NEIGH, "ipv4"); + NET_IPV4_NEIGH, "ipv4", NULL); #endif register_netdevice_notifier(&arp_netdev_notifier); } diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index fe58fb5b59da..4bdfd7319ae6 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -155,7 +155,7 @@ struct in_device *inetdev_init(struct net_device *dev) dev_hold(dev); #ifdef CONFIG_SYSCTL neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, - NET_IPV4_NEIGH, "ipv4"); + NET_IPV4_NEIGH, "ipv4", NULL); #endif write_lock_bh(&inetdev_lock); dev->ip_ptr = in_dev; @@ -910,7 +910,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, devinet_sysctl_unregister(&in_dev->cnf); neigh_sysctl_unregister(in_dev->arp_parms); 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); #endif break; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b614c23b0e14..237a4c933680 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -373,9 +373,10 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) 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 } @@ -1890,6 +1891,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, 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. */ @@ -1921,7 +1924,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, 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 @@ -2031,6 +2034,10 @@ static int addrconf_ifdown(struct net_device *dev, int how) 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) { @@ -2732,17 +2739,19 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf, #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; @@ -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); + 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); @@ -2764,6 +2780,14 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, /* 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; @@ -2798,8 +2822,8 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) 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; @@ -2810,6 +2834,26 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) 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, }, diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f2e7c8bb7fe9..f725744e6d48 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1115,6 +1115,8 @@ static void ndisc_router_discovery(struct sk_buff *skb) 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); @@ -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->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); } } } @@ -1492,6 +1496,21 @@ struct notifier_block ndisc_netdev_notifier = { .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; @@ -1522,7 +1541,8 @@ int __init ndisc_init(struct net_proto_family *ops) 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); -- 2.30.9