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