Blame view

net/decnet/dn_route.c 45.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  /*
   * DECnet       An implementation of the DECnet protocol suite for the LINUX
   *              operating system.  DECnet is implemented using the  BSD Socket
   *              interface as the means of communication with the user level.
   *
   *              DECnet Routing Functions (Endnode and Router)
   *
   * Authors:     Steve Whitehouse <SteveW@ACM.org>
   *              Eduardo Marcelo Serrat <emserrat@geocities.com>
   *
   * Changes:
   *              Steve Whitehouse : Fixes to allow "intra-ethernet" and
   *                                 "return-to-sender" bits on outgoing
   *                                 packets.
   *		Steve Whitehouse : Timeouts for cached routes.
   *              Steve Whitehouse : Use dst cache for input routes too.
   *              Steve Whitehouse : Fixed error values in dn_send_skb.
   *              Steve Whitehouse : Rework routing functions to better fit
   *                                 DECnet routing design
   *              Alexey Kuznetsov : New SMP locking
   *              Steve Whitehouse : More SMP locking changes & dn_cache_dump()
   *              Steve Whitehouse : Prerouting NF hook, now really is prerouting.
   *				   Fixed possible skb leak in rtnetlink funcs.
   *              Steve Whitehouse : Dave Miller's dynamic hash table sizing and
   *                                 Alexey Kuznetsov's finer grained locking
   *                                 from ipv4/route.c.
   *              Steve Whitehouse : Routing is now starting to look like a
   *                                 sensible set of code now, mainly due to
   *                                 my copying the IPv4 routing code. The
   *                                 hooks here are modified and will continue
   *                                 to evolve for a while.
   *              Steve Whitehouse : Real SMP at last :-) Also new netfilter
   *                                 stuff. Look out raw sockets your days
   *                                 are numbered!
   *              Steve Whitehouse : Added return-to-sender functions. Added
   *                                 backlog congestion level return codes.
   *		Steve Whitehouse : Fixed bug where routes were set up with
   *                                 no ref count on net devices.
   *              Steve Whitehouse : RCU for the route cache
   *              Steve Whitehouse : Preparations for the flow cache
   *              Steve Whitehouse : Prepare for nonlinear skbs
   */
  
  /******************************************************************************
      (c) 1995-1998 E.M. Serrat		emserrat@geocities.com
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
46

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
54
55
56
      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      any later version.
  
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  *******************************************************************************/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
60
61
62
63
64
65
66
67
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/socket.h>
  #include <linux/in.h>
  #include <linux/kernel.h>
  #include <linux/sockios.h>
  #include <linux/net.h>
  #include <linux/netdevice.h>
  #include <linux/inet.h>
  #include <linux/route.h>
  #include <linux/in_route.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
68
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
72
73
74
75
76
77
78
  #include <net/sock.h>
  #include <linux/mm.h>
  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
  #include <linux/init.h>
  #include <linux/rtnetlink.h>
  #include <linux/string.h>
  #include <linux/netfilter_decnet.h>
  #include <linux/rcupdate.h>
  #include <linux/times.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
79
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  #include <asm/errno.h>
457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
81
  #include <net/net_namespace.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
82
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
  #include <net/neighbour.h>
  #include <net/dst.h>
  #include <net/flow.h>
a8731cbf6   Steven Whitehouse   [DECNET]: Covert ...
86
  #include <net/fib_rules.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
89
90
91
92
93
94
95
  #include <net/dn.h>
  #include <net/dn_dev.h>
  #include <net/dn_nsp.h>
  #include <net/dn_route.h>
  #include <net/dn_neigh.h>
  #include <net/dn_fib.h>
  
  struct dn_rt_hash_bucket
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
96
  	struct dn_route __rcu *chain;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
  	spinlock_t lock;
fca09fb73   Eric Dumazet   [DECNET] ROUTE: r...
98
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
103
104
105
106
107
108
109
  
  extern struct neigh_table dn_neigh_table;
  
  
  static unsigned char dn_hiord_addr[6] = {0xAA,0x00,0x04,0x00,0x00,0x00};
  
  static const int dn_rt_min_delay = 2 * HZ;
  static const int dn_rt_max_delay = 10 * HZ;
  static const int dn_rt_mtu_expires = 10 * 60 * HZ;
  
  static unsigned long dn_rt_deadline;
569d36452   Daniel Lezcano   [NETNS][DST] dst:...
110
  static int dn_dst_gc(struct dst_ops *ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  static struct dst_entry *dn_dst_check(struct dst_entry *, __u32);
0dbaee3b3   David S. Miller   net: Abstract def...
112
  static unsigned int dn_dst_default_advmss(const struct dst_entry *dst);
ebb762f27   Steffen Klassert   net: Rename the d...
113
  static unsigned int dn_dst_mtu(const struct dst_entry *dst);
62fa8a846   David S. Miller   net: Implement re...
114
  static void dn_dst_destroy(struct dst_entry *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
  static struct dst_entry *dn_dst_negative_advice(struct dst_entry *);
  static void dn_dst_link_failure(struct sk_buff *);
  static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu);
d3aaeb38c   David S. Miller   net: Add ->neigh_...
118
  static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst, const void *daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
122
123
124
125
  static int dn_route_input(struct sk_buff *);
  static void dn_run_flush(unsigned long dummy);
  
  static struct dn_rt_hash_bucket *dn_rt_hash_table;
  static unsigned dn_rt_hash_mask;
  
  static struct timer_list dn_route_timer;
8d06afab7   Ingo Molnar   [PATCH] timer ini...
126
  static DEFINE_TIMER(dn_rt_flush_timer, dn_run_flush, 0, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
129
130
  int decnet_dst_gc_interval = 2;
  
  static struct dst_ops dn_dst_ops = {
  	.family =		PF_DECnet,
09640e636   Harvey Harrison   net: replace uses...
131
  	.protocol =		cpu_to_be16(ETH_P_DNA_RT),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
  	.gc_thresh =		128,
  	.gc =			dn_dst_gc,
  	.check =		dn_dst_check,
0dbaee3b3   David S. Miller   net: Abstract def...
135
  	.default_advmss =	dn_dst_default_advmss,
ebb762f27   Steffen Klassert   net: Rename the d...
136
  	.mtu =			dn_dst_mtu,
62fa8a846   David S. Miller   net: Implement re...
137
138
  	.cow_metrics =		dst_cow_metrics_generic,
  	.destroy =		dn_dst_destroy,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
141
  	.negative_advice =	dn_dst_negative_advice,
  	.link_failure =		dn_dst_link_failure,
  	.update_pmtu =		dn_dst_update_pmtu,
d3aaeb38c   David S. Miller   net: Add ->neigh_...
142
  	.neigh_lookup =		dn_dst_neigh_lookup,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  };
62fa8a846   David S. Miller   net: Implement re...
144
145
146
147
  static void dn_dst_destroy(struct dst_entry *dst)
  {
  	dst_destroy_metrics_generic(dst);
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
148
  static __inline__ unsigned dn_hash(__le16 src, __le16 dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
150
  	__u16 tmp = (__u16 __force)(src ^ dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
156
157
158
  	tmp ^= (tmp >> 3);
  	tmp ^= (tmp >> 5);
  	tmp ^= (tmp >> 10);
  	return dn_rt_hash_mask & (unsigned)tmp;
  }
  
  static inline void dnrt_free(struct dn_route *rt)
  {
d8d1f30b9   Changli Gao   net-next: remove ...
159
  	call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
163
  }
  
  static inline void dnrt_drop(struct dn_route *rt)
  {
d8d1f30b9   Changli Gao   net-next: remove ...
164
165
  	dst_release(&rt->dst);
  	call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
  }
  
  static void dn_dst_check_expire(unsigned long dummy)
  {
  	int i;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
171
172
  	struct dn_route *rt;
  	struct dn_route __rcu **rtp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
  	unsigned long now = jiffies;
  	unsigned long expire = 120 * HZ;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
175
  	for (i = 0; i <= dn_rt_hash_mask; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
  		rtp = &dn_rt_hash_table[i].chain;
  
  		spin_lock(&dn_rt_hash_table[i].lock);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
179
180
  		while ((rt = rcu_dereference_protected(*rtp,
  						lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
d8d1f30b9   Changli Gao   net-next: remove ...
181
182
183
  			if (atomic_read(&rt->dst.__refcnt) ||
  					(now - rt->dst.lastuse) < expire) {
  				rtp = &rt->dst.dn_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
  				continue;
  			}
d8d1f30b9   Changli Gao   net-next: remove ...
186
187
  			*rtp = rt->dst.dn_next;
  			rt->dst.dn_next = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
194
195
196
197
  			dnrt_free(rt);
  		}
  		spin_unlock(&dn_rt_hash_table[i].lock);
  
  		if ((jiffies - now) > 0)
  			break;
  	}
  
  	mod_timer(&dn_route_timer, now + decnet_dst_gc_interval * HZ);
  }
569d36452   Daniel Lezcano   [NETNS][DST] dst:...
198
  static int dn_dst_gc(struct dst_ops *ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
200
201
  	struct dn_route *rt;
  	struct dn_route __rcu **rtp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
  	int i;
  	unsigned long now = jiffies;
  	unsigned long expire = 10 * HZ;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
205
  	for (i = 0; i <= dn_rt_hash_mask; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
  
  		spin_lock_bh(&dn_rt_hash_table[i].lock);
  		rtp = &dn_rt_hash_table[i].chain;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
209
210
  		while ((rt = rcu_dereference_protected(*rtp,
  						lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
d8d1f30b9   Changli Gao   net-next: remove ...
211
212
213
  			if (atomic_read(&rt->dst.__refcnt) ||
  					(now - rt->dst.lastuse) < expire) {
  				rtp = &rt->dst.dn_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  				continue;
  			}
d8d1f30b9   Changli Gao   net-next: remove ...
216
217
  			*rtp = rt->dst.dn_next;
  			rt->dst.dn_next = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  			dnrt_drop(rt);
  			break;
  		}
  		spin_unlock_bh(&dn_rt_hash_table[i].lock);
  	}
  
  	return 0;
  }
  
  /*
   * The decnet standards don't impose a particular minimum mtu, what they
   * do insist on is that the routing layer accepts a datagram of at least
   * 230 bytes long. Here we have to subtract the routing header length from
   * 230 to get the minimum acceptable mtu. If there is no neighbour, then we
   * assume the worst and use a long header size.
   *
   * We update both the mtu and the advertised mss (i.e. the segment size we
   * advertise to the other end).
   */
  static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu)
  {
272174550   David Miller   net: Rename dst_g...
239
  	struct neighbour *n = dst_get_neighbour_noref(dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  	u32 min_mtu = 230;
69cce1d14   David S. Miller   net: Abstract dst...
241
242
243
  	struct dn_dev *dn;
  
  	dn = n ? rcu_dereference_raw(n->dev->dn_ptr) : NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
248
  
  	if (dn && dn->use_long == 0)
  		min_mtu -= 6;
  	else
  		min_mtu -= 21;
5ffc02a15   Satoru SATOH   ip: Use inline fu...
249
  	if (dst_metric(dst, RTAX_MTU) > mtu && mtu >= min_mtu) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  		if (!(dst_metric_locked(dst, RTAX_MTU))) {
defb3519a   David S. Miller   net: Abstract awa...
251
  			dst_metric_set(dst, RTAX_MTU, mtu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
  			dst_set_expires(dst, dn_rt_mtu_expires);
  		}
  		if (!(dst_metric_locked(dst, RTAX_ADVMSS))) {
  			u32 mss = mtu - DN_MAX_NSP_DATA_HEADER;
0dbaee3b3   David S. Miller   net: Abstract def...
256
257
  			u32 existing_mss = dst_metric_raw(dst, RTAX_ADVMSS);
  			if (!existing_mss || existing_mss > mss)
defb3519a   David S. Miller   net: Abstract awa...
258
  				dst_metric_set(dst, RTAX_ADVMSS, mss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
  		}
  	}
  }
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
262
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
   * When a route has been marked obsolete. (e.g. routing cache flush)
   */
  static struct dst_entry *dn_dst_check(struct dst_entry *dst, __u32 cookie)
  {
  	return NULL;
  }
  
  static struct dst_entry *dn_dst_negative_advice(struct dst_entry *dst)
  {
  	dst_release(dst);
  	return NULL;
  }
  
  static void dn_dst_link_failure(struct sk_buff *skb)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  }
bef55aebd   David S. Miller   decnet: Convert t...
279
  static inline int compare_keys(struct flowidn *fl1, struct flowidn *fl2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  {
bef55aebd   David S. Miller   decnet: Convert t...
281
282
283
284
285
286
  	return ((fl1->daddr ^ fl2->daddr) |
  		(fl1->saddr ^ fl2->saddr) |
  		(fl1->flowidn_mark ^ fl2->flowidn_mark) |
  		(fl1->flowidn_scope ^ fl2->flowidn_scope) |
  		(fl1->flowidn_oif ^ fl2->flowidn_oif) |
  		(fl1->flowidn_iif ^ fl2->flowidn_iif)) == 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
  }
  
  static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route **rp)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
291
292
  	struct dn_route *rth;
  	struct dn_route __rcu **rthp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
  	unsigned long now = jiffies;
  
  	rthp = &dn_rt_hash_table[hash].chain;
  
  	spin_lock_bh(&dn_rt_hash_table[hash].lock);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
298
299
  	while ((rth = rcu_dereference_protected(*rthp,
  						lockdep_is_held(&dn_rt_hash_table[hash].lock))) != NULL) {
bef55aebd   David S. Miller   decnet: Convert t...
300
  		if (compare_keys(&rth->fld, &rt->fld)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
  			/* Put it first */
d8d1f30b9   Changli Gao   net-next: remove ...
302
303
  			*rthp = rth->dst.dn_next;
  			rcu_assign_pointer(rth->dst.dn_next,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
  					   dn_rt_hash_table[hash].chain);
  			rcu_assign_pointer(dn_rt_hash_table[hash].chain, rth);
d8d1f30b9   Changli Gao   net-next: remove ...
306
  			dst_use(&rth->dst, now);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
310
311
312
  			spin_unlock_bh(&dn_rt_hash_table[hash].lock);
  
  			dnrt_drop(rt);
  			*rp = rth;
  			return 0;
  		}
d8d1f30b9   Changli Gao   net-next: remove ...
313
  		rthp = &rth->dst.dn_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
315
  	rcu_assign_pointer(rt->dst.dn_next, dn_rt_hash_table[hash].chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  	rcu_assign_pointer(dn_rt_hash_table[hash].chain, rt);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
317

d8d1f30b9   Changli Gao   net-next: remove ...
318
  	dst_use(&rt->dst, now);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
  	spin_unlock_bh(&dn_rt_hash_table[hash].lock);
  	*rp = rt;
  	return 0;
  }
5eaa65b24   Roel Kluin   net: Make static
323
  static void dn_run_flush(unsigned long dummy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
326
  {
  	int i;
  	struct dn_route *rt, *next;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
327
  	for (i = 0; i < dn_rt_hash_mask; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
  		spin_lock_bh(&dn_rt_hash_table[i].lock);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
329
  		if ((rt = xchg((struct dn_route **)&dn_rt_hash_table[i].chain, NULL)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  			goto nothing_to_declare;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
331
332
333
  		for(; rt; rt = next) {
  			next = rcu_dereference_raw(rt->dst.dn_next);
  			RCU_INIT_POINTER(rt->dst.dn_next, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  			dst_free((struct dst_entry *)rt);
  		}
  
  nothing_to_declare:
  		spin_unlock_bh(&dn_rt_hash_table[i].lock);
  	}
  }
  
  static DEFINE_SPINLOCK(dn_rt_flush_lock);
  
  void dn_rt_cache_flush(int delay)
  {
  	unsigned long now = jiffies;
  	int user_mode = !in_interrupt();
  
  	if (delay < 0)
  		delay = dn_rt_min_delay;
  
  	spin_lock_bh(&dn_rt_flush_lock);
  
  	if (del_timer(&dn_rt_flush_timer) && delay > 0 && dn_rt_deadline) {
  		long tmo = (long)(dn_rt_deadline - now);
  
  		if (user_mode && tmo < dn_rt_max_delay - dn_rt_min_delay)
  			tmo = 0;
  
  		if (delay > tmo)
  			delay = tmo;
  	}
  
  	if (delay <= 0) {
  		spin_unlock_bh(&dn_rt_flush_lock);
  		dn_run_flush(0);
  		return;
  	}
  
  	if (dn_rt_deadline == 0)
  		dn_rt_deadline = now + dn_rt_max_delay;
  
  	dn_rt_flush_timer.expires = now + delay;
  	add_timer(&dn_rt_flush_timer);
  	spin_unlock_bh(&dn_rt_flush_lock);
  }
  
  /**
   * dn_return_short - Return a short packet to its sender
   * @skb: The packet to return
   *
   */
  static int dn_return_short(struct sk_buff *skb)
  {
  	struct dn_skb_cb *cb;
  	unsigned char *ptr;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
387
388
  	__le16 *src;
  	__le16 *dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
  
  	/* Add back headers */
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
391
  	skb_push(skb, skb->data - skb_network_header(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
395
396
397
398
399
  
  	if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL)
  		return NET_RX_DROP;
  
  	cb = DN_SKB_CB(skb);
  	/* Skip packet length and point to flags */
  	ptr = skb->data + 2;
  	*ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
400
  	dst = (__le16 *)ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
  	ptr += 2;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
402
  	src = (__le16 *)ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
  	ptr += 2;
  	*ptr = 0; /* Zero hop count */
a0bffffc1   Ilpo Järvinen   net/*: use linux/...
405
  	swap(*src, *dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  
  	skb->pkt_type = PACKET_OUTGOING;
  	dn_rt_finish_output(skb, NULL, NULL);
  	return NET_RX_SUCCESS;
  }
  
  /**
   * dn_return_long - Return a long packet to its sender
   * @skb: The long format packet to return
   *
   */
  static int dn_return_long(struct sk_buff *skb)
  {
  	struct dn_skb_cb *cb;
  	unsigned char *ptr;
  	unsigned char *src_addr, *dst_addr;
  	unsigned char tmp[ETH_ALEN];
  
  	/* Add back all headers */
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
425
  	skb_push(skb, skb->data - skb_network_header(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
  
  	if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL)
  		return NET_RX_DROP;
  
  	cb = DN_SKB_CB(skb);
  	/* Ignore packet length and point to flags */
  	ptr = skb->data + 2;
  
  	/* Skip padding */
  	if (*ptr & DN_RT_F_PF) {
  		char padlen = (*ptr & ~DN_RT_F_PF);
  		ptr += padlen;
  	}
  
  	*ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
  	ptr += 2;
  	dst_addr = ptr;
  	ptr += 8;
  	src_addr = ptr;
  	ptr += 6;
  	*ptr = 0; /* Zero hop count */
  
  	/* Swap source and destination */
  	memcpy(tmp, src_addr, ETH_ALEN);
  	memcpy(src_addr, dst_addr, ETH_ALEN);
  	memcpy(dst_addr, tmp, ETH_ALEN);
  
  	skb->pkt_type = PACKET_OUTGOING;
  	dn_rt_finish_output(skb, dst_addr, src_addr);
  	return NET_RX_SUCCESS;
  }
  
  /**
   * dn_route_rx_packet - Try and find a route for an incoming packet
   * @skb: The packet to find a route for
   *
   * Returns: result of input function if route is found, error code otherwise
   */
  static int dn_route_rx_packet(struct sk_buff *skb)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
466
  	struct dn_skb_cb *cb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
  	int err;
  
  	if ((err = dn_route_input(skb)) == 0)
  		return dst_input(skb);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
471
  	cb = DN_SKB_CB(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
  	if (decnet_debug_level & 4) {
  		char *devname = skb->dev ? skb->dev->name : "???";
fc766e4c4   Eric Dumazet   decnet: RCU conve...
474

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
  		printk(KERN_DEBUG
  			"DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d
  ",
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
478
  			(int)cb->rt_flags, devname, skb->len,
c4106aa88   Harvey Harrison   decnet: remove pr...
479
  			le16_to_cpu(cb->src), le16_to_cpu(cb->dst),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
483
  			err, skb->pkt_type);
  	}
  
  	if ((skb->pkt_type == PACKET_HOST) && (cb->rt_flags & DN_RT_F_RQR)) {
06f8fe11b   Joe Perches   decnet: Reduce sw...
484
485
486
487
488
  		switch (cb->rt_flags & DN_RT_PKT_MSK) {
  		case DN_RT_PKT_SHORT:
  			return dn_return_short(skb);
  		case DN_RT_PKT_LONG:
  			return dn_return_long(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
  		}
  	}
  
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
  
  static int dn_route_rx_long(struct sk_buff *skb)
  {
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	unsigned char *ptr = skb->data;
  
  	if (!pskb_may_pull(skb, 21)) /* 20 for long header, 1 for shortest nsp */
  		goto drop_it;
  
  	skb_pull(skb, 20);
badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
505
  	skb_reset_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
507
508
  	/* Destination info */
  	ptr += 2;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
509
  	cb->dst = dn_eth2dn(ptr);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
510
511
512
  	if (memcmp(ptr, dn_hiord_addr, 4) != 0)
  		goto drop_it;
  	ptr += 6;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
514
515
  	/* Source info */
  	ptr += 2;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
516
  	cb->src = dn_eth2dn(ptr);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
517
518
519
520
521
522
  	if (memcmp(ptr, dn_hiord_addr, 4) != 0)
  		goto drop_it;
  	ptr += 6;
  	/* Other junk */
  	ptr++;
  	cb->hops = *ptr++; /* Visit Count */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523

5d877d876   Jan Engelhardt   netfilter: decnet...
524
525
  	return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, skb, skb->dev, NULL,
  		       dn_route_rx_packet);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
  
  drop_it:
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
  
  
  
  static int dn_route_rx_short(struct sk_buff *skb)
  {
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	unsigned char *ptr = skb->data;
  
  	if (!pskb_may_pull(skb, 6)) /* 5 for short header + 1 for shortest nsp */
  		goto drop_it;
  
  	skb_pull(skb, 5);
badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
543
  	skb_reset_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544

c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
545
  	cb->dst = *(__le16 *)ptr;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
546
547
548
549
  	ptr += 2;
  	cb->src = *(__le16 *)ptr;
  	ptr += 2;
  	cb->hops = *ptr & 0x3f;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550

5d877d876   Jan Engelhardt   netfilter: decnet...
551
552
  	return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, skb, skb->dev, NULL,
  		       dn_route_rx_packet);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
  
  drop_it:
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
555
556
  	kfree_skb(skb);
  	return NET_RX_DROP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
  }
  
  static int dn_route_discard(struct sk_buff *skb)
  {
  	/*
  	 * I know we drop the packet here, but thats considered success in
  	 * this case
  	 */
  	kfree_skb(skb);
  	return NET_RX_SUCCESS;
  }
  
  static int dn_route_ptp_hello(struct sk_buff *skb)
  {
  	dn_dev_hello(skb);
  	dn_neigh_pointopoint_hello(skb);
  	return NET_RX_SUCCESS;
  }
f2ccd8fa0   David S. Miller   [NET]: Kill skb->...
575
  int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
  {
  	struct dn_skb_cb *cb;
  	unsigned char flags = 0;
c4106aa88   Harvey Harrison   decnet: remove pr...
579
  	__u16 len = le16_to_cpu(*(__le16 *)skb->data);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
580
  	struct dn_dev *dn = rcu_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
  	unsigned char padlen = 0;
721499e89   YOSHIFUJI Hideaki   netns: Use net_eq...
582
  	if (!net_eq(dev_net(dev), &init_net))
e730c1551   Eric W. Biederman   [NET]: Make packe...
583
  		goto dump_it;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
  	if (dn == NULL)
  		goto dump_it;
  
  	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
  		goto out;
  
  	if (!pskb_may_pull(skb, 3))
  		goto dump_it;
  
  	skb_pull(skb, 2);
  
  	if (len > skb->len)
  		goto dump_it;
  
  	skb_trim(skb, len);
  
  	flags = *skb->data;
  
  	cb = DN_SKB_CB(skb);
  	cb->stamp = jiffies;
  	cb->iif = dev->ifindex;
  
  	/*
  	 * If we have padding, remove it.
  	 */
  	if (flags & DN_RT_F_PF) {
  		padlen = flags & ~DN_RT_F_PF;
  		if (!pskb_may_pull(skb, padlen + 1))
  			goto dump_it;
  		skb_pull(skb, padlen);
  		flags = *skb->data;
  	}
c1d2bbe1c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
616
  	skb_reset_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
621
622
623
624
625
626
  
  	/*
  	 * Weed out future version DECnet
  	 */
  	if (flags & DN_RT_F_VER)
  		goto dump_it;
  
  	cb->rt_flags = flags;
  
  	if (decnet_debug_level & 1)
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
627
  		printk(KERN_DEBUG
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
  			"dn_route_rcv: got 0x%02x from %s [%d %d %d]
  ",
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
630
  			(int)flags, (dev) ? dev->name : "???", len, skb->len,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
  			padlen);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
632
  	if (flags & DN_RT_PKT_CNTL) {
364c6badd   Herbert Xu   [NET]: Clean up s...
633
  		if (unlikely(skb_linearize(skb)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  			goto dump_it;
06f8fe11b   Joe Perches   decnet: Reduce sw...
635
636
637
638
639
640
641
  		switch (flags & DN_RT_CNTL_MSK) {
  		case DN_RT_PKT_INIT:
  			dn_dev_init_pkt(skb);
  			break;
  		case DN_RT_PKT_VERI:
  			dn_dev_veri_pkt(skb);
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
  		}
  
  		if (dn->parms.state != DN_DEV_S_RU)
  			goto dump_it;
06f8fe11b   Joe Perches   decnet: Reduce sw...
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
  		switch (flags & DN_RT_CNTL_MSK) {
  		case DN_RT_PKT_HELO:
  			return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
  				       skb, skb->dev, NULL,
  				       dn_route_ptp_hello);
  
  		case DN_RT_PKT_L1RT:
  		case DN_RT_PKT_L2RT:
  			return NF_HOOK(NFPROTO_DECNET, NF_DN_ROUTE,
  				       skb, skb->dev, NULL,
  				       dn_route_discard);
  		case DN_RT_PKT_ERTH:
  			return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
  				       skb, skb->dev, NULL,
  				       dn_neigh_router_hello);
  
  		case DN_RT_PKT_EEDH:
  			return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
  				       skb, skb->dev, NULL,
  				       dn_neigh_endnode_hello);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
666
667
  		}
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
669
670
671
  		if (dn->parms.state != DN_DEV_S_RU)
  			goto dump_it;
  
  		skb_pull(skb, 1); /* Pull flags */
06f8fe11b   Joe Perches   decnet: Reduce sw...
672
673
674
675
676
  		switch (flags & DN_RT_PKT_MSK) {
  		case DN_RT_PKT_LONG:
  			return dn_route_rx_long(skb);
  		case DN_RT_PKT_SHORT:
  			return dn_route_rx_short(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
  		}
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
678
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
680
681
682
683
684
  
  dump_it:
  	kfree_skb(skb);
  out:
  	return NET_RX_DROP;
  }
8f40b161d   David S. Miller   neigh: Pass neigh...
685
686
687
  static int dn_to_neigh_output(struct sk_buff *skb)
  {
  	struct dst_entry *dst = skb_dst(skb);
272174550   David Miller   net: Rename dst_g...
688
  	struct neighbour *n = dst_get_neighbour_noref(dst);
8f40b161d   David S. Miller   neigh: Pass neigh...
689
690
691
  
  	return n->output(n, skb);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
  static int dn_output(struct sk_buff *skb)
  {
adf30907d   Eric Dumazet   net: skb->dst acc...
694
  	struct dst_entry *dst = skb_dst(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
696
697
698
699
700
  	struct dn_route *rt = (struct dn_route *)dst;
  	struct net_device *dev = dst->dev;
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	struct neighbour *neigh;
  
  	int err = -EINVAL;
272174550   David Miller   net: Rename dst_g...
701
  	if ((neigh = dst_get_neighbour_noref(dst)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
  		goto error;
  
  	skb->dev = dev;
  
  	cb->src = rt->rt_saddr;
  	cb->dst = rt->rt_daddr;
  
  	/*
  	 * Always set the Intra-Ethernet bit on all outgoing packets
  	 * originated on this node. Only valid flag from upper layers
  	 * is return-to-sender-requested. Set hop count to 0 too.
  	 */
  	cb->rt_flags &= ~DN_RT_F_RQR;
  	cb->rt_flags |= DN_RT_F_IE;
  	cb->hops = 0;
5d877d876   Jan Engelhardt   netfilter: decnet...
717
  	return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, skb, NULL, dev,
8f40b161d   David S. Miller   neigh: Pass neigh...
718
  		       dn_to_neigh_output);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
720
721
722
723
724
725
726
727
728
729
730
731
732
  
  error:
  	if (net_ratelimit())
  		printk(KERN_DEBUG "dn_output: This should not happen
  ");
  
  	kfree_skb(skb);
  
  	return err;
  }
  
  static int dn_forward(struct sk_buff *skb)
  {
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
adf30907d   Eric Dumazet   net: skb->dst acc...
733
  	struct dst_entry *dst = skb_dst(skb);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
734
  	struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735
  	struct dn_route *rt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
739
740
741
742
743
744
  	int header_len;
  #ifdef CONFIG_NETFILTER
  	struct net_device *dev = skb->dev;
  #endif
  
  	if (skb->pkt_type != PACKET_HOST)
  		goto drop;
  
  	/* Ensure that we have enough space for headers */
adf30907d   Eric Dumazet   net: skb->dst acc...
745
  	rt = (struct dn_route *)skb_dst(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
  	header_len = dn_db->use_long ? 21 : 6;
d8d1f30b9   Changli Gao   net-next: remove ...
747
  	if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+header_len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
749
750
751
752
753
754
  		goto drop;
  
  	/*
  	 * Hop count exceeded.
  	 */
  	if (++cb->hops > 30)
  		goto drop;
d8d1f30b9   Changli Gao   net-next: remove ...
755
  	skb->dev = rt->dst.dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
758
759
760
761
762
763
764
  
  	/*
  	 * If packet goes out same interface it came in on, then set
  	 * the Intra-Ethernet bit. This has no effect for short
  	 * packets, so we don't need to test for them here.
  	 */
  	cb->rt_flags &= ~DN_RT_F_IE;
  	if (rt->rt_flags & RTCF_DOREDIRECT)
  		cb->rt_flags |= DN_RT_F_IE;
5d877d876   Jan Engelhardt   netfilter: decnet...
765
  	return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, skb, dev, skb->dev,
8f40b161d   David S. Miller   neigh: Pass neigh...
766
  		       dn_to_neigh_output);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
768
769
770
771
772
773
  
  drop:
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
776
777
778
779
780
781
782
783
   * Used to catch bugs. This should never normally get
   * called.
   */
  static int dn_rt_bug(struct sk_buff *skb)
  {
  	if (net_ratelimit()) {
  		struct dn_skb_cb *cb = DN_SKB_CB(skb);
  
  		printk(KERN_DEBUG "dn_rt_bug: skb from:%04x to:%04x
  ",
c4106aa88   Harvey Harrison   decnet: remove pr...
784
  				le16_to_cpu(cb->src), le16_to_cpu(cb->dst));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
786
787
  	}
  
  	kfree_skb(skb);
0e8635a8e   Florian Westphal   net: remove NET_R...
788
  	return NET_RX_DROP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
  }
0dbaee3b3   David S. Miller   net: Abstract def...
790
791
792
793
  static unsigned int dn_dst_default_advmss(const struct dst_entry *dst)
  {
  	return dn_mss_from_pmtu(dst->dev, dst_mtu(dst));
  }
ebb762f27   Steffen Klassert   net: Rename the d...
794
  static unsigned int dn_dst_mtu(const struct dst_entry *dst)
d33e45533   David S. Miller   net: Abstract def...
795
  {
618f9bc74   Steffen Klassert   net: Move mtu han...
796
797
798
  	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
  
  	return mtu ? : dst->dev->mtu;
d33e45533   David S. Miller   net: Abstract def...
799
  }
d3aaeb38c   David S. Miller   net: Add ->neigh_...
800
801
802
803
  static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst, const void *daddr)
  {
  	return __neigh_lookup_errno(&dn_neigh_table, daddr, dst->dev);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
  static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
  {
  	struct dn_fib_info *fi = res->fi;
d8d1f30b9   Changli Gao   net-next: remove ...
807
  	struct net_device *dev = rt->dst.dev;
62fa8a846   David S. Miller   net: Implement re...
808
  	unsigned int mss_metric;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
809
  	struct neighbour *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
811
812
813
814
  
  	if (fi) {
  		if (DN_FIB_RES_GW(*res) &&
  		    DN_FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
  			rt->rt_gateway = DN_FIB_RES_GW(*res);
62fa8a846   David S. Miller   net: Implement re...
815
  		dst_init_metrics(&rt->dst, fi->fib_metrics, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
817
  	}
  	rt->rt_type = res->type;
272174550   David Miller   net: Rename dst_g...
818
  	if (dev != NULL && dst_get_neighbour_noref(&rt->dst) == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
821
  		n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev);
  		if (IS_ERR(n))
  			return PTR_ERR(n);
69cce1d14   David S. Miller   net: Abstract dst...
822
  		dst_set_neighbour(&rt->dst, n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
823
  	}
d33e45533   David S. Miller   net: Abstract def...
824
  	if (dst_metric(&rt->dst, RTAX_MTU) > rt->dst.dev->mtu)
defb3519a   David S. Miller   net: Abstract awa...
825
  		dst_metric_set(&rt->dst, RTAX_MTU, rt->dst.dev->mtu);
62fa8a846   David S. Miller   net: Implement re...
826
827
  	mss_metric = dst_metric_raw(&rt->dst, RTAX_ADVMSS);
  	if (mss_metric) {
0dbaee3b3   David S. Miller   net: Abstract def...
828
  		unsigned int mss = dn_mss_from_pmtu(dev, dst_mtu(&rt->dst));
62fa8a846   David S. Miller   net: Implement re...
829
  		if (mss_metric > mss)
0dbaee3b3   David S. Miller   net: Abstract def...
830
831
  			dst_metric_set(&rt->dst, RTAX_ADVMSS, mss);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
833
  	return 0;
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
834
  static inline int dn_match_addr(__le16 addr1, __le16 addr2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
  {
c4106aa88   Harvey Harrison   decnet: remove pr...
836
  	__u16 tmp = le16_to_cpu(addr1) ^ le16_to_cpu(addr2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
838
839
840
841
842
843
  	int match = 16;
  	while(tmp) {
  		tmp >>= 1;
  		match--;
  	}
  	return match;
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
844
  static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
  {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
846
  	__le16 saddr = 0;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
847
  	struct dn_dev *dn_db;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
849
850
  	struct dn_ifaddr *ifa;
  	int best_match = 0;
  	int ret;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
851
852
853
854
855
  	rcu_read_lock();
  	dn_db = rcu_dereference(dev->dn_ptr);
  	for (ifa = rcu_dereference(dn_db->ifa_list);
  	     ifa != NULL;
  	     ifa = rcu_dereference(ifa->ifa_next)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
857
858
859
860
861
862
863
864
865
866
867
  		if (ifa->ifa_scope > scope)
  			continue;
  		if (!daddr) {
  			saddr = ifa->ifa_local;
  			break;
  		}
  		ret = dn_match_addr(daddr, ifa->ifa_local);
  		if (ret > best_match)
  			saddr = ifa->ifa_local;
  		if (best_match == 0)
  			saddr = ifa->ifa_local;
  	}
fc766e4c4   Eric Dumazet   decnet: RCU conve...
868
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
871
  
  	return saddr;
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
872
  static inline __le16 __dn_fib_res_prefsrc(struct dn_fib_res *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
875
  {
  	return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope);
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
876
  static inline __le16 dn_fib_rules_map_destination(__le16 daddr, struct dn_fib_res *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
  {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
878
  	__le16 mask = dnet_make_mask(res->prefixlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
880
  	return (daddr&~mask)|res->fi->fib_nh->nh_gw;
  }
bef55aebd   David S. Miller   decnet: Convert t...
881
  static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *oldflp, int try_hard)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
  {
bef55aebd   David S. Miller   decnet: Convert t...
883
884
885
886
887
888
889
  	struct flowidn fld = {
  		.daddr = oldflp->daddr,
  		.saddr = oldflp->saddr,
  		.flowidn_scope = RT_SCOPE_UNIVERSE,
  		.flowidn_mark = oldflp->flowidn_mark,
  		.flowidn_iif = init_net.loopback_dev->ifindex,
  		.flowidn_oif = oldflp->flowidn_oif,
1d28f42c1   David S. Miller   net: Put flowi_* ...
890
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891
  	struct dn_route *rt = NULL;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
892
  	struct net_device *dev_out = NULL, *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
898
  	struct neighbour *neigh = NULL;
  	unsigned hash;
  	unsigned flags = 0;
  	struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST };
  	int err;
  	int free_res = 0;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
899
  	__le16 gateway = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900
901
902
903
  
  	if (decnet_debug_level & 16)
  		printk(KERN_DEBUG
  		       "dn_route_output_slow: dst=%04x src=%04x mark=%d"
bef55aebd   David S. Miller   decnet: Convert t...
904
905
906
907
908
  		       " iif=%d oif=%d
  ", le16_to_cpu(oldflp->daddr),
  		       le16_to_cpu(oldflp->saddr),
  		       oldflp->flowidn_mark, init_net.loopback_dev->ifindex,
  		       oldflp->flowidn_oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
909
910
  
  	/* If we have an output interface, verify its a DECnet device */
bef55aebd   David S. Miller   decnet: Convert t...
911
912
  	if (oldflp->flowidn_oif) {
  		dev_out = dev_get_by_index(&init_net, oldflp->flowidn_oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
913
914
915
916
917
918
919
920
921
922
  		err = -ENODEV;
  		if (dev_out && dev_out->dn_ptr == NULL) {
  			dev_put(dev_out);
  			dev_out = NULL;
  		}
  		if (dev_out == NULL)
  			goto out;
  	}
  
  	/* If we have a source address, verify that its a local address */
bef55aebd   David S. Miller   decnet: Convert t...
923
  	if (oldflp->saddr) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
925
926
  		err = -EADDRNOTAVAIL;
  
  		if (dev_out) {
bef55aebd   David S. Miller   decnet: Convert t...
927
  			if (dn_dev_islocal(dev_out, oldflp->saddr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
929
930
931
  				goto source_ok;
  			dev_put(dev_out);
  			goto out;
  		}
c6d14c845   Eric Dumazet   net: Introduce fo...
932
933
  		rcu_read_lock();
  		for_each_netdev_rcu(&init_net, dev) {
7562f876c   Pavel Emelianov   [NET]: Rework dev...
934
  			if (!dev->dn_ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
  				continue;
bef55aebd   David S. Miller   decnet: Convert t...
936
  			if (!dn_dev_islocal(dev, oldflp->saddr))
9bbf28a1f   Patrick Caulfield   [DECNET]: Fix for...
937
  				continue;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
938
  			if ((dev->flags & IFF_LOOPBACK) &&
bef55aebd   David S. Miller   decnet: Convert t...
939
940
  			    oldflp->daddr &&
  			    !dn_dev_islocal(dev, oldflp->daddr))
9bbf28a1f   Patrick Caulfield   [DECNET]: Fix for...
941
  				continue;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
942
943
  
  			dev_out = dev;
9bbf28a1f   Patrick Caulfield   [DECNET]: Fix for...
944
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
  		}
c6d14c845   Eric Dumazet   net: Introduce fo...
946
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
948
949
950
951
952
953
954
  		if (dev_out == NULL)
  			goto out;
  		dev_hold(dev_out);
  source_ok:
  		;
  	}
  
  	/* No destination? Assume its local */
bef55aebd   David S. Miller   decnet: Convert t...
955
956
  	if (!fld.daddr) {
  		fld.daddr = fld.saddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
958
959
960
  
  		err = -EADDRNOTAVAIL;
  		if (dev_out)
  			dev_put(dev_out);
2774c7aba   Eric W. Biederman   [NET]: Make the l...
961
  		dev_out = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
  		dev_hold(dev_out);
bef55aebd   David S. Miller   decnet: Convert t...
963
964
965
  		if (!fld.daddr) {
  			fld.daddr =
  			fld.saddr = dnet_select_source(dev_out, 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
  						       RT_SCOPE_HOST);
bef55aebd   David S. Miller   decnet: Convert t...
967
  			if (!fld.daddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
969
  				goto out;
  		}
bef55aebd   David S. Miller   decnet: Convert t...
970
  		fld.flowidn_oif = init_net.loopback_dev->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
972
973
974
975
976
977
  		res.type = RTN_LOCAL;
  		goto make_route;
  	}
  
  	if (decnet_debug_level & 16)
  		printk(KERN_DEBUG
  		       "dn_route_output_slow: initial checks complete."
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
978
979
  		       " dst=%o4x src=%04x oif=%d try_hard=%d
  ",
bef55aebd   David S. Miller   decnet: Convert t...
980
981
  		       le16_to_cpu(fld.daddr), le16_to_cpu(fld.saddr),
  		       fld.flowidn_oif, try_hard);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
982
983
984
985
986
987
988
  
  	/*
  	 * N.B. If the kernel is compiled without router support then
  	 * dn_fib_lookup() will evaluate to non-zero so this if () block
  	 * will always be executed.
  	 */
  	err = -ESRCH;
bef55aebd   David S. Miller   decnet: Convert t...
989
  	if (try_hard || (err = dn_fib_lookup(&fld, &res)) != 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
991
992
993
  		struct dn_dev *dn_db;
  		if (err != -ESRCH)
  			goto out;
  		/*
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
994
  		 * Here the fallback is basically the standard algorithm for
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
996
997
998
999
  		 * routing in endnodes which is described in the DECnet routing
  		 * docs
  		 *
  		 * If we are not trying hard, look in neighbour cache.
  		 * The result is tested to ensure that if a specific output
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1000
  		 * device/source address was requested, then we honour that
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
1002
1003
  		 * here
  		 */
  		if (!try_hard) {
bef55aebd   David S. Miller   decnet: Convert t...
1004
  			neigh = neigh_lookup_nodev(&dn_neigh_table, &init_net, &fld.daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
  			if (neigh) {
bef55aebd   David S. Miller   decnet: Convert t...
1006
1007
1008
  				if ((oldflp->flowidn_oif &&
  				    (neigh->dev->ifindex != oldflp->flowidn_oif)) ||
  				    (oldflp->saddr &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009
  				    (!dn_dev_islocal(neigh->dev,
bef55aebd   David S. Miller   decnet: Convert t...
1010
  						     oldflp->saddr)))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011
1012
1013
1014
1015
  					neigh_release(neigh);
  					neigh = NULL;
  				} else {
  					if (dev_out)
  						dev_put(dev_out);
bef55aebd   David S. Miller   decnet: Convert t...
1016
  					if (dn_dev_islocal(neigh->dev, fld.daddr)) {
2774c7aba   Eric W. Biederman   [NET]: Make the l...
1017
  						dev_out = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
  						res.type = RTN_LOCAL;
  					} else {
  						dev_out = neigh->dev;
  					}
  					dev_hold(dev_out);
  					goto select_source;
  				}
  			}
  		}
  
  		/* Not there? Perhaps its a local address */
  		if (dev_out == NULL)
  			dev_out = dn_dev_get_default();
  		err = -ENODEV;
  		if (dev_out == NULL)
  			goto out;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1034
  		dn_db = rcu_dereference_raw(dev_out->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
  		/* Possible improvement - check all devices for local addr */
bef55aebd   David S. Miller   decnet: Convert t...
1036
  		if (dn_dev_islocal(dev_out, fld.daddr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1037
  			dev_put(dev_out);
2774c7aba   Eric W. Biederman   [NET]: Make the l...
1038
  			dev_out = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
  			dev_hold(dev_out);
  			res.type = RTN_LOCAL;
  			goto select_source;
  		}
  		/* Not local either.... try sending it to the default router */
  		neigh = neigh_clone(dn_db->router);
  		BUG_ON(neigh && neigh->dev != dev_out);
  
  		/* Ok then, we assume its directly connected and move on */
  select_source:
  		if (neigh)
  			gateway = ((struct dn_neigh *)neigh)->addr;
  		if (gateway == 0)
bef55aebd   David S. Miller   decnet: Convert t...
1052
1053
1054
1055
1056
1057
1058
  			gateway = fld.daddr;
  		if (fld.saddr == 0) {
  			fld.saddr = dnet_select_source(dev_out, gateway,
  						       res.type == RTN_LOCAL ?
  						       RT_SCOPE_HOST :
  						       RT_SCOPE_LINK);
  			if (fld.saddr == 0 && res.type != RTN_LOCAL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059
1060
  				goto e_addr;
  		}
bef55aebd   David S. Miller   decnet: Convert t...
1061
  		fld.flowidn_oif = dev_out->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
1063
1064
1065
1066
1067
1068
1069
  		goto make_route;
  	}
  	free_res = 1;
  
  	if (res.type == RTN_NAT)
  		goto e_inval;
  
  	if (res.type == RTN_LOCAL) {
bef55aebd   David S. Miller   decnet: Convert t...
1070
1071
  		if (!fld.saddr)
  			fld.saddr = fld.daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1072
1073
  		if (dev_out)
  			dev_put(dev_out);
2774c7aba   Eric W. Biederman   [NET]: Make the l...
1074
  		dev_out = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1075
  		dev_hold(dev_out);
bef55aebd   David S. Miller   decnet: Convert t...
1076
  		fld.flowidn_oif = dev_out->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1077
1078
1079
1080
1081
  		if (res.fi)
  			dn_fib_info_put(res.fi);
  		res.fi = NULL;
  		goto make_route;
  	}
bef55aebd   David S. Miller   decnet: Convert t...
1082
1083
  	if (res.fi->fib_nhs > 1 && fld.flowidn_oif == 0)
  		dn_fib_select_multipath(&fld, &res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1084

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1085
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
1087
1088
  	 * We could add some logic to deal with default routes here and
  	 * get rid of some of the special casing above.
  	 */
bef55aebd   David S. Miller   decnet: Convert t...
1089
1090
  	if (!fld.saddr)
  		fld.saddr = DN_FIB_RES_PREFSRC(res);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1091

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1092
1093
1094
1095
  	if (dev_out)
  		dev_put(dev_out);
  	dev_out = DN_FIB_RES_DEV(res);
  	dev_hold(dev_out);
bef55aebd   David S. Miller   decnet: Convert t...
1096
  	fld.flowidn_oif = dev_out->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
1098
1099
1100
1101
  	gateway = DN_FIB_RES_GW(res);
  
  make_route:
  	if (dev_out->flags & IFF_LOOPBACK)
  		flags |= RTCF_LOCAL;
5c1e6aa30   David S. Miller   net: Make dst_all...
1102
  	rt = dst_alloc(&dn_dst_ops, dev_out, 1, 0, DST_HOST);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1103
1104
  	if (rt == NULL)
  		goto e_nobufs;
cf9116622   David S. Miller   net: Use non-zero...
1105
  	memset(&rt->fld, 0, sizeof(rt->fld));
bef55aebd   David S. Miller   decnet: Convert t...
1106
1107
1108
1109
1110
  	rt->fld.saddr        = oldflp->saddr;
  	rt->fld.daddr        = oldflp->daddr;
  	rt->fld.flowidn_oif  = oldflp->flowidn_oif;
  	rt->fld.flowidn_iif  = 0;
  	rt->fld.flowidn_mark = oldflp->flowidn_mark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1111

bef55aebd   David S. Miller   decnet: Convert t...
1112
1113
1114
1115
  	rt->rt_saddr      = fld.saddr;
  	rt->rt_daddr      = fld.daddr;
  	rt->rt_gateway    = gateway ? gateway : fld.daddr;
  	rt->rt_local_src  = fld.saddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1116

bef55aebd   David S. Miller   decnet: Convert t...
1117
1118
  	rt->rt_dst_map    = fld.daddr;
  	rt->rt_src_map    = fld.saddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1119

69cce1d14   David S. Miller   net: Abstract dst...
1120
  	dst_set_neighbour(&rt->dst, neigh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121
  	neigh = NULL;
d8d1f30b9   Changli Gao   net-next: remove ...
1122
1123
1124
  	rt->dst.lastuse = jiffies;
  	rt->dst.output  = dn_output;
  	rt->dst.input   = dn_rt_bug;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1125
1126
  	rt->rt_flags      = flags;
  	if (flags & RTCF_LOCAL)
d8d1f30b9   Changli Gao   net-next: remove ...
1127
  		rt->dst.input = dn_nsp_rx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1128
1129
1130
1131
  
  	err = dn_rt_set_next_hop(rt, &res);
  	if (err)
  		goto e_neighbour;
bef55aebd   David S. Miller   decnet: Convert t...
1132
  	hash = dn_hash(rt->fld.saddr, rt->fld.daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
  	dn_insert_route(rt, hash, (struct dn_route **)pprt);
  
  done:
  	if (neigh)
  		neigh_release(neigh);
  	if (free_res)
  		dn_fib_res_put(&res);
  	if (dev_out)
  		dev_put(dev_out);
  out:
  	return err;
  
  e_addr:
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1146
1147
  	err = -EADDRNOTAVAIL;
  	goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1148
1149
1150
1151
1152
1153
1154
  e_inval:
  	err = -EINVAL;
  	goto done;
  e_nobufs:
  	err = -ENOBUFS;
  	goto done;
  e_neighbour:
d8d1f30b9   Changli Gao   net-next: remove ...
1155
  	dst_free(&rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1156
1157
1158
1159
1160
1161
1162
  	goto e_nobufs;
  }
  
  
  /*
   * N.B. The flags may be moved into the flowi at some future stage.
   */
bef55aebd   David S. Miller   decnet: Convert t...
1163
  static int __dn_route_output_key(struct dst_entry **pprt, const struct flowidn *flp, int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1164
  {
bef55aebd   David S. Miller   decnet: Convert t...
1165
  	unsigned hash = dn_hash(flp->saddr, flp->daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1166
1167
1168
1169
  	struct dn_route *rt = NULL;
  
  	if (!(flags & MSG_TRYHARD)) {
  		rcu_read_lock_bh();
a898def29   Paul E. McKenney   net: Add checking...
1170
  		for (rt = rcu_dereference_bh(dn_rt_hash_table[hash].chain); rt;
d8d1f30b9   Changli Gao   net-next: remove ...
1171
  			rt = rcu_dereference_bh(rt->dst.dn_next)) {
bef55aebd   David S. Miller   decnet: Convert t...
1172
1173
1174
  			if ((flp->daddr == rt->fld.daddr) &&
  			    (flp->saddr == rt->fld.saddr) &&
  			    (flp->flowidn_mark == rt->fld.flowidn_mark) &&
c75379676   David S. Miller   ipv4: Make rt->fl...
1175
  			    dn_is_output_route(rt) &&
bef55aebd   David S. Miller   decnet: Convert t...
1176
  			    (rt->fld.flowidn_oif == flp->flowidn_oif)) {
d8d1f30b9   Changli Gao   net-next: remove ...
1177
  				dst_use(&rt->dst, jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1178
  				rcu_read_unlock_bh();
d8d1f30b9   Changli Gao   net-next: remove ...
1179
  				*pprt = &rt->dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1180
1181
1182
1183
1184
1185
1186
1187
  				return 0;
  			}
  		}
  		rcu_read_unlock_bh();
  	}
  
  	return dn_route_output_slow(pprt, flp, flags);
  }
bef55aebd   David S. Miller   decnet: Convert t...
1188
  static int dn_route_output_key(struct dst_entry **pprt, struct flowidn *flp, int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1189
1190
1191
1192
  {
  	int err;
  
  	err = __dn_route_output_key(pprt, flp, flags);
bef55aebd   David S. Miller   decnet: Convert t...
1193
1194
1195
  	if (err == 0 && flp->flowidn_proto) {
  		*pprt = xfrm_lookup(&init_net, *pprt,
  				    flowidn_to_flowi(flp), NULL, 0);
452edd598   David S. Miller   xfrm: Return dst ...
1196
1197
1198
1199
  		if (IS_ERR(*pprt)) {
  			err = PTR_ERR(*pprt);
  			*pprt = NULL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1200
1201
1202
  	}
  	return err;
  }
bef55aebd   David S. Miller   decnet: Convert t...
1203
  int dn_route_output_sock(struct dst_entry **pprt, struct flowidn *fl, struct sock *sk, int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1204
1205
1206
1207
  {
  	int err;
  
  	err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD);
bef55aebd   David S. Miller   decnet: Convert t...
1208
  	if (err == 0 && fl->flowidn_proto) {
80c0bc9e3   David S. Miller   xfrm: Kill XFRM_L...
1209
  		if (!(flags & MSG_DONTWAIT))
bef55aebd   David S. Miller   decnet: Convert t...
1210
1211
1212
  			fl->flowidn_flags |= FLOWI_FLAG_CAN_SLEEP;
  		*pprt = xfrm_lookup(&init_net, *pprt,
  				    flowidn_to_flowi(fl), sk, 0);
452edd598   David S. Miller   xfrm: Return dst ...
1213
1214
1215
1216
  		if (IS_ERR(*pprt)) {
  			err = PTR_ERR(*pprt);
  			*pprt = NULL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
  	}
  	return err;
  }
  
  static int dn_route_input_slow(struct sk_buff *skb)
  {
  	struct dn_route *rt = NULL;
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	struct net_device *in_dev = skb->dev;
  	struct net_device *out_dev = NULL;
  	struct dn_dev *dn_db;
  	struct neighbour *neigh = NULL;
  	unsigned hash;
  	int flags = 0;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
1231
1232
  	__le16 gateway = 0;
  	__le16 local_src = 0;
bef55aebd   David S. Miller   decnet: Convert t...
1233
1234
1235
1236
1237
1238
  	struct flowidn fld = {
  		.daddr = cb->dst,
  		.saddr = cb->src,
  		.flowidn_scope = RT_SCOPE_UNIVERSE,
  		.flowidn_mark = skb->mark,
  		.flowidn_iif = skb->dev->ifindex,
1d28f42c1   David S. Miller   net: Put flowi_* ...
1239
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1240
1241
1242
1243
1244
  	struct dn_fib_res res = { .fi = NULL, .type = RTN_UNREACHABLE };
  	int err = -EINVAL;
  	int free_res = 0;
  
  	dev_hold(in_dev);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1245
  	if ((dn_db = rcu_dereference(in_dev->dn_ptr)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1246
1247
1248
  		goto out;
  
  	/* Zero source addresses are not allowed */
bef55aebd   David S. Miller   decnet: Convert t...
1249
  	if (fld.saddr == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
  		goto out;
  
  	/*
  	 * In this case we've just received a packet from a source
  	 * outside ourselves pretending to come from us. We don't
  	 * allow it any further to prevent routing loops, spoofing and
  	 * other nasties. Loopback packets already have the dst attached
  	 * so this only affects packets which have originated elsewhere.
  	 */
  	err  = -ENOTUNIQ;
  	if (dn_dev_islocal(in_dev, cb->src))
  		goto out;
bef55aebd   David S. Miller   decnet: Convert t...
1262
  	err = dn_fib_lookup(&fld, &res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
  	if (err) {
  		if (err != -ESRCH)
  			goto out;
  		/*
  		 * Is the destination us ?
  		 */
  		if (!dn_dev_islocal(in_dev, cb->dst))
  			goto e_inval;
  
  		res.type = RTN_LOCAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1273
  	} else {
bef55aebd   David S. Miller   decnet: Convert t...
1274
  		__le16 src_map = fld.saddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
  		free_res = 1;
  
  		out_dev = DN_FIB_RES_DEV(res);
  		if (out_dev == NULL) {
  			if (net_ratelimit())
  				printk(KERN_CRIT "Bug in dn_route_input_slow() "
  						 "No output device
  ");
  			goto e_inval;
  		}
  		dev_hold(out_dev);
  
  		if (res.r)
bef55aebd   David S. Miller   decnet: Convert t...
1288
  			src_map = fld.saddr; /* no NAT support for now */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1289
1290
1291
  
  		gateway = DN_FIB_RES_GW(res);
  		if (res.type == RTN_NAT) {
bef55aebd   David S. Miller   decnet: Convert t...
1292
  			fld.daddr = dn_fib_rules_map_destination(fld.daddr, &res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1293
1294
  			dn_fib_res_put(&res);
  			free_res = 0;
bef55aebd   David S. Miller   decnet: Convert t...
1295
  			if (dn_fib_lookup(&fld, &res))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1296
1297
1298
1299
1300
  				goto e_inval;
  			free_res = 1;
  			if (res.type != RTN_UNICAST)
  				goto e_inval;
  			flags |= RTCF_DNAT;
bef55aebd   David S. Miller   decnet: Convert t...
1301
  			gateway = fld.daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1302
  		}
bef55aebd   David S. Miller   decnet: Convert t...
1303
  		fld.saddr = src_map;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
  	}
  
  	switch(res.type) {
  	case RTN_UNICAST:
  		/*
  		 * Forwarding check here, we only check for forwarding
  		 * being turned off, if you want to only forward intra
  		 * area, its up to you to set the routing tables up
  		 * correctly.
  		 */
  		if (dn_db->parms.forwarding == 0)
  			goto e_inval;
bef55aebd   David S. Miller   decnet: Convert t...
1316
1317
  		if (res.fi->fib_nhs > 1 && fld.flowidn_oif == 0)
  			dn_fib_select_multipath(&fld, &res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1319
  		/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
  		 * Check for out_dev == in_dev. We use the RTCF_DOREDIRECT
  		 * flag as a hint to set the intra-ethernet bit when
  		 * forwarding. If we've got NAT in operation, we don't do
  		 * this optimisation.
  		 */
  		if (out_dev == in_dev && !(flags & RTCF_NAT))
  			flags |= RTCF_DOREDIRECT;
  
  		local_src = DN_FIB_RES_PREFSRC(res);
  
  	case RTN_BLACKHOLE:
  	case RTN_UNREACHABLE:
  		break;
  	case RTN_LOCAL:
  		flags |= RTCF_LOCAL;
bef55aebd   David S. Miller   decnet: Convert t...
1335
1336
  		fld.saddr = cb->dst;
  		fld.daddr = cb->src;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1337
1338
1339
1340
1341
1342
  
  		/* Routing tables gave us a gateway */
  		if (gateway)
  			goto make_route;
  
  		/* Packet was intra-ethernet, so we know its on-link */
3a31b9d2f   Steven Whitehouse   [DECNET]: Fix inp...
1343
  		if (cb->rt_flags & DN_RT_F_IE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
  			gateway = cb->src;
  			flags |= RTCF_DIRECTSRC;
  			goto make_route;
  		}
  
  		/* Use the default router if there is one */
  		neigh = neigh_clone(dn_db->router);
  		if (neigh) {
  			gateway = ((struct dn_neigh *)neigh)->addr;
  			goto make_route;
  		}
  
  		/* Close eyes and pray */
  		gateway = cb->src;
  		flags |= RTCF_DIRECTSRC;
  		goto make_route;
  	default:
  		goto e_inval;
  	}
  
  make_route:
5c1e6aa30   David S. Miller   net: Make dst_all...
1365
  	rt = dst_alloc(&dn_dst_ops, out_dev, 0, 0, DST_HOST);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1366
1367
  	if (rt == NULL)
  		goto e_nobufs;
cf9116622   David S. Miller   net: Use non-zero...
1368
  	memset(&rt->fld, 0, sizeof(rt->fld));
bef55aebd   David S. Miller   decnet: Convert t...
1369
1370
1371
  	rt->rt_saddr      = fld.saddr;
  	rt->rt_daddr      = fld.daddr;
  	rt->rt_gateway    = fld.daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1372
1373
1374
  	if (gateway)
  		rt->rt_gateway = gateway;
  	rt->rt_local_src  = local_src ? local_src : rt->rt_saddr;
bef55aebd   David S. Miller   decnet: Convert t...
1375
1376
  	rt->rt_dst_map    = fld.daddr;
  	rt->rt_src_map    = fld.saddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1377

bef55aebd   David S. Miller   decnet: Convert t...
1378
1379
1380
1381
1382
  	rt->fld.saddr        = cb->src;
  	rt->fld.daddr        = cb->dst;
  	rt->fld.flowidn_oif  = 0;
  	rt->fld.flowidn_iif  = in_dev->ifindex;
  	rt->fld.flowidn_mark = fld.flowidn_mark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383

69cce1d14   David S. Miller   net: Abstract dst...
1384
  	dst_set_neighbour(&rt->dst, neigh);
d8d1f30b9   Changli Gao   net-next: remove ...
1385
1386
  	rt->dst.lastuse = jiffies;
  	rt->dst.output = dn_rt_bug;
06f8fe11b   Joe Perches   decnet: Reduce sw...
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
  	switch (res.type) {
  	case RTN_UNICAST:
  		rt->dst.input = dn_forward;
  		break;
  	case RTN_LOCAL:
  		rt->dst.output = dn_output;
  		rt->dst.input = dn_nsp_rx;
  		rt->dst.dev = in_dev;
  		flags |= RTCF_LOCAL;
  		break;
  	default:
  	case RTN_UNREACHABLE:
  	case RTN_BLACKHOLE:
  		rt->dst.input = dst_discard;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1401
1402
  	}
  	rt->rt_flags = flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1403
1404
1405
1406
  
  	err = dn_rt_set_next_hop(rt, &res);
  	if (err)
  		goto e_neighbour;
bef55aebd   David S. Miller   decnet: Convert t...
1407
  	hash = dn_hash(rt->fld.saddr, rt->fld.daddr);
adf30907d   Eric Dumazet   net: skb->dst acc...
1408
  	dn_insert_route(rt, hash, &rt);
d8d1f30b9   Changli Gao   net-next: remove ...
1409
  	skb_dst_set(skb, &rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
  
  done:
  	if (neigh)
  		neigh_release(neigh);
  	if (free_res)
  		dn_fib_res_put(&res);
  	dev_put(in_dev);
  	if (out_dev)
  		dev_put(out_dev);
  out:
  	return err;
  
  e_inval:
  	err = -EINVAL;
  	goto done;
  
  e_nobufs:
  	err = -ENOBUFS;
  	goto done;
  
  e_neighbour:
d8d1f30b9   Changli Gao   net-next: remove ...
1431
  	dst_free(&rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1432
1433
  	goto done;
  }
5eaa65b24   Roel Kluin   net: Make static
1434
  static int dn_route_input(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1435
1436
1437
1438
  {
  	struct dn_route *rt;
  	struct dn_skb_cb *cb = DN_SKB_CB(skb);
  	unsigned hash = dn_hash(cb->src, cb->dst);
adf30907d   Eric Dumazet   net: skb->dst acc...
1439
  	if (skb_dst(skb))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1440
1441
1442
1443
  		return 0;
  
  	rcu_read_lock();
  	for(rt = rcu_dereference(dn_rt_hash_table[hash].chain); rt != NULL;
d8d1f30b9   Changli Gao   net-next: remove ...
1444
  	    rt = rcu_dereference(rt->dst.dn_next)) {
bef55aebd   David S. Miller   decnet: Convert t...
1445
1446
1447
1448
1449
  		if ((rt->fld.saddr == cb->src) &&
  		    (rt->fld.daddr == cb->dst) &&
  		    (rt->fld.flowidn_oif == 0) &&
  		    (rt->fld.flowidn_mark == skb->mark) &&
  		    (rt->fld.flowidn_iif == cb->iif)) {
d8d1f30b9   Changli Gao   net-next: remove ...
1450
  			dst_use(&rt->dst, jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1451
  			rcu_read_unlock();
adf30907d   Eric Dumazet   net: skb->dst acc...
1452
  			skb_dst_set(skb, (struct dst_entry *)rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1453
1454
1455
1456
1457
1458
1459
  			return 0;
  		}
  	}
  	rcu_read_unlock();
  
  	return dn_route_input_slow(skb);
  }
b6544c0b4   Jamal Hadi Salim   [NETLINK]: Correc...
1460
1461
  static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
  			   int event, int nowait, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1462
  {
adf30907d   Eric Dumazet   net: skb->dst acc...
1463
  	struct dn_route *rt = (struct dn_route *)skb_dst(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1464
1465
  	struct rtmsg *r;
  	struct nlmsghdr *nlh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1466
  	unsigned char *b = skb_tail_pointer(skb);
e3703b3de   Thomas Graf   [RTNETLINK]: Add ...
1467
  	long expires;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1468

b6544c0b4   Jamal Hadi Salim   [NETLINK]: Correc...
1469
  	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*r), flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1470
  	r = NLMSG_DATA(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1471
1472
1473
1474
1475
  	r->rtm_family = AF_DECnet;
  	r->rtm_dst_len = 16;
  	r->rtm_src_len = 0;
  	r->rtm_tos = 0;
  	r->rtm_table = RT_TABLE_MAIN;
9e762a4a8   Patrick McHardy   [NET]: Introduce ...
1476
  	RTA_PUT_U32(skb, RTA_TABLE, RT_TABLE_MAIN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1477
1478
1479
1480
1481
1482
1483
  	r->rtm_type = rt->rt_type;
  	r->rtm_flags = (rt->rt_flags & ~0xFFFF) | RTM_F_CLONED;
  	r->rtm_scope = RT_SCOPE_UNIVERSE;
  	r->rtm_protocol = RTPROT_UNSPEC;
  	if (rt->rt_flags & RTCF_NOTIFY)
  		r->rtm_flags |= RTM_F_NOTIFY;
  	RTA_PUT(skb, RTA_DST, 2, &rt->rt_daddr);
bef55aebd   David S. Miller   decnet: Convert t...
1484
  	if (rt->fld.saddr) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1485
  		r->rtm_src_len = 16;
bef55aebd   David S. Miller   decnet: Convert t...
1486
  		RTA_PUT(skb, RTA_SRC, 2, &rt->fld.saddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1487
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
1488
1489
  	if (rt->dst.dev)
  		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->dst.dev->ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1490
1491
1492
1493
1494
1495
1496
1497
  	/*
  	 * Note to self - change this if input routes reverse direction when
  	 * they deal only with inputs and not with replies like they do
  	 * currently.
  	 */
  	RTA_PUT(skb, RTA_PREFSRC, 2, &rt->rt_local_src);
  	if (rt->rt_daddr != rt->rt_gateway)
  		RTA_PUT(skb, RTA_GATEWAY, 2, &rt->rt_gateway);
defb3519a   David S. Miller   net: Abstract awa...
1498
  	if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1499
  		goto rtattr_failure;
d8d1f30b9   Changli Gao   net-next: remove ...
1500
1501
1502
  	expires = rt->dst.expires ? rt->dst.expires - jiffies : 0;
  	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0, expires,
  			       rt->dst.error) < 0)
e3703b3de   Thomas Graf   [RTNETLINK]: Add ...
1503
  		goto rtattr_failure;
c75379676   David S. Miller   ipv4: Make rt->fl...
1504
  	if (dn_is_input_route(rt))
bef55aebd   David S. Miller   decnet: Convert t...
1505
  		RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->fld.flowidn_iif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1506

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1507
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1508
1509
1510
1511
  	return skb->len;
  
  nlmsg_failure:
  rtattr_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
1512
  	nlmsg_trim(skb, b);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1513
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1514
1515
1516
1517
1518
  }
  
  /*
   * This is called by both endnodes and routers now.
   */
fa34ddd73   Thomas Graf   [DECNet]: Use rtn...
1519
  static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1520
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1521
  	struct net *net = sock_net(in_skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1522
1523
1524
1525
1526
1527
  	struct rtattr **rta = arg;
  	struct rtmsg *rtm = NLMSG_DATA(nlh);
  	struct dn_route *rt = NULL;
  	struct dn_skb_cb *cb;
  	int err;
  	struct sk_buff *skb;
bef55aebd   David S. Miller   decnet: Convert t...
1528
  	struct flowidn fld;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1529

09ad9bc75   Octavian Purdila   net: use net_eq t...
1530
  	if (!net_eq(net, &init_net))
b854272b3   Denis V. Lunev   [NET]: Modify all...
1531
  		return -EINVAL;
bef55aebd   David S. Miller   decnet: Convert t...
1532
1533
  	memset(&fld, 0, sizeof(fld));
  	fld.flowidn_proto = DNPROTO_NSP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1534
1535
1536
1537
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (skb == NULL)
  		return -ENOBUFS;
459a98ed8   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1538
  	skb_reset_mac_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1539
1540
1541
  	cb = DN_SKB_CB(skb);
  
  	if (rta[RTA_SRC-1])
bef55aebd   David S. Miller   decnet: Convert t...
1542
  		memcpy(&fld.saddr, RTA_DATA(rta[RTA_SRC-1]), 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1543
  	if (rta[RTA_DST-1])
bef55aebd   David S. Miller   decnet: Convert t...
1544
  		memcpy(&fld.daddr, RTA_DATA(rta[RTA_DST-1]), 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1545
  	if (rta[RTA_IIF-1])
bef55aebd   David S. Miller   decnet: Convert t...
1546
  		memcpy(&fld.flowidn_iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1547

bef55aebd   David S. Miller   decnet: Convert t...
1548
  	if (fld.flowidn_iif) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1549
  		struct net_device *dev;
bef55aebd   David S. Miller   decnet: Convert t...
1550
  		if ((dev = dev_get_by_index(&init_net, fld.flowidn_iif)) == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1551
1552
1553
1554
1555
1556
1557
1558
  			kfree_skb(skb);
  			return -ENODEV;
  		}
  		if (!dev->dn_ptr) {
  			dev_put(dev);
  			kfree_skb(skb);
  			return -ENODEV;
  		}
b98999dc3   YOSHIFUJI Hideaki   [DECNET]: Use hto...
1559
  		skb->protocol = htons(ETH_P_DNA_RT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1560
  		skb->dev = dev;
bef55aebd   David S. Miller   decnet: Convert t...
1561
1562
  		cb->src = fld.saddr;
  		cb->dst = fld.daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1563
1564
1565
1566
  		local_bh_disable();
  		err = dn_route_input(skb);
  		local_bh_enable();
  		memset(cb, 0, sizeof(struct dn_skb_cb));
adf30907d   Eric Dumazet   net: skb->dst acc...
1567
  		rt = (struct dn_route *)skb_dst(skb);
d8d1f30b9   Changli Gao   net-next: remove ...
1568
1569
  		if (!err && -rt->dst.error)
  			err = rt->dst.error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1570
1571
1572
1573
  	} else {
  		int oif = 0;
  		if (rta[RTA_OIF - 1])
  			memcpy(&oif, RTA_DATA(rta[RTA_OIF - 1]), sizeof(int));
bef55aebd   David S. Miller   decnet: Convert t...
1574
1575
  		fld.flowidn_oif = oif;
  		err = dn_route_output_key((struct dst_entry **)&rt, &fld, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1576
1577
1578
1579
1580
1581
1582
  	}
  
  	if (skb->dev)
  		dev_put(skb->dev);
  	skb->dev = NULL;
  	if (err)
  		goto out_free;
d8d1f30b9   Changli Gao   net-next: remove ...
1583
  	skb_dst_set(skb, &rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1584
1585
  	if (rtm->rtm_flags & RTM_F_NOTIFY)
  		rt->rt_flags |= RTCF_NOTIFY;
b6544c0b4   Jamal Hadi Salim   [NETLINK]: Correc...
1586
  	err = dn_rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1587
1588
1589
1590
1591
1592
1593
  
  	if (err == 0)
  		goto out_free;
  	if (err < 0) {
  		err = -EMSGSIZE;
  		goto out_free;
  	}
97c53cacf   Denis V. Lunev   [NET]: Make rtnet...
1594
  	return rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
  
  out_free:
  	kfree_skb(skb);
  	return err;
  }
  
  /*
   * For routers, this is called from dn_fib_dump, but for endnodes its
   * called directly from the rtnetlink dispatch table.
   */
  int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb)
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1607
  	struct net *net = sock_net(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1608
1609
1610
  	struct dn_route *rt;
  	int h, s_h;
  	int idx, s_idx;
09ad9bc75   Octavian Purdila   net: use net_eq t...
1611
  	if (!net_eq(net, &init_net))
b854272b3   Denis V. Lunev   [NET]: Modify all...
1612
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
  	if (NLMSG_PAYLOAD(cb->nlh, 0) < sizeof(struct rtmsg))
  		return -EINVAL;
  	if (!(((struct rtmsg *)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED))
  		return 0;
  
  	s_h = cb->args[0];
  	s_idx = idx = cb->args[1];
  	for(h = 0; h <= dn_rt_hash_mask; h++) {
  		if (h < s_h)
  			continue;
  		if (h > s_h)
  			s_idx = 0;
  		rcu_read_lock_bh();
a898def29   Paul E. McKenney   net: Add checking...
1626
  		for(rt = rcu_dereference_bh(dn_rt_hash_table[h].chain), idx = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1627
  			rt;
d8d1f30b9   Changli Gao   net-next: remove ...
1628
  			rt = rcu_dereference_bh(rt->dst.dn_next), idx++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1629
1630
  			if (idx < s_idx)
  				continue;
d8d1f30b9   Changli Gao   net-next: remove ...
1631
  			skb_dst_set(skb, dst_clone(&rt->dst));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1632
  			if (dn_rt_fill_info(skb, NETLINK_CB(cb->skb).pid,
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1633
  					cb->nlh->nlmsg_seq, RTM_NEWROUTE,
b6544c0b4   Jamal Hadi Salim   [NETLINK]: Correc...
1634
  					1, NLM_F_MULTI) <= 0) {
adf30907d   Eric Dumazet   net: skb->dst acc...
1635
  				skb_dst_drop(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1636
1637
1638
  				rcu_read_unlock_bh();
  				goto done;
  			}
adf30907d   Eric Dumazet   net: skb->dst acc...
1639
  			skb_dst_drop(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
  		}
  		rcu_read_unlock_bh();
  	}
  
  done:
  	cb->args[0] = h;
  	cb->args[1] = idx;
  	return skb->len;
  }
  
  #ifdef CONFIG_PROC_FS
  struct dn_rt_cache_iter_state {
  	int bucket;
  };
  
  static struct dn_route *dn_rt_cache_get_first(struct seq_file *seq)
  {
  	struct dn_route *rt = NULL;
  	struct dn_rt_cache_iter_state *s = seq->private;
  
  	for(s->bucket = dn_rt_hash_mask; s->bucket >= 0; --s->bucket) {
  		rcu_read_lock_bh();
a898def29   Paul E. McKenney   net: Add checking...
1662
  		rt = rcu_dereference_bh(dn_rt_hash_table[s->bucket].chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1663
1664
1665
1666
  		if (rt)
  			break;
  		rcu_read_unlock_bh();
  	}
a898def29   Paul E. McKenney   net: Add checking...
1667
  	return rt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1668
1669
1670
1671
  }
  
  static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_route *rt)
  {
0d89d7944   Eric Dumazet   [DECNET] ROUTE: f...
1672
  	struct dn_rt_cache_iter_state *s = seq->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1673

fc766e4c4   Eric Dumazet   decnet: RCU conve...
1674
1675
  	rt = rcu_dereference_bh(rt->dst.dn_next);
  	while (!rt) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1676
1677
1678
1679
  		rcu_read_unlock_bh();
  		if (--s->bucket < 0)
  			break;
  		rcu_read_lock_bh();
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1680
  		rt = rcu_dereference_bh(dn_rt_hash_table[s->bucket].chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1681
  	}
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1682
  	return rt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
  }
  
  static void *dn_rt_cache_seq_start(struct seq_file *seq, loff_t *pos)
  {
  	struct dn_route *rt = dn_rt_cache_get_first(seq);
  
  	if (rt) {
  		while(*pos && (rt = dn_rt_cache_get_next(seq, rt)))
  			--*pos;
  	}
  	return *pos ? NULL : rt;
  }
  
  static void *dn_rt_cache_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct dn_route *rt = dn_rt_cache_get_next(seq, v);
  	++*pos;
  	return rt;
  }
  
  static void dn_rt_cache_seq_stop(struct seq_file *seq, void *v)
  {
  	if (v)
  		rcu_read_unlock_bh();
  }
  
  static int dn_rt_cache_seq_show(struct seq_file *seq, void *v)
  {
  	struct dn_route *rt = v;
  	char buf1[DN_ASCBUF_LEN], buf2[DN_ASCBUF_LEN];
  
  	seq_printf(seq, "%-8s %-7s %-7s %04d %04d %04d
  ",
d8d1f30b9   Changli Gao   net-next: remove ...
1716
  			rt->dst.dev ? rt->dst.dev->name : "*",
c4106aa88   Harvey Harrison   decnet: remove pr...
1717
1718
  			dn_addr2asc(le16_to_cpu(rt->rt_daddr), buf1),
  			dn_addr2asc(le16_to_cpu(rt->rt_saddr), buf2),
d8d1f30b9   Changli Gao   net-next: remove ...
1719
1720
1721
  			atomic_read(&rt->dst.__refcnt),
  			rt->dst.__use,
  			(int) dst_metric(&rt->dst, RTAX_RTT));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1722
  	return 0;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1723
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1724

56b3d975b   Philippe De Muyter   [NET]: Make all i...
1725
  static const struct seq_operations dn_rt_cache_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1726
1727
1728
1729
1730
1731
1732
1733
  	.start	= dn_rt_cache_seq_start,
  	.next	= dn_rt_cache_seq_next,
  	.stop	= dn_rt_cache_seq_stop,
  	.show	= dn_rt_cache_seq_show,
  };
  
  static int dn_rt_cache_seq_open(struct inode *inode, struct file *file)
  {
31164088d   Pavel Emelyanov   [DECNET]: Make de...
1734
1735
  	return seq_open_private(file, &dn_rt_cache_seq_ops,
  			sizeof(struct dn_rt_cache_iter_state));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1736
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
1737
  static const struct file_operations dn_rt_cache_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
  	.owner	 = THIS_MODULE,
  	.open	 = dn_rt_cache_seq_open,
  	.read	 = seq_read,
  	.llseek	 = seq_lseek,
  	.release = seq_release_private,
  };
  
  #endif /* CONFIG_PROC_FS */
  
  void __init dn_route_init(void)
  {
  	int i, goal, order;
e5d679f33   Alexey Dobriyan   [NET]: Use SLAB_P...
1750
1751
  	dn_dst_ops.kmem_cachep =
  		kmem_cache_create("dn_dst_cache", sizeof(struct dn_route), 0,
20c2df83d   Paul Mundt   mm: Remove slab d...
1752
  				  SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
fc66f95c6   Eric Dumazet   net dst: use a pe...
1753
  	dst_entries_init(&dn_dst_ops);
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1754
  	setup_timer(&dn_route_timer, dn_dst_check_expire, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1755
1756
  	dn_route_timer.expires = jiffies + decnet_dst_gc_interval * HZ;
  	add_timer(&dn_route_timer);
4481374ce   Jan Beulich   mm: replace vario...
1757
  	goal = totalram_pages >> (26 - PAGE_SHIFT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1758
1759
1760
  
  	for(order = 0; (1UL << order) < goal; order++)
  		/* NOTHING */;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
  	/*
  	 * Only want 1024 entries max, since the table is very, very unlikely
  	 * to be larger than that.
  	 */
  	while(order && ((((1UL << order) * PAGE_SIZE) /
  				sizeof(struct dn_rt_hash_bucket)) >= 2048))
  		order--;
  
  	do {
  		dn_rt_hash_mask = (1UL << order) * PAGE_SIZE /
  			sizeof(struct dn_rt_hash_bucket);
  		while(dn_rt_hash_mask & (dn_rt_hash_mask - 1))
  			dn_rt_hash_mask--;
  		dn_rt_hash_table = (struct dn_rt_hash_bucket *)
  			__get_free_pages(GFP_ATOMIC, order);
  	} while (dn_rt_hash_table == NULL && --order > 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1777
1778
  
  	if (!dn_rt_hash_table)
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1779
1780
  		panic("Failed to allocate DECnet route cache hash table
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1781

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1782
1783
1784
1785
  	printk(KERN_INFO
  		"DECnet: Routing cache hash table of %u buckets, %ldKbytes
  ",
  		dn_rt_hash_mask,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1786
1787
1788
  		(long)(dn_rt_hash_mask*sizeof(struct dn_rt_hash_bucket))/1024);
  
  	dn_rt_hash_mask--;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1789
1790
1791
1792
  	for(i = 0; i <= dn_rt_hash_mask; i++) {
  		spin_lock_init(&dn_rt_hash_table[i].lock);
  		dn_rt_hash_table[i].chain = NULL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1793

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1794
  	dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1795

457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
1796
  	proc_net_fops_create(&init_net, "decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops);
fa34ddd73   Thomas Graf   [DECNet]: Use rtn...
1797
1798
  
  #ifdef CONFIG_DECNET_ROUTER
c7ac8679b   Greg Rose   rtnetlink: Comput...
1799
1800
  	rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute,
  		      dn_fib_dump, NULL);
fa34ddd73   Thomas Graf   [DECNet]: Use rtn...
1801
1802
  #else
  	rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute,
c7ac8679b   Greg Rose   rtnetlink: Comput...
1803
  		      dn_cache_dump, NULL);
fa34ddd73   Thomas Graf   [DECNet]: Use rtn...
1804
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1805
1806
1807
1808
1809
1810
  }
  
  void __exit dn_route_cleanup(void)
  {
  	del_timer(&dn_route_timer);
  	dn_run_flush(0);
457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
1811
  	proc_net_remove(&init_net, "decnet_cache");
fc66f95c6   Eric Dumazet   net dst: use a pe...
1812
  	dst_entries_destroy(&dn_dst_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1813
  }