162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * lec.c: Lan Emulation driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Marko Kiiskila <mkiiskila@yahoo.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/bitops.h> 1362306a36Sopenharmony_ci#include <linux/capability.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* We are ethernet device */ 1662306a36Sopenharmony_ci#include <linux/if_ether.h> 1762306a36Sopenharmony_ci#include <linux/netdevice.h> 1862306a36Sopenharmony_ci#include <linux/etherdevice.h> 1962306a36Sopenharmony_ci#include <net/sock.h> 2062306a36Sopenharmony_ci#include <linux/skbuff.h> 2162306a36Sopenharmony_ci#include <linux/ip.h> 2262306a36Sopenharmony_ci#include <asm/byteorder.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <net/arp.h> 2562306a36Sopenharmony_ci#include <net/dst.h> 2662306a36Sopenharmony_ci#include <linux/proc_fs.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/seq_file.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* And atm device */ 3162306a36Sopenharmony_ci#include <linux/atmdev.h> 3262306a36Sopenharmony_ci#include <linux/atmlec.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Proxy LEC knows about bridging */ 3562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_BRIDGE) 3662306a36Sopenharmony_ci#include "../bridge/br_private.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic unsigned char bridge_ula_lec[] = { 0x01, 0x80, 0xc2, 0x00, 0x00 }; 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Modular too */ 4262306a36Sopenharmony_ci#include <linux/module.h> 4362306a36Sopenharmony_ci#include <linux/init.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* Hardening for Spectre-v1 */ 4662306a36Sopenharmony_ci#include <linux/nospec.h> 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include "lec.h" 4962306a36Sopenharmony_ci#include "lec_arpc.h" 5062306a36Sopenharmony_ci#include "resources.h" 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define DUMP_PACKETS 0 /* 5362306a36Sopenharmony_ci * 0 = None, 5462306a36Sopenharmony_ci * 1 = 30 first bytes 5562306a36Sopenharmony_ci * 2 = Whole packet 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define LEC_UNRES_QUE_LEN 8 /* 5962306a36Sopenharmony_ci * number of tx packets to queue for a 6062306a36Sopenharmony_ci * single destination while waiting for SVC 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int lec_open(struct net_device *dev); 6462306a36Sopenharmony_cistatic netdev_tx_t lec_start_xmit(struct sk_buff *skb, 6562306a36Sopenharmony_ci struct net_device *dev); 6662306a36Sopenharmony_cistatic int lec_close(struct net_device *dev); 6762306a36Sopenharmony_cistatic struct lec_arp_table *lec_arp_find(struct lec_priv *priv, 6862306a36Sopenharmony_ci const unsigned char *mac_addr); 6962306a36Sopenharmony_cistatic int lec_arp_remove(struct lec_priv *priv, 7062306a36Sopenharmony_ci struct lec_arp_table *to_remove); 7162306a36Sopenharmony_ci/* LANE2 functions */ 7262306a36Sopenharmony_cistatic void lane2_associate_ind(struct net_device *dev, const u8 *mac_address, 7362306a36Sopenharmony_ci const u8 *tlvs, u32 sizeoftlvs); 7462306a36Sopenharmony_cistatic int lane2_resolve(struct net_device *dev, const u8 *dst_mac, int force, 7562306a36Sopenharmony_ci u8 **tlvs, u32 *sizeoftlvs); 7662306a36Sopenharmony_cistatic int lane2_associate_req(struct net_device *dev, const u8 *lan_dst, 7762306a36Sopenharmony_ci const u8 *tlvs, u32 sizeoftlvs); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int lec_addr_delete(struct lec_priv *priv, const unsigned char *atm_addr, 8062306a36Sopenharmony_ci unsigned long permanent); 8162306a36Sopenharmony_cistatic void lec_arp_check_empties(struct lec_priv *priv, 8262306a36Sopenharmony_ci struct atm_vcc *vcc, struct sk_buff *skb); 8362306a36Sopenharmony_cistatic void lec_arp_destroy(struct lec_priv *priv); 8462306a36Sopenharmony_cistatic void lec_arp_init(struct lec_priv *priv); 8562306a36Sopenharmony_cistatic struct atm_vcc *lec_arp_resolve(struct lec_priv *priv, 8662306a36Sopenharmony_ci const unsigned char *mac_to_find, 8762306a36Sopenharmony_ci int is_rdesc, 8862306a36Sopenharmony_ci struct lec_arp_table **ret_entry); 8962306a36Sopenharmony_cistatic void lec_arp_update(struct lec_priv *priv, const unsigned char *mac_addr, 9062306a36Sopenharmony_ci const unsigned char *atm_addr, 9162306a36Sopenharmony_ci unsigned long remoteflag, 9262306a36Sopenharmony_ci unsigned int targetless_le_arp); 9362306a36Sopenharmony_cistatic void lec_flush_complete(struct lec_priv *priv, unsigned long tran_id); 9462306a36Sopenharmony_cistatic int lec_mcast_make(struct lec_priv *priv, struct atm_vcc *vcc); 9562306a36Sopenharmony_cistatic void lec_set_flush_tran_id(struct lec_priv *priv, 9662306a36Sopenharmony_ci const unsigned char *atm_addr, 9762306a36Sopenharmony_ci unsigned long tran_id); 9862306a36Sopenharmony_cistatic void lec_vcc_added(struct lec_priv *priv, 9962306a36Sopenharmony_ci const struct atmlec_ioc *ioc_data, 10062306a36Sopenharmony_ci struct atm_vcc *vcc, 10162306a36Sopenharmony_ci void (*old_push)(struct atm_vcc *vcc, 10262306a36Sopenharmony_ci struct sk_buff *skb)); 10362306a36Sopenharmony_cistatic void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* must be done under lec_arp_lock */ 10662306a36Sopenharmony_cistatic inline void lec_arp_hold(struct lec_arp_table *entry) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci refcount_inc(&entry->usage); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic inline void lec_arp_put(struct lec_arp_table *entry) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci if (refcount_dec_and_test(&entry->usage)) 11462306a36Sopenharmony_ci kfree(entry); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic struct lane2_ops lane2_ops = { 11862306a36Sopenharmony_ci .resolve = lane2_resolve, /* spec 3.1.3 */ 11962306a36Sopenharmony_ci .associate_req = lane2_associate_req, /* spec 3.1.4 */ 12062306a36Sopenharmony_ci .associate_indicator = NULL /* spec 3.1.5 */ 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic unsigned char bus_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* Device structures */ 12662306a36Sopenharmony_cistatic struct net_device *dev_lec[MAX_LEC_ITF]; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_BRIDGE) 12962306a36Sopenharmony_cistatic void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci char *buff; 13262306a36Sopenharmony_ci struct lec_priv *priv; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* 13562306a36Sopenharmony_ci * Check if this is a BPDU. If so, ask zeppelin to send 13662306a36Sopenharmony_ci * LE_TOPOLOGY_REQUEST with the same value of Topology Change bit 13762306a36Sopenharmony_ci * as the Config BPDU has 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci buff = skb->data + skb->dev->hard_header_len; 14062306a36Sopenharmony_ci if (*buff++ == 0x42 && *buff++ == 0x42 && *buff++ == 0x03) { 14162306a36Sopenharmony_ci struct sock *sk; 14262306a36Sopenharmony_ci struct sk_buff *skb2; 14362306a36Sopenharmony_ci struct atmlec_msg *mesg; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); 14662306a36Sopenharmony_ci if (skb2 == NULL) 14762306a36Sopenharmony_ci return; 14862306a36Sopenharmony_ci skb2->len = sizeof(struct atmlec_msg); 14962306a36Sopenharmony_ci mesg = (struct atmlec_msg *)skb2->data; 15062306a36Sopenharmony_ci mesg->type = l_topology_change; 15162306a36Sopenharmony_ci buff += 4; 15262306a36Sopenharmony_ci mesg->content.normal.flag = *buff & 0x01; 15362306a36Sopenharmony_ci /* 0x01 is topology change */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci priv = netdev_priv(dev); 15662306a36Sopenharmony_ci atm_force_charge(priv->lecd, skb2->truesize); 15762306a36Sopenharmony_ci sk = sk_atm(priv->lecd); 15862306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb2); 15962306a36Sopenharmony_ci sk->sk_data_ready(sk); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_BRIDGE) */ 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * Open/initialize the netdevice. This is called (in the current kernel) 16662306a36Sopenharmony_ci * sometime after booting when the 'ifconfig' program is run. 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * This routine should set everything up anew at each open, even 16962306a36Sopenharmony_ci * registers that "should" only need to be set once at boot, so that 17062306a36Sopenharmony_ci * there is non-reboot way to recover if something goes wrong. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int lec_open(struct net_device *dev) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci netif_start_queue(dev); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void 18162306a36Sopenharmony_cilec_send(struct atm_vcc *vcc, struct sk_buff *skb) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct net_device *dev = skb->dev; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ATM_SKB(skb)->vcc = vcc; 18662306a36Sopenharmony_ci atm_account_tx(vcc, skb); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (vcc->send(vcc, skb) < 0) { 18962306a36Sopenharmony_ci dev->stats.tx_dropped++; 19062306a36Sopenharmony_ci return; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci dev->stats.tx_packets++; 19462306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void lec_tx_timeout(struct net_device *dev, unsigned int txqueue) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci pr_info("%s\n", dev->name); 20062306a36Sopenharmony_ci netif_trans_update(dev); 20162306a36Sopenharmony_ci netif_wake_queue(dev); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic netdev_tx_t lec_start_xmit(struct sk_buff *skb, 20562306a36Sopenharmony_ci struct net_device *dev) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct sk_buff *skb2; 20862306a36Sopenharmony_ci struct lec_priv *priv = netdev_priv(dev); 20962306a36Sopenharmony_ci struct lecdatahdr_8023 *lec_h; 21062306a36Sopenharmony_ci struct atm_vcc *vcc; 21162306a36Sopenharmony_ci struct lec_arp_table *entry; 21262306a36Sopenharmony_ci unsigned char *dst; 21362306a36Sopenharmony_ci int min_frame_size; 21462306a36Sopenharmony_ci int is_rdesc; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci pr_debug("called\n"); 21762306a36Sopenharmony_ci if (!priv->lecd) { 21862306a36Sopenharmony_ci pr_info("%s:No lecd attached\n", dev->name); 21962306a36Sopenharmony_ci dev->stats.tx_errors++; 22062306a36Sopenharmony_ci netif_stop_queue(dev); 22162306a36Sopenharmony_ci kfree_skb(skb); 22262306a36Sopenharmony_ci return NETDEV_TX_OK; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci pr_debug("skbuff head:%lx data:%lx tail:%lx end:%lx\n", 22662306a36Sopenharmony_ci (long)skb->head, (long)skb->data, (long)skb_tail_pointer(skb), 22762306a36Sopenharmony_ci (long)skb_end_pointer(skb)); 22862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_BRIDGE) 22962306a36Sopenharmony_ci if (memcmp(skb->data, bridge_ula_lec, sizeof(bridge_ula_lec)) == 0) 23062306a36Sopenharmony_ci lec_handle_bridge(skb, dev); 23162306a36Sopenharmony_ci#endif 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Make sure we have room for lec_id */ 23462306a36Sopenharmony_ci if (skb_headroom(skb) < 2) { 23562306a36Sopenharmony_ci pr_debug("reallocating skb\n"); 23662306a36Sopenharmony_ci skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN); 23762306a36Sopenharmony_ci if (unlikely(!skb2)) { 23862306a36Sopenharmony_ci kfree_skb(skb); 23962306a36Sopenharmony_ci return NETDEV_TX_OK; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci consume_skb(skb); 24262306a36Sopenharmony_ci skb = skb2; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci skb_push(skb, 2); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Put le header to place */ 24762306a36Sopenharmony_ci lec_h = (struct lecdatahdr_8023 *)skb->data; 24862306a36Sopenharmony_ci lec_h->le_header = htons(priv->lecid); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci#if DUMP_PACKETS >= 2 25162306a36Sopenharmony_ci#define MAX_DUMP_SKB 99 25262306a36Sopenharmony_ci#elif DUMP_PACKETS >= 1 25362306a36Sopenharmony_ci#define MAX_DUMP_SKB 30 25462306a36Sopenharmony_ci#endif 25562306a36Sopenharmony_ci#if DUMP_PACKETS >= 1 25662306a36Sopenharmony_ci printk(KERN_DEBUG "%s: send datalen:%ld lecid:%4.4x\n", 25762306a36Sopenharmony_ci dev->name, skb->len, priv->lecid); 25862306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, "", DUMP_OFFSET, 16, 1, 25962306a36Sopenharmony_ci skb->data, min(skb->len, MAX_DUMP_SKB), true); 26062306a36Sopenharmony_ci#endif /* DUMP_PACKETS >= 1 */ 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Minimum ethernet-frame size */ 26362306a36Sopenharmony_ci min_frame_size = LEC_MINIMUM_8023_SIZE; 26462306a36Sopenharmony_ci if (skb->len < min_frame_size) { 26562306a36Sopenharmony_ci if ((skb->len + skb_tailroom(skb)) < min_frame_size) { 26662306a36Sopenharmony_ci skb2 = skb_copy_expand(skb, 0, 26762306a36Sopenharmony_ci min_frame_size - skb->truesize, 26862306a36Sopenharmony_ci GFP_ATOMIC); 26962306a36Sopenharmony_ci dev_kfree_skb(skb); 27062306a36Sopenharmony_ci if (skb2 == NULL) { 27162306a36Sopenharmony_ci dev->stats.tx_dropped++; 27262306a36Sopenharmony_ci return NETDEV_TX_OK; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci skb = skb2; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci skb_put(skb, min_frame_size - skb->len); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Send to right vcc */ 28062306a36Sopenharmony_ci is_rdesc = 0; 28162306a36Sopenharmony_ci dst = lec_h->h_dest; 28262306a36Sopenharmony_ci entry = NULL; 28362306a36Sopenharmony_ci vcc = lec_arp_resolve(priv, dst, is_rdesc, &entry); 28462306a36Sopenharmony_ci pr_debug("%s:vcc:%p vcc_flags:%lx, entry:%p\n", 28562306a36Sopenharmony_ci dev->name, vcc, vcc ? vcc->flags : 0, entry); 28662306a36Sopenharmony_ci if (!vcc || !test_bit(ATM_VF_READY, &vcc->flags)) { 28762306a36Sopenharmony_ci if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) { 28862306a36Sopenharmony_ci pr_debug("%s:queuing packet, MAC address %pM\n", 28962306a36Sopenharmony_ci dev->name, lec_h->h_dest); 29062306a36Sopenharmony_ci skb_queue_tail(&entry->tx_wait, skb); 29162306a36Sopenharmony_ci } else { 29262306a36Sopenharmony_ci pr_debug("%s:tx queue full or no arp entry, dropping, MAC address: %pM\n", 29362306a36Sopenharmony_ci dev->name, lec_h->h_dest); 29462306a36Sopenharmony_ci dev->stats.tx_dropped++; 29562306a36Sopenharmony_ci dev_kfree_skb(skb); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci goto out; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci#if DUMP_PACKETS > 0 30062306a36Sopenharmony_ci printk(KERN_DEBUG "%s:sending to vpi:%d vci:%d\n", 30162306a36Sopenharmony_ci dev->name, vcc->vpi, vcc->vci); 30262306a36Sopenharmony_ci#endif /* DUMP_PACKETS > 0 */ 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) { 30562306a36Sopenharmony_ci pr_debug("emptying tx queue, MAC address %pM\n", lec_h->h_dest); 30662306a36Sopenharmony_ci lec_send(vcc, skb2); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci lec_send(vcc, skb); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!atm_may_send(vcc, 0)) { 31262306a36Sopenharmony_ci struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci vpriv->xoff = 1; 31562306a36Sopenharmony_ci netif_stop_queue(dev); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * vcc->pop() might have occurred in between, making 31962306a36Sopenharmony_ci * the vcc usuable again. Since xmit is serialized, 32062306a36Sopenharmony_ci * this is the only situation we have to re-test. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (atm_may_send(vcc, 0)) 32462306a36Sopenharmony_ci netif_wake_queue(dev); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciout: 32862306a36Sopenharmony_ci if (entry) 32962306a36Sopenharmony_ci lec_arp_put(entry); 33062306a36Sopenharmony_ci netif_trans_update(dev); 33162306a36Sopenharmony_ci return NETDEV_TX_OK; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/* The inverse routine to net_open(). */ 33562306a36Sopenharmony_cistatic int lec_close(struct net_device *dev) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci netif_stop_queue(dev); 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci static const u8 zero_addr[ETH_ALEN] = {}; 34462306a36Sopenharmony_ci unsigned long flags; 34562306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)vcc->proto_data; 34662306a36Sopenharmony_ci struct lec_priv *priv = netdev_priv(dev); 34762306a36Sopenharmony_ci struct atmlec_msg *mesg; 34862306a36Sopenharmony_ci struct lec_arp_table *entry; 34962306a36Sopenharmony_ci char *tmp; /* FIXME */ 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc)); 35262306a36Sopenharmony_ci mesg = (struct atmlec_msg *)skb->data; 35362306a36Sopenharmony_ci tmp = skb->data; 35462306a36Sopenharmony_ci tmp += sizeof(struct atmlec_msg); 35562306a36Sopenharmony_ci pr_debug("%s: msg from zeppelin:%d\n", dev->name, mesg->type); 35662306a36Sopenharmony_ci switch (mesg->type) { 35762306a36Sopenharmony_ci case l_set_mac_addr: 35862306a36Sopenharmony_ci eth_hw_addr_set(dev, mesg->content.normal.mac_addr); 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case l_del_mac_addr: 36162306a36Sopenharmony_ci eth_hw_addr_set(dev, zero_addr); 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci case l_addr_delete: 36462306a36Sopenharmony_ci lec_addr_delete(priv, mesg->content.normal.atm_addr, 36562306a36Sopenharmony_ci mesg->content.normal.flag); 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci case l_topology_change: 36862306a36Sopenharmony_ci priv->topology_change = mesg->content.normal.flag; 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci case l_flush_complete: 37162306a36Sopenharmony_ci lec_flush_complete(priv, mesg->content.normal.flag); 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci case l_narp_req: /* LANE2: see 7.1.35 in the lane2 spec */ 37462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 37562306a36Sopenharmony_ci entry = lec_arp_find(priv, mesg->content.normal.mac_addr); 37662306a36Sopenharmony_ci lec_arp_remove(priv, entry); 37762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (mesg->content.normal.no_source_le_narp) 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci fallthrough; 38262306a36Sopenharmony_ci case l_arp_update: 38362306a36Sopenharmony_ci lec_arp_update(priv, mesg->content.normal.mac_addr, 38462306a36Sopenharmony_ci mesg->content.normal.atm_addr, 38562306a36Sopenharmony_ci mesg->content.normal.flag, 38662306a36Sopenharmony_ci mesg->content.normal.targetless_le_arp); 38762306a36Sopenharmony_ci pr_debug("in l_arp_update\n"); 38862306a36Sopenharmony_ci if (mesg->sizeoftlvs != 0) { /* LANE2 3.1.5 */ 38962306a36Sopenharmony_ci pr_debug("LANE2 3.1.5, got tlvs, size %d\n", 39062306a36Sopenharmony_ci mesg->sizeoftlvs); 39162306a36Sopenharmony_ci lane2_associate_ind(dev, mesg->content.normal.mac_addr, 39262306a36Sopenharmony_ci tmp, mesg->sizeoftlvs); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case l_config: 39662306a36Sopenharmony_ci priv->maximum_unknown_frame_count = 39762306a36Sopenharmony_ci mesg->content.config.maximum_unknown_frame_count; 39862306a36Sopenharmony_ci priv->max_unknown_frame_time = 39962306a36Sopenharmony_ci (mesg->content.config.max_unknown_frame_time * HZ); 40062306a36Sopenharmony_ci priv->max_retry_count = mesg->content.config.max_retry_count; 40162306a36Sopenharmony_ci priv->aging_time = (mesg->content.config.aging_time * HZ); 40262306a36Sopenharmony_ci priv->forward_delay_time = 40362306a36Sopenharmony_ci (mesg->content.config.forward_delay_time * HZ); 40462306a36Sopenharmony_ci priv->arp_response_time = 40562306a36Sopenharmony_ci (mesg->content.config.arp_response_time * HZ); 40662306a36Sopenharmony_ci priv->flush_timeout = (mesg->content.config.flush_timeout * HZ); 40762306a36Sopenharmony_ci priv->path_switching_delay = 40862306a36Sopenharmony_ci (mesg->content.config.path_switching_delay * HZ); 40962306a36Sopenharmony_ci priv->lane_version = mesg->content.config.lane_version; 41062306a36Sopenharmony_ci /* LANE2 */ 41162306a36Sopenharmony_ci priv->lane2_ops = NULL; 41262306a36Sopenharmony_ci if (priv->lane_version > 1) 41362306a36Sopenharmony_ci priv->lane2_ops = &lane2_ops; 41462306a36Sopenharmony_ci rtnl_lock(); 41562306a36Sopenharmony_ci if (dev_set_mtu(dev, mesg->content.config.mtu)) 41662306a36Sopenharmony_ci pr_info("%s: change_mtu to %d failed\n", 41762306a36Sopenharmony_ci dev->name, mesg->content.config.mtu); 41862306a36Sopenharmony_ci rtnl_unlock(); 41962306a36Sopenharmony_ci priv->is_proxy = mesg->content.config.is_proxy; 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case l_flush_tran_id: 42262306a36Sopenharmony_ci lec_set_flush_tran_id(priv, mesg->content.normal.atm_addr, 42362306a36Sopenharmony_ci mesg->content.normal.flag); 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case l_set_lecid: 42662306a36Sopenharmony_ci priv->lecid = 42762306a36Sopenharmony_ci (unsigned short)(0xffff & mesg->content.normal.flag); 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci case l_should_bridge: 43062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_BRIDGE) 43162306a36Sopenharmony_ci { 43262306a36Sopenharmony_ci pr_debug("%s: bridge zeppelin asks about %pM\n", 43362306a36Sopenharmony_ci dev->name, mesg->content.proxy.mac_addr); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (br_fdb_test_addr_hook == NULL) 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (br_fdb_test_addr_hook(dev, mesg->content.proxy.mac_addr)) { 43962306a36Sopenharmony_ci /* hit from bridge table, send LE_ARP_RESPONSE */ 44062306a36Sopenharmony_ci struct sk_buff *skb2; 44162306a36Sopenharmony_ci struct sock *sk; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci pr_debug("%s: entry found, responding to zeppelin\n", 44462306a36Sopenharmony_ci dev->name); 44562306a36Sopenharmony_ci skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); 44662306a36Sopenharmony_ci if (skb2 == NULL) 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci skb2->len = sizeof(struct atmlec_msg); 44962306a36Sopenharmony_ci skb_copy_to_linear_data(skb2, mesg, sizeof(*mesg)); 45062306a36Sopenharmony_ci atm_force_charge(priv->lecd, skb2->truesize); 45162306a36Sopenharmony_ci sk = sk_atm(priv->lecd); 45262306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb2); 45362306a36Sopenharmony_ci sk->sk_data_ready(sk); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_BRIDGE) */ 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci default: 45962306a36Sopenharmony_ci pr_info("%s: Unknown message type %d\n", dev->name, mesg->type); 46062306a36Sopenharmony_ci dev_kfree_skb(skb); 46162306a36Sopenharmony_ci return -EINVAL; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci dev_kfree_skb(skb); 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void lec_atm_close(struct atm_vcc *vcc) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct sk_buff *skb; 47062306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)vcc->proto_data; 47162306a36Sopenharmony_ci struct lec_priv *priv = netdev_priv(dev); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci priv->lecd = NULL; 47462306a36Sopenharmony_ci /* Do something needful? */ 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci netif_stop_queue(dev); 47762306a36Sopenharmony_ci lec_arp_destroy(priv); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (skb_peek(&sk_atm(vcc)->sk_receive_queue)) 48062306a36Sopenharmony_ci pr_info("%s closing with messages pending\n", dev->name); 48162306a36Sopenharmony_ci while ((skb = skb_dequeue(&sk_atm(vcc)->sk_receive_queue))) { 48262306a36Sopenharmony_ci atm_return(vcc, skb->truesize); 48362306a36Sopenharmony_ci dev_kfree_skb(skb); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci pr_info("%s: Shut down!\n", dev->name); 48762306a36Sopenharmony_ci module_put(THIS_MODULE); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic const struct atmdev_ops lecdev_ops = { 49162306a36Sopenharmony_ci .close = lec_atm_close, 49262306a36Sopenharmony_ci .send = lec_atm_send 49362306a36Sopenharmony_ci}; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic struct atm_dev lecatm_dev = { 49662306a36Sopenharmony_ci .ops = &lecdev_ops, 49762306a36Sopenharmony_ci .type = "lec", 49862306a36Sopenharmony_ci .number = 999, /* dummy device number */ 49962306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(lecatm_dev.lock) 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* 50362306a36Sopenharmony_ci * LANE2: new argument struct sk_buff *data contains 50462306a36Sopenharmony_ci * the LE_ARP based TLVs introduced in the LANE2 spec 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_cistatic int 50762306a36Sopenharmony_cisend_to_lecd(struct lec_priv *priv, atmlec_msg_type type, 50862306a36Sopenharmony_ci const unsigned char *mac_addr, const unsigned char *atm_addr, 50962306a36Sopenharmony_ci struct sk_buff *data) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct sock *sk; 51262306a36Sopenharmony_ci struct sk_buff *skb; 51362306a36Sopenharmony_ci struct atmlec_msg *mesg; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (!priv || !priv->lecd) 51662306a36Sopenharmony_ci return -1; 51762306a36Sopenharmony_ci skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); 51862306a36Sopenharmony_ci if (!skb) 51962306a36Sopenharmony_ci return -1; 52062306a36Sopenharmony_ci skb->len = sizeof(struct atmlec_msg); 52162306a36Sopenharmony_ci mesg = (struct atmlec_msg *)skb->data; 52262306a36Sopenharmony_ci memset(mesg, 0, sizeof(struct atmlec_msg)); 52362306a36Sopenharmony_ci mesg->type = type; 52462306a36Sopenharmony_ci if (data != NULL) 52562306a36Sopenharmony_ci mesg->sizeoftlvs = data->len; 52662306a36Sopenharmony_ci if (mac_addr) 52762306a36Sopenharmony_ci ether_addr_copy(mesg->content.normal.mac_addr, mac_addr); 52862306a36Sopenharmony_ci else 52962306a36Sopenharmony_ci mesg->content.normal.targetless_le_arp = 1; 53062306a36Sopenharmony_ci if (atm_addr) 53162306a36Sopenharmony_ci memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci atm_force_charge(priv->lecd, skb->truesize); 53462306a36Sopenharmony_ci sk = sk_atm(priv->lecd); 53562306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 53662306a36Sopenharmony_ci sk->sk_data_ready(sk); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (data != NULL) { 53962306a36Sopenharmony_ci pr_debug("about to send %d bytes of data\n", data->len); 54062306a36Sopenharmony_ci atm_force_charge(priv->lecd, data->truesize); 54162306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, data); 54262306a36Sopenharmony_ci sk->sk_data_ready(sk); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void lec_set_multicast_list(struct net_device *dev) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * by default, all multicast frames arrive over the bus. 55262306a36Sopenharmony_ci * eventually support selective multicast service 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic const struct net_device_ops lec_netdev_ops = { 55762306a36Sopenharmony_ci .ndo_open = lec_open, 55862306a36Sopenharmony_ci .ndo_stop = lec_close, 55962306a36Sopenharmony_ci .ndo_start_xmit = lec_start_xmit, 56062306a36Sopenharmony_ci .ndo_tx_timeout = lec_tx_timeout, 56162306a36Sopenharmony_ci .ndo_set_rx_mode = lec_set_multicast_list, 56262306a36Sopenharmony_ci}; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic const unsigned char lec_ctrl_magic[] = { 56562306a36Sopenharmony_ci 0xff, 56662306a36Sopenharmony_ci 0x00, 56762306a36Sopenharmony_ci 0x01, 56862306a36Sopenharmony_ci 0x01 56962306a36Sopenharmony_ci}; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci#define LEC_DATA_DIRECT_8023 2 57262306a36Sopenharmony_ci#define LEC_DATA_DIRECT_8025 3 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int lec_is_data_direct(struct atm_vcc *vcc) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci return ((vcc->sap.blli[0].l3.tr9577.snap[4] == LEC_DATA_DIRECT_8023) || 57762306a36Sopenharmony_ci (vcc->sap.blli[0].l3.tr9577.snap[4] == LEC_DATA_DIRECT_8025)); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic void lec_push(struct atm_vcc *vcc, struct sk_buff *skb) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci unsigned long flags; 58362306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)vcc->proto_data; 58462306a36Sopenharmony_ci struct lec_priv *priv = netdev_priv(dev); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci#if DUMP_PACKETS > 0 58762306a36Sopenharmony_ci printk(KERN_DEBUG "%s: vcc vpi:%d vci:%d\n", 58862306a36Sopenharmony_ci dev->name, vcc->vpi, vcc->vci); 58962306a36Sopenharmony_ci#endif 59062306a36Sopenharmony_ci if (!skb) { 59162306a36Sopenharmony_ci pr_debug("%s: null skb\n", dev->name); 59262306a36Sopenharmony_ci lec_vcc_close(priv, vcc); 59362306a36Sopenharmony_ci return; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci#if DUMP_PACKETS >= 2 59662306a36Sopenharmony_ci#define MAX_SKB_DUMP 99 59762306a36Sopenharmony_ci#elif DUMP_PACKETS >= 1 59862306a36Sopenharmony_ci#define MAX_SKB_DUMP 30 59962306a36Sopenharmony_ci#endif 60062306a36Sopenharmony_ci#if DUMP_PACKETS > 0 60162306a36Sopenharmony_ci printk(KERN_DEBUG "%s: rcv datalen:%ld lecid:%4.4x\n", 60262306a36Sopenharmony_ci dev->name, skb->len, priv->lecid); 60362306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, "", DUMP_OFFSET, 16, 1, 60462306a36Sopenharmony_ci skb->data, min(MAX_SKB_DUMP, skb->len), true); 60562306a36Sopenharmony_ci#endif /* DUMP_PACKETS > 0 */ 60662306a36Sopenharmony_ci if (memcmp(skb->data, lec_ctrl_magic, 4) == 0) { 60762306a36Sopenharmony_ci /* Control frame, to daemon */ 60862306a36Sopenharmony_ci struct sock *sk = sk_atm(vcc); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci pr_debug("%s: To daemon\n", dev->name); 61162306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 61262306a36Sopenharmony_ci sk->sk_data_ready(sk); 61362306a36Sopenharmony_ci } else { /* Data frame, queue to protocol handlers */ 61462306a36Sopenharmony_ci struct lec_arp_table *entry; 61562306a36Sopenharmony_ci unsigned char *src, *dst; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci atm_return(vcc, skb->truesize); 61862306a36Sopenharmony_ci if (*(__be16 *) skb->data == htons(priv->lecid) || 61962306a36Sopenharmony_ci !priv->lecd || !(dev->flags & IFF_UP)) { 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * Probably looping back, or if lecd is missing, 62262306a36Sopenharmony_ci * lecd has gone down 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ci pr_debug("Ignoring frame...\n"); 62562306a36Sopenharmony_ci dev_kfree_skb(skb); 62662306a36Sopenharmony_ci return; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci dst = ((struct lecdatahdr_8023 *)skb->data)->h_dest; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* 63162306a36Sopenharmony_ci * If this is a Data Direct VCC, and the VCC does not match 63262306a36Sopenharmony_ci * the LE_ARP cache entry, delete the LE_ARP cache entry. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 63562306a36Sopenharmony_ci if (lec_is_data_direct(vcc)) { 63662306a36Sopenharmony_ci src = ((struct lecdatahdr_8023 *)skb->data)->h_source; 63762306a36Sopenharmony_ci entry = lec_arp_find(priv, src); 63862306a36Sopenharmony_ci if (entry && entry->vcc != vcc) { 63962306a36Sopenharmony_ci lec_arp_remove(priv, entry); 64062306a36Sopenharmony_ci lec_arp_put(entry); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!(dst[0] & 0x01) && /* Never filter Multi/Broadcast */ 64662306a36Sopenharmony_ci !priv->is_proxy && /* Proxy wants all the packets */ 64762306a36Sopenharmony_ci memcmp(dst, dev->dev_addr, dev->addr_len)) { 64862306a36Sopenharmony_ci dev_kfree_skb(skb); 64962306a36Sopenharmony_ci return; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci if (!hlist_empty(&priv->lec_arp_empty_ones)) 65262306a36Sopenharmony_ci lec_arp_check_empties(priv, vcc, skb); 65362306a36Sopenharmony_ci skb_pull(skb, 2); /* skip lec_id */ 65462306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 65562306a36Sopenharmony_ci dev->stats.rx_packets++; 65662306a36Sopenharmony_ci dev->stats.rx_bytes += skb->len; 65762306a36Sopenharmony_ci memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); 65862306a36Sopenharmony_ci netif_rx(skb); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic void lec_pop(struct atm_vcc *vcc, struct sk_buff *skb) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); 66562306a36Sopenharmony_ci struct net_device *dev = skb->dev; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (vpriv == NULL) { 66862306a36Sopenharmony_ci pr_info("vpriv = NULL!?!?!?\n"); 66962306a36Sopenharmony_ci return; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci vpriv->old_pop(vcc, skb); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (vpriv->xoff && atm_may_send(vcc, 0)) { 67562306a36Sopenharmony_ci vpriv->xoff = 0; 67662306a36Sopenharmony_ci if (netif_running(dev) && netif_queue_stopped(dev)) 67762306a36Sopenharmony_ci netif_wake_queue(dev); 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic int lec_vcc_attach(struct atm_vcc *vcc, void __user *arg) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct lec_vcc_priv *vpriv; 68462306a36Sopenharmony_ci int bytes_left; 68562306a36Sopenharmony_ci struct atmlec_ioc ioc_data; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Lecd must be up in this case */ 68862306a36Sopenharmony_ci bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmlec_ioc)); 68962306a36Sopenharmony_ci if (bytes_left != 0) 69062306a36Sopenharmony_ci pr_info("copy from user failed for %d bytes\n", bytes_left); 69162306a36Sopenharmony_ci if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF) 69262306a36Sopenharmony_ci return -EINVAL; 69362306a36Sopenharmony_ci ioc_data.dev_num = array_index_nospec(ioc_data.dev_num, MAX_LEC_ITF); 69462306a36Sopenharmony_ci if (!dev_lec[ioc_data.dev_num]) 69562306a36Sopenharmony_ci return -EINVAL; 69662306a36Sopenharmony_ci vpriv = kmalloc(sizeof(struct lec_vcc_priv), GFP_KERNEL); 69762306a36Sopenharmony_ci if (!vpriv) 69862306a36Sopenharmony_ci return -ENOMEM; 69962306a36Sopenharmony_ci vpriv->xoff = 0; 70062306a36Sopenharmony_ci vpriv->old_pop = vcc->pop; 70162306a36Sopenharmony_ci vcc->user_back = vpriv; 70262306a36Sopenharmony_ci vcc->pop = lec_pop; 70362306a36Sopenharmony_ci lec_vcc_added(netdev_priv(dev_lec[ioc_data.dev_num]), 70462306a36Sopenharmony_ci &ioc_data, vcc, vcc->push); 70562306a36Sopenharmony_ci vcc->proto_data = dev_lec[ioc_data.dev_num]; 70662306a36Sopenharmony_ci vcc->push = lec_push; 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int lec_mcast_attach(struct atm_vcc *vcc, int arg) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci if (arg < 0 || arg >= MAX_LEC_ITF) 71362306a36Sopenharmony_ci return -EINVAL; 71462306a36Sopenharmony_ci arg = array_index_nospec(arg, MAX_LEC_ITF); 71562306a36Sopenharmony_ci if (!dev_lec[arg]) 71662306a36Sopenharmony_ci return -EINVAL; 71762306a36Sopenharmony_ci vcc->proto_data = dev_lec[arg]; 71862306a36Sopenharmony_ci return lec_mcast_make(netdev_priv(dev_lec[arg]), vcc); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci/* Initialize device. */ 72262306a36Sopenharmony_cistatic int lecd_attach(struct atm_vcc *vcc, int arg) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci int i; 72562306a36Sopenharmony_ci struct lec_priv *priv; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (arg < 0) 72862306a36Sopenharmony_ci arg = 0; 72962306a36Sopenharmony_ci if (arg >= MAX_LEC_ITF) 73062306a36Sopenharmony_ci return -EINVAL; 73162306a36Sopenharmony_ci i = array_index_nospec(arg, MAX_LEC_ITF); 73262306a36Sopenharmony_ci if (!dev_lec[i]) { 73362306a36Sopenharmony_ci int size; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci size = sizeof(struct lec_priv); 73662306a36Sopenharmony_ci dev_lec[i] = alloc_etherdev(size); 73762306a36Sopenharmony_ci if (!dev_lec[i]) 73862306a36Sopenharmony_ci return -ENOMEM; 73962306a36Sopenharmony_ci dev_lec[i]->netdev_ops = &lec_netdev_ops; 74062306a36Sopenharmony_ci dev_lec[i]->max_mtu = 18190; 74162306a36Sopenharmony_ci snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i); 74262306a36Sopenharmony_ci if (register_netdev(dev_lec[i])) { 74362306a36Sopenharmony_ci free_netdev(dev_lec[i]); 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci priv = netdev_priv(dev_lec[i]); 74862306a36Sopenharmony_ci } else { 74962306a36Sopenharmony_ci priv = netdev_priv(dev_lec[i]); 75062306a36Sopenharmony_ci if (priv->lecd) 75162306a36Sopenharmony_ci return -EADDRINUSE; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci lec_arp_init(priv); 75462306a36Sopenharmony_ci priv->itfnum = i; /* LANE2 addition */ 75562306a36Sopenharmony_ci priv->lecd = vcc; 75662306a36Sopenharmony_ci vcc->dev = &lecatm_dev; 75762306a36Sopenharmony_ci vcc_insert_socket(sk_atm(vcc)); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci vcc->proto_data = dev_lec[i]; 76062306a36Sopenharmony_ci set_bit(ATM_VF_META, &vcc->flags); 76162306a36Sopenharmony_ci set_bit(ATM_VF_READY, &vcc->flags); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* Set default values to these variables */ 76462306a36Sopenharmony_ci priv->maximum_unknown_frame_count = 1; 76562306a36Sopenharmony_ci priv->max_unknown_frame_time = (1 * HZ); 76662306a36Sopenharmony_ci priv->vcc_timeout_period = (1200 * HZ); 76762306a36Sopenharmony_ci priv->max_retry_count = 1; 76862306a36Sopenharmony_ci priv->aging_time = (300 * HZ); 76962306a36Sopenharmony_ci priv->forward_delay_time = (15 * HZ); 77062306a36Sopenharmony_ci priv->topology_change = 0; 77162306a36Sopenharmony_ci priv->arp_response_time = (1 * HZ); 77262306a36Sopenharmony_ci priv->flush_timeout = (4 * HZ); 77362306a36Sopenharmony_ci priv->path_switching_delay = (6 * HZ); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (dev_lec[i]->flags & IFF_UP) 77662306a36Sopenharmony_ci netif_start_queue(dev_lec[i]); 77762306a36Sopenharmony_ci __module_get(THIS_MODULE); 77862306a36Sopenharmony_ci return i; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 78262306a36Sopenharmony_cistatic const char *lec_arp_get_status_string(unsigned char status) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci static const char *const lec_arp_status_string[] = { 78562306a36Sopenharmony_ci "ESI_UNKNOWN ", 78662306a36Sopenharmony_ci "ESI_ARP_PENDING ", 78762306a36Sopenharmony_ci "ESI_VC_PENDING ", 78862306a36Sopenharmony_ci "<Undefined> ", 78962306a36Sopenharmony_ci "ESI_FLUSH_PENDING ", 79062306a36Sopenharmony_ci "ESI_FORWARD_DIRECT" 79162306a36Sopenharmony_ci }; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (status > ESI_FORWARD_DIRECT) 79462306a36Sopenharmony_ci status = 3; /* ESI_UNDEFINED */ 79562306a36Sopenharmony_ci return lec_arp_status_string[status]; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic void lec_info(struct seq_file *seq, struct lec_arp_table *entry) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci seq_printf(seq, "%pM ", entry->mac_addr); 80162306a36Sopenharmony_ci seq_printf(seq, "%*phN ", ATM_ESA_LEN, entry->atm_addr); 80262306a36Sopenharmony_ci seq_printf(seq, "%s %4.4x", lec_arp_get_status_string(entry->status), 80362306a36Sopenharmony_ci entry->flags & 0xffff); 80462306a36Sopenharmony_ci if (entry->vcc) 80562306a36Sopenharmony_ci seq_printf(seq, "%3d %3d ", entry->vcc->vpi, entry->vcc->vci); 80662306a36Sopenharmony_ci else 80762306a36Sopenharmony_ci seq_printf(seq, " "); 80862306a36Sopenharmony_ci if (entry->recv_vcc) { 80962306a36Sopenharmony_ci seq_printf(seq, " %3d %3d", entry->recv_vcc->vpi, 81062306a36Sopenharmony_ci entry->recv_vcc->vci); 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci seq_putc(seq, '\n'); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistruct lec_state { 81662306a36Sopenharmony_ci unsigned long flags; 81762306a36Sopenharmony_ci struct lec_priv *locked; 81862306a36Sopenharmony_ci struct hlist_node *node; 81962306a36Sopenharmony_ci struct net_device *dev; 82062306a36Sopenharmony_ci int itf; 82162306a36Sopenharmony_ci int arp_table; 82262306a36Sopenharmony_ci int misc_table; 82362306a36Sopenharmony_ci}; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void *lec_tbl_walk(struct lec_state *state, struct hlist_head *tbl, 82662306a36Sopenharmony_ci loff_t *l) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct hlist_node *e = state->node; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (!e) 83162306a36Sopenharmony_ci e = tbl->first; 83262306a36Sopenharmony_ci if (e == SEQ_START_TOKEN) { 83362306a36Sopenharmony_ci e = tbl->first; 83462306a36Sopenharmony_ci --*l; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci for (; e; e = e->next) { 83862306a36Sopenharmony_ci if (--*l < 0) 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci state->node = e; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci return (*l < 0) ? state : NULL; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic void *lec_arp_walk(struct lec_state *state, loff_t *l, 84762306a36Sopenharmony_ci struct lec_priv *priv) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci void *v = NULL; 85062306a36Sopenharmony_ci int p; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci for (p = state->arp_table; p < LEC_ARP_TABLE_SIZE; p++) { 85362306a36Sopenharmony_ci v = lec_tbl_walk(state, &priv->lec_arp_tables[p], l); 85462306a36Sopenharmony_ci if (v) 85562306a36Sopenharmony_ci break; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci state->arp_table = p; 85862306a36Sopenharmony_ci return v; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic void *lec_misc_walk(struct lec_state *state, loff_t *l, 86262306a36Sopenharmony_ci struct lec_priv *priv) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct hlist_head *lec_misc_tables[] = { 86562306a36Sopenharmony_ci &priv->lec_arp_empty_ones, 86662306a36Sopenharmony_ci &priv->lec_no_forward, 86762306a36Sopenharmony_ci &priv->mcast_fwds 86862306a36Sopenharmony_ci }; 86962306a36Sopenharmony_ci void *v = NULL; 87062306a36Sopenharmony_ci int q; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci for (q = state->misc_table; q < ARRAY_SIZE(lec_misc_tables); q++) { 87362306a36Sopenharmony_ci v = lec_tbl_walk(state, lec_misc_tables[q], l); 87462306a36Sopenharmony_ci if (v) 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci state->misc_table = q; 87862306a36Sopenharmony_ci return v; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic void *lec_priv_walk(struct lec_state *state, loff_t *l, 88262306a36Sopenharmony_ci struct lec_priv *priv) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci if (!state->locked) { 88562306a36Sopenharmony_ci state->locked = priv; 88662306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, state->flags); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci if (!lec_arp_walk(state, l, priv) && !lec_misc_walk(state, l, priv)) { 88962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, state->flags); 89062306a36Sopenharmony_ci state->locked = NULL; 89162306a36Sopenharmony_ci /* Partial state reset for the next time we get called */ 89262306a36Sopenharmony_ci state->arp_table = state->misc_table = 0; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci return state->locked; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic void *lec_itf_walk(struct lec_state *state, loff_t *l) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci struct net_device *dev; 90062306a36Sopenharmony_ci void *v; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci dev = state->dev ? state->dev : dev_lec[state->itf]; 90362306a36Sopenharmony_ci v = (dev && netdev_priv(dev)) ? 90462306a36Sopenharmony_ci lec_priv_walk(state, l, netdev_priv(dev)) : NULL; 90562306a36Sopenharmony_ci if (!v && dev) { 90662306a36Sopenharmony_ci dev_put(dev); 90762306a36Sopenharmony_ci /* Partial state reset for the next time we get called */ 90862306a36Sopenharmony_ci dev = NULL; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci state->dev = dev; 91162306a36Sopenharmony_ci return v; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic void *lec_get_idx(struct lec_state *state, loff_t l) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci void *v = NULL; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci for (; state->itf < MAX_LEC_ITF; state->itf++) { 91962306a36Sopenharmony_ci v = lec_itf_walk(state, &l); 92062306a36Sopenharmony_ci if (v) 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci return v; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic void *lec_seq_start(struct seq_file *seq, loff_t *pos) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci struct lec_state *state = seq->private; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci state->itf = 0; 93162306a36Sopenharmony_ci state->dev = NULL; 93262306a36Sopenharmony_ci state->locked = NULL; 93362306a36Sopenharmony_ci state->arp_table = 0; 93462306a36Sopenharmony_ci state->misc_table = 0; 93562306a36Sopenharmony_ci state->node = SEQ_START_TOKEN; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci return *pos ? lec_get_idx(state, *pos) : SEQ_START_TOKEN; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic void lec_seq_stop(struct seq_file *seq, void *v) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci struct lec_state *state = seq->private; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (state->dev) { 94562306a36Sopenharmony_ci spin_unlock_irqrestore(&state->locked->lec_arp_lock, 94662306a36Sopenharmony_ci state->flags); 94762306a36Sopenharmony_ci dev_put(state->dev); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic void *lec_seq_next(struct seq_file *seq, void *v, loff_t *pos) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct lec_state *state = seq->private; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci ++*pos; 95662306a36Sopenharmony_ci return lec_get_idx(state, 1); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int lec_seq_show(struct seq_file *seq, void *v) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci static const char lec_banner[] = 96262306a36Sopenharmony_ci "Itf MAC ATM destination" 96362306a36Sopenharmony_ci " Status Flags " 96462306a36Sopenharmony_ci "VPI/VCI Recv VPI/VCI\n"; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 96762306a36Sopenharmony_ci seq_puts(seq, lec_banner); 96862306a36Sopenharmony_ci else { 96962306a36Sopenharmony_ci struct lec_state *state = seq->private; 97062306a36Sopenharmony_ci struct net_device *dev = state->dev; 97162306a36Sopenharmony_ci struct lec_arp_table *entry = hlist_entry(state->node, 97262306a36Sopenharmony_ci struct lec_arp_table, 97362306a36Sopenharmony_ci next); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci seq_printf(seq, "%s ", dev->name); 97662306a36Sopenharmony_ci lec_info(seq, entry); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic const struct seq_operations lec_seq_ops = { 98262306a36Sopenharmony_ci .start = lec_seq_start, 98362306a36Sopenharmony_ci .next = lec_seq_next, 98462306a36Sopenharmony_ci .stop = lec_seq_stop, 98562306a36Sopenharmony_ci .show = lec_seq_show, 98662306a36Sopenharmony_ci}; 98762306a36Sopenharmony_ci#endif 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic int lane_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci struct atm_vcc *vcc = ATM_SD(sock); 99262306a36Sopenharmony_ci int err = 0; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci switch (cmd) { 99562306a36Sopenharmony_ci case ATMLEC_CTRL: 99662306a36Sopenharmony_ci case ATMLEC_MCAST: 99762306a36Sopenharmony_ci case ATMLEC_DATA: 99862306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 99962306a36Sopenharmony_ci return -EPERM; 100062306a36Sopenharmony_ci break; 100162306a36Sopenharmony_ci default: 100262306a36Sopenharmony_ci return -ENOIOCTLCMD; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci switch (cmd) { 100662306a36Sopenharmony_ci case ATMLEC_CTRL: 100762306a36Sopenharmony_ci err = lecd_attach(vcc, (int)arg); 100862306a36Sopenharmony_ci if (err >= 0) 100962306a36Sopenharmony_ci sock->state = SS_CONNECTED; 101062306a36Sopenharmony_ci break; 101162306a36Sopenharmony_ci case ATMLEC_MCAST: 101262306a36Sopenharmony_ci err = lec_mcast_attach(vcc, (int)arg); 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci case ATMLEC_DATA: 101562306a36Sopenharmony_ci err = lec_vcc_attach(vcc, (void __user *)arg); 101662306a36Sopenharmony_ci break; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci return err; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic struct atm_ioctl lane_ioctl_ops = { 102362306a36Sopenharmony_ci .owner = THIS_MODULE, 102462306a36Sopenharmony_ci .ioctl = lane_ioctl, 102562306a36Sopenharmony_ci}; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int __init lane_module_init(void) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 103062306a36Sopenharmony_ci struct proc_dir_entry *p; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci p = proc_create_seq_private("lec", 0444, atm_proc_root, &lec_seq_ops, 103362306a36Sopenharmony_ci sizeof(struct lec_state), NULL); 103462306a36Sopenharmony_ci if (!p) { 103562306a36Sopenharmony_ci pr_err("Unable to initialize /proc/net/atm/lec\n"); 103662306a36Sopenharmony_ci return -ENOMEM; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci#endif 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci register_atm_ioctl(&lane_ioctl_ops); 104162306a36Sopenharmony_ci pr_info("lec.c: initialized\n"); 104262306a36Sopenharmony_ci return 0; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic void __exit lane_module_cleanup(void) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci int i; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 105062306a36Sopenharmony_ci remove_proc_entry("lec", atm_proc_root); 105162306a36Sopenharmony_ci#endif 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci deregister_atm_ioctl(&lane_ioctl_ops); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci for (i = 0; i < MAX_LEC_ITF; i++) { 105662306a36Sopenharmony_ci if (dev_lec[i] != NULL) { 105762306a36Sopenharmony_ci unregister_netdev(dev_lec[i]); 105862306a36Sopenharmony_ci free_netdev(dev_lec[i]); 105962306a36Sopenharmony_ci dev_lec[i] = NULL; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cimodule_init(lane_module_init); 106562306a36Sopenharmony_cimodule_exit(lane_module_cleanup); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci/* 106862306a36Sopenharmony_ci * LANE2: 3.1.3, LE_RESOLVE.request 106962306a36Sopenharmony_ci * Non force allocates memory and fills in *tlvs, fills in *sizeoftlvs. 107062306a36Sopenharmony_ci * If sizeoftlvs == NULL the default TLVs associated with this 107162306a36Sopenharmony_ci * lec will be used. 107262306a36Sopenharmony_ci * If dst_mac == NULL, targetless LE_ARP will be sent 107362306a36Sopenharmony_ci */ 107462306a36Sopenharmony_cistatic int lane2_resolve(struct net_device *dev, const u8 *dst_mac, int force, 107562306a36Sopenharmony_ci u8 **tlvs, u32 *sizeoftlvs) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci unsigned long flags; 107862306a36Sopenharmony_ci struct lec_priv *priv = netdev_priv(dev); 107962306a36Sopenharmony_ci struct lec_arp_table *table; 108062306a36Sopenharmony_ci struct sk_buff *skb; 108162306a36Sopenharmony_ci int retval; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (force == 0) { 108462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 108562306a36Sopenharmony_ci table = lec_arp_find(priv, dst_mac); 108662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 108762306a36Sopenharmony_ci if (table == NULL) 108862306a36Sopenharmony_ci return -1; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci *tlvs = kmemdup(table->tlvs, table->sizeoftlvs, GFP_ATOMIC); 109162306a36Sopenharmony_ci if (*tlvs == NULL) 109262306a36Sopenharmony_ci return -1; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci *sizeoftlvs = table->sizeoftlvs; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci return 0; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (sizeoftlvs == NULL) 110062306a36Sopenharmony_ci retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, NULL); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci else { 110362306a36Sopenharmony_ci skb = alloc_skb(*sizeoftlvs, GFP_ATOMIC); 110462306a36Sopenharmony_ci if (skb == NULL) 110562306a36Sopenharmony_ci return -1; 110662306a36Sopenharmony_ci skb->len = *sizeoftlvs; 110762306a36Sopenharmony_ci skb_copy_to_linear_data(skb, *tlvs, *sizeoftlvs); 110862306a36Sopenharmony_ci retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, skb); 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci return retval; 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci/* 111462306a36Sopenharmony_ci * LANE2: 3.1.4, LE_ASSOCIATE.request 111562306a36Sopenharmony_ci * Associate the *tlvs with the *lan_dst address. 111662306a36Sopenharmony_ci * Will overwrite any previous association 111762306a36Sopenharmony_ci * Returns 1 for success, 0 for failure (out of memory) 111862306a36Sopenharmony_ci * 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_cistatic int lane2_associate_req(struct net_device *dev, const u8 *lan_dst, 112162306a36Sopenharmony_ci const u8 *tlvs, u32 sizeoftlvs) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci int retval; 112462306a36Sopenharmony_ci struct sk_buff *skb; 112562306a36Sopenharmony_ci struct lec_priv *priv = netdev_priv(dev); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (!ether_addr_equal(lan_dst, dev->dev_addr)) 112862306a36Sopenharmony_ci return 0; /* not our mac address */ 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci kfree(priv->tlvs); /* NULL if there was no previous association */ 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci priv->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL); 113362306a36Sopenharmony_ci if (priv->tlvs == NULL) 113462306a36Sopenharmony_ci return 0; 113562306a36Sopenharmony_ci priv->sizeoftlvs = sizeoftlvs; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci skb = alloc_skb(sizeoftlvs, GFP_ATOMIC); 113862306a36Sopenharmony_ci if (skb == NULL) 113962306a36Sopenharmony_ci return 0; 114062306a36Sopenharmony_ci skb->len = sizeoftlvs; 114162306a36Sopenharmony_ci skb_copy_to_linear_data(skb, tlvs, sizeoftlvs); 114262306a36Sopenharmony_ci retval = send_to_lecd(priv, l_associate_req, NULL, NULL, skb); 114362306a36Sopenharmony_ci if (retval != 0) 114462306a36Sopenharmony_ci pr_info("lec.c: lane2_associate_req() failed\n"); 114562306a36Sopenharmony_ci /* 114662306a36Sopenharmony_ci * If the previous association has changed we must 114762306a36Sopenharmony_ci * somehow notify other LANE entities about the change 114862306a36Sopenharmony_ci */ 114962306a36Sopenharmony_ci return 1; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci/* 115362306a36Sopenharmony_ci * LANE2: 3.1.5, LE_ASSOCIATE.indication 115462306a36Sopenharmony_ci * 115562306a36Sopenharmony_ci */ 115662306a36Sopenharmony_cistatic void lane2_associate_ind(struct net_device *dev, const u8 *mac_addr, 115762306a36Sopenharmony_ci const u8 *tlvs, u32 sizeoftlvs) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci#if 0 116062306a36Sopenharmony_ci int i = 0; 116162306a36Sopenharmony_ci#endif 116262306a36Sopenharmony_ci struct lec_priv *priv = netdev_priv(dev); 116362306a36Sopenharmony_ci#if 0 /* 116462306a36Sopenharmony_ci * Why have the TLVs in LE_ARP entries 116562306a36Sopenharmony_ci * since we do not use them? When you 116662306a36Sopenharmony_ci * uncomment this code, make sure the 116762306a36Sopenharmony_ci * TLVs get freed when entry is killed 116862306a36Sopenharmony_ci */ 116962306a36Sopenharmony_ci struct lec_arp_table *entry = lec_arp_find(priv, mac_addr); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (entry == NULL) 117262306a36Sopenharmony_ci return; /* should not happen */ 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci kfree(entry->tlvs); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci entry->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL); 117762306a36Sopenharmony_ci if (entry->tlvs == NULL) 117862306a36Sopenharmony_ci return; 117962306a36Sopenharmony_ci entry->sizeoftlvs = sizeoftlvs; 118062306a36Sopenharmony_ci#endif 118162306a36Sopenharmony_ci#if 0 118262306a36Sopenharmony_ci pr_info("\n"); 118362306a36Sopenharmony_ci pr_info("dump of tlvs, sizeoftlvs=%d\n", sizeoftlvs); 118462306a36Sopenharmony_ci while (i < sizeoftlvs) 118562306a36Sopenharmony_ci pr_cont("%02x ", tlvs[i++]); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci pr_cont("\n"); 118862306a36Sopenharmony_ci#endif 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* tell MPOA about the TLVs we saw */ 119162306a36Sopenharmony_ci if (priv->lane2_ops && priv->lane2_ops->associate_indicator) { 119262306a36Sopenharmony_ci priv->lane2_ops->associate_indicator(dev, mac_addr, 119362306a36Sopenharmony_ci tlvs, sizeoftlvs); 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci/* 119862306a36Sopenharmony_ci * Here starts what used to lec_arpc.c 119962306a36Sopenharmony_ci * 120062306a36Sopenharmony_ci * lec_arpc.c was added here when making 120162306a36Sopenharmony_ci * lane client modular. October 1997 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci#include <linux/types.h> 120562306a36Sopenharmony_ci#include <linux/timer.h> 120662306a36Sopenharmony_ci#include <linux/param.h> 120762306a36Sopenharmony_ci#include <linux/atomic.h> 120862306a36Sopenharmony_ci#include <linux/inetdevice.h> 120962306a36Sopenharmony_ci#include <net/route.h> 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci#if 0 121262306a36Sopenharmony_ci#define pr_debug(format, args...) 121362306a36Sopenharmony_ci/* 121462306a36Sopenharmony_ci #define pr_debug printk 121562306a36Sopenharmony_ci*/ 121662306a36Sopenharmony_ci#endif 121762306a36Sopenharmony_ci#define DEBUG_ARP_TABLE 0 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci#define LEC_ARP_REFRESH_INTERVAL (3*HZ) 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic void lec_arp_check_expire(struct work_struct *work); 122262306a36Sopenharmony_cistatic void lec_arp_expire_arp(struct timer_list *t); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci/* 122562306a36Sopenharmony_ci * Arp table funcs 122662306a36Sopenharmony_ci */ 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci#define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE - 1)) 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci/* 123162306a36Sopenharmony_ci * Initialization of arp-cache 123262306a36Sopenharmony_ci */ 123362306a36Sopenharmony_cistatic void lec_arp_init(struct lec_priv *priv) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci unsigned short i; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) 123862306a36Sopenharmony_ci INIT_HLIST_HEAD(&priv->lec_arp_tables[i]); 123962306a36Sopenharmony_ci INIT_HLIST_HEAD(&priv->lec_arp_empty_ones); 124062306a36Sopenharmony_ci INIT_HLIST_HEAD(&priv->lec_no_forward); 124162306a36Sopenharmony_ci INIT_HLIST_HEAD(&priv->mcast_fwds); 124262306a36Sopenharmony_ci spin_lock_init(&priv->lec_arp_lock); 124362306a36Sopenharmony_ci INIT_DELAYED_WORK(&priv->lec_arp_work, lec_arp_check_expire); 124462306a36Sopenharmony_ci schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL); 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic void lec_arp_clear_vccs(struct lec_arp_table *entry) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci if (entry->vcc) { 125062306a36Sopenharmony_ci struct atm_vcc *vcc = entry->vcc; 125162306a36Sopenharmony_ci struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); 125262306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)vcc->proto_data; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci vcc->pop = vpriv->old_pop; 125562306a36Sopenharmony_ci if (vpriv->xoff) 125662306a36Sopenharmony_ci netif_wake_queue(dev); 125762306a36Sopenharmony_ci kfree(vpriv); 125862306a36Sopenharmony_ci vcc->user_back = NULL; 125962306a36Sopenharmony_ci vcc->push = entry->old_push; 126062306a36Sopenharmony_ci vcc_release_async(vcc, -EPIPE); 126162306a36Sopenharmony_ci entry->vcc = NULL; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci if (entry->recv_vcc) { 126462306a36Sopenharmony_ci struct atm_vcc *vcc = entry->recv_vcc; 126562306a36Sopenharmony_ci struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci kfree(vpriv); 126862306a36Sopenharmony_ci vcc->user_back = NULL; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci entry->recv_vcc->push = entry->old_recv_push; 127162306a36Sopenharmony_ci vcc_release_async(entry->recv_vcc, -EPIPE); 127262306a36Sopenharmony_ci entry->recv_vcc = NULL; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci/* 127762306a36Sopenharmony_ci * Insert entry to lec_arp_table 127862306a36Sopenharmony_ci * LANE2: Add to the end of the list to satisfy 8.1.13 127962306a36Sopenharmony_ci */ 128062306a36Sopenharmony_cistatic inline void 128162306a36Sopenharmony_cilec_arp_add(struct lec_priv *priv, struct lec_arp_table *entry) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct hlist_head *tmp; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci tmp = &priv->lec_arp_tables[HASH(entry->mac_addr[ETH_ALEN - 1])]; 128662306a36Sopenharmony_ci hlist_add_head(&entry->next, tmp); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci pr_debug("Added entry:%pM\n", entry->mac_addr); 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci/* 129262306a36Sopenharmony_ci * Remove entry from lec_arp_table 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_cistatic int 129562306a36Sopenharmony_cilec_arp_remove(struct lec_priv *priv, struct lec_arp_table *to_remove) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci struct lec_arp_table *entry; 129862306a36Sopenharmony_ci int i, remove_vcc = 1; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (!to_remove) 130162306a36Sopenharmony_ci return -1; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci hlist_del(&to_remove->next); 130462306a36Sopenharmony_ci del_timer(&to_remove->timer); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci /* 130762306a36Sopenharmony_ci * If this is the only MAC connected to this VCC, 130862306a36Sopenharmony_ci * also tear down the VCC 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_ci if (to_remove->status >= ESI_FLUSH_PENDING) { 131162306a36Sopenharmony_ci /* 131262306a36Sopenharmony_ci * ESI_FLUSH_PENDING, ESI_FORWARD_DIRECT 131362306a36Sopenharmony_ci */ 131462306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 131562306a36Sopenharmony_ci hlist_for_each_entry(entry, 131662306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 131762306a36Sopenharmony_ci if (memcmp(to_remove->atm_addr, 131862306a36Sopenharmony_ci entry->atm_addr, ATM_ESA_LEN) == 0) { 131962306a36Sopenharmony_ci remove_vcc = 0; 132062306a36Sopenharmony_ci break; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci if (remove_vcc) 132562306a36Sopenharmony_ci lec_arp_clear_vccs(to_remove); 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci skb_queue_purge(&to_remove->tx_wait); /* FIXME: good place for this? */ 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci pr_debug("Removed entry:%pM\n", to_remove->mac_addr); 133062306a36Sopenharmony_ci return 0; 133162306a36Sopenharmony_ci} 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci#if DEBUG_ARP_TABLE 133462306a36Sopenharmony_cistatic const char *get_status_string(unsigned char st) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci switch (st) { 133762306a36Sopenharmony_ci case ESI_UNKNOWN: 133862306a36Sopenharmony_ci return "ESI_UNKNOWN"; 133962306a36Sopenharmony_ci case ESI_ARP_PENDING: 134062306a36Sopenharmony_ci return "ESI_ARP_PENDING"; 134162306a36Sopenharmony_ci case ESI_VC_PENDING: 134262306a36Sopenharmony_ci return "ESI_VC_PENDING"; 134362306a36Sopenharmony_ci case ESI_FLUSH_PENDING: 134462306a36Sopenharmony_ci return "ESI_FLUSH_PENDING"; 134562306a36Sopenharmony_ci case ESI_FORWARD_DIRECT: 134662306a36Sopenharmony_ci return "ESI_FORWARD_DIRECT"; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci return "<UNKNOWN>"; 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic void dump_arp_table(struct lec_priv *priv) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci struct lec_arp_table *rulla; 135462306a36Sopenharmony_ci char buf[256]; 135562306a36Sopenharmony_ci int i, offset; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci pr_info("Dump %p:\n", priv); 135862306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 135962306a36Sopenharmony_ci hlist_for_each_entry(rulla, 136062306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 136162306a36Sopenharmony_ci offset = 0; 136262306a36Sopenharmony_ci offset += sprintf(buf, "%d: %p\n", i, rulla); 136362306a36Sopenharmony_ci offset += sprintf(buf + offset, "Mac: %pM ", 136462306a36Sopenharmony_ci rulla->mac_addr); 136562306a36Sopenharmony_ci offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, 136662306a36Sopenharmony_ci rulla->atm_addr); 136762306a36Sopenharmony_ci offset += sprintf(buf + offset, 136862306a36Sopenharmony_ci "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", 136962306a36Sopenharmony_ci rulla->vcc ? rulla->vcc->vpi : 0, 137062306a36Sopenharmony_ci rulla->vcc ? rulla->vcc->vci : 0, 137162306a36Sopenharmony_ci rulla->recv_vcc ? rulla->recv_vcc-> 137262306a36Sopenharmony_ci vpi : 0, 137362306a36Sopenharmony_ci rulla->recv_vcc ? rulla->recv_vcc-> 137462306a36Sopenharmony_ci vci : 0, rulla->last_used, 137562306a36Sopenharmony_ci rulla->timestamp, rulla->no_tries); 137662306a36Sopenharmony_ci offset += 137762306a36Sopenharmony_ci sprintf(buf + offset, 137862306a36Sopenharmony_ci "Flags:%x, Packets_flooded:%x, Status: %s ", 137962306a36Sopenharmony_ci rulla->flags, rulla->packets_flooded, 138062306a36Sopenharmony_ci get_status_string(rulla->status)); 138162306a36Sopenharmony_ci pr_info("%s\n", buf); 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (!hlist_empty(&priv->lec_no_forward)) 138662306a36Sopenharmony_ci pr_info("No forward\n"); 138762306a36Sopenharmony_ci hlist_for_each_entry(rulla, &priv->lec_no_forward, next) { 138862306a36Sopenharmony_ci offset = 0; 138962306a36Sopenharmony_ci offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); 139062306a36Sopenharmony_ci offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, 139162306a36Sopenharmony_ci rulla->atm_addr); 139262306a36Sopenharmony_ci offset += sprintf(buf + offset, 139362306a36Sopenharmony_ci "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", 139462306a36Sopenharmony_ci rulla->vcc ? rulla->vcc->vpi : 0, 139562306a36Sopenharmony_ci rulla->vcc ? rulla->vcc->vci : 0, 139662306a36Sopenharmony_ci rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, 139762306a36Sopenharmony_ci rulla->recv_vcc ? rulla->recv_vcc->vci : 0, 139862306a36Sopenharmony_ci rulla->last_used, 139962306a36Sopenharmony_ci rulla->timestamp, rulla->no_tries); 140062306a36Sopenharmony_ci offset += sprintf(buf + offset, 140162306a36Sopenharmony_ci "Flags:%x, Packets_flooded:%x, Status: %s ", 140262306a36Sopenharmony_ci rulla->flags, rulla->packets_flooded, 140362306a36Sopenharmony_ci get_status_string(rulla->status)); 140462306a36Sopenharmony_ci pr_info("%s\n", buf); 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (!hlist_empty(&priv->lec_arp_empty_ones)) 140862306a36Sopenharmony_ci pr_info("Empty ones\n"); 140962306a36Sopenharmony_ci hlist_for_each_entry(rulla, &priv->lec_arp_empty_ones, next) { 141062306a36Sopenharmony_ci offset = 0; 141162306a36Sopenharmony_ci offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); 141262306a36Sopenharmony_ci offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, 141362306a36Sopenharmony_ci rulla->atm_addr); 141462306a36Sopenharmony_ci offset += sprintf(buf + offset, 141562306a36Sopenharmony_ci "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", 141662306a36Sopenharmony_ci rulla->vcc ? rulla->vcc->vpi : 0, 141762306a36Sopenharmony_ci rulla->vcc ? rulla->vcc->vci : 0, 141862306a36Sopenharmony_ci rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, 141962306a36Sopenharmony_ci rulla->recv_vcc ? rulla->recv_vcc->vci : 0, 142062306a36Sopenharmony_ci rulla->last_used, 142162306a36Sopenharmony_ci rulla->timestamp, rulla->no_tries); 142262306a36Sopenharmony_ci offset += sprintf(buf + offset, 142362306a36Sopenharmony_ci "Flags:%x, Packets_flooded:%x, Status: %s ", 142462306a36Sopenharmony_ci rulla->flags, rulla->packets_flooded, 142562306a36Sopenharmony_ci get_status_string(rulla->status)); 142662306a36Sopenharmony_ci pr_info("%s", buf); 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci if (!hlist_empty(&priv->mcast_fwds)) 143062306a36Sopenharmony_ci pr_info("Multicast Forward VCCs\n"); 143162306a36Sopenharmony_ci hlist_for_each_entry(rulla, &priv->mcast_fwds, next) { 143262306a36Sopenharmony_ci offset = 0; 143362306a36Sopenharmony_ci offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); 143462306a36Sopenharmony_ci offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, 143562306a36Sopenharmony_ci rulla->atm_addr); 143662306a36Sopenharmony_ci offset += sprintf(buf + offset, 143762306a36Sopenharmony_ci "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", 143862306a36Sopenharmony_ci rulla->vcc ? rulla->vcc->vpi : 0, 143962306a36Sopenharmony_ci rulla->vcc ? rulla->vcc->vci : 0, 144062306a36Sopenharmony_ci rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, 144162306a36Sopenharmony_ci rulla->recv_vcc ? rulla->recv_vcc->vci : 0, 144262306a36Sopenharmony_ci rulla->last_used, 144362306a36Sopenharmony_ci rulla->timestamp, rulla->no_tries); 144462306a36Sopenharmony_ci offset += sprintf(buf + offset, 144562306a36Sopenharmony_ci "Flags:%x, Packets_flooded:%x, Status: %s ", 144662306a36Sopenharmony_ci rulla->flags, rulla->packets_flooded, 144762306a36Sopenharmony_ci get_status_string(rulla->status)); 144862306a36Sopenharmony_ci pr_info("%s\n", buf); 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci} 145262306a36Sopenharmony_ci#else 145362306a36Sopenharmony_ci#define dump_arp_table(priv) do { } while (0) 145462306a36Sopenharmony_ci#endif 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci/* 145762306a36Sopenharmony_ci * Destruction of arp-cache 145862306a36Sopenharmony_ci */ 145962306a36Sopenharmony_cistatic void lec_arp_destroy(struct lec_priv *priv) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci unsigned long flags; 146262306a36Sopenharmony_ci struct hlist_node *next; 146362306a36Sopenharmony_ci struct lec_arp_table *entry; 146462306a36Sopenharmony_ci int i; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci cancel_delayed_work_sync(&priv->lec_arp_work); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci /* 146962306a36Sopenharmony_ci * Remove all entries 147062306a36Sopenharmony_ci */ 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 147362306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 147462306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 147562306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 147662306a36Sopenharmony_ci lec_arp_remove(priv, entry); 147762306a36Sopenharmony_ci lec_arp_put(entry); 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci INIT_HLIST_HEAD(&priv->lec_arp_tables[i]); 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 148362306a36Sopenharmony_ci &priv->lec_arp_empty_ones, next) { 148462306a36Sopenharmony_ci del_timer_sync(&entry->timer); 148562306a36Sopenharmony_ci lec_arp_clear_vccs(entry); 148662306a36Sopenharmony_ci hlist_del(&entry->next); 148762306a36Sopenharmony_ci lec_arp_put(entry); 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci INIT_HLIST_HEAD(&priv->lec_arp_empty_ones); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 149262306a36Sopenharmony_ci &priv->lec_no_forward, next) { 149362306a36Sopenharmony_ci del_timer_sync(&entry->timer); 149462306a36Sopenharmony_ci lec_arp_clear_vccs(entry); 149562306a36Sopenharmony_ci hlist_del(&entry->next); 149662306a36Sopenharmony_ci lec_arp_put(entry); 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci INIT_HLIST_HEAD(&priv->lec_no_forward); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, &priv->mcast_fwds, next) { 150162306a36Sopenharmony_ci /* No timer, LANEv2 7.1.20 and 2.3.5.3 */ 150262306a36Sopenharmony_ci lec_arp_clear_vccs(entry); 150362306a36Sopenharmony_ci hlist_del(&entry->next); 150462306a36Sopenharmony_ci lec_arp_put(entry); 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci INIT_HLIST_HEAD(&priv->mcast_fwds); 150762306a36Sopenharmony_ci priv->mcast_vcc = NULL; 150862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 150962306a36Sopenharmony_ci} 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci/* 151262306a36Sopenharmony_ci * Find entry by mac_address 151362306a36Sopenharmony_ci */ 151462306a36Sopenharmony_cistatic struct lec_arp_table *lec_arp_find(struct lec_priv *priv, 151562306a36Sopenharmony_ci const unsigned char *mac_addr) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct hlist_head *head; 151862306a36Sopenharmony_ci struct lec_arp_table *entry; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci pr_debug("%pM\n", mac_addr); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci head = &priv->lec_arp_tables[HASH(mac_addr[ETH_ALEN - 1])]; 152362306a36Sopenharmony_ci hlist_for_each_entry(entry, head, next) { 152462306a36Sopenharmony_ci if (ether_addr_equal(mac_addr, entry->mac_addr)) 152562306a36Sopenharmony_ci return entry; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci return NULL; 152862306a36Sopenharmony_ci} 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_cistatic struct lec_arp_table *make_entry(struct lec_priv *priv, 153162306a36Sopenharmony_ci const unsigned char *mac_addr) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci struct lec_arp_table *to_return; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci to_return = kzalloc(sizeof(struct lec_arp_table), GFP_ATOMIC); 153662306a36Sopenharmony_ci if (!to_return) 153762306a36Sopenharmony_ci return NULL; 153862306a36Sopenharmony_ci ether_addr_copy(to_return->mac_addr, mac_addr); 153962306a36Sopenharmony_ci INIT_HLIST_NODE(&to_return->next); 154062306a36Sopenharmony_ci timer_setup(&to_return->timer, lec_arp_expire_arp, 0); 154162306a36Sopenharmony_ci to_return->last_used = jiffies; 154262306a36Sopenharmony_ci to_return->priv = priv; 154362306a36Sopenharmony_ci skb_queue_head_init(&to_return->tx_wait); 154462306a36Sopenharmony_ci refcount_set(&to_return->usage, 1); 154562306a36Sopenharmony_ci return to_return; 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci/* Arp sent timer expired */ 154962306a36Sopenharmony_cistatic void lec_arp_expire_arp(struct timer_list *t) 155062306a36Sopenharmony_ci{ 155162306a36Sopenharmony_ci struct lec_arp_table *entry; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci entry = from_timer(entry, t, timer); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci pr_debug("\n"); 155662306a36Sopenharmony_ci if (entry->status == ESI_ARP_PENDING) { 155762306a36Sopenharmony_ci if (entry->no_tries <= entry->priv->max_retry_count) { 155862306a36Sopenharmony_ci if (entry->is_rdesc) 155962306a36Sopenharmony_ci send_to_lecd(entry->priv, l_rdesc_arp_xmt, 156062306a36Sopenharmony_ci entry->mac_addr, NULL, NULL); 156162306a36Sopenharmony_ci else 156262306a36Sopenharmony_ci send_to_lecd(entry->priv, l_arp_xmt, 156362306a36Sopenharmony_ci entry->mac_addr, NULL, NULL); 156462306a36Sopenharmony_ci entry->no_tries++; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci mod_timer(&entry->timer, jiffies + (1 * HZ)); 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci} 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci/* Unknown/unused vcc expire, remove associated entry */ 157162306a36Sopenharmony_cistatic void lec_arp_expire_vcc(struct timer_list *t) 157262306a36Sopenharmony_ci{ 157362306a36Sopenharmony_ci unsigned long flags; 157462306a36Sopenharmony_ci struct lec_arp_table *to_remove = from_timer(to_remove, t, timer); 157562306a36Sopenharmony_ci struct lec_priv *priv = to_remove->priv; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci del_timer(&to_remove->timer); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci pr_debug("%p %p: vpi:%d vci:%d\n", 158062306a36Sopenharmony_ci to_remove, priv, 158162306a36Sopenharmony_ci to_remove->vcc ? to_remove->recv_vcc->vpi : 0, 158262306a36Sopenharmony_ci to_remove->vcc ? to_remove->recv_vcc->vci : 0); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 158562306a36Sopenharmony_ci hlist_del(&to_remove->next); 158662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci lec_arp_clear_vccs(to_remove); 158962306a36Sopenharmony_ci lec_arp_put(to_remove); 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_cistatic bool __lec_arp_check_expire(struct lec_arp_table *entry, 159362306a36Sopenharmony_ci unsigned long now, 159462306a36Sopenharmony_ci struct lec_priv *priv) 159562306a36Sopenharmony_ci{ 159662306a36Sopenharmony_ci unsigned long time_to_check; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci if ((entry->flags) & LEC_REMOTE_FLAG && priv->topology_change) 159962306a36Sopenharmony_ci time_to_check = priv->forward_delay_time; 160062306a36Sopenharmony_ci else 160162306a36Sopenharmony_ci time_to_check = priv->aging_time; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci pr_debug("About to expire: %lx - %lx > %lx\n", 160462306a36Sopenharmony_ci now, entry->last_used, time_to_check); 160562306a36Sopenharmony_ci if (time_after(now, entry->last_used + time_to_check) && 160662306a36Sopenharmony_ci !(entry->flags & LEC_PERMANENT_FLAG) && 160762306a36Sopenharmony_ci !(entry->mac_addr[0] & 0x01)) { /* LANE2: 7.1.20 */ 160862306a36Sopenharmony_ci /* Remove entry */ 160962306a36Sopenharmony_ci pr_debug("Entry timed out\n"); 161062306a36Sopenharmony_ci lec_arp_remove(priv, entry); 161162306a36Sopenharmony_ci lec_arp_put(entry); 161262306a36Sopenharmony_ci } else { 161362306a36Sopenharmony_ci /* Something else */ 161462306a36Sopenharmony_ci if ((entry->status == ESI_VC_PENDING || 161562306a36Sopenharmony_ci entry->status == ESI_ARP_PENDING) && 161662306a36Sopenharmony_ci time_after_eq(now, entry->timestamp + 161762306a36Sopenharmony_ci priv->max_unknown_frame_time)) { 161862306a36Sopenharmony_ci entry->timestamp = jiffies; 161962306a36Sopenharmony_ci entry->packets_flooded = 0; 162062306a36Sopenharmony_ci if (entry->status == ESI_VC_PENDING) 162162306a36Sopenharmony_ci send_to_lecd(priv, l_svc_setup, 162262306a36Sopenharmony_ci entry->mac_addr, 162362306a36Sopenharmony_ci entry->atm_addr, 162462306a36Sopenharmony_ci NULL); 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci if (entry->status == ESI_FLUSH_PENDING && 162762306a36Sopenharmony_ci time_after_eq(now, entry->timestamp + 162862306a36Sopenharmony_ci priv->path_switching_delay)) { 162962306a36Sopenharmony_ci lec_arp_hold(entry); 163062306a36Sopenharmony_ci return true; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci return false; 163562306a36Sopenharmony_ci} 163662306a36Sopenharmony_ci/* 163762306a36Sopenharmony_ci * Expire entries. 163862306a36Sopenharmony_ci * 1. Re-set timer 163962306a36Sopenharmony_ci * 2. For each entry, delete entries that have aged past the age limit. 164062306a36Sopenharmony_ci * 3. For each entry, depending on the status of the entry, perform 164162306a36Sopenharmony_ci * the following maintenance. 164262306a36Sopenharmony_ci * a. If status is ESI_VC_PENDING or ESI_ARP_PENDING then if the 164362306a36Sopenharmony_ci * tick_count is above the max_unknown_frame_time, clear 164462306a36Sopenharmony_ci * the tick_count to zero and clear the packets_flooded counter 164562306a36Sopenharmony_ci * to zero. This supports the packet rate limit per address 164662306a36Sopenharmony_ci * while flooding unknowns. 164762306a36Sopenharmony_ci * b. If the status is ESI_FLUSH_PENDING and the tick_count is greater 164862306a36Sopenharmony_ci * than or equal to the path_switching_delay, change the status 164962306a36Sopenharmony_ci * to ESI_FORWARD_DIRECT. This causes the flush period to end 165062306a36Sopenharmony_ci * regardless of the progress of the flush protocol. 165162306a36Sopenharmony_ci */ 165262306a36Sopenharmony_cistatic void lec_arp_check_expire(struct work_struct *work) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci unsigned long flags; 165562306a36Sopenharmony_ci struct lec_priv *priv = 165662306a36Sopenharmony_ci container_of(work, struct lec_priv, lec_arp_work.work); 165762306a36Sopenharmony_ci struct hlist_node *next; 165862306a36Sopenharmony_ci struct lec_arp_table *entry; 165962306a36Sopenharmony_ci unsigned long now; 166062306a36Sopenharmony_ci int i; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci pr_debug("%p\n", priv); 166362306a36Sopenharmony_ci now = jiffies; 166462306a36Sopenharmony_cirestart: 166562306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 166662306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 166762306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 166862306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 166962306a36Sopenharmony_ci if (__lec_arp_check_expire(entry, now, priv)) { 167062306a36Sopenharmony_ci struct sk_buff *skb; 167162306a36Sopenharmony_ci struct atm_vcc *vcc = entry->vcc; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, 167462306a36Sopenharmony_ci flags); 167562306a36Sopenharmony_ci while ((skb = skb_dequeue(&entry->tx_wait))) 167662306a36Sopenharmony_ci lec_send(vcc, skb); 167762306a36Sopenharmony_ci entry->last_used = jiffies; 167862306a36Sopenharmony_ci entry->status = ESI_FORWARD_DIRECT; 167962306a36Sopenharmony_ci lec_arp_put(entry); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci goto restart; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL); 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci/* 169162306a36Sopenharmony_ci * Try to find vcc where mac_address is attached. 169262306a36Sopenharmony_ci * 169362306a36Sopenharmony_ci */ 169462306a36Sopenharmony_cistatic struct atm_vcc *lec_arp_resolve(struct lec_priv *priv, 169562306a36Sopenharmony_ci const unsigned char *mac_to_find, 169662306a36Sopenharmony_ci int is_rdesc, 169762306a36Sopenharmony_ci struct lec_arp_table **ret_entry) 169862306a36Sopenharmony_ci{ 169962306a36Sopenharmony_ci unsigned long flags; 170062306a36Sopenharmony_ci struct lec_arp_table *entry; 170162306a36Sopenharmony_ci struct atm_vcc *found; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci if (mac_to_find[0] & 0x01) { 170462306a36Sopenharmony_ci switch (priv->lane_version) { 170562306a36Sopenharmony_ci case 1: 170662306a36Sopenharmony_ci return priv->mcast_vcc; 170762306a36Sopenharmony_ci case 2: /* LANE2 wants arp for multicast addresses */ 170862306a36Sopenharmony_ci if (ether_addr_equal(mac_to_find, bus_mac)) 170962306a36Sopenharmony_ci return priv->mcast_vcc; 171062306a36Sopenharmony_ci break; 171162306a36Sopenharmony_ci default: 171262306a36Sopenharmony_ci break; 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 171762306a36Sopenharmony_ci entry = lec_arp_find(priv, mac_to_find); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (entry) { 172062306a36Sopenharmony_ci if (entry->status == ESI_FORWARD_DIRECT) { 172162306a36Sopenharmony_ci /* Connection Ok */ 172262306a36Sopenharmony_ci entry->last_used = jiffies; 172362306a36Sopenharmony_ci lec_arp_hold(entry); 172462306a36Sopenharmony_ci *ret_entry = entry; 172562306a36Sopenharmony_ci found = entry->vcc; 172662306a36Sopenharmony_ci goto out; 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci /* 172962306a36Sopenharmony_ci * If the LE_ARP cache entry is still pending, reset count to 0 173062306a36Sopenharmony_ci * so another LE_ARP request can be made for this frame. 173162306a36Sopenharmony_ci */ 173262306a36Sopenharmony_ci if (entry->status == ESI_ARP_PENDING) 173362306a36Sopenharmony_ci entry->no_tries = 0; 173462306a36Sopenharmony_ci /* 173562306a36Sopenharmony_ci * Data direct VC not yet set up, check to see if the unknown 173662306a36Sopenharmony_ci * frame count is greater than the limit. If the limit has 173762306a36Sopenharmony_ci * not been reached, allow the caller to send packet to 173862306a36Sopenharmony_ci * BUS. 173962306a36Sopenharmony_ci */ 174062306a36Sopenharmony_ci if (entry->status != ESI_FLUSH_PENDING && 174162306a36Sopenharmony_ci entry->packets_flooded < 174262306a36Sopenharmony_ci priv->maximum_unknown_frame_count) { 174362306a36Sopenharmony_ci entry->packets_flooded++; 174462306a36Sopenharmony_ci pr_debug("Flooding..\n"); 174562306a36Sopenharmony_ci found = priv->mcast_vcc; 174662306a36Sopenharmony_ci goto out; 174762306a36Sopenharmony_ci } 174862306a36Sopenharmony_ci /* 174962306a36Sopenharmony_ci * We got here because entry->status == ESI_FLUSH_PENDING 175062306a36Sopenharmony_ci * or BUS flood limit was reached for an entry which is 175162306a36Sopenharmony_ci * in ESI_ARP_PENDING or ESI_VC_PENDING state. 175262306a36Sopenharmony_ci */ 175362306a36Sopenharmony_ci lec_arp_hold(entry); 175462306a36Sopenharmony_ci *ret_entry = entry; 175562306a36Sopenharmony_ci pr_debug("entry->status %d entry->vcc %p\n", entry->status, 175662306a36Sopenharmony_ci entry->vcc); 175762306a36Sopenharmony_ci found = NULL; 175862306a36Sopenharmony_ci } else { 175962306a36Sopenharmony_ci /* No matching entry was found */ 176062306a36Sopenharmony_ci entry = make_entry(priv, mac_to_find); 176162306a36Sopenharmony_ci pr_debug("Making entry\n"); 176262306a36Sopenharmony_ci if (!entry) { 176362306a36Sopenharmony_ci found = priv->mcast_vcc; 176462306a36Sopenharmony_ci goto out; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci lec_arp_add(priv, entry); 176762306a36Sopenharmony_ci /* We want arp-request(s) to be sent */ 176862306a36Sopenharmony_ci entry->packets_flooded = 1; 176962306a36Sopenharmony_ci entry->status = ESI_ARP_PENDING; 177062306a36Sopenharmony_ci entry->no_tries = 1; 177162306a36Sopenharmony_ci entry->last_used = entry->timestamp = jiffies; 177262306a36Sopenharmony_ci entry->is_rdesc = is_rdesc; 177362306a36Sopenharmony_ci if (entry->is_rdesc) 177462306a36Sopenharmony_ci send_to_lecd(priv, l_rdesc_arp_xmt, mac_to_find, NULL, 177562306a36Sopenharmony_ci NULL); 177662306a36Sopenharmony_ci else 177762306a36Sopenharmony_ci send_to_lecd(priv, l_arp_xmt, mac_to_find, NULL, NULL); 177862306a36Sopenharmony_ci entry->timer.expires = jiffies + (1 * HZ); 177962306a36Sopenharmony_ci entry->timer.function = lec_arp_expire_arp; 178062306a36Sopenharmony_ci add_timer(&entry->timer); 178162306a36Sopenharmony_ci found = priv->mcast_vcc; 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ciout: 178562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 178662306a36Sopenharmony_ci return found; 178762306a36Sopenharmony_ci} 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_cistatic int 179062306a36Sopenharmony_cilec_addr_delete(struct lec_priv *priv, const unsigned char *atm_addr, 179162306a36Sopenharmony_ci unsigned long permanent) 179262306a36Sopenharmony_ci{ 179362306a36Sopenharmony_ci unsigned long flags; 179462306a36Sopenharmony_ci struct hlist_node *next; 179562306a36Sopenharmony_ci struct lec_arp_table *entry; 179662306a36Sopenharmony_ci int i; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci pr_debug("\n"); 179962306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 180062306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 180162306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 180262306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 180362306a36Sopenharmony_ci if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN) && 180462306a36Sopenharmony_ci (permanent || 180562306a36Sopenharmony_ci !(entry->flags & LEC_PERMANENT_FLAG))) { 180662306a36Sopenharmony_ci lec_arp_remove(priv, entry); 180762306a36Sopenharmony_ci lec_arp_put(entry); 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 181062306a36Sopenharmony_ci return 0; 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 181462306a36Sopenharmony_ci return -1; 181562306a36Sopenharmony_ci} 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci/* 181862306a36Sopenharmony_ci * Notifies: Response to arp_request (atm_addr != NULL) 181962306a36Sopenharmony_ci */ 182062306a36Sopenharmony_cistatic void 182162306a36Sopenharmony_cilec_arp_update(struct lec_priv *priv, const unsigned char *mac_addr, 182262306a36Sopenharmony_ci const unsigned char *atm_addr, unsigned long remoteflag, 182362306a36Sopenharmony_ci unsigned int targetless_le_arp) 182462306a36Sopenharmony_ci{ 182562306a36Sopenharmony_ci unsigned long flags; 182662306a36Sopenharmony_ci struct hlist_node *next; 182762306a36Sopenharmony_ci struct lec_arp_table *entry, *tmp; 182862306a36Sopenharmony_ci int i; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci pr_debug("%smac:%pM\n", 183162306a36Sopenharmony_ci (targetless_le_arp) ? "targetless " : "", mac_addr); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 183462306a36Sopenharmony_ci entry = lec_arp_find(priv, mac_addr); 183562306a36Sopenharmony_ci if (entry == NULL && targetless_le_arp) 183662306a36Sopenharmony_ci goto out; /* 183762306a36Sopenharmony_ci * LANE2: ignore targetless LE_ARPs for which 183862306a36Sopenharmony_ci * we have no entry in the cache. 7.1.30 183962306a36Sopenharmony_ci */ 184062306a36Sopenharmony_ci if (!hlist_empty(&priv->lec_arp_empty_ones)) { 184162306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 184262306a36Sopenharmony_ci &priv->lec_arp_empty_ones, next) { 184362306a36Sopenharmony_ci if (memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN) == 0) { 184462306a36Sopenharmony_ci hlist_del(&entry->next); 184562306a36Sopenharmony_ci del_timer(&entry->timer); 184662306a36Sopenharmony_ci tmp = lec_arp_find(priv, mac_addr); 184762306a36Sopenharmony_ci if (tmp) { 184862306a36Sopenharmony_ci del_timer(&tmp->timer); 184962306a36Sopenharmony_ci tmp->status = ESI_FORWARD_DIRECT; 185062306a36Sopenharmony_ci memcpy(tmp->atm_addr, atm_addr, ATM_ESA_LEN); 185162306a36Sopenharmony_ci tmp->vcc = entry->vcc; 185262306a36Sopenharmony_ci tmp->old_push = entry->old_push; 185362306a36Sopenharmony_ci tmp->last_used = jiffies; 185462306a36Sopenharmony_ci del_timer(&entry->timer); 185562306a36Sopenharmony_ci lec_arp_put(entry); 185662306a36Sopenharmony_ci entry = tmp; 185762306a36Sopenharmony_ci } else { 185862306a36Sopenharmony_ci entry->status = ESI_FORWARD_DIRECT; 185962306a36Sopenharmony_ci ether_addr_copy(entry->mac_addr, 186062306a36Sopenharmony_ci mac_addr); 186162306a36Sopenharmony_ci entry->last_used = jiffies; 186262306a36Sopenharmony_ci lec_arp_add(priv, entry); 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci if (remoteflag) 186562306a36Sopenharmony_ci entry->flags |= LEC_REMOTE_FLAG; 186662306a36Sopenharmony_ci else 186762306a36Sopenharmony_ci entry->flags &= ~LEC_REMOTE_FLAG; 186862306a36Sopenharmony_ci pr_debug("After update\n"); 186962306a36Sopenharmony_ci dump_arp_table(priv); 187062306a36Sopenharmony_ci goto out; 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci entry = lec_arp_find(priv, mac_addr); 187662306a36Sopenharmony_ci if (!entry) { 187762306a36Sopenharmony_ci entry = make_entry(priv, mac_addr); 187862306a36Sopenharmony_ci if (!entry) 187962306a36Sopenharmony_ci goto out; 188062306a36Sopenharmony_ci entry->status = ESI_UNKNOWN; 188162306a36Sopenharmony_ci lec_arp_add(priv, entry); 188262306a36Sopenharmony_ci /* Temporary, changes before end of function */ 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN); 188562306a36Sopenharmony_ci del_timer(&entry->timer); 188662306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 188762306a36Sopenharmony_ci hlist_for_each_entry(tmp, 188862306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 188962306a36Sopenharmony_ci if (entry != tmp && 189062306a36Sopenharmony_ci !memcmp(tmp->atm_addr, atm_addr, ATM_ESA_LEN)) { 189162306a36Sopenharmony_ci /* Vcc to this host exists */ 189262306a36Sopenharmony_ci if (tmp->status > ESI_VC_PENDING) { 189362306a36Sopenharmony_ci /* 189462306a36Sopenharmony_ci * ESI_FLUSH_PENDING, 189562306a36Sopenharmony_ci * ESI_FORWARD_DIRECT 189662306a36Sopenharmony_ci */ 189762306a36Sopenharmony_ci entry->vcc = tmp->vcc; 189862306a36Sopenharmony_ci entry->old_push = tmp->old_push; 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci entry->status = tmp->status; 190162306a36Sopenharmony_ci break; 190262306a36Sopenharmony_ci } 190362306a36Sopenharmony_ci } 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci if (remoteflag) 190662306a36Sopenharmony_ci entry->flags |= LEC_REMOTE_FLAG; 190762306a36Sopenharmony_ci else 190862306a36Sopenharmony_ci entry->flags &= ~LEC_REMOTE_FLAG; 190962306a36Sopenharmony_ci if (entry->status == ESI_ARP_PENDING || entry->status == ESI_UNKNOWN) { 191062306a36Sopenharmony_ci entry->status = ESI_VC_PENDING; 191162306a36Sopenharmony_ci send_to_lecd(priv, l_svc_setup, entry->mac_addr, atm_addr, NULL); 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci pr_debug("After update2\n"); 191462306a36Sopenharmony_ci dump_arp_table(priv); 191562306a36Sopenharmony_ciout: 191662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci/* 192062306a36Sopenharmony_ci * Notifies: Vcc setup ready 192162306a36Sopenharmony_ci */ 192262306a36Sopenharmony_cistatic void 192362306a36Sopenharmony_cilec_vcc_added(struct lec_priv *priv, const struct atmlec_ioc *ioc_data, 192462306a36Sopenharmony_ci struct atm_vcc *vcc, 192562306a36Sopenharmony_ci void (*old_push) (struct atm_vcc *vcc, struct sk_buff *skb)) 192662306a36Sopenharmony_ci{ 192762306a36Sopenharmony_ci unsigned long flags; 192862306a36Sopenharmony_ci struct lec_arp_table *entry; 192962306a36Sopenharmony_ci int i, found_entry = 0; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 193262306a36Sopenharmony_ci /* Vcc for Multicast Forward. No timer, LANEv2 7.1.20 and 2.3.5.3 */ 193362306a36Sopenharmony_ci if (ioc_data->receive == 2) { 193462306a36Sopenharmony_ci pr_debug("LEC_ARP: Attaching mcast forward\n"); 193562306a36Sopenharmony_ci#if 0 193662306a36Sopenharmony_ci entry = lec_arp_find(priv, bus_mac); 193762306a36Sopenharmony_ci if (!entry) { 193862306a36Sopenharmony_ci pr_info("LEC_ARP: Multicast entry not found!\n"); 193962306a36Sopenharmony_ci goto out; 194062306a36Sopenharmony_ci } 194162306a36Sopenharmony_ci memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); 194262306a36Sopenharmony_ci entry->recv_vcc = vcc; 194362306a36Sopenharmony_ci entry->old_recv_push = old_push; 194462306a36Sopenharmony_ci#endif 194562306a36Sopenharmony_ci entry = make_entry(priv, bus_mac); 194662306a36Sopenharmony_ci if (entry == NULL) 194762306a36Sopenharmony_ci goto out; 194862306a36Sopenharmony_ci del_timer(&entry->timer); 194962306a36Sopenharmony_ci memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); 195062306a36Sopenharmony_ci entry->recv_vcc = vcc; 195162306a36Sopenharmony_ci entry->old_recv_push = old_push; 195262306a36Sopenharmony_ci hlist_add_head(&entry->next, &priv->mcast_fwds); 195362306a36Sopenharmony_ci goto out; 195462306a36Sopenharmony_ci } else if (ioc_data->receive == 1) { 195562306a36Sopenharmony_ci /* 195662306a36Sopenharmony_ci * Vcc which we don't want to make default vcc, 195762306a36Sopenharmony_ci * attach it anyway. 195862306a36Sopenharmony_ci */ 195962306a36Sopenharmony_ci pr_debug("LEC_ARP:Attaching data direct, not default: %*phN\n", 196062306a36Sopenharmony_ci ATM_ESA_LEN, ioc_data->atm_addr); 196162306a36Sopenharmony_ci entry = make_entry(priv, bus_mac); 196262306a36Sopenharmony_ci if (entry == NULL) 196362306a36Sopenharmony_ci goto out; 196462306a36Sopenharmony_ci memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); 196562306a36Sopenharmony_ci eth_zero_addr(entry->mac_addr); 196662306a36Sopenharmony_ci entry->recv_vcc = vcc; 196762306a36Sopenharmony_ci entry->old_recv_push = old_push; 196862306a36Sopenharmony_ci entry->status = ESI_UNKNOWN; 196962306a36Sopenharmony_ci entry->timer.expires = jiffies + priv->vcc_timeout_period; 197062306a36Sopenharmony_ci entry->timer.function = lec_arp_expire_vcc; 197162306a36Sopenharmony_ci hlist_add_head(&entry->next, &priv->lec_no_forward); 197262306a36Sopenharmony_ci add_timer(&entry->timer); 197362306a36Sopenharmony_ci dump_arp_table(priv); 197462306a36Sopenharmony_ci goto out; 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci pr_debug("LEC_ARP:Attaching data direct, default: %*phN\n", 197762306a36Sopenharmony_ci ATM_ESA_LEN, ioc_data->atm_addr); 197862306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 197962306a36Sopenharmony_ci hlist_for_each_entry(entry, 198062306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 198162306a36Sopenharmony_ci if (memcmp 198262306a36Sopenharmony_ci (ioc_data->atm_addr, entry->atm_addr, 198362306a36Sopenharmony_ci ATM_ESA_LEN) == 0) { 198462306a36Sopenharmony_ci pr_debug("LEC_ARP: Attaching data direct\n"); 198562306a36Sopenharmony_ci pr_debug("Currently -> Vcc: %d, Rvcc:%d\n", 198662306a36Sopenharmony_ci entry->vcc ? entry->vcc->vci : 0, 198762306a36Sopenharmony_ci entry->recv_vcc ? entry->recv_vcc-> 198862306a36Sopenharmony_ci vci : 0); 198962306a36Sopenharmony_ci found_entry = 1; 199062306a36Sopenharmony_ci del_timer(&entry->timer); 199162306a36Sopenharmony_ci entry->vcc = vcc; 199262306a36Sopenharmony_ci entry->old_push = old_push; 199362306a36Sopenharmony_ci if (entry->status == ESI_VC_PENDING) { 199462306a36Sopenharmony_ci if (priv->maximum_unknown_frame_count 199562306a36Sopenharmony_ci == 0) 199662306a36Sopenharmony_ci entry->status = 199762306a36Sopenharmony_ci ESI_FORWARD_DIRECT; 199862306a36Sopenharmony_ci else { 199962306a36Sopenharmony_ci entry->timestamp = jiffies; 200062306a36Sopenharmony_ci entry->status = 200162306a36Sopenharmony_ci ESI_FLUSH_PENDING; 200262306a36Sopenharmony_ci#if 0 200362306a36Sopenharmony_ci send_to_lecd(priv, l_flush_xmt, 200462306a36Sopenharmony_ci NULL, 200562306a36Sopenharmony_ci entry->atm_addr, 200662306a36Sopenharmony_ci NULL); 200762306a36Sopenharmony_ci#endif 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci } else { 201062306a36Sopenharmony_ci /* 201162306a36Sopenharmony_ci * They were forming a connection 201262306a36Sopenharmony_ci * to us, and we to them. Our 201362306a36Sopenharmony_ci * ATM address is numerically lower 201462306a36Sopenharmony_ci * than theirs, so we make connection 201562306a36Sopenharmony_ci * we formed into default VCC (8.1.11). 201662306a36Sopenharmony_ci * Connection they made gets torn 201762306a36Sopenharmony_ci * down. This might confuse some 201862306a36Sopenharmony_ci * clients. Can be changed if 201962306a36Sopenharmony_ci * someone reports trouble... 202062306a36Sopenharmony_ci */ 202162306a36Sopenharmony_ci ; 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci if (found_entry) { 202762306a36Sopenharmony_ci pr_debug("After vcc was added\n"); 202862306a36Sopenharmony_ci dump_arp_table(priv); 202962306a36Sopenharmony_ci goto out; 203062306a36Sopenharmony_ci } 203162306a36Sopenharmony_ci /* 203262306a36Sopenharmony_ci * Not found, snatch address from first data packet that arrives 203362306a36Sopenharmony_ci * from this vcc 203462306a36Sopenharmony_ci */ 203562306a36Sopenharmony_ci entry = make_entry(priv, bus_mac); 203662306a36Sopenharmony_ci if (!entry) 203762306a36Sopenharmony_ci goto out; 203862306a36Sopenharmony_ci entry->vcc = vcc; 203962306a36Sopenharmony_ci entry->old_push = old_push; 204062306a36Sopenharmony_ci memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); 204162306a36Sopenharmony_ci eth_zero_addr(entry->mac_addr); 204262306a36Sopenharmony_ci entry->status = ESI_UNKNOWN; 204362306a36Sopenharmony_ci hlist_add_head(&entry->next, &priv->lec_arp_empty_ones); 204462306a36Sopenharmony_ci entry->timer.expires = jiffies + priv->vcc_timeout_period; 204562306a36Sopenharmony_ci entry->timer.function = lec_arp_expire_vcc; 204662306a36Sopenharmony_ci add_timer(&entry->timer); 204762306a36Sopenharmony_ci pr_debug("After vcc was added\n"); 204862306a36Sopenharmony_ci dump_arp_table(priv); 204962306a36Sopenharmony_ciout: 205062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic void lec_flush_complete(struct lec_priv *priv, unsigned long tran_id) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci unsigned long flags; 205662306a36Sopenharmony_ci struct lec_arp_table *entry; 205762306a36Sopenharmony_ci int i; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci pr_debug("%lx\n", tran_id); 206062306a36Sopenharmony_cirestart: 206162306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 206262306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 206362306a36Sopenharmony_ci hlist_for_each_entry(entry, 206462306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 206562306a36Sopenharmony_ci if (entry->flush_tran_id == tran_id && 206662306a36Sopenharmony_ci entry->status == ESI_FLUSH_PENDING) { 206762306a36Sopenharmony_ci struct sk_buff *skb; 206862306a36Sopenharmony_ci struct atm_vcc *vcc = entry->vcc; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci lec_arp_hold(entry); 207162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, 207262306a36Sopenharmony_ci flags); 207362306a36Sopenharmony_ci while ((skb = skb_dequeue(&entry->tx_wait))) 207462306a36Sopenharmony_ci lec_send(vcc, skb); 207562306a36Sopenharmony_ci entry->last_used = jiffies; 207662306a36Sopenharmony_ci entry->status = ESI_FORWARD_DIRECT; 207762306a36Sopenharmony_ci lec_arp_put(entry); 207862306a36Sopenharmony_ci pr_debug("LEC_ARP: Flushed\n"); 207962306a36Sopenharmony_ci goto restart; 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci } 208362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 208462306a36Sopenharmony_ci dump_arp_table(priv); 208562306a36Sopenharmony_ci} 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_cistatic void 208862306a36Sopenharmony_cilec_set_flush_tran_id(struct lec_priv *priv, 208962306a36Sopenharmony_ci const unsigned char *atm_addr, unsigned long tran_id) 209062306a36Sopenharmony_ci{ 209162306a36Sopenharmony_ci unsigned long flags; 209262306a36Sopenharmony_ci struct lec_arp_table *entry; 209362306a36Sopenharmony_ci int i; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 209662306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) 209762306a36Sopenharmony_ci hlist_for_each_entry(entry, 209862306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 209962306a36Sopenharmony_ci if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN)) { 210062306a36Sopenharmony_ci entry->flush_tran_id = tran_id; 210162306a36Sopenharmony_ci pr_debug("Set flush transaction id to %lx for %p\n", 210262306a36Sopenharmony_ci tran_id, entry); 210362306a36Sopenharmony_ci } 210462306a36Sopenharmony_ci } 210562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 210662306a36Sopenharmony_ci} 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_cistatic int lec_mcast_make(struct lec_priv *priv, struct atm_vcc *vcc) 210962306a36Sopenharmony_ci{ 211062306a36Sopenharmony_ci unsigned long flags; 211162306a36Sopenharmony_ci unsigned char mac_addr[] = { 211262306a36Sopenharmony_ci 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 211362306a36Sopenharmony_ci }; 211462306a36Sopenharmony_ci struct lec_arp_table *to_add; 211562306a36Sopenharmony_ci struct lec_vcc_priv *vpriv; 211662306a36Sopenharmony_ci int err = 0; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci vpriv = kmalloc(sizeof(struct lec_vcc_priv), GFP_KERNEL); 211962306a36Sopenharmony_ci if (!vpriv) 212062306a36Sopenharmony_ci return -ENOMEM; 212162306a36Sopenharmony_ci vpriv->xoff = 0; 212262306a36Sopenharmony_ci vpriv->old_pop = vcc->pop; 212362306a36Sopenharmony_ci vcc->user_back = vpriv; 212462306a36Sopenharmony_ci vcc->pop = lec_pop; 212562306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 212662306a36Sopenharmony_ci to_add = make_entry(priv, mac_addr); 212762306a36Sopenharmony_ci if (!to_add) { 212862306a36Sopenharmony_ci vcc->pop = vpriv->old_pop; 212962306a36Sopenharmony_ci kfree(vpriv); 213062306a36Sopenharmony_ci err = -ENOMEM; 213162306a36Sopenharmony_ci goto out; 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci memcpy(to_add->atm_addr, vcc->remote.sas_addr.prv, ATM_ESA_LEN); 213462306a36Sopenharmony_ci to_add->status = ESI_FORWARD_DIRECT; 213562306a36Sopenharmony_ci to_add->flags |= LEC_PERMANENT_FLAG; 213662306a36Sopenharmony_ci to_add->vcc = vcc; 213762306a36Sopenharmony_ci to_add->old_push = vcc->push; 213862306a36Sopenharmony_ci vcc->push = lec_push; 213962306a36Sopenharmony_ci priv->mcast_vcc = vcc; 214062306a36Sopenharmony_ci lec_arp_add(priv, to_add); 214162306a36Sopenharmony_ciout: 214262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 214362306a36Sopenharmony_ci return err; 214462306a36Sopenharmony_ci} 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_cistatic void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc) 214762306a36Sopenharmony_ci{ 214862306a36Sopenharmony_ci unsigned long flags; 214962306a36Sopenharmony_ci struct hlist_node *next; 215062306a36Sopenharmony_ci struct lec_arp_table *entry; 215162306a36Sopenharmony_ci int i; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci pr_debug("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n", vcc->vpi, vcc->vci); 215462306a36Sopenharmony_ci dump_arp_table(priv); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { 215962306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 216062306a36Sopenharmony_ci &priv->lec_arp_tables[i], next) { 216162306a36Sopenharmony_ci if (vcc == entry->vcc) { 216262306a36Sopenharmony_ci lec_arp_remove(priv, entry); 216362306a36Sopenharmony_ci lec_arp_put(entry); 216462306a36Sopenharmony_ci if (priv->mcast_vcc == vcc) 216562306a36Sopenharmony_ci priv->mcast_vcc = NULL; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci } 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 217162306a36Sopenharmony_ci &priv->lec_arp_empty_ones, next) { 217262306a36Sopenharmony_ci if (entry->vcc == vcc) { 217362306a36Sopenharmony_ci lec_arp_clear_vccs(entry); 217462306a36Sopenharmony_ci del_timer(&entry->timer); 217562306a36Sopenharmony_ci hlist_del(&entry->next); 217662306a36Sopenharmony_ci lec_arp_put(entry); 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 218162306a36Sopenharmony_ci &priv->lec_no_forward, next) { 218262306a36Sopenharmony_ci if (entry->recv_vcc == vcc) { 218362306a36Sopenharmony_ci lec_arp_clear_vccs(entry); 218462306a36Sopenharmony_ci del_timer(&entry->timer); 218562306a36Sopenharmony_ci hlist_del(&entry->next); 218662306a36Sopenharmony_ci lec_arp_put(entry); 218762306a36Sopenharmony_ci } 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, &priv->mcast_fwds, next) { 219162306a36Sopenharmony_ci if (entry->recv_vcc == vcc) { 219262306a36Sopenharmony_ci lec_arp_clear_vccs(entry); 219362306a36Sopenharmony_ci /* No timer, LANEv2 7.1.20 and 2.3.5.3 */ 219462306a36Sopenharmony_ci hlist_del(&entry->next); 219562306a36Sopenharmony_ci lec_arp_put(entry); 219662306a36Sopenharmony_ci } 219762306a36Sopenharmony_ci } 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 220062306a36Sopenharmony_ci dump_arp_table(priv); 220162306a36Sopenharmony_ci} 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_cistatic void 220462306a36Sopenharmony_cilec_arp_check_empties(struct lec_priv *priv, 220562306a36Sopenharmony_ci struct atm_vcc *vcc, struct sk_buff *skb) 220662306a36Sopenharmony_ci{ 220762306a36Sopenharmony_ci unsigned long flags; 220862306a36Sopenharmony_ci struct hlist_node *next; 220962306a36Sopenharmony_ci struct lec_arp_table *entry, *tmp; 221062306a36Sopenharmony_ci struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data; 221162306a36Sopenharmony_ci unsigned char *src = hdr->h_source; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci spin_lock_irqsave(&priv->lec_arp_lock, flags); 221462306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, next, 221562306a36Sopenharmony_ci &priv->lec_arp_empty_ones, next) { 221662306a36Sopenharmony_ci if (vcc == entry->vcc) { 221762306a36Sopenharmony_ci del_timer(&entry->timer); 221862306a36Sopenharmony_ci ether_addr_copy(entry->mac_addr, src); 221962306a36Sopenharmony_ci entry->status = ESI_FORWARD_DIRECT; 222062306a36Sopenharmony_ci entry->last_used = jiffies; 222162306a36Sopenharmony_ci /* We might have got an entry */ 222262306a36Sopenharmony_ci tmp = lec_arp_find(priv, src); 222362306a36Sopenharmony_ci if (tmp) { 222462306a36Sopenharmony_ci lec_arp_remove(priv, tmp); 222562306a36Sopenharmony_ci lec_arp_put(tmp); 222662306a36Sopenharmony_ci } 222762306a36Sopenharmony_ci hlist_del(&entry->next); 222862306a36Sopenharmony_ci lec_arp_add(priv, entry); 222962306a36Sopenharmony_ci goto out; 223062306a36Sopenharmony_ci } 223162306a36Sopenharmony_ci } 223262306a36Sopenharmony_ci pr_debug("LEC_ARP: Arp_check_empties: entry not found!\n"); 223362306a36Sopenharmony_ciout: 223462306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lec_arp_lock, flags); 223562306a36Sopenharmony_ci} 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2238