This is enhanced from the proposed patch by Igor Maravic in 2011 to
support per interface IPv4 stats. The enhancement is mainly adding a
kernel configuration option CONFIG_IP_IFSTATS_TABLE.

Signed-off-by: Stephen Suryaputra <ssuryae...@gmail.com>
---
 drivers/net/vrf.c               |   2 +-
 include/linux/inetdevice.h      |  22 +++++++
 include/net/icmp.h              |  44 ++++++++++++--
 include/net/ip.h                |  72 +++++++++++++++++++++--
 include/net/ipv6.h              |  62 ++++----------------
 include/net/netns/mib.h         |   3 +
 include/net/snmp.h              |  95 ++++++++++++++++++++++++++++++
 net/bridge/br_netfilter_hooks.c |  10 ++--
 net/dccp/ipv4.c                 |   4 +-
 net/ipv4/Kconfig                |   8 +++
 net/ipv4/af_inet.c              |   7 ++-
 net/ipv4/datagram.c             |   2 +-
 net/ipv4/devinet.c              |  85 ++++++++++++++++++++++++++-
 net/ipv4/icmp.c                 |  32 +++++-----
 net/ipv4/inet_connection_sock.c |   8 ++-
 net/ipv4/ip_forward.c           |   8 +--
 net/ipv4/ip_fragment.c          |  20 ++++---
 net/ipv4/ip_input.c             |  29 ++++-----
 net/ipv4/ip_output.c            |  40 ++++++++-----
 net/ipv4/ipmr.c                 |   6 +-
 net/ipv4/ping.c                 |   9 ++-
 net/ipv4/proc.c                 | 126 ++++++++++++++++++++++++++++++++++++++++
 net/ipv4/raw.c                  |   4 +-
 net/ipv4/route.c                |   6 +-
 net/ipv4/tcp_ipv4.c             |   4 +-
 net/ipv4/udp.c                  |   4 +-
 net/l2tp/l2tp_ip.c              |   4 +-
 net/l2tp/l2tp_ip6.c             |   2 +-
 net/mpls/af_mpls.c              |   2 +-
 net/netfilter/ipvs/ip_vs_xmit.c |   2 +-
 net/sctp/input.c                |   2 +-
 net/sctp/output.c               |   2 +-
 32 files changed, 570 insertions(+), 156 deletions(-)

diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 0a2b180..509c2ca 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -592,7 +592,7 @@ static int vrf_output(struct net *net, struct sock *sk, 
struct sk_buff *skb)
 {
        struct net_device *dev = skb_dst(skb)->dev;
 
-       IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
+       IP_UPD_PO_STATS(net, dev, IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index e16fe7d..3d120cb 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -22,6 +22,15 @@ struct ipv4_devconf {
 
 #define MC_HASH_SZ_LOG 9
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+struct ipv4_devstat {
+       struct proc_dir_entry *proc_dir_entry;
+       DEFINE_SNMP_STAT(struct ipstats_mib, ip);
+       DEFINE_SNMP_STAT_ATOMIC(struct icmp_mib_device, icmpdev);
+       DEFINE_SNMP_STAT_ATOMIC(struct icmpmsg_mib_device, icmpmsgdev);
+};
+#endif
+
 struct in_device {
        struct net_device       *dev;
        refcount_t              refcnt;
@@ -45,6 +54,9 @@ struct in_device {
 
        struct neigh_parms      *arp_parms;
        struct ipv4_devconf     cnf;
+#ifdef CONFIG_IP_IFSTATS_TABLE
+       struct ipv4_devstat stats;
+#endif
        struct rcu_head         rcu_head;
 };
 
@@ -216,6 +228,16 @@ static inline struct in_device *__in_dev_get_rcu(const 
struct net_device *dev)
        return rcu_dereference(dev->ip_ptr);
 }
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+static inline struct in_device *__in_dev_get_rcu_safely(const struct 
net_device *dev)
+{
+       if (likely(dev))
+               return rcu_dereference(dev->ip_ptr);
+       else
+               return NULL;
+}
+#endif
+
 static inline struct in_device *in_dev_get(const struct net_device *dev)
 {
        struct in_device *in_dev;
diff --git a/include/net/icmp.h b/include/net/icmp.h
index 3ef2743..fdfbc0f 100644
--- a/include/net/icmp.h
+++ b/include/net/icmp.h
@@ -29,10 +29,44 @@ struct icmp_err {
 };
 
 extern const struct icmp_err icmp_err_convert[];
-#define ICMP_INC_STATS(net, field)     
SNMP_INC_STATS((net)->mib.icmp_statistics, field)
-#define __ICMP_INC_STATS(net, field)   
__SNMP_INC_STATS((net)->mib.icmp_statistics, field)
-#define ICMPMSGOUT_INC_STATS(net, field)       
SNMP_INC_STATS_ATOMIC_LONG((net)->mib.icmpmsg_statistics, field+256)
-#define ICMPMSGIN_INC_STATS(net, field)                
SNMP_INC_STATS_ATOMIC_LONG((net)->mib.icmpmsg_statistics, field)
+#ifdef CONFIG_IP_IFSTATS_TABLE
+#define ICMP_INC_STATS(net, dev, field)        \
+       ({      \
+               rcu_read_lock();        \
+               _DEVINCATOMIC(net, icmp, struct in_device,      \
+                       __in_dev_get_rcu_safely(dev), field);   \
+               rcu_read_unlock();      \
+       })
+
+#define __ICMP_INC_STATS(net, dev, field)      \
+       ({      \
+               rcu_read_lock();        \
+               ___DEVINCATOMIC(net, icmp, struct in_device,    \
+                       __in_dev_get_rcu_safely(dev), field);   \
+               rcu_read_unlock();      \
+       })
+
+#define ICMPMSGOUT_INC_STATS(net, dev, field)  \
+       ({      \
+               rcu_read_lock();        \
+               _DEVINC_ATOMIC_ATOMIC(net, icmpmsg, struct in_device,   \
+                       __in_dev_get_rcu_safely(dev), field+256);       \
+               rcu_read_unlock();      \
+       })
+
+#define ICMPMSGIN_INC_STATS(net, dev, field)   \
+       ({      \
+               rcu_read_lock();        \
+               _DEVINC_ATOMIC_ATOMIC(net, icmpmsg, struct in_device,   \
+                       __in_dev_get_rcu_safely(dev), field);   \
+               rcu_read_unlock();      \
+       })
+#else
+#define ICMP_INC_STATS(net, dev, field)        
SNMP_INC_STATS((net)->mib.icmp_statistics, field)
+#define __ICMP_INC_STATS(net, dev, field)      
__SNMP_INC_STATS((net)->mib.icmp_statistics, field)
+#define ICMPMSGOUT_INC_STATS(net, dev, field)  
SNMP_INC_STATS_ATOMIC_LONG((net)->mib.icmpmsg_statistics, field+256)
+#define ICMPMSGIN_INC_STATS(net, dev, field)           
SNMP_INC_STATS_ATOMIC_LONG((net)->mib.icmpmsg_statistics, field)
+#endif
 
 struct dst_entry;
 struct net_proto_family;
@@ -43,6 +77,6 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, 
__be32 info);
 int icmp_rcv(struct sk_buff *skb);
 void icmp_err(struct sk_buff *skb, u32 info);
 int icmp_init(void);
-void icmp_out_count(struct net *net, unsigned char type);
+void icmp_out_count(struct net_device *dev, unsigned char type);
 
 #endif /* _ICMP_H */
diff --git a/include/net/ip.h b/include/net/ip.h
index ecffd84..aa8a55b 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -27,6 +27,7 @@
 #include <linux/in.h>
 #include <linux/skbuff.h>
 #include <linux/jhash.h>
+#include <linux/inetdevice.h>
 
 #include <net/inet_sock.h>
 #include <net/route.h>
@@ -218,12 +219,62 @@ void ip_send_unicast_reply(struct sock *sk, struct 
sk_buff *skb,
                           const struct ip_reply_arg *arg,
                           unsigned int len);
 
-#define IP_INC_STATS(net, field)       
SNMP_INC_STATS64((net)->mib.ip_statistics, field)
-#define __IP_INC_STATS(net, field)     
__SNMP_INC_STATS64((net)->mib.ip_statistics, field)
-#define IP_ADD_STATS(net, field, val)  
SNMP_ADD_STATS64((net)->mib.ip_statistics, field, val)
-#define __IP_ADD_STATS(net, field, val) 
__SNMP_ADD_STATS64((net)->mib.ip_statistics, field, val)
-#define IP_UPD_PO_STATS(net, field, val) 
SNMP_UPD_PO_STATS64((net)->mib.ip_statistics, field, val)
-#define __IP_UPD_PO_STATS(net, field, val) 
__SNMP_UPD_PO_STATS64((net)->mib.ip_statistics, field, val)
+#ifdef CONFIG_IP_IFSTATS_TABLE
+#define IP_INC_STATS(net, dev, field)  \
+       ({      \
+               rcu_read_lock();        \
+               _DEVINC(net, ip, struct in_device,      \
+                       __in_dev_get_rcu_safely(dev), field);   \
+               rcu_read_unlock();      \
+       })
+
+#define __IP_INC_STATS(net, dev, field)        \
+       ({      \
+               rcu_read_lock();        \
+               ___DEVINC(net, ip, struct in_device,    \
+                       __in_dev_get_rcu_safely(dev), field);   \
+               rcu_read_unlock();      \
+       })
+
+#define IP_ADD_STATS(net, dev, field, val)     \
+       ({      \
+               rcu_read_lock();        \
+               _DEVADD(net, ip, struct in_device,      \
+                       __in_dev_get_rcu_safely(dev), field, val);      \
+               rcu_read_unlock();      \
+       })
+
+#define __IP_ADD_STATS(net, dev, field, val)   \
+       ({      \
+               rcu_read_lock();        \
+               ___DEVADD(net, ip, struct in_device,    \
+                       __in_dev_get_rcu_safely(dev), field, val);      \
+               rcu_read_unlock();      \
+       })
+
+#define IP_UPD_PO_STATS(net, dev, field, val)  \
+       ({      \
+               rcu_read_lock();        \
+               _DEVUPD(net, ip, struct in_device,      \
+                       __in_dev_get_rcu_safely(dev), field, val);      \
+               rcu_read_unlock();      \
+       })
+
+#define __IP_UPD_PO_STATS(net, dev, field, val)        \
+       ({      \
+               rcu_read_lock();        \
+               ___DEVUPD(net, ip, struct in_device,    \
+                       __in_dev_get_rcu_safely(dev), field, val);      \
+               rcu_read_unlock();      \
+       })
+#else
+#define IP_INC_STATS(net, dev, field)  
SNMP_INC_STATS64((net)->mib.ip_statistics, field)
+#define __IP_INC_STATS(net, dev, field)        
__SNMP_INC_STATS64((net)->mib.ip_statistics, field)
+#define IP_ADD_STATS(net, dev, field, val)     
SNMP_ADD_STATS64((net)->mib.ip_statistics, field, val)
+#define __IP_ADD_STATS(net, dev, field, val) 
__SNMP_ADD_STATS64((net)->mib.ip_statistics, field, val)
+#define IP_UPD_PO_STATS(net, dev, field, val) 
SNMP_UPD_PO_STATS64((net)->mib.ip_statistics, field, val)
+#define __IP_UPD_PO_STATS(net, dev, field, val) 
__SNMP_UPD_PO_STATS64((net)->mib.ip_statistics, field, val)
+#endif
 #define NET_INC_STATS(net, field)      
SNMP_INC_STATS((net)->mib.net_statistics, field)
 #define __NET_INC_STATS(net, field)    
__SNMP_INC_STATS((net)->mib.net_statistics, field)
 #define NET_ADD_STATS(net, field, adnd)        
SNMP_ADD_STATS((net)->mib.net_statistics, field, adnd)
@@ -660,4 +711,13 @@ extern int sysctl_icmp_msgs_burst;
 int ip_misc_proc_init(void);
 #endif
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+#ifdef CONFIG_PROC_FS
+extern int snmp_register_dev(struct in_device *idev);
+extern int snmp_unregister_dev(struct in_device *idev);
+#else
+extern int snmp_register_dev(struct in_device *idev) { return 0; }
+extern int snmp_unregister_dev(struct in_device *idev) { return 0; }
+#endif
+#endif
 #endif /* _IP_H */
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 9b6e7f5..c9be5e1 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -163,71 +163,29 @@ struct frag_hdr {
 extern int sysctl_mld_max_msf;
 extern int sysctl_mld_qrv;
 
-#define _DEVINC(net, statname, mod, idev, field)                       \
-({                                                                     \
-       struct inet6_dev *_idev = (idev);                               \
-       if (likely(_idev != NULL))                                      \
-               mod##SNMP_INC_STATS64((_idev)->stats.statname, (field));\
-       mod##SNMP_INC_STATS64((net)->mib.statname##_statistics, (field));\
-})
-
-/* per device counters are atomic_long_t */
-#define _DEVINCATOMIC(net, statname, mod, idev, field)                 \
-({                                                                     \
-       struct inet6_dev *_idev = (idev);                               \
-       if (likely(_idev != NULL))                                      \
-               SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, 
(field)); \
-       mod##SNMP_INC_STATS((net)->mib.statname##_statistics, (field));\
-})
-
-/* per device and per net counters are atomic_long_t */
-#define _DEVINC_ATOMIC_ATOMIC(net, statname, idev, field)              \
-({                                                                     \
-       struct inet6_dev *_idev = (idev);                               \
-       if (likely(_idev != NULL))                                      \
-               SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, 
(field)); \
-       SNMP_INC_STATS_ATOMIC_LONG((net)->mib.statname##_statistics, (field));\
-})
-
-#define _DEVADD(net, statname, mod, idev, field, val)                  \
-({                                                                     \
-       struct inet6_dev *_idev = (idev);                               \
-       if (likely(_idev != NULL))                                      \
-               mod##SNMP_ADD_STATS((_idev)->stats.statname, (field), (val)); \
-       mod##SNMP_ADD_STATS((net)->mib.statname##_statistics, (field), (val));\
-})
-
-#define _DEVUPD(net, statname, mod, idev, field, val)                  \
-({                                                                     \
-       struct inet6_dev *_idev = (idev);                               \
-       if (likely(_idev != NULL))                                      \
-               mod##SNMP_UPD_PO_STATS((_idev)->stats.statname, field, (val)); \
-       mod##SNMP_UPD_PO_STATS((net)->mib.statname##_statistics, field, (val));\
-})
-
 /* MIBs */
 
 #define IP6_INC_STATS(net, idev,field)         \
-               _DEVINC(net, ipv6, , idev, field)
+               _DEVINC(net, ipv6, struct inet6_dev, idev, field)
 #define __IP6_INC_STATS(net, idev,field)       \
-               _DEVINC(net, ipv6, __, idev, field)
+               ___DEVINC(net, ipv6, struct inet6_dev, idev, field)
 #define IP6_ADD_STATS(net, idev,field,val)     \
-               _DEVADD(net, ipv6, , idev, field, val)
+               _DEVADD(net, ipv6, struct inet6_dev, idev, field, val)
 #define __IP6_ADD_STATS(net, idev,field,val)   \
-               _DEVADD(net, ipv6, __, idev, field, val)
+               ___DEVADD(net, ipv6, struct inet6_dev, idev, field, val)
 #define IP6_UPD_PO_STATS(net, idev,field,val)   \
-               _DEVUPD(net, ipv6, , idev, field, val)
+               _DEVUPD(net, ipv6, struct inet6_dev, idev, field, val)
 #define __IP6_UPD_PO_STATS(net, idev,field,val)   \
-               _DEVUPD(net, ipv6, __, idev, field, val)
+               ___DEVUPD(net, ipv6, struct inet6_dev, idev, field, val)
 #define ICMP6_INC_STATS(net, idev, field)      \
-               _DEVINCATOMIC(net, icmpv6, , idev, field)
+               _DEVINCATOMIC(net, icmpv6, struct inet6_dev, idev, field)
 #define __ICMP6_INC_STATS(net, idev, field)    \
-               _DEVINCATOMIC(net, icmpv6, __, idev, field)
+               ___DEVINCATOMIC(net, icmpv6, struct inet6_dev, idev, field)
 
 #define ICMP6MSGOUT_INC_STATS(net, idev, field)                \
-       _DEVINC_ATOMIC_ATOMIC(net, icmpv6msg, idev, field +256)
+       _DEVINC_ATOMIC_ATOMIC(net, icmpv6msg, struct inet6_dev, idev, field+256)
 #define ICMP6MSGIN_INC_STATS(net, idev, field) \
-       _DEVINC_ATOMIC_ATOMIC(net, icmpv6msg, idev, field)
+       _DEVINC_ATOMIC_ATOMIC(net, icmpv6msg, struct inet6_dev, idev, field)
 
 struct ip6_ra_chain {
        struct ip6_ra_chain     *next;
diff --git a/include/net/netns/mib.h b/include/net/netns/mib.h
index 830bdf3..798bbc2 100644
--- a/include/net/netns/mib.h
+++ b/include/net/netns/mib.h
@@ -5,6 +5,9 @@
 #include <net/snmp.h>
 
 struct netns_mib {
+#ifdef CONFIG_IP_IFSTATS_TABLE
+       struct proc_dir_entry *proc_net_devsnmp;
+#endif
        DEFINE_SNMP_STAT(struct tcp_mib, tcp_statistics);
        DEFINE_SNMP_STAT(struct ipstats_mib, ip_statistics);
        DEFINE_SNMP_STAT(struct linux_mib, net_statistics);
diff --git a/include/net/snmp.h b/include/net/snmp.h
index c9228ad..56e1f98 100644
--- a/include/net/snmp.h
+++ b/include/net/snmp.h
@@ -61,14 +61,26 @@ struct ipstats_mib {
 
 /* ICMP */
 #define ICMP_MIB_MAX   __ICMP_MIB_MAX
+/* per network ns counters */
 struct icmp_mib {
        unsigned long   mibs[ICMP_MIB_MAX];
 };
 
 #define ICMPMSG_MIB_MAX        __ICMPMSG_MIB_MAX
+/* per network ns counters */
 struct icmpmsg_mib {
        atomic_long_t   mibs[ICMPMSG_MIB_MAX];
 };
+#ifdef CONFIG_IP_IFSTATS_TABLE
+/* per device counters, (shared on all cpus) */
+struct icmp_mib_device {
+       atomic_long_t   mibs[ICMP_MIB_MAX];
+};
+/* per device counters, (shared on all cpus) */
+struct icmpmsg_mib_device {
+       atomic_long_t   mibs[ICMPMSG_MIB_MAX];
+};
+#endif
 
 /* ICMP6 (IPv6-ICMP) */
 #define ICMP6_MIB_MAX  __ICMP6_MIB_MAX
@@ -198,4 +210,87 @@ struct linux_xfrm_mib {
 #define __SNMP_UPD_PO_STATS64(mib, basefield, addend) __SNMP_UPD_PO_STATS(mib, 
basefield, addend)
 #endif
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+/* Macros for enabling per device statistics */
+#define _DEVINC(net, statname, type, idev, field)                      \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               SNMP_INC_STATS((_idev)->stats.statname, (field));       \
+       SNMP_INC_STATS((net)->mib.statname##_statistics, (field));      \
+})
+#define ___DEVINC(net, statname, type, idev, field)                    \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               __SNMP_INC_STATS((_idev)->stats.statname, (field));     \
+       __SNMP_INC_STATS((net)->mib.statname##_statistics, (field));    \
+})
+
+/* per device counters are atomic_long_t */
+#define _DEVINCATOMIC(net, statname, type, idev, field)        \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, 
(field));      \
+       SNMP_INC_STATS((net)->mib.statname##_statistics, (field));      \
+})
+#define ___DEVINCATOMIC(net, statname, type, idev, field)      \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, 
(field));      \
+       __SNMP_INC_STATS((net)->mib.statname##_statistics, (field));    \
+})
+
+/* per device and per net counters are atomic_long_t */
+#define _DEVINC_ATOMIC_ATOMIC(net, statname, type, idev, field)        \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, 
(field));      \
+       SNMP_INC_STATS_ATOMIC_LONG((net)->mib.statname##_statistics, (field));  
\
+})
+
+#define _DEVADD(net, statname, type, idev, field, val) \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               SNMP_ADD_STATS((_idev)->stats.statname, (field), (val));        
\
+       SNMP_ADD_STATS((net)->mib.statname##_statistics, (field), (val));       
\
+})
+#define ___DEVADD(net, statname, type, idev, field, val)       \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               __SNMP_ADD_STATS((_idev)->stats.statname, (field), (val));      
\
+       __SNMP_ADD_STATS((net)->mib.statname##_statistics, (field), (val));     
\
+})
+
+#define _DEVUPD(net, statname, type, idev, field, val) \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               SNMP_UPD_PO_STATS((_idev)->stats.statname, field, (val));       
\
+       SNMP_UPD_PO_STATS((net)->mib.statname##_statistics, field, (val));      
\
+})
+#define ___DEVUPD(net, statname, type, idev, field, val)       \
+({     \
+       __typeof__(type) *_idev = (idev);       \
+       if (likely(_idev))      \
+               __SNMP_UPD_PO_STATS((_idev)->stats.statname, field, (val));     
\
+       __SNMP_UPD_PO_STATS((net)->mib.statname##_statistics, field, (val));    
\
+})
+#else
+#define _DEVINC(net, statname, type, idev, field) 
SNMP_INC_STATS((net)->mib.statname##_statistics, (field))
+#define ___DEVINC(net, statname, type, idev, field) 
__SNMP_INC_STATS((net)->mib.statname##_statistics, (field))
+#define _DEVINCATOMIC(net, statname, type, idev, field) 
SNMP_INC_STATS((net)->mib.statname##_statistics, (field))
+#define ___DEVINCATOMIC(net, statname, type, idev, field) 
__SNMP_INC_STATS((net)->mib.statname##_statistics, (field))
+#define _DEVINC_ATOMIC_ATOMIC(net, statname, type, idev, field)        
SNMP_INC_STATS_ATOMIC_LONG((net)->mib.statname##_statistics, (field))
+#define _DEVADD(net, statname, type, idev, field, val) 
SNMP_ADD_STATS((net)->mib.statname##_statistics, (field), (val))
+#define ___DEVADD(net, statname, type, idev, field, val)       
__SNMP_ADD_STATS((net)->mib.statname##_statistics, (field), (val))
+#define _DEVUPD(net, statname, type, idev, field, val) 
SNMP_UPD_PO_STATS((net)->mib.statname##_statistics, field, (val))
+#define ___DEVUPD(net, statname, type, idev, field, val)       
__SNMP_UPD_PO_STATS((net)->mib.statname##_statistics, field, (val))
+#endif
+
 #endif
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 9b16eaf..f9576b7 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -218,13 +218,13 @@ static int br_validate_ipv4(struct net *net, struct 
sk_buff *skb)
 
        len = ntohs(iph->tot_len);
        if (skb->len < len) {
-               __IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS);
+               __IP_INC_STATS(net, skb->dev, IPSTATS_MIB_INTRUNCATEDPKTS);
                goto drop;
        } else if (len < (iph->ihl*4))
                goto inhdr_error;
 
        if (pskb_trim_rcsum(skb, len)) {
-               __IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
+               __IP_INC_STATS(net, skb->dev, IPSTATS_MIB_INDISCARDS);
                goto drop;
        }
 
@@ -237,9 +237,9 @@ static int br_validate_ipv4(struct net *net, struct sk_buff 
*skb)
        return 0;
 
 csum_error:
-       __IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS);
+       __IP_INC_STATS(net, skb->dev, IPSTATS_MIB_CSUMERRORS);
 inhdr_error:
-       __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
+       __IP_INC_STATS(net, skb->dev, IPSTATS_MIB_INHDRERRORS);
 drop:
        return -1;
 }
@@ -691,7 +691,7 @@ br_nf_ip_fragment(struct net *net, struct sock *sk, struct 
sk_buff *skb,
        if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
                     (IPCB(skb)->frag_max_size &&
                      IPCB(skb)->frag_max_size > mtu))) {
-               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, skb_dst(skb)->dev, IPSTATS_MIB_FRAGFAILS);
                kfree_skb(skb);
                return -EMSGSIZE;
        }
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index e65fcb4..11e0d4c 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -258,7 +258,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
                                       iph->saddr, ntohs(dh->dccph_sport),
                                       inet_iif(skb), 0);
        if (!sk) {
-               __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+               __ICMP_INC_STATS(net, skb->dev, ICMP_MIB_INERRORS);
                return;
        }
 
@@ -468,7 +468,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, 
struct sock *sk,
        security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
        rt = ip_route_output_flow(net, &fl4, sk);
        if (IS_ERR(rt)) {
-               IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
+               IP_INC_STATS(net, NULL, IPSTATS_MIB_OUTNOROUTES);
                return NULL;
        }
 
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 80dad30..6470b95 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -52,6 +52,14 @@ config IP_ADVANCED_ROUTER
 
          If unsure, say N here.
 
+config IP_IFSTATS_TABLE
+       def_bool n
+       depends on IP_ADVANCED_ROUTER
+       prompt "IP: interface statistics"
+       help
+         This option enables per interface statistics for IPv4. Refer to
+         RFC 4293.
+
 config IP_FIB_TRIE_STATS
        bool "FIB TRIE statistics"
        depends on IP_ADVANCED_ROUTER
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index eaed036..c99da7a 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1938,6 +1938,11 @@ static int __init inet_init(void)
                inet_register_protosw(q);
 
        /*
+        *      Init proc fs before initializing devinet
+        */
+       ipv4_proc_init();
+
+       /*
         *      Set the ARP module up
         */
 
@@ -1984,8 +1989,6 @@ static int __init inet_init(void)
        if (init_ipv4_mibs())
                pr_crit("%s: Cannot init ipv4 mibs\n", __func__);
 
-       ipv4_proc_init();
-
        ipfrag_init();
 
        dev_add_pack(&ip_packet_type);
diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c
index f915abf..7acaadb 100644
--- a/net/ipv4/datagram.c
+++ b/net/ipv4/datagram.c
@@ -55,7 +55,7 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr 
*uaddr, int addr_len
        if (IS_ERR(rt)) {
                err = PTR_ERR(rt);
                if (err == -ENETUNREACH)
-                       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+                       IP_INC_STATS(sock_net(sk), NULL, 
IPSTATS_MIB_OUTNOROUTES);
                goto out;
        }
 
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 40f0017..7600e1e 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -218,6 +218,49 @@ static void inet_free_ifa(struct in_ifaddr *ifa)
        call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
 }
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+static int snmp_alloc_dev(struct in_device *idev)
+{
+       int i;
+
+       idev->stats.ip = alloc_percpu(struct ipstats_mib);
+       if (!idev->stats.ip)
+               goto err_ip;
+
+       for_each_possible_cpu(i) {
+               struct ipstats_mib *addrconf_stats;
+
+               addrconf_stats = per_cpu_ptr(idev->stats.ip, i);
+               u64_stats_init(&addrconf_stats->syncp);
+       }
+
+       idev->stats.icmpdev = kzalloc(sizeof(*idev->stats.icmpdev),
+                                     GFP_KERNEL);
+       if (!idev->stats.icmpdev)
+               goto err_icmp;
+       idev->stats.icmpmsgdev = kzalloc(sizeof(*idev->stats.icmpmsgdev),
+                                        GFP_KERNEL);
+       if (!idev->stats.icmpmsgdev)
+               goto err_icmpmsg;
+
+       return 0;
+
+err_icmpmsg:
+       kfree(idev->stats.icmpdev);
+err_icmp:
+       free_percpu(idev->stats.ip);
+err_ip:
+       return -ENOMEM;
+}
+
+static void snmp_free_dev(struct in_device *idev)
+{
+       kfree(idev->stats.icmpmsgdev);
+       kfree(idev->stats.icmpdev);
+       free_percpu(idev->stats.ip);
+}
+#endif
+
 void in_dev_finish_destroy(struct in_device *idev)
 {
        struct net_device *dev = idev->dev;
@@ -229,10 +272,14 @@ void in_dev_finish_destroy(struct in_device *idev)
        pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
 #endif
        dev_put(dev);
-       if (!idev->dead)
+       if (!idev->dead) {
                pr_err("Freeing alive in_device %p\n", idev);
-       else
+       } else {
+#ifdef CONFIG_IP_IFSTATS_TABLE
+               snmp_free_dev(idev);
+#endif
                kfree(idev);
+       }
 }
 EXPORT_SYMBOL(in_dev_finish_destroy);
 
@@ -257,6 +304,25 @@ static struct in_device *inetdev_init(struct net_device 
*dev)
                dev_disable_lro(dev);
        /* Reference in_dev->dev */
        dev_hold(dev);
+#ifdef CONFIG_IP_IFSTATS_TABLE
+       err = snmp_alloc_dev(in_dev);
+       if (err < 0) {
+               netdev_crit(dev,
+                           "%s(): cannot allocate memory for statistics; 
dev=%s err=%d.\n",
+                           __func__, dev->name, err);
+               neigh_parms_release(&arp_tbl, in_dev->arp_parms);
+               dev_put(dev);
+               kfree(in_dev);
+               return NULL;
+       }
+
+       err = snmp_register_dev(in_dev);
+       if (err < 0)
+               netdev_warn(dev,
+                           "%s(): cannot create /proc/net/dev_snmp/%s 
err=%d\n",
+                           __func__, dev->name, err);
+#endif
+
        /* Account for reference dev->ip_ptr (below) */
        refcount_set(&in_dev->refcnt, 1);
 
@@ -306,6 +372,9 @@ static void inetdev_destroy(struct in_device *in_dev)
        }
 
        RCU_INIT_POINTER(dev->ip_ptr, NULL);
+#ifdef CONFIG_IP_IFSTATS_TABLE
+       snmp_unregister_dev(in_dev);
+#endif
 
        devinet_sysctl_unregister(in_dev);
        neigh_parms_release(&arp_tbl, in_dev->arp_parms);
@@ -1529,8 +1598,20 @@ static int inetdev_event(struct notifier_block *this, 
unsigned long event,
                 */
                inetdev_changename(dev, in_dev);
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+               snmp_unregister_dev(in_dev);
+#endif
                devinet_sysctl_unregister(in_dev);
                devinet_sysctl_register(in_dev);
+#ifdef CONFIG_IP_IFSTATS_TABLE
+               {
+                       int err = snmp_register_dev(in_dev);
+
+                       if (err)
+                               return notifier_from_errno(err);
+               }
+#endif
+               /* CONFIG_EXTREME: End */
                break;
        }
 out:
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 1617604..4d5c092 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -338,10 +338,10 @@ static bool icmpv4_xrlim_allow(struct net *net, struct 
rtable *rt,
 /*
  *     Maintain the counters used in the SNMP statistics for outgoing ICMP
  */
-void icmp_out_count(struct net *net, unsigned char type)
+void icmp_out_count(struct net_device *dev, unsigned char type)
 {
-       ICMPMSGOUT_INC_STATS(net, type);
-       ICMP_INC_STATS(net, ICMP_MIB_OUTMSGS);
+       ICMPMSGOUT_INC_STATS(dev_net(dev), dev, type);
+       ICMP_INC_STATS(dev_net(dev), dev, ICMP_MIB_OUTMSGS);
 }
 
 /*
@@ -370,13 +370,14 @@ static void icmp_push_reply(struct icmp_bxm *icmp_param,
 {
        struct sock *sk;
        struct sk_buff *skb;
+       struct net_device *dev = (*rt)->dst.dev;
 
-       sk = icmp_sk(dev_net((*rt)->dst.dev));
+       sk = icmp_sk(dev_net(dev));
        if (ip_append_data(sk, fl4, icmp_glue_bits, icmp_param,
                           icmp_param->data_len+icmp_param->head_len,
                           icmp_param->head_len,
                           ipc, rt, MSG_DONTWAIT) < 0) {
-               __ICMP_INC_STATS(sock_net(sk), ICMP_MIB_OUTERRORS);
+               __ICMP_INC_STATS(sock_net(sk), dev, ICMP_MIB_OUTERRORS);
                ip_flush_pending_frames(sk);
        } else if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) {
                struct icmphdr *icmph = icmp_hdr(skb);
@@ -760,7 +761,7 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 
info)
         * avoid additional coding at protocol handlers.
         */
        if (!pskb_may_pull(skb, iph->ihl * 4 + 8)) {
-               __ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS);
+               __ICMP_INC_STATS(dev_net(skb->dev), skb->dev, 
ICMP_MIB_INERRORS);
                return;
        }
 
@@ -792,8 +793,9 @@ static bool icmp_unreach(struct sk_buff *skb)
        struct icmphdr *icmph;
        struct net *net;
        u32 info = 0;
+       struct net_device *dev = skb_dst(skb)->dev;
 
-       net = dev_net(skb_dst(skb)->dev);
+       net = dev_net(dev);
 
        /*
         *      Incomplete header ?
@@ -852,7 +854,7 @@ static bool icmp_unreach(struct sk_buff *skb)
                info = ntohl(icmph->un.gateway) >> 24;
                break;
        case ICMP_TIME_EXCEEDED:
-               __ICMP_INC_STATS(net, ICMP_MIB_INTIMEEXCDS);
+               __ICMP_INC_STATS(net, dev, ICMP_MIB_INTIMEEXCDS);
                if (icmph->code == ICMP_EXC_FRAGTIME)
                        goto out;
                break;
@@ -890,7 +892,7 @@ static bool icmp_unreach(struct sk_buff *skb)
 out:
        return true;
 out_err:
-       __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+       __ICMP_INC_STATS(net, dev, ICMP_MIB_INERRORS);
        return false;
 }
 
@@ -902,7 +904,7 @@ static bool icmp_unreach(struct sk_buff *skb)
 static bool icmp_redirect(struct sk_buff *skb)
 {
        if (skb->len < sizeof(struct iphdr)) {
-               __ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS);
+               __ICMP_INC_STATS(dev_net(skb->dev), skb->dev, 
ICMP_MIB_INERRORS);
                return false;
        }
 
@@ -982,7 +984,7 @@ static bool icmp_timestamp(struct sk_buff *skb)
        return true;
 
 out_err:
-       __ICMP_INC_STATS(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
+       __ICMP_INC_STATS(dev_net(skb_dst(skb)->dev), skb_dst(skb)->dev, 
ICMP_MIB_INERRORS);
        return false;
 }
 
@@ -1022,7 +1024,7 @@ int icmp_rcv(struct sk_buff *skb)
                skb_set_network_header(skb, nh);
        }
 
-       __ICMP_INC_STATS(net, ICMP_MIB_INMSGS);
+       __ICMP_INC_STATS(net, skb->dev, ICMP_MIB_INMSGS);
 
        if (skb_checksum_simple_validate(skb))
                goto csum_error;
@@ -1032,7 +1034,7 @@ int icmp_rcv(struct sk_buff *skb)
 
        icmph = icmp_hdr(skb);
 
-       ICMPMSGIN_INC_STATS(net, icmph->type);
+       ICMPMSGIN_INC_STATS(net, skb->dev, icmph->type);
        /*
         *      18 is the highest 'known' ICMP type. Anything else is a mystery
         *
@@ -1078,9 +1080,9 @@ int icmp_rcv(struct sk_buff *skb)
        kfree_skb(skb);
        return NET_RX_DROP;
 csum_error:
-       __ICMP_INC_STATS(net, ICMP_MIB_CSUMERRORS);
+       __ICMP_INC_STATS(net, skb->dev, ICMP_MIB_CSUMERRORS);
 error:
-       __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+       __ICMP_INC_STATS(net, skb->dev, ICMP_MIB_INERRORS);
        goto drop;
 }
 
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 881ac6d..30afff4 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -539,6 +539,7 @@ struct dst_entry *inet_csk_route_req(const struct sock *sk,
        struct net *net = read_pnet(&ireq->ireq_net);
        struct ip_options_rcu *opt;
        struct rtable *rt;
+       struct net_device *dev = NULL;
 
        opt = ireq_opt_deref(ireq);
 
@@ -557,9 +558,10 @@ struct dst_entry *inet_csk_route_req(const struct sock *sk,
        return &rt->dst;
 
 route_err:
+       dev = rt->dst.dev;
        ip_rt_put(rt);
 no_route:
-       __IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_OUTNOROUTES);
        return NULL;
 }
 EXPORT_SYMBOL_GPL(inet_csk_route_req);
@@ -574,6 +576,7 @@ struct dst_entry *inet_csk_route_child_sock(const struct 
sock *sk,
        struct ip_options_rcu *opt;
        struct flowi4 *fl4;
        struct rtable *rt;
+       struct net_device *dev = NULL;
 
        opt = rcu_dereference(ireq->ireq_opt);
        fl4 = &newinet->cork.fl.u.ip4;
@@ -593,9 +596,10 @@ struct dst_entry *inet_csk_route_child_sock(const struct 
sock *sk,
        return &rt->dst;
 
 route_err:
+       dev = rt->dst.dev;
        ip_rt_put(rt);
 no_route:
-       __IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_OUTNOROUTES);
        return NULL;
 }
 EXPORT_SYMBOL_GPL(inet_csk_route_child_sock);
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
index b54b948..bb9be11 100644
--- a/net/ipv4/ip_forward.c
+++ b/net/ipv4/ip_forward.c
@@ -66,8 +66,8 @@ static int ip_forward_finish(struct net *net, struct sock 
*sk, struct sk_buff *s
 {
        struct ip_options *opt  = &(IPCB(skb)->opt);
 
-       __IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
-       __IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len);
+       __IP_INC_STATS(net, skb_dst(skb)->dev, IPSTATS_MIB_OUTFORWDATAGRAMS);
+       __IP_ADD_STATS(net, skb_dst(skb)->dev, IPSTATS_MIB_OUTOCTETS, skb->len);
 
        if (unlikely(opt->optlen))
                ip_forward_options(skb);
@@ -121,7 +121,7 @@ int ip_forward(struct sk_buff *skb)
        IPCB(skb)->flags |= IPSKB_FORWARDED;
        mtu = ip_dst_mtu_maybe_forward(&rt->dst, true);
        if (ip_exceeds_mtu(skb, mtu)) {
-               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, rt->dst.dev, IPSTATS_MIB_FRAGFAILS);
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
                          htonl(mtu));
                goto drop;
@@ -158,7 +158,7 @@ int ip_forward(struct sk_buff *skb)
 
 too_many_hops:
        /* Tell the sender its packet died... */
-       __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
+       __IP_INC_STATS(net, skb_dst(skb)->dev, IPSTATS_MIB_INHDRERRORS);
        icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
 drop:
        kfree_skb(skb);
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 994fa70..40aadd4 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -136,6 +136,7 @@ static void ip_expire(struct timer_list *t)
 {
        struct inet_frag_queue *frag = from_timer(frag, t, timer);
        const struct iphdr *iph;
+       struct net_device *dev;
        struct sk_buff *head;
        struct net *net;
        struct ipq *qp;
@@ -151,16 +152,17 @@ static void ip_expire(struct timer_list *t)
                goto out;
 
        ipq_kill(qp);
-       __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
+       dev = dev_get_by_index_rcu(net, qp->iif);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_REASMFAILS);
 
        head = qp->q.fragments;
 
-       __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_REASMTIMEOUT);
 
        if (!(qp->q.flags & INET_FRAG_FIRST_IN) || !head)
                goto out;
 
-       head->dev = dev_get_by_index_rcu(net, qp->iif);
+       head->dev = dev;
        if (!head->dev)
                goto out;
 
@@ -237,7 +239,9 @@ static int ip_frag_too_far(struct ipq *qp)
                struct net *net;
 
                net = container_of(qp->q.net, struct net, ipv4.frags);
-               __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
+               rcu_read_lock();
+               __IP_INC_STATS(net, dev_get_by_index_rcu(net, qp->iif), 
IPSTATS_MIB_REASMFAILS);
+               rcu_read_unlock();
        }
 
        return rc;
@@ -582,7 +586,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff 
*prev,
 
        ip_send_check(iph);
 
-       __IP_INC_STATS(net, IPSTATS_MIB_REASMOKS);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_REASMOKS);
        qp->q.fragments = NULL;
        qp->q.fragments_tail = NULL;
        return 0;
@@ -594,7 +598,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff 
*prev,
 out_oversize:
        net_info_ratelimited("Oversized IP packet from %pI4\n", 
&qp->q.key.v4.saddr);
 out_fail:
-       __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_REASMFAILS);
        return err;
 }
 
@@ -605,7 +609,7 @@ int ip_defrag(struct net *net, struct sk_buff *skb, u32 
user)
        int vif = l3mdev_master_ifindex_rcu(dev);
        struct ipq *qp;
 
-       __IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_REASMREQDS);
        skb_orphan(skb);
 
        /* Lookup (or create) queue header */
@@ -622,7 +626,7 @@ int ip_defrag(struct net *net, struct sk_buff *skb, u32 
user)
                return ret;
        }
 
-       __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_REASMFAILS);
        kfree_skb(skb);
        return -ENOMEM;
 }
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index 7582713..1143fe2 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -197,6 +197,7 @@ static int ip_local_deliver_finish(struct net *net, struct 
sock *sk, struct sk_b
                int protocol = ip_hdr(skb)->protocol;
                const struct net_protocol *ipprot;
                int raw;
+               struct net_device *dev = skb->dev;
 
        resubmit:
                raw = raw_local_deliver(skb, protocol);
@@ -217,17 +218,17 @@ static int ip_local_deliver_finish(struct net *net, 
struct sock *sk, struct sk_b
                                protocol = -ret;
                                goto resubmit;
                        }
-                       __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
+                       __IP_INC_STATS(net, dev, IPSTATS_MIB_INDELIVERS);
                } else {
                        if (!raw) {
                                if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, 
skb)) {
-                                       __IP_INC_STATS(net, 
IPSTATS_MIB_INUNKNOWNPROTOS);
+                                       __IP_INC_STATS(net, dev, 
IPSTATS_MIB_INUNKNOWNPROTOS);
                                        icmp_send(skb, ICMP_DEST_UNREACH,
                                                  ICMP_PROT_UNREACH, 0);
                                }
                                kfree_skb(skb);
                        } else {
-                               __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
+                               __IP_INC_STATS(net, dev, 
IPSTATS_MIB_INDELIVERS);
                                consume_skb(skb);
                        }
                }
@@ -272,7 +273,7 @@ static inline bool ip_rcv_options(struct sk_buff *skb)
                                              --ANK (980813)
        */
        if (skb_cow(skb, skb_headroom(skb))) {
-               __IP_INC_STATS(dev_net(dev), IPSTATS_MIB_INDISCARDS);
+               __IP_INC_STATS(dev_net(dev), dev, IPSTATS_MIB_INDISCARDS);
                goto drop;
        }
 
@@ -281,7 +282,7 @@ static inline bool ip_rcv_options(struct sk_buff *skb)
        opt->optlen = iph->ihl*4 - sizeof(struct iphdr);
 
        if (ip_options_compile(dev_net(dev), opt, skb)) {
-               __IP_INC_STATS(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
+               __IP_INC_STATS(dev_net(dev), dev, IPSTATS_MIB_INHDRERRORS);
                goto drop;
        }
 
@@ -366,9 +367,9 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, 
struct sk_buff *skb)
 
        rt = skb_rtable(skb);
        if (rt->rt_type == RTN_MULTICAST) {
-               __IP_UPD_PO_STATS(net, IPSTATS_MIB_INMCAST, skb->len);
+               __IP_UPD_PO_STATS(net, dev, IPSTATS_MIB_INMCAST, skb->len);
        } else if (rt->rt_type == RTN_BROADCAST) {
-               __IP_UPD_PO_STATS(net, IPSTATS_MIB_INBCAST, skb->len);
+               __IP_UPD_PO_STATS(net, dev, IPSTATS_MIB_INBCAST, skb->len);
        } else if (skb->pkt_type == PACKET_BROADCAST ||
                   skb->pkt_type == PACKET_MULTICAST) {
                struct in_device *in_dev = __in_dev_get_rcu(dev);
@@ -422,11 +423,11 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, 
struct packet_type *pt,
 
 
        net = dev_net(dev);
-       __IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len);
+       __IP_UPD_PO_STATS(net, dev, IPSTATS_MIB_IN, skb->len);
 
        skb = skb_share_check(skb, GFP_ATOMIC);
        if (!skb) {
-               __IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
+               __IP_INC_STATS(net, dev, IPSTATS_MIB_INDISCARDS);
                goto out;
        }
 
@@ -452,7 +453,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, 
struct packet_type *pt,
        BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + 
INET_ECN_ECT_1);
        BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + 
INET_ECN_ECT_0);
        BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE);
-       __IP_ADD_STATS(net,
+       __IP_ADD_STATS(net, dev,
                       IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
                       max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
 
@@ -466,7 +467,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, 
struct packet_type *pt,
 
        len = ntohs(iph->tot_len);
        if (skb->len < len) {
-               __IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS);
+               __IP_INC_STATS(net, dev, IPSTATS_MIB_INTRUNCATEDPKTS);
                goto drop;
        } else if (len < (iph->ihl*4))
                goto inhdr_error;
@@ -476,7 +477,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, 
struct packet_type *pt,
         * Note this now means skb->len holds ntohs(iph->tot_len).
         */
        if (pskb_trim_rcsum(skb, len)) {
-               __IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
+               __IP_INC_STATS(net, dev, IPSTATS_MIB_INDISCARDS);
                goto drop;
        }
 
@@ -494,9 +495,9 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, 
struct packet_type *pt,
                       ip_rcv_finish);
 
 csum_error:
-       __IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_CSUMERRORS);
 inhdr_error:
-       __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
+       __IP_INC_STATS(net, dev, IPSTATS_MIB_INHDRERRORS);
 drop:
        kfree_skb(skb);
 out:
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 94cacae..a0cf93d 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -191,9 +191,9 @@ static int ip_finish_output2(struct net *net, struct sock 
*sk, struct sk_buff *s
        u32 nexthop;
 
        if (rt->rt_type == RTN_MULTICAST) {
-               IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);
+               IP_UPD_PO_STATS(net, dev, IPSTATS_MIB_OUTMCAST, skb->len);
        } else if (rt->rt_type == RTN_BROADCAST)
-               IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len);
+               IP_UPD_PO_STATS(net, dev, IPSTATS_MIB_OUTBCAST, skb->len);
 
        /* Be paranoid, rather than too clever. */
        if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
@@ -339,7 +339,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct 
sk_buff *skb)
        /*
         *      If the indicated interface is up and running, send the packet.
         */
-       IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
+       IP_UPD_PO_STATS(net, dev, IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
@@ -397,7 +397,7 @@ int ip_output(struct net *net, struct sock *sk, struct 
sk_buff *skb)
 {
        struct net_device *dev = skb_dst(skb)->dev;
 
-       IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
+       IP_UPD_PO_STATS(net, dev, IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
@@ -507,7 +507,7 @@ int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, 
struct flowi *fl)
 
 no_route:
        rcu_read_unlock();
-       IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
+       IP_INC_STATS(net, NULL, IPSTATS_MIB_OUTNOROUTES);
        kfree_skb(skb);
        return -EHOSTUNREACH;
 }
@@ -548,7 +548,7 @@ static int ip_fragment(struct net *net, struct sock *sk, 
struct sk_buff *skb,
        if (unlikely(!skb->ignore_df ||
                     (IPCB(skb)->frag_max_size &&
                      IPCB(skb)->frag_max_size > mtu))) {
-               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, skb->dev, IPSTATS_MIB_FRAGFAILS);
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
                          htonl(mtu));
                kfree_skb(skb);
@@ -575,8 +575,11 @@ int ip_do_fragment(struct net *net, struct sock *sk, 
struct sk_buff *skb,
        int offset;
        __be16 not_last_frag;
        struct rtable *rt = skb_rtable(skb);
+       struct net_device *dev = rt->dst.dev;
        int err = 0;
 
+       dev_hold(dev);
+
        /* for offloaded checksums cleanup checksum before fragmentation */
        if (skb->ip_summed == CHECKSUM_PARTIAL &&
            (err = skb_checksum_help(skb)))
@@ -675,7 +678,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct 
sk_buff *skb,
                        err = output(net, sk, skb);
 
                        if (!err)
-                               IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
+                               IP_INC_STATS(net, dev, IPSTATS_MIB_FRAGCREATES);
                        if (err || !frag)
                                break;
 
@@ -685,7 +688,8 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct 
sk_buff *skb,
                }
 
                if (err == 0) {
-                       IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS);
+                       IP_INC_STATS(net, dev, IPSTATS_MIB_FRAGOKS);
+                       dev_put(dev);
                        return 0;
                }
 
@@ -694,7 +698,8 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct 
sk_buff *skb,
                        kfree_skb(frag);
                        frag = skb;
                }
-               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, dev, IPSTATS_MIB_FRAGFAILS);
+               dev_put(dev);
                return err;
 
 slow_path_clean:
@@ -811,15 +816,17 @@ int ip_do_fragment(struct net *net, struct sock *sk, 
struct sk_buff *skb,
                if (err)
                        goto fail;
 
-               IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
+               IP_INC_STATS(net, dev, IPSTATS_MIB_FRAGCREATES);
        }
        consume_skb(skb);
-       IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS);
+       IP_INC_STATS(net, dev, IPSTATS_MIB_FRAGOKS);
+       dev_put(dev);
        return err;
 
 fail:
        kfree_skb(skb);
-       IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
+       IP_INC_STATS(net, dev, IPSTATS_MIB_FRAGFAILS);
+       dev_put(dev);
        return err;
 }
 EXPORT_SYMBOL(ip_do_fragment);
@@ -1097,7 +1104,7 @@ static int __ip_append_data(struct sock *sk,
        err = -EFAULT;
 error:
        cork->length -= length;
-       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS);
+       IP_INC_STATS(sock_net(sk), rt->dst.dev, IPSTATS_MIB_OUTDISCARDS);
        refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc);
        return err;
 }
@@ -1305,7 +1312,7 @@ ssize_t   ip_append_page(struct sock *sk, struct flowi4 
*fl4, struct page *page,
 
 error:
        cork->length -= size;
-       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS);
+       IP_INC_STATS(sock_net(sk), rt->dst.dev, IPSTATS_MIB_OUTDISCARDS);
        return err;
 }
 
@@ -1406,7 +1413,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
        skb_dst_set(skb, &rt->dst);
 
        if (iph->protocol == IPPROTO_ICMP)
-               icmp_out_count(net, ((struct icmphdr *)
+               icmp_out_count(skb_dst(skb)->dev, ((struct icmphdr *)
                        skb_transport_header(skb))->type);
 
        ip_cork_release(cork);
@@ -1417,13 +1424,14 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
 int ip_send_skb(struct net *net, struct sk_buff *skb)
 {
        int err;
+       struct net_device *dev = skb_dst(skb)->dev;
 
        err = ip_local_out(net, skb->sk, skb);
        if (err) {
                if (err > 0)
                        err = net_xmit_errno(err);
                if (err)
-                       IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
+                       IP_INC_STATS(net, dev, IPSTATS_MIB_OUTDISCARDS);
        }
 
        return err;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 2fb4de3..67ec987 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1778,8 +1778,8 @@ static inline int ipmr_forward_finish(struct net *net, 
struct sock *sk,
 {
        struct ip_options *opt = &(IPCB(skb)->opt);
 
-       IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
-       IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len);
+       IP_INC_STATS(net, skb_dst(skb)->dev, IPSTATS_MIB_OUTFORWDATAGRAMS);
+       IP_ADD_STATS(net, skb_dst(skb)->dev, IPSTATS_MIB_OUTOCTETS, skb->len);
 
        if (unlikely(opt->optlen))
                ip_forward_options(skb);
@@ -1862,7 +1862,7 @@ static void ipmr_queue_xmit(struct net *net, struct 
mr_table *mrt,
                 * allow to send ICMP, so that packets will disappear
                 * to blackhole.
                 */
-               IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(net, dev, IPSTATS_MIB_FRAGFAILS);
                ip_rt_put(rt);
                goto out_free;
        }
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 05e47d7..1b4db11 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -712,6 +712,7 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr 
*msg, size_t len)
        __be32 saddr, daddr, faddr;
        u8  tos;
        int err;
+       struct net_device *dev;
 
        pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
 
@@ -805,7 +806,7 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr 
*msg, size_t len)
                err = PTR_ERR(rt);
                rt = NULL;
                if (err == -ENETUNREACH)
-                       IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
+                       IP_INC_STATS(net, NULL, IPSTATS_MIB_OUTNOROUTES);
                goto out;
        }
 
@@ -841,13 +842,17 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr 
*msg, size_t len)
        release_sock(sk);
 
 out:
+       dev = rt->dst.dev;
+       dev_hold(dev);
        ip_rt_put(rt);
        if (free)
                kfree(ipc.opt);
        if (!err) {
-               icmp_out_count(sock_net(sk), user_icmph.type);
+               icmp_out_count(dev, user_icmph.type);
+               dev_put(dev);
                return len;
        }
+       dev_put(dev);
        return err;
 
 do_confirm:
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index a058de6..c0c822f 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -134,6 +134,17 @@ static const struct snmp_mib snmp4_ipextstats_list[] = {
        SNMP_MIB_SENTINEL
 };
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+static const struct snmp_mib snmp4_icmp_list[] = {
+       SNMP_MIB_ITEM("InMsgs", ICMP_MIB_INMSGS),
+       SNMP_MIB_ITEM("InErrors", ICMP_MIB_INERRORS),
+       SNMP_MIB_ITEM("OutMsgs", ICMP_MIB_OUTMSGS),
+       SNMP_MIB_ITEM("OutErrors", ICMP_MIB_OUTERRORS),
+       SNMP_MIB_ITEM("InCsumErrors", ICMP_MIB_CSUMERRORS),
+       SNMP_MIB_SENTINEL
+};
+#endif
+
 static const struct {
        const char *name;
        int index;
@@ -473,6 +484,109 @@ static const struct file_operations snmp_seq_fops = {
 };
 
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+static void snmp_seq_show_item(struct seq_file *seq, void __percpu **pcpumib,
+                              atomic_long_t *smib,
+                              const struct snmp_mib *itemlist,
+                              char *prefix)
+{
+       char name[32];
+       int i;
+       unsigned long val;
+
+       for (i = 0; itemlist[i].name; i++) {
+               val = pcpumib ?
+                       snmp_fold_field64(pcpumib, itemlist[i].entry,
+                                         offsetof(struct ipstats_mib, syncp)) :
+                       atomic_long_read(smib + itemlist[i].entry);
+               snprintf(name, sizeof(name), "%s%s",
+                        prefix, itemlist[i].name);
+               seq_printf(seq, "%-32s\t%lu\n", name, val);
+       }
+}
+
+static void snmp_seq_show_icmpmsg(struct seq_file *seq, atomic_long_t *smib)
+{
+       char name[32];
+       int i;
+       unsigned long val;
+
+       for (i = 0; i < ICMPMSG_MIB_MAX; i++) {
+               val = atomic_long_read(smib + i);
+               if (val) {
+                       snprintf(name, sizeof(name), "Icmp%sType%u",
+                                i & 0x100 ? "Out" : "In", i & 0xff);
+                       seq_printf(seq, "%-32s\t%lu\n", name, val);
+               }
+       }
+}
+
+static int snmp_dev_seq_show(struct seq_file *seq, void *v)
+{
+       struct in_device *idev = (struct in_device *)seq->private;
+
+       seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
+
+       BUILD_BUG_ON(offsetof(struct ipstats_mib, mibs) != 0);
+
+       snmp_seq_show_item(seq, (void __percpu **)idev->stats.ip, NULL,
+                          snmp4_ipstats_list, "Ip");
+       snmp_seq_show_item(seq, (void __percpu **)idev->stats.ip, NULL,
+                          snmp4_ipextstats_list, "Ip");
+       snmp_seq_show_item(seq, NULL, idev->stats.icmpdev->mibs,
+                          snmp4_icmp_list, "Icmp");
+       snmp_seq_show_icmpmsg(seq, idev->stats.icmpmsgdev->mibs);
+       return 0;
+}
+
+static int snmp_dev_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, snmp_dev_seq_show, PDE_DATA(inode));
+}
+
+static const struct file_operations snmp_dev_seq_fops = {
+       .owner   = THIS_MODULE,
+       .open    = snmp_dev_seq_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = single_release,
+};
+
+int snmp_register_dev(struct in_device *idev)
+{
+       struct proc_dir_entry *p;
+       struct net *net;
+
+       if (!idev || !idev->dev)
+               return -EINVAL;
+
+       net = dev_net(idev->dev);
+       if (!net->mib.proc_net_devsnmp)
+               return -ENOENT;
+
+       p = proc_create_data(idev->dev->name, 0444,
+                            net->mib.proc_net_devsnmp,
+                            &snmp_dev_seq_fops, idev);
+       if (!p)
+               return -ENOMEM;
+
+       idev->stats.proc_dir_entry = p;
+       return 0;
+}
+
+int snmp_unregister_dev(struct in_device *idev)
+{
+       struct net *net = dev_net(idev->dev);
+
+       if (!net->mib.proc_net_devsnmp)
+               return -ENOENT;
+       if (!idev->stats.proc_dir_entry)
+               return -EINVAL;
+       proc_remove(idev->stats.proc_dir_entry);
+       idev->stats.proc_dir_entry = NULL;
+       return 0;
+}
+#endif
 
 /*
  *     Output /proc/net/netstat
@@ -528,9 +642,18 @@ static __net_init int ip_proc_init_net(struct net *net)
                goto out_netstat;
        if (!proc_create("snmp", 0444, net->proc_net, &snmp_seq_fops))
                goto out_snmp;
+#ifdef CONFIG_IP_IFSTATS_TABLE
+       net->mib.proc_net_devsnmp = proc_mkdir("dev_snmp", net->proc_net);
+       if (!net->mib.proc_net_devsnmp)
+               goto out_dev_snmp;
+#endif
 
        return 0;
 
+#ifdef CONFIG_IP_IFSTATS_TABLE
+out_dev_snmp:
+       remove_proc_entry("snmp", net->proc_net);
+#endif
 out_snmp:
        remove_proc_entry("netstat", net->proc_net);
 out_netstat:
@@ -544,6 +667,9 @@ static __net_exit void ip_proc_exit_net(struct net *net)
        remove_proc_entry("snmp", net->proc_net);
        remove_proc_entry("netstat", net->proc_net);
        remove_proc_entry("sockstat", net->proc_net);
+#ifdef CONFIG_IP_IFSTATS_TABLE
+       remove_proc_entry("dev_snmp", net->proc_net);
+#endif
 }
 
 static __net_initdata struct pernet_operations ip_proc_ops = {
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 1b4d335..f39f87a 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -425,7 +425,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 
*fl4,
                skb->transport_header += iphlen;
                if (iph->protocol == IPPROTO_ICMP &&
                    length >= iphlen + sizeof(struct icmphdr))
-                       icmp_out_count(net, ((struct icmphdr *)
+                       icmp_out_count(rt->dst.dev, ((struct icmphdr *)
                                skb_transport_header(skb))->type);
        }
 
@@ -442,7 +442,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 
*fl4,
 error_free:
        kfree_skb(skb);
 error:
-       IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
+       IP_INC_STATS(net, rt->dst.dev, IPSTATS_MIB_OUTDISCARDS);
        if (err == -ENOBUFS && !inet->recverr)
                err = 0;
        return err;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 8322e47..5115f0d3 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -961,11 +961,11 @@ static int ip_error(struct sk_buff *skb)
        if (!IN_DEV_FORWARD(in_dev)) {
                switch (rt->dst.error) {
                case EHOSTUNREACH:
-                       __IP_INC_STATS(net, IPSTATS_MIB_INADDRERRORS);
+                       __IP_INC_STATS(net, rt->dst.dev, 
IPSTATS_MIB_INADDRERRORS);
                        break;
 
                case ENETUNREACH:
-                       __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES);
+                       __IP_INC_STATS(net, rt->dst.dev, 
IPSTATS_MIB_INNOROUTES);
                        break;
                }
                goto out;
@@ -980,7 +980,7 @@ static int ip_error(struct sk_buff *skb)
                break;
        case ENETUNREACH:
                code = ICMP_NET_UNREACH;
-               __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES);
+               __IP_INC_STATS(net, rt->dst.dev, IPSTATS_MIB_INNOROUTES);
                break;
        case EACCES:
                code = ICMP_PKT_FILTERED;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index f70586b..df1b989 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -194,7 +194,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, 
int addr_len)
        if (IS_ERR(rt)) {
                err = PTR_ERR(rt);
                if (err == -ENETUNREACH)
-                       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+                       IP_INC_STATS(sock_net(sk), NULL, 
IPSTATS_MIB_OUTNOROUTES);
                return err;
        }
 
@@ -402,7 +402,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                                       th->dest, iph->saddr, ntohs(th->source),
                                       inet_iif(icmp_skb), 0);
        if (!sk) {
-               __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+               __ICMP_INC_STATS(net, icmp_skb->dev, ICMP_MIB_INERRORS);
                return;
        }
        if (sk->sk_state == TCP_TIME_WAIT) {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 24b5c59..a8b6567 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -609,7 +609,7 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct 
udp_table *udptable)
                               iph->saddr, uh->source, skb->dev->ifindex, 0,
                               udptable, NULL);
        if (!sk) {
-               __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+               __ICMP_INC_STATS(net, skb->dev, ICMP_MIB_INERRORS);
                return; /* No socket for error */
        }
 
@@ -1008,7 +1008,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, 
size_t len)
                        err = PTR_ERR(rt);
                        rt = NULL;
                        if (err == -ENETUNREACH)
-                               IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
+                               IP_INC_STATS(net, NULL, 
IPSTATS_MIB_OUTNOROUTES);
                        goto out;
                }
 
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index a9c05b2..b52b2e3 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -381,7 +381,7 @@ static int l2tp_ip_backlog_recv(struct sock *sk, struct 
sk_buff *skb)
        return 0;
 
 drop:
-       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS);
+       IP_INC_STATS(sock_net(sk), skb->dev, IPSTATS_MIB_INDISCARDS);
        kfree_skb(skb);
        return 0;
 }
@@ -504,7 +504,7 @@ static int l2tp_ip_sendmsg(struct sock *sk, struct msghdr 
*msg, size_t len)
 
 no_route:
        rcu_read_unlock();
-       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+       IP_INC_STATS(sock_net(sk), NULL, IPSTATS_MIB_OUTNOROUTES);
        kfree_skb(skb);
        rc = -EHOSTUNREACH;
        goto out;
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index 9573691..cf2172d 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -462,7 +462,7 @@ static int l2tp_ip6_backlog_recv(struct sock *sk, struct 
sk_buff *skb)
        return 0;
 
 drop:
-       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS);
+       IP_INC_STATS(sock_net(sk), skb->dev, IPSTATS_MIB_INDISCARDS);
        kfree_skb(skb);
        return -1;
 }
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 7a4de6d..c629239 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -141,7 +141,7 @@ void mpls_stats_inc_outucastpkts(struct net_device *dev,
                                           tx_packets,
                                           tx_bytes);
        } else if (skb->protocol == htons(ETH_P_IP)) {
-               IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
+               IP_UPD_PO_STATS(dev_net(dev), dev, IPSTATS_MIB_OUT, skb->len);
 #if IS_ENABLED(CONFIG_IPV6)
        } else if (skb->protocol == htons(ETH_P_IPV6)) {
                struct inet6_dev *in6dev = __in6_dev_get(dev);
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 4527921..32bd3af 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -286,7 +286,7 @@ static inline bool decrement_ttl(struct netns_ipvs *ipvs,
        {
                if (ip_hdr(skb)->ttl <= 1) {
                        /* Tell the sender its packet died... */
-                       __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
+                       __IP_INC_STATS(net, skb_dst(skb)->dev, 
IPSTATS_MIB_INHDRERRORS);
                        icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
                        return false;
                }
diff --git a/net/sctp/input.c b/net/sctp/input.c
index ba8a6e6..fef625a 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -596,7 +596,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
        skb->network_header = saveip;
        skb->transport_header = savesctp;
        if (!sk) {
-               __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+               __ICMP_INC_STATS(net, skb->dev, ICMP_MIB_INERRORS);
                return;
        }
        /* Warning:  The sock lock is held.  Remember to call
diff --git a/net/sctp/output.c b/net/sctp/output.c
index d6e1c90..a2a27fa 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -600,7 +600,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t 
gfp)
        /* drop packet if no dst */
        dst = dst_clone(tp->dst);
        if (!dst) {
-               IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+               IP_INC_STATS(sock_net(sk), NULL, IPSTATS_MIB_OUTNOROUTES);
                kfree_skb(head);
                goto out;
        }
-- 
2.7.4

Reply via email to