diff options
Diffstat (limited to 'net/decnet')
-rw-r--r-- | net/decnet/Kconfig | 27 | ||||
-rw-r--r-- | net/decnet/Makefile | 10 | ||||
-rw-r--r-- | net/decnet/README | 8 | ||||
-rw-r--r-- | net/decnet/TODO | 41 | ||||
-rw-r--r-- | net/decnet/af_decnet.c | 2405 | ||||
-rw-r--r-- | net/decnet/dn_dev.c | 1481 | ||||
-rw-r--r-- | net/decnet/dn_fib.c | 802 | ||||
-rw-r--r-- | net/decnet/dn_neigh.c | 627 | ||||
-rw-r--r-- | net/decnet/dn_nsp_in.c | 934 | ||||
-rw-r--r-- | net/decnet/dn_nsp_out.c | 782 | ||||
-rw-r--r-- | net/decnet/dn_route.c | 1840 | ||||
-rw-r--r-- | net/decnet/dn_rules.c | 416 | ||||
-rw-r--r-- | net/decnet/dn_table.c | 825 | ||||
-rw-r--r-- | net/decnet/dn_timer.c | 109 | ||||
-rw-r--r-- | net/decnet/netfilter/Kconfig | 15 | ||||
-rw-r--r-- | net/decnet/netfilter/Makefile | 6 | ||||
-rw-r--r-- | net/decnet/netfilter/dn_rtmsg.c | 167 | ||||
-rw-r--r-- | net/decnet/sysctl_net_decnet.c | 480 |
18 files changed, 10975 insertions, 0 deletions
diff --git a/net/decnet/Kconfig b/net/decnet/Kconfig new file mode 100644 index 000000000000..2101da542ba8 --- /dev/null +++ b/net/decnet/Kconfig | |||
@@ -0,0 +1,27 @@ | |||
1 | # | ||
2 | # DECnet configuration | ||
3 | # | ||
4 | config DECNET_ROUTER | ||
5 | bool "DECnet: router support (EXPERIMENTAL)" | ||
6 | depends on DECNET && EXPERIMENTAL | ||
7 | ---help--- | ||
8 | Add support for turning your DECnet Endnode into a level 1 or 2 | ||
9 | router. This is an experimental, but functional option. If you | ||
10 | do say Y here, then make sure that you also say Y to "Kernel/User | ||
11 | network link driver", "Routing messages" and "Network packet | ||
12 | filtering". The first two are required to allow configuration via | ||
13 | rtnetlink (you will need Alexey Kuznetsov's iproute2 package | ||
14 | from <ftp://ftp.tux.org/pub/net/ip-routing/>). The "Network packet | ||
15 | filtering" option will be required for the forthcoming routing daemon | ||
16 | to work. | ||
17 | |||
18 | See <file:Documentation/networking/decnet.txt> for more information. | ||
19 | |||
20 | config DECNET_ROUTE_FWMARK | ||
21 | bool "DECnet: use FWMARK value as routing key (EXPERIMENTAL)" | ||
22 | depends on DECNET_ROUTER && NETFILTER | ||
23 | help | ||
24 | If you say Y here, you will be able to specify different routes for | ||
25 | packets with different FWMARK ("firewalling mark") values | ||
26 | (see ipchains(8), "-m" argument). | ||
27 | |||
diff --git a/net/decnet/Makefile b/net/decnet/Makefile new file mode 100644 index 000000000000..e44003af71f6 --- /dev/null +++ b/net/decnet/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | |||
2 | obj-$(CONFIG_DECNET) += decnet.o | ||
3 | |||
4 | decnet-y := af_decnet.o dn_nsp_in.o dn_nsp_out.o \ | ||
5 | dn_route.o dn_dev.o dn_neigh.o dn_timer.o | ||
6 | decnet-$(CONFIG_DECNET_ROUTER) += dn_fib.o dn_rules.o dn_table.o | ||
7 | decnet-y += sysctl_net_decnet.o | ||
8 | |||
9 | obj-$(CONFIG_NETFILTER) += netfilter/ | ||
10 | |||
diff --git a/net/decnet/README b/net/decnet/README new file mode 100644 index 000000000000..60e7ec88c81f --- /dev/null +++ b/net/decnet/README | |||
@@ -0,0 +1,8 @@ | |||
1 | Linux DECnet Project | ||
2 | ====================== | ||
3 | |||
4 | The documentation for this kernel subsystem is available in the | ||
5 | Documentation/networking subdirectory of this distribution and also | ||
6 | on line at http://www.chygwyn.com/DECnet/ | ||
7 | |||
8 | Steve Whitehouse <SteveW@ACM.org> | ||
diff --git a/net/decnet/TODO b/net/decnet/TODO new file mode 100644 index 000000000000..ebb5ac69d128 --- /dev/null +++ b/net/decnet/TODO | |||
@@ -0,0 +1,41 @@ | |||
1 | Steve's quick list of things that need finishing off: | ||
2 | [they are in no particular order and range from the trivial to the long winded] | ||
3 | |||
4 | o Proper timeouts on each neighbour (in routing mode) rather than | ||
5 | just the 60 second On-Ethernet cache value. | ||
6 | |||
7 | o Support for X.25 linklayer | ||
8 | |||
9 | o Support for DDCMP link layer | ||
10 | |||
11 | o The DDCMP device itself | ||
12 | |||
13 | o PPP support (rfc1762) | ||
14 | |||
15 | o Lots of testing with real applications | ||
16 | |||
17 | o Verify errors etc. against POSIX 1003.1g (draft) | ||
18 | |||
19 | o Using send/recvmsg() to get at connect/disconnect data (POSIX 1003.1g) | ||
20 | [maybe this should be done at socket level... the control data in the | ||
21 | send/recvmsg() calls should simply be a vector of set/getsockopt() | ||
22 | calls] | ||
23 | |||
24 | o check MSG_CTRUNC is set where it should be. | ||
25 | |||
26 | o Find all the commonality between DECnet and IPv4 routing code and extract | ||
27 | it into a small library of routines. [probably a project for 2.7.xx] | ||
28 | |||
29 | o Add perfect socket hashing - an idea suggested by Paul Koning. Currently | ||
30 | we have a half-way house scheme which seems to work reasonably well, but | ||
31 | the full scheme is still worth implementing, its not not top of my list | ||
32 | right now. | ||
33 | |||
34 | o Add session control message flow control | ||
35 | |||
36 | o Add NSP message flow control | ||
37 | |||
38 | o DECnet sendpages() function | ||
39 | |||
40 | o AIO for DECnet | ||
41 | |||
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c new file mode 100644 index 000000000000..29bb3cd21965 --- /dev/null +++ b/net/decnet/af_decnet.c | |||
@@ -0,0 +1,2405 @@ | |||
1 | |||
2 | /* | ||
3 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
4 | * operating system. DECnet is implemented using the BSD Socket | ||
5 | * interface as the means of communication with the user level. | ||
6 | * | ||
7 | * DECnet Socket Layer Interface | ||
8 | * | ||
9 | * Authors: Eduardo Marcelo Serrat <emserrat@geocities.com> | ||
10 | * Patrick Caulfield <patrick@pandh.demon.co.uk> | ||
11 | * | ||
12 | * Changes: | ||
13 | * Steve Whitehouse: Copied from Eduardo Serrat and Patrick Caulfield's | ||
14 | * version of the code. Original copyright preserved | ||
15 | * below. | ||
16 | * Steve Whitehouse: Some bug fixes, cleaning up some code to make it | ||
17 | * compatible with my routing layer. | ||
18 | * Steve Whitehouse: Merging changes from Eduardo Serrat and Patrick | ||
19 | * Caulfield. | ||
20 | * Steve Whitehouse: Further bug fixes, checking module code still works | ||
21 | * with new routing layer. | ||
22 | * Steve Whitehouse: Additional set/get_sockopt() calls. | ||
23 | * Steve Whitehouse: Fixed TIOCINQ ioctl to be same as Eduardo's new | ||
24 | * code. | ||
25 | * Steve Whitehouse: recvmsg() changed to try and behave in a POSIX like | ||
26 | * way. Didn't manage it entirely, but its better. | ||
27 | * Steve Whitehouse: ditto for sendmsg(). | ||
28 | * Steve Whitehouse: A selection of bug fixes to various things. | ||
29 | * Steve Whitehouse: Added TIOCOUTQ ioctl. | ||
30 | * Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username. | ||
31 | * Steve Whitehouse: Fixes to connect() error returns. | ||
32 | * Patrick Caulfield: Fixes to delayed acceptance logic. | ||
33 | * David S. Miller: New socket locking | ||
34 | * Steve Whitehouse: Socket list hashing/locking | ||
35 | * Arnaldo C. Melo: use capable, not suser | ||
36 | * Steve Whitehouse: Removed unused code. Fix to use sk->allocation | ||
37 | * when required. | ||
38 | * Patrick Caulfield: /proc/net/decnet now has object name/number | ||
39 | * Steve Whitehouse: Fixed local port allocation, hashed sk list | ||
40 | * Matthew Wilcox: Fixes for dn_ioctl() | ||
41 | * Steve Whitehouse: New connect/accept logic to allow timeouts and | ||
42 | * prepare for sendpage etc. | ||
43 | */ | ||
44 | |||
45 | |||
46 | /****************************************************************************** | ||
47 | (c) 1995-1998 E.M. Serrat emserrat@geocities.com | ||
48 | |||
49 | This program is free software; you can redistribute it and/or modify | ||
50 | it under the terms of the GNU General Public License as published by | ||
51 | the Free Software Foundation; either version 2 of the License, or | ||
52 | any later version. | ||
53 | |||
54 | This program is distributed in the hope that it will be useful, | ||
55 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
56 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
57 | GNU General Public License for more details. | ||
58 | |||
59 | HISTORY: | ||
60 | |||
61 | Version Kernel Date Author/Comments | ||
62 | ------- ------ ---- --------------- | ||
63 | Version 0.0.1 2.0.30 01-dic-97 Eduardo Marcelo Serrat | ||
64 | (emserrat@geocities.com) | ||
65 | |||
66 | First Development of DECnet Socket La- | ||
67 | yer for Linux. Only supports outgoing | ||
68 | connections. | ||
69 | |||
70 | Version 0.0.2 2.1.105 20-jun-98 Patrick J. Caulfield | ||
71 | (patrick@pandh.demon.co.uk) | ||
72 | |||
73 | Port to new kernel development version. | ||
74 | |||
75 | Version 0.0.3 2.1.106 25-jun-98 Eduardo Marcelo Serrat | ||
76 | (emserrat@geocities.com) | ||
77 | _ | ||
78 | Added support for incoming connections | ||
79 | so we can start developing server apps | ||
80 | on Linux. | ||
81 | - | ||
82 | Module Support | ||
83 | Version 0.0.4 2.1.109 21-jul-98 Eduardo Marcelo Serrat | ||
84 | (emserrat@geocities.com) | ||
85 | _ | ||
86 | Added support for X11R6.4. Now we can | ||
87 | use DECnet transport for X on Linux!!! | ||
88 | - | ||
89 | Version 0.0.5 2.1.110 01-aug-98 Eduardo Marcelo Serrat | ||
90 | (emserrat@geocities.com) | ||
91 | Removed bugs on flow control | ||
92 | Removed bugs on incoming accessdata | ||
93 | order | ||
94 | - | ||
95 | Version 0.0.6 2.1.110 07-aug-98 Eduardo Marcelo Serrat | ||
96 | dn_recvmsg fixes | ||
97 | |||
98 | Patrick J. Caulfield | ||
99 | dn_bind fixes | ||
100 | *******************************************************************************/ | ||
101 | |||
102 | #include <linux/config.h> | ||
103 | #include <linux/module.h> | ||
104 | #include <linux/errno.h> | ||
105 | #include <linux/types.h> | ||
106 | #include <linux/slab.h> | ||
107 | #include <linux/socket.h> | ||
108 | #include <linux/in.h> | ||
109 | #include <linux/kernel.h> | ||
110 | #include <linux/sched.h> | ||
111 | #include <linux/timer.h> | ||
112 | #include <linux/string.h> | ||
113 | #include <linux/sockios.h> | ||
114 | #include <linux/net.h> | ||
115 | #include <linux/netdevice.h> | ||
116 | #include <linux/inet.h> | ||
117 | #include <linux/route.h> | ||
118 | #include <linux/netfilter.h> | ||
119 | #include <linux/seq_file.h> | ||
120 | #include <net/sock.h> | ||
121 | #include <net/tcp.h> | ||
122 | #include <net/flow.h> | ||
123 | #include <asm/system.h> | ||
124 | #include <asm/ioctls.h> | ||
125 | #include <linux/mm.h> | ||
126 | #include <linux/interrupt.h> | ||
127 | #include <linux/proc_fs.h> | ||
128 | #include <linux/stat.h> | ||
129 | #include <linux/init.h> | ||
130 | #include <linux/poll.h> | ||
131 | #include <net/neighbour.h> | ||
132 | #include <net/dst.h> | ||
133 | #include <net/dn.h> | ||
134 | #include <net/dn_nsp.h> | ||
135 | #include <net/dn_dev.h> | ||
136 | #include <net/dn_route.h> | ||
137 | #include <net/dn_fib.h> | ||
138 | #include <net/dn_neigh.h> | ||
139 | |||
140 | struct dn_sock { | ||
141 | struct sock sk; | ||
142 | struct dn_scp scp; | ||
143 | }; | ||
144 | |||
145 | static void dn_keepalive(struct sock *sk); | ||
146 | |||
147 | #define DN_SK_HASH_SHIFT 8 | ||
148 | #define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT) | ||
149 | #define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1) | ||
150 | |||
151 | |||
152 | static struct proto_ops dn_proto_ops; | ||
153 | static DEFINE_RWLOCK(dn_hash_lock); | ||
154 | static struct hlist_head dn_sk_hash[DN_SK_HASH_SIZE]; | ||
155 | static struct hlist_head dn_wild_sk; | ||
156 | |||
157 | static int __dn_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen, int flags); | ||
158 | static int __dn_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen, int flags); | ||
159 | |||
160 | static struct hlist_head *dn_find_list(struct sock *sk) | ||
161 | { | ||
162 | struct dn_scp *scp = DN_SK(sk); | ||
163 | |||
164 | if (scp->addr.sdn_flags & SDF_WILD) | ||
165 | return hlist_empty(&dn_wild_sk) ? &dn_wild_sk : NULL; | ||
166 | |||
167 | return &dn_sk_hash[scp->addrloc & DN_SK_HASH_MASK]; | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * Valid ports are those greater than zero and not already in use. | ||
172 | */ | ||
173 | static int check_port(unsigned short port) | ||
174 | { | ||
175 | struct sock *sk; | ||
176 | struct hlist_node *node; | ||
177 | |||
178 | if (port == 0) | ||
179 | return -1; | ||
180 | |||
181 | sk_for_each(sk, node, &dn_sk_hash[port & DN_SK_HASH_MASK]) { | ||
182 | struct dn_scp *scp = DN_SK(sk); | ||
183 | if (scp->addrloc == port) | ||
184 | return -1; | ||
185 | } | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static unsigned short port_alloc(struct sock *sk) | ||
190 | { | ||
191 | struct dn_scp *scp = DN_SK(sk); | ||
192 | static unsigned short port = 0x2000; | ||
193 | unsigned short i_port = port; | ||
194 | |||
195 | while(check_port(++port) != 0) { | ||
196 | if (port == i_port) | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | scp->addrloc = port; | ||
201 | |||
202 | return 1; | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * Since this is only ever called from user | ||
207 | * level, we don't need a write_lock() version | ||
208 | * of this. | ||
209 | */ | ||
210 | static int dn_hash_sock(struct sock *sk) | ||
211 | { | ||
212 | struct dn_scp *scp = DN_SK(sk); | ||
213 | struct hlist_head *list; | ||
214 | int rv = -EUSERS; | ||
215 | |||
216 | BUG_ON(sk_hashed(sk)); | ||
217 | |||
218 | write_lock_bh(&dn_hash_lock); | ||
219 | |||
220 | if (!scp->addrloc && !port_alloc(sk)) | ||
221 | goto out; | ||
222 | |||
223 | rv = -EADDRINUSE; | ||
224 | if ((list = dn_find_list(sk)) == NULL) | ||
225 | goto out; | ||
226 | |||
227 | sk_add_node(sk, list); | ||
228 | rv = 0; | ||
229 | out: | ||
230 | write_unlock_bh(&dn_hash_lock); | ||
231 | return rv; | ||
232 | } | ||
233 | |||
234 | static void dn_unhash_sock(struct sock *sk) | ||
235 | { | ||
236 | write_lock(&dn_hash_lock); | ||
237 | sk_del_node_init(sk); | ||
238 | write_unlock(&dn_hash_lock); | ||
239 | } | ||
240 | |||
241 | static void dn_unhash_sock_bh(struct sock *sk) | ||
242 | { | ||
243 | write_lock_bh(&dn_hash_lock); | ||
244 | sk_del_node_init(sk); | ||
245 | write_unlock_bh(&dn_hash_lock); | ||
246 | } | ||
247 | |||
248 | static struct hlist_head *listen_hash(struct sockaddr_dn *addr) | ||
249 | { | ||
250 | int i; | ||
251 | unsigned hash = addr->sdn_objnum; | ||
252 | |||
253 | if (hash == 0) { | ||
254 | hash = addr->sdn_objnamel; | ||
255 | for(i = 0; i < dn_ntohs(addr->sdn_objnamel); i++) { | ||
256 | hash ^= addr->sdn_objname[i]; | ||
257 | hash ^= (hash << 3); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | return &dn_sk_hash[hash & DN_SK_HASH_MASK]; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Called to transform a socket from bound (i.e. with a local address) | ||
266 | * into a listening socket (doesn't need a local port number) and rehashes | ||
267 | * based upon the object name/number. | ||
268 | */ | ||
269 | static void dn_rehash_sock(struct sock *sk) | ||
270 | { | ||
271 | struct hlist_head *list; | ||
272 | struct dn_scp *scp = DN_SK(sk); | ||
273 | |||
274 | if (scp->addr.sdn_flags & SDF_WILD) | ||
275 | return; | ||
276 | |||
277 | write_lock_bh(&dn_hash_lock); | ||
278 | sk_del_node_init(sk); | ||
279 | DN_SK(sk)->addrloc = 0; | ||
280 | list = listen_hash(&DN_SK(sk)->addr); | ||
281 | sk_add_node(sk, list); | ||
282 | write_unlock_bh(&dn_hash_lock); | ||
283 | } | ||
284 | |||
285 | int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type) | ||
286 | { | ||
287 | int len = 2; | ||
288 | |||
289 | *buf++ = type; | ||
290 | |||
291 | switch(type) { | ||
292 | case 0: | ||
293 | *buf++ = sdn->sdn_objnum; | ||
294 | break; | ||
295 | case 1: | ||
296 | *buf++ = 0; | ||
297 | *buf++ = dn_ntohs(sdn->sdn_objnamel); | ||
298 | memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel)); | ||
299 | len = 3 + dn_ntohs(sdn->sdn_objnamel); | ||
300 | break; | ||
301 | case 2: | ||
302 | memset(buf, 0, 5); | ||
303 | buf += 5; | ||
304 | *buf++ = dn_ntohs(sdn->sdn_objnamel); | ||
305 | memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel)); | ||
306 | len = 7 + dn_ntohs(sdn->sdn_objnamel); | ||
307 | break; | ||
308 | } | ||
309 | |||
310 | return len; | ||
311 | } | ||
312 | |||
313 | /* | ||
314 | * On reception of usernames, we handle types 1 and 0 for destination | ||
315 | * addresses only. Types 2 and 4 are used for source addresses, but the | ||
316 | * UIC, GIC are ignored and they are both treated the same way. Type 3 | ||
317 | * is never used as I've no idea what its purpose might be or what its | ||
318 | * format is. | ||
319 | */ | ||
320 | int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, unsigned char *fmt) | ||
321 | { | ||
322 | unsigned char type; | ||
323 | int size = len; | ||
324 | int namel = 12; | ||
325 | |||
326 | sdn->sdn_objnum = 0; | ||
327 | sdn->sdn_objnamel = dn_htons(0); | ||
328 | memset(sdn->sdn_objname, 0, DN_MAXOBJL); | ||
329 | |||
330 | if (len < 2) | ||
331 | return -1; | ||
332 | |||
333 | len -= 2; | ||
334 | *fmt = *data++; | ||
335 | type = *data++; | ||
336 | |||
337 | switch(*fmt) { | ||
338 | case 0: | ||
339 | sdn->sdn_objnum = type; | ||
340 | return 2; | ||
341 | case 1: | ||
342 | namel = 16; | ||
343 | break; | ||
344 | case 2: | ||
345 | len -= 4; | ||
346 | data += 4; | ||
347 | break; | ||
348 | case 4: | ||
349 | len -= 8; | ||
350 | data += 8; | ||
351 | break; | ||
352 | default: | ||
353 | return -1; | ||
354 | } | ||
355 | |||
356 | len -= 1; | ||
357 | |||
358 | if (len < 0) | ||
359 | return -1; | ||
360 | |||
361 | sdn->sdn_objnamel = dn_htons(*data++); | ||
362 | len -= dn_ntohs(sdn->sdn_objnamel); | ||
363 | |||
364 | if ((len < 0) || (dn_ntohs(sdn->sdn_objnamel) > namel)) | ||
365 | return -1; | ||
366 | |||
367 | memcpy(sdn->sdn_objname, data, dn_ntohs(sdn->sdn_objnamel)); | ||
368 | |||
369 | return size - len; | ||
370 | } | ||
371 | |||
372 | struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr) | ||
373 | { | ||
374 | struct hlist_head *list = listen_hash(addr); | ||
375 | struct hlist_node *node; | ||
376 | struct sock *sk; | ||
377 | |||
378 | read_lock(&dn_hash_lock); | ||
379 | sk_for_each(sk, node, list) { | ||
380 | struct dn_scp *scp = DN_SK(sk); | ||
381 | if (sk->sk_state != TCP_LISTEN) | ||
382 | continue; | ||
383 | if (scp->addr.sdn_objnum) { | ||
384 | if (scp->addr.sdn_objnum != addr->sdn_objnum) | ||
385 | continue; | ||
386 | } else { | ||
387 | if (addr->sdn_objnum) | ||
388 | continue; | ||
389 | if (scp->addr.sdn_objnamel != addr->sdn_objnamel) | ||
390 | continue; | ||
391 | if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, dn_ntohs(addr->sdn_objnamel)) != 0) | ||
392 | continue; | ||
393 | } | ||
394 | sock_hold(sk); | ||
395 | read_unlock(&dn_hash_lock); | ||
396 | return sk; | ||
397 | } | ||
398 | |||
399 | sk = sk_head(&dn_wild_sk); | ||
400 | if (sk) { | ||
401 | if (sk->sk_state == TCP_LISTEN) | ||
402 | sock_hold(sk); | ||
403 | else | ||
404 | sk = NULL; | ||
405 | } | ||
406 | |||
407 | read_unlock(&dn_hash_lock); | ||
408 | return sk; | ||
409 | } | ||
410 | |||
411 | struct sock *dn_find_by_skb(struct sk_buff *skb) | ||
412 | { | ||
413 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
414 | struct sock *sk; | ||
415 | struct hlist_node *node; | ||
416 | struct dn_scp *scp; | ||
417 | |||
418 | read_lock(&dn_hash_lock); | ||
419 | sk_for_each(sk, node, &dn_sk_hash[cb->dst_port & DN_SK_HASH_MASK]) { | ||
420 | scp = DN_SK(sk); | ||
421 | if (cb->src != dn_saddr2dn(&scp->peer)) | ||
422 | continue; | ||
423 | if (cb->dst_port != scp->addrloc) | ||
424 | continue; | ||
425 | if (scp->addrrem && (cb->src_port != scp->addrrem)) | ||
426 | continue; | ||
427 | sock_hold(sk); | ||
428 | goto found; | ||
429 | } | ||
430 | sk = NULL; | ||
431 | found: | ||
432 | read_unlock(&dn_hash_lock); | ||
433 | return sk; | ||
434 | } | ||
435 | |||
436 | |||
437 | |||
438 | static void dn_destruct(struct sock *sk) | ||
439 | { | ||
440 | struct dn_scp *scp = DN_SK(sk); | ||
441 | |||
442 | skb_queue_purge(&scp->data_xmit_queue); | ||
443 | skb_queue_purge(&scp->other_xmit_queue); | ||
444 | skb_queue_purge(&scp->other_receive_queue); | ||
445 | |||
446 | dst_release(xchg(&sk->sk_dst_cache, NULL)); | ||
447 | } | ||
448 | |||
449 | static struct proto dn_proto = { | ||
450 | .name = "DECNET", | ||
451 | .owner = THIS_MODULE, | ||
452 | .obj_size = sizeof(struct dn_sock), | ||
453 | }; | ||
454 | |||
455 | static struct sock *dn_alloc_sock(struct socket *sock, int gfp) | ||
456 | { | ||
457 | struct dn_scp *scp; | ||
458 | struct sock *sk = sk_alloc(PF_DECnet, gfp, &dn_proto, 1); | ||
459 | |||
460 | if (!sk) | ||
461 | goto out; | ||
462 | |||
463 | if (sock) | ||
464 | sock->ops = &dn_proto_ops; | ||
465 | sock_init_data(sock, sk); | ||
466 | |||
467 | sk->sk_backlog_rcv = dn_nsp_backlog_rcv; | ||
468 | sk->sk_destruct = dn_destruct; | ||
469 | sk->sk_no_check = 1; | ||
470 | sk->sk_family = PF_DECnet; | ||
471 | sk->sk_protocol = 0; | ||
472 | sk->sk_allocation = gfp; | ||
473 | |||
474 | /* Initialization of DECnet Session Control Port */ | ||
475 | scp = DN_SK(sk); | ||
476 | scp->state = DN_O; /* Open */ | ||
477 | scp->numdat = 1; /* Next data seg to tx */ | ||
478 | scp->numoth = 1; /* Next oth data to tx */ | ||
479 | scp->ackxmt_dat = 0; /* Last data seg ack'ed */ | ||
480 | scp->ackxmt_oth = 0; /* Last oth data ack'ed */ | ||
481 | scp->ackrcv_dat = 0; /* Highest data ack recv*/ | ||
482 | scp->ackrcv_oth = 0; /* Last oth data ack rec*/ | ||
483 | scp->flowrem_sw = DN_SEND; | ||
484 | scp->flowloc_sw = DN_SEND; | ||
485 | scp->flowrem_dat = 0; | ||
486 | scp->flowrem_oth = 1; | ||
487 | scp->flowloc_dat = 0; | ||
488 | scp->flowloc_oth = 1; | ||
489 | scp->services_rem = 0; | ||
490 | scp->services_loc = 1 | NSP_FC_NONE; | ||
491 | scp->info_rem = 0; | ||
492 | scp->info_loc = 0x03; /* NSP version 4.1 */ | ||
493 | scp->segsize_rem = 230 - DN_MAX_NSP_DATA_HEADER; /* Default: Updated by remote segsize */ | ||
494 | scp->nonagle = 0; | ||
495 | scp->multi_ireq = 1; | ||
496 | scp->accept_mode = ACC_IMMED; | ||
497 | scp->addr.sdn_family = AF_DECnet; | ||
498 | scp->peer.sdn_family = AF_DECnet; | ||
499 | scp->accessdata.acc_accl = 5; | ||
500 | memcpy(scp->accessdata.acc_acc, "LINUX", 5); | ||
501 | |||
502 | scp->max_window = NSP_MAX_WINDOW; | ||
503 | scp->snd_window = NSP_MIN_WINDOW; | ||
504 | scp->nsp_srtt = NSP_INITIAL_SRTT; | ||
505 | scp->nsp_rttvar = NSP_INITIAL_RTTVAR; | ||
506 | scp->nsp_rxtshift = 0; | ||
507 | |||
508 | skb_queue_head_init(&scp->data_xmit_queue); | ||
509 | skb_queue_head_init(&scp->other_xmit_queue); | ||
510 | skb_queue_head_init(&scp->other_receive_queue); | ||
511 | |||
512 | scp->persist = 0; | ||
513 | scp->persist_fxn = NULL; | ||
514 | scp->keepalive = 10 * HZ; | ||
515 | scp->keepalive_fxn = dn_keepalive; | ||
516 | |||
517 | init_timer(&scp->delack_timer); | ||
518 | scp->delack_pending = 0; | ||
519 | scp->delack_fxn = dn_nsp_delayed_ack; | ||
520 | |||
521 | dn_start_slow_timer(sk); | ||
522 | out: | ||
523 | return sk; | ||
524 | } | ||
525 | |||
526 | /* | ||
527 | * Keepalive timer. | ||
528 | * FIXME: Should respond to SO_KEEPALIVE etc. | ||
529 | */ | ||
530 | static void dn_keepalive(struct sock *sk) | ||
531 | { | ||
532 | struct dn_scp *scp = DN_SK(sk); | ||
533 | |||
534 | /* | ||
535 | * By checking the other_data transmit queue is empty | ||
536 | * we are double checking that we are not sending too | ||
537 | * many of these keepalive frames. | ||
538 | */ | ||
539 | if (skb_queue_len(&scp->other_xmit_queue) == 0) | ||
540 | dn_nsp_send_link(sk, DN_NOCHANGE, 0); | ||
541 | } | ||
542 | |||
543 | |||
544 | /* | ||
545 | * Timer for shutdown/destroyed sockets. | ||
546 | * When socket is dead & no packets have been sent for a | ||
547 | * certain amount of time, they are removed by this | ||
548 | * routine. Also takes care of sending out DI & DC | ||
549 | * frames at correct times. | ||
550 | */ | ||
551 | int dn_destroy_timer(struct sock *sk) | ||
552 | { | ||
553 | struct dn_scp *scp = DN_SK(sk); | ||
554 | |||
555 | scp->persist = dn_nsp_persist(sk); | ||
556 | |||
557 | switch(scp->state) { | ||
558 | case DN_DI: | ||
559 | dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC); | ||
560 | if (scp->nsp_rxtshift >= decnet_di_count) | ||
561 | scp->state = DN_CN; | ||
562 | return 0; | ||
563 | |||
564 | case DN_DR: | ||
565 | dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC); | ||
566 | if (scp->nsp_rxtshift >= decnet_dr_count) | ||
567 | scp->state = DN_DRC; | ||
568 | return 0; | ||
569 | |||
570 | case DN_DN: | ||
571 | if (scp->nsp_rxtshift < decnet_dn_count) { | ||
572 | /* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */ | ||
573 | dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC); | ||
574 | return 0; | ||
575 | } | ||
576 | } | ||
577 | |||
578 | scp->persist = (HZ * decnet_time_wait); | ||
579 | |||
580 | if (sk->sk_socket) | ||
581 | return 0; | ||
582 | |||
583 | if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) { | ||
584 | dn_unhash_sock(sk); | ||
585 | sock_put(sk); | ||
586 | return 1; | ||
587 | } | ||
588 | |||
589 | return 0; | ||
590 | } | ||
591 | |||
592 | static void dn_destroy_sock(struct sock *sk) | ||
593 | { | ||
594 | struct dn_scp *scp = DN_SK(sk); | ||
595 | |||
596 | scp->nsp_rxtshift = 0; /* reset back off */ | ||
597 | |||
598 | if (sk->sk_socket) { | ||
599 | if (sk->sk_socket->state != SS_UNCONNECTED) | ||
600 | sk->sk_socket->state = SS_DISCONNECTING; | ||
601 | } | ||
602 | |||
603 | sk->sk_state = TCP_CLOSE; | ||
604 | |||
605 | switch(scp->state) { | ||
606 | case DN_DN: | ||
607 | dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, | ||
608 | sk->sk_allocation); | ||
609 | scp->persist_fxn = dn_destroy_timer; | ||
610 | scp->persist = dn_nsp_persist(sk); | ||
611 | break; | ||
612 | case DN_CR: | ||
613 | scp->state = DN_DR; | ||
614 | goto disc_reject; | ||
615 | case DN_RUN: | ||
616 | scp->state = DN_DI; | ||
617 | case DN_DI: | ||
618 | case DN_DR: | ||
619 | disc_reject: | ||
620 | dn_nsp_send_disc(sk, NSP_DISCINIT, 0, sk->sk_allocation); | ||
621 | case DN_NC: | ||
622 | case DN_NR: | ||
623 | case DN_RJ: | ||
624 | case DN_DIC: | ||
625 | case DN_CN: | ||
626 | case DN_DRC: | ||
627 | case DN_CI: | ||
628 | case DN_CD: | ||
629 | scp->persist_fxn = dn_destroy_timer; | ||
630 | scp->persist = dn_nsp_persist(sk); | ||
631 | break; | ||
632 | default: | ||
633 | printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n"); | ||
634 | case DN_O: | ||
635 | dn_stop_slow_timer(sk); | ||
636 | |||
637 | dn_unhash_sock_bh(sk); | ||
638 | sock_put(sk); | ||
639 | |||
640 | break; | ||
641 | } | ||
642 | } | ||
643 | |||
644 | char *dn_addr2asc(dn_address addr, char *buf) | ||
645 | { | ||
646 | unsigned short node, area; | ||
647 | |||
648 | node = addr & 0x03ff; | ||
649 | area = addr >> 10; | ||
650 | sprintf(buf, "%hd.%hd", area, node); | ||
651 | |||
652 | return buf; | ||
653 | } | ||
654 | |||
655 | |||
656 | |||
657 | static int dn_create(struct socket *sock, int protocol) | ||
658 | { | ||
659 | struct sock *sk; | ||
660 | |||
661 | switch(sock->type) { | ||
662 | case SOCK_SEQPACKET: | ||
663 | if (protocol != DNPROTO_NSP) | ||
664 | return -EPROTONOSUPPORT; | ||
665 | break; | ||
666 | case SOCK_STREAM: | ||
667 | break; | ||
668 | default: | ||
669 | return -ESOCKTNOSUPPORT; | ||
670 | } | ||
671 | |||
672 | |||
673 | if ((sk = dn_alloc_sock(sock, GFP_KERNEL)) == NULL) | ||
674 | return -ENOBUFS; | ||
675 | |||
676 | sk->sk_protocol = protocol; | ||
677 | |||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | |||
682 | static int | ||
683 | dn_release(struct socket *sock) | ||
684 | { | ||
685 | struct sock *sk = sock->sk; | ||
686 | |||
687 | if (sk) { | ||
688 | sock_orphan(sk); | ||
689 | sock_hold(sk); | ||
690 | lock_sock(sk); | ||
691 | dn_destroy_sock(sk); | ||
692 | release_sock(sk); | ||
693 | sock_put(sk); | ||
694 | } | ||
695 | |||
696 | return 0; | ||
697 | } | ||
698 | |||
699 | static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | ||
700 | { | ||
701 | struct sock *sk = sock->sk; | ||
702 | struct dn_scp *scp = DN_SK(sk); | ||
703 | struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr; | ||
704 | struct net_device *dev; | ||
705 | int rv; | ||
706 | |||
707 | if (addr_len != sizeof(struct sockaddr_dn)) | ||
708 | return -EINVAL; | ||
709 | |||
710 | if (saddr->sdn_family != AF_DECnet) | ||
711 | return -EINVAL; | ||
712 | |||
713 | if (dn_ntohs(saddr->sdn_nodeaddrl) && (dn_ntohs(saddr->sdn_nodeaddrl) != 2)) | ||
714 | return -EINVAL; | ||
715 | |||
716 | if (dn_ntohs(saddr->sdn_objnamel) > DN_MAXOBJL) | ||
717 | return -EINVAL; | ||
718 | |||
719 | if (saddr->sdn_flags & ~SDF_WILD) | ||
720 | return -EINVAL; | ||
721 | |||
722 | #if 1 | ||
723 | if (!capable(CAP_NET_BIND_SERVICE) && (saddr->sdn_objnum || | ||
724 | (saddr->sdn_flags & SDF_WILD))) | ||
725 | return -EACCES; | ||
726 | #else | ||
727 | /* | ||
728 | * Maybe put the default actions in the default security ops for | ||
729 | * dn_prot_sock ? Would be nice if the capable call would go there | ||
730 | * too. | ||
731 | */ | ||
732 | if (security_dn_prot_sock(saddr) && | ||
733 | !capable(CAP_NET_BIND_SERVICE) || | ||
734 | saddr->sdn_objnum || (saddr->sdn_flags & SDF_WILD)) | ||
735 | return -EACCES; | ||
736 | #endif | ||
737 | |||
738 | |||
739 | if (!(saddr->sdn_flags & SDF_WILD)) { | ||
740 | if (dn_ntohs(saddr->sdn_nodeaddrl)) { | ||
741 | read_lock(&dev_base_lock); | ||
742 | for(dev = dev_base; dev; dev = dev->next) { | ||
743 | if (!dev->dn_ptr) | ||
744 | continue; | ||
745 | if (dn_dev_islocal(dev, dn_saddr2dn(saddr))) | ||
746 | break; | ||
747 | } | ||
748 | read_unlock(&dev_base_lock); | ||
749 | if (dev == NULL) | ||
750 | return -EADDRNOTAVAIL; | ||
751 | } | ||
752 | } | ||
753 | |||
754 | rv = -EINVAL; | ||
755 | lock_sock(sk); | ||
756 | if (sock_flag(sk, SOCK_ZAPPED)) { | ||
757 | memcpy(&scp->addr, saddr, addr_len); | ||
758 | sock_reset_flag(sk, SOCK_ZAPPED); | ||
759 | |||
760 | rv = dn_hash_sock(sk); | ||
761 | if (rv) | ||
762 | sock_set_flag(sk, SOCK_ZAPPED); | ||
763 | } | ||
764 | release_sock(sk); | ||
765 | |||
766 | return rv; | ||
767 | } | ||
768 | |||
769 | |||
770 | static int dn_auto_bind(struct socket *sock) | ||
771 | { | ||
772 | struct sock *sk = sock->sk; | ||
773 | struct dn_scp *scp = DN_SK(sk); | ||
774 | int rv; | ||
775 | |||
776 | sock_reset_flag(sk, SOCK_ZAPPED); | ||
777 | |||
778 | scp->addr.sdn_flags = 0; | ||
779 | scp->addr.sdn_objnum = 0; | ||
780 | |||
781 | /* | ||
782 | * This stuff is to keep compatibility with Eduardo's | ||
783 | * patch. I hope I can dispense with it shortly... | ||
784 | */ | ||
785 | if ((scp->accessdata.acc_accl != 0) && | ||
786 | (scp->accessdata.acc_accl <= 12)) { | ||
787 | |||
788 | scp->addr.sdn_objnamel = dn_htons(scp->accessdata.acc_accl); | ||
789 | memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, dn_ntohs(scp->addr.sdn_objnamel)); | ||
790 | |||
791 | scp->accessdata.acc_accl = 0; | ||
792 | memset(scp->accessdata.acc_acc, 0, 40); | ||
793 | } | ||
794 | /* End of compatibility stuff */ | ||
795 | |||
796 | scp->addr.sdn_add.a_len = dn_htons(2); | ||
797 | rv = dn_dev_bind_default((dn_address *)scp->addr.sdn_add.a_addr); | ||
798 | if (rv == 0) { | ||
799 | rv = dn_hash_sock(sk); | ||
800 | if (rv) | ||
801 | sock_set_flag(sk, SOCK_ZAPPED); | ||
802 | } | ||
803 | |||
804 | return rv; | ||
805 | } | ||
806 | |||
807 | static int dn_confirm_accept(struct sock *sk, long *timeo, int allocation) | ||
808 | { | ||
809 | struct dn_scp *scp = DN_SK(sk); | ||
810 | DEFINE_WAIT(wait); | ||
811 | int err; | ||
812 | |||
813 | if (scp->state != DN_CR) | ||
814 | return -EINVAL; | ||
815 | |||
816 | scp->state = DN_CC; | ||
817 | scp->segsize_loc = dst_metric(__sk_dst_get(sk), RTAX_ADVMSS); | ||
818 | dn_send_conn_conf(sk, allocation); | ||
819 | |||
820 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
821 | for(;;) { | ||
822 | release_sock(sk); | ||
823 | if (scp->state == DN_CC) | ||
824 | *timeo = schedule_timeout(*timeo); | ||
825 | lock_sock(sk); | ||
826 | err = 0; | ||
827 | if (scp->state == DN_RUN) | ||
828 | break; | ||
829 | err = sock_error(sk); | ||
830 | if (err) | ||
831 | break; | ||
832 | err = sock_intr_errno(*timeo); | ||
833 | if (signal_pending(current)) | ||
834 | break; | ||
835 | err = -EAGAIN; | ||
836 | if (!*timeo) | ||
837 | break; | ||
838 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
839 | } | ||
840 | finish_wait(sk->sk_sleep, &wait); | ||
841 | if (err == 0) { | ||
842 | sk->sk_socket->state = SS_CONNECTED; | ||
843 | } else if (scp->state != DN_CC) { | ||
844 | sk->sk_socket->state = SS_UNCONNECTED; | ||
845 | } | ||
846 | return err; | ||
847 | } | ||
848 | |||
849 | static int dn_wait_run(struct sock *sk, long *timeo) | ||
850 | { | ||
851 | struct dn_scp *scp = DN_SK(sk); | ||
852 | DEFINE_WAIT(wait); | ||
853 | int err = 0; | ||
854 | |||
855 | if (scp->state == DN_RUN) | ||
856 | goto out; | ||
857 | |||
858 | if (!*timeo) | ||
859 | return -EALREADY; | ||
860 | |||
861 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
862 | for(;;) { | ||
863 | release_sock(sk); | ||
864 | if (scp->state == DN_CI || scp->state == DN_CC) | ||
865 | *timeo = schedule_timeout(*timeo); | ||
866 | lock_sock(sk); | ||
867 | err = 0; | ||
868 | if (scp->state == DN_RUN) | ||
869 | break; | ||
870 | err = sock_error(sk); | ||
871 | if (err) | ||
872 | break; | ||
873 | err = sock_intr_errno(*timeo); | ||
874 | if (signal_pending(current)) | ||
875 | break; | ||
876 | err = -ETIMEDOUT; | ||
877 | if (!*timeo) | ||
878 | break; | ||
879 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
880 | } | ||
881 | finish_wait(sk->sk_sleep, &wait); | ||
882 | out: | ||
883 | if (err == 0) { | ||
884 | sk->sk_socket->state = SS_CONNECTED; | ||
885 | } else if (scp->state != DN_CI && scp->state != DN_CC) { | ||
886 | sk->sk_socket->state = SS_UNCONNECTED; | ||
887 | } | ||
888 | return err; | ||
889 | } | ||
890 | |||
891 | static int __dn_connect(struct sock *sk, struct sockaddr_dn *addr, int addrlen, long *timeo, int flags) | ||
892 | { | ||
893 | struct socket *sock = sk->sk_socket; | ||
894 | struct dn_scp *scp = DN_SK(sk); | ||
895 | int err = -EISCONN; | ||
896 | struct flowi fl; | ||
897 | |||
898 | if (sock->state == SS_CONNECTED) | ||
899 | goto out; | ||
900 | |||
901 | if (sock->state == SS_CONNECTING) { | ||
902 | err = 0; | ||
903 | if (scp->state == DN_RUN) { | ||
904 | sock->state = SS_CONNECTED; | ||
905 | goto out; | ||
906 | } | ||
907 | err = -ECONNREFUSED; | ||
908 | if (scp->state != DN_CI && scp->state != DN_CC) { | ||
909 | sock->state = SS_UNCONNECTED; | ||
910 | goto out; | ||
911 | } | ||
912 | return dn_wait_run(sk, timeo); | ||
913 | } | ||
914 | |||
915 | err = -EINVAL; | ||
916 | if (scp->state != DN_O) | ||
917 | goto out; | ||
918 | |||
919 | if (addr == NULL || addrlen != sizeof(struct sockaddr_dn)) | ||
920 | goto out; | ||
921 | if (addr->sdn_family != AF_DECnet) | ||
922 | goto out; | ||
923 | if (addr->sdn_flags & SDF_WILD) | ||
924 | goto out; | ||
925 | |||
926 | if (sock_flag(sk, SOCK_ZAPPED)) { | ||
927 | err = dn_auto_bind(sk->sk_socket); | ||
928 | if (err) | ||
929 | goto out; | ||
930 | } | ||
931 | |||
932 | memcpy(&scp->peer, addr, sizeof(struct sockaddr_dn)); | ||
933 | |||
934 | err = -EHOSTUNREACH; | ||
935 | memset(&fl, 0, sizeof(fl)); | ||
936 | fl.oif = sk->sk_bound_dev_if; | ||
937 | fl.fld_dst = dn_saddr2dn(&scp->peer); | ||
938 | fl.fld_src = dn_saddr2dn(&scp->addr); | ||
939 | dn_sk_ports_copy(&fl, scp); | ||
940 | fl.proto = DNPROTO_NSP; | ||
941 | if (dn_route_output_sock(&sk->sk_dst_cache, &fl, sk, flags) < 0) | ||
942 | goto out; | ||
943 | sk->sk_route_caps = sk->sk_dst_cache->dev->features; | ||
944 | sock->state = SS_CONNECTING; | ||
945 | scp->state = DN_CI; | ||
946 | scp->segsize_loc = dst_metric(sk->sk_dst_cache, RTAX_ADVMSS); | ||
947 | |||
948 | dn_nsp_send_conninit(sk, NSP_CI); | ||
949 | err = -EINPROGRESS; | ||
950 | if (*timeo) { | ||
951 | err = dn_wait_run(sk, timeo); | ||
952 | } | ||
953 | out: | ||
954 | return err; | ||
955 | } | ||
956 | |||
957 | static int dn_connect(struct socket *sock, struct sockaddr *uaddr, int addrlen, int flags) | ||
958 | { | ||
959 | struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr; | ||
960 | struct sock *sk = sock->sk; | ||
961 | int err; | ||
962 | long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); | ||
963 | |||
964 | lock_sock(sk); | ||
965 | err = __dn_connect(sk, addr, addrlen, &timeo, 0); | ||
966 | release_sock(sk); | ||
967 | |||
968 | return err; | ||
969 | } | ||
970 | |||
971 | static inline int dn_check_state(struct sock *sk, struct sockaddr_dn *addr, int addrlen, long *timeo, int flags) | ||
972 | { | ||
973 | struct dn_scp *scp = DN_SK(sk); | ||
974 | |||
975 | switch(scp->state) { | ||
976 | case DN_RUN: | ||
977 | return 0; | ||
978 | case DN_CR: | ||
979 | return dn_confirm_accept(sk, timeo, sk->sk_allocation); | ||
980 | case DN_CI: | ||
981 | case DN_CC: | ||
982 | return dn_wait_run(sk, timeo); | ||
983 | case DN_O: | ||
984 | return __dn_connect(sk, addr, addrlen, timeo, flags); | ||
985 | } | ||
986 | |||
987 | return -EINVAL; | ||
988 | } | ||
989 | |||
990 | |||
991 | static void dn_access_copy(struct sk_buff *skb, struct accessdata_dn *acc) | ||
992 | { | ||
993 | unsigned char *ptr = skb->data; | ||
994 | |||
995 | acc->acc_userl = *ptr++; | ||
996 | memcpy(&acc->acc_user, ptr, acc->acc_userl); | ||
997 | ptr += acc->acc_userl; | ||
998 | |||
999 | acc->acc_passl = *ptr++; | ||
1000 | memcpy(&acc->acc_pass, ptr, acc->acc_passl); | ||
1001 | ptr += acc->acc_passl; | ||
1002 | |||
1003 | acc->acc_accl = *ptr++; | ||
1004 | memcpy(&acc->acc_acc, ptr, acc->acc_accl); | ||
1005 | |||
1006 | skb_pull(skb, acc->acc_accl + acc->acc_passl + acc->acc_userl + 3); | ||
1007 | |||
1008 | } | ||
1009 | |||
1010 | static void dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt) | ||
1011 | { | ||
1012 | unsigned char *ptr = skb->data; | ||
1013 | |||
1014 | opt->opt_optl = *ptr++; | ||
1015 | opt->opt_status = 0; | ||
1016 | memcpy(opt->opt_data, ptr, opt->opt_optl); | ||
1017 | skb_pull(skb, opt->opt_optl + 1); | ||
1018 | |||
1019 | } | ||
1020 | |||
1021 | static struct sk_buff *dn_wait_for_connect(struct sock *sk, long *timeo) | ||
1022 | { | ||
1023 | DEFINE_WAIT(wait); | ||
1024 | struct sk_buff *skb = NULL; | ||
1025 | int err = 0; | ||
1026 | |||
1027 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
1028 | for(;;) { | ||
1029 | release_sock(sk); | ||
1030 | skb = skb_dequeue(&sk->sk_receive_queue); | ||
1031 | if (skb == NULL) { | ||
1032 | *timeo = schedule_timeout(*timeo); | ||
1033 | skb = skb_dequeue(&sk->sk_receive_queue); | ||
1034 | } | ||
1035 | lock_sock(sk); | ||
1036 | if (skb != NULL) | ||
1037 | break; | ||
1038 | err = -EINVAL; | ||
1039 | if (sk->sk_state != TCP_LISTEN) | ||
1040 | break; | ||
1041 | err = sock_intr_errno(*timeo); | ||
1042 | if (signal_pending(current)) | ||
1043 | break; | ||
1044 | err = -EAGAIN; | ||
1045 | if (!*timeo) | ||
1046 | break; | ||
1047 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
1048 | } | ||
1049 | finish_wait(sk->sk_sleep, &wait); | ||
1050 | |||
1051 | return skb == NULL ? ERR_PTR(err) : skb; | ||
1052 | } | ||
1053 | |||
1054 | static int dn_accept(struct socket *sock, struct socket *newsock, int flags) | ||
1055 | { | ||
1056 | struct sock *sk = sock->sk, *newsk; | ||
1057 | struct sk_buff *skb = NULL; | ||
1058 | struct dn_skb_cb *cb; | ||
1059 | unsigned char menuver; | ||
1060 | int err = 0; | ||
1061 | unsigned char type; | ||
1062 | long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); | ||
1063 | |||
1064 | lock_sock(sk); | ||
1065 | |||
1066 | if (sk->sk_state != TCP_LISTEN || DN_SK(sk)->state != DN_O) { | ||
1067 | release_sock(sk); | ||
1068 | return -EINVAL; | ||
1069 | } | ||
1070 | |||
1071 | skb = skb_dequeue(&sk->sk_receive_queue); | ||
1072 | if (skb == NULL) { | ||
1073 | skb = dn_wait_for_connect(sk, &timeo); | ||
1074 | if (IS_ERR(skb)) { | ||
1075 | release_sock(sk); | ||
1076 | return PTR_ERR(skb); | ||
1077 | } | ||
1078 | } | ||
1079 | |||
1080 | cb = DN_SKB_CB(skb); | ||
1081 | sk->sk_ack_backlog--; | ||
1082 | newsk = dn_alloc_sock(newsock, sk->sk_allocation); | ||
1083 | if (newsk == NULL) { | ||
1084 | release_sock(sk); | ||
1085 | kfree_skb(skb); | ||
1086 | return -ENOBUFS; | ||
1087 | } | ||
1088 | release_sock(sk); | ||
1089 | |||
1090 | dst_release(xchg(&newsk->sk_dst_cache, skb->dst)); | ||
1091 | skb->dst = NULL; | ||
1092 | |||
1093 | DN_SK(newsk)->state = DN_CR; | ||
1094 | DN_SK(newsk)->addrrem = cb->src_port; | ||
1095 | DN_SK(newsk)->services_rem = cb->services; | ||
1096 | DN_SK(newsk)->info_rem = cb->info; | ||
1097 | DN_SK(newsk)->segsize_rem = cb->segsize; | ||
1098 | DN_SK(newsk)->accept_mode = DN_SK(sk)->accept_mode; | ||
1099 | |||
1100 | if (DN_SK(newsk)->segsize_rem < 230) | ||
1101 | DN_SK(newsk)->segsize_rem = 230; | ||
1102 | |||
1103 | if ((DN_SK(newsk)->services_rem & NSP_FC_MASK) == NSP_FC_NONE) | ||
1104 | DN_SK(newsk)->max_window = decnet_no_fc_max_cwnd; | ||
1105 | |||
1106 | newsk->sk_state = TCP_LISTEN; | ||
1107 | memcpy(&(DN_SK(newsk)->addr), &(DN_SK(sk)->addr), sizeof(struct sockaddr_dn)); | ||
1108 | |||
1109 | /* | ||
1110 | * If we are listening on a wild socket, we don't want | ||
1111 | * the newly created socket on the wrong hash queue. | ||
1112 | */ | ||
1113 | DN_SK(newsk)->addr.sdn_flags &= ~SDF_WILD; | ||
1114 | |||
1115 | skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->addr), &type)); | ||
1116 | skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->peer), &type)); | ||
1117 | *(dn_address *)(DN_SK(newsk)->peer.sdn_add.a_addr) = cb->src; | ||
1118 | *(dn_address *)(DN_SK(newsk)->addr.sdn_add.a_addr) = cb->dst; | ||
1119 | |||
1120 | menuver = *skb->data; | ||
1121 | skb_pull(skb, 1); | ||
1122 | |||
1123 | if (menuver & DN_MENUVER_ACC) | ||
1124 | dn_access_copy(skb, &(DN_SK(newsk)->accessdata)); | ||
1125 | |||
1126 | if (menuver & DN_MENUVER_USR) | ||
1127 | dn_user_copy(skb, &(DN_SK(newsk)->conndata_in)); | ||
1128 | |||
1129 | if (menuver & DN_MENUVER_PRX) | ||
1130 | DN_SK(newsk)->peer.sdn_flags |= SDF_PROXY; | ||
1131 | |||
1132 | if (menuver & DN_MENUVER_UIC) | ||
1133 | DN_SK(newsk)->peer.sdn_flags |= SDF_UICPROXY; | ||
1134 | |||
1135 | kfree_skb(skb); | ||
1136 | |||
1137 | memcpy(&(DN_SK(newsk)->conndata_out), &(DN_SK(sk)->conndata_out), | ||
1138 | sizeof(struct optdata_dn)); | ||
1139 | memcpy(&(DN_SK(newsk)->discdata_out), &(DN_SK(sk)->discdata_out), | ||
1140 | sizeof(struct optdata_dn)); | ||
1141 | |||
1142 | lock_sock(newsk); | ||
1143 | err = dn_hash_sock(newsk); | ||
1144 | if (err == 0) { | ||
1145 | sock_reset_flag(newsk, SOCK_ZAPPED); | ||
1146 | dn_send_conn_ack(newsk); | ||
1147 | |||
1148 | /* | ||
1149 | * Here we use sk->sk_allocation since although the conn conf is | ||
1150 | * for the newsk, the context is the old socket. | ||
1151 | */ | ||
1152 | if (DN_SK(newsk)->accept_mode == ACC_IMMED) | ||
1153 | err = dn_confirm_accept(newsk, &timeo, | ||
1154 | sk->sk_allocation); | ||
1155 | } | ||
1156 | release_sock(newsk); | ||
1157 | return err; | ||
1158 | } | ||
1159 | |||
1160 | |||
1161 | static int dn_getname(struct socket *sock, struct sockaddr *uaddr,int *uaddr_len,int peer) | ||
1162 | { | ||
1163 | struct sockaddr_dn *sa = (struct sockaddr_dn *)uaddr; | ||
1164 | struct sock *sk = sock->sk; | ||
1165 | struct dn_scp *scp = DN_SK(sk); | ||
1166 | |||
1167 | *uaddr_len = sizeof(struct sockaddr_dn); | ||
1168 | |||
1169 | lock_sock(sk); | ||
1170 | |||
1171 | if (peer) { | ||
1172 | if ((sock->state != SS_CONNECTED && | ||
1173 | sock->state != SS_CONNECTING) && | ||
1174 | scp->accept_mode == ACC_IMMED) | ||
1175 | return -ENOTCONN; | ||
1176 | |||
1177 | memcpy(sa, &scp->peer, sizeof(struct sockaddr_dn)); | ||
1178 | } else { | ||
1179 | memcpy(sa, &scp->addr, sizeof(struct sockaddr_dn)); | ||
1180 | } | ||
1181 | |||
1182 | release_sock(sk); | ||
1183 | |||
1184 | return 0; | ||
1185 | } | ||
1186 | |||
1187 | |||
1188 | static unsigned int dn_poll(struct file *file, struct socket *sock, poll_table *wait) | ||
1189 | { | ||
1190 | struct sock *sk = sock->sk; | ||
1191 | struct dn_scp *scp = DN_SK(sk); | ||
1192 | int mask = datagram_poll(file, sock, wait); | ||
1193 | |||
1194 | if (skb_queue_len(&scp->other_receive_queue)) | ||
1195 | mask |= POLLRDBAND; | ||
1196 | |||
1197 | return mask; | ||
1198 | } | ||
1199 | |||
1200 | static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
1201 | { | ||
1202 | struct sock *sk = sock->sk; | ||
1203 | struct dn_scp *scp = DN_SK(sk); | ||
1204 | int err = -EOPNOTSUPP; | ||
1205 | long amount = 0; | ||
1206 | struct sk_buff *skb; | ||
1207 | int val; | ||
1208 | |||
1209 | switch(cmd) | ||
1210 | { | ||
1211 | case SIOCGIFADDR: | ||
1212 | case SIOCSIFADDR: | ||
1213 | return dn_dev_ioctl(cmd, (void __user *)arg); | ||
1214 | |||
1215 | case SIOCATMARK: | ||
1216 | lock_sock(sk); | ||
1217 | val = (skb_queue_len(&scp->other_receive_queue) != 0); | ||
1218 | if (scp->state != DN_RUN) | ||
1219 | val = -ENOTCONN; | ||
1220 | release_sock(sk); | ||
1221 | return val; | ||
1222 | |||
1223 | case TIOCOUTQ: | ||
1224 | amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); | ||
1225 | if (amount < 0) | ||
1226 | amount = 0; | ||
1227 | err = put_user(amount, (int __user *)arg); | ||
1228 | break; | ||
1229 | |||
1230 | case TIOCINQ: | ||
1231 | lock_sock(sk); | ||
1232 | if ((skb = skb_peek(&scp->other_receive_queue)) != NULL) { | ||
1233 | amount = skb->len; | ||
1234 | } else { | ||
1235 | struct sk_buff *skb = sk->sk_receive_queue.next; | ||
1236 | for(;;) { | ||
1237 | if (skb == | ||
1238 | (struct sk_buff *)&sk->sk_receive_queue) | ||
1239 | break; | ||
1240 | amount += skb->len; | ||
1241 | skb = skb->next; | ||
1242 | } | ||
1243 | } | ||
1244 | release_sock(sk); | ||
1245 | err = put_user(amount, (int __user *)arg); | ||
1246 | break; | ||
1247 | |||
1248 | default: | ||
1249 | err = dev_ioctl(cmd, (void __user *)arg); | ||
1250 | break; | ||
1251 | } | ||
1252 | |||
1253 | return err; | ||
1254 | } | ||
1255 | |||
1256 | static int dn_listen(struct socket *sock, int backlog) | ||
1257 | { | ||
1258 | struct sock *sk = sock->sk; | ||
1259 | int err = -EINVAL; | ||
1260 | |||
1261 | lock_sock(sk); | ||
1262 | |||
1263 | if (sock_flag(sk, SOCK_ZAPPED)) | ||
1264 | goto out; | ||
1265 | |||
1266 | if ((DN_SK(sk)->state != DN_O) || (sk->sk_state == TCP_LISTEN)) | ||
1267 | goto out; | ||
1268 | |||
1269 | sk->sk_max_ack_backlog = backlog; | ||
1270 | sk->sk_ack_backlog = 0; | ||
1271 | sk->sk_state = TCP_LISTEN; | ||
1272 | err = 0; | ||
1273 | dn_rehash_sock(sk); | ||
1274 | |||
1275 | out: | ||
1276 | release_sock(sk); | ||
1277 | |||
1278 | return err; | ||
1279 | } | ||
1280 | |||
1281 | |||
1282 | static int dn_shutdown(struct socket *sock, int how) | ||
1283 | { | ||
1284 | struct sock *sk = sock->sk; | ||
1285 | struct dn_scp *scp = DN_SK(sk); | ||
1286 | int err = -ENOTCONN; | ||
1287 | |||
1288 | lock_sock(sk); | ||
1289 | |||
1290 | if (sock->state == SS_UNCONNECTED) | ||
1291 | goto out; | ||
1292 | |||
1293 | err = 0; | ||
1294 | if (sock->state == SS_DISCONNECTING) | ||
1295 | goto out; | ||
1296 | |||
1297 | err = -EINVAL; | ||
1298 | if (scp->state == DN_O) | ||
1299 | goto out; | ||
1300 | |||
1301 | if (how != SHUTDOWN_MASK) | ||
1302 | goto out; | ||
1303 | |||
1304 | sk->sk_shutdown = how; | ||
1305 | dn_destroy_sock(sk); | ||
1306 | err = 0; | ||
1307 | |||
1308 | out: | ||
1309 | release_sock(sk); | ||
1310 | |||
1311 | return err; | ||
1312 | } | ||
1313 | |||
1314 | static int dn_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) | ||
1315 | { | ||
1316 | struct sock *sk = sock->sk; | ||
1317 | int err; | ||
1318 | |||
1319 | lock_sock(sk); | ||
1320 | err = __dn_setsockopt(sock, level, optname, optval, optlen, 0); | ||
1321 | release_sock(sk); | ||
1322 | |||
1323 | return err; | ||
1324 | } | ||
1325 | |||
1326 | static int __dn_setsockopt(struct socket *sock, int level,int optname, char __user *optval, int optlen, int flags) | ||
1327 | { | ||
1328 | struct sock *sk = sock->sk; | ||
1329 | struct dn_scp *scp = DN_SK(sk); | ||
1330 | long timeo; | ||
1331 | union { | ||
1332 | struct optdata_dn opt; | ||
1333 | struct accessdata_dn acc; | ||
1334 | int mode; | ||
1335 | unsigned long win; | ||
1336 | int val; | ||
1337 | unsigned char services; | ||
1338 | unsigned char info; | ||
1339 | } u; | ||
1340 | int err; | ||
1341 | |||
1342 | if (optlen && !optval) | ||
1343 | return -EINVAL; | ||
1344 | |||
1345 | if (optlen > sizeof(u)) | ||
1346 | return -EINVAL; | ||
1347 | |||
1348 | if (copy_from_user(&u, optval, optlen)) | ||
1349 | return -EFAULT; | ||
1350 | |||
1351 | switch(optname) { | ||
1352 | case DSO_CONDATA: | ||
1353 | if (sock->state == SS_CONNECTED) | ||
1354 | return -EISCONN; | ||
1355 | if ((scp->state != DN_O) && (scp->state != DN_CR)) | ||
1356 | return -EINVAL; | ||
1357 | |||
1358 | if (optlen != sizeof(struct optdata_dn)) | ||
1359 | return -EINVAL; | ||
1360 | |||
1361 | if (u.opt.opt_optl > 16) | ||
1362 | return -EINVAL; | ||
1363 | |||
1364 | memcpy(&scp->conndata_out, &u.opt, optlen); | ||
1365 | break; | ||
1366 | |||
1367 | case DSO_DISDATA: | ||
1368 | if (sock->state != SS_CONNECTED && scp->accept_mode == ACC_IMMED) | ||
1369 | return -ENOTCONN; | ||
1370 | |||
1371 | if (optlen != sizeof(struct optdata_dn)) | ||
1372 | return -EINVAL; | ||
1373 | |||
1374 | if (u.opt.opt_optl > 16) | ||
1375 | return -EINVAL; | ||
1376 | |||
1377 | memcpy(&scp->discdata_out, &u.opt, optlen); | ||
1378 | break; | ||
1379 | |||
1380 | case DSO_CONACCESS: | ||
1381 | if (sock->state == SS_CONNECTED) | ||
1382 | return -EISCONN; | ||
1383 | if (scp->state != DN_O) | ||
1384 | return -EINVAL; | ||
1385 | |||
1386 | if (optlen != sizeof(struct accessdata_dn)) | ||
1387 | return -EINVAL; | ||
1388 | |||
1389 | if ((u.acc.acc_accl > DN_MAXACCL) || | ||
1390 | (u.acc.acc_passl > DN_MAXACCL) || | ||
1391 | (u.acc.acc_userl > DN_MAXACCL)) | ||
1392 | return -EINVAL; | ||
1393 | |||
1394 | memcpy(&scp->accessdata, &u.acc, optlen); | ||
1395 | break; | ||
1396 | |||
1397 | case DSO_ACCEPTMODE: | ||
1398 | if (sock->state == SS_CONNECTED) | ||
1399 | return -EISCONN; | ||
1400 | if (scp->state != DN_O) | ||
1401 | return -EINVAL; | ||
1402 | |||
1403 | if (optlen != sizeof(int)) | ||
1404 | return -EINVAL; | ||
1405 | |||
1406 | if ((u.mode != ACC_IMMED) && (u.mode != ACC_DEFER)) | ||
1407 | return -EINVAL; | ||
1408 | |||
1409 | scp->accept_mode = (unsigned char)u.mode; | ||
1410 | break; | ||
1411 | |||
1412 | case DSO_CONACCEPT: | ||
1413 | |||
1414 | if (scp->state != DN_CR) | ||
1415 | return -EINVAL; | ||
1416 | timeo = sock_rcvtimeo(sk, 0); | ||
1417 | err = dn_confirm_accept(sk, &timeo, sk->sk_allocation); | ||
1418 | return err; | ||
1419 | |||
1420 | case DSO_CONREJECT: | ||
1421 | |||
1422 | if (scp->state != DN_CR) | ||
1423 | return -EINVAL; | ||
1424 | |||
1425 | scp->state = DN_DR; | ||
1426 | sk->sk_shutdown = SHUTDOWN_MASK; | ||
1427 | dn_nsp_send_disc(sk, 0x38, 0, sk->sk_allocation); | ||
1428 | break; | ||
1429 | |||
1430 | default: | ||
1431 | #ifdef CONFIG_NETFILTER | ||
1432 | return nf_setsockopt(sk, PF_DECnet, optname, optval, optlen); | ||
1433 | #endif | ||
1434 | case DSO_LINKINFO: | ||
1435 | case DSO_STREAM: | ||
1436 | case DSO_SEQPACKET: | ||
1437 | return -ENOPROTOOPT; | ||
1438 | |||
1439 | case DSO_MAXWINDOW: | ||
1440 | if (optlen != sizeof(unsigned long)) | ||
1441 | return -EINVAL; | ||
1442 | if (u.win > NSP_MAX_WINDOW) | ||
1443 | u.win = NSP_MAX_WINDOW; | ||
1444 | if (u.win == 0) | ||
1445 | return -EINVAL; | ||
1446 | scp->max_window = u.win; | ||
1447 | if (scp->snd_window > u.win) | ||
1448 | scp->snd_window = u.win; | ||
1449 | break; | ||
1450 | |||
1451 | case DSO_NODELAY: | ||
1452 | if (optlen != sizeof(int)) | ||
1453 | return -EINVAL; | ||
1454 | if (scp->nonagle == 2) | ||
1455 | return -EINVAL; | ||
1456 | scp->nonagle = (u.val == 0) ? 0 : 1; | ||
1457 | /* if (scp->nonagle == 1) { Push pending frames } */ | ||
1458 | break; | ||
1459 | |||
1460 | case DSO_CORK: | ||
1461 | if (optlen != sizeof(int)) | ||
1462 | return -EINVAL; | ||
1463 | if (scp->nonagle == 1) | ||
1464 | return -EINVAL; | ||
1465 | scp->nonagle = (u.val == 0) ? 0 : 2; | ||
1466 | /* if (scp->nonagle == 0) { Push pending frames } */ | ||
1467 | break; | ||
1468 | |||
1469 | case DSO_SERVICES: | ||
1470 | if (optlen != sizeof(unsigned char)) | ||
1471 | return -EINVAL; | ||
1472 | if ((u.services & ~NSP_FC_MASK) != 0x01) | ||
1473 | return -EINVAL; | ||
1474 | if ((u.services & NSP_FC_MASK) == NSP_FC_MASK) | ||
1475 | return -EINVAL; | ||
1476 | scp->services_loc = u.services; | ||
1477 | break; | ||
1478 | |||
1479 | case DSO_INFO: | ||
1480 | if (optlen != sizeof(unsigned char)) | ||
1481 | return -EINVAL; | ||
1482 | if (u.info & 0xfc) | ||
1483 | return -EINVAL; | ||
1484 | scp->info_loc = u.info; | ||
1485 | break; | ||
1486 | } | ||
1487 | |||
1488 | return 0; | ||
1489 | } | ||
1490 | |||
1491 | static int dn_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) | ||
1492 | { | ||
1493 | struct sock *sk = sock->sk; | ||
1494 | int err; | ||
1495 | |||
1496 | lock_sock(sk); | ||
1497 | err = __dn_getsockopt(sock, level, optname, optval, optlen, 0); | ||
1498 | release_sock(sk); | ||
1499 | |||
1500 | return err; | ||
1501 | } | ||
1502 | |||
1503 | static int __dn_getsockopt(struct socket *sock, int level,int optname, char __user *optval,int __user *optlen, int flags) | ||
1504 | { | ||
1505 | struct sock *sk = sock->sk; | ||
1506 | struct dn_scp *scp = DN_SK(sk); | ||
1507 | struct linkinfo_dn link; | ||
1508 | unsigned int r_len; | ||
1509 | void *r_data = NULL; | ||
1510 | unsigned int val; | ||
1511 | |||
1512 | if(get_user(r_len , optlen)) | ||
1513 | return -EFAULT; | ||
1514 | |||
1515 | switch(optname) { | ||
1516 | case DSO_CONDATA: | ||
1517 | if (r_len > sizeof(struct optdata_dn)) | ||
1518 | r_len = sizeof(struct optdata_dn); | ||
1519 | r_data = &scp->conndata_in; | ||
1520 | break; | ||
1521 | |||
1522 | case DSO_DISDATA: | ||
1523 | if (r_len > sizeof(struct optdata_dn)) | ||
1524 | r_len = sizeof(struct optdata_dn); | ||
1525 | r_data = &scp->discdata_in; | ||
1526 | break; | ||
1527 | |||
1528 | case DSO_CONACCESS: | ||
1529 | if (r_len > sizeof(struct accessdata_dn)) | ||
1530 | r_len = sizeof(struct accessdata_dn); | ||
1531 | r_data = &scp->accessdata; | ||
1532 | break; | ||
1533 | |||
1534 | case DSO_ACCEPTMODE: | ||
1535 | if (r_len > sizeof(unsigned char)) | ||
1536 | r_len = sizeof(unsigned char); | ||
1537 | r_data = &scp->accept_mode; | ||
1538 | break; | ||
1539 | |||
1540 | case DSO_LINKINFO: | ||
1541 | if (r_len > sizeof(struct linkinfo_dn)) | ||
1542 | r_len = sizeof(struct linkinfo_dn); | ||
1543 | |||
1544 | switch(sock->state) { | ||
1545 | case SS_CONNECTING: | ||
1546 | link.idn_linkstate = LL_CONNECTING; | ||
1547 | break; | ||
1548 | case SS_DISCONNECTING: | ||
1549 | link.idn_linkstate = LL_DISCONNECTING; | ||
1550 | break; | ||
1551 | case SS_CONNECTED: | ||
1552 | link.idn_linkstate = LL_RUNNING; | ||
1553 | break; | ||
1554 | default: | ||
1555 | link.idn_linkstate = LL_INACTIVE; | ||
1556 | } | ||
1557 | |||
1558 | link.idn_segsize = scp->segsize_rem; | ||
1559 | r_data = &link; | ||
1560 | break; | ||
1561 | |||
1562 | default: | ||
1563 | #ifdef CONFIG_NETFILTER | ||
1564 | { | ||
1565 | int val, len; | ||
1566 | |||
1567 | if(get_user(len, optlen)) | ||
1568 | return -EFAULT; | ||
1569 | |||
1570 | val = nf_getsockopt(sk, PF_DECnet, optname, | ||
1571 | optval, &len); | ||
1572 | if (val >= 0) | ||
1573 | val = put_user(len, optlen); | ||
1574 | return val; | ||
1575 | } | ||
1576 | #endif | ||
1577 | case DSO_STREAM: | ||
1578 | case DSO_SEQPACKET: | ||
1579 | case DSO_CONACCEPT: | ||
1580 | case DSO_CONREJECT: | ||
1581 | return -ENOPROTOOPT; | ||
1582 | |||
1583 | case DSO_MAXWINDOW: | ||
1584 | if (r_len > sizeof(unsigned long)) | ||
1585 | r_len = sizeof(unsigned long); | ||
1586 | r_data = &scp->max_window; | ||
1587 | break; | ||
1588 | |||
1589 | case DSO_NODELAY: | ||
1590 | if (r_len > sizeof(int)) | ||
1591 | r_len = sizeof(int); | ||
1592 | val = (scp->nonagle == 1); | ||
1593 | r_data = &val; | ||
1594 | break; | ||
1595 | |||
1596 | case DSO_CORK: | ||
1597 | if (r_len > sizeof(int)) | ||
1598 | r_len = sizeof(int); | ||
1599 | val = (scp->nonagle == 2); | ||
1600 | r_data = &val; | ||
1601 | break; | ||
1602 | |||
1603 | case DSO_SERVICES: | ||
1604 | if (r_len > sizeof(unsigned char)) | ||
1605 | r_len = sizeof(unsigned char); | ||
1606 | r_data = &scp->services_rem; | ||
1607 | break; | ||
1608 | |||
1609 | case DSO_INFO: | ||
1610 | if (r_len > sizeof(unsigned char)) | ||
1611 | r_len = sizeof(unsigned char); | ||
1612 | r_data = &scp->info_rem; | ||
1613 | break; | ||
1614 | } | ||
1615 | |||
1616 | if (r_data) { | ||
1617 | if (copy_to_user(optval, r_data, r_len)) | ||
1618 | return -EFAULT; | ||
1619 | if (put_user(r_len, optlen)) | ||
1620 | return -EFAULT; | ||
1621 | } | ||
1622 | |||
1623 | return 0; | ||
1624 | } | ||
1625 | |||
1626 | |||
1627 | static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int target) | ||
1628 | { | ||
1629 | struct sk_buff *skb = q->next; | ||
1630 | int len = 0; | ||
1631 | |||
1632 | if (flags & MSG_OOB) | ||
1633 | return skb_queue_len(q) ? 1 : 0; | ||
1634 | |||
1635 | while(skb != (struct sk_buff *)q) { | ||
1636 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
1637 | len += skb->len; | ||
1638 | |||
1639 | if (cb->nsp_flags & 0x40) { | ||
1640 | /* SOCK_SEQPACKET reads to EOM */ | ||
1641 | if (sk->sk_type == SOCK_SEQPACKET) | ||
1642 | return 1; | ||
1643 | /* so does SOCK_STREAM unless WAITALL is specified */ | ||
1644 | if (!(flags & MSG_WAITALL)) | ||
1645 | return 1; | ||
1646 | } | ||
1647 | |||
1648 | /* minimum data length for read exceeded */ | ||
1649 | if (len >= target) | ||
1650 | return 1; | ||
1651 | |||
1652 | skb = skb->next; | ||
1653 | } | ||
1654 | |||
1655 | return 0; | ||
1656 | } | ||
1657 | |||
1658 | |||
1659 | static int dn_recvmsg(struct kiocb *iocb, struct socket *sock, | ||
1660 | struct msghdr *msg, size_t size, int flags) | ||
1661 | { | ||
1662 | struct sock *sk = sock->sk; | ||
1663 | struct dn_scp *scp = DN_SK(sk); | ||
1664 | struct sk_buff_head *queue = &sk->sk_receive_queue; | ||
1665 | size_t target = size > 1 ? 1 : 0; | ||
1666 | size_t copied = 0; | ||
1667 | int rv = 0; | ||
1668 | struct sk_buff *skb, *nskb; | ||
1669 | struct dn_skb_cb *cb = NULL; | ||
1670 | unsigned char eor = 0; | ||
1671 | long timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); | ||
1672 | |||
1673 | lock_sock(sk); | ||
1674 | |||
1675 | if (sock_flag(sk, SOCK_ZAPPED)) { | ||
1676 | rv = -EADDRNOTAVAIL; | ||
1677 | goto out; | ||
1678 | } | ||
1679 | |||
1680 | rv = dn_check_state(sk, NULL, 0, &timeo, flags); | ||
1681 | if (rv) | ||
1682 | goto out; | ||
1683 | |||
1684 | if (sk->sk_shutdown & RCV_SHUTDOWN) { | ||
1685 | if (!(flags & MSG_NOSIGNAL)) | ||
1686 | send_sig(SIGPIPE, current, 0); | ||
1687 | rv = -EPIPE; | ||
1688 | goto out; | ||
1689 | } | ||
1690 | |||
1691 | if (flags & ~(MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT|MSG_NOSIGNAL)) { | ||
1692 | rv = -EOPNOTSUPP; | ||
1693 | goto out; | ||
1694 | } | ||
1695 | |||
1696 | if (flags & MSG_OOB) | ||
1697 | queue = &scp->other_receive_queue; | ||
1698 | |||
1699 | if (flags & MSG_WAITALL) | ||
1700 | target = size; | ||
1701 | |||
1702 | |||
1703 | /* | ||
1704 | * See if there is data ready to read, sleep if there isn't | ||
1705 | */ | ||
1706 | for(;;) { | ||
1707 | if (sk->sk_err) | ||
1708 | goto out; | ||
1709 | |||
1710 | if (skb_queue_len(&scp->other_receive_queue)) { | ||
1711 | if (!(flags & MSG_OOB)) { | ||
1712 | msg->msg_flags |= MSG_OOB; | ||
1713 | if (!scp->other_report) { | ||
1714 | scp->other_report = 1; | ||
1715 | goto out; | ||
1716 | } | ||
1717 | } | ||
1718 | } | ||
1719 | |||
1720 | if (scp->state != DN_RUN) | ||
1721 | goto out; | ||
1722 | |||
1723 | if (signal_pending(current)) { | ||
1724 | rv = sock_intr_errno(timeo); | ||
1725 | goto out; | ||
1726 | } | ||
1727 | |||
1728 | if (dn_data_ready(sk, queue, flags, target)) | ||
1729 | break; | ||
1730 | |||
1731 | if (flags & MSG_DONTWAIT) { | ||
1732 | rv = -EWOULDBLOCK; | ||
1733 | goto out; | ||
1734 | } | ||
1735 | |||
1736 | set_bit(SOCK_ASYNC_WAITDATA, &sock->flags); | ||
1737 | SOCK_SLEEP_PRE(sk) | ||
1738 | |||
1739 | if (!dn_data_ready(sk, queue, flags, target)) | ||
1740 | schedule(); | ||
1741 | |||
1742 | SOCK_SLEEP_POST(sk) | ||
1743 | clear_bit(SOCK_ASYNC_WAITDATA, &sock->flags); | ||
1744 | } | ||
1745 | |||
1746 | for(skb = queue->next; skb != (struct sk_buff *)queue; skb = nskb) { | ||
1747 | unsigned int chunk = skb->len; | ||
1748 | cb = DN_SKB_CB(skb); | ||
1749 | |||
1750 | if ((chunk + copied) > size) | ||
1751 | chunk = size - copied; | ||
1752 | |||
1753 | if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { | ||
1754 | rv = -EFAULT; | ||
1755 | break; | ||
1756 | } | ||
1757 | copied += chunk; | ||
1758 | |||
1759 | if (!(flags & MSG_PEEK)) | ||
1760 | skb_pull(skb, chunk); | ||
1761 | |||
1762 | eor = cb->nsp_flags & 0x40; | ||
1763 | nskb = skb->next; | ||
1764 | |||
1765 | if (skb->len == 0) { | ||
1766 | skb_unlink(skb); | ||
1767 | kfree_skb(skb); | ||
1768 | /* | ||
1769 | * N.B. Don't refer to skb or cb after this point | ||
1770 | * in loop. | ||
1771 | */ | ||
1772 | if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) { | ||
1773 | scp->flowloc_sw = DN_SEND; | ||
1774 | dn_nsp_send_link(sk, DN_SEND, 0); | ||
1775 | } | ||
1776 | } | ||
1777 | |||
1778 | if (eor) { | ||
1779 | if (sk->sk_type == SOCK_SEQPACKET) | ||
1780 | break; | ||
1781 | if (!(flags & MSG_WAITALL)) | ||
1782 | break; | ||
1783 | } | ||
1784 | |||
1785 | if (flags & MSG_OOB) | ||
1786 | break; | ||
1787 | |||
1788 | if (copied >= target) | ||
1789 | break; | ||
1790 | } | ||
1791 | |||
1792 | rv = copied; | ||
1793 | |||
1794 | |||
1795 | if (eor && (sk->sk_type == SOCK_SEQPACKET)) | ||
1796 | msg->msg_flags |= MSG_EOR; | ||
1797 | |||
1798 | out: | ||
1799 | if (rv == 0) | ||
1800 | rv = (flags & MSG_PEEK) ? -sk->sk_err : sock_error(sk); | ||
1801 | |||
1802 | if ((rv >= 0) && msg->msg_name) { | ||
1803 | memcpy(msg->msg_name, &scp->peer, sizeof(struct sockaddr_dn)); | ||
1804 | msg->msg_namelen = sizeof(struct sockaddr_dn); | ||
1805 | } | ||
1806 | |||
1807 | release_sock(sk); | ||
1808 | |||
1809 | return rv; | ||
1810 | } | ||
1811 | |||
1812 | |||
1813 | static inline int dn_queue_too_long(struct dn_scp *scp, struct sk_buff_head *queue, int flags) | ||
1814 | { | ||
1815 | unsigned char fctype = scp->services_rem & NSP_FC_MASK; | ||
1816 | if (skb_queue_len(queue) >= scp->snd_window) | ||
1817 | return 1; | ||
1818 | if (fctype != NSP_FC_NONE) { | ||
1819 | if (flags & MSG_OOB) { | ||
1820 | if (scp->flowrem_oth == 0) | ||
1821 | return 1; | ||
1822 | } else { | ||
1823 | if (scp->flowrem_dat == 0) | ||
1824 | return 1; | ||
1825 | } | ||
1826 | } | ||
1827 | return 0; | ||
1828 | } | ||
1829 | |||
1830 | /* | ||
1831 | * The DECnet spec requires the the "routing layer" accepts packets which | ||
1832 | * are at least 230 bytes in size. This excludes any headers which the NSP | ||
1833 | * layer might add, so we always assume that we'll be using the maximal | ||
1834 | * length header on data packets. The variation in length is due to the | ||
1835 | * inclusion (or not) of the two 16 bit acknowledgement fields so it doesn't | ||
1836 | * make much practical difference. | ||
1837 | */ | ||
1838 | unsigned dn_mss_from_pmtu(struct net_device *dev, int mtu) | ||
1839 | { | ||
1840 | unsigned mss = 230 - DN_MAX_NSP_DATA_HEADER; | ||
1841 | if (dev) { | ||
1842 | struct dn_dev *dn_db = dev->dn_ptr; | ||
1843 | mtu -= LL_RESERVED_SPACE(dev); | ||
1844 | if (dn_db->use_long) | ||
1845 | mtu -= 21; | ||
1846 | else | ||
1847 | mtu -= 6; | ||
1848 | mtu -= DN_MAX_NSP_DATA_HEADER; | ||
1849 | } else { | ||
1850 | /* | ||
1851 | * 21 = long header, 16 = guess at MAC header length | ||
1852 | */ | ||
1853 | mtu -= (21 + DN_MAX_NSP_DATA_HEADER + 16); | ||
1854 | } | ||
1855 | if (mtu > mss) | ||
1856 | mss = mtu; | ||
1857 | return mss; | ||
1858 | } | ||
1859 | |||
1860 | static inline unsigned int dn_current_mss(struct sock *sk, int flags) | ||
1861 | { | ||
1862 | struct dst_entry *dst = __sk_dst_get(sk); | ||
1863 | struct dn_scp *scp = DN_SK(sk); | ||
1864 | int mss_now = min_t(int, scp->segsize_loc, scp->segsize_rem); | ||
1865 | |||
1866 | /* Other data messages are limited to 16 bytes per packet */ | ||
1867 | if (flags & MSG_OOB) | ||
1868 | return 16; | ||
1869 | |||
1870 | /* This works out the maximum size of segment we can send out */ | ||
1871 | if (dst) { | ||
1872 | u32 mtu = dst_mtu(dst); | ||
1873 | mss_now = min_t(int, dn_mss_from_pmtu(dst->dev, mtu), mss_now); | ||
1874 | } | ||
1875 | |||
1876 | return mss_now; | ||
1877 | } | ||
1878 | |||
1879 | static int dn_error(struct sock *sk, int flags, int err) | ||
1880 | { | ||
1881 | if (err == -EPIPE) | ||
1882 | err = sock_error(sk) ? : -EPIPE; | ||
1883 | if (err == -EPIPE && !(flags & MSG_NOSIGNAL)) | ||
1884 | send_sig(SIGPIPE, current, 0); | ||
1885 | return err; | ||
1886 | } | ||
1887 | |||
1888 | static int dn_sendmsg(struct kiocb *iocb, struct socket *sock, | ||
1889 | struct msghdr *msg, size_t size) | ||
1890 | { | ||
1891 | struct sock *sk = sock->sk; | ||
1892 | struct dn_scp *scp = DN_SK(sk); | ||
1893 | size_t mss; | ||
1894 | struct sk_buff_head *queue = &scp->data_xmit_queue; | ||
1895 | int flags = msg->msg_flags; | ||
1896 | int err = 0; | ||
1897 | size_t sent = 0; | ||
1898 | int addr_len = msg->msg_namelen; | ||
1899 | struct sockaddr_dn *addr = (struct sockaddr_dn *)msg->msg_name; | ||
1900 | struct sk_buff *skb = NULL; | ||
1901 | struct dn_skb_cb *cb; | ||
1902 | size_t len; | ||
1903 | unsigned char fctype; | ||
1904 | long timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); | ||
1905 | |||
1906 | if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|MSG_MORE|MSG_CMSG_COMPAT)) | ||
1907 | return -EOPNOTSUPP; | ||
1908 | |||
1909 | if (addr_len && (addr_len != sizeof(struct sockaddr_dn))) | ||
1910 | return -EINVAL; | ||
1911 | |||
1912 | /* | ||
1913 | * The only difference between stream sockets and sequenced packet | ||
1914 | * sockets is that the stream sockets always behave as if MSG_EOR | ||
1915 | * has been set. | ||
1916 | */ | ||
1917 | if (sock->type == SOCK_STREAM) { | ||
1918 | if (flags & MSG_EOR) | ||
1919 | return -EINVAL; | ||
1920 | flags |= MSG_EOR; | ||
1921 | } | ||
1922 | |||
1923 | lock_sock(sk); | ||
1924 | |||
1925 | err = dn_check_state(sk, addr, addr_len, &timeo, flags); | ||
1926 | if (err) | ||
1927 | goto out_err; | ||
1928 | |||
1929 | if (sk->sk_shutdown & SEND_SHUTDOWN) { | ||
1930 | err = -EPIPE; | ||
1931 | goto out_err; | ||
1932 | } | ||
1933 | |||
1934 | if ((flags & MSG_TRYHARD) && sk->sk_dst_cache) | ||
1935 | dst_negative_advice(&sk->sk_dst_cache); | ||
1936 | |||
1937 | mss = scp->segsize_rem; | ||
1938 | fctype = scp->services_rem & NSP_FC_MASK; | ||
1939 | |||
1940 | mss = dn_current_mss(sk, flags); | ||
1941 | |||
1942 | if (flags & MSG_OOB) { | ||
1943 | queue = &scp->other_xmit_queue; | ||
1944 | if (size > mss) { | ||
1945 | err = -EMSGSIZE; | ||
1946 | goto out; | ||
1947 | } | ||
1948 | } | ||
1949 | |||
1950 | scp->persist_fxn = dn_nsp_xmit_timeout; | ||
1951 | |||
1952 | while(sent < size) { | ||
1953 | err = sock_error(sk); | ||
1954 | if (err) | ||
1955 | goto out; | ||
1956 | |||
1957 | if (signal_pending(current)) { | ||
1958 | err = sock_intr_errno(timeo); | ||
1959 | goto out; | ||
1960 | } | ||
1961 | |||
1962 | /* | ||
1963 | * Calculate size that we wish to send. | ||
1964 | */ | ||
1965 | len = size - sent; | ||
1966 | |||
1967 | if (len > mss) | ||
1968 | len = mss; | ||
1969 | |||
1970 | /* | ||
1971 | * Wait for queue size to go down below the window | ||
1972 | * size. | ||
1973 | */ | ||
1974 | if (dn_queue_too_long(scp, queue, flags)) { | ||
1975 | if (flags & MSG_DONTWAIT) { | ||
1976 | err = -EWOULDBLOCK; | ||
1977 | goto out; | ||
1978 | } | ||
1979 | |||
1980 | SOCK_SLEEP_PRE(sk) | ||
1981 | |||
1982 | if (dn_queue_too_long(scp, queue, flags)) | ||
1983 | schedule(); | ||
1984 | |||
1985 | SOCK_SLEEP_POST(sk) | ||
1986 | |||
1987 | continue; | ||
1988 | } | ||
1989 | |||
1990 | /* | ||
1991 | * Get a suitably sized skb. | ||
1992 | */ | ||
1993 | skb = dn_alloc_send_skb(sk, &len, flags & MSG_DONTWAIT, timeo, &err); | ||
1994 | |||
1995 | if (err) | ||
1996 | break; | ||
1997 | |||
1998 | if (!skb) | ||
1999 | continue; | ||
2000 | |||
2001 | cb = DN_SKB_CB(skb); | ||
2002 | |||
2003 | skb_reserve(skb, DN_MAX_NSP_DATA_HEADER); | ||
2004 | |||
2005 | if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { | ||
2006 | err = -EFAULT; | ||
2007 | goto out; | ||
2008 | } | ||
2009 | |||
2010 | if (flags & MSG_OOB) { | ||
2011 | cb->nsp_flags = 0x30; | ||
2012 | if (fctype != NSP_FC_NONE) | ||
2013 | scp->flowrem_oth--; | ||
2014 | } else { | ||
2015 | cb->nsp_flags = 0x00; | ||
2016 | if (scp->seg_total == 0) | ||
2017 | cb->nsp_flags |= 0x20; | ||
2018 | |||
2019 | scp->seg_total += len; | ||
2020 | |||
2021 | if (((sent + len) == size) && (flags & MSG_EOR)) { | ||
2022 | cb->nsp_flags |= 0x40; | ||
2023 | scp->seg_total = 0; | ||
2024 | if (fctype == NSP_FC_SCMC) | ||
2025 | scp->flowrem_dat--; | ||
2026 | } | ||
2027 | if (fctype == NSP_FC_SRC) | ||
2028 | scp->flowrem_dat--; | ||
2029 | } | ||
2030 | |||
2031 | sent += len; | ||
2032 | dn_nsp_queue_xmit(sk, skb, sk->sk_allocation, flags & MSG_OOB); | ||
2033 | skb = NULL; | ||
2034 | |||
2035 | scp->persist = dn_nsp_persist(sk); | ||
2036 | |||
2037 | } | ||
2038 | out: | ||
2039 | |||
2040 | if (skb) | ||
2041 | kfree_skb(skb); | ||
2042 | |||
2043 | release_sock(sk); | ||
2044 | |||
2045 | return sent ? sent : err; | ||
2046 | |||
2047 | out_err: | ||
2048 | err = dn_error(sk, flags, err); | ||
2049 | release_sock(sk); | ||
2050 | return err; | ||
2051 | } | ||
2052 | |||
2053 | static int dn_device_event(struct notifier_block *this, unsigned long event, | ||
2054 | void *ptr) | ||
2055 | { | ||
2056 | struct net_device *dev = (struct net_device *)ptr; | ||
2057 | |||
2058 | switch(event) { | ||
2059 | case NETDEV_UP: | ||
2060 | dn_dev_up(dev); | ||
2061 | break; | ||
2062 | case NETDEV_DOWN: | ||
2063 | dn_dev_down(dev); | ||
2064 | break; | ||
2065 | default: | ||
2066 | break; | ||
2067 | } | ||
2068 | |||
2069 | return NOTIFY_DONE; | ||
2070 | } | ||
2071 | |||
2072 | static struct notifier_block dn_dev_notifier = { | ||
2073 | .notifier_call = dn_device_event, | ||
2074 | }; | ||
2075 | |||
2076 | extern int dn_route_rcv(struct sk_buff *, struct net_device *, struct packet_type *); | ||
2077 | |||
2078 | static struct packet_type dn_dix_packet_type = { | ||
2079 | .type = __constant_htons(ETH_P_DNA_RT), | ||
2080 | .dev = NULL, /* All devices */ | ||
2081 | .func = dn_route_rcv, | ||
2082 | }; | ||
2083 | |||
2084 | #ifdef CONFIG_PROC_FS | ||
2085 | struct dn_iter_state { | ||
2086 | int bucket; | ||
2087 | }; | ||
2088 | |||
2089 | static struct sock *dn_socket_get_first(struct seq_file *seq) | ||
2090 | { | ||
2091 | struct dn_iter_state *state = seq->private; | ||
2092 | struct sock *n = NULL; | ||
2093 | |||
2094 | for(state->bucket = 0; | ||
2095 | state->bucket < DN_SK_HASH_SIZE; | ||
2096 | ++state->bucket) { | ||
2097 | n = sk_head(&dn_sk_hash[state->bucket]); | ||
2098 | if (n) | ||
2099 | break; | ||
2100 | } | ||
2101 | |||
2102 | return n; | ||
2103 | } | ||
2104 | |||
2105 | static struct sock *dn_socket_get_next(struct seq_file *seq, | ||
2106 | struct sock *n) | ||
2107 | { | ||
2108 | struct dn_iter_state *state = seq->private; | ||
2109 | |||
2110 | n = sk_next(n); | ||
2111 | try_again: | ||
2112 | if (n) | ||
2113 | goto out; | ||
2114 | if (++state->bucket >= DN_SK_HASH_SIZE) | ||
2115 | goto out; | ||
2116 | n = sk_head(&dn_sk_hash[state->bucket]); | ||
2117 | goto try_again; | ||
2118 | out: | ||
2119 | return n; | ||
2120 | } | ||
2121 | |||
2122 | static struct sock *socket_get_idx(struct seq_file *seq, loff_t *pos) | ||
2123 | { | ||
2124 | struct sock *sk = dn_socket_get_first(seq); | ||
2125 | |||
2126 | if (sk) { | ||
2127 | while(*pos && (sk = dn_socket_get_next(seq, sk))) | ||
2128 | --*pos; | ||
2129 | } | ||
2130 | return *pos ? NULL : sk; | ||
2131 | } | ||
2132 | |||
2133 | static void *dn_socket_get_idx(struct seq_file *seq, loff_t pos) | ||
2134 | { | ||
2135 | void *rc; | ||
2136 | read_lock_bh(&dn_hash_lock); | ||
2137 | rc = socket_get_idx(seq, &pos); | ||
2138 | if (!rc) { | ||
2139 | read_unlock_bh(&dn_hash_lock); | ||
2140 | } | ||
2141 | return rc; | ||
2142 | } | ||
2143 | |||
2144 | static void *dn_socket_seq_start(struct seq_file *seq, loff_t *pos) | ||
2145 | { | ||
2146 | return *pos ? dn_socket_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; | ||
2147 | } | ||
2148 | |||
2149 | static void *dn_socket_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
2150 | { | ||
2151 | void *rc; | ||
2152 | |||
2153 | if (v == SEQ_START_TOKEN) { | ||
2154 | rc = dn_socket_get_idx(seq, 0); | ||
2155 | goto out; | ||
2156 | } | ||
2157 | |||
2158 | rc = dn_socket_get_next(seq, v); | ||
2159 | if (rc) | ||
2160 | goto out; | ||
2161 | read_unlock_bh(&dn_hash_lock); | ||
2162 | out: | ||
2163 | ++*pos; | ||
2164 | return rc; | ||
2165 | } | ||
2166 | |||
2167 | static void dn_socket_seq_stop(struct seq_file *seq, void *v) | ||
2168 | { | ||
2169 | if (v && v != SEQ_START_TOKEN) | ||
2170 | read_unlock_bh(&dn_hash_lock); | ||
2171 | } | ||
2172 | |||
2173 | #define IS_NOT_PRINTABLE(x) ((x) < 32 || (x) > 126) | ||
2174 | |||
2175 | static void dn_printable_object(struct sockaddr_dn *dn, unsigned char *buf) | ||
2176 | { | ||
2177 | int i; | ||
2178 | |||
2179 | switch (dn_ntohs(dn->sdn_objnamel)) { | ||
2180 | case 0: | ||
2181 | sprintf(buf, "%d", dn->sdn_objnum); | ||
2182 | break; | ||
2183 | default: | ||
2184 | for (i = 0; i < dn_ntohs(dn->sdn_objnamel); i++) { | ||
2185 | buf[i] = dn->sdn_objname[i]; | ||
2186 | if (IS_NOT_PRINTABLE(buf[i])) | ||
2187 | buf[i] = '.'; | ||
2188 | } | ||
2189 | buf[i] = 0; | ||
2190 | } | ||
2191 | } | ||
2192 | |||
2193 | static char *dn_state2asc(unsigned char state) | ||
2194 | { | ||
2195 | switch(state) { | ||
2196 | case DN_O: | ||
2197 | return "OPEN"; | ||
2198 | case DN_CR: | ||
2199 | return " CR"; | ||
2200 | case DN_DR: | ||
2201 | return " DR"; | ||
2202 | case DN_DRC: | ||
2203 | return " DRC"; | ||
2204 | case DN_CC: | ||
2205 | return " CC"; | ||
2206 | case DN_CI: | ||
2207 | return " CI"; | ||
2208 | case DN_NR: | ||
2209 | return " NR"; | ||
2210 | case DN_NC: | ||
2211 | return " NC"; | ||
2212 | case DN_CD: | ||
2213 | return " CD"; | ||
2214 | case DN_RJ: | ||
2215 | return " RJ"; | ||
2216 | case DN_RUN: | ||
2217 | return " RUN"; | ||
2218 | case DN_DI: | ||
2219 | return " DI"; | ||
2220 | case DN_DIC: | ||
2221 | return " DIC"; | ||
2222 | case DN_DN: | ||
2223 | return " DN"; | ||
2224 | case DN_CL: | ||
2225 | return " CL"; | ||
2226 | case DN_CN: | ||
2227 | return " CN"; | ||
2228 | } | ||
2229 | |||
2230 | return "????"; | ||
2231 | } | ||
2232 | |||
2233 | static inline void dn_socket_format_entry(struct seq_file *seq, struct sock *sk) | ||
2234 | { | ||
2235 | struct dn_scp *scp = DN_SK(sk); | ||
2236 | char buf1[DN_ASCBUF_LEN]; | ||
2237 | char buf2[DN_ASCBUF_LEN]; | ||
2238 | char local_object[DN_MAXOBJL+3]; | ||
2239 | char remote_object[DN_MAXOBJL+3]; | ||
2240 | |||
2241 | dn_printable_object(&scp->addr, local_object); | ||
2242 | dn_printable_object(&scp->peer, remote_object); | ||
2243 | |||
2244 | seq_printf(seq, | ||
2245 | "%6s/%04X %04d:%04d %04d:%04d %01d %-16s " | ||
2246 | "%6s/%04X %04d:%04d %04d:%04d %01d %-16s %4s %s\n", | ||
2247 | dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->addr)), buf1), | ||
2248 | scp->addrloc, | ||
2249 | scp->numdat, | ||
2250 | scp->numoth, | ||
2251 | scp->ackxmt_dat, | ||
2252 | scp->ackxmt_oth, | ||
2253 | scp->flowloc_sw, | ||
2254 | local_object, | ||
2255 | dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->peer)), buf2), | ||
2256 | scp->addrrem, | ||
2257 | scp->numdat_rcv, | ||
2258 | scp->numoth_rcv, | ||
2259 | scp->ackrcv_dat, | ||
2260 | scp->ackrcv_oth, | ||
2261 | scp->flowrem_sw, | ||
2262 | remote_object, | ||
2263 | dn_state2asc(scp->state), | ||
2264 | ((scp->accept_mode == ACC_IMMED) ? "IMMED" : "DEFER")); | ||
2265 | } | ||
2266 | |||
2267 | static int dn_socket_seq_show(struct seq_file *seq, void *v) | ||
2268 | { | ||
2269 | if (v == SEQ_START_TOKEN) { | ||
2270 | seq_puts(seq, "Local Remote\n"); | ||
2271 | } else { | ||
2272 | dn_socket_format_entry(seq, v); | ||
2273 | } | ||
2274 | return 0; | ||
2275 | } | ||
2276 | |||
2277 | static struct seq_operations dn_socket_seq_ops = { | ||
2278 | .start = dn_socket_seq_start, | ||
2279 | .next = dn_socket_seq_next, | ||
2280 | .stop = dn_socket_seq_stop, | ||
2281 | .show = dn_socket_seq_show, | ||
2282 | }; | ||
2283 | |||
2284 | static int dn_socket_seq_open(struct inode *inode, struct file *file) | ||
2285 | { | ||
2286 | struct seq_file *seq; | ||
2287 | int rc = -ENOMEM; | ||
2288 | struct dn_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
2289 | |||
2290 | if (!s) | ||
2291 | goto out; | ||
2292 | |||
2293 | rc = seq_open(file, &dn_socket_seq_ops); | ||
2294 | if (rc) | ||
2295 | goto out_kfree; | ||
2296 | |||
2297 | seq = file->private_data; | ||
2298 | seq->private = s; | ||
2299 | memset(s, 0, sizeof(*s)); | ||
2300 | out: | ||
2301 | return rc; | ||
2302 | out_kfree: | ||
2303 | kfree(s); | ||
2304 | goto out; | ||
2305 | } | ||
2306 | |||
2307 | static struct file_operations dn_socket_seq_fops = { | ||
2308 | .owner = THIS_MODULE, | ||
2309 | .open = dn_socket_seq_open, | ||
2310 | .read = seq_read, | ||
2311 | .llseek = seq_lseek, | ||
2312 | .release = seq_release_private, | ||
2313 | }; | ||
2314 | #endif | ||
2315 | |||
2316 | static struct net_proto_family dn_family_ops = { | ||
2317 | .family = AF_DECnet, | ||
2318 | .create = dn_create, | ||
2319 | .owner = THIS_MODULE, | ||
2320 | }; | ||
2321 | |||
2322 | static struct proto_ops dn_proto_ops = { | ||
2323 | .family = AF_DECnet, | ||
2324 | .owner = THIS_MODULE, | ||
2325 | .release = dn_release, | ||
2326 | .bind = dn_bind, | ||
2327 | .connect = dn_connect, | ||
2328 | .socketpair = sock_no_socketpair, | ||
2329 | .accept = dn_accept, | ||
2330 | .getname = dn_getname, | ||
2331 | .poll = dn_poll, | ||
2332 | .ioctl = dn_ioctl, | ||
2333 | .listen = dn_listen, | ||
2334 | .shutdown = dn_shutdown, | ||
2335 | .setsockopt = dn_setsockopt, | ||
2336 | .getsockopt = dn_getsockopt, | ||
2337 | .sendmsg = dn_sendmsg, | ||
2338 | .recvmsg = dn_recvmsg, | ||
2339 | .mmap = sock_no_mmap, | ||
2340 | .sendpage = sock_no_sendpage, | ||
2341 | }; | ||
2342 | |||
2343 | void dn_register_sysctl(void); | ||
2344 | void dn_unregister_sysctl(void); | ||
2345 | |||
2346 | MODULE_DESCRIPTION("The Linux DECnet Network Protocol"); | ||
2347 | MODULE_AUTHOR("Linux DECnet Project Team"); | ||
2348 | MODULE_LICENSE("GPL"); | ||
2349 | MODULE_ALIAS_NETPROTO(PF_DECnet); | ||
2350 | |||
2351 | static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.5.68s (C) 1995-2003 Linux DECnet Project Team\n"; | ||
2352 | |||
2353 | static int __init decnet_init(void) | ||
2354 | { | ||
2355 | int rc; | ||
2356 | |||
2357 | printk(banner); | ||
2358 | |||
2359 | rc = proto_register(&dn_proto, 1); | ||
2360 | if (rc != 0) | ||
2361 | goto out; | ||
2362 | |||
2363 | dn_neigh_init(); | ||
2364 | dn_dev_init(); | ||
2365 | dn_route_init(); | ||
2366 | dn_fib_init(); | ||
2367 | |||
2368 | sock_register(&dn_family_ops); | ||
2369 | dev_add_pack(&dn_dix_packet_type); | ||
2370 | register_netdevice_notifier(&dn_dev_notifier); | ||
2371 | |||
2372 | proc_net_fops_create("decnet", S_IRUGO, &dn_socket_seq_fops); | ||
2373 | dn_register_sysctl(); | ||
2374 | out: | ||
2375 | return rc; | ||
2376 | |||
2377 | } | ||
2378 | module_init(decnet_init); | ||
2379 | |||
2380 | /* | ||
2381 | * Prevent DECnet module unloading until its fixed properly. | ||
2382 | * Requires an audit of the code to check for memory leaks and | ||
2383 | * initialisation problems etc. | ||
2384 | */ | ||
2385 | #if 0 | ||
2386 | static void __exit decnet_exit(void) | ||
2387 | { | ||
2388 | sock_unregister(AF_DECnet); | ||
2389 | dev_remove_pack(&dn_dix_packet_type); | ||
2390 | |||
2391 | dn_unregister_sysctl(); | ||
2392 | |||
2393 | unregister_netdevice_notifier(&dn_dev_notifier); | ||
2394 | |||
2395 | dn_route_cleanup(); | ||
2396 | dn_dev_cleanup(); | ||
2397 | dn_neigh_cleanup(); | ||
2398 | dn_fib_cleanup(); | ||
2399 | |||
2400 | proc_net_remove("decnet"); | ||
2401 | |||
2402 | proto_unregister(&dn_proto); | ||
2403 | } | ||
2404 | module_exit(decnet_exit); | ||
2405 | #endif | ||
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c new file mode 100644 index 000000000000..c2a0346f423b --- /dev/null +++ b/net/decnet/dn_dev.c | |||
@@ -0,0 +1,1481 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet Device Layer | ||
7 | * | ||
8 | * Authors: Steve Whitehouse <SteveW@ACM.org> | ||
9 | * Eduardo Marcelo Serrat <emserrat@geocities.com> | ||
10 | * | ||
11 | * Changes: | ||
12 | * Steve Whitehouse : Devices now see incoming frames so they | ||
13 | * can mark on who it came from. | ||
14 | * Steve Whitehouse : Fixed bug in creating neighbours. Each neighbour | ||
15 | * can now have a device specific setup func. | ||
16 | * Steve Whitehouse : Added /proc/sys/net/decnet/conf/<dev>/ | ||
17 | * Steve Whitehouse : Fixed bug which sometimes killed timer | ||
18 | * Steve Whitehouse : Multiple ifaddr support | ||
19 | * Steve Whitehouse : SIOCGIFCONF is now a compile time option | ||
20 | * Steve Whitehouse : /proc/sys/net/decnet/conf/<sys>/forwarding | ||
21 | * Steve Whitehouse : Removed timer1 - it's a user space issue now | ||
22 | * Patrick Caulfield : Fixed router hello message format | ||
23 | * Steve Whitehouse : Got rid of constant sizes for blksize for | ||
24 | * devices. All mtu based now. | ||
25 | */ | ||
26 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/moduleparam.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/net.h> | ||
32 | #include <linux/netdevice.h> | ||
33 | #include <linux/proc_fs.h> | ||
34 | #include <linux/seq_file.h> | ||
35 | #include <linux/timer.h> | ||
36 | #include <linux/string.h> | ||
37 | #include <linux/if_arp.h> | ||
38 | #include <linux/if_ether.h> | ||
39 | #include <linux/skbuff.h> | ||
40 | #include <linux/rtnetlink.h> | ||
41 | #include <linux/sysctl.h> | ||
42 | #include <linux/notifier.h> | ||
43 | #include <asm/uaccess.h> | ||
44 | #include <asm/system.h> | ||
45 | #include <net/neighbour.h> | ||
46 | #include <net/dst.h> | ||
47 | #include <net/flow.h> | ||
48 | #include <net/dn.h> | ||
49 | #include <net/dn_dev.h> | ||
50 | #include <net/dn_route.h> | ||
51 | #include <net/dn_neigh.h> | ||
52 | #include <net/dn_fib.h> | ||
53 | |||
54 | #define DN_IFREQ_SIZE (sizeof(struct ifreq) - sizeof(struct sockaddr) + sizeof(struct sockaddr_dn)) | ||
55 | |||
56 | static char dn_rt_all_end_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x04,0x00,0x00}; | ||
57 | static char dn_rt_all_rt_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x03,0x00,0x00}; | ||
58 | static char dn_hiord[ETH_ALEN] = {0xAA,0x00,0x04,0x00,0x00,0x00}; | ||
59 | static unsigned char dn_eco_version[3] = {0x02,0x00,0x00}; | ||
60 | |||
61 | extern struct neigh_table dn_neigh_table; | ||
62 | |||
63 | /* | ||
64 | * decnet_address is kept in network order. | ||
65 | */ | ||
66 | dn_address decnet_address = 0; | ||
67 | |||
68 | static DEFINE_RWLOCK(dndev_lock); | ||
69 | static struct net_device *decnet_default_device; | ||
70 | static struct notifier_block *dnaddr_chain; | ||
71 | |||
72 | static struct dn_dev *dn_dev_create(struct net_device *dev, int *err); | ||
73 | static void dn_dev_delete(struct net_device *dev); | ||
74 | static void rtmsg_ifa(int event, struct dn_ifaddr *ifa); | ||
75 | |||
76 | static int dn_eth_up(struct net_device *); | ||
77 | static void dn_eth_down(struct net_device *); | ||
78 | static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa); | ||
79 | static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa); | ||
80 | |||
81 | static struct dn_dev_parms dn_dev_list[] = { | ||
82 | { | ||
83 | .type = ARPHRD_ETHER, /* Ethernet */ | ||
84 | .mode = DN_DEV_BCAST, | ||
85 | .state = DN_DEV_S_RU, | ||
86 | .t2 = 1, | ||
87 | .t3 = 10, | ||
88 | .name = "ethernet", | ||
89 | .ctl_name = NET_DECNET_CONF_ETHER, | ||
90 | .up = dn_eth_up, | ||
91 | .down = dn_eth_down, | ||
92 | .timer3 = dn_send_brd_hello, | ||
93 | }, | ||
94 | { | ||
95 | .type = ARPHRD_IPGRE, /* DECnet tunneled over GRE in IP */ | ||
96 | .mode = DN_DEV_BCAST, | ||
97 | .state = DN_DEV_S_RU, | ||
98 | .t2 = 1, | ||
99 | .t3 = 10, | ||
100 | .name = "ipgre", | ||
101 | .ctl_name = NET_DECNET_CONF_GRE, | ||
102 | .timer3 = dn_send_brd_hello, | ||
103 | }, | ||
104 | #if 0 | ||
105 | { | ||
106 | .type = ARPHRD_X25, /* Bog standard X.25 */ | ||
107 | .mode = DN_DEV_UCAST, | ||
108 | .state = DN_DEV_S_DS, | ||
109 | .t2 = 1, | ||
110 | .t3 = 120, | ||
111 | .name = "x25", | ||
112 | .ctl_name = NET_DECNET_CONF_X25, | ||
113 | .timer3 = dn_send_ptp_hello, | ||
114 | }, | ||
115 | #endif | ||
116 | #if 0 | ||
117 | { | ||
118 | .type = ARPHRD_PPP, /* DECnet over PPP */ | ||
119 | .mode = DN_DEV_BCAST, | ||
120 | .state = DN_DEV_S_RU, | ||
121 | .t2 = 1, | ||
122 | .t3 = 10, | ||
123 | .name = "ppp", | ||
124 | .ctl_name = NET_DECNET_CONF_PPP, | ||
125 | .timer3 = dn_send_brd_hello, | ||
126 | }, | ||
127 | #endif | ||
128 | { | ||
129 | .type = ARPHRD_DDCMP, /* DECnet over DDCMP */ | ||
130 | .mode = DN_DEV_UCAST, | ||
131 | .state = DN_DEV_S_DS, | ||
132 | .t2 = 1, | ||
133 | .t3 = 120, | ||
134 | .name = "ddcmp", | ||
135 | .ctl_name = NET_DECNET_CONF_DDCMP, | ||
136 | .timer3 = dn_send_ptp_hello, | ||
137 | }, | ||
138 | { | ||
139 | .type = ARPHRD_LOOPBACK, /* Loopback interface - always last */ | ||
140 | .mode = DN_DEV_BCAST, | ||
141 | .state = DN_DEV_S_RU, | ||
142 | .t2 = 1, | ||
143 | .t3 = 10, | ||
144 | .name = "loopback", | ||
145 | .ctl_name = NET_DECNET_CONF_LOOPBACK, | ||
146 | .timer3 = dn_send_brd_hello, | ||
147 | } | ||
148 | }; | ||
149 | |||
150 | #define DN_DEV_LIST_SIZE (sizeof(dn_dev_list)/sizeof(struct dn_dev_parms)) | ||
151 | |||
152 | #define DN_DEV_PARMS_OFFSET(x) ((int) ((char *) &((struct dn_dev_parms *)0)->x)) | ||
153 | |||
154 | #ifdef CONFIG_SYSCTL | ||
155 | |||
156 | static int min_t2[] = { 1 }; | ||
157 | static int max_t2[] = { 60 }; /* No max specified, but this seems sensible */ | ||
158 | static int min_t3[] = { 1 }; | ||
159 | static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MULT or T3MULT */ | ||
160 | |||
161 | static int min_priority[1]; | ||
162 | static int max_priority[] = { 127 }; /* From DECnet spec */ | ||
163 | |||
164 | static int dn_forwarding_proc(ctl_table *, int, struct file *, | ||
165 | void __user *, size_t *, loff_t *); | ||
166 | static int dn_forwarding_sysctl(ctl_table *table, int __user *name, int nlen, | ||
167 | void __user *oldval, size_t __user *oldlenp, | ||
168 | void __user *newval, size_t newlen, | ||
169 | void **context); | ||
170 | |||
171 | static struct dn_dev_sysctl_table { | ||
172 | struct ctl_table_header *sysctl_header; | ||
173 | ctl_table dn_dev_vars[5]; | ||
174 | ctl_table dn_dev_dev[2]; | ||
175 | ctl_table dn_dev_conf_dir[2]; | ||
176 | ctl_table dn_dev_proto_dir[2]; | ||
177 | ctl_table dn_dev_root_dir[2]; | ||
178 | } dn_dev_sysctl = { | ||
179 | NULL, | ||
180 | { | ||
181 | { | ||
182 | .ctl_name = NET_DECNET_CONF_DEV_FORWARDING, | ||
183 | .procname = "forwarding", | ||
184 | .data = (void *)DN_DEV_PARMS_OFFSET(forwarding), | ||
185 | .maxlen = sizeof(int), | ||
186 | .mode = 0644, | ||
187 | .proc_handler = dn_forwarding_proc, | ||
188 | .strategy = dn_forwarding_sysctl, | ||
189 | }, | ||
190 | { | ||
191 | .ctl_name = NET_DECNET_CONF_DEV_PRIORITY, | ||
192 | .procname = "priority", | ||
193 | .data = (void *)DN_DEV_PARMS_OFFSET(priority), | ||
194 | .maxlen = sizeof(int), | ||
195 | .mode = 0644, | ||
196 | .proc_handler = proc_dointvec_minmax, | ||
197 | .strategy = sysctl_intvec, | ||
198 | .extra1 = &min_priority, | ||
199 | .extra2 = &max_priority | ||
200 | }, | ||
201 | { | ||
202 | .ctl_name = NET_DECNET_CONF_DEV_T2, | ||
203 | .procname = "t2", | ||
204 | .data = (void *)DN_DEV_PARMS_OFFSET(t2), | ||
205 | .maxlen = sizeof(int), | ||
206 | .mode = 0644, | ||
207 | .proc_handler = proc_dointvec_minmax, | ||
208 | .strategy = sysctl_intvec, | ||
209 | .extra1 = &min_t2, | ||
210 | .extra2 = &max_t2 | ||
211 | }, | ||
212 | { | ||
213 | .ctl_name = NET_DECNET_CONF_DEV_T3, | ||
214 | .procname = "t3", | ||
215 | .data = (void *)DN_DEV_PARMS_OFFSET(t3), | ||
216 | .maxlen = sizeof(int), | ||
217 | .mode = 0644, | ||
218 | .proc_handler = proc_dointvec_minmax, | ||
219 | .strategy = sysctl_intvec, | ||
220 | .extra1 = &min_t3, | ||
221 | .extra2 = &max_t3 | ||
222 | }, | ||
223 | {0} | ||
224 | }, | ||
225 | {{ | ||
226 | .ctl_name = 0, | ||
227 | .procname = "", | ||
228 | .mode = 0555, | ||
229 | .child = dn_dev_sysctl.dn_dev_vars | ||
230 | }, {0}}, | ||
231 | {{ | ||
232 | .ctl_name = NET_DECNET_CONF, | ||
233 | .procname = "conf", | ||
234 | .mode = 0555, | ||
235 | .child = dn_dev_sysctl.dn_dev_dev | ||
236 | }, {0}}, | ||
237 | {{ | ||
238 | .ctl_name = NET_DECNET, | ||
239 | .procname = "decnet", | ||
240 | .mode = 0555, | ||
241 | .child = dn_dev_sysctl.dn_dev_conf_dir | ||
242 | }, {0}}, | ||
243 | {{ | ||
244 | .ctl_name = CTL_NET, | ||
245 | .procname = "net", | ||
246 | .mode = 0555, | ||
247 | .child = dn_dev_sysctl.dn_dev_proto_dir | ||
248 | }, {0}} | ||
249 | }; | ||
250 | |||
251 | static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms *parms) | ||
252 | { | ||
253 | struct dn_dev_sysctl_table *t; | ||
254 | int i; | ||
255 | |||
256 | t = kmalloc(sizeof(*t), GFP_KERNEL); | ||
257 | if (t == NULL) | ||
258 | return; | ||
259 | |||
260 | memcpy(t, &dn_dev_sysctl, sizeof(*t)); | ||
261 | |||
262 | for(i = 0; i < ARRAY_SIZE(t->dn_dev_vars) - 1; i++) { | ||
263 | long offset = (long)t->dn_dev_vars[i].data; | ||
264 | t->dn_dev_vars[i].data = ((char *)parms) + offset; | ||
265 | t->dn_dev_vars[i].de = NULL; | ||
266 | } | ||
267 | |||
268 | if (dev) { | ||
269 | t->dn_dev_dev[0].procname = dev->name; | ||
270 | t->dn_dev_dev[0].ctl_name = dev->ifindex; | ||
271 | } else { | ||
272 | t->dn_dev_dev[0].procname = parms->name; | ||
273 | t->dn_dev_dev[0].ctl_name = parms->ctl_name; | ||
274 | } | ||
275 | |||
276 | t->dn_dev_dev[0].child = t->dn_dev_vars; | ||
277 | t->dn_dev_dev[0].de = NULL; | ||
278 | t->dn_dev_conf_dir[0].child = t->dn_dev_dev; | ||
279 | t->dn_dev_conf_dir[0].de = NULL; | ||
280 | t->dn_dev_proto_dir[0].child = t->dn_dev_conf_dir; | ||
281 | t->dn_dev_proto_dir[0].de = NULL; | ||
282 | t->dn_dev_root_dir[0].child = t->dn_dev_proto_dir; | ||
283 | t->dn_dev_root_dir[0].de = NULL; | ||
284 | t->dn_dev_vars[0].extra1 = (void *)dev; | ||
285 | |||
286 | t->sysctl_header = register_sysctl_table(t->dn_dev_root_dir, 0); | ||
287 | if (t->sysctl_header == NULL) | ||
288 | kfree(t); | ||
289 | else | ||
290 | parms->sysctl = t; | ||
291 | } | ||
292 | |||
293 | static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms) | ||
294 | { | ||
295 | if (parms->sysctl) { | ||
296 | struct dn_dev_sysctl_table *t = parms->sysctl; | ||
297 | parms->sysctl = NULL; | ||
298 | unregister_sysctl_table(t->sysctl_header); | ||
299 | kfree(t); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | static int dn_forwarding_proc(ctl_table *table, int write, | ||
304 | struct file *filep, | ||
305 | void __user *buffer, | ||
306 | size_t *lenp, loff_t *ppos) | ||
307 | { | ||
308 | #ifdef CONFIG_DECNET_ROUTER | ||
309 | struct net_device *dev = table->extra1; | ||
310 | struct dn_dev *dn_db; | ||
311 | int err; | ||
312 | int tmp, old; | ||
313 | |||
314 | if (table->extra1 == NULL) | ||
315 | return -EINVAL; | ||
316 | |||
317 | dn_db = dev->dn_ptr; | ||
318 | old = dn_db->parms.forwarding; | ||
319 | |||
320 | err = proc_dointvec(table, write, filep, buffer, lenp, ppos); | ||
321 | |||
322 | if ((err >= 0) && write) { | ||
323 | if (dn_db->parms.forwarding < 0) | ||
324 | dn_db->parms.forwarding = 0; | ||
325 | if (dn_db->parms.forwarding > 2) | ||
326 | dn_db->parms.forwarding = 2; | ||
327 | /* | ||
328 | * What an ugly hack this is... its works, just. It | ||
329 | * would be nice if sysctl/proc were just that little | ||
330 | * bit more flexible so I don't have to write a special | ||
331 | * routine, or suffer hacks like this - SJW | ||
332 | */ | ||
333 | tmp = dn_db->parms.forwarding; | ||
334 | dn_db->parms.forwarding = old; | ||
335 | if (dn_db->parms.down) | ||
336 | dn_db->parms.down(dev); | ||
337 | dn_db->parms.forwarding = tmp; | ||
338 | if (dn_db->parms.up) | ||
339 | dn_db->parms.up(dev); | ||
340 | } | ||
341 | |||
342 | return err; | ||
343 | #else | ||
344 | return -EINVAL; | ||
345 | #endif | ||
346 | } | ||
347 | |||
348 | static int dn_forwarding_sysctl(ctl_table *table, int __user *name, int nlen, | ||
349 | void __user *oldval, size_t __user *oldlenp, | ||
350 | void __user *newval, size_t newlen, | ||
351 | void **context) | ||
352 | { | ||
353 | #ifdef CONFIG_DECNET_ROUTER | ||
354 | struct net_device *dev = table->extra1; | ||
355 | struct dn_dev *dn_db; | ||
356 | int value; | ||
357 | |||
358 | if (table->extra1 == NULL) | ||
359 | return -EINVAL; | ||
360 | |||
361 | dn_db = dev->dn_ptr; | ||
362 | |||
363 | if (newval && newlen) { | ||
364 | if (newlen != sizeof(int)) | ||
365 | return -EINVAL; | ||
366 | |||
367 | if (get_user(value, (int __user *)newval)) | ||
368 | return -EFAULT; | ||
369 | if (value < 0) | ||
370 | return -EINVAL; | ||
371 | if (value > 2) | ||
372 | return -EINVAL; | ||
373 | |||
374 | if (dn_db->parms.down) | ||
375 | dn_db->parms.down(dev); | ||
376 | dn_db->parms.forwarding = value; | ||
377 | if (dn_db->parms.up) | ||
378 | dn_db->parms.up(dev); | ||
379 | } | ||
380 | |||
381 | return 0; | ||
382 | #else | ||
383 | return -EINVAL; | ||
384 | #endif | ||
385 | } | ||
386 | |||
387 | #else /* CONFIG_SYSCTL */ | ||
388 | static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms) | ||
389 | { | ||
390 | } | ||
391 | static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms *parms) | ||
392 | { | ||
393 | } | ||
394 | |||
395 | #endif /* CONFIG_SYSCTL */ | ||
396 | |||
397 | static inline __u16 mtu2blksize(struct net_device *dev) | ||
398 | { | ||
399 | u32 blksize = dev->mtu; | ||
400 | if (blksize > 0xffff) | ||
401 | blksize = 0xffff; | ||
402 | |||
403 | if (dev->type == ARPHRD_ETHER || | ||
404 | dev->type == ARPHRD_PPP || | ||
405 | dev->type == ARPHRD_IPGRE || | ||
406 | dev->type == ARPHRD_LOOPBACK) | ||
407 | blksize -= 2; | ||
408 | |||
409 | return (__u16)blksize; | ||
410 | } | ||
411 | |||
412 | static struct dn_ifaddr *dn_dev_alloc_ifa(void) | ||
413 | { | ||
414 | struct dn_ifaddr *ifa; | ||
415 | |||
416 | ifa = kmalloc(sizeof(*ifa), GFP_KERNEL); | ||
417 | |||
418 | if (ifa) { | ||
419 | memset(ifa, 0, sizeof(*ifa)); | ||
420 | } | ||
421 | |||
422 | return ifa; | ||
423 | } | ||
424 | |||
425 | static __inline__ void dn_dev_free_ifa(struct dn_ifaddr *ifa) | ||
426 | { | ||
427 | kfree(ifa); | ||
428 | } | ||
429 | |||
430 | static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int destroy) | ||
431 | { | ||
432 | struct dn_ifaddr *ifa1 = *ifap; | ||
433 | unsigned char mac_addr[6]; | ||
434 | struct net_device *dev = dn_db->dev; | ||
435 | |||
436 | ASSERT_RTNL(); | ||
437 | |||
438 | *ifap = ifa1->ifa_next; | ||
439 | |||
440 | if (dn_db->dev->type == ARPHRD_ETHER) { | ||
441 | if (ifa1->ifa_local != dn_htons(dn_eth2dn(dev->dev_addr))) { | ||
442 | dn_dn2eth(mac_addr, ifa1->ifa_local); | ||
443 | dev_mc_delete(dev, mac_addr, ETH_ALEN, 0); | ||
444 | } | ||
445 | } | ||
446 | |||
447 | rtmsg_ifa(RTM_DELADDR, ifa1); | ||
448 | notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1); | ||
449 | if (destroy) { | ||
450 | dn_dev_free_ifa(ifa1); | ||
451 | |||
452 | if (dn_db->ifa_list == NULL) | ||
453 | dn_dev_delete(dn_db->dev); | ||
454 | } | ||
455 | } | ||
456 | |||
457 | static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) | ||
458 | { | ||
459 | struct net_device *dev = dn_db->dev; | ||
460 | struct dn_ifaddr *ifa1; | ||
461 | unsigned char mac_addr[6]; | ||
462 | |||
463 | ASSERT_RTNL(); | ||
464 | |||
465 | /* Check for duplicates */ | ||
466 | for(ifa1 = dn_db->ifa_list; ifa1; ifa1 = ifa1->ifa_next) { | ||
467 | if (ifa1->ifa_local == ifa->ifa_local) | ||
468 | return -EEXIST; | ||
469 | } | ||
470 | |||
471 | if (dev->type == ARPHRD_ETHER) { | ||
472 | if (ifa->ifa_local != dn_htons(dn_eth2dn(dev->dev_addr))) { | ||
473 | dn_dn2eth(mac_addr, ifa->ifa_local); | ||
474 | dev_mc_add(dev, mac_addr, ETH_ALEN, 0); | ||
475 | dev_mc_upload(dev); | ||
476 | } | ||
477 | } | ||
478 | |||
479 | ifa->ifa_next = dn_db->ifa_list; | ||
480 | dn_db->ifa_list = ifa; | ||
481 | |||
482 | rtmsg_ifa(RTM_NEWADDR, ifa); | ||
483 | notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa); | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int dn_dev_set_ifa(struct net_device *dev, struct dn_ifaddr *ifa) | ||
489 | { | ||
490 | struct dn_dev *dn_db = dev->dn_ptr; | ||
491 | int rv; | ||
492 | |||
493 | if (dn_db == NULL) { | ||
494 | int err; | ||
495 | dn_db = dn_dev_create(dev, &err); | ||
496 | if (dn_db == NULL) | ||
497 | return err; | ||
498 | } | ||
499 | |||
500 | ifa->ifa_dev = dn_db; | ||
501 | |||
502 | if (dev->flags & IFF_LOOPBACK) | ||
503 | ifa->ifa_scope = RT_SCOPE_HOST; | ||
504 | |||
505 | rv = dn_dev_insert_ifa(dn_db, ifa); | ||
506 | if (rv) | ||
507 | dn_dev_free_ifa(ifa); | ||
508 | return rv; | ||
509 | } | ||
510 | |||
511 | |||
512 | int dn_dev_ioctl(unsigned int cmd, void __user *arg) | ||
513 | { | ||
514 | char buffer[DN_IFREQ_SIZE]; | ||
515 | struct ifreq *ifr = (struct ifreq *)buffer; | ||
516 | struct sockaddr_dn *sdn = (struct sockaddr_dn *)&ifr->ifr_addr; | ||
517 | struct dn_dev *dn_db; | ||
518 | struct net_device *dev; | ||
519 | struct dn_ifaddr *ifa = NULL, **ifap = NULL; | ||
520 | int ret = 0; | ||
521 | |||
522 | if (copy_from_user(ifr, arg, DN_IFREQ_SIZE)) | ||
523 | return -EFAULT; | ||
524 | ifr->ifr_name[IFNAMSIZ-1] = 0; | ||
525 | |||
526 | #ifdef CONFIG_KMOD | ||
527 | dev_load(ifr->ifr_name); | ||
528 | #endif | ||
529 | |||
530 | switch(cmd) { | ||
531 | case SIOCGIFADDR: | ||
532 | break; | ||
533 | case SIOCSIFADDR: | ||
534 | if (!capable(CAP_NET_ADMIN)) | ||
535 | return -EACCES; | ||
536 | if (sdn->sdn_family != AF_DECnet) | ||
537 | return -EINVAL; | ||
538 | break; | ||
539 | default: | ||
540 | return -EINVAL; | ||
541 | } | ||
542 | |||
543 | rtnl_lock(); | ||
544 | |||
545 | if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) { | ||
546 | ret = -ENODEV; | ||
547 | goto done; | ||
548 | } | ||
549 | |||
550 | if ((dn_db = dev->dn_ptr) != NULL) { | ||
551 | for (ifap = &dn_db->ifa_list; (ifa=*ifap) != NULL; ifap = &ifa->ifa_next) | ||
552 | if (strcmp(ifr->ifr_name, ifa->ifa_label) == 0) | ||
553 | break; | ||
554 | } | ||
555 | |||
556 | if (ifa == NULL && cmd != SIOCSIFADDR) { | ||
557 | ret = -EADDRNOTAVAIL; | ||
558 | goto done; | ||
559 | } | ||
560 | |||
561 | switch(cmd) { | ||
562 | case SIOCGIFADDR: | ||
563 | *((dn_address *)sdn->sdn_nodeaddr) = ifa->ifa_local; | ||
564 | goto rarok; | ||
565 | |||
566 | case SIOCSIFADDR: | ||
567 | if (!ifa) { | ||
568 | if ((ifa = dn_dev_alloc_ifa()) == NULL) { | ||
569 | ret = -ENOBUFS; | ||
570 | break; | ||
571 | } | ||
572 | memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); | ||
573 | } else { | ||
574 | if (ifa->ifa_local == dn_saddr2dn(sdn)) | ||
575 | break; | ||
576 | dn_dev_del_ifa(dn_db, ifap, 0); | ||
577 | } | ||
578 | |||
579 | ifa->ifa_local = ifa->ifa_address = dn_saddr2dn(sdn); | ||
580 | |||
581 | ret = dn_dev_set_ifa(dev, ifa); | ||
582 | } | ||
583 | done: | ||
584 | rtnl_unlock(); | ||
585 | |||
586 | return ret; | ||
587 | rarok: | ||
588 | if (copy_to_user(arg, ifr, DN_IFREQ_SIZE)) | ||
589 | ret = -EFAULT; | ||
590 | goto done; | ||
591 | } | ||
592 | |||
593 | struct net_device *dn_dev_get_default(void) | ||
594 | { | ||
595 | struct net_device *dev; | ||
596 | read_lock(&dndev_lock); | ||
597 | dev = decnet_default_device; | ||
598 | if (dev) { | ||
599 | if (dev->dn_ptr) | ||
600 | dev_hold(dev); | ||
601 | else | ||
602 | dev = NULL; | ||
603 | } | ||
604 | read_unlock(&dndev_lock); | ||
605 | return dev; | ||
606 | } | ||
607 | |||
608 | int dn_dev_set_default(struct net_device *dev, int force) | ||
609 | { | ||
610 | struct net_device *old = NULL; | ||
611 | int rv = -EBUSY; | ||
612 | if (!dev->dn_ptr) | ||
613 | return -ENODEV; | ||
614 | write_lock(&dndev_lock); | ||
615 | if (force || decnet_default_device == NULL) { | ||
616 | old = decnet_default_device; | ||
617 | decnet_default_device = dev; | ||
618 | rv = 0; | ||
619 | } | ||
620 | write_unlock(&dndev_lock); | ||
621 | if (old) | ||
622 | dev_put(dev); | ||
623 | return rv; | ||
624 | } | ||
625 | |||
626 | static void dn_dev_check_default(struct net_device *dev) | ||
627 | { | ||
628 | write_lock(&dndev_lock); | ||
629 | if (dev == decnet_default_device) { | ||
630 | decnet_default_device = NULL; | ||
631 | } else { | ||
632 | dev = NULL; | ||
633 | } | ||
634 | write_unlock(&dndev_lock); | ||
635 | if (dev) | ||
636 | dev_put(dev); | ||
637 | } | ||
638 | |||
639 | static struct dn_dev *dn_dev_by_index(int ifindex) | ||
640 | { | ||
641 | struct net_device *dev; | ||
642 | struct dn_dev *dn_dev = NULL; | ||
643 | dev = dev_get_by_index(ifindex); | ||
644 | if (dev) { | ||
645 | dn_dev = dev->dn_ptr; | ||
646 | dev_put(dev); | ||
647 | } | ||
648 | |||
649 | return dn_dev; | ||
650 | } | ||
651 | |||
652 | static int dn_dev_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
653 | { | ||
654 | struct rtattr **rta = arg; | ||
655 | struct dn_dev *dn_db; | ||
656 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); | ||
657 | struct dn_ifaddr *ifa, **ifap; | ||
658 | |||
659 | if ((dn_db = dn_dev_by_index(ifm->ifa_index)) == NULL) | ||
660 | return -EADDRNOTAVAIL; | ||
661 | |||
662 | for(ifap = &dn_db->ifa_list; (ifa=*ifap) != NULL; ifap = &ifa->ifa_next) { | ||
663 | void *tmp = rta[IFA_LOCAL-1]; | ||
664 | if ((tmp && memcmp(RTA_DATA(tmp), &ifa->ifa_local, 2)) || | ||
665 | (rta[IFA_LABEL-1] && rtattr_strcmp(rta[IFA_LABEL-1], ifa->ifa_label))) | ||
666 | continue; | ||
667 | |||
668 | dn_dev_del_ifa(dn_db, ifap, 1); | ||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | return -EADDRNOTAVAIL; | ||
673 | } | ||
674 | |||
675 | static int dn_dev_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
676 | { | ||
677 | struct rtattr **rta = arg; | ||
678 | struct net_device *dev; | ||
679 | struct dn_dev *dn_db; | ||
680 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); | ||
681 | struct dn_ifaddr *ifa; | ||
682 | int rv; | ||
683 | |||
684 | if (rta[IFA_LOCAL-1] == NULL) | ||
685 | return -EINVAL; | ||
686 | |||
687 | if ((dev = __dev_get_by_index(ifm->ifa_index)) == NULL) | ||
688 | return -ENODEV; | ||
689 | |||
690 | if ((dn_db = dev->dn_ptr) == NULL) { | ||
691 | int err; | ||
692 | dn_db = dn_dev_create(dev, &err); | ||
693 | if (!dn_db) | ||
694 | return err; | ||
695 | } | ||
696 | |||
697 | if ((ifa = dn_dev_alloc_ifa()) == NULL) | ||
698 | return -ENOBUFS; | ||
699 | |||
700 | if (!rta[IFA_ADDRESS - 1]) | ||
701 | rta[IFA_ADDRESS - 1] = rta[IFA_LOCAL - 1]; | ||
702 | memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL-1]), 2); | ||
703 | memcpy(&ifa->ifa_address, RTA_DATA(rta[IFA_ADDRESS-1]), 2); | ||
704 | ifa->ifa_flags = ifm->ifa_flags; | ||
705 | ifa->ifa_scope = ifm->ifa_scope; | ||
706 | ifa->ifa_dev = dn_db; | ||
707 | if (rta[IFA_LABEL-1]) | ||
708 | rtattr_strlcpy(ifa->ifa_label, rta[IFA_LABEL-1], IFNAMSIZ); | ||
709 | else | ||
710 | memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); | ||
711 | |||
712 | rv = dn_dev_insert_ifa(dn_db, ifa); | ||
713 | if (rv) | ||
714 | dn_dev_free_ifa(ifa); | ||
715 | return rv; | ||
716 | } | ||
717 | |||
718 | static int dn_dev_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, | ||
719 | u32 pid, u32 seq, int event) | ||
720 | { | ||
721 | struct ifaddrmsg *ifm; | ||
722 | struct nlmsghdr *nlh; | ||
723 | unsigned char *b = skb->tail; | ||
724 | |||
725 | nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm)); | ||
726 | ifm = NLMSG_DATA(nlh); | ||
727 | |||
728 | ifm->ifa_family = AF_DECnet; | ||
729 | ifm->ifa_prefixlen = 16; | ||
730 | ifm->ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT; | ||
731 | ifm->ifa_scope = ifa->ifa_scope; | ||
732 | ifm->ifa_index = ifa->ifa_dev->dev->ifindex; | ||
733 | if (ifa->ifa_address) | ||
734 | RTA_PUT(skb, IFA_ADDRESS, 2, &ifa->ifa_address); | ||
735 | if (ifa->ifa_local) | ||
736 | RTA_PUT(skb, IFA_LOCAL, 2, &ifa->ifa_local); | ||
737 | if (ifa->ifa_label[0]) | ||
738 | RTA_PUT(skb, IFA_LABEL, IFNAMSIZ, &ifa->ifa_label); | ||
739 | nlh->nlmsg_len = skb->tail - b; | ||
740 | return skb->len; | ||
741 | |||
742 | nlmsg_failure: | ||
743 | rtattr_failure: | ||
744 | skb_trim(skb, b - skb->data); | ||
745 | return -1; | ||
746 | } | ||
747 | |||
748 | static void rtmsg_ifa(int event, struct dn_ifaddr *ifa) | ||
749 | { | ||
750 | struct sk_buff *skb; | ||
751 | int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128); | ||
752 | |||
753 | skb = alloc_skb(size, GFP_KERNEL); | ||
754 | if (!skb) { | ||
755 | netlink_set_err(rtnl, 0, RTMGRP_DECnet_IFADDR, ENOBUFS); | ||
756 | return; | ||
757 | } | ||
758 | if (dn_dev_fill_ifaddr(skb, ifa, 0, 0, event) < 0) { | ||
759 | kfree_skb(skb); | ||
760 | netlink_set_err(rtnl, 0, RTMGRP_DECnet_IFADDR, EINVAL); | ||
761 | return; | ||
762 | } | ||
763 | NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_IFADDR; | ||
764 | netlink_broadcast(rtnl, skb, 0, RTMGRP_DECnet_IFADDR, GFP_KERNEL); | ||
765 | } | ||
766 | |||
767 | static int dn_dev_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) | ||
768 | { | ||
769 | int idx, dn_idx; | ||
770 | int s_idx, s_dn_idx; | ||
771 | struct net_device *dev; | ||
772 | struct dn_dev *dn_db; | ||
773 | struct dn_ifaddr *ifa; | ||
774 | |||
775 | s_idx = cb->args[0]; | ||
776 | s_dn_idx = dn_idx = cb->args[1]; | ||
777 | read_lock(&dev_base_lock); | ||
778 | for(dev = dev_base, idx = 0; dev; dev = dev->next, idx++) { | ||
779 | if (idx < s_idx) | ||
780 | continue; | ||
781 | if (idx > s_idx) | ||
782 | s_dn_idx = 0; | ||
783 | if ((dn_db = dev->dn_ptr) == NULL) | ||
784 | continue; | ||
785 | |||
786 | for(ifa = dn_db->ifa_list, dn_idx = 0; ifa; ifa = ifa->ifa_next, dn_idx++) { | ||
787 | if (dn_idx < s_dn_idx) | ||
788 | continue; | ||
789 | |||
790 | if (dn_dev_fill_ifaddr(skb, ifa, | ||
791 | NETLINK_CB(cb->skb).pid, | ||
792 | cb->nlh->nlmsg_seq, | ||
793 | RTM_NEWADDR) <= 0) | ||
794 | goto done; | ||
795 | } | ||
796 | } | ||
797 | done: | ||
798 | read_unlock(&dev_base_lock); | ||
799 | cb->args[0] = idx; | ||
800 | cb->args[1] = dn_idx; | ||
801 | |||
802 | return skb->len; | ||
803 | } | ||
804 | |||
805 | static int dn_dev_get_first(struct net_device *dev, dn_address *addr) | ||
806 | { | ||
807 | struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; | ||
808 | struct dn_ifaddr *ifa; | ||
809 | int rv = -ENODEV; | ||
810 | if (dn_db == NULL) | ||
811 | goto out; | ||
812 | ifa = dn_db->ifa_list; | ||
813 | if (ifa != NULL) { | ||
814 | *addr = ifa->ifa_local; | ||
815 | rv = 0; | ||
816 | } | ||
817 | out: | ||
818 | return rv; | ||
819 | } | ||
820 | |||
821 | /* | ||
822 | * Find a default address to bind to. | ||
823 | * | ||
824 | * This is one of those areas where the initial VMS concepts don't really | ||
825 | * map onto the Linux concepts, and since we introduced multiple addresses | ||
826 | * per interface we have to cope with slightly odd ways of finding out what | ||
827 | * "our address" really is. Mostly it's not a problem; for this we just guess | ||
828 | * a sensible default. Eventually the routing code will take care of all the | ||
829 | * nasties for us I hope. | ||
830 | */ | ||
831 | int dn_dev_bind_default(dn_address *addr) | ||
832 | { | ||
833 | struct net_device *dev; | ||
834 | int rv; | ||
835 | dev = dn_dev_get_default(); | ||
836 | last_chance: | ||
837 | if (dev) { | ||
838 | read_lock(&dev_base_lock); | ||
839 | rv = dn_dev_get_first(dev, addr); | ||
840 | read_unlock(&dev_base_lock); | ||
841 | dev_put(dev); | ||
842 | if (rv == 0 || dev == &loopback_dev) | ||
843 | return rv; | ||
844 | } | ||
845 | dev = &loopback_dev; | ||
846 | dev_hold(dev); | ||
847 | goto last_chance; | ||
848 | } | ||
849 | |||
850 | static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa) | ||
851 | { | ||
852 | struct endnode_hello_message *msg; | ||
853 | struct sk_buff *skb = NULL; | ||
854 | unsigned short int *pktlen; | ||
855 | struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; | ||
856 | |||
857 | if ((skb = dn_alloc_skb(NULL, sizeof(*msg), GFP_ATOMIC)) == NULL) | ||
858 | return; | ||
859 | |||
860 | skb->dev = dev; | ||
861 | |||
862 | msg = (struct endnode_hello_message *)skb_put(skb,sizeof(*msg)); | ||
863 | |||
864 | msg->msgflg = 0x0D; | ||
865 | memcpy(msg->tiver, dn_eco_version, 3); | ||
866 | dn_dn2eth(msg->id, ifa->ifa_local); | ||
867 | msg->iinfo = DN_RT_INFO_ENDN; | ||
868 | msg->blksize = dn_htons(mtu2blksize(dev)); | ||
869 | msg->area = 0x00; | ||
870 | memset(msg->seed, 0, 8); | ||
871 | memcpy(msg->neighbor, dn_hiord, ETH_ALEN); | ||
872 | |||
873 | if (dn_db->router) { | ||
874 | struct dn_neigh *dn = (struct dn_neigh *)dn_db->router; | ||
875 | dn_dn2eth(msg->neighbor, dn->addr); | ||
876 | } | ||
877 | |||
878 | msg->timer = dn_htons((unsigned short)dn_db->parms.t3); | ||
879 | msg->mpd = 0x00; | ||
880 | msg->datalen = 0x02; | ||
881 | memset(msg->data, 0xAA, 2); | ||
882 | |||
883 | pktlen = (unsigned short *)skb_push(skb,2); | ||
884 | *pktlen = dn_htons(skb->len - 2); | ||
885 | |||
886 | skb->nh.raw = skb->data; | ||
887 | |||
888 | dn_rt_finish_output(skb, dn_rt_all_rt_mcast, msg->id); | ||
889 | } | ||
890 | |||
891 | |||
892 | #define DRDELAY (5 * HZ) | ||
893 | |||
894 | static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db, struct dn_ifaddr *ifa) | ||
895 | { | ||
896 | /* First check time since device went up */ | ||
897 | if ((jiffies - dn_db->uptime) < DRDELAY) | ||
898 | return 0; | ||
899 | |||
900 | /* If there is no router, then yes... */ | ||
901 | if (!dn_db->router) | ||
902 | return 1; | ||
903 | |||
904 | /* otherwise only if we have a higher priority or.. */ | ||
905 | if (dn->priority < dn_db->parms.priority) | ||
906 | return 1; | ||
907 | |||
908 | /* if we have equal priority and a higher node number */ | ||
909 | if (dn->priority != dn_db->parms.priority) | ||
910 | return 0; | ||
911 | |||
912 | if (dn_ntohs(dn->addr) < dn_ntohs(ifa->ifa_local)) | ||
913 | return 1; | ||
914 | |||
915 | return 0; | ||
916 | } | ||
917 | |||
918 | static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa) | ||
919 | { | ||
920 | int n; | ||
921 | struct dn_dev *dn_db = dev->dn_ptr; | ||
922 | struct dn_neigh *dn = (struct dn_neigh *)dn_db->router; | ||
923 | struct sk_buff *skb; | ||
924 | size_t size; | ||
925 | unsigned char *ptr; | ||
926 | unsigned char *i1, *i2; | ||
927 | unsigned short *pktlen; | ||
928 | char *src; | ||
929 | |||
930 | if (mtu2blksize(dev) < (26 + 7)) | ||
931 | return; | ||
932 | |||
933 | n = mtu2blksize(dev) - 26; | ||
934 | n /= 7; | ||
935 | |||
936 | if (n > 32) | ||
937 | n = 32; | ||
938 | |||
939 | size = 2 + 26 + 7 * n; | ||
940 | |||
941 | if ((skb = dn_alloc_skb(NULL, size, GFP_ATOMIC)) == NULL) | ||
942 | return; | ||
943 | |||
944 | skb->dev = dev; | ||
945 | ptr = skb_put(skb, size); | ||
946 | |||
947 | *ptr++ = DN_RT_PKT_CNTL | DN_RT_PKT_ERTH; | ||
948 | *ptr++ = 2; /* ECO */ | ||
949 | *ptr++ = 0; | ||
950 | *ptr++ = 0; | ||
951 | dn_dn2eth(ptr, ifa->ifa_local); | ||
952 | src = ptr; | ||
953 | ptr += ETH_ALEN; | ||
954 | *ptr++ = dn_db->parms.forwarding == 1 ? | ||
955 | DN_RT_INFO_L1RT : DN_RT_INFO_L2RT; | ||
956 | *((unsigned short *)ptr) = dn_htons(mtu2blksize(dev)); | ||
957 | ptr += 2; | ||
958 | *ptr++ = dn_db->parms.priority; /* Priority */ | ||
959 | *ptr++ = 0; /* Area: Reserved */ | ||
960 | *((unsigned short *)ptr) = dn_htons((unsigned short)dn_db->parms.t3); | ||
961 | ptr += 2; | ||
962 | *ptr++ = 0; /* MPD: Reserved */ | ||
963 | i1 = ptr++; | ||
964 | memset(ptr, 0, 7); /* Name: Reserved */ | ||
965 | ptr += 7; | ||
966 | i2 = ptr++; | ||
967 | |||
968 | n = dn_neigh_elist(dev, ptr, n); | ||
969 | |||
970 | *i2 = 7 * n; | ||
971 | *i1 = 8 + *i2; | ||
972 | |||
973 | skb_trim(skb, (27 + *i2)); | ||
974 | |||
975 | pktlen = (unsigned short *)skb_push(skb, 2); | ||
976 | *pktlen = dn_htons(skb->len - 2); | ||
977 | |||
978 | skb->nh.raw = skb->data; | ||
979 | |||
980 | if (dn_am_i_a_router(dn, dn_db, ifa)) { | ||
981 | struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC); | ||
982 | if (skb2) { | ||
983 | dn_rt_finish_output(skb2, dn_rt_all_end_mcast, src); | ||
984 | } | ||
985 | } | ||
986 | |||
987 | dn_rt_finish_output(skb, dn_rt_all_rt_mcast, src); | ||
988 | } | ||
989 | |||
990 | static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa) | ||
991 | { | ||
992 | struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; | ||
993 | |||
994 | if (dn_db->parms.forwarding == 0) | ||
995 | dn_send_endnode_hello(dev, ifa); | ||
996 | else | ||
997 | dn_send_router_hello(dev, ifa); | ||
998 | } | ||
999 | |||
1000 | static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa) | ||
1001 | { | ||
1002 | int tdlen = 16; | ||
1003 | int size = dev->hard_header_len + 2 + 4 + tdlen; | ||
1004 | struct sk_buff *skb = dn_alloc_skb(NULL, size, GFP_ATOMIC); | ||
1005 | int i; | ||
1006 | unsigned char *ptr; | ||
1007 | char src[ETH_ALEN]; | ||
1008 | |||
1009 | if (skb == NULL) | ||
1010 | return ; | ||
1011 | |||
1012 | skb->dev = dev; | ||
1013 | skb_push(skb, dev->hard_header_len); | ||
1014 | ptr = skb_put(skb, 2 + 4 + tdlen); | ||
1015 | |||
1016 | *ptr++ = DN_RT_PKT_HELO; | ||
1017 | *((dn_address *)ptr) = ifa->ifa_local; | ||
1018 | ptr += 2; | ||
1019 | *ptr++ = tdlen; | ||
1020 | |||
1021 | for(i = 0; i < tdlen; i++) | ||
1022 | *ptr++ = 0252; | ||
1023 | |||
1024 | dn_dn2eth(src, ifa->ifa_local); | ||
1025 | dn_rt_finish_output(skb, dn_rt_all_rt_mcast, src); | ||
1026 | } | ||
1027 | |||
1028 | static int dn_eth_up(struct net_device *dev) | ||
1029 | { | ||
1030 | struct dn_dev *dn_db = dev->dn_ptr; | ||
1031 | |||
1032 | if (dn_db->parms.forwarding == 0) | ||
1033 | dev_mc_add(dev, dn_rt_all_end_mcast, ETH_ALEN, 0); | ||
1034 | else | ||
1035 | dev_mc_add(dev, dn_rt_all_rt_mcast, ETH_ALEN, 0); | ||
1036 | |||
1037 | dev_mc_upload(dev); | ||
1038 | |||
1039 | dn_db->use_long = 1; | ||
1040 | |||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | static void dn_eth_down(struct net_device *dev) | ||
1045 | { | ||
1046 | struct dn_dev *dn_db = dev->dn_ptr; | ||
1047 | |||
1048 | if (dn_db->parms.forwarding == 0) | ||
1049 | dev_mc_delete(dev, dn_rt_all_end_mcast, ETH_ALEN, 0); | ||
1050 | else | ||
1051 | dev_mc_delete(dev, dn_rt_all_rt_mcast, ETH_ALEN, 0); | ||
1052 | } | ||
1053 | |||
1054 | static void dn_dev_set_timer(struct net_device *dev); | ||
1055 | |||
1056 | static void dn_dev_timer_func(unsigned long arg) | ||
1057 | { | ||
1058 | struct net_device *dev = (struct net_device *)arg; | ||
1059 | struct dn_dev *dn_db = dev->dn_ptr; | ||
1060 | struct dn_ifaddr *ifa; | ||
1061 | |||
1062 | if (dn_db->t3 <= dn_db->parms.t2) { | ||
1063 | if (dn_db->parms.timer3) { | ||
1064 | for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) { | ||
1065 | if (!(ifa->ifa_flags & IFA_F_SECONDARY)) | ||
1066 | dn_db->parms.timer3(dev, ifa); | ||
1067 | } | ||
1068 | } | ||
1069 | dn_db->t3 = dn_db->parms.t3; | ||
1070 | } else { | ||
1071 | dn_db->t3 -= dn_db->parms.t2; | ||
1072 | } | ||
1073 | |||
1074 | dn_dev_set_timer(dev); | ||
1075 | } | ||
1076 | |||
1077 | static void dn_dev_set_timer(struct net_device *dev) | ||
1078 | { | ||
1079 | struct dn_dev *dn_db = dev->dn_ptr; | ||
1080 | |||
1081 | if (dn_db->parms.t2 > dn_db->parms.t3) | ||
1082 | dn_db->parms.t2 = dn_db->parms.t3; | ||
1083 | |||
1084 | dn_db->timer.data = (unsigned long)dev; | ||
1085 | dn_db->timer.function = dn_dev_timer_func; | ||
1086 | dn_db->timer.expires = jiffies + (dn_db->parms.t2 * HZ); | ||
1087 | |||
1088 | add_timer(&dn_db->timer); | ||
1089 | } | ||
1090 | |||
1091 | struct dn_dev *dn_dev_create(struct net_device *dev, int *err) | ||
1092 | { | ||
1093 | int i; | ||
1094 | struct dn_dev_parms *p = dn_dev_list; | ||
1095 | struct dn_dev *dn_db; | ||
1096 | |||
1097 | for(i = 0; i < DN_DEV_LIST_SIZE; i++, p++) { | ||
1098 | if (p->type == dev->type) | ||
1099 | break; | ||
1100 | } | ||
1101 | |||
1102 | *err = -ENODEV; | ||
1103 | if (i == DN_DEV_LIST_SIZE) | ||
1104 | return NULL; | ||
1105 | |||
1106 | *err = -ENOBUFS; | ||
1107 | if ((dn_db = kmalloc(sizeof(struct dn_dev), GFP_ATOMIC)) == NULL) | ||
1108 | return NULL; | ||
1109 | |||
1110 | memset(dn_db, 0, sizeof(struct dn_dev)); | ||
1111 | memcpy(&dn_db->parms, p, sizeof(struct dn_dev_parms)); | ||
1112 | smp_wmb(); | ||
1113 | dev->dn_ptr = dn_db; | ||
1114 | dn_db->dev = dev; | ||
1115 | init_timer(&dn_db->timer); | ||
1116 | |||
1117 | dn_db->uptime = jiffies; | ||
1118 | if (dn_db->parms.up) { | ||
1119 | if (dn_db->parms.up(dev) < 0) { | ||
1120 | dev->dn_ptr = NULL; | ||
1121 | kfree(dn_db); | ||
1122 | return NULL; | ||
1123 | } | ||
1124 | } | ||
1125 | |||
1126 | dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table); | ||
1127 | |||
1128 | dn_dev_sysctl_register(dev, &dn_db->parms); | ||
1129 | |||
1130 | dn_dev_set_timer(dev); | ||
1131 | |||
1132 | *err = 0; | ||
1133 | return dn_db; | ||
1134 | } | ||
1135 | |||
1136 | |||
1137 | /* | ||
1138 | * This processes a device up event. We only start up | ||
1139 | * the loopback device & ethernet devices with correct | ||
1140 | * MAC addreses automatically. Others must be started | ||
1141 | * specifically. | ||
1142 | * | ||
1143 | * FIXME: How should we configure the loopback address ? If we could dispense | ||
1144 | * with using decnet_address here and for autobind, it will be one less thing | ||
1145 | * for users to worry about setting up. | ||
1146 | */ | ||
1147 | |||
1148 | void dn_dev_up(struct net_device *dev) | ||
1149 | { | ||
1150 | struct dn_ifaddr *ifa; | ||
1151 | dn_address addr = decnet_address; | ||
1152 | int maybe_default = 0; | ||
1153 | struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; | ||
1154 | |||
1155 | if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK)) | ||
1156 | return; | ||
1157 | |||
1158 | /* | ||
1159 | * Need to ensure that loopback device has a dn_db attached to it | ||
1160 | * to allow creation of neighbours against it, even though it might | ||
1161 | * not have a local address of its own. Might as well do the same for | ||
1162 | * all autoconfigured interfaces. | ||
1163 | */ | ||
1164 | if (dn_db == NULL) { | ||
1165 | int err; | ||
1166 | dn_db = dn_dev_create(dev, &err); | ||
1167 | if (dn_db == NULL) | ||
1168 | return; | ||
1169 | } | ||
1170 | |||
1171 | if (dev->type == ARPHRD_ETHER) { | ||
1172 | if (memcmp(dev->dev_addr, dn_hiord, 4) != 0) | ||
1173 | return; | ||
1174 | addr = dn_htons(dn_eth2dn(dev->dev_addr)); | ||
1175 | maybe_default = 1; | ||
1176 | } | ||
1177 | |||
1178 | if (addr == 0) | ||
1179 | return; | ||
1180 | |||
1181 | if ((ifa = dn_dev_alloc_ifa()) == NULL) | ||
1182 | return; | ||
1183 | |||
1184 | ifa->ifa_local = ifa->ifa_address = addr; | ||
1185 | ifa->ifa_flags = 0; | ||
1186 | ifa->ifa_scope = RT_SCOPE_UNIVERSE; | ||
1187 | strcpy(ifa->ifa_label, dev->name); | ||
1188 | |||
1189 | dn_dev_set_ifa(dev, ifa); | ||
1190 | |||
1191 | /* | ||
1192 | * Automagically set the default device to the first automatically | ||
1193 | * configured ethernet card in the system. | ||
1194 | */ | ||
1195 | if (maybe_default) { | ||
1196 | dev_hold(dev); | ||
1197 | if (dn_dev_set_default(dev, 0)) | ||
1198 | dev_put(dev); | ||
1199 | } | ||
1200 | } | ||
1201 | |||
1202 | static void dn_dev_delete(struct net_device *dev) | ||
1203 | { | ||
1204 | struct dn_dev *dn_db = dev->dn_ptr; | ||
1205 | |||
1206 | if (dn_db == NULL) | ||
1207 | return; | ||
1208 | |||
1209 | del_timer_sync(&dn_db->timer); | ||
1210 | dn_dev_sysctl_unregister(&dn_db->parms); | ||
1211 | dn_dev_check_default(dev); | ||
1212 | neigh_ifdown(&dn_neigh_table, dev); | ||
1213 | |||
1214 | if (dn_db->parms.down) | ||
1215 | dn_db->parms.down(dev); | ||
1216 | |||
1217 | dev->dn_ptr = NULL; | ||
1218 | |||
1219 | neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms); | ||
1220 | neigh_ifdown(&dn_neigh_table, dev); | ||
1221 | |||
1222 | if (dn_db->router) | ||
1223 | neigh_release(dn_db->router); | ||
1224 | if (dn_db->peer) | ||
1225 | neigh_release(dn_db->peer); | ||
1226 | |||
1227 | kfree(dn_db); | ||
1228 | } | ||
1229 | |||
1230 | void dn_dev_down(struct net_device *dev) | ||
1231 | { | ||
1232 | struct dn_dev *dn_db = dev->dn_ptr; | ||
1233 | struct dn_ifaddr *ifa; | ||
1234 | |||
1235 | if (dn_db == NULL) | ||
1236 | return; | ||
1237 | |||
1238 | while((ifa = dn_db->ifa_list) != NULL) { | ||
1239 | dn_dev_del_ifa(dn_db, &dn_db->ifa_list, 0); | ||
1240 | dn_dev_free_ifa(ifa); | ||
1241 | } | ||
1242 | |||
1243 | dn_dev_delete(dev); | ||
1244 | } | ||
1245 | |||
1246 | void dn_dev_init_pkt(struct sk_buff *skb) | ||
1247 | { | ||
1248 | return; | ||
1249 | } | ||
1250 | |||
1251 | void dn_dev_veri_pkt(struct sk_buff *skb) | ||
1252 | { | ||
1253 | return; | ||
1254 | } | ||
1255 | |||
1256 | void dn_dev_hello(struct sk_buff *skb) | ||
1257 | { | ||
1258 | return; | ||
1259 | } | ||
1260 | |||
1261 | void dn_dev_devices_off(void) | ||
1262 | { | ||
1263 | struct net_device *dev; | ||
1264 | |||
1265 | rtnl_lock(); | ||
1266 | for(dev = dev_base; dev; dev = dev->next) | ||
1267 | dn_dev_down(dev); | ||
1268 | rtnl_unlock(); | ||
1269 | |||
1270 | } | ||
1271 | |||
1272 | void dn_dev_devices_on(void) | ||
1273 | { | ||
1274 | struct net_device *dev; | ||
1275 | |||
1276 | rtnl_lock(); | ||
1277 | for(dev = dev_base; dev; dev = dev->next) { | ||
1278 | if (dev->flags & IFF_UP) | ||
1279 | dn_dev_up(dev); | ||
1280 | } | ||
1281 | rtnl_unlock(); | ||
1282 | } | ||
1283 | |||
1284 | int register_dnaddr_notifier(struct notifier_block *nb) | ||
1285 | { | ||
1286 | return notifier_chain_register(&dnaddr_chain, nb); | ||
1287 | } | ||
1288 | |||
1289 | int unregister_dnaddr_notifier(struct notifier_block *nb) | ||
1290 | { | ||
1291 | return notifier_chain_unregister(&dnaddr_chain, nb); | ||
1292 | } | ||
1293 | |||
1294 | #ifdef CONFIG_PROC_FS | ||
1295 | static inline struct net_device *dn_dev_get_next(struct seq_file *seq, struct net_device *dev) | ||
1296 | { | ||
1297 | do { | ||
1298 | dev = dev->next; | ||
1299 | } while(dev && !dev->dn_ptr); | ||
1300 | |||
1301 | return dev; | ||
1302 | } | ||
1303 | |||
1304 | static struct net_device *dn_dev_get_idx(struct seq_file *seq, loff_t pos) | ||
1305 | { | ||
1306 | struct net_device *dev; | ||
1307 | |||
1308 | dev = dev_base; | ||
1309 | if (dev && !dev->dn_ptr) | ||
1310 | dev = dn_dev_get_next(seq, dev); | ||
1311 | if (pos) { | ||
1312 | while(dev && (dev = dn_dev_get_next(seq, dev))) | ||
1313 | --pos; | ||
1314 | } | ||
1315 | return dev; | ||
1316 | } | ||
1317 | |||
1318 | static void *dn_dev_seq_start(struct seq_file *seq, loff_t *pos) | ||
1319 | { | ||
1320 | if (*pos) { | ||
1321 | struct net_device *dev; | ||
1322 | read_lock(&dev_base_lock); | ||
1323 | dev = dn_dev_get_idx(seq, *pos - 1); | ||
1324 | if (dev == NULL) | ||
1325 | read_unlock(&dev_base_lock); | ||
1326 | return dev; | ||
1327 | } | ||
1328 | return SEQ_START_TOKEN; | ||
1329 | } | ||
1330 | |||
1331 | static void *dn_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
1332 | { | ||
1333 | struct net_device *dev = v; | ||
1334 | loff_t one = 1; | ||
1335 | |||
1336 | if (v == SEQ_START_TOKEN) { | ||
1337 | dev = dn_dev_seq_start(seq, &one); | ||
1338 | } else { | ||
1339 | dev = dn_dev_get_next(seq, dev); | ||
1340 | if (dev == NULL) | ||
1341 | read_unlock(&dev_base_lock); | ||
1342 | } | ||
1343 | ++*pos; | ||
1344 | return dev; | ||
1345 | } | ||
1346 | |||
1347 | static void dn_dev_seq_stop(struct seq_file *seq, void *v) | ||
1348 | { | ||
1349 | if (v && v != SEQ_START_TOKEN) | ||
1350 | read_unlock(&dev_base_lock); | ||
1351 | } | ||
1352 | |||
1353 | static char *dn_type2asc(char type) | ||
1354 | { | ||
1355 | switch(type) { | ||
1356 | case DN_DEV_BCAST: | ||
1357 | return "B"; | ||
1358 | case DN_DEV_UCAST: | ||
1359 | return "U"; | ||
1360 | case DN_DEV_MPOINT: | ||
1361 | return "M"; | ||
1362 | } | ||
1363 | |||
1364 | return "?"; | ||
1365 | } | ||
1366 | |||
1367 | static int dn_dev_seq_show(struct seq_file *seq, void *v) | ||
1368 | { | ||
1369 | if (v == SEQ_START_TOKEN) | ||
1370 | seq_puts(seq, "Name Flags T1 Timer1 T3 Timer3 BlkSize Pri State DevType Router Peer\n"); | ||
1371 | else { | ||
1372 | struct net_device *dev = v; | ||
1373 | char peer_buf[DN_ASCBUF_LEN]; | ||
1374 | char router_buf[DN_ASCBUF_LEN]; | ||
1375 | struct dn_dev *dn_db = dev->dn_ptr; | ||
1376 | |||
1377 | seq_printf(seq, "%-8s %1s %04u %04u %04lu %04lu" | ||
1378 | " %04hu %03d %02x %-10s %-7s %-7s\n", | ||
1379 | dev->name ? dev->name : "???", | ||
1380 | dn_type2asc(dn_db->parms.mode), | ||
1381 | 0, 0, | ||
1382 | dn_db->t3, dn_db->parms.t3, | ||
1383 | mtu2blksize(dev), | ||
1384 | dn_db->parms.priority, | ||
1385 | dn_db->parms.state, dn_db->parms.name, | ||
1386 | dn_db->router ? dn_addr2asc(dn_ntohs(*(dn_address *)dn_db->router->primary_key), router_buf) : "", | ||
1387 | dn_db->peer ? dn_addr2asc(dn_ntohs(*(dn_address *)dn_db->peer->primary_key), peer_buf) : ""); | ||
1388 | } | ||
1389 | return 0; | ||
1390 | } | ||
1391 | |||
1392 | static struct seq_operations dn_dev_seq_ops = { | ||
1393 | .start = dn_dev_seq_start, | ||
1394 | .next = dn_dev_seq_next, | ||
1395 | .stop = dn_dev_seq_stop, | ||
1396 | .show = dn_dev_seq_show, | ||
1397 | }; | ||
1398 | |||
1399 | static int dn_dev_seq_open(struct inode *inode, struct file *file) | ||
1400 | { | ||
1401 | return seq_open(file, &dn_dev_seq_ops); | ||
1402 | } | ||
1403 | |||
1404 | static struct file_operations dn_dev_seq_fops = { | ||
1405 | .owner = THIS_MODULE, | ||
1406 | .open = dn_dev_seq_open, | ||
1407 | .read = seq_read, | ||
1408 | .llseek = seq_lseek, | ||
1409 | .release = seq_release, | ||
1410 | }; | ||
1411 | |||
1412 | #endif /* CONFIG_PROC_FS */ | ||
1413 | |||
1414 | static struct rtnetlink_link dnet_rtnetlink_table[RTM_MAX-RTM_BASE+1] = | ||
1415 | { | ||
1416 | [4] = { .doit = dn_dev_rtm_newaddr, }, | ||
1417 | [5] = { .doit = dn_dev_rtm_deladdr, }, | ||
1418 | [6] = { .dumpit = dn_dev_dump_ifaddr, }, | ||
1419 | |||
1420 | #ifdef CONFIG_DECNET_ROUTER | ||
1421 | [8] = { .doit = dn_fib_rtm_newroute, }, | ||
1422 | [9] = { .doit = dn_fib_rtm_delroute, }, | ||
1423 | [10] = { .doit = dn_cache_getroute, .dumpit = dn_fib_dump, }, | ||
1424 | [16] = { .doit = dn_fib_rtm_newrule, }, | ||
1425 | [17] = { .doit = dn_fib_rtm_delrule, }, | ||
1426 | [18] = { .dumpit = dn_fib_dump_rules, }, | ||
1427 | #else | ||
1428 | [10] = { .doit = dn_cache_getroute, .dumpit = dn_cache_dump, }, | ||
1429 | #endif | ||
1430 | |||
1431 | }; | ||
1432 | |||
1433 | static int __initdata addr[2]; | ||
1434 | module_param_array(addr, int, NULL, 0444); | ||
1435 | MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node"); | ||
1436 | |||
1437 | void __init dn_dev_init(void) | ||
1438 | { | ||
1439 | if (addr[0] > 63 || addr[0] < 0) { | ||
1440 | printk(KERN_ERR "DECnet: Area must be between 0 and 63"); | ||
1441 | return; | ||
1442 | } | ||
1443 | |||
1444 | if (addr[1] > 1023 || addr[1] < 0) { | ||
1445 | printk(KERN_ERR "DECnet: Node must be between 0 and 1023"); | ||
1446 | return; | ||
1447 | } | ||
1448 | |||
1449 | decnet_address = dn_htons((addr[0] << 10) | addr[1]); | ||
1450 | |||
1451 | dn_dev_devices_on(); | ||
1452 | |||
1453 | rtnetlink_links[PF_DECnet] = dnet_rtnetlink_table; | ||
1454 | |||
1455 | proc_net_fops_create("decnet_dev", S_IRUGO, &dn_dev_seq_fops); | ||
1456 | |||
1457 | #ifdef CONFIG_SYSCTL | ||
1458 | { | ||
1459 | int i; | ||
1460 | for(i = 0; i < DN_DEV_LIST_SIZE; i++) | ||
1461 | dn_dev_sysctl_register(NULL, &dn_dev_list[i]); | ||
1462 | } | ||
1463 | #endif /* CONFIG_SYSCTL */ | ||
1464 | } | ||
1465 | |||
1466 | void __exit dn_dev_cleanup(void) | ||
1467 | { | ||
1468 | rtnetlink_links[PF_DECnet] = NULL; | ||
1469 | |||
1470 | #ifdef CONFIG_SYSCTL | ||
1471 | { | ||
1472 | int i; | ||
1473 | for(i = 0; i < DN_DEV_LIST_SIZE; i++) | ||
1474 | dn_dev_sysctl_unregister(&dn_dev_list[i]); | ||
1475 | } | ||
1476 | #endif /* CONFIG_SYSCTL */ | ||
1477 | |||
1478 | proc_net_remove("decnet_dev"); | ||
1479 | |||
1480 | dn_dev_devices_off(); | ||
1481 | } | ||
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c new file mode 100644 index 000000000000..9934b25720e4 --- /dev/null +++ b/net/decnet/dn_fib.c | |||
@@ -0,0 +1,802 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet Routing Forwarding Information Base (Glue/Info List) | ||
7 | * | ||
8 | * Author: Steve Whitehouse <SteveW@ACM.org> | ||
9 | * | ||
10 | * | ||
11 | * Changes: | ||
12 | * Alexey Kuznetsov : SMP locking changes | ||
13 | * Steve Whitehouse : Rewrote it... Well to be more correct, I | ||
14 | * copied most of it from the ipv4 fib code. | ||
15 | * Steve Whitehouse : Updated it in style and fixed a few bugs | ||
16 | * which were fixed in the ipv4 code since | ||
17 | * this code was copied from it. | ||
18 | * | ||
19 | */ | ||
20 | #include <linux/config.h> | ||
21 | #include <linux/string.h> | ||
22 | #include <linux/net.h> | ||
23 | #include <linux/socket.h> | ||
24 | #include <linux/sockios.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/skbuff.h> | ||
27 | #include <linux/netlink.h> | ||
28 | #include <linux/rtnetlink.h> | ||
29 | #include <linux/proc_fs.h> | ||
30 | #include <linux/netdevice.h> | ||
31 | #include <linux/timer.h> | ||
32 | #include <linux/spinlock.h> | ||
33 | #include <asm/atomic.h> | ||
34 | #include <asm/uaccess.h> | ||
35 | #include <net/neighbour.h> | ||
36 | #include <net/dst.h> | ||
37 | #include <net/flow.h> | ||
38 | #include <net/dn.h> | ||
39 | #include <net/dn_route.h> | ||
40 | #include <net/dn_fib.h> | ||
41 | #include <net/dn_neigh.h> | ||
42 | #include <net/dn_dev.h> | ||
43 | |||
44 | #define RT_MIN_TABLE 1 | ||
45 | |||
46 | #define for_fib_info() { struct dn_fib_info *fi;\ | ||
47 | for(fi = dn_fib_info_list; fi; fi = fi->fib_next) | ||
48 | #define endfor_fib_info() } | ||
49 | |||
50 | #define for_nexthops(fi) { int nhsel; const struct dn_fib_nh *nh;\ | ||
51 | for(nhsel = 0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++) | ||
52 | |||
53 | #define change_nexthops(fi) { int nhsel; struct dn_fib_nh *nh;\ | ||
54 | for(nhsel = 0, nh = (struct dn_fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++) | ||
55 | |||
56 | #define endfor_nexthops(fi) } | ||
57 | |||
58 | extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb); | ||
59 | |||
60 | static DEFINE_SPINLOCK(dn_fib_multipath_lock); | ||
61 | static struct dn_fib_info *dn_fib_info_list; | ||
62 | static DEFINE_RWLOCK(dn_fib_info_lock); | ||
63 | |||
64 | static struct | ||
65 | { | ||
66 | int error; | ||
67 | u8 scope; | ||
68 | } dn_fib_props[RTA_MAX+1] = { | ||
69 | [RTN_UNSPEC] = { .error = 0, .scope = RT_SCOPE_NOWHERE }, | ||
70 | [RTN_UNICAST] = { .error = 0, .scope = RT_SCOPE_UNIVERSE }, | ||
71 | [RTN_LOCAL] = { .error = 0, .scope = RT_SCOPE_HOST }, | ||
72 | [RTN_BROADCAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }, | ||
73 | [RTN_ANYCAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }, | ||
74 | [RTN_MULTICAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }, | ||
75 | [RTN_BLACKHOLE] = { .error = -EINVAL, .scope = RT_SCOPE_UNIVERSE }, | ||
76 | [RTN_UNREACHABLE] = { .error = -EHOSTUNREACH, .scope = RT_SCOPE_UNIVERSE }, | ||
77 | [RTN_PROHIBIT] = { .error = -EACCES, .scope = RT_SCOPE_UNIVERSE }, | ||
78 | [RTN_THROW] = { .error = -EAGAIN, .scope = RT_SCOPE_UNIVERSE }, | ||
79 | [RTN_NAT] = { .error = 0, .scope = RT_SCOPE_NOWHERE }, | ||
80 | [RTN_XRESOLVE] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE }, | ||
81 | }; | ||
82 | |||
83 | void dn_fib_free_info(struct dn_fib_info *fi) | ||
84 | { | ||
85 | if (fi->fib_dead == 0) { | ||
86 | printk(KERN_DEBUG "DECnet: BUG! Attempt to free alive dn_fib_info\n"); | ||
87 | return; | ||
88 | } | ||
89 | |||
90 | change_nexthops(fi) { | ||
91 | if (nh->nh_dev) | ||
92 | dev_put(nh->nh_dev); | ||
93 | nh->nh_dev = NULL; | ||
94 | } endfor_nexthops(fi); | ||
95 | kfree(fi); | ||
96 | } | ||
97 | |||
98 | void dn_fib_release_info(struct dn_fib_info *fi) | ||
99 | { | ||
100 | write_lock(&dn_fib_info_lock); | ||
101 | if (fi && --fi->fib_treeref == 0) { | ||
102 | if (fi->fib_next) | ||
103 | fi->fib_next->fib_prev = fi->fib_prev; | ||
104 | if (fi->fib_prev) | ||
105 | fi->fib_prev->fib_next = fi->fib_next; | ||
106 | if (fi == dn_fib_info_list) | ||
107 | dn_fib_info_list = fi->fib_next; | ||
108 | fi->fib_dead = 1; | ||
109 | dn_fib_info_put(fi); | ||
110 | } | ||
111 | write_unlock(&dn_fib_info_lock); | ||
112 | } | ||
113 | |||
114 | static inline int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi) | ||
115 | { | ||
116 | const struct dn_fib_nh *onh = ofi->fib_nh; | ||
117 | |||
118 | for_nexthops(fi) { | ||
119 | if (nh->nh_oif != onh->nh_oif || | ||
120 | nh->nh_gw != onh->nh_gw || | ||
121 | nh->nh_scope != onh->nh_scope || | ||
122 | nh->nh_weight != onh->nh_weight || | ||
123 | ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD)) | ||
124 | return -1; | ||
125 | onh++; | ||
126 | } endfor_nexthops(fi); | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi) | ||
131 | { | ||
132 | for_fib_info() { | ||
133 | if (fi->fib_nhs != nfi->fib_nhs) | ||
134 | continue; | ||
135 | if (nfi->fib_protocol == fi->fib_protocol && | ||
136 | nfi->fib_prefsrc == fi->fib_prefsrc && | ||
137 | nfi->fib_priority == fi->fib_priority && | ||
138 | memcmp(nfi->fib_metrics, fi->fib_metrics, sizeof(fi->fib_metrics)) == 0 && | ||
139 | ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 && | ||
140 | (nfi->fib_nhs == 0 || dn_fib_nh_comp(fi, nfi) == 0)) | ||
141 | return fi; | ||
142 | } endfor_fib_info(); | ||
143 | return NULL; | ||
144 | } | ||
145 | |||
146 | u16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type) | ||
147 | { | ||
148 | while(RTA_OK(attr,attrlen)) { | ||
149 | if (attr->rta_type == type) | ||
150 | return *(u16*)RTA_DATA(attr); | ||
151 | attr = RTA_NEXT(attr, attrlen); | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int dn_fib_count_nhs(struct rtattr *rta) | ||
158 | { | ||
159 | int nhs = 0; | ||
160 | struct rtnexthop *nhp = RTA_DATA(rta); | ||
161 | int nhlen = RTA_PAYLOAD(rta); | ||
162 | |||
163 | while(nhlen >= (int)sizeof(struct rtnexthop)) { | ||
164 | if ((nhlen -= nhp->rtnh_len) < 0) | ||
165 | return 0; | ||
166 | nhs++; | ||
167 | nhp = RTNH_NEXT(nhp); | ||
168 | } | ||
169 | |||
170 | return nhs; | ||
171 | } | ||
172 | |||
173 | static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, const struct rtmsg *r) | ||
174 | { | ||
175 | struct rtnexthop *nhp = RTA_DATA(rta); | ||
176 | int nhlen = RTA_PAYLOAD(rta); | ||
177 | |||
178 | change_nexthops(fi) { | ||
179 | int attrlen = nhlen - sizeof(struct rtnexthop); | ||
180 | if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0) | ||
181 | return -EINVAL; | ||
182 | |||
183 | nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags; | ||
184 | nh->nh_oif = nhp->rtnh_ifindex; | ||
185 | nh->nh_weight = nhp->rtnh_hops + 1; | ||
186 | |||
187 | if (attrlen) { | ||
188 | nh->nh_gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY); | ||
189 | } | ||
190 | nhp = RTNH_NEXT(nhp); | ||
191 | } endfor_nexthops(fi); | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | |||
197 | static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct dn_fib_nh *nh) | ||
198 | { | ||
199 | int err; | ||
200 | |||
201 | if (nh->nh_gw) { | ||
202 | struct flowi fl; | ||
203 | struct dn_fib_res res; | ||
204 | |||
205 | memset(&fl, 0, sizeof(fl)); | ||
206 | |||
207 | if (nh->nh_flags&RTNH_F_ONLINK) { | ||
208 | struct net_device *dev; | ||
209 | |||
210 | if (r->rtm_scope >= RT_SCOPE_LINK) | ||
211 | return -EINVAL; | ||
212 | if (dnet_addr_type(nh->nh_gw) != RTN_UNICAST) | ||
213 | return -EINVAL; | ||
214 | if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL) | ||
215 | return -ENODEV; | ||
216 | if (!(dev->flags&IFF_UP)) | ||
217 | return -ENETDOWN; | ||
218 | nh->nh_dev = dev; | ||
219 | dev_hold(dev); | ||
220 | nh->nh_scope = RT_SCOPE_LINK; | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | memset(&fl, 0, sizeof(fl)); | ||
225 | fl.fld_dst = nh->nh_gw; | ||
226 | fl.oif = nh->nh_oif; | ||
227 | fl.fld_scope = r->rtm_scope + 1; | ||
228 | |||
229 | if (fl.fld_scope < RT_SCOPE_LINK) | ||
230 | fl.fld_scope = RT_SCOPE_LINK; | ||
231 | |||
232 | if ((err = dn_fib_lookup(&fl, &res)) != 0) | ||
233 | return err; | ||
234 | |||
235 | err = -EINVAL; | ||
236 | if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) | ||
237 | goto out; | ||
238 | nh->nh_scope = res.scope; | ||
239 | nh->nh_oif = DN_FIB_RES_OIF(res); | ||
240 | nh->nh_dev = DN_FIB_RES_DEV(res); | ||
241 | if (nh->nh_dev == NULL) | ||
242 | goto out; | ||
243 | dev_hold(nh->nh_dev); | ||
244 | err = -ENETDOWN; | ||
245 | if (!(nh->nh_dev->flags & IFF_UP)) | ||
246 | goto out; | ||
247 | err = 0; | ||
248 | out: | ||
249 | dn_fib_res_put(&res); | ||
250 | return err; | ||
251 | } else { | ||
252 | struct net_device *dev; | ||
253 | |||
254 | if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK)) | ||
255 | return -EINVAL; | ||
256 | |||
257 | dev = __dev_get_by_index(nh->nh_oif); | ||
258 | if (dev == NULL || dev->dn_ptr == NULL) | ||
259 | return -ENODEV; | ||
260 | if (!(dev->flags&IFF_UP)) | ||
261 | return -ENETDOWN; | ||
262 | nh->nh_dev = dev; | ||
263 | dev_hold(nh->nh_dev); | ||
264 | nh->nh_scope = RT_SCOPE_HOST; | ||
265 | } | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | |||
271 | struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta *rta, const struct nlmsghdr *nlh, int *errp) | ||
272 | { | ||
273 | int err; | ||
274 | struct dn_fib_info *fi = NULL; | ||
275 | struct dn_fib_info *ofi; | ||
276 | int nhs = 1; | ||
277 | |||
278 | if (dn_fib_props[r->rtm_type].scope > r->rtm_scope) | ||
279 | goto err_inval; | ||
280 | |||
281 | if (rta->rta_mp) { | ||
282 | nhs = dn_fib_count_nhs(rta->rta_mp); | ||
283 | if (nhs == 0) | ||
284 | goto err_inval; | ||
285 | } | ||
286 | |||
287 | fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL); | ||
288 | err = -ENOBUFS; | ||
289 | if (fi == NULL) | ||
290 | goto failure; | ||
291 | memset(fi, 0, sizeof(*fi)+nhs*sizeof(struct dn_fib_nh)); | ||
292 | |||
293 | fi->fib_protocol = r->rtm_protocol; | ||
294 | fi->fib_nhs = nhs; | ||
295 | fi->fib_flags = r->rtm_flags; | ||
296 | if (rta->rta_priority) | ||
297 | fi->fib_priority = *rta->rta_priority; | ||
298 | if (rta->rta_mx) { | ||
299 | int attrlen = RTA_PAYLOAD(rta->rta_mx); | ||
300 | struct rtattr *attr = RTA_DATA(rta->rta_mx); | ||
301 | |||
302 | while(RTA_OK(attr, attrlen)) { | ||
303 | unsigned flavour = attr->rta_type; | ||
304 | if (flavour) { | ||
305 | if (flavour > RTAX_MAX) | ||
306 | goto err_inval; | ||
307 | fi->fib_metrics[flavour-1] = *(unsigned*)RTA_DATA(attr); | ||
308 | } | ||
309 | attr = RTA_NEXT(attr, attrlen); | ||
310 | } | ||
311 | } | ||
312 | if (rta->rta_prefsrc) | ||
313 | memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2); | ||
314 | |||
315 | if (rta->rta_mp) { | ||
316 | if ((err = dn_fib_get_nhs(fi, rta->rta_mp, r)) != 0) | ||
317 | goto failure; | ||
318 | if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif) | ||
319 | goto err_inval; | ||
320 | if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 2)) | ||
321 | goto err_inval; | ||
322 | } else { | ||
323 | struct dn_fib_nh *nh = fi->fib_nh; | ||
324 | if (rta->rta_oif) | ||
325 | nh->nh_oif = *rta->rta_oif; | ||
326 | if (rta->rta_gw) | ||
327 | memcpy(&nh->nh_gw, rta->rta_gw, 2); | ||
328 | nh->nh_flags = r->rtm_flags; | ||
329 | nh->nh_weight = 1; | ||
330 | } | ||
331 | |||
332 | if (r->rtm_type == RTN_NAT) { | ||
333 | if (rta->rta_gw == NULL || nhs != 1 || rta->rta_oif) | ||
334 | goto err_inval; | ||
335 | memcpy(&fi->fib_nh->nh_gw, rta->rta_gw, 2); | ||
336 | goto link_it; | ||
337 | } | ||
338 | |||
339 | if (dn_fib_props[r->rtm_type].error) { | ||
340 | if (rta->rta_gw || rta->rta_oif || rta->rta_mp) | ||
341 | goto err_inval; | ||
342 | goto link_it; | ||
343 | } | ||
344 | |||
345 | if (r->rtm_scope > RT_SCOPE_HOST) | ||
346 | goto err_inval; | ||
347 | |||
348 | if (r->rtm_scope == RT_SCOPE_HOST) { | ||
349 | struct dn_fib_nh *nh = fi->fib_nh; | ||
350 | |||
351 | /* Local address is added */ | ||
352 | if (nhs != 1 || nh->nh_gw) | ||
353 | goto err_inval; | ||
354 | nh->nh_scope = RT_SCOPE_NOWHERE; | ||
355 | nh->nh_dev = dev_get_by_index(fi->fib_nh->nh_oif); | ||
356 | err = -ENODEV; | ||
357 | if (nh->nh_dev == NULL) | ||
358 | goto failure; | ||
359 | } else { | ||
360 | change_nexthops(fi) { | ||
361 | if ((err = dn_fib_check_nh(r, fi, nh)) != 0) | ||
362 | goto failure; | ||
363 | } endfor_nexthops(fi) | ||
364 | } | ||
365 | |||
366 | if (fi->fib_prefsrc) { | ||
367 | if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL || | ||
368 | memcmp(&fi->fib_prefsrc, rta->rta_dst, 2)) | ||
369 | if (dnet_addr_type(fi->fib_prefsrc) != RTN_LOCAL) | ||
370 | goto err_inval; | ||
371 | } | ||
372 | |||
373 | link_it: | ||
374 | if ((ofi = dn_fib_find_info(fi)) != NULL) { | ||
375 | fi->fib_dead = 1; | ||
376 | dn_fib_free_info(fi); | ||
377 | ofi->fib_treeref++; | ||
378 | return ofi; | ||
379 | } | ||
380 | |||
381 | fi->fib_treeref++; | ||
382 | atomic_inc(&fi->fib_clntref); | ||
383 | write_lock(&dn_fib_info_lock); | ||
384 | fi->fib_next = dn_fib_info_list; | ||
385 | fi->fib_prev = NULL; | ||
386 | if (dn_fib_info_list) | ||
387 | dn_fib_info_list->fib_prev = fi; | ||
388 | dn_fib_info_list = fi; | ||
389 | write_unlock(&dn_fib_info_lock); | ||
390 | return fi; | ||
391 | |||
392 | err_inval: | ||
393 | err = -EINVAL; | ||
394 | |||
395 | failure: | ||
396 | *errp = err; | ||
397 | if (fi) { | ||
398 | fi->fib_dead = 1; | ||
399 | dn_fib_free_info(fi); | ||
400 | } | ||
401 | |||
402 | return NULL; | ||
403 | } | ||
404 | |||
405 | int dn_fib_semantic_match(int type, struct dn_fib_info *fi, const struct flowi *fl, struct dn_fib_res *res) | ||
406 | { | ||
407 | int err = dn_fib_props[type].error; | ||
408 | |||
409 | if (err == 0) { | ||
410 | if (fi->fib_flags & RTNH_F_DEAD) | ||
411 | return 1; | ||
412 | |||
413 | res->fi = fi; | ||
414 | |||
415 | switch(type) { | ||
416 | case RTN_NAT: | ||
417 | DN_FIB_RES_RESET(*res); | ||
418 | atomic_inc(&fi->fib_clntref); | ||
419 | return 0; | ||
420 | case RTN_UNICAST: | ||
421 | case RTN_LOCAL: | ||
422 | for_nexthops(fi) { | ||
423 | if (nh->nh_flags & RTNH_F_DEAD) | ||
424 | continue; | ||
425 | if (!fl->oif || fl->oif == nh->nh_oif) | ||
426 | break; | ||
427 | } | ||
428 | if (nhsel < fi->fib_nhs) { | ||
429 | res->nh_sel = nhsel; | ||
430 | atomic_inc(&fi->fib_clntref); | ||
431 | return 0; | ||
432 | } | ||
433 | endfor_nexthops(fi); | ||
434 | res->fi = NULL; | ||
435 | return 1; | ||
436 | default: | ||
437 | if (net_ratelimit()) | ||
438 | printk("DECnet: impossible routing event : dn_fib_semantic_match type=%d\n", type); | ||
439 | res->fi = NULL; | ||
440 | return -EINVAL; | ||
441 | } | ||
442 | } | ||
443 | return err; | ||
444 | } | ||
445 | |||
446 | void dn_fib_select_multipath(const struct flowi *fl, struct dn_fib_res *res) | ||
447 | { | ||
448 | struct dn_fib_info *fi = res->fi; | ||
449 | int w; | ||
450 | |||
451 | spin_lock_bh(&dn_fib_multipath_lock); | ||
452 | if (fi->fib_power <= 0) { | ||
453 | int power = 0; | ||
454 | change_nexthops(fi) { | ||
455 | if (!(nh->nh_flags&RTNH_F_DEAD)) { | ||
456 | power += nh->nh_weight; | ||
457 | nh->nh_power = nh->nh_weight; | ||
458 | } | ||
459 | } endfor_nexthops(fi); | ||
460 | fi->fib_power = power; | ||
461 | if (power < 0) { | ||
462 | spin_unlock_bh(&dn_fib_multipath_lock); | ||
463 | res->nh_sel = 0; | ||
464 | return; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | w = jiffies % fi->fib_power; | ||
469 | |||
470 | change_nexthops(fi) { | ||
471 | if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) { | ||
472 | if ((w -= nh->nh_power) <= 0) { | ||
473 | nh->nh_power--; | ||
474 | fi->fib_power--; | ||
475 | res->nh_sel = nhsel; | ||
476 | spin_unlock_bh(&dn_fib_multipath_lock); | ||
477 | return; | ||
478 | } | ||
479 | } | ||
480 | } endfor_nexthops(fi); | ||
481 | res->nh_sel = 0; | ||
482 | spin_unlock_bh(&dn_fib_multipath_lock); | ||
483 | } | ||
484 | |||
485 | |||
486 | static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta) | ||
487 | { | ||
488 | int i; | ||
489 | |||
490 | for(i = 1; i <= RTA_MAX; i++) { | ||
491 | struct rtattr *attr = rta[i-1]; | ||
492 | if (attr) { | ||
493 | if (RTA_PAYLOAD(attr) < 4 && RTA_PAYLOAD(attr) != 2) | ||
494 | return -EINVAL; | ||
495 | if (i != RTA_MULTIPATH && i != RTA_METRICS) | ||
496 | rta[i-1] = (struct rtattr *)RTA_DATA(attr); | ||
497 | } | ||
498 | } | ||
499 | |||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
504 | { | ||
505 | struct dn_fib_table *tb; | ||
506 | struct rtattr **rta = arg; | ||
507 | struct rtmsg *r = NLMSG_DATA(nlh); | ||
508 | |||
509 | if (dn_fib_check_attr(r, rta)) | ||
510 | return -EINVAL; | ||
511 | |||
512 | tb = dn_fib_get_table(r->rtm_table, 0); | ||
513 | if (tb) | ||
514 | return tb->delete(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb)); | ||
515 | |||
516 | return -ESRCH; | ||
517 | } | ||
518 | |||
519 | int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
520 | { | ||
521 | struct dn_fib_table *tb; | ||
522 | struct rtattr **rta = arg; | ||
523 | struct rtmsg *r = NLMSG_DATA(nlh); | ||
524 | |||
525 | if (dn_fib_check_attr(r, rta)) | ||
526 | return -EINVAL; | ||
527 | |||
528 | tb = dn_fib_get_table(r->rtm_table, 1); | ||
529 | if (tb) | ||
530 | return tb->insert(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb)); | ||
531 | |||
532 | return -ENOBUFS; | ||
533 | } | ||
534 | |||
535 | |||
536 | int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb) | ||
537 | { | ||
538 | int t; | ||
539 | int s_t; | ||
540 | struct dn_fib_table *tb; | ||
541 | |||
542 | if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) && | ||
543 | ((struct rtmsg *)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED) | ||
544 | return dn_cache_dump(skb, cb); | ||
545 | |||
546 | s_t = cb->args[0]; | ||
547 | if (s_t == 0) | ||
548 | s_t = cb->args[0] = RT_MIN_TABLE; | ||
549 | |||
550 | for(t = s_t; t <= RT_TABLE_MAX; t++) { | ||
551 | if (t < s_t) | ||
552 | continue; | ||
553 | if (t > s_t) | ||
554 | memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int)); | ||
555 | tb = dn_fib_get_table(t, 0); | ||
556 | if (tb == NULL) | ||
557 | continue; | ||
558 | if (tb->dump(tb, skb, cb) < 0) | ||
559 | break; | ||
560 | } | ||
561 | |||
562 | cb->args[0] = t; | ||
563 | |||
564 | return skb->len; | ||
565 | } | ||
566 | |||
567 | static void fib_magic(int cmd, int type, __u16 dst, int dst_len, struct dn_ifaddr *ifa) | ||
568 | { | ||
569 | struct dn_fib_table *tb; | ||
570 | struct { | ||
571 | struct nlmsghdr nlh; | ||
572 | struct rtmsg rtm; | ||
573 | } req; | ||
574 | struct dn_kern_rta rta; | ||
575 | |||
576 | memset(&req.rtm, 0, sizeof(req.rtm)); | ||
577 | memset(&rta, 0, sizeof(rta)); | ||
578 | |||
579 | if (type == RTN_UNICAST) | ||
580 | tb = dn_fib_get_table(RT_MIN_TABLE, 1); | ||
581 | else | ||
582 | tb = dn_fib_get_table(RT_TABLE_LOCAL, 1); | ||
583 | |||
584 | if (tb == NULL) | ||
585 | return; | ||
586 | |||
587 | req.nlh.nlmsg_len = sizeof(req); | ||
588 | req.nlh.nlmsg_type = cmd; | ||
589 | req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND; | ||
590 | req.nlh.nlmsg_pid = 0; | ||
591 | req.nlh.nlmsg_seq = 0; | ||
592 | |||
593 | req.rtm.rtm_dst_len = dst_len; | ||
594 | req.rtm.rtm_table = tb->n; | ||
595 | req.rtm.rtm_protocol = RTPROT_KERNEL; | ||
596 | req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST); | ||
597 | req.rtm.rtm_type = type; | ||
598 | |||
599 | rta.rta_dst = &dst; | ||
600 | rta.rta_prefsrc = &ifa->ifa_local; | ||
601 | rta.rta_oif = &ifa->ifa_dev->dev->ifindex; | ||
602 | |||
603 | if (cmd == RTM_NEWROUTE) | ||
604 | tb->insert(tb, &req.rtm, &rta, &req.nlh, NULL); | ||
605 | else | ||
606 | tb->delete(tb, &req.rtm, &rta, &req.nlh, NULL); | ||
607 | } | ||
608 | |||
609 | static void dn_fib_add_ifaddr(struct dn_ifaddr *ifa) | ||
610 | { | ||
611 | |||
612 | fib_magic(RTM_NEWROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa); | ||
613 | |||
614 | #if 0 | ||
615 | if (!(dev->flags&IFF_UP)) | ||
616 | return; | ||
617 | /* In the future, we will want to add default routes here */ | ||
618 | |||
619 | #endif | ||
620 | } | ||
621 | |||
622 | static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa) | ||
623 | { | ||
624 | int found_it = 0; | ||
625 | struct net_device *dev; | ||
626 | struct dn_dev *dn_db; | ||
627 | struct dn_ifaddr *ifa2; | ||
628 | |||
629 | ASSERT_RTNL(); | ||
630 | |||
631 | /* Scan device list */ | ||
632 | read_lock(&dev_base_lock); | ||
633 | for(dev = dev_base; dev; dev = dev->next) { | ||
634 | dn_db = dev->dn_ptr; | ||
635 | if (dn_db == NULL) | ||
636 | continue; | ||
637 | for(ifa2 = dn_db->ifa_list; ifa2; ifa2 = ifa2->ifa_next) { | ||
638 | if (ifa2->ifa_local == ifa->ifa_local) { | ||
639 | found_it = 1; | ||
640 | break; | ||
641 | } | ||
642 | } | ||
643 | } | ||
644 | read_unlock(&dev_base_lock); | ||
645 | |||
646 | if (found_it == 0) { | ||
647 | fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa); | ||
648 | |||
649 | if (dnet_addr_type(ifa->ifa_local) != RTN_LOCAL) { | ||
650 | if (dn_fib_sync_down(ifa->ifa_local, NULL, 0)) | ||
651 | dn_fib_flush(); | ||
652 | } | ||
653 | } | ||
654 | } | ||
655 | |||
656 | static void dn_fib_disable_addr(struct net_device *dev, int force) | ||
657 | { | ||
658 | if (dn_fib_sync_down(0, dev, force)) | ||
659 | dn_fib_flush(); | ||
660 | dn_rt_cache_flush(0); | ||
661 | neigh_ifdown(&dn_neigh_table, dev); | ||
662 | } | ||
663 | |||
664 | static int dn_fib_dnaddr_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
665 | { | ||
666 | struct dn_ifaddr *ifa = (struct dn_ifaddr *)ptr; | ||
667 | |||
668 | switch(event) { | ||
669 | case NETDEV_UP: | ||
670 | dn_fib_add_ifaddr(ifa); | ||
671 | dn_fib_sync_up(ifa->ifa_dev->dev); | ||
672 | dn_rt_cache_flush(-1); | ||
673 | break; | ||
674 | case NETDEV_DOWN: | ||
675 | dn_fib_del_ifaddr(ifa); | ||
676 | if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) { | ||
677 | dn_fib_disable_addr(ifa->ifa_dev->dev, 1); | ||
678 | } else { | ||
679 | dn_rt_cache_flush(-1); | ||
680 | } | ||
681 | break; | ||
682 | } | ||
683 | return NOTIFY_DONE; | ||
684 | } | ||
685 | |||
686 | int dn_fib_sync_down(dn_address local, struct net_device *dev, int force) | ||
687 | { | ||
688 | int ret = 0; | ||
689 | int scope = RT_SCOPE_NOWHERE; | ||
690 | |||
691 | if (force) | ||
692 | scope = -1; | ||
693 | |||
694 | for_fib_info() { | ||
695 | /* | ||
696 | * This makes no sense for DECnet.... we will almost | ||
697 | * certainly have more than one local address the same | ||
698 | * over all our interfaces. It needs thinking about | ||
699 | * some more. | ||
700 | */ | ||
701 | if (local && fi->fib_prefsrc == local) { | ||
702 | fi->fib_flags |= RTNH_F_DEAD; | ||
703 | ret++; | ||
704 | } else if (dev && fi->fib_nhs) { | ||
705 | int dead = 0; | ||
706 | |||
707 | change_nexthops(fi) { | ||
708 | if (nh->nh_flags&RTNH_F_DEAD) | ||
709 | dead++; | ||
710 | else if (nh->nh_dev == dev && | ||
711 | nh->nh_scope != scope) { | ||
712 | spin_lock_bh(&dn_fib_multipath_lock); | ||
713 | nh->nh_flags |= RTNH_F_DEAD; | ||
714 | fi->fib_power -= nh->nh_power; | ||
715 | nh->nh_power = 0; | ||
716 | spin_unlock_bh(&dn_fib_multipath_lock); | ||
717 | dead++; | ||
718 | } | ||
719 | } endfor_nexthops(fi) | ||
720 | if (dead == fi->fib_nhs) { | ||
721 | fi->fib_flags |= RTNH_F_DEAD; | ||
722 | ret++; | ||
723 | } | ||
724 | } | ||
725 | } endfor_fib_info(); | ||
726 | return ret; | ||
727 | } | ||
728 | |||
729 | |||
730 | int dn_fib_sync_up(struct net_device *dev) | ||
731 | { | ||
732 | int ret = 0; | ||
733 | |||
734 | if (!(dev->flags&IFF_UP)) | ||
735 | return 0; | ||
736 | |||
737 | for_fib_info() { | ||
738 | int alive = 0; | ||
739 | |||
740 | change_nexthops(fi) { | ||
741 | if (!(nh->nh_flags&RTNH_F_DEAD)) { | ||
742 | alive++; | ||
743 | continue; | ||
744 | } | ||
745 | if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP)) | ||
746 | continue; | ||
747 | if (nh->nh_dev != dev || dev->dn_ptr == NULL) | ||
748 | continue; | ||
749 | alive++; | ||
750 | spin_lock_bh(&dn_fib_multipath_lock); | ||
751 | nh->nh_power = 0; | ||
752 | nh->nh_flags &= ~RTNH_F_DEAD; | ||
753 | spin_unlock_bh(&dn_fib_multipath_lock); | ||
754 | } endfor_nexthops(fi); | ||
755 | |||
756 | if (alive > 0) { | ||
757 | fi->fib_flags &= ~RTNH_F_DEAD; | ||
758 | ret++; | ||
759 | } | ||
760 | } endfor_fib_info(); | ||
761 | return ret; | ||
762 | } | ||
763 | |||
764 | void dn_fib_flush(void) | ||
765 | { | ||
766 | int flushed = 0; | ||
767 | struct dn_fib_table *tb; | ||
768 | int id; | ||
769 | |||
770 | for(id = RT_TABLE_MAX; id > 0; id--) { | ||
771 | if ((tb = dn_fib_get_table(id, 0)) == NULL) | ||
772 | continue; | ||
773 | flushed += tb->flush(tb); | ||
774 | } | ||
775 | |||
776 | if (flushed) | ||
777 | dn_rt_cache_flush(-1); | ||
778 | } | ||
779 | |||
780 | static struct notifier_block dn_fib_dnaddr_notifier = { | ||
781 | .notifier_call = dn_fib_dnaddr_event, | ||
782 | }; | ||
783 | |||
784 | void __exit dn_fib_cleanup(void) | ||
785 | { | ||
786 | dn_fib_table_cleanup(); | ||
787 | dn_fib_rules_cleanup(); | ||
788 | |||
789 | unregister_dnaddr_notifier(&dn_fib_dnaddr_notifier); | ||
790 | } | ||
791 | |||
792 | |||
793 | void __init dn_fib_init(void) | ||
794 | { | ||
795 | |||
796 | dn_fib_table_init(); | ||
797 | dn_fib_rules_init(); | ||
798 | |||
799 | register_dnaddr_notifier(&dn_fib_dnaddr_notifier); | ||
800 | } | ||
801 | |||
802 | |||
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c new file mode 100644 index 000000000000..f6dfe96f45b7 --- /dev/null +++ b/net/decnet/dn_neigh.c | |||
@@ -0,0 +1,627 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet Neighbour Functions (Adjacency Database and | ||
7 | * On-Ethernet Cache) | ||
8 | * | ||
9 | * Author: Steve Whitehouse <SteveW@ACM.org> | ||
10 | * | ||
11 | * | ||
12 | * Changes: | ||
13 | * Steve Whitehouse : Fixed router listing routine | ||
14 | * Steve Whitehouse : Added error_report functions | ||
15 | * Steve Whitehouse : Added default router detection | ||
16 | * Steve Whitehouse : Hop counts in outgoing messages | ||
17 | * Steve Whitehouse : Fixed src/dst in outgoing messages so | ||
18 | * forwarding now stands a good chance of | ||
19 | * working. | ||
20 | * Steve Whitehouse : Fixed neighbour states (for now anyway). | ||
21 | * Steve Whitehouse : Made error_report functions dummies. This | ||
22 | * is not the right place to return skbs. | ||
23 | * Steve Whitehouse : Convert to seq_file | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/net.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/socket.h> | ||
31 | #include <linux/if_arp.h> | ||
32 | #include <linux/if_ether.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/proc_fs.h> | ||
35 | #include <linux/string.h> | ||
36 | #include <linux/netfilter_decnet.h> | ||
37 | #include <linux/spinlock.h> | ||
38 | #include <linux/seq_file.h> | ||
39 | #include <linux/rcupdate.h> | ||
40 | #include <linux/jhash.h> | ||
41 | #include <asm/atomic.h> | ||
42 | #include <net/neighbour.h> | ||
43 | #include <net/dst.h> | ||
44 | #include <net/flow.h> | ||
45 | #include <net/dn.h> | ||
46 | #include <net/dn_dev.h> | ||
47 | #include <net/dn_neigh.h> | ||
48 | #include <net/dn_route.h> | ||
49 | |||
50 | static u32 dn_neigh_hash(const void *pkey, const struct net_device *dev); | ||
51 | static int dn_neigh_construct(struct neighbour *); | ||
52 | static void dn_long_error_report(struct neighbour *, struct sk_buff *); | ||
53 | static void dn_short_error_report(struct neighbour *, struct sk_buff *); | ||
54 | static int dn_long_output(struct sk_buff *); | ||
55 | static int dn_short_output(struct sk_buff *); | ||
56 | static int dn_phase3_output(struct sk_buff *); | ||
57 | |||
58 | |||
59 | /* | ||
60 | * For talking to broadcast devices: Ethernet & PPP | ||
61 | */ | ||
62 | static struct neigh_ops dn_long_ops = { | ||
63 | .family = AF_DECnet, | ||
64 | .error_report = dn_long_error_report, | ||
65 | .output = dn_long_output, | ||
66 | .connected_output = dn_long_output, | ||
67 | .hh_output = dev_queue_xmit, | ||
68 | .queue_xmit = dev_queue_xmit, | ||
69 | }; | ||
70 | |||
71 | /* | ||
72 | * For talking to pointopoint and multidrop devices: DDCMP and X.25 | ||
73 | */ | ||
74 | static struct neigh_ops dn_short_ops = { | ||
75 | .family = AF_DECnet, | ||
76 | .error_report = dn_short_error_report, | ||
77 | .output = dn_short_output, | ||
78 | .connected_output = dn_short_output, | ||
79 | .hh_output = dev_queue_xmit, | ||
80 | .queue_xmit = dev_queue_xmit, | ||
81 | }; | ||
82 | |||
83 | /* | ||
84 | * For talking to DECnet phase III nodes | ||
85 | */ | ||
86 | static struct neigh_ops dn_phase3_ops = { | ||
87 | .family = AF_DECnet, | ||
88 | .error_report = dn_short_error_report, /* Can use short version here */ | ||
89 | .output = dn_phase3_output, | ||
90 | .connected_output = dn_phase3_output, | ||
91 | .hh_output = dev_queue_xmit, | ||
92 | .queue_xmit = dev_queue_xmit | ||
93 | }; | ||
94 | |||
95 | struct neigh_table dn_neigh_table = { | ||
96 | .family = PF_DECnet, | ||
97 | .entry_size = sizeof(struct dn_neigh), | ||
98 | .key_len = sizeof(dn_address), | ||
99 | .hash = dn_neigh_hash, | ||
100 | .constructor = dn_neigh_construct, | ||
101 | .id = "dn_neigh_cache", | ||
102 | .parms ={ | ||
103 | .tbl = &dn_neigh_table, | ||
104 | .entries = 0, | ||
105 | .base_reachable_time = 30 * HZ, | ||
106 | .retrans_time = 1 * HZ, | ||
107 | .gc_staletime = 60 * HZ, | ||
108 | .reachable_time = 30 * HZ, | ||
109 | .delay_probe_time = 5 * HZ, | ||
110 | .queue_len = 3, | ||
111 | .ucast_probes = 0, | ||
112 | .app_probes = 0, | ||
113 | .mcast_probes = 0, | ||
114 | .anycast_delay = 0, | ||
115 | .proxy_delay = 0, | ||
116 | .proxy_qlen = 0, | ||
117 | .locktime = 1 * HZ, | ||
118 | }, | ||
119 | .gc_interval = 30 * HZ, | ||
120 | .gc_thresh1 = 128, | ||
121 | .gc_thresh2 = 512, | ||
122 | .gc_thresh3 = 1024, | ||
123 | }; | ||
124 | |||
125 | static u32 dn_neigh_hash(const void *pkey, const struct net_device *dev) | ||
126 | { | ||
127 | return jhash_2words(*(dn_address *)pkey, 0, dn_neigh_table.hash_rnd); | ||
128 | } | ||
129 | |||
130 | static int dn_neigh_construct(struct neighbour *neigh) | ||
131 | { | ||
132 | struct net_device *dev = neigh->dev; | ||
133 | struct dn_neigh *dn = (struct dn_neigh *)neigh; | ||
134 | struct dn_dev *dn_db; | ||
135 | struct neigh_parms *parms; | ||
136 | |||
137 | rcu_read_lock(); | ||
138 | dn_db = rcu_dereference(dev->dn_ptr); | ||
139 | if (dn_db == NULL) { | ||
140 | rcu_read_unlock(); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | parms = dn_db->neigh_parms; | ||
145 | if (!parms) { | ||
146 | rcu_read_unlock(); | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | __neigh_parms_put(neigh->parms); | ||
151 | neigh->parms = neigh_parms_clone(parms); | ||
152 | rcu_read_unlock(); | ||
153 | |||
154 | if (dn_db->use_long) | ||
155 | neigh->ops = &dn_long_ops; | ||
156 | else | ||
157 | neigh->ops = &dn_short_ops; | ||
158 | |||
159 | if (dn->flags & DN_NDFLAG_P3) | ||
160 | neigh->ops = &dn_phase3_ops; | ||
161 | |||
162 | neigh->nud_state = NUD_NOARP; | ||
163 | neigh->output = neigh->ops->connected_output; | ||
164 | |||
165 | if ((dev->type == ARPHRD_IPGRE) || (dev->flags & IFF_POINTOPOINT)) | ||
166 | memcpy(neigh->ha, dev->broadcast, dev->addr_len); | ||
167 | else if ((dev->type == ARPHRD_ETHER) || (dev->type == ARPHRD_LOOPBACK)) | ||
168 | dn_dn2eth(neigh->ha, dn->addr); | ||
169 | else { | ||
170 | if (net_ratelimit()) | ||
171 | printk(KERN_DEBUG "Trying to create neigh for hw %d\n", dev->type); | ||
172 | return -EINVAL; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * Make an estimate of the remote block size by assuming that its | ||
177 | * two less then the device mtu, which it true for ethernet (and | ||
178 | * other things which support long format headers) since there is | ||
179 | * an extra length field (of 16 bits) which isn't part of the | ||
180 | * ethernet headers and which the DECnet specs won't admit is part | ||
181 | * of the DECnet routing headers either. | ||
182 | * | ||
183 | * If we over estimate here its no big deal, the NSP negotiations | ||
184 | * will prevent us from sending packets which are too large for the | ||
185 | * remote node to handle. In any case this figure is normally updated | ||
186 | * by a hello message in most cases. | ||
187 | */ | ||
188 | dn->blksize = dev->mtu - 2; | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static void dn_long_error_report(struct neighbour *neigh, struct sk_buff *skb) | ||
194 | { | ||
195 | printk(KERN_DEBUG "dn_long_error_report: called\n"); | ||
196 | kfree_skb(skb); | ||
197 | } | ||
198 | |||
199 | |||
200 | static void dn_short_error_report(struct neighbour *neigh, struct sk_buff *skb) | ||
201 | { | ||
202 | printk(KERN_DEBUG "dn_short_error_report: called\n"); | ||
203 | kfree_skb(skb); | ||
204 | } | ||
205 | |||
206 | static int dn_neigh_output_packet(struct sk_buff *skb) | ||
207 | { | ||
208 | struct dst_entry *dst = skb->dst; | ||
209 | struct dn_route *rt = (struct dn_route *)dst; | ||
210 | struct neighbour *neigh = dst->neighbour; | ||
211 | struct net_device *dev = neigh->dev; | ||
212 | char mac_addr[ETH_ALEN]; | ||
213 | |||
214 | dn_dn2eth(mac_addr, rt->rt_local_src); | ||
215 | if (!dev->hard_header || dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, mac_addr, skb->len) >= 0) | ||
216 | return neigh->ops->queue_xmit(skb); | ||
217 | |||
218 | if (net_ratelimit()) | ||
219 | printk(KERN_DEBUG "dn_neigh_output_packet: oops, can't send packet\n"); | ||
220 | |||
221 | kfree_skb(skb); | ||
222 | return -EINVAL; | ||
223 | } | ||
224 | |||
225 | static int dn_long_output(struct sk_buff *skb) | ||
226 | { | ||
227 | struct dst_entry *dst = skb->dst; | ||
228 | struct neighbour *neigh = dst->neighbour; | ||
229 | struct net_device *dev = neigh->dev; | ||
230 | int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3; | ||
231 | unsigned char *data; | ||
232 | struct dn_long_packet *lp; | ||
233 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
234 | |||
235 | |||
236 | if (skb_headroom(skb) < headroom) { | ||
237 | struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom); | ||
238 | if (skb2 == NULL) { | ||
239 | if (net_ratelimit()) | ||
240 | printk(KERN_CRIT "dn_long_output: no memory\n"); | ||
241 | kfree_skb(skb); | ||
242 | return -ENOBUFS; | ||
243 | } | ||
244 | kfree_skb(skb); | ||
245 | skb = skb2; | ||
246 | if (net_ratelimit()) | ||
247 | printk(KERN_INFO "dn_long_output: Increasing headroom\n"); | ||
248 | } | ||
249 | |||
250 | data = skb_push(skb, sizeof(struct dn_long_packet) + 3); | ||
251 | lp = (struct dn_long_packet *)(data+3); | ||
252 | |||
253 | *((unsigned short *)data) = dn_htons(skb->len - 2); | ||
254 | *(data + 2) = 1 | DN_RT_F_PF; /* Padding */ | ||
255 | |||
256 | lp->msgflg = DN_RT_PKT_LONG|(cb->rt_flags&(DN_RT_F_IE|DN_RT_F_RQR|DN_RT_F_RTS)); | ||
257 | lp->d_area = lp->d_subarea = 0; | ||
258 | dn_dn2eth(lp->d_id, dn_ntohs(cb->dst)); | ||
259 | lp->s_area = lp->s_subarea = 0; | ||
260 | dn_dn2eth(lp->s_id, dn_ntohs(cb->src)); | ||
261 | lp->nl2 = 0; | ||
262 | lp->visit_ct = cb->hops & 0x3f; | ||
263 | lp->s_class = 0; | ||
264 | lp->pt = 0; | ||
265 | |||
266 | skb->nh.raw = skb->data; | ||
267 | |||
268 | return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet); | ||
269 | } | ||
270 | |||
271 | static int dn_short_output(struct sk_buff *skb) | ||
272 | { | ||
273 | struct dst_entry *dst = skb->dst; | ||
274 | struct neighbour *neigh = dst->neighbour; | ||
275 | struct net_device *dev = neigh->dev; | ||
276 | int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2; | ||
277 | struct dn_short_packet *sp; | ||
278 | unsigned char *data; | ||
279 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
280 | |||
281 | |||
282 | if (skb_headroom(skb) < headroom) { | ||
283 | struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom); | ||
284 | if (skb2 == NULL) { | ||
285 | if (net_ratelimit()) | ||
286 | printk(KERN_CRIT "dn_short_output: no memory\n"); | ||
287 | kfree_skb(skb); | ||
288 | return -ENOBUFS; | ||
289 | } | ||
290 | kfree_skb(skb); | ||
291 | skb = skb2; | ||
292 | if (net_ratelimit()) | ||
293 | printk(KERN_INFO "dn_short_output: Increasing headroom\n"); | ||
294 | } | ||
295 | |||
296 | data = skb_push(skb, sizeof(struct dn_short_packet) + 2); | ||
297 | *((unsigned short *)data) = dn_htons(skb->len - 2); | ||
298 | sp = (struct dn_short_packet *)(data+2); | ||
299 | |||
300 | sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS)); | ||
301 | sp->dstnode = cb->dst; | ||
302 | sp->srcnode = cb->src; | ||
303 | sp->forward = cb->hops & 0x3f; | ||
304 | |||
305 | skb->nh.raw = skb->data; | ||
306 | |||
307 | return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet); | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * Phase 3 output is the same is short output, execpt that | ||
312 | * it clears the area bits before transmission. | ||
313 | */ | ||
314 | static int dn_phase3_output(struct sk_buff *skb) | ||
315 | { | ||
316 | struct dst_entry *dst = skb->dst; | ||
317 | struct neighbour *neigh = dst->neighbour; | ||
318 | struct net_device *dev = neigh->dev; | ||
319 | int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2; | ||
320 | struct dn_short_packet *sp; | ||
321 | unsigned char *data; | ||
322 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
323 | |||
324 | if (skb_headroom(skb) < headroom) { | ||
325 | struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom); | ||
326 | if (skb2 == NULL) { | ||
327 | if (net_ratelimit()) | ||
328 | printk(KERN_CRIT "dn_phase3_output: no memory\n"); | ||
329 | kfree_skb(skb); | ||
330 | return -ENOBUFS; | ||
331 | } | ||
332 | kfree_skb(skb); | ||
333 | skb = skb2; | ||
334 | if (net_ratelimit()) | ||
335 | printk(KERN_INFO "dn_phase3_output: Increasing headroom\n"); | ||
336 | } | ||
337 | |||
338 | data = skb_push(skb, sizeof(struct dn_short_packet) + 2); | ||
339 | *((unsigned short *)data) = dn_htons(skb->len - 2); | ||
340 | sp = (struct dn_short_packet *)(data + 2); | ||
341 | |||
342 | sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS)); | ||
343 | sp->dstnode = cb->dst & dn_htons(0x03ff); | ||
344 | sp->srcnode = cb->src & dn_htons(0x03ff); | ||
345 | sp->forward = cb->hops & 0x3f; | ||
346 | |||
347 | skb->nh.raw = skb->data; | ||
348 | |||
349 | return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet); | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Unfortunately, the neighbour code uses the device in its hash | ||
354 | * function, so we don't get any advantage from it. This function | ||
355 | * basically does a neigh_lookup(), but without comparing the device | ||
356 | * field. This is required for the On-Ethernet cache | ||
357 | */ | ||
358 | |||
359 | /* | ||
360 | * Pointopoint link receives a hello message | ||
361 | */ | ||
362 | void dn_neigh_pointopoint_hello(struct sk_buff *skb) | ||
363 | { | ||
364 | kfree_skb(skb); | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * Ethernet router hello message received | ||
369 | */ | ||
370 | int dn_neigh_router_hello(struct sk_buff *skb) | ||
371 | { | ||
372 | struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data; | ||
373 | |||
374 | struct neighbour *neigh; | ||
375 | struct dn_neigh *dn; | ||
376 | struct dn_dev *dn_db; | ||
377 | dn_address src; | ||
378 | |||
379 | src = dn_htons(dn_eth2dn(msg->id)); | ||
380 | |||
381 | neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1); | ||
382 | |||
383 | dn = (struct dn_neigh *)neigh; | ||
384 | |||
385 | if (neigh) { | ||
386 | write_lock(&neigh->lock); | ||
387 | |||
388 | neigh->used = jiffies; | ||
389 | dn_db = (struct dn_dev *)neigh->dev->dn_ptr; | ||
390 | |||
391 | if (!(neigh->nud_state & NUD_PERMANENT)) { | ||
392 | neigh->updated = jiffies; | ||
393 | |||
394 | if (neigh->dev->type == ARPHRD_ETHER) | ||
395 | memcpy(neigh->ha, ð_hdr(skb)->h_source, ETH_ALEN); | ||
396 | |||
397 | dn->blksize = dn_ntohs(msg->blksize); | ||
398 | dn->priority = msg->priority; | ||
399 | |||
400 | dn->flags &= ~DN_NDFLAG_P3; | ||
401 | |||
402 | switch(msg->iinfo & DN_RT_INFO_TYPE) { | ||
403 | case DN_RT_INFO_L1RT: | ||
404 | dn->flags &=~DN_NDFLAG_R2; | ||
405 | dn->flags |= DN_NDFLAG_R1; | ||
406 | break; | ||
407 | case DN_RT_INFO_L2RT: | ||
408 | dn->flags |= DN_NDFLAG_R2; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | if (!dn_db->router) { | ||
413 | dn_db->router = neigh_clone(neigh); | ||
414 | } else { | ||
415 | if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority) | ||
416 | neigh_release(xchg(&dn_db->router, neigh_clone(neigh))); | ||
417 | } | ||
418 | write_unlock(&neigh->lock); | ||
419 | neigh_release(neigh); | ||
420 | } | ||
421 | |||
422 | kfree_skb(skb); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | /* | ||
427 | * Endnode hello message received | ||
428 | */ | ||
429 | int dn_neigh_endnode_hello(struct sk_buff *skb) | ||
430 | { | ||
431 | struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data; | ||
432 | struct neighbour *neigh; | ||
433 | struct dn_neigh *dn; | ||
434 | dn_address src; | ||
435 | |||
436 | src = dn_htons(dn_eth2dn(msg->id)); | ||
437 | |||
438 | neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1); | ||
439 | |||
440 | dn = (struct dn_neigh *)neigh; | ||
441 | |||
442 | if (neigh) { | ||
443 | write_lock(&neigh->lock); | ||
444 | |||
445 | neigh->used = jiffies; | ||
446 | |||
447 | if (!(neigh->nud_state & NUD_PERMANENT)) { | ||
448 | neigh->updated = jiffies; | ||
449 | |||
450 | if (neigh->dev->type == ARPHRD_ETHER) | ||
451 | memcpy(neigh->ha, ð_hdr(skb)->h_source, ETH_ALEN); | ||
452 | dn->flags &= ~(DN_NDFLAG_R1 | DN_NDFLAG_R2); | ||
453 | dn->blksize = dn_ntohs(msg->blksize); | ||
454 | dn->priority = 0; | ||
455 | } | ||
456 | |||
457 | write_unlock(&neigh->lock); | ||
458 | neigh_release(neigh); | ||
459 | } | ||
460 | |||
461 | kfree_skb(skb); | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static char *dn_find_slot(char *base, int max, int priority) | ||
466 | { | ||
467 | int i; | ||
468 | unsigned char *min = NULL; | ||
469 | |||
470 | base += 6; /* skip first id */ | ||
471 | |||
472 | for(i = 0; i < max; i++) { | ||
473 | if (!min || (*base < *min)) | ||
474 | min = base; | ||
475 | base += 7; /* find next priority */ | ||
476 | } | ||
477 | |||
478 | if (!min) | ||
479 | return NULL; | ||
480 | |||
481 | return (*min < priority) ? (min - 6) : NULL; | ||
482 | } | ||
483 | |||
484 | struct elist_cb_state { | ||
485 | struct net_device *dev; | ||
486 | unsigned char *ptr; | ||
487 | unsigned char *rs; | ||
488 | int t, n; | ||
489 | }; | ||
490 | |||
491 | static void neigh_elist_cb(struct neighbour *neigh, void *_info) | ||
492 | { | ||
493 | struct elist_cb_state *s = _info; | ||
494 | struct dn_dev *dn_db; | ||
495 | struct dn_neigh *dn; | ||
496 | |||
497 | if (neigh->dev != s->dev) | ||
498 | return; | ||
499 | |||
500 | dn = (struct dn_neigh *) neigh; | ||
501 | if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2))) | ||
502 | return; | ||
503 | |||
504 | dn_db = (struct dn_dev *) s->dev->dn_ptr; | ||
505 | if (dn_db->parms.forwarding == 1 && (dn->flags & DN_NDFLAG_R2)) | ||
506 | return; | ||
507 | |||
508 | if (s->t == s->n) | ||
509 | s->rs = dn_find_slot(s->ptr, s->n, dn->priority); | ||
510 | else | ||
511 | s->t++; | ||
512 | if (s->rs == NULL) | ||
513 | return; | ||
514 | |||
515 | dn_dn2eth(s->rs, dn->addr); | ||
516 | s->rs += 6; | ||
517 | *(s->rs) = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0; | ||
518 | *(s->rs) |= dn->priority; | ||
519 | s->rs++; | ||
520 | } | ||
521 | |||
522 | int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n) | ||
523 | { | ||
524 | struct elist_cb_state state; | ||
525 | |||
526 | state.dev = dev; | ||
527 | state.t = 0; | ||
528 | state.n = n; | ||
529 | state.ptr = ptr; | ||
530 | state.rs = ptr; | ||
531 | |||
532 | neigh_for_each(&dn_neigh_table, neigh_elist_cb, &state); | ||
533 | |||
534 | return state.t; | ||
535 | } | ||
536 | |||
537 | |||
538 | #ifdef CONFIG_PROC_FS | ||
539 | |||
540 | static inline void dn_neigh_format_entry(struct seq_file *seq, | ||
541 | struct neighbour *n) | ||
542 | { | ||
543 | struct dn_neigh *dn = (struct dn_neigh *) n; | ||
544 | char buf[DN_ASCBUF_LEN]; | ||
545 | |||
546 | read_lock(&n->lock); | ||
547 | seq_printf(seq, "%-7s %s%s%s %02x %02d %07ld %-8s\n", | ||
548 | dn_addr2asc(dn_ntohs(dn->addr), buf), | ||
549 | (dn->flags&DN_NDFLAG_R1) ? "1" : "-", | ||
550 | (dn->flags&DN_NDFLAG_R2) ? "2" : "-", | ||
551 | (dn->flags&DN_NDFLAG_P3) ? "3" : "-", | ||
552 | dn->n.nud_state, | ||
553 | atomic_read(&dn->n.refcnt), | ||
554 | dn->blksize, | ||
555 | (dn->n.dev) ? dn->n.dev->name : "?"); | ||
556 | read_unlock(&n->lock); | ||
557 | } | ||
558 | |||
559 | static int dn_neigh_seq_show(struct seq_file *seq, void *v) | ||
560 | { | ||
561 | if (v == SEQ_START_TOKEN) { | ||
562 | seq_puts(seq, "Addr Flags State Use Blksize Dev\n"); | ||
563 | } else { | ||
564 | dn_neigh_format_entry(seq, v); | ||
565 | } | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos) | ||
571 | { | ||
572 | return neigh_seq_start(seq, pos, &dn_neigh_table, | ||
573 | NEIGH_SEQ_NEIGH_ONLY); | ||
574 | } | ||
575 | |||
576 | static struct seq_operations dn_neigh_seq_ops = { | ||
577 | .start = dn_neigh_seq_start, | ||
578 | .next = neigh_seq_next, | ||
579 | .stop = neigh_seq_stop, | ||
580 | .show = dn_neigh_seq_show, | ||
581 | }; | ||
582 | |||
583 | static int dn_neigh_seq_open(struct inode *inode, struct file *file) | ||
584 | { | ||
585 | struct seq_file *seq; | ||
586 | int rc = -ENOMEM; | ||
587 | struct neigh_seq_state *s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
588 | |||
589 | if (!s) | ||
590 | goto out; | ||
591 | |||
592 | memset(s, 0, sizeof(*s)); | ||
593 | rc = seq_open(file, &dn_neigh_seq_ops); | ||
594 | if (rc) | ||
595 | goto out_kfree; | ||
596 | |||
597 | seq = file->private_data; | ||
598 | seq->private = s; | ||
599 | memset(s, 0, sizeof(*s)); | ||
600 | out: | ||
601 | return rc; | ||
602 | out_kfree: | ||
603 | kfree(s); | ||
604 | goto out; | ||
605 | } | ||
606 | |||
607 | static struct file_operations dn_neigh_seq_fops = { | ||
608 | .owner = THIS_MODULE, | ||
609 | .open = dn_neigh_seq_open, | ||
610 | .read = seq_read, | ||
611 | .llseek = seq_lseek, | ||
612 | .release = seq_release_private, | ||
613 | }; | ||
614 | |||
615 | #endif | ||
616 | |||
617 | void __init dn_neigh_init(void) | ||
618 | { | ||
619 | neigh_table_init(&dn_neigh_table); | ||
620 | proc_net_fops_create("decnet_neigh", S_IRUGO, &dn_neigh_seq_fops); | ||
621 | } | ||
622 | |||
623 | void __exit dn_neigh_cleanup(void) | ||
624 | { | ||
625 | proc_net_remove("decnet_neigh"); | ||
626 | neigh_table_clear(&dn_neigh_table); | ||
627 | } | ||
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c new file mode 100644 index 000000000000..202dbde9850d --- /dev/null +++ b/net/decnet/dn_nsp_in.c | |||
@@ -0,0 +1,934 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet Network Services Protocol (Input) | ||
7 | * | ||
8 | * Author: Eduardo Marcelo Serrat <emserrat@geocities.com> | ||
9 | * | ||
10 | * Changes: | ||
11 | * | ||
12 | * Steve Whitehouse: Split into dn_nsp_in.c and dn_nsp_out.c from | ||
13 | * original dn_nsp.c. | ||
14 | * Steve Whitehouse: Updated to work with my new routing architecture. | ||
15 | * Steve Whitehouse: Add changes from Eduardo Serrat's patches. | ||
16 | * Steve Whitehouse: Put all ack handling code in a common routine. | ||
17 | * Steve Whitehouse: Put other common bits into dn_nsp_rx() | ||
18 | * Steve Whitehouse: More checks on skb->len to catch bogus packets | ||
19 | * Fixed various race conditions and possible nasties. | ||
20 | * Steve Whitehouse: Now handles returned conninit frames. | ||
21 | * David S. Miller: New socket locking | ||
22 | * Steve Whitehouse: Fixed lockup when socket filtering was enabled. | ||
23 | * Paul Koning: Fix to push CC sockets into RUN when acks are | ||
24 | * received. | ||
25 | * Steve Whitehouse: | ||
26 | * Patrick Caulfield: Checking conninits for correctness & sending of error | ||
27 | * responses. | ||
28 | * Steve Whitehouse: Added backlog congestion level return codes. | ||
29 | * Patrick Caulfield: | ||
30 | * Steve Whitehouse: Added flow control support (outbound) | ||
31 | * Steve Whitehouse: Prepare for nonlinear skbs | ||
32 | */ | ||
33 | |||
34 | /****************************************************************************** | ||
35 | (c) 1995-1998 E.M. Serrat emserrat@geocities.com | ||
36 | |||
37 | This program is free software; you can redistribute it and/or modify | ||
38 | it under the terms of the GNU General Public License as published by | ||
39 | the Free Software Foundation; either version 2 of the License, or | ||
40 | any later version. | ||
41 | |||
42 | This program is distributed in the hope that it will be useful, | ||
43 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
44 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
45 | GNU General Public License for more details. | ||
46 | *******************************************************************************/ | ||
47 | |||
48 | #include <linux/config.h> | ||
49 | #include <linux/errno.h> | ||
50 | #include <linux/types.h> | ||
51 | #include <linux/socket.h> | ||
52 | #include <linux/in.h> | ||
53 | #include <linux/kernel.h> | ||
54 | #include <linux/sched.h> | ||
55 | #include <linux/timer.h> | ||
56 | #include <linux/string.h> | ||
57 | #include <linux/sockios.h> | ||
58 | #include <linux/net.h> | ||
59 | #include <linux/netdevice.h> | ||
60 | #include <linux/inet.h> | ||
61 | #include <linux/route.h> | ||
62 | #include <net/sock.h> | ||
63 | #include <net/tcp.h> | ||
64 | #include <asm/system.h> | ||
65 | #include <linux/fcntl.h> | ||
66 | #include <linux/mm.h> | ||
67 | #include <linux/termios.h> | ||
68 | #include <linux/interrupt.h> | ||
69 | #include <linux/proc_fs.h> | ||
70 | #include <linux/stat.h> | ||
71 | #include <linux/init.h> | ||
72 | #include <linux/poll.h> | ||
73 | #include <linux/netfilter_decnet.h> | ||
74 | #include <net/neighbour.h> | ||
75 | #include <net/dst.h> | ||
76 | #include <net/dn.h> | ||
77 | #include <net/dn_nsp.h> | ||
78 | #include <net/dn_dev.h> | ||
79 | #include <net/dn_route.h> | ||
80 | |||
81 | extern int decnet_log_martians; | ||
82 | |||
83 | static void dn_log_martian(struct sk_buff *skb, const char *msg) | ||
84 | { | ||
85 | if (decnet_log_martians && net_ratelimit()) { | ||
86 | char *devname = skb->dev ? skb->dev->name : "???"; | ||
87 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
88 | printk(KERN_INFO "DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * For this function we've flipped the cross-subchannel bit | ||
94 | * if the message is an otherdata or linkservice message. Thus | ||
95 | * we can use it to work out what to update. | ||
96 | */ | ||
97 | static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack) | ||
98 | { | ||
99 | struct dn_scp *scp = DN_SK(sk); | ||
100 | unsigned short type = ((ack >> 12) & 0x0003); | ||
101 | int wakeup = 0; | ||
102 | |||
103 | switch(type) { | ||
104 | case 0: /* ACK - Data */ | ||
105 | if (dn_after(ack, scp->ackrcv_dat)) { | ||
106 | scp->ackrcv_dat = ack & 0x0fff; | ||
107 | wakeup |= dn_nsp_check_xmit_queue(sk, skb, &scp->data_xmit_queue, ack); | ||
108 | } | ||
109 | break; | ||
110 | case 1: /* NAK - Data */ | ||
111 | break; | ||
112 | case 2: /* ACK - OtherData */ | ||
113 | if (dn_after(ack, scp->ackrcv_oth)) { | ||
114 | scp->ackrcv_oth = ack & 0x0fff; | ||
115 | wakeup |= dn_nsp_check_xmit_queue(sk, skb, &scp->other_xmit_queue, ack); | ||
116 | } | ||
117 | break; | ||
118 | case 3: /* NAK - OtherData */ | ||
119 | break; | ||
120 | } | ||
121 | |||
122 | if (wakeup && !sock_flag(sk, SOCK_DEAD)) | ||
123 | sk->sk_state_change(sk); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * This function is a universal ack processor. | ||
128 | */ | ||
129 | static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth) | ||
130 | { | ||
131 | unsigned short *ptr = (unsigned short *)skb->data; | ||
132 | int len = 0; | ||
133 | unsigned short ack; | ||
134 | |||
135 | if (skb->len < 2) | ||
136 | return len; | ||
137 | |||
138 | if ((ack = dn_ntohs(*ptr)) & 0x8000) { | ||
139 | skb_pull(skb, 2); | ||
140 | ptr++; | ||
141 | len += 2; | ||
142 | if ((ack & 0x4000) == 0) { | ||
143 | if (oth) | ||
144 | ack ^= 0x2000; | ||
145 | dn_ack(sk, skb, ack); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | if (skb->len < 2) | ||
150 | return len; | ||
151 | |||
152 | if ((ack = dn_ntohs(*ptr)) & 0x8000) { | ||
153 | skb_pull(skb, 2); | ||
154 | len += 2; | ||
155 | if ((ack & 0x4000) == 0) { | ||
156 | if (oth) | ||
157 | ack ^= 0x2000; | ||
158 | dn_ack(sk, skb, ack); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | return len; | ||
163 | } | ||
164 | |||
165 | |||
166 | /** | ||
167 | * dn_check_idf - Check an image data field format is correct. | ||
168 | * @pptr: Pointer to pointer to image data | ||
169 | * @len: Pointer to length of image data | ||
170 | * @max: The maximum allowed length of the data in the image data field | ||
171 | * @follow_on: Check that this many bytes exist beyond the end of the image data | ||
172 | * | ||
173 | * Returns: 0 if ok, -1 on error | ||
174 | */ | ||
175 | static inline int dn_check_idf(unsigned char **pptr, int *len, unsigned char max, unsigned char follow_on) | ||
176 | { | ||
177 | unsigned char *ptr = *pptr; | ||
178 | unsigned char flen = *ptr++; | ||
179 | |||
180 | (*len)--; | ||
181 | if (flen > max) | ||
182 | return -1; | ||
183 | if ((flen + follow_on) > *len) | ||
184 | return -1; | ||
185 | |||
186 | *len -= flen; | ||
187 | *pptr = ptr + flen; | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Table of reason codes to pass back to node which sent us a badly | ||
193 | * formed message, plus text messages for the log. A zero entry in | ||
194 | * the reason field means "don't reply" otherwise a disc init is sent with | ||
195 | * the specified reason code. | ||
196 | */ | ||
197 | static struct { | ||
198 | unsigned short reason; | ||
199 | const char *text; | ||
200 | } ci_err_table[] = { | ||
201 | { 0, "CI: Truncated message" }, | ||
202 | { NSP_REASON_ID, "CI: Destination username error" }, | ||
203 | { NSP_REASON_ID, "CI: Destination username type" }, | ||
204 | { NSP_REASON_US, "CI: Source username error" }, | ||
205 | { 0, "CI: Truncated at menuver" }, | ||
206 | { 0, "CI: Truncated before access or user data" }, | ||
207 | { NSP_REASON_IO, "CI: Access data format error" }, | ||
208 | { NSP_REASON_IO, "CI: User data format error" } | ||
209 | }; | ||
210 | |||
211 | /* | ||
212 | * This function uses a slightly different lookup method | ||
213 | * to find its sockets, since it searches on object name/number | ||
214 | * rather than port numbers. Various tests are done to ensure that | ||
215 | * the incoming data is in the correct format before it is queued to | ||
216 | * a socket. | ||
217 | */ | ||
218 | static struct sock *dn_find_listener(struct sk_buff *skb, unsigned short *reason) | ||
219 | { | ||
220 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
221 | struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data; | ||
222 | struct sockaddr_dn dstaddr; | ||
223 | struct sockaddr_dn srcaddr; | ||
224 | unsigned char type = 0; | ||
225 | int dstlen; | ||
226 | int srclen; | ||
227 | unsigned char *ptr; | ||
228 | int len; | ||
229 | int err = 0; | ||
230 | unsigned char menuver; | ||
231 | |||
232 | memset(&dstaddr, 0, sizeof(struct sockaddr_dn)); | ||
233 | memset(&srcaddr, 0, sizeof(struct sockaddr_dn)); | ||
234 | |||
235 | /* | ||
236 | * 1. Decode & remove message header | ||
237 | */ | ||
238 | cb->src_port = msg->srcaddr; | ||
239 | cb->dst_port = msg->dstaddr; | ||
240 | cb->services = msg->services; | ||
241 | cb->info = msg->info; | ||
242 | cb->segsize = dn_ntohs(msg->segsize); | ||
243 | |||
244 | if (!pskb_may_pull(skb, sizeof(*msg))) | ||
245 | goto err_out; | ||
246 | |||
247 | skb_pull(skb, sizeof(*msg)); | ||
248 | |||
249 | len = skb->len; | ||
250 | ptr = skb->data; | ||
251 | |||
252 | /* | ||
253 | * 2. Check destination end username format | ||
254 | */ | ||
255 | dstlen = dn_username2sockaddr(ptr, len, &dstaddr, &type); | ||
256 | err++; | ||
257 | if (dstlen < 0) | ||
258 | goto err_out; | ||
259 | |||
260 | err++; | ||
261 | if (type > 1) | ||
262 | goto err_out; | ||
263 | |||
264 | len -= dstlen; | ||
265 | ptr += dstlen; | ||
266 | |||
267 | /* | ||
268 | * 3. Check source end username format | ||
269 | */ | ||
270 | srclen = dn_username2sockaddr(ptr, len, &srcaddr, &type); | ||
271 | err++; | ||
272 | if (srclen < 0) | ||
273 | goto err_out; | ||
274 | |||
275 | len -= srclen; | ||
276 | ptr += srclen; | ||
277 | err++; | ||
278 | if (len < 1) | ||
279 | goto err_out; | ||
280 | |||
281 | menuver = *ptr; | ||
282 | ptr++; | ||
283 | len--; | ||
284 | |||
285 | /* | ||
286 | * 4. Check that optional data actually exists if menuver says it does | ||
287 | */ | ||
288 | err++; | ||
289 | if ((menuver & (DN_MENUVER_ACC | DN_MENUVER_USR)) && (len < 1)) | ||
290 | goto err_out; | ||
291 | |||
292 | /* | ||
293 | * 5. Check optional access data format | ||
294 | */ | ||
295 | err++; | ||
296 | if (menuver & DN_MENUVER_ACC) { | ||
297 | if (dn_check_idf(&ptr, &len, 39, 1)) | ||
298 | goto err_out; | ||
299 | if (dn_check_idf(&ptr, &len, 39, 1)) | ||
300 | goto err_out; | ||
301 | if (dn_check_idf(&ptr, &len, 39, (menuver & DN_MENUVER_USR) ? 1 : 0)) | ||
302 | goto err_out; | ||
303 | } | ||
304 | |||
305 | /* | ||
306 | * 6. Check optional user data format | ||
307 | */ | ||
308 | err++; | ||
309 | if (menuver & DN_MENUVER_USR) { | ||
310 | if (dn_check_idf(&ptr, &len, 16, 0)) | ||
311 | goto err_out; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * 7. Look up socket based on destination end username | ||
316 | */ | ||
317 | return dn_sklist_find_listener(&dstaddr); | ||
318 | err_out: | ||
319 | dn_log_martian(skb, ci_err_table[err].text); | ||
320 | *reason = ci_err_table[err].reason; | ||
321 | return NULL; | ||
322 | } | ||
323 | |||
324 | |||
325 | static void dn_nsp_conn_init(struct sock *sk, struct sk_buff *skb) | ||
326 | { | ||
327 | if (sk_acceptq_is_full(sk)) { | ||
328 | kfree_skb(skb); | ||
329 | return; | ||
330 | } | ||
331 | |||
332 | sk->sk_ack_backlog++; | ||
333 | skb_queue_tail(&sk->sk_receive_queue, skb); | ||
334 | sk->sk_state_change(sk); | ||
335 | } | ||
336 | |||
337 | static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb) | ||
338 | { | ||
339 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
340 | struct dn_scp *scp = DN_SK(sk); | ||
341 | unsigned char *ptr; | ||
342 | |||
343 | if (skb->len < 4) | ||
344 | goto out; | ||
345 | |||
346 | ptr = skb->data; | ||
347 | cb->services = *ptr++; | ||
348 | cb->info = *ptr++; | ||
349 | cb->segsize = dn_ntohs(*(__u16 *)ptr); | ||
350 | |||
351 | if ((scp->state == DN_CI) || (scp->state == DN_CD)) { | ||
352 | scp->persist = 0; | ||
353 | scp->addrrem = cb->src_port; | ||
354 | sk->sk_state = TCP_ESTABLISHED; | ||
355 | scp->state = DN_RUN; | ||
356 | scp->services_rem = cb->services; | ||
357 | scp->info_rem = cb->info; | ||
358 | scp->segsize_rem = cb->segsize; | ||
359 | |||
360 | if ((scp->services_rem & NSP_FC_MASK) == NSP_FC_NONE) | ||
361 | scp->max_window = decnet_no_fc_max_cwnd; | ||
362 | |||
363 | if (skb->len > 0) { | ||
364 | unsigned char dlen = *skb->data; | ||
365 | if ((dlen <= 16) && (dlen <= skb->len)) { | ||
366 | scp->conndata_in.opt_optl = dlen; | ||
367 | memcpy(scp->conndata_in.opt_data, skb->data + 1, dlen); | ||
368 | } | ||
369 | } | ||
370 | dn_nsp_send_link(sk, DN_NOCHANGE, 0); | ||
371 | if (!sock_flag(sk, SOCK_DEAD)) | ||
372 | sk->sk_state_change(sk); | ||
373 | } | ||
374 | |||
375 | out: | ||
376 | kfree_skb(skb); | ||
377 | } | ||
378 | |||
379 | static void dn_nsp_conn_ack(struct sock *sk, struct sk_buff *skb) | ||
380 | { | ||
381 | struct dn_scp *scp = DN_SK(sk); | ||
382 | |||
383 | if (scp->state == DN_CI) { | ||
384 | scp->state = DN_CD; | ||
385 | scp->persist = 0; | ||
386 | } | ||
387 | |||
388 | kfree_skb(skb); | ||
389 | } | ||
390 | |||
391 | static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb) | ||
392 | { | ||
393 | struct dn_scp *scp = DN_SK(sk); | ||
394 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
395 | unsigned short reason; | ||
396 | |||
397 | if (skb->len < 2) | ||
398 | goto out; | ||
399 | |||
400 | reason = dn_ntohs(*(__u16 *)skb->data); | ||
401 | skb_pull(skb, 2); | ||
402 | |||
403 | scp->discdata_in.opt_status = reason; | ||
404 | scp->discdata_in.opt_optl = 0; | ||
405 | memset(scp->discdata_in.opt_data, 0, 16); | ||
406 | |||
407 | if (skb->len > 0) { | ||
408 | unsigned char dlen = *skb->data; | ||
409 | if ((dlen <= 16) && (dlen <= skb->len)) { | ||
410 | scp->discdata_in.opt_optl = dlen; | ||
411 | memcpy(scp->discdata_in.opt_data, skb->data + 1, dlen); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | scp->addrrem = cb->src_port; | ||
416 | sk->sk_state = TCP_CLOSE; | ||
417 | |||
418 | switch(scp->state) { | ||
419 | case DN_CI: | ||
420 | case DN_CD: | ||
421 | scp->state = DN_RJ; | ||
422 | sk->sk_err = ECONNREFUSED; | ||
423 | break; | ||
424 | case DN_RUN: | ||
425 | sk->sk_shutdown |= SHUTDOWN_MASK; | ||
426 | scp->state = DN_DN; | ||
427 | break; | ||
428 | case DN_DI: | ||
429 | scp->state = DN_DIC; | ||
430 | break; | ||
431 | } | ||
432 | |||
433 | if (!sock_flag(sk, SOCK_DEAD)) { | ||
434 | if (sk->sk_socket->state != SS_UNCONNECTED) | ||
435 | sk->sk_socket->state = SS_DISCONNECTING; | ||
436 | sk->sk_state_change(sk); | ||
437 | } | ||
438 | |||
439 | /* | ||
440 | * It appears that its possible for remote machines to send disc | ||
441 | * init messages with no port identifier if we are in the CI and | ||
442 | * possibly also the CD state. Obviously we shouldn't reply with | ||
443 | * a message if we don't know what the end point is. | ||
444 | */ | ||
445 | if (scp->addrrem) { | ||
446 | dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC); | ||
447 | } | ||
448 | scp->persist_fxn = dn_destroy_timer; | ||
449 | scp->persist = dn_nsp_persist(sk); | ||
450 | |||
451 | out: | ||
452 | kfree_skb(skb); | ||
453 | } | ||
454 | |||
455 | /* | ||
456 | * disc_conf messages are also called no_resources or no_link | ||
457 | * messages depending upon the "reason" field. | ||
458 | */ | ||
459 | static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb) | ||
460 | { | ||
461 | struct dn_scp *scp = DN_SK(sk); | ||
462 | unsigned short reason; | ||
463 | |||
464 | if (skb->len != 2) | ||
465 | goto out; | ||
466 | |||
467 | reason = dn_ntohs(*(__u16 *)skb->data); | ||
468 | |||
469 | sk->sk_state = TCP_CLOSE; | ||
470 | |||
471 | switch(scp->state) { | ||
472 | case DN_CI: | ||
473 | scp->state = DN_NR; | ||
474 | break; | ||
475 | case DN_DR: | ||
476 | if (reason == NSP_REASON_DC) | ||
477 | scp->state = DN_DRC; | ||
478 | if (reason == NSP_REASON_NL) | ||
479 | scp->state = DN_CN; | ||
480 | break; | ||
481 | case DN_DI: | ||
482 | scp->state = DN_DIC; | ||
483 | break; | ||
484 | case DN_RUN: | ||
485 | sk->sk_shutdown |= SHUTDOWN_MASK; | ||
486 | case DN_CC: | ||
487 | scp->state = DN_CN; | ||
488 | } | ||
489 | |||
490 | if (!sock_flag(sk, SOCK_DEAD)) { | ||
491 | if (sk->sk_socket->state != SS_UNCONNECTED) | ||
492 | sk->sk_socket->state = SS_DISCONNECTING; | ||
493 | sk->sk_state_change(sk); | ||
494 | } | ||
495 | |||
496 | scp->persist_fxn = dn_destroy_timer; | ||
497 | scp->persist = dn_nsp_persist(sk); | ||
498 | |||
499 | out: | ||
500 | kfree_skb(skb); | ||
501 | } | ||
502 | |||
503 | static void dn_nsp_linkservice(struct sock *sk, struct sk_buff *skb) | ||
504 | { | ||
505 | struct dn_scp *scp = DN_SK(sk); | ||
506 | unsigned short segnum; | ||
507 | unsigned char lsflags; | ||
508 | signed char fcval; | ||
509 | int wake_up = 0; | ||
510 | char *ptr = skb->data; | ||
511 | unsigned char fctype = scp->services_rem & NSP_FC_MASK; | ||
512 | |||
513 | if (skb->len != 4) | ||
514 | goto out; | ||
515 | |||
516 | segnum = dn_ntohs(*(__u16 *)ptr); | ||
517 | ptr += 2; | ||
518 | lsflags = *(unsigned char *)ptr++; | ||
519 | fcval = *ptr; | ||
520 | |||
521 | /* | ||
522 | * Here we ignore erronous packets which should really | ||
523 | * should cause a connection abort. It is not critical | ||
524 | * for now though. | ||
525 | */ | ||
526 | if (lsflags & 0xf8) | ||
527 | goto out; | ||
528 | |||
529 | if (seq_next(scp->numoth_rcv, segnum)) { | ||
530 | seq_add(&scp->numoth_rcv, 1); | ||
531 | switch(lsflags & 0x04) { /* FCVAL INT */ | ||
532 | case 0x00: /* Normal Request */ | ||
533 | switch(lsflags & 0x03) { /* FCVAL MOD */ | ||
534 | case 0x00: /* Request count */ | ||
535 | if (fcval < 0) { | ||
536 | unsigned char p_fcval = -fcval; | ||
537 | if ((scp->flowrem_dat > p_fcval) && | ||
538 | (fctype == NSP_FC_SCMC)) { | ||
539 | scp->flowrem_dat -= p_fcval; | ||
540 | } | ||
541 | } else if (fcval > 0) { | ||
542 | scp->flowrem_dat += fcval; | ||
543 | wake_up = 1; | ||
544 | } | ||
545 | break; | ||
546 | case 0x01: /* Stop outgoing data */ | ||
547 | scp->flowrem_sw = DN_DONTSEND; | ||
548 | break; | ||
549 | case 0x02: /* Ok to start again */ | ||
550 | scp->flowrem_sw = DN_SEND; | ||
551 | dn_nsp_output(sk); | ||
552 | wake_up = 1; | ||
553 | } | ||
554 | break; | ||
555 | case 0x04: /* Interrupt Request */ | ||
556 | if (fcval > 0) { | ||
557 | scp->flowrem_oth += fcval; | ||
558 | wake_up = 1; | ||
559 | } | ||
560 | break; | ||
561 | } | ||
562 | if (wake_up && !sock_flag(sk, SOCK_DEAD)) | ||
563 | sk->sk_state_change(sk); | ||
564 | } | ||
565 | |||
566 | dn_nsp_send_oth_ack(sk); | ||
567 | |||
568 | out: | ||
569 | kfree_skb(skb); | ||
570 | } | ||
571 | |||
572 | /* | ||
573 | * Copy of sock_queue_rcv_skb (from sock.h) without | ||
574 | * bh_lock_sock() (its already held when this is called) which | ||
575 | * also allows data and other data to be queued to a socket. | ||
576 | */ | ||
577 | static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig, struct sk_buff_head *queue) | ||
578 | { | ||
579 | int err; | ||
580 | |||
581 | /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces | ||
582 | number of warnings when compiling with -W --ANK | ||
583 | */ | ||
584 | if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= | ||
585 | (unsigned)sk->sk_rcvbuf) { | ||
586 | err = -ENOMEM; | ||
587 | goto out; | ||
588 | } | ||
589 | |||
590 | err = sk_filter(sk, skb, 0); | ||
591 | if (err) | ||
592 | goto out; | ||
593 | |||
594 | skb_set_owner_r(skb, sk); | ||
595 | skb_queue_tail(queue, skb); | ||
596 | |||
597 | /* This code only runs from BH or BH protected context. | ||
598 | * Therefore the plain read_lock is ok here. -DaveM | ||
599 | */ | ||
600 | read_lock(&sk->sk_callback_lock); | ||
601 | if (!sock_flag(sk, SOCK_DEAD)) { | ||
602 | struct socket *sock = sk->sk_socket; | ||
603 | wake_up_interruptible(sk->sk_sleep); | ||
604 | if (sock && sock->fasync_list && | ||
605 | !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags)) | ||
606 | __kill_fasync(sock->fasync_list, sig, | ||
607 | (sig == SIGURG) ? POLL_PRI : POLL_IN); | ||
608 | } | ||
609 | read_unlock(&sk->sk_callback_lock); | ||
610 | out: | ||
611 | return err; | ||
612 | } | ||
613 | |||
614 | static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb) | ||
615 | { | ||
616 | struct dn_scp *scp = DN_SK(sk); | ||
617 | unsigned short segnum; | ||
618 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
619 | int queued = 0; | ||
620 | |||
621 | if (skb->len < 2) | ||
622 | goto out; | ||
623 | |||
624 | cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data); | ||
625 | skb_pull(skb, 2); | ||
626 | |||
627 | if (seq_next(scp->numoth_rcv, segnum)) { | ||
628 | |||
629 | if (dn_queue_skb(sk, skb, SIGURG, &scp->other_receive_queue) == 0) { | ||
630 | seq_add(&scp->numoth_rcv, 1); | ||
631 | scp->other_report = 0; | ||
632 | queued = 1; | ||
633 | } | ||
634 | } | ||
635 | |||
636 | dn_nsp_send_oth_ack(sk); | ||
637 | out: | ||
638 | if (!queued) | ||
639 | kfree_skb(skb); | ||
640 | } | ||
641 | |||
642 | static void dn_nsp_data(struct sock *sk, struct sk_buff *skb) | ||
643 | { | ||
644 | int queued = 0; | ||
645 | unsigned short segnum; | ||
646 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
647 | struct dn_scp *scp = DN_SK(sk); | ||
648 | |||
649 | if (skb->len < 2) | ||
650 | goto out; | ||
651 | |||
652 | cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data); | ||
653 | skb_pull(skb, 2); | ||
654 | |||
655 | if (seq_next(scp->numdat_rcv, segnum)) { | ||
656 | if (dn_queue_skb(sk, skb, SIGIO, &sk->sk_receive_queue) == 0) { | ||
657 | seq_add(&scp->numdat_rcv, 1); | ||
658 | queued = 1; | ||
659 | } | ||
660 | |||
661 | if ((scp->flowloc_sw == DN_SEND) && dn_congested(sk)) { | ||
662 | scp->flowloc_sw = DN_DONTSEND; | ||
663 | dn_nsp_send_link(sk, DN_DONTSEND, 0); | ||
664 | } | ||
665 | } | ||
666 | |||
667 | dn_nsp_send_data_ack(sk); | ||
668 | out: | ||
669 | if (!queued) | ||
670 | kfree_skb(skb); | ||
671 | } | ||
672 | |||
673 | /* | ||
674 | * If one of our conninit messages is returned, this function | ||
675 | * deals with it. It puts the socket into the NO_COMMUNICATION | ||
676 | * state. | ||
677 | */ | ||
678 | static void dn_returned_conn_init(struct sock *sk, struct sk_buff *skb) | ||
679 | { | ||
680 | struct dn_scp *scp = DN_SK(sk); | ||
681 | |||
682 | if (scp->state == DN_CI) { | ||
683 | scp->state = DN_NC; | ||
684 | sk->sk_state = TCP_CLOSE; | ||
685 | if (!sock_flag(sk, SOCK_DEAD)) | ||
686 | sk->sk_state_change(sk); | ||
687 | } | ||
688 | |||
689 | kfree_skb(skb); | ||
690 | } | ||
691 | |||
692 | static int dn_nsp_no_socket(struct sk_buff *skb, unsigned short reason) | ||
693 | { | ||
694 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
695 | int ret = NET_RX_DROP; | ||
696 | |||
697 | /* Must not reply to returned packets */ | ||
698 | if (cb->rt_flags & DN_RT_F_RTS) | ||
699 | goto out; | ||
700 | |||
701 | if ((reason != NSP_REASON_OK) && ((cb->nsp_flags & 0x0c) == 0x08)) { | ||
702 | switch(cb->nsp_flags & 0x70) { | ||
703 | case 0x10: | ||
704 | case 0x60: /* (Retransmitted) Connect Init */ | ||
705 | dn_nsp_return_disc(skb, NSP_DISCINIT, reason); | ||
706 | ret = NET_RX_SUCCESS; | ||
707 | break; | ||
708 | case 0x20: /* Connect Confirm */ | ||
709 | dn_nsp_return_disc(skb, NSP_DISCCONF, reason); | ||
710 | ret = NET_RX_SUCCESS; | ||
711 | break; | ||
712 | } | ||
713 | } | ||
714 | |||
715 | out: | ||
716 | kfree_skb(skb); | ||
717 | return ret; | ||
718 | } | ||
719 | |||
720 | static int dn_nsp_rx_packet(struct sk_buff *skb) | ||
721 | { | ||
722 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
723 | struct sock *sk = NULL; | ||
724 | unsigned char *ptr = (unsigned char *)skb->data; | ||
725 | unsigned short reason = NSP_REASON_NL; | ||
726 | |||
727 | if (!pskb_may_pull(skb, 2)) | ||
728 | goto free_out; | ||
729 | |||
730 | skb->h.raw = skb->data; | ||
731 | cb->nsp_flags = *ptr++; | ||
732 | |||
733 | if (decnet_debug_level & 2) | ||
734 | printk(KERN_DEBUG "dn_nsp_rx: Message type 0x%02x\n", (int)cb->nsp_flags); | ||
735 | |||
736 | if (cb->nsp_flags & 0x83) | ||
737 | goto free_out; | ||
738 | |||
739 | /* | ||
740 | * Filter out conninits and useless packet types | ||
741 | */ | ||
742 | if ((cb->nsp_flags & 0x0c) == 0x08) { | ||
743 | switch(cb->nsp_flags & 0x70) { | ||
744 | case 0x00: /* NOP */ | ||
745 | case 0x70: /* Reserved */ | ||
746 | case 0x50: /* Reserved, Phase II node init */ | ||
747 | goto free_out; | ||
748 | case 0x10: | ||
749 | case 0x60: | ||
750 | if (unlikely(cb->rt_flags & DN_RT_F_RTS)) | ||
751 | goto free_out; | ||
752 | sk = dn_find_listener(skb, &reason); | ||
753 | goto got_it; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | if (!pskb_may_pull(skb, 3)) | ||
758 | goto free_out; | ||
759 | |||
760 | /* | ||
761 | * Grab the destination address. | ||
762 | */ | ||
763 | cb->dst_port = *(unsigned short *)ptr; | ||
764 | cb->src_port = 0; | ||
765 | ptr += 2; | ||
766 | |||
767 | /* | ||
768 | * If not a connack, grab the source address too. | ||
769 | */ | ||
770 | if (pskb_may_pull(skb, 5)) { | ||
771 | cb->src_port = *(unsigned short *)ptr; | ||
772 | ptr += 2; | ||
773 | skb_pull(skb, 5); | ||
774 | } | ||
775 | |||
776 | /* | ||
777 | * Returned packets... | ||
778 | * Swap src & dst and look up in the normal way. | ||
779 | */ | ||
780 | if (unlikely(cb->rt_flags & DN_RT_F_RTS)) { | ||
781 | unsigned short tmp = cb->dst_port; | ||
782 | cb->dst_port = cb->src_port; | ||
783 | cb->src_port = tmp; | ||
784 | tmp = cb->dst; | ||
785 | cb->dst = cb->src; | ||
786 | cb->src = tmp; | ||
787 | } | ||
788 | |||
789 | /* | ||
790 | * Find the socket to which this skb is destined. | ||
791 | */ | ||
792 | sk = dn_find_by_skb(skb); | ||
793 | got_it: | ||
794 | if (sk != NULL) { | ||
795 | struct dn_scp *scp = DN_SK(sk); | ||
796 | int ret; | ||
797 | |||
798 | /* Reset backoff */ | ||
799 | scp->nsp_rxtshift = 0; | ||
800 | |||
801 | /* | ||
802 | * We linearize everything except data segments here. | ||
803 | */ | ||
804 | if (cb->nsp_flags & ~0x60) { | ||
805 | if (unlikely(skb_is_nonlinear(skb)) && | ||
806 | skb_linearize(skb, GFP_ATOMIC) != 0) | ||
807 | goto free_out; | ||
808 | } | ||
809 | |||
810 | bh_lock_sock(sk); | ||
811 | ret = NET_RX_SUCCESS; | ||
812 | if (decnet_debug_level & 8) | ||
813 | printk(KERN_DEBUG "NSP: 0x%02x 0x%02x 0x%04x 0x%04x %d\n", | ||
814 | (int)cb->rt_flags, (int)cb->nsp_flags, | ||
815 | (int)cb->src_port, (int)cb->dst_port, | ||
816 | !!sock_owned_by_user(sk)); | ||
817 | if (!sock_owned_by_user(sk)) | ||
818 | ret = dn_nsp_backlog_rcv(sk, skb); | ||
819 | else | ||
820 | sk_add_backlog(sk, skb); | ||
821 | bh_unlock_sock(sk); | ||
822 | sock_put(sk); | ||
823 | |||
824 | return ret; | ||
825 | } | ||
826 | |||
827 | return dn_nsp_no_socket(skb, reason); | ||
828 | |||
829 | free_out: | ||
830 | kfree_skb(skb); | ||
831 | return NET_RX_DROP; | ||
832 | } | ||
833 | |||
834 | int dn_nsp_rx(struct sk_buff *skb) | ||
835 | { | ||
836 | return NF_HOOK(PF_DECnet, NF_DN_LOCAL_IN, skb, skb->dev, NULL, dn_nsp_rx_packet); | ||
837 | } | ||
838 | |||
839 | /* | ||
840 | * This is the main receive routine for sockets. It is called | ||
841 | * from the above when the socket is not busy, and also from | ||
842 | * sock_release() when there is a backlog queued up. | ||
843 | */ | ||
844 | int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb) | ||
845 | { | ||
846 | struct dn_scp *scp = DN_SK(sk); | ||
847 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
848 | |||
849 | if (cb->rt_flags & DN_RT_F_RTS) { | ||
850 | if (cb->nsp_flags == 0x18 || cb->nsp_flags == 0x68) | ||
851 | dn_returned_conn_init(sk, skb); | ||
852 | else | ||
853 | kfree_skb(skb); | ||
854 | return NET_RX_SUCCESS; | ||
855 | } | ||
856 | |||
857 | /* | ||
858 | * Control packet. | ||
859 | */ | ||
860 | if ((cb->nsp_flags & 0x0c) == 0x08) { | ||
861 | switch(cb->nsp_flags & 0x70) { | ||
862 | case 0x10: | ||
863 | case 0x60: | ||
864 | dn_nsp_conn_init(sk, skb); | ||
865 | break; | ||
866 | case 0x20: | ||
867 | dn_nsp_conn_conf(sk, skb); | ||
868 | break; | ||
869 | case 0x30: | ||
870 | dn_nsp_disc_init(sk, skb); | ||
871 | break; | ||
872 | case 0x40: | ||
873 | dn_nsp_disc_conf(sk, skb); | ||
874 | break; | ||
875 | } | ||
876 | |||
877 | } else if (cb->nsp_flags == 0x24) { | ||
878 | /* | ||
879 | * Special for connacks, 'cos they don't have | ||
880 | * ack data or ack otherdata info. | ||
881 | */ | ||
882 | dn_nsp_conn_ack(sk, skb); | ||
883 | } else { | ||
884 | int other = 1; | ||
885 | |||
886 | /* both data and ack frames can kick a CC socket into RUN */ | ||
887 | if ((scp->state == DN_CC) && !sock_flag(sk, SOCK_DEAD)) { | ||
888 | scp->state = DN_RUN; | ||
889 | sk->sk_state = TCP_ESTABLISHED; | ||
890 | sk->sk_state_change(sk); | ||
891 | } | ||
892 | |||
893 | if ((cb->nsp_flags & 0x1c) == 0) | ||
894 | other = 0; | ||
895 | if (cb->nsp_flags == 0x04) | ||
896 | other = 0; | ||
897 | |||
898 | /* | ||
899 | * Read out ack data here, this applies equally | ||
900 | * to data, other data, link serivce and both | ||
901 | * ack data and ack otherdata. | ||
902 | */ | ||
903 | dn_process_ack(sk, skb, other); | ||
904 | |||
905 | /* | ||
906 | * If we've some sort of data here then call a | ||
907 | * suitable routine for dealing with it, otherwise | ||
908 | * the packet is an ack and can be discarded. | ||
909 | */ | ||
910 | if ((cb->nsp_flags & 0x0c) == 0) { | ||
911 | |||
912 | if (scp->state != DN_RUN) | ||
913 | goto free_out; | ||
914 | |||
915 | switch(cb->nsp_flags) { | ||
916 | case 0x10: /* LS */ | ||
917 | dn_nsp_linkservice(sk, skb); | ||
918 | break; | ||
919 | case 0x30: /* OD */ | ||
920 | dn_nsp_otherdata(sk, skb); | ||
921 | break; | ||
922 | default: | ||
923 | dn_nsp_data(sk, skb); | ||
924 | } | ||
925 | |||
926 | } else { /* Ack, chuck it out here */ | ||
927 | free_out: | ||
928 | kfree_skb(skb); | ||
929 | } | ||
930 | } | ||
931 | |||
932 | return NET_RX_SUCCESS; | ||
933 | } | ||
934 | |||
diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c new file mode 100644 index 000000000000..42abbf3f524f --- /dev/null +++ b/net/decnet/dn_nsp_out.c | |||
@@ -0,0 +1,782 @@ | |||
1 | |||
2 | /* | ||
3 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
4 | * operating system. DECnet is implemented using the BSD Socket | ||
5 | * interface as the means of communication with the user level. | ||
6 | * | ||
7 | * DECnet Network Services Protocol (Output) | ||
8 | * | ||
9 | * Author: Eduardo Marcelo Serrat <emserrat@geocities.com> | ||
10 | * | ||
11 | * Changes: | ||
12 | * | ||
13 | * Steve Whitehouse: Split into dn_nsp_in.c and dn_nsp_out.c from | ||
14 | * original dn_nsp.c. | ||
15 | * Steve Whitehouse: Updated to work with my new routing architecture. | ||
16 | * Steve Whitehouse: Added changes from Eduardo Serrat's patches. | ||
17 | * Steve Whitehouse: Now conninits have the "return" bit set. | ||
18 | * Steve Whitehouse: Fixes to check alloc'd skbs are non NULL! | ||
19 | * Moved output state machine into one function | ||
20 | * Steve Whitehouse: New output state machine | ||
21 | * Paul Koning: Connect Confirm message fix. | ||
22 | * Eduardo Serrat: Fix to stop dn_nsp_do_disc() sending malformed packets. | ||
23 | * Steve Whitehouse: dn_nsp_output() and friends needed a spring clean | ||
24 | * Steve Whitehouse: Moved dn_nsp_send() in here from route.h | ||
25 | */ | ||
26 | |||
27 | /****************************************************************************** | ||
28 | (c) 1995-1998 E.M. Serrat emserrat@geocities.com | ||
29 | |||
30 | This program is free software; you can redistribute it and/or modify | ||
31 | it under the terms of the GNU General Public License as published by | ||
32 | the Free Software Foundation; either version 2 of the License, or | ||
33 | any later version. | ||
34 | |||
35 | This program is distributed in the hope that it will be useful, | ||
36 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
37 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
38 | GNU General Public License for more details. | ||
39 | *******************************************************************************/ | ||
40 | |||
41 | #include <linux/errno.h> | ||
42 | #include <linux/types.h> | ||
43 | #include <linux/socket.h> | ||
44 | #include <linux/in.h> | ||
45 | #include <linux/kernel.h> | ||
46 | #include <linux/sched.h> | ||
47 | #include <linux/timer.h> | ||
48 | #include <linux/string.h> | ||
49 | #include <linux/sockios.h> | ||
50 | #include <linux/net.h> | ||
51 | #include <linux/netdevice.h> | ||
52 | #include <linux/inet.h> | ||
53 | #include <linux/route.h> | ||
54 | #include <net/sock.h> | ||
55 | #include <asm/system.h> | ||
56 | #include <linux/fcntl.h> | ||
57 | #include <linux/mm.h> | ||
58 | #include <linux/termios.h> | ||
59 | #include <linux/interrupt.h> | ||
60 | #include <linux/proc_fs.h> | ||
61 | #include <linux/stat.h> | ||
62 | #include <linux/init.h> | ||
63 | #include <linux/poll.h> | ||
64 | #include <linux/if_packet.h> | ||
65 | #include <net/neighbour.h> | ||
66 | #include <net/dst.h> | ||
67 | #include <net/flow.h> | ||
68 | #include <net/dn.h> | ||
69 | #include <net/dn_nsp.h> | ||
70 | #include <net/dn_dev.h> | ||
71 | #include <net/dn_route.h> | ||
72 | |||
73 | |||
74 | static int nsp_backoff[NSP_MAXRXTSHIFT + 1] = { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; | ||
75 | |||
76 | static void dn_nsp_send(struct sk_buff *skb) | ||
77 | { | ||
78 | struct sock *sk = skb->sk; | ||
79 | struct dn_scp *scp = DN_SK(sk); | ||
80 | struct dst_entry *dst; | ||
81 | struct flowi fl; | ||
82 | |||
83 | skb->h.raw = skb->data; | ||
84 | scp->stamp = jiffies; | ||
85 | |||
86 | dst = sk_dst_check(sk, 0); | ||
87 | if (dst) { | ||
88 | try_again: | ||
89 | skb->dst = dst; | ||
90 | dst_output(skb); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | memset(&fl, 0, sizeof(fl)); | ||
95 | fl.oif = sk->sk_bound_dev_if; | ||
96 | fl.fld_src = dn_saddr2dn(&scp->addr); | ||
97 | fl.fld_dst = dn_saddr2dn(&scp->peer); | ||
98 | dn_sk_ports_copy(&fl, scp); | ||
99 | fl.proto = DNPROTO_NSP; | ||
100 | if (dn_route_output_sock(&sk->sk_dst_cache, &fl, sk, 0) == 0) { | ||
101 | dst = sk_dst_get(sk); | ||
102 | sk->sk_route_caps = dst->dev->features; | ||
103 | goto try_again; | ||
104 | } | ||
105 | |||
106 | sk->sk_err = EHOSTUNREACH; | ||
107 | if (!sock_flag(sk, SOCK_DEAD)) | ||
108 | sk->sk_state_change(sk); | ||
109 | } | ||
110 | |||
111 | |||
112 | /* | ||
113 | * If sk == NULL, then we assume that we are supposed to be making | ||
114 | * a routing layer skb. If sk != NULL, then we are supposed to be | ||
115 | * creating an skb for the NSP layer. | ||
116 | * | ||
117 | * The eventual aim is for each socket to have a cached header size | ||
118 | * for its outgoing packets, and to set hdr from this when sk != NULL. | ||
119 | */ | ||
120 | struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri) | ||
121 | { | ||
122 | struct sk_buff *skb; | ||
123 | int hdr = 64; | ||
124 | |||
125 | if ((skb = alloc_skb(size + hdr, pri)) == NULL) | ||
126 | return NULL; | ||
127 | |||
128 | skb->protocol = __constant_htons(ETH_P_DNA_RT); | ||
129 | skb->pkt_type = PACKET_OUTGOING; | ||
130 | |||
131 | if (sk) | ||
132 | skb_set_owner_w(skb, sk); | ||
133 | |||
134 | skb_reserve(skb, hdr); | ||
135 | |||
136 | return skb; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Wrapper for the above, for allocs of data skbs. We try and get the | ||
141 | * whole size thats been asked for (plus 11 bytes of header). If this | ||
142 | * fails, then we try for any size over 16 bytes for SOCK_STREAMS. | ||
143 | */ | ||
144 | struct sk_buff *dn_alloc_send_skb(struct sock *sk, size_t *size, int noblock, long timeo, int *err) | ||
145 | { | ||
146 | int space; | ||
147 | int len; | ||
148 | struct sk_buff *skb = NULL; | ||
149 | |||
150 | *err = 0; | ||
151 | |||
152 | while(skb == NULL) { | ||
153 | if (signal_pending(current)) { | ||
154 | *err = sock_intr_errno(timeo); | ||
155 | break; | ||
156 | } | ||
157 | |||
158 | if (sk->sk_shutdown & SEND_SHUTDOWN) { | ||
159 | *err = EINVAL; | ||
160 | break; | ||
161 | } | ||
162 | |||
163 | if (sk->sk_err) | ||
164 | break; | ||
165 | |||
166 | len = *size + 11; | ||
167 | space = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); | ||
168 | |||
169 | if (space < len) { | ||
170 | if ((sk->sk_socket->type == SOCK_STREAM) && | ||
171 | (space >= (16 + 11))) | ||
172 | len = space; | ||
173 | } | ||
174 | |||
175 | if (space < len) { | ||
176 | set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); | ||
177 | if (noblock) { | ||
178 | *err = EWOULDBLOCK; | ||
179 | break; | ||
180 | } | ||
181 | |||
182 | clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); | ||
183 | SOCK_SLEEP_PRE(sk) | ||
184 | |||
185 | if ((sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc)) < | ||
186 | len) | ||
187 | schedule(); | ||
188 | |||
189 | SOCK_SLEEP_POST(sk) | ||
190 | continue; | ||
191 | } | ||
192 | |||
193 | if ((skb = dn_alloc_skb(sk, len, sk->sk_allocation)) == NULL) | ||
194 | continue; | ||
195 | |||
196 | *size = len - 11; | ||
197 | } | ||
198 | |||
199 | return skb; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * Calculate persist timer based upon the smoothed round | ||
204 | * trip time and the variance. Backoff according to the | ||
205 | * nsp_backoff[] array. | ||
206 | */ | ||
207 | unsigned long dn_nsp_persist(struct sock *sk) | ||
208 | { | ||
209 | struct dn_scp *scp = DN_SK(sk); | ||
210 | |||
211 | unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1; | ||
212 | |||
213 | t *= nsp_backoff[scp->nsp_rxtshift]; | ||
214 | |||
215 | if (t < HZ) t = HZ; | ||
216 | if (t > (600*HZ)) t = (600*HZ); | ||
217 | |||
218 | if (scp->nsp_rxtshift < NSP_MAXRXTSHIFT) | ||
219 | scp->nsp_rxtshift++; | ||
220 | |||
221 | /* printk(KERN_DEBUG "rxtshift %lu, t=%lu\n", scp->nsp_rxtshift, t); */ | ||
222 | |||
223 | return t; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * This is called each time we get an estimate for the rtt | ||
228 | * on the link. | ||
229 | */ | ||
230 | static void dn_nsp_rtt(struct sock *sk, long rtt) | ||
231 | { | ||
232 | struct dn_scp *scp = DN_SK(sk); | ||
233 | long srtt = (long)scp->nsp_srtt; | ||
234 | long rttvar = (long)scp->nsp_rttvar; | ||
235 | long delta; | ||
236 | |||
237 | /* | ||
238 | * If the jiffies clock flips over in the middle of timestamp | ||
239 | * gathering this value might turn out negative, so we make sure | ||
240 | * that is it always positive here. | ||
241 | */ | ||
242 | if (rtt < 0) | ||
243 | rtt = -rtt; | ||
244 | /* | ||
245 | * Add new rtt to smoothed average | ||
246 | */ | ||
247 | delta = ((rtt << 3) - srtt); | ||
248 | srtt += (delta >> 3); | ||
249 | if (srtt >= 1) | ||
250 | scp->nsp_srtt = (unsigned long)srtt; | ||
251 | else | ||
252 | scp->nsp_srtt = 1; | ||
253 | |||
254 | /* | ||
255 | * Add new rtt varience to smoothed varience | ||
256 | */ | ||
257 | delta >>= 1; | ||
258 | rttvar += ((((delta>0)?(delta):(-delta)) - rttvar) >> 2); | ||
259 | if (rttvar >= 1) | ||
260 | scp->nsp_rttvar = (unsigned long)rttvar; | ||
261 | else | ||
262 | scp->nsp_rttvar = 1; | ||
263 | |||
264 | /* printk(KERN_DEBUG "srtt=%lu rttvar=%lu\n", scp->nsp_srtt, scp->nsp_rttvar); */ | ||
265 | } | ||
266 | |||
267 | /** | ||
268 | * dn_nsp_clone_and_send - Send a data packet by cloning it | ||
269 | * @skb: The packet to clone and transmit | ||
270 | * @gfp: memory allocation flag | ||
271 | * | ||
272 | * Clone a queued data or other data packet and transmit it. | ||
273 | * | ||
274 | * Returns: The number of times the packet has been sent previously | ||
275 | */ | ||
276 | static inline unsigned dn_nsp_clone_and_send(struct sk_buff *skb, int gfp) | ||
277 | { | ||
278 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
279 | struct sk_buff *skb2; | ||
280 | int ret = 0; | ||
281 | |||
282 | if ((skb2 = skb_clone(skb, gfp)) != NULL) { | ||
283 | ret = cb->xmit_count; | ||
284 | cb->xmit_count++; | ||
285 | cb->stamp = jiffies; | ||
286 | skb2->sk = skb->sk; | ||
287 | dn_nsp_send(skb2); | ||
288 | } | ||
289 | |||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * dn_nsp_output - Try and send something from socket queues | ||
295 | * @sk: The socket whose queues are to be investigated | ||
296 | * @gfp: The memory allocation flags | ||
297 | * | ||
298 | * Try and send the packet on the end of the data and other data queues. | ||
299 | * Other data gets priority over data, and if we retransmit a packet we | ||
300 | * reduce the window by dividing it in two. | ||
301 | * | ||
302 | */ | ||
303 | void dn_nsp_output(struct sock *sk) | ||
304 | { | ||
305 | struct dn_scp *scp = DN_SK(sk); | ||
306 | struct sk_buff *skb; | ||
307 | unsigned reduce_win = 0; | ||
308 | |||
309 | /* | ||
310 | * First we check for otherdata/linkservice messages | ||
311 | */ | ||
312 | if ((skb = skb_peek(&scp->other_xmit_queue)) != NULL) | ||
313 | reduce_win = dn_nsp_clone_and_send(skb, GFP_ATOMIC); | ||
314 | |||
315 | /* | ||
316 | * If we may not send any data, we don't. | ||
317 | * If we are still trying to get some other data down the | ||
318 | * channel, we don't try and send any data. | ||
319 | */ | ||
320 | if (reduce_win || (scp->flowrem_sw != DN_SEND)) | ||
321 | goto recalc_window; | ||
322 | |||
323 | if ((skb = skb_peek(&scp->data_xmit_queue)) != NULL) | ||
324 | reduce_win = dn_nsp_clone_and_send(skb, GFP_ATOMIC); | ||
325 | |||
326 | /* | ||
327 | * If we've sent any frame more than once, we cut the | ||
328 | * send window size in half. There is always a minimum | ||
329 | * window size of one available. | ||
330 | */ | ||
331 | recalc_window: | ||
332 | if (reduce_win) { | ||
333 | scp->snd_window >>= 1; | ||
334 | if (scp->snd_window < NSP_MIN_WINDOW) | ||
335 | scp->snd_window = NSP_MIN_WINDOW; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | int dn_nsp_xmit_timeout(struct sock *sk) | ||
340 | { | ||
341 | struct dn_scp *scp = DN_SK(sk); | ||
342 | |||
343 | dn_nsp_output(sk); | ||
344 | |||
345 | if (skb_queue_len(&scp->data_xmit_queue) || skb_queue_len(&scp->other_xmit_queue)) | ||
346 | scp->persist = dn_nsp_persist(sk); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static inline unsigned char *dn_mk_common_header(struct dn_scp *scp, struct sk_buff *skb, unsigned char msgflag, int len) | ||
352 | { | ||
353 | unsigned char *ptr = skb_push(skb, len); | ||
354 | |||
355 | BUG_ON(len < 5); | ||
356 | |||
357 | *ptr++ = msgflag; | ||
358 | *((unsigned short *)ptr) = scp->addrrem; | ||
359 | ptr += 2; | ||
360 | *((unsigned short *)ptr) = scp->addrloc; | ||
361 | ptr += 2; | ||
362 | return ptr; | ||
363 | } | ||
364 | |||
365 | static unsigned short *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, unsigned char msgflag, int hlen, int other) | ||
366 | { | ||
367 | struct dn_scp *scp = DN_SK(sk); | ||
368 | unsigned short acknum = scp->numdat_rcv & 0x0FFF; | ||
369 | unsigned short ackcrs = scp->numoth_rcv & 0x0FFF; | ||
370 | unsigned short *ptr; | ||
371 | |||
372 | BUG_ON(hlen < 9); | ||
373 | |||
374 | scp->ackxmt_dat = acknum; | ||
375 | scp->ackxmt_oth = ackcrs; | ||
376 | acknum |= 0x8000; | ||
377 | ackcrs |= 0x8000; | ||
378 | |||
379 | /* If this is an "other data/ack" message, swap acknum and ackcrs */ | ||
380 | if (other) { | ||
381 | unsigned short tmp = acknum; | ||
382 | acknum = ackcrs; | ||
383 | ackcrs = tmp; | ||
384 | } | ||
385 | |||
386 | /* Set "cross subchannel" bit in ackcrs */ | ||
387 | ackcrs |= 0x2000; | ||
388 | |||
389 | ptr = (unsigned short *)dn_mk_common_header(scp, skb, msgflag, hlen); | ||
390 | |||
391 | *ptr++ = dn_htons(acknum); | ||
392 | *ptr++ = dn_htons(ackcrs); | ||
393 | |||
394 | return ptr; | ||
395 | } | ||
396 | |||
397 | static unsigned short *dn_nsp_mk_data_header(struct sock *sk, struct sk_buff *skb, int oth) | ||
398 | { | ||
399 | struct dn_scp *scp = DN_SK(sk); | ||
400 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
401 | unsigned short *ptr = dn_mk_ack_header(sk, skb, cb->nsp_flags, 11, oth); | ||
402 | |||
403 | if (unlikely(oth)) { | ||
404 | cb->segnum = scp->numoth; | ||
405 | seq_add(&scp->numoth, 1); | ||
406 | } else { | ||
407 | cb->segnum = scp->numdat; | ||
408 | seq_add(&scp->numdat, 1); | ||
409 | } | ||
410 | *(ptr++) = dn_htons(cb->segnum); | ||
411 | |||
412 | return ptr; | ||
413 | } | ||
414 | |||
415 | void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, int gfp, int oth) | ||
416 | { | ||
417 | struct dn_scp *scp = DN_SK(sk); | ||
418 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
419 | unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1; | ||
420 | |||
421 | cb->xmit_count = 0; | ||
422 | dn_nsp_mk_data_header(sk, skb, oth); | ||
423 | |||
424 | /* | ||
425 | * Slow start: If we have been idle for more than | ||
426 | * one RTT, then reset window to min size. | ||
427 | */ | ||
428 | if ((jiffies - scp->stamp) > t) | ||
429 | scp->snd_window = NSP_MIN_WINDOW; | ||
430 | |||
431 | if (oth) | ||
432 | skb_queue_tail(&scp->other_xmit_queue, skb); | ||
433 | else | ||
434 | skb_queue_tail(&scp->data_xmit_queue, skb); | ||
435 | |||
436 | if (scp->flowrem_sw != DN_SEND) | ||
437 | return; | ||
438 | |||
439 | dn_nsp_clone_and_send(skb, gfp); | ||
440 | } | ||
441 | |||
442 | |||
443 | int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *q, unsigned short acknum) | ||
444 | { | ||
445 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
446 | struct dn_scp *scp = DN_SK(sk); | ||
447 | struct sk_buff *skb2, *list, *ack = NULL; | ||
448 | int wakeup = 0; | ||
449 | int try_retrans = 0; | ||
450 | unsigned long reftime = cb->stamp; | ||
451 | unsigned long pkttime; | ||
452 | unsigned short xmit_count; | ||
453 | unsigned short segnum; | ||
454 | |||
455 | skb2 = q->next; | ||
456 | list = (struct sk_buff *)q; | ||
457 | while(list != skb2) { | ||
458 | struct dn_skb_cb *cb2 = DN_SKB_CB(skb2); | ||
459 | |||
460 | if (dn_before_or_equal(cb2->segnum, acknum)) | ||
461 | ack = skb2; | ||
462 | |||
463 | /* printk(KERN_DEBUG "ack: %s %04x %04x\n", ack ? "ACK" : "SKIP", (int)cb2->segnum, (int)acknum); */ | ||
464 | |||
465 | skb2 = skb2->next; | ||
466 | |||
467 | if (ack == NULL) | ||
468 | continue; | ||
469 | |||
470 | /* printk(KERN_DEBUG "check_xmit_queue: %04x, %d\n", acknum, cb2->xmit_count); */ | ||
471 | |||
472 | /* Does _last_ packet acked have xmit_count > 1 */ | ||
473 | try_retrans = 0; | ||
474 | /* Remember to wake up the sending process */ | ||
475 | wakeup = 1; | ||
476 | /* Keep various statistics */ | ||
477 | pkttime = cb2->stamp; | ||
478 | xmit_count = cb2->xmit_count; | ||
479 | segnum = cb2->segnum; | ||
480 | /* Remove and drop ack'ed packet */ | ||
481 | skb_unlink(ack); | ||
482 | kfree_skb(ack); | ||
483 | ack = NULL; | ||
484 | |||
485 | /* | ||
486 | * We don't expect to see acknowledgements for packets we | ||
487 | * haven't sent yet. | ||
488 | */ | ||
489 | WARN_ON(xmit_count == 0); | ||
490 | |||
491 | /* | ||
492 | * If the packet has only been sent once, we can use it | ||
493 | * to calculate the RTT and also open the window a little | ||
494 | * further. | ||
495 | */ | ||
496 | if (xmit_count == 1) { | ||
497 | if (dn_equal(segnum, acknum)) | ||
498 | dn_nsp_rtt(sk, (long)(pkttime - reftime)); | ||
499 | |||
500 | if (scp->snd_window < scp->max_window) | ||
501 | scp->snd_window++; | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * Packet has been sent more than once. If this is the last | ||
506 | * packet to be acknowledged then we want to send the next | ||
507 | * packet in the send queue again (assumes the remote host does | ||
508 | * go-back-N error control). | ||
509 | */ | ||
510 | if (xmit_count > 1) | ||
511 | try_retrans = 1; | ||
512 | } | ||
513 | |||
514 | if (try_retrans) | ||
515 | dn_nsp_output(sk); | ||
516 | |||
517 | return wakeup; | ||
518 | } | ||
519 | |||
520 | void dn_nsp_send_data_ack(struct sock *sk) | ||
521 | { | ||
522 | struct sk_buff *skb = NULL; | ||
523 | |||
524 | if ((skb = dn_alloc_skb(sk, 9, GFP_ATOMIC)) == NULL) | ||
525 | return; | ||
526 | |||
527 | skb_reserve(skb, 9); | ||
528 | dn_mk_ack_header(sk, skb, 0x04, 9, 0); | ||
529 | dn_nsp_send(skb); | ||
530 | } | ||
531 | |||
532 | void dn_nsp_send_oth_ack(struct sock *sk) | ||
533 | { | ||
534 | struct sk_buff *skb = NULL; | ||
535 | |||
536 | if ((skb = dn_alloc_skb(sk, 9, GFP_ATOMIC)) == NULL) | ||
537 | return; | ||
538 | |||
539 | skb_reserve(skb, 9); | ||
540 | dn_mk_ack_header(sk, skb, 0x14, 9, 1); | ||
541 | dn_nsp_send(skb); | ||
542 | } | ||
543 | |||
544 | |||
545 | void dn_send_conn_ack (struct sock *sk) | ||
546 | { | ||
547 | struct dn_scp *scp = DN_SK(sk); | ||
548 | struct sk_buff *skb = NULL; | ||
549 | struct nsp_conn_ack_msg *msg; | ||
550 | |||
551 | if ((skb = dn_alloc_skb(sk, 3, sk->sk_allocation)) == NULL) | ||
552 | return; | ||
553 | |||
554 | msg = (struct nsp_conn_ack_msg *)skb_put(skb, 3); | ||
555 | msg->msgflg = 0x24; | ||
556 | msg->dstaddr = scp->addrrem; | ||
557 | |||
558 | dn_nsp_send(skb); | ||
559 | } | ||
560 | |||
561 | void dn_nsp_delayed_ack(struct sock *sk) | ||
562 | { | ||
563 | struct dn_scp *scp = DN_SK(sk); | ||
564 | |||
565 | if (scp->ackxmt_oth != scp->numoth_rcv) | ||
566 | dn_nsp_send_oth_ack(sk); | ||
567 | |||
568 | if (scp->ackxmt_dat != scp->numdat_rcv) | ||
569 | dn_nsp_send_data_ack(sk); | ||
570 | } | ||
571 | |||
572 | static int dn_nsp_retrans_conn_conf(struct sock *sk) | ||
573 | { | ||
574 | struct dn_scp *scp = DN_SK(sk); | ||
575 | |||
576 | if (scp->state == DN_CC) | ||
577 | dn_send_conn_conf(sk, GFP_ATOMIC); | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | void dn_send_conn_conf(struct sock *sk, int gfp) | ||
583 | { | ||
584 | struct dn_scp *scp = DN_SK(sk); | ||
585 | struct sk_buff *skb = NULL; | ||
586 | struct nsp_conn_init_msg *msg; | ||
587 | unsigned char len = scp->conndata_out.opt_optl; | ||
588 | |||
589 | if ((skb = dn_alloc_skb(sk, 50 + scp->conndata_out.opt_optl, gfp)) == NULL) | ||
590 | return; | ||
591 | |||
592 | msg = (struct nsp_conn_init_msg *)skb_put(skb, sizeof(*msg)); | ||
593 | msg->msgflg = 0x28; | ||
594 | msg->dstaddr = scp->addrrem; | ||
595 | msg->srcaddr = scp->addrloc; | ||
596 | msg->services = scp->services_loc; | ||
597 | msg->info = scp->info_loc; | ||
598 | msg->segsize = dn_htons(scp->segsize_loc); | ||
599 | |||
600 | *skb_put(skb,1) = len; | ||
601 | |||
602 | if (len > 0) | ||
603 | memcpy(skb_put(skb, len), scp->conndata_out.opt_data, len); | ||
604 | |||
605 | |||
606 | dn_nsp_send(skb); | ||
607 | |||
608 | scp->persist = dn_nsp_persist(sk); | ||
609 | scp->persist_fxn = dn_nsp_retrans_conn_conf; | ||
610 | } | ||
611 | |||
612 | |||
613 | static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg, | ||
614 | unsigned short reason, int gfp, struct dst_entry *dst, | ||
615 | int ddl, unsigned char *dd, __u16 rem, __u16 loc) | ||
616 | { | ||
617 | struct sk_buff *skb = NULL; | ||
618 | int size = 7 + ddl + ((msgflg == NSP_DISCINIT) ? 1 : 0); | ||
619 | unsigned char *msg; | ||
620 | |||
621 | if ((dst == NULL) || (rem == 0)) { | ||
622 | if (net_ratelimit()) | ||
623 | printk(KERN_DEBUG "DECnet: dn_nsp_do_disc: BUG! Please report this to SteveW@ACM.org rem=%u dst=%p\n", (unsigned)rem, dst); | ||
624 | return; | ||
625 | } | ||
626 | |||
627 | if ((skb = dn_alloc_skb(sk, size, gfp)) == NULL) | ||
628 | return; | ||
629 | |||
630 | msg = skb_put(skb, size); | ||
631 | *msg++ = msgflg; | ||
632 | *(__u16 *)msg = rem; | ||
633 | msg += 2; | ||
634 | *(__u16 *)msg = loc; | ||
635 | msg += 2; | ||
636 | *(__u16 *)msg = dn_htons(reason); | ||
637 | msg += 2; | ||
638 | if (msgflg == NSP_DISCINIT) | ||
639 | *msg++ = ddl; | ||
640 | |||
641 | if (ddl) { | ||
642 | memcpy(msg, dd, ddl); | ||
643 | } | ||
644 | |||
645 | /* | ||
646 | * This doesn't go via the dn_nsp_send() function since we need | ||
647 | * to be able to send disc packets out which have no socket | ||
648 | * associations. | ||
649 | */ | ||
650 | skb->dst = dst_clone(dst); | ||
651 | dst_output(skb); | ||
652 | } | ||
653 | |||
654 | |||
655 | void dn_nsp_send_disc(struct sock *sk, unsigned char msgflg, | ||
656 | unsigned short reason, int gfp) | ||
657 | { | ||
658 | struct dn_scp *scp = DN_SK(sk); | ||
659 | int ddl = 0; | ||
660 | |||
661 | if (msgflg == NSP_DISCINIT) | ||
662 | ddl = scp->discdata_out.opt_optl; | ||
663 | |||
664 | if (reason == 0) | ||
665 | reason = scp->discdata_out.opt_status; | ||
666 | |||
667 | dn_nsp_do_disc(sk, msgflg, reason, gfp, sk->sk_dst_cache, ddl, | ||
668 | scp->discdata_out.opt_data, scp->addrrem, scp->addrloc); | ||
669 | } | ||
670 | |||
671 | |||
672 | void dn_nsp_return_disc(struct sk_buff *skb, unsigned char msgflg, | ||
673 | unsigned short reason) | ||
674 | { | ||
675 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
676 | int ddl = 0; | ||
677 | int gfp = GFP_ATOMIC; | ||
678 | |||
679 | dn_nsp_do_disc(NULL, msgflg, reason, gfp, skb->dst, ddl, | ||
680 | NULL, cb->src_port, cb->dst_port); | ||
681 | } | ||
682 | |||
683 | |||
684 | void dn_nsp_send_link(struct sock *sk, unsigned char lsflags, char fcval) | ||
685 | { | ||
686 | struct dn_scp *scp = DN_SK(sk); | ||
687 | struct sk_buff *skb; | ||
688 | unsigned char *ptr; | ||
689 | int gfp = GFP_ATOMIC; | ||
690 | |||
691 | if ((skb = dn_alloc_skb(sk, DN_MAX_NSP_DATA_HEADER + 2, gfp)) == NULL) | ||
692 | return; | ||
693 | |||
694 | skb_reserve(skb, DN_MAX_NSP_DATA_HEADER); | ||
695 | ptr = skb_put(skb, 2); | ||
696 | DN_SKB_CB(skb)->nsp_flags = 0x10; | ||
697 | *ptr++ = lsflags; | ||
698 | *ptr = fcval; | ||
699 | |||
700 | dn_nsp_queue_xmit(sk, skb, gfp, 1); | ||
701 | |||
702 | scp->persist = dn_nsp_persist(sk); | ||
703 | scp->persist_fxn = dn_nsp_xmit_timeout; | ||
704 | } | ||
705 | |||
706 | static int dn_nsp_retrans_conninit(struct sock *sk) | ||
707 | { | ||
708 | struct dn_scp *scp = DN_SK(sk); | ||
709 | |||
710 | if (scp->state == DN_CI) | ||
711 | dn_nsp_send_conninit(sk, NSP_RCI); | ||
712 | |||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg) | ||
717 | { | ||
718 | struct dn_scp *scp = DN_SK(sk); | ||
719 | struct nsp_conn_init_msg *msg; | ||
720 | unsigned char aux; | ||
721 | unsigned char menuver; | ||
722 | struct dn_skb_cb *cb; | ||
723 | unsigned char type = 1; | ||
724 | int allocation = (msgflg == NSP_CI) ? sk->sk_allocation : GFP_ATOMIC; | ||
725 | struct sk_buff *skb = dn_alloc_skb(sk, 200, allocation); | ||
726 | |||
727 | if (!skb) | ||
728 | return; | ||
729 | |||
730 | cb = DN_SKB_CB(skb); | ||
731 | msg = (struct nsp_conn_init_msg *)skb_put(skb,sizeof(*msg)); | ||
732 | |||
733 | msg->msgflg = msgflg; | ||
734 | msg->dstaddr = 0x0000; /* Remote Node will assign it*/ | ||
735 | |||
736 | msg->srcaddr = scp->addrloc; | ||
737 | msg->services = scp->services_loc; /* Requested flow control */ | ||
738 | msg->info = scp->info_loc; /* Version Number */ | ||
739 | msg->segsize = dn_htons(scp->segsize_loc); /* Max segment size */ | ||
740 | |||
741 | if (scp->peer.sdn_objnum) | ||
742 | type = 0; | ||
743 | |||
744 | skb_put(skb, dn_sockaddr2username(&scp->peer, skb->tail, type)); | ||
745 | skb_put(skb, dn_sockaddr2username(&scp->addr, skb->tail, 2)); | ||
746 | |||
747 | menuver = DN_MENUVER_ACC | DN_MENUVER_USR; | ||
748 | if (scp->peer.sdn_flags & SDF_PROXY) | ||
749 | menuver |= DN_MENUVER_PRX; | ||
750 | if (scp->peer.sdn_flags & SDF_UICPROXY) | ||
751 | menuver |= DN_MENUVER_UIC; | ||
752 | |||
753 | *skb_put(skb, 1) = menuver; /* Menu Version */ | ||
754 | |||
755 | aux = scp->accessdata.acc_userl; | ||
756 | *skb_put(skb, 1) = aux; | ||
757 | if (aux > 0) | ||
758 | memcpy(skb_put(skb, aux), scp->accessdata.acc_user, aux); | ||
759 | |||
760 | aux = scp->accessdata.acc_passl; | ||
761 | *skb_put(skb, 1) = aux; | ||
762 | if (aux > 0) | ||
763 | memcpy(skb_put(skb, aux), scp->accessdata.acc_pass, aux); | ||
764 | |||
765 | aux = scp->accessdata.acc_accl; | ||
766 | *skb_put(skb, 1) = aux; | ||
767 | if (aux > 0) | ||
768 | memcpy(skb_put(skb, aux), scp->accessdata.acc_acc, aux); | ||
769 | |||
770 | aux = scp->conndata_out.opt_optl; | ||
771 | *skb_put(skb, 1) = aux; | ||
772 | if (aux > 0) | ||
773 | memcpy(skb_put(skb,aux), scp->conndata_out.opt_data, aux); | ||
774 | |||
775 | scp->persist = dn_nsp_persist(sk); | ||
776 | scp->persist_fxn = dn_nsp_retrans_conninit; | ||
777 | |||
778 | cb->rt_flags = DN_RT_F_RQR; | ||
779 | |||
780 | dn_nsp_send(skb); | ||
781 | } | ||
782 | |||
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c new file mode 100644 index 000000000000..1e7b5c3ea215 --- /dev/null +++ b/net/decnet/dn_route.c | |||
@@ -0,0 +1,1840 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet Routing Functions (Endnode and Router) | ||
7 | * | ||
8 | * Authors: Steve Whitehouse <SteveW@ACM.org> | ||
9 | * Eduardo Marcelo Serrat <emserrat@geocities.com> | ||
10 | * | ||
11 | * Changes: | ||
12 | * Steve Whitehouse : Fixes to allow "intra-ethernet" and | ||
13 | * "return-to-sender" bits on outgoing | ||
14 | * packets. | ||
15 | * Steve Whitehouse : Timeouts for cached routes. | ||
16 | * Steve Whitehouse : Use dst cache for input routes too. | ||
17 | * Steve Whitehouse : Fixed error values in dn_send_skb. | ||
18 | * Steve Whitehouse : Rework routing functions to better fit | ||
19 | * DECnet routing design | ||
20 | * Alexey Kuznetsov : New SMP locking | ||
21 | * Steve Whitehouse : More SMP locking changes & dn_cache_dump() | ||
22 | * Steve Whitehouse : Prerouting NF hook, now really is prerouting. | ||
23 | * Fixed possible skb leak in rtnetlink funcs. | ||
24 | * Steve Whitehouse : Dave Miller's dynamic hash table sizing and | ||
25 | * Alexey Kuznetsov's finer grained locking | ||
26 | * from ipv4/route.c. | ||
27 | * Steve Whitehouse : Routing is now starting to look like a | ||
28 | * sensible set of code now, mainly due to | ||
29 | * my copying the IPv4 routing code. The | ||
30 | * hooks here are modified and will continue | ||
31 | * to evolve for a while. | ||
32 | * Steve Whitehouse : Real SMP at last :-) Also new netfilter | ||
33 | * stuff. Look out raw sockets your days | ||
34 | * are numbered! | ||
35 | * Steve Whitehouse : Added return-to-sender functions. Added | ||
36 | * backlog congestion level return codes. | ||
37 | * Steve Whitehouse : Fixed bug where routes were set up with | ||
38 | * no ref count on net devices. | ||
39 | * Steve Whitehouse : RCU for the route cache | ||
40 | * Steve Whitehouse : Preparations for the flow cache | ||
41 | * Steve Whitehouse : Prepare for nonlinear skbs | ||
42 | */ | ||
43 | |||
44 | /****************************************************************************** | ||
45 | (c) 1995-1998 E.M. Serrat emserrat@geocities.com | ||
46 | |||
47 | This program is free software; you can redistribute it and/or modify | ||
48 | it under the terms of the GNU General Public License as published by | ||
49 | the Free Software Foundation; either version 2 of the License, or | ||
50 | any later version. | ||
51 | |||
52 | This program is distributed in the hope that it will be useful, | ||
53 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
54 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
55 | GNU General Public License for more details. | ||
56 | *******************************************************************************/ | ||
57 | |||
58 | #include <linux/config.h> | ||
59 | #include <linux/errno.h> | ||
60 | #include <linux/types.h> | ||
61 | #include <linux/socket.h> | ||
62 | #include <linux/in.h> | ||
63 | #include <linux/kernel.h> | ||
64 | #include <linux/sockios.h> | ||
65 | #include <linux/net.h> | ||
66 | #include <linux/netdevice.h> | ||
67 | #include <linux/inet.h> | ||
68 | #include <linux/route.h> | ||
69 | #include <linux/in_route.h> | ||
70 | #include <net/sock.h> | ||
71 | #include <linux/mm.h> | ||
72 | #include <linux/proc_fs.h> | ||
73 | #include <linux/seq_file.h> | ||
74 | #include <linux/init.h> | ||
75 | #include <linux/rtnetlink.h> | ||
76 | #include <linux/string.h> | ||
77 | #include <linux/netfilter_decnet.h> | ||
78 | #include <linux/rcupdate.h> | ||
79 | #include <linux/times.h> | ||
80 | #include <asm/errno.h> | ||
81 | #include <net/neighbour.h> | ||
82 | #include <net/dst.h> | ||
83 | #include <net/flow.h> | ||
84 | #include <net/dn.h> | ||
85 | #include <net/dn_dev.h> | ||
86 | #include <net/dn_nsp.h> | ||
87 | #include <net/dn_route.h> | ||
88 | #include <net/dn_neigh.h> | ||
89 | #include <net/dn_fib.h> | ||
90 | |||
91 | struct dn_rt_hash_bucket | ||
92 | { | ||
93 | struct dn_route *chain; | ||
94 | spinlock_t lock; | ||
95 | } __attribute__((__aligned__(8))); | ||
96 | |||
97 | extern struct neigh_table dn_neigh_table; | ||
98 | |||
99 | |||
100 | static unsigned char dn_hiord_addr[6] = {0xAA,0x00,0x04,0x00,0x00,0x00}; | ||
101 | |||
102 | static const int dn_rt_min_delay = 2 * HZ; | ||
103 | static const int dn_rt_max_delay = 10 * HZ; | ||
104 | static const int dn_rt_mtu_expires = 10 * 60 * HZ; | ||
105 | |||
106 | static unsigned long dn_rt_deadline; | ||
107 | |||
108 | static int dn_dst_gc(void); | ||
109 | static struct dst_entry *dn_dst_check(struct dst_entry *, __u32); | ||
110 | static struct dst_entry *dn_dst_negative_advice(struct dst_entry *); | ||
111 | static void dn_dst_link_failure(struct sk_buff *); | ||
112 | static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu); | ||
113 | static int dn_route_input(struct sk_buff *); | ||
114 | static void dn_run_flush(unsigned long dummy); | ||
115 | |||
116 | static struct dn_rt_hash_bucket *dn_rt_hash_table; | ||
117 | static unsigned dn_rt_hash_mask; | ||
118 | |||
119 | static struct timer_list dn_route_timer; | ||
120 | static struct timer_list dn_rt_flush_timer = | ||
121 | TIMER_INITIALIZER(dn_run_flush, 0, 0); | ||
122 | int decnet_dst_gc_interval = 2; | ||
123 | |||
124 | static struct dst_ops dn_dst_ops = { | ||
125 | .family = PF_DECnet, | ||
126 | .protocol = __constant_htons(ETH_P_DNA_RT), | ||
127 | .gc_thresh = 128, | ||
128 | .gc = dn_dst_gc, | ||
129 | .check = dn_dst_check, | ||
130 | .negative_advice = dn_dst_negative_advice, | ||
131 | .link_failure = dn_dst_link_failure, | ||
132 | .update_pmtu = dn_dst_update_pmtu, | ||
133 | .entry_size = sizeof(struct dn_route), | ||
134 | .entries = ATOMIC_INIT(0), | ||
135 | }; | ||
136 | |||
137 | static __inline__ unsigned dn_hash(unsigned short src, unsigned short dst) | ||
138 | { | ||
139 | unsigned short tmp = src ^ dst; | ||
140 | tmp ^= (tmp >> 3); | ||
141 | tmp ^= (tmp >> 5); | ||
142 | tmp ^= (tmp >> 10); | ||
143 | return dn_rt_hash_mask & (unsigned)tmp; | ||
144 | } | ||
145 | |||
146 | static inline void dnrt_free(struct dn_route *rt) | ||
147 | { | ||
148 | call_rcu_bh(&rt->u.dst.rcu_head, dst_rcu_free); | ||
149 | } | ||
150 | |||
151 | static inline void dnrt_drop(struct dn_route *rt) | ||
152 | { | ||
153 | if (rt) | ||
154 | dst_release(&rt->u.dst); | ||
155 | call_rcu_bh(&rt->u.dst.rcu_head, dst_rcu_free); | ||
156 | } | ||
157 | |||
158 | static void dn_dst_check_expire(unsigned long dummy) | ||
159 | { | ||
160 | int i; | ||
161 | struct dn_route *rt, **rtp; | ||
162 | unsigned long now = jiffies; | ||
163 | unsigned long expire = 120 * HZ; | ||
164 | |||
165 | for(i = 0; i <= dn_rt_hash_mask; i++) { | ||
166 | rtp = &dn_rt_hash_table[i].chain; | ||
167 | |||
168 | spin_lock(&dn_rt_hash_table[i].lock); | ||
169 | while((rt=*rtp) != NULL) { | ||
170 | if (atomic_read(&rt->u.dst.__refcnt) || | ||
171 | (now - rt->u.dst.lastuse) < expire) { | ||
172 | rtp = &rt->u.rt_next; | ||
173 | continue; | ||
174 | } | ||
175 | *rtp = rt->u.rt_next; | ||
176 | rt->u.rt_next = NULL; | ||
177 | dnrt_free(rt); | ||
178 | } | ||
179 | spin_unlock(&dn_rt_hash_table[i].lock); | ||
180 | |||
181 | if ((jiffies - now) > 0) | ||
182 | break; | ||
183 | } | ||
184 | |||
185 | mod_timer(&dn_route_timer, now + decnet_dst_gc_interval * HZ); | ||
186 | } | ||
187 | |||
188 | static int dn_dst_gc(void) | ||
189 | { | ||
190 | struct dn_route *rt, **rtp; | ||
191 | int i; | ||
192 | unsigned long now = jiffies; | ||
193 | unsigned long expire = 10 * HZ; | ||
194 | |||
195 | for(i = 0; i <= dn_rt_hash_mask; i++) { | ||
196 | |||
197 | spin_lock_bh(&dn_rt_hash_table[i].lock); | ||
198 | rtp = &dn_rt_hash_table[i].chain; | ||
199 | |||
200 | while((rt=*rtp) != NULL) { | ||
201 | if (atomic_read(&rt->u.dst.__refcnt) || | ||
202 | (now - rt->u.dst.lastuse) < expire) { | ||
203 | rtp = &rt->u.rt_next; | ||
204 | continue; | ||
205 | } | ||
206 | *rtp = rt->u.rt_next; | ||
207 | rt->u.rt_next = NULL; | ||
208 | dnrt_drop(rt); | ||
209 | break; | ||
210 | } | ||
211 | spin_unlock_bh(&dn_rt_hash_table[i].lock); | ||
212 | } | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | /* | ||
218 | * The decnet standards don't impose a particular minimum mtu, what they | ||
219 | * do insist on is that the routing layer accepts a datagram of at least | ||
220 | * 230 bytes long. Here we have to subtract the routing header length from | ||
221 | * 230 to get the minimum acceptable mtu. If there is no neighbour, then we | ||
222 | * assume the worst and use a long header size. | ||
223 | * | ||
224 | * We update both the mtu and the advertised mss (i.e. the segment size we | ||
225 | * advertise to the other end). | ||
226 | */ | ||
227 | static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu) | ||
228 | { | ||
229 | u32 min_mtu = 230; | ||
230 | struct dn_dev *dn = dst->neighbour ? | ||
231 | (struct dn_dev *)dst->neighbour->dev->dn_ptr : NULL; | ||
232 | |||
233 | if (dn && dn->use_long == 0) | ||
234 | min_mtu -= 6; | ||
235 | else | ||
236 | min_mtu -= 21; | ||
237 | |||
238 | if (dst->metrics[RTAX_MTU-1] > mtu && mtu >= min_mtu) { | ||
239 | if (!(dst_metric_locked(dst, RTAX_MTU))) { | ||
240 | dst->metrics[RTAX_MTU-1] = mtu; | ||
241 | dst_set_expires(dst, dn_rt_mtu_expires); | ||
242 | } | ||
243 | if (!(dst_metric_locked(dst, RTAX_ADVMSS))) { | ||
244 | u32 mss = mtu - DN_MAX_NSP_DATA_HEADER; | ||
245 | if (dst->metrics[RTAX_ADVMSS-1] > mss) | ||
246 | dst->metrics[RTAX_ADVMSS-1] = mss; | ||
247 | } | ||
248 | } | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * When a route has been marked obsolete. (e.g. routing cache flush) | ||
253 | */ | ||
254 | static struct dst_entry *dn_dst_check(struct dst_entry *dst, __u32 cookie) | ||
255 | { | ||
256 | return NULL; | ||
257 | } | ||
258 | |||
259 | static struct dst_entry *dn_dst_negative_advice(struct dst_entry *dst) | ||
260 | { | ||
261 | dst_release(dst); | ||
262 | return NULL; | ||
263 | } | ||
264 | |||
265 | static void dn_dst_link_failure(struct sk_buff *skb) | ||
266 | { | ||
267 | return; | ||
268 | } | ||
269 | |||
270 | static inline int compare_keys(struct flowi *fl1, struct flowi *fl2) | ||
271 | { | ||
272 | return memcmp(&fl1->nl_u.dn_u, &fl2->nl_u.dn_u, sizeof(fl1->nl_u.dn_u)) == 0 && | ||
273 | fl1->oif == fl2->oif && | ||
274 | fl1->iif == fl2->iif; | ||
275 | } | ||
276 | |||
277 | static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route **rp) | ||
278 | { | ||
279 | struct dn_route *rth, **rthp; | ||
280 | unsigned long now = jiffies; | ||
281 | |||
282 | rthp = &dn_rt_hash_table[hash].chain; | ||
283 | |||
284 | spin_lock_bh(&dn_rt_hash_table[hash].lock); | ||
285 | while((rth = *rthp) != NULL) { | ||
286 | if (compare_keys(&rth->fl, &rt->fl)) { | ||
287 | /* Put it first */ | ||
288 | *rthp = rth->u.rt_next; | ||
289 | rcu_assign_pointer(rth->u.rt_next, | ||
290 | dn_rt_hash_table[hash].chain); | ||
291 | rcu_assign_pointer(dn_rt_hash_table[hash].chain, rth); | ||
292 | |||
293 | rth->u.dst.__use++; | ||
294 | dst_hold(&rth->u.dst); | ||
295 | rth->u.dst.lastuse = now; | ||
296 | spin_unlock_bh(&dn_rt_hash_table[hash].lock); | ||
297 | |||
298 | dnrt_drop(rt); | ||
299 | *rp = rth; | ||
300 | return 0; | ||
301 | } | ||
302 | rthp = &rth->u.rt_next; | ||
303 | } | ||
304 | |||
305 | rcu_assign_pointer(rt->u.rt_next, dn_rt_hash_table[hash].chain); | ||
306 | rcu_assign_pointer(dn_rt_hash_table[hash].chain, rt); | ||
307 | |||
308 | dst_hold(&rt->u.dst); | ||
309 | rt->u.dst.__use++; | ||
310 | rt->u.dst.lastuse = now; | ||
311 | spin_unlock_bh(&dn_rt_hash_table[hash].lock); | ||
312 | *rp = rt; | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | void dn_run_flush(unsigned long dummy) | ||
317 | { | ||
318 | int i; | ||
319 | struct dn_route *rt, *next; | ||
320 | |||
321 | for(i = 0; i < dn_rt_hash_mask; i++) { | ||
322 | spin_lock_bh(&dn_rt_hash_table[i].lock); | ||
323 | |||
324 | if ((rt = xchg(&dn_rt_hash_table[i].chain, NULL)) == NULL) | ||
325 | goto nothing_to_declare; | ||
326 | |||
327 | for(; rt; rt=next) { | ||
328 | next = rt->u.rt_next; | ||
329 | rt->u.rt_next = NULL; | ||
330 | dst_free((struct dst_entry *)rt); | ||
331 | } | ||
332 | |||
333 | nothing_to_declare: | ||
334 | spin_unlock_bh(&dn_rt_hash_table[i].lock); | ||
335 | } | ||
336 | } | ||
337 | |||
338 | static DEFINE_SPINLOCK(dn_rt_flush_lock); | ||
339 | |||
340 | void dn_rt_cache_flush(int delay) | ||
341 | { | ||
342 | unsigned long now = jiffies; | ||
343 | int user_mode = !in_interrupt(); | ||
344 | |||
345 | if (delay < 0) | ||
346 | delay = dn_rt_min_delay; | ||
347 | |||
348 | spin_lock_bh(&dn_rt_flush_lock); | ||
349 | |||
350 | if (del_timer(&dn_rt_flush_timer) && delay > 0 && dn_rt_deadline) { | ||
351 | long tmo = (long)(dn_rt_deadline - now); | ||
352 | |||
353 | if (user_mode && tmo < dn_rt_max_delay - dn_rt_min_delay) | ||
354 | tmo = 0; | ||
355 | |||
356 | if (delay > tmo) | ||
357 | delay = tmo; | ||
358 | } | ||
359 | |||
360 | if (delay <= 0) { | ||
361 | spin_unlock_bh(&dn_rt_flush_lock); | ||
362 | dn_run_flush(0); | ||
363 | return; | ||
364 | } | ||
365 | |||
366 | if (dn_rt_deadline == 0) | ||
367 | dn_rt_deadline = now + dn_rt_max_delay; | ||
368 | |||
369 | dn_rt_flush_timer.expires = now + delay; | ||
370 | add_timer(&dn_rt_flush_timer); | ||
371 | spin_unlock_bh(&dn_rt_flush_lock); | ||
372 | } | ||
373 | |||
374 | /** | ||
375 | * dn_return_short - Return a short packet to its sender | ||
376 | * @skb: The packet to return | ||
377 | * | ||
378 | */ | ||
379 | static int dn_return_short(struct sk_buff *skb) | ||
380 | { | ||
381 | struct dn_skb_cb *cb; | ||
382 | unsigned char *ptr; | ||
383 | dn_address *src; | ||
384 | dn_address *dst; | ||
385 | dn_address tmp; | ||
386 | |||
387 | /* Add back headers */ | ||
388 | skb_push(skb, skb->data - skb->nh.raw); | ||
389 | |||
390 | if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL) | ||
391 | return NET_RX_DROP; | ||
392 | |||
393 | cb = DN_SKB_CB(skb); | ||
394 | /* Skip packet length and point to flags */ | ||
395 | ptr = skb->data + 2; | ||
396 | *ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS; | ||
397 | |||
398 | dst = (dn_address *)ptr; | ||
399 | ptr += 2; | ||
400 | src = (dn_address *)ptr; | ||
401 | ptr += 2; | ||
402 | *ptr = 0; /* Zero hop count */ | ||
403 | |||
404 | /* Swap source and destination */ | ||
405 | tmp = *src; | ||
406 | *src = *dst; | ||
407 | *dst = tmp; | ||
408 | |||
409 | skb->pkt_type = PACKET_OUTGOING; | ||
410 | dn_rt_finish_output(skb, NULL, NULL); | ||
411 | return NET_RX_SUCCESS; | ||
412 | } | ||
413 | |||
414 | /** | ||
415 | * dn_return_long - Return a long packet to its sender | ||
416 | * @skb: The long format packet to return | ||
417 | * | ||
418 | */ | ||
419 | static int dn_return_long(struct sk_buff *skb) | ||
420 | { | ||
421 | struct dn_skb_cb *cb; | ||
422 | unsigned char *ptr; | ||
423 | unsigned char *src_addr, *dst_addr; | ||
424 | unsigned char tmp[ETH_ALEN]; | ||
425 | |||
426 | /* Add back all headers */ | ||
427 | skb_push(skb, skb->data - skb->nh.raw); | ||
428 | |||
429 | if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL) | ||
430 | return NET_RX_DROP; | ||
431 | |||
432 | cb = DN_SKB_CB(skb); | ||
433 | /* Ignore packet length and point to flags */ | ||
434 | ptr = skb->data + 2; | ||
435 | |||
436 | /* Skip padding */ | ||
437 | if (*ptr & DN_RT_F_PF) { | ||
438 | char padlen = (*ptr & ~DN_RT_F_PF); | ||
439 | ptr += padlen; | ||
440 | } | ||
441 | |||
442 | *ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS; | ||
443 | ptr += 2; | ||
444 | dst_addr = ptr; | ||
445 | ptr += 8; | ||
446 | src_addr = ptr; | ||
447 | ptr += 6; | ||
448 | *ptr = 0; /* Zero hop count */ | ||
449 | |||
450 | /* Swap source and destination */ | ||
451 | memcpy(tmp, src_addr, ETH_ALEN); | ||
452 | memcpy(src_addr, dst_addr, ETH_ALEN); | ||
453 | memcpy(dst_addr, tmp, ETH_ALEN); | ||
454 | |||
455 | skb->pkt_type = PACKET_OUTGOING; | ||
456 | dn_rt_finish_output(skb, dst_addr, src_addr); | ||
457 | return NET_RX_SUCCESS; | ||
458 | } | ||
459 | |||
460 | /** | ||
461 | * dn_route_rx_packet - Try and find a route for an incoming packet | ||
462 | * @skb: The packet to find a route for | ||
463 | * | ||
464 | * Returns: result of input function if route is found, error code otherwise | ||
465 | */ | ||
466 | static int dn_route_rx_packet(struct sk_buff *skb) | ||
467 | { | ||
468 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
469 | int err; | ||
470 | |||
471 | if ((err = dn_route_input(skb)) == 0) | ||
472 | return dst_input(skb); | ||
473 | |||
474 | if (decnet_debug_level & 4) { | ||
475 | char *devname = skb->dev ? skb->dev->name : "???"; | ||
476 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
477 | printk(KERN_DEBUG | ||
478 | "DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d\n", | ||
479 | (int)cb->rt_flags, devname, skb->len, cb->src, cb->dst, | ||
480 | err, skb->pkt_type); | ||
481 | } | ||
482 | |||
483 | if ((skb->pkt_type == PACKET_HOST) && (cb->rt_flags & DN_RT_F_RQR)) { | ||
484 | switch(cb->rt_flags & DN_RT_PKT_MSK) { | ||
485 | case DN_RT_PKT_SHORT: | ||
486 | return dn_return_short(skb); | ||
487 | case DN_RT_PKT_LONG: | ||
488 | return dn_return_long(skb); | ||
489 | } | ||
490 | } | ||
491 | |||
492 | kfree_skb(skb); | ||
493 | return NET_RX_DROP; | ||
494 | } | ||
495 | |||
496 | static int dn_route_rx_long(struct sk_buff *skb) | ||
497 | { | ||
498 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
499 | unsigned char *ptr = skb->data; | ||
500 | |||
501 | if (!pskb_may_pull(skb, 21)) /* 20 for long header, 1 for shortest nsp */ | ||
502 | goto drop_it; | ||
503 | |||
504 | skb_pull(skb, 20); | ||
505 | skb->h.raw = skb->data; | ||
506 | |||
507 | /* Destination info */ | ||
508 | ptr += 2; | ||
509 | cb->dst = dn_htons(dn_eth2dn(ptr)); | ||
510 | if (memcmp(ptr, dn_hiord_addr, 4) != 0) | ||
511 | goto drop_it; | ||
512 | ptr += 6; | ||
513 | |||
514 | |||
515 | /* Source info */ | ||
516 | ptr += 2; | ||
517 | cb->src = dn_htons(dn_eth2dn(ptr)); | ||
518 | if (memcmp(ptr, dn_hiord_addr, 4) != 0) | ||
519 | goto drop_it; | ||
520 | ptr += 6; | ||
521 | /* Other junk */ | ||
522 | ptr++; | ||
523 | cb->hops = *ptr++; /* Visit Count */ | ||
524 | |||
525 | return NF_HOOK(PF_DECnet, NF_DN_PRE_ROUTING, skb, skb->dev, NULL, dn_route_rx_packet); | ||
526 | |||
527 | drop_it: | ||
528 | kfree_skb(skb); | ||
529 | return NET_RX_DROP; | ||
530 | } | ||
531 | |||
532 | |||
533 | |||
534 | static int dn_route_rx_short(struct sk_buff *skb) | ||
535 | { | ||
536 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
537 | unsigned char *ptr = skb->data; | ||
538 | |||
539 | if (!pskb_may_pull(skb, 6)) /* 5 for short header + 1 for shortest nsp */ | ||
540 | goto drop_it; | ||
541 | |||
542 | skb_pull(skb, 5); | ||
543 | skb->h.raw = skb->data; | ||
544 | |||
545 | cb->dst = *(dn_address *)ptr; | ||
546 | ptr += 2; | ||
547 | cb->src = *(dn_address *)ptr; | ||
548 | ptr += 2; | ||
549 | cb->hops = *ptr & 0x3f; | ||
550 | |||
551 | return NF_HOOK(PF_DECnet, NF_DN_PRE_ROUTING, skb, skb->dev, NULL, dn_route_rx_packet); | ||
552 | |||
553 | drop_it: | ||
554 | kfree_skb(skb); | ||
555 | return NET_RX_DROP; | ||
556 | } | ||
557 | |||
558 | static int dn_route_discard(struct sk_buff *skb) | ||
559 | { | ||
560 | /* | ||
561 | * I know we drop the packet here, but thats considered success in | ||
562 | * this case | ||
563 | */ | ||
564 | kfree_skb(skb); | ||
565 | return NET_RX_SUCCESS; | ||
566 | } | ||
567 | |||
568 | static int dn_route_ptp_hello(struct sk_buff *skb) | ||
569 | { | ||
570 | dn_dev_hello(skb); | ||
571 | dn_neigh_pointopoint_hello(skb); | ||
572 | return NET_RX_SUCCESS; | ||
573 | } | ||
574 | |||
575 | int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) | ||
576 | { | ||
577 | struct dn_skb_cb *cb; | ||
578 | unsigned char flags = 0; | ||
579 | __u16 len = dn_ntohs(*(__u16 *)skb->data); | ||
580 | struct dn_dev *dn = (struct dn_dev *)dev->dn_ptr; | ||
581 | unsigned char padlen = 0; | ||
582 | |||
583 | if (dn == NULL) | ||
584 | goto dump_it; | ||
585 | |||
586 | if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) | ||
587 | goto out; | ||
588 | |||
589 | if (!pskb_may_pull(skb, 3)) | ||
590 | goto dump_it; | ||
591 | |||
592 | skb_pull(skb, 2); | ||
593 | |||
594 | if (len > skb->len) | ||
595 | goto dump_it; | ||
596 | |||
597 | skb_trim(skb, len); | ||
598 | |||
599 | flags = *skb->data; | ||
600 | |||
601 | cb = DN_SKB_CB(skb); | ||
602 | cb->stamp = jiffies; | ||
603 | cb->iif = dev->ifindex; | ||
604 | |||
605 | /* | ||
606 | * If we have padding, remove it. | ||
607 | */ | ||
608 | if (flags & DN_RT_F_PF) { | ||
609 | padlen = flags & ~DN_RT_F_PF; | ||
610 | if (!pskb_may_pull(skb, padlen + 1)) | ||
611 | goto dump_it; | ||
612 | skb_pull(skb, padlen); | ||
613 | flags = *skb->data; | ||
614 | } | ||
615 | |||
616 | skb->nh.raw = skb->data; | ||
617 | |||
618 | /* | ||
619 | * Weed out future version DECnet | ||
620 | */ | ||
621 | if (flags & DN_RT_F_VER) | ||
622 | goto dump_it; | ||
623 | |||
624 | cb->rt_flags = flags; | ||
625 | |||
626 | if (decnet_debug_level & 1) | ||
627 | printk(KERN_DEBUG | ||
628 | "dn_route_rcv: got 0x%02x from %s [%d %d %d]\n", | ||
629 | (int)flags, (dev) ? dev->name : "???", len, skb->len, | ||
630 | padlen); | ||
631 | |||
632 | if (flags & DN_RT_PKT_CNTL) { | ||
633 | if (unlikely(skb_is_nonlinear(skb)) && | ||
634 | skb_linearize(skb, GFP_ATOMIC) != 0) | ||
635 | goto dump_it; | ||
636 | |||
637 | switch(flags & DN_RT_CNTL_MSK) { | ||
638 | case DN_RT_PKT_INIT: | ||
639 | dn_dev_init_pkt(skb); | ||
640 | break; | ||
641 | case DN_RT_PKT_VERI: | ||
642 | dn_dev_veri_pkt(skb); | ||
643 | break; | ||
644 | } | ||
645 | |||
646 | if (dn->parms.state != DN_DEV_S_RU) | ||
647 | goto dump_it; | ||
648 | |||
649 | switch(flags & DN_RT_CNTL_MSK) { | ||
650 | case DN_RT_PKT_HELO: | ||
651 | return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_route_ptp_hello); | ||
652 | |||
653 | case DN_RT_PKT_L1RT: | ||
654 | case DN_RT_PKT_L2RT: | ||
655 | return NF_HOOK(PF_DECnet, NF_DN_ROUTE, skb, skb->dev, NULL, dn_route_discard); | ||
656 | case DN_RT_PKT_ERTH: | ||
657 | return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_neigh_router_hello); | ||
658 | |||
659 | case DN_RT_PKT_EEDH: | ||
660 | return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_neigh_endnode_hello); | ||
661 | } | ||
662 | } else { | ||
663 | if (dn->parms.state != DN_DEV_S_RU) | ||
664 | goto dump_it; | ||
665 | |||
666 | skb_pull(skb, 1); /* Pull flags */ | ||
667 | |||
668 | switch(flags & DN_RT_PKT_MSK) { | ||
669 | case DN_RT_PKT_LONG: | ||
670 | return dn_route_rx_long(skb); | ||
671 | case DN_RT_PKT_SHORT: | ||
672 | return dn_route_rx_short(skb); | ||
673 | } | ||
674 | } | ||
675 | |||
676 | dump_it: | ||
677 | kfree_skb(skb); | ||
678 | out: | ||
679 | return NET_RX_DROP; | ||
680 | } | ||
681 | |||
682 | static int dn_output(struct sk_buff *skb) | ||
683 | { | ||
684 | struct dst_entry *dst = skb->dst; | ||
685 | struct dn_route *rt = (struct dn_route *)dst; | ||
686 | struct net_device *dev = dst->dev; | ||
687 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
688 | struct neighbour *neigh; | ||
689 | |||
690 | int err = -EINVAL; | ||
691 | |||
692 | if ((neigh = dst->neighbour) == NULL) | ||
693 | goto error; | ||
694 | |||
695 | skb->dev = dev; | ||
696 | |||
697 | cb->src = rt->rt_saddr; | ||
698 | cb->dst = rt->rt_daddr; | ||
699 | |||
700 | /* | ||
701 | * Always set the Intra-Ethernet bit on all outgoing packets | ||
702 | * originated on this node. Only valid flag from upper layers | ||
703 | * is return-to-sender-requested. Set hop count to 0 too. | ||
704 | */ | ||
705 | cb->rt_flags &= ~DN_RT_F_RQR; | ||
706 | cb->rt_flags |= DN_RT_F_IE; | ||
707 | cb->hops = 0; | ||
708 | |||
709 | return NF_HOOK(PF_DECnet, NF_DN_LOCAL_OUT, skb, NULL, dev, neigh->output); | ||
710 | |||
711 | error: | ||
712 | if (net_ratelimit()) | ||
713 | printk(KERN_DEBUG "dn_output: This should not happen\n"); | ||
714 | |||
715 | kfree_skb(skb); | ||
716 | |||
717 | return err; | ||
718 | } | ||
719 | |||
720 | static int dn_forward(struct sk_buff *skb) | ||
721 | { | ||
722 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
723 | struct dst_entry *dst = skb->dst; | ||
724 | struct dn_dev *dn_db = dst->dev->dn_ptr; | ||
725 | struct dn_route *rt; | ||
726 | struct neighbour *neigh = dst->neighbour; | ||
727 | int header_len; | ||
728 | #ifdef CONFIG_NETFILTER | ||
729 | struct net_device *dev = skb->dev; | ||
730 | #endif | ||
731 | |||
732 | if (skb->pkt_type != PACKET_HOST) | ||
733 | goto drop; | ||
734 | |||
735 | /* Ensure that we have enough space for headers */ | ||
736 | rt = (struct dn_route *)skb->dst; | ||
737 | header_len = dn_db->use_long ? 21 : 6; | ||
738 | if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+header_len)) | ||
739 | goto drop; | ||
740 | |||
741 | /* | ||
742 | * Hop count exceeded. | ||
743 | */ | ||
744 | if (++cb->hops > 30) | ||
745 | goto drop; | ||
746 | |||
747 | skb->dev = rt->u.dst.dev; | ||
748 | |||
749 | /* | ||
750 | * If packet goes out same interface it came in on, then set | ||
751 | * the Intra-Ethernet bit. This has no effect for short | ||
752 | * packets, so we don't need to test for them here. | ||
753 | */ | ||
754 | cb->rt_flags &= ~DN_RT_F_IE; | ||
755 | if (rt->rt_flags & RTCF_DOREDIRECT) | ||
756 | cb->rt_flags |= DN_RT_F_IE; | ||
757 | |||
758 | return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, dev, skb->dev, neigh->output); | ||
759 | |||
760 | drop: | ||
761 | kfree_skb(skb); | ||
762 | return NET_RX_DROP; | ||
763 | } | ||
764 | |||
765 | /* | ||
766 | * Drop packet. This is used for endnodes and for | ||
767 | * when we should not be forwarding packets from | ||
768 | * this dest. | ||
769 | */ | ||
770 | static int dn_blackhole(struct sk_buff *skb) | ||
771 | { | ||
772 | kfree_skb(skb); | ||
773 | return NET_RX_DROP; | ||
774 | } | ||
775 | |||
776 | /* | ||
777 | * Used to catch bugs. This should never normally get | ||
778 | * called. | ||
779 | */ | ||
780 | static int dn_rt_bug(struct sk_buff *skb) | ||
781 | { | ||
782 | if (net_ratelimit()) { | ||
783 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
784 | |||
785 | printk(KERN_DEBUG "dn_rt_bug: skb from:%04x to:%04x\n", | ||
786 | cb->src, cb->dst); | ||
787 | } | ||
788 | |||
789 | kfree_skb(skb); | ||
790 | |||
791 | return NET_RX_BAD; | ||
792 | } | ||
793 | |||
794 | static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res) | ||
795 | { | ||
796 | struct dn_fib_info *fi = res->fi; | ||
797 | struct net_device *dev = rt->u.dst.dev; | ||
798 | struct neighbour *n; | ||
799 | unsigned mss; | ||
800 | |||
801 | if (fi) { | ||
802 | if (DN_FIB_RES_GW(*res) && | ||
803 | DN_FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) | ||
804 | rt->rt_gateway = DN_FIB_RES_GW(*res); | ||
805 | memcpy(rt->u.dst.metrics, fi->fib_metrics, | ||
806 | sizeof(rt->u.dst.metrics)); | ||
807 | } | ||
808 | rt->rt_type = res->type; | ||
809 | |||
810 | if (dev != NULL && rt->u.dst.neighbour == NULL) { | ||
811 | n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev); | ||
812 | if (IS_ERR(n)) | ||
813 | return PTR_ERR(n); | ||
814 | rt->u.dst.neighbour = n; | ||
815 | } | ||
816 | |||
817 | if (rt->u.dst.metrics[RTAX_MTU-1] == 0 || | ||
818 | rt->u.dst.metrics[RTAX_MTU-1] > rt->u.dst.dev->mtu) | ||
819 | rt->u.dst.metrics[RTAX_MTU-1] = rt->u.dst.dev->mtu; | ||
820 | mss = dn_mss_from_pmtu(dev, dst_mtu(&rt->u.dst)); | ||
821 | if (rt->u.dst.metrics[RTAX_ADVMSS-1] == 0 || | ||
822 | rt->u.dst.metrics[RTAX_ADVMSS-1] > mss) | ||
823 | rt->u.dst.metrics[RTAX_ADVMSS-1] = mss; | ||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | static inline int dn_match_addr(__u16 addr1, __u16 addr2) | ||
828 | { | ||
829 | __u16 tmp = dn_ntohs(addr1) ^ dn_ntohs(addr2); | ||
830 | int match = 16; | ||
831 | while(tmp) { | ||
832 | tmp >>= 1; | ||
833 | match--; | ||
834 | } | ||
835 | return match; | ||
836 | } | ||
837 | |||
838 | static __u16 dnet_select_source(const struct net_device *dev, __u16 daddr, int scope) | ||
839 | { | ||
840 | __u16 saddr = 0; | ||
841 | struct dn_dev *dn_db = dev->dn_ptr; | ||
842 | struct dn_ifaddr *ifa; | ||
843 | int best_match = 0; | ||
844 | int ret; | ||
845 | |||
846 | read_lock(&dev_base_lock); | ||
847 | for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) { | ||
848 | if (ifa->ifa_scope > scope) | ||
849 | continue; | ||
850 | if (!daddr) { | ||
851 | saddr = ifa->ifa_local; | ||
852 | break; | ||
853 | } | ||
854 | ret = dn_match_addr(daddr, ifa->ifa_local); | ||
855 | if (ret > best_match) | ||
856 | saddr = ifa->ifa_local; | ||
857 | if (best_match == 0) | ||
858 | saddr = ifa->ifa_local; | ||
859 | } | ||
860 | read_unlock(&dev_base_lock); | ||
861 | |||
862 | return saddr; | ||
863 | } | ||
864 | |||
865 | static inline __u16 __dn_fib_res_prefsrc(struct dn_fib_res *res) | ||
866 | { | ||
867 | return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope); | ||
868 | } | ||
869 | |||
870 | static inline __u16 dn_fib_rules_map_destination(__u16 daddr, struct dn_fib_res *res) | ||
871 | { | ||
872 | __u16 mask = dnet_make_mask(res->prefixlen); | ||
873 | return (daddr&~mask)|res->fi->fib_nh->nh_gw; | ||
874 | } | ||
875 | |||
876 | static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *oldflp, int try_hard) | ||
877 | { | ||
878 | struct flowi fl = { .nl_u = { .dn_u = | ||
879 | { .daddr = oldflp->fld_dst, | ||
880 | .saddr = oldflp->fld_src, | ||
881 | .scope = RT_SCOPE_UNIVERSE, | ||
882 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
883 | .fwmark = oldflp->fld_fwmark | ||
884 | #endif | ||
885 | } }, | ||
886 | .iif = loopback_dev.ifindex, | ||
887 | .oif = oldflp->oif }; | ||
888 | struct dn_route *rt = NULL; | ||
889 | struct net_device *dev_out = NULL; | ||
890 | struct neighbour *neigh = NULL; | ||
891 | unsigned hash; | ||
892 | unsigned flags = 0; | ||
893 | struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST }; | ||
894 | int err; | ||
895 | int free_res = 0; | ||
896 | __u16 gateway = 0; | ||
897 | |||
898 | if (decnet_debug_level & 16) | ||
899 | printk(KERN_DEBUG | ||
900 | "dn_route_output_slow: dst=%04x src=%04x mark=%d" | ||
901 | " iif=%d oif=%d\n", oldflp->fld_dst, oldflp->fld_src, | ||
902 | oldflp->fld_fwmark, loopback_dev.ifindex, oldflp->oif); | ||
903 | |||
904 | /* If we have an output interface, verify its a DECnet device */ | ||
905 | if (oldflp->oif) { | ||
906 | dev_out = dev_get_by_index(oldflp->oif); | ||
907 | err = -ENODEV; | ||
908 | if (dev_out && dev_out->dn_ptr == NULL) { | ||
909 | dev_put(dev_out); | ||
910 | dev_out = NULL; | ||
911 | } | ||
912 | if (dev_out == NULL) | ||
913 | goto out; | ||
914 | } | ||
915 | |||
916 | /* If we have a source address, verify that its a local address */ | ||
917 | if (oldflp->fld_src) { | ||
918 | err = -EADDRNOTAVAIL; | ||
919 | |||
920 | if (dev_out) { | ||
921 | if (dn_dev_islocal(dev_out, oldflp->fld_src)) | ||
922 | goto source_ok; | ||
923 | dev_put(dev_out); | ||
924 | goto out; | ||
925 | } | ||
926 | read_lock(&dev_base_lock); | ||
927 | for(dev_out = dev_base; dev_out; dev_out = dev_out->next) { | ||
928 | if (!dev_out->dn_ptr) | ||
929 | continue; | ||
930 | if (dn_dev_islocal(dev_out, oldflp->fld_src)) | ||
931 | break; | ||
932 | } | ||
933 | read_unlock(&dev_base_lock); | ||
934 | if (dev_out == NULL) | ||
935 | goto out; | ||
936 | dev_hold(dev_out); | ||
937 | source_ok: | ||
938 | ; | ||
939 | } | ||
940 | |||
941 | /* No destination? Assume its local */ | ||
942 | if (!fl.fld_dst) { | ||
943 | fl.fld_dst = fl.fld_src; | ||
944 | |||
945 | err = -EADDRNOTAVAIL; | ||
946 | if (dev_out) | ||
947 | dev_put(dev_out); | ||
948 | dev_out = &loopback_dev; | ||
949 | dev_hold(dev_out); | ||
950 | if (!fl.fld_dst) { | ||
951 | fl.fld_dst = | ||
952 | fl.fld_src = dnet_select_source(dev_out, 0, | ||
953 | RT_SCOPE_HOST); | ||
954 | if (!fl.fld_dst) | ||
955 | goto out; | ||
956 | } | ||
957 | fl.oif = loopback_dev.ifindex; | ||
958 | res.type = RTN_LOCAL; | ||
959 | goto make_route; | ||
960 | } | ||
961 | |||
962 | if (decnet_debug_level & 16) | ||
963 | printk(KERN_DEBUG | ||
964 | "dn_route_output_slow: initial checks complete." | ||
965 | " dst=%o4x src=%04x oif=%d try_hard=%d\n", fl.fld_dst, | ||
966 | fl.fld_src, fl.oif, try_hard); | ||
967 | |||
968 | /* | ||
969 | * N.B. If the kernel is compiled without router support then | ||
970 | * dn_fib_lookup() will evaluate to non-zero so this if () block | ||
971 | * will always be executed. | ||
972 | */ | ||
973 | err = -ESRCH; | ||
974 | if (try_hard || (err = dn_fib_lookup(&fl, &res)) != 0) { | ||
975 | struct dn_dev *dn_db; | ||
976 | if (err != -ESRCH) | ||
977 | goto out; | ||
978 | /* | ||
979 | * Here the fallback is basically the standard algorithm for | ||
980 | * routing in endnodes which is described in the DECnet routing | ||
981 | * docs | ||
982 | * | ||
983 | * If we are not trying hard, look in neighbour cache. | ||
984 | * The result is tested to ensure that if a specific output | ||
985 | * device/source address was requested, then we honour that | ||
986 | * here | ||
987 | */ | ||
988 | if (!try_hard) { | ||
989 | neigh = neigh_lookup_nodev(&dn_neigh_table, &fl.fld_dst); | ||
990 | if (neigh) { | ||
991 | if ((oldflp->oif && | ||
992 | (neigh->dev->ifindex != oldflp->oif)) || | ||
993 | (oldflp->fld_src && | ||
994 | (!dn_dev_islocal(neigh->dev, | ||
995 | oldflp->fld_src)))) { | ||
996 | neigh_release(neigh); | ||
997 | neigh = NULL; | ||
998 | } else { | ||
999 | if (dev_out) | ||
1000 | dev_put(dev_out); | ||
1001 | if (dn_dev_islocal(neigh->dev, fl.fld_dst)) { | ||
1002 | dev_out = &loopback_dev; | ||
1003 | res.type = RTN_LOCAL; | ||
1004 | } else { | ||
1005 | dev_out = neigh->dev; | ||
1006 | } | ||
1007 | dev_hold(dev_out); | ||
1008 | goto select_source; | ||
1009 | } | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | /* Not there? Perhaps its a local address */ | ||
1014 | if (dev_out == NULL) | ||
1015 | dev_out = dn_dev_get_default(); | ||
1016 | err = -ENODEV; | ||
1017 | if (dev_out == NULL) | ||
1018 | goto out; | ||
1019 | dn_db = dev_out->dn_ptr; | ||
1020 | /* Possible improvement - check all devices for local addr */ | ||
1021 | if (dn_dev_islocal(dev_out, fl.fld_dst)) { | ||
1022 | dev_put(dev_out); | ||
1023 | dev_out = &loopback_dev; | ||
1024 | dev_hold(dev_out); | ||
1025 | res.type = RTN_LOCAL; | ||
1026 | goto select_source; | ||
1027 | } | ||
1028 | /* Not local either.... try sending it to the default router */ | ||
1029 | neigh = neigh_clone(dn_db->router); | ||
1030 | BUG_ON(neigh && neigh->dev != dev_out); | ||
1031 | |||
1032 | /* Ok then, we assume its directly connected and move on */ | ||
1033 | select_source: | ||
1034 | if (neigh) | ||
1035 | gateway = ((struct dn_neigh *)neigh)->addr; | ||
1036 | if (gateway == 0) | ||
1037 | gateway = fl.fld_dst; | ||
1038 | if (fl.fld_src == 0) { | ||
1039 | fl.fld_src = dnet_select_source(dev_out, gateway, | ||
1040 | res.type == RTN_LOCAL ? | ||
1041 | RT_SCOPE_HOST : | ||
1042 | RT_SCOPE_LINK); | ||
1043 | if (fl.fld_src == 0 && res.type != RTN_LOCAL) | ||
1044 | goto e_addr; | ||
1045 | } | ||
1046 | fl.oif = dev_out->ifindex; | ||
1047 | goto make_route; | ||
1048 | } | ||
1049 | free_res = 1; | ||
1050 | |||
1051 | if (res.type == RTN_NAT) | ||
1052 | goto e_inval; | ||
1053 | |||
1054 | if (res.type == RTN_LOCAL) { | ||
1055 | if (!fl.fld_src) | ||
1056 | fl.fld_src = fl.fld_dst; | ||
1057 | if (dev_out) | ||
1058 | dev_put(dev_out); | ||
1059 | dev_out = &loopback_dev; | ||
1060 | dev_hold(dev_out); | ||
1061 | fl.oif = dev_out->ifindex; | ||
1062 | if (res.fi) | ||
1063 | dn_fib_info_put(res.fi); | ||
1064 | res.fi = NULL; | ||
1065 | goto make_route; | ||
1066 | } | ||
1067 | |||
1068 | if (res.fi->fib_nhs > 1 && fl.oif == 0) | ||
1069 | dn_fib_select_multipath(&fl, &res); | ||
1070 | |||
1071 | /* | ||
1072 | * We could add some logic to deal with default routes here and | ||
1073 | * get rid of some of the special casing above. | ||
1074 | */ | ||
1075 | |||
1076 | if (!fl.fld_src) | ||
1077 | fl.fld_src = DN_FIB_RES_PREFSRC(res); | ||
1078 | |||
1079 | if (dev_out) | ||
1080 | dev_put(dev_out); | ||
1081 | dev_out = DN_FIB_RES_DEV(res); | ||
1082 | dev_hold(dev_out); | ||
1083 | fl.oif = dev_out->ifindex; | ||
1084 | gateway = DN_FIB_RES_GW(res); | ||
1085 | |||
1086 | make_route: | ||
1087 | if (dev_out->flags & IFF_LOOPBACK) | ||
1088 | flags |= RTCF_LOCAL; | ||
1089 | |||
1090 | rt = dst_alloc(&dn_dst_ops); | ||
1091 | if (rt == NULL) | ||
1092 | goto e_nobufs; | ||
1093 | |||
1094 | atomic_set(&rt->u.dst.__refcnt, 1); | ||
1095 | rt->u.dst.flags = DST_HOST; | ||
1096 | |||
1097 | rt->fl.fld_src = oldflp->fld_src; | ||
1098 | rt->fl.fld_dst = oldflp->fld_dst; | ||
1099 | rt->fl.oif = oldflp->oif; | ||
1100 | rt->fl.iif = 0; | ||
1101 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
1102 | rt->fl.fld_fwmark = oldflp->fld_fwmark; | ||
1103 | #endif | ||
1104 | |||
1105 | rt->rt_saddr = fl.fld_src; | ||
1106 | rt->rt_daddr = fl.fld_dst; | ||
1107 | rt->rt_gateway = gateway ? gateway : fl.fld_dst; | ||
1108 | rt->rt_local_src = fl.fld_src; | ||
1109 | |||
1110 | rt->rt_dst_map = fl.fld_dst; | ||
1111 | rt->rt_src_map = fl.fld_src; | ||
1112 | |||
1113 | rt->u.dst.dev = dev_out; | ||
1114 | dev_hold(dev_out); | ||
1115 | rt->u.dst.neighbour = neigh; | ||
1116 | neigh = NULL; | ||
1117 | |||
1118 | rt->u.dst.lastuse = jiffies; | ||
1119 | rt->u.dst.output = dn_output; | ||
1120 | rt->u.dst.input = dn_rt_bug; | ||
1121 | rt->rt_flags = flags; | ||
1122 | if (flags & RTCF_LOCAL) | ||
1123 | rt->u.dst.input = dn_nsp_rx; | ||
1124 | |||
1125 | err = dn_rt_set_next_hop(rt, &res); | ||
1126 | if (err) | ||
1127 | goto e_neighbour; | ||
1128 | |||
1129 | hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst); | ||
1130 | dn_insert_route(rt, hash, (struct dn_route **)pprt); | ||
1131 | |||
1132 | done: | ||
1133 | if (neigh) | ||
1134 | neigh_release(neigh); | ||
1135 | if (free_res) | ||
1136 | dn_fib_res_put(&res); | ||
1137 | if (dev_out) | ||
1138 | dev_put(dev_out); | ||
1139 | out: | ||
1140 | return err; | ||
1141 | |||
1142 | e_addr: | ||
1143 | err = -EADDRNOTAVAIL; | ||
1144 | goto done; | ||
1145 | e_inval: | ||
1146 | err = -EINVAL; | ||
1147 | goto done; | ||
1148 | e_nobufs: | ||
1149 | err = -ENOBUFS; | ||
1150 | goto done; | ||
1151 | e_neighbour: | ||
1152 | dst_free(&rt->u.dst); | ||
1153 | goto e_nobufs; | ||
1154 | } | ||
1155 | |||
1156 | |||
1157 | /* | ||
1158 | * N.B. The flags may be moved into the flowi at some future stage. | ||
1159 | */ | ||
1160 | static int __dn_route_output_key(struct dst_entry **pprt, const struct flowi *flp, int flags) | ||
1161 | { | ||
1162 | unsigned hash = dn_hash(flp->fld_src, flp->fld_dst); | ||
1163 | struct dn_route *rt = NULL; | ||
1164 | |||
1165 | if (!(flags & MSG_TRYHARD)) { | ||
1166 | rcu_read_lock_bh(); | ||
1167 | for(rt = rcu_dereference(dn_rt_hash_table[hash].chain); rt; | ||
1168 | rt = rcu_dereference(rt->u.rt_next)) { | ||
1169 | if ((flp->fld_dst == rt->fl.fld_dst) && | ||
1170 | (flp->fld_src == rt->fl.fld_src) && | ||
1171 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
1172 | (flp->fld_fwmark == rt->fl.fld_fwmark) && | ||
1173 | #endif | ||
1174 | (rt->fl.iif == 0) && | ||
1175 | (rt->fl.oif == flp->oif)) { | ||
1176 | rt->u.dst.lastuse = jiffies; | ||
1177 | dst_hold(&rt->u.dst); | ||
1178 | rt->u.dst.__use++; | ||
1179 | rcu_read_unlock_bh(); | ||
1180 | *pprt = &rt->u.dst; | ||
1181 | return 0; | ||
1182 | } | ||
1183 | } | ||
1184 | rcu_read_unlock_bh(); | ||
1185 | } | ||
1186 | |||
1187 | return dn_route_output_slow(pprt, flp, flags); | ||
1188 | } | ||
1189 | |||
1190 | static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int flags) | ||
1191 | { | ||
1192 | int err; | ||
1193 | |||
1194 | err = __dn_route_output_key(pprt, flp, flags); | ||
1195 | if (err == 0 && flp->proto) { | ||
1196 | err = xfrm_lookup(pprt, flp, NULL, 0); | ||
1197 | } | ||
1198 | return err; | ||
1199 | } | ||
1200 | |||
1201 | int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock *sk, int flags) | ||
1202 | { | ||
1203 | int err; | ||
1204 | |||
1205 | err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD); | ||
1206 | if (err == 0 && fl->proto) { | ||
1207 | err = xfrm_lookup(pprt, fl, sk, !(flags & MSG_DONTWAIT)); | ||
1208 | } | ||
1209 | return err; | ||
1210 | } | ||
1211 | |||
1212 | static int dn_route_input_slow(struct sk_buff *skb) | ||
1213 | { | ||
1214 | struct dn_route *rt = NULL; | ||
1215 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
1216 | struct net_device *in_dev = skb->dev; | ||
1217 | struct net_device *out_dev = NULL; | ||
1218 | struct dn_dev *dn_db; | ||
1219 | struct neighbour *neigh = NULL; | ||
1220 | unsigned hash; | ||
1221 | int flags = 0; | ||
1222 | __u16 gateway = 0; | ||
1223 | __u16 local_src = 0; | ||
1224 | struct flowi fl = { .nl_u = { .dn_u = | ||
1225 | { .daddr = cb->dst, | ||
1226 | .saddr = cb->src, | ||
1227 | .scope = RT_SCOPE_UNIVERSE, | ||
1228 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
1229 | .fwmark = skb->nfmark | ||
1230 | #endif | ||
1231 | } }, | ||
1232 | .iif = skb->dev->ifindex }; | ||
1233 | struct dn_fib_res res = { .fi = NULL, .type = RTN_UNREACHABLE }; | ||
1234 | int err = -EINVAL; | ||
1235 | int free_res = 0; | ||
1236 | |||
1237 | dev_hold(in_dev); | ||
1238 | |||
1239 | if ((dn_db = in_dev->dn_ptr) == NULL) | ||
1240 | goto out; | ||
1241 | |||
1242 | /* Zero source addresses are not allowed */ | ||
1243 | if (fl.fld_src == 0) | ||
1244 | goto out; | ||
1245 | |||
1246 | /* | ||
1247 | * In this case we've just received a packet from a source | ||
1248 | * outside ourselves pretending to come from us. We don't | ||
1249 | * allow it any further to prevent routing loops, spoofing and | ||
1250 | * other nasties. Loopback packets already have the dst attached | ||
1251 | * so this only affects packets which have originated elsewhere. | ||
1252 | */ | ||
1253 | err = -ENOTUNIQ; | ||
1254 | if (dn_dev_islocal(in_dev, cb->src)) | ||
1255 | goto out; | ||
1256 | |||
1257 | err = dn_fib_lookup(&fl, &res); | ||
1258 | if (err) { | ||
1259 | if (err != -ESRCH) | ||
1260 | goto out; | ||
1261 | /* | ||
1262 | * Is the destination us ? | ||
1263 | */ | ||
1264 | if (!dn_dev_islocal(in_dev, cb->dst)) | ||
1265 | goto e_inval; | ||
1266 | |||
1267 | res.type = RTN_LOCAL; | ||
1268 | flags |= RTCF_DIRECTSRC; | ||
1269 | } else { | ||
1270 | __u16 src_map = fl.fld_src; | ||
1271 | free_res = 1; | ||
1272 | |||
1273 | out_dev = DN_FIB_RES_DEV(res); | ||
1274 | if (out_dev == NULL) { | ||
1275 | if (net_ratelimit()) | ||
1276 | printk(KERN_CRIT "Bug in dn_route_input_slow() " | ||
1277 | "No output device\n"); | ||
1278 | goto e_inval; | ||
1279 | } | ||
1280 | dev_hold(out_dev); | ||
1281 | |||
1282 | if (res.r) | ||
1283 | src_map = dn_fib_rules_policy(fl.fld_src, &res, &flags); | ||
1284 | |||
1285 | gateway = DN_FIB_RES_GW(res); | ||
1286 | if (res.type == RTN_NAT) { | ||
1287 | fl.fld_dst = dn_fib_rules_map_destination(fl.fld_dst, &res); | ||
1288 | dn_fib_res_put(&res); | ||
1289 | free_res = 0; | ||
1290 | if (dn_fib_lookup(&fl, &res)) | ||
1291 | goto e_inval; | ||
1292 | free_res = 1; | ||
1293 | if (res.type != RTN_UNICAST) | ||
1294 | goto e_inval; | ||
1295 | flags |= RTCF_DNAT; | ||
1296 | gateway = fl.fld_dst; | ||
1297 | } | ||
1298 | fl.fld_src = src_map; | ||
1299 | } | ||
1300 | |||
1301 | switch(res.type) { | ||
1302 | case RTN_UNICAST: | ||
1303 | /* | ||
1304 | * Forwarding check here, we only check for forwarding | ||
1305 | * being turned off, if you want to only forward intra | ||
1306 | * area, its up to you to set the routing tables up | ||
1307 | * correctly. | ||
1308 | */ | ||
1309 | if (dn_db->parms.forwarding == 0) | ||
1310 | goto e_inval; | ||
1311 | |||
1312 | if (res.fi->fib_nhs > 1 && fl.oif == 0) | ||
1313 | dn_fib_select_multipath(&fl, &res); | ||
1314 | |||
1315 | /* | ||
1316 | * Check for out_dev == in_dev. We use the RTCF_DOREDIRECT | ||
1317 | * flag as a hint to set the intra-ethernet bit when | ||
1318 | * forwarding. If we've got NAT in operation, we don't do | ||
1319 | * this optimisation. | ||
1320 | */ | ||
1321 | if (out_dev == in_dev && !(flags & RTCF_NAT)) | ||
1322 | flags |= RTCF_DOREDIRECT; | ||
1323 | |||
1324 | local_src = DN_FIB_RES_PREFSRC(res); | ||
1325 | |||
1326 | case RTN_BLACKHOLE: | ||
1327 | case RTN_UNREACHABLE: | ||
1328 | break; | ||
1329 | case RTN_LOCAL: | ||
1330 | flags |= RTCF_LOCAL; | ||
1331 | fl.fld_src = cb->dst; | ||
1332 | fl.fld_dst = cb->src; | ||
1333 | |||
1334 | /* Routing tables gave us a gateway */ | ||
1335 | if (gateway) | ||
1336 | goto make_route; | ||
1337 | |||
1338 | /* Packet was intra-ethernet, so we know its on-link */ | ||
1339 | if (cb->rt_flags | DN_RT_F_IE) { | ||
1340 | gateway = cb->src; | ||
1341 | flags |= RTCF_DIRECTSRC; | ||
1342 | goto make_route; | ||
1343 | } | ||
1344 | |||
1345 | /* Use the default router if there is one */ | ||
1346 | neigh = neigh_clone(dn_db->router); | ||
1347 | if (neigh) { | ||
1348 | gateway = ((struct dn_neigh *)neigh)->addr; | ||
1349 | goto make_route; | ||
1350 | } | ||
1351 | |||
1352 | /* Close eyes and pray */ | ||
1353 | gateway = cb->src; | ||
1354 | flags |= RTCF_DIRECTSRC; | ||
1355 | goto make_route; | ||
1356 | default: | ||
1357 | goto e_inval; | ||
1358 | } | ||
1359 | |||
1360 | make_route: | ||
1361 | rt = dst_alloc(&dn_dst_ops); | ||
1362 | if (rt == NULL) | ||
1363 | goto e_nobufs; | ||
1364 | |||
1365 | rt->rt_saddr = fl.fld_src; | ||
1366 | rt->rt_daddr = fl.fld_dst; | ||
1367 | rt->rt_gateway = fl.fld_dst; | ||
1368 | if (gateway) | ||
1369 | rt->rt_gateway = gateway; | ||
1370 | rt->rt_local_src = local_src ? local_src : rt->rt_saddr; | ||
1371 | |||
1372 | rt->rt_dst_map = fl.fld_dst; | ||
1373 | rt->rt_src_map = fl.fld_src; | ||
1374 | |||
1375 | rt->fl.fld_src = cb->src; | ||
1376 | rt->fl.fld_dst = cb->dst; | ||
1377 | rt->fl.oif = 0; | ||
1378 | rt->fl.iif = in_dev->ifindex; | ||
1379 | rt->fl.fld_fwmark = fl.fld_fwmark; | ||
1380 | |||
1381 | rt->u.dst.flags = DST_HOST; | ||
1382 | rt->u.dst.neighbour = neigh; | ||
1383 | rt->u.dst.dev = out_dev; | ||
1384 | rt->u.dst.lastuse = jiffies; | ||
1385 | rt->u.dst.output = dn_rt_bug; | ||
1386 | switch(res.type) { | ||
1387 | case RTN_UNICAST: | ||
1388 | rt->u.dst.input = dn_forward; | ||
1389 | break; | ||
1390 | case RTN_LOCAL: | ||
1391 | rt->u.dst.output = dn_output; | ||
1392 | rt->u.dst.input = dn_nsp_rx; | ||
1393 | rt->u.dst.dev = in_dev; | ||
1394 | flags |= RTCF_LOCAL; | ||
1395 | break; | ||
1396 | default: | ||
1397 | case RTN_UNREACHABLE: | ||
1398 | case RTN_BLACKHOLE: | ||
1399 | rt->u.dst.input = dn_blackhole; | ||
1400 | } | ||
1401 | rt->rt_flags = flags; | ||
1402 | if (rt->u.dst.dev) | ||
1403 | dev_hold(rt->u.dst.dev); | ||
1404 | |||
1405 | err = dn_rt_set_next_hop(rt, &res); | ||
1406 | if (err) | ||
1407 | goto e_neighbour; | ||
1408 | |||
1409 | hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst); | ||
1410 | dn_insert_route(rt, hash, (struct dn_route **)&skb->dst); | ||
1411 | |||
1412 | done: | ||
1413 | if (neigh) | ||
1414 | neigh_release(neigh); | ||
1415 | if (free_res) | ||
1416 | dn_fib_res_put(&res); | ||
1417 | dev_put(in_dev); | ||
1418 | if (out_dev) | ||
1419 | dev_put(out_dev); | ||
1420 | out: | ||
1421 | return err; | ||
1422 | |||
1423 | e_inval: | ||
1424 | err = -EINVAL; | ||
1425 | goto done; | ||
1426 | |||
1427 | e_nobufs: | ||
1428 | err = -ENOBUFS; | ||
1429 | goto done; | ||
1430 | |||
1431 | e_neighbour: | ||
1432 | dst_free(&rt->u.dst); | ||
1433 | goto done; | ||
1434 | } | ||
1435 | |||
1436 | int dn_route_input(struct sk_buff *skb) | ||
1437 | { | ||
1438 | struct dn_route *rt; | ||
1439 | struct dn_skb_cb *cb = DN_SKB_CB(skb); | ||
1440 | unsigned hash = dn_hash(cb->src, cb->dst); | ||
1441 | |||
1442 | if (skb->dst) | ||
1443 | return 0; | ||
1444 | |||
1445 | rcu_read_lock(); | ||
1446 | for(rt = rcu_dereference(dn_rt_hash_table[hash].chain); rt != NULL; | ||
1447 | rt = rcu_dereference(rt->u.rt_next)) { | ||
1448 | if ((rt->fl.fld_src == cb->src) && | ||
1449 | (rt->fl.fld_dst == cb->dst) && | ||
1450 | (rt->fl.oif == 0) && | ||
1451 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
1452 | (rt->fl.fld_fwmark == skb->nfmark) && | ||
1453 | #endif | ||
1454 | (rt->fl.iif == cb->iif)) { | ||
1455 | rt->u.dst.lastuse = jiffies; | ||
1456 | dst_hold(&rt->u.dst); | ||
1457 | rt->u.dst.__use++; | ||
1458 | rcu_read_unlock(); | ||
1459 | skb->dst = (struct dst_entry *)rt; | ||
1460 | return 0; | ||
1461 | } | ||
1462 | } | ||
1463 | rcu_read_unlock(); | ||
1464 | |||
1465 | return dn_route_input_slow(skb); | ||
1466 | } | ||
1467 | |||
1468 | static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait) | ||
1469 | { | ||
1470 | struct dn_route *rt = (struct dn_route *)skb->dst; | ||
1471 | struct rtmsg *r; | ||
1472 | struct nlmsghdr *nlh; | ||
1473 | unsigned char *b = skb->tail; | ||
1474 | struct rta_cacheinfo ci; | ||
1475 | |||
1476 | nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r)); | ||
1477 | r = NLMSG_DATA(nlh); | ||
1478 | nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; | ||
1479 | r->rtm_family = AF_DECnet; | ||
1480 | r->rtm_dst_len = 16; | ||
1481 | r->rtm_src_len = 0; | ||
1482 | r->rtm_tos = 0; | ||
1483 | r->rtm_table = RT_TABLE_MAIN; | ||
1484 | r->rtm_type = rt->rt_type; | ||
1485 | r->rtm_flags = (rt->rt_flags & ~0xFFFF) | RTM_F_CLONED; | ||
1486 | r->rtm_scope = RT_SCOPE_UNIVERSE; | ||
1487 | r->rtm_protocol = RTPROT_UNSPEC; | ||
1488 | if (rt->rt_flags & RTCF_NOTIFY) | ||
1489 | r->rtm_flags |= RTM_F_NOTIFY; | ||
1490 | RTA_PUT(skb, RTA_DST, 2, &rt->rt_daddr); | ||
1491 | if (rt->fl.fld_src) { | ||
1492 | r->rtm_src_len = 16; | ||
1493 | RTA_PUT(skb, RTA_SRC, 2, &rt->fl.fld_src); | ||
1494 | } | ||
1495 | if (rt->u.dst.dev) | ||
1496 | RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex); | ||
1497 | /* | ||
1498 | * Note to self - change this if input routes reverse direction when | ||
1499 | * they deal only with inputs and not with replies like they do | ||
1500 | * currently. | ||
1501 | */ | ||
1502 | RTA_PUT(skb, RTA_PREFSRC, 2, &rt->rt_local_src); | ||
1503 | if (rt->rt_daddr != rt->rt_gateway) | ||
1504 | RTA_PUT(skb, RTA_GATEWAY, 2, &rt->rt_gateway); | ||
1505 | if (rtnetlink_put_metrics(skb, rt->u.dst.metrics) < 0) | ||
1506 | goto rtattr_failure; | ||
1507 | ci.rta_lastuse = jiffies_to_clock_t(jiffies - rt->u.dst.lastuse); | ||
1508 | ci.rta_used = rt->u.dst.__use; | ||
1509 | ci.rta_clntref = atomic_read(&rt->u.dst.__refcnt); | ||
1510 | if (rt->u.dst.expires) | ||
1511 | ci.rta_expires = jiffies_to_clock_t(rt->u.dst.expires - jiffies); | ||
1512 | else | ||
1513 | ci.rta_expires = 0; | ||
1514 | ci.rta_error = rt->u.dst.error; | ||
1515 | ci.rta_id = ci.rta_ts = ci.rta_tsage = 0; | ||
1516 | RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci); | ||
1517 | if (rt->fl.iif) | ||
1518 | RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->fl.iif); | ||
1519 | |||
1520 | nlh->nlmsg_len = skb->tail - b; | ||
1521 | return skb->len; | ||
1522 | |||
1523 | nlmsg_failure: | ||
1524 | rtattr_failure: | ||
1525 | skb_trim(skb, b - skb->data); | ||
1526 | return -1; | ||
1527 | } | ||
1528 | |||
1529 | /* | ||
1530 | * This is called by both endnodes and routers now. | ||
1531 | */ | ||
1532 | int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg) | ||
1533 | { | ||
1534 | struct rtattr **rta = arg; | ||
1535 | struct rtmsg *rtm = NLMSG_DATA(nlh); | ||
1536 | struct dn_route *rt = NULL; | ||
1537 | struct dn_skb_cb *cb; | ||
1538 | int err; | ||
1539 | struct sk_buff *skb; | ||
1540 | struct flowi fl; | ||
1541 | |||
1542 | memset(&fl, 0, sizeof(fl)); | ||
1543 | fl.proto = DNPROTO_NSP; | ||
1544 | |||
1545 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | ||
1546 | if (skb == NULL) | ||
1547 | return -ENOBUFS; | ||
1548 | skb->mac.raw = skb->data; | ||
1549 | cb = DN_SKB_CB(skb); | ||
1550 | |||
1551 | if (rta[RTA_SRC-1]) | ||
1552 | memcpy(&fl.fld_src, RTA_DATA(rta[RTA_SRC-1]), 2); | ||
1553 | if (rta[RTA_DST-1]) | ||
1554 | memcpy(&fl.fld_dst, RTA_DATA(rta[RTA_DST-1]), 2); | ||
1555 | if (rta[RTA_IIF-1]) | ||
1556 | memcpy(&fl.iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int)); | ||
1557 | |||
1558 | if (fl.iif) { | ||
1559 | struct net_device *dev; | ||
1560 | if ((dev = dev_get_by_index(fl.iif)) == NULL) { | ||
1561 | kfree_skb(skb); | ||
1562 | return -ENODEV; | ||
1563 | } | ||
1564 | if (!dev->dn_ptr) { | ||
1565 | dev_put(dev); | ||
1566 | kfree_skb(skb); | ||
1567 | return -ENODEV; | ||
1568 | } | ||
1569 | skb->protocol = __constant_htons(ETH_P_DNA_RT); | ||
1570 | skb->dev = dev; | ||
1571 | cb->src = fl.fld_src; | ||
1572 | cb->dst = fl.fld_dst; | ||
1573 | local_bh_disable(); | ||
1574 | err = dn_route_input(skb); | ||
1575 | local_bh_enable(); | ||
1576 | memset(cb, 0, sizeof(struct dn_skb_cb)); | ||
1577 | rt = (struct dn_route *)skb->dst; | ||
1578 | if (!err && -rt->u.dst.error) | ||
1579 | err = rt->u.dst.error; | ||
1580 | } else { | ||
1581 | int oif = 0; | ||
1582 | if (rta[RTA_OIF - 1]) | ||
1583 | memcpy(&oif, RTA_DATA(rta[RTA_OIF - 1]), sizeof(int)); | ||
1584 | fl.oif = oif; | ||
1585 | err = dn_route_output_key((struct dst_entry **)&rt, &fl, 0); | ||
1586 | } | ||
1587 | |||
1588 | if (skb->dev) | ||
1589 | dev_put(skb->dev); | ||
1590 | skb->dev = NULL; | ||
1591 | if (err) | ||
1592 | goto out_free; | ||
1593 | skb->dst = &rt->u.dst; | ||
1594 | if (rtm->rtm_flags & RTM_F_NOTIFY) | ||
1595 | rt->rt_flags |= RTCF_NOTIFY; | ||
1596 | |||
1597 | NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid; | ||
1598 | |||
1599 | err = dn_rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0); | ||
1600 | |||
1601 | if (err == 0) | ||
1602 | goto out_free; | ||
1603 | if (err < 0) { | ||
1604 | err = -EMSGSIZE; | ||
1605 | goto out_free; | ||
1606 | } | ||
1607 | |||
1608 | err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); | ||
1609 | |||
1610 | return err; | ||
1611 | |||
1612 | out_free: | ||
1613 | kfree_skb(skb); | ||
1614 | return err; | ||
1615 | } | ||
1616 | |||
1617 | /* | ||
1618 | * For routers, this is called from dn_fib_dump, but for endnodes its | ||
1619 | * called directly from the rtnetlink dispatch table. | ||
1620 | */ | ||
1621 | int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb) | ||
1622 | { | ||
1623 | struct dn_route *rt; | ||
1624 | int h, s_h; | ||
1625 | int idx, s_idx; | ||
1626 | |||
1627 | if (NLMSG_PAYLOAD(cb->nlh, 0) < sizeof(struct rtmsg)) | ||
1628 | return -EINVAL; | ||
1629 | if (!(((struct rtmsg *)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)) | ||
1630 | return 0; | ||
1631 | |||
1632 | s_h = cb->args[0]; | ||
1633 | s_idx = idx = cb->args[1]; | ||
1634 | for(h = 0; h <= dn_rt_hash_mask; h++) { | ||
1635 | if (h < s_h) | ||
1636 | continue; | ||
1637 | if (h > s_h) | ||
1638 | s_idx = 0; | ||
1639 | rcu_read_lock_bh(); | ||
1640 | for(rt = rcu_dereference(dn_rt_hash_table[h].chain), idx = 0; | ||
1641 | rt; | ||
1642 | rt = rcu_dereference(rt->u.rt_next), idx++) { | ||
1643 | if (idx < s_idx) | ||
1644 | continue; | ||
1645 | skb->dst = dst_clone(&rt->u.dst); | ||
1646 | if (dn_rt_fill_info(skb, NETLINK_CB(cb->skb).pid, | ||
1647 | cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) { | ||
1648 | dst_release(xchg(&skb->dst, NULL)); | ||
1649 | rcu_read_unlock_bh(); | ||
1650 | goto done; | ||
1651 | } | ||
1652 | dst_release(xchg(&skb->dst, NULL)); | ||
1653 | } | ||
1654 | rcu_read_unlock_bh(); | ||
1655 | } | ||
1656 | |||
1657 | done: | ||
1658 | cb->args[0] = h; | ||
1659 | cb->args[1] = idx; | ||
1660 | return skb->len; | ||
1661 | } | ||
1662 | |||
1663 | #ifdef CONFIG_PROC_FS | ||
1664 | struct dn_rt_cache_iter_state { | ||
1665 | int bucket; | ||
1666 | }; | ||
1667 | |||
1668 | static struct dn_route *dn_rt_cache_get_first(struct seq_file *seq) | ||
1669 | { | ||
1670 | struct dn_route *rt = NULL; | ||
1671 | struct dn_rt_cache_iter_state *s = seq->private; | ||
1672 | |||
1673 | for(s->bucket = dn_rt_hash_mask; s->bucket >= 0; --s->bucket) { | ||
1674 | rcu_read_lock_bh(); | ||
1675 | rt = dn_rt_hash_table[s->bucket].chain; | ||
1676 | if (rt) | ||
1677 | break; | ||
1678 | rcu_read_unlock_bh(); | ||
1679 | } | ||
1680 | return rt; | ||
1681 | } | ||
1682 | |||
1683 | static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_route *rt) | ||
1684 | { | ||
1685 | struct dn_rt_cache_iter_state *s = rcu_dereference(seq->private); | ||
1686 | |||
1687 | rt = rt->u.rt_next; | ||
1688 | while(!rt) { | ||
1689 | rcu_read_unlock_bh(); | ||
1690 | if (--s->bucket < 0) | ||
1691 | break; | ||
1692 | rcu_read_lock_bh(); | ||
1693 | rt = dn_rt_hash_table[s->bucket].chain; | ||
1694 | } | ||
1695 | return rt; | ||
1696 | } | ||
1697 | |||
1698 | static void *dn_rt_cache_seq_start(struct seq_file *seq, loff_t *pos) | ||
1699 | { | ||
1700 | struct dn_route *rt = dn_rt_cache_get_first(seq); | ||
1701 | |||
1702 | if (rt) { | ||
1703 | while(*pos && (rt = dn_rt_cache_get_next(seq, rt))) | ||
1704 | --*pos; | ||
1705 | } | ||
1706 | return *pos ? NULL : rt; | ||
1707 | } | ||
1708 | |||
1709 | static void *dn_rt_cache_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
1710 | { | ||
1711 | struct dn_route *rt = dn_rt_cache_get_next(seq, v); | ||
1712 | ++*pos; | ||
1713 | return rt; | ||
1714 | } | ||
1715 | |||
1716 | static void dn_rt_cache_seq_stop(struct seq_file *seq, void *v) | ||
1717 | { | ||
1718 | if (v) | ||
1719 | rcu_read_unlock_bh(); | ||
1720 | } | ||
1721 | |||
1722 | static int dn_rt_cache_seq_show(struct seq_file *seq, void *v) | ||
1723 | { | ||
1724 | struct dn_route *rt = v; | ||
1725 | char buf1[DN_ASCBUF_LEN], buf2[DN_ASCBUF_LEN]; | ||
1726 | |||
1727 | seq_printf(seq, "%-8s %-7s %-7s %04d %04d %04d\n", | ||
1728 | rt->u.dst.dev ? rt->u.dst.dev->name : "*", | ||
1729 | dn_addr2asc(dn_ntohs(rt->rt_daddr), buf1), | ||
1730 | dn_addr2asc(dn_ntohs(rt->rt_saddr), buf2), | ||
1731 | atomic_read(&rt->u.dst.__refcnt), | ||
1732 | rt->u.dst.__use, | ||
1733 | (int) dst_metric(&rt->u.dst, RTAX_RTT)); | ||
1734 | return 0; | ||
1735 | } | ||
1736 | |||
1737 | static struct seq_operations dn_rt_cache_seq_ops = { | ||
1738 | .start = dn_rt_cache_seq_start, | ||
1739 | .next = dn_rt_cache_seq_next, | ||
1740 | .stop = dn_rt_cache_seq_stop, | ||
1741 | .show = dn_rt_cache_seq_show, | ||
1742 | }; | ||
1743 | |||
1744 | static int dn_rt_cache_seq_open(struct inode *inode, struct file *file) | ||
1745 | { | ||
1746 | struct seq_file *seq; | ||
1747 | int rc = -ENOMEM; | ||
1748 | struct dn_rt_cache_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
1749 | |||
1750 | if (!s) | ||
1751 | goto out; | ||
1752 | rc = seq_open(file, &dn_rt_cache_seq_ops); | ||
1753 | if (rc) | ||
1754 | goto out_kfree; | ||
1755 | seq = file->private_data; | ||
1756 | seq->private = s; | ||
1757 | memset(s, 0, sizeof(*s)); | ||
1758 | out: | ||
1759 | return rc; | ||
1760 | out_kfree: | ||
1761 | kfree(s); | ||
1762 | goto out; | ||
1763 | } | ||
1764 | |||
1765 | static struct file_operations dn_rt_cache_seq_fops = { | ||
1766 | .owner = THIS_MODULE, | ||
1767 | .open = dn_rt_cache_seq_open, | ||
1768 | .read = seq_read, | ||
1769 | .llseek = seq_lseek, | ||
1770 | .release = seq_release_private, | ||
1771 | }; | ||
1772 | |||
1773 | #endif /* CONFIG_PROC_FS */ | ||
1774 | |||
1775 | void __init dn_route_init(void) | ||
1776 | { | ||
1777 | int i, goal, order; | ||
1778 | |||
1779 | dn_dst_ops.kmem_cachep = kmem_cache_create("dn_dst_cache", | ||
1780 | sizeof(struct dn_route), | ||
1781 | 0, SLAB_HWCACHE_ALIGN, | ||
1782 | NULL, NULL); | ||
1783 | |||
1784 | if (!dn_dst_ops.kmem_cachep) | ||
1785 | panic("DECnet: Failed to allocate dn_dst_cache\n"); | ||
1786 | |||
1787 | init_timer(&dn_route_timer); | ||
1788 | dn_route_timer.function = dn_dst_check_expire; | ||
1789 | dn_route_timer.expires = jiffies + decnet_dst_gc_interval * HZ; | ||
1790 | add_timer(&dn_route_timer); | ||
1791 | |||
1792 | goal = num_physpages >> (26 - PAGE_SHIFT); | ||
1793 | |||
1794 | for(order = 0; (1UL << order) < goal; order++) | ||
1795 | /* NOTHING */; | ||
1796 | |||
1797 | /* | ||
1798 | * Only want 1024 entries max, since the table is very, very unlikely | ||
1799 | * to be larger than that. | ||
1800 | */ | ||
1801 | while(order && ((((1UL << order) * PAGE_SIZE) / | ||
1802 | sizeof(struct dn_rt_hash_bucket)) >= 2048)) | ||
1803 | order--; | ||
1804 | |||
1805 | do { | ||
1806 | dn_rt_hash_mask = (1UL << order) * PAGE_SIZE / | ||
1807 | sizeof(struct dn_rt_hash_bucket); | ||
1808 | while(dn_rt_hash_mask & (dn_rt_hash_mask - 1)) | ||
1809 | dn_rt_hash_mask--; | ||
1810 | dn_rt_hash_table = (struct dn_rt_hash_bucket *) | ||
1811 | __get_free_pages(GFP_ATOMIC, order); | ||
1812 | } while (dn_rt_hash_table == NULL && --order > 0); | ||
1813 | |||
1814 | if (!dn_rt_hash_table) | ||
1815 | panic("Failed to allocate DECnet route cache hash table\n"); | ||
1816 | |||
1817 | printk(KERN_INFO | ||
1818 | "DECnet: Routing cache hash table of %u buckets, %ldKbytes\n", | ||
1819 | dn_rt_hash_mask, | ||
1820 | (long)(dn_rt_hash_mask*sizeof(struct dn_rt_hash_bucket))/1024); | ||
1821 | |||
1822 | dn_rt_hash_mask--; | ||
1823 | for(i = 0; i <= dn_rt_hash_mask; i++) { | ||
1824 | spin_lock_init(&dn_rt_hash_table[i].lock); | ||
1825 | dn_rt_hash_table[i].chain = NULL; | ||
1826 | } | ||
1827 | |||
1828 | dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1); | ||
1829 | |||
1830 | proc_net_fops_create("decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops); | ||
1831 | } | ||
1832 | |||
1833 | void __exit dn_route_cleanup(void) | ||
1834 | { | ||
1835 | del_timer(&dn_route_timer); | ||
1836 | dn_run_flush(0); | ||
1837 | |||
1838 | proc_net_remove("decnet_cache"); | ||
1839 | } | ||
1840 | |||
diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c new file mode 100644 index 000000000000..597587d170d8 --- /dev/null +++ b/net/decnet/dn_rules.c | |||
@@ -0,0 +1,416 @@ | |||
1 | |||
2 | /* | ||
3 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
4 | * operating system. DECnet is implemented using the BSD Socket | ||
5 | * interface as the means of communication with the user level. | ||
6 | * | ||
7 | * DECnet Routing Forwarding Information Base (Rules) | ||
8 | * | ||
9 | * Author: Steve Whitehouse <SteveW@ACM.org> | ||
10 | * Mostly copied from Alexey Kuznetsov's ipv4/fib_rules.c | ||
11 | * | ||
12 | * | ||
13 | * Changes: | ||
14 | * | ||
15 | */ | ||
16 | #include <linux/config.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/net.h> | ||
19 | #include <linux/socket.h> | ||
20 | #include <linux/sockios.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/skbuff.h> | ||
23 | #include <linux/netlink.h> | ||
24 | #include <linux/rtnetlink.h> | ||
25 | #include <linux/proc_fs.h> | ||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/spinlock.h> | ||
29 | #include <linux/in_route.h> | ||
30 | #include <asm/atomic.h> | ||
31 | #include <asm/uaccess.h> | ||
32 | #include <net/neighbour.h> | ||
33 | #include <net/dst.h> | ||
34 | #include <net/flow.h> | ||
35 | #include <net/dn.h> | ||
36 | #include <net/dn_fib.h> | ||
37 | #include <net/dn_neigh.h> | ||
38 | #include <net/dn_dev.h> | ||
39 | |||
40 | struct dn_fib_rule | ||
41 | { | ||
42 | struct dn_fib_rule *r_next; | ||
43 | atomic_t r_clntref; | ||
44 | u32 r_preference; | ||
45 | unsigned char r_table; | ||
46 | unsigned char r_action; | ||
47 | unsigned char r_dst_len; | ||
48 | unsigned char r_src_len; | ||
49 | dn_address r_src; | ||
50 | dn_address r_srcmask; | ||
51 | dn_address r_dst; | ||
52 | dn_address r_dstmask; | ||
53 | dn_address r_srcmap; | ||
54 | u8 r_flags; | ||
55 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
56 | u32 r_fwmark; | ||
57 | #endif | ||
58 | int r_ifindex; | ||
59 | char r_ifname[IFNAMSIZ]; | ||
60 | int r_dead; | ||
61 | }; | ||
62 | |||
63 | static struct dn_fib_rule default_rule = { | ||
64 | .r_clntref = ATOMIC_INIT(2), | ||
65 | .r_preference = 0x7fff, | ||
66 | .r_table = RT_TABLE_MAIN, | ||
67 | .r_action = RTN_UNICAST | ||
68 | }; | ||
69 | |||
70 | static struct dn_fib_rule *dn_fib_rules = &default_rule; | ||
71 | static DEFINE_RWLOCK(dn_fib_rules_lock); | ||
72 | |||
73 | |||
74 | int dn_fib_rtm_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
75 | { | ||
76 | struct rtattr **rta = arg; | ||
77 | struct rtmsg *rtm = NLMSG_DATA(nlh); | ||
78 | struct dn_fib_rule *r, **rp; | ||
79 | int err = -ESRCH; | ||
80 | |||
81 | for(rp=&dn_fib_rules; (r=*rp) != NULL; rp = &r->r_next) { | ||
82 | if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 2) == 0) && | ||
83 | rtm->rtm_src_len == r->r_src_len && | ||
84 | rtm->rtm_dst_len == r->r_dst_len && | ||
85 | (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 2) == 0) && | ||
86 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
87 | (!rta[RTA_PROTOINFO-1] || memcmp(RTA_DATA(rta[RTA_PROTOINFO-1]), &r->r_fwmark, 4) == 0) && | ||
88 | #endif | ||
89 | (!rtm->rtm_type || rtm->rtm_type == r->r_action) && | ||
90 | (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) && | ||
91 | (!rta[RTA_IIF-1] || rtattr_strcmp(rta[RTA_IIF-1], r->r_ifname) == 0) && | ||
92 | (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) { | ||
93 | |||
94 | err = -EPERM; | ||
95 | if (r == &default_rule) | ||
96 | break; | ||
97 | |||
98 | write_lock_bh(&dn_fib_rules_lock); | ||
99 | *rp = r->r_next; | ||
100 | r->r_dead = 1; | ||
101 | write_unlock_bh(&dn_fib_rules_lock); | ||
102 | dn_fib_rule_put(r); | ||
103 | err = 0; | ||
104 | break; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | return err; | ||
109 | } | ||
110 | |||
111 | void dn_fib_rule_put(struct dn_fib_rule *r) | ||
112 | { | ||
113 | if (atomic_dec_and_test(&r->r_clntref)) { | ||
114 | if (r->r_dead) | ||
115 | kfree(r); | ||
116 | else | ||
117 | printk(KERN_DEBUG "Attempt to free alive dn_fib_rule\n"); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | |||
122 | int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
123 | { | ||
124 | struct rtattr **rta = arg; | ||
125 | struct rtmsg *rtm = NLMSG_DATA(nlh); | ||
126 | struct dn_fib_rule *r, *new_r, **rp; | ||
127 | unsigned char table_id; | ||
128 | |||
129 | if (rtm->rtm_src_len > 16 || rtm->rtm_dst_len > 16) | ||
130 | return -EINVAL; | ||
131 | |||
132 | if (rta[RTA_IIF-1] && RTA_PAYLOAD(rta[RTA_IIF-1]) > IFNAMSIZ) | ||
133 | return -EINVAL; | ||
134 | |||
135 | if (rtm->rtm_type == RTN_NAT) | ||
136 | return -EINVAL; | ||
137 | |||
138 | table_id = rtm->rtm_table; | ||
139 | if (table_id == RT_TABLE_UNSPEC) { | ||
140 | struct dn_fib_table *tb; | ||
141 | if (rtm->rtm_type == RTN_UNICAST) { | ||
142 | if ((tb = dn_fib_empty_table()) == NULL) | ||
143 | return -ENOBUFS; | ||
144 | table_id = tb->n; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | new_r = kmalloc(sizeof(*new_r), GFP_KERNEL); | ||
149 | if (!new_r) | ||
150 | return -ENOMEM; | ||
151 | memset(new_r, 0, sizeof(*new_r)); | ||
152 | if (rta[RTA_SRC-1]) | ||
153 | memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 2); | ||
154 | if (rta[RTA_DST-1]) | ||
155 | memcpy(&new_r->r_dst, RTA_DATA(rta[RTA_DST-1]), 2); | ||
156 | if (rta[RTA_GATEWAY-1]) | ||
157 | memcpy(&new_r->r_srcmap, RTA_DATA(rta[RTA_GATEWAY-1]), 2); | ||
158 | new_r->r_src_len = rtm->rtm_src_len; | ||
159 | new_r->r_dst_len = rtm->rtm_dst_len; | ||
160 | new_r->r_srcmask = dnet_make_mask(rtm->rtm_src_len); | ||
161 | new_r->r_dstmask = dnet_make_mask(rtm->rtm_dst_len); | ||
162 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
163 | if (rta[RTA_PROTOINFO-1]) | ||
164 | memcpy(&new_r->r_fwmark, RTA_DATA(rta[RTA_PROTOINFO-1]), 4); | ||
165 | #endif | ||
166 | new_r->r_action = rtm->rtm_type; | ||
167 | new_r->r_flags = rtm->rtm_flags; | ||
168 | if (rta[RTA_PRIORITY-1]) | ||
169 | memcpy(&new_r->r_preference, RTA_DATA(rta[RTA_PRIORITY-1]), 4); | ||
170 | new_r->r_table = table_id; | ||
171 | if (rta[RTA_IIF-1]) { | ||
172 | struct net_device *dev; | ||
173 | rtattr_strlcpy(new_r->r_ifname, rta[RTA_IIF-1], IFNAMSIZ); | ||
174 | new_r->r_ifindex = -1; | ||
175 | dev = dev_get_by_name(new_r->r_ifname); | ||
176 | if (dev) { | ||
177 | new_r->r_ifindex = dev->ifindex; | ||
178 | dev_put(dev); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | rp = &dn_fib_rules; | ||
183 | if (!new_r->r_preference) { | ||
184 | r = dn_fib_rules; | ||
185 | if (r && (r = r->r_next) != NULL) { | ||
186 | rp = &dn_fib_rules->r_next; | ||
187 | if (r->r_preference) | ||
188 | new_r->r_preference = r->r_preference - 1; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | while((r=*rp) != NULL) { | ||
193 | if (r->r_preference > new_r->r_preference) | ||
194 | break; | ||
195 | rp = &r->r_next; | ||
196 | } | ||
197 | |||
198 | new_r->r_next = r; | ||
199 | atomic_inc(&new_r->r_clntref); | ||
200 | write_lock_bh(&dn_fib_rules_lock); | ||
201 | *rp = new_r; | ||
202 | write_unlock_bh(&dn_fib_rules_lock); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | |||
207 | int dn_fib_lookup(const struct flowi *flp, struct dn_fib_res *res) | ||
208 | { | ||
209 | struct dn_fib_rule *r, *policy; | ||
210 | struct dn_fib_table *tb; | ||
211 | dn_address saddr = flp->fld_src; | ||
212 | dn_address daddr = flp->fld_dst; | ||
213 | int err; | ||
214 | |||
215 | read_lock(&dn_fib_rules_lock); | ||
216 | for(r = dn_fib_rules; r; r = r->r_next) { | ||
217 | if (((saddr^r->r_src) & r->r_srcmask) || | ||
218 | ((daddr^r->r_dst) & r->r_dstmask) || | ||
219 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
220 | (r->r_fwmark && r->r_fwmark != flp->fld_fwmark) || | ||
221 | #endif | ||
222 | (r->r_ifindex && r->r_ifindex != flp->iif)) | ||
223 | continue; | ||
224 | |||
225 | switch(r->r_action) { | ||
226 | case RTN_UNICAST: | ||
227 | case RTN_NAT: | ||
228 | policy = r; | ||
229 | break; | ||
230 | case RTN_UNREACHABLE: | ||
231 | read_unlock(&dn_fib_rules_lock); | ||
232 | return -ENETUNREACH; | ||
233 | default: | ||
234 | case RTN_BLACKHOLE: | ||
235 | read_unlock(&dn_fib_rules_lock); | ||
236 | return -EINVAL; | ||
237 | case RTN_PROHIBIT: | ||
238 | read_unlock(&dn_fib_rules_lock); | ||
239 | return -EACCES; | ||
240 | } | ||
241 | |||
242 | if ((tb = dn_fib_get_table(r->r_table, 0)) == NULL) | ||
243 | continue; | ||
244 | err = tb->lookup(tb, flp, res); | ||
245 | if (err == 0) { | ||
246 | res->r = policy; | ||
247 | if (policy) | ||
248 | atomic_inc(&policy->r_clntref); | ||
249 | read_unlock(&dn_fib_rules_lock); | ||
250 | return 0; | ||
251 | } | ||
252 | if (err < 0 && err != -EAGAIN) { | ||
253 | read_unlock(&dn_fib_rules_lock); | ||
254 | return err; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | read_unlock(&dn_fib_rules_lock); | ||
259 | return -ESRCH; | ||
260 | } | ||
261 | |||
262 | unsigned dnet_addr_type(__u16 addr) | ||
263 | { | ||
264 | struct flowi fl = { .nl_u = { .dn_u = { .daddr = addr } } }; | ||
265 | struct dn_fib_res res; | ||
266 | unsigned ret = RTN_UNICAST; | ||
267 | struct dn_fib_table *tb = dn_fib_tables[RT_TABLE_LOCAL]; | ||
268 | |||
269 | res.r = NULL; | ||
270 | |||
271 | if (tb) { | ||
272 | if (!tb->lookup(tb, &fl, &res)) { | ||
273 | ret = res.type; | ||
274 | dn_fib_res_put(&res); | ||
275 | } | ||
276 | } | ||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | __u16 dn_fib_rules_policy(__u16 saddr, struct dn_fib_res *res, unsigned *flags) | ||
281 | { | ||
282 | struct dn_fib_rule *r = res->r; | ||
283 | |||
284 | if (r->r_action == RTN_NAT) { | ||
285 | int addrtype = dnet_addr_type(r->r_srcmap); | ||
286 | |||
287 | if (addrtype == RTN_NAT) { | ||
288 | saddr = (saddr&~r->r_srcmask)|r->r_srcmap; | ||
289 | *flags |= RTCF_SNAT; | ||
290 | } else if (addrtype == RTN_LOCAL || r->r_srcmap == 0) { | ||
291 | saddr = r->r_srcmap; | ||
292 | *flags |= RTCF_MASQ; | ||
293 | } | ||
294 | } | ||
295 | return saddr; | ||
296 | } | ||
297 | |||
298 | static void dn_fib_rules_detach(struct net_device *dev) | ||
299 | { | ||
300 | struct dn_fib_rule *r; | ||
301 | |||
302 | for(r = dn_fib_rules; r; r = r->r_next) { | ||
303 | if (r->r_ifindex == dev->ifindex) { | ||
304 | write_lock_bh(&dn_fib_rules_lock); | ||
305 | r->r_ifindex = -1; | ||
306 | write_unlock_bh(&dn_fib_rules_lock); | ||
307 | } | ||
308 | } | ||
309 | } | ||
310 | |||
311 | static void dn_fib_rules_attach(struct net_device *dev) | ||
312 | { | ||
313 | struct dn_fib_rule *r; | ||
314 | |||
315 | for(r = dn_fib_rules; r; r = r->r_next) { | ||
316 | if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) { | ||
317 | write_lock_bh(&dn_fib_rules_lock); | ||
318 | r->r_ifindex = dev->ifindex; | ||
319 | write_unlock_bh(&dn_fib_rules_lock); | ||
320 | } | ||
321 | } | ||
322 | } | ||
323 | |||
324 | static int dn_fib_rules_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
325 | { | ||
326 | struct net_device *dev = ptr; | ||
327 | |||
328 | switch(event) { | ||
329 | case NETDEV_UNREGISTER: | ||
330 | dn_fib_rules_detach(dev); | ||
331 | dn_fib_sync_down(0, dev, 1); | ||
332 | case NETDEV_REGISTER: | ||
333 | dn_fib_rules_attach(dev); | ||
334 | dn_fib_sync_up(dev); | ||
335 | } | ||
336 | |||
337 | return NOTIFY_DONE; | ||
338 | } | ||
339 | |||
340 | |||
341 | static struct notifier_block dn_fib_rules_notifier = { | ||
342 | .notifier_call = dn_fib_rules_event, | ||
343 | }; | ||
344 | |||
345 | static int dn_fib_fill_rule(struct sk_buff *skb, struct dn_fib_rule *r, struct netlink_callback *cb) | ||
346 | { | ||
347 | struct rtmsg *rtm; | ||
348 | struct nlmsghdr *nlh; | ||
349 | unsigned char *b = skb->tail; | ||
350 | |||
351 | |||
352 | nlh = NLMSG_PUT(skb, NETLINK_CREDS(cb->skb)->pid, cb->nlh->nlmsg_seq, RTM_NEWRULE, sizeof(*rtm)); | ||
353 | rtm = NLMSG_DATA(nlh); | ||
354 | rtm->rtm_family = AF_DECnet; | ||
355 | rtm->rtm_dst_len = r->r_dst_len; | ||
356 | rtm->rtm_src_len = r->r_src_len; | ||
357 | rtm->rtm_tos = 0; | ||
358 | #ifdef CONFIG_DECNET_ROUTE_FWMARK | ||
359 | if (r->r_fwmark) | ||
360 | RTA_PUT(skb, RTA_PROTOINFO, 4, &r->r_fwmark); | ||
361 | #endif | ||
362 | rtm->rtm_table = r->r_table; | ||
363 | rtm->rtm_protocol = 0; | ||
364 | rtm->rtm_scope = 0; | ||
365 | rtm->rtm_type = r->r_action; | ||
366 | rtm->rtm_flags = r->r_flags; | ||
367 | |||
368 | if (r->r_dst_len) | ||
369 | RTA_PUT(skb, RTA_DST, 2, &r->r_dst); | ||
370 | if (r->r_src_len) | ||
371 | RTA_PUT(skb, RTA_SRC, 2, &r->r_src); | ||
372 | if (r->r_ifname[0]) | ||
373 | RTA_PUT(skb, RTA_IIF, IFNAMSIZ, &r->r_ifname); | ||
374 | if (r->r_preference) | ||
375 | RTA_PUT(skb, RTA_PRIORITY, 4, &r->r_preference); | ||
376 | if (r->r_srcmap) | ||
377 | RTA_PUT(skb, RTA_GATEWAY, 2, &r->r_srcmap); | ||
378 | nlh->nlmsg_len = skb->tail - b; | ||
379 | return skb->len; | ||
380 | |||
381 | nlmsg_failure: | ||
382 | rtattr_failure: | ||
383 | skb_trim(skb, b - skb->data); | ||
384 | return -1; | ||
385 | } | ||
386 | |||
387 | int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) | ||
388 | { | ||
389 | int idx; | ||
390 | int s_idx = cb->args[0]; | ||
391 | struct dn_fib_rule *r; | ||
392 | |||
393 | read_lock(&dn_fib_rules_lock); | ||
394 | for(r = dn_fib_rules, idx = 0; r; r = r->r_next, idx++) { | ||
395 | if (idx < s_idx) | ||
396 | continue; | ||
397 | if (dn_fib_fill_rule(skb, r, cb) < 0) | ||
398 | break; | ||
399 | } | ||
400 | read_unlock(&dn_fib_rules_lock); | ||
401 | cb->args[0] = idx; | ||
402 | |||
403 | return skb->len; | ||
404 | } | ||
405 | |||
406 | void __init dn_fib_rules_init(void) | ||
407 | { | ||
408 | register_netdevice_notifier(&dn_fib_rules_notifier); | ||
409 | } | ||
410 | |||
411 | void __exit dn_fib_rules_cleanup(void) | ||
412 | { | ||
413 | unregister_netdevice_notifier(&dn_fib_rules_notifier); | ||
414 | } | ||
415 | |||
416 | |||
diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c new file mode 100644 index 000000000000..dad5603912be --- /dev/null +++ b/net/decnet/dn_table.c | |||
@@ -0,0 +1,825 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet Routing Forwarding Information Base (Routing Tables) | ||
7 | * | ||
8 | * Author: Steve Whitehouse <SteveW@ACM.org> | ||
9 | * Mostly copied from the IPv4 routing code | ||
10 | * | ||
11 | * | ||
12 | * Changes: | ||
13 | * | ||
14 | */ | ||
15 | #include <linux/config.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/net.h> | ||
18 | #include <linux/socket.h> | ||
19 | #include <linux/sockios.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/skbuff.h> | ||
22 | #include <linux/netlink.h> | ||
23 | #include <linux/rtnetlink.h> | ||
24 | #include <linux/proc_fs.h> | ||
25 | #include <linux/netdevice.h> | ||
26 | #include <linux/timer.h> | ||
27 | #include <linux/spinlock.h> | ||
28 | #include <asm/atomic.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | #include <linux/route.h> /* RTF_xxx */ | ||
31 | #include <net/neighbour.h> | ||
32 | #include <net/dst.h> | ||
33 | #include <net/flow.h> | ||
34 | #include <net/dn.h> | ||
35 | #include <net/dn_route.h> | ||
36 | #include <net/dn_fib.h> | ||
37 | #include <net/dn_neigh.h> | ||
38 | #include <net/dn_dev.h> | ||
39 | |||
40 | struct dn_zone | ||
41 | { | ||
42 | struct dn_zone *dz_next; | ||
43 | struct dn_fib_node **dz_hash; | ||
44 | int dz_nent; | ||
45 | int dz_divisor; | ||
46 | u32 dz_hashmask; | ||
47 | #define DZ_HASHMASK(dz) ((dz)->dz_hashmask) | ||
48 | int dz_order; | ||
49 | u16 dz_mask; | ||
50 | #define DZ_MASK(dz) ((dz)->dz_mask) | ||
51 | }; | ||
52 | |||
53 | struct dn_hash | ||
54 | { | ||
55 | struct dn_zone *dh_zones[17]; | ||
56 | struct dn_zone *dh_zone_list; | ||
57 | }; | ||
58 | |||
59 | #define dz_key_0(key) ((key).datum = 0) | ||
60 | #define dz_prefix(key,dz) ((key).datum) | ||
61 | |||
62 | #define for_nexthops(fi) { int nhsel; const struct dn_fib_nh *nh;\ | ||
63 | for(nhsel = 0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++) | ||
64 | |||
65 | #define endfor_nexthops(fi) } | ||
66 | |||
67 | #define DN_MAX_DIVISOR 1024 | ||
68 | #define DN_S_ZOMBIE 1 | ||
69 | #define DN_S_ACCESSED 2 | ||
70 | |||
71 | #define DN_FIB_SCAN(f, fp) \ | ||
72 | for( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fn_next) | ||
73 | |||
74 | #define DN_FIB_SCAN_KEY(f, fp, key) \ | ||
75 | for( ; ((f) = *(fp)) != NULL && dn_key_eq((f)->fn_key, (key)); (fp) = &(f)->fn_next) | ||
76 | |||
77 | #define RT_TABLE_MIN 1 | ||
78 | |||
79 | static DEFINE_RWLOCK(dn_fib_tables_lock); | ||
80 | struct dn_fib_table *dn_fib_tables[RT_TABLE_MAX + 1]; | ||
81 | |||
82 | static kmem_cache_t *dn_hash_kmem; | ||
83 | static int dn_fib_hash_zombies; | ||
84 | |||
85 | static inline dn_fib_idx_t dn_hash(dn_fib_key_t key, struct dn_zone *dz) | ||
86 | { | ||
87 | u16 h = ntohs(key.datum)>>(16 - dz->dz_order); | ||
88 | h ^= (h >> 10); | ||
89 | h ^= (h >> 6); | ||
90 | h &= DZ_HASHMASK(dz); | ||
91 | return *(dn_fib_idx_t *)&h; | ||
92 | } | ||
93 | |||
94 | static inline dn_fib_key_t dz_key(u16 dst, struct dn_zone *dz) | ||
95 | { | ||
96 | dn_fib_key_t k; | ||
97 | k.datum = dst & DZ_MASK(dz); | ||
98 | return k; | ||
99 | } | ||
100 | |||
101 | static inline struct dn_fib_node **dn_chain_p(dn_fib_key_t key, struct dn_zone *dz) | ||
102 | { | ||
103 | return &dz->dz_hash[dn_hash(key, dz).datum]; | ||
104 | } | ||
105 | |||
106 | static inline struct dn_fib_node *dz_chain(dn_fib_key_t key, struct dn_zone *dz) | ||
107 | { | ||
108 | return dz->dz_hash[dn_hash(key, dz).datum]; | ||
109 | } | ||
110 | |||
111 | static inline int dn_key_eq(dn_fib_key_t a, dn_fib_key_t b) | ||
112 | { | ||
113 | return a.datum == b.datum; | ||
114 | } | ||
115 | |||
116 | static inline int dn_key_leq(dn_fib_key_t a, dn_fib_key_t b) | ||
117 | { | ||
118 | return a.datum <= b.datum; | ||
119 | } | ||
120 | |||
121 | static inline void dn_rebuild_zone(struct dn_zone *dz, | ||
122 | struct dn_fib_node **old_ht, | ||
123 | int old_divisor) | ||
124 | { | ||
125 | int i; | ||
126 | struct dn_fib_node *f, **fp, *next; | ||
127 | |||
128 | for(i = 0; i < old_divisor; i++) { | ||
129 | for(f = old_ht[i]; f; f = f->fn_next) { | ||
130 | next = f->fn_next; | ||
131 | for(fp = dn_chain_p(f->fn_key, dz); | ||
132 | *fp && dn_key_leq((*fp)->fn_key, f->fn_key); | ||
133 | fp = &(*fp)->fn_next) | ||
134 | /* NOTHING */; | ||
135 | f->fn_next = *fp; | ||
136 | *fp = f; | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static void dn_rehash_zone(struct dn_zone *dz) | ||
142 | { | ||
143 | struct dn_fib_node **ht, **old_ht; | ||
144 | int old_divisor, new_divisor; | ||
145 | u32 new_hashmask; | ||
146 | |||
147 | old_divisor = dz->dz_divisor; | ||
148 | |||
149 | switch(old_divisor) { | ||
150 | case 16: | ||
151 | new_divisor = 256; | ||
152 | new_hashmask = 0xFF; | ||
153 | break; | ||
154 | default: | ||
155 | printk(KERN_DEBUG "DECnet: dn_rehash_zone: BUG! %d\n", old_divisor); | ||
156 | case 256: | ||
157 | new_divisor = 1024; | ||
158 | new_hashmask = 0x3FF; | ||
159 | break; | ||
160 | } | ||
161 | |||
162 | ht = kmalloc(new_divisor*sizeof(struct dn_fib_node*), GFP_KERNEL); | ||
163 | |||
164 | if (ht == NULL) | ||
165 | return; | ||
166 | |||
167 | memset(ht, 0, new_divisor*sizeof(struct dn_fib_node *)); | ||
168 | write_lock_bh(&dn_fib_tables_lock); | ||
169 | old_ht = dz->dz_hash; | ||
170 | dz->dz_hash = ht; | ||
171 | dz->dz_hashmask = new_hashmask; | ||
172 | dz->dz_divisor = new_divisor; | ||
173 | dn_rebuild_zone(dz, old_ht, old_divisor); | ||
174 | write_unlock_bh(&dn_fib_tables_lock); | ||
175 | kfree(old_ht); | ||
176 | } | ||
177 | |||
178 | static void dn_free_node(struct dn_fib_node *f) | ||
179 | { | ||
180 | dn_fib_release_info(DN_FIB_INFO(f)); | ||
181 | kmem_cache_free(dn_hash_kmem, f); | ||
182 | } | ||
183 | |||
184 | |||
185 | static struct dn_zone *dn_new_zone(struct dn_hash *table, int z) | ||
186 | { | ||
187 | int i; | ||
188 | struct dn_zone *dz = kmalloc(sizeof(struct dn_zone), GFP_KERNEL); | ||
189 | if (!dz) | ||
190 | return NULL; | ||
191 | |||
192 | memset(dz, 0, sizeof(struct dn_zone)); | ||
193 | if (z) { | ||
194 | dz->dz_divisor = 16; | ||
195 | dz->dz_hashmask = 0x0F; | ||
196 | } else { | ||
197 | dz->dz_divisor = 1; | ||
198 | dz->dz_hashmask = 0; | ||
199 | } | ||
200 | |||
201 | dz->dz_hash = kmalloc(dz->dz_divisor*sizeof(struct dn_fib_node *), GFP_KERNEL); | ||
202 | |||
203 | if (!dz->dz_hash) { | ||
204 | kfree(dz); | ||
205 | return NULL; | ||
206 | } | ||
207 | |||
208 | memset(dz->dz_hash, 0, dz->dz_divisor*sizeof(struct dn_fib_node*)); | ||
209 | dz->dz_order = z; | ||
210 | dz->dz_mask = dnet_make_mask(z); | ||
211 | |||
212 | for(i = z + 1; i <= 16; i++) | ||
213 | if (table->dh_zones[i]) | ||
214 | break; | ||
215 | |||
216 | write_lock_bh(&dn_fib_tables_lock); | ||
217 | if (i>16) { | ||
218 | dz->dz_next = table->dh_zone_list; | ||
219 | table->dh_zone_list = dz; | ||
220 | } else { | ||
221 | dz->dz_next = table->dh_zones[i]->dz_next; | ||
222 | table->dh_zones[i]->dz_next = dz; | ||
223 | } | ||
224 | table->dh_zones[z] = dz; | ||
225 | write_unlock_bh(&dn_fib_tables_lock); | ||
226 | return dz; | ||
227 | } | ||
228 | |||
229 | |||
230 | static int dn_fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct dn_kern_rta *rta, struct dn_fib_info *fi) | ||
231 | { | ||
232 | struct rtnexthop *nhp; | ||
233 | int nhlen; | ||
234 | |||
235 | if (rta->rta_priority && *rta->rta_priority != fi->fib_priority) | ||
236 | return 1; | ||
237 | |||
238 | if (rta->rta_oif || rta->rta_gw) { | ||
239 | if ((!rta->rta_oif || *rta->rta_oif == fi->fib_nh->nh_oif) && | ||
240 | (!rta->rta_gw || memcmp(rta->rta_gw, &fi->fib_nh->nh_gw, 2) == 0)) | ||
241 | return 0; | ||
242 | return 1; | ||
243 | } | ||
244 | |||
245 | if (rta->rta_mp == NULL) | ||
246 | return 0; | ||
247 | |||
248 | nhp = RTA_DATA(rta->rta_mp); | ||
249 | nhlen = RTA_PAYLOAD(rta->rta_mp); | ||
250 | |||
251 | for_nexthops(fi) { | ||
252 | int attrlen = nhlen - sizeof(struct rtnexthop); | ||
253 | dn_address gw; | ||
254 | |||
255 | if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0) | ||
256 | return -EINVAL; | ||
257 | if (nhp->rtnh_ifindex && nhp->rtnh_ifindex != nh->nh_oif) | ||
258 | return 1; | ||
259 | if (attrlen) { | ||
260 | gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY); | ||
261 | |||
262 | if (gw && gw != nh->nh_gw) | ||
263 | return 1; | ||
264 | } | ||
265 | nhp = RTNH_NEXT(nhp); | ||
266 | } endfor_nexthops(fi); | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, | ||
272 | u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, | ||
273 | struct dn_fib_info *fi) | ||
274 | { | ||
275 | struct rtmsg *rtm; | ||
276 | struct nlmsghdr *nlh; | ||
277 | unsigned char *b = skb->tail; | ||
278 | |||
279 | nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm)); | ||
280 | rtm = NLMSG_DATA(nlh); | ||
281 | rtm->rtm_family = AF_DECnet; | ||
282 | rtm->rtm_dst_len = dst_len; | ||
283 | rtm->rtm_src_len = 0; | ||
284 | rtm->rtm_tos = 0; | ||
285 | rtm->rtm_table = tb_id; | ||
286 | rtm->rtm_flags = fi->fib_flags; | ||
287 | rtm->rtm_scope = scope; | ||
288 | rtm->rtm_type = type; | ||
289 | if (rtm->rtm_dst_len) | ||
290 | RTA_PUT(skb, RTA_DST, 2, dst); | ||
291 | rtm->rtm_protocol = fi->fib_protocol; | ||
292 | if (fi->fib_priority) | ||
293 | RTA_PUT(skb, RTA_PRIORITY, 4, &fi->fib_priority); | ||
294 | if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0) | ||
295 | goto rtattr_failure; | ||
296 | if (fi->fib_nhs == 1) { | ||
297 | if (fi->fib_nh->nh_gw) | ||
298 | RTA_PUT(skb, RTA_GATEWAY, 2, &fi->fib_nh->nh_gw); | ||
299 | if (fi->fib_nh->nh_oif) | ||
300 | RTA_PUT(skb, RTA_OIF, sizeof(int), &fi->fib_nh->nh_oif); | ||
301 | } | ||
302 | if (fi->fib_nhs > 1) { | ||
303 | struct rtnexthop *nhp; | ||
304 | struct rtattr *mp_head; | ||
305 | if (skb_tailroom(skb) <= RTA_SPACE(0)) | ||
306 | goto rtattr_failure; | ||
307 | mp_head = (struct rtattr *)skb_put(skb, RTA_SPACE(0)); | ||
308 | |||
309 | for_nexthops(fi) { | ||
310 | if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4)) | ||
311 | goto rtattr_failure; | ||
312 | nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp))); | ||
313 | nhp->rtnh_flags = nh->nh_flags & 0xFF; | ||
314 | nhp->rtnh_hops = nh->nh_weight - 1; | ||
315 | nhp->rtnh_ifindex = nh->nh_oif; | ||
316 | if (nh->nh_gw) | ||
317 | RTA_PUT(skb, RTA_GATEWAY, 2, &nh->nh_gw); | ||
318 | nhp->rtnh_len = skb->tail - (unsigned char *)nhp; | ||
319 | } endfor_nexthops(fi); | ||
320 | mp_head->rta_type = RTA_MULTIPATH; | ||
321 | mp_head->rta_len = skb->tail - (u8*)mp_head; | ||
322 | } | ||
323 | |||
324 | nlh->nlmsg_len = skb->tail - b; | ||
325 | return skb->len; | ||
326 | |||
327 | |||
328 | nlmsg_failure: | ||
329 | rtattr_failure: | ||
330 | skb_trim(skb, b - skb->data); | ||
331 | return -1; | ||
332 | } | ||
333 | |||
334 | |||
335 | static void dn_rtmsg_fib(int event, struct dn_fib_node *f, int z, int tb_id, | ||
336 | struct nlmsghdr *nlh, struct netlink_skb_parms *req) | ||
337 | { | ||
338 | struct sk_buff *skb; | ||
339 | u32 pid = req ? req->pid : 0; | ||
340 | int size = NLMSG_SPACE(sizeof(struct rtmsg) + 256); | ||
341 | |||
342 | skb = alloc_skb(size, GFP_KERNEL); | ||
343 | if (!skb) | ||
344 | return; | ||
345 | |||
346 | if (dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, tb_id, | ||
347 | f->fn_type, f->fn_scope, &f->fn_key, z, | ||
348 | DN_FIB_INFO(f)) < 0) { | ||
349 | kfree_skb(skb); | ||
350 | return; | ||
351 | } | ||
352 | NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_ROUTE; | ||
353 | if (nlh->nlmsg_flags & NLM_F_ECHO) | ||
354 | atomic_inc(&skb->users); | ||
355 | netlink_broadcast(rtnl, skb, pid, RTMGRP_DECnet_ROUTE, GFP_KERNEL); | ||
356 | if (nlh->nlmsg_flags & NLM_F_ECHO) | ||
357 | netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT); | ||
358 | } | ||
359 | |||
360 | static __inline__ int dn_hash_dump_bucket(struct sk_buff *skb, | ||
361 | struct netlink_callback *cb, | ||
362 | struct dn_fib_table *tb, | ||
363 | struct dn_zone *dz, | ||
364 | struct dn_fib_node *f) | ||
365 | { | ||
366 | int i, s_i; | ||
367 | |||
368 | s_i = cb->args[3]; | ||
369 | for(i = 0; f; i++, f = f->fn_next) { | ||
370 | if (i < s_i) | ||
371 | continue; | ||
372 | if (f->fn_state & DN_S_ZOMBIE) | ||
373 | continue; | ||
374 | if (dn_fib_dump_info(skb, NETLINK_CB(cb->skb).pid, | ||
375 | cb->nlh->nlmsg_seq, | ||
376 | RTM_NEWROUTE, | ||
377 | tb->n, | ||
378 | (f->fn_state & DN_S_ZOMBIE) ? 0 : f->fn_type, | ||
379 | f->fn_scope, &f->fn_key, dz->dz_order, | ||
380 | f->fn_info) < 0) { | ||
381 | cb->args[3] = i; | ||
382 | return -1; | ||
383 | } | ||
384 | } | ||
385 | cb->args[3] = i; | ||
386 | return skb->len; | ||
387 | } | ||
388 | |||
389 | static __inline__ int dn_hash_dump_zone(struct sk_buff *skb, | ||
390 | struct netlink_callback *cb, | ||
391 | struct dn_fib_table *tb, | ||
392 | struct dn_zone *dz) | ||
393 | { | ||
394 | int h, s_h; | ||
395 | |||
396 | s_h = cb->args[2]; | ||
397 | for(h = 0; h < dz->dz_divisor; h++) { | ||
398 | if (h < s_h) | ||
399 | continue; | ||
400 | if (h > s_h) | ||
401 | memset(&cb->args[3], 0, sizeof(cb->args) - 3*sizeof(cb->args[0])); | ||
402 | if (dz->dz_hash == NULL || dz->dz_hash[h] == NULL) | ||
403 | continue; | ||
404 | if (dn_hash_dump_bucket(skb, cb, tb, dz, dz->dz_hash[h]) < 0) { | ||
405 | cb->args[2] = h; | ||
406 | return -1; | ||
407 | } | ||
408 | } | ||
409 | cb->args[2] = h; | ||
410 | return skb->len; | ||
411 | } | ||
412 | |||
413 | static int dn_fib_table_dump(struct dn_fib_table *tb, struct sk_buff *skb, | ||
414 | struct netlink_callback *cb) | ||
415 | { | ||
416 | int m, s_m; | ||
417 | struct dn_zone *dz; | ||
418 | struct dn_hash *table = (struct dn_hash *)tb->data; | ||
419 | |||
420 | s_m = cb->args[1]; | ||
421 | read_lock(&dn_fib_tables_lock); | ||
422 | for(dz = table->dh_zone_list, m = 0; dz; dz = dz->dz_next, m++) { | ||
423 | if (m < s_m) | ||
424 | continue; | ||
425 | if (m > s_m) | ||
426 | memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0])); | ||
427 | |||
428 | if (dn_hash_dump_zone(skb, cb, tb, dz) < 0) { | ||
429 | cb->args[1] = m; | ||
430 | read_unlock(&dn_fib_tables_lock); | ||
431 | return -1; | ||
432 | } | ||
433 | } | ||
434 | read_unlock(&dn_fib_tables_lock); | ||
435 | cb->args[1] = m; | ||
436 | |||
437 | return skb->len; | ||
438 | } | ||
439 | |||
440 | static int dn_fib_table_insert(struct dn_fib_table *tb, struct rtmsg *r, struct dn_kern_rta *rta, struct nlmsghdr *n, struct netlink_skb_parms *req) | ||
441 | { | ||
442 | struct dn_hash *table = (struct dn_hash *)tb->data; | ||
443 | struct dn_fib_node *new_f, *f, **fp, **del_fp; | ||
444 | struct dn_zone *dz; | ||
445 | struct dn_fib_info *fi; | ||
446 | int z = r->rtm_dst_len; | ||
447 | int type = r->rtm_type; | ||
448 | dn_fib_key_t key; | ||
449 | int err; | ||
450 | |||
451 | if (z > 16) | ||
452 | return -EINVAL; | ||
453 | |||
454 | dz = table->dh_zones[z]; | ||
455 | if (!dz && !(dz = dn_new_zone(table, z))) | ||
456 | return -ENOBUFS; | ||
457 | |||
458 | dz_key_0(key); | ||
459 | if (rta->rta_dst) { | ||
460 | dn_address dst; | ||
461 | memcpy(&dst, rta->rta_dst, 2); | ||
462 | if (dst & ~DZ_MASK(dz)) | ||
463 | return -EINVAL; | ||
464 | key = dz_key(dst, dz); | ||
465 | } | ||
466 | |||
467 | if ((fi = dn_fib_create_info(r, rta, n, &err)) == NULL) | ||
468 | return err; | ||
469 | |||
470 | if (dz->dz_nent > (dz->dz_divisor << 2) && | ||
471 | dz->dz_divisor > DN_MAX_DIVISOR && | ||
472 | (z==16 || (1<<z) > dz->dz_divisor)) | ||
473 | dn_rehash_zone(dz); | ||
474 | |||
475 | fp = dn_chain_p(key, dz); | ||
476 | |||
477 | DN_FIB_SCAN(f, fp) { | ||
478 | if (dn_key_leq(key, f->fn_key)) | ||
479 | break; | ||
480 | } | ||
481 | |||
482 | del_fp = NULL; | ||
483 | |||
484 | if (f && (f->fn_state & DN_S_ZOMBIE) && | ||
485 | dn_key_eq(f->fn_key, key)) { | ||
486 | del_fp = fp; | ||
487 | fp = &f->fn_next; | ||
488 | f = *fp; | ||
489 | goto create; | ||
490 | } | ||
491 | |||
492 | DN_FIB_SCAN_KEY(f, fp, key) { | ||
493 | if (fi->fib_priority <= DN_FIB_INFO(f)->fib_priority) | ||
494 | break; | ||
495 | } | ||
496 | |||
497 | if (f && dn_key_eq(f->fn_key, key) && | ||
498 | fi->fib_priority == DN_FIB_INFO(f)->fib_priority) { | ||
499 | struct dn_fib_node **ins_fp; | ||
500 | |||
501 | err = -EEXIST; | ||
502 | if (n->nlmsg_flags & NLM_F_EXCL) | ||
503 | goto out; | ||
504 | |||
505 | if (n->nlmsg_flags & NLM_F_REPLACE) { | ||
506 | del_fp = fp; | ||
507 | fp = &f->fn_next; | ||
508 | f = *fp; | ||
509 | goto replace; | ||
510 | } | ||
511 | |||
512 | ins_fp = fp; | ||
513 | err = -EEXIST; | ||
514 | |||
515 | DN_FIB_SCAN_KEY(f, fp, key) { | ||
516 | if (fi->fib_priority != DN_FIB_INFO(f)->fib_priority) | ||
517 | break; | ||
518 | if (f->fn_type == type && f->fn_scope == r->rtm_scope | ||
519 | && DN_FIB_INFO(f) == fi) | ||
520 | goto out; | ||
521 | } | ||
522 | |||
523 | if (!(n->nlmsg_flags & NLM_F_APPEND)) { | ||
524 | fp = ins_fp; | ||
525 | f = *fp; | ||
526 | } | ||
527 | } | ||
528 | |||
529 | create: | ||
530 | err = -ENOENT; | ||
531 | if (!(n->nlmsg_flags & NLM_F_CREATE)) | ||
532 | goto out; | ||
533 | |||
534 | replace: | ||
535 | err = -ENOBUFS; | ||
536 | new_f = kmem_cache_alloc(dn_hash_kmem, SLAB_KERNEL); | ||
537 | if (new_f == NULL) | ||
538 | goto out; | ||
539 | |||
540 | memset(new_f, 0, sizeof(struct dn_fib_node)); | ||
541 | |||
542 | new_f->fn_key = key; | ||
543 | new_f->fn_type = type; | ||
544 | new_f->fn_scope = r->rtm_scope; | ||
545 | DN_FIB_INFO(new_f) = fi; | ||
546 | |||
547 | new_f->fn_next = f; | ||
548 | write_lock_bh(&dn_fib_tables_lock); | ||
549 | *fp = new_f; | ||
550 | write_unlock_bh(&dn_fib_tables_lock); | ||
551 | dz->dz_nent++; | ||
552 | |||
553 | if (del_fp) { | ||
554 | f = *del_fp; | ||
555 | write_lock_bh(&dn_fib_tables_lock); | ||
556 | *del_fp = f->fn_next; | ||
557 | write_unlock_bh(&dn_fib_tables_lock); | ||
558 | |||
559 | if (!(f->fn_state & DN_S_ZOMBIE)) | ||
560 | dn_rtmsg_fib(RTM_DELROUTE, f, z, tb->n, n, req); | ||
561 | if (f->fn_state & DN_S_ACCESSED) | ||
562 | dn_rt_cache_flush(-1); | ||
563 | dn_free_node(f); | ||
564 | dz->dz_nent--; | ||
565 | } else { | ||
566 | dn_rt_cache_flush(-1); | ||
567 | } | ||
568 | |||
569 | dn_rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->n, n, req); | ||
570 | |||
571 | return 0; | ||
572 | out: | ||
573 | dn_fib_release_info(fi); | ||
574 | return err; | ||
575 | } | ||
576 | |||
577 | |||
578 | static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct dn_kern_rta *rta, struct nlmsghdr *n, struct netlink_skb_parms *req) | ||
579 | { | ||
580 | struct dn_hash *table = (struct dn_hash*)tb->data; | ||
581 | struct dn_fib_node **fp, **del_fp, *f; | ||
582 | int z = r->rtm_dst_len; | ||
583 | struct dn_zone *dz; | ||
584 | dn_fib_key_t key; | ||
585 | int matched; | ||
586 | |||
587 | |||
588 | if (z > 16) | ||
589 | return -EINVAL; | ||
590 | |||
591 | if ((dz = table->dh_zones[z]) == NULL) | ||
592 | return -ESRCH; | ||
593 | |||
594 | dz_key_0(key); | ||
595 | if (rta->rta_dst) { | ||
596 | dn_address dst; | ||
597 | memcpy(&dst, rta->rta_dst, 2); | ||
598 | if (dst & ~DZ_MASK(dz)) | ||
599 | return -EINVAL; | ||
600 | key = dz_key(dst, dz); | ||
601 | } | ||
602 | |||
603 | fp = dn_chain_p(key, dz); | ||
604 | |||
605 | DN_FIB_SCAN(f, fp) { | ||
606 | if (dn_key_eq(f->fn_key, key)) | ||
607 | break; | ||
608 | if (dn_key_leq(key, f->fn_key)) | ||
609 | return -ESRCH; | ||
610 | } | ||
611 | |||
612 | matched = 0; | ||
613 | del_fp = NULL; | ||
614 | DN_FIB_SCAN_KEY(f, fp, key) { | ||
615 | struct dn_fib_info *fi = DN_FIB_INFO(f); | ||
616 | |||
617 | if (f->fn_state & DN_S_ZOMBIE) | ||
618 | return -ESRCH; | ||
619 | |||
620 | matched++; | ||
621 | |||
622 | if (del_fp == NULL && | ||
623 | (!r->rtm_type || f->fn_type == r->rtm_type) && | ||
624 | (r->rtm_scope == RT_SCOPE_NOWHERE || f->fn_scope == r->rtm_scope) && | ||
625 | (!r->rtm_protocol || | ||
626 | fi->fib_protocol == r->rtm_protocol) && | ||
627 | dn_fib_nh_match(r, n, rta, fi) == 0) | ||
628 | del_fp = fp; | ||
629 | } | ||
630 | |||
631 | if (del_fp) { | ||
632 | f = *del_fp; | ||
633 | dn_rtmsg_fib(RTM_DELROUTE, f, z, tb->n, n, req); | ||
634 | |||
635 | if (matched != 1) { | ||
636 | write_lock_bh(&dn_fib_tables_lock); | ||
637 | *del_fp = f->fn_next; | ||
638 | write_unlock_bh(&dn_fib_tables_lock); | ||
639 | |||
640 | if (f->fn_state & DN_S_ACCESSED) | ||
641 | dn_rt_cache_flush(-1); | ||
642 | dn_free_node(f); | ||
643 | dz->dz_nent--; | ||
644 | } else { | ||
645 | f->fn_state |= DN_S_ZOMBIE; | ||
646 | if (f->fn_state & DN_S_ACCESSED) { | ||
647 | f->fn_state &= ~DN_S_ACCESSED; | ||
648 | dn_rt_cache_flush(-1); | ||
649 | } | ||
650 | if (++dn_fib_hash_zombies > 128) | ||
651 | dn_fib_flush(); | ||
652 | } | ||
653 | |||
654 | return 0; | ||
655 | } | ||
656 | |||
657 | return -ESRCH; | ||
658 | } | ||
659 | |||
660 | static inline int dn_flush_list(struct dn_fib_node **fp, int z, struct dn_hash *table) | ||
661 | { | ||
662 | int found = 0; | ||
663 | struct dn_fib_node *f; | ||
664 | |||
665 | while((f = *fp) != NULL) { | ||
666 | struct dn_fib_info *fi = DN_FIB_INFO(f); | ||
667 | |||
668 | if (fi && ((f->fn_state & DN_S_ZOMBIE) || (fi->fib_flags & RTNH_F_DEAD))) { | ||
669 | write_lock_bh(&dn_fib_tables_lock); | ||
670 | *fp = f->fn_next; | ||
671 | write_unlock_bh(&dn_fib_tables_lock); | ||
672 | |||
673 | dn_free_node(f); | ||
674 | found++; | ||
675 | continue; | ||
676 | } | ||
677 | fp = &f->fn_next; | ||
678 | } | ||
679 | |||
680 | return found; | ||
681 | } | ||
682 | |||
683 | static int dn_fib_table_flush(struct dn_fib_table *tb) | ||
684 | { | ||
685 | struct dn_hash *table = (struct dn_hash *)tb->data; | ||
686 | struct dn_zone *dz; | ||
687 | int found = 0; | ||
688 | |||
689 | dn_fib_hash_zombies = 0; | ||
690 | for(dz = table->dh_zone_list; dz; dz = dz->dz_next) { | ||
691 | int i; | ||
692 | int tmp = 0; | ||
693 | for(i = dz->dz_divisor-1; i >= 0; i--) | ||
694 | tmp += dn_flush_list(&dz->dz_hash[i], dz->dz_order, table); | ||
695 | dz->dz_nent -= tmp; | ||
696 | found += tmp; | ||
697 | } | ||
698 | |||
699 | return found; | ||
700 | } | ||
701 | |||
702 | static int dn_fib_table_lookup(struct dn_fib_table *tb, const struct flowi *flp, struct dn_fib_res *res) | ||
703 | { | ||
704 | int err; | ||
705 | struct dn_zone *dz; | ||
706 | struct dn_hash *t = (struct dn_hash *)tb->data; | ||
707 | |||
708 | read_lock(&dn_fib_tables_lock); | ||
709 | for(dz = t->dh_zone_list; dz; dz = dz->dz_next) { | ||
710 | struct dn_fib_node *f; | ||
711 | dn_fib_key_t k = dz_key(flp->fld_dst, dz); | ||
712 | |||
713 | for(f = dz_chain(k, dz); f; f = f->fn_next) { | ||
714 | if (!dn_key_eq(k, f->fn_key)) { | ||
715 | if (dn_key_leq(k, f->fn_key)) | ||
716 | break; | ||
717 | else | ||
718 | continue; | ||
719 | } | ||
720 | |||
721 | f->fn_state |= DN_S_ACCESSED; | ||
722 | |||
723 | if (f->fn_state&DN_S_ZOMBIE) | ||
724 | continue; | ||
725 | |||
726 | if (f->fn_scope < flp->fld_scope) | ||
727 | continue; | ||
728 | |||
729 | err = dn_fib_semantic_match(f->fn_type, DN_FIB_INFO(f), flp, res); | ||
730 | |||
731 | if (err == 0) { | ||
732 | res->type = f->fn_type; | ||
733 | res->scope = f->fn_scope; | ||
734 | res->prefixlen = dz->dz_order; | ||
735 | goto out; | ||
736 | } | ||
737 | if (err < 0) | ||
738 | goto out; | ||
739 | } | ||
740 | } | ||
741 | err = 1; | ||
742 | out: | ||
743 | read_unlock(&dn_fib_tables_lock); | ||
744 | return err; | ||
745 | } | ||
746 | |||
747 | |||
748 | struct dn_fib_table *dn_fib_get_table(int n, int create) | ||
749 | { | ||
750 | struct dn_fib_table *t; | ||
751 | |||
752 | if (n < RT_TABLE_MIN) | ||
753 | return NULL; | ||
754 | |||
755 | if (n > RT_TABLE_MAX) | ||
756 | return NULL; | ||
757 | |||
758 | if (dn_fib_tables[n]) | ||
759 | return dn_fib_tables[n]; | ||
760 | |||
761 | if (!create) | ||
762 | return NULL; | ||
763 | |||
764 | if (in_interrupt() && net_ratelimit()) { | ||
765 | printk(KERN_DEBUG "DECnet: BUG! Attempt to create routing table from interrupt\n"); | ||
766 | return NULL; | ||
767 | } | ||
768 | if ((t = kmalloc(sizeof(struct dn_fib_table) + sizeof(struct dn_hash), GFP_KERNEL)) == NULL) | ||
769 | return NULL; | ||
770 | |||
771 | memset(t, 0, sizeof(struct dn_fib_table)); | ||
772 | |||
773 | t->n = n; | ||
774 | t->insert = dn_fib_table_insert; | ||
775 | t->delete = dn_fib_table_delete; | ||
776 | t->lookup = dn_fib_table_lookup; | ||
777 | t->flush = dn_fib_table_flush; | ||
778 | t->dump = dn_fib_table_dump; | ||
779 | memset(t->data, 0, sizeof(struct dn_hash)); | ||
780 | dn_fib_tables[n] = t; | ||
781 | |||
782 | return t; | ||
783 | } | ||
784 | |||
785 | static void dn_fib_del_tree(int n) | ||
786 | { | ||
787 | struct dn_fib_table *t; | ||
788 | |||
789 | write_lock(&dn_fib_tables_lock); | ||
790 | t = dn_fib_tables[n]; | ||
791 | dn_fib_tables[n] = NULL; | ||
792 | write_unlock(&dn_fib_tables_lock); | ||
793 | |||
794 | if (t) { | ||
795 | kfree(t); | ||
796 | } | ||
797 | } | ||
798 | |||
799 | struct dn_fib_table *dn_fib_empty_table(void) | ||
800 | { | ||
801 | int id; | ||
802 | |||
803 | for(id = RT_TABLE_MIN; id <= RT_TABLE_MAX; id++) | ||
804 | if (dn_fib_tables[id] == NULL) | ||
805 | return dn_fib_get_table(id, 1); | ||
806 | return NULL; | ||
807 | } | ||
808 | |||
809 | void __init dn_fib_table_init(void) | ||
810 | { | ||
811 | dn_hash_kmem = kmem_cache_create("dn_fib_info_cache", | ||
812 | sizeof(struct dn_fib_info), | ||
813 | 0, SLAB_HWCACHE_ALIGN, | ||
814 | NULL, NULL); | ||
815 | } | ||
816 | |||
817 | void __exit dn_fib_table_cleanup(void) | ||
818 | { | ||
819 | int i; | ||
820 | |||
821 | for (i = RT_TABLE_MIN; i <= RT_TABLE_MAX; ++i) | ||
822 | dn_fib_del_tree(i); | ||
823 | |||
824 | return; | ||
825 | } | ||
diff --git a/net/decnet/dn_timer.c b/net/decnet/dn_timer.c new file mode 100644 index 000000000000..09825711d58a --- /dev/null +++ b/net/decnet/dn_timer.c | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet Socket Timer Functions | ||
7 | * | ||
8 | * Author: Steve Whitehouse <SteveW@ACM.org> | ||
9 | * | ||
10 | * | ||
11 | * Changes: | ||
12 | * Steve Whitehouse : Made keepalive timer part of the same | ||
13 | * timer idea. | ||
14 | * Steve Whitehouse : Added checks for sk->sock_readers | ||
15 | * David S. Miller : New socket locking | ||
16 | * Steve Whitehouse : Timer grabs socket ref. | ||
17 | */ | ||
18 | #include <linux/net.h> | ||
19 | #include <linux/socket.h> | ||
20 | #include <linux/skbuff.h> | ||
21 | #include <linux/netdevice.h> | ||
22 | #include <linux/timer.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <net/sock.h> | ||
25 | #include <asm/atomic.h> | ||
26 | #include <net/flow.h> | ||
27 | #include <net/dn.h> | ||
28 | |||
29 | /* | ||
30 | * Slow timer is for everything else (n * 500mS) | ||
31 | */ | ||
32 | |||
33 | #define SLOW_INTERVAL (HZ/2) | ||
34 | |||
35 | static void dn_slow_timer(unsigned long arg); | ||
36 | |||
37 | void dn_start_slow_timer(struct sock *sk) | ||
38 | { | ||
39 | sk->sk_timer.expires = jiffies + SLOW_INTERVAL; | ||
40 | sk->sk_timer.function = dn_slow_timer; | ||
41 | sk->sk_timer.data = (unsigned long)sk; | ||
42 | |||
43 | add_timer(&sk->sk_timer); | ||
44 | } | ||
45 | |||
46 | void dn_stop_slow_timer(struct sock *sk) | ||
47 | { | ||
48 | del_timer(&sk->sk_timer); | ||
49 | } | ||
50 | |||
51 | static void dn_slow_timer(unsigned long arg) | ||
52 | { | ||
53 | struct sock *sk = (struct sock *)arg; | ||
54 | struct dn_scp *scp = DN_SK(sk); | ||
55 | |||
56 | sock_hold(sk); | ||
57 | bh_lock_sock(sk); | ||
58 | |||
59 | if (sock_owned_by_user(sk)) { | ||
60 | sk->sk_timer.expires = jiffies + HZ / 10; | ||
61 | add_timer(&sk->sk_timer); | ||
62 | goto out; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * The persist timer is the standard slow timer used for retransmits | ||
67 | * in both connection establishment and disconnection as well as | ||
68 | * in the RUN state. The different states are catered for by changing | ||
69 | * the function pointer in the socket. Setting the timer to a value | ||
70 | * of zero turns it off. We allow the persist_fxn to turn the | ||
71 | * timer off in a permant way by returning non-zero, so that | ||
72 | * timer based routines may remove sockets. This is why we have a | ||
73 | * sock_hold()/sock_put() around the timer to prevent the socket | ||
74 | * going away in the middle. | ||
75 | */ | ||
76 | if (scp->persist && scp->persist_fxn) { | ||
77 | if (scp->persist <= SLOW_INTERVAL) { | ||
78 | scp->persist = 0; | ||
79 | |||
80 | if (scp->persist_fxn(sk)) | ||
81 | goto out; | ||
82 | } else { | ||
83 | scp->persist -= SLOW_INTERVAL; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * Check for keepalive timeout. After the other timer 'cos if | ||
89 | * the previous timer caused a retransmit, we don't need to | ||
90 | * do this. scp->stamp is the last time that we sent a packet. | ||
91 | * The keepalive function sends a link service packet to the | ||
92 | * other end. If it remains unacknowledged, the standard | ||
93 | * socket timers will eventually shut the socket down. Each | ||
94 | * time we do this, scp->stamp will be updated, thus | ||
95 | * we won't try and send another until scp->keepalive has passed | ||
96 | * since the last successful transmission. | ||
97 | */ | ||
98 | if (scp->keepalive && scp->keepalive_fxn && (scp->state == DN_RUN)) { | ||
99 | if ((jiffies - scp->stamp) >= scp->keepalive) | ||
100 | scp->keepalive_fxn(sk); | ||
101 | } | ||
102 | |||
103 | sk->sk_timer.expires = jiffies + SLOW_INTERVAL; | ||
104 | |||
105 | add_timer(&sk->sk_timer); | ||
106 | out: | ||
107 | bh_unlock_sock(sk); | ||
108 | sock_put(sk); | ||
109 | } | ||
diff --git a/net/decnet/netfilter/Kconfig b/net/decnet/netfilter/Kconfig new file mode 100644 index 000000000000..ecdb3f9f14ca --- /dev/null +++ b/net/decnet/netfilter/Kconfig | |||
@@ -0,0 +1,15 @@ | |||
1 | # | ||
2 | # DECnet netfilter configuration | ||
3 | # | ||
4 | |||
5 | menu "DECnet: Netfilter Configuration" | ||
6 | depends on DECNET && NETFILTER && EXPERIMENTAL | ||
7 | |||
8 | config DECNET_NF_GRABULATOR | ||
9 | tristate "Routing message grabulator (for userland routing daemon)" | ||
10 | help | ||
11 | Enable this module if you want to use the userland DECnet routing | ||
12 | daemon. You will also need to enable routing support for DECnet | ||
13 | unless you just want to monitor routing messages from other nodes. | ||
14 | |||
15 | endmenu | ||
diff --git a/net/decnet/netfilter/Makefile b/net/decnet/netfilter/Makefile new file mode 100644 index 000000000000..255c1ae9daeb --- /dev/null +++ b/net/decnet/netfilter/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for DECnet netfilter modules | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_DECNET_NF_GRABULATOR) += dn_rtmsg.o | ||
6 | |||
diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c new file mode 100644 index 000000000000..f86a6259fd12 --- /dev/null +++ b/net/decnet/netfilter/dn_rtmsg.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet Routing Message Grabulator | ||
7 | * | ||
8 | * (C) 2000 ChyGwyn Limited - http://www.chygwyn.com/ | ||
9 | * This code may be copied under the GPL v.2 or at your option | ||
10 | * any later version. | ||
11 | * | ||
12 | * Author: Steven Whitehouse <steve@chygwyn.com> | ||
13 | * | ||
14 | */ | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/skbuff.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/netfilter.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/netlink.h> | ||
22 | |||
23 | #include <net/sock.h> | ||
24 | #include <net/flow.h> | ||
25 | #include <net/dn.h> | ||
26 | #include <net/dn_route.h> | ||
27 | |||
28 | #include <linux/netfilter_decnet.h> | ||
29 | |||
30 | static struct sock *dnrmg = NULL; | ||
31 | |||
32 | |||
33 | static struct sk_buff *dnrmg_build_message(struct sk_buff *rt_skb, int *errp) | ||
34 | { | ||
35 | struct sk_buff *skb = NULL; | ||
36 | size_t size; | ||
37 | unsigned char *old_tail; | ||
38 | struct nlmsghdr *nlh; | ||
39 | unsigned char *ptr; | ||
40 | struct nf_dn_rtmsg *rtm; | ||
41 | |||
42 | size = NLMSG_SPACE(rt_skb->len); | ||
43 | size += NLMSG_ALIGN(sizeof(struct nf_dn_rtmsg)); | ||
44 | skb = alloc_skb(size, GFP_ATOMIC); | ||
45 | if (!skb) | ||
46 | goto nlmsg_failure; | ||
47 | old_tail = skb->tail; | ||
48 | nlh = NLMSG_PUT(skb, 0, 0, 0, size - sizeof(*nlh)); | ||
49 | rtm = (struct nf_dn_rtmsg *)NLMSG_DATA(nlh); | ||
50 | rtm->nfdn_ifindex = rt_skb->dev->ifindex; | ||
51 | ptr = NFDN_RTMSG(rtm); | ||
52 | memcpy(ptr, rt_skb->data, rt_skb->len); | ||
53 | nlh->nlmsg_len = skb->tail - old_tail; | ||
54 | return skb; | ||
55 | |||
56 | nlmsg_failure: | ||
57 | if (skb) | ||
58 | kfree_skb(skb); | ||
59 | *errp = -ENOMEM; | ||
60 | if (net_ratelimit()) | ||
61 | printk(KERN_ERR "dn_rtmsg: error creating netlink message\n"); | ||
62 | return NULL; | ||
63 | } | ||
64 | |||
65 | static void dnrmg_send_peer(struct sk_buff *skb) | ||
66 | { | ||
67 | struct sk_buff *skb2; | ||
68 | int status = 0; | ||
69 | int group = 0; | ||
70 | unsigned char flags = *skb->data; | ||
71 | |||
72 | switch(flags & DN_RT_CNTL_MSK) { | ||
73 | case DN_RT_PKT_L1RT: | ||
74 | group = DNRMG_L1_GROUP; | ||
75 | break; | ||
76 | case DN_RT_PKT_L2RT: | ||
77 | group = DNRMG_L2_GROUP; | ||
78 | break; | ||
79 | default: | ||
80 | return; | ||
81 | } | ||
82 | |||
83 | skb2 = dnrmg_build_message(skb, &status); | ||
84 | if (skb2 == NULL) | ||
85 | return; | ||
86 | NETLINK_CB(skb2).dst_groups = group; | ||
87 | netlink_broadcast(dnrmg, skb2, 0, group, GFP_ATOMIC); | ||
88 | } | ||
89 | |||
90 | |||
91 | static unsigned int dnrmg_hook(unsigned int hook, | ||
92 | struct sk_buff **pskb, | ||
93 | const struct net_device *in, | ||
94 | const struct net_device *out, | ||
95 | int (*okfn)(struct sk_buff *)) | ||
96 | { | ||
97 | dnrmg_send_peer(*pskb); | ||
98 | return NF_ACCEPT; | ||
99 | } | ||
100 | |||
101 | |||
102 | #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) | ||
103 | |||
104 | static inline void dnrmg_receive_user_skb(struct sk_buff *skb) | ||
105 | { | ||
106 | struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; | ||
107 | |||
108 | if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) | ||
109 | return; | ||
110 | |||
111 | if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) | ||
112 | RCV_SKB_FAIL(-EPERM); | ||
113 | |||
114 | /* Eventually we might send routing messages too */ | ||
115 | |||
116 | RCV_SKB_FAIL(-EINVAL); | ||
117 | } | ||
118 | |||
119 | static void dnrmg_receive_user_sk(struct sock *sk, int len) | ||
120 | { | ||
121 | struct sk_buff *skb; | ||
122 | |||
123 | while((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { | ||
124 | dnrmg_receive_user_skb(skb); | ||
125 | kfree_skb(skb); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | static struct nf_hook_ops dnrmg_ops = { | ||
130 | .hook = dnrmg_hook, | ||
131 | .pf = PF_DECnet, | ||
132 | .hooknum = NF_DN_ROUTE, | ||
133 | .priority = NF_DN_PRI_DNRTMSG, | ||
134 | }; | ||
135 | |||
136 | static int __init init(void) | ||
137 | { | ||
138 | int rv = 0; | ||
139 | |||
140 | dnrmg = netlink_kernel_create(NETLINK_DNRTMSG, dnrmg_receive_user_sk); | ||
141 | if (dnrmg == NULL) { | ||
142 | printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket"); | ||
143 | return -ENOMEM; | ||
144 | } | ||
145 | |||
146 | rv = nf_register_hook(&dnrmg_ops); | ||
147 | if (rv) { | ||
148 | sock_release(dnrmg->sk_socket); | ||
149 | } | ||
150 | |||
151 | return rv; | ||
152 | } | ||
153 | |||
154 | static void __exit fini(void) | ||
155 | { | ||
156 | nf_unregister_hook(&dnrmg_ops); | ||
157 | sock_release(dnrmg->sk_socket); | ||
158 | } | ||
159 | |||
160 | |||
161 | MODULE_DESCRIPTION("DECnet Routing Message Grabulator"); | ||
162 | MODULE_AUTHOR("Steven Whitehouse <steve@chygwyn.com>"); | ||
163 | MODULE_LICENSE("GPL"); | ||
164 | |||
165 | module_init(init); | ||
166 | module_exit(fini); | ||
167 | |||
diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c new file mode 100644 index 000000000000..02bca49cb508 --- /dev/null +++ b/net/decnet/sysctl_net_decnet.c | |||
@@ -0,0 +1,480 @@ | |||
1 | /* | ||
2 | * DECnet An implementation of the DECnet protocol suite for the LINUX | ||
3 | * operating system. DECnet is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * DECnet sysctl support functions | ||
7 | * | ||
8 | * Author: Steve Whitehouse <SteveW@ACM.org> | ||
9 | * | ||
10 | * | ||
11 | * Changes: | ||
12 | * Steve Whitehouse - C99 changes and default device handling | ||
13 | * | ||
14 | */ | ||
15 | #include <linux/config.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/sysctl.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/netdevice.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <net/neighbour.h> | ||
22 | #include <net/dst.h> | ||
23 | #include <net/flow.h> | ||
24 | |||
25 | #include <asm/uaccess.h> | ||
26 | |||
27 | #include <net/dn.h> | ||
28 | #include <net/dn_dev.h> | ||
29 | #include <net/dn_route.h> | ||
30 | |||
31 | |||
32 | int decnet_debug_level; | ||
33 | int decnet_time_wait = 30; | ||
34 | int decnet_dn_count = 1; | ||
35 | int decnet_di_count = 3; | ||
36 | int decnet_dr_count = 3; | ||
37 | int decnet_log_martians = 1; | ||
38 | int decnet_no_fc_max_cwnd = NSP_MIN_WINDOW; | ||
39 | |||
40 | #ifdef CONFIG_SYSCTL | ||
41 | extern int decnet_dst_gc_interval; | ||
42 | static int min_decnet_time_wait[] = { 5 }; | ||
43 | static int max_decnet_time_wait[] = { 600 }; | ||
44 | static int min_state_count[] = { 1 }; | ||
45 | static int max_state_count[] = { NSP_MAXRXTSHIFT }; | ||
46 | static int min_decnet_dst_gc_interval[] = { 1 }; | ||
47 | static int max_decnet_dst_gc_interval[] = { 60 }; | ||
48 | static int min_decnet_no_fc_max_cwnd[] = { NSP_MIN_WINDOW }; | ||
49 | static int max_decnet_no_fc_max_cwnd[] = { NSP_MAX_WINDOW }; | ||
50 | static char node_name[7] = "???"; | ||
51 | |||
52 | static struct ctl_table_header *dn_table_header = NULL; | ||
53 | |||
54 | /* | ||
55 | * ctype.h :-) | ||
56 | */ | ||
57 | #define ISNUM(x) (((x) >= '0') && ((x) <= '9')) | ||
58 | #define ISLOWER(x) (((x) >= 'a') && ((x) <= 'z')) | ||
59 | #define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z')) | ||
60 | #define ISALPHA(x) (ISLOWER(x) || ISUPPER(x)) | ||
61 | #define INVALID_END_CHAR(x) (ISNUM(x) || ISALPHA(x)) | ||
62 | |||
63 | static void strip_it(char *str) | ||
64 | { | ||
65 | for(;;) { | ||
66 | switch(*str) { | ||
67 | case ' ': | ||
68 | case '\n': | ||
69 | case '\r': | ||
70 | case ':': | ||
71 | *str = 0; | ||
72 | case 0: | ||
73 | return; | ||
74 | } | ||
75 | str++; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * Simple routine to parse an ascii DECnet address | ||
81 | * into a network order address. | ||
82 | */ | ||
83 | static int parse_addr(dn_address *addr, char *str) | ||
84 | { | ||
85 | dn_address area, node; | ||
86 | |||
87 | while(*str && !ISNUM(*str)) str++; | ||
88 | |||
89 | if (*str == 0) | ||
90 | return -1; | ||
91 | |||
92 | area = (*str++ - '0'); | ||
93 | if (ISNUM(*str)) { | ||
94 | area *= 10; | ||
95 | area += (*str++ - '0'); | ||
96 | } | ||
97 | |||
98 | if (*str++ != '.') | ||
99 | return -1; | ||
100 | |||
101 | if (!ISNUM(*str)) | ||
102 | return -1; | ||
103 | |||
104 | node = *str++ - '0'; | ||
105 | if (ISNUM(*str)) { | ||
106 | node *= 10; | ||
107 | node += (*str++ - '0'); | ||
108 | } | ||
109 | if (ISNUM(*str)) { | ||
110 | node *= 10; | ||
111 | node += (*str++ - '0'); | ||
112 | } | ||
113 | if (ISNUM(*str)) { | ||
114 | node *= 10; | ||
115 | node += (*str++ - '0'); | ||
116 | } | ||
117 | |||
118 | if ((node > 1023) || (area > 63)) | ||
119 | return -1; | ||
120 | |||
121 | if (INVALID_END_CHAR(*str)) | ||
122 | return -1; | ||
123 | |||
124 | *addr = dn_htons((area << 10) | node); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | |||
130 | static int dn_node_address_strategy(ctl_table *table, int __user *name, int nlen, | ||
131 | void __user *oldval, size_t __user *oldlenp, | ||
132 | void __user *newval, size_t newlen, | ||
133 | void **context) | ||
134 | { | ||
135 | size_t len; | ||
136 | dn_address addr; | ||
137 | |||
138 | if (oldval && oldlenp) { | ||
139 | if (get_user(len, oldlenp)) | ||
140 | return -EFAULT; | ||
141 | if (len) { | ||
142 | if (len != sizeof(unsigned short)) | ||
143 | return -EINVAL; | ||
144 | if (put_user(decnet_address, (unsigned short __user *)oldval)) | ||
145 | return -EFAULT; | ||
146 | } | ||
147 | } | ||
148 | if (newval && newlen) { | ||
149 | if (newlen != sizeof(unsigned short)) | ||
150 | return -EINVAL; | ||
151 | if (get_user(addr, (unsigned short __user *)newval)) | ||
152 | return -EFAULT; | ||
153 | |||
154 | dn_dev_devices_off(); | ||
155 | |||
156 | decnet_address = addr; | ||
157 | |||
158 | dn_dev_devices_on(); | ||
159 | } | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int dn_node_address_handler(ctl_table *table, int write, | ||
164 | struct file *filp, | ||
165 | void __user *buffer, | ||
166 | size_t *lenp, loff_t *ppos) | ||
167 | { | ||
168 | char addr[DN_ASCBUF_LEN]; | ||
169 | size_t len; | ||
170 | dn_address dnaddr; | ||
171 | |||
172 | if (!*lenp || (*ppos && !write)) { | ||
173 | *lenp = 0; | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | if (write) { | ||
178 | int len = (*lenp < DN_ASCBUF_LEN) ? *lenp : (DN_ASCBUF_LEN-1); | ||
179 | |||
180 | if (copy_from_user(addr, buffer, len)) | ||
181 | return -EFAULT; | ||
182 | |||
183 | addr[len] = 0; | ||
184 | strip_it(addr); | ||
185 | |||
186 | if (parse_addr(&dnaddr, addr)) | ||
187 | return -EINVAL; | ||
188 | |||
189 | dn_dev_devices_off(); | ||
190 | |||
191 | decnet_address = dnaddr; | ||
192 | |||
193 | dn_dev_devices_on(); | ||
194 | |||
195 | *ppos += len; | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | dn_addr2asc(dn_ntohs(decnet_address), addr); | ||
201 | len = strlen(addr); | ||
202 | addr[len++] = '\n'; | ||
203 | |||
204 | if (len > *lenp) len = *lenp; | ||
205 | |||
206 | if (copy_to_user(buffer, addr, len)) | ||
207 | return -EFAULT; | ||
208 | |||
209 | *lenp = len; | ||
210 | *ppos += len; | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | |||
216 | static int dn_def_dev_strategy(ctl_table *table, int __user *name, int nlen, | ||
217 | void __user *oldval, size_t __user *oldlenp, | ||
218 | void __user *newval, size_t newlen, | ||
219 | void **context) | ||
220 | { | ||
221 | size_t len; | ||
222 | struct net_device *dev; | ||
223 | char devname[17]; | ||
224 | size_t namel; | ||
225 | int rv = 0; | ||
226 | |||
227 | devname[0] = 0; | ||
228 | |||
229 | if (oldval && oldlenp) { | ||
230 | if (get_user(len, oldlenp)) | ||
231 | return -EFAULT; | ||
232 | if (len) { | ||
233 | dev = dn_dev_get_default(); | ||
234 | if (dev) { | ||
235 | strcpy(devname, dev->name); | ||
236 | dev_put(dev); | ||
237 | } | ||
238 | |||
239 | namel = strlen(devname) + 1; | ||
240 | if (len > namel) len = namel; | ||
241 | |||
242 | if (copy_to_user(oldval, devname, len)) | ||
243 | return -EFAULT; | ||
244 | |||
245 | if (put_user(len, oldlenp)) | ||
246 | return -EFAULT; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | if (newval && newlen) { | ||
251 | if (newlen > 16) | ||
252 | return -E2BIG; | ||
253 | |||
254 | if (copy_from_user(devname, newval, newlen)) | ||
255 | return -EFAULT; | ||
256 | |||
257 | devname[newlen] = 0; | ||
258 | |||
259 | dev = dev_get_by_name(devname); | ||
260 | if (dev == NULL) | ||
261 | return -ENODEV; | ||
262 | |||
263 | rv = -ENODEV; | ||
264 | if (dev->dn_ptr != NULL) { | ||
265 | rv = dn_dev_set_default(dev, 1); | ||
266 | if (rv) | ||
267 | dev_put(dev); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | return rv; | ||
272 | } | ||
273 | |||
274 | |||
275 | static int dn_def_dev_handler(ctl_table *table, int write, | ||
276 | struct file * filp, | ||
277 | void __user *buffer, | ||
278 | size_t *lenp, loff_t *ppos) | ||
279 | { | ||
280 | size_t len; | ||
281 | struct net_device *dev; | ||
282 | char devname[17]; | ||
283 | |||
284 | if (!*lenp || (*ppos && !write)) { | ||
285 | *lenp = 0; | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | if (write) { | ||
290 | if (*lenp > 16) | ||
291 | return -E2BIG; | ||
292 | |||
293 | if (copy_from_user(devname, buffer, *lenp)) | ||
294 | return -EFAULT; | ||
295 | |||
296 | devname[*lenp] = 0; | ||
297 | strip_it(devname); | ||
298 | |||
299 | dev = dev_get_by_name(devname); | ||
300 | if (dev == NULL) | ||
301 | return -ENODEV; | ||
302 | |||
303 | if (dev->dn_ptr == NULL) { | ||
304 | dev_put(dev); | ||
305 | return -ENODEV; | ||
306 | } | ||
307 | |||
308 | if (dn_dev_set_default(dev, 1)) { | ||
309 | dev_put(dev); | ||
310 | return -ENODEV; | ||
311 | } | ||
312 | *ppos += *lenp; | ||
313 | |||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | dev = dn_dev_get_default(); | ||
318 | if (dev == NULL) { | ||
319 | *lenp = 0; | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | strcpy(devname, dev->name); | ||
324 | dev_put(dev); | ||
325 | len = strlen(devname); | ||
326 | devname[len++] = '\n'; | ||
327 | |||
328 | if (len > *lenp) len = *lenp; | ||
329 | |||
330 | if (copy_to_user(buffer, devname, len)) | ||
331 | return -EFAULT; | ||
332 | |||
333 | *lenp = len; | ||
334 | *ppos += len; | ||
335 | |||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | static ctl_table dn_table[] = { | ||
340 | { | ||
341 | .ctl_name = NET_DECNET_NODE_ADDRESS, | ||
342 | .procname = "node_address", | ||
343 | .maxlen = 7, | ||
344 | .mode = 0644, | ||
345 | .proc_handler = dn_node_address_handler, | ||
346 | .strategy = dn_node_address_strategy, | ||
347 | }, | ||
348 | { | ||
349 | .ctl_name = NET_DECNET_NODE_NAME, | ||
350 | .procname = "node_name", | ||
351 | .data = node_name, | ||
352 | .maxlen = 7, | ||
353 | .mode = 0644, | ||
354 | .proc_handler = &proc_dostring, | ||
355 | .strategy = &sysctl_string, | ||
356 | }, | ||
357 | { | ||
358 | .ctl_name = NET_DECNET_DEFAULT_DEVICE, | ||
359 | .procname = "default_device", | ||
360 | .maxlen = 16, | ||
361 | .mode = 0644, | ||
362 | .proc_handler = dn_def_dev_handler, | ||
363 | .strategy = dn_def_dev_strategy, | ||
364 | }, | ||
365 | { | ||
366 | .ctl_name = NET_DECNET_TIME_WAIT, | ||
367 | .procname = "time_wait", | ||
368 | .data = &decnet_time_wait, | ||
369 | .maxlen = sizeof(int), | ||
370 | .mode = 0644, | ||
371 | .proc_handler = &proc_dointvec_minmax, | ||
372 | .strategy = &sysctl_intvec, | ||
373 | .extra1 = &min_decnet_time_wait, | ||
374 | .extra2 = &max_decnet_time_wait | ||
375 | }, | ||
376 | { | ||
377 | .ctl_name = NET_DECNET_DN_COUNT, | ||
378 | .procname = "dn_count", | ||
379 | .data = &decnet_dn_count, | ||
380 | .maxlen = sizeof(int), | ||
381 | .mode = 0644, | ||
382 | .proc_handler = &proc_dointvec_minmax, | ||
383 | .strategy = &sysctl_intvec, | ||
384 | .extra1 = &min_state_count, | ||
385 | .extra2 = &max_state_count | ||
386 | }, | ||
387 | { | ||
388 | .ctl_name = NET_DECNET_DI_COUNT, | ||
389 | .procname = "di_count", | ||
390 | .data = &decnet_di_count, | ||
391 | .maxlen = sizeof(int), | ||
392 | .mode = 0644, | ||
393 | .proc_handler = &proc_dointvec_minmax, | ||
394 | .strategy = &sysctl_intvec, | ||
395 | .extra1 = &min_state_count, | ||
396 | .extra2 = &max_state_count | ||
397 | }, | ||
398 | { | ||
399 | .ctl_name = NET_DECNET_DR_COUNT, | ||
400 | .procname = "dr_count", | ||
401 | .data = &decnet_dr_count, | ||
402 | .maxlen = sizeof(int), | ||
403 | .mode = 0644, | ||
404 | .proc_handler = &proc_dointvec_minmax, | ||
405 | .strategy = &sysctl_intvec, | ||
406 | .extra1 = &min_state_count, | ||
407 | .extra2 = &max_state_count | ||
408 | }, | ||
409 | { | ||
410 | .ctl_name = NET_DECNET_DST_GC_INTERVAL, | ||
411 | .procname = "dst_gc_interval", | ||
412 | .data = &decnet_dst_gc_interval, | ||
413 | .maxlen = sizeof(int), | ||
414 | .mode = 0644, | ||
415 | .proc_handler = &proc_dointvec_minmax, | ||
416 | .strategy = &sysctl_intvec, | ||
417 | .extra1 = &min_decnet_dst_gc_interval, | ||
418 | .extra2 = &max_decnet_dst_gc_interval | ||
419 | }, | ||
420 | { | ||
421 | .ctl_name = NET_DECNET_NO_FC_MAX_CWND, | ||
422 | .procname = "no_fc_max_cwnd", | ||
423 | .data = &decnet_no_fc_max_cwnd, | ||
424 | .maxlen = sizeof(int), | ||
425 | .mode = 0644, | ||
426 | .proc_handler = &proc_dointvec_minmax, | ||
427 | .strategy = &sysctl_intvec, | ||
428 | .extra1 = &min_decnet_no_fc_max_cwnd, | ||
429 | .extra2 = &max_decnet_no_fc_max_cwnd | ||
430 | }, | ||
431 | { | ||
432 | .ctl_name = NET_DECNET_DEBUG_LEVEL, | ||
433 | .procname = "debug", | ||
434 | .data = &decnet_debug_level, | ||
435 | .maxlen = sizeof(int), | ||
436 | .mode = 0644, | ||
437 | .proc_handler = &proc_dointvec, | ||
438 | .strategy = &sysctl_intvec, | ||
439 | }, | ||
440 | {0} | ||
441 | }; | ||
442 | |||
443 | static ctl_table dn_dir_table[] = { | ||
444 | { | ||
445 | .ctl_name = NET_DECNET, | ||
446 | .procname = "decnet", | ||
447 | .mode = 0555, | ||
448 | .child = dn_table}, | ||
449 | {0} | ||
450 | }; | ||
451 | |||
452 | static ctl_table dn_root_table[] = { | ||
453 | { | ||
454 | .ctl_name = CTL_NET, | ||
455 | .procname = "net", | ||
456 | .mode = 0555, | ||
457 | .child = dn_dir_table | ||
458 | }, | ||
459 | {0} | ||
460 | }; | ||
461 | |||
462 | void dn_register_sysctl(void) | ||
463 | { | ||
464 | dn_table_header = register_sysctl_table(dn_root_table, 1); | ||
465 | } | ||
466 | |||
467 | void dn_unregister_sysctl(void) | ||
468 | { | ||
469 | unregister_sysctl_table(dn_table_header); | ||
470 | } | ||
471 | |||
472 | #else /* CONFIG_SYSCTL */ | ||
473 | void dn_unregister_sysctl(void) | ||
474 | { | ||
475 | } | ||
476 | void dn_register_sysctl(void) | ||
477 | { | ||
478 | } | ||
479 | |||
480 | #endif | ||