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