#include <linux/config.h>
#ifdef CONFIG_PROC_FS
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/time.h>
#include <asm/uaccess.h>
#include <linux/atmmpc.h>
#include <linux/atm.h>
#include "mpc.h"
#include "mpoa_caches.h"
/*
* mpoa_proc.c: Implementation MPOA client's proc
* file system statistics
*/
#if 1
#define dprintk printk /* debug */
#else
#define dprintk(format,args...)
#endif
#define STAT_FILE_NAME "mpc" /* Our statistic file's name */
extern struct mpoa_client *mpcs;
extern struct proc_dir_entry *atm_proc_root; /* from proc.c. */
static ssize_t proc_mpc_read(struct file *file, char *buff,
size_t count, loff_t *pos);
static ssize_t proc_mpc_write(struct file *file, const char *buff,
size_t nbytes, loff_t *ppos);
static int parse_qos(const char *buff, int len);
/*
* Define allowed FILE OPERATIONS
*/
static struct file_operations mpc_file_operations = {
read: proc_mpc_read,
write: proc_mpc_write,
};
static int print_header(char *buff,struct mpoa_client *mpc){
if(mpc != NULL){
return sprintf(buff,"\nInterface %d:\n\n",mpc->dev_num);
}
return 0;
}
/*
* Returns the state of an ingress cache entry as a string
*/
static const char *ingress_state_string(int state){
switch(state) {
case INGRESS_RESOLVING:
return "resolving ";
break;
case INGRESS_RESOLVED:
return "resolved ";
break;
case INGRESS_INVALID:
return "invalid ";
break;
case INGRESS_REFRESHING:
return "refreshing ";
break;
default:
return "";
}
}
/*
* Returns the state of an egress cache entry as a string
*/
static const char *egress_state_string(int state){
switch(state) {
case EGRESS_RESOLVED:
return "resolved ";
break;
case EGRESS_PURGE:
return "purge ";
break;
case EGRESS_INVALID:
return "invalid ";
break;
default:
return "";
}
}
/*
* READING function - called when the /proc/atm/mpoa file is read from.
*/
static ssize_t proc_mpc_read(struct file *file, char *buff,
size_t count, loff_t *pos){
unsigned long page = 0;
unsigned char *temp;
int length = 0;
int i = 0;
struct mpoa_client *mpc = mpcs;
in_cache_entry *in_entry;
eg_cache_entry *eg_entry;
struct timeval now;
unsigned char ip_string[16];
loff_t n = *pos;
if(count == 0)
return 0;
page = get_free_page(GFP_KERNEL);
if(!page)
return -ENOMEM;
atm_mpoa_disp_qos((char *)page, &length);
while(mpc != NULL){
length += print_header((char *)page + length, mpc);
length += sprintf((char *)page + length,"Ingress Entries:\nIP address State Holding time Packets fwded VPI VCI\n");
in_entry = mpc->in_cache;
do_gettimeofday(&now);
while(in_entry != NULL){
temp = (unsigned char *)&in_entry->ctrl_info.in_dst_ip; sprintf(ip_string,"%d.%d.%d.%d", temp[0], temp[1], temp[2], temp[3]);
length += sprintf((char *)page + length,"%-16s%s%-14lu%-12u", ip_string, ingress_state_string(in_entry->entry_state), (in_entry->ctrl_info.holding_time-(now.tv_sec-in_entry->tv.tv_sec)), in_entry->packets_fwded);
if(in_entry->shortcut)
length += sprintf((char *)page + length," %-3d %-3d",in_entry->shortcut->vpi,in_entry->shortcut->vci);
length += sprintf((char *)page + length,"\n");
in_entry = in_entry->next;
}
length += sprintf((char *)page + length,"\n");
eg_entry = mpc->eg_cache;
length += sprintf((char *)page + length,"Egress Entries:\nIngress MPC ATM addr\nCache-id State Holding time Packets recvd Latest IP addr VPI VCI\n");
while(eg_entry != NULL){
for(i=0;i<ATM_ESA_LEN;i++){
length += sprintf((char *)page + length,"%02x",eg_entry->ctrl_info.in_MPC_data_ATM_addr[i]);}
length += sprintf((char *)page + length,"\n%-16lu%s%-14lu%-15u",(unsigned long) ntohl(eg_entry->ctrl_info.cache_id), egress_state_string(eg_entry->entry_state), (eg_entry->ctrl_info.holding_time-(now.tv_sec-eg_entry->tv.tv_sec)), eg_entry->packets_rcvd);
/* latest IP address */
temp = (unsigned char *)&eg_entry->latest_ip_addr;
sprintf(ip_string, "%d.%d.%d.%d", temp[0], temp[1], temp[2], temp[3]);
length += sprintf((char *)page + length, "%-16s", ip_string);
if(eg_entry->shortcut)
length += sprintf((char *)page + length," %-3d %-3d",eg_entry->shortcut->vpi,eg_entry->shortcut->vci);
length += sprintf((char *)page + length,"\n");
eg_entry = eg_entry->next;
}
length += sprintf((char *)page + length,"\n");
mpc = mpc->next;
}
if (n != (unsigned)n || n >= length) length = 0;
else {
if (count > length - n) count = length - n;
if (copy_to_user(buff, (char *)page , count)) {
free_page(page);
return -EFAULT;
}
*pos = n + count;
}
free_page(page);
return length;
}
static ssize_t proc_mpc_write(struct file *file, const char *buff,
size_t nbytes, loff_t *ppos)
{
int incoming, error, retval;
char *page, c;
const char *tmp;
if (nbytes == 0) return 0;
if (nbytes >= PAGE_SIZE) nbytes = PAGE_SIZE-1;
error = verify_area(VERIFY_READ, buff, nbytes);
if (error) return error;
page = (char *)__get_free_page(GFP_KERNEL);
if (page == NULL) return -ENOMEM;
incoming = 0;
tmp = buff;
while(incoming < nbytes){
if (get_user(c, tmp++)) return -EFAULT;
incoming++;
if (c == '\0' || c == '\n')
break;
}
retval = copy_from_user(page, buff, incoming);
if (retval != 0) {
printk("mpoa: proc_mpc_write: copy_from_user() failed\n");
return -EFAULT;
}
*ppos += incoming;
page[incoming] = '\0';
retval = parse_qos(page, incoming);
if (retval == 0)
printk("mpoa: proc_mpc_write: could not parse '%s'\n", page);
free_page((unsigned long)page);
return nbytes;
}
static int parse_qos(const char *buff, int len)
{
/* possible lines look like this
* add 130.230.54.142 tx=max_pcr,max_sdu rx=max_pcr,max_sdu
*/
int pos, i;
uint32_t ipaddr;
unsigned char ip[4];
char cmd[4], temp[256];
const char *tmp, *prev;
struct atm_qos qos;
int value[5];
memset(&qos, 0, sizeof(struct atm_qos));
strncpy(cmd, buff, 3);
if( strncmp(cmd,"add", 3) && strncmp(cmd,"del", 3))
return 0; /* not add or del */
pos = 4;
/* next parse ip */
prev = buff + pos;
for (i = 0; i < 3; i++) {
tmp = strchr(prev, '.');
if (tmp == NULL) return 0;
memset(temp, '\0', 256);
memcpy(temp, prev, tmp-prev);
ip[i] = (char)simple_strtoul(temp, NULL, 0);
tmp ++;
prev = tmp;
}
tmp = strchr(prev, ' ');
if (tmp == NULL) return 0;
memset(temp, '\0', 256);
memcpy(temp, prev, tmp-prev);
ip[i] = (char)simple_strtoul(temp, NULL, 0);
ipaddr = *(uint32_t *)ip;
if(!strncmp(cmd, "del", 3))
return atm_mpoa_delete_qos(atm_mpoa_search_qos(ipaddr));
/* next transmit values */
tmp = strstr(buff, "tx=");
if(tmp == NULL) return 0;
tmp += 3;
prev = tmp;
for( i = 0; i < 1; i++){
tmp = strchr(prev, ',');
if (tmp == NULL) return 0;
memset(temp, '\0', 256);
memcpy(temp, prev, tmp-prev);
value[i] = (int)simple_strtoul(temp, NULL, 0);
tmp ++;
prev = tmp;
}
tmp = strchr(prev, ' ');
if (tmp == NULL) return 0;
memset(temp, '\0', 256);
memcpy(temp, prev, tmp-prev);
value[i] = (int)simple_strtoul(temp, NULL, 0);
qos.txtp.traffic_class = ATM_CBR;
qos.txtp.max_pcr = value[0];
qos.txtp.max_sdu = value[1];
/* next receive values */
tmp = strstr(buff, "rx=");
if(tmp == NULL) return 0;
if (strstr(buff, "rx=tx")) { /* rx == tx */
qos.rxtp.traffic_class = qos.txtp.traffic_class;
qos.rxtp.max_pcr = qos.txtp.max_pcr;
qos.rxtp.max_cdv = qos.txtp.max_cdv;
qos.rxtp.max_sdu = qos.txtp.max_sdu;
} else {
tmp += 3;
prev = tmp;
for( i = 0; i < 1; i++){
tmp = strchr(prev, ',');
if (tmp == NULL) return 0;
memset(temp, '\0', 256);
memcpy(temp, prev, tmp-prev);
value[i] = (int)simple_strtoul(temp, NULL, 0);
tmp ++;
prev = tmp;
}
tmp = strchr(prev, '\0');
if (tmp == NULL) return 0;
memset(temp, '\0', 256);
memcpy(temp, prev, tmp-prev);
value[i] = (int)simple_strtoul(temp, NULL, 0);
qos.rxtp.traffic_class = ATM_CBR;
qos.rxtp.max_pcr = value[0];
qos.rxtp.max_sdu = value[1];
}
qos.aal = ATM_AAL5;
dprintk("mpoa: mpoa_proc.c: parse_qos(): setting qos paramameters to tx=%d,%d rx=%d,%d\n",
qos.txtp.max_pcr,
qos.txtp.max_sdu,
qos.rxtp.max_pcr,
qos.rxtp.max_sdu
);
atm_mpoa_add_qos(ipaddr, &qos);
return 1;
}
/*
* INITIALIZATION function - called when module is initialized/loaded.
*/
int mpc_proc_init(void)
{
struct proc_dir_entry *p;
p = create_proc_entry(STAT_FILE_NAME, 0, atm_proc_root);
if (!p) {
printk(KERN_ERR "Unable to initialize /proc/atm/%s\n", STAT_FILE_NAME);
return -ENOMEM;
}
p->proc_fops = &mpc_file_operations;
p->owner = THIS_MODULE;
return 0;
}
/*
* DELETING function - called when module is removed.
*/
void mpc_proc_clean(void)
{
remove_proc_entry(STAT_FILE_NAME,atm_proc_root);
}
#endif /* CONFIG_PROC_FS */