| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | 
|---|
| 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 (davem@caip.rutgers.edu) | 
|---|
| 12 | *		YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> | 
|---|
| 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(m: seq, fmt: "TCP6: inuse %d\n", | 
|---|
| 39 | sock_prot_inuse_get(net, proto: &tcpv6_prot)); | 
|---|
| 40 | seq_printf(m: seq, fmt: "UDP6: inuse %d\n", | 
|---|
| 41 | sock_prot_inuse_get(net, proto: &udpv6_prot)); | 
|---|
| 42 | seq_printf(m: seq, fmt: "UDPLITE6: inuse %d\n", | 
|---|
| 43 | sock_prot_inuse_get(net, proto: &udplitev6_prot)); | 
|---|
| 44 | seq_printf(m: seq, fmt: "RAW6: inuse %d\n", | 
|---|
| 45 | sock_prot_inuse_get(net, proto: &rawv6_prot)); | 
|---|
| 46 | seq_printf(m: seq, fmt: "FRAG6: inuse %u memory %lu\n", | 
|---|
| 47 | atomic_read(v: &net->ipv6.fqdir->rhashtable.nelems), | 
|---|
| 48 | frag_mem_limit(fqdir: net->ipv6.fqdir)); | 
|---|
| 49 | return 0; | 
|---|
| 50 | } | 
|---|
| 51 |  | 
|---|
| 52 | static const struct snmp_mib snmp6_ipstats_list[] = { | 
|---|
| 53 | /* ipv6 mib according to RFC 2465 */ | 
|---|
| 54 | SNMP_MIB_ITEM( "Ip6InReceives", IPSTATS_MIB_INPKTS), | 
|---|
| 55 | SNMP_MIB_ITEM( "Ip6InHdrErrors", IPSTATS_MIB_INHDRERRORS), | 
|---|
| 56 | SNMP_MIB_ITEM( "Ip6InTooBigErrors", IPSTATS_MIB_INTOOBIGERRORS), | 
|---|
| 57 | SNMP_MIB_ITEM( "Ip6InNoRoutes", IPSTATS_MIB_INNOROUTES), | 
|---|
| 58 | SNMP_MIB_ITEM( "Ip6InAddrErrors", IPSTATS_MIB_INADDRERRORS), | 
|---|
| 59 | SNMP_MIB_ITEM( "Ip6InUnknownProtos", IPSTATS_MIB_INUNKNOWNPROTOS), | 
|---|
| 60 | SNMP_MIB_ITEM( "Ip6InTruncatedPkts", IPSTATS_MIB_INTRUNCATEDPKTS), | 
|---|
| 61 | SNMP_MIB_ITEM( "Ip6InDiscards", IPSTATS_MIB_INDISCARDS), | 
|---|
| 62 | SNMP_MIB_ITEM( "Ip6InDelivers", IPSTATS_MIB_INDELIVERS), | 
|---|
| 63 | SNMP_MIB_ITEM( "Ip6OutForwDatagrams", IPSTATS_MIB_OUTFORWDATAGRAMS), | 
|---|
| 64 | SNMP_MIB_ITEM( "Ip6OutRequests", IPSTATS_MIB_OUTREQUESTS), | 
|---|
| 65 | SNMP_MIB_ITEM( "Ip6OutDiscards", IPSTATS_MIB_OUTDISCARDS), | 
|---|
| 66 | SNMP_MIB_ITEM( "Ip6OutNoRoutes", IPSTATS_MIB_OUTNOROUTES), | 
|---|
| 67 | SNMP_MIB_ITEM( "Ip6ReasmTimeout", IPSTATS_MIB_REASMTIMEOUT), | 
|---|
| 68 | SNMP_MIB_ITEM( "Ip6ReasmReqds", IPSTATS_MIB_REASMREQDS), | 
|---|
| 69 | SNMP_MIB_ITEM( "Ip6ReasmOKs", IPSTATS_MIB_REASMOKS), | 
|---|
| 70 | SNMP_MIB_ITEM( "Ip6ReasmFails", IPSTATS_MIB_REASMFAILS), | 
|---|
| 71 | SNMP_MIB_ITEM( "Ip6FragOKs", IPSTATS_MIB_FRAGOKS), | 
|---|
| 72 | SNMP_MIB_ITEM( "Ip6FragFails", IPSTATS_MIB_FRAGFAILS), | 
|---|
| 73 | SNMP_MIB_ITEM( "Ip6FragCreates", IPSTATS_MIB_FRAGCREATES), | 
|---|
| 74 | SNMP_MIB_ITEM( "Ip6InMcastPkts", IPSTATS_MIB_INMCASTPKTS), | 
|---|
| 75 | SNMP_MIB_ITEM( "Ip6OutMcastPkts", IPSTATS_MIB_OUTMCASTPKTS), | 
|---|
| 76 | SNMP_MIB_ITEM( "Ip6InOctets", IPSTATS_MIB_INOCTETS), | 
|---|
| 77 | SNMP_MIB_ITEM( "Ip6OutOctets", IPSTATS_MIB_OUTOCTETS), | 
|---|
| 78 | SNMP_MIB_ITEM( "Ip6InMcastOctets", IPSTATS_MIB_INMCASTOCTETS), | 
|---|
| 79 | SNMP_MIB_ITEM( "Ip6OutMcastOctets", IPSTATS_MIB_OUTMCASTOCTETS), | 
|---|
| 80 | SNMP_MIB_ITEM( "Ip6InBcastOctets", IPSTATS_MIB_INBCASTOCTETS), | 
|---|
| 81 | SNMP_MIB_ITEM( "Ip6OutBcastOctets", IPSTATS_MIB_OUTBCASTOCTETS), | 
|---|
| 82 | /* IPSTATS_MIB_CSUMERRORS is not relevant in IPv6 (no checksum) */ | 
|---|
| 83 | SNMP_MIB_ITEM( "Ip6InNoECTPkts", IPSTATS_MIB_NOECTPKTS), | 
|---|
| 84 | SNMP_MIB_ITEM( "Ip6InECT1Pkts", IPSTATS_MIB_ECT1PKTS), | 
|---|
| 85 | SNMP_MIB_ITEM( "Ip6InECT0Pkts", IPSTATS_MIB_ECT0PKTS), | 
|---|
| 86 | SNMP_MIB_ITEM( "Ip6InCEPkts", IPSTATS_MIB_CEPKTS), | 
|---|
| 87 | SNMP_MIB_ITEM( "Ip6OutTransmits", IPSTATS_MIB_OUTPKTS), | 
|---|
| 88 | }; | 
|---|
| 89 |  | 
|---|
| 90 | static const struct snmp_mib snmp6_icmp6_list[] = { | 
|---|
| 91 | /* icmpv6 mib according to RFC 2466 */ | 
|---|
| 92 | SNMP_MIB_ITEM( "Icmp6InMsgs", ICMP6_MIB_INMSGS), | 
|---|
| 93 | SNMP_MIB_ITEM( "Icmp6InErrors", ICMP6_MIB_INERRORS), | 
|---|
| 94 | SNMP_MIB_ITEM( "Icmp6OutMsgs", ICMP6_MIB_OUTMSGS), | 
|---|
| 95 | SNMP_MIB_ITEM( "Icmp6OutErrors", ICMP6_MIB_OUTERRORS), | 
|---|
| 96 | SNMP_MIB_ITEM( "Icmp6InCsumErrors", ICMP6_MIB_CSUMERRORS), | 
|---|
| 97 | /* ICMP6_MIB_RATELIMITHOST needs to be last, see snmp6_dev_seq_show(). */ | 
|---|
| 98 | SNMP_MIB_ITEM( "Icmp6OutRateLimitHost", ICMP6_MIB_RATELIMITHOST), | 
|---|
| 99 | }; | 
|---|
| 100 |  | 
|---|
| 101 | static const struct snmp_mib snmp6_udp6_list[] = { | 
|---|
| 102 | SNMP_MIB_ITEM( "Udp6InDatagrams", UDP_MIB_INDATAGRAMS), | 
|---|
| 103 | SNMP_MIB_ITEM( "Udp6NoPorts", UDP_MIB_NOPORTS), | 
|---|
| 104 | SNMP_MIB_ITEM( "Udp6InErrors", UDP_MIB_INERRORS), | 
|---|
| 105 | SNMP_MIB_ITEM( "Udp6OutDatagrams", UDP_MIB_OUTDATAGRAMS), | 
|---|
| 106 | SNMP_MIB_ITEM( "Udp6RcvbufErrors", UDP_MIB_RCVBUFERRORS), | 
|---|
| 107 | SNMP_MIB_ITEM( "Udp6SndbufErrors", UDP_MIB_SNDBUFERRORS), | 
|---|
| 108 | SNMP_MIB_ITEM( "Udp6InCsumErrors", UDP_MIB_CSUMERRORS), | 
|---|
| 109 | SNMP_MIB_ITEM( "Udp6IgnoredMulti", UDP_MIB_IGNOREDMULTI), | 
|---|
| 110 | SNMP_MIB_ITEM( "Udp6MemErrors", UDP_MIB_MEMERRORS), | 
|---|
| 111 | }; | 
|---|
| 112 |  | 
|---|
| 113 | static const struct snmp_mib snmp6_udplite6_list[] = { | 
|---|
| 114 | SNMP_MIB_ITEM( "UdpLite6InDatagrams", UDP_MIB_INDATAGRAMS), | 
|---|
| 115 | SNMP_MIB_ITEM( "UdpLite6NoPorts", UDP_MIB_NOPORTS), | 
|---|
| 116 | SNMP_MIB_ITEM( "UdpLite6InErrors", UDP_MIB_INERRORS), | 
|---|
| 117 | SNMP_MIB_ITEM( "UdpLite6OutDatagrams", UDP_MIB_OUTDATAGRAMS), | 
|---|
| 118 | SNMP_MIB_ITEM( "UdpLite6RcvbufErrors", UDP_MIB_RCVBUFERRORS), | 
|---|
| 119 | SNMP_MIB_ITEM( "UdpLite6SndbufErrors", UDP_MIB_SNDBUFERRORS), | 
|---|
| 120 | SNMP_MIB_ITEM( "UdpLite6InCsumErrors", UDP_MIB_CSUMERRORS), | 
|---|
| 121 | SNMP_MIB_ITEM( "UdpLite6MemErrors", UDP_MIB_MEMERRORS), | 
|---|
| 122 | }; | 
|---|
| 123 |  | 
|---|
| 124 | static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, atomic_long_t *smib) | 
|---|
| 125 | { | 
|---|
| 126 | char name[32]; | 
|---|
| 127 | int i; | 
|---|
| 128 |  | 
|---|
| 129 | /* print by name -- deprecated items */ | 
|---|
| 130 | for (i = 0; i < ICMP6MSG_MIB_MAX; i++) { | 
|---|
| 131 | const char *p = NULL; | 
|---|
| 132 | int icmptype; | 
|---|
| 133 |  | 
|---|
| 134 | #define CASE(TYP, STR) case TYP: p = STR; break; | 
|---|
| 135 |  | 
|---|
| 136 | icmptype = i & 0xff; | 
|---|
| 137 | switch (icmptype) { | 
|---|
| 138 | /* RFC 4293 v6 ICMPMsgStatsTable; named items for RFC 2466 compatibility */ | 
|---|
| 139 | CASE(ICMPV6_DEST_UNREACH, "DestUnreachs") | 
|---|
| 140 | CASE(ICMPV6_PKT_TOOBIG, "PktTooBigs") | 
|---|
| 141 | CASE(ICMPV6_TIME_EXCEED, "TimeExcds") | 
|---|
| 142 | CASE(ICMPV6_PARAMPROB, "ParmProblems") | 
|---|
| 143 | CASE(ICMPV6_ECHO_REQUEST, "Echos") | 
|---|
| 144 | CASE(ICMPV6_ECHO_REPLY, "EchoReplies") | 
|---|
| 145 | CASE(ICMPV6_MGM_QUERY, "GroupMembQueries") | 
|---|
| 146 | CASE(ICMPV6_MGM_REPORT, "GroupMembResponses") | 
|---|
| 147 | CASE(ICMPV6_MGM_REDUCTION, "GroupMembReductions") | 
|---|
| 148 | CASE(ICMPV6_MLD2_REPORT, "MLDv2Reports") | 
|---|
| 149 | CASE(NDISC_ROUTER_ADVERTISEMENT, "RouterAdvertisements") | 
|---|
| 150 | CASE(NDISC_ROUTER_SOLICITATION, "RouterSolicits") | 
|---|
| 151 | CASE(NDISC_NEIGHBOUR_ADVERTISEMENT, "NeighborAdvertisements") | 
|---|
| 152 | CASE(NDISC_NEIGHBOUR_SOLICITATION, "NeighborSolicits") | 
|---|
| 153 | CASE(NDISC_REDIRECT, "Redirects") | 
|---|
| 154 | } | 
|---|
| 155 | #undef CASE | 
|---|
| 156 | if (!p)	/* don't print un-named types here */ | 
|---|
| 157 | continue; | 
|---|
| 158 | snprintf(buf: name, size: sizeof(name), fmt: "Icmp6%s%s", | 
|---|
| 159 | i & 0x100 ? "Out": "In", p); | 
|---|
| 160 | seq_printf(m: seq, fmt: "%-32s\t%lu\n", name, | 
|---|
| 161 | atomic_long_read(v: smib + i)); | 
|---|
| 162 | } | 
|---|
| 163 |  | 
|---|
| 164 | /* print by number (nonzero only) - ICMPMsgStat format */ | 
|---|
| 165 | for (i = 0; i < ICMP6MSG_MIB_MAX; i++) { | 
|---|
| 166 | unsigned long val; | 
|---|
| 167 |  | 
|---|
| 168 | val = atomic_long_read(v: smib + i); | 
|---|
| 169 | if (!val) | 
|---|
| 170 | continue; | 
|---|
| 171 | snprintf(buf: name, size: sizeof(name), fmt: "Icmp6%sType%u", | 
|---|
| 172 | i & 0x100 ? "Out": "In", i & 0xff); | 
|---|
| 173 | seq_printf(m: seq, fmt: "%-32s\t%lu\n", name, val); | 
|---|
| 174 | } | 
|---|
| 175 | } | 
|---|
| 176 |  | 
|---|
| 177 | /* can be called either with percpu mib (pcpumib != NULL), | 
|---|
| 178 | * or shared one (smib != NULL) | 
|---|
| 179 | */ | 
|---|
| 180 | static void snmp6_seq_show_item(struct seq_file *seq, void __percpu *pcpumib, | 
|---|
| 181 | atomic_long_t *smib, | 
|---|
| 182 | const struct snmp_mib *itemlist, | 
|---|
| 183 | int cnt) | 
|---|
| 184 | { | 
|---|
| 185 | unsigned long buff[SNMP_MIB_MAX]; | 
|---|
| 186 | int i; | 
|---|
| 187 |  | 
|---|
| 188 | if (pcpumib) { | 
|---|
| 189 | memset(s: buff, c: 0, n: sizeof(unsigned long) * cnt); | 
|---|
| 190 |  | 
|---|
| 191 | snmp_get_cpu_field_batch_cnt(buff, itemlist, cnt, pcpumib); | 
|---|
| 192 | for (i = 0; i < cnt; i++) | 
|---|
| 193 | seq_printf(m: seq, fmt: "%-32s\t%lu\n", | 
|---|
| 194 | itemlist[i].name, buff[i]); | 
|---|
| 195 | } else { | 
|---|
| 196 | for (i = 0; i < cnt; i++) | 
|---|
| 197 | seq_printf(m: seq, fmt: "%-32s\t%lu\n", itemlist[i].name, | 
|---|
| 198 | atomic_long_read(v: smib + itemlist[i].entry)); | 
|---|
| 199 | } | 
|---|
| 200 | } | 
|---|
| 201 |  | 
|---|
| 202 | static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu *mib, | 
|---|
| 203 | const struct snmp_mib *itemlist, | 
|---|
| 204 | int cnt, size_t syncpoff) | 
|---|
| 205 | { | 
|---|
| 206 | u64 buff64[SNMP_MIB_MAX]; | 
|---|
| 207 | int i; | 
|---|
| 208 |  | 
|---|
| 209 | memset(s: buff64, c: 0, n: sizeof(u64) * cnt); | 
|---|
| 210 |  | 
|---|
| 211 | snmp_get_cpu_field64_batch_cnt(buff64, itemlist, cnt, mib, syncpoff); | 
|---|
| 212 | for (i = 0; i < cnt; i++) | 
|---|
| 213 | seq_printf(m: seq, fmt: "%-32s\t%llu\n", itemlist[i].name, buff64[i]); | 
|---|
| 214 | } | 
|---|
| 215 |  | 
|---|
| 216 | static int snmp6_seq_show(struct seq_file *seq, void *v) | 
|---|
| 217 | { | 
|---|
| 218 | struct net *net = (struct net *)seq->private; | 
|---|
| 219 |  | 
|---|
| 220 | snmp6_seq_show_item64(seq, mib: net->mib.ipv6_statistics, | 
|---|
| 221 | itemlist: snmp6_ipstats_list, | 
|---|
| 222 | ARRAY_SIZE(snmp6_ipstats_list), | 
|---|
| 223 | offsetof(struct ipstats_mib, syncp)); | 
|---|
| 224 | snmp6_seq_show_item(seq, pcpumib: net->mib.icmpv6_statistics, | 
|---|
| 225 | NULL, itemlist: snmp6_icmp6_list, | 
|---|
| 226 | ARRAY_SIZE(snmp6_icmp6_list)); | 
|---|
| 227 | snmp6_seq_show_icmpv6msg(seq, smib: net->mib.icmpv6msg_statistics->mibs); | 
|---|
| 228 | snmp6_seq_show_item(seq, pcpumib: net->mib.udp_stats_in6, | 
|---|
| 229 | NULL, itemlist: snmp6_udp6_list, | 
|---|
| 230 | ARRAY_SIZE(snmp6_udp6_list)); | 
|---|
| 231 | snmp6_seq_show_item(seq, pcpumib: net->mib.udplite_stats_in6, | 
|---|
| 232 | NULL, itemlist: snmp6_udplite6_list, | 
|---|
| 233 | ARRAY_SIZE(snmp6_udplite6_list)); | 
|---|
| 234 | return 0; | 
|---|
| 235 | } | 
|---|
| 236 |  | 
|---|
| 237 | static int snmp6_dev_seq_show(struct seq_file *seq, void *v) | 
|---|
| 238 | { | 
|---|
| 239 | struct inet6_dev *idev = (struct inet6_dev *)seq->private; | 
|---|
| 240 |  | 
|---|
| 241 | seq_printf(m: seq, fmt: "%-32s\t%u\n", "ifIndex", idev->dev->ifindex); | 
|---|
| 242 | snmp6_seq_show_item64(seq, mib: idev->stats.ipv6, | 
|---|
| 243 | itemlist: snmp6_ipstats_list, | 
|---|
| 244 | ARRAY_SIZE(snmp6_ipstats_list), | 
|---|
| 245 | offsetof(struct ipstats_mib, syncp)); | 
|---|
| 246 |  | 
|---|
| 247 | /* Per idev icmp stats do not have ICMP6_MIB_RATELIMITHOST */ | 
|---|
| 248 | snmp6_seq_show_item(seq, NULL, smib: idev->stats.icmpv6dev->mibs, | 
|---|
| 249 | itemlist: snmp6_icmp6_list, ARRAY_SIZE(snmp6_icmp6_list) - 1); | 
|---|
| 250 |  | 
|---|
| 251 | snmp6_seq_show_icmpv6msg(seq, smib: idev->stats.icmpv6msgdev->mibs); | 
|---|
| 252 | return 0; | 
|---|
| 253 | } | 
|---|
| 254 |  | 
|---|
| 255 | int snmp6_register_dev(struct inet6_dev *idev) | 
|---|
| 256 | { | 
|---|
| 257 | struct proc_dir_entry *p; | 
|---|
| 258 | struct net *net; | 
|---|
| 259 |  | 
|---|
| 260 | if (!idev || !idev->dev) | 
|---|
| 261 | return -EINVAL; | 
|---|
| 262 |  | 
|---|
| 263 | net = dev_net(dev: idev->dev); | 
|---|
| 264 | if (!net->mib.proc_net_devsnmp6) | 
|---|
| 265 | return -ENOENT; | 
|---|
| 266 |  | 
|---|
| 267 | p = proc_create_single_data(name: idev->dev->name, mode: 0444, | 
|---|
| 268 | parent: net->mib.proc_net_devsnmp6, show: snmp6_dev_seq_show, data: idev); | 
|---|
| 269 | if (!p) | 
|---|
| 270 | return -ENOMEM; | 
|---|
| 271 |  | 
|---|
| 272 | idev->stats.proc_dir_entry = p; | 
|---|
| 273 | return 0; | 
|---|
| 274 | } | 
|---|
| 275 |  | 
|---|
| 276 | int snmp6_unregister_dev(struct inet6_dev *idev) | 
|---|
| 277 | { | 
|---|
| 278 | struct net *net = dev_net(dev: idev->dev); | 
|---|
| 279 | if (!net->mib.proc_net_devsnmp6) | 
|---|
| 280 | return -ENOENT; | 
|---|
| 281 | if (!idev->stats.proc_dir_entry) | 
|---|
| 282 | return -EINVAL; | 
|---|
| 283 | proc_remove(idev->stats.proc_dir_entry); | 
|---|
| 284 | idev->stats.proc_dir_entry = NULL; | 
|---|
| 285 | return 0; | 
|---|
| 286 | } | 
|---|
| 287 |  | 
|---|
| 288 | static int __net_init ipv6_proc_init_net(struct net *net) | 
|---|
| 289 | { | 
|---|
| 290 | if (!proc_create_net_single(name: "sockstat6", mode: 0444, parent: net->proc_net, | 
|---|
| 291 | show: sockstat6_seq_show, NULL)) | 
|---|
| 292 | return -ENOMEM; | 
|---|
| 293 |  | 
|---|
| 294 | if (!proc_create_net_single(name: "snmp6", mode: 0444, parent: net->proc_net, | 
|---|
| 295 | show: snmp6_seq_show, NULL)) | 
|---|
| 296 | goto proc_snmp6_fail; | 
|---|
| 297 |  | 
|---|
| 298 | net->mib.proc_net_devsnmp6 = proc_mkdir( "dev_snmp6", net->proc_net); | 
|---|
| 299 | if (!net->mib.proc_net_devsnmp6) | 
|---|
| 300 | goto proc_dev_snmp6_fail; | 
|---|
| 301 | return 0; | 
|---|
| 302 |  | 
|---|
| 303 | proc_dev_snmp6_fail: | 
|---|
| 304 | remove_proc_entry( "snmp6", net->proc_net); | 
|---|
| 305 | proc_snmp6_fail: | 
|---|
| 306 | remove_proc_entry( "sockstat6", net->proc_net); | 
|---|
| 307 | return -ENOMEM; | 
|---|
| 308 | } | 
|---|
| 309 |  | 
|---|
| 310 | static void __net_exit ipv6_proc_exit_net(struct net *net) | 
|---|
| 311 | { | 
|---|
| 312 | remove_proc_entry( "sockstat6", net->proc_net); | 
|---|
| 313 | remove_proc_entry( "dev_snmp6", net->proc_net); | 
|---|
| 314 | remove_proc_entry( "snmp6", net->proc_net); | 
|---|
| 315 | } | 
|---|
| 316 |  | 
|---|
| 317 | static struct pernet_operations ipv6_proc_ops = { | 
|---|
| 318 | .init = ipv6_proc_init_net, | 
|---|
| 319 | .exit = ipv6_proc_exit_net, | 
|---|
| 320 | }; | 
|---|
| 321 |  | 
|---|
| 322 | int __init ipv6_misc_proc_init(void) | 
|---|
| 323 | { | 
|---|
| 324 | return register_pernet_subsys(&ipv6_proc_ops); | 
|---|
| 325 | } | 
|---|
| 326 |  | 
|---|
| 327 | void ipv6_misc_proc_exit(void) | 
|---|
| 328 | { | 
|---|
| 329 | unregister_pernet_subsys(&ipv6_proc_ops); | 
|---|
| 330 | } | 
|---|
| 331 |  | 
|---|