| 1 | |
| 2 | |
| 3 | * INET An implementation of the TCP/IP protocol suite for the LINUX
|
| 4 | * operating system. INET is implemented using the BSD Socket
|
| 5 | * interface as the means of communication with the user level.
|
| 6 | *
|
| 7 | * This file implements the various access functions for the
|
| 8 | * PROC file system. This is very similar to the IPv4 version,
|
| 9 | * except it reports the sockets in the INET6 address family.
|
| 10 | *
|
| 11 | * Authors: David S. Miller ([email protected])
|
| 12 | * YOSHIFUJI Hideaki <[email protected]>
|
| 13 |
|
| 14 | #include <linux/socket.h>
|
| 15 | #include <linux/net.h>
|
| 16 | #include <linux/ipv6.h>
|
| 17 | #include <linux/proc_fs.h>
|
| 18 | #include <linux/seq_file.h>
|
| 19 | #include <linux/stddef.h>
|
| 20 | #include <linux/export.h>
|
| 21 | #include <net/net_namespace.h>
|
| 22 | #include <net/ip.h>
|
| 23 | #include <net/sock.h>
|
| 24 | #include <net/tcp.h>
|
| 25 | #include <net/udp.h>
|
| 26 | #include <net/transp_v6.h>
|
| 27 | #include <net/ipv6.h>
|
| 28 |
|
| 29 | #define MAX4(a, b, c, d) \
|
| 30 | MAX_T(u32, MAX_T(u32, a, b), MAX_T(u32, c, d))
|
| 31 | #define SNMP_MIB_MAX MAX4(UDP_MIB_MAX, TCP_MIB_MAX, \
|
| 32 | IPSTATS_MIB_MAX, ICMP_MIB_MAX)
|
| 33 |
|
| 34 | static int sockstat6_seq_show(struct seq_file *seq, void *v)
|
| 35 | {
|
| 36 | struct net *net = seq->private;
|
| 37 |
|
| 38 | seq_printf(seq, "TCP6: inuse %d\n",
|
| 39 | sock_prot_inuse_get(net, &tcpv6_prot));
|
| 40 | seq_printf(seq, "UDP6: inuse %d\n",
|
| 41 | sock_prot_inuse_get(net, &udpv6_prot));
|
| 42 | seq_printf(seq, "RAW6: inuse %d\n",
|
| 43 | sock_prot_inuse_get(net, &rawv6_prot));
|
| 44 | seq_printf(seq, "FRAG6: inuse %u memory %lu\n",
|
| 45 | atomic_read(&net->ipv6.fqdir->rhashtable.nelems),
|
| 46 | frag_mem_limit(net->ipv6.fqdir));
|
| 47 | return 0;
|
| 48 | }
|
| 49 |
|
| 50 | static const struct snmp_mib snmp6_ipstats_list[] = {
|
| 51 |
|
| 52 | SNMP_MIB_ITEM("Ip6InReceives", IPSTATS_MIB_INPKTS),
|
| 53 | SNMP_MIB_ITEM("Ip6InHdrErrors", IPSTATS_MIB_INHDRERRORS),
|
| 54 | SNMP_MIB_ITEM("Ip6InTooBigErrors", IPSTATS_MIB_INTOOBIGERRORS),
|
| 55 | SNMP_MIB_ITEM("Ip6InNoRoutes", IPSTATS_MIB_INNOROUTES),
|
| 56 | SNMP_MIB_ITEM("Ip6InAddrErrors", IPSTATS_MIB_INADDRERRORS),
|
| 57 | SNMP_MIB_ITEM("Ip6InUnknownProtos", IPSTATS_MIB_INUNKNOWNPROTOS),
|
| 58 | SNMP_MIB_ITEM("Ip6InTruncatedPkts", IPSTATS_MIB_INTRUNCATEDPKTS),
|
| 59 | SNMP_MIB_ITEM("Ip6InDiscards", IPSTATS_MIB_INDISCARDS),
|
| 60 | SNMP_MIB_ITEM("Ip6InDelivers", IPSTATS_MIB_INDELIVERS),
|
| 61 | SNMP_MIB_ITEM("Ip6OutForwDatagrams", IPSTATS_MIB_OUTFORWDATAGRAMS),
|
| 62 | SNMP_MIB_ITEM("Ip6OutRequests", IPSTATS_MIB_OUTREQUESTS),
|
| 63 | SNMP_MIB_ITEM("Ip6OutDiscards", IPSTATS_MIB_OUTDISCARDS),
|
| 64 | SNMP_MIB_ITEM("Ip6OutNoRoutes", IPSTATS_MIB_OUTNOROUTES),
|
| 65 | SNMP_MIB_ITEM("Ip6ReasmTimeout", IPSTATS_MIB_REASMTIMEOUT),
|
| 66 | SNMP_MIB_ITEM("Ip6ReasmReqds", IPSTATS_MIB_REASMREQDS),
|
| 67 | SNMP_MIB_ITEM("Ip6ReasmOKs", IPSTATS_MIB_REASMOKS),
|
| 68 | SNMP_MIB_ITEM("Ip6ReasmFails", IPSTATS_MIB_REASMFAILS),
|
| 69 | SNMP_MIB_ITEM("Ip6FragOKs", IPSTATS_MIB_FRAGOKS),
|
| 70 | SNMP_MIB_ITEM("Ip6FragFails", IPSTATS_MIB_FRAGFAILS),
|
| 71 | SNMP_MIB_ITEM("Ip6FragCreates", IPSTATS_MIB_FRAGCREATES),
|
| 72 | SNMP_MIB_ITEM("Ip6InMcastPkts", IPSTATS_MIB_INMCASTPKTS),
|
| 73 | SNMP_MIB_ITEM("Ip6OutMcastPkts", IPSTATS_MIB_OUTMCASTPKTS),
|
| 74 | SNMP_MIB_ITEM("Ip6InOctets", IPSTATS_MIB_INOCTETS),
|
| 75 | SNMP_MIB_ITEM("Ip6OutOctets", IPSTATS_MIB_OUTOCTETS),
|
| 76 | SNMP_MIB_ITEM("Ip6InMcastOctets", IPSTATS_MIB_INMCASTOCTETS),
|
| 77 | SNMP_MIB_ITEM("Ip6OutMcastOctets", IPSTATS_MIB_OUTMCASTOCTETS),
|
| 78 | SNMP_MIB_ITEM("Ip6InBcastOctets", IPSTATS_MIB_INBCASTOCTETS),
|
| 79 | SNMP_MIB_ITEM("Ip6OutBcastOctets", IPSTATS_MIB_OUTBCASTOCTETS),
|
| 80 |
|
| 81 | SNMP_MIB_ITEM("Ip6InNoECTPkts", IPSTATS_MIB_NOECTPKTS),
|
| 82 | SNMP_MIB_ITEM("Ip6InECT1Pkts", IPSTATS_MIB_ECT1PKTS),
|
| 83 | SNMP_MIB_ITEM("Ip6InECT0Pkts", IPSTATS_MIB_ECT0PKTS),
|
| 84 | SNMP_MIB_ITEM("Ip6InCEPkts", IPSTATS_MIB_CEPKTS),
|
| 85 | SNMP_MIB_ITEM("Ip6OutTransmits", IPSTATS_MIB_OUTPKTS),
|
| 86 | };
|
| 87 |
|
| 88 | static const struct snmp_mib snmp6_icmp6_list[] = {
|
| 89 |
|
| 90 | SNMP_MIB_ITEM("Icmp6InMsgs", ICMP6_MIB_INMSGS),
|
| 91 | SNMP_MIB_ITEM("Icmp6InErrors", ICMP6_MIB_INERRORS),
|
| 92 | SNMP_MIB_ITEM("Icmp6OutMsgs", ICMP6_MIB_OUTMSGS),
|
| 93 | SNMP_MIB_ITEM("Icmp6OutErrors", ICMP6_MIB_OUTERRORS),
|
| 94 | SNMP_MIB_ITEM("Icmp6InCsumErrors", ICMP6_MIB_CSUMERRORS),
|
| 95 |
|
| 96 | SNMP_MIB_ITEM("Icmp6OutRateLimitHost", ICMP6_MIB_RATELIMITHOST),
|
| 97 | };
|
| 98 |
|
| 99 | static const struct snmp_mib snmp6_udp6_list[] = {
|
| 100 | SNMP_MIB_ITEM("Udp6InDatagrams", UDP_MIB_INDATAGRAMS),
|
| 101 | SNMP_MIB_ITEM("Udp6NoPorts", UDP_MIB_NOPORTS),
|
| 102 | SNMP_MIB_ITEM("Udp6InErrors", UDP_MIB_INERRORS),
|
| 103 | SNMP_MIB_ITEM("Udp6OutDatagrams", UDP_MIB_OUTDATAGRAMS),
|
| 104 | SNMP_MIB_ITEM("Udp6RcvbufErrors", UDP_MIB_RCVBUFERRORS),
|
| 105 | SNMP_MIB_ITEM("Udp6SndbufErrors", UDP_MIB_SNDBUFERRORS),
|
| 106 | SNMP_MIB_ITEM("Udp6InCsumErrors", UDP_MIB_CSUMERRORS),
|
| 107 | SNMP_MIB_ITEM("Udp6IgnoredMulti", UDP_MIB_IGNOREDMULTI),
|
| 108 | SNMP_MIB_ITEM("Udp6MemErrors", UDP_MIB_MEMERRORS),
|
| 109 | };
|
| 110 |
|
| 111 | static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, atomic_long_t *smib)
|
| 112 | {
|
| 113 | char name[32];
|
| 114 | int i;
|
| 115 |
|
| 116 |
|
| 117 | for (i = 0; i < ICMP6MSG_MIB_MAX; i++) {
|
| 118 | const char *p = NULL;
|
| 119 | int icmptype;
|
| 120 |
|
| 121 | #define CASE(TYP, STR) case TYP: p = STR; break;
|
| 122 |
|
| 123 | icmptype = i & 0xff;
|
| 124 | switch (icmptype) {
|
| 125 |
|
| 126 | CASE(ICMPV6_DEST_UNREACH, "DestUnreachs")
|
| 127 | CASE(ICMPV6_PKT_TOOBIG, "PktTooBigs")
|
| 128 | CASE(ICMPV6_TIME_EXCEED, "TimeExcds")
|
| 129 | CASE(ICMPV6_PARAMPROB, "ParmProblems")
|
| 130 | CASE(ICMPV6_ECHO_REQUEST, "Echos")
|
| 131 | CASE(ICMPV6_ECHO_REPLY, "EchoReplies")
|
| 132 | CASE(ICMPV6_MGM_QUERY, "GroupMembQueries")
|
| 133 | CASE(ICMPV6_MGM_REPORT, "GroupMembResponses")
|
| 134 | CASE(ICMPV6_MGM_REDUCTION, "GroupMembReductions")
|
| 135 | CASE(ICMPV6_MLD2_REPORT, "MLDv2Reports")
|
| 136 | CASE(NDISC_ROUTER_ADVERTISEMENT, "RouterAdvertisements")
|
| 137 | CASE(NDISC_ROUTER_SOLICITATION, "RouterSolicits")
|
| 138 | CASE(NDISC_NEIGHBOUR_ADVERTISEMENT, "NeighborAdvertisements")
|
| 139 | CASE(NDISC_NEIGHBOUR_SOLICITATION, "NeighborSolicits")
|
| 140 | CASE(NDISC_REDIRECT, "Redirects")
|
| 141 | }
|
| 142 | #undef CASE
|
| 143 | if (!p)
|
| 144 | continue;
|
| 145 | snprintf(name, sizeof(name), "Icmp6%s%s",
|
| 146 | i & 0x100 ? "Out" : "In", p);
|
| 147 | seq_printf(seq, "%-32s\t%lu\n", name,
|
| 148 | atomic_long_read(smib + i));
|
| 149 | }
|
| 150 |
|
| 151 |
|
| 152 | for (i = 0; i < ICMP6MSG_MIB_MAX; i++) {
|
| 153 | unsigned long val;
|
| 154 |
|
| 155 | val = atomic_long_read(smib + i);
|
| 156 | if (!val)
|
| 157 | continue;
|
| 158 | snprintf(name, sizeof(name), "Icmp6%sType%u",
|
| 159 | i & 0x100 ? "Out" : "In", i & 0xff);
|
| 160 | seq_printf(seq, "%-32s\t%lu\n", name, val);
|
| 161 | }
|
| 162 | }
|
| 163 |
|
| 164 | |
| 165 | * or shared one (smib != NULL)
|
| 166 |
|
| 167 | static void snmp6_seq_show_item(struct seq_file *seq, void __percpu *pcpumib,
|
| 168 | atomic_long_t *smib,
|
| 169 | const struct snmp_mib *itemlist,
|
| 170 | int cnt)
|
| 171 | {
|
| 172 | unsigned long buff[SNMP_MIB_MAX];
|
| 173 | int i;
|
| 174 |
|
| 175 | if (pcpumib) {
|
| 176 | memset(buff, 0, sizeof(unsigned long) * cnt);
|
| 177 |
|
| 178 | snmp_get_cpu_field_batch_cnt(buff, itemlist, cnt, pcpumib);
|
| 179 | for (i = 0; i < cnt; i++)
|
| 180 | seq_printf(seq, "%-32s\t%lu\n",
|
| 181 | itemlist[i].name, buff[i]);
|
| 182 | } else {
|
| 183 | for (i = 0; i < cnt; i++)
|
| 184 | seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name,
|
| 185 | atomic_long_read(smib + itemlist[i].entry));
|
| 186 | }
|
| 187 | }
|
| 188 |
|
| 189 | static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu *mib,
|
| 190 | const struct snmp_mib *itemlist,
|
| 191 | int cnt, size_t syncpoff)
|
| 192 | {
|
| 193 | u64 buff64[SNMP_MIB_MAX];
|
| 194 | int i;
|
| 195 |
|
| 196 | memset(buff64, 0, sizeof(u64) * cnt);
|
| 197 |
|
| 198 | snmp_get_cpu_field64_batch_cnt(buff64, itemlist, cnt, mib, syncpoff);
|
| 199 | for (i = 0; i < cnt; i++)
|
| 200 | seq_printf(seq, "%-32s\t%llu\n", itemlist[i].name, buff64[i]);
|
| 201 | }
|
| 202 |
|
| 203 | static int snmp6_seq_show(struct seq_file *seq, void *v)
|
| 204 | {
|
| 205 | struct net *net = (struct net *)seq->private;
|
| 206 |
|
| 207 | snmp6_seq_show_item64(seq, net->mib.ipv6_statistics,
|
| 208 | snmp6_ipstats_list,
|
| 209 | ARRAY_SIZE(snmp6_ipstats_list),
|
| 210 | offsetof(struct ipstats_mib, syncp));
|
| 211 | snmp6_seq_show_item(seq, net->mib.icmpv6_statistics,
|
| 212 | NULL, snmp6_icmp6_list,
|
| 213 | ARRAY_SIZE(snmp6_icmp6_list));
|
| 214 | snmp6_seq_show_icmpv6msg(seq, net->mib.icmpv6msg_statistics->mibs);
|
| 215 | snmp6_seq_show_item(seq, net->mib.udp_stats_in6,
|
| 216 | NULL, snmp6_udp6_list,
|
| 217 | ARRAY_SIZE(snmp6_udp6_list));
|
| 218 | return 0;
|
| 219 | }
|
| 220 |
|
| 221 | static int snmp6_dev_seq_show(struct seq_file *seq, void *v)
|
| 222 | {
|
| 223 | struct inet6_dev *idev = (struct inet6_dev *)seq->private;
|
| 224 |
|
| 225 | seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
|
| 226 | snmp6_seq_show_item64(seq, idev->stats.ipv6,
|
| 227 | snmp6_ipstats_list,
|
| 228 | ARRAY_SIZE(snmp6_ipstats_list),
|
| 229 | offsetof(struct ipstats_mib, syncp));
|
| 230 |
|
| 231 |
|
| 232 | snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs,
|
| 233 | snmp6_icmp6_list, ARRAY_SIZE(snmp6_icmp6_list) - 1);
|
| 234 |
|
| 235 | snmp6_seq_show_icmpv6msg(seq, idev->stats.icmpv6msgdev->mibs);
|
| 236 | return 0;
|
| 237 | }
|
| 238 |
|
| 239 | int snmp6_register_dev(struct inet6_dev *idev)
|
| 240 | {
|
| 241 | struct proc_dir_entry *p;
|
| 242 | struct net *net;
|
| 243 |
|
| 244 | if (!idev || !idev->dev)
|
| 245 | return -EINVAL;
|
| 246 |
|
| 247 | net = dev_net(idev->dev);
|
| 248 | if (!net->mib.proc_net_devsnmp6)
|
| 249 | return -ENOENT;
|
| 250 |
|
| 251 | p = proc_create_single_data(idev->dev->name, 0444,
|
| 252 | net->mib.proc_net_devsnmp6, snmp6_dev_seq_show, idev);
|
| 253 | if (!p)
|
| 254 | return -ENOMEM;
|
| 255 |
|
| 256 | idev->stats.proc_dir_entry = p;
|
| 257 | return 0;
|
| 258 | }
|
| 259 |
|
| 260 | int snmp6_unregister_dev(struct inet6_dev *idev)
|
| 261 | {
|
| 262 | struct net *net = dev_net(idev->dev);
|
| 263 | if (!net->mib.proc_net_devsnmp6)
|
| 264 | return -ENOENT;
|
| 265 | if (!idev->stats.proc_dir_entry)
|
| 266 | return -EINVAL;
|
| 267 | proc_remove(idev->stats.proc_dir_entry);
|
| 268 | idev->stats.proc_dir_entry = NULL;
|
| 269 | return 0;
|
| 270 | }
|
| 271 |
|
| 272 | static int __net_init ipv6_proc_init_net(struct net *net)
|
| 273 | {
|
| 274 | if (!proc_create_net_single("sockstat6", 0444, net->proc_net,
|
| 275 | sockstat6_seq_show, NULL))
|
| 276 | return -ENOMEM;
|
| 277 |
|
| 278 | if (!proc_create_net_single("snmp6", 0444, net->proc_net,
|
| 279 | snmp6_seq_show, NULL))
|
| 280 | goto proc_snmp6_fail;
|
| 281 |
|
| 282 | net->mib.proc_net_devsnmp6 = proc_mkdir("dev_snmp6", net->proc_net);
|
| 283 | if (!net->mib.proc_net_devsnmp6)
|
| 284 | goto proc_dev_snmp6_fail;
|
| 285 | return 0;
|
| 286 |
|
| 287 | proc_dev_snmp6_fail:
|
| 288 | remove_proc_entry("snmp6", net->proc_net);
|
| 289 | proc_snmp6_fail:
|
| 290 | remove_proc_entry("sockstat6", net->proc_net);
|
| 291 | return -ENOMEM;
|
| 292 | }
|
| 293 |
|
| 294 | static void __net_exit ipv6_proc_exit_net(struct net *net)
|
| 295 | {
|
| 296 | remove_proc_entry("sockstat6", net->proc_net);
|
| 297 | remove_proc_entry("dev_snmp6", net->proc_net);
|
| 298 | remove_proc_entry("snmp6", net->proc_net);
|
| 299 | }
|
| 300 |
|
| 301 | static struct pernet_operations ipv6_proc_ops = {
|
| 302 | .init = ipv6_proc_init_net,
|
| 303 | .exit = ipv6_proc_exit_net,
|
| 304 | };
|
| 305 |
|
| 306 | int __init ipv6_misc_proc_init(void)
|
| 307 | {
|
| 308 | return register_pernet_subsys(&ipv6_proc_ops);
|
| 309 | }
|
| 310 |
|
| 311 | void ipv6_misc_proc_exit(void)
|
| 312 | {
|
| 313 | unregister_pernet_subsys(&ipv6_proc_ops);
|
| 314 | }
|