[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1524268363-5846-1-git-send-email-ssuryaextr@gmail.com>
Date: Fri, 20 Apr 2018 19:52:43 -0400
From: Stephen Suryaputra <ssuryaextr@...il.com>
To: netdev@...r.kernel.org, ja@....bg
Cc: Stephen Suryaputra <ssuryaextr@...il.com>
Subject: [PATCH net-next, v2] Per interface IPv4 stats (CONFIG_IP_IFSTATS_TABLE)
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.
Changes from v1:
- Count input statistics in the input device (per Julian Anastasov).
- Changes so that the existing per interface IPv6 stats aren't affected
when the option isn't enabled.
- Restore the order of calling ipv4_proc_init().
Signed-off-by: Stephen Suryaputra <ssuryaextr@...il.com>
---
drivers/net/vrf.c | 2 +-
include/linux/inetdevice.h | 22 ++++++
include/net/icmp.h | 6 +-
include/net/ip.h | 144 ++++++++++++++++++++++++++++++++++++++--
include/net/ipv6.h | 30 ++++-----
include/net/netns/mib.h | 3 +
include/net/snmp.h | 12 ++++
net/bridge/br_netfilter_hooks.c | 10 +--
net/dccp/ipv4.c | 4 +-
net/ipv4/Kconfig | 8 +++
net/ipv4/datagram.c | 2 +-
net/ipv4/devinet.c | 84 ++++++++++++++++++++++-
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 | 31 +++++----
net/ipv4/ip_output.c | 42 +++++++-----
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 | 6 +-
net/sctp/input.c | 2 +-
net/sctp/output.c | 2 +-
31 files changed, 528 insertions(+), 117 deletions(-)
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 90b5f39..2b17ead 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -593,7 +593,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..70612ca 100644
--- a/include/net/icmp.h
+++ b/include/net/icmp.h
@@ -29,10 +29,6 @@ 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)
struct dst_entry;
struct net_proto_family;
@@ -43,6 +39,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 dc4a2d6..ada33da 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,134 @@ 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 _DEVINC(net, statname, mod, idev, field) \
+({ \
+ struct in_device *_idev = (idev); \
+ if (likely(_idev)) \
+ mod##SNMP_INC_STATS((_idev)->stats.statname, (field)); \
+ mod##SNMP_INC_STATS((net)->mib.statname##_statistics, (field)); \
+})
+
+/* per device counters are atomic_long_t */
+#define _DEVINCATOMIC(net, statname, mod, idev, field) \
+({ \
+ struct in_device *_idev = (idev); \
+ if (likely(_idev)) \
+ 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 in_device *_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, mod, idev, field, val) \
+({ \
+ struct in_device *_idev = (idev); \
+ if (likely(_idev)) \
+ 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 in_device *_idev = (idev); \
+ if (likely(_idev)) \
+ mod##SNMP_UPD_PO_STATS((_idev)->stats.statname, field, (val)); \
+ mod##SNMP_UPD_PO_STATS((net)->mib.statname##_statistics, field, (val)); \
+})
+
+#define IP_INC_STATS(net, dev, field) \
+ ({ \
+ rcu_read_lock(); \
+ _DEVINC(net, ip, , __in_dev_get_rcu_safely(dev), field); \
+ rcu_read_unlock(); \
+ })
+
+#define __IP_INC_STATS(net, dev, field) \
+ ({ \
+ rcu_read_lock(); \
+ _DEVINC(net, ip, __, __in_dev_get_rcu_safely(dev), field); \
+ rcu_read_unlock(); \
+ })
+
+#define IP_ADD_STATS(net, dev, field, val) \
+ ({ \
+ rcu_read_lock(); \
+ _DEVADD(net, ip, , __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, __, __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, , __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, __, __in_dev_get_rcu_safely(dev), field, val); \
+ rcu_read_unlock(); \
+ })
+
+#define ICMP_INC_STATS(net, dev, field) \
+ ({ \
+ rcu_read_lock(); \
+ _DEVINCATOMIC(net, icmp, , __in_dev_get_rcu_safely(dev), field); \
+ rcu_read_unlock(); \
+ })
+
+#define __ICMP_INC_STATS(net, dev, field) \
+ ({ \
+ rcu_read_lock(); \
+ _DEVINCATOMIC(net, icmp, __, \
+ __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, \
+ __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, \
+ __in_dev_get_rcu_safely(dev), field); \
+ 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)
+
+#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
#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)
@@ -663,4 +786,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 68b167d..a26ffc9 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -163,7 +163,7 @@ struct frag_hdr {
extern int sysctl_mld_max_msf;
extern int sysctl_mld_qrv;
-#define _DEVINC(net, statname, mod, idev, field) \
+#define _DEVINC6(net, statname, mod, idev, field) \
({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
@@ -172,7 +172,7 @@ extern int sysctl_mld_qrv;
})
/* per device counters are atomic_long_t */
-#define _DEVINCATOMIC(net, statname, mod, idev, field) \
+#define _DEVINCATOMIC6(net, statname, mod, idev, field) \
({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
@@ -181,7 +181,7 @@ extern int sysctl_mld_qrv;
})
/* per device and per net counters are atomic_long_t */
-#define _DEVINC_ATOMIC_ATOMIC(net, statname, idev, field) \
+#define _DEVINC_ATOMIC_ATOMIC6(net, statname, idev, field) \
({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
@@ -189,7 +189,7 @@ extern int sysctl_mld_qrv;
SNMP_INC_STATS_ATOMIC_LONG((net)->mib.statname##_statistics, (field));\
})
-#define _DEVADD(net, statname, mod, idev, field, val) \
+#define _DEVADD6(net, statname, mod, idev, field, val) \
({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
@@ -197,7 +197,7 @@ extern int sysctl_mld_qrv;
mod##SNMP_ADD_STATS((net)->mib.statname##_statistics, (field), (val));\
})
-#define _DEVUPD(net, statname, mod, idev, field, val) \
+#define _DEVUPD6(net, statname, mod, idev, field, val) \
({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
@@ -208,26 +208,26 @@ extern int sysctl_mld_qrv;
/* MIBs */
#define IP6_INC_STATS(net, idev,field) \
- _DEVINC(net, ipv6, , idev, field)
+ _DEVINC6(net, ipv6, , idev, field)
#define __IP6_INC_STATS(net, idev,field) \
- _DEVINC(net, ipv6, __, idev, field)
+ _DEVINC6(net, ipv6, __, idev, field)
#define IP6_ADD_STATS(net, idev,field,val) \
- _DEVADD(net, ipv6, , idev, field, val)
+ _DEVADD6(net, ipv6, , idev, field, val)
#define __IP6_ADD_STATS(net, idev,field,val) \
- _DEVADD(net, ipv6, __, idev, field, val)
+ _DEVADD6(net, ipv6, __, idev, field, val)
#define IP6_UPD_PO_STATS(net, idev,field,val) \
- _DEVUPD(net, ipv6, , idev, field, val)
+ _DEVUPD6(net, ipv6, , idev, field, val)
#define __IP6_UPD_PO_STATS(net, idev,field,val) \
- _DEVUPD(net, ipv6, __, idev, field, val)
+ _DEVUPD6(net, ipv6, __, idev, field, val)
#define ICMP6_INC_STATS(net, idev, field) \
- _DEVINCATOMIC(net, icmpv6, , idev, field)
+ _DEVINCATOMIC6(net, icmpv6, , idev, field)
#define __ICMP6_INC_STATS(net, idev, field) \
- _DEVINCATOMIC(net, icmpv6, __, idev, field)
+ _DEVINCATOMIC6(net, icmpv6, __, idev, field)
#define ICMP6MSGOUT_INC_STATS(net, idev, field) \
- _DEVINC_ATOMIC_ATOMIC(net, icmpv6msg, idev, field +256)
+ _DEVINC_ATOMIC_ATOMIC6(net, icmpv6msg, idev, field +256)
#define ICMP6MSGIN_INC_STATS(net, idev, field) \
- _DEVINC_ATOMIC_ATOMIC(net, icmpv6msg, idev, field)
+ _DEVINC_ATOMIC_ATOMIC6(net, icmpv6msg, 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..b7c99a3 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
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 b08feb2..40e4c00 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/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..c2809f3 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,19 @@ 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
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..c84e177 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->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 8e9528e..6324254 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..2d3e073 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -197,6 +197,9 @@ 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;
+#ifdef CONFIG_IP_IFSTATS_TABLE
+ struct net_device *dev = skb->dev;
+#endif
resubmit:
raw = raw_local_deliver(skb, protocol);
@@ -217,17 +220,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 +275,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 +284,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 +369,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 +425,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 +455,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 +469,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 +479,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 +497,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 4c11b81..54194a9 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);
@@ -1098,7 +1105,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;
}
@@ -1306,7 +1313,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;
}
@@ -1407,7 +1414,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);
@@ -1418,13 +1425,16 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
int ip_send_skb(struct net *net, struct sk_buff *skb)
{
int err;
+#ifdef CONFIG_IP_IFSTATS_TABLE
+ struct net_device *dev = skb_dst(skb)->dev;
+#endif
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 ccb25d8..8fc87b4 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -960,11 +960,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, dev, IPSTATS_MIB_INADDRERRORS);
break;
case ENETUNREACH:
- __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES);
+ __IP_INC_STATS(net, dev, IPSTATS_MIB_INNOROUTES);
break;
}
goto out;
@@ -979,7 +979,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, 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 ba0a0fd..9875463 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -287,7 +287,11 @@ 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);
+ /* at LOCAL_IN the stat is incremented for the input
+ * dev, at LOCAL_OUT the global is incremented since
+ * skb->dev is NULL
+ */
+ __IP_INC_STATS(net, 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 690d855..8ae6dc0 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -608,7 +608,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
Powered by blists - more mailing lists