From 12cb1f471833a7e145bdf7cb4d471d0bd74d73f0 Mon Sep 17 00:00:00 2001
From: Mitsuru Chinen <mitch@linux.vnet.ibm.com>
Date: Mon, 20 Oct 2008 16:08:06 +0900
Subject: [PATCH] Fix for Internet Address Table

From net-snmp patch tracker:
[ 1692817 ] ipAddressTable fixes
http://sourceforge.net/tracker/index.php?func=detail&aid=1692817&group_id=12694&atid=312694

[ 1712645 ] meaningful log message on duplicate IP address
http://sourceforge.net/tracker/index.php?func=detail&aid=1712645&group_id=12694&atid=312694

[ 1810660 ] Fix broadcast addresses in ipAddressTable on 64 bit
linux
http://sourceforge.net/tracker/index.php?func=detail&aid=1810660&group_id=12694&atid=312694

Signed-off-by: Mitsuru Chinen <mitch@linux.vnet.ibm.com>
---
 .../mibgroup/ip-mib/data_access/ipaddress_ioctl.c  |   63 +++++++++-
 .../mibgroup/ip-mib/data_access/ipaddress_ioctl.h  |   13 ++
 .../mibgroup/ip-mib/data_access/ipaddress_linux.c  |  121 ++++++++++++++++++--
 .../ip-mib/ipAddressTable/ipAddressTable.c         |    5 +-
 include/net-snmp/library/container.h               |    2 +-
 snmplib/container.c                                |    2 +-
 6 files changed, 189 insertions(+), 17 deletions(-)

diff --git a/agent/mibgroup/ip-mib/data_access/ipaddress_ioctl.c b/agent/mibgroup/ip-mib/data_access/ipaddress_ioctl.c
index d5e78f0..085653d 100644
--- a/agent/mibgroup/ip-mib/data_access/ipaddress_ioctl.c
+++ b/agent/mibgroup/ip-mib/data_access/ipaddress_ioctl.c
@@ -135,7 +135,9 @@ _netsnmp_ioctl_ipaddress_container_load_v4(netsnmp_container *container,
     struct ifreq   *ifrp;
     struct sockaddr save_addr;
     struct sockaddr_in * si;
-    netsnmp_ipaddress_entry *entry;
+    netsnmp_ipaddress_entry *entry, *bcastentry;
+    struct address_flag_info addr_info;
+    in_addr_t       ipval;
     _ioctl_extras           *extras;
 
     if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
@@ -184,6 +186,7 @@ _netsnmp_ioctl_ipaddress_container_load_v4(netsnmp_container *container,
         netsnmp_assert(AF_INET == ifrp->ifr_addr.sa_family);
         si = (struct sockaddr_in *) &ifrp->ifr_addr;
         entry->ia_address_len = sizeof(si->sin_addr.s_addr);
+        ipval = si->sin_addr.s_addr;
         memcpy(entry->ia_address, &si->sin_addr.s_addr,
                entry->ia_address_len);
 
@@ -220,6 +223,26 @@ _netsnmp_ioctl_ipaddress_container_load_v4(netsnmp_container *container,
         }
 
         /*
+         * get broadcast
+         */
+        memset(&addr_info, 0, sizeof(struct address_flag_info));
+#if defined (NETSNMP_ENABLE_IPV6)
+        addr_info = netsnmp_access_other_info_get(entry->if_index, AF_INET);
+        if(addr_info.bcastflg) {
+           bcastentry = netsnmp_access_ipaddress_entry_create();
+           if(NULL == entry) {
+              rc = -3;
+              break;
+           }
+           bcastentry->if_index = entry->if_index;
+           bcastentry->ns_ia_index = ++idx_offset;
+           bcastentry->ia_address_len = sizeof(addr_info.inp->s_addr);
+           memcpy(bcastentry->ia_address, &addr_info.inp->s_addr,
+                  bcastentry->ia_address_len);
+        }
+#endif
+
+        /*
          * get netmask
          */
         ifrp->ifr_addr = save_addr;
@@ -232,7 +255,10 @@ _netsnmp_ioctl_ipaddress_container_load_v4(netsnmp_container *container,
         netsnmp_assert(AF_INET == ifrp->ifr_addr.sa_family);
         si = (struct sockaddr_in *) &ifrp->ifr_addr;
         entry->ia_prefix_len =
-            netsnmp_ipaddress_ipv4_prefix_len(si->sin_addr.s_addr);
+            netsnmp_ipaddress_ipv4_prefix_len(ntohl(si->sin_addr.s_addr));
+        if(addr_info.bcastflg)
+           bcastentry->ia_prefix_len = entry->ia_prefix_len;
+
 
         /*
          * get flags
@@ -246,7 +272,12 @@ _netsnmp_ioctl_ipaddress_container_load_v4(netsnmp_container *container,
         }
         extras->flags = ifrp->ifr_flags;
 
-        entry->ia_type = IPADDRESSTYPE_UNICAST; /* assume unicast? */
+        if(addr_info.bcastflg)
+           bcastentry->ia_type = IPADDRESSTYPE_BROADCAST;
+        if(addr_info.anycastflg)
+           entry->ia_type = IPADDRESSTYPE_ANYCAST;
+        else
+           entry->ia_type = IPADDRESSTYPE_UNICAST;
 
         /** entry->ia_prefix_oid ? */
 
@@ -256,12 +287,23 @@ _netsnmp_ioctl_ipaddress_container_load_v4(netsnmp_container *container,
          *   always preferred(1).
          */
         entry->ia_status = IPADDRESSSTATUSTC_PREFERRED;
+        if(addr_info.bcastflg)
+           bcastentry->ia_status = IPADDRESSSTATUSTC_PREFERRED;
 
         /*
          * can we figure out if an address is from DHCP?
          * use manual until then...
          */
-        entry->ia_origin = IPADDRESSORIGINTC_MANUAL;
+        if(IS_APIPA(ipval)) {
+           entry->ia_origin = IPADDRESSORIGINTC_RANDOM;
+           if(addr_info.bcastflg)
+              bcastentry->ia_origin = IPADDRESSORIGINTC_RANDOM;
+        }
+        else {
+           entry->ia_origin = IPADDRESSORIGINTC_MANUAL;
+           if(addr_info.bcastflg)
+              bcastentry->ia_origin = IPADDRESSORIGINTC_MANUAL;
+        }
 
         DEBUGIF("access:ipaddress:container") {
             DEBUGMSGT_NC(("access:ipaddress:container",
@@ -279,12 +321,21 @@ _netsnmp_ioctl_ipaddress_container_load_v4(netsnmp_container *container,
         /*
          * add entry to container
          */
-        if (CONTAINER_INSERT(container, entry) < 0)
-        {
+        if(addr_info.bcastflg){
+            if (CONTAINER_INSERT(container, bcastentry) < 0) {
+                DEBUGMSGTL(("access:ipaddress:container","error with ipaddress_entry: insert broadcast entry into container failed.\n"));
+                netsnmp_access_ipaddress_entry_free(bcastentry);
+                netsnmp_access_ipaddress_entry_free(entry);
+                continue;
+            }
+        }
+
+        if (CONTAINER_INSERT(container, entry) < 0) {
             DEBUGMSGTL(("access:ipaddress:container","error with ipaddress_entry: insert into container failed.\n"));
             netsnmp_access_ipaddress_entry_free(entry);
             continue;
         }
+
     }
 
     /*
diff --git a/agent/mibgroup/ip-mib/data_access/ipaddress_ioctl.h b/agent/mibgroup/ip-mib/data_access/ipaddress_ioctl.h
index a7a0ea2..fc9774f 100644
--- a/agent/mibgroup/ip-mib/data_access/ipaddress_ioctl.h
+++ b/agent/mibgroup/ip-mib/data_access/ipaddress_ioctl.h
@@ -2,6 +2,17 @@
 extern          "C" {
 #endif
 
+/*
+ * struct for netlink extras
+ */
+struct address_flag_info {
+    int bcastflg;
+    int anycastflg;
+    struct in_addr *inp;
+};
+ 
+#define IS_APIPA(a)  (((in_addr_t)(a << 16)) == 0xFEA90000)
+
 int
 _netsnmp_ioctl_ipaddress_container_load_v4(netsnmp_container *container,
                                                   int idx_offset);
@@ -13,6 +24,8 @@ _netsnmp_ioctl_ipaddress_remove_v4(netsnmp_ipaddress_entry * entry);
 int
 netsnmp_access_ipaddress_ioctl_get_interface_count(int sd, struct ifconf * ifc);
 
+struct address_flag_info
+netsnmp_access_other_info_get(int index, int family);
 
 /*
  * struct ioctl for arch_data
diff --git a/agent/mibgroup/ip-mib/data_access/ipaddress_linux.c b/agent/mibgroup/ip-mib/data_access/ipaddress_linux.c
index 8cb06a2..ac37578 100644
--- a/agent/mibgroup/ip-mib/data_access/ipaddress_linux.c
+++ b/agent/mibgroup/ip-mib/data_access/ipaddress_linux.c
@@ -19,6 +19,7 @@
 #if defined (NETSNMP_ENABLE_IPV6)
 #include <linux/types.h>
 #include <asm/types.h>
+#include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #endif
 
@@ -188,6 +189,7 @@ _load_v6(netsnmp_container *container, int idx_offset)
     netsnmp_ipaddress_entry *entry;
     _ioctl_extras           *extras;
     static int      log_open_err = 1;
+    struct address_flag_info addr_info;
     
     netsnmp_assert(NULL != container);
 
@@ -268,6 +270,10 @@ _load_v6(netsnmp_container *container, int idx_offset)
          * every time it is called.
          */
         entry->if_index = netsnmp_access_interface_index_find(if_name);
+        memset(&addr_info, 0, sizeof(struct address_flag_info));
+#if defined (NETSNMP_ENABLE_IPV6)
+        addr_info = netsnmp_access_other_info_get(entry->if_index, AF_INET6);
+#endif
 
         /*
           #define IPADDRESSSTATUSTC_PREFERRED  1
@@ -278,7 +284,7 @@ _load_v6(netsnmp_container *container, int idx_offset)
           #define IPADDRESSSTATUSTC_TENTATIVE  6
           #define IPADDRESSSTATUSTC_DUPLICATE  7
         */
-        if(flags & IFA_F_PERMANENT)
+        if((flags & IFA_F_PERMANENT) || (!flags) || (flags & IFA_F_TEMPORARY))
             entry->ia_status = IPADDRESSSTATUSTC_PREFERRED; /* ?? */
         else if(flags & IFA_F_DEPRECATED)
             entry->ia_status = IPADDRESSSTATUSTC_DEPRECATED;
@@ -294,7 +300,7 @@ _load_v6(netsnmp_container *container, int idx_offset)
          * if it's not multi, it must be uni.
          *  (an ipv6 address is never broadcast)
          */
-        if (IN6_IS_ADDR_MULTICAST(entry->ia_address))
+        if(addr_info.anycastflg)
             entry->ia_type = IPADDRESSTYPE_ANYCAST;
         else
             entry->ia_type = IPADDRESSTYPE_UNICAST;
@@ -314,18 +320,28 @@ _load_v6(netsnmp_container *container, int idx_offset)
          *
          * are 'local' address assigned by link layer??
          */
-        if (IN6_IS_ADDR_LINKLOCAL(entry->ia_address) ||
-            IN6_IS_ADDR_SITELOCAL(entry->ia_address))
-            entry->ia_origin = IPADDRESSORIGINTC_LINKLAYER;
-        else
-            entry->ia_origin = IPADDRESSORIGINTC_MANUAL;
+         if (!flags)
+             entry->ia_origin = IPADDRESSORIGINTC_LINKLAYER;
+         else if (flags & IFA_F_TEMPORARY)
+             entry->ia_origin = IPADDRESSORIGINTC_RANDOM;
+         else if (IN6_IS_ADDR_LINKLOCAL(entry->ia_address))
+             entry->ia_origin = IPADDRESSORIGINTC_LINKLAYER;
+         else
+             entry->ia_origin = IPADDRESSORIGINTC_MANUAL;
+
+         if(entry->ia_origin == IPADDRESSORIGINTC_LINKLAYER)
+            entry->ia_storagetype = STORAGETYPE_PERMANENT;
 
         /* xxx-rks: what can we do with scope? */
 
         /*
          * add entry to container
          */
-        CONTAINER_INSERT(container, entry);
+        if (CONTAINER_INSERT(container, entry) < 0) {
+            DEBUGMSGTL(("access:ipaddress:container","error with ipaddress_entry: insert into container failed.\n"));
+            netsnmp_access_ipaddress_entry_free(entry);
+            continue;
+        }
     }
 
     fclose(in);
@@ -335,4 +351,93 @@ _load_v6(netsnmp_container *container, int idx_offset)
 
     return idx_offset;
 }
+
+struct address_flag_info
+netsnmp_access_other_info_get(int index, int family)
+{
+   struct {
+           struct nlmsghdr n;
+           struct ifaddrmsg r;
+           char   buf[1024];
+   } req;
+   struct address_flag_info addr;
+   struct rtattr    *rta;
+   int    status;
+   char   buf[16384];
+   struct nlmsghdr  *nlmp;
+   struct ifaddrmsg *rtmp;
+   struct rtattr    *rtatp;
+   int    rtattrlen;
+   int    sd;
+
+   memset(&addr, 0, sizeof(struct address_flag_info));
+   sd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+   if(sd < 0) {
+      snmp_log(LOG_ERR, "could not open netlink socket\n");
+      return addr;
+   }
+
+   memset(&req, 0, sizeof(req));
+   req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+   req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
+   req.n.nlmsg_type = RTM_GETADDR;
+   req.r.ifa_family = family;
+   rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len));
+   if(family == AF_INET)
+      rta->rta_len = RTA_LENGTH(4);
+   else
+      rta->rta_len = RTA_LENGTH(16);
+
+    status = send(sd, &req, req.n.nlmsg_len, 0);
+    if (status < 0) {
+        snmp_log(LOG_ERR, "could not send netlink request\n");
+        return addr;
+    }
+
+    status = recv(sd, buf, sizeof(buf), 0);
+    if (status < 0) {
+        snmp_log (LOG_ERR, "could not recieve netlink request\n");
+        return addr;
+    }
+
+    if(status == 0) {
+       snmp_log (LOG_ERR, "nothing to read\n");
+       return addr;
+    }
+
+    for(nlmp = (struct nlmsghdr *)buf; status > sizeof(*nlmp);) {
+        int len = nlmp->nlmsg_len;
+        int req_len = len - sizeof(*nlmp);
+
+        if (req_len < 0 || len > status) {
+            snmp_log (LOG_ERR, "invalid netlink message\n");
+            return addr;
+        }
+
+        if (!NLMSG_OK(nlmp, status)) {
+            snmp_log (LOG_ERR, "invalid NLMSG message\n");
+            return addr;
+        }
+        rtmp = (struct ifaddrmsg *)NLMSG_DATA(nlmp);
+        rtatp = (struct rtattr *)IFA_RTA(rtmp);
+        rtattrlen = IFA_PAYLOAD(nlmp);
+        if(index == rtmp->ifa_index){
+           for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) {
+                if(rtatp->rta_type == IFA_BROADCAST){
+                   addr.inp = (struct in_addr *)RTA_DATA(rtatp);
+                   addr.bcastflg = 1;
+                }
+                if(rtatp->rta_type == IFA_ANYCAST){
+                   addr.inp = (struct in_addr *)RTA_DATA(rtatp);
+                   addr.anycastflg = 1;
+                }
+           }
+        }
+        status -= NLMSG_ALIGN(len);
+        nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len));
+    }
+    close(sd);
+    return addr;
+}
 #endif
+
diff --git a/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable.c b/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable.c
index e695ab3..8bb3cbc 100644
--- a/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable.c
+++ b/agent/mibgroup/ip-mib/ipAddressTable/ipAddressTable.c
@@ -942,7 +942,10 @@ ipAddressRowStatus_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
     netsnmp_assert(NULL != ipAddressRowStatus_val_ptr);
 
     /** WARNING: this code might not work for netsnmp_ipaddress_entry */
-    (*ipAddressRowStatus_val_ptr) = rowreq_ctx->ipAddressRowStatus;
+    if(rowreq_ctx->data->if_index)
+       (*ipAddressRowStatus_val_ptr) = rowreq_ctx->ipAddressRowStatus;
+    else
+       (*ipAddressRowStatus_val_ptr) = ROWSTATUS_NOTREADY;
 
     return MFD_SUCCESS;
 }                               /* ipAddressRowStatus_get */
diff --git a/include/net-snmp/library/container.h b/include/net-snmp/library/container.h
index f88fa21..22684aa 100644
--- a/include/net-snmp/library/container.h
+++ b/include/net-snmp/library/container.h
@@ -358,7 +358,7 @@ extern "C" {
         if(x) {
             int rc = x->insert(x,k);
             if(rc)
-                snmp_log(LOG_ERR,"error on subcontainer '%s' insert (%d)\n",
+                snmp_log(LOG_DEBUG,"error on subcontainer '%s' insert (%d)\n",
                          x->container_name ? x->container_name : "", rc);
             else {
                 rc = CONTAINER_INSERT_HELPER(x->next, k);
diff --git a/snmplib/container.c b/snmplib/container.c
index e34e922..1255f0a 100644
--- a/snmplib/container.c
+++ b/snmplib/container.c
@@ -275,7 +275,7 @@ int CONTAINER_INSERT_HELPER(netsnmp_container* x, const void* k)
     if(x) {
         int rc = x->insert(x,k);
         if(rc)
-            snmp_log(LOG_ERR,"error on subcontainer '%s' insert (%d)\n",
+            snmp_log(LOG_DEBUG,"error on subcontainer '%s' insert (%d)\n",
                      x->container_name ? x->container_name : "", rc);
         else {
             rc = CONTAINER_INSERT_HELPER(x->next, k);
-- 
1.6.0.2

