File: /usr/src/linux/net/atm/proc.c

1     /* net/atm/proc.c - ATM /proc interface */
2     
3     /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4     
5     /*
6      * The mechanism used here isn't designed for speed but rather for convenience
7      * of implementation. We only return one entry per read system call, so we can
8      * be reasonably sure not to overrun the page and race conditions may lead to
9      * the addition or omission of some lines but never to any corruption of a
10      * line's internal structure.
11      *
12      * Making the whole thing slightly more efficient is left as an exercise to the
13      * reader. (Suggestions: wrapper which loops to get several entries per system
14      * call; or make --left slightly more clever to avoid O(n^2) characteristics.)
15      * I find it fast enough on my unloaded 266 MHz Pentium 2 :-)
16      */
17     
18     
19     #include <linux/config.h>
20     #include <linux/module.h> /* for EXPORT_SYMBOL */
21     #include <linux/string.h>
22     #include <linux/types.h>
23     #include <linux/mm.h>
24     #include <linux/fs.h>
25     #include <linux/stat.h>
26     #include <linux/proc_fs.h>
27     #include <linux/errno.h>
28     #include <linux/atm.h>
29     #include <linux/atmdev.h>
30     #include <linux/netdevice.h>
31     #include <linux/atmclip.h>
32     #include <linux/atmarp.h>
33     #include <linux/if_arp.h>
34     #include <linux/init.h> /* for __init */
35     #include <asm/uaccess.h>
36     #include <asm/atomic.h>
37     #include <asm/param.h> /* for HZ */
38     #include "resources.h"
39     #include "common.h" /* atm_proc_init prototype */
40     #include "signaling.h" /* to get sigd - ugly too */
41     
42     #ifdef CONFIG_ATM_CLIP
43     #include <net/atmclip.h>
44     #include "ipcommon.h"
45     extern void clip_push(struct atm_vcc *vcc,struct sk_buff *skb);
46     #endif
47     
48     #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
49     #include "lec.h"
50     #include "lec_arpc.h"
51     extern struct atm_lane_ops atm_lane_ops; /* in common.c */
52     #endif
53     
54     static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
55         loff_t *pos);
56     static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
57         loff_t *pos);
58     
59     static struct file_operations proc_dev_atm_operations = {
60     	read:		proc_dev_atm_read,
61     };
62     
63     static struct file_operations proc_spec_atm_operations = {
64     	read:		proc_spec_atm_read,
65     };
66     
67     static void add_stats(char *buf,const char *aal,
68       const struct k_atm_aal_stats *stats)
69     {
70     	sprintf(strchr(buf,0),"%s ( %d %d %d %d %d )",aal,
71     	    atomic_read(&stats->tx),atomic_read(&stats->tx_err),
72     	    atomic_read(&stats->rx),atomic_read(&stats->rx_err),
73     	    atomic_read(&stats->rx_drop));
74     }
75     
76     
77     static void dev_info(const struct atm_dev *dev,char *buf)
78     {
79     	int off,i;
80     
81     	off = sprintf(buf,"%3d %-8s",dev->number,dev->type);
82     	for (i = 0; i < ESI_LEN; i++)
83     		off += sprintf(buf+off,"%02x",dev->esi[i]);
84     	strcat(buf,"  ");
85     	add_stats(buf,"0",&dev->stats.aal0);
86     	strcat(buf,"  ");
87     	add_stats(buf,"5",&dev->stats.aal5);
88     	strcat(buf,"\n");
89     }
90     
91     
92     #ifdef CONFIG_ATM_CLIP
93     
94     
95     static int svc_addr(char *buf,struct sockaddr_atmsvc *addr)
96     {
97     	static int code[] = { 1,2,10,6,1,0 };
98     	static int e164[] = { 1,8,4,6,1,0 };
99     	int *fields;
100     	int len,i,j,pos;
101     
102     	len = 0;
103     	if (*addr->sas_addr.pub) {
104     		strcpy(buf,addr->sas_addr.pub);
105     		len = strlen(addr->sas_addr.pub);
106     		buf += len;
107     		if (*addr->sas_addr.prv) {
108     			*buf++ = '+';
109     			len++;
110     		}
111     	}
112     	else if (!*addr->sas_addr.prv) {
113     			strcpy(buf,"(none)");
114     			return strlen(buf);
115     		}
116     	if (*addr->sas_addr.prv) {
117     		len += 44;
118     		pos = 0;
119     		fields = *addr->sas_addr.prv == ATM_AFI_E164 ? e164 : code;
120     		for (i = 0; fields[i]; i++) {
121     			for (j = fields[i]; j; j--) {
122     				sprintf(buf,"%02X",addr->sas_addr.prv[pos++]);
123     				buf += 2;
124     			}
125     			if (fields[i+1]) *buf++ = '.';
126     		}
127     	}
128     	return len;
129     }
130     
131     
132     static void atmarp_info(struct net_device *dev,struct atmarp_entry *entry,
133         struct clip_vcc *clip_vcc,char *buf)
134     {
135     	unsigned char *ip;
136     	int svc,off,ip_len;
137     
138     	svc = !clip_vcc || clip_vcc->vcc->family == AF_ATMSVC;
139     	off = sprintf(buf,"%-6s%-4s%-4s%5ld ",dev->name,svc ? "SVC" : "PVC",
140     	    !clip_vcc || clip_vcc->encap ? "LLC" : "NULL",
141     	    (jiffies-(clip_vcc ? clip_vcc->last_use : entry->neigh->used))/
142     	    HZ);
143     	ip = (unsigned char *) &entry->ip;
144     	ip_len = sprintf(buf+off,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
145     	off += ip_len;
146     	while (ip_len++ < 16) buf[off++] = ' ';
147     	if (!clip_vcc)
148     		if (time_before(jiffies, entry->expires))
149     			strcpy(buf+off,"(resolving)\n");
150     		else sprintf(buf+off,"(expired, ref %d)\n",
151     			    atomic_read(&entry->neigh->refcnt));
152     	else if (!svc)
153     			sprintf(buf+off,"%d.%d.%d\n",clip_vcc->vcc->dev->number,
154     			    clip_vcc->vcc->vpi,clip_vcc->vcc->vci);
155     		else {
156     			off += svc_addr(buf+off,&clip_vcc->vcc->remote);
157     			strcpy(buf+off,"\n");
158     		}
159     }
160     
161     
162     #endif
163     
164     
165     static void pvc_info(struct atm_vcc *vcc,char *buf)
166     {
167     	static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
168     	static const char *aal_name[] = {
169     		"---",	"1",	"2",	"3/4",	/*  0- 3 */
170     		"???",	"5",	"???",	"???",	/*  4- 7 */
171     		"???",	"???",	"???",	"???",	/*  8-11 */
172     		"???",	"0",	"???",	"???"};	/* 12-15 */
173     	int off;
174     
175     	off = sprintf(buf,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
176     	    vcc->dev->number,vcc->vpi,vcc->vci,
177     	    vcc->qos.aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" :
178     	    aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr,
179     	    class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
180     	    class_name[vcc->qos.txtp.traffic_class]);
181     #ifdef CONFIG_ATM_CLIP
182     	if (vcc->push == clip_push) {
183     		struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
184     		struct net_device *dev;
185     
186     		dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL;
187     		off += sprintf(buf+off,"CLIP, Itf:%s, Encap:",
188     		    dev ? dev->name : "none?");
189     		if (clip_vcc->encap) off += sprintf(buf+off,"LLC/SNAP");
190     		else off += sprintf(buf+off,"None");
191     	}
192     #endif
193     	strcpy(buf+off,"\n");
194     }
195     
196     
197     static const char *vcc_state(struct atm_vcc *vcc)
198     {
199     	static const char *map[] = { ATM_VS2TXT_MAP };
200     
201     	return map[ATM_VF2VS(vcc->flags)];
202     }
203     
204     
205     static void vc_info(struct atm_vcc *vcc,char *buf)
206     {
207     	char *here;
208     
209     	here = buf+sprintf(buf,"%p ",vcc);
210     	if (!vcc->dev) here += sprintf(here,"Unassigned    ");
211     	else here += sprintf(here,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
212     		    vcc->vci);
213     	switch (vcc->family) {
214     		case AF_ATMPVC:
215     			here += sprintf(here,"PVC");
216     			break;
217     		case AF_ATMSVC:
218     			here += sprintf(here,"SVC");
219     			break;
220     		default:
221     			here += sprintf(here,"%3d",vcc->family);
222     	}
223     	here += sprintf(here," %04lx  %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
224     	    vcc->reply,
225     	    atomic_read(&vcc->tx_inuse),vcc->sk->sndbuf,
226     	    atomic_read(&vcc->rx_inuse),vcc->sk->rcvbuf);
227     }
228     
229     
230     static void svc_info(struct atm_vcc *vcc,char *buf)
231     {
232     	char *here;
233     	int i;
234     
235     	if (!vcc->dev)
236     		sprintf(buf,sizeof(void *) == 4 ? "N/A@%p%10s" : "N/A@%p%2s",
237     		    vcc,"");
238     	else sprintf(buf,"%3d %3d %5d         ",vcc->dev->number,vcc->vpi,
239     		    vcc->vci);
240     	here = strchr(buf,0);
241     	here += sprintf(here,"%-10s ",vcc_state(vcc));
242     	here += sprintf(here,"%s%s",vcc->remote.sas_addr.pub,
243     	    *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
244     	if (*vcc->remote.sas_addr.prv)
245     		for (i = 0; i < ATM_ESA_LEN; i++)
246     			here += sprintf(here,"%02x",
247     			    vcc->remote.sas_addr.prv[i]);
248     	strcat(here,"\n");
249     }
250     
251     
252     #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
253     
254     static char*
255     lec_arp_get_status_string(unsigned char status)
256     {
257       switch(status) {
258       case ESI_UNKNOWN:
259         return "ESI_UNKNOWN       ";
260       case ESI_ARP_PENDING:
261         return "ESI_ARP_PENDING   ";
262       case ESI_VC_PENDING:
263         return "ESI_VC_PENDING    ";
264       case ESI_FLUSH_PENDING:
265         return "ESI_FLUSH_PENDING ";
266       case ESI_FORWARD_DIRECT:
267         return "ESI_FORWARD_DIRECT";
268       default:
269         return "<Unknown>         ";
270       }
271     }
272     
273     static void 
274     lec_info(struct lec_arp_table *entry, char *buf)
275     {
276             int j, offset=0;
277     
278             for(j=0;j<ETH_ALEN;j++) {
279                     offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
280             }
281             offset+=sprintf(buf+offset, " ");
282             for(j=0;j<ATM_ESA_LEN;j++) {
283                     offset+=sprintf(buf+offset,"%2.2x",0xff&entry->atm_addr[j]);
284             }
285             offset+=sprintf(buf+offset, " %s %4.4x",
286                             lec_arp_get_status_string(entry->status),
287                             entry->flags&0xffff);
288             if (entry->vcc) {
289                     offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi, 
290                                     entry->vcc->vci);                
291             } else
292                     offset+=sprintf(buf+offset, "        ");
293             if (entry->recv_vcc) {
294                     offset+=sprintf(buf+offset, "     %3d %3d", 
295                                     entry->recv_vcc->vpi, entry->recv_vcc->vci);
296             }
297     
298             sprintf(buf+offset,"\n");
299     }
300     
301     #endif
302     
303     static int atm_devices_info(loff_t pos,char *buf)
304     {
305     	struct atm_dev *dev;
306     	int left;
307     
308     	if (!pos) {
309     		return sprintf(buf,"Itf Type    ESI/\"MAC\"addr "
310     		    "AAL(TX,err,RX,err,drop) ...\n");
311     	}
312     	left = pos-1;
313     	for (dev = atm_devs; dev && left; dev = dev->next) left--;
314     	if (!dev) return 0;
315     	dev_info(dev,buf);
316     	return strlen(buf);
317     }
318     
319     /*
320      * FIXME: it isn't safe to walk the VCC list without turning off interrupts.
321      * What is really needed is some lock on the devices. Ditto for ATMARP.
322      */
323     
324     static int atm_pvc_info(loff_t pos,char *buf)
325     {
326     	struct atm_dev *dev;
327     	struct atm_vcc *vcc;
328     	int left;
329     
330     	if (!pos) {
331     		return sprintf(buf,"Itf VPI VCI   AAL RX(PCR,Class) "
332     		    "TX(PCR,Class)\n");
333     	}
334     	left = pos-1;
335     	for (dev = atm_devs; dev; dev = dev->next)
336     		for (vcc = dev->vccs; vcc; vcc = vcc->next)
337     			if (vcc->family == PF_ATMPVC &&
338     			    vcc->dev && !left--) {
339     				pvc_info(vcc,buf);
340     				return strlen(buf);
341     			}
342     	return 0;
343     }
344     
345     
346     static int atm_vc_info(loff_t pos,char *buf)
347     {
348     	struct atm_dev *dev;
349     	struct atm_vcc *vcc;
350     	int left;
351     
352     	if (!pos)
353     		return sprintf(buf,sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
354     		    "Address"," Itf VPI VCI   Fam Flags Reply Send buffer"
355     		    "     Recv buffer\n");
356     	left = pos-1;
357     	for (dev = atm_devs; dev; dev = dev->next)
358     		for (vcc = dev->vccs; vcc; vcc = vcc->next)
359     			if (!left--) {
360     				vc_info(vcc,buf);
361     				return strlen(buf);
362     			}
363     	for (vcc = nodev_vccs; vcc; vcc = vcc->next)
364     		if (!left--) {
365     			vc_info(vcc,buf);
366     			return strlen(buf);
367     		}
368     
369     	return 0;
370     }
371     
372     
373     static int atm_svc_info(loff_t pos,char *buf)
374     {
375     	struct atm_dev *dev;
376     	struct atm_vcc *vcc;
377     	int left;
378     
379     	if (!pos)
380     		return sprintf(buf,"Itf VPI VCI           State      Remote\n");
381     	left = pos-1;
382     	for (dev = atm_devs; dev; dev = dev->next)
383     		for (vcc = dev->vccs; vcc; vcc = vcc->next)
384     			if (vcc->family == PF_ATMSVC && !left--) {
385     				svc_info(vcc,buf);
386     				return strlen(buf);
387     			}
388     	for (vcc = nodev_vccs; vcc; vcc = vcc->next)
389     		if (vcc->family == PF_ATMSVC && !left--) {
390     			svc_info(vcc,buf);
391     			return strlen(buf);
392     		}
393     	return 0;
394     }
395     
396     #ifdef CONFIG_ATM_CLIP
397     static int atm_arp_info(loff_t pos,char *buf)
398     {
399     	struct neighbour *n;
400     	int i,count;
401     
402     	if (!pos) {
403     		return sprintf(buf,"IPitf TypeEncp Idle IP address      "
404     		    "ATM address\n");
405     	}
406     	count = pos;
407     	read_lock_bh(&clip_tbl.lock);
408     	for (i = 0; i <= NEIGH_HASHMASK; i++)
409     		for (n = clip_tbl.hash_buckets[i]; n; n = n->next) {
410     			struct atmarp_entry *entry = NEIGH2ENTRY(n);
411     			struct clip_vcc *vcc;
412     
413     			if (!entry->vccs) {
414     				if (--count) continue;
415     				atmarp_info(n->dev,entry,NULL,buf);
416     				read_unlock_bh(&clip_tbl.lock);
417     				return strlen(buf);
418     			}
419     			for (vcc = entry->vccs; vcc;
420     			    vcc = vcc->next) {
421     				if (--count) continue;
422     				atmarp_info(n->dev,entry,vcc,buf);
423     				read_unlock_bh(&clip_tbl.lock);
424     				return strlen(buf);
425     			}
426     		}
427     	read_unlock_bh(&clip_tbl.lock);
428     	return 0;
429     }
430     #endif
431     
432     #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
433     static int atm_lec_info(loff_t pos,char *buf)
434     {
435     	struct lec_priv *priv;
436     	struct lec_arp_table *entry;
437     	int i, count, d, e;
438     	struct net_device **dev_lec;
439     
440     	if (!pos) {
441     		return sprintf(buf,"Itf  MAC          ATM destination"
442     		    "                          Status            Flags "
443     		    "VPI/VCI Recv VPI/VCI\n");
444     	}
445     	if (atm_lane_ops.get_lecs == NULL)
446     		return 0; /* the lane module is not there yet */
447     	else
448     		dev_lec = atm_lane_ops.get_lecs();
449     
450     	count = pos;
451     	for(d=0;d<MAX_LEC_ITF;d++) {
452     		if (!dev_lec[d] || !(priv =
453     		    (struct lec_priv *) dev_lec[d]->priv)) continue;
454     		for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
455     			entry = priv->lec_arp_tables[i];
456     			for(;entry;entry=entry->next) {
457     				if (--count) continue;
458     				e=sprintf(buf,"%s ",
459     				    dev_lec[d]->name);
460     				lec_info(entry,buf+e);
461     				return strlen(buf);
462     			}
463     		}
464     		for(entry=priv->lec_arp_empty_ones; entry;
465     		    entry=entry->next) {
466     			if (--count) continue;
467     			e=sprintf(buf,"%s ",dev_lec[d]->name);
468     			lec_info(entry, buf+e);
469     			return strlen(buf);
470     		}
471     		for(entry=priv->lec_no_forward; entry;
472     		    entry=entry->next) {
473     			if (--count) continue;
474     			e=sprintf(buf,"%s ",dev_lec[d]->name);
475     			lec_info(entry, buf+e);
476     			return strlen(buf);
477     		}
478     		for(entry=priv->mcast_fwds; entry;
479     		    entry=entry->next) {
480     			if (--count) continue;
481     			e=sprintf(buf,"%s ",dev_lec[d]->name);
482     			lec_info(entry, buf+e);
483     			return strlen(buf);
484     		}
485     	}
486     	return 0;
487     }
488     #endif
489     
490     
491     static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
492         loff_t *pos)
493     {
494     	struct atm_dev *dev;
495     	unsigned long page;
496     	int length;
497     
498     	if (count == 0) return 0;
499     	page = get_free_page(GFP_KERNEL);
500     	if (!page) return -ENOMEM;
501     	dev = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
502     	    ->data;
503     	if (!dev->ops->proc_read)
504     		length = -EINVAL;
505     	else {
506     		length = dev->ops->proc_read(dev,pos,(char *) page);
507     		if (length > count) length = -EINVAL;
508     	}
509     	if (length >= 0) {
510     		if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
511     		(*pos)++;
512     	}
513     	free_page(page);
514     	return length;
515     }
516     
517     
518     static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
519         loff_t *pos)
520     {
521     	unsigned long page;
522     	int length;
523     	int (*info)(loff_t,char *);
524     	info = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
525     	    ->data;
526     
527     	if (count == 0) return 0;
528     	page = get_free_page(GFP_KERNEL);
529     	if (!page) return -ENOMEM;
530     	length = (*info)(*pos,(char *) page);
531     	if (length > count) length = -EINVAL;
532     	if (length >= 0) {
533     		if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
534     		(*pos)++;
535     	}
536     	free_page(page);
537     	return length;
538     }
539     
540     
541     struct proc_dir_entry *atm_proc_root;
542     EXPORT_SYMBOL(atm_proc_root);
543     
544     
545     int atm_proc_dev_register(struct atm_dev *dev)
546     {
547     	int digits,num;
548     	int error;
549     
550     	error = -ENOMEM;
551     	digits = 0;
552     	for (num = dev->number; num; num /= 10) digits++;
553     	if (!digits) digits++;
554     	dev->proc_name = kmalloc(strlen(dev->type)+digits+2,GFP_KERNEL);
555     	if (!dev->proc_name) goto fail1;
556     	sprintf(dev->proc_name,"%s:%d",dev->type, dev->number);
557     	dev->proc_entry = create_proc_entry(dev->proc_name, 0, atm_proc_root);
558     	if (!dev->proc_entry)
559     		goto fail0;
560     	dev->proc_entry->data = dev;
561     	dev->proc_entry->proc_fops = &proc_dev_atm_operations;
562     	dev->proc_entry->owner = THIS_MODULE;
563     	return 0;
564     fail0:
565     	kfree(dev->proc_name);
566     fail1:
567     	return error;
568     }
569     
570     
571     void atm_proc_dev_deregister(struct atm_dev *dev)
572     {
573     	remove_proc_entry(dev->proc_name, atm_proc_root);
574     	kfree(dev->proc_name);
575     }
576     
577     
578     #define CREATE_ENTRY(name) \
579         name = create_proc_entry(#name,0,atm_proc_root); \
580         if (!name) goto cleanup; \
581         name->data = atm_##name##_info; \
582         name->proc_fops = &proc_spec_atm_operations; \
583         name->owner = THIS_MODULE
584     
585     
586     int __init atm_proc_init(void)
587     {
588     	struct proc_dir_entry *devices = NULL,*pvc = NULL,*svc = NULL;
589     	struct proc_dir_entry *arp = NULL,*lec = NULL,*vc = NULL;
590     
591     	atm_proc_root = proc_mkdir("net/atm",NULL);
592     	if (!atm_proc_root)
593     		return -ENOMEM;
594     	CREATE_ENTRY(devices);
595     	CREATE_ENTRY(pvc);
596     	CREATE_ENTRY(svc);
597     	CREATE_ENTRY(vc);
598     #ifdef CONFIG_ATM_CLIP
599     	CREATE_ENTRY(arp);
600     #endif
601     #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
602     	CREATE_ENTRY(lec);
603     #endif
604     	return 0;
605     
606     cleanup:
607     	if (devices) remove_proc_entry("devices",atm_proc_root);
608     	if (pvc) remove_proc_entry("pvc",atm_proc_root);
609     	if (svc) remove_proc_entry("svc",atm_proc_root);
610     	if (arp) remove_proc_entry("arp",atm_proc_root);
611     	if (lec) remove_proc_entry("lec",atm_proc_root);
612     	if (vc) remove_proc_entry("vc",atm_proc_root);
613     	remove_proc_entry("net/atm",NULL);
614     	return -ENOMEM;
615     }
616