diff -ur --new-file old/linux/include/linux/atmioc.h new/linux/include/linux/atmioc.h --- old/linux/include/linux/atmioc.h Thu Aug 13 15:24:20 1998 +++ new/linux/include/linux/atmioc.h Thu Aug 13 15:25:00 1998 @@ -1,6 +1,6 @@ /* atmioc.h - ranges for ATM-related ioctl numbers */ -/* Written 1995-1997 by Werner Almesberger, EPFL LRC */ +/* Written 1995-1998 by Werner Almesberger, EPFL LRC */ /* @@ -30,6 +30,7 @@ /* 0x90-0xbf: Reserved for future use */ #define ATMIOC_AREQUIPA 0xc0 /* Application requested IP over ATM, glob. u. */ #define ATMIOC_LANE 0xd0 /* LAN Emulation, globally unique */ +#define ATMIOC_MPOA 0xd8 /* MPOA, globally unique */ #define ATMIOC_CLIP 0xe0 /* Classical IP over ATM control, globally u. */ #define ATMIOC_CLIP_END 0xef #define ATMIOC_SPECIAL 0xf0 /* Special-purpose controls, globally unique */ diff -ur --new-file old/linux/include/linux/atmlec.h new/linux/include/linux/atmlec.h --- old/linux/include/linux/atmlec.h Thu Aug 13 15:24:20 1998 +++ new/linux/include/linux/atmlec.h Thu Aug 13 15:25:00 1998 @@ -25,11 +25,13 @@ l_svc_setup, l_addr_delete, l_topology_change, l_flush_complete, l_arp_update, + l_narp_req, /* LANE2 mandates the use of this */ l_config, l_flush_tran_id, - l_set_lecid, l_arp_xmt + l_set_lecid, l_arp_xmt, + l_associate_req } atmlec_msg_type; -#define ATMLEC_MSG_TYPE_MAX l_arp_xmt +#define ATMLEC_MSG_TYPE_MAX l_associate_req struct atmlec_config_msg { unsigned int maximum_unknown_frame_count; @@ -40,6 +42,7 @@ unsigned long arp_response_time; unsigned long flush_timeout; unsigned long path_switching_delay; + unsigned int lane_version; /* LANE2: 1 for LANEv1, 2 for LANEv2 */ }; struct atmlec_msg { @@ -52,6 +55,8 @@ unsigned long flag;/* Topology_change flag, remoteflag, permanent flag, lecid, transaction id */ + unsigned int targetless_le_arp; /* LANE2 */ + unsigned int no_source_le_narp; /* LANE2 */ } normal; struct atmlec_config_msg config; } content; @@ -60,6 +65,6 @@ struct atmlec_ioc { int dev_num; unsigned char atm_addr[ATM_ESA_LEN]; - unsigned char receive; /* 1= receive vcc, 0 = send_vcc */ + unsigned char receive; /* 1= receive vcc, 0 = send vcc */ }; #endif /* _ATMLEC_H_ */ diff -ur --new-file old/linux/include/linux/atmmpc.h new/linux/include/linux/atmmpc.h --- old/linux/include/linux/atmmpc.h Thu Jan 1 01:00:00 1970 +++ new/linux/include/linux/atmmpc.h Thu Aug 13 15:25:00 1998 @@ -0,0 +1,98 @@ +#ifndef _ATMMPC_H_ +#define _ATMMPC_H_ + +#include +#include + +#define ATMMPC_CTRL _IO('a', ATMIOC_MPOA) +#define ATMMPC_DATA _IO('a', ATMIOC_MPOA+1) + +#define MPC_SOCKET_INGRESS 1 +#define MPC_SOCKET_EGRESS 2 + +struct atmmpc_ioc { + int dev_num; + uint32_t ipaddr; /* the IP address of the shortcut */ + int type; /* ingress or egress */ +}; + +typedef struct in_ctrl_info { + uint8_t Last_NHRP_CIE_code; + uint8_t Last_Q2931_cause_value; + uint8_t eg_MPC_ATM_addr[ATM_ESA_LEN]; + uint32_t tag; + uint32_t in_dst_ip; /* IP address this ingress MPC sends packets to */ + uint32_t service_category; + uint16_t holding_time; + uint32_t request_id; +} in_ctrl_info; + +typedef struct eg_ctrl_info { + uint8_t DLL_header[256]; + uint8_t DH_length; + uint32_t cache_id; + uint32_t tag; + uint32_t mps_ip; + uint32_t eg_dst_ip; /* IP address to which ingress MPC sends packets */ + uint8_t in_MPC_data_ATM_addr[ATM_ESA_LEN]; + uint16_t holding_time; +} eg_ctrl_info; + +struct k_message{ + uint16_t type; + uint8_t MPS_ctrl[ATM_ESA_LEN]; + union { + in_ctrl_info in_info; + eg_ctrl_info eg_info; + } content; +} k_message; + +struct llc_snap_hdr { /* RFC 1483 LLC/SNAP encapsulation for routed IP PDUs */ + uint8_t dsap; /* Destination Service Access Point (0xAA) */ + uint8_t ssap; /* Source Service Access Point (0xAA) */ + uint8_t ui; /* Unnumbered Information (0x03) */ + uint8_t org[3]; /* Organizational identification (0x000000) */ + uint8_t type[2]; /* Ether type (for IP) (0x0800) */ +}; + +/* MPC parameter defaults */ + +#define MPC_P1 10 /* Shortcut-Setup Frame Count */ +#define MPC_P2 1 /* Shortcut-Setup Frame Time */ +#define MPC_P3 0 /* Flow-detection Protocols */ +#define MPC_P4 5 /* MPC Initial Retry Time */ +#define MPC_P5 40 /* MPC Retry Time Maximum */ +#define MPC_P6 160 /* Hold Down Time */ +#define HOLDING_TIME_DEFAULT 1200 /* same as MPS-p7 */ + +/* MPC constants */ + +#define MPC_C1 2 /* Retry Time Multiplier */ +#define MPC_C2 60 /* Initial Keep-Alive Lifetime */ + +/* Message types - to MPOA daemon */ + +#define SND_MPOA_RES_RQST 201 +#define SET_MPS_CTRL_ADDR 202 +#define SND_MPOA_RES_RTRY 203 /* Different type in a retry due to req id.*/ +#define STOP_KEEP_ALIVE_SM 204 +#define EGRESS_ENTRY_REMOVED 205 +#define SND_EGRESS_PURGE 206 +#define DIE 207 +#define DATA_PLANE_PURGE 208 /* Data plane purge because of egress cache hit miss or dead MPS */ +#define OPEN_INGRESS_SVC 209 + +/* Message types - from MPOA daemon */ + +#define MPOA_TRIGGER_RCVD 101 +#define MPOA_RES_REPLY_RCVD 102 +#define INGRESS_PURGE_RCVD 103 +#define EGRESS_PURGE_RCVD 104 +#define MPS_DEATH 105 +#define CACHE_IMPOS_RCVD 106 +#define SET_MPC_CTRL_ADDR 107 /* Our MPC's control ATM address */ +#define SET_MPS_MAC_ADDR 108 +#define CLEAN_UP_AND_EXIT 109 + +#endif /* _ATMMPC_H_ */ + diff -ur --new-file old/linux/net/Config.in new/linux/net/Config.in --- old/linux/net/Config.in Thu Aug 13 15:24:20 1998 +++ new/linux/net/Config.in Thu Aug 13 15:25:01 1998 @@ -35,6 +35,9 @@ # bool ' Application REQUested IP over ATM' CONFIG_AREQUIPA y fi tristate ' LAN Emulation (LANE) support' CONFIG_ATM_LANE y + if [ "$CONFIG_INET" = "y" -a "$CONFIG_ATM_LANE" != "n" ]; then + tristate ' Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA y + fi fi fi diff -ur --new-file old/linux/net/Makefile new/linux/net/Makefile --- old/linux/net/Makefile Thu Aug 13 15:24:20 1998 +++ new/linux/net/Makefile Thu Aug 13 15:25:01 1998 @@ -138,6 +138,9 @@ ifeq ($(CONFIG_ATM_LANE),m) MOD_SUB_DIRS += atm endif +ifeq ($(CONFIG_ATM_MPOA),m) + MOD_SUB_DIRS += atm +endif endif ifeq ($(CONFIG_DECNET),y) diff -ur --new-file old/linux/net/atm/Makefile new/linux/net/atm/Makefile --- old/linux/net/atm/Makefile Thu Aug 13 15:24:21 1998 +++ new/linux/net/atm/Makefile Thu Aug 13 15:25:01 1998 @@ -35,18 +35,30 @@ O_OBJS += $(NEED_IPCOM) ifeq ($(CONFIG_PROC_FS),y) -O_OBJS += proc.o +OX_OBJS += proc.o endif ifeq ($(CONFIG_ATM_LANE),y) -O_OBJS += lec.o +O_OBJS += lec.o lane_mpoa_init.o else ifeq ($(CONFIG_ATM_LANE),m) + O_OBJS += lane_mpoa_init.o M_OBJS += lec.o endif endif +ifeq ($(CONFIG_ATM_MPOA),y) +O_OBJS += mpc.o mpoa_caches.o mpoa_proc.o +else + ifeq ($(CONFIG_ATM_MPOA),m) + M_OBJS += mpoa.o + endif +endif + endif include $(TOPDIR)/Rules.make + +mpoa.o: mpc.o mpoa_caches.o mpoa_proc.o + ld -r -o mpoa.o mpc.o mpoa_caches.o mpoa_proc.o diff -ur --new-file old/linux/net/atm/common.c new/linux/net/atm/common.c --- old/linux/net/atm/common.c Thu Aug 13 15:24:21 1998 +++ new/linux/net/atm/common.c Thu Aug 13 15:25:01 1998 @@ -36,6 +36,22 @@ #include #include "lec.h" #include "lec_arpc.h" +struct atm_lane_ops atm_lane_ops; +#endif +#ifdef CONFIG_ATM_LANE_MODULE +EXPORT_SYMBOL(atm_lane_ops); +#endif + +#if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE) +#include +#include "mpc.h" +struct atm_mpoa_ops atm_mpoa_ops; +#endif +#ifdef CONFIG_ATM_MPOA_MODULE +EXPORT_SYMBOL(atm_mpoa_ops); +#ifndef CONFIG_ATM_LANE_MODULE +EXPORT_SYMBOL(atm_lane_ops); +#endif #endif #ifdef CONFIG_ATM_TCP @@ -57,40 +73,6 @@ #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) #else #define DPRINTK(format,args...) - -#endif -/* - * The attach functions are used in common.c::atm_ioctl(), - * ptr_to_get_dev_lec is used in proc.c::atm_info(). - * The functions are defined in lec.c. - * When lec module initializes itself it calls atm_lane_setup - * in lec.c::init_module(). - */ -#ifdef CONFIG_ATM_LANE -static int (*ptr_to_lecd_attach)(struct atm_vcc *vcc, int arg) = lecd_attach; -static int (*ptr_to_lec_mcast_attach)(struct atm_vcc *vcc, int arg) = lec_mcast_attach; -static int (*ptr_to_lec_vcc_attach)(struct atm_vcc *vcc, void *arg) = lec_vcc_attach; -struct device **(*ptr_to_get_dev_lec)(void) = get_dev_lec; -#endif - -#ifdef CONFIG_ATM_LANE_MODULE -static int (*ptr_to_lecd_attach)(struct atm_vcc *vcc, int arg) = NULL; -static int (*ptr_to_lec_mcast_attach)(struct atm_vcc *vcc, int arg) = NULL; -static int (*ptr_to_lec_vcc_attach)(struct atm_vcc *vcc, void *arg) = NULL; -struct device **(*ptr_to_get_dev_lec)(void) = NULL; - -void atm_lane_setup (int (*attach)(struct atm_vcc *vcc, int arg), - int (*mcast)(struct atm_vcc *vcc, int arg), - int (*vcc)(struct atm_vcc *vcc, void *arg), - struct device **(*dev_lec_ptr)(void)) { - ptr_to_lecd_attach = attach; - ptr_to_lec_mcast_attach = mcast; - ptr_to_lec_vcc_attach = vcc; - ptr_to_get_dev_lec = dev_lec_ptr; - -} - -EXPORT_SYMBOL(atm_lane_setup); #endif @@ -191,6 +173,9 @@ } +EXPORT_SYMBOL(atm_async_release_vcc); + + static int adjust_tp(struct atm_trafprm *tp,unsigned char aal) { int max_sdu; @@ -649,19 +634,33 @@ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) case ATMLEC_CTRL: if (!suser()) return -EPERM; - if (ptr_to_lecd_attach == NULL) { - printk ("no lec, try insmod lec first\n"); - return -ENOSYS; - } - error = ptr_to_lecd_attach(vcc, (int)arg); + if (atm_lane_ops.lecd_attach == NULL) + atm_lane_init(); + if (atm_lane_ops.lecd_attach == NULL) /* try again */ + return -ENOSYS; + error = atm_lane_ops.lecd_attach(vcc, (int)arg); if (error >= 0) sock->state = SS_CONNECTED; return error; case ATMLEC_MCAST: if (!suser()) return -EPERM; - return ptr_to_lec_mcast_attach(vcc, (int)arg); + return atm_lane_ops.mcast_attach(vcc, (int)arg); case ATMLEC_DATA: if (!suser()) return -EPERM; - return ptr_to_lec_vcc_attach(vcc, (void*)arg); + return atm_lane_ops.vcc_attach(vcc, (void*)arg); +#endif +#if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE) + case ATMMPC_CTRL: + if (!suser()) return -EPERM; + if (atm_mpoa_ops.mpoad_attach == NULL) + atm_mpoa_init(); + if (atm_mpoa_ops.mpoad_attach == NULL) /* try again */ + return -ENOSYS; + error = atm_mpoa_ops.mpoad_attach(vcc, (int)arg); + if (error >= 0) sock->state = SS_CONNECTED; + return error; + case ATMMPC_DATA: + if (!suser()) return -EPERM; + return atm_mpoa_ops.vcc_attach(vcc, arg); #endif #ifdef CONFIG_ATM_TCP case SIOCSIFATMTCP: diff -ur --new-file old/linux/net/atm/lane_mpoa_init.c new/linux/net/atm/lane_mpoa_init.c --- old/linux/net/atm/lane_mpoa_init.c Thu Jan 1 01:00:00 1970 +++ new/linux/net/atm/lane_mpoa_init.c Thu Aug 13 15:25:01 1998 @@ -0,0 +1,44 @@ +#include +#include + +#include "mpc.h" +#include "lec.h" + +/* + * lane_mpoa_init.c: A couple of helper functions + * to make modular LANE and MPOA client easier to implement + */ + +/* + * This is how it goes: + * + * if xxxx is not compiled as module, call atm_xxxx_init_ops() + * from here + * else call atm_mpoa_init_ops() from init_module() within + * the kernel when xxxx module is loaded + * + * In either case function pointers in struct atm_xxxx_ops + * are initialized to their correct values. Either they + * point to functions in the module or in the kernel + */ + +extern struct atm_mpoa_ops atm_mpoa_ops; /* in common.c */ +extern struct atm_lane_ops atm_lane_ops; /* in common.c */ + +void atm_mpoa_init(void) +{ +#ifndef CONFIG_ATM_MPOA_MODULE /* not module */ + atm_mpoa_init_ops(&atm_mpoa_ops); +#endif + + return; +} + +void atm_lane_init(void) +{ +#ifndef CONFIG_ATM_LANE_MODULE /* not module */ + atm_lane_init_ops(&atm_lane_ops); +#endif + + return; +} diff -ur --new-file old/linux/net/atm/lec.c new/linux/net/atm/lec.c --- old/linux/net/atm/lec.c Thu Aug 13 15:24:21 1998 +++ new/linux/net/atm/lec.c Thu Aug 13 15:25:01 1998 @@ -30,8 +30,7 @@ #include "lec.h" #include "lec_arpc.h" #include "tunable.h" -#include "resources.h" - +#include "resources.h" /* for bind_vcc() */ #define DPRINTK(format,args...) /* @@ -49,11 +48,21 @@ static int lec_init(struct device *dev); static __inline__ struct lec_arp_table* lec_arp_find(struct lec_priv *priv, unsigned char *mac_addr); +static __inline__ int lec_arp_remove(struct lec_arp_table **lec_arp_tables, + struct lec_arp_table *to_remove); /* LANE2 functions */ -void lane2_associate_ind (struct lec_priv *priv, u8 *mac_address, +static void lane2_associate_ind (struct device *dev, u8 *mac_address, u8 *tlvs, u32 sizeoftlvs); -int lane2_resolve(u8 *dst_mac, int force, struct device *dev, +static int lane2_resolve(struct device *dev, u8 *dst_mac, int force, u8 **tlvs, u32 *sizeoftlvs); +static int lane2_associate_req (struct device *dev, u8 *lan_dst, + u8 *tlvs, u32 sizeoftlvs); + +static struct lane2_ops lane2_ops = { + lane2_resolve, /* resolve, spec 3.1.3 */ + lane2_associate_req, /* associate_req, spec 3.1.4 */ + NULL /* associate indicator, spec 3.1.5 */ +}; /* will be lec0, lec1, lec2 etc. */ static char myname[] = "lecx"; @@ -87,21 +96,6 @@ return 0; } -void testi(struct sk_buff *skb, struct device *dev) -{ - char dst_mac[] = {0,2,3,4,5,0}; - u8 *tlvs = kmalloc(6, GFP_KERNEL); - size_t sizeoftlvs = 6; - tlvs[0] = 1; - tlvs[1] = 2; - tlvs[2] = 3; - tlvs[3] = 4; - tlvs[4] = 1; - tlvs[5] = 42; - - lane2_resolve(dst_mac, 1, dev, &tlvs, &sizeoftlvs); - kfree(tlvs); -} static int lec_send_packet(struct sk_buff *skb, struct device *dev) @@ -115,7 +109,6 @@ int i=0; #endif /* DUMP_PACKETS >0 */ - /* testi(skb, dev); */ DPRINTK("Lec_send_packet called\n"); if (!priv->lecd) { printk("%s:No lecd attached\n",dev->name); @@ -325,10 +318,14 @@ struct device *dev = (struct device*)vcc->proto_data; struct lec_priv *priv = (struct lec_priv*)dev->priv; struct atmlec_msg *mesg; + struct lec_arp_table *entry; int i; + char *tmp; /* FIXME */ atomic_sub(skb->truesize+ATM_PDU_OVHD, &vcc->tx_inuse); mesg = (struct atmlec_msg *)skb->data; + tmp = skb->data; + tmp += sizeof(struct atmlec_msg); DPRINTK("%s: msg from zeppelin:%d\n", dev->name, mesg->type); switch(mesg->type) { case l_set_mac_addr: @@ -351,15 +348,24 @@ case l_flush_complete: lec_flush_complete(priv, mesg->content.normal.flag); break; + case l_narp_req: /* LANE2: see 7.1.35 in the lane2 spec */ + entry = lec_arp_find(priv, mesg->content.normal.mac_addr); + lec_arp_remove(priv->lec_arp_tables, entry); + + if (mesg->content.normal.no_source_le_narp) + break; + /* FALL THROUGH */ case l_arp_update: lec_arp_update(priv, mesg->content.normal.mac_addr, mesg->content.normal.atm_addr, - mesg->content.normal.flag); + mesg->content.normal.flag, + mesg->content.normal.targetless_le_arp); + DPRINTK("lec: in l_arp_update\n"); if (mesg->sizeoftlvs != 0) { /* LANE2 3.1.5 */ - printk("lec: LANE2 3.1.5, got tlvs\n"); - lane2_associate_ind(priv, + DPRINTK("lec: LANE2 3.1.5, got tlvs\n"); + lane2_associate_ind(dev, mesg->content.normal.mac_addr, - (u8 *)mesg+1, mesg->sizeoftlvs); + tmp, mesg->sizeoftlvs); } break; case l_config: @@ -377,6 +383,10 @@ priv->flush_timeout = (mesg->content.config.flush_timeout*HZ); priv->path_switching_delay = (mesg->content.config.path_switching_delay*HZ); + priv->lane_version = mesg->content.config.lane_version; /* LANE2 */ + priv->lane2_ops = NULL; + if (priv->lane_version > 1) + priv->lane2_ops = &lane2_ops; break; case l_flush_tran_id: lec_set_flush_tran_id(priv, mesg->content.normal.atm_addr, @@ -413,8 +423,8 @@ printk("%s lec_atm_close: closing with messages pending\n", dev->name); while ((skb = skb_dequeue(&vcc->recvq))) { - dev_kfree_skb(skb); atm_return(vcc, skb->truesize); + dev_kfree_skb(skb); } printk("%s: Shut down!\n", dev->name); @@ -476,8 +486,12 @@ mesg = (struct atmlec_msg *)skb->data; memset(mesg, 0, sizeof(struct atmlec_msg)); mesg->type = type; + if (data != NULL) + mesg->sizeoftlvs = data->len; if (mac_addr) memcpy(&mesg->content.normal.mac_addr, mac_addr, ETH_ALEN); + else + mesg->content.normal.targetless_le_arp = 1; if (atm_addr) memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN); @@ -487,10 +501,9 @@ wake_up(&priv->lecd->sleep); if (data != NULL) { - printk("lec: about to send %d bytes of data\n", data->len); - mesg->sizeoftlvs = data->len; + DPRINTK("lec: about to send %d bytes of data\n", data->len); if (atm_charge(priv->lecd, data->truesize) == 0) - DPRINTK("lec: send_to_lecd, atm_charge()\n"); + printk("lec: send_to_lecd, atm_charge()\n"); skb_queue_tail(&priv->lecd->recvq, data); wake_up(&priv->lecd->sleep); } @@ -672,8 +685,9 @@ return -EADDRINUSE; } lec_arp_init(priv); + priv->itfnum = i; /* LANE2 addition */ priv->lecd = vcc; - bind_vcc(vcc,&lecatm_dev); + bind_vcc(vcc, &lecatm_dev); vcc->proto_data = dev_lec[i]; vcc->flags |= ATM_VF_READY | ATM_VF_META; @@ -693,58 +707,69 @@ if (dev_lec[i]->flags & IFF_UP) { dev_lec[i]->tbusy = 0; dev_lec[i]->start = 1; - printk("lec.c: lecd_attach() upping device\n"); } MOD_INC_USE_COUNT; return i; } -#ifdef MODULE -#include -extern void atm_lane_setup (int (*attach)(struct atm_vcc *vcc, int arg), - int (*mcast)(struct atm_vcc *vcc, int arg), - int (*vcc)(struct atm_vcc *vcc, void *arg), - struct device **(*dev_lec_ptr)(void)); -#ifndef LEC_VERSION -#define LEC_VERSION "0.34" -#endif +void atm_lane_init_ops(struct atm_lane_ops *ops) +{ + ops->lecd_attach = lecd_attach; + ops->mcast_attach = lec_mcast_attach; + ops->vcc_attach = lec_vcc_attach; + ops->get_lecs = get_dev_lec; + printk("lec.c: " __DATE__ " " __TIME__ " initialized\n"); + + return; +} + +#ifdef MODULE int init_module(void) { - printk("lec.c: v. %s.\n", LEC_VERSION); + extern struct atm_lane_ops atm_lane_ops; + + atm_lane_init_ops(&atm_lane_ops); - atm_lane_setup(lecd_attach, lec_mcast_attach, - lec_vcc_attach, get_dev_lec); return 0; } void cleanup_module(void) { + int i; + extern struct atm_lane_ops atm_lane_ops; + if (MOD_IN_USE) { - printk(KERN_NOTICE "lec: Device busy, remove delayed.\n"); + printk(KERN_NOTICE "lec.c: module in use\n"); + return; } - else { - int i; - atm_lane_setup(NULL, NULL, NULL, NULL); - for (i = 0; i < MAX_LEC_ITF; i++) { - if (dev_lec[i] != NULL) { - unregister_netdev(dev_lec[i]); - kfree(dev_lec[i]->priv); - kfree(dev_lec[i]); - dev_lec[i] = NULL; - } + + atm_lane_ops.lecd_attach = NULL; + atm_lane_ops.mcast_attach = NULL; + atm_lane_ops.vcc_attach = NULL; + atm_lane_ops.get_lecs = NULL; + + for (i = 0; i < MAX_LEC_ITF; i++) { + if (dev_lec[i] != NULL) { + unregister_netdev(dev_lec[i]); + kfree(dev_lec[i]->priv); + kfree(dev_lec[i]); + dev_lec[i] = NULL; } - return; } -} + return; +} #endif /* MODULE */ /* * LANE2: 3.1.3, LE_RESOLVE.request * Non force allocates memory and fills in *tlvs, fills in *sizeoftlvs. + * If sizeoftlvs == NULL the default TLVs associated with with this + * lec will be used. + * If dst_mac == NULL, targetless LE_ARP will be sent */ -int lane2_resolve(u8 *dst_mac, int force, struct device *dev, u8 **tlvs, u32 *sizeoftlvs) +static int lane2_resolve(struct device *dev, u8 *dst_mac, int force, u8 **tlvs, u32 *sizeoftlvs) { struct lec_priv *priv = (struct lec_priv *)dev->priv; struct lec_arp_table *table; @@ -766,13 +791,17 @@ return 0; } - skb = alloc_skb(*sizeoftlvs, GFP_ATOMIC); - if (skb == NULL) - return -1; - skb->len = *sizeoftlvs; - memcpy(skb->data, *tlvs, *sizeoftlvs); - retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, skb); - + if (sizeoftlvs == NULL) + retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, NULL); + + else { + skb = alloc_skb(*sizeoftlvs, GFP_ATOMIC); + if (skb == NULL) + return -1; + skb->len = *sizeoftlvs; + memcpy(skb->data, *tlvs, *sizeoftlvs); + retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, skb); + } return retval; } @@ -781,16 +810,17 @@ * LANE2: 3.1.4, LE_ASSOCIATE.request * Associate the *tlvs with the *lan_dst address. * Will overwrite any previous association - * Returns 1 for success, 0 for fail (out of memory, wrong lan_dst) + * Returns 1 for success, 0 for failure (out of memory) * - * Do we need to check the arp table for *lan_dst? */ -int lane2_associate_req (struct device *dev, u8 *lan_dst, +static int lane2_associate_req (struct device *dev, u8 *lan_dst, u8 *tlvs, u32 sizeoftlvs) { + int retval; + struct sk_buff *skb; struct lec_priv *priv = (struct lec_priv*)dev->priv; - if ( memcmp(lan_dst, dev->dev_addr, 6) != 0 ) + if ( memcmp(lan_dst, dev->dev_addr, ETH_ALEN) != 0 ) return (0); /* not our mac address */ kfree(priv->tlvs); /* NULL if there was no previous association */ @@ -801,6 +831,14 @@ priv->sizeoftlvs = sizeoftlvs; memcpy(priv->tlvs, tlvs, sizeoftlvs); + skb = alloc_skb(sizeoftlvs, GFP_ATOMIC); + if (skb == NULL) + return 0; + skb->len = sizeoftlvs; + memcpy(skb->data, tlvs, sizeoftlvs); + retval = send_to_lecd(priv, l_associate_req, NULL, NULL, skb); + if (retval != 0) + printk("lec.c: lane2_associate_req() failed\n"); /* If the previous association has changed we must * somehow notify other LANE entities about the change */ @@ -811,9 +849,10 @@ * LANE2: 3.1.5, LE_ASSOCIATE.indication * */ -void lane2_associate_ind (struct lec_priv *priv, u8 *mac_addr, u8 *tlvs, u32 sizeoftlvs) +static void lane2_associate_ind (struct device *dev, u8 *mac_addr, u8 *tlvs, u32 sizeoftlvs) { int i = 0; + struct lec_priv *priv = (struct lec_priv *)dev->priv; struct lec_arp_table *entry = lec_arp_find(priv, mac_addr); if (entry == NULL) @@ -828,18 +867,22 @@ entry->sizeoftlvs = sizeoftlvs; memcpy(entry->tlvs, tlvs, sizeoftlvs); - printk("lec.c: lane2_associate_ind() associated\n "); +#if 0 + printk("lec.c: lane2_associate_ind()\n"); printk("dump of tlvs, sizeoftlvs=%d\n", sizeoftlvs); while (i < sizeoftlvs) printk("%02x ", tlvs[i++]); printk("\n"); +#endif - - - - - + /* tell MPOA about the TLVs we saw */ + if (priv->lane2_ops && priv->lane2_ops->associate_indicator) { + priv->lane2_ops->associate_indicator(dev, mac_addr, + tlvs, sizeoftlvs); + } + else + printk("lane:(%s) lane2_associate_ind: could not notify MPOA\n", dev->name); return; } @@ -915,20 +958,27 @@ { if (entry->vcc) { entry->vcc->push = entry->old_push; +#if 0 /* August 6, 1998 */ entry->vcc->flags |= ATM_VF_RELEASED; entry->vcc->flags &= ~ATM_VF_READY; +#endif + atm_async_release_vcc(entry->vcc, -EPIPE); entry->vcc = NULL; } if (entry->recv_vcc) { entry->recv_vcc->push = entry->old_recv_push; +#if 0 entry->recv_vcc->flags |= ATM_VF_RELEASED; entry->recv_vcc->flags &= ~ATM_VF_READY; +#endif + atm_async_release_vcc(entry->recv_vcc, -EPIPE); entry->recv_vcc = NULL; } } /* * Insert entry to lec_arp_table + * LANE2: Add to the end of the list to satisfy 8.1.13 */ static __inline__ void lec_arp_put(struct lec_arp_table **lec_arp_tables, @@ -936,14 +986,23 @@ { unsigned short place; unsigned long flags; + struct lec_arp_table *tmp; save_flags(flags); cli(); place = HASH(to_put->mac_addr[ETH_ALEN-1]); - to_put->next = lec_arp_tables[place]; - lec_arp_tables[place] = to_put; + tmp = lec_arp_tables[place]; + to_put->next = NULL; + if (tmp == NULL) + lec_arp_tables[place] = to_put; + else { /* add to the end */ + while (tmp->next) + tmp = tmp->next; + tmp->next = to_put; + } + restore_flags(flags); DPRINTK("LEC_ARP: Added entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n", 0xff&to_put->mac_addr[0], 0xff&to_put->mac_addr[1], @@ -1362,8 +1421,9 @@ DPRINTK("About to expire: %lx - %lx > %lx\n", now,entry->last_used, time_to_check); - if((now-entry->last_used > time_to_check) && - !(entry->flags & LEC_PERMANENT_FLAG)) { + if( (now-entry->last_used > time_to_check) && + !(entry->flags & LEC_PERMANENT_FLAG) && + !(entry->mac_addr[0] & 0x01) ) { /* LANE2: 7.1.20 */ /* Remove entry */ DPRINTK("LEC:Entry timed out\n"); next = entry->next; @@ -1404,11 +1464,23 @@ struct atm_vcc* lec_arp_resolve(struct lec_priv *priv, unsigned char *mac_to_find) { + unsigned char bus_mac[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff}; struct lec_arp_table *entry; if (mac_to_find[0]&0x01) { - return priv->mcast_vcc; + switch (priv->lane_version) { + case 1: + return priv->mcast_vcc; + break; + case 2: /* LANE2 wants arp for multicast addresses */ + if ( memcmp(mac_to_find, &bus_mac, ETH_ALEN) == 0) + return priv->mcast_vcc; + break; + default: + break; + } } + entry = lec_arp_find(priv, mac_to_find); if (entry) { @@ -1485,14 +1557,22 @@ */ void lec_arp_update(struct lec_priv *priv, unsigned char *mac_addr, - unsigned char *atm_addr, unsigned long remoteflag) + unsigned char *atm_addr, unsigned long remoteflag, + unsigned int targetless_le_arp) { struct lec_arp_table *entry, *tmp; int i; - DPRINTK("LEC:lec_arp_update mac:%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", + DPRINTK("lec:%s", (targetless_le_arp) ? "targetless ": " "); + DPRINTK("lec_arp_update mac:%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", mac_addr[0],mac_addr[1],mac_addr[2],mac_addr[3], mac_addr[4],mac_addr[5]); + + entry = lec_arp_find(priv, mac_addr); + if (entry == NULL && targetless_le_arp) + return; /* LANE2: ignore targetless LE_ARPs for which + * we have no entry in the cache. 7.1.30 + */ lec_arp_lock(priv); if (priv->lec_arp_empty_ones) { entry = priv->lec_arp_empty_ones; diff -ur --new-file old/linux/net/atm/lec.h new/linux/net/atm/lec.h --- old/linux/net/atm/lec.h Thu Aug 13 15:24:21 1998 +++ new/linux/net/atm/lec.h Thu Aug 13 15:25:01 1998 @@ -31,6 +31,32 @@ }; /* + * Operations that LANE2 capable device can do. Two first functions + * are used to make the device do things. See spec 3.1.3 and 3.1.4. + * + * The third function is intented for the MPOA component sitting on + * top of the LANE device. The MPOA component assigns it's own function + * to (*associate_indicator)() and the LANE device will use that + * function to tell about TLVs it sees floating through. + * + */ +struct lane2_ops { + int (*resolve)(struct device *dev, u8 *dst_mac, int force, + u8 **tlvs, u32 *sizeoftlvs); + int (*associate_req)(struct device *dev, u8 *lan_dst, + u8 *tlvs, u32 sizeoftlvs); + void (*associate_indicator)(struct device *dev, u8 *mac_addr, + u8 *tlvs, u32 sizeoftlvs); +}; + +struct atm_lane_ops { + int (*lecd_attach)(struct atm_vcc *vcc, int arg); + int (*mcast_attach)(struct atm_vcc *vcc, int arg); + int (*vcc_attach)(struct atm_vcc *vcc, void *arg); + struct device **(*get_lecs)(void); +}; + +/* * ATM LAN Emulation supports both LLC & Dix Ethernet EtherType * frames. * 1. Dix Ethernet EtherType frames encoded by placing EtherType @@ -94,9 +120,11 @@ delivered to the recipient (C22) */ unsigned long path_switching_delay; - u8 *tlvs; /* LANE2: TLVs are new */ - u32 sizeoftlvs; /* The size of the tlv array in bytes */ - + u8 *tlvs; /* LANE2: TLVs are new */ + u32 sizeoftlvs; /* The size of the tlv array in bytes */ + int lane_version; /* LANE2 */ + int itfnum; /* e.g. 2 for lec2, 5 for lec5 */ + struct lane2_ops *lane2_ops; /* can be NULL for LANE v1 */ }; int lecd_attach(struct atm_vcc *vcc, int arg); @@ -108,5 +136,8 @@ atmlec_msg_type type, unsigned char *mac_addr, unsigned char *atm_addr, struct sk_buff *data); void lec_push(struct atm_vcc *vcc, struct sk_buff *skb); + +void atm_lane_init(void); +void atm_lane_init_ops(struct atm_lane_ops *ops); #endif _LEC_H_ diff -ur --new-file old/linux/net/atm/lec_arpc.h new/linux/net/atm/lec_arpc.h --- old/linux/net/atm/lec_arpc.h Thu Aug 13 15:24:21 1998 +++ new/linux/net/atm/lec_arpc.h Thu Aug 13 15:25:01 1998 @@ -105,7 +105,7 @@ void lec_flush_complete(struct lec_priv *priv, unsigned long tran_id); void lec_arp_update(struct lec_priv *priv, unsigned char *mac_addr, unsigned char *atm_addr, - unsigned long remoteflag); + unsigned long remoteflag, unsigned int targetless_le_arp); void lec_set_flush_tran_id(struct lec_priv *priv, unsigned char *mac_addr, unsigned long tran_id); diff -ur --new-file old/linux/net/atm/mpc.c new/linux/net/atm/mpc.c --- old/linux/net/atm/mpc.c Thu Jan 1 01:00:00 1970 +++ new/linux/net/atm/mpc.c Thu Aug 13 15:25:01 1998 @@ -0,0 +1,1269 @@ +#include +#include +#include +#include + +/* We are an ethernet device */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for ip_fast_csum() */ +#include +#include +#include + +/* And atm device */ +#include +#include +#include +/* Modular too */ +#include +#include + +#include "lec.h" +#include "mpc.h" +#include "tunable.h" +#include "resources.h" /* for bind_vcc() */ + +/* + * mpc.c: Implementation of MPOA client kernel part + */ + +#if 1 +#define dprintk printk /* debug */ +#else +#define dprintk(format,args...) +#endif + +#if 0 +#define ddprintk printk /* more debug */ +#else +#define ddprintk(format,args...) +#endif + + + +#define MPOA_TAG_LEN 4 + +/* mpc_daemon -> kernel */ +static void MPOA_trigger_rcvd (struct k_message *msg, struct mpoa_client *mpc); +static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc); +static void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc); +static void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc); +static void mps_death(struct k_message *msg, struct mpoa_client *mpc); +static void clean_up(struct k_message *msg, struct mpoa_client *mpc); +static void MPOA_cache_impos_rcvd(struct k_message *msg, struct mpoa_client *mpc); +static void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, struct mpoa_client *mpc); +static void set_mps_mac_addr_rcvd(struct k_message *mesg, struct mpoa_client *mpc); + +static void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry); + +static void mpoad_close(struct atm_vcc *vcc); +static int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb); + +static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb); +static int mpc_send_packet(struct sk_buff *skb, struct device *dev); +static int mpoa_event_listener(struct notifier_block *mpoa_notifier, unsigned long event, void *dev); +static void mpc_timer_refresh(void); +static void mpc_cache_check( unsigned long checking_time ); + +static struct llc_snap_hdr llc_snap_mpoa_ctrl = { + 0xaa, 0xaa, 0x03, + {0x00, 0x00, 0x5e}, + {0x00, 0x03} /* For MPOA control PDUs */ +}; +static struct llc_snap_hdr llc_snap_mpoa_data = { + 0xaa, 0xaa, 0x03, + {0x00, 0x00, 0x00}, + {0x08, 0x00} /* This is for IP PDUs only */ +}; +static struct llc_snap_hdr llc_snap_mpoa_data_tagged = { + 0xaa, 0xaa, 0x03, + {0x00, 0x00, 0x00}, + {0x88, 0x4c} /* This is for tagged data PDUs */ +}; + +static struct notifier_block mpoa_notifier = { + mpoa_event_listener, + NULL, + 0 +}; + +#ifdef CONFIG_PROC_FS +extern int mpc_proc_init(void); +extern void mpc_proc_clean(void); +#endif + +struct mpoa_client *mpcs = NULL; /* FIXME */ +static struct timer_list mpc_timer; + + +static struct mpoa_client *find_mpc_by_itfnum(int itf) +{ + struct mpoa_client *mpc; + + mpc = mpcs; /* our global linked list */ + while (mpc != NULL) { + if (mpc->dev_num == itf) + return mpc; + mpc = mpc->next; + } + + return NULL; /* not found */ +} + +static struct mpoa_client *find_mpc_by_vcc(struct atm_vcc *vcc) +{ + struct mpoa_client *mpc; + + mpc = mpcs; /* our global linked list */ + while (mpc != NULL) { + if (mpc->mpoad_vcc == vcc) + return mpc; + mpc = mpc->next; + } + + return NULL; /* not found */ +} + +static struct mpoa_client *find_mpc_by_lec(struct device *dev) +{ + struct mpoa_client *mpc; + + mpc = mpcs; /* our global linked list */ + while (mpc != NULL) { + if (mpc->dev == dev) + return mpc; + mpc = mpc->next; + } + + return NULL; /* not found */ +} + +static struct device *find_lec_by_itfnum(int itf) +{ + extern struct atm_lane_ops atm_lane_ops; /* in common.c */ + + if (atm_lane_ops.get_lecs == NULL) + return NULL; + + return atm_lane_ops.get_lecs()[itf]; /* FIXME: something better */ +} + +static struct mpoa_client *alloc_mpc(void) +{ + struct mpoa_client *mpc; + + mpc = kmalloc(sizeof (struct mpoa_client), GFP_KERNEL); + if (mpc == NULL) + return NULL; + memset(mpc, 0, sizeof(struct mpoa_client)); +#if 0 + mpc->ingress_lock = RW_LOCK_UNLOCKED; + mpc->egress_lock = RW_LOCK_UNLOCKED; +#endif + mpc->next = mpcs; + atm_mpoa_init_cache(mpc); + mpcs = mpc; + + return mpc; +} + +/* + * + * start_mpc() puts the MPC on line. All the packets destined + * to the lec underneath us are now being monitored and + * shortcuts will be established. + * + */ +static void start_mpc(struct mpoa_client *mpc, struct device *dev) +{ + + printk("mpoa: (%s) start_mpc:\n", mpc->dev->name); + if (dev->hard_start_xmit == NULL) { + printk("mpoa: (%s) start_mpc: dev->hard_start_xmit == NULL, not starting\n", + dev->name); + return; + } + mpc->old_hard_start_xmit = dev->hard_start_xmit; + dev->hard_start_xmit = mpc_send_packet; + + return; +} + +static void stop_mpc(struct mpoa_client *mpc) +{ + + printk("mpoa: (%s) stop_mpc:", mpc->dev->name); + + /* Lets not nullify lec device's dev->hard_start_xmit */ + if (mpc->dev->hard_start_xmit != mpc_send_packet) { + printk(" mpc already stopped, not fatal\n"); + return; + } + printk("\n"); + mpc->dev->hard_start_xmit = mpc->old_hard_start_xmit; + mpc->old_hard_start_xmit = NULL; + /* close_shortcuts(mpc); ??? FIXME */ + + return; +} + +static const char *mpoa_device_type_string (char type) +{ + switch(type) { + case NON_MPOA: + return "non-MPOA device"; + break; + case MPS: + return "MPS"; + break; + case MPC: + return "MPC"; + break; + case MPS_AND_MPC: + return "both MPS and MPC"; + break; + default: + return "unspecified (non-MPOA) device"; + break; + } + + return ""; /* not reached */ +} + +/* + * lec device calls this via its dev->priv->lan2_ops->associate_indicator() + * when it sees a TLV in LE_ARP packet. + * We fill in the pointer above when we see a LANE2 lec initializing + * See LANE2 spec 3.1.5 + * + * Quite a big and ugly function but when you look at it + * all it does is to try to locate and parse MPOA Device + * Type TLV. + * We give our lec a pointer to this function and when the + * lec sees a TLV it uses the pointer to call this function. + * + */ +static void lane2_assoc_ind(struct device *dev, uint8_t *mac_addr, + uint8_t *tlvs, uint32_t sizeoftlvs) +{ + uint32_t type; + uint8_t length, mpoa_device_type, number_of_mps_macs; + struct k_message *mesg; + struct sk_buff *skb; + uint8_t *end_of_tlvs; + struct mpoa_client *mpc; + + mpoa_device_type = number_of_mps_macs = 0; /* silence gcc */ + dprintk("mpoa: (%s) lane2_assoc_ind: received TLV(s), ", dev->name); + dprintk("total length of all TLVs %d\n", sizeoftlvs); + + end_of_tlvs = tlvs + sizeoftlvs; + while (end_of_tlvs - tlvs >= 5) { + type = (tlvs[0] << 24) | (tlvs[1] << 16) | (tlvs[2] << 8) | tlvs[3]; + length = tlvs[4]; + tlvs += 5; + dprintk(" type 0x%x length %02x\n", type, length); + + if (type == 0) { + printk("mpoa: (%s) lane2_assoc_ind: TLV type was 0, returning\n", dev->name); + return; + } + + if (type != TLV_MPOA_DEVICE_TYPE) { + tlvs += length; + continue; /* skip other TLVs */ + } + mpoa_device_type = *tlvs++; + number_of_mps_macs = *tlvs++; + printk("mpoa: (%s) MPOA device type '%s', ", dev->name, mpoa_device_type_string(mpoa_device_type)); + if (mpoa_device_type == MPS_AND_MPC && + length < (42 + number_of_mps_macs*ETH_ALEN)) { /* :) */ + printk("\nmpoa: (%s) lane2_assoc_ind: short MPOA Device Type TLV\n", + dev->name); + continue; + } + if ((mpoa_device_type == MPS || mpoa_device_type == MPC) + && length < 22 + number_of_mps_macs*ETH_ALEN) { + printk("\nmpoa: (%s) lane2_assoc_ind: short MPOA Device Type TLV\n", + dev->name); + continue; + } + if (mpoa_device_type != MPS && mpoa_device_type != MPS_AND_MPC) { + printk("ignoring non-MPS device\n"); + if (mpoa_device_type == MPC) tlvs += 20; + continue; /* we are only interested in MPSs */ + } + if (number_of_mps_macs == 0 && mpoa_device_type == MPS_AND_MPC) { + printk("\nmpoa: (%s) lane2_assoc_ind: MPS_AND_MPC has zero MACs\n", dev->name); + continue; /* someone should read the spec */ + } + printk("this MPS has %d MAC addresses\n", number_of_mps_macs); + + /* ok, now we can go and tell our daemon the control address of MPS */ + mpc = find_mpc_by_lec(dev); + skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); + if (skb == NULL) { + printk("mpoa: (%s) lane2_assoc_ind: alloc_skb()\n", dev->name); + return; + } + memcpy (mpc->mps_ctrl_addr, tlvs, ATM_ESA_LEN); + + mesg = (struct k_message *)skb->data; + mesg->type = SET_MPS_CTRL_ADDR; + memcpy(mesg->MPS_ctrl, tlvs, ATM_ESA_LEN); + msg_to_mpoad(mesg, mpc); + + tlvs += 20; if (mpoa_device_type == MPS_AND_MPC) tlvs += 20; + /* collect the MPS MAC addresses */ + if (mpc->number_of_mps_macs < number_of_mps_macs) { + kfree(mpc->mps_macs); /* need more space */ + mpc->mps_macs = kmalloc(number_of_mps_macs*ETH_ALEN, GFP_KERNEL); + if (mpc->mps_macs == NULL) { + printk("mpoa: (%s) lane2_assoc_ind: out of mem\n", dev->name); + return; + } + } + while (number_of_mps_macs > 0) { + memcpy(mpc->mps_macs, tlvs, ETH_ALEN); + tlvs += ETH_ALEN; number_of_mps_macs--; + } + } + if (end_of_tlvs - tlvs != 0) + printk("mpoa: (%s) lane2_assoc_ind: ignoring %d bytes of trailing TLV carbage\n", + dev->name, end_of_tlvs - tlvs); + return; +} + +/* FIXME: tarvitsee työtä */ +static int send_via_shortcut(struct sk_buff *skb, struct mpoa_client *mpc) +{ + in_cache_entry *entry; + struct iphdr *iph; + char *buff; + uint32_t ipaddr = 0; + + static struct { + struct llc_snap_hdr hdr; + uint32_t tag; + } tagged_llc_snap_hdr = { + {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}, {0x88, 0x4c}}, + 0 + }; + + buff = skb->data + mpc->dev->hard_header_len; + iph = (struct iphdr *)buff; + ipaddr = iph->daddr; + + ddprintk("mpoa: (%s) send_via_shortcut: ipaddr 0x%x\n", mpc->dev->name, ipaddr); + + + entry = mpc->in_ops->search(ipaddr, mpc); + if (entry == NULL) { + mpc->in_ops->new_entry(ipaddr, mpc); + return 1; + } + if (mpc->in_ops->cache_hit(entry, mpc) != OPEN){ /* threshold not exceeded or VCC not ready */ + ddprintk("mpoa: (%s) send_via_shortcut() cache_hit() returns != OPEN\n", mpc->dev->name); + return 1; + } + + ddprintk("mpoa: (%s) send_via_shortcut() using shortcut\n", mpc->dev->name); + /* MPOA spec A.1.4, MPOA client must decrement IP ttl at least by one */ + if (iph->ttl <= 1) { + ddprintk("mpoa: (%s) send_via_shortcut: IP ttl = %u, using LANE\n", mpc->dev->name, iph->ttl); + return 1; + } + iph->ttl--; + iph->check = 0; + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + + if (entry->ctrl_info.tag != 0) { + ddprintk("mpoa: (%s) send_via_shortcut() adding tag 0x%x\n", mpc->dev->name, entry->ctrl_info.tag); + tagged_llc_snap_hdr.tag = entry->ctrl_info.tag; + skb_pull(skb, LEC_HEADER_LEN); /* get rid of LANE+Eth header */ + skb_push(skb, sizeof(tagged_llc_snap_hdr)); /* add LLC/SNAP header */ + memcpy(skb->data, &tagged_llc_snap_hdr, sizeof(tagged_llc_snap_hdr)); + } else { + skb_pull(skb, LEC_HEADER_LEN); /* get rid of LANE+Eth header */ + skb_push(skb, sizeof(struct llc_snap_hdr)); /* add LLC/SNAP header + tag */ + memcpy(skb->data, &llc_snap_mpoa_data, sizeof(struct llc_snap_hdr)); + } + + atomic_add(skb->truesize, &entry->shortcut->tx_inuse); + entry->shortcut->dev->ops->send(entry->shortcut, skb); + entry->packets_fwded++; + + return 0; +} + +/* + * Probably needs some error checks and locking, not sure... + */ +static int mpc_send_packet(struct sk_buff *skb, struct device *dev) +{ + int retval; + struct mpoa_client *mpc; + int i = 0; + + mpc = find_mpc_by_lec(dev); /* this should NEVER fail */ + if(mpc == NULL) { + printk("mpoa: (%s) mpc_send_packet: no MPC found\n", dev->name); + goto non_ip; + } + + if ( ((struct ethhdr *)(skb->data + 2))->h_proto != htons(ETH_P_IP) ) + goto non_ip; /* Multi-Protocol Over ATM :-) */ + + while (i < mpc->number_of_mps_macs) { + if (memcmp(skb->data + 2, (mpc->mps_macs + i*ETH_ALEN), ETH_ALEN) == 0) + if ( send_via_shortcut(skb, mpc) == 0 ) /* try shortcut */ + return 0; /* success! */ + i++; + } + + non_ip: + retval = mpc->old_hard_start_xmit(skb,dev); + + return retval; +} + +int atm_mpoa_vcc_attach(struct atm_vcc *vcc, long arg) +{ + int bytes_left; + struct mpoa_client *mpc; + struct atmmpc_ioc ioc_data; + in_cache_entry *in_entry; + uint32_t ipaddr; + unsigned char *ip; + + bytes_left = copy_from_user(&ioc_data, (void *)arg, sizeof(struct atmmpc_ioc)); + if (bytes_left != 0) { + printk("mpoa: mpc_vcc_attach: Short read (missed %d bytes) from userland\n", bytes_left); + return -EFAULT; + } + ipaddr = ioc_data.ipaddr; + if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF) + return -EINVAL; + + mpc = find_mpc_by_itfnum(ioc_data.dev_num); + if (mpc == NULL) + return -EINVAL; + + if (ioc_data.type == MPC_SOCKET_INGRESS) { + in_entry = mpc->in_ops->search(ipaddr, mpc); + if (in_entry == NULL || in_entry->entry_state < INGRESS_RESOLVED) { + printk("mpoa: (%s) mpc_vcc_attach: did not find RESOLVED entry from ingress cache\n", + mpc->dev->name); + return -EINVAL; + } + ip = (unsigned char*)&in_entry->ctrl_info.in_dst_ip; + printk("mpoa: (%s) mpc_vcc_attach() attaching ingress SVC, entry = %u.%u.%u.%u\n", + mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); + in_entry->shortcut = vcc; + } else { + printk("mpoa: (%s) mpc_vcc_attach() attaching egress SVC\n", mpc->dev->name); + } + + vcc->proto_data = mpc->dev; + vcc->push = mpc_push; + + return 0; +} + +/* + * + */ +static void mpc_vcc_close(struct atm_vcc *vcc, struct device *dev) +{ + struct mpoa_client *mpc; + in_cache_entry *in_entry; + eg_cache_entry *eg_entry; + + mpc = find_mpc_by_lec(dev); + if (mpc == NULL) { + printk("mpoa: (%s) mpc_vcc_close: close for unknown MPC\n", dev->name); + return; + } + + printk("mpoa: (%s) mpc_vcc_close:\n", dev->name); + in_entry = mpc->in_ops->search_by_vcc(vcc, mpc); + if (in_entry) { + unsigned char *ip = (unsigned char *)&in_entry->ctrl_info.in_dst_ip; + dprintk("mpoa: (%s) mpoa_vcc_close, ingress SVC closed ip = %u.%u.%u.%u\n", + mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); + in_entry->shortcut = NULL; + } + eg_entry = mpc->eg_ops->search_by_vcc(vcc, mpc); + if (eg_entry) { + dprintk("mpoa: (%s) mpoa_vcc_close, egress SVC closed\n", mpc->dev->name); + eg_entry->shortcut = NULL; + } + + if (in_entry == NULL && eg_entry == NULL) + dprintk("mpoa: (%s) mpc_vcc_close: unused vcc closed\n", dev->name); + + return; +} + +static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct device *dev = (struct device *)vcc->proto_data; + struct sk_buff *new_skb; + eg_cache_entry *eg; + struct mpoa_client *mpc; + uint32_t tag; + char *tmp; + + ddprintk("mpoa: (%s) mpc_push()\n", dev->name); + if (skb == NULL) { + printk("mpoa: (%s) mpc_push() null skb, closing VCC\n", dev->name); + mpc_vcc_close(vcc, dev); + return; + } + + skb->dev = dev; + if (memcmp(skb->data, &llc_snap_mpoa_ctrl, sizeof(struct llc_snap_hdr)) == 0) { + printk("mpoa: (%s) mpc_push() control packet arrived\n", dev->name); + skb_queue_tail(&vcc->recvq, skb); /* Pass control packets to daemon */ + wake_up(&vcc->sleep); + return; + } + + /* data coming over the shortcut */ + atm_return(vcc, skb->truesize); + + mpc = find_mpc_by_lec(dev); + if (mpc == NULL) { + printk("mpoa: (%s) mpc_push: unknown MPC\n", dev->name); + return; + } + + if (memcmp(skb->data, &llc_snap_mpoa_data_tagged, sizeof(struct llc_snap_hdr)) == 0) { /* MPOA tagged data */ + ddprintk("mpoa: (%s) mpc_push() tagged data packet arrived\n", dev->name); + + } else if (memcmp(skb->data, &llc_snap_mpoa_data, sizeof(struct llc_snap_hdr)) == 0) { /* MPOA data */ + printk("mpoa: (%s) mpc_push() non-tagged data packet arrived\n", dev->name); + printk(" mpc_push() non-tagged data unsupported, purging\n"); + kfree_skb(skb); + return; + } else { + printk("mpoa:(%s) mpc_push() garbage arrived, purging\n", dev->name); + kfree_skb(skb); + return; + } + + tmp = skb->data + sizeof(struct llc_snap_hdr); + tag = *(uint32_t *)tmp; + + eg = mpc->eg_ops->search_by_tag(tag, mpc); + if (eg == NULL) { + printk("mpoa: (%s) mpc_push: Didn't find egress cache entry, tag = %u\n", + dev->name,tag); + purge_egress_shortcut(vcc, NULL); + kfree_skb(skb); + return; + } + + /* + * See if ingress MPC is using shortcut we opened as a return channel. + * This means we have a bi-directional vcc opened by us. + */ + if (eg->shortcut == NULL) { + eg->shortcut = vcc; + printk("mpoa: (%s) mpc_push: egress SVC in use\n", dev->name); + } + + skb_pull(skb, sizeof(struct llc_snap_hdr) + sizeof(tag)); /* get rid of LLC/SNAP header */ + new_skb = skb_realloc_headroom(skb, eg->ctrl_info.DH_length); /* LLC/SNAP is shorter than MAC header :( */ + kfree_skb(skb); + skb_push(new_skb, eg->ctrl_info.DH_length); /* add MAC header */ + memcpy(new_skb->data, eg->ctrl_info.DLL_header, eg->ctrl_info.DH_length); + new_skb->mac.raw = new_skb->data; + skb_pull(new_skb, ETH_HLEN); + + tmp = eg->ctrl_info.DLL_header + 2*ETH_ALEN; + memcpy(&new_skb->protocol, tmp, sizeof(uint16_t)); + if ( ntohs(new_skb->protocol) < 1536 ) { + printk("mpoa: (%s) mpc_push() received non-DIX Ethernet frame, purging\n", + dev->name); + kfree_skb(new_skb); + return; + } + + eg->packets_rcvd++; + + netif_rx(new_skb); + + return; +} + +static struct atmdev_ops mpc_ops = { /* only send is required */ + NULL, /* dev_close */ + NULL, /* open */ + mpoad_close, /* close */ + NULL, /* ioctl */ + NULL, /* getsockopt */ + NULL, /* setsockopt */ + msg_from_mpoad, /* send */ + NULL, /* sg_send */ + NULL, /* send_oam */ + NULL, /* phy_put */ + NULL, /* phy_get */ + NULL, /* feedback */ + NULL, /* change_qos */ + NULL, /* free_rx_skb */ + NULL /* proc_read */ +}; + +static struct atm_dev mpc_dev = { + &mpc_ops, /* device operations */ + NULL, /* PHY operations */ + "mpc", /* device type name */ + 42, /* device index (dummy) */ + NULL, /* VCC table */ + NULL, /* last VCC */ + NULL, /* per-device data */ + NULL, /* private PHY data */ + 0, /* device flags */ + NULL, /* local ATM address */ + { 0 } /* no ESI */ + /* rest of the members will be 0 */ +}; + +int atm_mpoa_mpoad_attach (struct atm_vcc *vcc, int arg) +{ + struct mpoa_client *mpc; + struct lec_priv *priv; + + if (mpcs == NULL) { + init_timer(&mpc_timer); + mpc_timer_refresh(); + + /* This lets us now how our LECs are doing */ + register_netdevice_notifier(&mpoa_notifier); + } + + mpc = find_mpc_by_itfnum(arg); + if (mpc == NULL) { + printk("mpoa: mpoad_attach: allocating new mpc for itf %d\n", arg); + mpc = alloc_mpc(); + mpc->dev_num = arg; + mpc->dev = find_lec_by_itfnum(arg); /* NULL if there was no lec */ + } + if (mpc->mpoad_vcc) { + printk("mpoa: mpoad_attach: mpoad is already present for itf %d\n", arg); + return -EADDRINUSE; + } + + if (mpc->dev) { /* check if the lec is LANE2 capable */ + priv = (struct lec_priv *)mpc->dev->priv; + if (priv->lane_version < 2) + mpc->dev = NULL; + else + priv->lane2_ops->associate_indicator = lane2_assoc_ind; + } + + mpc->mpoad_vcc = vcc; + bind_vcc(vcc, &mpc_dev); + vcc->flags |= ATM_VF_READY | ATM_VF_META; + if (mpc->dev) + start_mpc(mpc, mpc->dev); + MOD_INC_USE_COUNT; + + return arg; +} + +static void mpoad_close(struct atm_vcc *vcc) +{ + unsigned long flags; + struct mpoa_client *mpc; + struct sk_buff *skb; + + mpc = find_mpc_by_vcc(vcc); + if (mpc == NULL) { + printk("mpoa: mpoad_close: did not find MPC\n"); + return; + } + if (!mpc->mpoad_vcc) { + printk("mpoa: mpoad_close: close for non-present mpoad\n"); + return; + } + + mpc->mpoad_vcc = NULL; + if (mpc->dev) { + struct lec_priv *priv = (struct lec_priv *)mpc->dev->priv; + priv->lane2_ops->associate_indicator = NULL; + stop_mpc(mpc); + } + + /* clear the caches */ + write_lock_irqsave(&mpc->ingress_lock, flags); + while(mpc->in_ops->cache_remove(mpc->in_cache, mpc)); + write_unlock_irqrestore(&mpc->ingress_lock, flags); + + write_lock_irqsave(&mpc->egress_lock, flags); + while(mpc->eg_ops->cache_remove(mpc->eg_cache, mpc)); + write_unlock_irqrestore(&mpc->egress_lock, flags); + + while ( (skb = skb_dequeue(&vcc->recvq)) ){ + atm_return(vcc, skb->truesize); + kfree_skb(skb); + } + + printk("mpoa: (%s) going down\n", + (mpc->dev) ? mpc->dev->name : ""); + MOD_DEC_USE_COUNT; + + return; +} + +/* + * + */ +static int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb) +{ + + struct mpoa_client *mpc = find_mpc_by_vcc(vcc); + struct k_message *mesg = (struct k_message*)skb->data; + atomic_sub(skb->truesize+ATM_PDU_OVHD, &vcc->tx_inuse); + + if (mpc == NULL) { + printk("mpoa: msg_from_mpoad: no mpc found\n"); + return 0; + } + printk("mpoa: (%s) msg_from_mpoad:", (mpc->dev) ? mpc->dev->name : ""); + switch(mesg->type) { + case MPOA_RES_REPLY_RCVD: + printk(" mpoa_res_reply_rcvd.\n"); + MPOA_res_reply_rcvd(mesg, mpc); + break; + case MPOA_TRIGGER_RCVD: + printk(" mpoa_trigger_rcvd.\n"); + MPOA_trigger_rcvd(mesg, mpc); + break; + case INGRESS_PURGE_RCVD: + printk(" nhrp_purge_rcvd.\n"); + ingress_purge_rcvd(mesg, mpc); + break; + case EGRESS_PURGE_RCVD: + printk(" egress_purge_reply_rcvd.\n"); + egress_purge_rcvd(mesg, mpc); + break; + case MPS_DEATH: + printk(" mps_death.\n"); + mps_death(mesg, mpc); + break; + case CACHE_IMPOS_RCVD: + printk(" cache_impos_rcvd.\n"); + MPOA_cache_impos_rcvd(mesg, mpc); + break; + case SET_MPC_CTRL_ADDR: + printk(" set_mpc_ctrl_addr\n"); + set_mpc_ctrl_addr_rcvd(mesg, mpc); + break; + case SET_MPS_MAC_ADDR: + printk(" set_mps_mac_addr\n"); + set_mps_mac_addr_rcvd(mesg, mpc); + break; + case CLEAN_UP_AND_EXIT: + printk(" clean_up_and_exit\n"); + clean_up(mesg, mpc); + break; + default: + printk(" unknown message %d\n", mesg->type); + break; + } + kfree_skb(skb); + + return 0; +} + +int msg_to_mpoad(struct k_message *mesg, struct mpoa_client *mpc) +{ + struct sk_buff *skb; + + if (mpc == NULL || !mpc->mpoad_vcc) { + printk("mpoa: msg_to_mpoad: mesg %d to a non-existant mpoad\n", mesg->type); + return -ENXIO; + } + + skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); + if (skb == NULL) + return -ENOMEM; + skb_put(skb, sizeof(struct k_message)); + memcpy(skb->data, mesg, sizeof(struct k_message)); + if (atm_charge(mpc->mpoad_vcc, skb->truesize) == 0) { + printk("mpoa: msg_to_mpoad: atm_charge failed\n"); + return -ENOMEM; + } + + skb_queue_tail(&mpc->mpoad_vcc->recvq, skb); + wake_up(&mpc->mpoad_vcc->sleep); + + return 0; +} + +static int mpoa_event_listener(struct notifier_block *mpoa_notifier, unsigned long event, void *dev_ptr) +{ + struct device *dev; + struct mpoa_client *mpc; + struct lec_priv *priv; + + dev = (struct device *)dev_ptr; + if (dev->name == NULL || strncmp(dev->name, "lec", 3)) + return NOTIFY_DONE; /* we are only interested in lec:s */ + + switch (event) { + case NETDEV_REGISTER: /* a new lec device was allocated */ + priv = (struct lec_priv *)dev->priv; + if (priv->lane_version < 2) + break; + priv->lane2_ops->associate_indicator = lane2_assoc_ind; + mpc = find_mpc_by_itfnum(priv->itfnum); + if (mpc == NULL) { + printk("mpoa: mpoa_event_listener: allocating new mpc for %s\n", + dev->name); + mpc = alloc_mpc(); + if (mpc == NULL) { + printk("mpoa: mpoa_event_listener: no new mpc"); + break; + } + } + mpc->dev_num = priv->itfnum; + mpc->dev = dev; + printk("mpoa: (%s) was initialized\n", dev->name); + break; + case NETDEV_UNREGISTER: + /* the lec device was deallocated */ + mpc = find_mpc_by_lec(dev); + if (mpc == NULL) + break; + printk("mpoa: device (%s) was deallocated\n", dev->name); + stop_mpc(mpc); + mpc->dev = NULL; + break; + case NETDEV_UP: + /* the dev was ifconfig'ed up */ + mpc = find_mpc_by_lec(dev); + if (mpc == NULL) + break; + if (mpc->mpoad_vcc != NULL) { + start_mpc(mpc, dev); + } + break; + case NETDEV_DOWN: + /* the dev was ifconfig'ed down */ + /* this means dev->start == 0 and + * the flow of packets from the + * upper layer stops + */ + mpc = find_mpc_by_lec(dev); + if (mpc == NULL) + break; + if (mpc->mpoad_vcc != NULL) { + stop_mpc(mpc); + } + break; + case NETDEV_REBOOT: + case NETDEV_CHANGE: + case NETDEV_CHANGEMTU: + case NETDEV_CHANGEADDR: + case NETDEV_GOING_DOWN: + break; + default: + break; + } + + return NOTIFY_DONE; +} + +/* + * Functions which are called after a message is received from mpcd. + * Msg is reused on purpose. + */ + + +static void MPOA_trigger_rcvd(struct k_message *msg, struct mpoa_client *client) +{ + uint32_t dst_ip = msg->content.in_info.in_dst_ip; + in_cache_entry *entry; + + entry = client->in_ops->search(dst_ip, client); + if( entry == NULL ){ + entry = client->in_ops->new_entry(dst_ip, client); + entry->entry_state = INGRESS_RESOLVING; + msg->type = SND_MPOA_RES_RQST; + msg->content.in_info = entry->ctrl_info; + msg_to_mpoad(msg,client); + do_gettimeofday(&(entry->reply_wait)); + return; + } + + if( entry->entry_state == INGRESS_INVALID ){ + entry->entry_state = INGRESS_RESOLVING; + msg->type = SND_MPOA_RES_RQST; + msg->content.in_info = entry->ctrl_info; + msg_to_mpoad(msg,client); + do_gettimeofday(&(entry->reply_wait)); + return; + } + + printk("mpoa: (%s) MPOA_trigger_rcvd: entry already in resolving state\n", + (client->dev) ? client->dev->name : ""); + return; +} + +static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *client) +{ + uint32_t dst_ip = msg->content.in_info.in_dst_ip; + in_cache_entry *entry = client->in_ops->search(dst_ip, client); + + dprintk("mpoa: (%s) MPOA_res_reply_rcvd() entry = %p", client->dev->name, entry); + if(entry == NULL){ + printk("\nmpoa: (%s): ARGH, received res. reply for an entry that doesn't exist.\n", client->dev->name); + return; + } + printk(" entry_state = %d ", entry->entry_state); + + if (entry->entry_state == INGRESS_RESOLVED) { + printk("\nmpoa: (%s) MPOA_res_reply_rcvd for RESOLVED entry!\n", client->dev->name); + return; + } + + entry->ctrl_info = msg->content.in_info; + do_gettimeofday(&(entry->tv)); + do_gettimeofday(&(entry->reply_wait)); /* Used in refreshing func from now on */ + entry->refresh_time = 0; + printk("entry->shortcut = %p\n", entry->shortcut); + + if(entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL){ + entry->entry_state = INGRESS_RESOLVED; + return; /* Shortcut already open... */ + } + if (entry->shortcut == NULL) { + entry->entry_state = INGRESS_RESOLVED; + msg->type = OPEN_INGRESS_SVC; + msg_to_mpoad(msg,client); + return; + } + + printk("mpoa: (%s) MPOA_res_reply_rcvd: still here, impossible!\n", client->dev->name); + return; +} + +static void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc) +{ + uint32_t dst_ip = msg->content.in_info.in_dst_ip; + unsigned char *ip = (unsigned char *)&dst_ip; + + in_cache_entry *entry = mpc->in_ops->search(dst_ip, mpc); + if( entry == NULL ){ + printk("mpoa: (%s): ingress_purge_rcvd: recieved a purge for an entry that doesn't exist, ", mpc->dev->name); + printk("ip = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); + return; + } + + printk("mpoa: (%s) ingress_purge_rcvd: removing an ingress entry, ip = %u.%u.%u.%u\n" , + mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); + mpc->in_ops->cache_remove(entry, mpc); + + return; +} + +static void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc) +{ + unsigned long flags; + uint32_t cache_id = msg->content.eg_info.cache_id; + eg_cache_entry *entry = mpc->eg_ops->search_by_cache_id(cache_id, mpc); + + if( entry == NULL ){ + printk("mpoa: (%s): egress_purge_rcvd: recieved a purge reply for an entry that doesn't exist\n", mpc->dev->name); + return; + } + + write_lock_irqsave(&mpc->egress_lock, flags); + mpc->eg_ops->cache_remove(entry, mpc); + write_unlock_irqrestore(&mpc->egress_lock, flags); + + return; +} + +static void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry) +{ + struct k_message *purge_msg; + struct sk_buff *skb; + + dprintk("mpoa: purge_egress_shortcut:, entering\n"); + if (vcc == NULL) { + printk("mpoa: purge_egress_shortcut: vcc == NULL\n"); + return; + } + + skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); + if (skb == NULL) { + printk("mpoa: purge_egress_shortcut: out of memory\n"); + return; + } + + skb_put(skb, sizeof(struct k_message)); + memset(skb->data, 0, sizeof(struct k_message)); + purge_msg = (struct k_message *)skb->data; + purge_msg->type = DATA_PLANE_PURGE; + if (entry != NULL) + purge_msg->content.eg_info = entry->ctrl_info; + + if (atm_charge(vcc, skb->truesize) == 0) { + printk("mpoa: purge_egress_shortcut: atm_charge failed\n"); + return; + } + + skb_queue_tail(&vcc->recvq, skb); + wake_up(&vcc->sleep); + dprintk("mpoa: purge_egress_shortcut:, exiting:\n"); + + return; +} + +/* + * Our MPS died. Tell our daemon to send NHRP data plane purge to each + * of the egress shortcuts we have. + */ +static void mps_death( struct k_message * msg, struct mpoa_client * mpc ) +{ + + unsigned long flags; + eg_cache_entry *entry; + + printk("mpoa: (%s) mps_death:\n", mpc->dev->name); + + if(memcmp(msg->MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN)){ + printk("mpoa: (%s) mps_death: wrong MPS\n", mpc->dev->name); + return; + } + + entry = mpc->eg_cache; + while (entry != NULL) { + purge_egress_shortcut(entry->shortcut, entry); + entry = entry->next; + } + + write_lock_irqsave(&mpc->ingress_lock, flags); + while(mpc->in_ops->cache_remove(mpc->in_cache, mpc)); + write_unlock_irqrestore(&mpc->ingress_lock, flags); + + write_lock_irqsave(&mpc->egress_lock, flags); + while(mpc->eg_ops->cache_remove(mpc->eg_cache, mpc)); + write_unlock_irqrestore(&mpc->egress_lock, flags); + + return; +} + +static void MPOA_cache_impos_rcvd( struct k_message * msg, struct mpoa_client * mpc){ + + uint16_t holding_time; + unsigned long flags; + eg_cache_entry *entry = mpc->eg_ops->search_by_cache_id(msg->content.eg_info.cache_id, mpc); + + holding_time = msg->content.eg_info.holding_time; + printk("mpoa: (%s) MPOA_cache_impos_rcvd: entry = %p, holding_time = %u\n", + mpc->dev->name, entry, holding_time); + if(entry == NULL && holding_time) { + mpc->eg_ops->new_entry(msg, mpc); + return; + } + if(holding_time){ + mpc->eg_ops->update(entry, holding_time); + return; + } + + write_lock_irqsave(&mpc->egress_lock, flags); + mpc->eg_ops->cache_remove(entry, mpc); + write_unlock_irqrestore(&mpc->egress_lock, flags); + + + return; +} + +static void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, struct mpoa_client *mpc) +{ + struct lec_priv *priv; + int i, retval ; + + uint8_t tlv[4 + 1 + 1 + 1 + ATM_ESA_LEN]; + + tlv[0] = 00; tlv[1] = 0xa0; tlv[2] = 0x3e; tlv[3] = 0x2a; /* type */ + tlv[4] = 1 + 1 + ATM_ESA_LEN; /* length */ + tlv[5] = 0x02; /* MPOA client */ + tlv[6] = 0x00; /* number of MPS MAC addresses */ + + memcpy(&tlv[7], mesg->MPS_ctrl, ATM_ESA_LEN); /* MPC ctrl ATM addr */ + memcpy(mpc->our_ctrl_addr, mesg->MPS_ctrl, ATM_ESA_LEN); + + printk("mpoa: (%s) setting MPC ctrl ATM address to ", mpc->dev->name); + for (i = 7; i < sizeof(tlv); i++) + printk("%02x ", tlv[i]); + printk("\n"); + + if (mpc->dev) { + priv = (struct lec_priv *)mpc->dev->priv; + retval = priv->lane2_ops->associate_req(mpc->dev, mpc->dev->dev_addr, tlv, sizeof(tlv)); + if (retval == 0) + printk("mpoa: (%s) MPOA device type TLV association failed\n", mpc->dev->name); + retval = priv->lane2_ops->resolve(mpc->dev, NULL, 1, NULL, NULL); + if (retval < 0) + printk("mpoa: (%s) targetless LE_ARP request failed\n", mpc->dev->name); + } + + return; +} + +static void set_mps_mac_addr_rcvd(struct k_message *msg, struct mpoa_client *client){ + if(client->number_of_mps_macs) + kfree(client->mps_macs); + client->mps_macs = kmalloc(ETH_ALEN,GFP_KERNEL); + if (client->mps_macs == NULL) { + printk("mpoa: set_mps_mac_addr_rcvd: out of memory\n"); + return; + } + client->number_of_mps_macs = 1; + memcpy(client->mps_macs, msg->MPS_ctrl, ETH_ALEN); + + return; +} + +static void clean_up(struct k_message *msg, struct mpoa_client *mpc){ + + unsigned long flags; + eg_cache_entry *entry; + msg->type = SND_EGRESS_PURGE; + + + read_lock_irqsave(&mpc->egress_lock, flags); + entry = mpc->eg_cache; + while(entry != NULL){ + msg->content.eg_info = entry->ctrl_info; + printk("mpoa: cache_id %u\n", entry->ctrl_info.cache_id); + msg_to_mpoad(msg, mpc); + entry = entry->next; + } + read_unlock_irqrestore(&mpc->egress_lock, flags); + + msg->type = DIE; + msg_to_mpoad(msg, mpc); + return; +} + +static void mpc_timer_refresh() +{ + mpc_timer.expires = jiffies + (MPC_P2 * HZ); + mpc_timer.data = mpc_timer.expires; + mpc_timer.function = mpc_cache_check; + add_timer(&mpc_timer); + + return; +} + +static void mpc_cache_check( unsigned long checking_time ) +{ + struct mpoa_client *mpc = mpcs; + static unsigned long previous_resolving_check_time = 0; + static unsigned long previous_refresh_time = 0; + + while( mpc != NULL ){ + mpc->in_ops->clear_count(mpc); + mpc->eg_ops->clear_expired(mpc); + if(checking_time - previous_resolving_check_time > MPC_P4 * HZ ){ + mpc->in_ops->check_resolving(mpc); + previous_resolving_check_time = checking_time; + } + if(checking_time - previous_refresh_time > MPC_P5 * HZ ){ + mpc->in_ops->refresh(mpc); + previous_refresh_time = checking_time; + } + mpc = mpc->next; + } + mpc_timer_refresh(); + + return; +} + +void atm_mpoa_init_ops(struct atm_mpoa_ops *ops) +{ + ops->mpoad_attach = atm_mpoa_mpoad_attach; + ops->vcc_attach = atm_mpoa_vcc_attach; + +#ifdef CONFIG_PROC_FS + if(mpc_proc_init() != 0) + printk(KERN_INFO "mpoa: failed to initialize /proc/mpoa\n"); + else + printk(KERN_INFO "mpoa: /proc/mpoa initialized\n"); +#endif + + printk("mpc.c: " __DATE__ " " __TIME__ " initialized\n"); + + return; +} + +#ifdef MODULE +int init_module(void) +{ + extern struct atm_mpoa_ops atm_mpoa_ops; + + atm_mpoa_init_ops(&atm_mpoa_ops); + + return 0; +} + +void cleanup_module(void) +{ + extern struct atm_mpoa_ops atm_mpoa_ops; + struct mpoa_client *mpc, *tmp; + struct lec_priv *priv; + + if (MOD_IN_USE) { + printk("mpc.c: module in use\n"); + return; + } +#ifdef CONFIG_PROC_FS + mpc_proc_clean(); +#endif + + del_timer(&mpc_timer); + unregister_netdevice_notifier(&mpoa_notifier); + atm_mpoa_ops.mpoad_attach = NULL; + atm_mpoa_ops.vcc_attach = NULL; + + mpc = mpcs; + mpcs = NULL; + while (mpc != NULL) { + tmp = mpc->next; + if (mpc->dev != NULL) { + stop_mpc(mpc); + priv = (struct lec_priv *)mpc->dev->priv; + if (priv->lane2_ops != NULL) + priv->lane2_ops->associate_indicator = NULL; + } + ddprintk("mpoa: cleanup_module: about to clear caches\n"); + while(mpc->in_ops->cache_remove(mpc->in_cache, mpc)); + while(mpc->eg_ops->cache_remove(mpc->eg_cache, mpc)); + ddprintk("mpoa: cleanup_module: caches cleared\n"); + kfree(mpc->mps_macs); + memset(mpc, 0, sizeof(struct mpoa_client)); + ddprintk("mpoa: cleanup_module: about to kfree %p\n", mpc); + kfree(mpc); + ddprintk("mpoa: cleanup_module: kfree() done\n"); + ddprintk("mpoa: cleanup_module: next mpc is at %p\n", tmp); + mpc = tmp; + } + + return; +} +#endif /* MODULE */ + diff -ur --new-file old/linux/net/atm/mpc.h new/linux/net/atm/mpc.h --- old/linux/net/atm/mpc.h Thu Jan 1 01:00:00 1970 +++ new/linux/net/atm/mpc.h Thu Aug 13 15:25:01 1998 @@ -0,0 +1,59 @@ +#ifndef _MPC_H_ +#define _MPC_H_ + +#include +#include +#include +#include "mpoa_caches.h" + +/* kernel -> mpc-daemon */ +int msg_to_mpoad(struct k_message *msg, struct mpoa_client *mpc); + +/* Functions for ioctl(ATMMPC_*) operations */ +int atm_mpoa_mpoad_attach(struct atm_vcc *vcc, int arg); +int atm_mpoa_vcc_attach(struct atm_vcc *vcc, long arg); + + +struct mpoa_client { + struct mpoa_client *next; + struct device *dev; /* lec in question */ + int dev_num; /* e.g. 2 for lec2 */ + int (*old_hard_start_xmit)(struct sk_buff *skb, struct device *dev); + struct atm_vcc *mpoad_vcc; /* control channel to mpoad */ + uint8_t mps_ctrl_addr[ATM_ESA_LEN]; /* MPS control ATM address */ + uint8_t our_ctrl_addr[ATM_ESA_LEN]; /* MPC's control ATM address */ + + rwlock_t ingress_lock; + struct in_cache_ops *in_ops; /* ingress cache operations */ + in_cache_entry *in_cache; /* the ingress cache of this MPC */ + + rwlock_t egress_lock; + struct eg_cache_ops *eg_ops; /* egress cache operations */ + eg_cache_entry *eg_cache; /* the egress cache of this MPC */ + + uint8_t *mps_macs; /* array of MPS MAC addresses, >=1 */ + int number_of_mps_macs; /* number of the above MAC addresses */ +}; + +/* TLVs this MPC recognizes */ +#define TLV_MPOA_DEVICE_TYPE 0x00a03e2a + +/* MPOA device types in MPOA Device Type TLV */ +#define NON_MPOA 0 +#define MPS 1 +#define MPC 2 +#define MPS_AND_MPC 3 + + +/* Functions to call during ioctl(ATMMPC, ) */ +struct atm_mpoa_ops { + int (*mpoad_attach)(struct atm_vcc *vcc, int arg); /* attach mpoa daemon */ + int (*vcc_attach)(struct atm_vcc *vcc, long arg); /* attach shortcut vcc */ +}; + +/* Boot/module initialization function */ +void atm_mpoa_init(void); +void atm_mpoa_init_ops(struct atm_mpoa_ops *ops); + +#endif /* _MPC_H_ */ + diff -ur --new-file old/linux/net/atm/mpoa_caches.c new/linux/net/atm/mpoa_caches.c --- old/linux/net/atm/mpoa_caches.c Thu Jan 1 01:00:00 1970 +++ new/linux/net/atm/mpoa_caches.c Thu Aug 13 15:25:01 1998 @@ -0,0 +1,507 @@ +#include +#include +#include + +#include "mpoa_caches.h" +#include "mpc.h" + +/* + * mpoa_caches.c: Implementation of ingress and egress cache + * handling functions + */ + +#if 0 +#define dprintk printk /* debug */ +#else +#define dprintk(format,args...) +#endif + +#if 0 +#define ddprintk printk /* more debug */ +#else +#define ddprintk(format,args...) +#endif + +static in_cache_entry *in_cache_search(uint32_t dst_ip, + struct mpoa_client *client) +{ + unsigned long flags; + in_cache_entry *entry; + + read_lock_irqsave(&client->ingress_lock, flags); + entry = client->in_cache; + while(entry != NULL){ + if( entry->ctrl_info.in_dst_ip == dst_ip ){ + read_unlock_irqrestore(&client->ingress_lock, flags); + return entry; + } + entry = entry->next; + } + read_unlock_irqrestore(&client->ingress_lock, flags); + + return NULL; +} + +static in_cache_entry *in_cache_search_by_vcc(struct atm_vcc *vcc, + struct mpoa_client *client ) +{ + unsigned long flags; + in_cache_entry *entry; + + read_lock_irqsave(&client->ingress_lock, flags); + entry = client->in_cache; + while(entry != NULL){ + if(entry->shortcut == vcc) { + read_unlock_irqrestore(&client->ingress_lock, flags); + return entry; + } + entry = entry->next; + } + read_unlock_irqrestore(&client->ingress_lock, flags); + + return NULL; +} + +static in_cache_entry *new_in_cache_entry(uint32_t dst_ip, + struct mpoa_client *client) +{ + unsigned long flags; + unsigned char *ip = (unsigned char *)&dst_ip; + in_cache_entry* entry = kmalloc(sizeof(in_cache_entry), GFP_KERNEL); + + if (entry == NULL) { + printk("mpoa: mpoa_caches.c: new_in_cache_entry() out of memory\n"); + return NULL; + } + + printk("mpoa: mpoa_caches.c: adding an ingress entry, ip = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); + memset(entry,0,sizeof(in_cache_entry)); + + dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: about to lock\n"); + write_lock_irqsave(&client->ingress_lock, flags); + entry->next = client->in_cache; + entry->prev = NULL; + if (client->in_cache != NULL) + client->in_cache->prev = entry; + client->in_cache = entry; + write_unlock_irqrestore(&client->ingress_lock, flags); + dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: unlocked\n"); + + memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); + entry->ctrl_info.in_dst_ip = dst_ip; + do_gettimeofday(&(entry->tv)); + entry->retry_time = MPC_P4; + entry->count = 1; + entry->entry_state = INGRESS_INVALID; + entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT; + + return entry; +} + +static int cache_hit( in_cache_entry * entry, struct mpoa_client *mpc) +{ + struct k_message msg; + + entry->count++; + if(entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL) + return OPEN; + + if(entry->entry_state == INGRESS_REFRESHING){ + if(entry->count > MPC_P1){ + msg.type = SND_MPOA_RES_RTRY; + msg.content.in_info = entry->ctrl_info; + memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); + msg_to_mpoad(&msg, mpc); + do_gettimeofday(&(entry->reply_wait)); + entry->entry_state = INGRESS_RESOLVING; + } + if(entry->shortcut != NULL) + return OPEN; + return CLOSED; + } + + if(entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL) + return OPEN; + + if( entry->count > MPC_P1 && + entry->entry_state == INGRESS_INVALID){ + unsigned char *ip = (unsigned char *)&entry->ctrl_info.in_dst_ip; + + printk("mpoa: (%s) mpoa_caches.c: threshold exceeded for ip %u.%u.%u.%u, sending MPOA res req\n", mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); + entry->entry_state = INGRESS_RESOLVING; + msg.type = SND_MPOA_RES_RQST; + memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN ); + msg.content.in_info = entry->ctrl_info; + msg_to_mpoad( &msg, mpc); + do_gettimeofday(&(entry->reply_wait)); + } + + return CLOSED; +} + +/* + * If there are no more references to vcc in egress cache, + * we are ready to close it. + */ +static void close_unused_egress_vcc(struct atm_vcc *vcc, struct mpoa_client *mpc) +{ + if (vcc == NULL) + return; + + printk("mpoa: mpoa_caches.c: close_unused_egress_vcc:\n"); + if (mpc->eg_ops->search_by_vcc(vcc, mpc) != NULL) + return; /* entry still in use */ + + atm_async_release_vcc(vcc, -EPIPE); /* nobody uses this VCC anymore, close it */ + printk("mpoa: mpoa_caches.c: close_unused_egress_vcc, closed one:\n"); + + return; +} + +/* + * This should be called with write lock on + */ +static int in_cache_remove( in_cache_entry *entry, + struct mpoa_client *client ) +{ + struct atm_vcc *vcc; + struct k_message msg; + unsigned char *ip; + + if(entry == NULL) + return 0; + + vcc = entry->shortcut; + ip = (unsigned char *)&entry->ctrl_info.in_dst_ip; + printk("mpoa: mpoa_caches.c: removing an ingress entry, ip = %u.%u.%u.%u\n",ip[0], ip[1], ip[2], ip[3]); + + if (entry->prev != NULL) + entry->prev->next = entry->next; + else + client->in_cache = entry->next; + if (entry->next != NULL) + entry->next->prev = entry->prev; + memset(entry, 0, sizeof(in_cache_entry)); + kfree(entry); + if(client->in_cache == NULL && client->eg_cache == NULL){ + msg.type = STOP_KEEP_ALIVE_SM; + msg_to_mpoad(&msg,client); + } + + close_unused_egress_vcc(vcc, client); + return 1; +} + + +/* Call this every MPC-p2 seconds... Not exactly correct solution, + but an easy one... */ + +static void clear_count_and_expired(struct mpoa_client *client) +{ + unsigned char *ip; + unsigned long flags; + in_cache_entry *entry, *next_entry; + struct timeval now; + + do_gettimeofday(&now); + + write_lock_irqsave(&client->ingress_lock, flags); + entry = client->in_cache; + while(entry != NULL){ + entry->count=0; + next_entry = entry->next; + if((now.tv_sec - entry->tv.tv_sec) + > entry->ctrl_info.holding_time){ + ip = (unsigned char*)&entry->ctrl_info.in_dst_ip; + printk("mpoa: mpoa_caches.c: holding time expired, ip = %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + in_cache_remove(entry, client); + } + entry = next_entry; + } + write_unlock_irqrestore(&client->ingress_lock, flags); + + return; +} + +/* Call this every MPC-p4 seconds. */ + +static void check_resolving_entries( struct mpoa_client * client ) +{ + + unsigned long flags; + in_cache_entry *entry; + struct timeval now; + struct k_message msg; + + do_gettimeofday( &now ); + + read_lock_irqsave(&client->ingress_lock, flags); + entry = client->in_cache; + while( entry != NULL ){ + if(entry->entry_state == INGRESS_RESOLVING){ + if(now.tv_sec - entry->hold_down.tv_sec < MPC_P6){ + entry = entry->next; /* Entry in hold down */ + continue; + } + if( (now.tv_sec - entry->reply_wait.tv_sec) > + entry->retry_time ){ + entry->retry_time = MPC_C1*( entry->retry_time ); + if(entry->retry_time > MPC_P5){ + /* Retry time maximum exceeded, put entry in hold down. */ + do_gettimeofday(&(entry->hold_down)); + entry->retry_time = MPC_P4; + entry = entry->next; + continue; + } + /* Ask daemon to send a resolution request. */ + memset(&(entry->hold_down),0,sizeof(struct timeval)); + msg.type = SND_MPOA_RES_RTRY; + memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN); + msg.content.in_info = entry->ctrl_info; + msg_to_mpoad(&msg, client ); + do_gettimeofday(&(entry->reply_wait)); + } + } + entry = entry->next; + } + read_unlock_irqrestore(&client->ingress_lock, flags); +} + +/* Call this every MPC-p5 seconds. */ + +static void refresh_entries( struct mpoa_client * client ) +{ + unsigned long flags; + struct timeval now; + struct in_cache_entry *entry = client->in_cache; + + ddprintk("mpoa: mpoa_caches.c: refresh_entries\n"); + do_gettimeofday(&now); + + read_lock_irqsave(&client->ingress_lock, flags); + while( entry != NULL ){ + if( entry->entry_state == INGRESS_RESOLVED ){ + if(!(entry->refresh_time)) + entry->refresh_time = (2*(entry->ctrl_info.holding_time))/3; + if( (now.tv_sec - entry->reply_wait.tv_sec) > entry->refresh_time ){ + dprintk("mpoa: mpoa_caches.c: refreshing an entry.\n"); + entry->entry_state = INGRESS_REFRESHING; + + } + } + entry = entry->next; + } + read_unlock_irqrestore(&client->ingress_lock, flags); +} + +static eg_cache_entry *eg_cache_search_by_cache_id(uint32_t cache_id, + struct mpoa_client *client) +{ + eg_cache_entry *entry; + unsigned long flags; + + read_lock_irqsave(&client->egress_lock, flags); + entry = client->eg_cache; + while(entry != NULL){ + if( entry->ctrl_info.cache_id == cache_id){ + read_unlock_irqrestore(&client->egress_lock, flags); + return entry; + } + entry = entry->next; + } + read_unlock_irqrestore(&client->egress_lock, flags); + + return NULL; +} + +static eg_cache_entry *eg_cache_search_by_tag(uint32_t tag, + struct mpoa_client *client) +{ + unsigned long flags; + eg_cache_entry *entry; + + read_lock_irqsave(&client->egress_lock, flags); + entry = client->eg_cache; + while(entry != NULL){ + if( entry->ctrl_info.tag == tag){ + read_unlock_irqrestore(&client->egress_lock, flags); + return entry; + } + entry = entry->next; + } + read_unlock_irqrestore(&client->egress_lock, flags); + + return NULL; +} + +static eg_cache_entry *eg_cache_search_by_vcc(struct atm_vcc *vcc, + struct mpoa_client *client ) +{ + unsigned long flags; + eg_cache_entry *entry; + + read_lock_irqsave(&client->egress_lock, flags); + entry = client->eg_cache; + while( entry != NULL ){ + if( entry->shortcut == vcc ) { + read_unlock_irqrestore(&client->egress_lock, flags); + return entry; + } + entry = entry->next; + } + read_unlock_irqrestore(&client->egress_lock, flags); + + return NULL; +} + +/* + * If there are no more references to vcc in ingress cache, + * we are ready to close it. + */ +static void close_unused_ingress_vcc(struct atm_vcc *vcc, struct mpoa_client *mpc) +{ + if (vcc == NULL) + return; + + printk("mpoa: mpoa_caches.c: close_unused_ingress_vcc:\n"); + if (mpc->in_ops->search_by_vcc(vcc, mpc) != NULL) + return; /* entry still in use */ + + atm_async_release_vcc(vcc, -EPIPE); /* nobody uses this VCC anymore, close it */ + printk("mpoa: mpoa_caches.c: close_unused_ingress_vcc:, closed one\n"); + + return; +} +/* + * This should be called with write lock on + */ +static int eg_cache_remove(eg_cache_entry *entry, + struct mpoa_client *client) +{ + struct atm_vcc *vcc; + struct k_message msg; + if(entry == NULL) + return 0; + + vcc = entry->shortcut; + printk("mpoa: mpoa_caches.c: removing an egress entry.\n"); + if (entry->prev != NULL) + entry->prev->next = entry->next; + else + client->eg_cache = entry->next; + if (entry->next != NULL) + entry->next->prev = entry->prev; + memset(entry, 0, sizeof(eg_cache_entry)); + kfree(entry); + if(client->in_cache == NULL && client->eg_cache == NULL){ + msg.type = STOP_KEEP_ALIVE_SM; + msg_to_mpoad(&msg,client); + } + + close_unused_ingress_vcc(vcc, client); + + return 1; +} + +static eg_cache_entry *new_eg_cache_entry(struct k_message *msg, struct mpoa_client *client) +{ + unsigned long flags; + unsigned char *ip; + eg_cache_entry *entry = kmalloc(sizeof(eg_cache_entry), GFP_KERNEL); + + if (entry == NULL) { + printk("mpoa: mpoa_caches.c: new_eg_cache_entry() out of memory\n"); + return NULL; + } + + ip = (unsigned char *)&msg->content.eg_info.eg_dst_ip; + printk("mpoa: mpoa_caches.c: adding an egress entry, ip = %d.%d.%d.%d, this should be our IP\n", ip[0], ip[1], ip[2], ip[3]); + memset(entry, 0, sizeof(eg_cache_entry)); + + dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: about to lock\n"); + write_lock_irqsave(&client->egress_lock, flags); + entry->next = client->eg_cache; + entry->prev = NULL; + if (client->eg_cache != NULL) + client->eg_cache->prev = entry; + client->eg_cache = entry; + write_unlock_irqrestore(&client->egress_lock, flags); + dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: unlocked\n"); + + memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); + entry->ctrl_info = msg->content.eg_info; + do_gettimeofday(&(entry->tv)); + entry->entry_state = EGRESS_RESOLVED; + printk("mpoa: mpoa_caches.c: new_eg_cache_entry cache_id %lu\n", ntohl(entry->ctrl_info.cache_id)); + ip = (unsigned char *)&entry->ctrl_info.mps_ip; + printk("mpoa: mpoa_caches.c: mps_ip = %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + return entry; +} + +static void update_eg_cache_entry(eg_cache_entry * entry, uint16_t holding_time) +{ + do_gettimeofday(&(entry->tv)); + entry->entry_state = EGRESS_RESOLVED; + entry->ctrl_info.holding_time = holding_time; + + return; +} + +static void clear_expired(struct mpoa_client *client){ + eg_cache_entry *entry, *next_entry; + unsigned long flags; + struct timeval now; + struct k_message msg; + + do_gettimeofday(&now); + + write_lock_irqsave(&client->egress_lock, flags); + entry = client->eg_cache; + while(entry != NULL){ + next_entry = entry->next; + if((now.tv_sec - entry->tv.tv_sec) + > entry->ctrl_info.holding_time){ + msg.type = SND_EGRESS_PURGE; + msg.content.eg_info = entry->ctrl_info; + printk("mpoa: mpoa_caches.c: egress_cache:holding time expired, cache_id = %lu.\n",ntohl(entry->ctrl_info.cache_id)); + msg_to_mpoad(&msg,client); + eg_cache_remove(entry, client); + } + entry = next_entry; + } + write_unlock_irqrestore(&client->egress_lock, flags); + + return; +} + + + +static struct in_cache_ops ingress_ops = { + new_in_cache_entry, /* new_entry */ + in_cache_search, /* search */ + in_cache_search_by_vcc, /* search_by_vcc */ + cache_hit, /* cache_hit */ + in_cache_remove, /* cache_remove */ + clear_count_and_expired, /* clear_count */ + check_resolving_entries, /* check_resolving */ + refresh_entries /* refresh */ +}; + +static struct eg_cache_ops egress_ops = { + new_eg_cache_entry, /* new_entry */ + eg_cache_search_by_cache_id, /* search_by_cache_id */ + eg_cache_search_by_tag, /* search_by_tag */ + eg_cache_search_by_vcc, /* search_by_vcc */ + eg_cache_remove, /* cache_remove */ + update_eg_cache_entry, /* update */ + clear_expired /* clear_expired */ +}; + + +void atm_mpoa_init_cache(struct mpoa_client *mpc) +{ + mpc->in_ops = &ingress_ops; + mpc->eg_ops = &egress_ops; + + return; +} diff -ur --new-file old/linux/net/atm/mpoa_caches.h new/linux/net/atm/mpoa_caches.h --- old/linux/net/atm/mpoa_caches.h Thu Jan 1 01:00:00 1970 +++ new/linux/net/atm/mpoa_caches.h Thu Aug 13 15:25:01 1998 @@ -0,0 +1,87 @@ +#ifndef MPOA_CACHES_H +#define MPOA_CACHES_H + +#include +#include +#include +#include +#include + + + +struct mpoa_client; + +void atm_mpoa_init_cache(struct mpoa_client *mpc); + +typedef struct in_cache_entry { + struct in_cache_entry *next; + struct in_cache_entry *prev; + struct timeval tv; + struct timeval reply_wait; + struct timeval hold_down; + uint32_t packets_fwded; + uint16_t entry_state; + uint32_t retry_time; + uint32_t refresh_time; + uint32_t count; + struct atm_vcc *shortcut; + uint8_t MPS_ctrl_ATM_addr[ATM_ESA_LEN]; + struct in_ctrl_info ctrl_info; +} in_cache_entry; + +struct in_cache_ops{ + in_cache_entry *(*new_entry)(uint32_t dst_ip, + struct mpoa_client *client); + in_cache_entry *(*search)(uint32_t dst_ip, struct mpoa_client *client); + in_cache_entry *(*search_by_vcc)(struct atm_vcc *vcc, + struct mpoa_client *client); + int (*cache_hit)(in_cache_entry *entry, + struct mpoa_client *client); + int (*cache_remove)(in_cache_entry *delEntry, + struct mpoa_client *client ); + void (*clear_count)(struct mpoa_client *client); + void (*check_resolving)(struct mpoa_client *client); + void (*refresh)(struct mpoa_client *client); +}; + +typedef struct eg_cache_entry{ + struct eg_cache_entry *next; + struct eg_cache_entry *prev; + struct timeval tv; + uint8_t MPS_ctrl_ATM_addr[ATM_ESA_LEN]; + struct atm_vcc *shortcut; + uint32_t packets_rcvd; + uint16_t entry_state; + struct eg_ctrl_info ctrl_info; +} eg_cache_entry; + +struct eg_cache_ops{ + eg_cache_entry *(*new_entry)(struct k_message *msg, struct mpoa_client *client); + eg_cache_entry *(*search_by_cache_id)(uint32_t cache_id, struct mpoa_client *client); + eg_cache_entry *(*search_by_tag)(uint32_t cache_id, struct mpoa_client *client); + eg_cache_entry *(*search_by_vcc)(struct atm_vcc *vcc, struct mpoa_client *client); + int (*cache_remove)(eg_cache_entry *entry, struct mpoa_client *client); + void (*update)(eg_cache_entry *entry, uint16_t holding_time); + void (*clear_expired)(struct mpoa_client *client); +}; + + +/* Ingress cache entry states */ + +#define INGRESS_REFRESHING 3 +#define INGRESS_RESOLVED 2 +#define INGRESS_RESOLVING 1 +#define INGRESS_INVALID 0 + +/* VCC states */ + +#define OPEN 1 +#define CLOSED 0 + +/* Egress cache entry states */ + +#define EGRESS_RESOLVED 2 +#define EGRESS_PURGE 1 +#define EGRESS_INVALID 0 + +#endif diff -ur --new-file old/linux/net/atm/mpoa_proc.c new/linux/net/atm/mpoa_proc.c --- old/linux/net/atm/mpoa_proc.c Thu Jan 1 01:00:00 1970 +++ new/linux/net/atm/mpoa_proc.c Thu Aug 13 15:25:01 1998 @@ -0,0 +1,246 @@ +#include + +#ifdef CONFIG_PROC_FS +#include +#include +#include +#include +#include +#include +#include +#include +#include "mpc.h" +#include "mpoa_caches.h" + +/* + * mpoa_proc.c: Implementation MPOA client's proc + * file system statistics + */ + +extern struct mpoa_client *mpcs; + +static ssize_t proc_mpc_read(struct file *file, char *buff, + size_t count, loff_t *pos); + +/* + * /proc/mpoa DIRECTORY ENTRY. + */ +static struct proc_dir_entry mpc_proc_root = { + 0, /* low_ino (0=dynamic?) */ + 4, /* name length */ + "mpoa", /* name string */ + S_IFDIR | S_IRUGO | S_IXUGO, /* mode/permissions */ + 2, /* 2=dir */ + 0, /* UID */ + 0, /* GID */ + 0, /* size */ + &proc_dir_inode_operations, /* inode operations */ + NULL, /* get_info func-ptr */ + NULL, /* fill_inode func-ptr */ + NULL, /* next ptr */ + NULL, /* parent ptr */ + NULL /* subdir ptr */ +}; + +/* + * Define allowed FILE OPERATIONS + */ +static struct file_operations mpc_file_operations = { + NULL, /* lseek */ + proc_mpc_read, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* no fsync */ +}; + +/* + * Define allowed INODE OPERATIONS + */ +static struct inode_operations mpc_inode_operations = { + &mpc_file_operations, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +/* + * /proc/mpoa/mpc_stats REGULAR_FILE + */ +static struct proc_dir_entry mpc_stats = { + 0, /* low_ino */ + 9, /* name length */ + "mpc_stats", /* name */ + S_IFREG | S_IRUGO, /* mode */ + 1, /* 1=file */ + 0, /* UID */ + 0, /* GID */ + 0, /* size */ + &mpc_inode_operations, /* inode operations */ + NULL /* get_info func-ptr */ + +}; + +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; + ssize_t 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]; + if(count < 0) + return -EINVAL; + if(count == 0) + return 0; + page = get_free_page(GFP_KERNEL); + if(!page) + return -ENOMEM; + 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 VPI VCI\n"); + while(eg_entry != NULL){ + for(i=0;ictrl_info.in_MPC_data_ATM_addr[i]);} + length += sprintf((char *)page + length,"\n%-16lu%s%-14lu%-12u",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); + 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 (*pos >= length) length = 0; + else { + if ((count + *pos) > length) count = length - *pos; + copy_to_user(buff, (char *)page , count); + *pos += count; + } + + free_page(page); + return length; +} + +/* + * INITIALIZATION function - called when module is initialized/loaded. + */ +int mpc_proc_init(void) +{ + int retval = 0; + + if ( (retval = proc_register(&proc_root,&mpc_proc_root)) != 0 ) { + printk(KERN_ERR "Unable to initialize /proc/mpoa/\n"); + return retval; + } + + if ( (retval = proc_register(&mpc_proc_root,&mpc_stats)) != 0 ) { + printk(KERN_ERR "Unable to initialize /proc/mpoa/mpc_stats\n"); + return retval; + } + return 0; +} + +/* + * DELETING function - called when module is removed. + */ +void mpc_proc_clean(void) +{ + proc_unregister(&mpc_proc_root,mpc_stats.low_ino); + proc_unregister(&proc_root,mpc_proc_root.low_ino); +} + + +#endif /* CONFIG_PROC_FS */ + + + + + + diff -ur --new-file old/linux/net/atm/proc.c new/linux/net/atm/proc.c --- old/linux/net/atm/proc.c Thu Aug 13 15:24:21 1998 +++ new/linux/net/atm/proc.c Thu Aug 13 15:25:01 1998 @@ -17,6 +17,7 @@ #include +#include /* for EXPORT_SYMBOL */ #include #include #include @@ -51,7 +52,7 @@ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) #include "lec.h" #include "lec_arpc.h" -extern struct device **(*ptr_to_get_dev_lec)(void); +extern struct atm_lane_ops atm_lane_ops; /* in common.c */ #endif @@ -476,10 +477,10 @@ int i, count, d, e; struct device **dev_lec; - if (ptr_to_get_dev_lec == NULL) + if (atm_lane_ops.get_lecs == NULL) return 0; /* the lane module is not there yet */ else - dev_lec = ptr_to_get_dev_lec(); + dev_lec = atm_lane_ops.get_lecs(); count = *pos; for(d=0;d + +extern struct proc_dir_entry atm_proc_root; /* @@@ move elsewhere */ + int atm_proc_dev_register(struct atm_dev *dev); void atm_proc_dev_deregister(struct atm_dev *dev); + #endif #endif diff -ur --new-file old/linux/net/atm/svc.c new/linux/net/atm/svc.c --- old/linux/net/atm/svc.c Thu Aug 13 15:24:21 1998 +++ new/linux/net/atm/svc.c Thu Aug 13 15:25:01 1998 @@ -237,6 +237,8 @@ while (1) { while (!(skb = skb_dequeue(&old_vcc->listenq)) && sigd) { if (old_vcc->flags & ATM_VF_RELEASED) break; + if (old_vcc->flags & ATM_VF_CLOSE) + return old_vcc->reply; if (flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&old_vcc->sleep); if (signal_pending(current)) return -ERESTARTSYS; .