OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/linux/linux-2.4/net/atm
    from Rev 1275 to Rev 1765
    Reverse comparison

Rev 1275 → Rev 1765

/mpoa_caches.c
0,0 → 1,576
#include <linux/types.h>
#include <linux/atmmpc.h>
#include <linux/time.h>
 
#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_get(uint32_t dst_ip,
struct mpoa_client *client)
{
in_cache_entry *entry;
 
read_lock_bh(&client->ingress_lock);
entry = client->in_cache;
while(entry != NULL){
if( entry->ctrl_info.in_dst_ip == dst_ip ){
atomic_inc(&entry->use);
read_unlock_bh(&client->ingress_lock);
return entry;
}
entry = entry->next;
}
read_unlock_bh(&client->ingress_lock);
 
return NULL;
}
 
static in_cache_entry *in_cache_get_with_mask(uint32_t dst_ip,
struct mpoa_client *client,
uint32_t mask)
{
in_cache_entry *entry;
 
read_lock_bh(&client->ingress_lock);
entry = client->in_cache;
while(entry != NULL){
if((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask )){
atomic_inc(&entry->use);
read_unlock_bh(&client->ingress_lock);
return entry;
}
entry = entry->next;
}
read_unlock_bh(&client->ingress_lock);
 
return NULL;
 
}
 
static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc,
struct mpoa_client *client )
{
in_cache_entry *entry;
 
read_lock_bh(&client->ingress_lock);
entry = client->in_cache;
while(entry != NULL){
if(entry->shortcut == vcc) {
atomic_inc(&entry->use);
read_unlock_bh(&client->ingress_lock);
return entry;
}
entry = entry->next;
}
read_unlock_bh(&client->ingress_lock);
 
return NULL;
}
 
static in_cache_entry *in_cache_add_entry(uint32_t dst_ip,
struct mpoa_client *client)
{
unsigned char *ip __attribute__ ((unused)) = (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;
}
 
dprintk("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));
 
atomic_set(&entry->use, 1);
dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: about to lock\n");
write_lock_bh(&client->ingress_lock);
entry->next = client->in_cache;
entry->prev = NULL;
if (client->in_cache != NULL)
client->in_cache->prev = entry;
client->in_cache = entry;
 
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 = client->parameters.mpc_p4;
entry->count = 1;
entry->entry_state = INGRESS_INVALID;
entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT;
atomic_inc(&entry->use);
 
write_unlock_bh(&client->ingress_lock);
dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: unlocked\n");
 
return entry;
}
 
static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc)
{
struct atm_mpoa_qos *qos;
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->parameters.mpc_p1){
msg.type = SND_MPOA_RES_RQST;
msg.content.in_info = entry->ctrl_info;
memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
if (qos != NULL) msg.qos = qos->qos;
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->parameters.mpc_p1 &&
entry->entry_state == INGRESS_INVALID){
unsigned char *ip __attribute__ ((unused)) =
(unsigned char *)&entry->ctrl_info.in_dst_ip;
 
dprintk("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;
qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
if (qos != NULL) msg.qos = qos->qos;
msg_to_mpoad( &msg, mpc);
do_gettimeofday(&(entry->reply_wait));
}
 
return CLOSED;
}
 
static void in_cache_put(in_cache_entry *entry)
{
if (atomic_dec_and_test(&entry->use)) {
memset(entry, 0, sizeof(in_cache_entry));
kfree(entry);
}
 
return;
}
 
/*
* This should be called with write lock on
*/
static void in_cache_remove_entry(in_cache_entry *entry,
struct mpoa_client *client)
{
struct atm_vcc *vcc;
struct k_message msg;
unsigned char *ip;
 
vcc = entry->shortcut;
ip = (unsigned char *)&entry->ctrl_info.in_dst_ip;
dprintk("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;
client->in_ops->put(entry);
if(client->in_cache == NULL && client->eg_cache == NULL){
msg.type = STOP_KEEP_ALIVE_SM;
msg_to_mpoad(&msg,client);
}
 
/* Check if the egress side still uses this VCC */
if (vcc != NULL) {
eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc, client);
if (eg_entry != NULL) {
client->eg_ops->put(eg_entry);
return;
}
vcc_release_async(vcc, -EPIPE);
}
 
return;
}
 
 
/* 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;
in_cache_entry *entry, *next_entry;
struct timeval now;
 
do_gettimeofday(&now);
 
write_lock_bh(&client->ingress_lock);
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;
dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %u.%u.%u.%u\n", NIPQUAD(ip));
client->in_ops->remove_entry(entry, client);
}
entry = next_entry;
}
write_unlock_bh(&client->ingress_lock);
 
return;
}
 
/* Call this every MPC-p4 seconds. */
static void check_resolving_entries(struct mpoa_client *client)
{
 
struct atm_mpoa_qos *qos;
in_cache_entry *entry;
struct timeval now;
struct k_message msg;
 
do_gettimeofday( &now );
 
read_lock_bh(&client->ingress_lock);
entry = client->in_cache;
while( entry != NULL ){
if(entry->entry_state == INGRESS_RESOLVING){
if(now.tv_sec - entry->hold_down.tv_sec < client->parameters.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 > client->parameters.mpc_p5){
/* Retry time maximum exceeded, put entry in hold down. */
do_gettimeofday(&(entry->hold_down));
entry->retry_time = client->parameters.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;
qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
if (qos != NULL) msg.qos = qos->qos;
msg_to_mpoad(&msg, client);
do_gettimeofday(&(entry->reply_wait));
}
}
entry = entry->next;
}
read_unlock_bh(&client->ingress_lock);
}
 
/* Call this every MPC-p5 seconds. */
static void refresh_entries(struct mpoa_client *client)
{
struct timeval now;
struct in_cache_entry *entry = client->in_cache;
 
ddprintk("mpoa: mpoa_caches.c: refresh_entries\n");
do_gettimeofday(&now);
 
read_lock_bh(&client->ingress_lock);
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_bh(&client->ingress_lock);
}
 
static void in_destroy_cache(struct mpoa_client *mpc)
{
write_lock_irq(&mpc->ingress_lock);
while(mpc->in_cache != NULL)
mpc->in_ops->remove_entry(mpc->in_cache, mpc);
write_unlock_irq(&mpc->ingress_lock);
 
return;
}
 
static eg_cache_entry *eg_cache_get_by_cache_id(uint32_t cache_id, struct mpoa_client *mpc)
{
eg_cache_entry *entry;
 
read_lock_irq(&mpc->egress_lock);
entry = mpc->eg_cache;
while(entry != NULL){
if(entry->ctrl_info.cache_id == cache_id){
atomic_inc(&entry->use);
read_unlock_irq(&mpc->egress_lock);
return entry;
}
entry = entry->next;
}
read_unlock_irq(&mpc->egress_lock);
 
return NULL;
}
 
/* This can be called from any context since it saves CPU flags */
static eg_cache_entry *eg_cache_get_by_tag(uint32_t tag, struct mpoa_client *mpc)
{
unsigned long flags;
eg_cache_entry *entry;
 
read_lock_irqsave(&mpc->egress_lock, flags);
entry = mpc->eg_cache;
while (entry != NULL){
if (entry->ctrl_info.tag == tag) {
atomic_inc(&entry->use);
read_unlock_irqrestore(&mpc->egress_lock, flags);
return entry;
}
entry = entry->next;
}
read_unlock_irqrestore(&mpc->egress_lock, flags);
 
return NULL;
}
 
/* This can be called from any context since it saves CPU flags */
static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, struct mpoa_client *mpc)
{
unsigned long flags;
eg_cache_entry *entry;
 
read_lock_irqsave(&mpc->egress_lock, flags);
entry = mpc->eg_cache;
while (entry != NULL){
if (entry->shortcut == vcc) {
atomic_inc(&entry->use);
read_unlock_irqrestore(&mpc->egress_lock, flags);
return entry;
}
entry = entry->next;
}
read_unlock_irqrestore(&mpc->egress_lock, flags);
 
return NULL;
}
 
static eg_cache_entry *eg_cache_get_by_src_ip(uint32_t ipaddr, struct mpoa_client *mpc)
{
eg_cache_entry *entry;
 
read_lock_irq(&mpc->egress_lock);
entry = mpc->eg_cache;
while(entry != NULL){
if(entry->latest_ip_addr == ipaddr) {
atomic_inc(&entry->use);
read_unlock_irq(&mpc->egress_lock);
return entry;
}
entry = entry->next;
}
read_unlock_irq(&mpc->egress_lock);
 
return NULL;
}
 
static void eg_cache_put(eg_cache_entry *entry)
{
if (atomic_dec_and_test(&entry->use)) {
memset(entry, 0, sizeof(eg_cache_entry));
kfree(entry);
}
 
return;
}
 
/*
* This should be called with write lock on
*/
static void eg_cache_remove_entry(eg_cache_entry *entry,
struct mpoa_client *client)
{
struct atm_vcc *vcc;
struct k_message msg;
 
vcc = entry->shortcut;
dprintk("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;
client->eg_ops->put(entry);
if(client->in_cache == NULL && client->eg_cache == NULL){
msg.type = STOP_KEEP_ALIVE_SM;
msg_to_mpoad(&msg,client);
}
 
/* Check if the ingress side still uses this VCC */
if (vcc != NULL) {
in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client);
if (in_entry != NULL) {
client->in_ops->put(in_entry);
return;
}
vcc_release_async(vcc, -EPIPE);
}
 
return;
}
 
static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, struct mpoa_client *client)
{
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;
dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %u.%u.%u.%u, this should be our IP\n", NIPQUAD(ip));
memset(entry, 0, sizeof(eg_cache_entry));
 
atomic_set(&entry->use, 1);
dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: about to lock\n");
write_lock_irq(&client->egress_lock);
entry->next = client->eg_cache;
entry->prev = NULL;
if (client->eg_cache != NULL)
client->eg_cache->prev = entry;
client->eg_cache = entry;
 
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;
dprintk("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;
dprintk("mpoa: mpoa_caches.c: mps_ip = %u.%u.%u.%u\n", NIPQUAD(ip));
atomic_inc(&entry->use);
 
write_unlock_irq(&client->egress_lock);
dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: unlocked\n");
 
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;
struct timeval now;
struct k_message msg;
 
do_gettimeofday(&now);
 
write_lock_irq(&client->egress_lock);
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;
dprintk("mpoa: mpoa_caches.c: egress_cache: holding time expired, cache_id = %lu.\n",ntohl(entry->ctrl_info.cache_id));
msg_to_mpoad(&msg, client);
client->eg_ops->remove_entry(entry, client);
}
entry = next_entry;
}
write_unlock_irq(&client->egress_lock);
 
return;
}
 
static void eg_destroy_cache(struct mpoa_client *mpc)
{
write_lock_irq(&mpc->egress_lock);
while(mpc->eg_cache != NULL)
mpc->eg_ops->remove_entry(mpc->eg_cache, mpc);
write_unlock_irq(&mpc->egress_lock);
 
return;
}
 
 
 
static struct in_cache_ops ingress_ops = {
in_cache_add_entry, /* add_entry */
in_cache_get, /* get */
in_cache_get_with_mask, /* get_with_mask */
in_cache_get_by_vcc, /* get_by_vcc */
in_cache_put, /* put */
in_cache_remove_entry, /* remove_entry */
cache_hit, /* cache_hit */
clear_count_and_expired, /* clear_count */
check_resolving_entries, /* check_resolving */
refresh_entries, /* refresh */
in_destroy_cache /* destroy_cache */
};
 
static struct eg_cache_ops egress_ops = {
eg_cache_add_entry, /* add_entry */
eg_cache_get_by_cache_id, /* get_by_cache_id */
eg_cache_get_by_tag, /* get_by_tag */
eg_cache_get_by_vcc, /* get_by_vcc */
eg_cache_get_by_src_ip, /* get_by_src_ip */
eg_cache_put, /* put */
eg_cache_remove_entry, /* remove_entry */
update_eg_cache_entry, /* update */
clear_expired, /* clear_expired */
eg_destroy_cache /* destroy_cache */
};
 
 
void atm_mpoa_init_cache(struct mpoa_client *mpc)
{
mpc->in_ops = &ingress_ops;
mpc->eg_ops = &egress_ops;
 
return;
}
/lec.c
0,0 → 1,2194
/*
* lec.c: Lan Emulation driver
* Marko Kiiskila carnil@cs.tut.fi
*
*/
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
 
/* We are ethernet device */
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <net/sock.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
#include <net/arp.h>
#include <net/dst.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
 
/* TokenRing if needed */
#ifdef CONFIG_TR
#include <linux/trdevice.h>
#endif
 
/* And atm device */
#include <linux/atmdev.h>
#include <linux/atmlec.h>
 
/* Proxy LEC knows about bridging */
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
#include <linux/if_bridge.h>
#include "../bridge/br_private.h"
static unsigned char bridge_ula_lec[] = {0x01, 0x80, 0xc2, 0x00, 0x00};
#endif
 
/* Modular too */
#include <linux/module.h>
#include <linux/init.h>
 
#include "lec.h"
#include "lec_arpc.h"
#include "resources.h"
 
#if 0
#define DPRINTK printk
#else
#define DPRINTK(format,args...)
#endif
 
struct net_bridge;
extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
unsigned char *addr);
extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
 
#define DUMP_PACKETS 0 /* 0 = None,
* 1 = 30 first bytes
* 2 = Whole packet
*/
 
#define LEC_UNRES_QUE_LEN 8 /* number of tx packets to queue for a
single destination while waiting for SVC */
 
static int lec_open(struct net_device *dev);
static int lec_send_packet(struct sk_buff *skb, struct net_device *dev);
static int lec_close(struct net_device *dev);
static struct net_device_stats *lec_get_stats(struct net_device *dev);
static void lec_init(struct net_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_priv *priv,
struct lec_arp_table *to_remove);
/* LANE2 functions */
static void lane2_associate_ind (struct net_device *dev, u8 *mac_address,
u8 *tlvs, u32 sizeoftlvs);
static int lane2_resolve(struct net_device *dev, u8 *dst_mac, int force,
u8 **tlvs, u32 *sizeoftlvs);
static int lane2_associate_req (struct net_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 */
};
 
static unsigned char bus_mac[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff};
 
/* Device structures */
static struct net_device *dev_lec[MAX_LEC_ITF];
 
/* This will be called from proc.c via function pointer */
struct net_device *get_dev_lec(int itf)
{
struct net_device *dev;
 
if (itf >= MAX_LEC_ITF)
return NULL;
rtnl_lock();
dev = dev_lec[itf];
if (dev)
dev_hold(dev);
rtnl_unlock();
return dev;
}
 
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev)
{
struct ethhdr *eth;
char *buff;
struct lec_priv *priv;
 
/* Check if this is a BPDU. If so, ask zeppelin to send
* LE_TOPOLOGY_REQUEST with the same value of Topology Change bit
* as the Config BPDU has */
eth = (struct ethhdr *)skb->data;
buff = skb->data + skb->dev->hard_header_len;
if (*buff++ == 0x42 && *buff++ == 0x42 && *buff++ == 0x03) {
struct sk_buff *skb2;
struct atmlec_msg *mesg;
 
skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
if (skb2 == NULL) return;
skb2->len = sizeof(struct atmlec_msg);
mesg = (struct atmlec_msg *)skb2->data;
mesg->type = l_topology_change;
buff += 4;
mesg->content.normal.flag = *buff & 0x01; /* 0x01 is topology change */
 
priv = (struct lec_priv *)dev->priv;
atm_force_charge(priv->lecd, skb2->truesize);
skb_queue_tail(&priv->lecd->sk->receive_queue, skb2);
wake_up(&priv->lecd->sleep);
}
 
return;
}
#endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
 
/*
* Modelled after tr_type_trans
* All multicast and ARE or STE frames go to BUS.
* Non source routed frames go by destination address.
* Last hop source routed frames go by destination address.
* Not last hop source routed frames go by _next_ route descriptor.
* Returns pointer to destination MAC address or fills in rdesc
* and returns NULL.
*/
#ifdef CONFIG_TR
unsigned char *get_tr_dst(unsigned char *packet, unsigned char *rdesc)
{
struct trh_hdr *trh;
int riflen, num_rdsc;
trh = (struct trh_hdr *)packet;
if (trh->daddr[0] & (uint8_t)0x80)
return bus_mac; /* multicast */
 
if (trh->saddr[0] & TR_RII) {
riflen = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
if ((ntohs(trh->rcf) >> 13) != 0)
return bus_mac; /* ARE or STE */
}
else
return trh->daddr; /* not source routed */
 
if (riflen < 6)
return trh->daddr; /* last hop, source routed */
/* riflen is 6 or more, packet has more than one route descriptor */
num_rdsc = (riflen/2) - 1;
memset(rdesc, 0, ETH_ALEN);
/* offset 4 comes from LAN destination field in LE control frames */
if (trh->rcf & htons((uint16_t)TR_RCF_DIR_BIT))
memcpy(&rdesc[4], &trh->rseg[num_rdsc-2], sizeof(uint16_t));
else {
memcpy(&rdesc[4], &trh->rseg[1], sizeof(uint16_t));
rdesc[5] = ((ntohs(trh->rseg[0]) & 0x000f) | (rdesc[5] & 0xf0));
}
 
return NULL;
}
#endif /* CONFIG_TR */
 
/*
* Open/initialize the netdevice. This is called (in the current kernel)
* sometime after booting when the 'ifconfig' program is run.
*
* This routine should set everything up anew at each open, even
* registers that "should" only need to be set once at boot, so that
* there is non-reboot way to recover if something goes wrong.
*/
 
static int
lec_open(struct net_device *dev)
{
struct lec_priv *priv = (struct lec_priv *)dev->priv;
netif_start_queue(dev);
memset(&priv->stats,0,sizeof(struct net_device_stats));
return 0;
}
 
static __inline__ void
lec_send(struct atm_vcc *vcc, struct sk_buff *skb, struct lec_priv *priv)
{
if (atm_may_send(vcc, skb->len)) {
atomic_add(skb->truesize, &vcc->sk->wmem_alloc);
ATM_SKB(skb)->vcc = vcc;
ATM_SKB(skb)->atm_options = vcc->atm_options;
priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len;
vcc->send(vcc, skb);
} else {
priv->stats.tx_dropped++;
dev_kfree_skb(skb);
}
}
 
static int
lec_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct sk_buff *skb2;
struct lec_priv *priv = (struct lec_priv *)dev->priv;
struct lecdatahdr_8023 *lec_h;
struct atm_vcc *send_vcc;
struct lec_arp_table *entry;
unsigned char *dst;
int min_frame_size;
#ifdef CONFIG_TR
unsigned char rdesc[ETH_ALEN]; /* Token Ring route descriptor */
#endif
int is_rdesc;
#if DUMP_PACKETS > 0
char buf[300];
int i=0;
#endif /* DUMP_PACKETS >0 */
DPRINTK("Lec_send_packet called\n");
if (!priv->lecd) {
printk("%s:No lecd attached\n",dev->name);
priv->stats.tx_errors++;
netif_stop_queue(dev);
return -EUNATCH;
}
 
DPRINTK("skbuff head:%lx data:%lx tail:%lx end:%lx\n",
(long)skb->head, (long)skb->data, (long)skb->tail,
(long)skb->end);
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
if (memcmp(skb->data, bridge_ula_lec, sizeof(bridge_ula_lec)) == 0)
lec_handle_bridge(skb, dev);
#endif
 
/* Make sure we have room for lec_id */
if (skb_headroom(skb) < 2) {
 
DPRINTK("lec_send_packet: reallocating skb\n");
skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN);
kfree_skb(skb);
if (skb2 == NULL) return 0;
skb = skb2;
}
skb_push(skb, 2);
 
/* Put le header to place, works for TokenRing too */
lec_h = (struct lecdatahdr_8023*)skb->data;
lec_h->le_header = htons(priv->lecid);
 
#ifdef CONFIG_TR
/* Ugly. Use this to realign Token Ring packets for
* e.g. PCA-200E driver. */
if (priv->is_trdev) {
skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN);
kfree_skb(skb);
if (skb2 == NULL) return 0;
skb = skb2;
}
#endif
 
#if DUMP_PACKETS > 0
printk("%s: send datalen:%ld lecid:%4.4x\n", dev->name,
skb->len, priv->lecid);
#if DUMP_PACKETS >= 2
for(i=0;i<skb->len && i <99;i++) {
sprintf(buf+i*3,"%2.2x ",0xff&skb->data[i]);
}
#elif DUMP_PACKETS >= 1
for(i=0;i<skb->len && i < 30;i++) {
sprintf(buf+i*3,"%2.2x ", 0xff&skb->data[i]);
}
#endif /* DUMP_PACKETS >= 1 */
if (i==skb->len)
printk("%s\n",buf);
else
printk("%s...\n",buf);
#endif /* DUMP_PACKETS > 0 */
 
/* Minimum ethernet-frame size */
#ifdef CONFIG_TR
if (priv->is_trdev)
min_frame_size = LEC_MINIMUM_8025_SIZE;
else
#endif
min_frame_size = LEC_MINIMUM_8023_SIZE;
if (skb->len < min_frame_size) {
if ((skb->len + skb_tailroom(skb)) < min_frame_size) {
skb2 = skb_copy_expand(skb, 0,
min_frame_size - skb->truesize, GFP_ATOMIC);
dev_kfree_skb(skb);
if (skb2 == NULL) {
priv->stats.tx_dropped++;
return 0;
}
skb = skb2;
}
skb_put(skb, min_frame_size - skb->len);
}
/* Send to right vcc */
is_rdesc = 0;
dst = lec_h->h_dest;
#ifdef CONFIG_TR
if (priv->is_trdev) {
dst = get_tr_dst(skb->data+2, rdesc);
if (dst == NULL) {
dst = rdesc;
is_rdesc = 1;
}
}
#endif
entry = NULL;
send_vcc = lec_arp_resolve(priv, dst, is_rdesc, &entry);
DPRINTK("%s:send_vcc:%p vcc_flags:%x, entry:%p\n", dev->name,
send_vcc, send_vcc?send_vcc->flags:0, entry);
if (!send_vcc || !test_bit(ATM_VF_READY,&send_vcc->flags)) {
if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) {
DPRINTK("%s:lec_send_packet: queuing packet, ", dev->name);
DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
skb_queue_tail(&entry->tx_wait, skb);
} else {
DPRINTK("%s:lec_send_packet: tx queue full or no arp entry, dropping, ", dev->name);
DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
priv->stats.tx_dropped++;
dev_kfree_skb(skb);
}
return 0;
}
#if DUMP_PACKETS > 0
printk("%s:sending to vpi:%d vci:%d\n", dev->name,
send_vcc->vpi, send_vcc->vci);
#endif /* DUMP_PACKETS > 0 */
while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) {
DPRINTK("lec.c: emptying tx queue, ");
DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
lec_send(send_vcc, skb2, priv);
}
 
lec_send(send_vcc, skb, priv);
#if 0
/* Should we wait for card's device driver to notify us? */
dev->tbusy=0;
#endif
return 0;
}
 
/* The inverse routine to net_open(). */
static int
lec_close(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
 
/*
* Get the current statistics.
* This may be called with the card open or closed.
*/
static struct net_device_stats *
lec_get_stats(struct net_device *dev)
{
return &((struct lec_priv *)dev->priv)->stats;
}
 
static int
lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
{
struct net_device *dev = (struct net_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, &vcc->sk->wmem_alloc);
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:
for (i=0;i<6;i++) {
dev->dev_addr[i] = mesg->content.normal.mac_addr[i];
}
break;
case l_del_mac_addr:
for(i=0;i<6;i++) {
dev->dev_addr[i] = 0;
}
break;
case l_addr_delete:
lec_addr_delete(priv, mesg->content.normal.atm_addr,
mesg->content.normal.flag);
break;
case l_topology_change:
priv->topology_change = mesg->content.normal.flag;
break;
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, 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.targetless_le_arp);
DPRINTK("lec: in l_arp_update\n");
if (mesg->sizeoftlvs != 0) { /* LANE2 3.1.5 */
DPRINTK("lec: LANE2 3.1.5, got tlvs, size %d\n", mesg->sizeoftlvs);
lane2_associate_ind(dev,
mesg->content.normal.mac_addr,
tmp, mesg->sizeoftlvs);
}
break;
case l_config:
priv->maximum_unknown_frame_count =
mesg->content.config.maximum_unknown_frame_count;
priv->max_unknown_frame_time =
(mesg->content.config.max_unknown_frame_time*HZ);
priv->max_retry_count =
mesg->content.config.max_retry_count;
priv->aging_time = (mesg->content.config.aging_time*HZ);
priv->forward_delay_time =
(mesg->content.config.forward_delay_time*HZ);
priv->arp_response_time =
(mesg->content.config.arp_response_time*HZ);
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;
if (dev->change_mtu(dev, mesg->content.config.mtu))
printk("%s: change_mtu to %d failed\n", dev->name,
mesg->content.config.mtu);
priv->is_proxy = mesg->content.config.is_proxy;
break;
case l_flush_tran_id:
lec_set_flush_tran_id(priv, mesg->content.normal.atm_addr,
mesg->content.normal.flag);
break;
case l_set_lecid:
priv->lecid=(unsigned short)(0xffff&mesg->content.normal.flag);
break;
case l_should_bridge: {
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
struct net_bridge_fdb_entry *f;
 
DPRINTK("%s: bridge zeppelin asks about 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
dev->name,
mesg->content.proxy.mac_addr[0], mesg->content.proxy.mac_addr[1],
mesg->content.proxy.mac_addr[2], mesg->content.proxy.mac_addr[3],
mesg->content.proxy.mac_addr[4], mesg->content.proxy.mac_addr[5]);
 
if (br_fdb_get_hook == NULL || dev->br_port == NULL)
break;
 
f = br_fdb_get_hook(dev->br_port->br, mesg->content.proxy.mac_addr);
if (f != NULL &&
f->dst->dev != dev &&
f->dst->state == BR_STATE_FORWARDING) {
/* hit from bridge table, send LE_ARP_RESPONSE */
struct sk_buff *skb2;
 
DPRINTK("%s: entry found, responding to zeppelin\n", dev->name);
skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
if (skb2 == NULL) {
br_fdb_put_hook(f);
break;
}
skb2->len = sizeof(struct atmlec_msg);
memcpy(skb2->data, mesg, sizeof(struct atmlec_msg));
atm_force_charge(priv->lecd, skb2->truesize);
skb_queue_tail(&priv->lecd->sk->receive_queue, skb2);
wake_up(&priv->lecd->sleep);
}
if (f != NULL) br_fdb_put_hook(f);
#endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
}
break;
default:
printk("%s: Unknown message type %d\n", dev->name, mesg->type);
dev_kfree_skb(skb);
return -EINVAL;
}
dev_kfree_skb(skb);
return 0;
}
 
static void
lec_atm_close(struct atm_vcc *vcc)
{
struct sk_buff *skb;
struct net_device *dev = (struct net_device *)vcc->proto_data;
struct lec_priv *priv = (struct lec_priv *)dev->priv;
 
priv->lecd = NULL;
/* Do something needful? */
 
netif_stop_queue(dev);
lec_arp_destroy(priv);
 
if (skb_peek(&vcc->sk->receive_queue))
printk("%s lec_atm_close: closing with messages pending\n",
dev->name);
while ((skb = skb_dequeue(&vcc->sk->receive_queue))) {
atm_return(vcc, skb->truesize);
dev_kfree_skb(skb);
}
printk("%s: Shut down!\n", dev->name);
MOD_DEC_USE_COUNT;
}
 
static struct atmdev_ops lecdev_ops = {
.close = lec_atm_close,
.send = lec_atm_send
};
 
static struct atm_dev lecatm_dev = {
.ops = &lecdev_ops,
.type = "lec",
.number = 999,
.lock = SPIN_LOCK_UNLOCKED
};
 
/*
* LANE2: new argument struct sk_buff *data contains
* the LE_ARP based TLVs introduced in the LANE2 spec
*/
int
send_to_lecd(struct lec_priv *priv, atmlec_msg_type type,
unsigned char *mac_addr, unsigned char *atm_addr,
struct sk_buff *data)
{
struct sk_buff *skb;
struct atmlec_msg *mesg;
 
if (!priv || !priv->lecd) {
return -1;
}
skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
if (!skb)
return -1;
skb->len = sizeof(struct atmlec_msg);
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);
 
atm_force_charge(priv->lecd, skb->truesize);
skb_queue_tail(&priv->lecd->sk->receive_queue, skb);
wake_up(&priv->lecd->sleep);
 
if (data != NULL) {
DPRINTK("lec: about to send %d bytes of data\n", data->len);
atm_force_charge(priv->lecd, data->truesize);
skb_queue_tail(&priv->lecd->sk->receive_queue, data);
wake_up(&priv->lecd->sleep);
}
 
return 0;
}
 
/* shamelessly stolen from drivers/net/net_init.c */
static int lec_change_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < 68) || (new_mtu > 18190))
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
 
static void lec_set_multicast_list(struct net_device *dev)
{
/* by default, all multicast frames arrive over the bus.
* eventually support selective multicast service
*/
return;
}
 
static void
lec_init(struct net_device *dev)
{
dev->change_mtu = lec_change_mtu;
dev->open = lec_open;
dev->stop = lec_close;
dev->hard_start_xmit = lec_send_packet;
 
dev->get_stats = lec_get_stats;
dev->set_multicast_list = lec_set_multicast_list;
dev->do_ioctl = NULL;
printk("%s: Initialized!\n",dev->name);
return;
}
 
static unsigned char lec_ctrl_magic[] = {
0xff,
0x00,
0x01,
0x01 };
 
void
lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
{
struct net_device *dev = (struct net_device *)vcc->proto_data;
struct lec_priv *priv = (struct lec_priv *)dev->priv;
 
#if DUMP_PACKETS >0
int i=0;
char buf[300];
 
printk("%s: lec_push vcc vpi:%d vci:%d\n", dev->name,
vcc->vpi, vcc->vci);
#endif
if (!skb) {
DPRINTK("%s: null skb\n",dev->name);
lec_vcc_close(priv, vcc);
return;
}
#if DUMP_PACKETS > 0
printk("%s: rcv datalen:%ld lecid:%4.4x\n", dev->name,
skb->len, priv->lecid);
#if DUMP_PACKETS >= 2
for(i=0;i<skb->len && i <99;i++) {
sprintf(buf+i*3,"%2.2x ",0xff&skb->data[i]);
}
#elif DUMP_PACKETS >= 1
for(i=0;i<skb->len && i < 30;i++) {
sprintf(buf+i*3,"%2.2x ", 0xff&skb->data[i]);
}
#endif /* DUMP_PACKETS >= 1 */
if (i==skb->len)
printk("%s\n",buf);
else
printk("%s...\n",buf);
#endif /* DUMP_PACKETS > 0 */
if (memcmp(skb->data, lec_ctrl_magic, 4) ==0) { /* Control frame, to daemon*/
DPRINTK("%s: To daemon\n",dev->name);
skb_queue_tail(&vcc->sk->receive_queue, skb);
wake_up(&vcc->sleep);
} else { /* Data frame, queue to protocol handlers */
unsigned char *dst;
 
atm_return(vcc,skb->truesize);
if (*(uint16_t *)skb->data == htons(priv->lecid) ||
!priv->lecd) {
/* Probably looping back, or if lecd is missing,
lecd has gone down */
DPRINTK("Ignoring loopback frame...\n");
dev_kfree_skb(skb);
return;
}
#ifdef CONFIG_TR
if (priv->is_trdev) dst = ((struct lecdatahdr_8025 *)skb->data)->h_dest;
else
#endif
dst = ((struct lecdatahdr_8023 *)skb->data)->h_dest;
 
if (!(dst[0]&0x01) && /* Never filter Multi/Broadcast */
!priv->is_proxy && /* Proxy wants all the packets */
memcmp(dst, dev->dev_addr, dev->addr_len)) {
dev_kfree_skb(skb);
return;
}
if (priv->lec_arp_empty_ones) {
lec_arp_check_empties(priv, vcc, skb);
}
skb->dev = dev;
skb_pull(skb, 2); /* skip lec_id */
#ifdef CONFIG_TR
if (priv->is_trdev) skb->protocol = tr_type_trans(skb, dev);
else
#endif
skb->protocol = eth_type_trans(skb, dev);
priv->stats.rx_packets++;
priv->stats.rx_bytes += skb->len;
memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
netif_rx(skb);
}
}
 
int
lec_vcc_attach(struct atm_vcc *vcc, void *arg)
{
int bytes_left;
struct atmlec_ioc ioc_data;
 
/* Lecd must be up in this case */
bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmlec_ioc));
if (bytes_left != 0) {
printk("lec: lec_vcc_attach, copy from user failed for %d bytes\n",
bytes_left);
}
if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF ||
!dev_lec[ioc_data.dev_num])
return -EINVAL;
lec_vcc_added(dev_lec[ioc_data.dev_num]->priv,
&ioc_data, vcc, vcc->push);
vcc->proto_data = dev_lec[ioc_data.dev_num];
vcc->push = lec_push;
return 0;
}
 
int
lec_mcast_attach(struct atm_vcc *vcc, int arg)
{
if (arg <0 || arg >= MAX_LEC_ITF || !dev_lec[arg])
return -EINVAL;
vcc->proto_data = dev_lec[arg];
return (lec_mcast_make((struct lec_priv*)dev_lec[arg]->priv, vcc));
}
 
/* Initialize device. */
int
lecd_attach(struct atm_vcc *vcc, int arg)
{
int i;
struct lec_priv *priv;
 
if (arg<0)
i = 0;
else
i = arg;
#ifdef CONFIG_TR
if (arg >= MAX_LEC_ITF)
return -EINVAL;
#else /* Reserve the top NUM_TR_DEVS for TR */
if (arg >= (MAX_LEC_ITF-NUM_TR_DEVS))
return -EINVAL;
#endif
if (!dev_lec[i]) {
int is_trdev, size;
 
is_trdev = 0;
if (i >= (MAX_LEC_ITF - NUM_TR_DEVS))
is_trdev = 1;
 
size = sizeof(struct lec_priv);
#ifdef CONFIG_TR
if (is_trdev)
dev_lec[i] = alloc_trdev(size);
else
#endif
dev_lec[i] = alloc_etherdev(size);
if (!dev_lec[i])
return -ENOMEM;
snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i);
if (register_netdev(dev_lec[i])) {
kfree(dev_lec[i]);
return -EINVAL;
}
 
priv = dev_lec[i]->priv;
priv->is_trdev = is_trdev;
lec_init(dev_lec[i]);
} else {
priv = dev_lec[i]->priv;
if (priv->lecd)
return -EADDRINUSE;
}
lec_arp_init(priv);
priv->itfnum = i; /* LANE2 addition */
priv->lecd = vcc;
vcc->dev = &lecatm_dev;
vcc_insert_socket(vcc->sk);
vcc->proto_data = dev_lec[i];
set_bit(ATM_VF_META,&vcc->flags);
set_bit(ATM_VF_READY,&vcc->flags);
 
/* Set default values to these variables */
priv->maximum_unknown_frame_count = 1;
priv->max_unknown_frame_time = (1*HZ);
priv->vcc_timeout_period = (1200*HZ);
priv->max_retry_count = 1;
priv->aging_time = (300*HZ);
priv->forward_delay_time = (15*HZ);
priv->topology_change = 0;
priv->arp_response_time = (1*HZ);
priv->flush_timeout = (4*HZ);
priv->path_switching_delay = (6*HZ);
 
if (dev_lec[i]->flags & IFF_UP) {
netif_start_queue(dev_lec[i]);
}
MOD_INC_USE_COUNT;
return i;
}
 
static struct atm_lane_ops __atm_lane_ops =
{
.lecd_attach = lecd_attach,
.mcast_attach = lec_mcast_attach,
.vcc_attach = lec_vcc_attach,
.get_lec = get_dev_lec,
.owner = THIS_MODULE
};
 
static int __init lane_module_init(void)
{
atm_lane_ops_set(&__atm_lane_ops);
printk("lec.c: " __DATE__ " " __TIME__ " initialized\n");
return 0;
}
 
static void __exit lane_module_cleanup(void)
{
int i;
struct lec_priv *priv;
 
atm_lane_ops_set(NULL);
 
for (i = 0; i < MAX_LEC_ITF; i++) {
if (dev_lec[i] != NULL) {
priv = (struct lec_priv *)dev_lec[i]->priv;
#if defined(CONFIG_TR)
if (priv->is_trdev)
unregister_trdev(dev_lec[i]);
else
#endif
unregister_netdev(dev_lec[i]);
kfree(dev_lec[i]);
dev_lec[i] = NULL;
}
}
 
return;
}
 
module_init(lane_module_init);
module_exit(lane_module_cleanup);
 
/*
* 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
*/
static int lane2_resolve(struct net_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;
struct sk_buff *skb;
int retval;
 
if (force == 0) {
table = lec_arp_find(priv, dst_mac);
if(table == NULL)
return -1;
*tlvs = kmalloc(table->sizeoftlvs, GFP_KERNEL);
if (*tlvs == NULL)
return -1;
memcpy(*tlvs, table->tlvs, table->sizeoftlvs);
*sizeoftlvs = table->sizeoftlvs;
return 0;
}
 
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;
}
 
 
/*
* 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 failure (out of memory)
*
*/
static int lane2_associate_req (struct net_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, ETH_ALEN) != 0 )
return (0); /* not our mac address */
 
kfree(priv->tlvs); /* NULL if there was no previous association */
 
priv->tlvs = kmalloc(sizeoftlvs, GFP_KERNEL);
if (priv->tlvs == NULL)
return (0);
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
*/
return (1);
}
 
/*
* LANE2: 3.1.5, LE_ASSOCIATE.indication
*
*/
static void lane2_associate_ind (struct net_device *dev, u8 *mac_addr,
u8 *tlvs, u32 sizeoftlvs)
{
#if 0
int i = 0;
#endif
struct lec_priv *priv = (struct lec_priv *)dev->priv;
#if 0 /* Why have the TLVs in LE_ARP entries since we do not use them? When you
uncomment this code, make sure the TLVs get freed when entry is killed */
struct lec_arp_table *entry = lec_arp_find(priv, mac_addr);
 
if (entry == NULL)
return; /* should not happen */
 
kfree(entry->tlvs);
 
entry->tlvs = kmalloc(sizeoftlvs, GFP_KERNEL);
if (entry->tlvs == NULL)
return;
 
entry->sizeoftlvs = sizeoftlvs;
memcpy(entry->tlvs, tlvs, sizeoftlvs);
#endif
#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);
}
return;
}
 
/*
* Here starts what used to lec_arpc.c
*
* lec_arpc.c was added here when making
* lane client modular. October 1997
*
*/
 
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <asm/param.h>
#include <asm/atomic.h>
#include <linux/inetdevice.h>
#include <net/route.h>
 
 
#if 0
#define DPRINTK(format,args...)
/*
#define DPRINTK printk
*/
#endif
#define DEBUG_ARP_TABLE 0
 
#define LEC_ARP_REFRESH_INTERVAL (3*HZ)
 
static void lec_arp_check_expire(unsigned long data);
static void lec_arp_expire_arp(unsigned long data);
void dump_arp_table(struct lec_priv *priv);
 
/*
* Arp table funcs
*/
 
#define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE -1))
 
static __inline__ void
lec_arp_get(struct lec_priv *priv)
{
atomic_inc(&priv->lec_arp_users);
}
 
static __inline__ void
lec_arp_put(struct lec_priv *priv)
{
atomic_dec(&priv->lec_arp_users);
}
 
/*
* Initialization of arp-cache
*/
void
lec_arp_init(struct lec_priv *priv)
{
unsigned short i;
 
for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
priv->lec_arp_tables[i] = NULL;
}
spin_lock_init(&priv->lec_arp_lock);
init_timer(&priv->lec_arp_timer);
priv->lec_arp_timer.expires = jiffies+LEC_ARP_REFRESH_INTERVAL;
priv->lec_arp_timer.data = (unsigned long)priv;
priv->lec_arp_timer.function = lec_arp_check_expire;
add_timer(&priv->lec_arp_timer);
}
 
void
lec_arp_clear_vccs(struct lec_arp_table *entry)
{
if (entry->vcc) {
entry->vcc->push = entry->old_push;
#if 0 /* August 6, 1998 */
set_bit(ATM_VF_RELEASED,&entry->vcc->flags);
clear_bit(ATM_VF_READY,&entry->vcc->flags);
entry->vcc->push(entry->vcc, NULL);
#endif
vcc_release_async(entry->vcc, -EPIPE);
entry->vcc = NULL;
}
if (entry->recv_vcc) {
entry->recv_vcc->push = entry->old_recv_push;
#if 0
set_bit(ATM_VF_RELEASED,&entry->recv_vcc->flags);
clear_bit(ATM_VF_READY,&entry->recv_vcc->flags);
entry->recv_vcc->push(entry->recv_vcc, NULL);
#endif
vcc_release_async(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_add(struct lec_priv *priv, struct lec_arp_table *to_add)
{
unsigned long flags;
unsigned short place;
struct lec_arp_table *tmp;
 
spin_lock_irqsave(&priv->lec_arp_lock, flags);
 
place = HASH(to_add->mac_addr[ETH_ALEN-1]);
tmp = priv->lec_arp_tables[place];
to_add->next = NULL;
if (tmp == NULL)
priv->lec_arp_tables[place] = to_add;
else { /* add to the end */
while (tmp->next)
tmp = tmp->next;
tmp->next = to_add;
}
 
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
 
DPRINTK("LEC_ARP: Added entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
0xff&to_add->mac_addr[0], 0xff&to_add->mac_addr[1],
0xff&to_add->mac_addr[2], 0xff&to_add->mac_addr[3],
0xff&to_add->mac_addr[4], 0xff&to_add->mac_addr[5]);
}
 
/*
* Remove entry from lec_arp_table
*/
static inline int
lec_arp_remove(struct lec_priv *priv,
struct lec_arp_table *to_remove)
{
unsigned long flags;
unsigned short place;
struct lec_arp_table *tmp;
int remove_vcc=1;
 
spin_lock_irqsave(&priv->lec_arp_lock, flags);
 
if (!to_remove) {
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
return -1;
}
place = HASH(to_remove->mac_addr[ETH_ALEN-1]);
tmp = priv->lec_arp_tables[place];
if (tmp == to_remove) {
priv->lec_arp_tables[place] = tmp->next;
} else {
while(tmp && tmp->next != to_remove) {
tmp = tmp->next;
}
if (!tmp) {/* Entry was not found */
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
return -1;
}
}
tmp->next = to_remove->next;
del_timer(&to_remove->timer);
/* If this is the only MAC connected to this VCC, also tear down
the VCC */
if (to_remove->status >= ESI_FLUSH_PENDING) {
/*
* ESI_FLUSH_PENDING, ESI_FORWARD_DIRECT
*/
for(place=0;place<LEC_ARP_TABLE_SIZE;place++) {
for(tmp = priv->lec_arp_tables[place]; tmp != NULL; tmp = tmp->next) {
if (memcmp(tmp->atm_addr, to_remove->atm_addr,
ATM_ESA_LEN)==0) {
remove_vcc=0;
break;
}
}
}
if (remove_vcc)
lec_arp_clear_vccs(to_remove);
}
skb_queue_purge(&to_remove->tx_wait); /* FIXME: good place for this? */
 
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
 
DPRINTK("LEC_ARP: Removed entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
0xff&to_remove->mac_addr[0], 0xff&to_remove->mac_addr[1],
0xff&to_remove->mac_addr[2], 0xff&to_remove->mac_addr[3],
0xff&to_remove->mac_addr[4], 0xff&to_remove->mac_addr[5]);
return 0;
}
 
#if DEBUG_ARP_TABLE
static char*
get_status_string(unsigned char st)
{
switch(st) {
case ESI_UNKNOWN:
return "ESI_UNKNOWN";
case ESI_ARP_PENDING:
return "ESI_ARP_PENDING";
case ESI_VC_PENDING:
return "ESI_VC_PENDING";
case ESI_FLUSH_PENDING:
return "ESI_FLUSH_PENDING";
case ESI_FORWARD_DIRECT:
return "ESI_FORWARD_DIRECT";
default:
return "<UNKNOWN>";
}
}
#endif
 
void
dump_arp_table(struct lec_priv *priv)
{
#if DEBUG_ARP_TABLE
int i,j, offset;
struct lec_arp_table *rulla;
char buf[1024];
struct lec_arp_table **lec_arp_tables =
(struct lec_arp_table **)priv->lec_arp_tables;
struct lec_arp_table *lec_arp_empty_ones =
(struct lec_arp_table *)priv->lec_arp_empty_ones;
struct lec_arp_table *lec_no_forward =
(struct lec_arp_table *)priv->lec_no_forward;
struct lec_arp_table *mcast_fwds = priv->mcast_fwds;
 
 
printk("Dump %p:\n",priv);
for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
rulla = lec_arp_tables[i];
offset = 0;
offset += sprintf(buf,"%d: %p\n",i, rulla);
while (rulla) {
offset += sprintf(buf+offset,"Mac:");
for(j=0;j<ETH_ALEN;j++) {
offset+=sprintf(buf+offset,
"%2.2x ",
rulla->mac_addr[j]&0xff);
}
offset +=sprintf(buf+offset,"Atm:");
for(j=0;j<ATM_ESA_LEN;j++) {
offset+=sprintf(buf+offset,
"%2.2x ",
rulla->atm_addr[j]&0xff);
}
offset+=sprintf(buf+offset,
"Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
rulla->vcc?rulla->vcc->vpi:0,
rulla->vcc?rulla->vcc->vci:0,
rulla->recv_vcc?rulla->recv_vcc->vpi:0,
rulla->recv_vcc?rulla->recv_vcc->vci:0,
rulla->last_used,
rulla->timestamp, rulla->no_tries);
offset+=sprintf(buf+offset,
"Flags:%x, Packets_flooded:%x, Status: %s ",
rulla->flags, rulla->packets_flooded,
get_status_string(rulla->status));
offset+=sprintf(buf+offset,"->%p\n",rulla->next);
rulla = rulla->next;
}
printk("%s",buf);
}
rulla = lec_no_forward;
if (rulla)
printk("No forward\n");
while(rulla) {
offset=0;
offset += sprintf(buf+offset,"Mac:");
for(j=0;j<ETH_ALEN;j++) {
offset+=sprintf(buf+offset,"%2.2x ",
rulla->mac_addr[j]&0xff);
}
offset +=sprintf(buf+offset,"Atm:");
for(j=0;j<ATM_ESA_LEN;j++) {
offset+=sprintf(buf+offset,"%2.2x ",
rulla->atm_addr[j]&0xff);
}
offset+=sprintf(buf+offset,
"Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
rulla->vcc?rulla->vcc->vpi:0,
rulla->vcc?rulla->vcc->vci:0,
rulla->recv_vcc?rulla->recv_vcc->vpi:0,
rulla->recv_vcc?rulla->recv_vcc->vci:0,
rulla->last_used,
rulla->timestamp, rulla->no_tries);
offset+=sprintf(buf+offset,
"Flags:%x, Packets_flooded:%x, Status: %s ",
rulla->flags, rulla->packets_flooded,
get_status_string(rulla->status));
offset+=sprintf(buf+offset,"->%lx\n",(long)rulla->next);
rulla = rulla->next;
printk("%s",buf);
}
rulla = lec_arp_empty_ones;
if (rulla)
printk("Empty ones\n");
while(rulla) {
offset=0;
offset += sprintf(buf+offset,"Mac:");
for(j=0;j<ETH_ALEN;j++) {
offset+=sprintf(buf+offset,"%2.2x ",
rulla->mac_addr[j]&0xff);
}
offset +=sprintf(buf+offset,"Atm:");
for(j=0;j<ATM_ESA_LEN;j++) {
offset+=sprintf(buf+offset,"%2.2x ",
rulla->atm_addr[j]&0xff);
}
offset+=sprintf(buf+offset,
"Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
rulla->vcc?rulla->vcc->vpi:0,
rulla->vcc?rulla->vcc->vci:0,
rulla->recv_vcc?rulla->recv_vcc->vpi:0,
rulla->recv_vcc?rulla->recv_vcc->vci:0,
rulla->last_used,
rulla->timestamp, rulla->no_tries);
offset+=sprintf(buf+offset,
"Flags:%x, Packets_flooded:%x, Status: %s ",
rulla->flags, rulla->packets_flooded,
get_status_string(rulla->status));
offset+=sprintf(buf+offset,"->%lx\n",(long)rulla->next);
rulla = rulla->next;
printk("%s",buf);
}
 
rulla = mcast_fwds;
if (rulla)
printk("Multicast Forward VCCs\n");
while(rulla) {
offset=0;
offset += sprintf(buf+offset,"Mac:");
for(j=0;j<ETH_ALEN;j++) {
offset+=sprintf(buf+offset,"%2.2x ",
rulla->mac_addr[j]&0xff);
}
offset +=sprintf(buf+offset,"Atm:");
for(j=0;j<ATM_ESA_LEN;j++) {
offset+=sprintf(buf+offset,"%2.2x ",
rulla->atm_addr[j]&0xff);
}
offset+=sprintf(buf+offset,
"Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
rulla->vcc?rulla->vcc->vpi:0,
rulla->vcc?rulla->vcc->vci:0,
rulla->recv_vcc?rulla->recv_vcc->vpi:0,
rulla->recv_vcc?rulla->recv_vcc->vci:0,
rulla->last_used,
rulla->timestamp, rulla->no_tries);
offset+=sprintf(buf+offset,
"Flags:%x, Packets_flooded:%x, Status: %s ",
rulla->flags, rulla->packets_flooded,
get_status_string(rulla->status));
offset+=sprintf(buf+offset,"->%lx\n",(long)rulla->next);
rulla = rulla->next;
printk("%s",buf);
}
 
#endif
}
 
/*
* Destruction of arp-cache
*/
void
lec_arp_destroy(struct lec_priv *priv)
{
struct lec_arp_table *entry, *next;
int i;
 
del_timer_sync(&priv->lec_arp_timer);
/*
* Remove all entries
*/
for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
for(entry =priv->lec_arp_tables[i];entry != NULL; entry=next) {
next = entry->next;
lec_arp_remove(priv, entry);
kfree(entry);
}
}
entry = priv->lec_arp_empty_ones;
while(entry) {
next = entry->next;
del_timer_sync(&entry->timer);
lec_arp_clear_vccs(entry);
kfree(entry);
entry = next;
}
priv->lec_arp_empty_ones = NULL;
entry = priv->lec_no_forward;
while(entry) {
next = entry->next;
del_timer_sync(&entry->timer);
lec_arp_clear_vccs(entry);
kfree(entry);
entry = next;
}
priv->lec_no_forward = NULL;
entry = priv->mcast_fwds;
while(entry) {
next = entry->next;
/* No timer, LANEv2 7.1.20 and 2.3.5.3 */
lec_arp_clear_vccs(entry);
kfree(entry);
entry = next;
}
priv->mcast_fwds = NULL;
priv->mcast_vcc = NULL;
memset(priv->lec_arp_tables, 0,
sizeof(struct lec_arp_table*)*LEC_ARP_TABLE_SIZE);
}
 
 
/*
* Find entry by mac_address
*/
static inline struct lec_arp_table*
lec_arp_find(struct lec_priv *priv,
unsigned char *mac_addr)
{
unsigned short place;
struct lec_arp_table *to_return;
 
DPRINTK("LEC_ARP: lec_arp_find :%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
mac_addr[0]&0xff, mac_addr[1]&0xff, mac_addr[2]&0xff,
mac_addr[3]&0xff, mac_addr[4]&0xff, mac_addr[5]&0xff);
lec_arp_get(priv);
place = HASH(mac_addr[ETH_ALEN-1]);
to_return = priv->lec_arp_tables[place];
while(to_return) {
if (memcmp(mac_addr, to_return->mac_addr, ETH_ALEN) == 0) {
lec_arp_put(priv);
return to_return;
}
to_return = to_return->next;
}
lec_arp_put(priv);
return NULL;
}
 
static struct lec_arp_table*
make_entry(struct lec_priv *priv, unsigned char *mac_addr)
{
struct lec_arp_table *to_return;
 
to_return=(struct lec_arp_table *)kmalloc(sizeof(struct lec_arp_table),
GFP_ATOMIC);
if (!to_return) {
printk("LEC: Arp entry kmalloc failed\n");
return NULL;
}
memset(to_return,0,sizeof(struct lec_arp_table));
memcpy(to_return->mac_addr, mac_addr, ETH_ALEN);
init_timer(&to_return->timer);
to_return->timer.function = lec_arp_expire_arp;
to_return->timer.data = (unsigned long)to_return;
to_return->last_used = jiffies;
to_return->priv = priv;
skb_queue_head_init(&to_return->tx_wait);
return to_return;
}
 
/*
*
* Arp sent timer expired
*
*/
static void
lec_arp_expire_arp(unsigned long data)
{
struct lec_arp_table *entry;
 
entry = (struct lec_arp_table *)data;
 
DPRINTK("lec_arp_expire_arp\n");
if (entry->status == ESI_ARP_PENDING) {
if (entry->no_tries <= entry->priv->max_retry_count) {
if (entry->is_rdesc)
send_to_lecd(entry->priv, l_rdesc_arp_xmt, entry->mac_addr, NULL, NULL);
else
send_to_lecd(entry->priv, l_arp_xmt, entry->mac_addr, NULL, NULL);
entry->no_tries++;
}
mod_timer(&entry->timer, jiffies + (1*HZ));
}
}
 
/*
*
* Unknown/unused vcc expire, remove associated entry
*
*/
static void
lec_arp_expire_vcc(unsigned long data)
{
struct lec_arp_table *to_remove = (struct lec_arp_table*)data;
struct lec_priv *priv = (struct lec_priv *)to_remove->priv;
struct lec_arp_table *entry = NULL;
 
del_timer(&to_remove->timer);
 
DPRINTK("LEC_ARP %p %p: lec_arp_expire_vcc vpi:%d vci:%d\n",
to_remove, priv,
to_remove->vcc?to_remove->recv_vcc->vpi:0,
to_remove->vcc?to_remove->recv_vcc->vci:0);
DPRINTK("eo:%p nf:%p\n",priv->lec_arp_empty_ones,priv->lec_no_forward);
if (to_remove == priv->lec_arp_empty_ones)
priv->lec_arp_empty_ones = to_remove->next;
else {
entry = priv->lec_arp_empty_ones;
while (entry && entry->next != to_remove)
entry = entry->next;
if (entry)
entry->next = to_remove->next;
}
if (!entry) {
if (to_remove == priv->lec_no_forward) {
priv->lec_no_forward = to_remove->next;
} else {
entry = priv->lec_no_forward;
while (entry && entry->next != to_remove)
entry = entry->next;
if (entry)
entry->next = to_remove->next;
}
}
lec_arp_clear_vccs(to_remove);
kfree(to_remove);
}
 
/*
* Expire entries.
* 1. Re-set timer
* 2. For each entry, delete entries that have aged past the age limit.
* 3. For each entry, depending on the status of the entry, perform
* the following maintenance.
* a. If status is ESI_VC_PENDING or ESI_ARP_PENDING then if the
* tick_count is above the max_unknown_frame_time, clear
* the tick_count to zero and clear the packets_flooded counter
* to zero. This supports the packet rate limit per address
* while flooding unknowns.
* b. If the status is ESI_FLUSH_PENDING and the tick_count is greater
* than or equal to the path_switching_delay, change the status
* to ESI_FORWARD_DIRECT. This causes the flush period to end
* regardless of the progress of the flush protocol.
*/
static void
lec_arp_check_expire(unsigned long data)
{
struct lec_priv *priv = (struct lec_priv *)data;
struct lec_arp_table *entry, *next;
unsigned long now;
unsigned long time_to_check;
int i;
 
DPRINTK("lec_arp_check_expire %p,%d\n",priv,
atomic_read(&priv->lec_arp_users));
DPRINTK("expire: eo:%p nf:%p\n",priv->lec_arp_empty_ones,
priv->lec_no_forward);
if (!atomic_read(&priv->lec_arp_users)) {
lec_arp_get(priv);
now = jiffies;
for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
for(entry = priv->lec_arp_tables[i]; entry != NULL; ) {
if ((entry->flags) & LEC_REMOTE_FLAG &&
priv->topology_change)
time_to_check=priv->forward_delay_time;
else
time_to_check = priv->aging_time;
 
DPRINTK("About to expire: %lx - %lx > %lx\n",
now,entry->last_used, time_to_check);
if( time_after(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;
lec_arp_remove(priv, entry);
kfree(entry);
entry = next;
} else {
/* Something else */
if ((entry->status == ESI_VC_PENDING ||
entry->status == ESI_ARP_PENDING)
&& time_after_eq(now,
entry->timestamp +
priv->max_unknown_frame_time)) {
entry->timestamp = jiffies;
entry->packets_flooded = 0;
if (entry->status == ESI_VC_PENDING)
send_to_lecd(priv, l_svc_setup, entry->mac_addr, entry->atm_addr, NULL);
}
if (entry->status == ESI_FLUSH_PENDING
&&
time_after_eq(now, entry->timestamp+
priv->path_switching_delay)) {
struct sk_buff *skb;
 
while ((skb = skb_dequeue(&entry->tx_wait)))
lec_send(entry->vcc, skb, entry->priv);
entry->last_used = jiffies;
entry->status =
ESI_FORWARD_DIRECT;
}
entry = entry->next;
}
}
}
lec_arp_put(priv);
}
 
mod_timer(&priv->lec_arp_timer, jiffies + LEC_ARP_REFRESH_INTERVAL);
}
/*
* Try to find vcc where mac_address is attached.
*
*/
struct atm_vcc*
lec_arp_resolve(struct lec_priv *priv, unsigned char *mac_to_find, int is_rdesc,
struct lec_arp_table **ret_entry)
{
struct lec_arp_table *entry;
 
if (mac_to_find[0]&0x01) {
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) {
if (entry->status == ESI_FORWARD_DIRECT) {
/* Connection Ok */
entry->last_used = jiffies;
*ret_entry = entry;
return entry->vcc;
}
/* Data direct VC not yet set up, check to see if the unknown
frame count is greater than the limit. If the limit has
not been reached, allow the caller to send packet to
BUS. */
if (entry->status != ESI_FLUSH_PENDING &&
entry->packets_flooded<priv->maximum_unknown_frame_count) {
entry->packets_flooded++;
DPRINTK("LEC_ARP: Flooding..\n");
return priv->mcast_vcc;
}
/* We got here because entry->status == ESI_FLUSH_PENDING
* or BUS flood limit was reached for an entry which is
* in ESI_ARP_PENDING or ESI_VC_PENDING state.
*/
*ret_entry = entry;
DPRINTK("lec: entry->status %d entry->vcc %p\n", entry->status, entry->vcc);
return NULL;
} else {
/* No matching entry was found */
entry = make_entry(priv, mac_to_find);
DPRINTK("LEC_ARP: Making entry\n");
if (!entry) {
return priv->mcast_vcc;
}
lec_arp_add(priv, entry);
/* We want arp-request(s) to be sent */
entry->packets_flooded =1;
entry->status = ESI_ARP_PENDING;
entry->no_tries = 1;
entry->last_used = entry->timestamp = jiffies;
entry->is_rdesc = is_rdesc;
if (entry->is_rdesc)
send_to_lecd(priv, l_rdesc_arp_xmt, mac_to_find, NULL, NULL);
else
send_to_lecd(priv, l_arp_xmt, mac_to_find, NULL, NULL);
entry->timer.expires = jiffies + (1*HZ);
entry->timer.function = lec_arp_expire_arp;
add_timer(&entry->timer);
return priv->mcast_vcc;
}
}
 
int
lec_addr_delete(struct lec_priv *priv, unsigned char *atm_addr,
unsigned long permanent)
{
struct lec_arp_table *entry, *next;
int i;
 
lec_arp_get(priv);
DPRINTK("lec_addr_delete\n");
for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
for(entry=priv->lec_arp_tables[i];entry != NULL; entry=next) {
next = entry->next;
if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN)
&& (permanent ||
!(entry->flags & LEC_PERMANENT_FLAG))) {
lec_arp_remove(priv, entry);
kfree(entry);
}
lec_arp_put(priv);
return 0;
}
}
lec_arp_put(priv);
return -1;
}
 
/*
* Notifies: Response to arp_request (atm_addr != NULL)
*/
void
lec_arp_update(struct lec_priv *priv, unsigned char *mac_addr,
unsigned char *atm_addr, unsigned long remoteflag,
unsigned int targetless_le_arp)
{
struct lec_arp_table *entry, *tmp;
int i;
 
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_get(priv);
if (priv->lec_arp_empty_ones) {
entry = priv->lec_arp_empty_ones;
if (!memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN)) {
priv->lec_arp_empty_ones = entry->next;
} else {
while(entry->next && memcmp(entry->next->atm_addr,
atm_addr, ATM_ESA_LEN))
entry = entry->next;
if (entry->next) {
tmp = entry;
entry = entry->next;
tmp->next = entry->next;
} else
entry = NULL;
}
if (entry) {
del_timer(&entry->timer);
tmp = lec_arp_find(priv, mac_addr);
if (tmp) {
del_timer(&tmp->timer);
tmp->status = ESI_FORWARD_DIRECT;
memcpy(tmp->atm_addr, atm_addr, ATM_ESA_LEN);
tmp->vcc = entry->vcc;
tmp->old_push = entry->old_push;
tmp->last_used = jiffies;
del_timer(&entry->timer);
kfree(entry);
entry=tmp;
} else {
entry->status = ESI_FORWARD_DIRECT;
memcpy(entry->mac_addr, mac_addr, ETH_ALEN);
entry->last_used = jiffies;
lec_arp_add(priv, entry);
}
if (remoteflag)
entry->flags|=LEC_REMOTE_FLAG;
else
entry->flags&=~LEC_REMOTE_FLAG;
lec_arp_put(priv);
DPRINTK("After update\n");
dump_arp_table(priv);
return;
}
}
entry = lec_arp_find(priv, mac_addr);
if (!entry) {
entry = make_entry(priv, mac_addr);
if (!entry) {
lec_arp_put(priv);
return;
}
entry->status = ESI_UNKNOWN;
lec_arp_add(priv, entry);
/* Temporary, changes before end of function */
}
memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN);
del_timer(&entry->timer);
for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
for(tmp=priv->lec_arp_tables[i];tmp;tmp=tmp->next) {
if (entry != tmp &&
!memcmp(tmp->atm_addr, atm_addr,
ATM_ESA_LEN)) {
/* Vcc to this host exists */
if (tmp->status > ESI_VC_PENDING) {
/*
* ESI_FLUSH_PENDING,
* ESI_FORWARD_DIRECT
*/
entry->vcc = tmp->vcc;
entry->old_push=tmp->old_push;
}
entry->status=tmp->status;
break;
}
}
}
if (remoteflag)
entry->flags|=LEC_REMOTE_FLAG;
else
entry->flags&=~LEC_REMOTE_FLAG;
if (entry->status == ESI_ARP_PENDING ||
entry->status == ESI_UNKNOWN) {
entry->status = ESI_VC_PENDING;
send_to_lecd(priv, l_svc_setup, entry->mac_addr, atm_addr, NULL);
}
DPRINTK("After update2\n");
dump_arp_table(priv);
lec_arp_put(priv);
}
 
/*
* Notifies: Vcc setup ready
*/
void
lec_vcc_added(struct lec_priv *priv, struct atmlec_ioc *ioc_data,
struct atm_vcc *vcc,
void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb))
{
struct lec_arp_table *entry;
int i, found_entry=0;
 
lec_arp_get(priv);
if (ioc_data->receive == 2) {
/* Vcc for Multicast Forward. No timer, LANEv2 7.1.20 and 2.3.5.3 */
 
DPRINTK("LEC_ARP: Attaching mcast forward\n");
#if 0
entry = lec_arp_find(priv, bus_mac);
if (!entry) {
printk("LEC_ARP: Multicast entry not found!\n");
lec_arp_put(priv);
return;
}
memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
entry->recv_vcc = vcc;
entry->old_recv_push = old_push;
#endif
entry = make_entry(priv, bus_mac);
if (entry == NULL) {
lec_arp_put(priv);
return;
}
del_timer(&entry->timer);
memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
entry->recv_vcc = vcc;
entry->old_recv_push = old_push;
entry->next = priv->mcast_fwds;
priv->mcast_fwds = entry;
lec_arp_put(priv);
return;
} else if (ioc_data->receive == 1) {
/* Vcc which we don't want to make default vcc, attach it
anyway. */
DPRINTK("LEC_ARP:Attaching data direct, not default :%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
ioc_data->atm_addr[0],ioc_data->atm_addr[1],
ioc_data->atm_addr[2],ioc_data->atm_addr[3],
ioc_data->atm_addr[4],ioc_data->atm_addr[5],
ioc_data->atm_addr[6],ioc_data->atm_addr[7],
ioc_data->atm_addr[8],ioc_data->atm_addr[9],
ioc_data->atm_addr[10],ioc_data->atm_addr[11],
ioc_data->atm_addr[12],ioc_data->atm_addr[13],
ioc_data->atm_addr[14],ioc_data->atm_addr[15],
ioc_data->atm_addr[16],ioc_data->atm_addr[17],
ioc_data->atm_addr[18],ioc_data->atm_addr[19]);
entry = make_entry(priv, bus_mac);
if (entry == NULL) {
lec_arp_put(priv);
return;
}
memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
memset(entry->mac_addr, 0, ETH_ALEN);
entry->recv_vcc = vcc;
entry->old_recv_push = old_push;
entry->status = ESI_UNKNOWN;
entry->timer.expires = jiffies + priv->vcc_timeout_period;
entry->timer.function = lec_arp_expire_vcc;
add_timer(&entry->timer);
entry->next = priv->lec_no_forward;
priv->lec_no_forward = entry;
lec_arp_put(priv);
dump_arp_table(priv);
return;
}
DPRINTK("LEC_ARP:Attaching data direct, default:%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
ioc_data->atm_addr[0],ioc_data->atm_addr[1],
ioc_data->atm_addr[2],ioc_data->atm_addr[3],
ioc_data->atm_addr[4],ioc_data->atm_addr[5],
ioc_data->atm_addr[6],ioc_data->atm_addr[7],
ioc_data->atm_addr[8],ioc_data->atm_addr[9],
ioc_data->atm_addr[10],ioc_data->atm_addr[11],
ioc_data->atm_addr[12],ioc_data->atm_addr[13],
ioc_data->atm_addr[14],ioc_data->atm_addr[15],
ioc_data->atm_addr[16],ioc_data->atm_addr[17],
ioc_data->atm_addr[18],ioc_data->atm_addr[19]);
for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
for (entry = priv->lec_arp_tables[i];entry;entry=entry->next) {
if (memcmp(ioc_data->atm_addr, entry->atm_addr,
ATM_ESA_LEN)==0) {
DPRINTK("LEC_ARP: Attaching data direct\n");
DPRINTK("Currently -> Vcc: %d, Rvcc:%d\n",
entry->vcc?entry->vcc->vci:0,
entry->recv_vcc?entry->recv_vcc->vci:0);
found_entry=1;
del_timer(&entry->timer);
entry->vcc = vcc;
entry->old_push = old_push;
if (entry->status == ESI_VC_PENDING) {
if(priv->maximum_unknown_frame_count
==0)
entry->status =
ESI_FORWARD_DIRECT;
else {
entry->timestamp = jiffies;
entry->status =
ESI_FLUSH_PENDING;
#if 0
send_to_lecd(priv,l_flush_xmt,
NULL,
entry->atm_addr,
NULL);
#endif
}
} else {
/* They were forming a connection
to us, and we to them. Our
ATM address is numerically lower
than theirs, so we make connection
we formed into default VCC (8.1.11).
Connection they made gets torn
down. This might confuse some
clients. Can be changed if
someone reports trouble... */
;
}
}
}
}
if (found_entry) {
lec_arp_put(priv);
DPRINTK("After vcc was added\n");
dump_arp_table(priv);
return;
}
/* Not found, snatch address from first data packet that arrives from
this vcc */
entry = make_entry(priv, bus_mac);
if (!entry) {
lec_arp_put(priv);
return;
}
entry->vcc = vcc;
entry->old_push = old_push;
memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
memset(entry->mac_addr, 0, ETH_ALEN);
entry->status = ESI_UNKNOWN;
entry->next = priv->lec_arp_empty_ones;
priv->lec_arp_empty_ones = entry;
entry->timer.expires = jiffies + priv->vcc_timeout_period;
entry->timer.function = lec_arp_expire_vcc;
add_timer(&entry->timer);
lec_arp_put(priv);
DPRINTK("After vcc was added\n");
dump_arp_table(priv);
}
 
void
lec_flush_complete(struct lec_priv *priv, unsigned long tran_id)
{
struct lec_arp_table *entry;
int i;
DPRINTK("LEC:lec_flush_complete %lx\n",tran_id);
for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
for (entry=priv->lec_arp_tables[i];entry;entry=entry->next) {
if (entry->flush_tran_id == tran_id &&
entry->status == ESI_FLUSH_PENDING) {
struct sk_buff *skb;
 
while ((skb = skb_dequeue(&entry->tx_wait)))
lec_send(entry->vcc, skb, entry->priv);
entry->status = ESI_FORWARD_DIRECT;
DPRINTK("LEC_ARP: Flushed\n");
}
}
}
dump_arp_table(priv);
}
 
void
lec_set_flush_tran_id(struct lec_priv *priv,
unsigned char *atm_addr, unsigned long tran_id)
{
struct lec_arp_table *entry;
int i;
 
for (i=0;i<LEC_ARP_TABLE_SIZE;i++)
for(entry=priv->lec_arp_tables[i];entry;entry=entry->next)
if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN)) {
entry->flush_tran_id = tran_id;
DPRINTK("Set flush transaction id to %lx for %p\n",tran_id,entry);
}
}
 
int
lec_mcast_make(struct lec_priv *priv, struct atm_vcc *vcc)
{
unsigned char mac_addr[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct lec_arp_table *to_add;
lec_arp_get(priv);
to_add = make_entry(priv, mac_addr);
if (!to_add) {
lec_arp_put(priv);
return -ENOMEM;
}
memcpy(to_add->atm_addr, vcc->remote.sas_addr.prv, ATM_ESA_LEN);
to_add->status = ESI_FORWARD_DIRECT;
to_add->flags |= LEC_PERMANENT_FLAG;
to_add->vcc = vcc;
to_add->old_push = vcc->push;
vcc->push = lec_push;
priv->mcast_vcc = vcc;
lec_arp_add(priv, to_add);
lec_arp_put(priv);
return 0;
}
 
void
lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc)
{
struct lec_arp_table *entry, *next;
int i;
 
DPRINTK("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n",vcc->vpi,vcc->vci);
dump_arp_table(priv);
lec_arp_get(priv);
for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
for(entry = priv->lec_arp_tables[i];entry; entry=next) {
next = entry->next;
if (vcc == entry->vcc) {
lec_arp_remove(priv, entry);
kfree(entry);
if (priv->mcast_vcc == vcc) {
priv->mcast_vcc = NULL;
}
}
}
}
 
entry = priv->lec_arp_empty_ones;
priv->lec_arp_empty_ones = NULL;
while (entry != NULL) {
next = entry->next;
if (entry->vcc == vcc) { /* leave it out from the list */
lec_arp_clear_vccs(entry);
del_timer(&entry->timer);
kfree(entry);
}
else { /* put it back to the list */
entry->next = priv->lec_arp_empty_ones;
priv->lec_arp_empty_ones = entry;
}
entry = next;
}
entry = priv->lec_no_forward;
priv->lec_no_forward = NULL;
while (entry != NULL) {
next = entry->next;
if (entry->recv_vcc == vcc) {
lec_arp_clear_vccs(entry);
del_timer(&entry->timer);
kfree(entry);
}
else {
entry->next = priv->lec_no_forward;
priv->lec_no_forward = entry;
}
entry = next;
}
 
entry = priv->mcast_fwds;
priv->mcast_fwds = NULL;
while (entry != NULL) {
next = entry->next;
if (entry->recv_vcc == vcc) {
lec_arp_clear_vccs(entry);
/* No timer, LANEv2 7.1.20 and 2.3.5.3 */
kfree(entry);
}
else {
entry->next = priv->mcast_fwds;
priv->mcast_fwds = entry;
}
entry = next;
}
 
lec_arp_put(priv);
dump_arp_table(priv);
}
 
void
lec_arp_check_empties(struct lec_priv *priv,
struct atm_vcc *vcc, struct sk_buff *skb)
{
unsigned long flags;
struct lec_arp_table *entry, *prev;
struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data;
unsigned char *src;
#ifdef CONFIG_TR
struct lecdatahdr_8025 *tr_hdr = (struct lecdatahdr_8025 *)skb->data;
 
if (priv->is_trdev) src = tr_hdr->h_source;
else
#endif
src = hdr->h_source;
 
lec_arp_get(priv);
entry = priv->lec_arp_empty_ones;
if (vcc == entry->vcc) {
spin_lock_irqsave(&priv->lec_arp_lock, flags);
del_timer(&entry->timer);
memcpy(entry->mac_addr, src, ETH_ALEN);
entry->status = ESI_FORWARD_DIRECT;
entry->last_used = jiffies;
priv->lec_arp_empty_ones = entry->next;
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
/* We might have got an entry */
if ((prev=lec_arp_find(priv,src))) {
lec_arp_remove(priv, prev);
kfree(prev);
}
lec_arp_add(priv, entry);
lec_arp_put(priv);
return;
}
spin_lock_irqsave(&priv->lec_arp_lock, flags);
prev = entry;
entry = entry->next;
while (entry && entry->vcc != vcc) {
prev= entry;
entry = entry->next;
}
if (!entry) {
DPRINTK("LEC_ARP: Arp_check_empties: entry not found!\n");
lec_arp_put(priv);
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
return;
}
del_timer(&entry->timer);
memcpy(entry->mac_addr, src, ETH_ALEN);
entry->status = ESI_FORWARD_DIRECT;
entry->last_used = jiffies;
prev->next = entry->next;
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
if ((prev = lec_arp_find(priv, src))) {
lec_arp_remove(priv, prev);
kfree(prev);
}
lec_arp_add(priv, entry);
lec_arp_put(priv);
}
MODULE_LICENSE("GPL");
/proc.c
0,0 → 1,684
/* net/atm/proc.c - ATM /proc interface */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
/*
* The mechanism used here isn't designed for speed but rather for convenience
* of implementation. We only return one entry per read system call, so we can
* be reasonably sure not to overrun the page and race conditions may lead to
* the addition or omission of some lines but never to any corruption of a
* line's internal structure.
*
* Making the whole thing slightly more efficient is left as an exercise to the
* reader. (Suggestions: wrapper which loops to get several entries per system
* call; or make --left slightly more clever to avoid O(n^2) characteristics.)
* I find it fast enough on my unloaded 266 MHz Pentium 2 :-)
*/
 
 
#include <linux/config.h>
#include <linux/module.h> /* for EXPORT_SYMBOL */
#include <linux/string.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/netdevice.h>
#include <linux/atmclip.h>
#include <linux/atmarp.h>
#include <linux/if_arp.h>
#include <linux/init.h> /* for __init */
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/param.h> /* for HZ */
#include "resources.h"
#include "common.h" /* atm_proc_init prototype */
#include "signaling.h" /* to get sigd - ugly too */
 
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
#include <net/atmclip.h>
#include "ipcommon.h"
#endif
 
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
#include "lec.h"
#include "lec_arpc.h"
#endif
 
static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
loff_t *pos);
static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
loff_t *pos);
 
static struct file_operations proc_dev_atm_operations = {
read: proc_dev_atm_read,
};
 
static struct file_operations proc_spec_atm_operations = {
read: proc_spec_atm_read,
};
 
static void add_stats(char *buf,const char *aal,
const struct k_atm_aal_stats *stats)
{
sprintf(strchr(buf,0),"%s ( %d %d %d %d %d )",aal,
atomic_read(&stats->tx),atomic_read(&stats->tx_err),
atomic_read(&stats->rx),atomic_read(&stats->rx_err),
atomic_read(&stats->rx_drop));
}
 
 
static void dev_info(const struct atm_dev *dev,char *buf)
{
int off,i;
 
off = sprintf(buf,"%3d %-8s",dev->number,dev->type);
for (i = 0; i < ESI_LEN; i++)
off += sprintf(buf+off,"%02x",dev->esi[i]);
strcat(buf," ");
add_stats(buf,"0",&dev->stats.aal0);
strcat(buf," ");
add_stats(buf,"5",&dev->stats.aal5);
sprintf(strchr(buf,0), "\t[%d]", atomic_read(&dev->refcnt));
strcat(buf,"\n");
}
 
 
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
 
 
static int svc_addr(char *buf,struct sockaddr_atmsvc *addr)
{
static int code[] = { 1,2,10,6,1,0 };
static int e164[] = { 1,8,4,6,1,0 };
int *fields;
int len,i,j,pos;
 
len = 0;
if (*addr->sas_addr.pub) {
strcpy(buf,addr->sas_addr.pub);
len = strlen(addr->sas_addr.pub);
buf += len;
if (*addr->sas_addr.prv) {
*buf++ = '+';
len++;
}
}
else if (!*addr->sas_addr.prv) {
strcpy(buf,"(none)");
return strlen(buf);
}
if (*addr->sas_addr.prv) {
len += 44;
pos = 0;
fields = *addr->sas_addr.prv == ATM_AFI_E164 ? e164 : code;
for (i = 0; fields[i]; i++) {
for (j = fields[i]; j; j--) {
sprintf(buf,"%02X",addr->sas_addr.prv[pos++]);
buf += 2;
}
if (fields[i+1]) *buf++ = '.';
}
}
return len;
}
 
 
static void atmarp_info(struct net_device *dev,struct atmarp_entry *entry,
struct clip_vcc *clip_vcc,char *buf)
{
unsigned char *ip;
int svc,off,ip_len;
 
svc = !clip_vcc || clip_vcc->vcc->sk->family == AF_ATMSVC;
off = sprintf(buf,"%-6s%-4s%-4s%5ld ",dev->name,svc ? "SVC" : "PVC",
!clip_vcc || clip_vcc->encap ? "LLC" : "NULL",
(jiffies-(clip_vcc ? clip_vcc->last_use : entry->neigh->used))/
HZ);
ip = (unsigned char *) &entry->ip;
ip_len = sprintf(buf+off,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
off += ip_len;
while (ip_len++ < 16) buf[off++] = ' ';
if (!clip_vcc)
if (time_before(jiffies, entry->expires))
strcpy(buf+off,"(resolving)\n");
else sprintf(buf+off,"(expired, ref %d)\n",
atomic_read(&entry->neigh->refcnt));
else if (!svc)
sprintf(buf+off,"%d.%d.%d\n",clip_vcc->vcc->dev->number,
clip_vcc->vcc->vpi,clip_vcc->vcc->vci);
else {
off += svc_addr(buf+off,&clip_vcc->vcc->remote);
strcpy(buf+off,"\n");
}
}
 
 
#endif
 
 
static void pvc_info(struct atm_vcc *vcc, char *buf, int clip_info)
{
static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
static const char *aal_name[] = {
"---", "1", "2", "3/4", /* 0- 3 */
"???", "5", "???", "???", /* 4- 7 */
"???", "???", "???", "???", /* 8-11 */
"???", "0", "???", "???"}; /* 12-15 */
int off;
 
off = sprintf(buf,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
vcc->dev->number,vcc->vpi,vcc->vci,
vcc->qos.aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" :
aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr,
class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
class_name[vcc->qos.txtp.traffic_class]);
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
if (clip_info && (vcc->push == atm_clip_ops->clip_push)) {
struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
struct net_device *dev;
 
dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL;
off += sprintf(buf+off,"CLIP, Itf:%s, Encap:",
dev ? dev->name : "none?");
if (clip_vcc->encap)
off += sprintf(buf+off,"LLC/SNAP");
else
off += sprintf(buf+off,"None");
}
#endif
strcpy(buf+off,"\n");
}
 
 
static const char *vcc_state(struct atm_vcc *vcc)
{
static const char *map[] = { ATM_VS2TXT_MAP };
 
return map[ATM_VF2VS(vcc->flags)];
}
 
 
static void vc_info(struct atm_vcc *vcc,char *buf)
{
char *here;
 
here = buf+sprintf(buf,"%p ",vcc);
if (!vcc->dev) here += sprintf(here,"Unassigned ");
else here += sprintf(here,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
vcc->vci);
switch (vcc->sk->family) {
case AF_ATMPVC:
here += sprintf(here,"PVC");
break;
case AF_ATMSVC:
here += sprintf(here,"SVC");
break;
default:
here += sprintf(here,"%3d",vcc->sk->family);
}
here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
vcc->reply,
atomic_read(&vcc->sk->wmem_alloc),vcc->sk->sndbuf,
atomic_read(&vcc->sk->rmem_alloc),vcc->sk->rcvbuf);
}
 
 
static void svc_info(struct atm_vcc *vcc,char *buf)
{
char *here;
int i;
 
if (!vcc->dev)
sprintf(buf,sizeof(void *) == 4 ? "N/A@%p%10s" : "N/A@%p%2s",
vcc,"");
else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
vcc->vci);
here = strchr(buf,0);
here += sprintf(here,"%-10s ",vcc_state(vcc));
here += sprintf(here,"%s%s",vcc->remote.sas_addr.pub,
*vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
if (*vcc->remote.sas_addr.prv)
for (i = 0; i < ATM_ESA_LEN; i++)
here += sprintf(here,"%02x",
vcc->remote.sas_addr.prv[i]);
strcat(here,"\n");
}
 
 
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
 
static char*
lec_arp_get_status_string(unsigned char status)
{
switch(status) {
case ESI_UNKNOWN:
return "ESI_UNKNOWN ";
case ESI_ARP_PENDING:
return "ESI_ARP_PENDING ";
case ESI_VC_PENDING:
return "ESI_VC_PENDING ";
case ESI_FLUSH_PENDING:
return "ESI_FLUSH_PENDING ";
case ESI_FORWARD_DIRECT:
return "ESI_FORWARD_DIRECT";
default:
return "<Unknown> ";
}
}
 
static void
lec_info(struct lec_arp_table *entry, char *buf)
{
int j, offset=0;
 
for(j=0;j<ETH_ALEN;j++) {
offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
}
offset+=sprintf(buf+offset, " ");
for(j=0;j<ATM_ESA_LEN;j++) {
offset+=sprintf(buf+offset,"%2.2x",0xff&entry->atm_addr[j]);
}
offset+=sprintf(buf+offset, " %s %4.4x",
lec_arp_get_status_string(entry->status),
entry->flags&0xffff);
if (entry->vcc) {
offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi,
entry->vcc->vci);
} else
offset+=sprintf(buf+offset, " ");
if (entry->recv_vcc) {
offset+=sprintf(buf+offset, " %3d %3d",
entry->recv_vcc->vpi, entry->recv_vcc->vci);
}
 
sprintf(buf+offset,"\n");
}
 
#endif
 
static int atm_devices_info(loff_t pos,char *buf)
{
struct atm_dev *dev;
struct list_head *p;
int left;
 
if (!pos) {
return sprintf(buf,"Itf Type ESI/\"MAC\"addr "
"AAL(TX,err,RX,err,drop) ... [refcnt]\n");
}
left = pos-1;
spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list);
if (left-- == 0) {
dev_info(dev,buf);
spin_unlock(&atm_dev_lock);
return strlen(buf);
}
}
spin_unlock(&atm_dev_lock);
return 0;
}
 
/*
* FIXME: it isn't safe to walk the VCC list without turning off interrupts.
* What is really needed is some lock on the devices. Ditto for ATMARP.
*/
 
static int atm_pvc_info(loff_t pos,char *buf)
{
struct sock *s;
struct atm_vcc *vcc;
int left, clip_info = 0;
 
if (!pos) {
return sprintf(buf,"Itf VPI VCI AAL RX(PCR,Class) "
"TX(PCR,Class)\n");
}
left = pos-1;
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
if (try_atm_clip_ops())
clip_info = 1;
#endif
read_lock(&vcc_sklist_lock);
for(s = vcc_sklist; s; s = s->next) {
vcc = s->protinfo.af_atm;
if (vcc->sk->family == PF_ATMPVC && vcc->dev && !left--) {
pvc_info(vcc,buf,clip_info);
read_unlock(&vcc_sklist_lock);
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
if (clip_info && atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
#endif
return strlen(buf);
}
}
read_unlock(&vcc_sklist_lock);
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
if (clip_info && atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
#endif
return 0;
}
 
 
static int atm_vc_info(loff_t pos,char *buf)
{
struct atm_vcc *vcc;
struct sock *s;
int left;
 
if (!pos)
return sprintf(buf,sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
"Address"," Itf VPI VCI Fam Flags Reply Send buffer"
" Recv buffer\n");
left = pos-1;
read_lock(&vcc_sklist_lock);
for(s = vcc_sklist; s; s = s->next) {
vcc = s->protinfo.af_atm;
if (!left--) {
vc_info(vcc,buf);
read_unlock(&vcc_sklist_lock);
return strlen(buf);
}
}
read_unlock(&vcc_sklist_lock);
 
return 0;
}
 
 
static int atm_svc_info(loff_t pos,char *buf)
{
struct sock *s;
struct atm_vcc *vcc;
int left;
 
if (!pos)
return sprintf(buf,"Itf VPI VCI State Remote\n");
left = pos-1;
read_lock(&vcc_sklist_lock);
for(s = vcc_sklist; s; s = s->next) {
vcc = s->protinfo.af_atm;
if (vcc->sk->family == PF_ATMSVC && !left--) {
svc_info(vcc,buf);
read_unlock(&vcc_sklist_lock);
return strlen(buf);
}
}
read_unlock(&vcc_sklist_lock);
 
return 0;
}
 
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
static int atm_arp_info(loff_t pos,char *buf)
{
struct neighbour *n;
int i,count;
 
if (!pos) {
return sprintf(buf,"IPitf TypeEncp Idle IP address "
"ATM address\n");
}
if (!try_atm_clip_ops())
return 0;
count = pos;
read_lock_bh(&clip_tbl_hook->lock);
for (i = 0; i <= NEIGH_HASHMASK; i++)
for (n = clip_tbl_hook->hash_buckets[i]; n; n = n->next) {
struct atmarp_entry *entry = NEIGH2ENTRY(n);
struct clip_vcc *vcc;
 
if (!entry->vccs) {
if (--count) continue;
atmarp_info(n->dev,entry,NULL,buf);
read_unlock_bh(&clip_tbl_hook->lock);
if (atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
return strlen(buf);
}
for (vcc = entry->vccs; vcc;
vcc = vcc->next) {
if (--count) continue;
atmarp_info(n->dev,entry,vcc,buf);
read_unlock_bh(&clip_tbl_hook->lock);
if (atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
return strlen(buf);
}
}
read_unlock_bh(&clip_tbl_hook->lock);
if (atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
return 0;
}
#endif
 
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
static int atm_lec_info(loff_t pos,char *buf)
{
unsigned long flags;
struct lec_priv *priv;
struct lec_arp_table *entry;
int i, count, d, e;
struct net_device *dev;
 
if (!pos) {
return sprintf(buf,"Itf MAC ATM destination"
" Status Flags "
"VPI/VCI Recv VPI/VCI\n");
}
if (!try_atm_lane_ops())
return 0; /* the lane module is not there yet */
 
count = pos;
for(d = 0; d < MAX_LEC_ITF; d++) {
dev = atm_lane_ops->get_lec(d);
if (!dev || !(priv = (struct lec_priv *) dev->priv))
continue;
spin_lock_irqsave(&priv->lec_arp_lock, flags);
for(i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
for(entry = priv->lec_arp_tables[i]; entry; entry = entry->next) {
if (--count)
continue;
e = sprintf(buf,"%s ", dev->name);
lec_info(entry, buf+e);
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
dev_put(dev);
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
return strlen(buf);
}
}
for(entry = priv->lec_arp_empty_ones; entry; entry = entry->next) {
if (--count)
continue;
e = sprintf(buf,"%s ", dev->name);
lec_info(entry, buf+e);
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
dev_put(dev);
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
return strlen(buf);
}
for(entry = priv->lec_no_forward; entry; entry=entry->next) {
if (--count)
continue;
e = sprintf(buf,"%s ", dev->name);
lec_info(entry, buf+e);
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
dev_put(dev);
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
return strlen(buf);
}
for(entry = priv->mcast_fwds; entry; entry = entry->next) {
if (--count)
continue;
e = sprintf(buf,"%s ", dev->name);
lec_info(entry, buf+e);
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
dev_put(dev);
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
return strlen(buf);
}
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
dev_put(dev);
}
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
return 0;
}
#endif
 
 
static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
loff_t *pos)
{
struct atm_dev *dev;
unsigned long page;
int length;
 
if (count == 0) return 0;
page = get_free_page(GFP_KERNEL);
if (!page) return -ENOMEM;
dev = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
->data;
if (!dev->ops->proc_read)
length = -EINVAL;
else {
length = dev->ops->proc_read(dev,pos,(char *) page);
if (length > count) length = -EINVAL;
}
if (length >= 0) {
if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
(*pos)++;
}
free_page(page);
return length;
}
 
 
static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
loff_t *pos)
{
unsigned long page;
int length;
int (*info)(loff_t,char *);
info = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
->data;
 
if (count == 0) return 0;
page = get_free_page(GFP_KERNEL);
if (!page) return -ENOMEM;
length = (*info)(*pos,(char *) page);
if (length > count) length = -EINVAL;
if (length >= 0) {
if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
(*pos)++;
}
free_page(page);
return length;
}
 
 
struct proc_dir_entry *atm_proc_root;
EXPORT_SYMBOL(atm_proc_root);
 
 
int atm_proc_dev_register(struct atm_dev *dev)
{
int digits,num;
int error;
 
error = -ENOMEM;
digits = 0;
for (num = dev->number; num; num /= 10) digits++;
if (!digits) digits++;
 
dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_ATOMIC);
if (!dev->proc_name)
goto fail1;
sprintf(dev->proc_name,"%s:%d",dev->type, dev->number);
 
dev->proc_entry = create_proc_entry(dev->proc_name, 0, atm_proc_root);
if (!dev->proc_entry)
goto fail0;
dev->proc_entry->data = dev;
dev->proc_entry->proc_fops = &proc_dev_atm_operations;
dev->proc_entry->owner = THIS_MODULE;
return 0;
fail0:
kfree(dev->proc_name);
fail1:
return error;
}
 
 
void atm_proc_dev_deregister(struct atm_dev *dev)
{
remove_proc_entry(dev->proc_name, atm_proc_root);
kfree(dev->proc_name);
}
 
 
#define CREATE_ENTRY(name) \
name = create_proc_entry(#name,0,atm_proc_root); \
if (!name) goto cleanup; \
name->data = atm_##name##_info; \
name->proc_fops = &proc_spec_atm_operations; \
name->owner = THIS_MODULE
 
static struct proc_dir_entry *devices = NULL, *pvc = NULL,
*svc = NULL, *arp = NULL, *lec = NULL, *vc = NULL;
 
static void atm_proc_cleanup(void)
{
if (devices)
remove_proc_entry("devices",atm_proc_root);
if (pvc)
remove_proc_entry("pvc",atm_proc_root);
if (svc)
remove_proc_entry("svc",atm_proc_root);
if (arp)
remove_proc_entry("arp",atm_proc_root);
if (lec)
remove_proc_entry("lec",atm_proc_root);
if (vc)
remove_proc_entry("vc",atm_proc_root);
remove_proc_entry("net/atm",NULL);
}
 
int atm_proc_init(void)
{
atm_proc_root = proc_mkdir("net/atm",NULL);
if (!atm_proc_root)
return -ENOMEM;
CREATE_ENTRY(devices);
CREATE_ENTRY(pvc);
CREATE_ENTRY(svc);
CREATE_ENTRY(vc);
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
CREATE_ENTRY(arp);
#endif
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
CREATE_ENTRY(lec);
#endif
return 0;
 
cleanup:
atm_proc_cleanup();
return -ENOMEM;
}
 
void atm_proc_exit(void)
{
atm_proc_cleanup();
}
/mpoa_caches.h
0,0 → 1,96
#ifndef MPOA_CACHES_H
#define MPOA_CACHES_H
 
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/atmmpc.h>
 
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;
atomic_t use;
} in_cache_entry;
 
struct in_cache_ops{
in_cache_entry *(*add_entry)(uint32_t dst_ip,
struct mpoa_client *client);
in_cache_entry *(*get)(uint32_t dst_ip, struct mpoa_client *client);
in_cache_entry *(*get_with_mask)(uint32_t dst_ip,
struct mpoa_client *client,
uint32_t mask);
in_cache_entry *(*get_by_vcc)(struct atm_vcc *vcc,
struct mpoa_client *client);
void (*put)(in_cache_entry *entry);
void (*remove_entry)(in_cache_entry *delEntry,
struct mpoa_client *client );
int (*cache_hit)(in_cache_entry *entry,
struct mpoa_client *client);
void (*clear_count)(struct mpoa_client *client);
void (*check_resolving)(struct mpoa_client *client);
void (*refresh)(struct mpoa_client *client);
void (*destroy_cache)(struct mpoa_client *mpc);
};
 
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;
uint32_t latest_ip_addr; /* The src IP address of the last packet */
struct eg_ctrl_info ctrl_info;
atomic_t use;
} eg_cache_entry;
 
struct eg_cache_ops{
eg_cache_entry *(*add_entry)(struct k_message *msg, struct mpoa_client *client);
eg_cache_entry *(*get_by_cache_id)(uint32_t cache_id, struct mpoa_client *client);
eg_cache_entry *(*get_by_tag)(uint32_t cache_id, struct mpoa_client *client);
eg_cache_entry *(*get_by_vcc)(struct atm_vcc *vcc, struct mpoa_client *client);
eg_cache_entry *(*get_by_src_ip)(uint32_t ipaddr, struct mpoa_client *client);
void (*put)(eg_cache_entry *entry);
void (*remove_entry)(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);
void (*destroy_cache)(struct mpoa_client *mpc);
};
 
 
/* 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
/lec.h
0,0 → 1,165
/*
*
* Lan Emulation client header file
*
* Marko Kiiskila carnil@cs.tut.fi
*
*/
 
#ifndef _LEC_H_
#define _LEC_H_
 
#include <linux/config.h>
#include <linux/atmdev.h>
#include <linux/netdevice.h>
#include <linux/atmlec.h>
 
#if defined (CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
#include <linux/if_bridge.h>
extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
unsigned char *addr);
extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
#endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
 
#define LEC_HEADER_LEN 16
 
struct lecdatahdr_8023 {
unsigned short le_header;
unsigned char h_dest[ETH_ALEN];
unsigned char h_source[ETH_ALEN];
unsigned short h_type;
};
 
struct lecdatahdr_8025 {
unsigned short le_header;
unsigned char ac_pad;
unsigned char fc;
unsigned char h_dest[ETH_ALEN];
unsigned char h_source[ETH_ALEN];
};
 
#define LEC_MINIMUM_8023_SIZE 62
#define LEC_MINIMUM_8025_SIZE 16
 
/*
* 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 net_device *dev, u8 *dst_mac, int force,
u8 **tlvs, u32 *sizeoftlvs);
int (*associate_req)(struct net_device *dev, u8 *lan_dst,
u8 *tlvs, u32 sizeoftlvs);
void (*associate_indicator)(struct net_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 net_device * (*get_lec)(int itf);
struct module *owner;
};
 
/*
* ATM LAN Emulation supports both LLC & Dix Ethernet EtherType
* frames.
* 1. Dix Ethernet EtherType frames encoded by placing EtherType
* field in h_type field. Data follows immediatelly after header.
* 2. LLC Data frames whose total length, including LLC field and data,
* but not padding required to meet the minimum data frame length,
* is less than 1536(0x0600) MUST be encoded by placing that length
* in the h_type field. The LLC field follows header immediatelly.
* 3. LLC data frames longer than this maximum MUST be encoded by placing
* the value 0 in the h_type field.
*
*/
 
/* Hash table size */
#define LEC_ARP_TABLE_SIZE 16
 
struct lec_priv {
struct net_device_stats stats;
unsigned short lecid; /* Lecid of this client */
struct lec_arp_table *lec_arp_empty_ones;
/* Used for storing VCC's that don't have a MAC address attached yet */
struct lec_arp_table *lec_arp_tables[LEC_ARP_TABLE_SIZE];
/* Actual LE ARP table */
struct lec_arp_table *lec_no_forward;
/* Used for storing VCC's (and forward packets from) which are to
age out by not using them to forward packets.
This is because to some LE clients there will be 2 VCCs. Only
one of them gets used. */
struct lec_arp_table *mcast_fwds;
/* With LANEv2 it is possible that BUS (or a special multicast server)
establishes multiple Multicast Forward VCCs to us. This list
collects all those VCCs. LANEv1 client has only one item in this
list. These entries are not aged out. */
atomic_t lec_arp_users;
spinlock_t lec_arp_lock;
struct atm_vcc *mcast_vcc; /* Default Multicast Send VCC */
struct atm_vcc *lecd;
struct timer_list lec_arp_timer;
/* C10 */
unsigned int maximum_unknown_frame_count;
/* Within the period of time defined by this variable, the client will send
no more than C10 frames to BUS for a given unicast destination. (C11) */
unsigned long max_unknown_frame_time;
/* If no traffic has been sent in this vcc for this period of time,
vcc will be torn down (C12)*/
unsigned long vcc_timeout_period;
/* An LE Client MUST not retry an LE_ARP_REQUEST for a
given frame's LAN Destination more than maximum retry count times,
after the first LEC_ARP_REQUEST (C13)*/
unsigned short max_retry_count;
/* Max time the client will maintain an entry in its arp cache in
absence of a verification of that relationship (C17)*/
unsigned long aging_time;
/* Max time the client will maintain an entry in cache when
topology change flag is true (C18) */
unsigned long forward_delay_time;
/* Topology change flag (C19)*/
int topology_change;
/* Max time the client expects an LE_ARP_REQUEST/LE_ARP_RESPONSE
cycle to take (C20)*/
unsigned long arp_response_time;
/* Time limit ot wait to receive an LE_FLUSH_RESPONSE after the
LE_FLUSH_REQUEST has been sent before taking recover action. (C21)*/
unsigned long flush_timeout;
/* The time since sending a frame to the bus after which the
LE Client may assume that the frame has been either discarded or
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 */
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 is_proxy; /* bridge between ATM and Ethernet */
int is_trdev; /* Device type, 0 = Ethernet, 1 = TokenRing */
};
 
int lecd_attach(struct atm_vcc *vcc, int arg);
int lec_vcc_attach(struct atm_vcc *vcc, void *arg);
int lec_mcast_attach(struct atm_vcc *vcc, int arg);
struct net_device *get_dev_lec(int itf);
int make_lec(struct atm_vcc *vcc);
int send_to_lecd(struct lec_priv *priv,
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);
 
extern struct atm_lane_ops *atm_lane_ops;
void atm_lane_ops_set(struct atm_lane_ops *hook);
int try_atm_lane_ops(void);
 
#endif /* _LEC_H_ */
 
/addr.c
0,0 → 1,141
/* net/atm/addr.c - Local ATM address registry */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
 
#include "signaling.h"
#include "addr.h"
 
 
static int check_addr(struct sockaddr_atmsvc *addr)
{
int i;
 
if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
if (!*addr->sas_addr.pub)
return *addr->sas_addr.prv ? 0 : -EINVAL;
for (i = 1; i < ATM_E164_LEN+1; i++) /* make sure it's \0-terminated */
if (!addr->sas_addr.pub[i]) return 0;
return -EINVAL;
}
 
 
static int identical(struct sockaddr_atmsvc *a,struct sockaddr_atmsvc *b)
{
if (*a->sas_addr.prv)
if (memcmp(a->sas_addr.prv,b->sas_addr.prv,ATM_ESA_LEN))
return 0;
if (!*a->sas_addr.pub) return !*b->sas_addr.pub;
if (!*b->sas_addr.pub) return 0;
return !strcmp(a->sas_addr.pub,b->sas_addr.pub);
}
 
 
static void notify_sigd(struct atm_dev *dev)
{
struct sockaddr_atmpvc pvc;
 
pvc.sap_addr.itf = dev->number;
sigd_enq(NULL,as_itf_notify,NULL,&pvc,NULL);
}
 
 
void atm_reset_addr(struct atm_dev *dev)
{
unsigned long flags;
struct atm_dev_addr *this;
 
spin_lock_irqsave(&dev->lock, flags);
while (dev->local) {
this = dev->local;
dev->local = this->next;
kfree(this);
}
spin_unlock_irqrestore(&dev->lock, flags);
notify_sigd(dev);
}
 
 
int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
{
unsigned long flags;
struct atm_dev_addr **walk;
int error;
 
error = check_addr(addr);
if (error)
return error;
spin_lock_irqsave(&dev->lock, flags);
for (walk = &dev->local; *walk; walk = &(*walk)->next)
if (identical(&(*walk)->addr,addr)) {
spin_unlock_irqrestore(&dev->lock, flags);
return -EEXIST;
}
*walk = kmalloc(sizeof(struct atm_dev_addr), GFP_ATOMIC);
if (!*walk) {
spin_unlock_irqrestore(&dev->lock, flags);
return -ENOMEM;
}
(*walk)->addr = *addr;
(*walk)->next = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
notify_sigd(dev);
return 0;
}
 
 
int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
{
unsigned long flags;
struct atm_dev_addr **walk,*this;
int error;
 
error = check_addr(addr);
if (error)
return error;
spin_lock_irqsave(&dev->lock, flags);
for (walk = &dev->local; *walk; walk = &(*walk)->next)
if (identical(&(*walk)->addr,addr)) break;
if (!*walk) {
spin_unlock_irqrestore(&dev->lock, flags);
return -ENOENT;
}
this = *walk;
*walk = this->next;
kfree(this);
spin_unlock_irqrestore(&dev->lock, flags);
notify_sigd(dev);
return 0;
}
 
 
int atm_get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *u_buf,int size)
{
unsigned long flags;
struct atm_dev_addr *walk;
int total = 0, error;
struct sockaddr_atmsvc *tmp_buf, *tmp_bufp;
 
 
spin_lock_irqsave(&dev->lock, flags);
for (walk = dev->local; walk; walk = walk->next)
total += sizeof(struct sockaddr_atmsvc);
tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC);
if (!tmp_buf) {
spin_unlock_irqrestore(&dev->lock, flags);
return -ENOMEM;
}
for (walk = dev->local; walk; walk = walk->next)
memcpy(tmp_bufp++, &walk->addr, sizeof(struct sockaddr_atmsvc));
spin_unlock_irqrestore(&dev->lock, flags);
error = total > size ? -E2BIG : total;
if (copy_to_user(u_buf, tmp_buf, total < size ? total : size))
error = -EFAULT;
kfree(tmp_buf);
return error;
}
/resources.c
0,0 → 1,405
/* net/atm/resources.c - Staticly allocated resources */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/config.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/atmdev.h>
#include <linux/sonet.h>
#include <linux/kernel.h> /* for barrier */
#include <linux/module.h>
#include <linux/bitops.h>
#include <net/sock.h> /* for struct sock */
#include <asm/segment.h> /* for get_fs_long and put_fs_long */
 
#include "common.h"
#include "resources.h"
#include "addr.h"
 
 
LIST_HEAD(atm_devs);
spinlock_t atm_dev_lock = SPIN_LOCK_UNLOCKED;
 
 
static struct atm_dev *__alloc_atm_dev(const char *type)
{
struct atm_dev *dev;
 
dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
if (!dev)
return NULL;
memset(dev, 0, sizeof(*dev));
dev->type = type;
dev->signal = ATM_PHY_SIG_UNKNOWN;
dev->link_rate = ATM_OC3_PCR;
spin_lock_init(&dev->lock);
 
return dev;
}
 
 
static void __free_atm_dev(struct atm_dev *dev)
{
kfree(dev);
}
 
static struct atm_dev *__atm_dev_lookup(int number)
{
struct atm_dev *dev;
struct list_head *p;
 
list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list);
if ((dev->ops) && (dev->number == number)) {
atm_dev_hold(dev);
return dev;
}
}
return NULL;
}
 
struct atm_dev *atm_dev_lookup(int number)
{
struct atm_dev *dev;
 
spin_lock(&atm_dev_lock);
dev = __atm_dev_lookup(number);
spin_unlock(&atm_dev_lock);
return dev;
}
 
struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
int number, atm_dev_flags_t *flags)
{
struct atm_dev *dev, *inuse;
 
 
dev = __alloc_atm_dev(type);
if (!dev) {
printk(KERN_ERR "atm_dev_register: no space for dev %s\n",
type);
return NULL;
}
spin_lock(&atm_dev_lock);
if (number != -1) {
if ((inuse = __atm_dev_lookup(number))) {
atm_dev_put(inuse);
spin_unlock(&atm_dev_lock);
__free_atm_dev(dev);
return NULL;
}
dev->number = number;
} else {
dev->number = 0;
while ((inuse = __atm_dev_lookup(dev->number))) {
atm_dev_put(inuse);
dev->number++;
}
}
 
dev->ops = ops;
if (flags)
dev->flags = *flags;
else
memset(&dev->flags, 0, sizeof(dev->flags));
memset(&dev->stats, 0, sizeof(dev->stats));
atomic_set(&dev->refcnt, 1);
list_add_tail(&dev->dev_list, &atm_devs);
spin_unlock(&atm_dev_lock);
 
#ifdef CONFIG_PROC_FS
if (ops->proc_read) {
if (atm_proc_dev_register(dev) < 0) {
printk(KERN_ERR "atm_dev_register: "
"atm_proc_dev_register failed for dev %s\n",
type);
spin_lock(&atm_dev_lock);
list_del(&dev->dev_list);
spin_unlock(&atm_dev_lock);
__free_atm_dev(dev);
return NULL;
}
}
#endif
 
return dev;
}
 
 
void atm_dev_deregister(struct atm_dev *dev)
{
unsigned long warning_time;
 
#ifdef CONFIG_PROC_FS
if (dev->ops->proc_read)
atm_proc_dev_deregister(dev);
#endif
spin_lock(&atm_dev_lock);
list_del(&dev->dev_list);
spin_unlock(&atm_dev_lock);
 
warning_time = jiffies;
while (atomic_read(&dev->refcnt) != 1) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ / 4);
current->state = TASK_RUNNING;
if ((jiffies - warning_time) > 10 * HZ) {
printk(KERN_EMERG "atm_dev_deregister: waiting for "
"dev %d to become free. Usage count = %d\n",
dev->number, atomic_read(&dev->refcnt));
warning_time = jiffies;
}
}
 
__free_atm_dev(dev);
}
 
void shutdown_atm_dev(struct atm_dev *dev)
{
if (atomic_read(&dev->refcnt) > 1) {
set_bit(ATM_DF_CLOSE, &dev->flags);
return;
}
if (dev->ops->dev_close)
dev->ops->dev_close(dev);
atm_dev_deregister(dev);
}
 
 
static void copy_aal_stats(struct k_atm_aal_stats *from,
struct atm_aal_stats *to)
{
#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
__AAL_STAT_ITEMS
#undef __HANDLE_ITEM
}
 
 
static void subtract_aal_stats(struct k_atm_aal_stats *from,
struct atm_aal_stats *to)
{
#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
__AAL_STAT_ITEMS
#undef __HANDLE_ITEM
}
 
 
static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats *arg, int zero)
{
struct atm_dev_stats tmp;
int error = 0;
 
copy_aal_stats(&dev->stats.aal0, &tmp.aal0);
copy_aal_stats(&dev->stats.aal34, &tmp.aal34);
copy_aal_stats(&dev->stats.aal5, &tmp.aal5);
if (arg)
error = copy_to_user(arg, &tmp, sizeof(tmp));
if (zero && !error) {
subtract_aal_stats(&dev->stats.aal0, &tmp.aal0);
subtract_aal_stats(&dev->stats.aal34, &tmp.aal34);
subtract_aal_stats(&dev->stats.aal5, &tmp.aal5);
}
return error ? -EFAULT : 0;
}
 
 
int atm_dev_ioctl(unsigned int cmd, unsigned long arg)
{
void *buf;
int error = 0, len, number, size = 0;
struct atm_dev *dev;
 
if (cmd == ATM_GETNAMES) {
int *tmp_buf, *tmp_bufp;
struct list_head *p;
/*
* ATM_GETNAMES is a special case: it doesn't require a
* device number argument
*/
if (get_user(buf, &((struct atm_iobuf *) arg)->buffer))
return -EFAULT;
if (get_user(len, &((struct atm_iobuf *) arg)->length))
return -EFAULT;
spin_lock(&atm_dev_lock);
list_for_each(p, &atm_devs)
size += sizeof(int);
if (size > len) {
spin_unlock(&atm_dev_lock);
return -E2BIG;
}
tmp_buf = tmp_bufp = kmalloc(size, GFP_ATOMIC);
if (!tmp_buf) {
spin_unlock(&atm_dev_lock);
return -ENOMEM;
}
list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list);
*tmp_bufp++ = dev->number;
}
spin_unlock(&atm_dev_lock);
error = (copy_to_user(buf, tmp_buf, size) ||
put_user(size, &((struct atm_iobuf *) arg)->length))
? -EFAULT : 0;
kfree(tmp_buf);
return error;
}
 
if (get_user(buf, &((struct atmif_sioc *) arg)->arg))
return -EFAULT;
if (get_user(len, &((struct atmif_sioc *) arg)->length))
return -EFAULT;
if (get_user(number, &((struct atmif_sioc *) arg)->number))
return -EFAULT;
 
if (!(dev = atm_dev_lookup(number)))
return -ENODEV;
switch (cmd) {
case ATM_GETTYPE:
size = strlen(dev->type) + 1;
if (copy_to_user(buf, dev->type, size)) {
error = -EFAULT;
goto done;
}
break;
case ATM_GETESI:
size = ESI_LEN;
if (copy_to_user(buf, dev->esi, size)) {
error = -EFAULT;
goto done;
}
break;
case ATM_SETESI:
{
int i;
 
for (i = 0; i < ESI_LEN; i++)
if (dev->esi[i]) {
error = -EEXIST;
goto done;
}
}
/* fall through */
case ATM_SETESIF:
{
unsigned char esi[ESI_LEN];
 
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (copy_from_user(esi, buf, ESI_LEN)) {
error = -EFAULT;
goto done;
}
memcpy(dev->esi, esi, ESI_LEN);
error = ESI_LEN;
goto done;
}
case ATM_GETSTATZ:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
/* fall through */
case ATM_GETSTAT:
size = sizeof(struct atm_dev_stats);
error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ);
if (error)
goto done;
break;
case ATM_GETCIRANGE:
size = sizeof(struct atm_cirange);
if (copy_to_user(buf, &dev->ci_range, size)) {
error = -EFAULT;
goto done;
}
break;
case ATM_GETLINKRATE:
size = sizeof(int);
if (copy_to_user(buf, &dev->link_rate, size)) {
error = -EFAULT;
goto done;
}
break;
case ATM_RSTADDR:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
atm_reset_addr(dev);
break;
case ATM_ADDADDR:
case ATM_DELADDR:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
{
struct sockaddr_atmsvc addr;
 
if (copy_from_user(&addr, buf, sizeof(addr))) {
error = -EFAULT;
goto done;
}
if (cmd == ATM_ADDADDR)
error = atm_add_addr(dev, &addr);
else
error = atm_del_addr(dev, &addr);
goto done;
}
case ATM_GETADDR:
error = atm_get_addr(dev, buf, len);
if (error < 0)
goto done;
size = error;
/* write back size even if it's zero */
goto write_size;
case ATM_SETLOOP:
if (__ATM_LM_XTRMT((int) (long) buf) &&
__ATM_LM_XTLOC((int) (long) buf) >
__ATM_LM_XTRMT((int) (long) buf)) {
error = -EINVAL;
goto done;
}
/* fall through */
case ATM_SETCIRANGE:
case SONET_GETSTATZ:
case SONET_SETDIAG:
case SONET_CLRDIAG:
case SONET_SETFRAMING:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
/* fall through */
default:
if (!dev->ops->ioctl) {
error = -EINVAL;
goto done;
}
size = dev->ops->ioctl(dev, cmd, buf);
if (size < 0) {
error = (size == -ENOIOCTLCMD ? -EINVAL : size);
goto done;
}
}
if (size) {
write_size:
error = put_user(size,
&((struct atmif_sioc *) arg)->length)
? -EFAULT : 0;
}
done:
atm_dev_put(dev);
return error;
}
 
 
EXPORT_SYMBOL(atm_dev_register);
EXPORT_SYMBOL(atm_dev_deregister);
EXPORT_SYMBOL(atm_dev_lookup);
EXPORT_SYMBOL(shutdown_atm_dev);
/signaling.c
0,0 → 1,260
/* net/atm/signaling.c - ATM signaling */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/errno.h> /* error codes */
#include <linux/kernel.h> /* printk */
#include <linux/skbuff.h>
#include <linux/wait.h>
#include <linux/sched.h> /* jiffies and HZ */
#include <linux/atm.h> /* ATM stuff */
#include <linux/atmsap.h>
#include <linux/atmsvc.h>
#include <linux/atmdev.h>
#include <linux/bitops.h>
 
#include "resources.h"
#include "signaling.h"
 
 
#undef WAIT_FOR_DEMON /* #define this if system calls on SVC sockets
should block until the demon runs.
Danger: may cause nasty hangs if the demon
crashes. */
 
#if 0
#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
#else
#define DPRINTK(format,args...)
#endif
 
 
struct atm_vcc *sigd = NULL;
static DECLARE_WAIT_QUEUE_HEAD(sigd_sleep);
 
 
static void sigd_put_skb(struct sk_buff *skb)
{
#ifdef WAIT_FOR_DEMON
static unsigned long silence = 0;
DECLARE_WAITQUEUE(wait,current);
 
add_wait_queue(&sigd_sleep,&wait);
while (!sigd) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (time_after(jiffies, silence) || silence == 0) {
printk(KERN_INFO "atmsvc: waiting for signaling demon "
"...\n");
silence = (jiffies+30*HZ)|1;
}
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&sigd_sleep,&wait);
#else
if (!sigd) {
printk(KERN_WARNING "atmsvc: no signaling demon\n");
kfree_skb(skb);
return;
}
#endif
atm_force_charge(sigd,skb->truesize);
skb_queue_tail(&sigd->sk->receive_queue,skb);
wake_up(&sigd->sleep);
}
 
 
static void modify_qos(struct atm_vcc *vcc,struct atmsvc_msg *msg)
{
struct sk_buff *skb;
 
if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
!test_bit(ATM_VF_READY,&vcc->flags))
return;
msg->type = as_error;
if (!vcc->dev->ops->change_qos) msg->reply = -EOPNOTSUPP;
else {
/* should lock VCC */
msg->reply = vcc->dev->ops->change_qos(vcc,&msg->qos,
msg->reply);
if (!msg->reply) msg->type = as_okay;
}
/*
* Should probably just turn around the old skb. But the, the buffer
* space accounting needs to follow the change too. Maybe later.
*/
while (!(skb = alloc_skb(sizeof(struct atmsvc_msg),GFP_KERNEL)))
schedule();
*(struct atmsvc_msg *) skb_put(skb,sizeof(struct atmsvc_msg)) = *msg;
sigd_put_skb(skb);
}
 
 
static int sigd_send(struct atm_vcc *vcc,struct sk_buff *skb)
{
struct atmsvc_msg *msg;
struct atm_vcc *session_vcc;
 
msg = (struct atmsvc_msg *) skb->data;
atomic_sub(skb->truesize, &vcc->sk->wmem_alloc);
DPRINTK("sigd_send %d (0x%lx)\n",(int) msg->type,
(unsigned long) msg->vcc);
vcc = *(struct atm_vcc **) &msg->vcc;
switch (msg->type) {
case as_okay:
vcc->reply = msg->reply;
if (!*vcc->local.sas_addr.prv &&
!*vcc->local.sas_addr.pub) {
vcc->local.sas_family = AF_ATMSVC;
memcpy(vcc->local.sas_addr.prv,
msg->local.sas_addr.prv,ATM_ESA_LEN);
memcpy(vcc->local.sas_addr.pub,
msg->local.sas_addr.pub,ATM_E164_LEN+1);
}
session_vcc = vcc->session ? vcc->session : vcc;
if (session_vcc->vpi || session_vcc->vci) break;
session_vcc->itf = msg->pvc.sap_addr.itf;
session_vcc->vpi = msg->pvc.sap_addr.vpi;
session_vcc->vci = msg->pvc.sap_addr.vci;
if (session_vcc->vpi || session_vcc->vci)
session_vcc->qos = msg->qos;
break;
case as_error:
clear_bit(ATM_VF_REGIS,&vcc->flags);
clear_bit(ATM_VF_READY,&vcc->flags);
vcc->reply = msg->reply;
vcc->sk->err = -msg->reply;
break;
case as_indicate:
vcc = *(struct atm_vcc **) &msg->listen_vcc;
DPRINTK("as_indicate!!!\n");
lock_sock(vcc->sk);
if (vcc->sk->ack_backlog == vcc->sk->max_ack_backlog) {
sigd_enq(0,as_reject,vcc,NULL,NULL);
goto as_indicate_complete;
}
vcc->sk->ack_backlog++;
skb_queue_tail(&vcc->sk->receive_queue,skb);
if (vcc->callback) {
DPRINTK("waking vcc->sleep 0x%p\n",
&vcc->sleep);
vcc->callback(vcc);
}
as_indicate_complete:
release_sock(vcc->sk);
return 0;
case as_close:
set_bit(ATM_VF_RELEASED,&vcc->flags);
clear_bit(ATM_VF_READY,&vcc->flags);
vcc->reply = msg->reply;
vcc->sk->err = -msg->reply;
break;
case as_modify:
modify_qos(vcc,msg);
break;
default:
printk(KERN_ALERT "sigd_send: bad message type %d\n",
(int) msg->type);
return -EINVAL;
}
if (vcc->callback) vcc->callback(vcc);
dev_kfree_skb(skb);
return 0;
}
 
 
void sigd_enq2(struct atm_vcc *vcc,enum atmsvc_msg_type type,
struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
const struct sockaddr_atmsvc *svc,const struct atm_qos *qos,int reply)
{
struct sk_buff *skb;
struct atmsvc_msg *msg;
 
DPRINTK("sigd_enq %d (0x%p)\n",(int) type,vcc);
while (!(skb = alloc_skb(sizeof(struct atmsvc_msg),GFP_KERNEL)))
schedule();
msg = (struct atmsvc_msg *) skb_put(skb,sizeof(struct atmsvc_msg));
memset(msg,0,sizeof(*msg));
msg->type = type;
*(struct atm_vcc **) &msg->vcc = vcc;
*(struct atm_vcc **) &msg->listen_vcc = listen_vcc;
msg->reply = reply;
if (qos) msg->qos = *qos;
if (vcc) msg->sap = vcc->sap;
if (svc) msg->svc = *svc;
if (vcc) msg->local = vcc->local;
if (pvc) msg->pvc = *pvc;
sigd_put_skb(skb);
if (vcc) set_bit(ATM_VF_REGIS,&vcc->flags);
}
 
 
void sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type,
struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
const struct sockaddr_atmsvc *svc)
{
sigd_enq2(vcc,type,listen_vcc,pvc,svc,vcc ? &vcc->qos : NULL,0);
/* other ISP applications may use "reply" */
}
 
 
static void purge_vcc(struct atm_vcc *vcc)
{
if (vcc->sk->family == PF_ATMSVC &&
!test_bit(ATM_VF_META, &vcc->flags)) {
set_bit(ATM_VF_RELEASED, &vcc->flags);
vcc->reply = -EUNATCH;
vcc->sk->err = EUNATCH;
wake_up(&vcc->sleep);
}
}
 
 
static void sigd_close(struct atm_vcc *vcc)
{
struct sock *s;
 
DPRINTK("sigd_close\n");
sigd = NULL;
if (skb_peek(&vcc->sk->receive_queue))
printk(KERN_ERR "sigd_close: closing with requests pending\n");
skb_queue_purge(&vcc->sk->receive_queue);
 
read_lock(&vcc_sklist_lock);
for(s = vcc_sklist; s; s = s->next) {
struct atm_vcc *vcc = s->protinfo.af_atm;
 
if (vcc->dev)
purge_vcc(vcc);
}
read_unlock(&vcc_sklist_lock);
}
 
 
static struct atmdev_ops sigd_dev_ops = {
.close = sigd_close,
.send = sigd_send
};
 
 
static struct atm_dev sigd_dev = {
.ops = &sigd_dev_ops,
.type = "sig",
.number = 999,
.lock = SPIN_LOCK_UNLOCKED
};
 
 
int sigd_attach(struct atm_vcc *vcc)
{
if (sigd) return -EADDRINUSE;
DPRINTK("sigd_attach\n");
sigd = vcc;
vcc->dev = &sigd_dev;
vcc_insert_socket(vcc->sk);
set_bit(ATM_VF_META,&vcc->flags);
set_bit(ATM_VF_READY,&vcc->flags);
wake_up(&sigd_sleep);
return 0;
}
/lec_arpc.h
0,0 → 1,116
/*
* Lec arp cache
* Marko Kiiskila carnil@cs.tut.fi
*
*/
#ifndef _LEC_ARP_H
#define _LEC_ARP_H
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/if_ether.h>
#include <linux/atmlec.h>
 
struct lec_arp_table {
struct lec_arp_table *next; /* Linked entry list */
unsigned char atm_addr[ATM_ESA_LEN]; /* Atm address */
unsigned char mac_addr[ETH_ALEN]; /* Mac address */
int is_rdesc; /* Mac address is a route descriptor */
struct atm_vcc *vcc; /* Vcc this entry is attached */
struct atm_vcc *recv_vcc; /* Vcc we receive data from */
void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb);
/* Push that leads to daemon */
void (*old_recv_push)(struct atm_vcc *vcc, struct sk_buff *skb);
/* Push that leads to daemon */
void (*old_close)(struct atm_vcc *vcc);
/* We want to see when this
* vcc gets closed */
unsigned long last_used; /* For expiry */
unsigned long timestamp; /* Used for various timestamping
* things:
* 1. FLUSH started
* (status=ESI_FLUSH_PENDING)
* 2. Counting to
* max_unknown_frame_time
* (status=ESI_ARP_PENDING||
* status=ESI_VC_PENDING)
*/
unsigned char no_tries; /* No of times arp retry has been
tried */
unsigned char status; /* Status of this entry */
unsigned short flags; /* Flags for this entry */
unsigned short packets_flooded; /* Data packets flooded */
unsigned long flush_tran_id; /* Transaction id in flush protocol */
struct timer_list timer; /* Arping timer */
struct lec_priv *priv; /* Pointer back */
 
u8 *tlvs; /* LANE2: Each MAC address can have TLVs */
u32 sizeoftlvs; /* associated with it. sizeoftlvs tells the */
/* the length of the tlvs array */
struct sk_buff_head tx_wait; /* wait queue for outgoing packets */
};
 
struct tlv { /* LANE2: Template tlv struct for accessing */
/* the tlvs in the lec_arp_table->tlvs array*/
u32 type;
u8 length;
u8 value[255];
};
 
/* Status fields */
#define ESI_UNKNOWN 0 /*
* Next packet sent to this mac address
* causes ARP-request to be sent
*/
#define ESI_ARP_PENDING 1 /*
* There is no ATM address associated with this
* 48-bit address. The LE-ARP protocol is in
* progress.
*/
#define ESI_VC_PENDING 2 /*
* There is a valid ATM address associated with
* this 48-bit address but there is no VC set
* up to that ATM address. The signaling
* protocol is in process.
*/
#define ESI_FLUSH_PENDING 4 /*
* The LEC has been notified of the FLUSH_START
* status and it is assumed that the flush
* protocol is in process.
*/
#define ESI_FORWARD_DIRECT 5 /*
* Either the Path Switching Delay (C22) has
* elapsed or the LEC has notified the Mapping
* that the flush protocol has completed. In
* either case, it is safe to forward packets
* to this address via the data direct VC.
*/
 
/* Flag values */
#define LEC_REMOTE_FLAG 0x0001
#define LEC_PERMANENT_FLAG 0x0002
 
/* Protos */
void lec_arp_init(struct lec_priv *priv);
int lec_mcast_make(struct lec_priv *priv, struct atm_vcc *vcc);
void lec_arp_destroy(struct lec_priv *priv);
void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc);
 
struct atm_vcc *lec_arp_resolve(struct lec_priv *priv,
unsigned char *mac_to_addr,
int is_rdesc,
struct lec_arp_table **ret_entry);
void lec_vcc_added(struct lec_priv *dev,
struct atmlec_ioc *ioc_data, struct atm_vcc *vcc,
void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb));
void lec_arp_check_empties(struct lec_priv *priv,
struct atm_vcc *vcc, struct sk_buff *skb);
int lec_addr_delete(struct lec_priv *priv,
unsigned char *mac_addr, unsigned long permanent);
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 int targetless_le_arp);
void lec_set_flush_tran_id(struct lec_priv *priv,
unsigned char *mac_addr, unsigned long tran_id);
 
#endif
/addr.h
0,0 → 1,18
/* net/atm/addr.h - Local ATM address registry */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#ifndef NET_ATM_ADDR_H
#define NET_ATM_ADDR_H
 
#include <linux/atm.h>
#include <linux/atmdev.h>
 
 
void atm_reset_addr(struct atm_dev *dev);
int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr);
int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr);
int atm_get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *u_buf,int size);
 
#endif
/resources.h
0,0 → 1,29
/* net/atm/resources.h - ATM-related resources */
 
/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
 
 
#ifndef NET_ATM_RESOURCES_H
#define NET_ATM_RESOURCES_H
 
#include <linux/config.h>
#include <linux/atmdev.h>
 
 
extern struct list_head atm_devs;
extern spinlock_t atm_dev_lock;
 
 
int atm_dev_ioctl(unsigned int cmd, unsigned long arg);
 
 
#ifdef CONFIG_PROC_FS
 
#include <linux/proc_fs.h>
 
int atm_proc_dev_register(struct atm_dev *dev);
void atm_proc_dev_deregister(struct atm_dev *dev);
 
#endif
 
#endif
/mpc.c
0,0 → 1,1478
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/bitops.h>
 
/* We are an ethernet device */
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <net/sock.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
#include <asm/checksum.h> /* for ip_fast_csum() */
#include <net/arp.h>
#include <net/dst.h>
#include <linux/proc_fs.h>
 
/* And atm device */
#include <linux/atmdev.h>
#include <linux/atmlec.h>
#include <linux/atmmpc.h>
/* Modular too */
#include <linux/config.h>
#include <linux/module.h>
 
#include "lec.h"
#include "mpc.h"
#include "resources.h"
 
/*
* mpc.c: Implementation of MPOA client kernel part
*/
 
#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
 
 
 
#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, int action);
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 uint8_t *copy_macs(struct mpoa_client *mpc, uint8_t *router_mac,
uint8_t *tlvs, uint8_t mps_macs, uint8_t device_type);
static void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry);
 
static void send_set_mps_ctrl_addr(char *addr, struct mpoa_client *mpc);
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 net_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 atm_mpoa_qos *qos_head = NULL;
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 net_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 */
}
 
/*
* Functions for managing QoS list
*/
 
/*
* Overwrites the old entry or makes a new one.
*/
struct atm_mpoa_qos *atm_mpoa_add_qos(uint32_t dst_ip, struct atm_qos *qos)
{
struct atm_mpoa_qos *entry;
 
entry = atm_mpoa_search_qos(dst_ip);
if (entry != NULL) {
entry->qos = *qos;
return entry;
}
 
entry = kmalloc(sizeof(struct atm_mpoa_qos), GFP_KERNEL);
if (entry == NULL) {
printk("mpoa: atm_mpoa_add_qos: out of memory\n");
return entry;
}
 
entry->ipaddr = dst_ip;
entry->qos = *qos;
 
entry->next = qos_head;
qos_head = entry;
 
return entry;
}
 
struct atm_mpoa_qos *atm_mpoa_search_qos(uint32_t dst_ip)
{
struct atm_mpoa_qos *qos;
 
qos = qos_head;
while( qos != NULL ){
if(qos->ipaddr == dst_ip) {
break;
}
qos = qos->next;
}
 
return qos;
}
 
/*
* Returns 0 for failure
*/
int atm_mpoa_delete_qos(struct atm_mpoa_qos *entry)
{
 
struct atm_mpoa_qos *curr;
 
if (entry == NULL) return 0;
if (entry == qos_head) {
qos_head = qos_head->next;
kfree(entry);
return 1;
}
 
curr = qos_head;
while (curr != NULL) {
if (curr->next == entry) {
curr->next = entry->next;
kfree(entry);
return 1;
}
curr = curr->next;
}
 
return 0;
}
 
void atm_mpoa_disp_qos(char *page, int *len)
{
 
unsigned char *ip;
char ipaddr[16];
struct atm_mpoa_qos *qos;
 
qos = qos_head;
*len += sprintf(page + *len, "QoS entries for shortcuts:\n");
*len += sprintf(page + *len, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n");
 
ipaddr[sizeof(ipaddr)-1] = '\0';
while (qos != NULL) {
ip = (unsigned char *)&qos->ipaddr;
sprintf(ipaddr, "%u.%u.%u.%u", NIPQUAD(ip));
*len += sprintf(page + *len, "%u.%u.%u.%u\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n",
NIPQUAD(ipaddr),
qos->qos.txtp.max_pcr, qos->qos.txtp.pcr, qos->qos.txtp.min_pcr, qos->qos.txtp.max_cdv, qos->qos.txtp.max_sdu,
qos->qos.rxtp.max_pcr, qos->qos.rxtp.pcr, qos->qos.rxtp.min_pcr, qos->qos.rxtp.max_cdv, qos->qos.rxtp.max_sdu);
qos = qos->next;
}
return;
}
 
static struct net_device *find_lec_by_itfnum(int itf)
{
struct net_device *dev;
if (!try_atm_lane_ops())
return NULL;
 
dev = atm_lane_ops->get_lec(itf);
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
return dev;
}
 
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));
mpc->ingress_lock = RW_LOCK_UNLOCKED;
mpc->egress_lock = RW_LOCK_UNLOCKED;
mpc->next = mpcs;
atm_mpoa_init_cache(mpc);
 
mpc->parameters.mpc_p1 = MPC_P1;
mpc->parameters.mpc_p2 = MPC_P2;
memset(mpc->parameters.mpc_p3,0,sizeof(mpc->parameters.mpc_p3));
mpc->parameters.mpc_p4 = MPC_P4;
mpc->parameters.mpc_p5 = MPC_P5;
mpc->parameters.mpc_p6 = MPC_P6;
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 net_device *dev)
{
dprintk("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)
{
dprintk("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) {
dprintk(" mpc already stopped, not fatal\n");
return;
}
dprintk("\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 * __attribute__ ((unused)) 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->lane2_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 net_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;
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);
mpc = find_mpc_by_lec(dev); /* Sampo-Fix: moved here from below */
if (mpc == NULL) {
printk("mpoa: (%s) lane2_assoc_ind: no mpc\n", dev->name);
return;
}
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 (tlvs + length > end_of_tlvs) {
printk("TLV value extends past its buffer, aborting parse\n");
return;
}
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++;
dprintk("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) {
dprintk("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 */
}
dprintk("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 */
send_set_mps_ctrl_addr(tlvs, mpc);
tlvs = copy_macs(mpc, mac_addr, tlvs, number_of_mps_macs, mpoa_device_type);
if (tlvs == NULL) return;
}
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;
}
 
/*
* Store at least advertizing router's MAC address
* plus the possible MAC address(es) to mpc->mps_macs.
* For a freshly allocated MPOA client mpc->mps_macs == 0.
*/
static uint8_t *copy_macs(struct mpoa_client *mpc, uint8_t *router_mac,
uint8_t *tlvs, uint8_t mps_macs, uint8_t device_type)
{
int num_macs;
num_macs = (mps_macs > 1) ? mps_macs : 1;
 
if (mpc->number_of_mps_macs != num_macs) { /* need to reallocate? */
if (mpc->number_of_mps_macs != 0) kfree(mpc->mps_macs);
mpc->number_of_mps_macs = 0;
mpc->mps_macs = kmalloc(num_macs*ETH_ALEN, GFP_KERNEL);
if (mpc->mps_macs == NULL) {
printk("mpoa: (%s) copy_macs: out of mem\n", mpc->dev->name);
return NULL;
}
}
memcpy(mpc->mps_macs, router_mac, ETH_ALEN);
tlvs += 20; if (device_type == MPS_AND_MPC) tlvs += 20;
if (mps_macs > 0)
memcpy(mpc->mps_macs, tlvs, mps_macs*ETH_ALEN);
tlvs += mps_macs*ETH_ALEN;
mpc->number_of_mps_macs = num_macs;
 
return tlvs;
}
 
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->get(ipaddr, mpc);
if (entry == NULL) {
entry = mpc->in_ops->add_entry(ipaddr, mpc);
if (entry != NULL) mpc->in_ops->put(entry);
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);
mpc->in_ops->put(entry);
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);
mpc->in_ops->put(entry);
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, ETH_HLEN); /* get rid of 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, ETH_HLEN); /* get rid of 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->sk->wmem_alloc);
ATM_SKB(skb)->atm_options = entry->shortcut->atm_options;
entry->shortcut->send(entry->shortcut, skb);
entry->packets_fwded++;
mpc->in_ops->put(entry);
 
return 0;
}
 
/*
* Probably needs some error checks and locking, not sure...
*/
static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev)
{
int retval;
struct mpoa_client *mpc;
struct ethhdr *eth;
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;
}
 
eth = (struct ethhdr *)skb->data;
if (eth->h_proto != htons(ETH_P_IP))
goto non_ip; /* Multi-Protocol Over ATM :-) */
 
while (i < mpc->number_of_mps_macs) {
if (memcmp(eth->h_dest, (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->get(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);
if (in_entry != NULL) mpc->in_ops->put(in_entry);
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;
mpc->in_ops->put(in_entry);
} 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 net_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;
}
 
dprintk("mpoa: (%s) mpc_vcc_close:\n", dev->name);
in_entry = mpc->in_ops->get_by_vcc(vcc, mpc);
if (in_entry) {
unsigned char *ip __attribute__ ((unused)) =
(unsigned char *)&in_entry->ctrl_info.in_dst_ip;
dprintk("mpoa: (%s) mpc_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;
mpc->in_ops->put(in_entry);
}
eg_entry = mpc->eg_ops->get_by_vcc(vcc, mpc);
if (eg_entry) {
dprintk("mpoa: (%s) mpc_vcc_close: egress SVC closed\n", mpc->dev->name);
eg_entry->shortcut = NULL;
mpc->eg_ops->put(eg_entry);
}
 
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 net_device *dev = (struct net_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) {
dprintk("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) {
dprintk("mpoa: (%s) mpc_push: control packet arrived\n", dev->name);
skb_queue_tail(&vcc->sk->receive_queue, 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");
dev_kfree_skb_any(skb);
return;
} else {
printk("mpoa: (%s) mpc_push: garbage arrived, purging\n", dev->name);
dev_kfree_skb_any(skb);
return;
}
 
tmp = skb->data + sizeof(struct llc_snap_hdr);
tag = *(uint32_t *)tmp;
 
eg = mpc->eg_ops->get_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);
dev_kfree_skb_any(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 :( */
dev_kfree_skb_any(skb);
if (new_skb == NULL){
mpc->eg_ops->put(eg);
return;
}
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->protocol = eth_type_trans(new_skb, dev);
new_skb->nh.raw = new_skb->data;
 
eg->latest_ip_addr = new_skb->nh.iph->saddr;
eg->packets_rcvd++;
mpc->eg_ops->put(eg);
 
memset(ATM_SKB(new_skb), 0, sizeof(struct atm_skb_data));
netif_rx(new_skb);
 
return;
}
 
static struct atmdev_ops mpc_ops = { /* only send is required */
.close = mpoad_close,
.send = msg_from_mpoad
};
 
static struct atm_dev mpc_dev = {
.ops = &mpc_ops,
.type = "mpc",
.number = 42,
.lock = SPIN_LOCK_UNLOCKED
/* 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) {
dprintk("mpoa: mpoad_attach: allocating new mpc for itf %d\n", arg);
mpc = alloc_mpc();
if (mpc == NULL)
return -ENOMEM;
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) {
dev_put(mpc->dev);
mpc->dev = NULL;
} else
priv->lane2_ops->associate_indicator = lane2_assoc_ind;
}
 
mpc->mpoad_vcc = vcc;
vcc->dev = &mpc_dev;
vcc_insert_socket(vcc->sk);
set_bit(ATM_VF_META,&vcc->flags);
set_bit(ATM_VF_READY,&vcc->flags);
 
if (mpc->dev) {
char empty[ATM_ESA_LEN];
memset(empty, 0, ATM_ESA_LEN);
start_mpc(mpc, mpc->dev);
/* set address if mpcd e.g. gets killed and restarted.
* If we do not do it now we have to wait for the next LE_ARP
*/
if ( memcmp(mpc->mps_ctrl_addr, empty, ATM_ESA_LEN) != 0 )
send_set_mps_ctrl_addr(mpc->mps_ctrl_addr, mpc);
}
 
MOD_INC_USE_COUNT;
return arg;
}
 
static void send_set_mps_ctrl_addr(char *addr, struct mpoa_client *mpc)
{
struct k_message mesg;
 
memcpy (mpc->mps_ctrl_addr, addr, ATM_ESA_LEN);
mesg.type = SET_MPS_CTRL_ADDR;
memcpy(mesg.MPS_ctrl, addr, ATM_ESA_LEN);
msg_to_mpoad(&mesg, mpc);
 
return;
}
 
static void mpoad_close(struct atm_vcc *vcc)
{
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);
dev_put(mpc->dev);
}
 
mpc->in_ops->destroy_cache(mpc);
mpc->eg_ops->destroy_cache(mpc);
 
while ( (skb = skb_dequeue(&vcc->sk->receive_queue)) ){
atm_return(vcc, skb->truesize);
kfree_skb(skb);
}
printk("mpoa: (%s) going down\n",
(mpc->dev) ? mpc->dev->name : "<unknown>");
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, &vcc->sk->wmem_alloc);
if (mpc == NULL) {
printk("mpoa: msg_from_mpoad: no mpc found\n");
return 0;
}
dprintk("mpoa: (%s) msg_from_mpoad:", (mpc->dev) ? mpc->dev->name : "<unknown>");
switch(mesg->type) {
case MPOA_RES_REPLY_RCVD:
dprintk(" mpoa_res_reply_rcvd\n");
MPOA_res_reply_rcvd(mesg, mpc);
break;
case MPOA_TRIGGER_RCVD:
dprintk(" mpoa_trigger_rcvd\n");
MPOA_trigger_rcvd(mesg, mpc);
break;
case INGRESS_PURGE_RCVD:
dprintk(" nhrp_purge_rcvd\n");
ingress_purge_rcvd(mesg, mpc);
break;
case EGRESS_PURGE_RCVD:
dprintk(" egress_purge_reply_rcvd\n");
egress_purge_rcvd(mesg, mpc);
break;
case MPS_DEATH:
dprintk(" mps_death\n");
mps_death(mesg, mpc);
break;
case CACHE_IMPOS_RCVD:
dprintk(" cache_impos_rcvd\n");
MPOA_cache_impos_rcvd(mesg, mpc);
break;
case SET_MPC_CTRL_ADDR:
dprintk(" set_mpc_ctrl_addr\n");
set_mpc_ctrl_addr_rcvd(mesg, mpc);
break;
case SET_MPS_MAC_ADDR:
dprintk(" set_mps_mac_addr\n");
set_mps_mac_addr_rcvd(mesg, mpc);
break;
case CLEAN_UP_AND_EXIT:
dprintk(" clean_up_and_exit\n");
clean_up(mesg, mpc, DIE);
break;
case RELOAD:
dprintk(" reload\n");
clean_up(mesg, mpc, RELOAD);
break;
case SET_MPC_PARAMS:
dprintk(" set_mpc_params\n");
mpc->parameters = mesg->content.params;
break;
default:
dprintk(" unknown message %d\n", mesg->type);
break;
}
kfree_skb(skb);
 
return 0;
}
 
/* Remember that this function may not do things that sleep */
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-existent 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));
atm_force_charge(mpc->mpoad_vcc, skb->truesize);
skb_queue_tail(&mpc->mpoad_vcc->sk->receive_queue, 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 net_device *dev;
struct mpoa_client *mpc;
struct lec_priv *priv;
 
dev = (struct net_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) {
dprintk("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;
dev_hold(dev);
dprintk("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;
dprintk("mpoa: device (%s) was deallocated\n", dev->name);
stop_mpc(mpc);
dev_put(mpc->dev);
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 that 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 *mpc)
{
uint32_t dst_ip = msg->content.in_info.in_dst_ip;
in_cache_entry *entry;
 
entry = mpc->in_ops->get(dst_ip, mpc);
if(entry == NULL){
entry = mpc->in_ops->add_entry(dst_ip, mpc);
entry->entry_state = INGRESS_RESOLVING;
msg->type = SND_MPOA_RES_RQST;
msg->content.in_info = entry->ctrl_info;
msg_to_mpoad(msg, mpc);
do_gettimeofday(&(entry->reply_wait));
mpc->in_ops->put(entry);
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, mpc);
do_gettimeofday(&(entry->reply_wait));
mpc->in_ops->put(entry);
return;
}
printk("mpoa: (%s) MPOA_trigger_rcvd: entry already in resolving state\n",
(mpc->dev) ? mpc->dev->name : "<unknown>");
mpc->in_ops->put(entry);
return;
}
 
/*
* Things get complicated because we have to check if there's an egress
* shortcut with suitable traffic parameters we could use.
*/
static void check_qos_and_open_shortcut(struct k_message *msg, struct mpoa_client *client, in_cache_entry *entry)
{
uint32_t dst_ip = msg->content.in_info.in_dst_ip;
unsigned char *ip __attribute__ ((unused)) = (unsigned char *)&dst_ip;
struct atm_mpoa_qos *qos = atm_mpoa_search_qos(dst_ip);
eg_cache_entry *eg_entry = client->eg_ops->get_by_src_ip(dst_ip, client);
 
if(eg_entry && eg_entry->shortcut){
if(eg_entry->shortcut->qos.txtp.traffic_class &
msg->qos.txtp.traffic_class &
(qos ? qos->qos.txtp.traffic_class : ATM_UBR | ATM_CBR)){
if(eg_entry->shortcut->qos.txtp.traffic_class == ATM_UBR)
entry->shortcut = eg_entry->shortcut;
else if(eg_entry->shortcut->qos.txtp.max_pcr > 0)
entry->shortcut = eg_entry->shortcut;
}
if(entry->shortcut){
dprintk("mpoa: (%s) using egress SVC to reach %u.%u.%u.%u\n",client->dev->name, NIPQUAD(ip));
client->eg_ops->put(eg_entry);
return;
}
}
if (eg_entry != NULL)
client->eg_ops->put(eg_entry);
 
/* No luck in the egress cache we must open an ingress SVC */
msg->type = OPEN_INGRESS_SVC;
if (qos && (qos->qos.txtp.traffic_class == msg->qos.txtp.traffic_class))
{
msg->qos = qos->qos;
printk("mpoa: (%s) trying to get a CBR shortcut\n",client->dev->name);
}
else memset(&msg->qos,0,sizeof(struct atm_qos));
msg_to_mpoad(msg, client);
return;
}
 
static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc)
{
unsigned char *ip;
 
uint32_t dst_ip = msg->content.in_info.in_dst_ip;
in_cache_entry *entry = mpc->in_ops->get(dst_ip, mpc);
ip = (unsigned char *)&dst_ip;
dprintk("mpoa: (%s) MPOA_res_reply_rcvd: ip %u.%u.%u.%u\n", mpc->dev->name, NIPQUAD(ip));
ddprintk("mpoa: (%s) MPOA_res_reply_rcvd() entry = %p", mpc->dev->name, entry);
if(entry == NULL){
printk("\nmpoa: (%s) ARGH, received res. reply for an entry that doesn't exist.\n", mpc->dev->name);
return;
}
ddprintk(" entry_state = %d ", entry->entry_state);
 
if (entry->entry_state == INGRESS_RESOLVED) {
printk("\nmpoa: (%s) MPOA_res_reply_rcvd for RESOLVED entry!\n", mpc->dev->name);
mpc->in_ops->put(entry);
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;
ddprintk("entry->shortcut = %p\n", entry->shortcut);
 
if(entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL){
entry->entry_state = INGRESS_RESOLVED;
mpc->in_ops->put(entry);
return; /* Shortcut already open... */
}
 
if (entry->shortcut != NULL) {
printk("mpoa: (%s) MPOA_res_reply_rcvd: entry->shortcut != NULL, impossible!\n",
mpc->dev->name);
mpc->in_ops->put(entry);
return;
}
check_qos_and_open_shortcut(msg, mpc, entry);
entry->entry_state = INGRESS_RESOLVED;
mpc->in_ops->put(entry);
 
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;
uint32_t mask = msg->ip_mask;
unsigned char *ip = (unsigned char *)&dst_ip;
in_cache_entry *entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask);
 
if(entry == NULL){
printk("mpoa: (%s) ingress_purge_rcvd: purge for a non-existing entry, ", mpc->dev->name);
printk("ip = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
return;
}
 
do {
dprintk("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]);
write_lock_bh(&mpc->ingress_lock);
mpc->in_ops->remove_entry(entry, mpc);
write_unlock_bh(&mpc->ingress_lock);
mpc->in_ops->put(entry);
entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask);
} while (entry != NULL);
 
return;
}
 
static void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc)
{
uint32_t cache_id = msg->content.eg_info.cache_id;
eg_cache_entry *entry = mpc->eg_ops->get_by_cache_id(cache_id, mpc);
if (entry == NULL) {
dprintk("mpoa: (%s) egress_purge_rcvd: purge for a non-existing entry\n", mpc->dev->name);
return;
}
 
write_lock_irq(&mpc->egress_lock);
mpc->eg_ops->remove_entry(entry, mpc);
write_unlock_irq(&mpc->egress_lock);
 
mpc->eg_ops->put(entry);
 
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;
 
atm_force_charge(vcc, skb->truesize);
skb_queue_tail(&vcc->sk->receive_queue, 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 )
{
eg_cache_entry *entry;
 
dprintk("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;
}
 
/* FIXME: This knows too much of the cache structure */
read_lock_irq(&mpc->egress_lock);
entry = mpc->eg_cache;
while (entry != NULL) {
purge_egress_shortcut(entry->shortcut, entry);
entry = entry->next;
}
read_unlock_irq(&mpc->egress_lock);
 
mpc->in_ops->destroy_cache(mpc);
mpc->eg_ops->destroy_cache(mpc);
 
return;
}
 
static void MPOA_cache_impos_rcvd( struct k_message * msg, struct mpoa_client * mpc)
{
uint16_t holding_time;
eg_cache_entry *entry = mpc->eg_ops->get_by_cache_id(msg->content.eg_info.cache_id, mpc);
holding_time = msg->content.eg_info.holding_time;
dprintk("mpoa: (%s) MPOA_cache_impos_rcvd: entry = %p, holding_time = %u\n",
mpc->dev->name, entry, holding_time);
if(entry == NULL && holding_time) {
entry = mpc->eg_ops->add_entry(msg, mpc);
mpc->eg_ops->put(entry);
return;
}
if(holding_time){
mpc->eg_ops->update(entry, holding_time);
return;
}
write_lock_irq(&mpc->egress_lock);
mpc->eg_ops->remove_entry(entry, mpc);
write_unlock_irq(&mpc->egress_lock);
 
mpc->eg_ops->put(entry);
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);
 
dprintk("mpoa: (%s) setting MPC ctrl ATM address to ",
(mpc->dev) ? mpc->dev->name : "<unknown>");
for (i = 7; i < sizeof(tlv); i++)
dprintk("%02x ", tlv[i]);
dprintk("\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->number_of_mps_macs = 0;
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;
}
 
/*
* purge egress cache and tell daemon to 'action' (DIE, RELOAD)
*/
static void clean_up(struct k_message *msg, struct mpoa_client *mpc, int action)
{
 
eg_cache_entry *entry;
msg->type = SND_EGRESS_PURGE;
 
 
/* FIXME: This knows too much of the cache structure */
read_lock_irq(&mpc->egress_lock);
entry = mpc->eg_cache;
while (entry != NULL){
msg->content.eg_info = entry->ctrl_info;
dprintk("mpoa: cache_id %u\n", entry->ctrl_info.cache_id);
msg_to_mpoad(msg, mpc);
entry = entry->next;
}
read_unlock_irq(&mpc->egress_lock);
 
msg->type = action;
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->parameters.mpc_p4 * HZ ){
mpc->in_ops->check_resolving(mpc);
previous_resolving_check_time = checking_time;
}
if(checking_time - previous_refresh_time > mpc->parameters.mpc_p5 * HZ ){
mpc->in_ops->refresh(mpc);
previous_refresh_time = checking_time;
}
mpc = mpc->next;
}
mpc_timer_refresh();
return;
}
 
static struct atm_mpoa_ops __atm_mpoa_ops = {
.mpoad_attach = atm_mpoa_mpoad_attach,
.vcc_attach = atm_mpoa_vcc_attach,
.owner = THIS_MODULE
};
 
static __init int atm_mpoa_init(void)
{
atm_mpoa_ops_set(&__atm_mpoa_ops);
 
#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 0;
}
 
void __exit atm_mpoa_cleanup(void)
{
struct mpoa_client *mpc, *tmp;
struct atm_mpoa_qos *qos, *nextqos;
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_set(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");
mpc->in_ops->destroy_cache(mpc);
mpc->eg_ops->destroy_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: next mpc is at %p\n", tmp);
mpc = tmp;
}
 
qos = qos_head;
qos_head = NULL;
while (qos != NULL) {
nextqos = qos->next;
dprintk("mpoa: cleanup_module: freeing qos entry %p\n", qos);
kfree(qos);
qos = nextqos;
}
 
return;
}
 
module_init(atm_mpoa_init);
module_exit(atm_mpoa_cleanup);
 
MODULE_LICENSE("GPL");
/mpoa_proc.c
0,0 → 1,347
#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];
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 (*pos >= length) length = 0;
else {
if ((count + *pos) > length) count = length - *pos;
if (copy_to_user(buff, (char *)page , count)) {
free_page(page);
return -EFAULT;
}
*pos += 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 */
 
 
 
 
 
 
/signaling.h
0,0 → 1,33
/* net/atm/signaling.h - ATM signaling */
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
#ifndef NET_ATM_SIGNALING_H
#define NET_ATM_SIGNALING_H
 
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/atmsvc.h>
 
 
#define WAITING 1 /* for reply: 0: no error, < 0: error, ... */
 
 
extern struct atm_vcc *sigd; /* needed in svc_release */
 
 
/*
* sigd_enq is a wrapper for sigd_enq2, covering the more common cases, and
* avoiding huge lists of null values.
*/
 
void sigd_enq2(struct atm_vcc *vcc,enum atmsvc_msg_type type,
struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
const struct sockaddr_atmsvc *svc,const struct atm_qos *qos,int reply);
void sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type,
struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
const struct sockaddr_atmsvc *svc);
int sigd_attach(struct atm_vcc *vcc);
 
#endif
/pppoatm.c
0,0 → 1,365
/* net/atm/pppoatm.c - RFC2364 PPP over ATM/AAL5 */
 
/* Copyright 1999-2000 by Mitchell Blank Jr */
/* Based on clip.c; 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
/* And on ppp_async.c; Copyright 1999 Paul Mackerras */
/* And help from Jens Axboe */
 
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* This driver provides the encapsulation and framing for sending
* and receiving PPP frames in ATM AAL5 PDUs.
*/
 
/*
* One shortcoming of this driver is that it does not comply with
* section 8 of RFC2364 - we are supposed to detect a change
* in encapsulation and immediately abort the connection (in order
* to avoid a black-hole being created if our peer loses state
* and changes encapsulation unilaterally. However, since the
* ppp_generic layer actually does the decapsulation, we need
* a way of notifying it when we _think_ there might be a problem)
* There's two cases:
* 1. LLC-encapsulation was missing when it was enabled. In
* this case, we should tell the upper layer "tear down
* this session if this skb looks ok to you"
* 2. LLC-encapsulation was present when it was disabled. Then
* we need to tell the upper layer "this packet may be
* ok, but if its in error tear down the session"
* These hooks are not yet available in ppp_generic
*/
 
#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
#include <linux/atmppp.h>
 
#include "common.h"
 
#if 0
#define DPRINTK(format, args...) \
printk(KERN_DEBUG "pppoatm: " format, ##args)
#else
#define DPRINTK(format, args...)
#endif
 
enum pppoatm_encaps {
e_autodetect = PPPOATM_ENCAPS_AUTODETECT,
e_vc = PPPOATM_ENCAPS_VC,
e_llc = PPPOATM_ENCAPS_LLC,
};
 
struct pppoatm_vcc {
struct atm_vcc *atmvcc; /* VCC descriptor */
void (*old_push)(struct atm_vcc *, struct sk_buff *);
void (*old_pop)(struct atm_vcc *, struct sk_buff *);
/* keep old push/pop for detaching */
enum pppoatm_encaps encaps;
int flags; /* SC_COMP_PROT - compress protocol */
struct ppp_channel chan; /* interface to generic ppp layer */
struct tasklet_struct wakeup_tasklet;
};
 
/*
* Header used for LLC Encapsulated PPP (4 bytes) followed by the LCP protocol
* ID (0xC021) used in autodetection
*/
static const unsigned char pppllc[6] = { 0xFE, 0xFE, 0x03, 0xCF, 0xC0, 0x21 };
#define LLC_LEN (4)
 
static inline struct pppoatm_vcc *atmvcc_to_pvcc(const struct atm_vcc *atmvcc)
{
return (struct pppoatm_vcc *) (atmvcc->user_back);
}
 
static inline struct pppoatm_vcc *chan_to_pvcc(const struct ppp_channel *chan)
{
return (struct pppoatm_vcc *) (chan->private);
}
 
/*
* We can't do this directly from our _pop handler, since the ppp code
* doesn't want to be called in interrupt context, so we do it from
* a tasklet
*/
static void pppoatm_wakeup_sender(unsigned long arg)
{
ppp_output_wakeup((struct ppp_channel *) arg);
}
 
/*
* This gets called every time the ATM card has finished sending our
* skb. The ->old_pop will take care up normal atm flow control,
* but we also need to wake up the device if we blocked it
*/
static void pppoatm_pop(struct atm_vcc *atmvcc, struct sk_buff *skb)
{
struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc);
pvcc->old_pop(atmvcc, skb);
/*
* We don't really always want to do this since it's
* really inefficient - it would be much better if we could
* test if we had actually throttled the generic layer.
* Unfortunately then there would be a nasty SMP race where
* we could clear that flag just as we refuse another packet.
* For now we do the safe thing.
*/
tasklet_schedule(&pvcc->wakeup_tasklet);
}
 
/*
* Unbind from PPP - currently we only do this when closing the socket,
* but we could put this into an ioctl if need be
*/
static void pppoatm_unassign_vcc(struct atm_vcc *atmvcc)
{
struct pppoatm_vcc *pvcc;
pvcc = atmvcc_to_pvcc(atmvcc);
atmvcc->push = pvcc->old_push;
atmvcc->pop = pvcc->old_pop;
tasklet_kill(&pvcc->wakeup_tasklet);
ppp_unregister_channel(&pvcc->chan);
atmvcc->user_back = NULL;
kfree(pvcc);
/* Gee, I hope we have the big kernel lock here... */
MOD_DEC_USE_COUNT;
}
 
/* Called when an AAL5 PDU comes in */
static void pppoatm_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
{
struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc);
DPRINTK("pppoatm push\n");
if (skb == NULL) { /* VCC was closed */
DPRINTK("removing ATMPPP VCC %p\n", pvcc);
pppoatm_unassign_vcc(atmvcc);
atmvcc->push(atmvcc, NULL); /* Pass along bad news */
return;
}
atm_return(atmvcc, skb->truesize);
switch (pvcc->encaps) {
case e_llc:
if (skb->len < LLC_LEN ||
memcmp(skb->data, pppllc, LLC_LEN))
goto error;
skb_pull(skb, LLC_LEN);
break;
case e_autodetect:
if (pvcc->chan.ppp == NULL) { /* Not bound yet! */
kfree_skb(skb);
return;
}
if (skb->len >= sizeof(pppllc) &&
!memcmp(skb->data, pppllc, sizeof(pppllc))) {
pvcc->encaps = e_llc;
skb_pull(skb, LLC_LEN);
break;
}
if (skb->len >= (sizeof(pppllc) - LLC_LEN) &&
!memcmp(skb->data, &pppllc[LLC_LEN],
sizeof(pppllc) - LLC_LEN)) {
pvcc->encaps = e_vc;
pvcc->chan.mtu += LLC_LEN;
break;
}
DPRINTK("(unit %d): Couldn't autodetect yet "
"(skb: %02X %02X %02X %02X %02X %02X)\n",
pvcc->chan.unit,
skb->data[0], skb->data[1], skb->data[2],
skb->data[3], skb->data[4], skb->data[5]);
goto error;
case e_vc:
break;
}
ppp_input(&pvcc->chan, skb);
return;
error:
kfree_skb(skb);
ppp_input_error(&pvcc->chan, 0);
}
 
/*
* Called by the ppp_generic.c to send a packet - returns true if packet
* was accepted. If we return false, then it's our job to call
* ppp_output_wakeup(chan) when we're feeling more up to it.
* Note that in the ENOMEM case (as opposed to the !atm_may_send case)
* we should really drop the packet, but the generic layer doesn't
* support this yet. We just return 'DROP_PACKET' which we actually define
* as success, just to be clear what we're really doing.
*/
#define DROP_PACKET 1
static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
{
struct pppoatm_vcc *pvcc = chan_to_pvcc(chan);
ATM_SKB(skb)->vcc = pvcc->atmvcc;
DPRINTK("(unit %d): pppoatm_send (skb=0x%p, vcc=0x%p)\n",
pvcc->chan.unit, skb, pvcc->atmvcc);
if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT))
(void) skb_pull(skb, 1);
switch (pvcc->encaps) { /* LLC encapsulation needed */
case e_llc:
if (skb_headroom(skb) < LLC_LEN) {
struct sk_buff *n;
n = skb_realloc_headroom(skb, LLC_LEN);
if (n != NULL &&
!atm_may_send(pvcc->atmvcc, n->truesize)) {
kfree_skb(n);
goto nospace;
}
kfree_skb(skb);
if ((skb = n) == NULL)
return DROP_PACKET;
} else if (!atm_may_send(pvcc->atmvcc, skb->truesize))
goto nospace;
memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN);
break;
case e_vc:
if (!atm_may_send(pvcc->atmvcc, skb->truesize))
goto nospace;
break;
case e_autodetect:
DPRINTK("(unit %d): Trying to send without setting encaps!\n",
pvcc->chan.unit);
kfree_skb(skb);
return 1;
}
atomic_add(skb->truesize, &ATM_SKB(skb)->vcc->sk->wmem_alloc);
ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options;
DPRINTK("(unit %d): atm_skb(%p)->vcc(%p)->dev(%p)\n",
pvcc->chan.unit, skb, ATM_SKB(skb)->vcc,
ATM_SKB(skb)->vcc->dev);
return ATM_SKB(skb)->vcc->send(ATM_SKB(skb)->vcc, skb)
? DROP_PACKET : 1;
nospace:
/*
* We don't have space to send this SKB now, but we might have
* already applied SC_COMP_PROT compression, so may need to undo
*/
if ((pvcc->flags & SC_COMP_PROT) && skb_headroom(skb) > 0 &&
skb->data[-1] == '\0')
(void) skb_push(skb, 1);
return 0;
}
 
/* This handles ioctls sent to the /dev/ppp interface */
static int pppoatm_devppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case PPPIOCGFLAGS:
return put_user(chan_to_pvcc(chan)->flags, (int *) arg)
? -EFAULT : 0;
case PPPIOCSFLAGS:
return get_user(chan_to_pvcc(chan)->flags, (int *) arg)
? -EFAULT : 0;
}
return -ENOTTY;
}
 
static /*const*/ struct ppp_channel_ops pppoatm_ops = {
start_xmit: pppoatm_send,
ioctl: pppoatm_devppp_ioctl,
};
 
static int pppoatm_assign_vcc(struct atm_vcc *atmvcc, unsigned long arg)
{
struct atm_backend_ppp be;
struct pppoatm_vcc *pvcc;
int err;
/*
* Each PPPoATM instance has its own tasklet - this is just a
* prototypical one used to initialize them
*/
static const DECLARE_TASKLET(tasklet_proto, pppoatm_wakeup_sender, 0);
if (copy_from_user(&be, (void *) arg, sizeof be))
return -EFAULT;
if (be.encaps != PPPOATM_ENCAPS_AUTODETECT &&
be.encaps != PPPOATM_ENCAPS_VC && be.encaps != PPPOATM_ENCAPS_LLC)
return -EINVAL;
MOD_INC_USE_COUNT;
pvcc = kmalloc(sizeof(*pvcc), GFP_KERNEL);
if (pvcc == NULL) {
MOD_DEC_USE_COUNT;
return -ENOMEM;
}
memset(pvcc, 0, sizeof(*pvcc));
pvcc->atmvcc = atmvcc;
pvcc->old_push = atmvcc->push;
pvcc->old_pop = atmvcc->pop;
pvcc->encaps = (enum pppoatm_encaps) be.encaps;
pvcc->chan.private = pvcc;
pvcc->chan.ops = &pppoatm_ops;
pvcc->chan.mtu = atmvcc->qos.txtp.max_sdu - PPP_HDRLEN -
(be.encaps == e_vc ? 0 : LLC_LEN);
pvcc->wakeup_tasklet = tasklet_proto;
pvcc->wakeup_tasklet.data = (unsigned long) &pvcc->chan;
if ((err = ppp_register_channel(&pvcc->chan)) != 0) {
kfree(pvcc);
return err;
}
atmvcc->user_back = pvcc;
atmvcc->push = pppoatm_push;
atmvcc->pop = pppoatm_pop;
return 0;
}
 
/*
* This handles ioctls actually performed on our vcc - we must return
* -ENOIOCTLCMD for any unrecognized ioctl
*/
static int pppoatm_ioctl(struct atm_vcc *atmvcc, unsigned int cmd,
unsigned long arg)
{
if (cmd != ATM_SETBACKEND && atmvcc->push != pppoatm_push)
return -ENOIOCTLCMD;
switch (cmd) {
case ATM_SETBACKEND: {
atm_backend_t b;
if (get_user(b, (atm_backend_t *) arg))
return -EFAULT;
if (b != ATM_BACKEND_PPP)
return -ENOIOCTLCMD;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
return pppoatm_assign_vcc(atmvcc, arg);
}
case PPPIOCGCHAN:
return put_user(ppp_channel_index(&atmvcc_to_pvcc(atmvcc)->
chan), (int *) arg) ? -EFAULT : 0;
case PPPIOCGUNIT:
return put_user(ppp_unit_number(&atmvcc_to_pvcc(atmvcc)->
chan), (int *) arg) ? -EFAULT : 0;
}
return -ENOIOCTLCMD;
}
 
/* the following avoids some spurious warnings from the compiler */
#define UNUSED __attribute__((unused))
 
static int __init UNUSED pppoatm_init(void)
{
pppoatm_ioctl_set(pppoatm_ioctl);
return 0;
}
 
static void __exit UNUSED pppoatm_exit(void)
{
pppoatm_ioctl_set(NULL);
}
 
module_init(pppoatm_init);
module_exit(pppoatm_exit);
 
MODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>");
MODULE_DESCRIPTION("RFC2364 PPP over ATM/AAL5");
MODULE_LICENSE("GPL");
/ipcommon.c
0,0 → 1,71
/* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */
 
/* Written 1996-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/module.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/atmdev.h>
#include <linux/atmclip.h>
 
#include "common.h"
#include "ipcommon.h"
 
 
#if 0
#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
#else
#define DPRINTK(format,args...)
#endif
 
 
const unsigned char llc_oui[] = {
0xaa, /* DSAP: non-ISO */
0xaa, /* SSAP: non-ISO */
0x03, /* Ctrl: Unnumbered Information Command PDU */
0x00, /* OUI: EtherType */
0x00,
0x00 };
 
 
/*
* skb_migrate appends the list at "from" to "to", emptying "from" in the
* process. skb_migrate is atomic with respect to all other skb operations on
* "from" and "to". Note that it locks both lists at the same time, so beware
* of potential deadlocks.
*
* This function should live in skbuff.c or skbuff.h.
*/
 
 
void skb_migrate(struct sk_buff_head *from,struct sk_buff_head *to)
{
struct sk_buff *skb;
unsigned long flags;
struct sk_buff *skb_from = (struct sk_buff *) from;
struct sk_buff *skb_to = (struct sk_buff *) to;
struct sk_buff *prev;
 
spin_lock_irqsave(&from->lock,flags);
spin_lock(&to->lock);
prev = from->prev;
from->next->prev = to->prev;
prev->next = skb_to;
to->prev->next = from->next;
to->prev = from->prev;
for (skb = from->next; skb != skb_to; skb = skb->next)
skb->list = to;
to->qlen += from->qlen;
spin_unlock(&to->lock);
from->prev = skb_from;
from->next = skb_from;
from->qlen = 0;
spin_unlock_irqrestore(&from->lock,flags);
}
 
 
EXPORT_SYMBOL(llc_oui);
EXPORT_SYMBOL(skb_migrate);
/mpc.h
0,0 → 1,67
#ifndef _MPC_H_
#define _MPC_H_
 
#include <linux/atm.h>
#include <linux/atmmpc.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#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 net_device *dev; /* lec in question */
int dev_num; /* e.g. 2 for lec2 */
int (*old_hard_start_xmit)(struct sk_buff *skb, struct net_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 */
struct mpc_parameters parameters; /* parameters for this client */
};
 
 
struct atm_mpoa_qos {
struct atm_mpoa_qos *next;
uint32_t ipaddr;
struct atm_qos qos;
};
 
 
/* 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 */
struct module *owner;
};
 
/* Boot/module initialization function */
extern struct atm_mpoa_ops *atm_mpoa_ops;
int try_atm_mpoa_ops(void);
void atm_mpoa_ops_set(struct atm_mpoa_ops *hook);
 
/* MPOA QoS operations */
struct atm_mpoa_qos *atm_mpoa_add_qos(uint32_t dst_ip, struct atm_qos *qos);
struct atm_mpoa_qos *atm_mpoa_search_qos(uint32_t dst_ip);
int atm_mpoa_delete_qos(struct atm_mpoa_qos *qos);
 
/* Display QoS entries. This is for the procfs */
void atm_mpoa_disp_qos(char *page, int *len);
 
#endif /* _MPC_H_ */
/ipcommon.h
0,0 → 1,25
/* net/atm/ipcommon.h - Common items for all ways of doing IP over ATM */
 
/* Written 1996-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#ifndef NET_ATM_IPCOMMON_H
#define NET_ATM_IPCOMMON_H
 
 
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/atmdev.h>
 
 
extern struct net_device *clip_devs;
 
/*
* Appends all skbs from "from" to "to". The operation is atomic with respect
* to all other skb operations on "from" or "to".
*/
 
void skb_migrate(struct sk_buff_head *from,struct sk_buff_head *to);
 
#endif
/br2684.c
0,0 → 1,815
/*
Experimental ethernet netdevice using ATM AAL5 as underlying carrier
(RFC1483 obsoleted by RFC2684) for Linux 2.4
Author: Marcell GAL, 2000, XDSL Ltd, Hungary
*/
 
#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <linux/ip.h>
#include <asm/uaccess.h>
#include <net/arp.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
 
#include <linux/atmbr2684.h>
 
#include "common.h"
#include "ipcommon.h"
 
/*
* Define this to use a version of the code which interacts with the higher
* layers in a more intellegent way, by always reserving enough space for
* our header at the begining of the packet. However, there may still be
* some problems with programs like tcpdump. In 2.5 we'll sort out what
* we need to do to get this perfect. For now we just will copy the packet
* if we need space for the header
*/
/* #define FASTER_VERSION */
 
#ifdef DEBUG
#define DPRINTK(format, args...) printk(KERN_DEBUG "br2684: " format, ##args)
#else
#define DPRINTK(format, args...)
#endif
 
#ifdef SKB_DEBUG
static void skb_debug(const struct sk_buff *skb)
{
#define NUM2PRINT 50
char buf[NUM2PRINT * 3 + 1]; /* 3 chars per byte */
int i = 0;
for (i = 0; i < skb->len && i < NUM2PRINT; i++) {
sprintf(buf + i * 3, "%2.2x ", 0xff & skb->data[i]);
}
printk(KERN_DEBUG "br2684: skb: %s\n", buf);
}
#else
#define skb_debug(skb) do {} while (0)
#endif
 
static unsigned char llc_oui_pid_pad[] =
{ 0xAA, 0xAA, 0x03, 0x00, 0x80, 0xC2, 0x00, 0x07, 0x00, 0x00 };
#define PADLEN (2)
 
enum br2684_encaps {
e_vc = BR2684_ENCAPS_VC,
e_llc = BR2684_ENCAPS_LLC,
};
 
struct br2684_vcc {
struct atm_vcc *atmvcc;
struct br2684_dev *brdev;
/* keep old push,pop functions for chaining */
void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb);
/* void (*old_pop)(struct atm_vcc *vcc,struct sk_buff *skb); */
enum br2684_encaps encaps;
struct list_head brvccs;
#ifdef CONFIG_ATM_BR2684_IPFILTER
struct br2684_filter filter;
#endif /* CONFIG_ATM_BR2684_IPFILTER */
#ifndef FASTER_VERSION
unsigned copies_needed, copies_failed;
#endif /* FASTER_VERSION */
};
 
struct br2684_dev {
struct net_device net_dev;
struct list_head br2684_devs;
int number;
struct list_head brvccs; /* one device <=> one vcc (before xmas) */
struct net_device_stats stats;
int mac_was_set;
};
 
/*
* This lock should be held for writing any time the list of devices or
* their attached vcc's could be altered. It should be held for reading
* any time these are being queried. Note that we sometimes need to
* do read-locking under interrupt context, so write locking must block
* the current CPU's interrupts
*/
static rwlock_t devs_lock = RW_LOCK_UNLOCKED;
 
static LIST_HEAD(br2684_devs);
 
static inline struct br2684_dev *BRPRIV(const struct net_device *net_dev)
{
return (struct br2684_dev *) ((char *) (net_dev) -
(unsigned long) (&((struct br2684_dev *) 0)->net_dev));
}
 
static inline struct br2684_dev *list_entry_brdev(const struct list_head *le)
{
return list_entry(le, struct br2684_dev, br2684_devs);
}
 
static inline struct br2684_vcc *BR2684_VCC(const struct atm_vcc *atmvcc)
{
return (struct br2684_vcc *) (atmvcc->user_back);
}
 
static inline struct br2684_vcc *list_entry_brvcc(const struct list_head *le)
{
return list_entry(le, struct br2684_vcc, brvccs);
}
 
/* Caller should hold read_lock(&devs_lock) */
static struct br2684_dev *br2684_find_dev(const struct br2684_if_spec *s)
{
struct list_head *lh;
struct br2684_dev *brdev;
switch (s->method) {
case BR2684_FIND_BYNUM:
list_for_each(lh, &br2684_devs) {
brdev = list_entry_brdev(lh);
if (brdev->number == s->spec.devnum)
return brdev;
}
break;
case BR2684_FIND_BYIFNAME:
list_for_each(lh, &br2684_devs) {
brdev = list_entry_brdev(lh);
if (!strncmp(brdev->net_dev.name, s->spec.ifname,
sizeof brdev->net_dev.name))
return brdev;
}
break;
}
return NULL;
}
 
/*
* Send a packet out a particular vcc. Not to useful right now, but paves
* the way for multiple vcc's per itf. Returns true if we can send,
* otherwise false
*/
static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev,
struct br2684_vcc *brvcc)
{
struct atm_vcc *atmvcc;
#ifdef FASTER_VERSION
if (brvcc->encaps == e_llc)
memcpy(skb_push(skb, 8), llc_oui_pid_pad, 8);
/* last 2 bytes of llc_oui_pid_pad are managed by header routines;
yes, you got it: 8 + 2 = sizeof(llc_oui_pid_pad)
*/
#else
int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2;
if (skb_headroom(skb) < minheadroom) {
struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom);
brvcc->copies_needed++;
dev_kfree_skb(skb);
if (skb2 == NULL) {
brvcc->copies_failed++;
return 0;
}
skb = skb2;
}
skb_push(skb, minheadroom);
if (brvcc->encaps == e_llc)
memcpy(skb->data, llc_oui_pid_pad, 10);
else
memset(skb->data, 0, 2);
#endif /* FASTER_VERSION */
skb_debug(skb);
 
ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc;
DPRINTK("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, atmvcc, atmvcc->dev);
if (!atm_may_send(atmvcc, skb->truesize)) {
/* we free this here for now, because we cannot know in a higher
layer whether the skb point it supplied wasn't freed yet.
now, it always is.
*/
dev_kfree_skb(skb);
return 0;
}
atomic_add(skb->truesize, &atmvcc->sk->wmem_alloc);
ATM_SKB(skb)->atm_options = atmvcc->atm_options;
brdev->stats.tx_packets++;
brdev->stats.tx_bytes += skb->len;
atmvcc->send(atmvcc, skb);
return 1;
}
 
static inline struct br2684_vcc *pick_outgoing_vcc(struct sk_buff *skb,
struct br2684_dev *brdev)
{
return list_empty(&brdev->brvccs) ? NULL :
list_entry_brvcc(brdev->brvccs.next); /* 1 vcc/dev right now */
}
 
static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct br2684_dev *brdev = BRPRIV(dev);
struct br2684_vcc *brvcc;
 
DPRINTK("br2684_start_xmit, skb->dst=%p\n", skb->dst);
read_lock(&devs_lock);
brvcc = pick_outgoing_vcc(skb, brdev);
if (brvcc == NULL) {
DPRINTK("no vcc attached to dev %s\n", dev->name);
brdev->stats.tx_errors++;
brdev->stats.tx_carrier_errors++;
/* netif_stop_queue(dev); */
dev_kfree_skb(skb);
read_unlock(&devs_lock);
return -EUNATCH;
}
if (!br2684_xmit_vcc(skb, brdev, brvcc)) {
/*
* We should probably use netif_*_queue() here, but that
* involves added complication. We need to walk before
* we can run
*/
/* don't free here! this pointer might be no longer valid!
dev_kfree_skb(skb);
*/
brdev->stats.tx_errors++;
brdev->stats.tx_fifo_errors++;
}
read_unlock(&devs_lock);
return 0;
}
 
static struct net_device_stats *br2684_get_stats(struct net_device *dev)
{
DPRINTK("br2684_get_stats\n");
return &BRPRIV(dev)->stats;
}
 
#ifdef FASTER_VERSION
/*
* These mirror eth_header and eth_header_cache. They are not usually
* exported for use in modules, so we grab them from net_device
* after ether_setup() is done with it. Bit of a hack.
*/
static int (*my_eth_header)(struct sk_buff *, struct net_device *,
unsigned short, void *, void *, unsigned);
static int (*my_eth_header_cache)(struct neighbour *, struct hh_cache *);
 
static int
br2684_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, void *daddr, void *saddr, unsigned len)
{
u16 *pad_before_eth;
int t = my_eth_header(skb, dev, type, daddr, saddr, len);
if (t > 0) {
pad_before_eth = (u16 *) skb_push(skb, 2);
*pad_before_eth = 0;
return dev->hard_header_len; /* or return 16; ? */
} else
return t;
}
 
static int
br2684_header_cache(struct neighbour *neigh, struct hh_cache *hh)
{
/* hh_data is 16 bytes long. if encaps is ether-llc we need 24, so
xmit will add the additional header part in that case */
u16 *pad_before_eth = (u16 *)(hh->hh_data);
int t = my_eth_header_cache(neigh, hh);
DPRINTK("br2684_header_cache, neigh=%p, hh_cache=%p\n", neigh, hh);
if (t < 0)
return t;
else {
*pad_before_eth = 0;
hh->hh_len = PADLEN + ETH_HLEN;
}
return 0;
}
 
/*
* This is similar to eth_type_trans, which cannot be used because of
* our dev->hard_header_len
*/
static inline unsigned short br_type_trans(struct sk_buff *skb,
struct net_device *dev)
{
struct ethhdr *eth;
unsigned char *rawp;
eth = skb->mac.ethernet;
 
if (*eth->h_dest & 1) {
if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
}
 
else if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
skb->pkt_type = PACKET_OTHERHOST;
 
if (ntohs(eth->h_proto) >= 1536)
return eth->h_proto;
 
rawp = skb->data;
 
/*
* This is a magic hack to spot IPX packets. Older Novell breaks
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
* layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
* won't work for fault tolerant netware but does for the rest.
*/
if (*(unsigned short *) rawp == 0xFFFF)
return htons(ETH_P_802_3);
 
/*
* Real 802.2 LLC
*/
return htons(ETH_P_802_2);
}
#endif /* FASTER_VERSION */
 
/*
* We remember when the MAC gets set, so we don't override it later with
* the ESI of the ATM card of the first VC
*/
static int (*my_eth_mac_addr)(struct net_device *, void *);
static int br2684_mac_addr(struct net_device *dev, void *p)
{
int err = my_eth_mac_addr(dev, p);
if (!err)
BRPRIV(dev)->mac_was_set = 1;
return err;
}
 
#ifdef CONFIG_ATM_BR2684_IPFILTER
/* this IOCTL is experimental. */
static int br2684_setfilt(struct atm_vcc *atmvcc, unsigned long arg)
{
struct br2684_vcc *brvcc;
struct br2684_filter_set fs;
 
if (copy_from_user(&fs, (void *) arg, sizeof fs))
return -EFAULT;
if (fs.ifspec.method != BR2684_FIND_BYNOTHING) {
/*
* This is really a per-vcc thing, but we can also search
* by device
*/
struct br2684_dev *brdev;
read_lock(&devs_lock);
brdev = br2684_find_dev(&fs.ifspec);
if (brdev == NULL || list_empty(&brdev->brvccs) ||
brdev->brvccs.next != brdev->brvccs.prev) /* >1 VCC */
brvcc = NULL;
else
brvcc = list_entry_brvcc(brdev->brvccs.next);
read_unlock(&devs_lock);
if (brvcc == NULL)
return -ESRCH;
} else
brvcc = BR2684_VCC(atmvcc);
memcpy(&brvcc->filter, &fs.filter, sizeof(brvcc->filter));
return 0;
}
 
/* Returns 1 if packet should be dropped */
static inline int
packet_fails_filter(u16 type, struct br2684_vcc *brvcc, struct sk_buff *skb)
{
if (brvcc->filter.netmask == 0)
return 0; /* no filter in place */
if (type == __constant_htons(ETH_P_IP) &&
(((struct iphdr *) (skb->data))->daddr & brvcc->filter.
netmask) == brvcc->filter.prefix)
return 0;
if (type == __constant_htons(ETH_P_ARP))
return 0;
/* TODO: we should probably filter ARPs too.. don't want to have
* them returning values that don't make sense, or is that ok?
*/
return 1; /* drop */
}
#endif /* CONFIG_ATM_BR2684_IPFILTER */
 
static void br2684_close_vcc(struct br2684_vcc *brvcc)
{
DPRINTK("removing VCC %p from dev %p\n", brvcc, brvcc->brdev);
write_lock_irq(&devs_lock);
list_del(&brvcc->brvccs);
write_unlock_irq(&devs_lock);
brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */
brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */
kfree(brvcc);
MOD_DEC_USE_COUNT;
}
 
/* when AAL5 PDU comes in: */
static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
{
struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
struct br2684_dev *brdev = brvcc->brdev;
int plen = sizeof(llc_oui_pid_pad) + ETH_HLEN;
 
DPRINTK("br2684_push\n");
 
if (skb == NULL) { /* skb==NULL means VCC is being destroyed */
br2684_close_vcc(brvcc);
if (list_empty(&brdev->brvccs)) {
read_lock(&devs_lock);
list_del(&brdev->br2684_devs);
read_unlock(&devs_lock);
unregister_netdev(&brdev->net_dev);
kfree(brdev);
}
return;
}
 
skb_debug(skb);
atm_return(atmvcc, skb->truesize);
DPRINTK("skb from brdev %p\n", brdev);
if (brvcc->encaps == e_llc) {
/* let us waste some time for checking the encapsulation.
Note, that only 7 char is checked so frames with a valid FCS
are also accepted (but FCS is not checked of course) */
if (memcmp(skb->data, llc_oui_pid_pad, 7)) {
brdev->stats.rx_errors++;
dev_kfree_skb(skb);
return;
}
 
/* Strip FCS if present */
if (skb->len > 7 && skb->data[7] == 0x01)
__skb_trim(skb, skb->len - 4);
} else {
plen = PADLEN + ETH_HLEN; /* pad, dstmac,srcmac, ethtype */
/* first 2 chars should be 0 */
if (*((u16 *) (skb->data)) != 0) {
brdev->stats.rx_errors++;
dev_kfree_skb(skb);
return;
}
}
if (skb->len < plen) {
brdev->stats.rx_errors++;
dev_kfree_skb(skb); /* dev_ not needed? */
return;
}
 
#ifdef FASTER_VERSION
/* FIXME: tcpdump shows that pointer to mac header is 2 bytes earlier,
than should be. What else should I set? */
skb_pull(skb, plen);
skb->mac.raw = ((char *) (skb->data)) - ETH_HLEN;
skb->pkt_type = PACKET_HOST;
#ifdef CONFIG_BR2684_FAST_TRANS
skb->protocol = ((u16 *) skb->data)[-1];
#else /* some protocols might require this: */
skb->protocol = br_type_trans(skb, &brdev->net_dev);
#endif /* CONFIG_BR2684_FAST_TRANS */
#else
skb_pull(skb, plen - ETH_HLEN);
skb->protocol = eth_type_trans(skb, &brdev->net_dev);
#endif /* FASTER_VERSION */
#ifdef CONFIG_ATM_BR2684_IPFILTER
if (packet_fails_filter(skb->protocol, brvcc, skb)) {
brdev->stats.rx_dropped++;
dev_kfree_skb(skb);
return;
}
#endif /* CONFIG_ATM_BR2684_IPFILTER */
skb->dev = &brdev->net_dev;
ATM_SKB(skb)->vcc = atmvcc; /* needed ? */
DPRINTK("received packet's protocol: %x\n", ntohs(skb->protocol));
skb_debug(skb);
if (!(brdev->net_dev.flags & IFF_UP)) { /* sigh, interface is down */
brdev->stats.rx_dropped++;
dev_kfree_skb(skb);
return;
}
brdev->stats.rx_packets++;
brdev->stats.rx_bytes += skb->len;
memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
netif_rx(skb);
}
 
static int br2684_regvcc(struct atm_vcc *atmvcc, unsigned long arg)
{
/* assign a vcc to a dev
Note: we do not have explicit unassign, but look at _push()
*/
int err;
struct br2684_vcc *brvcc;
struct sk_buff_head copy;
struct sk_buff *skb;
struct br2684_dev *brdev;
struct atm_backend_br2684 be;
 
MOD_INC_USE_COUNT;
if (copy_from_user(&be, (void *) arg, sizeof be)) {
MOD_DEC_USE_COUNT;
return -EFAULT;
}
write_lock_irq(&devs_lock);
brdev = br2684_find_dev(&be.ifspec);
if (brdev == NULL) {
printk(KERN_ERR
"br2684: tried to attach to non-existant device\n");
err = -ENXIO;
goto error;
}
if (atmvcc->push == NULL) {
err = -EBADFD;
goto error;
}
if (!list_empty(&brdev->brvccs)) { /* Only 1 VCC/dev right now */
err = -EEXIST;
goto error;
}
if (be.fcs_in != BR2684_FCSIN_NO || be.fcs_out != BR2684_FCSOUT_NO ||
be.fcs_auto || be.has_vpiid || be.send_padding || (be.encaps !=
BR2684_ENCAPS_VC && be.encaps != BR2684_ENCAPS_LLC) ||
be.min_size != 0) {
err = -EINVAL;
goto error;
}
brvcc = kmalloc(sizeof(struct br2684_vcc), GFP_KERNEL);
if (!brvcc) {
err = -ENOMEM;
goto error;
}
memset(brvcc, 0, sizeof(struct br2684_vcc));
DPRINTK("br2684_regvcc vcc=%p, encaps=%d, brvcc=%p\n", atmvcc, be.encaps,
brvcc);
if (list_empty(&brdev->brvccs) && !brdev->mac_was_set) {
unsigned char *esi = atmvcc->dev->esi;
if (esi[0] | esi[1] | esi[2] | esi[3] | esi[4] | esi[5])
memcpy(brdev->net_dev.dev_addr, esi,
brdev->net_dev.addr_len);
else
brdev->net_dev.dev_addr[2] = 1;
}
list_add(&brvcc->brvccs, &brdev->brvccs);
write_unlock_irq(&devs_lock);
brvcc->brdev = brdev;
brvcc->atmvcc = atmvcc;
atmvcc->user_back = brvcc;
brvcc->encaps = (enum br2684_encaps) be.encaps;
brvcc->old_push = atmvcc->push;
barrier();
atmvcc->push = br2684_push;
skb_queue_head_init(&copy);
skb_migrate(&atmvcc->sk->receive_queue, &copy);
while ((skb = skb_dequeue(&copy))) {
BRPRIV(skb->dev)->stats.rx_bytes -= skb->len;
BRPRIV(skb->dev)->stats.rx_packets--;
br2684_push(atmvcc, skb);
}
return 0;
error:
write_unlock_irq(&devs_lock);
MOD_DEC_USE_COUNT;
return err;
}
 
static int br2684_create(unsigned long arg)
{
int err;
struct br2684_dev *brdev;
struct atm_newif_br2684 ni;
 
DPRINTK("br2684_create\n");
/*
* We track module use by vcc's NOT the devices they're on. We're
* protected here against module death by the kernel_lock, but if
* we need to sleep we should make sure that the module doesn't
* disappear under us.
*/
MOD_INC_USE_COUNT;
if (copy_from_user(&ni, (void *) arg, sizeof ni)) {
MOD_DEC_USE_COUNT;
return -EFAULT;
}
if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) {
MOD_DEC_USE_COUNT;
return -EINVAL;
}
if ((brdev = kmalloc(sizeof(struct br2684_dev), GFP_KERNEL)) == NULL) {
MOD_DEC_USE_COUNT;
return -ENOMEM;
}
memset(brdev, 0, sizeof(struct br2684_dev));
INIT_LIST_HEAD(&brdev->brvccs);
 
write_lock_irq(&devs_lock);
brdev->number = list_empty(&br2684_devs) ? 1 :
list_entry_brdev(br2684_devs.prev)->number + 1;
list_add_tail(&brdev->br2684_devs, &br2684_devs);
write_unlock_irq(&devs_lock);
 
if (ni.ifname[0] != '\0') {
memcpy(brdev->net_dev.name, ni.ifname,
sizeof(brdev->net_dev.name));
brdev->net_dev.name[sizeof(brdev->net_dev.name) - 1] = '\0';
} else
sprintf(brdev->net_dev.name, "nas%d", brdev->number);
DPRINTK("registered netdev %s\n", brdev->net_dev.name);
ether_setup(&brdev->net_dev);
brdev->mac_was_set = 0;
#ifdef FASTER_VERSION
my_eth_header = brdev->net_dev.hard_header;
brdev->net_dev.hard_header = br2684_header;
my_eth_header_cache = brdev->net_dev.hard_header_cache;
brdev->net_dev.hard_header_cache = br2684_header_cache;
brdev->net_dev.hard_header_len = sizeof(llc_oui_pid_pad) + ETH_HLEN; /* 10 + 14 */
#endif
my_eth_mac_addr = brdev->net_dev.set_mac_address;
brdev->net_dev.set_mac_address = br2684_mac_addr;
brdev->net_dev.hard_start_xmit = br2684_start_xmit;
brdev->net_dev.get_stats = br2684_get_stats;
 
/* open, stop, do_ioctl ? */
err = register_netdev(&brdev->net_dev);
MOD_DEC_USE_COUNT;
if (err < 0) {
printk(KERN_ERR "br2684_create: register_netdev failed\n");
write_lock_irq(&devs_lock);
list_del(&brdev->br2684_devs);
write_unlock_irq(&devs_lock);
kfree(brdev);
return err;
}
return 0;
}
 
/*
* This handles ioctls actually performed on our vcc - we must return
* -ENOIOCTLCMD for any unrecognized ioctl
*/
static int br2684_ioctl(struct atm_vcc *atmvcc, unsigned int cmd,
unsigned long arg)
{
int err;
switch(cmd) {
case ATM_SETBACKEND:
case ATM_NEWBACKENDIF: {
atm_backend_t b;
MOD_INC_USE_COUNT;
err = get_user(b, (atm_backend_t *) arg);
MOD_DEC_USE_COUNT;
if (err)
return -EFAULT;
if (b != ATM_BACKEND_BR2684)
return -ENOIOCTLCMD;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (cmd == ATM_SETBACKEND)
return br2684_regvcc(atmvcc, arg);
else
return br2684_create(arg);
}
#ifdef CONFIG_ATM_BR2684_IPFILTER
case BR2684_SETFILT:
if (atmvcc->push != br2684_push)
return -ENOIOCTLCMD;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
MOD_INC_USE_COUNT;
err = br2684_setfilt(atmvcc, arg);
MOD_DEC_USE_COUNT;
return err;
#endif /* CONFIG_ATM_BR2684_IPFILTER */
}
return -ENOIOCTLCMD;
}
 
#ifdef CONFIG_PROC_FS
/* Never put more than 256 bytes in at once */
static int br2684_proc_engine(loff_t pos, char *buf)
{
struct list_head *lhd, *lhc;
struct br2684_dev *brdev;
struct br2684_vcc *brvcc;
list_for_each(lhd, &br2684_devs) {
brdev = list_entry_brdev(lhd);
if (pos-- == 0)
return sprintf(buf, "dev %.16s: num=%d, mac=%02X:%02X:"
"%02X:%02X:%02X:%02X (%s)\n", brdev->net_dev.name,
brdev->number,
brdev->net_dev.dev_addr[0],
brdev->net_dev.dev_addr[1],
brdev->net_dev.dev_addr[2],
brdev->net_dev.dev_addr[3],
brdev->net_dev.dev_addr[4],
brdev->net_dev.dev_addr[5],
brdev->mac_was_set ? "set" : "auto");
list_for_each(lhc, &brdev->brvccs) {
brvcc = list_entry_brvcc(lhc);
if (pos-- == 0)
return sprintf(buf, " vcc %d.%d.%d: encaps=%s"
#ifndef FASTER_VERSION
", failed copies %u/%u"
#endif /* FASTER_VERSION */
"\n", brvcc->atmvcc->dev->number,
brvcc->atmvcc->vpi, brvcc->atmvcc->vci,
(brvcc->encaps == e_llc) ? "LLC" : "VC"
#ifndef FASTER_VERSION
, brvcc->copies_failed
, brvcc->copies_needed
#endif /* FASTER_VERSION */
);
#ifdef CONFIG_ATM_BR2684_IPFILTER
#define b1(var, byte) ((u8 *) &brvcc->filter.var)[byte]
#define bs(var) b1(var, 0), b1(var, 1), b1(var, 2), b1(var, 3)
if (brvcc->filter.netmask != 0 && pos-- == 0)
return sprintf(buf, " filter=%d.%d.%d.%d/"
"%d.%d.%d.%d\n", bs(prefix), bs(netmask));
#undef bs
#undef b1
#endif /* CONFIG_ATM_BR2684_IPFILTER */
}
}
return 0;
}
 
static ssize_t br2684_proc_read(struct file *file, char *buf, size_t count,
loff_t *pos)
{
unsigned long page;
int len = 0, x, left;
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
left = PAGE_SIZE - 256;
if (count < left)
left = count;
read_lock(&devs_lock);
for (;;) {
x = br2684_proc_engine(*pos, &((char *) page)[len]);
if (x == 0)
break;
if (x > left)
/*
* This should only happen if the user passed in
* a "count" too small for even one line
*/
x = -EINVAL;
if (x < 0) {
len = x;
break;
}
len += x;
left -= x;
(*pos)++;
if (left < 256)
break;
}
read_unlock(&devs_lock);
if (len > 0 && copy_to_user(buf, (char *) page, len))
len = -EFAULT;
free_page(page);
return len;
}
 
static struct file_operations br2684_proc_operations = {
read: br2684_proc_read,
};
 
extern struct proc_dir_entry *atm_proc_root; /* from proc.c */
#endif /* CONFIG_PROC_FS */
 
/* the following avoids some spurious warnings from the compiler */
#define UNUSED __attribute__((unused))
 
static int __init UNUSED br2684_init(void)
{
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *p;
if ((p = create_proc_entry("br2684", 0, atm_proc_root)) == NULL)
return -ENOMEM;
p->proc_fops = &br2684_proc_operations;
#endif /* CONFIG_PROC_FS */
br2684_ioctl_set(br2684_ioctl);
return 0;
}
 
static void __exit UNUSED br2684_exit(void)
{
struct br2684_dev *brdev;
br2684_ioctl_set(NULL);
#ifdef CONFIG_PROC_FS
remove_proc_entry("br2684", atm_proc_root);
#endif /* CONFIG_PROC_FS */
while (!list_empty(&br2684_devs)) {
brdev = list_entry_brdev(br2684_devs.next);
unregister_netdev(&brdev->net_dev);
list_del(&brdev->br2684_devs);
kfree(brdev);
}
}
 
module_init(br2684_init);
module_exit(br2684_exit);
 
MODULE_AUTHOR("Marcell GAL");
MODULE_DESCRIPTION("RFC2684 bridged protocols over ATM/AAL5");
MODULE_LICENSE("GPL");
/clip.c
0,0 → 1,815
/* net/atm/clip.c - RFC1577 Classical IP over ATM */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/config.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/kernel.h> /* for UINT_MAX */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/wait.h>
#include <linux/timer.h>
#include <linux/if_arp.h> /* for some manifest constants */
#include <linux/notifier.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/atmclip.h>
#include <linux/atmarp.h>
#include <linux/ip.h> /* for net/route.h */
#include <linux/in.h> /* for struct sockaddr_in */
#include <linux/if.h> /* for IFF_UP */
#include <linux/inetdevice.h>
#include <linux/bitops.h>
#include <net/route.h> /* for struct rtable and routing */
#include <net/icmp.h> /* icmp_send */
#include <asm/param.h> /* for HZ */
#include <asm/byteorder.h> /* for htons etc. */
#include <asm/system.h> /* save/restore_flags */
#include <asm/uaccess.h>
#include <asm/atomic.h>
 
#include "common.h"
#include "resources.h"
#include "ipcommon.h"
#include <net/atmclip.h>
 
 
#if 0
#define DPRINTK(format,args...) printk(format,##args)
#else
#define DPRINTK(format,args...)
#endif
 
 
struct net_device *clip_devs = NULL;
struct atm_vcc *atmarpd = NULL;
static struct neigh_table clip_tbl;
static struct timer_list idle_timer;
static int start_timer = 1;
 
 
static int to_atmarpd(enum atmarp_ctrl_type type,int itf,unsigned long ip)
{
struct atmarp_ctrl *ctrl;
struct sk_buff *skb;
 
DPRINTK("to_atmarpd(%d)\n",type);
if (!atmarpd) return -EUNATCH;
skb = alloc_skb(sizeof(struct atmarp_ctrl),GFP_ATOMIC);
if (!skb) return -ENOMEM;
ctrl = (struct atmarp_ctrl *) skb_put(skb,sizeof(struct atmarp_ctrl));
ctrl->type = type;
ctrl->itf_num = itf;
ctrl->ip = ip;
atm_force_charge(atmarpd,skb->truesize);
skb_queue_tail(&atmarpd->sk->receive_queue,skb);
wake_up(&atmarpd->sleep);
return 0;
}
 
 
static void link_vcc(struct clip_vcc *clip_vcc,struct atmarp_entry *entry)
{
DPRINTK("link_vcc %p to entry %p (neigh %p)\n",clip_vcc,entry,
entry->neigh);
clip_vcc->entry = entry;
clip_vcc->xoff = 0; /* @@@ may overrun buffer by one packet */
clip_vcc->next = entry->vccs;
entry->vccs = clip_vcc;
entry->neigh->used = jiffies;
}
 
 
static void unlink_clip_vcc(struct clip_vcc *clip_vcc)
{
struct atmarp_entry *entry = clip_vcc->entry;
struct clip_vcc **walk;
 
if (!entry) {
printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n",clip_vcc);
return;
}
spin_lock_bh(&entry->neigh->dev->xmit_lock); /* block clip_start_xmit() */
entry->neigh->used = jiffies;
for (walk = &entry->vccs; *walk; walk = &(*walk)->next)
if (*walk == clip_vcc) {
int error;
 
*walk = clip_vcc->next; /* atomic */
clip_vcc->entry = NULL;
if (clip_vcc->xoff)
netif_wake_queue(entry->neigh->dev);
if (entry->vccs)
goto out;
entry->expires = jiffies-1;
/* force resolution or expiration */
error = neigh_update(entry->neigh,NULL,NUD_NONE,0,0);
if (error)
printk(KERN_CRIT "unlink_clip_vcc: "
"neigh_update failed with %d\n",error);
goto out;
}
printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc "
"0x%p)\n",entry,clip_vcc);
out:
spin_unlock_bh(&entry->neigh->dev->xmit_lock);
}
 
 
static void idle_timer_check(unsigned long dummy)
{
int i;
 
/*DPRINTK("idle_timer_check\n");*/
write_lock(&clip_tbl.lock);
for (i = 0; i <= NEIGH_HASHMASK; i++) {
struct neighbour **np;
 
for (np = &clip_tbl.hash_buckets[i]; *np;) {
struct neighbour *n = *np;
struct atmarp_entry *entry = NEIGH2ENTRY(n);
struct clip_vcc *clip_vcc;
 
write_lock(&n->lock);
 
for (clip_vcc = entry->vccs; clip_vcc;
clip_vcc = clip_vcc->next)
if (clip_vcc->idle_timeout &&
time_after(jiffies, clip_vcc->last_use+
clip_vcc->idle_timeout)) {
DPRINTK("releasing vcc %p->%p of "
"entry %p\n",clip_vcc,clip_vcc->vcc,
entry);
vcc_release_async(clip_vcc->vcc,
-ETIMEDOUT);
}
if (entry->vccs ||
time_before(jiffies, entry->expires)) {
np = &n->next;
write_unlock(&n->lock);
continue;
}
if (atomic_read(&n->refcnt) > 1) {
struct sk_buff *skb;
 
DPRINTK("destruction postponed with ref %d\n",
atomic_read(&n->refcnt));
while ((skb = skb_dequeue(&n->arp_queue)) !=
NULL)
dev_kfree_skb(skb);
np = &n->next;
write_unlock(&n->lock);
continue;
}
*np = n->next;
DPRINTK("expired neigh %p\n",n);
n->dead = 1;
write_unlock(&n->lock);
neigh_release(n);
}
}
mod_timer(&idle_timer, jiffies+CLIP_CHECK_INTERVAL*HZ);
write_unlock(&clip_tbl.lock);
}
 
 
static int clip_arp_rcv(struct sk_buff *skb)
{
struct atm_vcc *vcc;
 
DPRINTK("clip_arp_rcv\n");
vcc = ATM_SKB(skb)->vcc;
if (!vcc || !atm_charge(vcc,skb->truesize)) {
dev_kfree_skb_any(skb);
return 0;
}
DPRINTK("pushing to %p\n",vcc);
DPRINTK("using %p\n",CLIP_VCC(vcc)->old_push);
CLIP_VCC(vcc)->old_push(vcc,skb);
return 0;
}
 
 
static void clip_push(struct atm_vcc *vcc,struct sk_buff *skb)
{
struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
 
DPRINTK("clip push\n");
if (!skb) {
DPRINTK("removing VCC %p\n",clip_vcc);
if (clip_vcc->entry) unlink_clip_vcc(clip_vcc);
clip_vcc->old_push(vcc,NULL); /* pass on the bad news */
kfree(clip_vcc);
return;
}
atm_return(vcc,skb->truesize);
skb->dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : clip_devs;
/* clip_vcc->entry == NULL if we don't have an IP address yet */
if (!skb->dev) {
dev_kfree_skb_any(skb);
return;
}
ATM_SKB(skb)->vcc = vcc;
skb->mac.raw = skb->data;
if (!clip_vcc->encap || skb->len < RFC1483LLC_LEN || memcmp(skb->data,
llc_oui,sizeof(llc_oui))) skb->protocol = htons(ETH_P_IP);
else {
skb->protocol = ((u16 *) skb->data)[3];
skb_pull(skb,RFC1483LLC_LEN);
if (skb->protocol == htons(ETH_P_ARP)) {
PRIV(skb->dev)->stats.rx_packets++;
PRIV(skb->dev)->stats.rx_bytes += skb->len;
clip_arp_rcv(skb);
return;
}
}
clip_vcc->last_use = jiffies;
PRIV(skb->dev)->stats.rx_packets++;
PRIV(skb->dev)->stats.rx_bytes += skb->len;
memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
netif_rx(skb);
}
 
 
/*
* Note: these spinlocks _must_not_ block on non-SMP. The only goal is that
* clip_pop is atomic with respect to the critical section in clip_start_xmit.
*/
 
 
static void clip_pop(struct atm_vcc *vcc,struct sk_buff *skb)
{
struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
struct net_device *dev = skb->dev;
int old;
unsigned long flags;
 
DPRINTK("clip_pop(vcc %p)\n",vcc);
clip_vcc->old_pop(vcc,skb);
/* skb->dev == NULL in outbound ARP packets */
if (!dev) return;
spin_lock_irqsave(&PRIV(dev)->xoff_lock,flags);
if (atm_may_send(vcc,0)) {
old = xchg(&clip_vcc->xoff,0);
if (old) netif_wake_queue(dev);
}
spin_unlock_irqrestore(&PRIV(dev)->xoff_lock,flags);
}
 
 
static void clip_neigh_destroy(struct neighbour *neigh)
{
DPRINTK("clip_neigh_destroy (neigh %p)\n",neigh);
if (NEIGH2ENTRY(neigh)->vccs)
printk(KERN_CRIT "clip_neigh_destroy: vccs != NULL !!!\n");
NEIGH2ENTRY(neigh)->vccs = (void *) 0xdeadbeef;
}
 
 
static void clip_neigh_solicit(struct neighbour *neigh,struct sk_buff *skb)
{
DPRINTK("clip_neigh_solicit (neigh %p, skb %p)\n",neigh,skb);
to_atmarpd(act_need,PRIV(neigh->dev)->number,NEIGH2ENTRY(neigh)->ip);
}
 
 
static void clip_neigh_error(struct neighbour *neigh,struct sk_buff *skb)
{
#ifndef CONFIG_ATM_CLIP_NO_ICMP
icmp_send(skb,ICMP_DEST_UNREACH,ICMP_HOST_UNREACH,0);
#endif
kfree_skb(skb);
}
 
 
static struct neigh_ops clip_neigh_ops = {
family: AF_INET,
destructor: clip_neigh_destroy,
solicit: clip_neigh_solicit,
error_report: clip_neigh_error,
output: dev_queue_xmit,
connected_output: dev_queue_xmit,
hh_output: dev_queue_xmit,
queue_xmit: dev_queue_xmit,
};
 
 
static int clip_constructor(struct neighbour *neigh)
{
struct atmarp_entry *entry = NEIGH2ENTRY(neigh);
struct net_device *dev = neigh->dev;
struct in_device *in_dev = dev->ip_ptr;
 
DPRINTK("clip_constructor (neigh %p, entry %p)\n",neigh,entry);
if (!in_dev) return -EINVAL;
neigh->type = inet_addr_type(entry->ip);
if (neigh->type != RTN_UNICAST) return -EINVAL;
if (in_dev->arp_parms) neigh->parms = in_dev->arp_parms;
neigh->ops = &clip_neigh_ops;
neigh->output = neigh->nud_state & NUD_VALID ?
neigh->ops->connected_output : neigh->ops->output;
entry->neigh = neigh;
entry->vccs = NULL;
entry->expires = jiffies-1;
return 0;
}
 
static u32 clip_hash(const void *pkey, const struct net_device *dev)
{
u32 hash_val;
 
hash_val = *(u32*)pkey;
hash_val ^= (hash_val>>16);
hash_val ^= hash_val>>8;
hash_val ^= hash_val>>3;
hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;
 
return hash_val;
}
 
 
static struct neigh_table clip_tbl = {
NULL, /* next */
AF_INET, /* family */
sizeof(struct neighbour)+sizeof(struct atmarp_entry), /* entry_size */
4, /* key_len */
clip_hash,
clip_constructor, /* constructor */
NULL, /* pconstructor */
NULL, /* pdestructor */
NULL, /* proxy_redo */
"clip_arp_cache",
{ /* neigh_parms */
NULL, /* next */
NULL, /* neigh_setup */
&clip_tbl, /* tbl */
0, /* entries */
NULL, /* priv */
NULL, /* sysctl_table */
30*HZ, /* base_reachable_time */
1*HZ, /* retrans_time */
60*HZ, /* gc_staletime */
30*HZ, /* reachable_time */
5*HZ, /* delay_probe_time */
3, /* queue_len */
3, /* ucast_probes */
0, /* app_probes */
3, /* mcast_probes */
1*HZ, /* anycast_delay */
(8*HZ)/10, /* proxy_delay */
1*HZ, /* proxy_qlen */
64 /* locktime */
},
30*HZ,128,512,1024 /* copied from ARP ... */
};
 
 
/* @@@ copy bh locking from arp.c -- need to bh-enable atm code before */
 
/*
* We play with the resolve flag: 0 and 1 have the usual meaning, but -1 means
* to allocate the neighbour entry but not to ask atmarpd for resolution. Also,
* don't increment the usage count. This is used to create entries in
* clip_setentry.
*/
 
 
static int clip_encap(struct atm_vcc *vcc,int mode)
{
CLIP_VCC(vcc)->encap = mode;
return 0;
}
 
 
static int clip_start_xmit(struct sk_buff *skb,struct net_device *dev)
{
struct clip_priv *clip_priv = PRIV(dev);
struct atmarp_entry *entry;
struct atm_vcc *vcc;
int old;
unsigned long flags;
 
DPRINTK("clip_start_xmit (skb %p)\n",skb);
if (!skb->dst) {
printk(KERN_ERR "clip_start_xmit: skb->dst == NULL\n");
dev_kfree_skb(skb);
clip_priv->stats.tx_dropped++;
return 0;
}
if (!skb->dst->neighbour) {
#if 0
skb->dst->neighbour = clip_find_neighbour(skb->dst,1);
if (!skb->dst->neighbour) {
dev_kfree_skb(skb); /* lost that one */
clip_priv->stats.tx_dropped++;
return 0;
}
#endif
printk(KERN_ERR "clip_start_xmit: NO NEIGHBOUR !\n");
dev_kfree_skb(skb);
clip_priv->stats.tx_dropped++;
return 0;
}
entry = NEIGH2ENTRY(skb->dst->neighbour);
if (!entry->vccs) {
if (time_after(jiffies, entry->expires)) {
/* should be resolved */
entry->expires = jiffies+ATMARP_RETRY_DELAY*HZ;
to_atmarpd(act_need,PRIV(dev)->number,entry->ip);
}
if (entry->neigh->arp_queue.qlen < ATMARP_MAX_UNRES_PACKETS)
skb_queue_tail(&entry->neigh->arp_queue,skb);
else {
dev_kfree_skb(skb);
clip_priv->stats.tx_dropped++;
}
return 0;
}
DPRINTK("neigh %p, vccs %p\n",entry,entry->vccs);
ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc;
DPRINTK("using neighbour %p, vcc %p\n",skb->dst->neighbour,vcc);
if (entry->vccs->encap) {
void *here;
 
here = skb_push(skb,RFC1483LLC_LEN);
memcpy(here,llc_oui,sizeof(llc_oui));
((u16 *) here)[3] = skb->protocol;
}
atomic_add(skb->truesize,&vcc->sk->wmem_alloc);
ATM_SKB(skb)->atm_options = vcc->atm_options;
entry->vccs->last_use = jiffies;
DPRINTK("atm_skb(%p)->vcc(%p)->dev(%p)\n",skb,vcc,vcc->dev);
old = xchg(&entry->vccs->xoff,1); /* assume XOFF ... */
if (old) {
printk(KERN_WARNING "clip_start_xmit: XOFF->XOFF transition\n");
return 0;
}
clip_priv->stats.tx_packets++;
clip_priv->stats.tx_bytes += skb->len;
(void) vcc->send(vcc,skb);
if (atm_may_send(vcc,0)) {
entry->vccs->xoff = 0;
return 0;
}
spin_lock_irqsave(&clip_priv->xoff_lock,flags);
netif_stop_queue(dev); /* XOFF -> throttle immediately */
barrier();
if (!entry->vccs->xoff)
netif_start_queue(dev);
/* Oh, we just raced with clip_pop. netif_start_queue should be
good enough, because nothing should really be asleep because
of the brief netif_stop_queue. If this isn't true or if it
changes, use netif_wake_queue instead. */
spin_unlock_irqrestore(&clip_priv->xoff_lock,flags);
return 0;
}
 
 
static struct net_device_stats *clip_get_stats(struct net_device *dev)
{
return &PRIV(dev)->stats;
}
 
 
static int clip_mkip(struct atm_vcc *vcc,int timeout)
{
struct clip_vcc *clip_vcc;
struct sk_buff_head copy;
struct sk_buff *skb;
 
if (!vcc->push) return -EBADFD;
clip_vcc = kmalloc(sizeof(struct clip_vcc),GFP_KERNEL);
if (!clip_vcc) return -ENOMEM;
DPRINTK("mkip clip_vcc %p vcc %p\n",clip_vcc,vcc);
clip_vcc->vcc = vcc;
vcc->user_back = clip_vcc;
clip_vcc->entry = NULL;
clip_vcc->xoff = 0;
clip_vcc->encap = 1;
clip_vcc->last_use = jiffies;
clip_vcc->idle_timeout = timeout*HZ;
clip_vcc->old_push = vcc->push;
clip_vcc->old_pop = vcc->pop;
vcc->push = clip_push;
vcc->pop = clip_pop;
skb_queue_head_init(&copy);
skb_migrate(&vcc->sk->receive_queue,&copy);
/* re-process everything received between connection setup and MKIP */
while ((skb = skb_dequeue(&copy)))
if (!clip_devs) {
atm_return(vcc,skb->truesize);
kfree_skb(skb);
}
else {
unsigned int len = skb->len;
 
clip_push(vcc,skb);
PRIV(skb->dev)->stats.rx_packets--;
PRIV(skb->dev)->stats.rx_bytes -= len;
}
return 0;
}
 
 
static int clip_setentry(struct atm_vcc *vcc,u32 ip)
{
struct neighbour *neigh;
struct atmarp_entry *entry;
int error;
struct clip_vcc *clip_vcc;
struct rtable *rt;
 
if (vcc->push != clip_push) {
printk(KERN_WARNING "clip_setentry: non-CLIP VCC\n");
return -EBADF;
}
clip_vcc = CLIP_VCC(vcc);
if (!ip) {
if (!clip_vcc->entry) {
printk(KERN_ERR "hiding hidden ATMARP entry\n");
return 0;
}
DPRINTK("setentry: remove\n");
unlink_clip_vcc(clip_vcc);
return 0;
}
error = ip_route_output(&rt,ip,0,1,0);
if (error) return error;
neigh = __neigh_lookup(&clip_tbl,&ip,rt->u.dst.dev,1);
ip_rt_put(rt);
if (!neigh)
return -ENOMEM;
entry = NEIGH2ENTRY(neigh);
if (entry != clip_vcc->entry) {
if (!clip_vcc->entry) DPRINTK("setentry: add\n");
else {
DPRINTK("setentry: update\n");
unlink_clip_vcc(clip_vcc);
}
link_vcc(clip_vcc,entry);
}
error = neigh_update(neigh,llc_oui,NUD_PERMANENT,1,0);
neigh_release(neigh);
return error;
}
 
 
static int clip_init(struct net_device *dev)
{
DPRINTK("clip_init %s\n",dev->name);
dev->hard_start_xmit = clip_start_xmit;
/* sg_xmit ... */
dev->hard_header = NULL;
dev->rebuild_header = NULL;
dev->set_mac_address = NULL;
dev->hard_header_parse = NULL;
dev->hard_header_cache = NULL;
dev->header_cache_update = NULL;
dev->change_mtu = NULL;
dev->do_ioctl = NULL;
dev->get_stats = clip_get_stats;
dev->type = ARPHRD_ATM;
dev->hard_header_len = RFC1483LLC_LEN;
dev->mtu = RFC1626_MTU;
dev->addr_len = 0;
dev->tx_queue_len = 100; /* "normal" queue (packets) */
/* When using a "real" qdisc, the qdisc determines the queue */
/* length. tx_queue_len is only used for the default case, */
/* without any more elaborate queuing. 100 is a reasonable */
/* compromise between decent burst-tolerance and protection */
/* against memory hogs. */
dev->flags = 0;
return 0;
}
 
 
static int clip_create(int number)
{
struct net_device *dev;
struct clip_priv *clip_priv;
int error;
 
if (number != -1) {
for (dev = clip_devs; dev; dev = PRIV(dev)->next)
if (PRIV(dev)->number == number) return -EEXIST;
}
else {
number = 0;
for (dev = clip_devs; dev; dev = PRIV(dev)->next)
if (PRIV(dev)->number >= number)
number = PRIV(dev)->number+1;
}
dev = kmalloc(sizeof(struct net_device)+sizeof(struct clip_priv),
GFP_KERNEL);
if (!dev) return -ENOMEM;
memset(dev,0,sizeof(struct net_device)+sizeof(struct clip_priv));
clip_priv = PRIV(dev);
sprintf(dev->name,"atm%d",number);
dev->init = clip_init;
spin_lock_init(&clip_priv->xoff_lock);
clip_priv->number = number;
error = register_netdev(dev);
if (error) {
kfree(dev);
return error;
}
clip_priv->next = clip_devs;
clip_devs = dev;
DPRINTK("registered (net:%s)\n",dev->name);
return number;
}
 
 
static int clip_device_event(struct notifier_block *this,unsigned long event,
void *dev)
{
/* ignore non-CLIP devices */
if (((struct net_device *) dev)->type != ARPHRD_ATM ||
((struct net_device *) dev)->init != clip_init)
return NOTIFY_DONE;
switch (event) {
case NETDEV_UP:
DPRINTK("clip_device_event NETDEV_UP\n");
(void) to_atmarpd(act_up,PRIV(dev)->number,0);
break;
case NETDEV_GOING_DOWN:
DPRINTK("clip_device_event NETDEV_DOWN\n");
(void) to_atmarpd(act_down,PRIV(dev)->number,0);
break;
case NETDEV_CHANGE:
case NETDEV_CHANGEMTU:
DPRINTK("clip_device_event NETDEV_CHANGE*\n");
(void) to_atmarpd(act_change,PRIV(dev)->number,0);
break;
case NETDEV_REBOOT:
case NETDEV_REGISTER:
case NETDEV_DOWN:
DPRINTK("clip_device_event %ld\n",event);
/* ignore */
break;
default:
printk(KERN_WARNING "clip_device_event: unknown event "
"%ld\n",event);
break;
}
return NOTIFY_DONE;
}
 
 
static int clip_inet_event(struct notifier_block *this,unsigned long event,
void *ifa)
{
struct in_device *in_dev;
 
in_dev = ((struct in_ifaddr *) ifa)->ifa_dev;
if (!in_dev || !in_dev->dev) {
printk(KERN_WARNING "clip_inet_event: no device\n");
return NOTIFY_DONE;
}
/*
* Transitions are of the down-change-up type, so it's sufficient to
* handle the change on up.
*/
if (event != NETDEV_UP) return NOTIFY_DONE;
return clip_device_event(this,NETDEV_CHANGE,in_dev->dev);
}
 
 
static struct notifier_block clip_dev_notifier = {
clip_device_event,
NULL,
0
};
 
 
 
static struct notifier_block clip_inet_notifier = {
clip_inet_event,
NULL,
0
};
 
 
 
static void atmarpd_close(struct atm_vcc *vcc)
{
DPRINTK("atmarpd_close\n");
atmarpd = NULL; /* assumed to be atomic */
barrier();
unregister_inetaddr_notifier(&clip_inet_notifier);
unregister_netdevice_notifier(&clip_dev_notifier);
if (skb_peek(&vcc->sk->receive_queue))
printk(KERN_ERR "atmarpd_close: closing with requests "
"pending\n");
skb_queue_purge(&vcc->sk->receive_queue);
DPRINTK("(done)\n");
MOD_DEC_USE_COUNT;
}
 
 
static struct atmdev_ops atmarpd_dev_ops = {
.close = atmarpd_close,
};
 
 
static struct atm_dev atmarpd_dev = {
.ops = &atmarpd_dev_ops,
.type = "arpd",
.number = 999,
.lock = SPIN_LOCK_UNLOCKED
};
 
 
static int atm_init_atmarp(struct atm_vcc *vcc)
{
struct net_device *dev;
 
if (atmarpd) return -EADDRINUSE;
if (start_timer) {
start_timer = 0;
init_timer(&idle_timer);
idle_timer.expires = jiffies+CLIP_CHECK_INTERVAL*HZ;
idle_timer.function = idle_timer_check;
add_timer(&idle_timer);
}
atmarpd = vcc;
set_bit(ATM_VF_META,&vcc->flags);
set_bit(ATM_VF_READY,&vcc->flags);
/* allow replies and avoid getting closed if signaling dies */
vcc->dev = &atmarpd_dev;
vcc_insert_socket(vcc->sk);
vcc->push = NULL;
vcc->pop = NULL; /* crash */
vcc->push_oam = NULL; /* crash */
if (register_netdevice_notifier(&clip_dev_notifier))
printk(KERN_ERR "register_netdevice_notifier failed\n");
if (register_inetaddr_notifier(&clip_inet_notifier))
printk(KERN_ERR "register_inetaddr_notifier failed\n");
for (dev = clip_devs; dev; dev = PRIV(dev)->next)
if (dev->flags & IFF_UP)
(void) to_atmarpd(act_up,PRIV(dev)->number,0);
MOD_INC_USE_COUNT;
return 0;
}
 
static struct atm_clip_ops __atm_clip_ops = {
.clip_create = clip_create,
.clip_mkip = clip_mkip,
.clip_setentry = clip_setentry,
.clip_encap = clip_encap,
.clip_push = clip_push,
.atm_init_atmarp = atm_init_atmarp,
.owner = THIS_MODULE
};
 
static int __init atm_clip_init(void)
{
/* we should use neigh_table_init() */
clip_tbl.lock = RW_LOCK_UNLOCKED;
clip_tbl.kmem_cachep = kmem_cache_create(clip_tbl.id,
clip_tbl.entry_size, 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
 
if (!clip_tbl.kmem_cachep)
return -ENOMEM;
 
/* so neigh_ifdown() doesn't complain */
clip_tbl.proxy_timer.data = 0;
clip_tbl.proxy_timer.function = 0;
init_timer(&clip_tbl.proxy_timer);
skb_queue_head_init(&clip_tbl.proxy_queue);
 
clip_tbl_hook = &clip_tbl;
atm_clip_ops_set(&__atm_clip_ops);
 
return 0;
}
 
static void __exit atm_clip_exit(void)
{
struct net_device *dev, *next;
 
atm_clip_ops_set(NULL);
 
neigh_ifdown(&clip_tbl, NULL);
dev = clip_devs;
while (dev) {
next = PRIV(dev)->next;
unregister_netdev(dev);
kfree(dev);
dev = next;
}
if (start_timer == 0) del_timer(&idle_timer);
 
kmem_cache_destroy(clip_tbl.kmem_cachep);
 
clip_tbl_hook = NULL;
}
 
module_init(atm_clip_init);
module_exit(atm_clip_exit);
 
MODULE_LICENSE("GPL");
/pvc.c
0,0 → 1,157
/* net/atm/pvc.c - ATM PVC sockets */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/config.h>
#include <linux/net.h> /* struct socket, struct net_proto,
struct proto_ops */
#include <linux/atm.h> /* ATM stuff */
#include <linux/atmdev.h> /* ATM devices */
#include <linux/errno.h> /* error codes */
#include <linux/kernel.h> /* printk */
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/bitops.h>
#include <net/sock.h> /* for sock_no_* */
 
#include "resources.h" /* devs and vccs */
#include "common.h" /* common for PVCs and SVCs */
 
 
static int pvc_shutdown(struct socket *sock,int how)
{
return 0;
}
 
 
static int pvc_bind(struct socket *sock,struct sockaddr *sockaddr,
int sockaddr_len)
{
struct sock *sk = sock->sk;
struct sockaddr_atmpvc *addr;
struct atm_vcc *vcc;
int error;
 
if (sockaddr_len != sizeof(struct sockaddr_atmpvc)) return -EINVAL;
addr = (struct sockaddr_atmpvc *) sockaddr;
if (addr->sap_family != AF_ATMPVC) return -EAFNOSUPPORT;
lock_sock(sk);
vcc = ATM_SD(sock);
if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) {
error = -EBADFD;
goto out;
}
if (test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
if (vcc->vpi != ATM_VPI_UNSPEC) addr->sap_addr.vpi = vcc->vpi;
if (vcc->vci != ATM_VCI_UNSPEC) addr->sap_addr.vci = vcc->vci;
}
error = vcc_connect(sock, addr->sap_addr.itf, addr->sap_addr.vpi,
addr->sap_addr.vci);
out:
release_sock(sk);
return error;
}
 
 
static int pvc_connect(struct socket *sock,struct sockaddr *sockaddr,
int sockaddr_len,int flags)
{
return pvc_bind(sock,sockaddr,sockaddr_len);
}
 
static int pvc_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
int error;
 
lock_sock(sk);
error = vcc_setsockopt(sock, level, optname, optval, optlen);
release_sock(sk);
return error;
}
 
 
static int pvc_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = sock->sk;
int error;
 
lock_sock(sk);
error = vcc_getsockopt(sock, level, optname, optval, optlen);
release_sock(sk);
return error;
}
 
 
static int pvc_getname(struct socket *sock,struct sockaddr *sockaddr,
int *sockaddr_len,int peer)
{
struct sockaddr_atmpvc *addr;
struct atm_vcc *vcc = ATM_SD(sock);
 
if (!vcc->dev || !test_bit(ATM_VF_ADDR,&vcc->flags)) return -ENOTCONN;
*sockaddr_len = sizeof(struct sockaddr_atmpvc);
addr = (struct sockaddr_atmpvc *) sockaddr;
addr->sap_family = AF_ATMPVC;
addr->sap_addr.itf = vcc->dev->number;
addr->sap_addr.vpi = vcc->vpi;
addr->sap_addr.vci = vcc->vci;
return 0;
}
 
 
static struct proto_ops pvc_proto_ops = {
.family = PF_ATMPVC,
 
.release = vcc_release,
.bind = pvc_bind,
.connect = pvc_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = pvc_getname,
.poll = atm_poll,
.ioctl = vcc_ioctl,
.listen = sock_no_listen,
.shutdown = pvc_shutdown,
.setsockopt = pvc_setsockopt,
.getsockopt = pvc_getsockopt,
.sendmsg = vcc_sendmsg,
.recvmsg = vcc_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
 
 
static int pvc_create(struct socket *sock,int protocol)
{
sock->ops = &pvc_proto_ops;
return vcc_create(sock, protocol, PF_ATMPVC);
}
 
 
static struct net_proto_family pvc_family_ops = {
PF_ATMPVC,
pvc_create,
0, /* no authentication */
0, /* no encryption */
0 /* no encrypt_net */
};
 
 
/*
* Initialize the ATM PVC protocol family
*/
 
 
int atmpvc_init(void)
{
return sock_register(&pvc_family_ops);
}
 
void atmpvc_exit(void)
{
sock_unregister(PF_ATMPVC);
}
/common.c
0,0 → 1,1128
/* net/atm/common.c - ATM sockets (common part for PVC and SVC) */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/net.h> /* struct socket, struct net_proto, struct
proto_ops */
#include <linux/atm.h> /* ATM stuff */
#include <linux/atmdev.h>
#include <linux/atmclip.h> /* CLIP_*ENCAP */
#include <linux/atmarp.h> /* manifest constants */
#include <linux/sonet.h> /* for ioctls */
#include <linux/socket.h> /* SOL_SOCKET */
#include <linux/errno.h> /* error codes */
#include <linux/capability.h>
#include <linux/mm.h> /* verify_area */
#include <linux/sched.h>
#include <linux/time.h> /* struct timeval */
#include <linux/skbuff.h>
#include <linux/bitops.h>
#include <linux/init.h>
#include <net/sock.h> /* struct sock */
 
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/poll.h>
#include <asm/ioctls.h>
 
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
#include <linux/atmlec.h>
#include "lec.h"
#include "lec_arpc.h"
struct atm_lane_ops *atm_lane_ops;
static DECLARE_MUTEX(atm_lane_ops_mutex);
 
void atm_lane_ops_set(struct atm_lane_ops *hook)
{
down(&atm_lane_ops_mutex);
atm_lane_ops = hook;
up(&atm_lane_ops_mutex);
}
 
int try_atm_lane_ops(void)
{
down(&atm_lane_ops_mutex);
if (atm_lane_ops && try_inc_mod_count(atm_lane_ops->owner)) {
up(&atm_lane_ops_mutex);
return 1;
}
up(&atm_lane_ops_mutex);
return 0;
}
 
#if defined(CONFIG_ATM_LANE_MODULE) || defined(CONFIG_ATM_MPOA_MODULE)
EXPORT_SYMBOL(atm_lane_ops);
EXPORT_SYMBOL(try_atm_lane_ops);
EXPORT_SYMBOL(atm_lane_ops_set);
#endif
#endif
 
#if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE)
#include <linux/atmmpc.h>
#include "mpc.h"
struct atm_mpoa_ops *atm_mpoa_ops;
static DECLARE_MUTEX(atm_mpoa_ops_mutex);
 
void atm_mpoa_ops_set(struct atm_mpoa_ops *hook)
{
down(&atm_mpoa_ops_mutex);
atm_mpoa_ops = hook;
up(&atm_mpoa_ops_mutex);
}
 
int try_atm_mpoa_ops(void)
{
down(&atm_mpoa_ops_mutex);
if (atm_mpoa_ops && try_inc_mod_count(atm_mpoa_ops->owner)) {
up(&atm_mpoa_ops_mutex);
return 1;
}
up(&atm_mpoa_ops_mutex);
return 0;
}
#ifdef CONFIG_ATM_MPOA_MODULE
EXPORT_SYMBOL(atm_mpoa_ops);
EXPORT_SYMBOL(try_atm_mpoa_ops);
EXPORT_SYMBOL(atm_mpoa_ops_set);
#endif
#endif
 
#if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE)
#include <linux/atm_tcp.h>
#ifdef CONFIG_ATM_TCP_MODULE
struct atm_tcp_ops atm_tcp_ops;
EXPORT_SYMBOL(atm_tcp_ops);
#endif
#endif
 
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
#include <net/atmclip.h>
struct atm_clip_ops *atm_clip_ops;
static DECLARE_MUTEX(atm_clip_ops_mutex);
 
void atm_clip_ops_set(struct atm_clip_ops *hook)
{
down(&atm_clip_ops_mutex);
atm_clip_ops = hook;
up(&atm_clip_ops_mutex);
}
 
int try_atm_clip_ops(void)
{
down(&atm_clip_ops_mutex);
if (atm_clip_ops && try_inc_mod_count(atm_clip_ops->owner)) {
up(&atm_clip_ops_mutex);
return 1;
}
up(&atm_clip_ops_mutex);
return 0;
}
 
#ifdef CONFIG_ATM_CLIP_MODULE
EXPORT_SYMBOL(atm_clip_ops);
EXPORT_SYMBOL(try_atm_clip_ops);
EXPORT_SYMBOL(atm_clip_ops_set);
#endif
#endif
 
#if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE)
static DECLARE_MUTEX(pppoatm_ioctl_mutex);
 
static int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
 
void pppoatm_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long))
{
down(&pppoatm_ioctl_mutex);
pppoatm_ioctl_hook = hook;
up(&pppoatm_ioctl_mutex);
}
#ifdef CONFIG_PPPOATM_MODULE
EXPORT_SYMBOL(pppoatm_ioctl_set);
#endif
#endif
 
#if defined(CONFIG_ATM_BR2684) || defined(CONFIG_ATM_BR2684_MODULE)
static DECLARE_MUTEX(br2684_ioctl_mutex);
 
static int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
void br2684_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long))
{
down(&br2684_ioctl_mutex);
br2684_ioctl_hook = hook;
up(&br2684_ioctl_mutex);
}
#ifdef CONFIG_ATM_BR2684_MODULE
EXPORT_SYMBOL(br2684_ioctl_set);
#endif
#endif
 
#include "resources.h" /* atm_find_dev */
#include "common.h" /* prototypes */
#include "protocols.h" /* atm_init_<transport> */
#include "addr.h" /* address registry */
#ifdef CONFIG_ATM_CLIP
#include <net/atmclip.h> /* for clip_create */
#endif
#include "signaling.h" /* for WAITING and sigd_attach */
 
 
#if 0
#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
#else
#define DPRINTK(format,args...)
#endif
 
 
struct sock *vcc_sklist;
rwlock_t vcc_sklist_lock = RW_LOCK_UNLOCKED;
 
void __vcc_insert_socket(struct sock *sk)
{
sk->next = vcc_sklist;
if (sk->next)
vcc_sklist->pprev = &sk->next;
vcc_sklist = sk;
sk->pprev = &vcc_sklist;
}
 
void vcc_insert_socket(struct sock *sk)
{
write_lock_irq(&vcc_sklist_lock);
__vcc_insert_socket(sk);
write_unlock_irq(&vcc_sklist_lock);
}
 
void vcc_remove_socket(struct sock *sk)
{
write_lock_irq(&vcc_sklist_lock);
if (sk->pprev) {
if (sk->next)
sk->next->pprev = sk->pprev;
*sk->pprev = sk->next;
sk->pprev = NULL;
}
write_unlock_irq(&vcc_sklist_lock);
}
 
 
static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size)
{
struct sk_buff *skb;
 
if (atomic_read(&vcc->sk->wmem_alloc) && !atm_may_send(vcc,size)) {
DPRINTK("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n",
atomic_read(&vcc->sk->wmem_alloc),size,vcc->sk->sndbuf);
return NULL;
}
while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule();
DPRINTK("AlTx %d += %d\n",atomic_read(&vcc->sk->wmem_alloc),skb->truesize);
atomic_add(skb->truesize, &vcc->sk->wmem_alloc);
return skb;
}
 
 
EXPORT_SYMBOL(vcc_sklist);
EXPORT_SYMBOL(vcc_sklist_lock);
EXPORT_SYMBOL(vcc_insert_socket);
EXPORT_SYMBOL(vcc_remove_socket);
 
static void vcc_sock_destruct(struct sock *sk)
{
struct atm_vcc *vcc = sk->protinfo.af_atm;
 
if (atomic_read(&vcc->sk->rmem_alloc))
printk(KERN_DEBUG "vcc_sock_destruct: rmem leakage (%d bytes) detected.\n", atomic_read(&sk->rmem_alloc));
 
if (atomic_read(&vcc->sk->wmem_alloc))
printk(KERN_DEBUG "vcc_sock_destruct: wmem leakage (%d bytes) detected.\n", atomic_read(&sk->wmem_alloc));
 
kfree(sk->protinfo.af_atm);
 
MOD_DEC_USE_COUNT;
}
 
int vcc_create(struct socket *sock, int protocol, int family)
{
struct sock *sk;
struct atm_vcc *vcc;
 
sock->sk = NULL;
if (sock->type == SOCK_STREAM)
return -EINVAL;
sk = sk_alloc(family, GFP_KERNEL, 1);
if (!sk)
return -ENOMEM;
sock_init_data(NULL, sk);
 
vcc = sk->protinfo.af_atm = kmalloc(sizeof(*vcc), GFP_KERNEL);
if (!vcc) {
sk_free(sk);
return -ENOMEM;
}
 
memset(vcc, 0, sizeof(*vcc));
vcc->sk = sk;
 
vcc->dev = NULL;
vcc->callback = NULL;
memset(&vcc->local,0,sizeof(struct sockaddr_atmsvc));
memset(&vcc->remote,0,sizeof(struct sockaddr_atmsvc));
vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */
atomic_set(&vcc->sk->wmem_alloc,0);
atomic_set(&vcc->sk->rmem_alloc,0);
vcc->push = NULL;
vcc->pop = NULL;
vcc->push_oam = NULL;
vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
vcc->atm_options = vcc->aal_options = 0;
init_waitqueue_head(&vcc->sleep);
sk->sleep = &vcc->sleep;
sk->destruct = vcc_sock_destruct;
sock->sk = sk;
 
MOD_INC_USE_COUNT;
 
return 0;
}
 
 
static void vcc_destroy_socket(struct sock *sk)
{
struct atm_vcc *vcc;
struct sk_buff *skb;
 
vcc = sk->protinfo.af_atm;
clear_bit(ATM_VF_READY, &vcc->flags);
if (vcc->dev) {
if (vcc->dev->ops->close)
vcc->dev->ops->close(vcc);
if (vcc->push)
vcc->push(vcc, NULL); /* atmarpd has no push */
 
vcc_remove_socket(sk); /* no more receive */
 
while ((skb = skb_dequeue(&vcc->sk->receive_queue))) {
atm_return(vcc,skb->truesize);
kfree_skb(skb);
}
 
if (vcc->dev->ops->owner)
__MOD_DEC_USE_COUNT(vcc->dev->ops->owner);
atm_dev_put(vcc->dev);
}
}
 
 
int vcc_release(struct socket *sock)
{
struct sock *sk = sock->sk;
 
if (sk) {
lock_sock(sk);
vcc_destroy_socket(sock->sk);
release_sock(sk);
sock_put(sk);
}
 
return 0;
}
 
 
void vcc_release_async(struct atm_vcc *vcc, int reply)
{
set_bit(ATM_VF_CLOSE, &vcc->flags);
vcc->reply = reply;
vcc->sk->err = -reply;
wake_up(&vcc->sleep);
}
 
 
EXPORT_SYMBOL(vcc_release_async);
 
 
static int adjust_tp(struct atm_trafprm *tp,unsigned char aal)
{
int max_sdu;
 
if (!tp->traffic_class) return 0;
switch (aal) {
case ATM_AAL0:
max_sdu = ATM_CELL_SIZE-1;
break;
case ATM_AAL34:
max_sdu = ATM_MAX_AAL34_PDU;
break;
default:
printk(KERN_WARNING "ATM: AAL problems ... "
"(%d)\n",aal);
/* fall through */
case ATM_AAL5:
max_sdu = ATM_MAX_AAL5_PDU;
}
if (!tp->max_sdu) tp->max_sdu = max_sdu;
else if (tp->max_sdu > max_sdu) return -EINVAL;
if (!tp->max_cdv) tp->max_cdv = ATM_MAX_CDV;
return 0;
}
 
 
static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, int vpi,
int vci)
{
int error;
 
if ((vpi != ATM_VPI_UNSPEC && vpi != ATM_VPI_ANY &&
vpi >> dev->ci_range.vpi_bits) || (vci != ATM_VCI_UNSPEC &&
vci != ATM_VCI_ANY && vci >> dev->ci_range.vci_bits))
return -EINVAL;
if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE))
return -EPERM;
error = 0;
if (!try_inc_mod_count(dev->ops->owner))
return -ENODEV;
vcc->dev = dev;
vcc_insert_socket(vcc->sk);
switch (vcc->qos.aal) {
case ATM_AAL0:
error = atm_init_aal0(vcc);
vcc->stats = &dev->stats.aal0;
break;
case ATM_AAL34:
error = atm_init_aal34(vcc);
vcc->stats = &dev->stats.aal34;
break;
case ATM_NO_AAL:
/* ATM_AAL5 is also used in the "0 for default" case */
vcc->qos.aal = ATM_AAL5;
/* fall through */
case ATM_AAL5:
error = atm_init_aal5(vcc);
vcc->stats = &dev->stats.aal5;
break;
default:
error = -EPROTOTYPE;
}
if (!error) error = adjust_tp(&vcc->qos.txtp,vcc->qos.aal);
if (!error) error = adjust_tp(&vcc->qos.rxtp,vcc->qos.aal);
if (error)
goto fail;
DPRINTK("VCC %d.%d, AAL %d\n",vpi,vci,vcc->qos.aal);
DPRINTK(" TX: %d, PCR %d..%d, SDU %d\n",vcc->qos.txtp.traffic_class,
vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu);
DPRINTK(" RX: %d, PCR %d..%d, SDU %d\n",vcc->qos.rxtp.traffic_class,
vcc->qos.rxtp.min_pcr,vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu);
if (dev->ops->open) {
if ((error = dev->ops->open(vcc,vpi,vci)))
goto fail;
}
return 0;
 
fail:
vcc_remove_socket(vcc->sk);
if (dev->ops->owner)
__MOD_DEC_USE_COUNT(dev->ops->owner);
/* ensure we get dev module ref count correct */
vcc->dev = NULL;
return error;
}
 
 
int vcc_connect(struct socket *sock, int itf, short vpi, int vci)
{
struct atm_dev *dev;
struct atm_vcc *vcc = ATM_SD(sock);
int error;
 
DPRINTK("vcc_connect (vpi %d, vci %d)\n",vpi,vci);
if (sock->state == SS_CONNECTED)
return -EISCONN;
if (sock->state != SS_UNCONNECTED)
return -EINVAL;
if (!(vpi || vci))
return -EINVAL;
 
if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC)
clear_bit(ATM_VF_PARTIAL,&vcc->flags);
else
if (test_bit(ATM_VF_PARTIAL,&vcc->flags))
return -EINVAL;
DPRINTK("vcc_connect (TX: cl %d,bw %d-%d,sdu %d; "
"RX: cl %d,bw %d-%d,sdu %d,AAL %s%d)\n",
vcc->qos.txtp.traffic_class,vcc->qos.txtp.min_pcr,
vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu,
vcc->qos.rxtp.traffic_class,vcc->qos.rxtp.min_pcr,
vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu,
vcc->qos.aal == ATM_AAL5 ? "" : vcc->qos.aal == ATM_AAL0 ? "" :
" ??? code ",vcc->qos.aal == ATM_AAL0 ? 0 : vcc->qos.aal);
if (!test_bit(ATM_VF_HASQOS, &vcc->flags))
return -EBADFD;
if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS ||
vcc->qos.rxtp.traffic_class == ATM_ANYCLASS)
return -EINVAL;
if (itf != ATM_ITF_ANY) {
dev = atm_dev_lookup(itf);
if (!dev)
return -ENODEV;
error = __vcc_connect(vcc, dev, vpi, vci);
if (error) {
atm_dev_put(dev);
return error;
}
} else {
struct list_head *p, *next;
 
dev = NULL;
spin_lock(&atm_dev_lock);
list_for_each_safe(p, next, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list);
atm_dev_hold(dev);
spin_unlock(&atm_dev_lock);
if (!__vcc_connect(vcc, dev, vpi, vci))
break;
atm_dev_put(dev);
dev = NULL;
spin_lock(&atm_dev_lock);
}
spin_unlock(&atm_dev_lock);
if (!dev)
return -ENODEV;
}
if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
set_bit(ATM_VF_PARTIAL,&vcc->flags);
if (test_bit(ATM_VF_READY,&ATM_SD(sock)->flags))
sock->state = SS_CONNECTED;
return 0;
}
 
 
int vcc_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct atm_vcc *vcc;
struct sk_buff *skb;
int copied, error = -EINVAL;
if (sock->state != SS_CONNECTED)
return -ENOTCONN;
if (flags & ~MSG_DONTWAIT) /* only handle MSG_DONTWAIT */
return -EOPNOTSUPP;
vcc = ATM_SD(sock);
if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
test_bit(ATM_VF_CLOSE, &vcc->flags) ||
!test_bit(ATM_VF_READY, &vcc->flags))
return 0;
skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &error);
if (!skb)
return error;
copied = skb->len;
if (copied > size) {
copied = size;
msg->msg_flags |= MSG_TRUNC;
}
error = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (error)
return error;
sock_recv_timestamp(msg, sk, skb);
if (vcc->dev->ops->feedback)
vcc->dev->ops->feedback(vcc, skb, (unsigned long) skb->data,
(unsigned long) msg->msg_iov->iov_base, copied);
DPRINTK("RcvM %d -= %d\n", atomic_read(&vcc->sk->rmem_alloc), skb->truesize);
atm_return(vcc, skb->truesize);
skb_free_datagram(sk, skb);
return copied;
}
 
int vcc_sendmsg(struct socket *sock, struct msghdr *m, int total_len,
struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
DECLARE_WAITQUEUE(wait,current);
struct atm_vcc *vcc;
struct sk_buff *skb;
int eff,error;
const void *buff;
int size;
 
lock_sock(sk);
if (sock->state != SS_CONNECTED) {
error = -ENOTCONN;
goto out;
}
if (m->msg_name) {
error = -EISCONN;
goto out;
}
if (m->msg_iovlen != 1) {
error = -ENOSYS; /* fix this later @@@ */
goto out;
}
buff = m->msg_iov->iov_base;
size = m->msg_iov->iov_len;
vcc = ATM_SD(sock);
if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
test_bit(ATM_VF_CLOSE, &vcc->flags) ||
!test_bit(ATM_VF_READY, &vcc->flags)) {
error = -EPIPE;
send_sig(SIGPIPE, current, 0);
goto out;
}
if (!size) {
error = 0;
goto out;
}
if (size < 0 || size > vcc->qos.txtp.max_sdu) {
error = -EMSGSIZE;
goto out;
}
/* verify_area is done by net/socket.c */
eff = (size+3) & ~3; /* align to word boundary */
add_wait_queue(&vcc->sleep,&wait);
set_current_state(TASK_INTERRUPTIBLE);
error = 0;
while (!(skb = alloc_tx(vcc,eff))) {
if (m->msg_flags & MSG_DONTWAIT) {
error = -EAGAIN;
break;
}
schedule();
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
error = -ERESTARTSYS;
break;
}
if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
test_bit(ATM_VF_CLOSE, &vcc->flags) ||
!test_bit(ATM_VF_READY, &vcc->flags)) {
error = -EPIPE;
send_sig(SIGPIPE, current, 0);
break;
}
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&vcc->sleep,&wait);
if (error)
goto out;
skb->dev = NULL; /* for paths shared with net_device interfaces */
ATM_SKB(skb)->atm_options = vcc->atm_options;
if (copy_from_user(skb_put(skb,size),buff,size)) {
kfree_skb(skb);
error = -EFAULT;
goto out;
}
if (eff != size) memset(skb->data+size,0,eff-size);
error = vcc->dev->ops->send(vcc,skb);
error = error ? error : size;
out:
release_sock(sk);
return error;
}
 
 
unsigned int atm_poll(struct file *file,struct socket *sock,poll_table *wait)
{
struct atm_vcc *vcc;
unsigned int mask;
 
vcc = ATM_SD(sock);
poll_wait(file,&vcc->sleep,wait);
mask = 0;
if (skb_peek(&vcc->sk->receive_queue))
mask |= POLLIN | POLLRDNORM;
if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
test_bit(ATM_VF_CLOSE,&vcc->flags))
mask |= POLLHUP;
if (sock->state != SS_CONNECTING) {
if (vcc->qos.txtp.traffic_class != ATM_NONE &&
vcc->qos.txtp.max_sdu+atomic_read(&vcc->sk->wmem_alloc) <= vcc->sk->sndbuf)
mask |= POLLOUT | POLLWRNORM;
}
else if (vcc->reply != WAITING) {
mask |= POLLOUT | POLLWRNORM;
if (vcc->reply) mask |= POLLERR;
}
return mask;
}
 
 
int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct atm_vcc *vcc;
int error;
 
vcc = ATM_SD(sock);
switch (cmd) {
case SIOCOUTQ:
if (sock->state != SS_CONNECTED ||
!test_bit(ATM_VF_READY, &vcc->flags)) {
error = -EINVAL;
goto done;
}
error = put_user(vcc->sk->sndbuf-
atomic_read(&vcc->sk->wmem_alloc),
(int *) arg) ? -EFAULT : 0;
goto done;
case SIOCINQ:
{
struct sk_buff *skb;
 
if (sock->state != SS_CONNECTED) {
error = -EINVAL;
goto done;
}
skb = skb_peek(&vcc->sk->receive_queue);
error = put_user(skb ? skb->len : 0,
(int *) arg) ? -EFAULT : 0;
goto done;
}
case SIOCGSTAMP: /* borrowed from IP */
if (!vcc->sk->stamp.tv_sec) {
error = -ENOENT;
goto done;
}
error = copy_to_user((void *) arg, &vcc->sk->stamp,
sizeof(struct timeval)) ? -EFAULT : 0;
goto done;
case ATM_SETSC:
printk(KERN_WARNING "ATM_SETSC is obsolete\n");
error = 0;
goto done;
case ATMSIGD_CTRL:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
/*
* The user/kernel protocol for exchanging signalling
* info uses kernel pointers as opaque references,
* so the holder of the file descriptor can scribble
* on the kernel... so we should make sure that we
* have the same privledges that /proc/kcore needs
*/
if (!capable(CAP_SYS_RAWIO)) {
error = -EPERM;
goto done;
}
error = sigd_attach(vcc);
if (!error)
sock->state = SS_CONNECTED;
goto done;
#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
case SIOCMKCLIP:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (try_atm_clip_ops()) {
error = atm_clip_ops->clip_create(arg);
if (atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
} else
error = -ENOSYS;
goto done;
case ATMARPD_CTRL:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
#if defined(CONFIG_ATM_CLIP_MODULE)
if (!atm_clip_ops)
request_module("clip");
#endif
if (try_atm_clip_ops()) {
error = atm_clip_ops->atm_init_atmarp(vcc);
if (atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
if (!error)
sock->state = SS_CONNECTED;
} else
error = -ENOSYS;
goto done;
case ATMARP_MKIP:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (try_atm_clip_ops()) {
error = atm_clip_ops->clip_mkip(vcc, arg);
if (atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
} else
error = -ENOSYS;
goto done;
case ATMARP_SETENTRY:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (try_atm_clip_ops()) {
error = atm_clip_ops->clip_setentry(vcc, arg);
if (atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
} else
error = -ENOSYS;
goto done;
case ATMARP_ENCAP:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (try_atm_clip_ops()) {
error = atm_clip_ops->clip_encap(vcc, arg);
if (atm_clip_ops->owner)
__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
} else
error = -ENOSYS;
goto done;
#endif
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
case ATMLEC_CTRL:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
#if defined(CONFIG_ATM_LANE_MODULE)
if (!atm_lane_ops)
request_module("lec");
#endif
if (try_atm_lane_ops()) {
error = atm_lane_ops->lecd_attach(vcc, (int) arg);
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
if (error >= 0)
sock->state = SS_CONNECTED;
} else
error = -ENOSYS;
goto done;
case ATMLEC_MCAST:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (try_atm_lane_ops()) {
error = atm_lane_ops->mcast_attach(vcc, (int) arg);
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
} else
error = -ENOSYS;
goto done;
case ATMLEC_DATA:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (try_atm_lane_ops()) {
error = atm_lane_ops->vcc_attach(vcc, (void *) arg);
if (atm_lane_ops->owner)
__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
} else
error = -ENOSYS;
goto done;
#endif
#if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE)
case ATMMPC_CTRL:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
#if defined(CONFIG_ATM_MPOA_MODULE)
if (!atm_mpoa_ops)
request_module("mpoa");
#endif
if (try_atm_mpoa_ops()) {
error = atm_mpoa_ops->mpoad_attach(vcc, (int) arg);
if (atm_mpoa_ops->owner)
__MOD_DEC_USE_COUNT(atm_mpoa_ops->owner);
if (error >= 0)
sock->state = SS_CONNECTED;
} else
error = -ENOSYS;
goto done;
case ATMMPC_DATA:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (try_atm_mpoa_ops()) {
error = atm_mpoa_ops->vcc_attach(vcc, arg);
if (atm_mpoa_ops->owner)
__MOD_DEC_USE_COUNT(atm_mpoa_ops->owner);
} else
error = -ENOSYS;
goto done;
#endif
#if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE)
case SIOCSIFATMTCP:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (!atm_tcp_ops.attach) {
error = -ENOPKG;
goto done;
}
fops_get(&atm_tcp_ops);
error = atm_tcp_ops.attach(vcc, (int) arg);
if (error >= 0)
sock->state = SS_CONNECTED;
else
fops_put(&atm_tcp_ops);
goto done;
case ATMTCP_CREATE:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (!atm_tcp_ops.create_persistent) {
error = -ENOPKG;
goto done;
}
error = atm_tcp_ops.create_persistent((int) arg);
if (error < 0)
fops_put(&atm_tcp_ops);
goto done;
case ATMTCP_REMOVE:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
}
if (!atm_tcp_ops.remove_persistent) {
error = -ENOPKG;
goto done;
}
error = atm_tcp_ops.remove_persistent((int) arg);
fops_put(&atm_tcp_ops);
goto done;
#endif
default:
break;
}
error = -ENOIOCTLCMD;
#if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE)
down(&pppoatm_ioctl_mutex);
if (pppoatm_ioctl_hook)
error = pppoatm_ioctl_hook(vcc, cmd, arg);
up(&pppoatm_ioctl_mutex);
if (error != -ENOIOCTLCMD)
goto done;
#endif
#if defined(CONFIG_ATM_BR2684) || defined(CONFIG_ATM_BR2684_MODULE)
down(&br2684_ioctl_mutex);
if (br2684_ioctl_hook)
error = br2684_ioctl_hook(vcc, cmd, arg);
up(&br2684_ioctl_mutex);
if (error != -ENOIOCTLCMD)
goto done;
#endif
 
error = atm_dev_ioctl(cmd, arg);
 
done:
return error;
}
 
 
static int atm_change_qos(struct atm_vcc *vcc,struct atm_qos *qos)
{
int error;
 
/*
* Don't let the QoS change the already connected AAL type nor the
* traffic class.
*/
if (qos->aal != vcc->qos.aal ||
qos->rxtp.traffic_class != vcc->qos.rxtp.traffic_class ||
qos->txtp.traffic_class != vcc->qos.txtp.traffic_class)
return -EINVAL;
error = adjust_tp(&qos->txtp,qos->aal);
if (!error) error = adjust_tp(&qos->rxtp,qos->aal);
if (error) return error;
if (!vcc->dev->ops->change_qos) return -EOPNOTSUPP;
if (vcc->sk->family == AF_ATMPVC)
return vcc->dev->ops->change_qos(vcc,qos,ATM_MF_SET);
return svc_change_qos(vcc,qos);
}
 
 
static int check_tp(struct atm_trafprm *tp)
{
/* @@@ Should be merged with adjust_tp */
if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS) return 0;
if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr &&
!tp->max_pcr) return -EINVAL;
if (tp->min_pcr == ATM_MAX_PCR) return -EINVAL;
if (tp->min_pcr && tp->max_pcr && tp->max_pcr != ATM_MAX_PCR &&
tp->min_pcr > tp->max_pcr) return -EINVAL;
/*
* We allow pcr to be outside [min_pcr,max_pcr], because later
* adjustment may still push it in the valid range.
*/
return 0;
}
 
 
static int check_qos(struct atm_qos *qos)
{
int error;
 
if (!qos->txtp.traffic_class && !qos->rxtp.traffic_class)
return -EINVAL;
if (qos->txtp.traffic_class != qos->rxtp.traffic_class &&
qos->txtp.traffic_class && qos->rxtp.traffic_class &&
qos->txtp.traffic_class != ATM_ANYCLASS &&
qos->rxtp.traffic_class != ATM_ANYCLASS) return -EINVAL;
error = check_tp(&qos->txtp);
if (error) return error;
return check_tp(&qos->rxtp);
}
 
int vcc_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct atm_vcc *vcc;
unsigned long value;
int error;
 
if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname))
return -EINVAL;
 
vcc = ATM_SD(sock);
switch (optname) {
case SO_ATMQOS:
{
struct atm_qos qos;
 
if (copy_from_user(&qos,optval,sizeof(qos)))
return -EFAULT;
error = check_qos(&qos);
if (error) return error;
if (sock->state == SS_CONNECTED)
return atm_change_qos(vcc,&qos);
if (sock->state != SS_UNCONNECTED)
return -EBADFD;
vcc->qos = qos;
set_bit(ATM_VF_HASQOS,&vcc->flags);
return 0;
}
case SO_SETCLP:
if (get_user(value,(unsigned long *) optval))
return -EFAULT;
if (value) vcc->atm_options |= ATM_ATMOPT_CLP;
else vcc->atm_options &= ~ATM_ATMOPT_CLP;
return 0;
default:
if (level == SOL_SOCKET) return -EINVAL;
break;
}
if (!vcc->dev || !vcc->dev->ops->setsockopt) return -EINVAL;
return vcc->dev->ops->setsockopt(vcc,level,optname,optval,optlen);
}
 
 
int vcc_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct atm_vcc *vcc;
int len;
 
if (get_user(len, optlen))
return -EFAULT;
if (__SO_LEVEL_MATCH(optname, level) && len != __SO_SIZE(optname))
return -EINVAL;
 
vcc = ATM_SD(sock);
switch (optname) {
case SO_ATMQOS:
if (!test_bit(ATM_VF_HASQOS,&vcc->flags))
return -EINVAL;
return copy_to_user(optval,&vcc->qos,sizeof(vcc->qos)) ?
-EFAULT : 0;
case SO_SETCLP:
return put_user(vcc->atm_options & ATM_ATMOPT_CLP ? 1 :
0,(unsigned long *) optval) ? -EFAULT : 0;
case SO_ATMPVC:
{
struct sockaddr_atmpvc pvc;
 
if (!vcc->dev ||
!test_bit(ATM_VF_ADDR,&vcc->flags))
return -ENOTCONN;
pvc.sap_family = AF_ATMPVC;
pvc.sap_addr.itf = vcc->dev->number;
pvc.sap_addr.vpi = vcc->vpi;
pvc.sap_addr.vci = vcc->vci;
return copy_to_user(optval,&pvc,sizeof(pvc)) ?
-EFAULT : 0;
}
default:
if (level == SOL_SOCKET) return -EINVAL;
break;
}
if (!vcc->dev || !vcc->dev->ops->getsockopt) return -EINVAL;
return vcc->dev->ops->getsockopt(vcc, level, optname, optval, len);
}
 
 
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
unsigned char *addr) = NULL;
void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent) = NULL;
#if defined(CONFIG_ATM_LANE_MODULE) || defined(CONFIG_BRIDGE_MODULE)
EXPORT_SYMBOL(br_fdb_get_hook);
EXPORT_SYMBOL(br_fdb_put_hook);
#endif /* defined(CONFIG_ATM_LANE_MODULE) || defined(CONFIG_BRIDGE_MODULE) */
#endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
#endif /* defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) */
 
 
static int __init atm_init(void)
{
int error;
 
if ((error = atmpvc_init()) < 0) {
printk(KERN_ERR "atmpvc_init() failed with %d\n", error);
goto failure;
}
if ((error = atmsvc_init()) < 0) {
printk(KERN_ERR "atmsvc_init() failed with %d\n", error);
goto failure;
}
#ifdef CONFIG_PROC_FS
if ((error = atm_proc_init()) < 0) {
printk(KERN_ERR "atm_proc_init() failed with %d\n",error);
goto failure;
}
#endif
return 0;
 
failure:
atmsvc_exit();
atmpvc_exit();
return error;
}
 
static void __exit atm_exit(void)
{
#ifdef CONFIG_PROC_FS
atm_proc_exit();
#endif
atmsvc_exit();
atmpvc_exit();
}
 
module_init(atm_init);
module_exit(atm_exit);
 
MODULE_LICENSE("GPL");
/raw.c
0,0 → 1,93
/* net/atm/raw.c - Raw AAL0 and AAL5 transports */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/atmdev.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/mm.h>
 
#include "common.h"
#include "protocols.h"
 
 
#if 0
#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
#else
#define DPRINTK(format,args...)
#endif
 
 
/*
* SKB == NULL indicates that the link is being closed
*/
 
void atm_push_raw(struct atm_vcc *vcc,struct sk_buff *skb)
{
if (skb) {
skb_queue_tail(&vcc->sk->receive_queue,skb);
wake_up(&vcc->sleep);
}
}
 
 
static void atm_pop_raw(struct atm_vcc *vcc,struct sk_buff *skb)
{
DPRINTK("APopR (%d) %d -= %d\n",vcc->vci,vcc->sk->wmem_alloc,skb->truesize);
atomic_sub(skb->truesize, &vcc->sk->wmem_alloc);
dev_kfree_skb_any(skb);
wake_up(&vcc->sleep);
}
 
 
static int atm_send_aal0(struct atm_vcc *vcc,struct sk_buff *skb)
{
/*
* Note that if vpi/vci are _ANY or _UNSPEC the below will
* still work
*/
if (!capable(CAP_NET_ADMIN) &&
(((u32 *) skb->data)[0] & (ATM_HDR_VPI_MASK | ATM_HDR_VCI_MASK)) !=
((vcc->vpi << ATM_HDR_VPI_SHIFT) | (vcc->vci << ATM_HDR_VCI_SHIFT)))
{
kfree_skb(skb);
return -EADDRNOTAVAIL;
}
return vcc->dev->ops->send(vcc,skb);
}
 
 
int atm_init_aal0(struct atm_vcc *vcc)
{
vcc->push = atm_push_raw;
vcc->pop = atm_pop_raw;
vcc->push_oam = NULL;
vcc->send = atm_send_aal0;
return 0;
}
 
 
int atm_init_aal34(struct atm_vcc *vcc)
{
vcc->push = atm_push_raw;
vcc->pop = atm_pop_raw;
vcc->push_oam = NULL;
vcc->send = vcc->dev->ops->send;
return 0;
}
 
 
int atm_init_aal5(struct atm_vcc *vcc)
{
vcc->push = atm_push_raw;
vcc->pop = atm_pop_raw;
vcc->push_oam = NULL;
vcc->send = vcc->dev->ops->send;
return 0;
}
 
 
EXPORT_SYMBOL(atm_init_aal5);
/protocols.h
0,0 → 1,16
/* net/atm/protocols.h - ATM protocol handler entry points */
 
/* Written 1995-1997 by Werner Almesberger, EPFL LRC */
 
 
#ifndef NET_ATM_PROTOCOLS_H
#define NET_ATM_PROTOCOLS_H
 
void atm_push_raw(struct atm_vcc *vcc,struct sk_buff *skb);
 
int atm_init_aal0(struct atm_vcc *vcc); /* "raw" AAL0 */
int atm_init_aal34(struct atm_vcc *vcc);/* "raw" AAL3/4 transport */
int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */
int atm_init_atmarp(struct atm_vcc *vcc);/* ATM ARP */
 
#endif
/svc.c
0,0 → 1,574
/* net/atm/svc.c - ATM SVC sockets */
 
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#include <linux/string.h>
#include <linux/net.h> /* struct socket, struct net_proto,
struct proto_ops */
#include <linux/errno.h> /* error codes */
#include <linux/kernel.h> /* printk */
#include <linux/skbuff.h>
#include <linux/wait.h>
#include <linux/sched.h> /* jiffies and HZ */
#include <linux/fcntl.h> /* O_NONBLOCK */
#include <linux/init.h>
#include <linux/atm.h> /* ATM stuff */
#include <linux/atmsap.h>
#include <linux/atmsvc.h>
#include <linux/atmdev.h>
#include <linux/bitops.h>
#include <net/sock.h> /* for sock_no_* */
#include <asm/uaccess.h>
 
#include "resources.h"
#include "common.h" /* common for PVCs and SVCs */
#include "signaling.h"
#include "addr.h"
 
 
#if 0
#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
#else
#define DPRINTK(format,args...)
#endif
 
 
static int svc_create(struct socket *sock,int protocol);
 
 
/*
* Note: since all this is still nicely synchronized with the signaling demon,
* there's no need to protect sleep loops with clis. If signaling is
* moved into the kernel, that would change.
*/
 
 
void svc_callback(struct atm_vcc *vcc)
{
wake_up(&vcc->sleep);
}
 
 
 
 
static int svc_shutdown(struct socket *sock,int how)
{
return 0;
}
 
 
static void svc_disconnect(struct atm_vcc *vcc)
{
DECLARE_WAITQUEUE(wait,current);
struct sk_buff *skb;
 
DPRINTK("svc_disconnect %p\n",vcc);
if (test_bit(ATM_VF_REGIS,&vcc->flags)) {
add_wait_queue(&vcc->sleep,&wait);
sigd_enq(vcc,as_close,NULL,NULL,NULL);
while (!test_bit(ATM_VF_RELEASED,&vcc->flags) && sigd) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
}
remove_wait_queue(&vcc->sleep,&wait);
}
/* beware - socket is still in use by atmsigd until the last
as_indicate has been answered */
while ((skb = skb_dequeue(&vcc->sk->receive_queue))) {
DPRINTK("LISTEN REL\n");
sigd_enq2(NULL,as_reject,vcc,NULL,NULL,&vcc->qos,0);
dev_kfree_skb(skb);
}
clear_bit(ATM_VF_REGIS,&vcc->flags);
clear_bit(ATM_VF_RELEASED,&vcc->flags);
clear_bit(ATM_VF_CLOSE,&vcc->flags);
/* ... may retry later */
}
 
 
static int svc_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct atm_vcc *vcc;
 
if (sk) {
vcc = ATM_SD(sock);
DPRINTK("svc_release %p\n", vcc);
clear_bit(ATM_VF_READY, &vcc->flags);
/* VCC pointer is used as a reference, so we must not free it
(thereby subjecting it to re-use) before all pending connections
are closed */
sock_hold(sk);
vcc_release(sock);
svc_disconnect(vcc);
sock_put(sk);
}
return 0;
}
 
 
static int svc_bind(struct socket *sock,struct sockaddr *sockaddr,
int sockaddr_len)
{
DECLARE_WAITQUEUE(wait,current);
struct sock *sk = sock->sk;
struct sockaddr_atmsvc *addr;
struct atm_vcc *vcc;
int error;
 
if (sockaddr_len != sizeof(struct sockaddr_atmsvc))
return -EINVAL;
lock_sock(sk);
if (sock->state == SS_CONNECTED) {
error = -EISCONN;
goto out;
}
if (sock->state != SS_UNCONNECTED) {
error = -EINVAL;
goto out;
}
vcc = ATM_SD(sock);
if (test_bit(ATM_VF_SESSION, &vcc->flags)) {
error = -EINVAL;
goto out;
}
addr = (struct sockaddr_atmsvc *) sockaddr;
if (addr->sas_family != AF_ATMSVC) {
error = -EAFNOSUPPORT;
goto out;
}
clear_bit(ATM_VF_BOUND,&vcc->flags);
/* failing rebind will kill old binding */
/* @@@ check memory (de)allocation on rebind */
if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) {
error = -EBADFD;
goto out;
}
vcc->local = *addr;
vcc->reply = WAITING;
add_wait_queue(&vcc->sleep,&wait);
sigd_enq(vcc,as_bind,NULL,NULL,&vcc->local);
while (vcc->reply == WAITING && sigd) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
}
remove_wait_queue(&vcc->sleep,&wait);
clear_bit(ATM_VF_REGIS,&vcc->flags); /* doesn't count */
if (!sigd) {
error = -EUNATCH;
goto out;
}
if (!vcc->reply)
set_bit(ATM_VF_BOUND,&vcc->flags);
error = vcc->reply;
out:
release_sock(sk);
return error;
}
 
 
static int svc_connect(struct socket *sock,struct sockaddr *sockaddr,
int sockaddr_len,int flags)
{
DECLARE_WAITQUEUE(wait,current);
struct sock *sk = sock->sk;
struct sockaddr_atmsvc *addr;
struct atm_vcc *vcc = ATM_SD(sock);
int error;
 
DPRINTK("svc_connect %p\n",vcc);
lock_sock(sk);
if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) {
error = -EINVAL;
goto out;
}
 
switch (sock->state) {
default:
error = -EINVAL;
goto out;
case SS_CONNECTED:
error = -EISCONN;
goto out;
case SS_CONNECTING:
if (vcc->reply == WAITING) {
error = -EALREADY;
goto out;
}
sock->state = SS_UNCONNECTED;
if (vcc->reply) {
error = vcc->reply;
goto out;
}
break;
case SS_UNCONNECTED:
if (test_bit(ATM_VF_SESSION, &vcc->flags)) {
error = -EINVAL;
goto out;
}
addr = (struct sockaddr_atmsvc *) sockaddr;
if (addr->sas_family != AF_ATMSVC) {
error = -EAFNOSUPPORT;
goto out;
}
if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) {
error = -EBADFD;
goto out;
}
if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS ||
vcc->qos.rxtp.traffic_class == ATM_ANYCLASS) {
error = -EINVAL;
goto out;
}
if (!vcc->qos.txtp.traffic_class &&
!vcc->qos.rxtp.traffic_class) {
error = -EINVAL;
goto out;
}
vcc->remote = *addr;
vcc->reply = WAITING;
add_wait_queue(&vcc->sleep,&wait);
sigd_enq(vcc,as_connect,NULL,NULL,&vcc->remote);
if (flags & O_NONBLOCK) {
remove_wait_queue(&vcc->sleep,&wait);
sock->state = SS_CONNECTING;
error = -EINPROGRESS;
goto out;
}
error = 0;
while (vcc->reply == WAITING && sigd) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
if (!signal_pending(current)) continue;
DPRINTK("*ABORT*\n");
/*
* This is tricky:
* Kernel ---close--> Demon
* Kernel <--close--- Demon
* or
* Kernel ---close--> Demon
* Kernel <--error--- Demon
* or
* Kernel ---close--> Demon
* Kernel <--okay---- Demon
* Kernel <--close--- Demon
*/
sigd_enq(vcc,as_close,NULL,NULL,NULL);
while (vcc->reply == WAITING && sigd) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
}
if (!vcc->reply)
while (!test_bit(ATM_VF_RELEASED,&vcc->flags)
&& sigd) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
}
clear_bit(ATM_VF_REGIS,&vcc->flags);
clear_bit(ATM_VF_RELEASED,&vcc->flags);
clear_bit(ATM_VF_CLOSE,&vcc->flags);
/* we're gone now but may connect later */
error = -EINTR;
break;
}
remove_wait_queue(&vcc->sleep,&wait);
if (error)
goto out;
if (!sigd) {
error = -EUNATCH;
goto out;
}
if (vcc->reply) {
error = vcc->reply;
goto out;
}
}
/*
* Not supported yet
*
* #ifndef CONFIG_SINGLE_SIGITF
*/
vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp);
vcc->qos.txtp.pcr = 0;
vcc->qos.txtp.min_pcr = 0;
/*
* #endif
*/
if (!(error = vcc_connect(sock, vcc->itf, vcc->vpi, vcc->vci)))
sock->state = SS_CONNECTED;
else (void) svc_disconnect(vcc);
out:
release_sock(sk);
return error;
}
 
 
static int svc_listen(struct socket *sock,int backlog)
{
DECLARE_WAITQUEUE(wait,current);
struct sock *sk = sock->sk;
struct atm_vcc *vcc = ATM_SD(sock);
int error;
 
DPRINTK("svc_listen %p\n",vcc);
lock_sock(sk);
/* let server handle listen on unbound sockets */
if (test_bit(ATM_VF_SESSION,&vcc->flags)) {
error = -EINVAL;
goto out;
}
vcc->reply = WAITING;
add_wait_queue(&vcc->sleep,&wait);
sigd_enq(vcc,as_listen,NULL,NULL,&vcc->local);
while (vcc->reply == WAITING && sigd) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
}
remove_wait_queue(&vcc->sleep,&wait);
if (!sigd) {
error = -EUNATCH;
goto out;
}
set_bit(ATM_VF_LISTEN,&vcc->flags);
vcc->sk->max_ack_backlog = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT;
error = vcc->reply;
out:
release_sock(sk);
return error;
}
 
 
static int svc_accept(struct socket *sock,struct socket *newsock,int flags)
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
struct atmsvc_msg *msg;
struct atm_vcc *old_vcc = ATM_SD(sock);
struct atm_vcc *new_vcc;
int error;
 
lock_sock(sk);
 
error = svc_create(newsock,0);
if (error)
goto out;
 
new_vcc = ATM_SD(newsock);
 
DPRINTK("svc_accept %p -> %p\n",old_vcc,new_vcc);
while (1) {
DECLARE_WAITQUEUE(wait,current);
 
add_wait_queue(&old_vcc->sleep,&wait);
while (!(skb = skb_dequeue(&old_vcc->sk->receive_queue)) && sigd) {
if (test_bit(ATM_VF_RELEASED,&old_vcc->flags)) break;
if (test_bit(ATM_VF_CLOSE,&old_vcc->flags)) {
error = old_vcc->reply;
break;
}
if (flags & O_NONBLOCK) {
error = -EAGAIN;
break;
}
release_sock(sk);
schedule();
lock_sock(sk);
if (signal_pending(current)) {
error = -ERESTARTSYS;
break;
}
}
remove_wait_queue(&old_vcc->sleep,&wait);
if (error)
goto out;
if (!skb) {
error = -EUNATCH;
goto out;
}
msg = (struct atmsvc_msg *) skb->data;
new_vcc->qos = msg->qos;
set_bit(ATM_VF_HASQOS,&new_vcc->flags);
new_vcc->remote = msg->svc;
new_vcc->local = msg->local;
new_vcc->sap = msg->sap;
error = vcc_connect(newsock, msg->pvc.sap_addr.itf,
msg->pvc.sap_addr.vpi, msg->pvc.sap_addr.vci);
dev_kfree_skb(skb);
old_vcc->sk->ack_backlog--;
if (error) {
sigd_enq2(NULL,as_reject,old_vcc,NULL,NULL,
&old_vcc->qos,error);
error = error == -EAGAIN ? -EBUSY : error;
goto out;
}
/* wait should be short, so we ignore the non-blocking flag */
new_vcc->reply = WAITING;
add_wait_queue(&new_vcc->sleep,&wait);
sigd_enq(new_vcc,as_accept,old_vcc,NULL,NULL);
while (new_vcc->reply == WAITING && sigd) {
set_current_state(TASK_UNINTERRUPTIBLE);
release_sock(sk);
schedule();
lock_sock(sk);
}
remove_wait_queue(&new_vcc->sleep,&wait);
if (!sigd) {
error = -EUNATCH;
goto out;
}
if (!new_vcc->reply) break;
if (new_vcc->reply != -ERESTARTSYS) {
error = new_vcc->reply;
goto out;
}
}
newsock->state = SS_CONNECTED;
out:
release_sock(sk);
return error;
}
 
 
static int svc_getname(struct socket *sock,struct sockaddr *sockaddr,
int *sockaddr_len,int peer)
{
struct sockaddr_atmsvc *addr;
 
*sockaddr_len = sizeof(struct sockaddr_atmsvc);
addr = (struct sockaddr_atmsvc *) sockaddr;
memcpy(addr,peer ? &ATM_SD(sock)->remote : &ATM_SD(sock)->local,
sizeof(struct sockaddr_atmsvc));
return 0;
}
 
 
int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos)
{
DECLARE_WAITQUEUE(wait,current);
 
vcc->reply = WAITING;
add_wait_queue(&vcc->sleep,&wait);
sigd_enq2(vcc,as_modify,NULL,NULL,&vcc->local,qos,0);
while (vcc->reply == WAITING && !test_bit(ATM_VF_RELEASED,&vcc->flags)
&& sigd) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
}
remove_wait_queue(&vcc->sleep,&wait);
if (!sigd) return -EUNATCH;
return vcc->reply;
}
 
 
static int svc_setsockopt(struct socket *sock,int level,int optname,
char *optval,int optlen)
{
struct sock *sk = sock->sk;
struct atm_vcc *vcc;
int error = 0;
 
if (!__SO_LEVEL_MATCH(optname, level) || optname != SO_ATMSAP ||
optlen != sizeof(struct atm_sap)) {
error = vcc_setsockopt(sock, level, optname, optval, optlen);
goto out;
}
vcc = ATM_SD(sock);
if (copy_from_user(&vcc->sap, optval, optlen)) {
error = -EFAULT;
goto out;
}
set_bit(ATM_VF_HASSAP, &vcc->flags);
out:
release_sock(sk);
return error;
}
 
 
static int svc_getsockopt(struct socket *sock,int level,int optname,
char *optval,int *optlen)
{
struct sock *sk = sock->sk;
int error = 0, len;
 
lock_sock(sk);
if (!__SO_LEVEL_MATCH(optname, level) || optname != SO_ATMSAP) {
error = vcc_getsockopt(sock, level, optname, optval, optlen);
goto out;
}
if (get_user(len, optlen)) {
error = -EFAULT;
goto out;
}
if (len != sizeof(struct atm_sap)) {
error = -EINVAL;
goto out;
}
if (copy_to_user(optval, &ATM_SD(sock)->sap, sizeof(struct atm_sap))) {
error = -EFAULT;
goto out;
}
out:
release_sock(sk);
return error;
}
 
 
static struct proto_ops svc_proto_ops = {
.family = PF_ATMSVC,
 
.release = svc_release,
.bind = svc_bind,
.connect = svc_connect,
.socketpair = sock_no_socketpair,
.accept = svc_accept,
.getname = svc_getname,
.poll = atm_poll,
.ioctl = vcc_ioctl,
.listen = svc_listen,
.shutdown = svc_shutdown,
.setsockopt = svc_setsockopt,
.getsockopt = svc_getsockopt,
.sendmsg = vcc_sendmsg,
.recvmsg = vcc_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
 
 
static int svc_create(struct socket *sock,int protocol)
{
int error;
 
sock->ops = &svc_proto_ops;
error = vcc_create(sock, protocol, AF_ATMSVC);
if (error) return error;
ATM_SD(sock)->callback = svc_callback;
ATM_SD(sock)->local.sas_family = AF_ATMSVC;
ATM_SD(sock)->remote.sas_family = AF_ATMSVC;
return 0;
}
 
 
static struct net_proto_family svc_family_ops = {
PF_ATMSVC,
svc_create,
0, /* no authentication */
0, /* no encryption */
0 /* no encrypt_net */
};
 
 
/*
* Initialize the ATM SVC protocol family
*/
 
int atmsvc_init(void)
{
return sock_register(&svc_family_ops);
}
 
void atmsvc_exit(void)
{
sock_unregister(PF_ATMSVC);
}
/atm_misc.c
0,0 → 1,173
/* net/atm/atm_misc.c - Various functions for use by ATM drivers */
 
/* Written 1995-2000 by Werner Almesberger, EPFL ICA */
 
 
#include <linux/module.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/skbuff.h>
#include <linux/sonet.h>
#include <linux/bitops.h>
#include <asm/atomic.h>
#include <asm/errno.h>
 
 
int atm_charge(struct atm_vcc *vcc,int truesize)
{
atm_force_charge(vcc,truesize);
if (atomic_read(&vcc->sk->rmem_alloc) <= vcc->sk->rcvbuf) return 1;
atm_return(vcc,truesize);
atomic_inc(&vcc->stats->rx_drop);
return 0;
}
 
 
struct sk_buff *atm_alloc_charge(struct atm_vcc *vcc,int pdu_size,
int gfp_flags)
{
int guess = atm_guess_pdu2truesize(pdu_size);
 
atm_force_charge(vcc,guess);
if (atomic_read(&vcc->sk->rmem_alloc) <= vcc->sk->rcvbuf) {
struct sk_buff *skb = alloc_skb(pdu_size,gfp_flags);
 
if (skb) {
atomic_add(skb->truesize-guess,&vcc->sk->rmem_alloc);
return skb;
}
}
atm_return(vcc,guess);
atomic_inc(&vcc->stats->rx_drop);
return NULL;
}
 
 
static int check_ci(struct atm_vcc *vcc,short vpi,int vci)
{
struct sock *s;
struct atm_vcc *walk;
 
for (s = vcc_sklist; s; s = s->next) {
walk = s->protinfo.af_atm;
if (walk->dev != vcc->dev)
continue;
if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->vpi == vpi &&
walk->vci == vci && ((walk->qos.txtp.traffic_class !=
ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) ||
(walk->qos.rxtp.traffic_class != ATM_NONE &&
vcc->qos.rxtp.traffic_class != ATM_NONE)))
return -EADDRINUSE;
}
/* allow VCCs with same VPI/VCI iff they don't collide on
TX/RX (but we may refuse such sharing for other reasons,
e.g. if protocol requires to have both channels) */
return 0;
}
 
 
int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci)
{
static short p = 0; /* poor man's per-device cache */
static int c = 0;
short old_p;
int old_c;
int err;
 
read_lock(&vcc_sklist_lock);
if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) {
err = check_ci(vcc,*vpi,*vci);
read_unlock(&vcc_sklist_lock);
return err;
}
/* last scan may have left values out of bounds for current device */
if (*vpi != ATM_VPI_ANY) p = *vpi;
else if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
if (*vci != ATM_VCI_ANY) c = *vci;
else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits)
c = ATM_NOT_RSV_VCI;
old_p = p;
old_c = c;
do {
if (!check_ci(vcc,p,c)) {
*vpi = p;
*vci = c;
read_unlock(&vcc_sklist_lock);
return 0;
}
if (*vci == ATM_VCI_ANY) {
c++;
if (c >= 1 << vcc->dev->ci_range.vci_bits)
c = ATM_NOT_RSV_VCI;
}
if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) &&
*vpi == ATM_VPI_ANY) {
p++;
if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
}
}
while (old_p != p || old_c != c);
read_unlock(&vcc_sklist_lock);
return -EADDRINUSE;
}
 
 
/*
* atm_pcr_goal returns the positive PCR if it should be rounded up, the
* negative PCR if it should be rounded down, and zero if the maximum available
* bandwidth should be used.
*
* The rules are as follows (* = maximum, - = absent (0), x = value "x",
* (x+ = x or next value above x, x- = x or next value below):
*
* min max pcr result min max pcr result
* - - - * (UBR only) x - - x+
* - - * * x - * *
* - - z z- x - z z-
* - * - * x * - x+
* - * * * x * * *
* - * z z- x * z z-
* - y - y- x y - x+
* - y * y- x y * y-
* - y z z- x y z z-
*
* All non-error cases can be converted with the following simple set of rules:
*
* if pcr == z then z-
* else if min == x && pcr == - then x+
* else if max == y then y-
* else *
*/
 
 
int atm_pcr_goal(struct atm_trafprm *tp)
{
if (tp->pcr && tp->pcr != ATM_MAX_PCR) return -tp->pcr;
if (tp->min_pcr && !tp->pcr) return tp->min_pcr;
if (tp->max_pcr != ATM_MAX_PCR) return -tp->max_pcr;
return 0;
}
 
 
void sonet_copy_stats(struct k_sonet_stats *from,struct sonet_stats *to)
{
#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
__SONET_ITEMS
#undef __HANDLE_ITEM
}
 
 
void sonet_subtract_stats(struct k_sonet_stats *from,struct sonet_stats *to)
{
#define __HANDLE_ITEM(i) atomic_sub(to->i,&from->i)
__SONET_ITEMS
#undef __HANDLE_ITEM
}
 
 
EXPORT_SYMBOL(atm_charge);
EXPORT_SYMBOL(atm_alloc_charge);
EXPORT_SYMBOL(atm_find_ci);
EXPORT_SYMBOL(atm_pcr_goal);
EXPORT_SYMBOL(sonet_copy_stats);
EXPORT_SYMBOL(sonet_subtract_stats);
/Makefile
0,0 → 1,53
#
# Makefile for the ATM Protocol Families.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now in the main makefile...
 
O_TARGET := atm.o
 
export-objs := common.o atm_misc.o raw.o resources.o ipcommon.o proc.o
 
list-multi := mpoa.o
mpoa-objs := mpc.o mpoa_caches.o mpoa_proc.o
 
obj-y := addr.o pvc.o signaling.o svc.o common.o atm_misc.o raw.o resources.o
ifeq ($(CONFIG_ATM),m)
obj-m += $(O_TARGET)
endif
 
ifneq ($(CONFIG_ATM_CLIP),n)
NEED_IPCOM = ipcommon.o
endif
obj-$(CONFIG_ATM_CLIP) += clip.o
 
ifeq ($(CONFIG_ATM_BR2684),y)
NEED_IPCOM = ipcommon.o
else
ifeq ($(CONFIG_ATM_BR2684),m)
NEED_IPCOM = ipcommon.o
endif
endif
obj-$(CONFIG_ATM_BR2684) += br2684.o
 
ifeq ($(CONFIG_NET_SCH_ATM),y)
NEED_IPCOM = ipcommon.o
endif
 
obj-y += $(NEED_IPCOM)
 
ifeq ($(CONFIG_PROC_FS),y)
obj-y += proc.o
endif
 
obj-$(CONFIG_ATM_LANE) += lec.o
obj-$(CONFIG_ATM_MPOA) += mpoa.o
obj-$(CONFIG_PPPOATM) += pppoatm.o
 
include $(TOPDIR)/Rules.make
 
mpoa.o: $(mpoa-objs)
$(LD) -r -o mpoa.o $(mpoa-objs)
/common.h
0,0 → 1,48
/* net/atm/common.h - ATM sockets (common part for PVC and SVC) */
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
 
 
#ifndef NET_ATM_COMMON_H
#define NET_ATM_COMMON_H
 
#include <linux/net.h>
#include <linux/poll.h> /* for poll_table */
 
 
int vcc_create(struct socket *sock, int protocol, int family);
int vcc_release(struct socket *sock);
int vcc_connect(struct socket *sock, int itf, short vpi, int vci);
int vcc_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags, struct scm_cookie *scm);
int vcc_sendmsg(struct socket *sock, struct msghdr *m, int total_len,
struct scm_cookie *scm);
unsigned int atm_poll(struct file *file,struct socket *sock,poll_table *wait);
int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
int vcc_setsockopt(struct socket *sock, int level, int optname, char *optval,
int optlen);
int vcc_getsockopt(struct socket *sock, int level, int optname, char *optval,
int *optlen);
 
void atm_shutdown_dev(struct atm_dev *dev);
 
void pppoatm_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long));
void br2684_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long));
 
int atmpvc_init(void);
void atmpvc_exit(void);
int atmsvc_init(void);
void atmsvc_exit(void);
int atm_proc_init(void);
void atm_proc_exit(void);
 
/* SVC */
 
void svc_callback(struct atm_vcc *vcc);
int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos);
 
/* p2mp */
 
int create_leaf(struct socket *leaf,struct socket *session);
 
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.