From patchwork Wed Apr 11 02:55:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Suryaputra X-Patchwork-Id: 897256 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="qIjIVaIq"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40LpY42LVxz9s0x for ; Thu, 12 Apr 2018 01:55:52 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753108AbeDKPzs (ORCPT ); Wed, 11 Apr 2018 11:55:48 -0400 Received: from mail-oi0-f65.google.com ([209.85.218.65]:37502 "EHLO mail-oi0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752798AbeDKPzo (ORCPT ); Wed, 11 Apr 2018 11:55:44 -0400 Received: by mail-oi0-f65.google.com with SMTP id f63-v6so2190559oic.4 for ; Wed, 11 Apr 2018 08:55:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=VpTN3I7T93HUGqPyjlSijwaQ/VU0AKc0W8PoALux3/k=; b=qIjIVaIqKcd7SyKZWgRMSkU5oKinKXKt3Pv8SL8k0WFvGAoLyYt03dgI4UHGf+Oupa pMOjPSJe/z/k3rMmhEL1+8wFoQnXVDo6iK67Ayp6iX53v6/yDeam0t9Ev/E/He4a5rYv JPKc3VMzsFj2B9RK6ZjleZ31jnFdWMuzeEbVFSXYCFkxK6FjsoXcOiBQ4i16uyBycnl0 qv9LfKQ79ZWtKRjjoRU36BiOWlCyXDUFtPEJ3rTSoLA26SrvV++BEeZ9j7cubJhXGhDf +bgAOkXUqJ/6ujZeROjJ5aAKpF5agd9J6AQNvfUE1dJwFBeE19ZsZYasaTe4dN6AMbA+ FGGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=VpTN3I7T93HUGqPyjlSijwaQ/VU0AKc0W8PoALux3/k=; b=XXPkQVobPOK1upKMk7A4yb1ejojC3biaPqQ5ykXcIyKm6QAK8GiGKY19qL8Gro6Zdw FeodsV52Quii7+ZArEpQhGqE1wwuCbhr+lTHqMos0y8NTRo+sHSYAI2agQvLgDgIwMDv R+IugAmr4BDqkZdztXnhuGmSR2XNBHHVE/0NVOBEZbEym4AYpL6IhJU6DeHbEw/lLiYX KAKQm3ZOC2FHzCoAOa0cPTfSV2BcvluO04xRm5wEueBA9N8ouHUUwSfB45Z6gNtwvnIV xrA8ZEfx+s3794LZNxtBKQr3ZgPEvE209FiZKYvK2IetQOnif2B+TnGNR5jq8+ucYexO VPBw== X-Gm-Message-State: ALQs6tBeq4L49oD9uJVCeO89bsCs4JkfZdSeG1wjknEtcEvovVFijuAY /Ld41zuASprdItTGwHUE+BY7 X-Google-Smtp-Source: AIpwx49Yhf6A5uC5OuWUMPqetScgZ4RQ8BLLItSkmwXIa+rPGBnGJRa3uF7zWyOVWjmnpYxMeMLTBA== X-Received: by 2002:aca:d644:: with SMTP id n65-v6mr3122532oig.189.1523462142779; Wed, 11 Apr 2018 08:55:42 -0700 (PDT) Received: from localhost.localdomain ([12.38.14.9]) by smtp.gmail.com with ESMTPSA id k26-v6sm947834otd.49.2018.04.11.08.55.41 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 11 Apr 2018 08:55:41 -0700 (PDT) From: Stephen Suryaputra To: netdev@vger.kernel.org Cc: Stephen Suryaputra Subject: [PATCH net-next] Per interface IPv4 stats (CONFIG_IP_IFSTATS_TABLE) Date: Tue, 10 Apr 2018 22:55:35 -0400 Message-Id: <1523415335-17154-1-git-send-email-ssuryaextr@gmail.com> X-Mailer: git-send-email 2.7.4 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 #include #include +#include #include #include @@ -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 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; }