From patchwork Fri Apr 20 23:52:43 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Suryaputra X-Patchwork-Id: 902252 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="FkcSBppH"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40SY2T0wFXz9s1l for ; Sat, 21 Apr 2018 10:07:45 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752844AbeDUAHn (ORCPT ); Fri, 20 Apr 2018 20:07:43 -0400 Received: from mail-ot0-f195.google.com ([74.125.82.195]:43208 "EHLO mail-ot0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752497AbeDUAHk (ORCPT ); Fri, 20 Apr 2018 20:07:40 -0400 Received: by mail-ot0-f195.google.com with SMTP id d9-v6so11404275oth.10 for ; Fri, 20 Apr 2018 17:07:40 -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=TUPufdQYTJzK4h+3yfwZN9Bm4pcqMMDbQvOZkPft5N4=; b=FkcSBppHDM0Uy5iNSAttz6pgNWWV9mUQT7OLczg27v6udOCHTDZwwgSgfzDMRlm/Z7 xi8hjTBJWbX1JOrNOQ9SUNfb6azOtaa5Wdl0fggtALFBVXGVCgVcQJ8ic+RHFA8079Vx 9ZmEZP1HBC/e6yrFO3t4O6saLYHdttWHPS0O2M0yfkbx4173Zfw6FynxJNO41nHxcuok Sr4F1xoDgBC/EzkbTcux95E5y6Gh84ZlwdWb+ummmhER4exTbQMx+4DPC6XpfV5f6Sm8 A4Zzadr4Qqp6X/aLrvMBNuyai8Bn9YdJM6MKeXzJXbVejJw+7HpHxq8C5MZBnIkLMckq oYDw== 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=TUPufdQYTJzK4h+3yfwZN9Bm4pcqMMDbQvOZkPft5N4=; b=iIr7vi6KpBv+ui95IXsH5A8O0FrOlAm3y6O6XIbrHhuSOUuv5mJiuMh7ofp3prN9yb e73dKfeJLLfK0xMj1OWW06P8vS7sXuy7aTV3g1o7Ofj8Hk16nJPIIIrju8UONJCJiAmZ LQtOY8gXHO7/ZITqx4SVO48OqhTj1hS8b/jxwsQUo3L9fpZN1TRulsksND7TOd24JfV4 ewCBFu5uYpZk5wTEziIwYTX/WD8kQq9YYxbpcSnDEriCvFcqry2/TITP11Fli+JIIa2X vmg9CjdVB9mtyJtA5eaw8pSezd4Anrid1l5sC43tzVO3CZDJAtv05zS0asNzPwYgJKHM tGwQ== X-Gm-Message-State: ALQs6tBDUzOu23QROu1359tGmL67Ln0GvFBLDhAhZLlkIDBy2kCkt8se rW6Y5KCiVxRGIiYSFl1NdAvU X-Google-Smtp-Source: AIpwx49p6Bu6vkR972+zJ+eQIf1ny9aD7QY2mwR12y6jMhTJ/Exd7S1dpoLexlbSGkBWLbzDjpBcsA== X-Received: by 2002:a9d:2456:: with SMTP id p80-v6mr5581395ota.231.1524269258780; Fri, 20 Apr 2018 17:07:38 -0700 (PDT) Received: from localhost.localdomain (107-138-158-165.lightspeed.rlghnc.sbcglobal.net. [107.138.158.165]) by smtp.gmail.com with ESMTPSA id b81-v6sm3808335oia.50.2018.04.20.17.07.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 20 Apr 2018 17:07:37 -0700 (PDT) From: Stephen Suryaputra To: netdev@vger.kernel.org, ja@ssi.bg Cc: Stephen Suryaputra Subject: [PATCH net-next, v2] Per interface IPv4 stats (CONFIG_IP_IFSTATS_TABLE) Date: Fri, 20 Apr 2018 19:52:43 -0400 Message-Id: <1524268363-5846-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. 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 --- 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 #include #include +#include #include #include @@ -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 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; }