diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/atm/Kconfig | 37 | ||||
| -rw-r--r-- | net/atm/Makefile | 4 | ||||
| -rw-r--r-- | net/atm/clip.c | 960 | ||||
| -rw-r--r-- | net/atm/ioctl.c | 14 | ||||
| -rw-r--r-- | net/atm/lec.c | 2274 | ||||
| -rw-r--r-- | net/atm/lec.h | 155 | ||||
| -rw-r--r-- | net/atm/lec_arpc.h | 97 | ||||
| -rw-r--r-- | net/atm/mpc.c | 1538 | ||||
| -rw-r--r-- | net/atm/mpc.h | 65 | ||||
| -rw-r--r-- | net/atm/mpoa_caches.c | 565 | ||||
| -rw-r--r-- | net/atm/mpoa_caches.h | 99 | ||||
| -rw-r--r-- | net/atm/mpoa_proc.c | 307 | ||||
| -rw-r--r-- | net/atm/proc.c | 11 | ||||
| -rw-r--r-- | net/bridge/br.c | 7 | ||||
| -rw-r--r-- | net/bridge/br_fdb.c | 29 | ||||
| -rw-r--r-- | net/bridge/br_private.h | 4 | ||||
| -rw-r--r-- | net/core/dev.c | 7 |
17 files changed, 0 insertions, 6173 deletions
diff --git a/net/atm/Kconfig b/net/atm/Kconfig index 77343d57ff2a..dfdc3a8553ba 100644 --- a/net/atm/Kconfig +++ b/net/atm/Kconfig @@ -19,43 +19,6 @@ config ATM of ATM. See the file <file:Documentation/networking/atm.rst> for further details. -config ATM_CLIP - tristate "Classical IP over ATM" - depends on ATM && INET - help - Classical IP over ATM for PVCs and SVCs, supporting InARP and - ATMARP. If you want to communication with other IP hosts on your ATM - network, you will typically either say Y here or to "LAN Emulation - (LANE)" below. - -config ATM_CLIP_NO_ICMP - bool "Do NOT send ICMP if no neighbour" - depends on ATM_CLIP - help - Normally, an "ICMP host unreachable" message is sent if a neighbour - cannot be reached because there is no VC to it in the kernel's - ATMARP table. This may cause problems when ATMARP table entries are - briefly removed during revalidation. If you say Y here, packets to - such neighbours are silently discarded instead. - -config ATM_LANE - tristate "LAN Emulation (LANE) support" - depends on ATM - help - LAN Emulation emulates services of existing LANs across an ATM - network. Besides operating as a normal ATM end station client, Linux - LANE client can also act as an proxy client bridging packets between - ELAN and Ethernet segments. You need LANE if you want to try MPOA. - -config ATM_MPOA - tristate "Multi-Protocol Over ATM (MPOA) support" - depends on ATM && INET && ATM_LANE!=n - help - Multi-Protocol Over ATM allows ATM edge devices such as routers, - bridges and ATM attached hosts establish direct ATM VCs across - subnetwork boundaries. These shortcut connections bypass routers - enhancing overall network performance. - config ATM_BR2684 tristate "RFC1483/2684 Bridged protocols" depends on ATM && INET diff --git a/net/atm/Makefile b/net/atm/Makefile index bfec0f2d83b5..484a1b1552cc 100644 --- a/net/atm/Makefile +++ b/net/atm/Makefile @@ -4,13 +4,9 @@ # atm-y := addr.o pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o -mpoa-objs := mpc.o mpoa_caches.o mpoa_proc.o obj-$(CONFIG_ATM) += atm.o -obj-$(CONFIG_ATM_CLIP) += clip.o obj-$(CONFIG_ATM_BR2684) += br2684.o atm-$(CONFIG_PROC_FS) += proc.o -obj-$(CONFIG_ATM_LANE) += lec.o -obj-$(CONFIG_ATM_MPOA) += mpoa.o obj-$(CONFIG_PPPOATM) += pppoatm.o diff --git a/net/atm/clip.c b/net/atm/clip.c deleted file mode 100644 index 516b2214680b..000000000000 --- a/net/atm/clip.c +++ /dev/null @@ -1,960 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* net/atm/clip.c - RFC1577 Classical IP over ATM */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/kernel.h> /* for UINT_MAX */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/wait.h> -#include <linux/timer.h> -#include <linux/if_arp.h> /* for some manifest constants */ -#include <linux/notifier.h> -#include <linux/atm.h> -#include <linux/atmdev.h> -#include <linux/atmclip.h> -#include <linux/atmarp.h> -#include <linux/capability.h> -#include <linux/ip.h> /* for net/route.h */ -#include <linux/in.h> /* for struct sockaddr_in */ -#include <linux/if.h> /* for IFF_UP */ -#include <linux/inetdevice.h> -#include <linux/bitops.h> -#include <linux/poison.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/rcupdate.h> -#include <linux/jhash.h> -#include <linux/slab.h> -#include <net/route.h> /* for struct rtable and routing */ -#include <net/icmp.h> /* icmp_send */ -#include <net/arp.h> -#include <linux/param.h> /* for HZ */ -#include <linux/uaccess.h> -#include <asm/byteorder.h> /* for htons etc. */ -#include <linux/atomic.h> - -#include "common.h" -#include "resources.h" -#include <net/atmclip.h> - -static struct net_device *clip_devs; -static struct atm_vcc __rcu *atmarpd; -static DEFINE_MUTEX(atmarpd_lock); -static struct timer_list idle_timer; -static const struct neigh_ops clip_neigh_ops; - -static int to_atmarpd(enum atmarp_ctrl_type type, int itf, __be32 ip) -{ - struct sock *sk; - struct atmarp_ctrl *ctrl; - struct atm_vcc *vcc; - struct sk_buff *skb; - int err = 0; - - pr_debug("(%d)\n", type); - - rcu_read_lock(); - vcc = rcu_dereference(atmarpd); - if (!vcc) { - err = -EUNATCH; - goto unlock; - } - skb = alloc_skb(sizeof(struct atmarp_ctrl), GFP_ATOMIC); - if (!skb) { - err = -ENOMEM; - goto unlock; - } - ctrl = skb_put(skb, sizeof(struct atmarp_ctrl)); - ctrl->type = type; - ctrl->itf_num = itf; - ctrl->ip = ip; - atm_force_charge(vcc, skb->truesize); - - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); -unlock: - rcu_read_unlock(); - return err; -} - -static void link_vcc(struct clip_vcc *clip_vcc, struct atmarp_entry *entry) -{ - pr_debug("%p to entry %p (neigh %p)\n", clip_vcc, entry, entry->neigh); - clip_vcc->entry = entry; - clip_vcc->xoff = 0; /* @@@ may overrun buffer by one packet */ - clip_vcc->next = entry->vccs; - entry->vccs = clip_vcc; - entry->neigh->used = jiffies; -} - -static void unlink_clip_vcc(struct clip_vcc *clip_vcc) -{ - struct atmarp_entry *entry = clip_vcc->entry; - struct clip_vcc **walk; - - if (!entry) { - pr_err("!clip_vcc->entry (clip_vcc %p)\n", clip_vcc); - return; - } - netif_tx_lock_bh(entry->neigh->dev); /* block clip_start_xmit() */ - entry->neigh->used = jiffies; - for (walk = &entry->vccs; *walk; walk = &(*walk)->next) - if (*walk == clip_vcc) { - int error; - - *walk = clip_vcc->next; /* atomic */ - clip_vcc->entry = NULL; - if (clip_vcc->xoff) - netif_wake_queue(entry->neigh->dev); - if (entry->vccs) - goto out; - entry->expires = jiffies - 1; - /* force resolution or expiration */ - error = neigh_update(entry->neigh, NULL, NUD_NONE, - NEIGH_UPDATE_F_ADMIN, 0); - if (error) - pr_err("neigh_update failed with %d\n", error); - goto out; - } - pr_err("ATMARP: failed (entry %p, vcc 0x%p)\n", entry, clip_vcc); -out: - netif_tx_unlock_bh(entry->neigh->dev); -} - -/* The neighbour entry n->lock is held. */ -static int neigh_check_cb(struct neighbour *n) -{ - struct atmarp_entry *entry = neighbour_priv(n); - struct clip_vcc *cv; - - if (n->ops != &clip_neigh_ops) - return 0; - for (cv = entry->vccs; cv; cv = cv->next) { - unsigned long exp = cv->last_use + cv->idle_timeout; - - if (cv->idle_timeout && time_after(jiffies, exp)) { - pr_debug("releasing vcc %p->%p of entry %p\n", - cv, cv->vcc, entry); - vcc_release_async(cv->vcc, -ETIMEDOUT); - } - } - - if (entry->vccs || time_before(jiffies, entry->expires)) - return 0; - - if (refcount_read(&n->refcnt) > 1) { - struct sk_buff *skb; - - pr_debug("destruction postponed with ref %d\n", - refcount_read(&n->refcnt)); - - while ((skb = skb_dequeue(&n->arp_queue)) != NULL) - dev_kfree_skb(skb); - - return 0; - } - - pr_debug("expired neigh %p\n", n); - return 1; -} - -static void idle_timer_check(struct timer_list *unused) -{ - spin_lock(&arp_tbl.lock); - __neigh_for_each_release(&arp_tbl, neigh_check_cb); - mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ); - spin_unlock(&arp_tbl.lock); -} - -static int clip_arp_rcv(struct sk_buff *skb) -{ - struct atm_vcc *vcc; - - pr_debug("\n"); - vcc = ATM_SKB(skb)->vcc; - if (!vcc || !atm_charge(vcc, skb->truesize)) { - dev_kfree_skb_any(skb); - return 0; - } - pr_debug("pushing to %p\n", vcc); - pr_debug("using %p\n", CLIP_VCC(vcc)->old_push); - CLIP_VCC(vcc)->old_push(vcc, skb); - return 0; -} - -static const unsigned char llc_oui[] = { - 0xaa, /* DSAP: non-ISO */ - 0xaa, /* SSAP: non-ISO */ - 0x03, /* Ctrl: Unnumbered Information Command PDU */ - 0x00, /* OUI: EtherType */ - 0x00, - 0x00 -}; - -static void clip_push(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct clip_vcc *clip_vcc = CLIP_VCC(vcc); - - pr_debug("\n"); - - if (!skb) { - pr_debug("removing VCC %p\n", clip_vcc); - if (clip_vcc->entry) - unlink_clip_vcc(clip_vcc); - clip_vcc->old_push(vcc, NULL); /* pass on the bad news */ - kfree(clip_vcc); - return; - } - atm_return(vcc, skb->truesize); - if (!clip_devs) { - kfree_skb(skb); - return; - } - - skb->dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : clip_devs; - /* clip_vcc->entry == NULL if we don't have an IP address yet */ - if (!skb->dev) { - dev_kfree_skb_any(skb); - return; - } - ATM_SKB(skb)->vcc = vcc; - skb_reset_mac_header(skb); - if (!clip_vcc->encap || - skb->len < RFC1483LLC_LEN || - memcmp(skb->data, llc_oui, sizeof(llc_oui))) - skb->protocol = htons(ETH_P_IP); - else { - skb->protocol = ((__be16 *)skb->data)[3]; - skb_pull(skb, RFC1483LLC_LEN); - if (skb->protocol == htons(ETH_P_ARP)) { - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - clip_arp_rcv(skb); - return; - } - } - clip_vcc->last_use = jiffies; - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); - netif_rx(skb); -} - -/* - * Note: these spinlocks _must_not_ block on non-SMP. The only goal is that - * clip_pop is atomic with respect to the critical section in clip_start_xmit. - */ - -static void clip_pop(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct clip_vcc *clip_vcc = CLIP_VCC(vcc); - struct net_device *dev = skb->dev; - int old; - unsigned long flags; - - pr_debug("(vcc %p)\n", vcc); - clip_vcc->old_pop(vcc, skb); - /* skb->dev == NULL in outbound ARP packets */ - if (!dev) - return; - spin_lock_irqsave(&PRIV(dev)->xoff_lock, flags); - if (atm_may_send(vcc, 0)) { - old = xchg(&clip_vcc->xoff, 0); - if (old) - netif_wake_queue(dev); - } - spin_unlock_irqrestore(&PRIV(dev)->xoff_lock, flags); -} - -static void clip_neigh_solicit(struct neighbour *neigh, struct sk_buff *skb) -{ - __be32 *ip = (__be32 *) neigh->primary_key; - - pr_debug("(neigh %p, skb %p)\n", neigh, skb); - to_atmarpd(act_need, PRIV(neigh->dev)->number, *ip); -} - -static void clip_neigh_error(struct neighbour *neigh, struct sk_buff *skb) -{ -#ifndef CONFIG_ATM_CLIP_NO_ICMP - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); -#endif - kfree_skb(skb); -} - -static const struct neigh_ops clip_neigh_ops = { - .family = AF_INET, - .solicit = clip_neigh_solicit, - .error_report = clip_neigh_error, - .output = neigh_direct_output, - .connected_output = neigh_direct_output, -}; - -static int clip_constructor(struct net_device *dev, struct neighbour *neigh) -{ - struct atmarp_entry *entry = neighbour_priv(neigh); - - if (neigh->tbl->family != AF_INET) - return -EINVAL; - - if (neigh->type != RTN_UNICAST) - return -EINVAL; - - neigh->nud_state = NUD_NONE; - neigh->ops = &clip_neigh_ops; - neigh->output = neigh->ops->output; - entry->neigh = neigh; - entry->vccs = NULL; - entry->expires = jiffies - 1; - - return 0; -} - -/* @@@ copy bh locking from arp.c -- need to bh-enable atm code before */ - -/* - * We play with the resolve flag: 0 and 1 have the usual meaning, but -1 means - * to allocate the neighbour entry but not to ask atmarpd for resolution. Also, - * don't increment the usage count. This is used to create entries in - * clip_setentry. - */ - -static int clip_encap(struct atm_vcc *vcc, int mode) -{ - if (!CLIP_VCC(vcc)) - return -EBADFD; - - CLIP_VCC(vcc)->encap = mode; - return 0; -} - -static netdev_tx_t clip_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct clip_priv *clip_priv = PRIV(dev); - struct dst_entry *dst = skb_dst(skb); - struct atmarp_entry *entry; - struct neighbour *n; - struct atm_vcc *vcc; - struct rtable *rt; - __be32 *daddr; - int old; - unsigned long flags; - - pr_debug("(skb %p)\n", skb); - if (!dst) { - pr_err("skb_dst(skb) == NULL\n"); - dev_kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - rt = dst_rtable(dst); - if (rt->rt_gw_family == AF_INET) - daddr = &rt->rt_gw4; - else - daddr = &ip_hdr(skb)->daddr; - n = dst_neigh_lookup(dst, daddr); - if (!n) { - pr_err("NO NEIGHBOUR !\n"); - dev_kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - entry = neighbour_priv(n); - if (!entry->vccs) { - if (time_after(jiffies, entry->expires)) { - /* should be resolved */ - entry->expires = jiffies + ATMARP_RETRY_DELAY * HZ; - to_atmarpd(act_need, PRIV(dev)->number, *((__be32 *)n->primary_key)); - } - if (entry->neigh->arp_queue.qlen < ATMARP_MAX_UNRES_PACKETS) - skb_queue_tail(&entry->neigh->arp_queue, skb); - else { - dev_kfree_skb(skb); - dev->stats.tx_dropped++; - } - goto out_release_neigh; - } - pr_debug("neigh %p, vccs %p\n", entry, entry->vccs); - ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc; - pr_debug("using neighbour %p, vcc %p\n", n, vcc); - if (entry->vccs->encap) { - void *here; - - here = skb_push(skb, RFC1483LLC_LEN); - memcpy(here, llc_oui, sizeof(llc_oui)); - ((__be16 *) here)[3] = skb->protocol; - } - atm_account_tx(vcc, skb); - entry->vccs->last_use = jiffies; - pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, vcc, vcc->dev); - old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */ - if (old) { - pr_warn("XOFF->XOFF transition\n"); - goto out_release_neigh; - } - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - vcc->send(vcc, skb); - if (atm_may_send(vcc, 0)) { - entry->vccs->xoff = 0; - goto out_release_neigh; - } - spin_lock_irqsave(&clip_priv->xoff_lock, flags); - netif_stop_queue(dev); /* XOFF -> throttle immediately */ - barrier(); - if (!entry->vccs->xoff) - netif_start_queue(dev); - /* Oh, we just raced with clip_pop. netif_start_queue should be - good enough, because nothing should really be asleep because - of the brief netif_stop_queue. If this isn't true or if it - changes, use netif_wake_queue instead. */ - spin_unlock_irqrestore(&clip_priv->xoff_lock, flags); -out_release_neigh: - neigh_release(n); - return NETDEV_TX_OK; -} - -static int clip_mkip(struct atm_vcc *vcc, int timeout) -{ - struct clip_vcc *clip_vcc; - - if (!vcc->push) - return -EBADFD; - if (vcc->user_back) - return -EINVAL; - clip_vcc = kmalloc_obj(struct clip_vcc); - if (!clip_vcc) - return -ENOMEM; - pr_debug("%p vcc %p\n", clip_vcc, vcc); - clip_vcc->vcc = vcc; - vcc->user_back = clip_vcc; - set_bit(ATM_VF_IS_CLIP, &vcc->flags); - clip_vcc->entry = NULL; - clip_vcc->xoff = 0; - clip_vcc->encap = 1; - clip_vcc->last_use = jiffies; - clip_vcc->idle_timeout = timeout * HZ; - clip_vcc->old_push = vcc->push; - clip_vcc->old_pop = vcc->pop; - vcc->push = clip_push; - vcc->pop = clip_pop; - - /* re-process everything received between connection setup and MKIP */ - vcc_process_recv_queue(vcc); - - return 0; -} - -static int clip_setentry(struct atm_vcc *vcc, __be32 ip) -{ - struct neighbour *neigh; - struct atmarp_entry *entry; - int error; - struct clip_vcc *clip_vcc; - struct rtable *rt; - - if (vcc->push != clip_push) { - pr_warn("non-CLIP VCC\n"); - return -EBADF; - } - clip_vcc = CLIP_VCC(vcc); - if (!ip) { - if (!clip_vcc->entry) { - pr_err("hiding hidden ATMARP entry\n"); - return 0; - } - pr_debug("remove\n"); - unlink_clip_vcc(clip_vcc); - return 0; - } - rt = ip_route_output(&init_net, ip, 0, 0, 0, RT_SCOPE_LINK); - if (IS_ERR(rt)) - return PTR_ERR(rt); - neigh = __neigh_lookup(&arp_tbl, &ip, rt->dst.dev, 1); - ip_rt_put(rt); - if (!neigh) - return -ENOMEM; - entry = neighbour_priv(neigh); - if (entry != clip_vcc->entry) { - if (!clip_vcc->entry) - pr_debug("add\n"); - else { - pr_debug("update\n"); - unlink_clip_vcc(clip_vcc); - } - link_vcc(clip_vcc, entry); - } - error = neigh_update(neigh, llc_oui, NUD_PERMANENT, - NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 0); - neigh_release(neigh); - return error; -} - -static const struct net_device_ops clip_netdev_ops = { - .ndo_start_xmit = clip_start_xmit, - .ndo_neigh_construct = clip_constructor, -}; - -static void clip_setup(struct net_device *dev) -{ - dev->netdev_ops = &clip_netdev_ops; - dev->type = ARPHRD_ATM; - dev->neigh_priv_len = sizeof(struct atmarp_entry); - dev->hard_header_len = RFC1483LLC_LEN; - dev->mtu = RFC1626_MTU; - dev->tx_queue_len = 100; /* "normal" queue (packets) */ - /* When using a "real" qdisc, the qdisc determines the queue */ - /* length. tx_queue_len is only used for the default case, */ - /* without any more elaborate queuing. 100 is a reasonable */ - /* compromise between decent burst-tolerance and protection */ - /* against memory hogs. */ - netif_keep_dst(dev); -} - -static int clip_create(int number) -{ - struct net_device *dev; - struct clip_priv *clip_priv; - int error; - - if (number != -1) { - for (dev = clip_devs; dev; dev = PRIV(dev)->next) - if (PRIV(dev)->number == number) - return -EEXIST; - } else { - number = 0; - for (dev = clip_devs; dev; dev = PRIV(dev)->next) - if (PRIV(dev)->number >= number) - number = PRIV(dev)->number + 1; - } - dev = alloc_netdev(sizeof(struct clip_priv), "", NET_NAME_UNKNOWN, - clip_setup); - if (!dev) - return -ENOMEM; - clip_priv = PRIV(dev); - sprintf(dev->name, "atm%d", number); - spin_lock_init(&clip_priv->xoff_lock); - clip_priv->number = number; - error = register_netdev(dev); - if (error) { - free_netdev(dev); - return error; - } - clip_priv->next = clip_devs; - clip_devs = dev; - pr_debug("registered (net:%s)\n", dev->name); - return number; -} - -static int clip_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event == NETDEV_UNREGISTER) - return NOTIFY_DONE; - - /* ignore non-CLIP devices */ - if (dev->type != ARPHRD_ATM || dev->netdev_ops != &clip_netdev_ops) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: - pr_debug("NETDEV_UP\n"); - to_atmarpd(act_up, PRIV(dev)->number, 0); - break; - case NETDEV_GOING_DOWN: - pr_debug("NETDEV_DOWN\n"); - to_atmarpd(act_down, PRIV(dev)->number, 0); - break; - case NETDEV_CHANGE: - case NETDEV_CHANGEMTU: - pr_debug("NETDEV_CHANGE*\n"); - to_atmarpd(act_change, PRIV(dev)->number, 0); - break; - } - return NOTIFY_DONE; -} - -static int clip_inet_event(struct notifier_block *this, unsigned long event, - void *ifa) -{ - struct in_device *in_dev; - struct netdev_notifier_info info; - - in_dev = ((struct in_ifaddr *)ifa)->ifa_dev; - /* - * Transitions are of the down-change-up type, so it's sufficient to - * handle the change on up. - */ - if (event != NETDEV_UP) - return NOTIFY_DONE; - netdev_notifier_info_init(&info, in_dev->dev); - return clip_device_event(this, NETDEV_CHANGE, &info); -} - -static struct notifier_block clip_dev_notifier = { - .notifier_call = clip_device_event, -}; - - - -static struct notifier_block clip_inet_notifier = { - .notifier_call = clip_inet_event, -}; - - - -static void atmarpd_close(struct atm_vcc *vcc) -{ - pr_debug("\n"); - - mutex_lock(&atmarpd_lock); - RCU_INIT_POINTER(atmarpd, NULL); - mutex_unlock(&atmarpd_lock); - - synchronize_rcu(); - skb_queue_purge(&sk_atm(vcc)->sk_receive_queue); - - pr_debug("(done)\n"); - module_put(THIS_MODULE); -} - -static int atmarpd_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - atm_return_tx(vcc, skb); - dev_kfree_skb_any(skb); - return 0; -} - -static const struct atmdev_ops atmarpd_dev_ops = { - .close = atmarpd_close, - .send = atmarpd_send -}; - - -static struct atm_dev atmarpd_dev = { - .ops = &atmarpd_dev_ops, - .type = "arpd", - .number = 999, - .lock = __SPIN_LOCK_UNLOCKED(atmarpd_dev.lock) -}; - - -static int atm_init_atmarp(struct atm_vcc *vcc) -{ - if (vcc->push == clip_push) - return -EINVAL; - - mutex_lock(&atmarpd_lock); - if (atmarpd) { - mutex_unlock(&atmarpd_lock); - return -EADDRINUSE; - } - - mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ); - - rcu_assign_pointer(atmarpd, vcc); - set_bit(ATM_VF_META, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - /* allow replies and avoid getting closed if signaling dies */ - vcc->dev = &atmarpd_dev; - vcc_insert_socket(sk_atm(vcc)); - vcc->push = NULL; - vcc->pop = NULL; /* crash */ - vcc->push_oam = NULL; /* crash */ - mutex_unlock(&atmarpd_lock); - return 0; -} - -static int clip_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct atm_vcc *vcc = ATM_SD(sock); - struct sock *sk = sock->sk; - int err = 0; - - switch (cmd) { - case SIOCMKCLIP: - case ATMARPD_CTRL: - case ATMARP_MKIP: - case ATMARP_SETENTRY: - case ATMARP_ENCAP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - break; - default: - return -ENOIOCTLCMD; - } - - switch (cmd) { - case SIOCMKCLIP: - err = clip_create(arg); - break; - case ATMARPD_CTRL: - lock_sock(sk); - err = atm_init_atmarp(vcc); - if (!err) { - sock->state = SS_CONNECTED; - __module_get(THIS_MODULE); - } - release_sock(sk); - break; - case ATMARP_MKIP: - lock_sock(sk); - err = clip_mkip(vcc, arg); - release_sock(sk); - break; - case ATMARP_SETENTRY: - err = clip_setentry(vcc, (__force __be32)arg); - break; - case ATMARP_ENCAP: - err = clip_encap(vcc, arg); - break; - } - return err; -} - -static struct atm_ioctl clip_ioctl_ops = { - .owner = THIS_MODULE, - .ioctl = clip_ioctl, -}; - -#ifdef CONFIG_PROC_FS - -static void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr) -{ - static int code[] = { 1, 2, 10, 6, 1, 0 }; - static int e164[] = { 1, 8, 4, 6, 1, 0 }; - - if (*addr->sas_addr.pub) { - seq_printf(seq, "%s", addr->sas_addr.pub); - if (*addr->sas_addr.prv) - seq_putc(seq, '+'); - } else if (!*addr->sas_addr.prv) { - seq_printf(seq, "%s", "(none)"); - return; - } - if (*addr->sas_addr.prv) { - unsigned char *prv = addr->sas_addr.prv; - int *fields; - int i, j; - - fields = *prv == ATM_AFI_E164 ? e164 : code; - for (i = 0; fields[i]; i++) { - for (j = fields[i]; j; j--) - seq_printf(seq, "%02X", *prv++); - if (fields[i + 1]) - seq_putc(seq, '.'); - } - } -} - -/* This means the neighbour entry has no attached VCC objects. */ -#define SEQ_NO_VCC_TOKEN ((void *) 2) - -static void atmarp_info(struct seq_file *seq, struct neighbour *n, - struct atmarp_entry *entry, struct clip_vcc *clip_vcc) -{ - struct net_device *dev = n->dev; - unsigned long exp; - char buf[17]; - int svc, llc, off; - - svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || - (sk_atm(clip_vcc->vcc)->sk_family == AF_ATMSVC)); - - llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || clip_vcc->encap); - - if (clip_vcc == SEQ_NO_VCC_TOKEN) - exp = entry->neigh->used; - else - exp = clip_vcc->last_use; - - exp = (jiffies - exp) / HZ; - - seq_printf(seq, "%-6s%-4s%-4s%5ld ", - dev->name, svc ? "SVC" : "PVC", llc ? "LLC" : "NULL", exp); - - off = scnprintf(buf, sizeof(buf) - 1, "%pI4", n->primary_key); - while (off < 16) - buf[off++] = ' '; - buf[off] = '\0'; - seq_printf(seq, "%s", buf); - - if (clip_vcc == SEQ_NO_VCC_TOKEN) { - if (time_before(jiffies, entry->expires)) - seq_printf(seq, "(resolving)\n"); - else - seq_printf(seq, "(expired, ref %d)\n", - refcount_read(&entry->neigh->refcnt)); - } else if (!svc) { - seq_printf(seq, "%d.%d.%d\n", - clip_vcc->vcc->dev->number, - clip_vcc->vcc->vpi, clip_vcc->vcc->vci); - } else { - svc_addr(seq, &clip_vcc->vcc->remote); - seq_putc(seq, '\n'); - } -} - -struct clip_seq_state { - /* This member must be first. */ - struct neigh_seq_state ns; - - /* Local to clip specific iteration. */ - struct clip_vcc *vcc; -}; - -static struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e, - struct clip_vcc *curr) -{ - if (!curr) { - curr = e->vccs; - if (!curr) - return SEQ_NO_VCC_TOKEN; - return curr; - } - if (curr == SEQ_NO_VCC_TOKEN) - return NULL; - - curr = curr->next; - - return curr; -} - -static void *clip_seq_vcc_walk(struct clip_seq_state *state, - struct atmarp_entry *e, loff_t * pos) -{ - struct clip_vcc *vcc = state->vcc; - - vcc = clip_seq_next_vcc(e, vcc); - if (vcc && pos != NULL) { - while (*pos) { - vcc = clip_seq_next_vcc(e, vcc); - if (!vcc) - break; - --(*pos); - } - } - state->vcc = vcc; - - return vcc; -} - -static void *clip_seq_sub_iter(struct neigh_seq_state *_state, - struct neighbour *n, loff_t * pos) -{ - struct clip_seq_state *state = (struct clip_seq_state *)_state; - - if (n->dev->type != ARPHRD_ATM) - return NULL; - - return clip_seq_vcc_walk(state, neighbour_priv(n), pos); -} - -static void *clip_seq_start(struct seq_file *seq, loff_t * pos) -{ - struct clip_seq_state *state = seq->private; - state->ns.neigh_sub_iter = clip_seq_sub_iter; - return neigh_seq_start(seq, pos, &arp_tbl, NEIGH_SEQ_NEIGH_ONLY); -} - -static int clip_seq_show(struct seq_file *seq, void *v) -{ - static char atm_arp_banner[] = - "IPitf TypeEncp Idle IP address ATM address\n"; - - if (v == SEQ_START_TOKEN) { - seq_puts(seq, atm_arp_banner); - } else { - struct clip_seq_state *state = seq->private; - struct clip_vcc *vcc = state->vcc; - struct neighbour *n = v; - - atmarp_info(seq, n, neighbour_priv(n), vcc); - } - return 0; -} - -static const struct seq_operations arp_seq_ops = { - .start = clip_seq_start, - .next = neigh_seq_next, - .stop = neigh_seq_stop, - .show = clip_seq_show, -}; -#endif - -static void atm_clip_exit_noproc(void); - -static int __init atm_clip_init(void) -{ - register_atm_ioctl(&clip_ioctl_ops); - register_netdevice_notifier(&clip_dev_notifier); - register_inetaddr_notifier(&clip_inet_notifier); - - timer_setup(&idle_timer, idle_timer_check, 0); - -#ifdef CONFIG_PROC_FS - { - struct proc_dir_entry *p; - - p = proc_create_net("arp", 0444, atm_proc_root, &arp_seq_ops, - sizeof(struct clip_seq_state)); - if (!p) { - pr_err("Unable to initialize /proc/net/atm/arp\n"); - atm_clip_exit_noproc(); - return -ENOMEM; - } - } -#endif - - return 0; -} - -static void atm_clip_exit_noproc(void) -{ - struct net_device *dev, *next; - - unregister_inetaddr_notifier(&clip_inet_notifier); - unregister_netdevice_notifier(&clip_dev_notifier); - - deregister_atm_ioctl(&clip_ioctl_ops); - - /* First, stop the idle timer, so it stops banging - * on the table. - */ - timer_delete_sync(&idle_timer); - - dev = clip_devs; - while (dev) { - next = PRIV(dev)->next; - unregister_netdev(dev); - free_netdev(dev); - dev = next; - } -} - -static void __exit atm_clip_exit(void) -{ - remove_proc_entry("arp", atm_proc_root); - - atm_clip_exit_noproc(); -} - -module_init(atm_clip_init); -module_exit(atm_clip_exit); -MODULE_AUTHOR("Werner Almesberger"); -MODULE_DESCRIPTION("Classical/IP over ATM interface"); -MODULE_LICENSE("GPL"); diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c index 0f7a39aeccc8..0f3f9ad8301f 100644 --- a/net/atm/ioctl.c +++ b/net/atm/ioctl.c @@ -11,14 +11,10 @@ #include <linux/net.h> /* struct socket, struct proto_ops */ #include <linux/atm.h> /* ATM stuff */ #include <linux/atmdev.h> -#include <linux/atmclip.h> /* CLIP_*ENCAP */ #include <linux/atmarp.h> /* manifest constants */ #include <linux/capability.h> #include <linux/sonet.h> /* for ioctls */ #include <linux/atmsvc.h> -#include <linux/atmmpc.h> -#include <net/atmclip.h> -#include <linux/atmlec.h> #include <linux/mutex.h> #include <asm/ioctls.h> #include <net/compat.h> @@ -138,16 +134,6 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, } break; } - case ATMMPC_CTRL: - case ATMMPC_DATA: - request_module("mpoa"); - break; - case ATMARPD_CTRL: - request_module("clip"); - break; - case ATMLEC_CTRL: - request_module("lec"); - break; } error = -ENOIOCTLCMD; diff --git a/net/atm/lec.c b/net/atm/lec.c deleted file mode 100644 index 10e260acf602..000000000000 --- a/net/atm/lec.c +++ /dev/null @@ -1,2274 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * lec.c: Lan Emulation driver - * - * Marko Kiiskila <mkiiskila@yahoo.com> - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#include <linux/slab.h> -#include <linux/kernel.h> -#include <linux/bitops.h> -#include <linux/capability.h> - -/* We are ethernet device */ -#include <linux/if_ether.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <net/sock.h> -#include <linux/skbuff.h> -#include <linux/ip.h> -#include <asm/byteorder.h> -#include <linux/uaccess.h> -#include <net/arp.h> -#include <net/dst.h> -#include <linux/proc_fs.h> -#include <linux/spinlock.h> -#include <linux/seq_file.h> - -/* And atm device */ -#include <linux/atmdev.h> -#include <linux/atmlec.h> - -/* Proxy LEC knows about bridging */ -#if IS_ENABLED(CONFIG_BRIDGE) -#include "../bridge/br_private.h" - -static unsigned char bridge_ula_lec[] = { 0x01, 0x80, 0xc2, 0x00, 0x00 }; -#endif - -/* Modular too */ -#include <linux/module.h> -#include <linux/init.h> - -/* Hardening for Spectre-v1 */ -#include <linux/nospec.h> - -#include "lec.h" -#include "lec_arpc.h" -#include "resources.h" - -#define DUMP_PACKETS 0 /* - * 0 = None, - * 1 = 30 first bytes - * 2 = Whole packet - */ - -#define LEC_UNRES_QUE_LEN 8 /* - * number of tx packets to queue for a - * single destination while waiting for SVC - */ - -static int lec_open(struct net_device *dev); -static netdev_tx_t lec_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static int lec_close(struct net_device *dev); -static struct lec_arp_table *lec_arp_find(struct lec_priv *priv, - const unsigned char *mac_addr); -static int lec_arp_remove(struct lec_priv *priv, - struct lec_arp_table *to_remove); -/* LANE2 functions */ -static void lane2_associate_ind(struct net_device *dev, const u8 *mac_address, - const u8 *tlvs, u32 sizeoftlvs); -static int lane2_resolve(struct net_device *dev, const u8 *dst_mac, int force, - u8 **tlvs, u32 *sizeoftlvs); -static int lane2_associate_req(struct net_device *dev, const u8 *lan_dst, - const u8 *tlvs, u32 sizeoftlvs); - -static int lec_addr_delete(struct lec_priv *priv, const unsigned char *atm_addr, - unsigned long permanent); -static void lec_arp_check_empties(struct lec_priv *priv, - struct atm_vcc *vcc, struct sk_buff *skb); -static void lec_arp_destroy(struct lec_priv *priv); -static void lec_arp_init(struct lec_priv *priv); -static struct atm_vcc *lec_arp_resolve(struct lec_priv *priv, - const unsigned char *mac_to_find, - int is_rdesc, - struct lec_arp_table **ret_entry); -static void lec_arp_update(struct lec_priv *priv, const unsigned char *mac_addr, - const unsigned char *atm_addr, - unsigned long remoteflag, - unsigned int targetless_le_arp); -static void lec_flush_complete(struct lec_priv *priv, unsigned long tran_id); -static int lec_mcast_make(struct lec_priv *priv, struct atm_vcc *vcc); -static void lec_set_flush_tran_id(struct lec_priv *priv, - const unsigned char *atm_addr, - unsigned long tran_id); -static void lec_vcc_added(struct lec_priv *priv, - const struct atmlec_ioc *ioc_data, - struct atm_vcc *vcc, - void (*old_push)(struct atm_vcc *vcc, - struct sk_buff *skb)); -static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc); - -/* must be done under lec_arp_lock */ -static inline void lec_arp_hold(struct lec_arp_table *entry) -{ - refcount_inc(&entry->usage); -} - -static inline void lec_arp_put(struct lec_arp_table *entry) -{ - if (refcount_dec_and_test(&entry->usage)) - kfree(entry); -} - -static struct lane2_ops lane2_ops = { - .resolve = lane2_resolve, /* spec 3.1.3 */ - .associate_req = lane2_associate_req, /* spec 3.1.4 */ - .associate_indicator = NULL /* spec 3.1.5 */ -}; - -static unsigned char bus_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -/* Device structures */ -static struct net_device *dev_lec[MAX_LEC_ITF]; -static DEFINE_MUTEX(lec_mutex); - -#if IS_ENABLED(CONFIG_BRIDGE) -static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev) -{ - char *buff; - struct lec_priv *priv; - - /* - * Check if this is a BPDU. If so, ask zeppelin to send - * LE_TOPOLOGY_REQUEST with the same value of Topology Change bit - * as the Config BPDU has - */ - buff = skb->data + skb->dev->hard_header_len; - if (*buff++ == 0x42 && *buff++ == 0x42 && *buff++ == 0x03) { - struct sock *sk; - struct sk_buff *skb2; - struct atmlec_msg *mesg; - - skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); - if (skb2 == NULL) - return; - skb2->len = sizeof(struct atmlec_msg); - mesg = (struct atmlec_msg *)skb2->data; - mesg->type = l_topology_change; - buff += 4; - mesg->content.normal.flag = *buff & 0x01; - /* 0x01 is topology change */ - - priv = netdev_priv(dev); - struct atm_vcc *vcc; - - rcu_read_lock(); - vcc = rcu_dereference(priv->lecd); - if (vcc) { - atm_force_charge(vcc, skb2->truesize); - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb2); - sk->sk_data_ready(sk); - } else { - dev_kfree_skb(skb2); - } - rcu_read_unlock(); - } -} -#endif /* IS_ENABLED(CONFIG_BRIDGE) */ - -/* - * Open/initialize the netdevice. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int lec_open(struct net_device *dev) -{ - netif_start_queue(dev); - - return 0; -} - -static void -lec_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int len = skb->len; - - ATM_SKB(skb)->vcc = vcc; - atm_account_tx(vcc, skb); - - if (vcc->send(vcc, skb) < 0) { - dev->stats.tx_dropped++; - return; - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += len; -} - -static void lec_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - pr_info("%s\n", dev->name); - netif_trans_update(dev); - netif_wake_queue(dev); -} - -static netdev_tx_t lec_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct sk_buff *skb2; - struct lec_priv *priv = netdev_priv(dev); - struct lecdatahdr_8023 *lec_h; - struct atm_vcc *vcc; - struct lec_arp_table *entry; - unsigned char *dst; - int min_frame_size; - int is_rdesc; - - pr_debug("called\n"); - if (!rcu_access_pointer(priv->lecd)) { - pr_info("%s:No lecd attached\n", dev->name); - dev->stats.tx_errors++; - netif_stop_queue(dev); - kfree_skb(skb); - return NETDEV_TX_OK; - } - - pr_debug("skbuff head:%lx data:%lx tail:%lx end:%lx\n", - (long)skb->head, (long)skb->data, (long)skb_tail_pointer(skb), - (long)skb_end_pointer(skb)); -#if IS_ENABLED(CONFIG_BRIDGE) - if (memcmp(skb->data, bridge_ula_lec, sizeof(bridge_ula_lec)) == 0) - lec_handle_bridge(skb, dev); -#endif - - /* Make sure we have room for lec_id */ - if (skb_headroom(skb) < 2) { - pr_debug("reallocating skb\n"); - skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN); - if (unlikely(!skb2)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - consume_skb(skb); - skb = skb2; - } - skb_push(skb, 2); - - /* Put le header to place */ - lec_h = (struct lecdatahdr_8023 *)skb->data; - lec_h->le_header = htons(priv->lecid); - -#if DUMP_PACKETS >= 2 -#define MAX_DUMP_SKB 99 -#elif DUMP_PACKETS >= 1 -#define MAX_DUMP_SKB 30 -#endif -#if DUMP_PACKETS >= 1 - printk(KERN_DEBUG "%s: send datalen:%ld lecid:%4.4x\n", - dev->name, skb->len, priv->lecid); - print_hex_dump(KERN_DEBUG, "", DUMP_OFFSET, 16, 1, - skb->data, min(skb->len, MAX_DUMP_SKB), true); -#endif /* DUMP_PACKETS >= 1 */ - - /* Minimum ethernet-frame size */ - min_frame_size = LEC_MINIMUM_8023_SIZE; - if (skb->len < min_frame_size) { - if ((skb->len + skb_tailroom(skb)) < min_frame_size) { - skb2 = skb_copy_expand(skb, 0, - min_frame_size - skb->truesize, - GFP_ATOMIC); - dev_kfree_skb(skb); - if (skb2 == NULL) { - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - skb = skb2; - } - skb_put(skb, min_frame_size - skb->len); - } - - /* Send to right vcc */ - is_rdesc = 0; - dst = lec_h->h_dest; - entry = NULL; - vcc = lec_arp_resolve(priv, dst, is_rdesc, &entry); - pr_debug("%s:vcc:%p vcc_flags:%lx, entry:%p\n", - dev->name, vcc, vcc ? vcc->flags : 0, entry); - if (!vcc || !test_bit(ATM_VF_READY, &vcc->flags)) { - if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) { - pr_debug("%s:queuing packet, MAC address %pM\n", - dev->name, lec_h->h_dest); - skb_queue_tail(&entry->tx_wait, skb); - } else { - pr_debug("%s:tx queue full or no arp entry, dropping, MAC address: %pM\n", - dev->name, lec_h->h_dest); - dev->stats.tx_dropped++; - dev_kfree_skb(skb); - } - goto out; - } -#if DUMP_PACKETS > 0 - printk(KERN_DEBUG "%s:sending to vpi:%d vci:%d\n", - dev->name, vcc->vpi, vcc->vci); -#endif /* DUMP_PACKETS > 0 */ - - while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) { - pr_debug("emptying tx queue, MAC address %pM\n", lec_h->h_dest); - lec_send(vcc, skb2); - } - - lec_send(vcc, skb); - - if (!atm_may_send(vcc, 0)) { - struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); - - vpriv->xoff = 1; - netif_stop_queue(dev); - - /* - * vcc->pop() might have occurred in between, making - * the vcc usuable again. Since xmit is serialized, - * this is the only situation we have to re-test. - */ - - if (atm_may_send(vcc, 0)) - netif_wake_queue(dev); - } - -out: - if (entry) - lec_arp_put(entry); - netif_trans_update(dev); - return NETDEV_TX_OK; -} - -/* The inverse routine to net_open(). */ -static int lec_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - static const u8 zero_addr[ETH_ALEN] = {}; - unsigned long flags; - struct net_device *dev = (struct net_device *)vcc->proto_data; - struct lec_priv *priv = netdev_priv(dev); - struct atmlec_msg *mesg; - struct lec_arp_table *entry; - char *tmp; /* FIXME */ - - WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc)); - mesg = (struct atmlec_msg *)skb->data; - tmp = skb->data; - tmp += sizeof(struct atmlec_msg); - pr_debug("%s: msg from zeppelin:%d\n", dev->name, mesg->type); - switch (mesg->type) { - case l_set_mac_addr: - eth_hw_addr_set(dev, mesg->content.normal.mac_addr); - break; - case l_del_mac_addr: - eth_hw_addr_set(dev, zero_addr); - break; - case l_addr_delete: - lec_addr_delete(priv, mesg->content.normal.atm_addr, - mesg->content.normal.flag); - break; - case l_topology_change: - priv->topology_change = mesg->content.normal.flag; - break; - case l_flush_complete: - lec_flush_complete(priv, mesg->content.normal.flag); - break; - case l_narp_req: /* LANE2: see 7.1.35 in the lane2 spec */ - spin_lock_irqsave(&priv->lec_arp_lock, flags); - entry = lec_arp_find(priv, mesg->content.normal.mac_addr); - lec_arp_remove(priv, entry); - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - - if (mesg->content.normal.no_source_le_narp) - break; - fallthrough; - case l_arp_update: - lec_arp_update(priv, mesg->content.normal.mac_addr, - mesg->content.normal.atm_addr, - mesg->content.normal.flag, - mesg->content.normal.targetless_le_arp); - pr_debug("in l_arp_update\n"); - if (mesg->sizeoftlvs != 0) { /* LANE2 3.1.5 */ - pr_debug("LANE2 3.1.5, got tlvs, size %d\n", - mesg->sizeoftlvs); - lane2_associate_ind(dev, mesg->content.normal.mac_addr, - tmp, mesg->sizeoftlvs); - } - break; - case l_config: - priv->maximum_unknown_frame_count = - mesg->content.config.maximum_unknown_frame_count; - priv->max_unknown_frame_time = - (mesg->content.config.max_unknown_frame_time * HZ); - priv->max_retry_count = mesg->content.config.max_retry_count; - priv->aging_time = (mesg->content.config.aging_time * HZ); - priv->forward_delay_time = - (mesg->content.config.forward_delay_time * HZ); - priv->arp_response_time = - (mesg->content.config.arp_response_time * HZ); - priv->flush_timeout = (mesg->content.config.flush_timeout * HZ); - priv->path_switching_delay = - (mesg->content.config.path_switching_delay * HZ); - priv->lane_version = mesg->content.config.lane_version; - /* LANE2 */ - priv->lane2_ops = NULL; - if (priv->lane_version > 1) - priv->lane2_ops = &lane2_ops; - rtnl_lock(); - if (dev_set_mtu(dev, mesg->content.config.mtu)) - pr_info("%s: change_mtu to %d failed\n", - dev->name, mesg->content.config.mtu); - rtnl_unlock(); - priv->is_proxy = mesg->content.config.is_proxy; - break; - case l_flush_tran_id: - lec_set_flush_tran_id(priv, mesg->content.normal.atm_addr, - mesg->content.normal.flag); - break; - case l_set_lecid: - priv->lecid = - (unsigned short)(0xffff & mesg->content.normal.flag); - break; - case l_should_bridge: -#if IS_ENABLED(CONFIG_BRIDGE) - { - pr_debug("%s: bridge zeppelin asks about %pM\n", - dev->name, mesg->content.proxy.mac_addr); - - if (br_fdb_test_addr_hook == NULL) - break; - - if (br_fdb_test_addr_hook(dev, mesg->content.proxy.mac_addr)) { - /* hit from bridge table, send LE_ARP_RESPONSE */ - struct sk_buff *skb2; - struct sock *sk; - - pr_debug("%s: entry found, responding to zeppelin\n", - dev->name); - skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); - if (skb2 == NULL) - break; - skb2->len = sizeof(struct atmlec_msg); - skb_copy_to_linear_data(skb2, mesg, sizeof(*mesg)); - struct atm_vcc *vcc; - - rcu_read_lock(); - vcc = rcu_dereference(priv->lecd); - if (vcc) { - atm_force_charge(vcc, skb2->truesize); - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb2); - sk->sk_data_ready(sk); - } else { - dev_kfree_skb(skb2); - } - rcu_read_unlock(); - } - } -#endif /* IS_ENABLED(CONFIG_BRIDGE) */ - break; - default: - pr_info("%s: Unknown message type %d\n", dev->name, mesg->type); - dev_kfree_skb(skb); - return -EINVAL; - } - dev_kfree_skb(skb); - return 0; -} - -static void lec_atm_close(struct atm_vcc *vcc) -{ - struct net_device *dev = (struct net_device *)vcc->proto_data; - struct lec_priv *priv = netdev_priv(dev); - - rcu_assign_pointer(priv->lecd, NULL); - synchronize_rcu(); - /* Do something needful? */ - - netif_stop_queue(dev); - lec_arp_destroy(priv); - - pr_info("%s: Shut down!\n", dev->name); - module_put(THIS_MODULE); -} - -static const struct atmdev_ops lecdev_ops = { - .close = lec_atm_close, - .send = lec_atm_send -}; - -static struct atm_dev lecatm_dev = { - .ops = &lecdev_ops, - .type = "lec", - .number = 999, /* dummy device number */ - .lock = __SPIN_LOCK_UNLOCKED(lecatm_dev.lock) -}; - -/* - * LANE2: new argument struct sk_buff *data contains - * the LE_ARP based TLVs introduced in the LANE2 spec - */ -static int -send_to_lecd(struct lec_priv *priv, atmlec_msg_type type, - const unsigned char *mac_addr, const unsigned char *atm_addr, - struct sk_buff *data) -{ - struct atm_vcc *vcc; - struct sock *sk; - struct sk_buff *skb; - struct atmlec_msg *mesg; - - if (!priv || !rcu_access_pointer(priv->lecd)) - return -1; - - skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); - if (!skb) - return -1; - skb->len = sizeof(struct atmlec_msg); - mesg = (struct atmlec_msg *)skb->data; - memset(mesg, 0, sizeof(struct atmlec_msg)); - mesg->type = type; - if (data != NULL) - mesg->sizeoftlvs = data->len; - if (mac_addr) - ether_addr_copy(mesg->content.normal.mac_addr, mac_addr); - else - mesg->content.normal.targetless_le_arp = 1; - if (atm_addr) - memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN); - - rcu_read_lock(); - vcc = rcu_dereference(priv->lecd); - if (!vcc) { - rcu_read_unlock(); - kfree_skb(skb); - return -1; - } - - atm_force_charge(vcc, skb->truesize); - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - - if (data != NULL) { - pr_debug("about to send %d bytes of data\n", data->len); - atm_force_charge(vcc, data->truesize); - skb_queue_tail(&sk->sk_receive_queue, data); - sk->sk_data_ready(sk); - } - - rcu_read_unlock(); - return 0; -} - -static void lec_set_multicast_list(struct net_device *dev) -{ - /* - * by default, all multicast frames arrive over the bus. - * eventually support selective multicast service - */ -} - -static const struct net_device_ops lec_netdev_ops = { - .ndo_open = lec_open, - .ndo_stop = lec_close, - .ndo_start_xmit = lec_start_xmit, - .ndo_tx_timeout = lec_tx_timeout, - .ndo_set_rx_mode = lec_set_multicast_list, -}; - -static const unsigned char lec_ctrl_magic[] = { - 0xff, - 0x00, - 0x01, - 0x01 -}; - -#define LEC_DATA_DIRECT_8023 2 -#define LEC_DATA_DIRECT_8025 3 - -static int lec_is_data_direct(struct atm_vcc *vcc) -{ - return ((vcc->sap.blli[0].l3.tr9577.snap[4] == LEC_DATA_DIRECT_8023) || - (vcc->sap.blli[0].l3.tr9577.snap[4] == LEC_DATA_DIRECT_8025)); -} - -static void lec_push(struct atm_vcc *vcc, struct sk_buff *skb) -{ - unsigned long flags; - struct net_device *dev = (struct net_device *)vcc->proto_data; - struct lec_priv *priv = netdev_priv(dev); - -#if DUMP_PACKETS > 0 - printk(KERN_DEBUG "%s: vcc vpi:%d vci:%d\n", - dev->name, vcc->vpi, vcc->vci); -#endif - if (!skb) { - pr_debug("%s: null skb\n", dev->name); - lec_vcc_close(priv, vcc); - return; - } -#if DUMP_PACKETS >= 2 -#define MAX_SKB_DUMP 99 -#elif DUMP_PACKETS >= 1 -#define MAX_SKB_DUMP 30 -#endif -#if DUMP_PACKETS > 0 - printk(KERN_DEBUG "%s: rcv datalen:%ld lecid:%4.4x\n", - dev->name, skb->len, priv->lecid); - print_hex_dump(KERN_DEBUG, "", DUMP_OFFSET, 16, 1, - skb->data, min(MAX_SKB_DUMP, skb->len), true); -#endif /* DUMP_PACKETS > 0 */ - if (memcmp(skb->data, lec_ctrl_magic, 4) == 0) { - /* Control frame, to daemon */ - struct sock *sk = sk_atm(vcc); - - pr_debug("%s: To daemon\n", dev->name); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - } else { /* Data frame, queue to protocol handlers */ - struct lec_arp_table *entry; - unsigned char *src, *dst; - - atm_return(vcc, skb->truesize); - if (*(__be16 *) skb->data == htons(priv->lecid) || - !rcu_access_pointer(priv->lecd) || !(dev->flags & IFF_UP)) { - /* - * Probably looping back, or if lecd is missing, - * lecd has gone down - */ - pr_debug("Ignoring frame...\n"); - dev_kfree_skb(skb); - return; - } - dst = ((struct lecdatahdr_8023 *)skb->data)->h_dest; - - /* - * If this is a Data Direct VCC, and the VCC does not match - * the LE_ARP cache entry, delete the LE_ARP cache entry. - */ - spin_lock_irqsave(&priv->lec_arp_lock, flags); - if (lec_is_data_direct(vcc)) { - src = ((struct lecdatahdr_8023 *)skb->data)->h_source; - entry = lec_arp_find(priv, src); - if (entry && entry->vcc != vcc) { - lec_arp_remove(priv, entry); - lec_arp_put(entry); - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - - if (!(dst[0] & 0x01) && /* Never filter Multi/Broadcast */ - !priv->is_proxy && /* Proxy wants all the packets */ - memcmp(dst, dev->dev_addr, dev->addr_len)) { - dev_kfree_skb(skb); - return; - } - if (!hlist_empty(&priv->lec_arp_empty_ones)) - lec_arp_check_empties(priv, vcc, skb); - skb_pull(skb, 2); /* skip lec_id */ - skb->protocol = eth_type_trans(skb, dev); - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); - netif_rx(skb); - } -} - -static void lec_pop(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); - struct net_device *dev = skb->dev; - - if (vpriv == NULL) { - pr_info("vpriv = NULL!?!?!?\n"); - return; - } - - vpriv->old_pop(vcc, skb); - - if (vpriv->xoff && atm_may_send(vcc, 0)) { - vpriv->xoff = 0; - if (netif_running(dev) && netif_queue_stopped(dev)) - netif_wake_queue(dev); - } -} - -static int lec_vcc_attach(struct atm_vcc *vcc, void __user *arg) -{ - struct lec_vcc_priv *vpriv; - int bytes_left; - struct atmlec_ioc ioc_data; - - lockdep_assert_held(&lec_mutex); - /* Lecd must be up in this case */ - bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmlec_ioc)); - if (bytes_left != 0) - pr_info("copy from user failed for %d bytes\n", bytes_left); - if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF) - return -EINVAL; - ioc_data.dev_num = array_index_nospec(ioc_data.dev_num, MAX_LEC_ITF); - if (!dev_lec[ioc_data.dev_num]) - return -EINVAL; - vpriv = kmalloc_obj(struct lec_vcc_priv); - if (!vpriv) - return -ENOMEM; - vpriv->xoff = 0; - vpriv->old_pop = vcc->pop; - vcc->user_back = vpriv; - vcc->pop = lec_pop; - lec_vcc_added(netdev_priv(dev_lec[ioc_data.dev_num]), - &ioc_data, vcc, vcc->push); - vcc->proto_data = dev_lec[ioc_data.dev_num]; - vcc->push = lec_push; - return 0; -} - -static int lec_mcast_attach(struct atm_vcc *vcc, int arg) -{ - lockdep_assert_held(&lec_mutex); - if (arg < 0 || arg >= MAX_LEC_ITF) - return -EINVAL; - arg = array_index_nospec(arg, MAX_LEC_ITF); - if (!dev_lec[arg]) - return -EINVAL; - vcc->proto_data = dev_lec[arg]; - return lec_mcast_make(netdev_priv(dev_lec[arg]), vcc); -} - -/* Initialize device. */ -static int lecd_attach(struct atm_vcc *vcc, int arg) -{ - int i; - struct lec_priv *priv; - - lockdep_assert_held(&lec_mutex); - if (arg < 0) - arg = 0; - if (arg >= MAX_LEC_ITF) - return -EINVAL; - i = array_index_nospec(arg, MAX_LEC_ITF); - if (!dev_lec[i]) { - int size; - - size = sizeof(struct lec_priv); - dev_lec[i] = alloc_etherdev(size); - if (!dev_lec[i]) - return -ENOMEM; - dev_lec[i]->netdev_ops = &lec_netdev_ops; - dev_lec[i]->max_mtu = 18190; - snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i); - if (register_netdev(dev_lec[i])) { - free_netdev(dev_lec[i]); - dev_lec[i] = NULL; - return -EINVAL; - } - - priv = netdev_priv(dev_lec[i]); - } else { - priv = netdev_priv(dev_lec[i]); - if (rcu_access_pointer(priv->lecd)) - return -EADDRINUSE; - } - lec_arp_init(priv); - priv->itfnum = i; /* LANE2 addition */ - rcu_assign_pointer(priv->lecd, vcc); - vcc->dev = &lecatm_dev; - vcc_insert_socket(sk_atm(vcc)); - - vcc->proto_data = dev_lec[i]; - set_bit(ATM_VF_META, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - - /* Set default values to these variables */ - priv->maximum_unknown_frame_count = 1; - priv->max_unknown_frame_time = (1 * HZ); - priv->vcc_timeout_period = (1200 * HZ); - priv->max_retry_count = 1; - priv->aging_time = (300 * HZ); - priv->forward_delay_time = (15 * HZ); - priv->topology_change = 0; - priv->arp_response_time = (1 * HZ); - priv->flush_timeout = (4 * HZ); - priv->path_switching_delay = (6 * HZ); - - if (dev_lec[i]->flags & IFF_UP) - netif_start_queue(dev_lec[i]); - __module_get(THIS_MODULE); - return i; -} - -#ifdef CONFIG_PROC_FS -static const char *lec_arp_get_status_string(unsigned char status) -{ - static const char *const lec_arp_status_string[] = { - "ESI_UNKNOWN ", - "ESI_ARP_PENDING ", - "ESI_VC_PENDING ", - "<Undefined> ", - "ESI_FLUSH_PENDING ", - "ESI_FORWARD_DIRECT" - }; - - if (status > ESI_FORWARD_DIRECT) - status = 3; /* ESI_UNDEFINED */ - return lec_arp_status_string[status]; -} - -static void lec_info(struct seq_file *seq, struct lec_arp_table *entry) -{ - seq_printf(seq, "%pM ", entry->mac_addr); - seq_printf(seq, "%*phN ", ATM_ESA_LEN, entry->atm_addr); - seq_printf(seq, "%s %4.4x", lec_arp_get_status_string(entry->status), - entry->flags & 0xffff); - if (entry->vcc) - seq_printf(seq, "%3d %3d ", entry->vcc->vpi, entry->vcc->vci); - else - seq_printf(seq, " "); - if (entry->recv_vcc) { - seq_printf(seq, " %3d %3d", entry->recv_vcc->vpi, - entry->recv_vcc->vci); - } - seq_putc(seq, '\n'); -} - -struct lec_state { - unsigned long flags; - struct lec_priv *locked; - struct hlist_node *node; - struct net_device *dev; - int itf; - int arp_table; - int misc_table; -}; - -static void *lec_tbl_walk(struct lec_state *state, struct hlist_head *tbl, - loff_t *l) -{ - struct hlist_node *e = state->node; - - if (!e) - e = tbl->first; - if (e == SEQ_START_TOKEN) { - e = tbl->first; - --*l; - } - - for (; e; e = e->next) { - if (--*l < 0) - break; - } - state->node = e; - - return (*l < 0) ? state : NULL; -} - -static void *lec_arp_walk(struct lec_state *state, loff_t *l, - struct lec_priv *priv) -{ - void *v = NULL; - int p; - - for (p = state->arp_table; p < LEC_ARP_TABLE_SIZE; p++) { - v = lec_tbl_walk(state, &priv->lec_arp_tables[p], l); - if (v) - break; - } - state->arp_table = p; - return v; -} - -static void *lec_misc_walk(struct lec_state *state, loff_t *l, - struct lec_priv *priv) -{ - struct hlist_head *lec_misc_tables[] = { - &priv->lec_arp_empty_ones, - &priv->lec_no_forward, - &priv->mcast_fwds - }; - void *v = NULL; - int q; - - for (q = state->misc_table; q < ARRAY_SIZE(lec_misc_tables); q++) { - v = lec_tbl_walk(state, lec_misc_tables[q], l); - if (v) - break; - } - state->misc_table = q; - return v; -} - -static void *lec_priv_walk(struct lec_state *state, loff_t *l, - struct lec_priv *priv) -{ - if (!state->locked) { - state->locked = priv; - spin_lock_irqsave(&priv->lec_arp_lock, state->flags); - } - if (!lec_arp_walk(state, l, priv) && !lec_misc_walk(state, l, priv)) { - spin_unlock_irqrestore(&priv->lec_arp_lock, state->flags); - state->locked = NULL; - /* Partial state reset for the next time we get called */ - state->arp_table = state->misc_table = 0; - } - return state->locked; -} - -static void *lec_itf_walk(struct lec_state *state, loff_t *l) -{ - struct net_device *dev; - void *v; - - dev = state->dev ? state->dev : dev_lec[state->itf]; - v = (dev && netdev_priv(dev)) ? - lec_priv_walk(state, l, netdev_priv(dev)) : NULL; - if (!v && dev) { - /* Partial state reset for the next time we get called */ - dev = NULL; - } - state->dev = dev; - return v; -} - -static void *lec_get_idx(struct lec_state *state, loff_t l) -{ - void *v = NULL; - - for (; state->itf < MAX_LEC_ITF; state->itf++) { - v = lec_itf_walk(state, &l); - if (v) - break; - } - return v; -} - -static void *lec_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct lec_state *state = seq->private; - - mutex_lock(&lec_mutex); - state->itf = 0; - state->dev = NULL; - state->locked = NULL; - state->arp_table = 0; - state->misc_table = 0; - state->node = SEQ_START_TOKEN; - - return *pos ? lec_get_idx(state, *pos) : SEQ_START_TOKEN; -} - -static void lec_seq_stop(struct seq_file *seq, void *v) -{ - struct lec_state *state = seq->private; - - if (state->dev) { - spin_unlock_irqrestore(&state->locked->lec_arp_lock, - state->flags); - state->dev = NULL; - } - mutex_unlock(&lec_mutex); -} - -static void *lec_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct lec_state *state = seq->private; - - ++*pos; - return lec_get_idx(state, 1); -} - -static int lec_seq_show(struct seq_file *seq, void *v) -{ - static const char lec_banner[] = - "Itf MAC ATM destination" - " Status Flags " - "VPI/VCI Recv VPI/VCI\n"; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, lec_banner); - else { - struct lec_state *state = seq->private; - struct net_device *dev = state->dev; - struct lec_arp_table *entry = hlist_entry(state->node, - struct lec_arp_table, - next); - - seq_printf(seq, "%s ", dev->name); - lec_info(seq, entry); - } - return 0; -} - -static const struct seq_operations lec_seq_ops = { - .start = lec_seq_start, - .next = lec_seq_next, - .stop = lec_seq_stop, - .show = lec_seq_show, -}; -#endif - -static int lane_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct atm_vcc *vcc = ATM_SD(sock); - int err = 0; - - switch (cmd) { - case ATMLEC_CTRL: - case ATMLEC_MCAST: - case ATMLEC_DATA: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - break; - default: - return -ENOIOCTLCMD; - } - - mutex_lock(&lec_mutex); - switch (cmd) { - case ATMLEC_CTRL: - err = lecd_attach(vcc, (int)arg); - if (err >= 0) - sock->state = SS_CONNECTED; - break; - case ATMLEC_MCAST: - err = lec_mcast_attach(vcc, (int)arg); - break; - case ATMLEC_DATA: - err = lec_vcc_attach(vcc, (void __user *)arg); - break; - } - - mutex_unlock(&lec_mutex); - return err; -} - -static struct atm_ioctl lane_ioctl_ops = { - .owner = THIS_MODULE, - .ioctl = lane_ioctl, -}; - -static int __init lane_module_init(void) -{ -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *p; - - p = proc_create_seq_private("lec", 0444, atm_proc_root, &lec_seq_ops, - sizeof(struct lec_state), NULL); - if (!p) { - pr_err("Unable to initialize /proc/net/atm/lec\n"); - return -ENOMEM; - } -#endif - - register_atm_ioctl(&lane_ioctl_ops); - pr_info("lec.c: initialized\n"); - return 0; -} - -static void __exit lane_module_cleanup(void) -{ - int i; - -#ifdef CONFIG_PROC_FS - remove_proc_entry("lec", atm_proc_root); -#endif - - deregister_atm_ioctl(&lane_ioctl_ops); - - for (i = 0; i < MAX_LEC_ITF; i++) { - if (dev_lec[i] != NULL) { - unregister_netdev(dev_lec[i]); - free_netdev(dev_lec[i]); - dev_lec[i] = NULL; - } - } -} - -module_init(lane_module_init); -module_exit(lane_module_cleanup); - -/* - * LANE2: 3.1.3, LE_RESOLVE.request - * Non force allocates memory and fills in *tlvs, fills in *sizeoftlvs. - * If sizeoftlvs == NULL the default TLVs associated with this - * lec will be used. - * If dst_mac == NULL, targetless LE_ARP will be sent - */ -static int lane2_resolve(struct net_device *dev, const u8 *dst_mac, int force, - u8 **tlvs, u32 *sizeoftlvs) -{ - unsigned long flags; - struct lec_priv *priv = netdev_priv(dev); - struct lec_arp_table *table; - struct sk_buff *skb; - int retval; - - if (force == 0) { - spin_lock_irqsave(&priv->lec_arp_lock, flags); - table = lec_arp_find(priv, dst_mac); - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - if (table == NULL) - return -1; - - *tlvs = kmemdup(table->tlvs, table->sizeoftlvs, GFP_ATOMIC); - if (*tlvs == NULL) - return -1; - - *sizeoftlvs = table->sizeoftlvs; - - return 0; - } - - if (sizeoftlvs == NULL) - retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, NULL); - - else { - skb = alloc_skb(*sizeoftlvs, GFP_ATOMIC); - if (skb == NULL) - return -1; - skb->len = *sizeoftlvs; - skb_copy_to_linear_data(skb, *tlvs, *sizeoftlvs); - retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, skb); - } - return retval; -} - -/* - * LANE2: 3.1.4, LE_ASSOCIATE.request - * Associate the *tlvs with the *lan_dst address. - * Will overwrite any previous association - * Returns 1 for success, 0 for failure (out of memory) - * - */ -static int lane2_associate_req(struct net_device *dev, const u8 *lan_dst, - const u8 *tlvs, u32 sizeoftlvs) -{ - int retval; - struct sk_buff *skb; - struct lec_priv *priv = netdev_priv(dev); - - if (!ether_addr_equal(lan_dst, dev->dev_addr)) - return 0; /* not our mac address */ - - kfree(priv->tlvs); /* NULL if there was no previous association */ - - priv->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL); - if (priv->tlvs == NULL) - return 0; - priv->sizeoftlvs = sizeoftlvs; - - skb = alloc_skb(sizeoftlvs, GFP_ATOMIC); - if (skb == NULL) - return 0; - skb->len = sizeoftlvs; - skb_copy_to_linear_data(skb, tlvs, sizeoftlvs); - retval = send_to_lecd(priv, l_associate_req, NULL, NULL, skb); - if (retval != 0) - pr_info("lec.c: lane2_associate_req() failed\n"); - /* - * If the previous association has changed we must - * somehow notify other LANE entities about the change - */ - return 1; -} - -/* - * LANE2: 3.1.5, LE_ASSOCIATE.indication - * - */ -static void lane2_associate_ind(struct net_device *dev, const u8 *mac_addr, - const u8 *tlvs, u32 sizeoftlvs) -{ -#if 0 - int i = 0; -#endif - struct lec_priv *priv = netdev_priv(dev); -#if 0 /* - * Why have the TLVs in LE_ARP entries - * since we do not use them? When you - * uncomment this code, make sure the - * TLVs get freed when entry is killed - */ - struct lec_arp_table *entry = lec_arp_find(priv, mac_addr); - - if (entry == NULL) - return; /* should not happen */ - - kfree(entry->tlvs); - - entry->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL); - if (entry->tlvs == NULL) - return; - entry->sizeoftlvs = sizeoftlvs; -#endif -#if 0 - pr_info("\n"); - pr_info("dump of tlvs, sizeoftlvs=%d\n", sizeoftlvs); - while (i < sizeoftlvs) - pr_cont("%02x ", tlvs[i++]); - - pr_cont("\n"); -#endif - - /* tell MPOA about the TLVs we saw */ - if (priv->lane2_ops && priv->lane2_ops->associate_indicator) { - priv->lane2_ops->associate_indicator(dev, mac_addr, - tlvs, sizeoftlvs); - } -} - -/* - * Here starts what used to lec_arpc.c - * - * lec_arpc.c was added here when making - * lane client modular. October 1997 - */ - -#include <linux/types.h> -#include <linux/timer.h> -#include <linux/param.h> -#include <linux/atomic.h> -#include <linux/inetdevice.h> -#include <net/route.h> - -#if 0 -#define pr_debug(format, args...) -/* - #define pr_debug printk -*/ -#endif -#define DEBUG_ARP_TABLE 0 - -#define LEC_ARP_REFRESH_INTERVAL (3*HZ) - -static void lec_arp_check_expire(struct work_struct *work); -static void lec_arp_expire_arp(struct timer_list *t); - -/* - * Arp table funcs - */ - -#define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE - 1)) - -/* - * Initialization of arp-cache - */ -static void lec_arp_init(struct lec_priv *priv) -{ - unsigned short i; - - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) - INIT_HLIST_HEAD(&priv->lec_arp_tables[i]); - INIT_HLIST_HEAD(&priv->lec_arp_empty_ones); - INIT_HLIST_HEAD(&priv->lec_no_forward); - INIT_HLIST_HEAD(&priv->mcast_fwds); - spin_lock_init(&priv->lec_arp_lock); - INIT_DELAYED_WORK(&priv->lec_arp_work, lec_arp_check_expire); - schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL); -} - -static void lec_arp_clear_vccs(struct lec_arp_table *entry) -{ - if (entry->vcc) { - struct atm_vcc *vcc = entry->vcc; - struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); - struct net_device *dev = (struct net_device *)vcc->proto_data; - - if (vpriv) { - vcc->pop = vpriv->old_pop; - if (vpriv->xoff) - netif_wake_queue(dev); - kfree(vpriv); - vcc->user_back = NULL; - vcc->push = entry->old_push; - vcc_release_async(vcc, -EPIPE); - } - entry->vcc = NULL; - } - if (entry->recv_vcc) { - struct atm_vcc *vcc = entry->recv_vcc; - struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); - - if (vpriv) { - kfree(vpriv); - vcc->user_back = NULL; - - entry->recv_vcc->push = entry->old_recv_push; - vcc_release_async(entry->recv_vcc, -EPIPE); - } - entry->recv_vcc = NULL; - } -} - -/* - * Insert entry to lec_arp_table - * LANE2: Add to the end of the list to satisfy 8.1.13 - */ -static inline void -lec_arp_add(struct lec_priv *priv, struct lec_arp_table *entry) -{ - struct hlist_head *tmp; - - tmp = &priv->lec_arp_tables[HASH(entry->mac_addr[ETH_ALEN - 1])]; - hlist_add_head(&entry->next, tmp); - - pr_debug("Added entry:%pM\n", entry->mac_addr); -} - -/* - * Remove entry from lec_arp_table - */ -static int -lec_arp_remove(struct lec_priv *priv, struct lec_arp_table *to_remove) -{ - struct lec_arp_table *entry; - int i, remove_vcc = 1; - - if (!to_remove) - return -1; - - hlist_del(&to_remove->next); - timer_delete(&to_remove->timer); - - /* - * If this is the only MAC connected to this VCC, - * also tear down the VCC - */ - if (to_remove->status >= ESI_FLUSH_PENDING) { - /* - * ESI_FLUSH_PENDING, ESI_FORWARD_DIRECT - */ - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(entry, - &priv->lec_arp_tables[i], next) { - if (memcmp(to_remove->atm_addr, - entry->atm_addr, ATM_ESA_LEN) == 0) { - remove_vcc = 0; - break; - } - } - } - if (remove_vcc) - lec_arp_clear_vccs(to_remove); - } - skb_queue_purge(&to_remove->tx_wait); /* FIXME: good place for this? */ - - pr_debug("Removed entry:%pM\n", to_remove->mac_addr); - return 0; -} - -#if DEBUG_ARP_TABLE -static const char *get_status_string(unsigned char st) -{ - switch (st) { - case ESI_UNKNOWN: - return "ESI_UNKNOWN"; - case ESI_ARP_PENDING: - return "ESI_ARP_PENDING"; - case ESI_VC_PENDING: - return "ESI_VC_PENDING"; - case ESI_FLUSH_PENDING: - return "ESI_FLUSH_PENDING"; - case ESI_FORWARD_DIRECT: - return "ESI_FORWARD_DIRECT"; - } - return "<UNKNOWN>"; -} - -static void dump_arp_table(struct lec_priv *priv) -{ - struct lec_arp_table *rulla; - char buf[256]; - int i, offset; - - pr_info("Dump %p:\n", priv); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(rulla, - &priv->lec_arp_tables[i], next) { - offset = 0; - offset += sprintf(buf, "%d: %p\n", i, rulla); - offset += sprintf(buf + offset, "Mac: %pM ", - rulla->mac_addr); - offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, - rulla->atm_addr); - offset += sprintf(buf + offset, - "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", - rulla->vcc ? rulla->vcc->vpi : 0, - rulla->vcc ? rulla->vcc->vci : 0, - rulla->recv_vcc ? rulla->recv_vcc-> - vpi : 0, - rulla->recv_vcc ? rulla->recv_vcc-> - vci : 0, rulla->last_used, - rulla->timestamp, rulla->no_tries); - offset += - sprintf(buf + offset, - "Flags:%x, Packets_flooded:%x, Status: %s ", - rulla->flags, rulla->packets_flooded, - get_status_string(rulla->status)); - pr_info("%s\n", buf); - } - } - - if (!hlist_empty(&priv->lec_no_forward)) - pr_info("No forward\n"); - hlist_for_each_entry(rulla, &priv->lec_no_forward, next) { - offset = 0; - offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); - offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, - rulla->atm_addr); - offset += sprintf(buf + offset, - "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", - rulla->vcc ? rulla->vcc->vpi : 0, - rulla->vcc ? rulla->vcc->vci : 0, - rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, - rulla->recv_vcc ? rulla->recv_vcc->vci : 0, - rulla->last_used, - rulla->timestamp, rulla->no_tries); - offset += sprintf(buf + offset, - "Flags:%x, Packets_flooded:%x, Status: %s ", - rulla->flags, rulla->packets_flooded, - get_status_string(rulla->status)); - pr_info("%s\n", buf); - } - - if (!hlist_empty(&priv->lec_arp_empty_ones)) - pr_info("Empty ones\n"); - hlist_for_each_entry(rulla, &priv->lec_arp_empty_ones, next) { - offset = 0; - offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); - offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, - rulla->atm_addr); - offset += sprintf(buf + offset, - "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", - rulla->vcc ? rulla->vcc->vpi : 0, - rulla->vcc ? rulla->vcc->vci : 0, - rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, - rulla->recv_vcc ? rulla->recv_vcc->vci : 0, - rulla->last_used, - rulla->timestamp, rulla->no_tries); - offset += sprintf(buf + offset, - "Flags:%x, Packets_flooded:%x, Status: %s ", - rulla->flags, rulla->packets_flooded, - get_status_string(rulla->status)); - pr_info("%s", buf); - } - - if (!hlist_empty(&priv->mcast_fwds)) - pr_info("Multicast Forward VCCs\n"); - hlist_for_each_entry(rulla, &priv->mcast_fwds, next) { - offset = 0; - offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); - offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, - rulla->atm_addr); - offset += sprintf(buf + offset, - "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", - rulla->vcc ? rulla->vcc->vpi : 0, - rulla->vcc ? rulla->vcc->vci : 0, - rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, - rulla->recv_vcc ? rulla->recv_vcc->vci : 0, - rulla->last_used, - rulla->timestamp, rulla->no_tries); - offset += sprintf(buf + offset, - "Flags:%x, Packets_flooded:%x, Status: %s ", - rulla->flags, rulla->packets_flooded, - get_status_string(rulla->status)); - pr_info("%s\n", buf); - } - -} -#else -#define dump_arp_table(priv) do { } while (0) -#endif - -/* - * Destruction of arp-cache - */ -static void lec_arp_destroy(struct lec_priv *priv) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry; - int i; - - cancel_delayed_work_sync(&priv->lec_arp_work); - - /* - * Remove all entries - */ - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_tables[i], next) { - lec_arp_remove(priv, entry); - lec_arp_put(entry); - } - INIT_HLIST_HEAD(&priv->lec_arp_tables[i]); - } - - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_empty_ones, next) { - timer_delete_sync(&entry->timer); - lec_arp_clear_vccs(entry); - hlist_del(&entry->next); - lec_arp_put(entry); - } - INIT_HLIST_HEAD(&priv->lec_arp_empty_ones); - - hlist_for_each_entry_safe(entry, next, - &priv->lec_no_forward, next) { - timer_delete_sync(&entry->timer); - lec_arp_clear_vccs(entry); - hlist_del(&entry->next); - lec_arp_put(entry); - } - INIT_HLIST_HEAD(&priv->lec_no_forward); - - hlist_for_each_entry_safe(entry, next, &priv->mcast_fwds, next) { - /* No timer, LANEv2 7.1.20 and 2.3.5.3 */ - lec_arp_clear_vccs(entry); - hlist_del(&entry->next); - lec_arp_put(entry); - } - INIT_HLIST_HEAD(&priv->mcast_fwds); - priv->mcast_vcc = NULL; - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -/* - * Find entry by mac_address - */ -static struct lec_arp_table *lec_arp_find(struct lec_priv *priv, - const unsigned char *mac_addr) -{ - struct hlist_head *head; - struct lec_arp_table *entry; - - pr_debug("%pM\n", mac_addr); - - head = &priv->lec_arp_tables[HASH(mac_addr[ETH_ALEN - 1])]; - hlist_for_each_entry(entry, head, next) { - if (ether_addr_equal(mac_addr, entry->mac_addr)) - return entry; - } - return NULL; -} - -static struct lec_arp_table *make_entry(struct lec_priv *priv, - const unsigned char *mac_addr) -{ - struct lec_arp_table *to_return; - - to_return = kzalloc_obj(struct lec_arp_table, GFP_ATOMIC); - if (!to_return) - return NULL; - ether_addr_copy(to_return->mac_addr, mac_addr); - INIT_HLIST_NODE(&to_return->next); - timer_setup(&to_return->timer, lec_arp_expire_arp, 0); - to_return->last_used = jiffies; - to_return->priv = priv; - skb_queue_head_init(&to_return->tx_wait); - refcount_set(&to_return->usage, 1); - return to_return; -} - -/* Arp sent timer expired */ -static void lec_arp_expire_arp(struct timer_list *t) -{ - struct lec_arp_table *entry; - - entry = timer_container_of(entry, t, timer); - - pr_debug("\n"); - if (entry->status == ESI_ARP_PENDING) { - if (entry->no_tries <= entry->priv->max_retry_count) { - if (entry->is_rdesc) - send_to_lecd(entry->priv, l_rdesc_arp_xmt, - entry->mac_addr, NULL, NULL); - else - send_to_lecd(entry->priv, l_arp_xmt, - entry->mac_addr, NULL, NULL); - entry->no_tries++; - } - mod_timer(&entry->timer, jiffies + (1 * HZ)); - } -} - -/* Unknown/unused vcc expire, remove associated entry */ -static void lec_arp_expire_vcc(struct timer_list *t) -{ - unsigned long flags; - struct lec_arp_table *to_remove = timer_container_of(to_remove, t, - timer); - struct lec_priv *priv = to_remove->priv; - - timer_delete(&to_remove->timer); - - pr_debug("%p %p: vpi:%d vci:%d\n", - to_remove, priv, - to_remove->vcc ? to_remove->recv_vcc->vpi : 0, - to_remove->vcc ? to_remove->recv_vcc->vci : 0); - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - hlist_del(&to_remove->next); - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - - lec_arp_clear_vccs(to_remove); - lec_arp_put(to_remove); -} - -static bool __lec_arp_check_expire(struct lec_arp_table *entry, - unsigned long now, - struct lec_priv *priv) -{ - unsigned long time_to_check; - - if ((entry->flags) & LEC_REMOTE_FLAG && priv->topology_change) - time_to_check = priv->forward_delay_time; - else - time_to_check = priv->aging_time; - - pr_debug("About to expire: %lx - %lx > %lx\n", - now, entry->last_used, time_to_check); - if (time_after(now, entry->last_used + time_to_check) && - !(entry->flags & LEC_PERMANENT_FLAG) && - !(entry->mac_addr[0] & 0x01)) { /* LANE2: 7.1.20 */ - /* Remove entry */ - pr_debug("Entry timed out\n"); - lec_arp_remove(priv, entry); - lec_arp_put(entry); - } else { - /* Something else */ - if ((entry->status == ESI_VC_PENDING || - entry->status == ESI_ARP_PENDING) && - time_after_eq(now, entry->timestamp + - priv->max_unknown_frame_time)) { - entry->timestamp = jiffies; - entry->packets_flooded = 0; - if (entry->status == ESI_VC_PENDING) - send_to_lecd(priv, l_svc_setup, - entry->mac_addr, - entry->atm_addr, - NULL); - } - if (entry->status == ESI_FLUSH_PENDING && - time_after_eq(now, entry->timestamp + - priv->path_switching_delay)) { - lec_arp_hold(entry); - return true; - } - } - - return false; -} -/* - * Expire entries. - * 1. Re-set timer - * 2. For each entry, delete entries that have aged past the age limit. - * 3. For each entry, depending on the status of the entry, perform - * the following maintenance. - * a. If status is ESI_VC_PENDING or ESI_ARP_PENDING then if the - * tick_count is above the max_unknown_frame_time, clear - * the tick_count to zero and clear the packets_flooded counter - * to zero. This supports the packet rate limit per address - * while flooding unknowns. - * b. If the status is ESI_FLUSH_PENDING and the tick_count is greater - * than or equal to the path_switching_delay, change the status - * to ESI_FORWARD_DIRECT. This causes the flush period to end - * regardless of the progress of the flush protocol. - */ -static void lec_arp_check_expire(struct work_struct *work) -{ - unsigned long flags; - struct lec_priv *priv = - container_of(work, struct lec_priv, lec_arp_work.work); - struct hlist_node *next; - struct lec_arp_table *entry; - unsigned long now; - int i; - - pr_debug("%p\n", priv); - now = jiffies; -restart: - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_tables[i], next) { - if (__lec_arp_check_expire(entry, now, priv)) { - struct sk_buff *skb; - struct atm_vcc *vcc = entry->vcc; - - spin_unlock_irqrestore(&priv->lec_arp_lock, - flags); - while ((skb = skb_dequeue(&entry->tx_wait))) - lec_send(vcc, skb); - entry->last_used = jiffies; - entry->status = ESI_FORWARD_DIRECT; - lec_arp_put(entry); - - goto restart; - } - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - - schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL); -} - -/* - * Try to find vcc where mac_address is attached. - * - */ -static struct atm_vcc *lec_arp_resolve(struct lec_priv *priv, - const unsigned char *mac_to_find, - int is_rdesc, - struct lec_arp_table **ret_entry) -{ - unsigned long flags; - struct lec_arp_table *entry; - struct atm_vcc *found; - - if (mac_to_find[0] & 0x01) { - switch (priv->lane_version) { - case 1: - return priv->mcast_vcc; - case 2: /* LANE2 wants arp for multicast addresses */ - if (ether_addr_equal(mac_to_find, bus_mac)) - return priv->mcast_vcc; - break; - default: - break; - } - } - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - entry = lec_arp_find(priv, mac_to_find); - - if (entry) { - if (entry->status == ESI_FORWARD_DIRECT) { - /* Connection Ok */ - entry->last_used = jiffies; - lec_arp_hold(entry); - *ret_entry = entry; - found = entry->vcc; - goto out; - } - /* - * If the LE_ARP cache entry is still pending, reset count to 0 - * so another LE_ARP request can be made for this frame. - */ - if (entry->status == ESI_ARP_PENDING) - entry->no_tries = 0; - /* - * Data direct VC not yet set up, check to see if the unknown - * frame count is greater than the limit. If the limit has - * not been reached, allow the caller to send packet to - * BUS. - */ - if (entry->status != ESI_FLUSH_PENDING && - entry->packets_flooded < - priv->maximum_unknown_frame_count) { - entry->packets_flooded++; - pr_debug("Flooding..\n"); - found = priv->mcast_vcc; - goto out; - } - /* - * We got here because entry->status == ESI_FLUSH_PENDING - * or BUS flood limit was reached for an entry which is - * in ESI_ARP_PENDING or ESI_VC_PENDING state. - */ - lec_arp_hold(entry); - *ret_entry = entry; - pr_debug("entry->status %d entry->vcc %p\n", entry->status, - entry->vcc); - found = NULL; - } else { - /* No matching entry was found */ - entry = make_entry(priv, mac_to_find); - pr_debug("Making entry\n"); - if (!entry) { - found = priv->mcast_vcc; - goto out; - } - lec_arp_add(priv, entry); - /* We want arp-request(s) to be sent */ - entry->packets_flooded = 1; - entry->status = ESI_ARP_PENDING; - entry->no_tries = 1; - entry->last_used = entry->timestamp = jiffies; - entry->is_rdesc = is_rdesc; - if (entry->is_rdesc) - send_to_lecd(priv, l_rdesc_arp_xmt, mac_to_find, NULL, - NULL); - else - send_to_lecd(priv, l_arp_xmt, mac_to_find, NULL, NULL); - entry->timer.expires = jiffies + (1 * HZ); - entry->timer.function = lec_arp_expire_arp; - add_timer(&entry->timer); - found = priv->mcast_vcc; - } - -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - return found; -} - -static int -lec_addr_delete(struct lec_priv *priv, const unsigned char *atm_addr, - unsigned long permanent) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry; - int i; - - pr_debug("\n"); - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_tables[i], next) { - if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN) && - (permanent || - !(entry->flags & LEC_PERMANENT_FLAG))) { - lec_arp_remove(priv, entry); - lec_arp_put(entry); - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - return 0; - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - return -1; -} - -/* - * Notifies: Response to arp_request (atm_addr != NULL) - */ -static void -lec_arp_update(struct lec_priv *priv, const unsigned char *mac_addr, - const unsigned char *atm_addr, unsigned long remoteflag, - unsigned int targetless_le_arp) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry, *tmp; - int i; - - pr_debug("%smac:%pM\n", - (targetless_le_arp) ? "targetless " : "", mac_addr); - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - entry = lec_arp_find(priv, mac_addr); - if (entry == NULL && targetless_le_arp) - goto out; /* - * LANE2: ignore targetless LE_ARPs for which - * we have no entry in the cache. 7.1.30 - */ - if (!hlist_empty(&priv->lec_arp_empty_ones)) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_empty_ones, next) { - if (memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN) == 0) { - hlist_del(&entry->next); - timer_delete(&entry->timer); - tmp = lec_arp_find(priv, mac_addr); - if (tmp) { - timer_delete(&tmp->timer); - tmp->status = ESI_FORWARD_DIRECT; - memcpy(tmp->atm_addr, atm_addr, ATM_ESA_LEN); - tmp->vcc = entry->vcc; - tmp->old_push = entry->old_push; - tmp->last_used = jiffies; - timer_delete(&entry->timer); - lec_arp_put(entry); - entry = tmp; - } else { - entry->status = ESI_FORWARD_DIRECT; - ether_addr_copy(entry->mac_addr, - mac_addr); - entry->last_used = jiffies; - lec_arp_add(priv, entry); - } - if (remoteflag) - entry->flags |= LEC_REMOTE_FLAG; - else - entry->flags &= ~LEC_REMOTE_FLAG; - pr_debug("After update\n"); - dump_arp_table(priv); - goto out; - } - } - } - - entry = lec_arp_find(priv, mac_addr); - if (!entry) { - entry = make_entry(priv, mac_addr); - if (!entry) - goto out; - entry->status = ESI_UNKNOWN; - lec_arp_add(priv, entry); - /* Temporary, changes before end of function */ - } - memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN); - timer_delete(&entry->timer); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(tmp, - &priv->lec_arp_tables[i], next) { - if (entry != tmp && - !memcmp(tmp->atm_addr, atm_addr, ATM_ESA_LEN)) { - /* Vcc to this host exists */ - if (tmp->status > ESI_VC_PENDING) { - /* - * ESI_FLUSH_PENDING, - * ESI_FORWARD_DIRECT - */ - entry->vcc = tmp->vcc; - entry->old_push = tmp->old_push; - } - entry->status = tmp->status; - break; - } - } - } - if (remoteflag) - entry->flags |= LEC_REMOTE_FLAG; - else - entry->flags &= ~LEC_REMOTE_FLAG; - if (entry->status == ESI_ARP_PENDING || entry->status == ESI_UNKNOWN) { - entry->status = ESI_VC_PENDING; - send_to_lecd(priv, l_svc_setup, entry->mac_addr, atm_addr, NULL); - } - pr_debug("After update2\n"); - dump_arp_table(priv); -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -/* - * Notifies: Vcc setup ready - */ -static void -lec_vcc_added(struct lec_priv *priv, const struct atmlec_ioc *ioc_data, - struct atm_vcc *vcc, - void (*old_push) (struct atm_vcc *vcc, struct sk_buff *skb)) -{ - unsigned long flags; - struct lec_arp_table *entry; - int i, found_entry = 0; - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - /* Vcc for Multicast Forward. No timer, LANEv2 7.1.20 and 2.3.5.3 */ - if (ioc_data->receive == 2) { - pr_debug("LEC_ARP: Attaching mcast forward\n"); -#if 0 - entry = lec_arp_find(priv, bus_mac); - if (!entry) { - pr_info("LEC_ARP: Multicast entry not found!\n"); - goto out; - } - memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); - entry->recv_vcc = vcc; - entry->old_recv_push = old_push; -#endif - entry = make_entry(priv, bus_mac); - if (entry == NULL) - goto out; - timer_delete(&entry->timer); - memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); - entry->recv_vcc = vcc; - entry->old_recv_push = old_push; - hlist_add_head(&entry->next, &priv->mcast_fwds); - goto out; - } else if (ioc_data->receive == 1) { - /* - * Vcc which we don't want to make default vcc, - * attach it anyway. - */ - pr_debug("LEC_ARP:Attaching data direct, not default: %*phN\n", - ATM_ESA_LEN, ioc_data->atm_addr); - entry = make_entry(priv, bus_mac); - if (entry == NULL) - goto out; - memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); - eth_zero_addr(entry->mac_addr); - entry->recv_vcc = vcc; - entry->old_recv_push = old_push; - entry->status = ESI_UNKNOWN; - entry->timer.expires = jiffies + priv->vcc_timeout_period; - entry->timer.function = lec_arp_expire_vcc; - hlist_add_head(&entry->next, &priv->lec_no_forward); - add_timer(&entry->timer); - dump_arp_table(priv); - goto out; - } - pr_debug("LEC_ARP:Attaching data direct, default: %*phN\n", - ATM_ESA_LEN, ioc_data->atm_addr); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(entry, - &priv->lec_arp_tables[i], next) { - if (memcmp - (ioc_data->atm_addr, entry->atm_addr, - ATM_ESA_LEN) == 0) { - pr_debug("LEC_ARP: Attaching data direct\n"); - pr_debug("Currently -> Vcc: %d, Rvcc:%d\n", - entry->vcc ? entry->vcc->vci : 0, - entry->recv_vcc ? entry->recv_vcc-> - vci : 0); - found_entry = 1; - timer_delete(&entry->timer); - entry->vcc = vcc; - entry->old_push = old_push; - if (entry->status == ESI_VC_PENDING) { - if (priv->maximum_unknown_frame_count - == 0) - entry->status = - ESI_FORWARD_DIRECT; - else { - entry->timestamp = jiffies; - entry->status = - ESI_FLUSH_PENDING; -#if 0 - send_to_lecd(priv, l_flush_xmt, - NULL, - entry->atm_addr, - NULL); -#endif - } - } else { - /* - * They were forming a connection - * to us, and we to them. Our - * ATM address is numerically lower - * than theirs, so we make connection - * we formed into default VCC (8.1.11). - * Connection they made gets torn - * down. This might confuse some - * clients. Can be changed if - * someone reports trouble... - */ - ; - } - } - } - } - if (found_entry) { - pr_debug("After vcc was added\n"); - dump_arp_table(priv); - goto out; - } - /* - * Not found, snatch address from first data packet that arrives - * from this vcc - */ - entry = make_entry(priv, bus_mac); - if (!entry) - goto out; - entry->vcc = vcc; - entry->old_push = old_push; - memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); - eth_zero_addr(entry->mac_addr); - entry->status = ESI_UNKNOWN; - hlist_add_head(&entry->next, &priv->lec_arp_empty_ones); - entry->timer.expires = jiffies + priv->vcc_timeout_period; - entry->timer.function = lec_arp_expire_vcc; - add_timer(&entry->timer); - pr_debug("After vcc was added\n"); - dump_arp_table(priv); -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -static void lec_flush_complete(struct lec_priv *priv, unsigned long tran_id) -{ - unsigned long flags; - struct lec_arp_table *entry; - int i; - - pr_debug("%lx\n", tran_id); -restart: - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(entry, - &priv->lec_arp_tables[i], next) { - if (entry->flush_tran_id == tran_id && - entry->status == ESI_FLUSH_PENDING) { - struct sk_buff *skb; - struct atm_vcc *vcc = entry->vcc; - - lec_arp_hold(entry); - spin_unlock_irqrestore(&priv->lec_arp_lock, - flags); - while ((skb = skb_dequeue(&entry->tx_wait))) - lec_send(vcc, skb); - entry->last_used = jiffies; - entry->status = ESI_FORWARD_DIRECT; - lec_arp_put(entry); - pr_debug("LEC_ARP: Flushed\n"); - goto restart; - } - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - dump_arp_table(priv); -} - -static void -lec_set_flush_tran_id(struct lec_priv *priv, - const unsigned char *atm_addr, unsigned long tran_id) -{ - unsigned long flags; - struct lec_arp_table *entry; - int i; - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) - hlist_for_each_entry(entry, - &priv->lec_arp_tables[i], next) { - if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN)) { - entry->flush_tran_id = tran_id; - pr_debug("Set flush transaction id to %lx for %p\n", - tran_id, entry); - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -static int lec_mcast_make(struct lec_priv *priv, struct atm_vcc *vcc) -{ - unsigned long flags; - unsigned char mac_addr[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; - struct lec_arp_table *to_add; - struct lec_vcc_priv *vpriv; - int err = 0; - - vpriv = kmalloc_obj(struct lec_vcc_priv); - if (!vpriv) - return -ENOMEM; - vpriv->xoff = 0; - vpriv->old_pop = vcc->pop; - vcc->user_back = vpriv; - vcc->pop = lec_pop; - spin_lock_irqsave(&priv->lec_arp_lock, flags); - to_add = make_entry(priv, mac_addr); - if (!to_add) { - vcc->pop = vpriv->old_pop; - kfree(vpriv); - err = -ENOMEM; - goto out; - } - memcpy(to_add->atm_addr, vcc->remote.sas_addr.prv, ATM_ESA_LEN); - to_add->status = ESI_FORWARD_DIRECT; - to_add->flags |= LEC_PERMANENT_FLAG; - to_add->vcc = vcc; - to_add->old_push = vcc->push; - vcc->push = lec_push; - priv->mcast_vcc = vcc; - lec_arp_add(priv, to_add); -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - return err; -} - -static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry; - int i; - - pr_debug("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n", vcc->vpi, vcc->vci); - dump_arp_table(priv); - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_tables[i], next) { - if (vcc == entry->vcc) { - lec_arp_remove(priv, entry); - lec_arp_put(entry); - if (priv->mcast_vcc == vcc) - priv->mcast_vcc = NULL; - } - } - } - - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_empty_ones, next) { - if (entry->vcc == vcc) { - lec_arp_clear_vccs(entry); - timer_delete(&entry->timer); - hlist_del(&entry->next); - lec_arp_put(entry); - } - } - - hlist_for_each_entry_safe(entry, next, - &priv->lec_no_forward, next) { - if (entry->recv_vcc == vcc) { - lec_arp_clear_vccs(entry); - timer_delete(&entry->timer); - hlist_del(&entry->next); - lec_arp_put(entry); - } - } - - hlist_for_each_entry_safe(entry, next, &priv->mcast_fwds, next) { - if (entry->recv_vcc == vcc) { - lec_arp_clear_vccs(entry); - /* No timer, LANEv2 7.1.20 and 2.3.5.3 */ - hlist_del(&entry->next); - lec_arp_put(entry); - } - } - - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - dump_arp_table(priv); -} - -static void -lec_arp_check_empties(struct lec_priv *priv, - struct atm_vcc *vcc, struct sk_buff *skb) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry, *tmp; - struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data; - unsigned char *src = hdr->h_source; - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_empty_ones, next) { - if (vcc == entry->vcc) { - timer_delete(&entry->timer); - ether_addr_copy(entry->mac_addr, src); - entry->status = ESI_FORWARD_DIRECT; - entry->last_used = jiffies; - /* We might have got an entry */ - tmp = lec_arp_find(priv, src); - if (tmp) { - lec_arp_remove(priv, tmp); - lec_arp_put(tmp); - } - hlist_del(&entry->next); - lec_arp_add(priv, entry); - goto out; - } - } - pr_debug("LEC_ARP: Arp_check_empties: entry not found!\n"); -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -MODULE_DESCRIPTION("ATM LAN Emulation (LANE) support"); -MODULE_LICENSE("GPL"); diff --git a/net/atm/lec.h b/net/atm/lec.h deleted file mode 100644 index ec85709bf818..000000000000 --- a/net/atm/lec.h +++ /dev/null @@ -1,155 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Lan Emulation client header file - * - * Marko Kiiskila <mkiiskila@yahoo.com> - */ - -#ifndef _LEC_H_ -#define _LEC_H_ - -#include <linux/atmdev.h> -#include <linux/netdevice.h> -#include <linux/atmlec.h> - -#define LEC_HEADER_LEN 16 - -struct lecdatahdr_8023 { - __be16 le_header; - unsigned char h_dest[ETH_ALEN]; - unsigned char h_source[ETH_ALEN]; - __be16 h_type; -}; - -struct lecdatahdr_8025 { - __be16 le_header; - unsigned char ac_pad; - unsigned char fc; - unsigned char h_dest[ETH_ALEN]; - unsigned char h_source[ETH_ALEN]; -}; - -#define LEC_MINIMUM_8023_SIZE 62 -#define LEC_MINIMUM_8025_SIZE 16 - -/* - * Operations that LANE2 capable device can do. Two first functions - * are used to make the device do things. See spec 3.1.3 and 3.1.4. - * - * The third function is intended for the MPOA component sitting on - * top of the LANE device. The MPOA component assigns it's own function - * to (*associate_indicator)() and the LANE device will use that - * function to tell about TLVs it sees floating through. - * - */ -struct lane2_ops { - int (*resolve) (struct net_device *dev, const u8 *dst_mac, int force, - u8 **tlvs, u32 *sizeoftlvs); - int (*associate_req) (struct net_device *dev, const u8 *lan_dst, - const u8 *tlvs, u32 sizeoftlvs); - void (*associate_indicator) (struct net_device *dev, const u8 *mac_addr, - const u8 *tlvs, u32 sizeoftlvs); -}; - -/* - * ATM LAN Emulation supports both LLC & Dix Ethernet EtherType - * frames. - * - * 1. Dix Ethernet EtherType frames encoded by placing EtherType - * field in h_type field. Data follows immediately after header. - * 2. LLC Data frames whose total length, including LLC field and data, - * but not padding required to meet the minimum data frame length, - * is less than ETH_P_802_3_MIN MUST be encoded by placing that length - * in the h_type field. The LLC field follows header immediately. - * 3. LLC data frames longer than this maximum MUST be encoded by placing - * the value 0 in the h_type field. - * - */ - -/* Hash table size */ -#define LEC_ARP_TABLE_SIZE 16 - -struct lec_priv { - unsigned short lecid; /* Lecid of this client */ - struct hlist_head lec_arp_empty_ones; - /* Used for storing VCC's that don't have a MAC address attached yet */ - struct hlist_head lec_arp_tables[LEC_ARP_TABLE_SIZE]; - /* Actual LE ARP table */ - struct hlist_head lec_no_forward; - /* - * Used for storing VCC's (and forward packets from) which are to - * age out by not using them to forward packets. - * This is because to some LE clients there will be 2 VCCs. Only - * one of them gets used. - */ - struct hlist_head mcast_fwds; - /* - * With LANEv2 it is possible that BUS (or a special multicast server) - * establishes multiple Multicast Forward VCCs to us. This list - * collects all those VCCs. LANEv1 client has only one item in this - * list. These entries are not aged out. - */ - spinlock_t lec_arp_lock; - struct atm_vcc *mcast_vcc; /* Default Multicast Send VCC */ - struct atm_vcc __rcu *lecd; - struct delayed_work lec_arp_work; /* C10 */ - unsigned int maximum_unknown_frame_count; - /* - * Within the period of time defined by this variable, the client will send - * no more than C10 frames to BUS for a given unicast destination. (C11) - */ - unsigned long max_unknown_frame_time; - /* - * If no traffic has been sent in this vcc for this period of time, - * vcc will be torn down (C12) - */ - unsigned long vcc_timeout_period; - /* - * An LE Client MUST not retry an LE_ARP_REQUEST for a - * given frame's LAN Destination more than maximum retry count times, - * after the first LEC_ARP_REQUEST (C13) - */ - unsigned short max_retry_count; - /* - * Max time the client will maintain an entry in its arp cache in - * absence of a verification of that relationship (C17) - */ - unsigned long aging_time; - /* - * Max time the client will maintain an entry in cache when - * topology change flag is true (C18) - */ - unsigned long forward_delay_time; /* Topology change flag (C19) */ - int topology_change; - /* - * Max time the client expects an LE_ARP_REQUEST/LE_ARP_RESPONSE - * cycle to take (C20) - */ - unsigned long arp_response_time; - /* - * Time limit ot wait to receive an LE_FLUSH_RESPONSE after the - * LE_FLUSH_REQUEST has been sent before taking recover action. (C21) - */ - unsigned long flush_timeout; - /* The time since sending a frame to the bus after which the - * LE Client may assume that the frame has been either discarded or - * delivered to the recipient (C22) - */ - unsigned long path_switching_delay; - - u8 *tlvs; /* LANE2: TLVs are new */ - u32 sizeoftlvs; /* The size of the tlv array in bytes */ - int lane_version; /* LANE2 */ - int itfnum; /* e.g. 2 for lec2, 5 for lec5 */ - struct lane2_ops *lane2_ops; /* can be NULL for LANE v1 */ - int is_proxy; /* bridge between ATM and Ethernet */ -}; - -struct lec_vcc_priv { - void (*old_pop) (struct atm_vcc *vcc, struct sk_buff *skb); - int xoff; -}; - -#define LEC_VCC_PRIV(vcc) ((struct lec_vcc_priv *)((vcc)->user_back)) - -#endif /* _LEC_H_ */ diff --git a/net/atm/lec_arpc.h b/net/atm/lec_arpc.h deleted file mode 100644 index 39115fe074c4..000000000000 --- a/net/atm/lec_arpc.h +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Lec arp cache - * - * Marko Kiiskila <mkiiskila@yahoo.com> - */ -#ifndef _LEC_ARP_H_ -#define _LEC_ARP_H_ -#include <linux/atm.h> -#include <linux/atmdev.h> -#include <linux/if_ether.h> -#include <linux/atmlec.h> - -struct lec_arp_table { - struct hlist_node next; /* Linked entry list */ - unsigned char atm_addr[ATM_ESA_LEN]; /* Atm address */ - unsigned char mac_addr[ETH_ALEN]; /* Mac address */ - int is_rdesc; /* Mac address is a route descriptor */ - struct atm_vcc *vcc; /* Vcc this entry is attached */ - struct atm_vcc *recv_vcc; /* Vcc we receive data from */ - - void (*old_push) (struct atm_vcc *vcc, struct sk_buff *skb); - /* Push that leads to daemon */ - - void (*old_recv_push) (struct atm_vcc *vcc, struct sk_buff *skb); - /* Push that leads to daemon */ - - unsigned long last_used; /* For expiry */ - unsigned long timestamp; /* Used for various timestamping things: - * 1. FLUSH started - * (status=ESI_FLUSH_PENDING) - * 2. Counting to - * max_unknown_frame_time - * (status=ESI_ARP_PENDING|| - * status=ESI_VC_PENDING) - */ - unsigned char no_tries; /* No of times arp retry has been tried */ - unsigned char status; /* Status of this entry */ - unsigned short flags; /* Flags for this entry */ - unsigned short packets_flooded; /* Data packets flooded */ - unsigned long flush_tran_id; /* Transaction id in flush protocol */ - struct timer_list timer; /* Arping timer */ - struct lec_priv *priv; /* Pointer back */ - u8 *tlvs; - u32 sizeoftlvs; /* - * LANE2: Each MAC address can have TLVs - * associated with it. sizeoftlvs tells - * the length of the tlvs array - */ - struct sk_buff_head tx_wait; /* wait queue for outgoing packets */ - refcount_t usage; /* usage count */ -}; - -/* - * LANE2: Template tlv struct for accessing - * the tlvs in the lec_arp_table->tlvs array - */ -struct tlv { - u32 type; - u8 length; - u8 value[255]; -}; - -/* Status fields */ -#define ESI_UNKNOWN 0 /* - * Next packet sent to this mac address - * causes ARP-request to be sent - */ -#define ESI_ARP_PENDING 1 /* - * There is no ATM address associated with this - * 48-bit address. The LE-ARP protocol is in - * progress. - */ -#define ESI_VC_PENDING 2 /* - * There is a valid ATM address associated with - * this 48-bit address but there is no VC set - * up to that ATM address. The signaling - * protocol is in process. - */ -#define ESI_FLUSH_PENDING 4 /* - * The LEC has been notified of the FLUSH_START - * status and it is assumed that the flush - * protocol is in process. - */ -#define ESI_FORWARD_DIRECT 5 /* - * Either the Path Switching Delay (C22) has - * elapsed or the LEC has notified the Mapping - * that the flush protocol has completed. In - * either case, it is safe to forward packets - * to this address via the data direct VC. - */ - -/* Flag values */ -#define LEC_REMOTE_FLAG 0x0001 -#define LEC_PERMANENT_FLAG 0x0002 - -#endif /* _LEC_ARP_H_ */ diff --git a/net/atm/mpc.c b/net/atm/mpc.c deleted file mode 100644 index ce8e9780373b..000000000000 --- a/net/atm/mpc.c +++ /dev/null @@ -1,1538 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/timer.h> -#include <linux/init.h> -#include <linux/bitops.h> -#include <linux/capability.h> -#include <linux/seq_file.h> - -/* We are an ethernet device */ -#include <linux/if_ether.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <net/sock.h> -#include <linux/skbuff.h> -#include <linux/ip.h> -#include <linux/uaccess.h> -#include <asm/byteorder.h> -#include <net/checksum.h> /* for ip_fast_csum() */ -#include <net/arp.h> -#include <net/dst.h> -#include <linux/proc_fs.h> - -/* And atm device */ -#include <linux/atmdev.h> -#include <linux/atmlec.h> -#include <linux/atmmpc.h> -/* Modular too */ -#include <linux/module.h> - -#include "lec.h" -#include "mpc.h" -#include "resources.h" - -/* - * mpc.c: Implementation of MPOA client kernel part - */ - -#if 0 -#define dprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args) -#define dprintk_cont(format, args...) printk(KERN_CONT format, ##args) -#else -#define dprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args);\ - } while (0) -#define dprintk_cont(format, args...) \ - do { if (0) printk(KERN_CONT format, ##args); } while (0) -#endif - -#if 0 -#define ddprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args) -#define ddprintk_cont(format, args...) printk(KERN_CONT format, ##args) -#else -#define ddprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args);\ - } while (0) -#define ddprintk_cont(format, args...) \ - do { if (0) printk(KERN_CONT format, ##args); } while (0) -#endif - -/* mpc_daemon -> kernel */ -static void MPOA_trigger_rcvd(struct k_message *msg, struct mpoa_client *mpc); -static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc); -static void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc); -static void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc); -static void mps_death(struct k_message *msg, struct mpoa_client *mpc); -static void clean_up(struct k_message *msg, struct mpoa_client *mpc, - int action); -static void MPOA_cache_impos_rcvd(struct k_message *msg, - struct mpoa_client *mpc); -static void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, - struct mpoa_client *mpc); -static void set_mps_mac_addr_rcvd(struct k_message *mesg, - struct mpoa_client *mpc); - -static const uint8_t *copy_macs(struct mpoa_client *mpc, - const uint8_t *router_mac, - const uint8_t *tlvs, uint8_t mps_macs, - uint8_t device_type); -static void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry); - -static void send_set_mps_ctrl_addr(const char *addr, struct mpoa_client *mpc); -static void mpoad_close(struct atm_vcc *vcc); -static int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb); - -static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb); -static netdev_tx_t mpc_send_packet(struct sk_buff *skb, - struct net_device *dev); -static int mpoa_event_listener(struct notifier_block *mpoa_notifier, - unsigned long event, void *dev); -static void mpc_timer_refresh(void); -static void mpc_cache_check(struct timer_list *unused); - -static struct llc_snap_hdr llc_snap_mpoa_ctrl = { - 0xaa, 0xaa, 0x03, - {0x00, 0x00, 0x5e}, - {0x00, 0x03} /* For MPOA control PDUs */ -}; -static struct llc_snap_hdr llc_snap_mpoa_data = { - 0xaa, 0xaa, 0x03, - {0x00, 0x00, 0x00}, - {0x08, 0x00} /* This is for IP PDUs only */ -}; -static struct llc_snap_hdr llc_snap_mpoa_data_tagged = { - 0xaa, 0xaa, 0x03, - {0x00, 0x00, 0x00}, - {0x88, 0x4c} /* This is for tagged data PDUs */ -}; - -static struct notifier_block mpoa_notifier = { - mpoa_event_listener, - NULL, - 0 -}; - -struct mpoa_client *mpcs = NULL; /* FIXME */ -static struct atm_mpoa_qos *qos_head = NULL; -static DEFINE_TIMER(mpc_timer, mpc_cache_check); - - -static struct mpoa_client *find_mpc_by_itfnum(int itf) -{ - struct mpoa_client *mpc; - - mpc = mpcs; /* our global linked list */ - while (mpc != NULL) { - if (mpc->dev_num == itf) - return mpc; - mpc = mpc->next; - } - - return NULL; /* not found */ -} - -static struct mpoa_client *find_mpc_by_vcc(struct atm_vcc *vcc) -{ - struct mpoa_client *mpc; - - mpc = mpcs; /* our global linked list */ - while (mpc != NULL) { - if (mpc->mpoad_vcc == vcc) - return mpc; - mpc = mpc->next; - } - - return NULL; /* not found */ -} - -static struct mpoa_client *find_mpc_by_lec(struct net_device *dev) -{ - struct mpoa_client *mpc; - - mpc = mpcs; /* our global linked list */ - while (mpc != NULL) { - if (mpc->dev == dev) - return mpc; - mpc = mpc->next; - } - - return NULL; /* not found */ -} - -/* - * Functions for managing QoS list - */ - -/* - * Overwrites the old entry or makes a new one. - */ -struct atm_mpoa_qos *atm_mpoa_add_qos(__be32 dst_ip, struct atm_qos *qos) -{ - struct atm_mpoa_qos *entry; - - entry = atm_mpoa_search_qos(dst_ip); - if (entry != NULL) { - entry->qos = *qos; - return entry; - } - - entry = kmalloc_obj(struct atm_mpoa_qos); - if (entry == NULL) { - pr_info("mpoa: out of memory\n"); - return entry; - } - - entry->ipaddr = dst_ip; - entry->qos = *qos; - - entry->next = qos_head; - qos_head = entry; - - return entry; -} - -struct atm_mpoa_qos *atm_mpoa_search_qos(__be32 dst_ip) -{ - struct atm_mpoa_qos *qos; - - qos = qos_head; - while (qos) { - if (qos->ipaddr == dst_ip) - break; - qos = qos->next; - } - - return qos; -} - -/* - * Returns 0 for failure - */ -int atm_mpoa_delete_qos(struct atm_mpoa_qos *entry) -{ - struct atm_mpoa_qos *curr; - - if (entry == NULL) - return 0; - if (entry == qos_head) { - qos_head = qos_head->next; - kfree(entry); - return 1; - } - - curr = qos_head; - while (curr != NULL) { - if (curr->next == entry) { - curr->next = entry->next; - kfree(entry); - return 1; - } - curr = curr->next; - } - - return 0; -} - -/* this is buggered - we need locking for qos_head */ -void atm_mpoa_disp_qos(struct seq_file *m) -{ - struct atm_mpoa_qos *qos; - - qos = qos_head; - seq_printf(m, "QoS entries for shortcuts:\n"); - seq_printf(m, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n"); - - while (qos != NULL) { - seq_printf(m, "%pI4\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n", - &qos->ipaddr, - qos->qos.txtp.max_pcr, - qos->qos.txtp.pcr, - qos->qos.txtp.min_pcr, - qos->qos.txtp.max_cdv, - qos->qos.txtp.max_sdu, - qos->qos.rxtp.max_pcr, - qos->qos.rxtp.pcr, - qos->qos.rxtp.min_pcr, - qos->qos.rxtp.max_cdv, - qos->qos.rxtp.max_sdu); - qos = qos->next; - } -} - -static struct net_device *find_lec_by_itfnum(int itf) -{ - struct net_device *dev; - char name[IFNAMSIZ]; - - sprintf(name, "lec%d", itf); - dev = dev_get_by_name(&init_net, name); - - return dev; -} - -static struct mpoa_client *alloc_mpc(void) -{ - struct mpoa_client *mpc; - - mpc = kzalloc_obj(struct mpoa_client); - if (mpc == NULL) - return NULL; - rwlock_init(&mpc->ingress_lock); - rwlock_init(&mpc->egress_lock); - mpc->next = mpcs; - atm_mpoa_init_cache(mpc); - - mpc->parameters.mpc_p1 = MPC_P1; - mpc->parameters.mpc_p2 = MPC_P2; - memset(mpc->parameters.mpc_p3, 0, sizeof(mpc->parameters.mpc_p3)); - mpc->parameters.mpc_p4 = MPC_P4; - mpc->parameters.mpc_p5 = MPC_P5; - mpc->parameters.mpc_p6 = MPC_P6; - - mpcs = mpc; - - return mpc; -} - -/* - * - * start_mpc() puts the MPC on line. All the packets destined - * to the lec underneath us are now being monitored and - * shortcuts will be established. - * - */ -static void start_mpc(struct mpoa_client *mpc, struct net_device *dev) -{ - - dprintk("(%s)\n", mpc->dev->name); - if (!dev->netdev_ops) - pr_info("(%s) not starting\n", dev->name); - else { - mpc->old_ops = dev->netdev_ops; - mpc->new_ops = *mpc->old_ops; - mpc->new_ops.ndo_start_xmit = mpc_send_packet; - dev->netdev_ops = &mpc->new_ops; - } -} - -static void stop_mpc(struct mpoa_client *mpc) -{ - struct net_device *dev = mpc->dev; - dprintk("(%s)", mpc->dev->name); - - /* Lets not nullify lec device's dev->hard_start_xmit */ - if (dev->netdev_ops != &mpc->new_ops) { - dprintk_cont(" mpc already stopped, not fatal\n"); - return; - } - dprintk_cont("\n"); - - dev->netdev_ops = mpc->old_ops; - mpc->old_ops = NULL; - - /* close_shortcuts(mpc); ??? FIXME */ -} - -static const char *mpoa_device_type_string(char type) __attribute__ ((unused)); - -static const char *mpoa_device_type_string(char type) -{ - switch (type) { - case NON_MPOA: - return "non-MPOA device"; - case MPS: - return "MPS"; - case MPC: - return "MPC"; - case MPS_AND_MPC: - return "both MPS and MPC"; - } - - return "unspecified (non-MPOA) device"; -} - -/* - * lec device calls this via its netdev_priv(dev)->lane2_ops - * ->associate_indicator() when it sees a TLV in LE_ARP packet. - * We fill in the pointer above when we see a LANE2 lec initializing - * See LANE2 spec 3.1.5 - * - * Quite a big and ugly function but when you look at it - * all it does is to try to locate and parse MPOA Device - * Type TLV. - * We give our lec a pointer to this function and when the - * lec sees a TLV it uses the pointer to call this function. - * - */ -static void lane2_assoc_ind(struct net_device *dev, const u8 *mac_addr, - const u8 *tlvs, u32 sizeoftlvs) -{ - uint32_t type; - uint8_t length, mpoa_device_type, number_of_mps_macs; - const uint8_t *end_of_tlvs; - struct mpoa_client *mpc; - - mpoa_device_type = number_of_mps_macs = 0; /* silence gcc */ - dprintk("(%s) received TLV(s), ", dev->name); - dprintk("total length of all TLVs %d\n", sizeoftlvs); - mpc = find_mpc_by_lec(dev); /* Sampo-Fix: moved here from below */ - if (mpc == NULL) { - pr_info("(%s) no mpc\n", dev->name); - return; - } - end_of_tlvs = tlvs + sizeoftlvs; - while (end_of_tlvs - tlvs >= 5) { - type = ((tlvs[0] << 24) | (tlvs[1] << 16) | - (tlvs[2] << 8) | tlvs[3]); - length = tlvs[4]; - tlvs += 5; - dprintk(" type 0x%x length %02x\n", type, length); - if (tlvs + length > end_of_tlvs) { - pr_info("TLV value extends past its buffer, aborting parse\n"); - return; - } - - if (type == 0) { - pr_info("mpoa: (%s) TLV type was 0, returning\n", - dev->name); - return; - } - - if (type != TLV_MPOA_DEVICE_TYPE) { - tlvs += length; - continue; /* skip other TLVs */ - } - mpoa_device_type = *tlvs++; - number_of_mps_macs = *tlvs++; - dprintk("(%s) MPOA device type '%s', ", - dev->name, mpoa_device_type_string(mpoa_device_type)); - if (mpoa_device_type == MPS_AND_MPC && - length < (42 + number_of_mps_macs*ETH_ALEN)) { /* :) */ - pr_info("(%s) short MPOA Device Type TLV\n", - dev->name); - continue; - } - if ((mpoa_device_type == MPS || mpoa_device_type == MPC) && - length < 22 + number_of_mps_macs*ETH_ALEN) { - pr_info("(%s) short MPOA Device Type TLV\n", dev->name); - continue; - } - if (mpoa_device_type != MPS && - mpoa_device_type != MPS_AND_MPC) { - dprintk("ignoring non-MPS device "); - if (mpoa_device_type == MPC) - tlvs += 20; - continue; /* we are only interested in MPSs */ - } - if (number_of_mps_macs == 0 && - mpoa_device_type == MPS_AND_MPC) { - pr_info("(%s) MPS_AND_MPC has zero MACs\n", dev->name); - continue; /* someone should read the spec */ - } - dprintk_cont("this MPS has %d MAC addresses\n", - number_of_mps_macs); - - /* - * ok, now we can go and tell our daemon - * the control address of MPS - */ - send_set_mps_ctrl_addr(tlvs, mpc); - - tlvs = copy_macs(mpc, mac_addr, tlvs, - number_of_mps_macs, mpoa_device_type); - if (tlvs == NULL) - return; - } - if (end_of_tlvs - tlvs != 0) - pr_info("(%s) ignoring %zd bytes of trailing TLV garbage\n", - dev->name, end_of_tlvs - tlvs); -} - -/* - * Store at least advertizing router's MAC address - * plus the possible MAC address(es) to mpc->mps_macs. - * For a freshly allocated MPOA client mpc->mps_macs == 0. - */ -static const uint8_t *copy_macs(struct mpoa_client *mpc, - const uint8_t *router_mac, - const uint8_t *tlvs, uint8_t mps_macs, - uint8_t device_type) -{ - int num_macs; - num_macs = (mps_macs > 1) ? mps_macs : 1; - - if (mpc->number_of_mps_macs != num_macs) { /* need to reallocate? */ - if (mpc->number_of_mps_macs != 0) - kfree(mpc->mps_macs); - mpc->number_of_mps_macs = 0; - mpc->mps_macs = kmalloc_array(ETH_ALEN, num_macs, GFP_KERNEL); - if (mpc->mps_macs == NULL) { - pr_info("(%s) out of mem\n", mpc->dev->name); - return NULL; - } - } - ether_addr_copy(mpc->mps_macs, router_mac); - tlvs += 20; if (device_type == MPS_AND_MPC) tlvs += 20; - if (mps_macs > 0) - memcpy(mpc->mps_macs, tlvs, mps_macs*ETH_ALEN); - tlvs += mps_macs*ETH_ALEN; - mpc->number_of_mps_macs = num_macs; - - return tlvs; -} - -static int send_via_shortcut(struct sk_buff *skb, struct mpoa_client *mpc) -{ - in_cache_entry *entry; - struct iphdr *iph; - char *buff; - __be32 ipaddr = 0; - - static struct { - struct llc_snap_hdr hdr; - __be32 tag; - } tagged_llc_snap_hdr = { - {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}, {0x88, 0x4c}}, - 0 - }; - - buff = skb->data + mpc->dev->hard_header_len; - iph = (struct iphdr *)buff; - ipaddr = iph->daddr; - - ddprintk("(%s) ipaddr 0x%x\n", - mpc->dev->name, ipaddr); - - entry = mpc->in_ops->get(ipaddr, mpc); - if (entry == NULL) { - entry = mpc->in_ops->add_entry(ipaddr, mpc); - if (entry != NULL) - mpc->in_ops->put(entry); - return 1; - } - /* threshold not exceeded or VCC not ready */ - if (mpc->in_ops->cache_hit(entry, mpc) != OPEN) { - ddprintk("(%s) cache_hit: returns != OPEN\n", - mpc->dev->name); - mpc->in_ops->put(entry); - return 1; - } - - ddprintk("(%s) using shortcut\n", - mpc->dev->name); - /* MPOA spec A.1.4, MPOA client must decrement IP ttl at least by one */ - if (iph->ttl <= 1) { - ddprintk("(%s) IP ttl = %u, using LANE\n", - mpc->dev->name, iph->ttl); - mpc->in_ops->put(entry); - return 1; - } - iph->ttl--; - iph->check = 0; - iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); - - if (entry->ctrl_info.tag != 0) { - ddprintk("(%s) adding tag 0x%x\n", - mpc->dev->name, entry->ctrl_info.tag); - tagged_llc_snap_hdr.tag = entry->ctrl_info.tag; - skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ - skb_push(skb, sizeof(tagged_llc_snap_hdr)); - /* add LLC/SNAP header */ - skb_copy_to_linear_data(skb, &tagged_llc_snap_hdr, - sizeof(tagged_llc_snap_hdr)); - } else { - skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ - skb_push(skb, sizeof(struct llc_snap_hdr)); - /* add LLC/SNAP header + tag */ - skb_copy_to_linear_data(skb, &llc_snap_mpoa_data, - sizeof(struct llc_snap_hdr)); - } - - atm_account_tx(entry->shortcut, skb); - entry->shortcut->send(entry->shortcut, skb); - entry->packets_fwded++; - mpc->in_ops->put(entry); - - return 0; -} - -/* - * Probably needs some error checks and locking, not sure... - */ -static netdev_tx_t mpc_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct mpoa_client *mpc; - struct ethhdr *eth; - int i = 0; - - mpc = find_mpc_by_lec(dev); /* this should NEVER fail */ - if (mpc == NULL) { - pr_info("(%s) no MPC found\n", dev->name); - goto non_ip; - } - - eth = (struct ethhdr *)skb->data; - if (eth->h_proto != htons(ETH_P_IP)) - goto non_ip; /* Multi-Protocol Over ATM :-) */ - - /* Weed out funny packets (e.g., AF_PACKET or raw). */ - if (skb->len < ETH_HLEN + sizeof(struct iphdr)) - goto non_ip; - skb_set_network_header(skb, ETH_HLEN); - if (skb->len < ETH_HLEN + ip_hdr(skb)->ihl * 4 || ip_hdr(skb)->ihl < 5) - goto non_ip; - - while (i < mpc->number_of_mps_macs) { - if (ether_addr_equal(eth->h_dest, mpc->mps_macs + i * ETH_ALEN)) - if (send_via_shortcut(skb, mpc) == 0) /* try shortcut */ - return NETDEV_TX_OK; - i++; - } - -non_ip: - return __netdev_start_xmit(mpc->old_ops, skb, dev, false); -} - -static int atm_mpoa_vcc_attach(struct atm_vcc *vcc, void __user *arg) -{ - int bytes_left; - struct mpoa_client *mpc; - struct atmmpc_ioc ioc_data; - in_cache_entry *in_entry; - __be32 ipaddr; - - bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmmpc_ioc)); - if (bytes_left != 0) { - pr_info("mpoa:Short read (missed %d bytes) from userland\n", - bytes_left); - return -EFAULT; - } - ipaddr = ioc_data.ipaddr; - if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF) - return -EINVAL; - - mpc = find_mpc_by_itfnum(ioc_data.dev_num); - if (mpc == NULL) - return -EINVAL; - - if (ioc_data.type == MPC_SOCKET_INGRESS) { - in_entry = mpc->in_ops->get(ipaddr, mpc); - if (in_entry == NULL || - in_entry->entry_state < INGRESS_RESOLVED) { - pr_info("(%s) did not find RESOLVED entry from ingress cache\n", - mpc->dev->name); - if (in_entry != NULL) - mpc->in_ops->put(in_entry); - return -EINVAL; - } - pr_info("(%s) attaching ingress SVC, entry = %pI4\n", - mpc->dev->name, &in_entry->ctrl_info.in_dst_ip); - in_entry->shortcut = vcc; - mpc->in_ops->put(in_entry); - } else { - pr_info("(%s) attaching egress SVC\n", mpc->dev->name); - } - - vcc->proto_data = mpc->dev; - vcc->push = mpc_push; - - return 0; -} - -/* - * - */ -static void mpc_vcc_close(struct atm_vcc *vcc, struct net_device *dev) -{ - struct mpoa_client *mpc; - in_cache_entry *in_entry; - eg_cache_entry *eg_entry; - - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) { - pr_info("(%s) close for unknown MPC\n", dev->name); - return; - } - - dprintk("(%s)\n", dev->name); - in_entry = mpc->in_ops->get_by_vcc(vcc, mpc); - if (in_entry) { - dprintk("(%s) ingress SVC closed ip = %pI4\n", - mpc->dev->name, &in_entry->ctrl_info.in_dst_ip); - in_entry->shortcut = NULL; - mpc->in_ops->put(in_entry); - } - eg_entry = mpc->eg_ops->get_by_vcc(vcc, mpc); - if (eg_entry) { - dprintk("(%s) egress SVC closed\n", mpc->dev->name); - eg_entry->shortcut = NULL; - mpc->eg_ops->put(eg_entry); - } - - if (in_entry == NULL && eg_entry == NULL) - dprintk("(%s) unused vcc closed\n", dev->name); -} - -static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct net_device *dev = (struct net_device *)vcc->proto_data; - struct sk_buff *new_skb; - eg_cache_entry *eg; - struct mpoa_client *mpc; - __be32 tag; - char *tmp; - - ddprintk("(%s)\n", dev->name); - if (skb == NULL) { - dprintk("(%s) null skb, closing VCC\n", dev->name); - mpc_vcc_close(vcc, dev); - return; - } - - skb->dev = dev; - if (memcmp(skb->data, &llc_snap_mpoa_ctrl, - sizeof(struct llc_snap_hdr)) == 0) { - struct sock *sk = sk_atm(vcc); - - dprintk("(%s) control packet arrived\n", dev->name); - /* Pass control packets to daemon */ - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - return; - } - - /* data coming over the shortcut */ - atm_return(vcc, skb->truesize); - - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) { - pr_info("(%s) unknown MPC\n", dev->name); - return; - } - - if (memcmp(skb->data, &llc_snap_mpoa_data_tagged, - sizeof(struct llc_snap_hdr)) == 0) { /* MPOA tagged data */ - ddprintk("(%s) tagged data packet arrived\n", dev->name); - - } else if (memcmp(skb->data, &llc_snap_mpoa_data, - sizeof(struct llc_snap_hdr)) == 0) { /* MPOA data */ - pr_info("(%s) Unsupported non-tagged data packet arrived. Purging\n", - dev->name); - dev_kfree_skb_any(skb); - return; - } else { - pr_info("(%s) garbage arrived, purging\n", dev->name); - dev_kfree_skb_any(skb); - return; - } - - tmp = skb->data + sizeof(struct llc_snap_hdr); - tag = *(__be32 *)tmp; - - eg = mpc->eg_ops->get_by_tag(tag, mpc); - if (eg == NULL) { - pr_info("mpoa: (%s) Didn't find egress cache entry, tag = %u\n", - dev->name, tag); - purge_egress_shortcut(vcc, NULL); - dev_kfree_skb_any(skb); - return; - } - - /* - * See if ingress MPC is using shortcut we opened as a return channel. - * This means we have a bi-directional vcc opened by us. - */ - if (eg->shortcut == NULL) { - eg->shortcut = vcc; - pr_info("(%s) egress SVC in use\n", dev->name); - } - - skb_pull(skb, sizeof(struct llc_snap_hdr) + sizeof(tag)); - /* get rid of LLC/SNAP header */ - new_skb = skb_realloc_headroom(skb, eg->ctrl_info.DH_length); - /* LLC/SNAP is shorter than MAC header :( */ - dev_kfree_skb_any(skb); - if (new_skb == NULL) { - mpc->eg_ops->put(eg); - return; - } - skb_push(new_skb, eg->ctrl_info.DH_length); /* add MAC header */ - skb_copy_to_linear_data(new_skb, eg->ctrl_info.DLL_header, - eg->ctrl_info.DH_length); - new_skb->protocol = eth_type_trans(new_skb, dev); - skb_reset_network_header(new_skb); - - eg->latest_ip_addr = ip_hdr(new_skb)->saddr; - eg->packets_rcvd++; - mpc->eg_ops->put(eg); - - memset(ATM_SKB(new_skb), 0, sizeof(struct atm_skb_data)); - netif_rx(new_skb); -} - -static const struct atmdev_ops mpc_ops = { /* only send is required */ - .close = mpoad_close, - .send = msg_from_mpoad -}; - -static struct atm_dev mpc_dev = { - .ops = &mpc_ops, - .type = "mpc", - .number = 42, - .lock = __SPIN_LOCK_UNLOCKED(mpc_dev.lock) - /* members not explicitly initialised will be 0 */ -}; - -static int atm_mpoa_mpoad_attach(struct atm_vcc *vcc, int arg) -{ - struct mpoa_client *mpc; - struct lec_priv *priv; - int err; - - if (mpcs == NULL) { - mpc_timer_refresh(); - - /* This lets us now how our LECs are doing */ - err = register_netdevice_notifier(&mpoa_notifier); - if (err < 0) { - timer_delete(&mpc_timer); - return err; - } - } - - mpc = find_mpc_by_itfnum(arg); - if (mpc == NULL) { - dprintk("allocating new mpc for itf %d\n", arg); - mpc = alloc_mpc(); - if (mpc == NULL) - return -ENOMEM; - mpc->dev_num = arg; - mpc->dev = find_lec_by_itfnum(arg); - /* NULL if there was no lec */ - } - if (mpc->mpoad_vcc) { - pr_info("mpoad is already present for itf %d\n", arg); - return -EADDRINUSE; - } - - if (mpc->dev) { /* check if the lec is LANE2 capable */ - priv = netdev_priv(mpc->dev); - if (priv->lane_version < 2) { - dev_put(mpc->dev); - mpc->dev = NULL; - } else - priv->lane2_ops->associate_indicator = lane2_assoc_ind; - } - - mpc->mpoad_vcc = vcc; - vcc->dev = &mpc_dev; - vcc_insert_socket(sk_atm(vcc)); - set_bit(ATM_VF_META, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - - if (mpc->dev) { - char empty[ATM_ESA_LEN]; - memset(empty, 0, ATM_ESA_LEN); - - start_mpc(mpc, mpc->dev); - /* set address if mpcd e.g. gets killed and restarted. - * If we do not do it now we have to wait for the next LE_ARP - */ - if (memcmp(mpc->mps_ctrl_addr, empty, ATM_ESA_LEN) != 0) - send_set_mps_ctrl_addr(mpc->mps_ctrl_addr, mpc); - } - - __module_get(THIS_MODULE); - return arg; -} - -static void send_set_mps_ctrl_addr(const char *addr, struct mpoa_client *mpc) -{ - struct k_message mesg; - - memcpy(mpc->mps_ctrl_addr, addr, ATM_ESA_LEN); - - mesg.type = SET_MPS_CTRL_ADDR; - memcpy(mesg.MPS_ctrl, addr, ATM_ESA_LEN); - msg_to_mpoad(&mesg, mpc); -} - -static void mpoad_close(struct atm_vcc *vcc) -{ - struct mpoa_client *mpc; - struct sk_buff *skb; - - mpc = find_mpc_by_vcc(vcc); - if (mpc == NULL) { - pr_info("did not find MPC\n"); - return; - } - if (!mpc->mpoad_vcc) { - pr_info("close for non-present mpoad\n"); - return; - } - - mpc->mpoad_vcc = NULL; - if (mpc->dev) { - struct lec_priv *priv = netdev_priv(mpc->dev); - priv->lane2_ops->associate_indicator = NULL; - stop_mpc(mpc); - dev_put(mpc->dev); - } - - mpc->in_ops->destroy_cache(mpc); - mpc->eg_ops->destroy_cache(mpc); - - while ((skb = skb_dequeue(&sk_atm(vcc)->sk_receive_queue))) { - atm_return(vcc, skb->truesize); - kfree_skb(skb); - } - - pr_info("(%s) going down\n", - (mpc->dev) ? mpc->dev->name : "<unknown>"); - module_put(THIS_MODULE); -} - -/* - * - */ -static int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb) -{ - - struct mpoa_client *mpc = find_mpc_by_vcc(vcc); - struct k_message *mesg = (struct k_message *)skb->data; - WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc)); - - if (mpc == NULL) { - pr_info("no mpc found\n"); - return 0; - } - dprintk("(%s)", mpc->dev ? mpc->dev->name : "<unknown>"); - switch (mesg->type) { - case MPOA_RES_REPLY_RCVD: - dprintk_cont("mpoa_res_reply_rcvd\n"); - MPOA_res_reply_rcvd(mesg, mpc); - break; - case MPOA_TRIGGER_RCVD: - dprintk_cont("mpoa_trigger_rcvd\n"); - MPOA_trigger_rcvd(mesg, mpc); - break; - case INGRESS_PURGE_RCVD: - dprintk_cont("nhrp_purge_rcvd\n"); - ingress_purge_rcvd(mesg, mpc); - break; - case EGRESS_PURGE_RCVD: - dprintk_cont("egress_purge_reply_rcvd\n"); - egress_purge_rcvd(mesg, mpc); - break; - case MPS_DEATH: - dprintk_cont("mps_death\n"); - mps_death(mesg, mpc); - break; - case CACHE_IMPOS_RCVD: - dprintk_cont("cache_impos_rcvd\n"); - MPOA_cache_impos_rcvd(mesg, mpc); - break; - case SET_MPC_CTRL_ADDR: - dprintk_cont("set_mpc_ctrl_addr\n"); - set_mpc_ctrl_addr_rcvd(mesg, mpc); - break; - case SET_MPS_MAC_ADDR: - dprintk_cont("set_mps_mac_addr\n"); - set_mps_mac_addr_rcvd(mesg, mpc); - break; - case CLEAN_UP_AND_EXIT: - dprintk_cont("clean_up_and_exit\n"); - clean_up(mesg, mpc, DIE); - break; - case RELOAD: - dprintk_cont("reload\n"); - clean_up(mesg, mpc, RELOAD); - break; - case SET_MPC_PARAMS: - dprintk_cont("set_mpc_params\n"); - mpc->parameters = mesg->content.params; - break; - default: - dprintk_cont("unknown message %d\n", mesg->type); - break; - } - kfree_skb(skb); - - return 0; -} - -/* Remember that this function may not do things that sleep */ -int msg_to_mpoad(struct k_message *mesg, struct mpoa_client *mpc) -{ - struct sk_buff *skb; - struct sock *sk; - - if (mpc == NULL || !mpc->mpoad_vcc) { - pr_info("mesg %d to a non-existent mpoad\n", mesg->type); - return -ENXIO; - } - - skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); - if (skb == NULL) - return -ENOMEM; - skb_put(skb, sizeof(struct k_message)); - skb_copy_to_linear_data(skb, mesg, sizeof(*mesg)); - atm_force_charge(mpc->mpoad_vcc, skb->truesize); - - sk = sk_atm(mpc->mpoad_vcc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - - return 0; -} - -static int mpoa_event_listener(struct notifier_block *mpoa_notifier, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct mpoa_client *mpc; - struct lec_priv *priv; - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (strncmp(dev->name, "lec", 3)) - return NOTIFY_DONE; /* we are only interested in lec:s */ - - switch (event) { - case NETDEV_REGISTER: /* a new lec device was allocated */ - priv = netdev_priv(dev); - if (priv->lane_version < 2) - break; - priv->lane2_ops->associate_indicator = lane2_assoc_ind; - mpc = find_mpc_by_itfnum(priv->itfnum); - if (mpc == NULL) { - dprintk("allocating new mpc for %s\n", dev->name); - mpc = alloc_mpc(); - if (mpc == NULL) { - pr_info("no new mpc"); - break; - } - } - mpc->dev_num = priv->itfnum; - mpc->dev = dev; - dev_hold(dev); - dprintk("(%s) was initialized\n", dev->name); - break; - case NETDEV_UNREGISTER: - /* the lec device was deallocated */ - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) - break; - dprintk("device (%s) was deallocated\n", dev->name); - stop_mpc(mpc); - dev_put(mpc->dev); - mpc->dev = NULL; - break; - case NETDEV_UP: - /* the dev was ifconfig'ed up */ - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) - break; - if (mpc->mpoad_vcc != NULL) - start_mpc(mpc, dev); - break; - case NETDEV_DOWN: - /* the dev was ifconfig'ed down */ - /* this means that the flow of packets from the - * upper layer stops - */ - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) - break; - if (mpc->mpoad_vcc != NULL) - stop_mpc(mpc); - break; - case NETDEV_REBOOT: - case NETDEV_CHANGE: - case NETDEV_CHANGEMTU: - case NETDEV_CHANGEADDR: - case NETDEV_GOING_DOWN: - break; - default: - break; - } - - return NOTIFY_DONE; -} - -/* - * Functions which are called after a message is received from mpcd. - * Msg is reused on purpose. - */ - - -static void MPOA_trigger_rcvd(struct k_message *msg, struct mpoa_client *mpc) -{ - __be32 dst_ip = msg->content.in_info.in_dst_ip; - in_cache_entry *entry; - - entry = mpc->in_ops->get(dst_ip, mpc); - if (entry == NULL) { - entry = mpc->in_ops->add_entry(dst_ip, mpc); - entry->entry_state = INGRESS_RESOLVING; - msg->type = SND_MPOA_RES_RQST; - msg->content.in_info = entry->ctrl_info; - msg_to_mpoad(msg, mpc); - entry->reply_wait = ktime_get_seconds(); - mpc->in_ops->put(entry); - return; - } - - if (entry->entry_state == INGRESS_INVALID) { - entry->entry_state = INGRESS_RESOLVING; - msg->type = SND_MPOA_RES_RQST; - msg->content.in_info = entry->ctrl_info; - msg_to_mpoad(msg, mpc); - entry->reply_wait = ktime_get_seconds(); - mpc->in_ops->put(entry); - return; - } - - pr_info("(%s) entry already in resolving state\n", - (mpc->dev) ? mpc->dev->name : "<unknown>"); - mpc->in_ops->put(entry); -} - -/* - * Things get complicated because we have to check if there's an egress - * shortcut with suitable traffic parameters we could use. - */ -static void check_qos_and_open_shortcut(struct k_message *msg, - struct mpoa_client *client, - in_cache_entry *entry) -{ - __be32 dst_ip = msg->content.in_info.in_dst_ip; - struct atm_mpoa_qos *qos = atm_mpoa_search_qos(dst_ip); - eg_cache_entry *eg_entry = client->eg_ops->get_by_src_ip(dst_ip, client); - - if (eg_entry && eg_entry->shortcut) { - if (eg_entry->shortcut->qos.txtp.traffic_class & - msg->qos.txtp.traffic_class & - (qos ? qos->qos.txtp.traffic_class : ATM_UBR | ATM_CBR)) { - if (eg_entry->shortcut->qos.txtp.traffic_class == ATM_UBR) - entry->shortcut = eg_entry->shortcut; - else if (eg_entry->shortcut->qos.txtp.max_pcr > 0) - entry->shortcut = eg_entry->shortcut; - } - if (entry->shortcut) { - dprintk("(%s) using egress SVC to reach %pI4\n", - client->dev->name, &dst_ip); - client->eg_ops->put(eg_entry); - return; - } - } - if (eg_entry != NULL) - client->eg_ops->put(eg_entry); - - /* No luck in the egress cache we must open an ingress SVC */ - msg->type = OPEN_INGRESS_SVC; - if (qos && - (qos->qos.txtp.traffic_class == msg->qos.txtp.traffic_class)) { - msg->qos = qos->qos; - pr_info("(%s) trying to get a CBR shortcut\n", - client->dev->name); - } else - memset(&msg->qos, 0, sizeof(struct atm_qos)); - msg_to_mpoad(msg, client); -} - -static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc) -{ - __be32 dst_ip = msg->content.in_info.in_dst_ip; - in_cache_entry *entry = mpc->in_ops->get(dst_ip, mpc); - - dprintk("(%s) ip %pI4\n", - mpc->dev->name, &dst_ip); - ddprintk("(%s) entry = %p", - mpc->dev->name, entry); - if (entry == NULL) { - pr_info("(%s) ARGH, received res. reply for an entry that doesn't exist.\n", - mpc->dev->name); - return; - } - ddprintk_cont(" entry_state = %d ", entry->entry_state); - - if (entry->entry_state == INGRESS_RESOLVED) { - pr_info("(%s) RESOLVED entry!\n", mpc->dev->name); - mpc->in_ops->put(entry); - return; - } - - entry->ctrl_info = msg->content.in_info; - entry->time = ktime_get_seconds(); - /* Used in refreshing func from now on */ - entry->reply_wait = ktime_get_seconds(); - entry->refresh_time = 0; - ddprintk_cont("entry->shortcut = %p\n", entry->shortcut); - - if (entry->entry_state == INGRESS_RESOLVING && - entry->shortcut != NULL) { - entry->entry_state = INGRESS_RESOLVED; - mpc->in_ops->put(entry); - return; /* Shortcut already open... */ - } - - if (entry->shortcut != NULL) { - pr_info("(%s) entry->shortcut != NULL, impossible!\n", - mpc->dev->name); - mpc->in_ops->put(entry); - return; - } - - check_qos_and_open_shortcut(msg, mpc, entry); - entry->entry_state = INGRESS_RESOLVED; - mpc->in_ops->put(entry); - - return; - -} - -static void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc) -{ - __be32 dst_ip = msg->content.in_info.in_dst_ip; - __be32 mask = msg->ip_mask; - in_cache_entry *entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask); - - if (entry == NULL) { - pr_info("(%s) purge for a non-existing entry, ip = %pI4\n", - mpc->dev->name, &dst_ip); - return; - } - - do { - dprintk("(%s) removing an ingress entry, ip = %pI4\n", - mpc->dev->name, &dst_ip); - write_lock_bh(&mpc->ingress_lock); - mpc->in_ops->remove_entry(entry, mpc); - write_unlock_bh(&mpc->ingress_lock); - mpc->in_ops->put(entry); - entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask); - } while (entry != NULL); -} - -static void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc) -{ - __be32 cache_id = msg->content.eg_info.cache_id; - eg_cache_entry *entry = mpc->eg_ops->get_by_cache_id(cache_id, mpc); - - if (entry == NULL) { - dprintk("(%s) purge for a non-existing entry\n", - mpc->dev->name); - return; - } - - write_lock_irq(&mpc->egress_lock); - mpc->eg_ops->remove_entry(entry, mpc); - write_unlock_irq(&mpc->egress_lock); - - mpc->eg_ops->put(entry); -} - -static void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry) -{ - struct sock *sk; - struct k_message *purge_msg; - struct sk_buff *skb; - - dprintk("entering\n"); - if (vcc == NULL) { - pr_info("vcc == NULL\n"); - return; - } - - skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); - if (skb == NULL) { - pr_info("out of memory\n"); - return; - } - - skb_put(skb, sizeof(struct k_message)); - memset(skb->data, 0, sizeof(struct k_message)); - purge_msg = (struct k_message *)skb->data; - purge_msg->type = DATA_PLANE_PURGE; - if (entry != NULL) - purge_msg->content.eg_info = entry->ctrl_info; - - atm_force_charge(vcc, skb->truesize); - - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - dprintk("exiting\n"); -} - -/* - * Our MPS died. Tell our daemon to send NHRP data plane purge to each - * of the egress shortcuts we have. - */ -static void mps_death(struct k_message *msg, struct mpoa_client *mpc) -{ - eg_cache_entry *entry; - - dprintk("(%s)\n", mpc->dev->name); - - if (memcmp(msg->MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN)) { - pr_info("(%s) wrong MPS\n", mpc->dev->name); - return; - } - - /* FIXME: This knows too much of the cache structure */ - read_lock_irq(&mpc->egress_lock); - entry = mpc->eg_cache; - while (entry != NULL) { - purge_egress_shortcut(entry->shortcut, entry); - entry = entry->next; - } - read_unlock_irq(&mpc->egress_lock); - - mpc->in_ops->destroy_cache(mpc); - mpc->eg_ops->destroy_cache(mpc); -} - -static void MPOA_cache_impos_rcvd(struct k_message *msg, - struct mpoa_client *mpc) -{ - uint16_t holding_time; - eg_cache_entry *entry = mpc->eg_ops->get_by_cache_id(msg->content.eg_info.cache_id, mpc); - - holding_time = msg->content.eg_info.holding_time; - dprintk("(%s) entry = %p, holding_time = %u\n", - mpc->dev->name, entry, holding_time); - if (entry == NULL && !holding_time) - return; - if (entry == NULL && holding_time) { - entry = mpc->eg_ops->add_entry(msg, mpc); - mpc->eg_ops->put(entry); - return; - } - if (holding_time) { - mpc->eg_ops->update(entry, holding_time); - return; - } - - write_lock_irq(&mpc->egress_lock); - mpc->eg_ops->remove_entry(entry, mpc); - write_unlock_irq(&mpc->egress_lock); - - mpc->eg_ops->put(entry); -} - -static void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, - struct mpoa_client *mpc) -{ - struct lec_priv *priv; - int i, retval ; - - uint8_t tlv[4 + 1 + 1 + 1 + ATM_ESA_LEN]; - - tlv[0] = 00; tlv[1] = 0xa0; tlv[2] = 0x3e; tlv[3] = 0x2a; /* type */ - tlv[4] = 1 + 1 + ATM_ESA_LEN; /* length */ - tlv[5] = 0x02; /* MPOA client */ - tlv[6] = 0x00; /* number of MPS MAC addresses */ - - memcpy(&tlv[7], mesg->MPS_ctrl, ATM_ESA_LEN); /* MPC ctrl ATM addr */ - memcpy(mpc->our_ctrl_addr, mesg->MPS_ctrl, ATM_ESA_LEN); - - dprintk("(%s) setting MPC ctrl ATM address to", - mpc->dev ? mpc->dev->name : "<unknown>"); - for (i = 7; i < sizeof(tlv); i++) - dprintk_cont(" %02x", tlv[i]); - dprintk_cont("\n"); - - if (mpc->dev) { - priv = netdev_priv(mpc->dev); - retval = priv->lane2_ops->associate_req(mpc->dev, - mpc->dev->dev_addr, - tlv, sizeof(tlv)); - if (retval == 0) - pr_info("(%s) MPOA device type TLV association failed\n", - mpc->dev->name); - retval = priv->lane2_ops->resolve(mpc->dev, NULL, 1, NULL, NULL); - if (retval < 0) - pr_info("(%s) targetless LE_ARP request failed\n", - mpc->dev->name); - } -} - -static void set_mps_mac_addr_rcvd(struct k_message *msg, - struct mpoa_client *client) -{ - - if (client->number_of_mps_macs) - kfree(client->mps_macs); - client->number_of_mps_macs = 0; - client->mps_macs = kmemdup(msg->MPS_ctrl, ETH_ALEN, GFP_KERNEL); - if (client->mps_macs == NULL) { - pr_info("out of memory\n"); - return; - } - client->number_of_mps_macs = 1; -} - -/* - * purge egress cache and tell daemon to 'action' (DIE, RELOAD) - */ -static void clean_up(struct k_message *msg, struct mpoa_client *mpc, int action) -{ - - eg_cache_entry *entry; - msg->type = SND_EGRESS_PURGE; - - - /* FIXME: This knows too much of the cache structure */ - read_lock_irq(&mpc->egress_lock); - entry = mpc->eg_cache; - while (entry != NULL) { - msg->content.eg_info = entry->ctrl_info; - dprintk("cache_id %u\n", entry->ctrl_info.cache_id); - msg_to_mpoad(msg, mpc); - entry = entry->next; - } - read_unlock_irq(&mpc->egress_lock); - - msg->type = action; - msg_to_mpoad(msg, mpc); -} - -static unsigned long checking_time; - -static void mpc_timer_refresh(void) -{ - mpc_timer.expires = jiffies + (MPC_P2 * HZ); - checking_time = mpc_timer.expires; - add_timer(&mpc_timer); -} - -static void mpc_cache_check(struct timer_list *unused) -{ - struct mpoa_client *mpc = mpcs; - static unsigned long previous_resolving_check_time; - static unsigned long previous_refresh_time; - - while (mpc != NULL) { - mpc->in_ops->clear_count(mpc); - mpc->eg_ops->clear_expired(mpc); - if (checking_time - previous_resolving_check_time > - mpc->parameters.mpc_p4 * HZ) { - mpc->in_ops->check_resolving(mpc); - previous_resolving_check_time = checking_time; - } - if (checking_time - previous_refresh_time > - mpc->parameters.mpc_p5 * HZ) { - mpc->in_ops->refresh(mpc); - previous_refresh_time = checking_time; - } - mpc = mpc->next; - } - mpc_timer_refresh(); -} - -static int atm_mpoa_ioctl(struct socket *sock, unsigned int cmd, - unsigned long arg) -{ - int err = 0; - struct atm_vcc *vcc = ATM_SD(sock); - - if (cmd != ATMMPC_CTRL && cmd != ATMMPC_DATA) - return -ENOIOCTLCMD; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch (cmd) { - case ATMMPC_CTRL: - err = atm_mpoa_mpoad_attach(vcc, (int)arg); - if (err >= 0) - sock->state = SS_CONNECTED; - break; - case ATMMPC_DATA: - err = atm_mpoa_vcc_attach(vcc, (void __user *)arg); - break; - default: - break; - } - return err; -} - -static struct atm_ioctl atm_ioctl_ops = { - .owner = THIS_MODULE, - .ioctl = atm_mpoa_ioctl, -}; - -static __init int atm_mpoa_init(void) -{ - register_atm_ioctl(&atm_ioctl_ops); - - if (mpc_proc_init() != 0) - pr_info("failed to initialize /proc/mpoa\n"); - - pr_info("mpc.c: initialized\n"); - - return 0; -} - -static void __exit atm_mpoa_cleanup(void) -{ - struct mpoa_client *mpc, *tmp; - struct atm_mpoa_qos *qos, *nextqos; - struct lec_priv *priv; - - mpc_proc_clean(); - - timer_delete_sync(&mpc_timer); - unregister_netdevice_notifier(&mpoa_notifier); - deregister_atm_ioctl(&atm_ioctl_ops); - - mpc = mpcs; - mpcs = NULL; - while (mpc != NULL) { - tmp = mpc->next; - if (mpc->dev != NULL) { - stop_mpc(mpc); - priv = netdev_priv(mpc->dev); - if (priv->lane2_ops != NULL) - priv->lane2_ops->associate_indicator = NULL; - } - ddprintk("about to clear caches\n"); - mpc->in_ops->destroy_cache(mpc); - mpc->eg_ops->destroy_cache(mpc); - ddprintk("caches cleared\n"); - kfree(mpc->mps_macs); - memset(mpc, 0, sizeof(struct mpoa_client)); - ddprintk("about to kfree %p\n", mpc); - kfree(mpc); - ddprintk("next mpc is at %p\n", tmp); - mpc = tmp; - } - - qos = qos_head; - qos_head = NULL; - while (qos != NULL) { - nextqos = qos->next; - dprintk("freeing qos entry %p\n", qos); - kfree(qos); - qos = nextqos; - } -} - -module_init(atm_mpoa_init); -module_exit(atm_mpoa_cleanup); - -MODULE_DESCRIPTION("Multi-Protocol Over ATM (MPOA) driver"); -MODULE_LICENSE("GPL"); diff --git a/net/atm/mpc.h b/net/atm/mpc.h deleted file mode 100644 index 454abd07651a..000000000000 --- a/net/atm/mpc.h +++ /dev/null @@ -1,65 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _MPC_H_ -#define _MPC_H_ - -#include <linux/types.h> -#include <linux/atm.h> -#include <linux/atmmpc.h> -#include <linux/skbuff.h> -#include <linux/spinlock.h> -#include "mpoa_caches.h" - -/* kernel -> mpc-daemon */ -int msg_to_mpoad(struct k_message *msg, struct mpoa_client *mpc); - -struct mpoa_client { - struct mpoa_client *next; - struct net_device *dev; /* lec in question */ - int dev_num; /* e.g. 2 for lec2 */ - - struct atm_vcc *mpoad_vcc; /* control channel to mpoad */ - uint8_t mps_ctrl_addr[ATM_ESA_LEN]; /* MPS control ATM address */ - uint8_t our_ctrl_addr[ATM_ESA_LEN]; /* MPC's control ATM address */ - - rwlock_t ingress_lock; - const struct in_cache_ops *in_ops; /* ingress cache operations */ - in_cache_entry *in_cache; /* the ingress cache of this MPC */ - - rwlock_t egress_lock; - const struct eg_cache_ops *eg_ops; /* egress cache operations */ - eg_cache_entry *eg_cache; /* the egress cache of this MPC */ - - uint8_t *mps_macs; /* array of MPS MAC addresses, >=1 */ - int number_of_mps_macs; /* number of the above MAC addresses */ - struct mpc_parameters parameters; /* parameters for this client */ - - const struct net_device_ops *old_ops; - struct net_device_ops new_ops; -}; - - -struct atm_mpoa_qos { - struct atm_mpoa_qos *next; - __be32 ipaddr; - struct atm_qos qos; -}; - - -/* MPOA QoS operations */ -struct atm_mpoa_qos *atm_mpoa_add_qos(__be32 dst_ip, struct atm_qos *qos); -struct atm_mpoa_qos *atm_mpoa_search_qos(__be32 dst_ip); -int atm_mpoa_delete_qos(struct atm_mpoa_qos *qos); - -/* Display QoS entries. This is for the procfs */ -struct seq_file; -void atm_mpoa_disp_qos(struct seq_file *m); - -#ifdef CONFIG_PROC_FS -int mpc_proc_init(void); -void mpc_proc_clean(void); -#else -#define mpc_proc_init() (0) -#define mpc_proc_clean() do { } while(0) -#endif - -#endif /* _MPC_H_ */ diff --git a/net/atm/mpoa_caches.c b/net/atm/mpoa_caches.c deleted file mode 100644 index c8d4e6f2e831..000000000000 --- a/net/atm/mpoa_caches.c +++ /dev/null @@ -1,565 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/types.h> -#include <linux/atmmpc.h> -#include <linux/slab.h> -#include <linux/time.h> - -#include "mpoa_caches.h" -#include "mpc.h" - -/* - * mpoa_caches.c: Implementation of ingress and egress cache - * handling functions - */ - -#if 0 -#define dprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ -#else -#define dprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ - } while (0) -#endif - -#if 0 -#define ddprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ -#else -#define ddprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ - } while (0) -#endif - -static in_cache_entry *in_cache_get(__be32 dst_ip, - struct mpoa_client *client) -{ - in_cache_entry *entry; - - read_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - if (entry->ctrl_info.in_dst_ip == dst_ip) { - refcount_inc(&entry->use); - read_unlock_bh(&client->ingress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); - - return NULL; -} - -static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip, - struct mpoa_client *client, - __be32 mask) -{ - in_cache_entry *entry; - - read_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - if ((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask)) { - refcount_inc(&entry->use); - read_unlock_bh(&client->ingress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); - - return NULL; - -} - -static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc, - struct mpoa_client *client) -{ - in_cache_entry *entry; - - read_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - if (entry->shortcut == vcc) { - refcount_inc(&entry->use); - read_unlock_bh(&client->ingress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); - - return NULL; -} - -static in_cache_entry *in_cache_add_entry(__be32 dst_ip, - struct mpoa_client *client) -{ - in_cache_entry *entry = kzalloc_obj(in_cache_entry); - - if (entry == NULL) { - pr_info("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n"); - return NULL; - } - - dprintk("adding an ingress entry, ip = %pI4\n", &dst_ip); - - refcount_set(&entry->use, 1); - dprintk("new_in_cache_entry: about to lock\n"); - write_lock_bh(&client->ingress_lock); - entry->next = client->in_cache; - entry->prev = NULL; - if (client->in_cache != NULL) - client->in_cache->prev = entry; - client->in_cache = entry; - - memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); - entry->ctrl_info.in_dst_ip = dst_ip; - entry->time = ktime_get_seconds(); - entry->retry_time = client->parameters.mpc_p4; - entry->count = 1; - entry->entry_state = INGRESS_INVALID; - entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT; - refcount_inc(&entry->use); - - write_unlock_bh(&client->ingress_lock); - dprintk("new_in_cache_entry: unlocked\n"); - - return entry; -} - -static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc) -{ - struct atm_mpoa_qos *qos; - struct k_message msg; - - entry->count++; - if (entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL) - return OPEN; - - if (entry->entry_state == INGRESS_REFRESHING) { - if (entry->count > mpc->parameters.mpc_p1) { - msg.type = SND_MPOA_RES_RQST; - msg.content.in_info = entry->ctrl_info; - memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); - qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); - if (qos != NULL) - msg.qos = qos->qos; - msg_to_mpoad(&msg, mpc); - entry->reply_wait = ktime_get_seconds(); - entry->entry_state = INGRESS_RESOLVING; - } - if (entry->shortcut != NULL) - return OPEN; - return CLOSED; - } - - if (entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL) - return OPEN; - - if (entry->count > mpc->parameters.mpc_p1 && - entry->entry_state == INGRESS_INVALID) { - dprintk("(%s) threshold exceeded for ip %pI4, sending MPOA res req\n", - mpc->dev->name, &entry->ctrl_info.in_dst_ip); - entry->entry_state = INGRESS_RESOLVING; - msg.type = SND_MPOA_RES_RQST; - memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); - msg.content.in_info = entry->ctrl_info; - qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); - if (qos != NULL) - msg.qos = qos->qos; - msg_to_mpoad(&msg, mpc); - entry->reply_wait = ktime_get_seconds(); - } - - return CLOSED; -} - -static void in_cache_put(in_cache_entry *entry) -{ - if (refcount_dec_and_test(&entry->use)) { - kfree_sensitive(entry); - } -} - -/* - * This should be called with write lock on - */ -static void in_cache_remove_entry(in_cache_entry *entry, - struct mpoa_client *client) -{ - struct atm_vcc *vcc; - struct k_message msg; - - vcc = entry->shortcut; - dprintk("removing an ingress entry, ip = %pI4\n", - &entry->ctrl_info.in_dst_ip); - - if (entry->prev != NULL) - entry->prev->next = entry->next; - else - client->in_cache = entry->next; - if (entry->next != NULL) - entry->next->prev = entry->prev; - client->in_ops->put(entry); - if (client->in_cache == NULL && client->eg_cache == NULL) { - msg.type = STOP_KEEP_ALIVE_SM; - msg_to_mpoad(&msg, client); - } - - /* Check if the egress side still uses this VCC */ - if (vcc != NULL) { - eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc, - client); - if (eg_entry != NULL) { - client->eg_ops->put(eg_entry); - return; - } - vcc_release_async(vcc, -EPIPE); - } -} - -/* Call this every MPC-p2 seconds... Not exactly correct solution, - but an easy one... */ -static void clear_count_and_expired(struct mpoa_client *client) -{ - in_cache_entry *entry, *next_entry; - time64_t now; - - now = ktime_get_seconds(); - - write_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - entry->count = 0; - next_entry = entry->next; - if ((now - entry->time) > entry->ctrl_info.holding_time) { - dprintk("holding time expired, ip = %pI4\n", - &entry->ctrl_info.in_dst_ip); - client->in_ops->remove_entry(entry, client); - } - entry = next_entry; - } - write_unlock_bh(&client->ingress_lock); -} - -/* Call this every MPC-p4 seconds. */ -static void check_resolving_entries(struct mpoa_client *client) -{ - - struct atm_mpoa_qos *qos; - in_cache_entry *entry; - time64_t now; - struct k_message msg; - - now = ktime_get_seconds(); - - read_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - if (entry->entry_state == INGRESS_RESOLVING) { - - if ((now - entry->hold_down) - < client->parameters.mpc_p6) { - entry = entry->next; /* Entry in hold down */ - continue; - } - if ((now - entry->reply_wait) > entry->retry_time) { - entry->retry_time = MPC_C1 * (entry->retry_time); - /* - * Retry time maximum exceeded, - * put entry in hold down. - */ - if (entry->retry_time > client->parameters.mpc_p5) { - entry->hold_down = ktime_get_seconds(); - entry->retry_time = client->parameters.mpc_p4; - entry = entry->next; - continue; - } - /* Ask daemon to send a resolution request. */ - memset(&entry->hold_down, 0, sizeof(time64_t)); - msg.type = SND_MPOA_RES_RTRY; - memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN); - msg.content.in_info = entry->ctrl_info; - qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); - if (qos != NULL) - msg.qos = qos->qos; - msg_to_mpoad(&msg, client); - entry->reply_wait = ktime_get_seconds(); - } - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); -} - -/* Call this every MPC-p5 seconds. */ -static void refresh_entries(struct mpoa_client *client) -{ - time64_t now; - struct in_cache_entry *entry = client->in_cache; - - ddprintk("refresh_entries\n"); - now = ktime_get_seconds(); - - read_lock_bh(&client->ingress_lock); - while (entry != NULL) { - if (entry->entry_state == INGRESS_RESOLVED) { - if (!(entry->refresh_time)) - entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3; - if ((now - entry->reply_wait) > - entry->refresh_time) { - dprintk("refreshing an entry.\n"); - entry->entry_state = INGRESS_REFRESHING; - - } - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); -} - -static void in_destroy_cache(struct mpoa_client *mpc) -{ - write_lock_irq(&mpc->ingress_lock); - while (mpc->in_cache != NULL) - mpc->in_ops->remove_entry(mpc->in_cache, mpc); - write_unlock_irq(&mpc->ingress_lock); -} - -static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id, - struct mpoa_client *mpc) -{ - eg_cache_entry *entry; - - read_lock_irq(&mpc->egress_lock); - entry = mpc->eg_cache; - while (entry != NULL) { - if (entry->ctrl_info.cache_id == cache_id) { - refcount_inc(&entry->use); - read_unlock_irq(&mpc->egress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_irq(&mpc->egress_lock); - - return NULL; -} - -/* This can be called from any context since it saves CPU flags */ -static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc) -{ - unsigned long flags; - eg_cache_entry *entry; - - read_lock_irqsave(&mpc->egress_lock, flags); - entry = mpc->eg_cache; - while (entry != NULL) { - if (entry->ctrl_info.tag == tag) { - refcount_inc(&entry->use); - read_unlock_irqrestore(&mpc->egress_lock, flags); - return entry; - } - entry = entry->next; - } - read_unlock_irqrestore(&mpc->egress_lock, flags); - - return NULL; -} - -/* This can be called from any context since it saves CPU flags */ -static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, - struct mpoa_client *mpc) -{ - unsigned long flags; - eg_cache_entry *entry; - - read_lock_irqsave(&mpc->egress_lock, flags); - entry = mpc->eg_cache; - while (entry != NULL) { - if (entry->shortcut == vcc) { - refcount_inc(&entry->use); - read_unlock_irqrestore(&mpc->egress_lock, flags); - return entry; - } - entry = entry->next; - } - read_unlock_irqrestore(&mpc->egress_lock, flags); - - return NULL; -} - -static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr, - struct mpoa_client *mpc) -{ - eg_cache_entry *entry; - - read_lock_irq(&mpc->egress_lock); - entry = mpc->eg_cache; - while (entry != NULL) { - if (entry->latest_ip_addr == ipaddr) { - refcount_inc(&entry->use); - read_unlock_irq(&mpc->egress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_irq(&mpc->egress_lock); - - return NULL; -} - -static void eg_cache_put(eg_cache_entry *entry) -{ - if (refcount_dec_and_test(&entry->use)) { - kfree_sensitive(entry); - } -} - -/* - * This should be called with write lock on - */ -static void eg_cache_remove_entry(eg_cache_entry *entry, - struct mpoa_client *client) -{ - struct atm_vcc *vcc; - struct k_message msg; - - vcc = entry->shortcut; - dprintk("removing an egress entry.\n"); - if (entry->prev != NULL) - entry->prev->next = entry->next; - else - client->eg_cache = entry->next; - if (entry->next != NULL) - entry->next->prev = entry->prev; - client->eg_ops->put(entry); - if (client->in_cache == NULL && client->eg_cache == NULL) { - msg.type = STOP_KEEP_ALIVE_SM; - msg_to_mpoad(&msg, client); - } - - /* Check if the ingress side still uses this VCC */ - if (vcc != NULL) { - in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client); - if (in_entry != NULL) { - client->in_ops->put(in_entry); - return; - } - vcc_release_async(vcc, -EPIPE); - } -} - -static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, - struct mpoa_client *client) -{ - eg_cache_entry *entry = kzalloc_obj(eg_cache_entry); - - if (entry == NULL) { - pr_info("out of memory\n"); - return NULL; - } - - dprintk("adding an egress entry, ip = %pI4, this should be our IP\n", - &msg->content.eg_info.eg_dst_ip); - - refcount_set(&entry->use, 1); - dprintk("new_eg_cache_entry: about to lock\n"); - write_lock_irq(&client->egress_lock); - entry->next = client->eg_cache; - entry->prev = NULL; - if (client->eg_cache != NULL) - client->eg_cache->prev = entry; - client->eg_cache = entry; - - memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); - entry->ctrl_info = msg->content.eg_info; - entry->time = ktime_get_seconds(); - entry->entry_state = EGRESS_RESOLVED; - dprintk("new_eg_cache_entry cache_id %u\n", - ntohl(entry->ctrl_info.cache_id)); - dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip); - refcount_inc(&entry->use); - - write_unlock_irq(&client->egress_lock); - dprintk("new_eg_cache_entry: unlocked\n"); - - return entry; -} - -static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time) -{ - entry->time = ktime_get_seconds(); - entry->entry_state = EGRESS_RESOLVED; - entry->ctrl_info.holding_time = holding_time; -} - -static void clear_expired(struct mpoa_client *client) -{ - eg_cache_entry *entry, *next_entry; - time64_t now; - struct k_message msg; - - now = ktime_get_seconds(); - - write_lock_irq(&client->egress_lock); - entry = client->eg_cache; - while (entry != NULL) { - next_entry = entry->next; - if ((now - entry->time) > entry->ctrl_info.holding_time) { - msg.type = SND_EGRESS_PURGE; - msg.content.eg_info = entry->ctrl_info; - dprintk("egress_cache: holding time expired, cache_id = %u.\n", - ntohl(entry->ctrl_info.cache_id)); - msg_to_mpoad(&msg, client); - client->eg_ops->remove_entry(entry, client); - } - entry = next_entry; - } - write_unlock_irq(&client->egress_lock); -} - -static void eg_destroy_cache(struct mpoa_client *mpc) -{ - write_lock_irq(&mpc->egress_lock); - while (mpc->eg_cache != NULL) - mpc->eg_ops->remove_entry(mpc->eg_cache, mpc); - write_unlock_irq(&mpc->egress_lock); -} - - -static const struct in_cache_ops ingress_ops = { - .add_entry = in_cache_add_entry, - .get = in_cache_get, - .get_with_mask = in_cache_get_with_mask, - .get_by_vcc = in_cache_get_by_vcc, - .put = in_cache_put, - .remove_entry = in_cache_remove_entry, - .cache_hit = cache_hit, - .clear_count = clear_count_and_expired, - .check_resolving = check_resolving_entries, - .refresh = refresh_entries, - .destroy_cache = in_destroy_cache -}; - -static const struct eg_cache_ops egress_ops = { - .add_entry = eg_cache_add_entry, - .get_by_cache_id = eg_cache_get_by_cache_id, - .get_by_tag = eg_cache_get_by_tag, - .get_by_vcc = eg_cache_get_by_vcc, - .get_by_src_ip = eg_cache_get_by_src_ip, - .put = eg_cache_put, - .remove_entry = eg_cache_remove_entry, - .update = update_eg_cache_entry, - .clear_expired = clear_expired, - .destroy_cache = eg_destroy_cache -}; - -void atm_mpoa_init_cache(struct mpoa_client *mpc) -{ - mpc->in_ops = &ingress_ops; - mpc->eg_ops = &egress_ops; -} diff --git a/net/atm/mpoa_caches.h b/net/atm/mpoa_caches.h deleted file mode 100644 index 464c4c7f8d1f..000000000000 --- a/net/atm/mpoa_caches.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef MPOA_CACHES_H -#define MPOA_CACHES_H - -#include <linux/time64.h> -#include <linux/netdevice.h> -#include <linux/types.h> -#include <linux/atm.h> -#include <linux/atmdev.h> -#include <linux/atmmpc.h> -#include <linux/refcount.h> - -struct mpoa_client; - -void atm_mpoa_init_cache(struct mpoa_client *mpc); - -typedef struct in_cache_entry { - struct in_cache_entry *next; - struct in_cache_entry *prev; - time64_t time; - time64_t reply_wait; - time64_t hold_down; - uint32_t packets_fwded; - uint16_t entry_state; - uint32_t retry_time; - uint32_t refresh_time; - uint32_t count; - struct atm_vcc *shortcut; - uint8_t MPS_ctrl_ATM_addr[ATM_ESA_LEN]; - struct in_ctrl_info ctrl_info; - refcount_t use; -} in_cache_entry; - -struct in_cache_ops{ - in_cache_entry *(*add_entry)(__be32 dst_ip, - struct mpoa_client *client); - in_cache_entry *(*get)(__be32 dst_ip, struct mpoa_client *client); - in_cache_entry *(*get_with_mask)(__be32 dst_ip, - struct mpoa_client *client, - __be32 mask); - in_cache_entry *(*get_by_vcc)(struct atm_vcc *vcc, - struct mpoa_client *client); - void (*put)(in_cache_entry *entry); - void (*remove_entry)(in_cache_entry *delEntry, - struct mpoa_client *client ); - int (*cache_hit)(in_cache_entry *entry, - struct mpoa_client *client); - void (*clear_count)(struct mpoa_client *client); - void (*check_resolving)(struct mpoa_client *client); - void (*refresh)(struct mpoa_client *client); - void (*destroy_cache)(struct mpoa_client *mpc); -}; - -typedef struct eg_cache_entry{ - struct eg_cache_entry *next; - struct eg_cache_entry *prev; - time64_t time; - uint8_t MPS_ctrl_ATM_addr[ATM_ESA_LEN]; - struct atm_vcc *shortcut; - uint32_t packets_rcvd; - uint16_t entry_state; - __be32 latest_ip_addr; /* The src IP address of the last packet */ - struct eg_ctrl_info ctrl_info; - refcount_t use; -} eg_cache_entry; - -struct eg_cache_ops{ - eg_cache_entry *(*add_entry)(struct k_message *msg, struct mpoa_client *client); - eg_cache_entry *(*get_by_cache_id)(__be32 cache_id, struct mpoa_client *client); - eg_cache_entry *(*get_by_tag)(__be32 cache_id, struct mpoa_client *client); - eg_cache_entry *(*get_by_vcc)(struct atm_vcc *vcc, struct mpoa_client *client); - eg_cache_entry *(*get_by_src_ip)(__be32 ipaddr, struct mpoa_client *client); - void (*put)(eg_cache_entry *entry); - void (*remove_entry)(eg_cache_entry *entry, struct mpoa_client *client); - void (*update)(eg_cache_entry *entry, uint16_t holding_time); - void (*clear_expired)(struct mpoa_client *client); - void (*destroy_cache)(struct mpoa_client *mpc); -}; - - -/* Ingress cache entry states */ - -#define INGRESS_REFRESHING 3 -#define INGRESS_RESOLVED 2 -#define INGRESS_RESOLVING 1 -#define INGRESS_INVALID 0 - -/* VCC states */ - -#define OPEN 1 -#define CLOSED 0 - -/* Egress cache entry states */ - -#define EGRESS_RESOLVED 2 -#define EGRESS_PURGE 1 -#define EGRESS_INVALID 0 - -#endif diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c deleted file mode 100644 index aaf64b953915..000000000000 --- a/net/atm/mpoa_proc.c +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#ifdef CONFIG_PROC_FS -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/ktime.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#include <linux/atmmpc.h> -#include <linux/atm.h> -#include <linux/gfp.h> -#include "mpc.h" -#include "mpoa_caches.h" - -/* - * mpoa_proc.c: Implementation MPOA client's proc - * file system statistics - */ - -#if 1 -#define dprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ -#else -#define dprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ - } while (0) -#endif - -#if 0 -#define ddprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ -#else -#define ddprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ - } while (0) -#endif - -#define STAT_FILE_NAME "mpc" /* Our statistic file's name */ - -extern struct mpoa_client *mpcs; -extern struct proc_dir_entry *atm_proc_root; /* from proc.c. */ - -static int proc_mpc_open(struct inode *inode, struct file *file); -static ssize_t proc_mpc_write(struct file *file, const char __user *buff, - size_t nbytes, loff_t *ppos); - -static int parse_qos(const char *buff); - -static const struct proc_ops mpc_proc_ops = { - .proc_open = proc_mpc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_write = proc_mpc_write, - .proc_release = seq_release, -}; - -/* - * Returns the state of an ingress cache entry as a string - */ -static const char *ingress_state_string(int state) -{ - switch (state) { - case INGRESS_RESOLVING: - return "resolving "; - case INGRESS_RESOLVED: - return "resolved "; - case INGRESS_INVALID: - return "invalid "; - case INGRESS_REFRESHING: - return "refreshing "; - } - - return ""; -} - -/* - * Returns the state of an egress cache entry as a string - */ -static const char *egress_state_string(int state) -{ - switch (state) { - case EGRESS_RESOLVED: - return "resolved "; - case EGRESS_PURGE: - return "purge "; - case EGRESS_INVALID: - return "invalid "; - } - - return ""; -} - -/* - * FIXME: mpcs (and per-mpc lists) have no locking whatsoever. - */ - -static void *mpc_start(struct seq_file *m, loff_t *pos) -{ - loff_t l = *pos; - struct mpoa_client *mpc; - - if (!l--) - return SEQ_START_TOKEN; - for (mpc = mpcs; mpc; mpc = mpc->next) - if (!l--) - return mpc; - return NULL; -} - -static void *mpc_next(struct seq_file *m, void *v, loff_t *pos) -{ - struct mpoa_client *p = v; - (*pos)++; - return v == SEQ_START_TOKEN ? mpcs : p->next; -} - -static void mpc_stop(struct seq_file *m, void *v) -{ -} - -/* - * READING function - called when the /proc/atm/mpoa file is read from. - */ -static int mpc_show(struct seq_file *m, void *v) -{ - struct mpoa_client *mpc = v; - int i; - in_cache_entry *in_entry; - eg_cache_entry *eg_entry; - time64_t now; - unsigned char ip_string[16]; - - if (v == SEQ_START_TOKEN) { - atm_mpoa_disp_qos(m); - return 0; - } - - seq_printf(m, "\nInterface %d:\n\n", mpc->dev_num); - seq_printf(m, "Ingress Entries:\nIP address State Holding time Packets fwded VPI VCI\n"); - now = ktime_get_seconds(); - - for (in_entry = mpc->in_cache; in_entry; in_entry = in_entry->next) { - unsigned long seconds_delta = now - in_entry->time; - - sprintf(ip_string, "%pI4", &in_entry->ctrl_info.in_dst_ip); - seq_printf(m, "%-16s%s%-14lu%-12u", - ip_string, - ingress_state_string(in_entry->entry_state), - in_entry->ctrl_info.holding_time - - seconds_delta, - in_entry->packets_fwded); - if (in_entry->shortcut) - seq_printf(m, " %-3d %-3d", - in_entry->shortcut->vpi, - in_entry->shortcut->vci); - seq_printf(m, "\n"); - } - - seq_printf(m, "\n"); - seq_printf(m, "Egress Entries:\nIngress MPC ATM addr\nCache-id State Holding time Packets recvd Latest IP addr VPI VCI\n"); - for (eg_entry = mpc->eg_cache; eg_entry; eg_entry = eg_entry->next) { - unsigned char *p = eg_entry->ctrl_info.in_MPC_data_ATM_addr; - unsigned long seconds_delta = now - eg_entry->time; - - for (i = 0; i < ATM_ESA_LEN; i++) - seq_printf(m, "%02x", p[i]); - seq_printf(m, "\n%-16lu%s%-14lu%-15u", - (unsigned long)ntohl(eg_entry->ctrl_info.cache_id), - egress_state_string(eg_entry->entry_state), - (eg_entry->ctrl_info.holding_time - seconds_delta), - eg_entry->packets_rcvd); - - /* latest IP address */ - sprintf(ip_string, "%pI4", &eg_entry->latest_ip_addr); - seq_printf(m, "%-16s", ip_string); - - if (eg_entry->shortcut) - seq_printf(m, " %-3d %-3d", - eg_entry->shortcut->vpi, - eg_entry->shortcut->vci); - seq_printf(m, "\n"); - } - seq_printf(m, "\n"); - return 0; -} - -static const struct seq_operations mpc_op = { - .start = mpc_start, - .next = mpc_next, - .stop = mpc_stop, - .show = mpc_show -}; - -static int proc_mpc_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &mpc_op); -} - -static ssize_t proc_mpc_write(struct file *file, const char __user *buff, - size_t nbytes, loff_t *ppos) -{ - char *page, *p; - unsigned int len; - - if (nbytes == 0) - return 0; - - if (nbytes >= PAGE_SIZE) - nbytes = PAGE_SIZE-1; - - page = (char *)__get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - for (p = page, len = 0; len < nbytes; p++) { - if (get_user(*p, buff++)) { - free_page((unsigned long)page); - return -EFAULT; - } - len += 1; - if (*p == '\0' || *p == '\n') - break; - } - - *p = '\0'; - - if (!parse_qos(page)) - printk("mpoa: proc_mpc_write: could not parse '%s'\n", page); - - free_page((unsigned long)page); - - return len; -} - -static int parse_qos(const char *buff) -{ - /* possible lines look like this - * add 130.230.54.142 tx=max_pcr,max_sdu rx=max_pcr,max_sdu - */ - unsigned char ip[4]; - int tx_pcr, tx_sdu, rx_pcr, rx_sdu; - __be32 ipaddr; - struct atm_qos qos; - - memset(&qos, 0, sizeof(struct atm_qos)); - - if (sscanf(buff, "del %hhu.%hhu.%hhu.%hhu", - ip, ip+1, ip+2, ip+3) == 4) { - ipaddr = *(__be32 *)ip; - return atm_mpoa_delete_qos(atm_mpoa_search_qos(ipaddr)); - } - - if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=tx", - ip, ip+1, ip+2, ip+3, &tx_pcr, &tx_sdu) == 6) { - rx_pcr = tx_pcr; - rx_sdu = tx_sdu; - } else if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=%d,%d", - ip, ip+1, ip+2, ip+3, &tx_pcr, &tx_sdu, &rx_pcr, &rx_sdu) != 8) - return 0; - - ipaddr = *(__be32 *)ip; - qos.txtp.traffic_class = ATM_CBR; - qos.txtp.max_pcr = tx_pcr; - qos.txtp.max_sdu = tx_sdu; - qos.rxtp.traffic_class = ATM_CBR; - qos.rxtp.max_pcr = rx_pcr; - qos.rxtp.max_sdu = rx_sdu; - qos.aal = ATM_AAL5; - dprintk("parse_qos(): setting qos parameters to tx=%d,%d rx=%d,%d\n", - qos.txtp.max_pcr, qos.txtp.max_sdu, - qos.rxtp.max_pcr, qos.rxtp.max_sdu); - - atm_mpoa_add_qos(ipaddr, &qos); - return 1; -} - -/* - * INITIALIZATION function - called when module is initialized/loaded. - */ -int mpc_proc_init(void) -{ - struct proc_dir_entry *p; - - p = proc_create(STAT_FILE_NAME, 0, atm_proc_root, &mpc_proc_ops); - if (!p) { - pr_err("Unable to initialize /proc/atm/%s\n", STAT_FILE_NAME); - return -ENOMEM; - } - return 0; -} - -/* - * DELETING function - called when module is removed. - */ -void mpc_proc_clean(void) -{ - remove_proc_entry(STAT_FILE_NAME, atm_proc_root); -} - -#endif /* CONFIG_PROC_FS */ diff --git a/net/atm/proc.c b/net/atm/proc.c index 9bf736290e48..b650da764a23 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -21,11 +21,9 @@ #include <linux/atm.h> #include <linux/atmdev.h> #include <linux/netdevice.h> -#include <linux/atmclip.h> #include <linux/init.h> /* for __init */ #include <linux/slab.h> #include <net/net_namespace.h> -#include <net/atmclip.h> #include <linux/uaccess.h> #include <linux/param.h> /* for HZ */ #include <linux/atomic.h> @@ -155,15 +153,6 @@ static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) class_name[vcc->qos.rxtp.traffic_class], vcc->qos.txtp.min_pcr, class_name[vcc->qos.txtp.traffic_class]); - if (test_bit(ATM_VF_IS_CLIP, &vcc->flags)) { - struct clip_vcc *clip_vcc = CLIP_VCC(vcc); - struct net_device *dev; - - dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL; - seq_printf(seq, "CLIP, Itf:%s, Encap:", - dev ? dev->name : "none?"); - seq_printf(seq, "%s", clip_vcc->encap ? "LLC/SNAP" : "None"); - } seq_putc(seq, '\n'); } diff --git a/net/bridge/br.c b/net/bridge/br.c index c37e52e2f29a..a5e5b2db110e 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -464,10 +464,6 @@ static int __init br_init(void) brioctl_set(br_ioctl_stub); -#if IS_ENABLED(CONFIG_ATM_LANE) - br_fdb_test_addr_hook = br_fdb_test_addr; -#endif - #if IS_MODULE(CONFIG_BRIDGE_NETFILTER) pr_info("bridge: filtering via arp/ip/ip6tables is no longer available " "by default. Update your scripts to load br_netfilter if you " @@ -506,9 +502,6 @@ static void __exit br_deinit(void) rcu_barrier(); /* Wait for completion of call_rcu()'s */ br_nf_core_fini(); -#if IS_ENABLED(CONFIG_ATM_LANE) - br_fdb_test_addr_hook = NULL; -#endif br_fdb_fini(); } diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index e2c17f620f00..9bcf6243914b 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -892,35 +892,6 @@ void br_fdb_delete_by_port(struct net_bridge *br, spin_unlock_bh(&br->hash_lock); } -#if IS_ENABLED(CONFIG_ATM_LANE) -/* Interface used by ATM LANE hook to test - * if an addr is on some other bridge port */ -int br_fdb_test_addr(struct net_device *dev, unsigned char *addr) -{ - struct net_bridge_fdb_entry *fdb; - struct net_bridge_port *port; - int ret; - - rcu_read_lock(); - port = br_port_get_rcu(dev); - if (!port) - ret = 0; - else { - const struct net_bridge_port *dst = NULL; - - fdb = br_fdb_find_rcu(port->br, addr, 0); - if (fdb) - dst = READ_ONCE(fdb->dst); - - ret = dst && dst->dev != dev && - dst->state == BR_STATE_FORWARDING; - } - rcu_read_unlock(); - - return ret; -} -#endif /* CONFIG_ATM_LANE */ - /* * Fill buffer with forwarding table records in * the API format. diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 361a9b84451e..bed1b1d9b282 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -855,7 +855,6 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br, const unsigned char *addr, __u16 vid); -int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, unsigned long off); int br_fdb_add_local(struct net_bridge *br, struct net_bridge_port *source, @@ -2065,9 +2064,6 @@ void br_stp_port_timer_init(struct net_bridge_port *p); unsigned long br_timer_value(const struct timer_list *timer); /* br.c */ -#if IS_ENABLED(CONFIG_ATM_LANE) -extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr); -#endif /* br_mrp.c */ #if IS_ENABLED(CONFIG_BRIDGE_MRP) diff --git a/net/core/dev.c b/net/core/dev.c index e59f6025067c..1be81928d6c7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5862,13 +5862,6 @@ static __latent_entropy void net_tx_action(void) xfrm_dev_backlog(sd); } -#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_ATM_LANE) -/* This hook is defined here for ATM LANE */ -int (*br_fdb_test_addr_hook)(struct net_device *dev, - unsigned char *addr) __read_mostly; -EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook); -#endif - /** * netdev_is_rx_handler_busy - check if receive handler is registered * @dev: device to check |
