xref: /openbmc/linux/net/atm/clip.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /* net/atm/clip.c - RFC1577 Classical IP over ATM */
2*1da177e4SLinus Torvalds 
3*1da177e4SLinus Torvalds /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4*1da177e4SLinus Torvalds 
5*1da177e4SLinus Torvalds 
6*1da177e4SLinus Torvalds #include <linux/config.h>
7*1da177e4SLinus Torvalds #include <linux/string.h>
8*1da177e4SLinus Torvalds #include <linux/errno.h>
9*1da177e4SLinus Torvalds #include <linux/kernel.h> /* for UINT_MAX */
10*1da177e4SLinus Torvalds #include <linux/module.h>
11*1da177e4SLinus Torvalds #include <linux/init.h>
12*1da177e4SLinus Torvalds #include <linux/netdevice.h>
13*1da177e4SLinus Torvalds #include <linux/skbuff.h>
14*1da177e4SLinus Torvalds #include <linux/wait.h>
15*1da177e4SLinus Torvalds #include <linux/timer.h>
16*1da177e4SLinus Torvalds #include <linux/if_arp.h> /* for some manifest constants */
17*1da177e4SLinus Torvalds #include <linux/notifier.h>
18*1da177e4SLinus Torvalds #include <linux/atm.h>
19*1da177e4SLinus Torvalds #include <linux/atmdev.h>
20*1da177e4SLinus Torvalds #include <linux/atmclip.h>
21*1da177e4SLinus Torvalds #include <linux/atmarp.h>
22*1da177e4SLinus Torvalds #include <linux/ip.h> /* for net/route.h */
23*1da177e4SLinus Torvalds #include <linux/in.h> /* for struct sockaddr_in */
24*1da177e4SLinus Torvalds #include <linux/if.h> /* for IFF_UP */
25*1da177e4SLinus Torvalds #include <linux/inetdevice.h>
26*1da177e4SLinus Torvalds #include <linux/bitops.h>
27*1da177e4SLinus Torvalds #include <linux/proc_fs.h>
28*1da177e4SLinus Torvalds #include <linux/seq_file.h>
29*1da177e4SLinus Torvalds #include <linux/rcupdate.h>
30*1da177e4SLinus Torvalds #include <linux/jhash.h>
31*1da177e4SLinus Torvalds #include <net/route.h> /* for struct rtable and routing */
32*1da177e4SLinus Torvalds #include <net/icmp.h> /* icmp_send */
33*1da177e4SLinus Torvalds #include <asm/param.h> /* for HZ */
34*1da177e4SLinus Torvalds #include <asm/byteorder.h> /* for htons etc. */
35*1da177e4SLinus Torvalds #include <asm/system.h> /* save/restore_flags */
36*1da177e4SLinus Torvalds #include <asm/uaccess.h>
37*1da177e4SLinus Torvalds #include <asm/atomic.h>
38*1da177e4SLinus Torvalds 
39*1da177e4SLinus Torvalds #include "common.h"
40*1da177e4SLinus Torvalds #include "resources.h"
41*1da177e4SLinus Torvalds #include "ipcommon.h"
42*1da177e4SLinus Torvalds #include <net/atmclip.h>
43*1da177e4SLinus Torvalds 
44*1da177e4SLinus Torvalds 
45*1da177e4SLinus Torvalds #if 0
46*1da177e4SLinus Torvalds #define DPRINTK(format,args...) printk(format,##args)
47*1da177e4SLinus Torvalds #else
48*1da177e4SLinus Torvalds #define DPRINTK(format,args...)
49*1da177e4SLinus Torvalds #endif
50*1da177e4SLinus Torvalds 
51*1da177e4SLinus Torvalds 
52*1da177e4SLinus Torvalds static struct net_device *clip_devs;
53*1da177e4SLinus Torvalds static struct atm_vcc *atmarpd;
54*1da177e4SLinus Torvalds static struct neigh_table clip_tbl;
55*1da177e4SLinus Torvalds static struct timer_list idle_timer;
56*1da177e4SLinus Torvalds static int start_timer = 1;
57*1da177e4SLinus Torvalds 
58*1da177e4SLinus Torvalds 
59*1da177e4SLinus Torvalds static int to_atmarpd(enum atmarp_ctrl_type type,int itf,unsigned long ip)
60*1da177e4SLinus Torvalds {
61*1da177e4SLinus Torvalds 	struct sock *sk;
62*1da177e4SLinus Torvalds 	struct atmarp_ctrl *ctrl;
63*1da177e4SLinus Torvalds 	struct sk_buff *skb;
64*1da177e4SLinus Torvalds 
65*1da177e4SLinus Torvalds 	DPRINTK("to_atmarpd(%d)\n",type);
66*1da177e4SLinus Torvalds 	if (!atmarpd) return -EUNATCH;
67*1da177e4SLinus Torvalds 	skb = alloc_skb(sizeof(struct atmarp_ctrl),GFP_ATOMIC);
68*1da177e4SLinus Torvalds 	if (!skb) return -ENOMEM;
69*1da177e4SLinus Torvalds 	ctrl = (struct atmarp_ctrl *) skb_put(skb,sizeof(struct atmarp_ctrl));
70*1da177e4SLinus Torvalds 	ctrl->type = type;
71*1da177e4SLinus Torvalds 	ctrl->itf_num = itf;
72*1da177e4SLinus Torvalds 	ctrl->ip = ip;
73*1da177e4SLinus Torvalds 	atm_force_charge(atmarpd,skb->truesize);
74*1da177e4SLinus Torvalds 
75*1da177e4SLinus Torvalds 	sk = sk_atm(atmarpd);
76*1da177e4SLinus Torvalds 	skb_queue_tail(&sk->sk_receive_queue, skb);
77*1da177e4SLinus Torvalds 	sk->sk_data_ready(sk, skb->len);
78*1da177e4SLinus Torvalds 	return 0;
79*1da177e4SLinus Torvalds }
80*1da177e4SLinus Torvalds 
81*1da177e4SLinus Torvalds 
82*1da177e4SLinus Torvalds static void link_vcc(struct clip_vcc *clip_vcc,struct atmarp_entry *entry)
83*1da177e4SLinus Torvalds {
84*1da177e4SLinus Torvalds 	DPRINTK("link_vcc %p to entry %p (neigh %p)\n",clip_vcc,entry,
85*1da177e4SLinus Torvalds 	    entry->neigh);
86*1da177e4SLinus Torvalds 	clip_vcc->entry = entry;
87*1da177e4SLinus Torvalds 	clip_vcc->xoff = 0; /* @@@ may overrun buffer by one packet */
88*1da177e4SLinus Torvalds 	clip_vcc->next = entry->vccs;
89*1da177e4SLinus Torvalds 	entry->vccs = clip_vcc;
90*1da177e4SLinus Torvalds 	entry->neigh->used = jiffies;
91*1da177e4SLinus Torvalds }
92*1da177e4SLinus Torvalds 
93*1da177e4SLinus Torvalds 
94*1da177e4SLinus Torvalds static void unlink_clip_vcc(struct clip_vcc *clip_vcc)
95*1da177e4SLinus Torvalds {
96*1da177e4SLinus Torvalds 	struct atmarp_entry *entry = clip_vcc->entry;
97*1da177e4SLinus Torvalds 	struct clip_vcc **walk;
98*1da177e4SLinus Torvalds 
99*1da177e4SLinus Torvalds 	if (!entry) {
100*1da177e4SLinus Torvalds 		printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n",clip_vcc);
101*1da177e4SLinus Torvalds 		return;
102*1da177e4SLinus Torvalds 	}
103*1da177e4SLinus Torvalds 	spin_lock_bh(&entry->neigh->dev->xmit_lock);	/* block clip_start_xmit() */
104*1da177e4SLinus Torvalds 	entry->neigh->used = jiffies;
105*1da177e4SLinus Torvalds 	for (walk = &entry->vccs; *walk; walk = &(*walk)->next)
106*1da177e4SLinus Torvalds 		if (*walk == clip_vcc) {
107*1da177e4SLinus Torvalds 			int error;
108*1da177e4SLinus Torvalds 
109*1da177e4SLinus Torvalds 			*walk = clip_vcc->next; /* atomic */
110*1da177e4SLinus Torvalds 			clip_vcc->entry = NULL;
111*1da177e4SLinus Torvalds 			if (clip_vcc->xoff)
112*1da177e4SLinus Torvalds 				netif_wake_queue(entry->neigh->dev);
113*1da177e4SLinus Torvalds 			if (entry->vccs)
114*1da177e4SLinus Torvalds 				goto out;
115*1da177e4SLinus Torvalds 			entry->expires = jiffies-1;
116*1da177e4SLinus Torvalds 				/* force resolution or expiration */
117*1da177e4SLinus Torvalds 			error = neigh_update(entry->neigh, NULL, NUD_NONE,
118*1da177e4SLinus Torvalds 					     NEIGH_UPDATE_F_ADMIN);
119*1da177e4SLinus Torvalds 			if (error)
120*1da177e4SLinus Torvalds 				printk(KERN_CRIT "unlink_clip_vcc: "
121*1da177e4SLinus Torvalds 				    "neigh_update failed with %d\n",error);
122*1da177e4SLinus Torvalds 			goto out;
123*1da177e4SLinus Torvalds 		}
124*1da177e4SLinus Torvalds 	printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc "
125*1da177e4SLinus Torvalds 	  "0x%p)\n",entry,clip_vcc);
126*1da177e4SLinus Torvalds out:
127*1da177e4SLinus Torvalds 	spin_unlock_bh(&entry->neigh->dev->xmit_lock);
128*1da177e4SLinus Torvalds }
129*1da177e4SLinus Torvalds 
130*1da177e4SLinus Torvalds /* The neighbour entry n->lock is held. */
131*1da177e4SLinus Torvalds static int neigh_check_cb(struct neighbour *n)
132*1da177e4SLinus Torvalds {
133*1da177e4SLinus Torvalds 	struct atmarp_entry *entry = NEIGH2ENTRY(n);
134*1da177e4SLinus Torvalds 	struct clip_vcc *cv;
135*1da177e4SLinus Torvalds 
136*1da177e4SLinus Torvalds 	for (cv = entry->vccs; cv; cv = cv->next) {
137*1da177e4SLinus Torvalds 		unsigned long exp = cv->last_use + cv->idle_timeout;
138*1da177e4SLinus Torvalds 
139*1da177e4SLinus Torvalds 		if (cv->idle_timeout && time_after(jiffies, exp)) {
140*1da177e4SLinus Torvalds 			DPRINTK("releasing vcc %p->%p of entry %p\n",
141*1da177e4SLinus Torvalds 				cv, cv->vcc, entry);
142*1da177e4SLinus Torvalds 			vcc_release_async(cv->vcc, -ETIMEDOUT);
143*1da177e4SLinus Torvalds 		}
144*1da177e4SLinus Torvalds 	}
145*1da177e4SLinus Torvalds 
146*1da177e4SLinus Torvalds 	if (entry->vccs || time_before(jiffies, entry->expires))
147*1da177e4SLinus Torvalds 		return 0;
148*1da177e4SLinus Torvalds 
149*1da177e4SLinus Torvalds 	if (atomic_read(&n->refcnt) > 1) {
150*1da177e4SLinus Torvalds 		struct sk_buff *skb;
151*1da177e4SLinus Torvalds 
152*1da177e4SLinus Torvalds 		DPRINTK("destruction postponed with ref %d\n",
153*1da177e4SLinus Torvalds 			atomic_read(&n->refcnt));
154*1da177e4SLinus Torvalds 
155*1da177e4SLinus Torvalds 		while ((skb = skb_dequeue(&n->arp_queue)) != NULL)
156*1da177e4SLinus Torvalds 			dev_kfree_skb(skb);
157*1da177e4SLinus Torvalds 
158*1da177e4SLinus Torvalds 		return 0;
159*1da177e4SLinus Torvalds 	}
160*1da177e4SLinus Torvalds 
161*1da177e4SLinus Torvalds 	DPRINTK("expired neigh %p\n",n);
162*1da177e4SLinus Torvalds 	return 1;
163*1da177e4SLinus Torvalds }
164*1da177e4SLinus Torvalds 
165*1da177e4SLinus Torvalds static void idle_timer_check(unsigned long dummy)
166*1da177e4SLinus Torvalds {
167*1da177e4SLinus Torvalds 	write_lock(&clip_tbl.lock);
168*1da177e4SLinus Torvalds 	__neigh_for_each_release(&clip_tbl, neigh_check_cb);
169*1da177e4SLinus Torvalds 	mod_timer(&idle_timer, jiffies+CLIP_CHECK_INTERVAL*HZ);
170*1da177e4SLinus Torvalds 	write_unlock(&clip_tbl.lock);
171*1da177e4SLinus Torvalds }
172*1da177e4SLinus Torvalds 
173*1da177e4SLinus Torvalds static int clip_arp_rcv(struct sk_buff *skb)
174*1da177e4SLinus Torvalds {
175*1da177e4SLinus Torvalds 	struct atm_vcc *vcc;
176*1da177e4SLinus Torvalds 
177*1da177e4SLinus Torvalds 	DPRINTK("clip_arp_rcv\n");
178*1da177e4SLinus Torvalds 	vcc = ATM_SKB(skb)->vcc;
179*1da177e4SLinus Torvalds 	if (!vcc || !atm_charge(vcc,skb->truesize)) {
180*1da177e4SLinus Torvalds 		dev_kfree_skb_any(skb);
181*1da177e4SLinus Torvalds 		return 0;
182*1da177e4SLinus Torvalds 	}
183*1da177e4SLinus Torvalds 	DPRINTK("pushing to %p\n",vcc);
184*1da177e4SLinus Torvalds 	DPRINTK("using %p\n",CLIP_VCC(vcc)->old_push);
185*1da177e4SLinus Torvalds 	CLIP_VCC(vcc)->old_push(vcc,skb);
186*1da177e4SLinus Torvalds 	return 0;
187*1da177e4SLinus Torvalds }
188*1da177e4SLinus Torvalds 
189*1da177e4SLinus Torvalds static const unsigned char llc_oui[] = {
190*1da177e4SLinus Torvalds 	0xaa,	/* DSAP: non-ISO */
191*1da177e4SLinus Torvalds 	0xaa,	/* SSAP: non-ISO */
192*1da177e4SLinus Torvalds 	0x03,	/* Ctrl: Unnumbered Information Command PDU */
193*1da177e4SLinus Torvalds 	0x00,	/* OUI: EtherType */
194*1da177e4SLinus Torvalds 	0x00,
195*1da177e4SLinus Torvalds 	0x00 };
196*1da177e4SLinus Torvalds 
197*1da177e4SLinus Torvalds static void clip_push(struct atm_vcc *vcc,struct sk_buff *skb)
198*1da177e4SLinus Torvalds {
199*1da177e4SLinus Torvalds 	struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
200*1da177e4SLinus Torvalds 
201*1da177e4SLinus Torvalds 	DPRINTK("clip push\n");
202*1da177e4SLinus Torvalds 	if (!skb) {
203*1da177e4SLinus Torvalds 		DPRINTK("removing VCC %p\n",clip_vcc);
204*1da177e4SLinus Torvalds 		if (clip_vcc->entry) unlink_clip_vcc(clip_vcc);
205*1da177e4SLinus Torvalds 		clip_vcc->old_push(vcc,NULL); /* pass on the bad news */
206*1da177e4SLinus Torvalds 		kfree(clip_vcc);
207*1da177e4SLinus Torvalds 		return;
208*1da177e4SLinus Torvalds 	}
209*1da177e4SLinus Torvalds 	atm_return(vcc,skb->truesize);
210*1da177e4SLinus Torvalds 	skb->dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : clip_devs;
211*1da177e4SLinus Torvalds 		/* clip_vcc->entry == NULL if we don't have an IP address yet */
212*1da177e4SLinus Torvalds 	if (!skb->dev) {
213*1da177e4SLinus Torvalds 		dev_kfree_skb_any(skb);
214*1da177e4SLinus Torvalds 		return;
215*1da177e4SLinus Torvalds 	}
216*1da177e4SLinus Torvalds 	ATM_SKB(skb)->vcc = vcc;
217*1da177e4SLinus Torvalds 	skb->mac.raw = skb->data;
218*1da177e4SLinus Torvalds 	if (!clip_vcc->encap || skb->len < RFC1483LLC_LEN || memcmp(skb->data,
219*1da177e4SLinus Torvalds 	    llc_oui,sizeof(llc_oui))) skb->protocol = htons(ETH_P_IP);
220*1da177e4SLinus Torvalds 	else {
221*1da177e4SLinus Torvalds 		skb->protocol = ((u16 *) skb->data)[3];
222*1da177e4SLinus Torvalds 		skb_pull(skb,RFC1483LLC_LEN);
223*1da177e4SLinus Torvalds 		if (skb->protocol == htons(ETH_P_ARP)) {
224*1da177e4SLinus Torvalds 			PRIV(skb->dev)->stats.rx_packets++;
225*1da177e4SLinus Torvalds 			PRIV(skb->dev)->stats.rx_bytes += skb->len;
226*1da177e4SLinus Torvalds 			clip_arp_rcv(skb);
227*1da177e4SLinus Torvalds 			return;
228*1da177e4SLinus Torvalds 		}
229*1da177e4SLinus Torvalds 	}
230*1da177e4SLinus Torvalds 	clip_vcc->last_use = jiffies;
231*1da177e4SLinus Torvalds 	PRIV(skb->dev)->stats.rx_packets++;
232*1da177e4SLinus Torvalds 	PRIV(skb->dev)->stats.rx_bytes += skb->len;
233*1da177e4SLinus Torvalds 	memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
234*1da177e4SLinus Torvalds 	netif_rx(skb);
235*1da177e4SLinus Torvalds }
236*1da177e4SLinus Torvalds 
237*1da177e4SLinus Torvalds 
238*1da177e4SLinus Torvalds /*
239*1da177e4SLinus Torvalds  * Note: these spinlocks _must_not_ block on non-SMP. The only goal is that
240*1da177e4SLinus Torvalds  * clip_pop is atomic with respect to the critical section in clip_start_xmit.
241*1da177e4SLinus Torvalds  */
242*1da177e4SLinus Torvalds 
243*1da177e4SLinus Torvalds 
244*1da177e4SLinus Torvalds static void clip_pop(struct atm_vcc *vcc,struct sk_buff *skb)
245*1da177e4SLinus Torvalds {
246*1da177e4SLinus Torvalds 	struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
247*1da177e4SLinus Torvalds 	struct net_device *dev = skb->dev;
248*1da177e4SLinus Torvalds 	int old;
249*1da177e4SLinus Torvalds 	unsigned long flags;
250*1da177e4SLinus Torvalds 
251*1da177e4SLinus Torvalds 	DPRINTK("clip_pop(vcc %p)\n",vcc);
252*1da177e4SLinus Torvalds 	clip_vcc->old_pop(vcc,skb);
253*1da177e4SLinus Torvalds 	/* skb->dev == NULL in outbound ARP packets */
254*1da177e4SLinus Torvalds 	if (!dev) return;
255*1da177e4SLinus Torvalds 	spin_lock_irqsave(&PRIV(dev)->xoff_lock,flags);
256*1da177e4SLinus Torvalds 	if (atm_may_send(vcc,0)) {
257*1da177e4SLinus Torvalds 		old = xchg(&clip_vcc->xoff,0);
258*1da177e4SLinus Torvalds 		if (old) netif_wake_queue(dev);
259*1da177e4SLinus Torvalds 	}
260*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&PRIV(dev)->xoff_lock,flags);
261*1da177e4SLinus Torvalds }
262*1da177e4SLinus Torvalds 
263*1da177e4SLinus Torvalds 
264*1da177e4SLinus Torvalds static void clip_neigh_destroy(struct neighbour *neigh)
265*1da177e4SLinus Torvalds {
266*1da177e4SLinus Torvalds 	DPRINTK("clip_neigh_destroy (neigh %p)\n",neigh);
267*1da177e4SLinus Torvalds 	if (NEIGH2ENTRY(neigh)->vccs)
268*1da177e4SLinus Torvalds 		printk(KERN_CRIT "clip_neigh_destroy: vccs != NULL !!!\n");
269*1da177e4SLinus Torvalds 	NEIGH2ENTRY(neigh)->vccs = (void *) 0xdeadbeef;
270*1da177e4SLinus Torvalds }
271*1da177e4SLinus Torvalds 
272*1da177e4SLinus Torvalds 
273*1da177e4SLinus Torvalds static void clip_neigh_solicit(struct neighbour *neigh,struct sk_buff *skb)
274*1da177e4SLinus Torvalds {
275*1da177e4SLinus Torvalds 	DPRINTK("clip_neigh_solicit (neigh %p, skb %p)\n",neigh,skb);
276*1da177e4SLinus Torvalds 	to_atmarpd(act_need,PRIV(neigh->dev)->number,NEIGH2ENTRY(neigh)->ip);
277*1da177e4SLinus Torvalds }
278*1da177e4SLinus Torvalds 
279*1da177e4SLinus Torvalds 
280*1da177e4SLinus Torvalds static void clip_neigh_error(struct neighbour *neigh,struct sk_buff *skb)
281*1da177e4SLinus Torvalds {
282*1da177e4SLinus Torvalds #ifndef CONFIG_ATM_CLIP_NO_ICMP
283*1da177e4SLinus Torvalds 	icmp_send(skb,ICMP_DEST_UNREACH,ICMP_HOST_UNREACH,0);
284*1da177e4SLinus Torvalds #endif
285*1da177e4SLinus Torvalds 	kfree_skb(skb);
286*1da177e4SLinus Torvalds }
287*1da177e4SLinus Torvalds 
288*1da177e4SLinus Torvalds 
289*1da177e4SLinus Torvalds static struct neigh_ops clip_neigh_ops = {
290*1da177e4SLinus Torvalds 	.family =		AF_INET,
291*1da177e4SLinus Torvalds 	.destructor =		clip_neigh_destroy,
292*1da177e4SLinus Torvalds 	.solicit =		clip_neigh_solicit,
293*1da177e4SLinus Torvalds 	.error_report =		clip_neigh_error,
294*1da177e4SLinus Torvalds 	.output =		dev_queue_xmit,
295*1da177e4SLinus Torvalds 	.connected_output =	dev_queue_xmit,
296*1da177e4SLinus Torvalds 	.hh_output =		dev_queue_xmit,
297*1da177e4SLinus Torvalds 	.queue_xmit =		dev_queue_xmit,
298*1da177e4SLinus Torvalds };
299*1da177e4SLinus Torvalds 
300*1da177e4SLinus Torvalds 
301*1da177e4SLinus Torvalds static int clip_constructor(struct neighbour *neigh)
302*1da177e4SLinus Torvalds {
303*1da177e4SLinus Torvalds 	struct atmarp_entry *entry = NEIGH2ENTRY(neigh);
304*1da177e4SLinus Torvalds 	struct net_device *dev = neigh->dev;
305*1da177e4SLinus Torvalds 	struct in_device *in_dev;
306*1da177e4SLinus Torvalds 	struct neigh_parms *parms;
307*1da177e4SLinus Torvalds 
308*1da177e4SLinus Torvalds 	DPRINTK("clip_constructor (neigh %p, entry %p)\n",neigh,entry);
309*1da177e4SLinus Torvalds 	neigh->type = inet_addr_type(entry->ip);
310*1da177e4SLinus Torvalds 	if (neigh->type != RTN_UNICAST) return -EINVAL;
311*1da177e4SLinus Torvalds 
312*1da177e4SLinus Torvalds 	rcu_read_lock();
313*1da177e4SLinus Torvalds 	in_dev = rcu_dereference(__in_dev_get(dev));
314*1da177e4SLinus Torvalds 	if (!in_dev) {
315*1da177e4SLinus Torvalds 		rcu_read_unlock();
316*1da177e4SLinus Torvalds 		return -EINVAL;
317*1da177e4SLinus Torvalds 	}
318*1da177e4SLinus Torvalds 
319*1da177e4SLinus Torvalds 	parms = in_dev->arp_parms;
320*1da177e4SLinus Torvalds 	__neigh_parms_put(neigh->parms);
321*1da177e4SLinus Torvalds 	neigh->parms = neigh_parms_clone(parms);
322*1da177e4SLinus Torvalds 	rcu_read_unlock();
323*1da177e4SLinus Torvalds 
324*1da177e4SLinus Torvalds 	neigh->ops = &clip_neigh_ops;
325*1da177e4SLinus Torvalds 	neigh->output = neigh->nud_state & NUD_VALID ?
326*1da177e4SLinus Torvalds 	    neigh->ops->connected_output : neigh->ops->output;
327*1da177e4SLinus Torvalds 	entry->neigh = neigh;
328*1da177e4SLinus Torvalds 	entry->vccs = NULL;
329*1da177e4SLinus Torvalds 	entry->expires = jiffies-1;
330*1da177e4SLinus Torvalds 	return 0;
331*1da177e4SLinus Torvalds }
332*1da177e4SLinus Torvalds 
333*1da177e4SLinus Torvalds static u32 clip_hash(const void *pkey, const struct net_device *dev)
334*1da177e4SLinus Torvalds {
335*1da177e4SLinus Torvalds 	return jhash_2words(*(u32 *)pkey, dev->ifindex, clip_tbl.hash_rnd);
336*1da177e4SLinus Torvalds }
337*1da177e4SLinus Torvalds 
338*1da177e4SLinus Torvalds static struct neigh_table clip_tbl = {
339*1da177e4SLinus Torvalds 	.family 	= AF_INET,
340*1da177e4SLinus Torvalds 	.entry_size 	= sizeof(struct neighbour)+sizeof(struct atmarp_entry),
341*1da177e4SLinus Torvalds 	.key_len 	= 4,
342*1da177e4SLinus Torvalds 	.hash 		= clip_hash,
343*1da177e4SLinus Torvalds 	.constructor 	= clip_constructor,
344*1da177e4SLinus Torvalds 	.id 		= "clip_arp_cache",
345*1da177e4SLinus Torvalds 
346*1da177e4SLinus Torvalds 	/* parameters are copied from ARP ... */
347*1da177e4SLinus Torvalds 	.parms = {
348*1da177e4SLinus Torvalds 		.tbl 			= &clip_tbl,
349*1da177e4SLinus Torvalds 		.base_reachable_time 	= 30 * HZ,
350*1da177e4SLinus Torvalds 		.retrans_time 		= 1 * HZ,
351*1da177e4SLinus Torvalds 		.gc_staletime 		= 60 * HZ,
352*1da177e4SLinus Torvalds 		.reachable_time 	= 30 * HZ,
353*1da177e4SLinus Torvalds 		.delay_probe_time 	= 5 * HZ,
354*1da177e4SLinus Torvalds 		.queue_len 		= 3,
355*1da177e4SLinus Torvalds 		.ucast_probes 		= 3,
356*1da177e4SLinus Torvalds 		.mcast_probes 		= 3,
357*1da177e4SLinus Torvalds 		.anycast_delay 		= 1 * HZ,
358*1da177e4SLinus Torvalds 		.proxy_delay 		= (8 * HZ) / 10,
359*1da177e4SLinus Torvalds 		.proxy_qlen 		= 64,
360*1da177e4SLinus Torvalds 		.locktime 		= 1 * HZ,
361*1da177e4SLinus Torvalds 	},
362*1da177e4SLinus Torvalds 	.gc_interval 	= 30 * HZ,
363*1da177e4SLinus Torvalds 	.gc_thresh1 	= 128,
364*1da177e4SLinus Torvalds 	.gc_thresh2 	= 512,
365*1da177e4SLinus Torvalds 	.gc_thresh3 	= 1024,
366*1da177e4SLinus Torvalds };
367*1da177e4SLinus Torvalds 
368*1da177e4SLinus Torvalds 
369*1da177e4SLinus Torvalds /* @@@ copy bh locking from arp.c -- need to bh-enable atm code before */
370*1da177e4SLinus Torvalds 
371*1da177e4SLinus Torvalds /*
372*1da177e4SLinus Torvalds  * We play with the resolve flag: 0 and 1 have the usual meaning, but -1 means
373*1da177e4SLinus Torvalds  * to allocate the neighbour entry but not to ask atmarpd for resolution. Also,
374*1da177e4SLinus Torvalds  * don't increment the usage count. This is used to create entries in
375*1da177e4SLinus Torvalds  * clip_setentry.
376*1da177e4SLinus Torvalds  */
377*1da177e4SLinus Torvalds 
378*1da177e4SLinus Torvalds 
379*1da177e4SLinus Torvalds static int clip_encap(struct atm_vcc *vcc,int mode)
380*1da177e4SLinus Torvalds {
381*1da177e4SLinus Torvalds 	CLIP_VCC(vcc)->encap = mode;
382*1da177e4SLinus Torvalds 	return 0;
383*1da177e4SLinus Torvalds }
384*1da177e4SLinus Torvalds 
385*1da177e4SLinus Torvalds 
386*1da177e4SLinus Torvalds static int clip_start_xmit(struct sk_buff *skb,struct net_device *dev)
387*1da177e4SLinus Torvalds {
388*1da177e4SLinus Torvalds 	struct clip_priv *clip_priv = PRIV(dev);
389*1da177e4SLinus Torvalds 	struct atmarp_entry *entry;
390*1da177e4SLinus Torvalds 	struct atm_vcc *vcc;
391*1da177e4SLinus Torvalds 	int old;
392*1da177e4SLinus Torvalds 	unsigned long flags;
393*1da177e4SLinus Torvalds 
394*1da177e4SLinus Torvalds 	DPRINTK("clip_start_xmit (skb %p)\n",skb);
395*1da177e4SLinus Torvalds 	if (!skb->dst) {
396*1da177e4SLinus Torvalds 		printk(KERN_ERR "clip_start_xmit: skb->dst == NULL\n");
397*1da177e4SLinus Torvalds 		dev_kfree_skb(skb);
398*1da177e4SLinus Torvalds 		clip_priv->stats.tx_dropped++;
399*1da177e4SLinus Torvalds 		return 0;
400*1da177e4SLinus Torvalds 	}
401*1da177e4SLinus Torvalds 	if (!skb->dst->neighbour) {
402*1da177e4SLinus Torvalds #if 0
403*1da177e4SLinus Torvalds 		skb->dst->neighbour = clip_find_neighbour(skb->dst,1);
404*1da177e4SLinus Torvalds 		if (!skb->dst->neighbour) {
405*1da177e4SLinus Torvalds 			dev_kfree_skb(skb); /* lost that one */
406*1da177e4SLinus Torvalds 			clip_priv->stats.tx_dropped++;
407*1da177e4SLinus Torvalds 			return 0;
408*1da177e4SLinus Torvalds 		}
409*1da177e4SLinus Torvalds #endif
410*1da177e4SLinus Torvalds 		printk(KERN_ERR "clip_start_xmit: NO NEIGHBOUR !\n");
411*1da177e4SLinus Torvalds 		dev_kfree_skb(skb);
412*1da177e4SLinus Torvalds 		clip_priv->stats.tx_dropped++;
413*1da177e4SLinus Torvalds 		return 0;
414*1da177e4SLinus Torvalds 	}
415*1da177e4SLinus Torvalds 	entry = NEIGH2ENTRY(skb->dst->neighbour);
416*1da177e4SLinus Torvalds 	if (!entry->vccs) {
417*1da177e4SLinus Torvalds 		if (time_after(jiffies, entry->expires)) {
418*1da177e4SLinus Torvalds 			/* should be resolved */
419*1da177e4SLinus Torvalds 			entry->expires = jiffies+ATMARP_RETRY_DELAY*HZ;
420*1da177e4SLinus Torvalds 			to_atmarpd(act_need,PRIV(dev)->number,entry->ip);
421*1da177e4SLinus Torvalds 		}
422*1da177e4SLinus Torvalds 		if (entry->neigh->arp_queue.qlen < ATMARP_MAX_UNRES_PACKETS)
423*1da177e4SLinus Torvalds 			skb_queue_tail(&entry->neigh->arp_queue,skb);
424*1da177e4SLinus Torvalds 		else {
425*1da177e4SLinus Torvalds 			dev_kfree_skb(skb);
426*1da177e4SLinus Torvalds 			clip_priv->stats.tx_dropped++;
427*1da177e4SLinus Torvalds 		}
428*1da177e4SLinus Torvalds 		return 0;
429*1da177e4SLinus Torvalds 	}
430*1da177e4SLinus Torvalds 	DPRINTK("neigh %p, vccs %p\n",entry,entry->vccs);
431*1da177e4SLinus Torvalds 	ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc;
432*1da177e4SLinus Torvalds 	DPRINTK("using neighbour %p, vcc %p\n",skb->dst->neighbour,vcc);
433*1da177e4SLinus Torvalds 	if (entry->vccs->encap) {
434*1da177e4SLinus Torvalds 		void *here;
435*1da177e4SLinus Torvalds 
436*1da177e4SLinus Torvalds 		here = skb_push(skb,RFC1483LLC_LEN);
437*1da177e4SLinus Torvalds 		memcpy(here,llc_oui,sizeof(llc_oui));
438*1da177e4SLinus Torvalds 		((u16 *) here)[3] = skb->protocol;
439*1da177e4SLinus Torvalds 	}
440*1da177e4SLinus Torvalds 	atomic_add(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc);
441*1da177e4SLinus Torvalds 	ATM_SKB(skb)->atm_options = vcc->atm_options;
442*1da177e4SLinus Torvalds 	entry->vccs->last_use = jiffies;
443*1da177e4SLinus Torvalds 	DPRINTK("atm_skb(%p)->vcc(%p)->dev(%p)\n",skb,vcc,vcc->dev);
444*1da177e4SLinus Torvalds 	old = xchg(&entry->vccs->xoff,1); /* assume XOFF ... */
445*1da177e4SLinus Torvalds 	if (old) {
446*1da177e4SLinus Torvalds 		printk(KERN_WARNING "clip_start_xmit: XOFF->XOFF transition\n");
447*1da177e4SLinus Torvalds 		return 0;
448*1da177e4SLinus Torvalds 	}
449*1da177e4SLinus Torvalds 	clip_priv->stats.tx_packets++;
450*1da177e4SLinus Torvalds 	clip_priv->stats.tx_bytes += skb->len;
451*1da177e4SLinus Torvalds 	(void) vcc->send(vcc,skb);
452*1da177e4SLinus Torvalds 	if (atm_may_send(vcc,0)) {
453*1da177e4SLinus Torvalds 		entry->vccs->xoff = 0;
454*1da177e4SLinus Torvalds 		return 0;
455*1da177e4SLinus Torvalds 	}
456*1da177e4SLinus Torvalds 	spin_lock_irqsave(&clip_priv->xoff_lock,flags);
457*1da177e4SLinus Torvalds 	netif_stop_queue(dev); /* XOFF -> throttle immediately */
458*1da177e4SLinus Torvalds 	barrier();
459*1da177e4SLinus Torvalds 	if (!entry->vccs->xoff)
460*1da177e4SLinus Torvalds 		netif_start_queue(dev);
461*1da177e4SLinus Torvalds 		/* Oh, we just raced with clip_pop. netif_start_queue should be
462*1da177e4SLinus Torvalds 		   good enough, because nothing should really be asleep because
463*1da177e4SLinus Torvalds 		   of the brief netif_stop_queue. If this isn't true or if it
464*1da177e4SLinus Torvalds 		   changes, use netif_wake_queue instead. */
465*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&clip_priv->xoff_lock,flags);
466*1da177e4SLinus Torvalds 	return 0;
467*1da177e4SLinus Torvalds }
468*1da177e4SLinus Torvalds 
469*1da177e4SLinus Torvalds 
470*1da177e4SLinus Torvalds static struct net_device_stats *clip_get_stats(struct net_device *dev)
471*1da177e4SLinus Torvalds {
472*1da177e4SLinus Torvalds 	return &PRIV(dev)->stats;
473*1da177e4SLinus Torvalds }
474*1da177e4SLinus Torvalds 
475*1da177e4SLinus Torvalds 
476*1da177e4SLinus Torvalds static int clip_mkip(struct atm_vcc *vcc,int timeout)
477*1da177e4SLinus Torvalds {
478*1da177e4SLinus Torvalds 	struct clip_vcc *clip_vcc;
479*1da177e4SLinus Torvalds 	struct sk_buff_head copy;
480*1da177e4SLinus Torvalds 	struct sk_buff *skb;
481*1da177e4SLinus Torvalds 
482*1da177e4SLinus Torvalds 	if (!vcc->push) return -EBADFD;
483*1da177e4SLinus Torvalds 	clip_vcc = kmalloc(sizeof(struct clip_vcc),GFP_KERNEL);
484*1da177e4SLinus Torvalds 	if (!clip_vcc) return -ENOMEM;
485*1da177e4SLinus Torvalds 	DPRINTK("mkip clip_vcc %p vcc %p\n",clip_vcc,vcc);
486*1da177e4SLinus Torvalds 	clip_vcc->vcc = vcc;
487*1da177e4SLinus Torvalds 	vcc->user_back = clip_vcc;
488*1da177e4SLinus Torvalds 	set_bit(ATM_VF_IS_CLIP, &vcc->flags);
489*1da177e4SLinus Torvalds 	clip_vcc->entry = NULL;
490*1da177e4SLinus Torvalds 	clip_vcc->xoff = 0;
491*1da177e4SLinus Torvalds 	clip_vcc->encap = 1;
492*1da177e4SLinus Torvalds 	clip_vcc->last_use = jiffies;
493*1da177e4SLinus Torvalds 	clip_vcc->idle_timeout = timeout*HZ;
494*1da177e4SLinus Torvalds 	clip_vcc->old_push = vcc->push;
495*1da177e4SLinus Torvalds 	clip_vcc->old_pop = vcc->pop;
496*1da177e4SLinus Torvalds 	vcc->push = clip_push;
497*1da177e4SLinus Torvalds 	vcc->pop = clip_pop;
498*1da177e4SLinus Torvalds 	skb_queue_head_init(&copy);
499*1da177e4SLinus Torvalds 	skb_migrate(&sk_atm(vcc)->sk_receive_queue, &copy);
500*1da177e4SLinus Torvalds 	/* re-process everything received between connection setup and MKIP */
501*1da177e4SLinus Torvalds 	while ((skb = skb_dequeue(&copy)) != NULL)
502*1da177e4SLinus Torvalds 		if (!clip_devs) {
503*1da177e4SLinus Torvalds 			atm_return(vcc,skb->truesize);
504*1da177e4SLinus Torvalds 			kfree_skb(skb);
505*1da177e4SLinus Torvalds 		}
506*1da177e4SLinus Torvalds 		else {
507*1da177e4SLinus Torvalds 			unsigned int len = skb->len;
508*1da177e4SLinus Torvalds 
509*1da177e4SLinus Torvalds 			clip_push(vcc,skb);
510*1da177e4SLinus Torvalds 			PRIV(skb->dev)->stats.rx_packets--;
511*1da177e4SLinus Torvalds 			PRIV(skb->dev)->stats.rx_bytes -= len;
512*1da177e4SLinus Torvalds 		}
513*1da177e4SLinus Torvalds 	return 0;
514*1da177e4SLinus Torvalds }
515*1da177e4SLinus Torvalds 
516*1da177e4SLinus Torvalds 
517*1da177e4SLinus Torvalds static int clip_setentry(struct atm_vcc *vcc,u32 ip)
518*1da177e4SLinus Torvalds {
519*1da177e4SLinus Torvalds 	struct neighbour *neigh;
520*1da177e4SLinus Torvalds 	struct atmarp_entry *entry;
521*1da177e4SLinus Torvalds 	int error;
522*1da177e4SLinus Torvalds 	struct clip_vcc *clip_vcc;
523*1da177e4SLinus Torvalds 	struct flowi fl = { .nl_u = { .ip4_u = { .daddr = ip, .tos = 1 } } };
524*1da177e4SLinus Torvalds 	struct rtable *rt;
525*1da177e4SLinus Torvalds 
526*1da177e4SLinus Torvalds 	if (vcc->push != clip_push) {
527*1da177e4SLinus Torvalds 		printk(KERN_WARNING "clip_setentry: non-CLIP VCC\n");
528*1da177e4SLinus Torvalds 		return -EBADF;
529*1da177e4SLinus Torvalds 	}
530*1da177e4SLinus Torvalds 	clip_vcc = CLIP_VCC(vcc);
531*1da177e4SLinus Torvalds 	if (!ip) {
532*1da177e4SLinus Torvalds 		if (!clip_vcc->entry) {
533*1da177e4SLinus Torvalds 			printk(KERN_ERR "hiding hidden ATMARP entry\n");
534*1da177e4SLinus Torvalds 			return 0;
535*1da177e4SLinus Torvalds 		}
536*1da177e4SLinus Torvalds 		DPRINTK("setentry: remove\n");
537*1da177e4SLinus Torvalds 		unlink_clip_vcc(clip_vcc);
538*1da177e4SLinus Torvalds 		return 0;
539*1da177e4SLinus Torvalds 	}
540*1da177e4SLinus Torvalds 	error = ip_route_output_key(&rt,&fl);
541*1da177e4SLinus Torvalds 	if (error) return error;
542*1da177e4SLinus Torvalds 	neigh = __neigh_lookup(&clip_tbl,&ip,rt->u.dst.dev,1);
543*1da177e4SLinus Torvalds 	ip_rt_put(rt);
544*1da177e4SLinus Torvalds 	if (!neigh)
545*1da177e4SLinus Torvalds 		return -ENOMEM;
546*1da177e4SLinus Torvalds 	entry = NEIGH2ENTRY(neigh);
547*1da177e4SLinus Torvalds 	if (entry != clip_vcc->entry) {
548*1da177e4SLinus Torvalds 		if (!clip_vcc->entry) DPRINTK("setentry: add\n");
549*1da177e4SLinus Torvalds 		else {
550*1da177e4SLinus Torvalds 			DPRINTK("setentry: update\n");
551*1da177e4SLinus Torvalds 			unlink_clip_vcc(clip_vcc);
552*1da177e4SLinus Torvalds 		}
553*1da177e4SLinus Torvalds 		link_vcc(clip_vcc,entry);
554*1da177e4SLinus Torvalds 	}
555*1da177e4SLinus Torvalds 	error = neigh_update(neigh, llc_oui, NUD_PERMANENT,
556*1da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_OVERRIDE|NEIGH_UPDATE_F_ADMIN);
557*1da177e4SLinus Torvalds 	neigh_release(neigh);
558*1da177e4SLinus Torvalds 	return error;
559*1da177e4SLinus Torvalds }
560*1da177e4SLinus Torvalds 
561*1da177e4SLinus Torvalds 
562*1da177e4SLinus Torvalds static void clip_setup(struct net_device *dev)
563*1da177e4SLinus Torvalds {
564*1da177e4SLinus Torvalds 	dev->hard_start_xmit = clip_start_xmit;
565*1da177e4SLinus Torvalds 	/* sg_xmit ... */
566*1da177e4SLinus Torvalds 	dev->get_stats = clip_get_stats;
567*1da177e4SLinus Torvalds 	dev->type = ARPHRD_ATM;
568*1da177e4SLinus Torvalds 	dev->hard_header_len = RFC1483LLC_LEN;
569*1da177e4SLinus Torvalds 	dev->mtu = RFC1626_MTU;
570*1da177e4SLinus Torvalds 	dev->tx_queue_len = 100; /* "normal" queue (packets) */
571*1da177e4SLinus Torvalds 	    /* When using a "real" qdisc, the qdisc determines the queue */
572*1da177e4SLinus Torvalds 	    /* length. tx_queue_len is only used for the default case, */
573*1da177e4SLinus Torvalds 	    /* without any more elaborate queuing. 100 is a reasonable */
574*1da177e4SLinus Torvalds 	    /* compromise between decent burst-tolerance and protection */
575*1da177e4SLinus Torvalds 	    /* against memory hogs. */
576*1da177e4SLinus Torvalds }
577*1da177e4SLinus Torvalds 
578*1da177e4SLinus Torvalds 
579*1da177e4SLinus Torvalds static int clip_create(int number)
580*1da177e4SLinus Torvalds {
581*1da177e4SLinus Torvalds 	struct net_device *dev;
582*1da177e4SLinus Torvalds 	struct clip_priv *clip_priv;
583*1da177e4SLinus Torvalds 	int error;
584*1da177e4SLinus Torvalds 
585*1da177e4SLinus Torvalds 	if (number != -1) {
586*1da177e4SLinus Torvalds 		for (dev = clip_devs; dev; dev = PRIV(dev)->next)
587*1da177e4SLinus Torvalds 			if (PRIV(dev)->number == number) return -EEXIST;
588*1da177e4SLinus Torvalds 	}
589*1da177e4SLinus Torvalds 	else {
590*1da177e4SLinus Torvalds 		number = 0;
591*1da177e4SLinus Torvalds 		for (dev = clip_devs; dev; dev = PRIV(dev)->next)
592*1da177e4SLinus Torvalds 			if (PRIV(dev)->number >= number)
593*1da177e4SLinus Torvalds 				number = PRIV(dev)->number+1;
594*1da177e4SLinus Torvalds 	}
595*1da177e4SLinus Torvalds 	dev = alloc_netdev(sizeof(struct clip_priv), "", clip_setup);
596*1da177e4SLinus Torvalds 	if (!dev)
597*1da177e4SLinus Torvalds 		return -ENOMEM;
598*1da177e4SLinus Torvalds 	clip_priv = PRIV(dev);
599*1da177e4SLinus Torvalds 	sprintf(dev->name,"atm%d",number);
600*1da177e4SLinus Torvalds 	spin_lock_init(&clip_priv->xoff_lock);
601*1da177e4SLinus Torvalds 	clip_priv->number = number;
602*1da177e4SLinus Torvalds 	error = register_netdev(dev);
603*1da177e4SLinus Torvalds 	if (error) {
604*1da177e4SLinus Torvalds 		free_netdev(dev);
605*1da177e4SLinus Torvalds 		return error;
606*1da177e4SLinus Torvalds 	}
607*1da177e4SLinus Torvalds 	clip_priv->next = clip_devs;
608*1da177e4SLinus Torvalds 	clip_devs = dev;
609*1da177e4SLinus Torvalds 	DPRINTK("registered (net:%s)\n",dev->name);
610*1da177e4SLinus Torvalds 	return number;
611*1da177e4SLinus Torvalds }
612*1da177e4SLinus Torvalds 
613*1da177e4SLinus Torvalds 
614*1da177e4SLinus Torvalds static int clip_device_event(struct notifier_block *this,unsigned long event,
615*1da177e4SLinus Torvalds     void *dev)
616*1da177e4SLinus Torvalds {
617*1da177e4SLinus Torvalds 	/* ignore non-CLIP devices */
618*1da177e4SLinus Torvalds 	if (((struct net_device *) dev)->type != ARPHRD_ATM ||
619*1da177e4SLinus Torvalds 	    ((struct net_device *) dev)->hard_start_xmit != clip_start_xmit)
620*1da177e4SLinus Torvalds 		return NOTIFY_DONE;
621*1da177e4SLinus Torvalds 	switch (event) {
622*1da177e4SLinus Torvalds 		case NETDEV_UP:
623*1da177e4SLinus Torvalds 			DPRINTK("clip_device_event NETDEV_UP\n");
624*1da177e4SLinus Torvalds 			(void) to_atmarpd(act_up,PRIV(dev)->number,0);
625*1da177e4SLinus Torvalds 			break;
626*1da177e4SLinus Torvalds 		case NETDEV_GOING_DOWN:
627*1da177e4SLinus Torvalds 			DPRINTK("clip_device_event NETDEV_DOWN\n");
628*1da177e4SLinus Torvalds 			(void) to_atmarpd(act_down,PRIV(dev)->number,0);
629*1da177e4SLinus Torvalds 			break;
630*1da177e4SLinus Torvalds 		case NETDEV_CHANGE:
631*1da177e4SLinus Torvalds 		case NETDEV_CHANGEMTU:
632*1da177e4SLinus Torvalds 			DPRINTK("clip_device_event NETDEV_CHANGE*\n");
633*1da177e4SLinus Torvalds 			(void) to_atmarpd(act_change,PRIV(dev)->number,0);
634*1da177e4SLinus Torvalds 			break;
635*1da177e4SLinus Torvalds 		case NETDEV_REBOOT:
636*1da177e4SLinus Torvalds 		case NETDEV_REGISTER:
637*1da177e4SLinus Torvalds 		case NETDEV_DOWN:
638*1da177e4SLinus Torvalds 			DPRINTK("clip_device_event %ld\n",event);
639*1da177e4SLinus Torvalds 			/* ignore */
640*1da177e4SLinus Torvalds 			break;
641*1da177e4SLinus Torvalds 		default:
642*1da177e4SLinus Torvalds 			printk(KERN_WARNING "clip_device_event: unknown event "
643*1da177e4SLinus Torvalds 			    "%ld\n",event);
644*1da177e4SLinus Torvalds 			break;
645*1da177e4SLinus Torvalds 	}
646*1da177e4SLinus Torvalds 	return NOTIFY_DONE;
647*1da177e4SLinus Torvalds }
648*1da177e4SLinus Torvalds 
649*1da177e4SLinus Torvalds 
650*1da177e4SLinus Torvalds static int clip_inet_event(struct notifier_block *this,unsigned long event,
651*1da177e4SLinus Torvalds     void *ifa)
652*1da177e4SLinus Torvalds {
653*1da177e4SLinus Torvalds 	struct in_device *in_dev;
654*1da177e4SLinus Torvalds 
655*1da177e4SLinus Torvalds 	in_dev = ((struct in_ifaddr *) ifa)->ifa_dev;
656*1da177e4SLinus Torvalds 	if (!in_dev || !in_dev->dev) {
657*1da177e4SLinus Torvalds 		printk(KERN_WARNING "clip_inet_event: no device\n");
658*1da177e4SLinus Torvalds 		return NOTIFY_DONE;
659*1da177e4SLinus Torvalds 	}
660*1da177e4SLinus Torvalds 	/*
661*1da177e4SLinus Torvalds 	 * Transitions are of the down-change-up type, so it's sufficient to
662*1da177e4SLinus Torvalds 	 * handle the change on up.
663*1da177e4SLinus Torvalds 	 */
664*1da177e4SLinus Torvalds 	if (event != NETDEV_UP) return NOTIFY_DONE;
665*1da177e4SLinus Torvalds 	return clip_device_event(this,NETDEV_CHANGE,in_dev->dev);
666*1da177e4SLinus Torvalds }
667*1da177e4SLinus Torvalds 
668*1da177e4SLinus Torvalds 
669*1da177e4SLinus Torvalds static struct notifier_block clip_dev_notifier = {
670*1da177e4SLinus Torvalds 	clip_device_event,
671*1da177e4SLinus Torvalds 	NULL,
672*1da177e4SLinus Torvalds 	0
673*1da177e4SLinus Torvalds };
674*1da177e4SLinus Torvalds 
675*1da177e4SLinus Torvalds 
676*1da177e4SLinus Torvalds 
677*1da177e4SLinus Torvalds static struct notifier_block clip_inet_notifier = {
678*1da177e4SLinus Torvalds 	clip_inet_event,
679*1da177e4SLinus Torvalds 	NULL,
680*1da177e4SLinus Torvalds 	0
681*1da177e4SLinus Torvalds };
682*1da177e4SLinus Torvalds 
683*1da177e4SLinus Torvalds 
684*1da177e4SLinus Torvalds 
685*1da177e4SLinus Torvalds static void atmarpd_close(struct atm_vcc *vcc)
686*1da177e4SLinus Torvalds {
687*1da177e4SLinus Torvalds 	DPRINTK("atmarpd_close\n");
688*1da177e4SLinus Torvalds 	atmarpd = NULL; /* assumed to be atomic */
689*1da177e4SLinus Torvalds 	barrier();
690*1da177e4SLinus Torvalds 	unregister_inetaddr_notifier(&clip_inet_notifier);
691*1da177e4SLinus Torvalds 	unregister_netdevice_notifier(&clip_dev_notifier);
692*1da177e4SLinus Torvalds 	if (skb_peek(&sk_atm(vcc)->sk_receive_queue))
693*1da177e4SLinus Torvalds 		printk(KERN_ERR "atmarpd_close: closing with requests "
694*1da177e4SLinus Torvalds 		    "pending\n");
695*1da177e4SLinus Torvalds 	skb_queue_purge(&sk_atm(vcc)->sk_receive_queue);
696*1da177e4SLinus Torvalds 	DPRINTK("(done)\n");
697*1da177e4SLinus Torvalds 	module_put(THIS_MODULE);
698*1da177e4SLinus Torvalds }
699*1da177e4SLinus Torvalds 
700*1da177e4SLinus Torvalds 
701*1da177e4SLinus Torvalds static struct atmdev_ops atmarpd_dev_ops = {
702*1da177e4SLinus Torvalds 	.close = atmarpd_close
703*1da177e4SLinus Torvalds };
704*1da177e4SLinus Torvalds 
705*1da177e4SLinus Torvalds 
706*1da177e4SLinus Torvalds static struct atm_dev atmarpd_dev = {
707*1da177e4SLinus Torvalds 	.ops =			&atmarpd_dev_ops,
708*1da177e4SLinus Torvalds 	.type =			"arpd",
709*1da177e4SLinus Torvalds 	.number = 		999,
710*1da177e4SLinus Torvalds 	.lock =			SPIN_LOCK_UNLOCKED
711*1da177e4SLinus Torvalds };
712*1da177e4SLinus Torvalds 
713*1da177e4SLinus Torvalds 
714*1da177e4SLinus Torvalds static int atm_init_atmarp(struct atm_vcc *vcc)
715*1da177e4SLinus Torvalds {
716*1da177e4SLinus Torvalds 	if (atmarpd) return -EADDRINUSE;
717*1da177e4SLinus Torvalds 	if (start_timer) {
718*1da177e4SLinus Torvalds 		start_timer = 0;
719*1da177e4SLinus Torvalds 		init_timer(&idle_timer);
720*1da177e4SLinus Torvalds 		idle_timer.expires = jiffies+CLIP_CHECK_INTERVAL*HZ;
721*1da177e4SLinus Torvalds 		idle_timer.function = idle_timer_check;
722*1da177e4SLinus Torvalds 		add_timer(&idle_timer);
723*1da177e4SLinus Torvalds 	}
724*1da177e4SLinus Torvalds 	atmarpd = vcc;
725*1da177e4SLinus Torvalds 	set_bit(ATM_VF_META,&vcc->flags);
726*1da177e4SLinus Torvalds 	set_bit(ATM_VF_READY,&vcc->flags);
727*1da177e4SLinus Torvalds 	    /* allow replies and avoid getting closed if signaling dies */
728*1da177e4SLinus Torvalds 	vcc->dev = &atmarpd_dev;
729*1da177e4SLinus Torvalds 	vcc_insert_socket(sk_atm(vcc));
730*1da177e4SLinus Torvalds 	vcc->push = NULL;
731*1da177e4SLinus Torvalds 	vcc->pop = NULL; /* crash */
732*1da177e4SLinus Torvalds 	vcc->push_oam = NULL; /* crash */
733*1da177e4SLinus Torvalds 	if (register_netdevice_notifier(&clip_dev_notifier))
734*1da177e4SLinus Torvalds 		printk(KERN_ERR "register_netdevice_notifier failed\n");
735*1da177e4SLinus Torvalds 	if (register_inetaddr_notifier(&clip_inet_notifier))
736*1da177e4SLinus Torvalds 		printk(KERN_ERR "register_inetaddr_notifier failed\n");
737*1da177e4SLinus Torvalds 	return 0;
738*1da177e4SLinus Torvalds }
739*1da177e4SLinus Torvalds 
740*1da177e4SLinus Torvalds static int clip_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
741*1da177e4SLinus Torvalds {
742*1da177e4SLinus Torvalds 	struct atm_vcc *vcc = ATM_SD(sock);
743*1da177e4SLinus Torvalds 	int err = 0;
744*1da177e4SLinus Torvalds 
745*1da177e4SLinus Torvalds 	switch (cmd) {
746*1da177e4SLinus Torvalds 		case SIOCMKCLIP:
747*1da177e4SLinus Torvalds 		case ATMARPD_CTRL:
748*1da177e4SLinus Torvalds 		case ATMARP_MKIP:
749*1da177e4SLinus Torvalds 		case ATMARP_SETENTRY:
750*1da177e4SLinus Torvalds 		case ATMARP_ENCAP:
751*1da177e4SLinus Torvalds 			if (!capable(CAP_NET_ADMIN))
752*1da177e4SLinus Torvalds 				return -EPERM;
753*1da177e4SLinus Torvalds 			break;
754*1da177e4SLinus Torvalds 		default:
755*1da177e4SLinus Torvalds 			return -ENOIOCTLCMD;
756*1da177e4SLinus Torvalds 	}
757*1da177e4SLinus Torvalds 
758*1da177e4SLinus Torvalds 	switch (cmd) {
759*1da177e4SLinus Torvalds 		case SIOCMKCLIP:
760*1da177e4SLinus Torvalds 			err = clip_create(arg);
761*1da177e4SLinus Torvalds 			break;
762*1da177e4SLinus Torvalds 		case ATMARPD_CTRL:
763*1da177e4SLinus Torvalds 			err = atm_init_atmarp(vcc);
764*1da177e4SLinus Torvalds 			if (!err) {
765*1da177e4SLinus Torvalds 				sock->state = SS_CONNECTED;
766*1da177e4SLinus Torvalds 				__module_get(THIS_MODULE);
767*1da177e4SLinus Torvalds 			}
768*1da177e4SLinus Torvalds 			break;
769*1da177e4SLinus Torvalds 		case ATMARP_MKIP:
770*1da177e4SLinus Torvalds 			err = clip_mkip(vcc ,arg);
771*1da177e4SLinus Torvalds 			break;
772*1da177e4SLinus Torvalds 		case ATMARP_SETENTRY:
773*1da177e4SLinus Torvalds 			err = clip_setentry(vcc, arg);
774*1da177e4SLinus Torvalds 			break;
775*1da177e4SLinus Torvalds 		case ATMARP_ENCAP:
776*1da177e4SLinus Torvalds 			err = clip_encap(vcc, arg);
777*1da177e4SLinus Torvalds 			break;
778*1da177e4SLinus Torvalds 	}
779*1da177e4SLinus Torvalds 	return err;
780*1da177e4SLinus Torvalds }
781*1da177e4SLinus Torvalds 
782*1da177e4SLinus Torvalds static struct atm_ioctl clip_ioctl_ops = {
783*1da177e4SLinus Torvalds 	.owner 	= THIS_MODULE,
784*1da177e4SLinus Torvalds 	.ioctl	= clip_ioctl,
785*1da177e4SLinus Torvalds };
786*1da177e4SLinus Torvalds 
787*1da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
788*1da177e4SLinus Torvalds 
789*1da177e4SLinus Torvalds static void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr)
790*1da177e4SLinus Torvalds {
791*1da177e4SLinus Torvalds 	static int code[] = { 1,2,10,6,1,0 };
792*1da177e4SLinus Torvalds 	static int e164[] = { 1,8,4,6,1,0 };
793*1da177e4SLinus Torvalds 
794*1da177e4SLinus Torvalds 	if (*addr->sas_addr.pub) {
795*1da177e4SLinus Torvalds 		seq_printf(seq, "%s", addr->sas_addr.pub);
796*1da177e4SLinus Torvalds 		if (*addr->sas_addr.prv)
797*1da177e4SLinus Torvalds 			seq_putc(seq, '+');
798*1da177e4SLinus Torvalds 	} else if (!*addr->sas_addr.prv) {
799*1da177e4SLinus Torvalds 		seq_printf(seq, "%s", "(none)");
800*1da177e4SLinus Torvalds 		return;
801*1da177e4SLinus Torvalds 	}
802*1da177e4SLinus Torvalds 	if (*addr->sas_addr.prv) {
803*1da177e4SLinus Torvalds 		unsigned char *prv = addr->sas_addr.prv;
804*1da177e4SLinus Torvalds 		int *fields;
805*1da177e4SLinus Torvalds 		int i, j;
806*1da177e4SLinus Torvalds 
807*1da177e4SLinus Torvalds 		fields = *prv == ATM_AFI_E164 ? e164 : code;
808*1da177e4SLinus Torvalds 		for (i = 0; fields[i]; i++) {
809*1da177e4SLinus Torvalds 			for (j = fields[i]; j; j--)
810*1da177e4SLinus Torvalds 				seq_printf(seq, "%02X", *prv++);
811*1da177e4SLinus Torvalds 			if (fields[i+1])
812*1da177e4SLinus Torvalds 				seq_putc(seq, '.');
813*1da177e4SLinus Torvalds 		}
814*1da177e4SLinus Torvalds 	}
815*1da177e4SLinus Torvalds }
816*1da177e4SLinus Torvalds 
817*1da177e4SLinus Torvalds /* This means the neighbour entry has no attached VCC objects. */
818*1da177e4SLinus Torvalds #define SEQ_NO_VCC_TOKEN	((void *) 2)
819*1da177e4SLinus Torvalds 
820*1da177e4SLinus Torvalds static void atmarp_info(struct seq_file *seq, struct net_device *dev,
821*1da177e4SLinus Torvalds 			struct atmarp_entry *entry, struct clip_vcc *clip_vcc)
822*1da177e4SLinus Torvalds {
823*1da177e4SLinus Torvalds 	unsigned long exp;
824*1da177e4SLinus Torvalds 	char buf[17];
825*1da177e4SLinus Torvalds 	int svc, llc, off;
826*1da177e4SLinus Torvalds 
827*1da177e4SLinus Torvalds 	svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) ||
828*1da177e4SLinus Torvalds 	       (sk_atm(clip_vcc->vcc)->sk_family == AF_ATMSVC));
829*1da177e4SLinus Torvalds 
830*1da177e4SLinus Torvalds 	llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) ||
831*1da177e4SLinus Torvalds 	       clip_vcc->encap);
832*1da177e4SLinus Torvalds 
833*1da177e4SLinus Torvalds 	if (clip_vcc == SEQ_NO_VCC_TOKEN)
834*1da177e4SLinus Torvalds 		exp = entry->neigh->used;
835*1da177e4SLinus Torvalds 	else
836*1da177e4SLinus Torvalds 		exp = clip_vcc->last_use;
837*1da177e4SLinus Torvalds 
838*1da177e4SLinus Torvalds 	exp = (jiffies - exp) / HZ;
839*1da177e4SLinus Torvalds 
840*1da177e4SLinus Torvalds 	seq_printf(seq, "%-6s%-4s%-4s%5ld ",
841*1da177e4SLinus Torvalds 		   dev->name,
842*1da177e4SLinus Torvalds 		   svc ? "SVC" : "PVC",
843*1da177e4SLinus Torvalds 		   llc ? "LLC" : "NULL",
844*1da177e4SLinus Torvalds 		   exp);
845*1da177e4SLinus Torvalds 
846*1da177e4SLinus Torvalds 	off = scnprintf(buf, sizeof(buf) - 1, "%d.%d.%d.%d",
847*1da177e4SLinus Torvalds 			NIPQUAD(entry->ip));
848*1da177e4SLinus Torvalds 	while (off < 16)
849*1da177e4SLinus Torvalds 		buf[off++] = ' ';
850*1da177e4SLinus Torvalds 	buf[off] = '\0';
851*1da177e4SLinus Torvalds 	seq_printf(seq, "%s", buf);
852*1da177e4SLinus Torvalds 
853*1da177e4SLinus Torvalds 	if (clip_vcc == SEQ_NO_VCC_TOKEN) {
854*1da177e4SLinus Torvalds 		if (time_before(jiffies, entry->expires))
855*1da177e4SLinus Torvalds 			seq_printf(seq, "(resolving)\n");
856*1da177e4SLinus Torvalds 		else
857*1da177e4SLinus Torvalds 			seq_printf(seq, "(expired, ref %d)\n",
858*1da177e4SLinus Torvalds 				   atomic_read(&entry->neigh->refcnt));
859*1da177e4SLinus Torvalds 	} else if (!svc) {
860*1da177e4SLinus Torvalds 		seq_printf(seq, "%d.%d.%d\n",
861*1da177e4SLinus Torvalds 			   clip_vcc->vcc->dev->number,
862*1da177e4SLinus Torvalds 			   clip_vcc->vcc->vpi,
863*1da177e4SLinus Torvalds 			   clip_vcc->vcc->vci);
864*1da177e4SLinus Torvalds 	} else {
865*1da177e4SLinus Torvalds 		svc_addr(seq, &clip_vcc->vcc->remote);
866*1da177e4SLinus Torvalds 		seq_putc(seq, '\n');
867*1da177e4SLinus Torvalds 	}
868*1da177e4SLinus Torvalds }
869*1da177e4SLinus Torvalds 
870*1da177e4SLinus Torvalds struct clip_seq_state {
871*1da177e4SLinus Torvalds 	/* This member must be first. */
872*1da177e4SLinus Torvalds 	struct neigh_seq_state ns;
873*1da177e4SLinus Torvalds 
874*1da177e4SLinus Torvalds 	/* Local to clip specific iteration. */
875*1da177e4SLinus Torvalds 	struct clip_vcc *vcc;
876*1da177e4SLinus Torvalds };
877*1da177e4SLinus Torvalds 
878*1da177e4SLinus Torvalds static struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e,
879*1da177e4SLinus Torvalds 					  struct clip_vcc *curr)
880*1da177e4SLinus Torvalds {
881*1da177e4SLinus Torvalds 	if (!curr) {
882*1da177e4SLinus Torvalds 		curr = e->vccs;
883*1da177e4SLinus Torvalds 		if (!curr)
884*1da177e4SLinus Torvalds 			return SEQ_NO_VCC_TOKEN;
885*1da177e4SLinus Torvalds 		return curr;
886*1da177e4SLinus Torvalds 	}
887*1da177e4SLinus Torvalds 	if (curr == SEQ_NO_VCC_TOKEN)
888*1da177e4SLinus Torvalds 		return NULL;
889*1da177e4SLinus Torvalds 
890*1da177e4SLinus Torvalds 	curr = curr->next;
891*1da177e4SLinus Torvalds 
892*1da177e4SLinus Torvalds 	return curr;
893*1da177e4SLinus Torvalds }
894*1da177e4SLinus Torvalds 
895*1da177e4SLinus Torvalds static void *clip_seq_vcc_walk(struct clip_seq_state *state,
896*1da177e4SLinus Torvalds 			       struct atmarp_entry *e, loff_t *pos)
897*1da177e4SLinus Torvalds {
898*1da177e4SLinus Torvalds 	struct clip_vcc *vcc = state->vcc;
899*1da177e4SLinus Torvalds 
900*1da177e4SLinus Torvalds 	vcc = clip_seq_next_vcc(e, vcc);
901*1da177e4SLinus Torvalds 	if (vcc && pos != NULL) {
902*1da177e4SLinus Torvalds 		while (*pos) {
903*1da177e4SLinus Torvalds 			vcc = clip_seq_next_vcc(e, vcc);
904*1da177e4SLinus Torvalds 			if (!vcc)
905*1da177e4SLinus Torvalds 				break;
906*1da177e4SLinus Torvalds 			--(*pos);
907*1da177e4SLinus Torvalds 		}
908*1da177e4SLinus Torvalds 	}
909*1da177e4SLinus Torvalds 	state->vcc = vcc;
910*1da177e4SLinus Torvalds 
911*1da177e4SLinus Torvalds 	return vcc;
912*1da177e4SLinus Torvalds }
913*1da177e4SLinus Torvalds 
914*1da177e4SLinus Torvalds static void *clip_seq_sub_iter(struct neigh_seq_state *_state,
915*1da177e4SLinus Torvalds 			       struct neighbour *n, loff_t *pos)
916*1da177e4SLinus Torvalds {
917*1da177e4SLinus Torvalds 	struct clip_seq_state *state = (struct clip_seq_state *) _state;
918*1da177e4SLinus Torvalds 
919*1da177e4SLinus Torvalds 	return clip_seq_vcc_walk(state, NEIGH2ENTRY(n), pos);
920*1da177e4SLinus Torvalds }
921*1da177e4SLinus Torvalds 
922*1da177e4SLinus Torvalds static void *clip_seq_start(struct seq_file *seq, loff_t *pos)
923*1da177e4SLinus Torvalds {
924*1da177e4SLinus Torvalds 	return neigh_seq_start(seq, pos, &clip_tbl, NEIGH_SEQ_NEIGH_ONLY);
925*1da177e4SLinus Torvalds }
926*1da177e4SLinus Torvalds 
927*1da177e4SLinus Torvalds static int clip_seq_show(struct seq_file *seq, void *v)
928*1da177e4SLinus Torvalds {
929*1da177e4SLinus Torvalds 	static char atm_arp_banner[] =
930*1da177e4SLinus Torvalds 		"IPitf TypeEncp Idle IP address      ATM address\n";
931*1da177e4SLinus Torvalds 
932*1da177e4SLinus Torvalds 	if (v == SEQ_START_TOKEN) {
933*1da177e4SLinus Torvalds 		seq_puts(seq, atm_arp_banner);
934*1da177e4SLinus Torvalds 	} else {
935*1da177e4SLinus Torvalds 		struct clip_seq_state *state = seq->private;
936*1da177e4SLinus Torvalds 		struct neighbour *n = v;
937*1da177e4SLinus Torvalds 		struct clip_vcc *vcc = state->vcc;
938*1da177e4SLinus Torvalds 
939*1da177e4SLinus Torvalds 		atmarp_info(seq, n->dev, NEIGH2ENTRY(n), vcc);
940*1da177e4SLinus Torvalds 	}
941*1da177e4SLinus Torvalds   	return 0;
942*1da177e4SLinus Torvalds }
943*1da177e4SLinus Torvalds 
944*1da177e4SLinus Torvalds static struct seq_operations arp_seq_ops = {
945*1da177e4SLinus Torvalds 	.start	= clip_seq_start,
946*1da177e4SLinus Torvalds 	.next	= neigh_seq_next,
947*1da177e4SLinus Torvalds 	.stop	= neigh_seq_stop,
948*1da177e4SLinus Torvalds 	.show	= clip_seq_show,
949*1da177e4SLinus Torvalds };
950*1da177e4SLinus Torvalds 
951*1da177e4SLinus Torvalds static int arp_seq_open(struct inode *inode, struct file *file)
952*1da177e4SLinus Torvalds {
953*1da177e4SLinus Torvalds 	struct clip_seq_state *state;
954*1da177e4SLinus Torvalds 	struct seq_file *seq;
955*1da177e4SLinus Torvalds 	int rc = -EAGAIN;
956*1da177e4SLinus Torvalds 
957*1da177e4SLinus Torvalds 	state = kmalloc(sizeof(*state), GFP_KERNEL);
958*1da177e4SLinus Torvalds 	if (!state) {
959*1da177e4SLinus Torvalds 		rc = -ENOMEM;
960*1da177e4SLinus Torvalds 		goto out_kfree;
961*1da177e4SLinus Torvalds 	}
962*1da177e4SLinus Torvalds 	memset(state, 0, sizeof(*state));
963*1da177e4SLinus Torvalds 	state->ns.neigh_sub_iter = clip_seq_sub_iter;
964*1da177e4SLinus Torvalds 
965*1da177e4SLinus Torvalds 	rc = seq_open(file, &arp_seq_ops);
966*1da177e4SLinus Torvalds 	if (rc)
967*1da177e4SLinus Torvalds 		goto out_kfree;
968*1da177e4SLinus Torvalds 
969*1da177e4SLinus Torvalds 	seq = file->private_data;
970*1da177e4SLinus Torvalds 	seq->private = state;
971*1da177e4SLinus Torvalds out:
972*1da177e4SLinus Torvalds 	return rc;
973*1da177e4SLinus Torvalds 
974*1da177e4SLinus Torvalds out_kfree:
975*1da177e4SLinus Torvalds 	kfree(state);
976*1da177e4SLinus Torvalds 	goto out;
977*1da177e4SLinus Torvalds }
978*1da177e4SLinus Torvalds 
979*1da177e4SLinus Torvalds static struct file_operations arp_seq_fops = {
980*1da177e4SLinus Torvalds 	.open		= arp_seq_open,
981*1da177e4SLinus Torvalds 	.read		= seq_read,
982*1da177e4SLinus Torvalds 	.llseek		= seq_lseek,
983*1da177e4SLinus Torvalds 	.release	= seq_release_private,
984*1da177e4SLinus Torvalds 	.owner		= THIS_MODULE
985*1da177e4SLinus Torvalds };
986*1da177e4SLinus Torvalds #endif
987*1da177e4SLinus Torvalds 
988*1da177e4SLinus Torvalds static int __init atm_clip_init(void)
989*1da177e4SLinus Torvalds {
990*1da177e4SLinus Torvalds 	neigh_table_init(&clip_tbl);
991*1da177e4SLinus Torvalds 
992*1da177e4SLinus Torvalds 	clip_tbl_hook = &clip_tbl;
993*1da177e4SLinus Torvalds 	register_atm_ioctl(&clip_ioctl_ops);
994*1da177e4SLinus Torvalds 
995*1da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
996*1da177e4SLinus Torvalds {
997*1da177e4SLinus Torvalds 	struct proc_dir_entry *p;
998*1da177e4SLinus Torvalds 
999*1da177e4SLinus Torvalds 	p = create_proc_entry("arp", S_IRUGO, atm_proc_root);
1000*1da177e4SLinus Torvalds 	if (p)
1001*1da177e4SLinus Torvalds 		p->proc_fops = &arp_seq_fops;
1002*1da177e4SLinus Torvalds }
1003*1da177e4SLinus Torvalds #endif
1004*1da177e4SLinus Torvalds 
1005*1da177e4SLinus Torvalds 	return 0;
1006*1da177e4SLinus Torvalds }
1007*1da177e4SLinus Torvalds 
1008*1da177e4SLinus Torvalds static void __exit atm_clip_exit(void)
1009*1da177e4SLinus Torvalds {
1010*1da177e4SLinus Torvalds 	struct net_device *dev, *next;
1011*1da177e4SLinus Torvalds 
1012*1da177e4SLinus Torvalds 	remove_proc_entry("arp", atm_proc_root);
1013*1da177e4SLinus Torvalds 
1014*1da177e4SLinus Torvalds 	deregister_atm_ioctl(&clip_ioctl_ops);
1015*1da177e4SLinus Torvalds 
1016*1da177e4SLinus Torvalds 	/* First, stop the idle timer, so it stops banging
1017*1da177e4SLinus Torvalds 	 * on the table.
1018*1da177e4SLinus Torvalds 	 */
1019*1da177e4SLinus Torvalds 	if (start_timer == 0)
1020*1da177e4SLinus Torvalds 		del_timer(&idle_timer);
1021*1da177e4SLinus Torvalds 
1022*1da177e4SLinus Torvalds 	/* Next, purge the table, so that the device
1023*1da177e4SLinus Torvalds 	 * unregister loop below does not hang due to
1024*1da177e4SLinus Torvalds 	 * device references remaining in the table.
1025*1da177e4SLinus Torvalds 	 */
1026*1da177e4SLinus Torvalds 	neigh_ifdown(&clip_tbl, NULL);
1027*1da177e4SLinus Torvalds 
1028*1da177e4SLinus Torvalds 	dev = clip_devs;
1029*1da177e4SLinus Torvalds 	while (dev) {
1030*1da177e4SLinus Torvalds 		next = PRIV(dev)->next;
1031*1da177e4SLinus Torvalds 		unregister_netdev(dev);
1032*1da177e4SLinus Torvalds 		free_netdev(dev);
1033*1da177e4SLinus Torvalds 		dev = next;
1034*1da177e4SLinus Torvalds 	}
1035*1da177e4SLinus Torvalds 
1036*1da177e4SLinus Torvalds 	/* Now it is safe to fully shutdown whole table. */
1037*1da177e4SLinus Torvalds 	neigh_table_clear(&clip_tbl);
1038*1da177e4SLinus Torvalds 
1039*1da177e4SLinus Torvalds 	clip_tbl_hook = NULL;
1040*1da177e4SLinus Torvalds }
1041*1da177e4SLinus Torvalds 
1042*1da177e4SLinus Torvalds module_init(atm_clip_init);
1043*1da177e4SLinus Torvalds module_exit(atm_clip_exit);
1044*1da177e4SLinus Torvalds 
1045*1da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1046