Blame view

net/econet/af_econet.c 24.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
  /*
   *	An implementation of the Acorn Econet and AUN protocols.
   *	Philip Blundell <philb@gnu.org>
   *
   *	This program is free software; you can redistribute it and/or
   *	modify it under the terms of the GNU General Public License
   *	as published by the Free Software Foundation; either version
   *	2 of the License, or (at your option) any later version.
   *
   */
ee9c88f22   Joe Perches   af_econet: Use cu...
11
  #define pr_fmt(fmt) fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
  #include <linux/module.h>
  
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  #include <linux/string.h>
  #include <linux/mm.h>
  #include <linux/socket.h>
  #include <linux/sockios.h>
  #include <linux/in.h>
  #include <linux/errno.h>
  #include <linux/interrupt.h>
  #include <linux/if_ether.h>
  #include <linux/netdevice.h>
  #include <linux/inetdevice.h>
  #include <linux/route.h>
  #include <linux/inet.h>
  #include <linux/etherdevice.h>
  #include <linux/if_arp.h>
  #include <linux/wireless.h>
  #include <linux/skbuff.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
32
  #include <linux/udp.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
33
  #include <linux/slab.h>
a27e13d37   Phil Blundell   econet: fix CVE-2...
34
  #include <linux/vmalloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
38
39
40
41
42
43
44
  #include <net/sock.h>
  #include <net/inet_common.h>
  #include <linux/stat.h>
  #include <linux/init.h>
  #include <linux/if_ec.h>
  #include <net/udp.h>
  #include <net/ip.h>
  #include <linux/spinlock.h>
  #include <linux/rcupdate.h>
  #include <linux/bitops.h>
1d1818316   David S. Miller   [ECONET]: Convert...
45
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46

ee9c88f22   Joe Perches   af_econet: Use cu...
47
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  #include <asm/system.h>
90ddc4f04   Eric Dumazet   [NET]: move struc...
49
  static const struct proto_ops econet_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
  static struct hlist_head econet_sklist;
0c78a92fb   Eric Dumazet   econet: fix locking
51
  static DEFINE_SPINLOCK(econet_lock);
1d1818316   David S. Miller   [ECONET]: Convert...
52
  static DEFINE_MUTEX(econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
57
58
59
60
  
  /* Since there are only 256 possible network numbers (or fewer, depends
     how you count) it makes sense to use a simple lookup table. */
  static struct net_device *net2dev_map[256];
  
  #define EC_PORT_IP	0xd2
  
  #ifdef CONFIG_ECONET_AUNUDP
ca4033024   YOSHIFUJI Hideaki   [ECONET]: Use mac...
61
  static DEFINE_SPINLOCK(aun_queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
  static struct socket *udpsock;
  #define AUN_PORT	0x8000
ee9c88f22   Joe Perches   af_econet: Use cu...
64
  struct aunhdr {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  	unsigned char code;		/* AUN magic protocol byte */
  	unsigned char port;
  	unsigned char cb;
  	unsigned char pad;
  	unsigned long handle;
  };
  
  static unsigned long aun_seq;
  
  /* Queue of packets waiting to be transmitted. */
  static struct sk_buff_head aun_queue;
  static struct timer_list ab_cleanup_timer;
  
  #endif		/* CONFIG_ECONET_AUNUDP */
  
  /* Per-packet information */
ee9c88f22   Joe Perches   af_econet: Use cu...
81
  struct ec_cb {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  	struct sockaddr_ec sec;
  	unsigned long cookie;		/* Supplied by user. */
  #ifdef CONFIG_ECONET_AUNUDP
  	int done;
  	unsigned long seq;		/* Sequencing */
  	unsigned long timeout;		/* Timeout */
  	unsigned long start;		/* jiffies */
  #endif
  #ifdef CONFIG_ECONET_NATIVE
  	void (*sent)(struct sk_buff *, int result);
  #endif
  };
  
  static void econet_remove_socket(struct hlist_head *list, struct sock *sk)
  {
0c78a92fb   Eric Dumazet   econet: fix locking
97
  	spin_lock_bh(&econet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  	sk_del_node_init(sk);
0c78a92fb   Eric Dumazet   econet: fix locking
99
  	spin_unlock_bh(&econet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
  }
  
  static void econet_insert_socket(struct hlist_head *list, struct sock *sk)
  {
0c78a92fb   Eric Dumazet   econet: fix locking
104
  	spin_lock_bh(&econet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
  	sk_add_node(sk, list);
0c78a92fb   Eric Dumazet   econet: fix locking
106
  	spin_unlock_bh(&econet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  }
  
  /*
   *	Pull a packet from our receive queue and hand it to the user.
   *	If necessary we block.
   */
  
  static int econet_recvmsg(struct kiocb *iocb, struct socket *sock,
  			  struct msghdr *msg, size_t len, int flags)
  {
  	struct sock *sk = sock->sk;
  	struct sk_buff *skb;
  	size_t copied;
  	int err;
  
  	msg->msg_namelen = sizeof(struct sockaddr_ec);
1d1818316   David S. Miller   [ECONET]: Convert...
123
  	mutex_lock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
131
  	/*
  	 *	Call the generic datagram receiver. This handles all sorts
  	 *	of horrible races and re-entrancy so we can forget about it
  	 *	in the protocol layers.
  	 *
  	 *	Now it will return ENETDOWN, if device have just gone down,
  	 *	but then it will block.
  	 */
ee9c88f22   Joe Perches   af_econet: Use cu...
132
  	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
  
  	/*
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
135
  	 *	An error occurred so return it. Because skb_recv_datagram()
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
  	 *	handles the blocking we don't see and worry about blocking
  	 *	retries.
  	 */
ee9c88f22   Joe Perches   af_econet: Use cu...
139
  	if (skb == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
145
146
147
  		goto out;
  
  	/*
  	 *	You lose any data beyond the buffer you gave. If it worries a
  	 *	user program they can ask the device for its MTU anyway.
  	 */
  
  	copied = skb->len;
ee9c88f22   Joe Perches   af_econet: Use cu...
148
149
150
  	if (copied > len) {
  		copied = len;
  		msg->msg_flags |= MSG_TRUNC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
156
  	}
  
  	/* We can't use skb_copy_datagram here */
  	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
  	if (err)
  		goto out_free;
b7aa0bf70   Eric Dumazet   [NET]: convert ne...
157
  	sk->sk_stamp = skb->tstamp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
161
162
163
164
165
166
167
168
169
170
  
  	if (msg->msg_name)
  		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
  
  	/*
  	 *	Free or return the buffer as appropriate. Again this
  	 *	hides all the races and re-entrancy issues from us.
  	 */
  	err = copied;
  
  out_free:
  	skb_free_datagram(sk, skb);
  out:
1d1818316   David S. Miller   [ECONET]: Convert...
171
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
175
176
177
  	return err;
  }
  
  /*
   *	Bind an Econet socket.
   */
ee9c88f22   Joe Perches   af_econet: Use cu...
178
179
  static int econet_bind(struct socket *sock, struct sockaddr *uaddr,
  		       int addr_len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
  {
  	struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;
1d1818316   David S. Miller   [ECONET]: Convert...
182
183
  	struct sock *sk;
  	struct econet_sock *eo;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
184

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
  	/*
  	 *	Check legality
  	 */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
188

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
  	if (addr_len < sizeof(struct sockaddr_ec) ||
  	    sec->sec_family != AF_ECONET)
  		return -EINVAL;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
192

1d1818316   David S. Miller   [ECONET]: Convert...
193
194
195
196
  	mutex_lock(&econet_mutex);
  
  	sk = sock->sk;
  	eo = ec_sk(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
  	eo->cb	    = sec->cb;
  	eo->port    = sec->port;
  	eo->station = sec->addr.station;
  	eo->net	    = sec->addr.net;
1d1818316   David S. Miller   [ECONET]: Convert...
201
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
207
208
209
210
211
212
213
214
  	return 0;
  }
  
  #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)
  /*
   *	Queue a transmit result for the user to be told about.
   */
  
  static void tx_result(struct sock *sk, unsigned long cookie, int result)
  {
  	struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC);
  	struct ec_cb *eb;
  	struct sockaddr_ec *sec;
ee9c88f22   Joe Perches   af_econet: Use cu...
215
216
217
  	if (skb == NULL) {
  		pr_debug("econet: memory squeeze, transmit result dropped
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  		return;
  	}
  
  	eb = (struct ec_cb *)&skb->cb;
  	sec = (struct sockaddr_ec *)&eb->sec;
  	memset(sec, 0, sizeof(struct sockaddr_ec));
  	sec->cookie = cookie;
  	sec->type = ECTYPE_TRANSMIT_STATUS | result;
  	sec->sec_family = AF_ECONET;
  
  	if (sock_queue_rcv_skb(sk, skb) < 0)
  		kfree_skb(skb);
  }
  #endif
  
  #ifdef CONFIG_ECONET_NATIVE
  /*
   *	Called by the Econet hardware driver when a packet transmit
   *	has completed.  Tell the user.
   */
  
  static void ec_tx_done(struct sk_buff *skb, int result)
  {
  	struct ec_cb *eb = (struct ec_cb *)&skb->cb;
  	tx_result(skb->sk, eb->cookie, result);
  }
  #endif
  
  /*
   *	Send a packet.  We have to work out which device it's going out on
   *	and hence whether to use real Econet or the UDP emulation.
   */
  
  static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
  			  struct msghdr *msg, size_t len)
  {
ee9c88f22   Joe Perches   af_econet: Use cu...
254
  	struct sockaddr_ec *saddr = (struct sockaddr_ec *)msg->msg_name;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
259
  	struct net_device *dev;
  	struct ec_addr addr;
  	int err;
  	unsigned char port, cb;
  #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)
389f2a18c   Eric Dumazet   econet: remove co...
260
  	struct sock *sk = sock->sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
265
  	struct sk_buff *skb;
  	struct ec_cb *eb;
  #endif
  #ifdef CONFIG_ECONET_AUNUDP
  	struct msghdr udpmsg;
a27e13d37   Phil Blundell   econet: fix CVE-2...
266
  	struct iovec iov[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
269
  	struct aunhdr ah;
  	struct sockaddr_in udpdest;
  	__kernel_size_t size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
  	mm_segment_t oldfs;
a27e13d37   Phil Blundell   econet: fix CVE-2...
271
  	char *userbuf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  #endif
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
273

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  	/*
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
275
  	 *	Check the flags.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
  	 */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
277
  	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
  		return -EINVAL;
  
  	/*
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
281
  	 *	Get and verify the address.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  	 */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
283

1d1818316   David S. Miller   [ECONET]: Convert...
284
  	mutex_lock(&econet_mutex);
ee9c88f22   Joe Perches   af_econet: Use cu...
285
286
287
288
289
290
291
292
  	if (saddr == NULL || msg->msg_namelen < sizeof(struct sockaddr_ec)) {
  		mutex_unlock(&econet_mutex);
  		return -EINVAL;
  	}
  	addr.station = saddr->addr.station;
  	addr.net = saddr->addr.net;
  	port = saddr->port;
  	cb = saddr->cb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
  
  	/* Look for a device with the right network number. */
  	dev = net2dev_map[addr.net];
  
  	/* If not directly reachable, use some default */
1d1818316   David S. Miller   [ECONET]: Convert...
298
  	if (dev == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
  		dev = net2dev_map[0];
  		/* No interfaces at all? */
1d1818316   David S. Miller   [ECONET]: Convert...
301
302
  		if (dev == NULL) {
  			mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
  			return -ENETDOWN;
1d1818316   David S. Miller   [ECONET]: Convert...
304
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  	}
1d1818316   David S. Miller   [ECONET]: Convert...
306
  	if (dev->type == ARPHRD_ECONET) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
  		/* Real hardware Econet.  We're not worthy etc. */
  #ifdef CONFIG_ECONET_NATIVE
  		unsigned short proto = 0;
ae641949d   Herbert Xu   net: Remove all u...
310
  		int hlen, tlen;
0c4e85813   Stephen Hemminger   [NET]: Wrap netde...
311
  		int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312

a27e13d37   Phil Blundell   econet: fix CVE-2...
313
314
315
316
  		if (len + 15 > dev->mtu) {
  			mutex_unlock(&econet_mutex);
  			return -EMSGSIZE;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
  		dev_hold(dev);
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
318

ae641949d   Herbert Xu   net: Remove all u...
319
320
321
  		hlen = LL_RESERVED_SPACE(dev);
  		tlen = dev->needed_tailroom;
  		skb = sock_alloc_send_skb(sk, len + hlen + tlen,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
  					  msg->msg_flags & MSG_DONTWAIT, &err);
ee9c88f22   Joe Perches   af_econet: Use cu...
323
  		if (skb == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
  			goto out_unlock;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
325

ae641949d   Herbert Xu   net: Remove all u...
326
  		skb_reserve(skb, hlen);
c1d2bbe1c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
327
  		skb_reset_network_header(skb);
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
328

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
  		eb = (struct ec_cb *)&skb->cb;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
330

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
  		eb->cookie = saddr->cookie;
  		eb->sec = *saddr;
  		eb->sent = ec_tx_done;
0c4e85813   Stephen Hemminger   [NET]: Wrap netde...
334
335
336
337
338
  		err = -EINVAL;
  		res = dev_hard_header(skb, dev, ntohs(proto), &addr, NULL, len);
  		if (res < 0)
  			goto out_free;
  		if (res > 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  			struct ec_framehdr *fh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
  			/* Poke in our control byte and
  			   port number.  Hack, hack.  */
ee9c88f22   Joe Perches   af_econet: Use cu...
342
  			fh = (struct ec_framehdr *)skb->data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
  			fh->cb = cb;
  			fh->port = port;
  			if (sock->type != SOCK_DGRAM) {
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
346
  				skb_reset_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
  				skb->len = 0;
0c4e85813   Stephen Hemminger   [NET]: Wrap netde...
348
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  		}
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
350

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  		/* Copy the data. Returns -EFAULT on error */
ee9c88f22   Joe Perches   af_econet: Use cu...
352
  		err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
  		skb->protocol = proto;
  		skb->dev = dev;
  		skb->priority = sk->sk_priority;
  		if (err)
  			goto out_free;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
358

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
  		err = -ENETDOWN;
  		if (!(dev->flags & IFF_UP))
  			goto out_free;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
362

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
  		/*
  		 *	Now send it
  		 */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
366

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
  		dev_queue_xmit(skb);
  		dev_put(dev);
1d1818316   David S. Miller   [ECONET]: Convert...
369
  		mutex_unlock(&econet_mutex);
a02cec215   Eric Dumazet   net: return opera...
370
  		return len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371

ee9c88f22   Joe Perches   af_econet: Use cu...
372
  out_free:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
  		kfree_skb(skb);
ee9c88f22   Joe Perches   af_econet: Use cu...
374
  out_unlock:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
  		if (dev)
  			dev_put(dev);
  #else
  		err = -EPROTOTYPE;
  #endif
1d1818316   David S. Miller   [ECONET]: Convert...
380
  		mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
  		return err;
  	}
  
  #ifdef CONFIG_ECONET_AUNUDP
  	/* AUN virtual Econet. */
1d1818316   David S. Miller   [ECONET]: Convert...
386
387
  	if (udpsock == NULL) {
  		mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
  		return -ENETDOWN;		/* No socket - can't send */
1d1818316   David S. Miller   [ECONET]: Convert...
389
  	}
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
390

a27e13d37   Phil Blundell   econet: fix CVE-2...
391
392
393
394
  	if (len > 32768) {
  		err = -E2BIG;
  		goto error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
396
397
398
399
400
401
402
403
404
405
406
407
408
  	/* Make up a UDP datagram and hand it off to some higher intellect. */
  
  	memset(&udpdest, 0, sizeof(udpdest));
  	udpdest.sin_family = AF_INET;
  	udpdest.sin_port = htons(AUN_PORT);
  
  	/* At the moment we use the stupid Acorn scheme of Econet address
  	   y.x maps to IP a.b.c.x.  This should be replaced with something
  	   more flexible and more aware of subnet masks.  */
  	{
  		struct in_device *idev;
  		unsigned long network = 0;
  
  		rcu_read_lock();
e5ed63991   Herbert Xu   [IPV4]: Replace _...
409
  		idev = __in_dev_get_rcu(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
  		if (idev) {
  			if (idev->ifa_list)
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
412
  				network = ntohl(idev->ifa_list->ifa_address) &
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
  					0xffffff00;		/* !!! */
  		}
  		rcu_read_unlock();
  		udpdest.sin_addr.s_addr = htonl(network | addr.station);
  	}
67c5c6cb8   Vasiliy Kulikov   econet: 4 byte in...
418
  	memset(&ah, 0, sizeof(ah));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
421
  	ah.port = port;
  	ah.cb = cb & 0x7f;
  	ah.code = 2;		/* magic */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
  
  	/* tack our header on the front of the iovec */
  	size = sizeof(struct aunhdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
  	iov[0].iov_base = (void *)&ah;
  	iov[0].iov_len = size;
a27e13d37   Phil Blundell   econet: fix CVE-2...
427
428
429
430
431
  
  	userbuf = vmalloc(len);
  	if (userbuf == NULL) {
  		err = -ENOMEM;
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  	}
a27e13d37   Phil Blundell   econet: fix CVE-2...
433
434
435
436
437
  	iov[1].iov_base = userbuf;
  	iov[1].iov_len = len;
  	err = memcpy_fromiovec(userbuf, msg->msg_iov, len);
  	if (err)
  		goto error_free_buf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
  	/* Get a skbuff (no data, just holds our cb information) */
ee9c88f22   Joe Perches   af_econet: Use cu...
439
440
  	skb = sock_alloc_send_skb(sk, 0, msg->msg_flags & MSG_DONTWAIT, &err);
  	if (skb == NULL)
a27e13d37   Phil Blundell   econet: fix CVE-2...
441
  		goto error_free_buf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
444
445
  
  	eb = (struct ec_cb *)&skb->cb;
  
  	eb->cookie = saddr->cookie;
ee9c88f22   Joe Perches   af_econet: Use cu...
446
  	eb->timeout = 5 * HZ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
450
451
452
453
454
455
456
  	eb->start = jiffies;
  	ah.handle = aun_seq;
  	eb->seq = (aun_seq++);
  	eb->sec = *saddr;
  
  	skb_queue_tail(&aun_queue, skb);
  
  	udpmsg.msg_name = (void *)&udpdest;
  	udpmsg.msg_namelen = sizeof(udpdest);
  	udpmsg.msg_iov = &iov[0];
a27e13d37   Phil Blundell   econet: fix CVE-2...
457
  	udpmsg.msg_iovlen = 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
  	udpmsg.msg_control = NULL;
  	udpmsg.msg_controllen = 0;
ee9c88f22   Joe Perches   af_econet: Use cu...
460
  	udpmsg.msg_flags = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461

ee9c88f22   Joe Perches   af_econet: Use cu...
462
463
  	oldfs = get_fs();
  	set_fs(KERNEL_DS);		/* More privs :-) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
  	err = sock_sendmsg(udpsock, &udpmsg, size);
  	set_fs(oldfs);
a27e13d37   Phil Blundell   econet: fix CVE-2...
466
467
468
  
  error_free_buf:
  	vfree(userbuf);
389f2a18c   Eric Dumazet   econet: remove co...
469
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
471
472
  #else
  	err = -EPROTOTYPE;
  #endif
1d1818316   David S. Miller   [ECONET]: Convert...
473
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
476
477
478
479
480
481
482
483
  	return err;
  }
  
  /*
   *	Look up the address of a socket.
   */
  
  static int econet_getname(struct socket *sock, struct sockaddr *uaddr,
  			  int *uaddr_len, int peer)
  {
1d1818316   David S. Miller   [ECONET]: Convert...
484
485
  	struct sock *sk;
  	struct econet_sock *eo;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
487
488
489
  	struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;
  
  	if (peer)
  		return -EOPNOTSUPP;
80922bbb1   Eric Dumazet   econet: Fix econe...
490
  	memset(sec, 0, sizeof(*sec));
1d1818316   David S. Miller   [ECONET]: Convert...
491
492
493
494
  	mutex_lock(&econet_mutex);
  
  	sk = sock->sk;
  	eo = ec_sk(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
  	sec->sec_family	  = AF_ECONET;
  	sec->port	  = eo->port;
  	sec->addr.station = eo->station;
  	sec->addr.net	  = eo->net;
1d1818316   David S. Miller   [ECONET]: Convert...
499
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
503
504
505
  	*uaddr_len = sizeof(*sec);
  	return 0;
  }
  
  static void econet_destroy_timer(unsigned long data)
  {
ee9c88f22   Joe Perches   af_econet: Use cu...
506
  	struct sock *sk = (struct sock *)data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507

c564039fd   Eric Dumazet   net: sk_wmem_allo...
508
  	if (!sk_has_allocations(sk)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
511
512
513
514
  		sk_free(sk);
  		return;
  	}
  
  	sk->sk_timer.expires = jiffies + 10 * HZ;
  	add_timer(&sk->sk_timer);
ee9c88f22   Joe Perches   af_econet: Use cu...
515
516
  	pr_debug("econet: socket destroy delayed
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
518
519
520
521
522
523
524
  }
  
  /*
   *	Close an econet socket.
   */
  
  static int econet_release(struct socket *sock)
  {
1d1818316   David S. Miller   [ECONET]: Convert...
525
  	struct sock *sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526

1d1818316   David S. Miller   [ECONET]: Convert...
527
528
529
  	mutex_lock(&econet_mutex);
  
  	sk = sock->sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
  	if (!sk)
1d1818316   David S. Miller   [ECONET]: Convert...
531
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
534
535
536
537
538
539
  
  	econet_remove_socket(&econet_sklist, sk);
  
  	/*
  	 *	Now the socket is dead. No more input will appear.
  	 */
  
  	sk->sk_state_change(sk);	/* It is useless. Just for sanity. */
0efffaf9d   David S. Miller   econet: Use sock_...
540
  	sock_orphan(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
542
543
544
  
  	/* Purge queues */
  
  	skb_queue_purge(&sk->sk_receive_queue);
c564039fd   Eric Dumazet   net: sk_wmem_allo...
545
  	if (sk_has_allocations(sk)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
549
  		sk->sk_timer.data     = (unsigned long)sk;
  		sk->sk_timer.expires  = jiffies + HZ;
  		sk->sk_timer.function = econet_destroy_timer;
  		add_timer(&sk->sk_timer);
1d1818316   David S. Miller   [ECONET]: Convert...
550
551
  
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
  	}
  
  	sk_free(sk);
1d1818316   David S. Miller   [ECONET]: Convert...
555
556
557
  
  out_unlock:
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
562
563
564
565
566
567
568
569
  	return 0;
  }
  
  static struct proto econet_proto = {
  	.name	  = "ECONET",
  	.owner	  = THIS_MODULE,
  	.obj_size = sizeof(struct econet_sock),
  };
  
  /*
   *	Create an Econet socket
   */
3f378b684   Eric Paris   net: pass kern to...
570
571
  static int econet_create(struct net *net, struct socket *sock, int protocol,
  			 int kern)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
573
574
575
  {
  	struct sock *sk;
  	struct econet_sock *eo;
  	int err;
09ad9bc75   Octavian Purdila   net: use net_eq t...
576
  	if (!net_eq(net, &init_net))
1b8d7ae42   Eric W. Biederman   [NET]: Make socke...
577
  		return -EAFNOSUPPORT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
580
581
582
583
584
  	/* Econet only provides datagram services. */
  	if (sock->type != SOCK_DGRAM)
  		return -ESOCKTNOSUPPORT;
  
  	sock->state = SS_UNCONNECTED;
  
  	err = -ENOBUFS;
6257ff217   Pavel Emelyanov   [NET]: Forget the...
585
  	sk = sk_alloc(net, PF_ECONET, GFP_KERNEL, &econet_proto);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
588
589
590
591
592
593
594
595
596
597
598
  	if (sk == NULL)
  		goto out;
  
  	sk->sk_reuse = 1;
  	sock->ops = &econet_ops;
  	sock_init_data(sock, sk);
  
  	eo = ec_sk(sk);
  	sock_reset_flag(sk, SOCK_ZAPPED);
  	sk->sk_family = PF_ECONET;
  	eo->num = protocol;
  
  	econet_insert_socket(&econet_sklist, sk);
a02cec215   Eric Dumazet   net: return opera...
599
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
602
603
604
605
606
607
608
609
610
611
612
613
  out:
  	return err;
  }
  
  /*
   *	Handle Econet specific ioctls
   */
  
  static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg)
  {
  	struct ifreq ifr;
  	struct ec_device *edev;
  	struct net_device *dev;
  	struct sockaddr_ec *sec;
1d1818316   David S. Miller   [ECONET]: Convert...
614
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
617
618
619
620
621
  
  	/*
  	 *	Fetch the caller's info block into kernel space
  	 */
  
  	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
  		return -EFAULT;
ee9c88f22   Joe Perches   af_econet: Use cu...
622
623
  	dev = dev_get_by_name(&init_net, ifr.ifr_name);
  	if (dev == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
626
  		return -ENODEV;
  
  	sec = (struct sockaddr_ec *)&ifr.ifr_addr;
1d1818316   David S. Miller   [ECONET]: Convert...
627
628
629
630
  	mutex_lock(&econet_mutex);
  
  	err = 0;
  	switch (cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
  	case SIOCSIFADDR:
0c62fc6dd   Nelson Elhage   econet: Do the co...
632
633
634
635
  		if (!capable(CAP_NET_ADMIN)) {
  			err = -EPERM;
  			break;
  		}
16c41745c   Phil Blundell   econet: fix CVE-2...
636

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
  		edev = dev->ec_ptr;
1d1818316   David S. Miller   [ECONET]: Convert...
638
  		if (edev == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  			/* Magic up a new one. */
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
640
  			edev = kzalloc(sizeof(struct ec_device), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
  			if (edev == NULL) {
1d1818316   David S. Miller   [ECONET]: Convert...
642
643
  				err = -ENOMEM;
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
  			dev->ec_ptr = edev;
1d1818316   David S. Miller   [ECONET]: Convert...
646
  		} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
649
650
651
652
  			net2dev_map[edev->net] = NULL;
  		edev->station = sec->addr.station;
  		edev->net = sec->addr.net;
  		net2dev_map[sec->addr.net] = dev;
  		if (!net2dev_map[0])
  			net2dev_map[0] = dev;
1d1818316   David S. Miller   [ECONET]: Convert...
653
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
655
656
  
  	case SIOCGIFADDR:
  		edev = dev->ec_ptr;
1d1818316   David S. Miller   [ECONET]: Convert...
657
658
659
  		if (edev == NULL) {
  			err = -ENODEV;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
662
663
664
665
666
  		}
  		memset(sec, 0, sizeof(struct sockaddr_ec));
  		sec->addr.station = edev->station;
  		sec->addr.net = edev->net;
  		sec->sec_family = AF_ECONET;
  		dev_put(dev);
  		if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
1d1818316   David S. Miller   [ECONET]: Convert...
667
668
669
670
671
672
  			err = -EFAULT;
  		break;
  
  	default:
  		err = -EINVAL;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673
  	}
1d1818316   David S. Miller   [ECONET]: Convert...
674
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
  	dev_put(dev);
1d1818316   David S. Miller   [ECONET]: Convert...
676
677
  
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
680
681
682
  }
  
  /*
   *	Handle generic ioctls
   */
ee9c88f22   Joe Perches   af_econet: Use cu...
683
684
  static int econet_ioctl(struct socket *sock, unsigned int cmd,
  			unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
687
  {
  	struct sock *sk = sock->sk;
  	void __user *argp = (void __user *)arg;
075e1913e   Joe Perches   econet: Reduce sw...
688
689
690
  	switch (cmd) {
  	case SIOCGSTAMP:
  		return sock_get_timestamp(sk, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691

075e1913e   Joe Perches   econet: Reduce sw...
692
693
  	case SIOCGSTAMPNS:
  		return sock_get_timestampns(sk, argp);
ae40eb1ef   Eric Dumazet   [NET]: Introduce ...
694

075e1913e   Joe Perches   econet: Reduce sw...
695
696
697
  	case SIOCSIFADDR:
  	case SIOCGIFADDR:
  		return ec_dev_ioctl(sock, cmd, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
  	}
075e1913e   Joe Perches   econet: Reduce sw...
700
701
  
  	return -ENOIOCTLCMD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
  }
ec1b4cf74   Stephen Hemminger   net: mark net_pro...
703
  static const struct net_proto_family econet_family_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
706
707
  	.family =	PF_ECONET,
  	.create =	econet_create,
  	.owner	=	THIS_MODULE,
  };
1d1818316   David S. Miller   [ECONET]: Convert...
708
  static const struct proto_ops econet_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
711
712
713
714
715
  	.family =	PF_ECONET,
  	.owner =	THIS_MODULE,
  	.release =	econet_release,
  	.bind =		econet_bind,
  	.connect =	sock_no_connect,
  	.socketpair =	sock_no_socketpair,
  	.accept =	sock_no_accept,
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
716
  	.getname =	econet_getname,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
720
721
722
723
724
725
726
727
  	.poll =		datagram_poll,
  	.ioctl =	econet_ioctl,
  	.listen =	sock_no_listen,
  	.shutdown =	sock_no_shutdown,
  	.setsockopt =	sock_no_setsockopt,
  	.getsockopt =	sock_no_getsockopt,
  	.sendmsg =	econet_sendmsg,
  	.recvmsg =	econet_recvmsg,
  	.mmap =		sock_no_mmap,
  	.sendpage =	sock_no_sendpage,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
731
732
733
734
735
736
737
  #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)
  /*
   *	Find the listening socket, if any, for the given data.
   */
  
  static struct sock *ec_listening_socket(unsigned char port, unsigned char
  				 station, unsigned char net)
  {
  	struct sock *sk;
  	struct hlist_node *node;
0c78a92fb   Eric Dumazet   econet: fix locking
738
  	spin_lock(&econet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
740
  	sk_for_each(sk, node, &econet_sklist) {
  		struct econet_sock *opt = ec_sk(sk);
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
741
  		if ((opt->port == port || opt->port == 0) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
  		    (opt->station == station || opt->station == 0) &&
0c78a92fb   Eric Dumazet   econet: fix locking
743
744
  		    (opt->net == net || opt->net == 0)) {
  			sock_hold(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
  			goto found;
0c78a92fb   Eric Dumazet   econet: fix locking
746
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
  	}
  	sk = NULL;
  found:
0c78a92fb   Eric Dumazet   econet: fix locking
750
  	spin_unlock(&econet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
  	return sk;
  }
  
  /*
   *	Queue a received packet for a socket.
   */
  
  static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
  			   unsigned char stn, unsigned char net,
  			   unsigned char cb, unsigned char port)
  {
  	struct ec_cb *eb = (struct ec_cb *)&skb->cb;
  	struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
  
  	memset(sec, 0, sizeof(struct sockaddr_ec));
  	sec->sec_family = AF_ECONET;
  	sec->type = ECTYPE_PACKET_RECEIVED;
  	sec->port = port;
  	sec->cb = cb;
  	sec->addr.net = net;
  	sec->addr.station = stn;
  
  	return sock_queue_rcv_skb(sk, skb);
  }
  #endif
  
  #ifdef CONFIG_ECONET_AUNUDP
  /*
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
779
   *	Send an AUN protocol response.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
780
781
782
783
784
785
786
787
788
789
790
791
   */
  
  static void aun_send_response(__u32 addr, unsigned long seq, int code, int cb)
  {
  	struct sockaddr_in sin = {
  		.sin_family = AF_INET,
  		.sin_port = htons(AUN_PORT),
  		.sin_addr = {.s_addr = addr}
  	};
  	struct aunhdr ah = {.code = code, .cb = cb, .handle = seq};
  	struct kvec iov = {.iov_base = (void *)&ah, .iov_len = sizeof(ah)};
  	struct msghdr udpmsg;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
792

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
794
795
796
  	udpmsg.msg_name = (void *)&sin;
  	udpmsg.msg_namelen = sizeof(sin);
  	udpmsg.msg_control = NULL;
  	udpmsg.msg_controllen = 0;
ee9c88f22   Joe Perches   af_econet: Use cu...
797
  	udpmsg.msg_flags = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
799
800
801
802
803
804
805
806
807
808
809
  
  	kernel_sendmsg(udpsock, &udpmsg, &iov, 1, sizeof(ah));
  }
  
  
  /*
   *	Handle incoming AUN packets.  Work out if anybody wants them,
   *	and send positive or negative acknowledgements as appropriate.
   */
  
  static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
  {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
810
  	struct iphdr *ip = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
  	unsigned char stn = ntohl(ip->saddr) & 0xff;
4e085e76c   David S. Miller   econet: Fix crash...
812
813
  	struct dst_entry *dst = skb_dst(skb);
  	struct ec_device *edev = NULL;
0c78a92fb   Eric Dumazet   econet: fix locking
814
  	struct sock *sk = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
815
  	struct sk_buff *newskb;
4e085e76c   David S. Miller   econet: Fix crash...
816
817
818
  
  	if (dst)
  		edev = dst->dev->ec_ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819

ee9c88f22   Joe Perches   af_econet: Use cu...
820
  	if (!edev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
  		goto bad;
ee9c88f22   Joe Perches   af_econet: Use cu...
822
823
  	sk = ec_listening_socket(ah->port, stn, edev->net);
  	if (sk == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
  		goto bad;		/* Nobody wants it */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
825
  	newskb = alloc_skb((len - sizeof(struct aunhdr) + 15) & ~15,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
  			   GFP_ATOMIC);
ee9c88f22   Joe Perches   af_econet: Use cu...
827
828
829
  	if (newskb == NULL) {
  		pr_debug("AUN: memory squeeze, dropping packet
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
831
832
  		/* Send nack and hope sender tries again */
  		goto bad;
  	}
ee9c88f22   Joe Perches   af_econet: Use cu...
833
  	memcpy(skb_put(newskb, len - sizeof(struct aunhdr)), (void *)(ah + 1),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
  	       len - sizeof(struct aunhdr));
ee9c88f22   Joe Perches   af_econet: Use cu...
835
  	if (ec_queue_packet(sk, newskb, stn, edev->net, ah->cb, ah->port)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
836
837
838
839
840
841
  		/* Socket is bankrupt. */
  		kfree_skb(newskb);
  		goto bad;
  	}
  
  	aun_send_response(ip->saddr, ah->handle, 3, 0);
0c78a92fb   Eric Dumazet   econet: fix locking
842
  	sock_put(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
844
845
846
  	return;
  
  bad:
  	aun_send_response(ip->saddr, ah->handle, 4, 0);
0c78a92fb   Eric Dumazet   econet: fix locking
847
848
  	if (sk)
  		sock_put(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
  }
  
  /*
   *	Handle incoming AUN transmit acknowledgements.  If the sequence
   *      number matches something in our backlog then kill it and tell
   *	the user.  If the remote took too long to reply then we may have
   *	dropped the packet already.
   */
  
  static void aun_tx_ack(unsigned long seq, int result)
  {
  	struct sk_buff *skb;
  	unsigned long flags;
  	struct ec_cb *eb;
  
  	spin_lock_irqsave(&aun_queue_lock, flags);
de1033428   David S. Miller   econet: Use SKB q...
865
  	skb_queue_walk(&aun_queue, skb) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
868
  		eb = (struct ec_cb *)&skb->cb;
  		if (eb->seq == seq)
  			goto foundit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
  	}
  	spin_unlock_irqrestore(&aun_queue_lock, flags);
ee9c88f22   Joe Perches   af_econet: Use cu...
871
872
  	pr_debug("AUN: unknown sequence %ld
  ", seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
875
876
  	return;
  
  foundit:
  	tx_result(skb->sk, eb->cookie, result);
8728b834b   David S. Miller   [NET]: Kill skb->...
877
  	skb_unlink(skb, &aun_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
  	spin_unlock_irqrestore(&aun_queue_lock, flags);
  	kfree_skb(skb);
  }
  
  /*
   *	Deal with received AUN frames - sort out what type of thing it is
   *	and hand it to the right function.
   */
  
  static void aun_data_available(struct sock *sk, int slen)
  {
  	int err;
  	struct sk_buff *skb;
  	unsigned char *data;
  	struct aunhdr *ah;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
  	size_t len;
  
  	while ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL) {
  		if (err == -EAGAIN) {
ee9c88f22   Joe Perches   af_econet: Use cu...
897
898
  			pr_err("AUN: no data available?!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
900
  			return;
  		}
ee9c88f22   Joe Perches   af_econet: Use cu...
901
902
  		pr_debug("AUN: recvfrom() error %d
  ", -err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903
  	}
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
904
  	data = skb_transport_header(skb) + sizeof(struct udphdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905
906
  	ah = (struct aunhdr *)data;
  	len = skb->len - sizeof(struct udphdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907

ee9c88f22   Joe Perches   af_econet: Use cu...
908
  	switch (ah->code) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
909
910
911
912
913
914
915
916
917
  	case 2:
  		aun_incoming(skb, ah, len);
  		break;
  	case 3:
  		aun_tx_ack(ah->handle, ECTYPE_TRANSMIT_OK);
  		break;
  	case 4:
  		aun_tx_ack(ah->handle, ECTYPE_TRANSMIT_NOT_LISTENING);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
  	default:
ee9c88f22   Joe Perches   af_econet: Use cu...
919
920
  		pr_debug("AUN: unknown packet type: %d
  ", data[0]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
922
923
924
925
926
927
928
929
930
931
932
933
934
  	}
  
  	skb_free_datagram(sk, skb);
  }
  
  /*
   *	Called by the timer to manage the AUN transmit queue.  If a packet
   *	was sent to a dead or nonexistent host then we will never get an
   *	acknowledgement back.  After a few seconds we need to spot this and
   *	drop the packet.
   */
  
  static void ab_cleanup(unsigned long h)
  {
de1033428   David S. Miller   econet: Use SKB q...
935
  	struct sk_buff *skb, *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
936
937
938
  	unsigned long flags;
  
  	spin_lock_irqsave(&aun_queue_lock, flags);
de1033428   David S. Miller   econet: Use SKB q...
939
  	skb_queue_walk_safe(&aun_queue, skb, n) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
940
  		struct ec_cb *eb = (struct ec_cb *)&skb->cb;
de1033428   David S. Miller   econet: Use SKB q...
941
  		if ((jiffies - eb->start) > eb->timeout) {
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
942
  			tx_result(skb->sk, eb->cookie,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
  				  ECTYPE_TRANSMIT_NOT_PRESENT);
8728b834b   David S. Miller   [NET]: Kill skb->...
944
  			skb_unlink(skb, &aun_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
946
  			kfree_skb(skb);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
948
  	}
  	spin_unlock_irqrestore(&aun_queue_lock, flags);
ee9c88f22   Joe Perches   af_econet: Use cu...
949
  	mod_timer(&ab_cleanup_timer, jiffies + (HZ * 2));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950
951
952
953
954
955
956
957
  }
  
  static int __init aun_udp_initialise(void)
  {
  	int error;
  	struct sockaddr_in sin;
  
  	skb_queue_head_init(&aun_queue);
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
958
  	setup_timer(&ab_cleanup_timer, ab_cleanup, 0);
ee9c88f22   Joe Perches   af_econet: Use cu...
959
  	ab_cleanup_timer.expires = jiffies + (HZ * 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
961
962
963
964
965
966
  	add_timer(&ab_cleanup_timer);
  
  	memset(&sin, 0, sizeof(sin));
  	sin.sin_port = htons(AUN_PORT);
  
  	/* We can count ourselves lucky Acorn machines are too dim to
  	   speak IPv6. :-) */
ee9c88f22   Joe Perches   af_econet: Use cu...
967
968
969
970
  	error = sock_create_kern(PF_INET, SOCK_DGRAM, 0, &udpsock);
  	if (error < 0) {
  		pr_err("AUN: socket error %d
  ", -error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
972
  		return error;
  	}
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
973

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
974
975
976
  	udpsock->sk->sk_reuse = 1;
  	udpsock->sk->sk_allocation = GFP_ATOMIC; /* we're going to call it
  						    from interrupts */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
977

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978
  	error = udpsock->ops->bind(udpsock, (struct sockaddr *)&sin,
ee9c88f22   Joe Perches   af_econet: Use cu...
979
980
981
982
  				   sizeof(sin));
  	if (error < 0) {
  		pr_err("AUN: bind error %d
  ", -error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
  		goto release;
  	}
  
  	udpsock->sk->sk_data_ready = aun_data_available;
  
  	return 0;
  
  release:
  	sock_release(udpsock);
  	udpsock = NULL;
  	return error;
  }
  #endif
  
  #ifdef CONFIG_ECONET_NATIVE
  
  /*
   *	Receive an Econet frame from a device.
   */
ee9c88f22   Joe Perches   af_econet: Use cu...
1002
1003
  static int econet_rcv(struct sk_buff *skb, struct net_device *dev,
  		      struct packet_type *pt, struct net_device *orig_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004
1005
  {
  	struct ec_framehdr *hdr;
0c78a92fb   Eric Dumazet   econet: fix locking
1006
  	struct sock *sk = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1007
  	struct ec_device *edev = dev->ec_ptr;
721499e89   YOSHIFUJI Hideaki   netns: Use net_eq...
1008
  	if (!net_eq(dev_net(dev), &init_net))
e730c1551   Eric W. Biederman   [NET]: Make packe...
1009
  		goto drop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1010
1011
1012
1013
1014
  	if (skb->pkt_type == PACKET_OTHERHOST)
  		goto drop;
  
  	if (!edev)
  		goto drop;
ee9c88f22   Joe Perches   af_econet: Use cu...
1015
1016
  	skb = skb_share_check(skb, GFP_ATOMIC);
  	if (skb == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1017
1018
1019
1020
  		return NET_RX_DROP;
  
  	if (!pskb_may_pull(skb, sizeof(struct ec_framehdr)))
  		goto drop;
ee9c88f22   Joe Perches   af_econet: Use cu...
1021
  	hdr = (struct ec_framehdr *)skb->data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
1023
1024
1025
1026
1027
  
  	/* First check for encapsulated IP */
  	if (hdr->port == EC_PORT_IP) {
  		skb->protocol = htons(ETH_P_IP);
  		skb_pull(skb, sizeof(struct ec_framehdr));
  		netif_rx(skb);
482d804cb   Mark Smith   econet: use NET_R...
1028
  		return NET_RX_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
1030
1031
1032
1033
1034
1035
1036
1037
  	}
  
  	sk = ec_listening_socket(hdr->port, hdr->src_stn, hdr->src_net);
  	if (!sk)
  		goto drop;
  
  	if (ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb,
  			    hdr->port))
  		goto drop;
0c78a92fb   Eric Dumazet   econet: fix locking
1038
  	sock_put(sk);
482d804cb   Mark Smith   econet: use NET_R...
1039
  	return NET_RX_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1040
1041
  
  drop:
0c78a92fb   Eric Dumazet   econet: fix locking
1042
1043
  	if (sk)
  		sock_put(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1044
1045
1046
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
7546dd97d   Stephen Hemminger   net: convert usag...
1047
  static struct packet_type econet_packet_type __read_mostly = {
ee9c88f22   Joe Perches   af_econet: Use cu...
1048
1049
  	.type =	cpu_to_be16(ETH_P_ECONET),
  	.func =	econet_rcv,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1050
1051
1052
1053
1054
1055
1056
1057
  };
  
  static void econet_hw_initialise(void)
  {
  	dev_add_pack(&econet_packet_type);
  }
  
  #endif
ee9c88f22   Joe Perches   af_econet: Use cu...
1058
1059
  static int econet_notifier(struct notifier_block *this, unsigned long msg,
  			   void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
  {
ee9c88f22   Joe Perches   af_econet: Use cu...
1061
  	struct net_device *dev = data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
  	struct ec_device *edev;
721499e89   YOSHIFUJI Hideaki   netns: Use net_eq...
1063
  	if (!net_eq(dev_net(dev), &init_net))
e9dc86534   Eric W. Biederman   [NET]: Make devic...
1064
  		return NOTIFY_DONE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
1066
1067
1068
  	switch (msg) {
  	case NETDEV_UNREGISTER:
  		/* A device has gone down - kill any data we hold for it. */
  		edev = dev->ec_ptr;
ee9c88f22   Joe Perches   af_econet: Use cu...
1069
  		if (edev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
  			if (net2dev_map[0] == dev)
  				net2dev_map[0] = NULL;
  			net2dev_map[edev->net] = NULL;
  			kfree(edev);
  			dev->ec_ptr = NULL;
  		}
  		break;
  	}
  
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block econet_netdev_notifier = {
ee9c88f22   Joe Perches   af_econet: Use cu...
1083
  	.notifier_call = econet_notifier,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
  };
  
  static void __exit econet_proto_exit(void)
  {
  #ifdef CONFIG_ECONET_AUNUDP
  	del_timer(&ab_cleanup_timer);
  	if (udpsock)
  		sock_release(udpsock);
  #endif
  	unregister_netdevice_notifier(&econet_netdev_notifier);
9c29a377f   Alexey Dobriyan   [ECONET]: remove ...
1094
1095
1096
  #ifdef CONFIG_ECONET_NATIVE
  	dev_remove_pack(&econet_packet_type);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
  	sock_unregister(econet_family_ops.family);
  	proto_unregister(&econet_proto);
  }
  
  static int __init econet_proto_init(void)
  {
  	int err = proto_register(&econet_proto, 0);
  
  	if (err != 0)
  		goto out;
  	sock_register(&econet_family_ops);
  #ifdef CONFIG_ECONET_AUNUDP
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
  	aun_udp_initialise();
  #endif
  #ifdef CONFIG_ECONET_NATIVE
  	econet_hw_initialise();
  #endif
  	register_netdevice_notifier(&econet_netdev_notifier);
  out:
  	return err;
  }
  
  module_init(econet_proto_init);
  module_exit(econet_proto_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_NETPROTO(PF_ECONET);