162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* net/atm/clip.c - RFC1577 Classical IP over ATM */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/string.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> /* for UINT_MAX */ 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/netdevice.h> 1462306a36Sopenharmony_ci#include <linux/skbuff.h> 1562306a36Sopenharmony_ci#include <linux/wait.h> 1662306a36Sopenharmony_ci#include <linux/timer.h> 1762306a36Sopenharmony_ci#include <linux/if_arp.h> /* for some manifest constants */ 1862306a36Sopenharmony_ci#include <linux/notifier.h> 1962306a36Sopenharmony_ci#include <linux/atm.h> 2062306a36Sopenharmony_ci#include <linux/atmdev.h> 2162306a36Sopenharmony_ci#include <linux/atmclip.h> 2262306a36Sopenharmony_ci#include <linux/atmarp.h> 2362306a36Sopenharmony_ci#include <linux/capability.h> 2462306a36Sopenharmony_ci#include <linux/ip.h> /* for net/route.h */ 2562306a36Sopenharmony_ci#include <linux/in.h> /* for struct sockaddr_in */ 2662306a36Sopenharmony_ci#include <linux/if.h> /* for IFF_UP */ 2762306a36Sopenharmony_ci#include <linux/inetdevice.h> 2862306a36Sopenharmony_ci#include <linux/bitops.h> 2962306a36Sopenharmony_ci#include <linux/poison.h> 3062306a36Sopenharmony_ci#include <linux/proc_fs.h> 3162306a36Sopenharmony_ci#include <linux/seq_file.h> 3262306a36Sopenharmony_ci#include <linux/rcupdate.h> 3362306a36Sopenharmony_ci#include <linux/jhash.h> 3462306a36Sopenharmony_ci#include <linux/slab.h> 3562306a36Sopenharmony_ci#include <net/route.h> /* for struct rtable and routing */ 3662306a36Sopenharmony_ci#include <net/icmp.h> /* icmp_send */ 3762306a36Sopenharmony_ci#include <net/arp.h> 3862306a36Sopenharmony_ci#include <linux/param.h> /* for HZ */ 3962306a36Sopenharmony_ci#include <linux/uaccess.h> 4062306a36Sopenharmony_ci#include <asm/byteorder.h> /* for htons etc. */ 4162306a36Sopenharmony_ci#include <linux/atomic.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include "common.h" 4462306a36Sopenharmony_ci#include "resources.h" 4562306a36Sopenharmony_ci#include <net/atmclip.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct net_device *clip_devs; 4862306a36Sopenharmony_cistatic struct atm_vcc *atmarpd; 4962306a36Sopenharmony_cistatic struct timer_list idle_timer; 5062306a36Sopenharmony_cistatic const struct neigh_ops clip_neigh_ops; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int to_atmarpd(enum atmarp_ctrl_type type, int itf, __be32 ip) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct sock *sk; 5562306a36Sopenharmony_ci struct atmarp_ctrl *ctrl; 5662306a36Sopenharmony_ci struct sk_buff *skb; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci pr_debug("(%d)\n", type); 5962306a36Sopenharmony_ci if (!atmarpd) 6062306a36Sopenharmony_ci return -EUNATCH; 6162306a36Sopenharmony_ci skb = alloc_skb(sizeof(struct atmarp_ctrl), GFP_ATOMIC); 6262306a36Sopenharmony_ci if (!skb) 6362306a36Sopenharmony_ci return -ENOMEM; 6462306a36Sopenharmony_ci ctrl = skb_put(skb, sizeof(struct atmarp_ctrl)); 6562306a36Sopenharmony_ci ctrl->type = type; 6662306a36Sopenharmony_ci ctrl->itf_num = itf; 6762306a36Sopenharmony_ci ctrl->ip = ip; 6862306a36Sopenharmony_ci atm_force_charge(atmarpd, skb->truesize); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci sk = sk_atm(atmarpd); 7162306a36Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 7262306a36Sopenharmony_ci sk->sk_data_ready(sk); 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void link_vcc(struct clip_vcc *clip_vcc, struct atmarp_entry *entry) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci pr_debug("%p to entry %p (neigh %p)\n", clip_vcc, entry, entry->neigh); 7962306a36Sopenharmony_ci clip_vcc->entry = entry; 8062306a36Sopenharmony_ci clip_vcc->xoff = 0; /* @@@ may overrun buffer by one packet */ 8162306a36Sopenharmony_ci clip_vcc->next = entry->vccs; 8262306a36Sopenharmony_ci entry->vccs = clip_vcc; 8362306a36Sopenharmony_ci entry->neigh->used = jiffies; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void unlink_clip_vcc(struct clip_vcc *clip_vcc) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct atmarp_entry *entry = clip_vcc->entry; 8962306a36Sopenharmony_ci struct clip_vcc **walk; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!entry) { 9262306a36Sopenharmony_ci pr_err("!clip_vcc->entry (clip_vcc %p)\n", clip_vcc); 9362306a36Sopenharmony_ci return; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci netif_tx_lock_bh(entry->neigh->dev); /* block clip_start_xmit() */ 9662306a36Sopenharmony_ci entry->neigh->used = jiffies; 9762306a36Sopenharmony_ci for (walk = &entry->vccs; *walk; walk = &(*walk)->next) 9862306a36Sopenharmony_ci if (*walk == clip_vcc) { 9962306a36Sopenharmony_ci int error; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci *walk = clip_vcc->next; /* atomic */ 10262306a36Sopenharmony_ci clip_vcc->entry = NULL; 10362306a36Sopenharmony_ci if (clip_vcc->xoff) 10462306a36Sopenharmony_ci netif_wake_queue(entry->neigh->dev); 10562306a36Sopenharmony_ci if (entry->vccs) 10662306a36Sopenharmony_ci goto out; 10762306a36Sopenharmony_ci entry->expires = jiffies - 1; 10862306a36Sopenharmony_ci /* force resolution or expiration */ 10962306a36Sopenharmony_ci error = neigh_update(entry->neigh, NULL, NUD_NONE, 11062306a36Sopenharmony_ci NEIGH_UPDATE_F_ADMIN, 0); 11162306a36Sopenharmony_ci if (error) 11262306a36Sopenharmony_ci pr_err("neigh_update failed with %d\n", error); 11362306a36Sopenharmony_ci goto out; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci pr_err("ATMARP: failed (entry %p, vcc 0x%p)\n", entry, clip_vcc); 11662306a36Sopenharmony_ciout: 11762306a36Sopenharmony_ci netif_tx_unlock_bh(entry->neigh->dev); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* The neighbour entry n->lock is held. */ 12162306a36Sopenharmony_cistatic int neigh_check_cb(struct neighbour *n) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct atmarp_entry *entry = neighbour_priv(n); 12462306a36Sopenharmony_ci struct clip_vcc *cv; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (n->ops != &clip_neigh_ops) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci for (cv = entry->vccs; cv; cv = cv->next) { 12962306a36Sopenharmony_ci unsigned long exp = cv->last_use + cv->idle_timeout; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (cv->idle_timeout && time_after(jiffies, exp)) { 13262306a36Sopenharmony_ci pr_debug("releasing vcc %p->%p of entry %p\n", 13362306a36Sopenharmony_ci cv, cv->vcc, entry); 13462306a36Sopenharmony_ci vcc_release_async(cv->vcc, -ETIMEDOUT); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (entry->vccs || time_before(jiffies, entry->expires)) 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (refcount_read(&n->refcnt) > 1) { 14262306a36Sopenharmony_ci struct sk_buff *skb; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci pr_debug("destruction postponed with ref %d\n", 14562306a36Sopenharmony_ci refcount_read(&n->refcnt)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci while ((skb = skb_dequeue(&n->arp_queue)) != NULL) 14862306a36Sopenharmony_ci dev_kfree_skb(skb); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci pr_debug("expired neigh %p\n", n); 15462306a36Sopenharmony_ci return 1; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void idle_timer_check(struct timer_list *unused) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci write_lock(&arp_tbl.lock); 16062306a36Sopenharmony_ci __neigh_for_each_release(&arp_tbl, neigh_check_cb); 16162306a36Sopenharmony_ci mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ); 16262306a36Sopenharmony_ci write_unlock(&arp_tbl.lock); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int clip_arp_rcv(struct sk_buff *skb) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct atm_vcc *vcc; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci pr_debug("\n"); 17062306a36Sopenharmony_ci vcc = ATM_SKB(skb)->vcc; 17162306a36Sopenharmony_ci if (!vcc || !atm_charge(vcc, skb->truesize)) { 17262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci pr_debug("pushing to %p\n", vcc); 17662306a36Sopenharmony_ci pr_debug("using %p\n", CLIP_VCC(vcc)->old_push); 17762306a36Sopenharmony_ci CLIP_VCC(vcc)->old_push(vcc, skb); 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const unsigned char llc_oui[] = { 18262306a36Sopenharmony_ci 0xaa, /* DSAP: non-ISO */ 18362306a36Sopenharmony_ci 0xaa, /* SSAP: non-ISO */ 18462306a36Sopenharmony_ci 0x03, /* Ctrl: Unnumbered Information Command PDU */ 18562306a36Sopenharmony_ci 0x00, /* OUI: EtherType */ 18662306a36Sopenharmony_ci 0x00, 18762306a36Sopenharmony_ci 0x00 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void clip_push(struct atm_vcc *vcc, struct sk_buff *skb) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct clip_vcc *clip_vcc = CLIP_VCC(vcc); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci pr_debug("\n"); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!clip_devs) { 19762306a36Sopenharmony_ci atm_return(vcc, skb->truesize); 19862306a36Sopenharmony_ci kfree_skb(skb); 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!skb) { 20362306a36Sopenharmony_ci pr_debug("removing VCC %p\n", clip_vcc); 20462306a36Sopenharmony_ci if (clip_vcc->entry) 20562306a36Sopenharmony_ci unlink_clip_vcc(clip_vcc); 20662306a36Sopenharmony_ci clip_vcc->old_push(vcc, NULL); /* pass on the bad news */ 20762306a36Sopenharmony_ci kfree(clip_vcc); 20862306a36Sopenharmony_ci return; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci atm_return(vcc, skb->truesize); 21162306a36Sopenharmony_ci skb->dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : clip_devs; 21262306a36Sopenharmony_ci /* clip_vcc->entry == NULL if we don't have an IP address yet */ 21362306a36Sopenharmony_ci if (!skb->dev) { 21462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 21562306a36Sopenharmony_ci return; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci ATM_SKB(skb)->vcc = vcc; 21862306a36Sopenharmony_ci skb_reset_mac_header(skb); 21962306a36Sopenharmony_ci if (!clip_vcc->encap || 22062306a36Sopenharmony_ci skb->len < RFC1483LLC_LEN || 22162306a36Sopenharmony_ci memcmp(skb->data, llc_oui, sizeof(llc_oui))) 22262306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 22362306a36Sopenharmony_ci else { 22462306a36Sopenharmony_ci skb->protocol = ((__be16 *)skb->data)[3]; 22562306a36Sopenharmony_ci skb_pull(skb, RFC1483LLC_LEN); 22662306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_ARP)) { 22762306a36Sopenharmony_ci skb->dev->stats.rx_packets++; 22862306a36Sopenharmony_ci skb->dev->stats.rx_bytes += skb->len; 22962306a36Sopenharmony_ci clip_arp_rcv(skb); 23062306a36Sopenharmony_ci return; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci clip_vcc->last_use = jiffies; 23462306a36Sopenharmony_ci skb->dev->stats.rx_packets++; 23562306a36Sopenharmony_ci skb->dev->stats.rx_bytes += skb->len; 23662306a36Sopenharmony_ci memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); 23762306a36Sopenharmony_ci netif_rx(skb); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* 24162306a36Sopenharmony_ci * Note: these spinlocks _must_not_ block on non-SMP. The only goal is that 24262306a36Sopenharmony_ci * clip_pop is atomic with respect to the critical section in clip_start_xmit. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void clip_pop(struct atm_vcc *vcc, struct sk_buff *skb) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct clip_vcc *clip_vcc = CLIP_VCC(vcc); 24862306a36Sopenharmony_ci struct net_device *dev = skb->dev; 24962306a36Sopenharmony_ci int old; 25062306a36Sopenharmony_ci unsigned long flags; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pr_debug("(vcc %p)\n", vcc); 25362306a36Sopenharmony_ci clip_vcc->old_pop(vcc, skb); 25462306a36Sopenharmony_ci /* skb->dev == NULL in outbound ARP packets */ 25562306a36Sopenharmony_ci if (!dev) 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci spin_lock_irqsave(&PRIV(dev)->xoff_lock, flags); 25862306a36Sopenharmony_ci if (atm_may_send(vcc, 0)) { 25962306a36Sopenharmony_ci old = xchg(&clip_vcc->xoff, 0); 26062306a36Sopenharmony_ci if (old) 26162306a36Sopenharmony_ci netif_wake_queue(dev); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci spin_unlock_irqrestore(&PRIV(dev)->xoff_lock, flags); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic void clip_neigh_solicit(struct neighbour *neigh, struct sk_buff *skb) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci __be32 *ip = (__be32 *) neigh->primary_key; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci pr_debug("(neigh %p, skb %p)\n", neigh, skb); 27162306a36Sopenharmony_ci to_atmarpd(act_need, PRIV(neigh->dev)->number, *ip); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void clip_neigh_error(struct neighbour *neigh, struct sk_buff *skb) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci#ifndef CONFIG_ATM_CLIP_NO_ICMP 27762306a36Sopenharmony_ci icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); 27862306a36Sopenharmony_ci#endif 27962306a36Sopenharmony_ci kfree_skb(skb); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic const struct neigh_ops clip_neigh_ops = { 28362306a36Sopenharmony_ci .family = AF_INET, 28462306a36Sopenharmony_ci .solicit = clip_neigh_solicit, 28562306a36Sopenharmony_ci .error_report = clip_neigh_error, 28662306a36Sopenharmony_ci .output = neigh_direct_output, 28762306a36Sopenharmony_ci .connected_output = neigh_direct_output, 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int clip_constructor(struct net_device *dev, struct neighbour *neigh) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct atmarp_entry *entry = neighbour_priv(neigh); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (neigh->tbl->family != AF_INET) 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (neigh->type != RTN_UNICAST) 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci neigh->nud_state = NUD_NONE; 30162306a36Sopenharmony_ci neigh->ops = &clip_neigh_ops; 30262306a36Sopenharmony_ci neigh->output = neigh->ops->output; 30362306a36Sopenharmony_ci entry->neigh = neigh; 30462306a36Sopenharmony_ci entry->vccs = NULL; 30562306a36Sopenharmony_ci entry->expires = jiffies - 1; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* @@@ copy bh locking from arp.c -- need to bh-enable atm code before */ 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* 31362306a36Sopenharmony_ci * We play with the resolve flag: 0 and 1 have the usual meaning, but -1 means 31462306a36Sopenharmony_ci * to allocate the neighbour entry but not to ask atmarpd for resolution. Also, 31562306a36Sopenharmony_ci * don't increment the usage count. This is used to create entries in 31662306a36Sopenharmony_ci * clip_setentry. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int clip_encap(struct atm_vcc *vcc, int mode) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci if (!CLIP_VCC(vcc)) 32262306a36Sopenharmony_ci return -EBADFD; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci CLIP_VCC(vcc)->encap = mode; 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic netdev_tx_t clip_start_xmit(struct sk_buff *skb, 32962306a36Sopenharmony_ci struct net_device *dev) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct clip_priv *clip_priv = PRIV(dev); 33262306a36Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 33362306a36Sopenharmony_ci struct atmarp_entry *entry; 33462306a36Sopenharmony_ci struct neighbour *n; 33562306a36Sopenharmony_ci struct atm_vcc *vcc; 33662306a36Sopenharmony_ci struct rtable *rt; 33762306a36Sopenharmony_ci __be32 *daddr; 33862306a36Sopenharmony_ci int old; 33962306a36Sopenharmony_ci unsigned long flags; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci pr_debug("(skb %p)\n", skb); 34262306a36Sopenharmony_ci if (!dst) { 34362306a36Sopenharmony_ci pr_err("skb_dst(skb) == NULL\n"); 34462306a36Sopenharmony_ci dev_kfree_skb(skb); 34562306a36Sopenharmony_ci dev->stats.tx_dropped++; 34662306a36Sopenharmony_ci return NETDEV_TX_OK; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci rt = (struct rtable *) dst; 34962306a36Sopenharmony_ci if (rt->rt_gw_family == AF_INET) 35062306a36Sopenharmony_ci daddr = &rt->rt_gw4; 35162306a36Sopenharmony_ci else 35262306a36Sopenharmony_ci daddr = &ip_hdr(skb)->daddr; 35362306a36Sopenharmony_ci n = dst_neigh_lookup(dst, daddr); 35462306a36Sopenharmony_ci if (!n) { 35562306a36Sopenharmony_ci pr_err("NO NEIGHBOUR !\n"); 35662306a36Sopenharmony_ci dev_kfree_skb(skb); 35762306a36Sopenharmony_ci dev->stats.tx_dropped++; 35862306a36Sopenharmony_ci return NETDEV_TX_OK; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci entry = neighbour_priv(n); 36162306a36Sopenharmony_ci if (!entry->vccs) { 36262306a36Sopenharmony_ci if (time_after(jiffies, entry->expires)) { 36362306a36Sopenharmony_ci /* should be resolved */ 36462306a36Sopenharmony_ci entry->expires = jiffies + ATMARP_RETRY_DELAY * HZ; 36562306a36Sopenharmony_ci to_atmarpd(act_need, PRIV(dev)->number, *((__be32 *)n->primary_key)); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci if (entry->neigh->arp_queue.qlen < ATMARP_MAX_UNRES_PACKETS) 36862306a36Sopenharmony_ci skb_queue_tail(&entry->neigh->arp_queue, skb); 36962306a36Sopenharmony_ci else { 37062306a36Sopenharmony_ci dev_kfree_skb(skb); 37162306a36Sopenharmony_ci dev->stats.tx_dropped++; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci goto out_release_neigh; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci pr_debug("neigh %p, vccs %p\n", entry, entry->vccs); 37662306a36Sopenharmony_ci ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc; 37762306a36Sopenharmony_ci pr_debug("using neighbour %p, vcc %p\n", n, vcc); 37862306a36Sopenharmony_ci if (entry->vccs->encap) { 37962306a36Sopenharmony_ci void *here; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci here = skb_push(skb, RFC1483LLC_LEN); 38262306a36Sopenharmony_ci memcpy(here, llc_oui, sizeof(llc_oui)); 38362306a36Sopenharmony_ci ((__be16 *) here)[3] = skb->protocol; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci atm_account_tx(vcc, skb); 38662306a36Sopenharmony_ci entry->vccs->last_use = jiffies; 38762306a36Sopenharmony_ci pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, vcc, vcc->dev); 38862306a36Sopenharmony_ci old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */ 38962306a36Sopenharmony_ci if (old) { 39062306a36Sopenharmony_ci pr_warn("XOFF->XOFF transition\n"); 39162306a36Sopenharmony_ci goto out_release_neigh; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci dev->stats.tx_packets++; 39462306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 39562306a36Sopenharmony_ci vcc->send(vcc, skb); 39662306a36Sopenharmony_ci if (atm_may_send(vcc, 0)) { 39762306a36Sopenharmony_ci entry->vccs->xoff = 0; 39862306a36Sopenharmony_ci goto out_release_neigh; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci spin_lock_irqsave(&clip_priv->xoff_lock, flags); 40162306a36Sopenharmony_ci netif_stop_queue(dev); /* XOFF -> throttle immediately */ 40262306a36Sopenharmony_ci barrier(); 40362306a36Sopenharmony_ci if (!entry->vccs->xoff) 40462306a36Sopenharmony_ci netif_start_queue(dev); 40562306a36Sopenharmony_ci /* Oh, we just raced with clip_pop. netif_start_queue should be 40662306a36Sopenharmony_ci good enough, because nothing should really be asleep because 40762306a36Sopenharmony_ci of the brief netif_stop_queue. If this isn't true or if it 40862306a36Sopenharmony_ci changes, use netif_wake_queue instead. */ 40962306a36Sopenharmony_ci spin_unlock_irqrestore(&clip_priv->xoff_lock, flags); 41062306a36Sopenharmony_ciout_release_neigh: 41162306a36Sopenharmony_ci neigh_release(n); 41262306a36Sopenharmony_ci return NETDEV_TX_OK; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int clip_mkip(struct atm_vcc *vcc, int timeout) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct clip_vcc *clip_vcc; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!vcc->push) 42062306a36Sopenharmony_ci return -EBADFD; 42162306a36Sopenharmony_ci clip_vcc = kmalloc(sizeof(struct clip_vcc), GFP_KERNEL); 42262306a36Sopenharmony_ci if (!clip_vcc) 42362306a36Sopenharmony_ci return -ENOMEM; 42462306a36Sopenharmony_ci pr_debug("%p vcc %p\n", clip_vcc, vcc); 42562306a36Sopenharmony_ci clip_vcc->vcc = vcc; 42662306a36Sopenharmony_ci vcc->user_back = clip_vcc; 42762306a36Sopenharmony_ci set_bit(ATM_VF_IS_CLIP, &vcc->flags); 42862306a36Sopenharmony_ci clip_vcc->entry = NULL; 42962306a36Sopenharmony_ci clip_vcc->xoff = 0; 43062306a36Sopenharmony_ci clip_vcc->encap = 1; 43162306a36Sopenharmony_ci clip_vcc->last_use = jiffies; 43262306a36Sopenharmony_ci clip_vcc->idle_timeout = timeout * HZ; 43362306a36Sopenharmony_ci clip_vcc->old_push = vcc->push; 43462306a36Sopenharmony_ci clip_vcc->old_pop = vcc->pop; 43562306a36Sopenharmony_ci vcc->push = clip_push; 43662306a36Sopenharmony_ci vcc->pop = clip_pop; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* re-process everything received between connection setup and MKIP */ 43962306a36Sopenharmony_ci vcc_process_recv_queue(vcc); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int clip_setentry(struct atm_vcc *vcc, __be32 ip) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct neighbour *neigh; 44762306a36Sopenharmony_ci struct atmarp_entry *entry; 44862306a36Sopenharmony_ci int error; 44962306a36Sopenharmony_ci struct clip_vcc *clip_vcc; 45062306a36Sopenharmony_ci struct rtable *rt; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (vcc->push != clip_push) { 45362306a36Sopenharmony_ci pr_warn("non-CLIP VCC\n"); 45462306a36Sopenharmony_ci return -EBADF; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci clip_vcc = CLIP_VCC(vcc); 45762306a36Sopenharmony_ci if (!ip) { 45862306a36Sopenharmony_ci if (!clip_vcc->entry) { 45962306a36Sopenharmony_ci pr_err("hiding hidden ATMARP entry\n"); 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci pr_debug("remove\n"); 46362306a36Sopenharmony_ci unlink_clip_vcc(clip_vcc); 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci rt = ip_route_output(&init_net, ip, 0, 1, 0); 46762306a36Sopenharmony_ci if (IS_ERR(rt)) 46862306a36Sopenharmony_ci return PTR_ERR(rt); 46962306a36Sopenharmony_ci neigh = __neigh_lookup(&arp_tbl, &ip, rt->dst.dev, 1); 47062306a36Sopenharmony_ci ip_rt_put(rt); 47162306a36Sopenharmony_ci if (!neigh) 47262306a36Sopenharmony_ci return -ENOMEM; 47362306a36Sopenharmony_ci entry = neighbour_priv(neigh); 47462306a36Sopenharmony_ci if (entry != clip_vcc->entry) { 47562306a36Sopenharmony_ci if (!clip_vcc->entry) 47662306a36Sopenharmony_ci pr_debug("add\n"); 47762306a36Sopenharmony_ci else { 47862306a36Sopenharmony_ci pr_debug("update\n"); 47962306a36Sopenharmony_ci unlink_clip_vcc(clip_vcc); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci link_vcc(clip_vcc, entry); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci error = neigh_update(neigh, llc_oui, NUD_PERMANENT, 48462306a36Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 0); 48562306a36Sopenharmony_ci neigh_release(neigh); 48662306a36Sopenharmony_ci return error; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic const struct net_device_ops clip_netdev_ops = { 49062306a36Sopenharmony_ci .ndo_start_xmit = clip_start_xmit, 49162306a36Sopenharmony_ci .ndo_neigh_construct = clip_constructor, 49262306a36Sopenharmony_ci}; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void clip_setup(struct net_device *dev) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci dev->netdev_ops = &clip_netdev_ops; 49762306a36Sopenharmony_ci dev->type = ARPHRD_ATM; 49862306a36Sopenharmony_ci dev->neigh_priv_len = sizeof(struct atmarp_entry); 49962306a36Sopenharmony_ci dev->hard_header_len = RFC1483LLC_LEN; 50062306a36Sopenharmony_ci dev->mtu = RFC1626_MTU; 50162306a36Sopenharmony_ci dev->tx_queue_len = 100; /* "normal" queue (packets) */ 50262306a36Sopenharmony_ci /* When using a "real" qdisc, the qdisc determines the queue */ 50362306a36Sopenharmony_ci /* length. tx_queue_len is only used for the default case, */ 50462306a36Sopenharmony_ci /* without any more elaborate queuing. 100 is a reasonable */ 50562306a36Sopenharmony_ci /* compromise between decent burst-tolerance and protection */ 50662306a36Sopenharmony_ci /* against memory hogs. */ 50762306a36Sopenharmony_ci netif_keep_dst(dev); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int clip_create(int number) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct net_device *dev; 51362306a36Sopenharmony_ci struct clip_priv *clip_priv; 51462306a36Sopenharmony_ci int error; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (number != -1) { 51762306a36Sopenharmony_ci for (dev = clip_devs; dev; dev = PRIV(dev)->next) 51862306a36Sopenharmony_ci if (PRIV(dev)->number == number) 51962306a36Sopenharmony_ci return -EEXIST; 52062306a36Sopenharmony_ci } else { 52162306a36Sopenharmony_ci number = 0; 52262306a36Sopenharmony_ci for (dev = clip_devs; dev; dev = PRIV(dev)->next) 52362306a36Sopenharmony_ci if (PRIV(dev)->number >= number) 52462306a36Sopenharmony_ci number = PRIV(dev)->number + 1; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci dev = alloc_netdev(sizeof(struct clip_priv), "", NET_NAME_UNKNOWN, 52762306a36Sopenharmony_ci clip_setup); 52862306a36Sopenharmony_ci if (!dev) 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci clip_priv = PRIV(dev); 53162306a36Sopenharmony_ci sprintf(dev->name, "atm%d", number); 53262306a36Sopenharmony_ci spin_lock_init(&clip_priv->xoff_lock); 53362306a36Sopenharmony_ci clip_priv->number = number; 53462306a36Sopenharmony_ci error = register_netdev(dev); 53562306a36Sopenharmony_ci if (error) { 53662306a36Sopenharmony_ci free_netdev(dev); 53762306a36Sopenharmony_ci return error; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci clip_priv->next = clip_devs; 54062306a36Sopenharmony_ci clip_devs = dev; 54162306a36Sopenharmony_ci pr_debug("registered (net:%s)\n", dev->name); 54262306a36Sopenharmony_ci return number; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int clip_device_event(struct notifier_block *this, unsigned long event, 54662306a36Sopenharmony_ci void *ptr) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (!net_eq(dev_net(dev), &init_net)) 55162306a36Sopenharmony_ci return NOTIFY_DONE; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (event == NETDEV_UNREGISTER) 55462306a36Sopenharmony_ci return NOTIFY_DONE; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* ignore non-CLIP devices */ 55762306a36Sopenharmony_ci if (dev->type != ARPHRD_ATM || dev->netdev_ops != &clip_netdev_ops) 55862306a36Sopenharmony_ci return NOTIFY_DONE; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci switch (event) { 56162306a36Sopenharmony_ci case NETDEV_UP: 56262306a36Sopenharmony_ci pr_debug("NETDEV_UP\n"); 56362306a36Sopenharmony_ci to_atmarpd(act_up, PRIV(dev)->number, 0); 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci case NETDEV_GOING_DOWN: 56662306a36Sopenharmony_ci pr_debug("NETDEV_DOWN\n"); 56762306a36Sopenharmony_ci to_atmarpd(act_down, PRIV(dev)->number, 0); 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci case NETDEV_CHANGE: 57062306a36Sopenharmony_ci case NETDEV_CHANGEMTU: 57162306a36Sopenharmony_ci pr_debug("NETDEV_CHANGE*\n"); 57262306a36Sopenharmony_ci to_atmarpd(act_change, PRIV(dev)->number, 0); 57362306a36Sopenharmony_ci break; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci return NOTIFY_DONE; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int clip_inet_event(struct notifier_block *this, unsigned long event, 57962306a36Sopenharmony_ci void *ifa) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct in_device *in_dev; 58262306a36Sopenharmony_ci struct netdev_notifier_info info; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci in_dev = ((struct in_ifaddr *)ifa)->ifa_dev; 58562306a36Sopenharmony_ci /* 58662306a36Sopenharmony_ci * Transitions are of the down-change-up type, so it's sufficient to 58762306a36Sopenharmony_ci * handle the change on up. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_ci if (event != NETDEV_UP) 59062306a36Sopenharmony_ci return NOTIFY_DONE; 59162306a36Sopenharmony_ci netdev_notifier_info_init(&info, in_dev->dev); 59262306a36Sopenharmony_ci return clip_device_event(this, NETDEV_CHANGE, &info); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic struct notifier_block clip_dev_notifier = { 59662306a36Sopenharmony_ci .notifier_call = clip_device_event, 59762306a36Sopenharmony_ci}; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic struct notifier_block clip_inet_notifier = { 60262306a36Sopenharmony_ci .notifier_call = clip_inet_event, 60362306a36Sopenharmony_ci}; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic void atmarpd_close(struct atm_vcc *vcc) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci pr_debug("\n"); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci rtnl_lock(); 61262306a36Sopenharmony_ci atmarpd = NULL; 61362306a36Sopenharmony_ci skb_queue_purge(&sk_atm(vcc)->sk_receive_queue); 61462306a36Sopenharmony_ci rtnl_unlock(); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci pr_debug("(done)\n"); 61762306a36Sopenharmony_ci module_put(THIS_MODULE); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic const struct atmdev_ops atmarpd_dev_ops = { 62162306a36Sopenharmony_ci .close = atmarpd_close 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic struct atm_dev atmarpd_dev = { 62662306a36Sopenharmony_ci .ops = &atmarpd_dev_ops, 62762306a36Sopenharmony_ci .type = "arpd", 62862306a36Sopenharmony_ci .number = 999, 62962306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(atmarpd_dev.lock) 63062306a36Sopenharmony_ci}; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int atm_init_atmarp(struct atm_vcc *vcc) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci rtnl_lock(); 63662306a36Sopenharmony_ci if (atmarpd) { 63762306a36Sopenharmony_ci rtnl_unlock(); 63862306a36Sopenharmony_ci return -EADDRINUSE; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci atmarpd = vcc; 64462306a36Sopenharmony_ci set_bit(ATM_VF_META, &vcc->flags); 64562306a36Sopenharmony_ci set_bit(ATM_VF_READY, &vcc->flags); 64662306a36Sopenharmony_ci /* allow replies and avoid getting closed if signaling dies */ 64762306a36Sopenharmony_ci vcc->dev = &atmarpd_dev; 64862306a36Sopenharmony_ci vcc_insert_socket(sk_atm(vcc)); 64962306a36Sopenharmony_ci vcc->push = NULL; 65062306a36Sopenharmony_ci vcc->pop = NULL; /* crash */ 65162306a36Sopenharmony_ci vcc->push_oam = NULL; /* crash */ 65262306a36Sopenharmony_ci rtnl_unlock(); 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int clip_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct atm_vcc *vcc = ATM_SD(sock); 65962306a36Sopenharmony_ci int err = 0; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci switch (cmd) { 66262306a36Sopenharmony_ci case SIOCMKCLIP: 66362306a36Sopenharmony_ci case ATMARPD_CTRL: 66462306a36Sopenharmony_ci case ATMARP_MKIP: 66562306a36Sopenharmony_ci case ATMARP_SETENTRY: 66662306a36Sopenharmony_ci case ATMARP_ENCAP: 66762306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 66862306a36Sopenharmony_ci return -EPERM; 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci default: 67162306a36Sopenharmony_ci return -ENOIOCTLCMD; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci switch (cmd) { 67562306a36Sopenharmony_ci case SIOCMKCLIP: 67662306a36Sopenharmony_ci err = clip_create(arg); 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci case ATMARPD_CTRL: 67962306a36Sopenharmony_ci err = atm_init_atmarp(vcc); 68062306a36Sopenharmony_ci if (!err) { 68162306a36Sopenharmony_ci sock->state = SS_CONNECTED; 68262306a36Sopenharmony_ci __module_get(THIS_MODULE); 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci case ATMARP_MKIP: 68662306a36Sopenharmony_ci err = clip_mkip(vcc, arg); 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci case ATMARP_SETENTRY: 68962306a36Sopenharmony_ci err = clip_setentry(vcc, (__force __be32)arg); 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci case ATMARP_ENCAP: 69262306a36Sopenharmony_ci err = clip_encap(vcc, arg); 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci return err; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic struct atm_ioctl clip_ioctl_ops = { 69962306a36Sopenharmony_ci .owner = THIS_MODULE, 70062306a36Sopenharmony_ci .ioctl = clip_ioctl, 70162306a36Sopenharmony_ci}; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci static int code[] = { 1, 2, 10, 6, 1, 0 }; 70862306a36Sopenharmony_ci static int e164[] = { 1, 8, 4, 6, 1, 0 }; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (*addr->sas_addr.pub) { 71162306a36Sopenharmony_ci seq_printf(seq, "%s", addr->sas_addr.pub); 71262306a36Sopenharmony_ci if (*addr->sas_addr.prv) 71362306a36Sopenharmony_ci seq_putc(seq, '+'); 71462306a36Sopenharmony_ci } else if (!*addr->sas_addr.prv) { 71562306a36Sopenharmony_ci seq_printf(seq, "%s", "(none)"); 71662306a36Sopenharmony_ci return; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci if (*addr->sas_addr.prv) { 71962306a36Sopenharmony_ci unsigned char *prv = addr->sas_addr.prv; 72062306a36Sopenharmony_ci int *fields; 72162306a36Sopenharmony_ci int i, j; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci fields = *prv == ATM_AFI_E164 ? e164 : code; 72462306a36Sopenharmony_ci for (i = 0; fields[i]; i++) { 72562306a36Sopenharmony_ci for (j = fields[i]; j; j--) 72662306a36Sopenharmony_ci seq_printf(seq, "%02X", *prv++); 72762306a36Sopenharmony_ci if (fields[i + 1]) 72862306a36Sopenharmony_ci seq_putc(seq, '.'); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci/* This means the neighbour entry has no attached VCC objects. */ 73462306a36Sopenharmony_ci#define SEQ_NO_VCC_TOKEN ((void *) 2) 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic void atmarp_info(struct seq_file *seq, struct neighbour *n, 73762306a36Sopenharmony_ci struct atmarp_entry *entry, struct clip_vcc *clip_vcc) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct net_device *dev = n->dev; 74062306a36Sopenharmony_ci unsigned long exp; 74162306a36Sopenharmony_ci char buf[17]; 74262306a36Sopenharmony_ci int svc, llc, off; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || 74562306a36Sopenharmony_ci (sk_atm(clip_vcc->vcc)->sk_family == AF_ATMSVC)); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || clip_vcc->encap); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (clip_vcc == SEQ_NO_VCC_TOKEN) 75062306a36Sopenharmony_ci exp = entry->neigh->used; 75162306a36Sopenharmony_ci else 75262306a36Sopenharmony_ci exp = clip_vcc->last_use; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci exp = (jiffies - exp) / HZ; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci seq_printf(seq, "%-6s%-4s%-4s%5ld ", 75762306a36Sopenharmony_ci dev->name, svc ? "SVC" : "PVC", llc ? "LLC" : "NULL", exp); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci off = scnprintf(buf, sizeof(buf) - 1, "%pI4", n->primary_key); 76062306a36Sopenharmony_ci while (off < 16) 76162306a36Sopenharmony_ci buf[off++] = ' '; 76262306a36Sopenharmony_ci buf[off] = '\0'; 76362306a36Sopenharmony_ci seq_printf(seq, "%s", buf); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (clip_vcc == SEQ_NO_VCC_TOKEN) { 76662306a36Sopenharmony_ci if (time_before(jiffies, entry->expires)) 76762306a36Sopenharmony_ci seq_printf(seq, "(resolving)\n"); 76862306a36Sopenharmony_ci else 76962306a36Sopenharmony_ci seq_printf(seq, "(expired, ref %d)\n", 77062306a36Sopenharmony_ci refcount_read(&entry->neigh->refcnt)); 77162306a36Sopenharmony_ci } else if (!svc) { 77262306a36Sopenharmony_ci seq_printf(seq, "%d.%d.%d\n", 77362306a36Sopenharmony_ci clip_vcc->vcc->dev->number, 77462306a36Sopenharmony_ci clip_vcc->vcc->vpi, clip_vcc->vcc->vci); 77562306a36Sopenharmony_ci } else { 77662306a36Sopenharmony_ci svc_addr(seq, &clip_vcc->vcc->remote); 77762306a36Sopenharmony_ci seq_putc(seq, '\n'); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistruct clip_seq_state { 78262306a36Sopenharmony_ci /* This member must be first. */ 78362306a36Sopenharmony_ci struct neigh_seq_state ns; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* Local to clip specific iteration. */ 78662306a36Sopenharmony_ci struct clip_vcc *vcc; 78762306a36Sopenharmony_ci}; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e, 79062306a36Sopenharmony_ci struct clip_vcc *curr) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci if (!curr) { 79362306a36Sopenharmony_ci curr = e->vccs; 79462306a36Sopenharmony_ci if (!curr) 79562306a36Sopenharmony_ci return SEQ_NO_VCC_TOKEN; 79662306a36Sopenharmony_ci return curr; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci if (curr == SEQ_NO_VCC_TOKEN) 79962306a36Sopenharmony_ci return NULL; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci curr = curr->next; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return curr; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic void *clip_seq_vcc_walk(struct clip_seq_state *state, 80762306a36Sopenharmony_ci struct atmarp_entry *e, loff_t * pos) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct clip_vcc *vcc = state->vcc; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci vcc = clip_seq_next_vcc(e, vcc); 81262306a36Sopenharmony_ci if (vcc && pos != NULL) { 81362306a36Sopenharmony_ci while (*pos) { 81462306a36Sopenharmony_ci vcc = clip_seq_next_vcc(e, vcc); 81562306a36Sopenharmony_ci if (!vcc) 81662306a36Sopenharmony_ci break; 81762306a36Sopenharmony_ci --(*pos); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci state->vcc = vcc; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return vcc; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void *clip_seq_sub_iter(struct neigh_seq_state *_state, 82662306a36Sopenharmony_ci struct neighbour *n, loff_t * pos) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct clip_seq_state *state = (struct clip_seq_state *)_state; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (n->dev->type != ARPHRD_ATM) 83162306a36Sopenharmony_ci return NULL; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci return clip_seq_vcc_walk(state, neighbour_priv(n), pos); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic void *clip_seq_start(struct seq_file *seq, loff_t * pos) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci struct clip_seq_state *state = seq->private; 83962306a36Sopenharmony_ci state->ns.neigh_sub_iter = clip_seq_sub_iter; 84062306a36Sopenharmony_ci return neigh_seq_start(seq, pos, &arp_tbl, NEIGH_SEQ_NEIGH_ONLY); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int clip_seq_show(struct seq_file *seq, void *v) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci static char atm_arp_banner[] = 84662306a36Sopenharmony_ci "IPitf TypeEncp Idle IP address ATM address\n"; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 84962306a36Sopenharmony_ci seq_puts(seq, atm_arp_banner); 85062306a36Sopenharmony_ci } else { 85162306a36Sopenharmony_ci struct clip_seq_state *state = seq->private; 85262306a36Sopenharmony_ci struct clip_vcc *vcc = state->vcc; 85362306a36Sopenharmony_ci struct neighbour *n = v; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci atmarp_info(seq, n, neighbour_priv(n), vcc); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci return 0; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic const struct seq_operations arp_seq_ops = { 86162306a36Sopenharmony_ci .start = clip_seq_start, 86262306a36Sopenharmony_ci .next = neigh_seq_next, 86362306a36Sopenharmony_ci .stop = neigh_seq_stop, 86462306a36Sopenharmony_ci .show = clip_seq_show, 86562306a36Sopenharmony_ci}; 86662306a36Sopenharmony_ci#endif 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic void atm_clip_exit_noproc(void); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int __init atm_clip_init(void) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci register_atm_ioctl(&clip_ioctl_ops); 87362306a36Sopenharmony_ci register_netdevice_notifier(&clip_dev_notifier); 87462306a36Sopenharmony_ci register_inetaddr_notifier(&clip_inet_notifier); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci timer_setup(&idle_timer, idle_timer_check, 0); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 87962306a36Sopenharmony_ci { 88062306a36Sopenharmony_ci struct proc_dir_entry *p; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci p = proc_create_net("arp", 0444, atm_proc_root, &arp_seq_ops, 88362306a36Sopenharmony_ci sizeof(struct clip_seq_state)); 88462306a36Sopenharmony_ci if (!p) { 88562306a36Sopenharmony_ci pr_err("Unable to initialize /proc/net/atm/arp\n"); 88662306a36Sopenharmony_ci atm_clip_exit_noproc(); 88762306a36Sopenharmony_ci return -ENOMEM; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci#endif 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci return 0; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void atm_clip_exit_noproc(void) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct net_device *dev, *next; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci unregister_inetaddr_notifier(&clip_inet_notifier); 90062306a36Sopenharmony_ci unregister_netdevice_notifier(&clip_dev_notifier); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci deregister_atm_ioctl(&clip_ioctl_ops); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* First, stop the idle timer, so it stops banging 90562306a36Sopenharmony_ci * on the table. 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_ci del_timer_sync(&idle_timer); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci dev = clip_devs; 91062306a36Sopenharmony_ci while (dev) { 91162306a36Sopenharmony_ci next = PRIV(dev)->next; 91262306a36Sopenharmony_ci unregister_netdev(dev); 91362306a36Sopenharmony_ci free_netdev(dev); 91462306a36Sopenharmony_ci dev = next; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic void __exit atm_clip_exit(void) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci remove_proc_entry("arp", atm_proc_root); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci atm_clip_exit_noproc(); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cimodule_init(atm_clip_init); 92662306a36Sopenharmony_cimodule_exit(atm_clip_exit); 92762306a36Sopenharmony_ciMODULE_AUTHOR("Werner Almesberger"); 92862306a36Sopenharmony_ciMODULE_DESCRIPTION("Classical/IP over ATM interface"); 92962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 930