1
2#ifdef CONFIG_PROC_FS
3#include <linux/errno.h>
4#include <linux/kernel.h>
5#include <linux/string.h>
6#include <linux/mm.h>
7#include <linux/module.h>
8#include <linux/proc_fs.h>
9#include <linux/time.h>
10#include <linux/seq_file.h>
11#include <asm/uaccess.h>
12#include <linux/atmmpc.h>
13#include <linux/atm.h>
14#include "mpc.h"
15#include "mpoa_caches.h"
16
17/*
18 * mpoa_proc.c: Implementation MPOA client's proc
19 * file system statistics
20 */
21
22#define dprintk printk   /* debug */
23
24#define STAT_FILE_NAME "mpc"     /* Our statistic file's name */
25
26extern struct mpoa_client *mpcs;
27extern struct proc_dir_entry *atm_proc_root;  /* from proc.c. */
28
29static int proc_mpc_open(struct inode *inode, struct file *file);
30static ssize_t proc_mpc_write(struct file *file, const char __user *buff,
31			      size_t nbytes, loff_t *ppos);
32
33static int parse_qos(const char *buff);
34
35/*
36 *   Define allowed FILE OPERATIONS
37 */
38static const struct file_operations mpc_file_operations = {
39	.owner =	THIS_MODULE,
40	.open =		proc_mpc_open,
41	.read =		seq_read,
42	.llseek =	seq_lseek,
43	.write =	proc_mpc_write,
44	.release =	seq_release,
45};
46
47/*
48 * Returns the state of an ingress cache entry as a string
49 */
50static const char *ingress_state_string(int state){
51	switch(state) {
52	case INGRESS_RESOLVING:
53		return "resolving  ";
54		break;
55	case INGRESS_RESOLVED:
56		return "resolved   ";
57		break;
58	case INGRESS_INVALID:
59		return "invalid    ";
60		break;
61	case INGRESS_REFRESHING:
62		return "refreshing ";
63		break;
64	default:
65	       return "";
66	}
67}
68
69/*
70 * Returns the state of an egress cache entry as a string
71 */
72static const char *egress_state_string(int state){
73	switch(state) {
74	case EGRESS_RESOLVED:
75		return "resolved   ";
76		break;
77	case EGRESS_PURGE:
78		return "purge      ";
79		break;
80	case EGRESS_INVALID:
81		return "invalid    ";
82		break;
83	default:
84	       return "";
85	}
86}
87
88
89static void *mpc_start(struct seq_file *m, loff_t *pos)
90{
91	loff_t l = *pos;
92	struct mpoa_client *mpc;
93
94	if (!l--)
95		return SEQ_START_TOKEN;
96	for (mpc = mpcs; mpc; mpc = mpc->next)
97		if (!l--)
98			return mpc;
99	return NULL;
100}
101
102static void *mpc_next(struct seq_file *m, void *v, loff_t *pos)
103{
104	struct mpoa_client *p = v;
105	(*pos)++;
106	return v == SEQ_START_TOKEN ? mpcs : p->next;
107}
108
109static void mpc_stop(struct seq_file *m, void *v)
110{
111}
112
113/*
114 * READING function - called when the /proc/atm/mpoa file is read from.
115 */
116static int mpc_show(struct seq_file *m, void *v)
117{
118	struct mpoa_client *mpc = v;
119	unsigned char *temp;
120	int i;
121	in_cache_entry *in_entry;
122	eg_cache_entry *eg_entry;
123	struct timeval now;
124	unsigned char ip_string[16];
125
126	if (v == SEQ_START_TOKEN) {
127		atm_mpoa_disp_qos(m);
128		return 0;
129	}
130
131	seq_printf(m, "\nInterface %d:\n\n", mpc->dev_num);
132	seq_printf(m, "Ingress Entries:\nIP address      State      Holding time  Packets fwded  VPI  VCI\n");
133	do_gettimeofday(&now);
134
135	for (in_entry = mpc->in_cache; in_entry; in_entry = in_entry->next) {
136		temp = (unsigned char *)&in_entry->ctrl_info.in_dst_ip;
137		sprintf(ip_string,"%d.%d.%d.%d", temp[0], temp[1], temp[2], temp[3]);
138		seq_printf(m, "%-16s%s%-14lu%-12u",
139			      ip_string,
140			      ingress_state_string(in_entry->entry_state),
141			      in_entry->ctrl_info.holding_time-(now.tv_sec-in_entry->tv.tv_sec),
142			      in_entry->packets_fwded);
143		if (in_entry->shortcut)
144			seq_printf(m, "   %-3d  %-3d",in_entry->shortcut->vpi,in_entry->shortcut->vci);
145		seq_printf(m, "\n");
146	}
147
148	seq_printf(m, "\n");
149	seq_printf(m, "Egress Entries:\nIngress MPC ATM addr\nCache-id        State      Holding time  Packets recvd  Latest IP addr   VPI VCI\n");
150	for (eg_entry = mpc->eg_cache; eg_entry; eg_entry = eg_entry->next) {
151		unsigned char *p = eg_entry->ctrl_info.in_MPC_data_ATM_addr;
152		for(i = 0; i < ATM_ESA_LEN; i++)
153			seq_printf(m, "%02x", p[i]);
154		seq_printf(m, "\n%-16lu%s%-14lu%-15u",
155			   (unsigned long)ntohl(eg_entry->ctrl_info.cache_id),
156			   egress_state_string(eg_entry->entry_state),
157			   (eg_entry->ctrl_info.holding_time-(now.tv_sec-eg_entry->tv.tv_sec)),
158			   eg_entry->packets_rcvd);
159
160		/* latest IP address */
161		temp = (unsigned char *)&eg_entry->latest_ip_addr;
162		sprintf(ip_string, "%d.%d.%d.%d", temp[0], temp[1], temp[2], temp[3]);
163		seq_printf(m, "%-16s", ip_string);
164
165		if (eg_entry->shortcut)
166			seq_printf(m, " %-3d %-3d",eg_entry->shortcut->vpi,eg_entry->shortcut->vci);
167		seq_printf(m, "\n");
168	}
169	seq_printf(m, "\n");
170	return 0;
171}
172
173static struct seq_operations mpc_op = {
174	.start =	mpc_start,
175	.next =		mpc_next,
176	.stop =		mpc_stop,
177	.show =		mpc_show
178};
179
180static int proc_mpc_open(struct inode *inode, struct file *file)
181{
182	return seq_open(file, &mpc_op);
183}
184
185static ssize_t proc_mpc_write(struct file *file, const char __user *buff,
186			      size_t nbytes, loff_t *ppos)
187{
188	char *page, *p;
189	unsigned len;
190
191	if (nbytes == 0)
192		return 0;
193
194	if (nbytes >= PAGE_SIZE)
195		nbytes = PAGE_SIZE-1;
196
197	page = (char *)__get_free_page(GFP_KERNEL);
198	if (!page)
199		return -ENOMEM;
200
201	for (p = page, len = 0; len < nbytes; p++, len++) {
202		if (get_user(*p, buff++)) {
203			free_page((unsigned long)page);
204			return -EFAULT;
205		}
206		if (*p == '\0' || *p == '\n')
207			break;
208	}
209
210	*p = '\0';
211
212	if (!parse_qos(page))
213		printk("mpoa: proc_mpc_write: could not parse '%s'\n", page);
214
215	free_page((unsigned long)page);
216
217	return len;
218}
219
220static int parse_qos(const char *buff)
221{
222	/* possible lines look like this
223	 * add 130.230.54.142 tx=max_pcr,max_sdu rx=max_pcr,max_sdu
224	 */
225	unsigned char ip[4];
226	int tx_pcr, tx_sdu, rx_pcr, rx_sdu;
227	__be32 ipaddr;
228	struct atm_qos qos;
229
230	memset(&qos, 0, sizeof(struct atm_qos));
231
232	if (sscanf(buff, "del %hhu.%hhu.%hhu.%hhu",
233			ip, ip+1, ip+2, ip+3) == 4) {
234		ipaddr = *(__be32 *)ip;
235		return atm_mpoa_delete_qos(atm_mpoa_search_qos(ipaddr));
236	}
237
238	if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=tx",
239			ip, ip+1, ip+2, ip+3, &tx_pcr, &tx_sdu) == 6) {
240		rx_pcr = tx_pcr;
241		rx_sdu = tx_sdu;
242	} else if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=%d,%d",
243		ip, ip+1, ip+2, ip+3, &tx_pcr, &tx_sdu, &rx_pcr, &rx_sdu) != 8)
244		return 0;
245
246	ipaddr = *(__be32 *)ip;
247	qos.txtp.traffic_class = ATM_CBR;
248	qos.txtp.max_pcr = tx_pcr;
249	qos.txtp.max_sdu = tx_sdu;
250	qos.rxtp.traffic_class = ATM_CBR;
251	qos.rxtp.max_pcr = rx_pcr;
252	qos.rxtp.max_sdu = rx_sdu;
253	qos.aal = ATM_AAL5;
254	dprintk("mpoa: mpoa_proc.c: parse_qos(): setting qos paramameters to tx=%d,%d rx=%d,%d\n",
255		qos.txtp.max_pcr,
256		qos.txtp.max_sdu,
257		qos.rxtp.max_pcr,
258		qos.rxtp.max_sdu
259		);
260
261	atm_mpoa_add_qos(ipaddr, &qos);
262	return 1;
263}
264
265/*
266 * INITIALIZATION function - called when module is initialized/loaded.
267 */
268int mpc_proc_init(void)
269{
270	struct proc_dir_entry *p;
271
272	p = create_proc_entry(STAT_FILE_NAME, 0, atm_proc_root);
273	if (!p) {
274		printk(KERN_ERR "Unable to initialize /proc/atm/%s\n", STAT_FILE_NAME);
275		return -ENOMEM;
276	}
277	p->proc_fops = &mpc_file_operations;
278	p->owner = THIS_MODULE;
279	return 0;
280}
281
282/*
283 * DELETING function - called when module is removed.
284 */
285void mpc_proc_clean(void)
286{
287	remove_proc_entry(STAT_FILE_NAME,atm_proc_root);
288}
289
290
291#endif /* CONFIG_PROC_FS */
292