Commit 63646029 authored by Sridhar Samudrala's avatar Sridhar Samudrala

[SCTP] /proc interface to display associations/endpoints.

parent d9441eda
...@@ -265,6 +265,8 @@ struct sctp_af { ...@@ -265,6 +265,8 @@ struct sctp_af {
int (*available) (const union sctp_addr *); int (*available) (const union sctp_addr *);
int (*skb_iif) (const struct sk_buff *sk); int (*skb_iif) (const struct sk_buff *sk);
int (*is_ce) (const struct sk_buff *sk); int (*is_ce) (const struct sk_buff *sk);
void (*seq_dump_addr)(struct seq_file *seq,
union sctp_addr *addr);
__u16 net_header_len; __u16 net_header_len;
int sockaddr_len; int sockaddr_len;
sa_family_t sa_family; sa_family_t sa_family;
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/seq_file.h>
#include <net/protocol.h> #include <net/protocol.h>
#include <net/tcp.h> #include <net/tcp.h>
...@@ -578,6 +579,13 @@ static int sctp_v6_is_ce(const struct sk_buff *skb) ...@@ -578,6 +579,13 @@ static int sctp_v6_is_ce(const struct sk_buff *skb)
return *((__u32 *)(skb->nh.ipv6h)) & htonl(1<<20); return *((__u32 *)(skb->nh.ipv6h)) & htonl(1<<20);
} }
/* Dump the v6 addr to the seq file. */
static void sctp_v6_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
{
seq_printf(seq, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
NIP6(addr->v6.sin6_addr));
}
/* Initialize a PF_INET6 socket msg_name. */ /* Initialize a PF_INET6 socket msg_name. */
static void sctp_inet6_msgname(char *msgname, int *addr_len) static void sctp_inet6_msgname(char *msgname, int *addr_len)
{ {
...@@ -840,6 +848,7 @@ static struct sctp_af sctp_ipv6_specific = { ...@@ -840,6 +848,7 @@ static struct sctp_af sctp_ipv6_specific = {
.available = sctp_v6_available, .available = sctp_v6_available,
.skb_iif = sctp_v6_skb_iif, .skb_iif = sctp_v6_skb_iif,
.is_ce = sctp_v6_is_ce, .is_ce = sctp_v6_is_ce,
.seq_dump_addr = sctp_v6_seq_dump_addr,
.net_header_len = sizeof(struct ipv6hdr), .net_header_len = sizeof(struct ipv6hdr),
.sockaddr_len = sizeof(struct sockaddr_in6), .sockaddr_len = sizeof(struct sockaddr_in6),
.sa_family = AF_INET6, .sa_family = AF_INET6,
......
...@@ -128,3 +128,162 @@ void sctp_snmp_proc_exit(void) ...@@ -128,3 +128,162 @@ void sctp_snmp_proc_exit(void)
{ {
remove_proc_entry("snmp", proc_net_sctp); remove_proc_entry("snmp", proc_net_sctp);
} }
/* Dump local addresses of an association/endpoint. */
static void sctp_seq_dump_local_addrs(struct seq_file *seq, struct sctp_ep_common *epb)
{
struct list_head *pos;
struct sockaddr_storage_list *laddr;
union sctp_addr *addr;
struct sctp_af *af;
list_for_each(pos, &epb->bind_addr.address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, list);
addr = (union sctp_addr *)&laddr->a;
af = sctp_get_af_specific(addr->sa.sa_family);
af->seq_dump_addr(seq, addr);
}
}
/* Dump remote addresses of an association. */
static void sctp_seq_dump_remote_addrs(struct seq_file *seq, struct sctp_association *assoc)
{
struct list_head *pos;
struct sctp_transport *transport;
union sctp_addr *addr;
struct sctp_af *af;
list_for_each(pos, &assoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
addr = (union sctp_addr *)&transport->ipaddr;
af = sctp_get_af_specific(addr->sa.sa_family);
af->seq_dump_addr(seq, addr);
}
}
/* Display sctp endpoints (/proc/net/sctp/eps). */
static int sctp_eps_seq_show(struct seq_file *seq, void *v)
{
struct sctp_hashbucket *head;
struct sctp_ep_common *epb;
struct sctp_endpoint *ep;
struct sock *sk;
int hash;
seq_printf(seq, " ENDPT SOCK STY SST HBKT LPORT LADDRS\n");
for (hash = 0; hash < sctp_ep_hashsize; hash++) {
head = &sctp_ep_hashbucket[hash];
read_lock(&head->lock);
for (epb = head->chain; epb; epb = epb->next) {
ep = sctp_ep(epb);
sk = epb->sk;
seq_printf(seq, "%8p %8p %-3d %-3d %-4d %-5d ", ep, sk,
sctp_sk(sk)->type, sk->state, hash,
epb->bind_addr.port);
sctp_seq_dump_local_addrs(seq, epb);
seq_printf(seq, "\n");
}
read_unlock(&head->lock);
}
return 0;
}
/* Initialize the seq file operations for 'eps' object. */
static int sctp_eps_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, sctp_eps_seq_show, NULL);
}
static struct file_operations sctp_eps_seq_fops = {
.open = sctp_eps_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/* Set up the proc fs entry for 'eps' object. */
int __init sctp_eps_proc_init(void)
{
struct proc_dir_entry *p;
p = create_proc_entry("eps", S_IRUGO, proc_net_sctp);
if (!p)
return -ENOMEM;
p->proc_fops = &sctp_eps_seq_fops;
return 0;
}
/* Cleanup the proc fs entry for 'eps' object. */
void sctp_eps_proc_exit(void)
{
remove_proc_entry("eps", proc_net_sctp);
}
/* Display sctp associations (/proc/net/sctp/assocs). */
static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
{
struct sctp_hashbucket *head;
struct sctp_ep_common *epb;
struct sctp_association *assoc;
struct sock *sk;
int hash;
seq_printf(seq, " ASSOC SOCK STY SST ST HBKT LPORT RPORT "
"LADDRS <-> RADDRS\n");
for (hash = 0; hash < sctp_assoc_hashsize; hash++) {
head = &sctp_assoc_hashbucket[hash];
read_lock(&head->lock);
for (epb = head->chain; epb; epb = epb->next) {
assoc = sctp_assoc(epb);
sk = epb->sk;
seq_printf(seq,
"%8p %8p %-3d %-3d %-2d %-4d %-5d %-5d ",
assoc, sk, sctp_sk(sk)->type, sk->state,
assoc->state, hash, epb->bind_addr.port,
assoc->peer.port);
sctp_seq_dump_local_addrs(seq, epb);
seq_printf(seq, "<-> ");
sctp_seq_dump_remote_addrs(seq, assoc);
seq_printf(seq, "\n");
}
read_unlock(&head->lock);
}
return 0;
}
/* Initialize the seq file operations for 'assocs' object. */
static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, sctp_assocs_seq_show, NULL);
}
static struct file_operations sctp_assocs_seq_fops = {
.open = sctp_assocs_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/* Set up the proc fs entry for 'assocs' object. */
int __init sctp_assocs_proc_init(void)
{
struct proc_dir_entry *p;
p = create_proc_entry("assocs", S_IRUGO, proc_net_sctp);
if (!p)
return -ENOMEM;
p->proc_fops = &sctp_assocs_seq_fops;
return 0;
}
/* Cleanup the proc fs entry for 'assocs' object. */
void sctp_assocs_proc_exit(void)
{
remove_proc_entry("assocs", proc_net_sctp);
}
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/seq_file.h>
#include <net/protocol.h> #include <net/protocol.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
...@@ -81,6 +82,10 @@ extern struct net_proto_family inet_family_ops; ...@@ -81,6 +82,10 @@ extern struct net_proto_family inet_family_ops;
extern int sctp_snmp_proc_init(void); extern int sctp_snmp_proc_init(void);
extern int sctp_snmp_proc_exit(void); extern int sctp_snmp_proc_exit(void);
extern int sctp_eps_proc_init(void);
extern int sctp_eps_proc_exit(void);
extern int sctp_assocs_proc_init(void);
extern int sctp_assocs_proc_exit(void);
/* Return the address of the control sock. */ /* Return the address of the control sock. */
struct sock *sctp_get_ctl_sock(void) struct sock *sctp_get_ctl_sock(void)
...@@ -91,8 +96,6 @@ struct sock *sctp_get_ctl_sock(void) ...@@ -91,8 +96,6 @@ struct sock *sctp_get_ctl_sock(void)
/* Set up the proc fs entry for the SCTP protocol. */ /* Set up the proc fs entry for the SCTP protocol. */
__init int sctp_proc_init(void) __init int sctp_proc_init(void)
{ {
int rc = 0;
if (!proc_net_sctp) { if (!proc_net_sctp) {
struct proc_dir_entry *ent; struct proc_dir_entry *ent;
ent = proc_mkdir("net/sctp", 0); ent = proc_mkdir("net/sctp", 0);
...@@ -100,13 +103,20 @@ __init int sctp_proc_init(void) ...@@ -100,13 +103,20 @@ __init int sctp_proc_init(void)
ent->owner = THIS_MODULE; ent->owner = THIS_MODULE;
proc_net_sctp = ent; proc_net_sctp = ent;
} else } else
rc = -ENOMEM; goto out_nomem;
} }
if (sctp_snmp_proc_init()) if (sctp_snmp_proc_init())
rc = -ENOMEM; goto out_nomem;
if (sctp_eps_proc_init())
goto out_nomem;
if (sctp_assocs_proc_init())
goto out_nomem;
return 0;
return rc; out_nomem:
return -ENOMEM;
} }
/* Clean up the proc fs entry for the SCTP protocol. /* Clean up the proc fs entry for the SCTP protocol.
...@@ -116,6 +126,8 @@ __init int sctp_proc_init(void) ...@@ -116,6 +126,8 @@ __init int sctp_proc_init(void)
void sctp_proc_exit(void) void sctp_proc_exit(void)
{ {
sctp_snmp_proc_exit(); sctp_snmp_proc_exit();
sctp_eps_proc_exit();
sctp_assocs_proc_exit();
if (proc_net_sctp) { if (proc_net_sctp) {
proc_net_sctp = NULL; proc_net_sctp = NULL;
...@@ -568,6 +580,12 @@ struct sock *sctp_v4_create_accept_sk(struct sock *sk, ...@@ -568,6 +580,12 @@ struct sock *sctp_v4_create_accept_sk(struct sock *sk,
return newsk; return newsk;
} }
/* Dump the v4 addr to the seq file. */
static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
{
seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr));
}
/* Event handler for inet address addition/deletion events. /* Event handler for inet address addition/deletion events.
* Basically, whenever there is an event, we re-build our local address list. * Basically, whenever there is an event, we re-build our local address list.
*/ */
...@@ -843,6 +861,7 @@ struct sctp_af sctp_ipv4_specific = { ...@@ -843,6 +861,7 @@ struct sctp_af sctp_ipv4_specific = {
.scope = sctp_v4_scope, .scope = sctp_v4_scope,
.skb_iif = sctp_v4_skb_iif, .skb_iif = sctp_v4_skb_iif,
.is_ce = sctp_v4_is_ce, .is_ce = sctp_v4_is_ce,
.seq_dump_addr = sctp_v4_seq_dump_addr,
.net_header_len = sizeof(struct iphdr), .net_header_len = sizeof(struct iphdr),
.sockaddr_len = sizeof(struct sockaddr_in), .sockaddr_len = sizeof(struct sockaddr_in),
.sa_family = AF_INET, .sa_family = AF_INET,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment