Blame view

net/decnet/dn_route.c 44.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
79
  #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>
  #include <asm/errno.h>
457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
80
  #include <net/net_namespace.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
81
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
  #include <net/neighbour.h>
  #include <net/dst.h>
  #include <net/flow.h>
a8731cbf6   Steven Whitehouse   [DECNET]: Covert ...
85
  #include <net/fib_rules.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
90
91
92
93
94
  #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...
95
  	struct dn_route __rcu *chain;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  	spinlock_t lock;
fca09fb73   Eric Dumazet   [DECNET] ROUTE: r...
97
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
102
103
104
105
106
107
108
  
  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:...
109
  static int dn_dst_gc(struct dst_ops *ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
  static struct dst_entry *dn_dst_check(struct dst_entry *, __u32);
0dbaee3b3   David S. Miller   net: Abstract def...
111
  static unsigned int dn_dst_default_advmss(const struct dst_entry *dst);
d33e45533   David S. Miller   net: Abstract def...
112
  static unsigned int dn_dst_default_mtu(const struct dst_entry *dst);
62fa8a846   David S. Miller   net: Implement re...
113
  static void dn_dst_destroy(struct dst_entry *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
117
118
119
120
121
122
123
  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);
  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...
124
  static DEFINE_TIMER(dn_rt_flush_timer, dn_run_flush, 0, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
  int decnet_dst_gc_interval = 2;
  
  static struct dst_ops dn_dst_ops = {
  	.family =		PF_DECnet,
09640e636   Harvey Harrison   net: replace uses...
129
  	.protocol =		cpu_to_be16(ETH_P_DNA_RT),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
  	.gc_thresh =		128,
  	.gc =			dn_dst_gc,
  	.check =		dn_dst_check,
0dbaee3b3   David S. Miller   net: Abstract def...
133
  	.default_advmss =	dn_dst_default_advmss,
d33e45533   David S. Miller   net: Abstract def...
134
  	.default_mtu =		dn_dst_default_mtu,
62fa8a846   David S. Miller   net: Implement re...
135
136
  	.cow_metrics =		dst_cow_metrics_generic,
  	.destroy =		dn_dst_destroy,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
  	.negative_advice =	dn_dst_negative_advice,
  	.link_failure =		dn_dst_link_failure,
  	.update_pmtu =		dn_dst_update_pmtu,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
  };
62fa8a846   David S. Miller   net: Implement re...
141
142
143
144
  static void dn_dst_destroy(struct dst_entry *dst)
  {
  	dst_destroy_metrics_generic(dst);
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
145
  static __inline__ unsigned dn_hash(__le16 src, __le16 dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
147
  	__u16 tmp = (__u16 __force)(src ^ dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
150
151
152
153
154
155
  	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 ...
156
  	call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
  }
  
  static inline void dnrt_drop(struct dn_route *rt)
  {
d8d1f30b9   Changli Gao   net-next: remove ...
161
162
  	dst_release(&rt->dst);
  	call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
166
167
  }
  
  static void dn_dst_check_expire(unsigned long dummy)
  {
  	int i;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
168
169
  	struct dn_route *rt;
  	struct dn_route __rcu **rtp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
  	unsigned long now = jiffies;
  	unsigned long expire = 120 * HZ;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
172
  	for (i = 0; i <= dn_rt_hash_mask; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
175
  		rtp = &dn_rt_hash_table[i].chain;
  
  		spin_lock(&dn_rt_hash_table[i].lock);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
176
177
  		while ((rt = rcu_dereference_protected(*rtp,
  						lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
d8d1f30b9   Changli Gao   net-next: remove ...
178
179
180
  			if (atomic_read(&rt->dst.__refcnt) ||
  					(now - rt->dst.lastuse) < expire) {
  				rtp = &rt->dst.dn_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
  				continue;
  			}
d8d1f30b9   Changli Gao   net-next: remove ...
183
184
  			*rtp = rt->dst.dn_next;
  			rt->dst.dn_next = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
191
192
193
194
  			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:...
195
  static int dn_dst_gc(struct dst_ops *ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
197
198
  	struct dn_route *rt;
  	struct dn_route __rcu **rtp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
  	int i;
  	unsigned long now = jiffies;
  	unsigned long expire = 10 * HZ;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
202
  	for (i = 0; i <= dn_rt_hash_mask; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
  
  		spin_lock_bh(&dn_rt_hash_table[i].lock);
  		rtp = &dn_rt_hash_table[i].chain;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
206
207
  		while ((rt = rcu_dereference_protected(*rtp,
  						lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
d8d1f30b9   Changli Gao   net-next: remove ...
208
209
210
  			if (atomic_read(&rt->dst.__refcnt) ||
  					(now - rt->dst.lastuse) < expire) {
  				rtp = &rt->dst.dn_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
  				continue;
  			}
d8d1f30b9   Changli Gao   net-next: remove ...
213
214
  			*rtp = rt->dst.dn_next;
  			rt->dst.dn_next = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  			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)
  {
  	u32 min_mtu = 230;
  	struct dn_dev *dn = dst->neighbour ?
fc766e4c4   Eric Dumazet   decnet: RCU conve...
238
  			    rcu_dereference_raw(dst->neighbour->dev->dn_ptr) : NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
  
  	if (dn && dn->use_long == 0)
  		min_mtu -= 6;
  	else
  		min_mtu -= 21;
5ffc02a15   Satoru SATOH   ip: Use inline fu...
244
  	if (dst_metric(dst, RTAX_MTU) > mtu && mtu >= min_mtu) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  		if (!(dst_metric_locked(dst, RTAX_MTU))) {
defb3519a   David S. Miller   net: Abstract awa...
246
  			dst_metric_set(dst, RTAX_MTU, mtu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
249
250
  			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...
251
252
  			u32 existing_mss = dst_metric_raw(dst, RTAX_ADVMSS);
  			if (!existing_mss || existing_mss > mss)
defb3519a   David S. Miller   net: Abstract awa...
253
  				dst_metric_set(dst, RTAX_ADVMSS, mss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
  		}
  	}
  }
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
257
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
   * 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
273
274
275
276
  }
  
  static inline int compare_keys(struct flowi *fl1, struct flowi *fl2)
  {
5811662b1   Changli Gao   net: use the macr...
277
278
  	return ((fl1->fld_dst ^ fl2->fld_dst) |
  		(fl1->fld_src ^ fl2->fld_src) |
47dcf0cb1   Thomas Graf   [NET]: Rethink ma...
279
  		(fl1->mark ^ fl2->mark) |
5811662b1   Changli Gao   net: use the macr...
280
  		(fl1->fld_scope ^ fl2->fld_scope) |
8238b218e   David S. Miller   [NET]: Do not mem...
281
282
  		(fl1->oif ^ fl2->oif) |
  		(fl1->iif ^ fl2->iif)) == 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
  }
  
  static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route **rp)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
287
288
  	struct dn_route *rth;
  	struct dn_route __rcu **rthp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
293
  	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...
294
295
  	while ((rth = rcu_dereference_protected(*rthp,
  						lockdep_is_held(&dn_rt_hash_table[hash].lock))) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
  		if (compare_keys(&rth->fl, &rt->fl)) {
  			/* Put it first */
d8d1f30b9   Changli Gao   net-next: remove ...
298
299
  			*rthp = rth->dst.dn_next;
  			rcu_assign_pointer(rth->dst.dn_next,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
  					   dn_rt_hash_table[hash].chain);
  			rcu_assign_pointer(dn_rt_hash_table[hash].chain, rth);
d8d1f30b9   Changli Gao   net-next: remove ...
302
  			dst_use(&rth->dst, now);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
308
  			spin_unlock_bh(&dn_rt_hash_table[hash].lock);
  
  			dnrt_drop(rt);
  			*rp = rth;
  			return 0;
  		}
d8d1f30b9   Changli Gao   net-next: remove ...
309
  		rthp = &rth->dst.dn_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
311
  	rcu_assign_pointer(rt->dst.dn_next, dn_rt_hash_table[hash].chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  	rcu_assign_pointer(dn_rt_hash_table[hash].chain, rt);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
313

d8d1f30b9   Changli Gao   net-next: remove ...
314
  	dst_use(&rt->dst, now);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
  	spin_unlock_bh(&dn_rt_hash_table[hash].lock);
  	*rp = rt;
  	return 0;
  }
5eaa65b24   Roel Kluin   net: Make static
319
  static void dn_run_flush(unsigned long dummy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
  {
  	int i;
  	struct dn_route *rt, *next;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
323
  	for (i = 0; i < dn_rt_hash_mask; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
  		spin_lock_bh(&dn_rt_hash_table[i].lock);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
325
  		if ((rt = xchg((struct dn_route **)&dn_rt_hash_table[i].chain, NULL)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
  			goto nothing_to_declare;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
327
328
329
  		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
330
331
332
333
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
  			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 ...
383
384
  	__le16 *src;
  	__le16 *dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
  
  	/* Add back headers */
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
387
  	skb_push(skb, skb->data - skb_network_header(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
390
391
392
393
394
395
  
  	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 ...
396
  	dst = (__le16 *)ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
  	ptr += 2;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
398
  	src = (__le16 *)ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
400
  	ptr += 2;
  	*ptr = 0; /* Zero hop count */
a0bffffc1   Ilpo Järvinen   net/*: use linux/...
401
  	swap(*src, *dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
  
  	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...
421
  	skb_push(skb, skb->data - skb_network_header(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
425
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
  
  	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...
462
  	struct dn_skb_cb *cb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
  	int err;
  
  	if ((err = dn_route_input(skb)) == 0)
  		return dst_input(skb);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
467
  	cb = DN_SKB_CB(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
  	if (decnet_debug_level & 4) {
  		char *devname = skb->dev ? skb->dev->name : "???";
fc766e4c4   Eric Dumazet   decnet: RCU conve...
470

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
472
473
  		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 ...
474
  			(int)cb->rt_flags, devname, skb->len,
c4106aa88   Harvey Harrison   decnet: remove pr...
475
  			le16_to_cpu(cb->src), le16_to_cpu(cb->dst),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
  			err, skb->pkt_type);
  	}
  
  	if ((skb->pkt_type == PACKET_HOST) && (cb->rt_flags & DN_RT_F_RQR)) {
  		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);
  		}
  	}
  
  	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...
501
  	skb_reset_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502

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

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
510
511
  	/* Source info */
  	ptr += 2;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
512
  	cb->src = dn_eth2dn(ptr);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
513
514
515
516
517
518
  	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
519

5d877d876   Jan Engelhardt   netfilter: decnet...
520
521
  	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
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
  
  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...
539
  	skb_reset_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540

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

5d877d876   Jan Engelhardt   netfilter: decnet...
547
548
  	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
549
550
  
  drop_it:
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
551
552
  	kfree_skb(skb);
  	return NET_RX_DROP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
  }
  
  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->...
571
  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
572
573
574
  {
  	struct dn_skb_cb *cb;
  	unsigned char flags = 0;
c4106aa88   Harvey Harrison   decnet: remove pr...
575
  	__u16 len = le16_to_cpu(*(__le16 *)skb->data);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
576
  	struct dn_dev *dn = rcu_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
  	unsigned char padlen = 0;
721499e89   YOSHIFUJI Hideaki   netns: Use net_eq...
578
  	if (!net_eq(dev_net(dev), &init_net))
e730c1551   Eric W. Biederman   [NET]: Make packe...
579
  		goto dump_it;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
582
583
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
  	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...
612
  	skb_reset_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
614
615
616
617
618
619
620
621
622
  
  	/*
  	 * 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...
623
  		printk(KERN_DEBUG
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
  			"dn_route_rcv: got 0x%02x from %s [%d %d %d]
  ",
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
626
  			(int)flags, (dev) ? dev->name : "???", len, skb->len,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
  			padlen);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
628
  	if (flags & DN_RT_PKT_CNTL) {
364c6badd   Herbert Xu   [NET]: Clean up s...
629
  		if (unlikely(skb_linearize(skb)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
  			goto dump_it;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
631
632
  		switch(flags & DN_RT_CNTL_MSK) {
  			case DN_RT_PKT_INIT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
634
  				dn_dev_init_pkt(skb);
  				break;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
635
  			case DN_RT_PKT_VERI:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
640
641
642
643
  				dn_dev_veri_pkt(skb);
  				break;
  		}
  
  		if (dn->parms.state != DN_DEV_S_RU)
  			goto dump_it;
  
  		switch(flags & DN_RT_CNTL_MSK) {
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
644
  			case DN_RT_PKT_HELO:
5d877d876   Jan Engelhardt   netfilter: decnet...
645
646
647
  				return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
  					       skb, skb->dev, NULL,
  					       dn_route_ptp_hello);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
649
650
  			case DN_RT_PKT_L1RT:
  			case DN_RT_PKT_L2RT:
5d877d876   Jan Engelhardt   netfilter: decnet...
651
652
653
  				return NF_HOOK(NFPROTO_DECNET, NF_DN_ROUTE,
  					       skb, skb->dev, NULL,
  					       dn_route_discard);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
654
  			case DN_RT_PKT_ERTH:
5d877d876   Jan Engelhardt   netfilter: decnet...
655
656
657
  				return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
  					       skb, skb->dev, NULL,
  					       dn_neigh_router_hello);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
659
  			case DN_RT_PKT_EEDH:
5d877d876   Jan Engelhardt   netfilter: decnet...
660
661
662
  				return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
  					       skb, skb->dev, NULL,
  					       dn_neigh_endnode_hello);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
663
664
  		}
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
667
668
  		if (dn->parms.state != DN_DEV_S_RU)
  			goto dump_it;
  
  		skb_pull(skb, 1); /* Pull flags */
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
669
670
671
672
673
  		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
674
  		}
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
675
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
677
678
679
680
681
682
683
684
  
  dump_it:
  	kfree_skb(skb);
  out:
  	return NET_RX_DROP;
  }
  
  static int dn_output(struct sk_buff *skb)
  {
adf30907d   Eric Dumazet   net: skb->dst acc...
685
  	struct dst_entry *dst = skb_dst(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
  	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;
  
  	if ((neigh = dst->neighbour) == NULL)
  		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...
709
710
  	return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, skb, NULL, dev,
  		       neigh->output);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
714
715
716
717
718
719
720
721
722
723
724
  
  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...
725
  	struct dst_entry *dst = skb_dst(skb);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
726
  	struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
729
730
731
732
733
734
735
736
737
  	struct dn_route *rt;
  	struct neighbour *neigh = dst->neighbour;
  	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...
738
  	rt = (struct dn_route *)skb_dst(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
  	header_len = dn_db->use_long ? 21 : 6;
d8d1f30b9   Changli Gao   net-next: remove ...
740
  	if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+header_len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
743
744
745
746
747
  		goto drop;
  
  	/*
  	 * Hop count exceeded.
  	 */
  	if (++cb->hops > 30)
  		goto drop;
d8d1f30b9   Changli Gao   net-next: remove ...
748
  	skb->dev = rt->dst.dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
750
751
752
753
754
755
756
757
  
  	/*
  	 * 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...
758
759
  	return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, skb, dev, skb->dev,
  		       neigh->output);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
760
761
762
763
764
765
766
  
  drop:
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
768
769
770
771
772
773
774
775
776
   * 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...
777
  				le16_to_cpu(cb->src), le16_to_cpu(cb->dst));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
  	}
  
  	kfree_skb(skb);
0e8635a8e   Florian Westphal   net: remove NET_R...
781
  	return NET_RX_DROP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
  }
0dbaee3b3   David S. Miller   net: Abstract def...
783
784
785
786
  static unsigned int dn_dst_default_advmss(const struct dst_entry *dst)
  {
  	return dn_mss_from_pmtu(dst->dev, dst_mtu(dst));
  }
d33e45533   David S. Miller   net: Abstract def...
787
788
789
790
  static unsigned int dn_dst_default_mtu(const struct dst_entry *dst)
  {
  	return dst->dev->mtu;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
793
  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 ...
794
  	struct net_device *dev = rt->dst.dev;
62fa8a846   David S. Miller   net: Implement re...
795
  	unsigned int mss_metric;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
796
  	struct neighbour *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797
798
799
800
801
  
  	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...
802
  		dst_init_metrics(&rt->dst, fi->fib_metrics, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
804
  	}
  	rt->rt_type = res->type;
d8d1f30b9   Changli Gao   net-next: remove ...
805
  	if (dev != NULL && rt->dst.neighbour == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806
807
808
  		n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev);
  		if (IS_ERR(n))
  			return PTR_ERR(n);
d8d1f30b9   Changli Gao   net-next: remove ...
809
  		rt->dst.neighbour = n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  	}
d33e45533   David S. Miller   net: Abstract def...
811
  	if (dst_metric(&rt->dst, RTAX_MTU) > rt->dst.dev->mtu)
defb3519a   David S. Miller   net: Abstract awa...
812
  		dst_metric_set(&rt->dst, RTAX_MTU, rt->dst.dev->mtu);
62fa8a846   David S. Miller   net: Implement re...
813
814
  	mss_metric = dst_metric_raw(&rt->dst, RTAX_ADVMSS);
  	if (mss_metric) {
0dbaee3b3   David S. Miller   net: Abstract def...
815
  		unsigned int mss = dn_mss_from_pmtu(dev, dst_mtu(&rt->dst));
62fa8a846   David S. Miller   net: Implement re...
816
  		if (mss_metric > mss)
0dbaee3b3   David S. Miller   net: Abstract def...
817
818
  			dst_metric_set(&rt->dst, RTAX_ADVMSS, mss);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
  	return 0;
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
821
  static inline int dn_match_addr(__le16 addr1, __le16 addr2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
  {
c4106aa88   Harvey Harrison   decnet: remove pr...
823
  	__u16 tmp = le16_to_cpu(addr1) ^ le16_to_cpu(addr2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
825
826
827
828
829
830
  	int match = 16;
  	while(tmp) {
  		tmp >>= 1;
  		match--;
  	}
  	return match;
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
831
  static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
  {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
833
  	__le16 saddr = 0;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
834
  	struct dn_dev *dn_db;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
836
837
  	struct dn_ifaddr *ifa;
  	int best_match = 0;
  	int ret;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
838
839
840
841
842
  	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
843
844
845
846
847
848
849
850
851
852
853
854
  		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...
855
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
857
858
  
  	return saddr;
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
859
  static inline __le16 __dn_fib_res_prefsrc(struct dn_fib_res *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
861
862
  {
  	return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope);
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
863
  static inline __le16 dn_fib_rules_map_destination(__le16 daddr, struct dn_fib_res *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
  {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
865
  	__le16 mask = dnet_make_mask(res->prefixlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
868
869
870
  	return (daddr&~mask)|res->fi->fib_nh->nh_gw;
  }
  
  static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *oldflp, int try_hard)
  {
5811662b1   Changli Gao   net: use the macr...
871
872
873
  	struct flowi fl = { .fld_dst = oldflp->fld_dst,
  			    .fld_src = oldflp->fld_src,
  			    .fld_scope = RT_SCOPE_UNIVERSE,
47dcf0cb1   Thomas Graf   [NET]: Rethink ma...
874
  			    .mark = oldflp->mark,
2774c7aba   Eric W. Biederman   [NET]: Make the l...
875
  			    .iif = init_net.loopback_dev->ifindex,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
  			    .oif = oldflp->oif };
  	struct dn_route *rt = NULL;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
878
  	struct net_device *dev_out = NULL, *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
880
881
882
883
884
  	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 ...
885
  	__le16 gateway = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
886
887
888
889
  
  	if (decnet_debug_level & 16)
  		printk(KERN_DEBUG
  		       "dn_route_output_slow: dst=%04x src=%04x mark=%d"
c4106aa88   Harvey Harrison   decnet: remove pr...
890
891
892
  		       " iif=%d oif=%d
  ", le16_to_cpu(oldflp->fld_dst),
  		       le16_to_cpu(oldflp->fld_src),
2774c7aba   Eric W. Biederman   [NET]: Make the l...
893
  		       oldflp->mark, init_net.loopback_dev->ifindex, oldflp->oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
896
  
  	/* If we have an output interface, verify its a DECnet device */
  	if (oldflp->oif) {
881d966b4   Eric W. Biederman   [NET]: Make the d...
897
  		dev_out = dev_get_by_index(&init_net, oldflp->oif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
  		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 */
  	if (oldflp->fld_src) {
  		err = -EADDRNOTAVAIL;
  
  		if (dev_out) {
  			if (dn_dev_islocal(dev_out, oldflp->fld_src))
  				goto source_ok;
  			dev_put(dev_out);
  			goto out;
  		}
c6d14c845   Eric Dumazet   net: Introduce fo...
917
918
  		rcu_read_lock();
  		for_each_netdev_rcu(&init_net, dev) {
7562f876c   Pavel Emelianov   [NET]: Rework dev...
919
  			if (!dev->dn_ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
  				continue;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
921
  			if (!dn_dev_islocal(dev, oldflp->fld_src))
9bbf28a1f   Patrick Caulfield   [DECNET]: Fix for...
922
  				continue;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
923
  			if ((dev->flags & IFF_LOOPBACK) &&
9bbf28a1f   Patrick Caulfield   [DECNET]: Fix for...
924
  			    oldflp->fld_dst &&
7562f876c   Pavel Emelianov   [NET]: Rework dev...
925
  			    !dn_dev_islocal(dev, oldflp->fld_dst))
9bbf28a1f   Patrick Caulfield   [DECNET]: Fix for...
926
  				continue;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
927
928
  
  			dev_out = dev;
9bbf28a1f   Patrick Caulfield   [DECNET]: Fix for...
929
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
  		}
c6d14c845   Eric Dumazet   net: Introduce fo...
931
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
932
933
934
935
936
937
938
939
940
941
942
943
944
945
  		if (dev_out == NULL)
  			goto out;
  		dev_hold(dev_out);
  source_ok:
  		;
  	}
  
  	/* No destination? Assume its local */
  	if (!fl.fld_dst) {
  		fl.fld_dst = fl.fld_src;
  
  		err = -EADDRNOTAVAIL;
  		if (dev_out)
  			dev_put(dev_out);
2774c7aba   Eric W. Biederman   [NET]: Make the l...
946
  		dev_out = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
948
949
950
951
952
953
954
  		dev_hold(dev_out);
  		if (!fl.fld_dst) {
  			fl.fld_dst =
  			fl.fld_src = dnet_select_source(dev_out, 0,
  						       RT_SCOPE_HOST);
  			if (!fl.fld_dst)
  				goto out;
  		}
2774c7aba   Eric W. Biederman   [NET]: Make the l...
955
  		fl.oif = init_net.loopback_dev->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
956
957
958
959
960
961
962
  		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 ...
963
964
  		       " dst=%o4x src=%04x oif=%d try_hard=%d
  ",
c4106aa88   Harvey Harrison   decnet: remove pr...
965
  		       le16_to_cpu(fl.fld_dst), le16_to_cpu(fl.fld_src),
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
966
  		       fl.oif, try_hard);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
968
969
970
971
972
973
974
975
976
977
978
  
  	/*
  	 * 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;
  	if (try_hard || (err = dn_fib_lookup(&fl, &res)) != 0) {
  		struct dn_dev *dn_db;
  		if (err != -ESRCH)
  			goto out;
  		/*
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
979
  		 * Here the fallback is basically the standard algorithm for
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
980
981
982
983
984
  		 * 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...
985
  		 * device/source address was requested, then we honour that
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
987
988
  		 * here
  		 */
  		if (!try_hard) {
426b5303e   Eric W. Biederman   [NETNS]: Modify t...
989
  			neigh = neigh_lookup_nodev(&dn_neigh_table, &init_net, &fl.fld_dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
  			if (neigh) {
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
991
  				if ((oldflp->oif &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
993
994
995
996
997
998
999
1000
1001
  				    (neigh->dev->ifindex != oldflp->oif)) ||
  				    (oldflp->fld_src &&
  				    (!dn_dev_islocal(neigh->dev,
  						      oldflp->fld_src)))) {
  					neigh_release(neigh);
  					neigh = NULL;
  				} else {
  					if (dev_out)
  						dev_put(dev_out);
  					if (dn_dev_islocal(neigh->dev, fl.fld_dst)) {
2774c7aba   Eric W. Biederman   [NET]: Make the l...
1002
  						dev_out = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
  						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...
1019
  		dn_db = rcu_dereference_raw(dev_out->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020
1021
1022
  		/* Possible improvement - check all devices for local addr */
  		if (dn_dev_islocal(dev_out, fl.fld_dst)) {
  			dev_put(dev_out);
2774c7aba   Eric W. Biederman   [NET]: Make the l...
1023
  			dev_out = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
  			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)
  			gateway = fl.fld_dst;
  		if (fl.fld_src == 0) {
  			fl.fld_src = dnet_select_source(dev_out, gateway,
  							 res.type == RTN_LOCAL ?
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1041
  							 RT_SCOPE_HOST :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
  							 RT_SCOPE_LINK);
  			if (fl.fld_src == 0 && res.type != RTN_LOCAL)
  				goto e_addr;
  		}
  		fl.oif = dev_out->ifindex;
  		goto make_route;
  	}
  	free_res = 1;
  
  	if (res.type == RTN_NAT)
  		goto e_inval;
  
  	if (res.type == RTN_LOCAL) {
  		if (!fl.fld_src)
  			fl.fld_src = fl.fld_dst;
  		if (dev_out)
  			dev_put(dev_out);
2774c7aba   Eric W. Biederman   [NET]: Make the l...
1059
  		dev_out = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
  		dev_hold(dev_out);
  		fl.oif = dev_out->ifindex;
  		if (res.fi)
  			dn_fib_info_put(res.fi);
  		res.fi = NULL;
  		goto make_route;
  	}
  
  	if (res.fi->fib_nhs > 1 && fl.oif == 0)
  		dn_fib_select_multipath(&fl, &res);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1070
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
1072
1073
1074
1075
1076
  	 * We could add some logic to deal with default routes here and
  	 * get rid of some of the special casing above.
  	 */
  
  	if (!fl.fld_src)
  		fl.fld_src = DN_FIB_RES_PREFSRC(res);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1077

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
  	if (dev_out)
  		dev_put(dev_out);
  	dev_out = DN_FIB_RES_DEV(res);
  	dev_hold(dev_out);
  	fl.oif = dev_out->ifindex;
  	gateway = DN_FIB_RES_GW(res);
  
  make_route:
  	if (dev_out->flags & IFF_LOOPBACK)
  		flags |= RTCF_LOCAL;
3c7bd1a14   David S. Miller   net: Add initial_...
1088
  	rt = dst_alloc(&dn_dst_ops, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1089
1090
  	if (rt == NULL)
  		goto e_nobufs;
d8d1f30b9   Changli Gao   net-next: remove ...
1091
1092
  	atomic_set(&rt->dst.__refcnt, 1);
  	rt->dst.flags   = DST_HOST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1093
1094
1095
1096
1097
  
  	rt->fl.fld_src    = oldflp->fld_src;
  	rt->fl.fld_dst    = oldflp->fld_dst;
  	rt->fl.oif        = oldflp->oif;
  	rt->fl.iif        = 0;
47dcf0cb1   Thomas Graf   [NET]: Rethink ma...
1098
  	rt->fl.mark       = oldflp->mark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1099
1100
1101
1102
1103
1104
1105
1106
  
  	rt->rt_saddr      = fl.fld_src;
  	rt->rt_daddr      = fl.fld_dst;
  	rt->rt_gateway    = gateway ? gateway : fl.fld_dst;
  	rt->rt_local_src  = fl.fld_src;
  
  	rt->rt_dst_map    = fl.fld_dst;
  	rt->rt_src_map    = fl.fld_src;
d8d1f30b9   Changli Gao   net-next: remove ...
1107
  	rt->dst.dev = dev_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1108
  	dev_hold(dev_out);
d8d1f30b9   Changli Gao   net-next: remove ...
1109
  	rt->dst.neighbour = neigh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1110
  	neigh = NULL;
d8d1f30b9   Changli Gao   net-next: remove ...
1111
1112
1113
  	rt->dst.lastuse = jiffies;
  	rt->dst.output  = dn_output;
  	rt->dst.input   = dn_rt_bug;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1114
1115
  	rt->rt_flags      = flags;
  	if (flags & RTCF_LOCAL)
d8d1f30b9   Changli Gao   net-next: remove ...
1116
  		rt->dst.input = dn_nsp_rx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
  
  	err = dn_rt_set_next_hop(rt, &res);
  	if (err)
  		goto e_neighbour;
  
  	hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst);
  	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...
1136
1137
  	err = -EADDRNOTAVAIL;
  	goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138
1139
1140
1141
1142
1143
1144
  e_inval:
  	err = -EINVAL;
  	goto done;
  e_nobufs:
  	err = -ENOBUFS;
  	goto done;
  e_neighbour:
d8d1f30b9   Changli Gao   net-next: remove ...
1145
  	dst_free(&rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
  	goto e_nobufs;
  }
  
  
  /*
   * N.B. The flags may be moved into the flowi at some future stage.
   */
  static int __dn_route_output_key(struct dst_entry **pprt, const struct flowi *flp, int flags)
  {
  	unsigned hash = dn_hash(flp->fld_src, flp->fld_dst);
  	struct dn_route *rt = NULL;
  
  	if (!(flags & MSG_TRYHARD)) {
  		rcu_read_lock_bh();
a898def29   Paul E. McKenney   net: Add checking...
1160
  		for (rt = rcu_dereference_bh(dn_rt_hash_table[hash].chain); rt;
d8d1f30b9   Changli Gao   net-next: remove ...
1161
  			rt = rcu_dereference_bh(rt->dst.dn_next)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1162
1163
  			if ((flp->fld_dst == rt->fl.fld_dst) &&
  			    (flp->fld_src == rt->fl.fld_src) &&
47dcf0cb1   Thomas Graf   [NET]: Rethink ma...
1164
  			    (flp->mark == rt->fl.mark) &&
c75379676   David S. Miller   ipv4: Make rt->fl...
1165
  			    dn_is_output_route(rt) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1166
  			    (rt->fl.oif == flp->oif)) {
d8d1f30b9   Changli Gao   net-next: remove ...
1167
  				dst_use(&rt->dst, jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1168
  				rcu_read_unlock_bh();
d8d1f30b9   Changli Gao   net-next: remove ...
1169
  				*pprt = &rt->dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
  				return 0;
  			}
  		}
  		rcu_read_unlock_bh();
  	}
  
  	return dn_route_output_slow(pprt, flp, flags);
  }
  
  static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int flags)
  {
  	int err;
  
  	err = __dn_route_output_key(pprt, flp, flags);
  	if (err == 0 && flp->proto) {
52479b623   Alexey Dobriyan   netns xfrm: looku...
1185
  		err = xfrm_lookup(&init_net, pprt, flp, NULL, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
  	}
  	return err;
  }
  
  int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock *sk, int flags)
  {
  	int err;
  
  	err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD);
  	if (err == 0 && fl->proto) {
52479b623   Alexey Dobriyan   netns xfrm: looku...
1196
1197
  		err = xfrm_lookup(&init_net, pprt, fl, sk,
  				 (flags & MSG_DONTWAIT) ? 0 : XFRM_LOOKUP_WAIT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
  	}
  	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 ...
1212
1213
  	__le16 gateway = 0;
  	__le16 local_src = 0;
5811662b1   Changli Gao   net: use the macr...
1214
1215
1216
  	struct flowi fl = { .fld_dst = cb->dst,
  			    .fld_src = cb->src,
  			    .fld_scope = RT_SCOPE_UNIVERSE,
47dcf0cb1   Thomas Graf   [NET]: Rethink ma...
1217
  			    .mark = skb->mark,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1218
1219
1220
1221
1222
1223
  			    .iif = skb->dev->ifindex };
  	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...
1224
  	if ((dn_db = rcu_dereference(in_dev->dn_ptr)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
  		goto out;
  
  	/* Zero source addresses are not allowed */
  	if (fl.fld_src == 0)
  		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;
  
  	err = dn_fib_lookup(&fl, &res);
  	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
1253
  	} else {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
1254
  		__le16 src_map = fl.fld_src;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
  		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)
a8731cbf6   Steven Whitehouse   [DECNET]: Covert ...
1268
  			src_map = fl.fld_src; /* no NAT support for now */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
  
  		gateway = DN_FIB_RES_GW(res);
  		if (res.type == RTN_NAT) {
  			fl.fld_dst = dn_fib_rules_map_destination(fl.fld_dst, &res);
  			dn_fib_res_put(&res);
  			free_res = 0;
  			if (dn_fib_lookup(&fl, &res))
  				goto e_inval;
  			free_res = 1;
  			if (res.type != RTN_UNICAST)
  				goto e_inval;
  			flags |= RTCF_DNAT;
  			gateway = fl.fld_dst;
  		}
  		fl.fld_src = src_map;
  	}
  
  	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;
  
  		if (res.fi->fib_nhs > 1 && fl.oif == 0)
  			dn_fib_select_multipath(&fl, &res);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1299
  		/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
  		 * 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;
  		fl.fld_src = cb->dst;
  		fl.fld_dst = cb->src;
  
  		/* 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...
1323
  		if (cb->rt_flags & DN_RT_F_IE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
  			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:
3c7bd1a14   David S. Miller   net: Add initial_...
1345
  	rt = dst_alloc(&dn_dst_ops, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
  	if (rt == NULL)
  		goto e_nobufs;
  
  	rt->rt_saddr      = fl.fld_src;
  	rt->rt_daddr      = fl.fld_dst;
  	rt->rt_gateway    = fl.fld_dst;
  	if (gateway)
  		rt->rt_gateway = gateway;
  	rt->rt_local_src  = local_src ? local_src : rt->rt_saddr;
  
  	rt->rt_dst_map    = fl.fld_dst;
  	rt->rt_src_map    = fl.fld_src;
  
  	rt->fl.fld_src    = cb->src;
  	rt->fl.fld_dst    = cb->dst;
  	rt->fl.oif        = 0;
  	rt->fl.iif        = in_dev->ifindex;
47dcf0cb1   Thomas Graf   [NET]: Rethink ma...
1363
  	rt->fl.mark       = fl.mark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1364

d8d1f30b9   Changli Gao   net-next: remove ...
1365
1366
1367
1368
1369
  	rt->dst.flags = DST_HOST;
  	rt->dst.neighbour = neigh;
  	rt->dst.dev = out_dev;
  	rt->dst.lastuse = jiffies;
  	rt->dst.output = dn_rt_bug;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1370
1371
  	switch(res.type) {
  		case RTN_UNICAST:
d8d1f30b9   Changli Gao   net-next: remove ...
1372
  			rt->dst.input = dn_forward;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1373
1374
  			break;
  		case RTN_LOCAL:
d8d1f30b9   Changli Gao   net-next: remove ...
1375
1376
1377
  			rt->dst.output = dn_output;
  			rt->dst.input = dn_nsp_rx;
  			rt->dst.dev = in_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1378
1379
1380
1381
1382
  			flags |= RTCF_LOCAL;
  			break;
  		default:
  		case RTN_UNREACHABLE:
  		case RTN_BLACKHOLE:
d8d1f30b9   Changli Gao   net-next: remove ...
1383
  			rt->dst.input = dst_discard;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1384
1385
  	}
  	rt->rt_flags = flags;
d8d1f30b9   Changli Gao   net-next: remove ...
1386
1387
  	if (rt->dst.dev)
  		dev_hold(rt->dst.dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1388
1389
1390
1391
1392
1393
  
  	err = dn_rt_set_next_hop(rt, &res);
  	if (err)
  		goto e_neighbour;
  
  	hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst);
adf30907d   Eric Dumazet   net: skb->dst acc...
1394
  	dn_insert_route(rt, hash, &rt);
d8d1f30b9   Changli Gao   net-next: remove ...
1395
  	skb_dst_set(skb, &rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
  
  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 ...
1417
  	dst_free(&rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1418
1419
  	goto done;
  }
5eaa65b24   Roel Kluin   net: Make static
1420
  static int dn_route_input(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1421
1422
1423
1424
  {
  	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...
1425
  	if (skb_dst(skb))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1426
1427
1428
1429
  		return 0;
  
  	rcu_read_lock();
  	for(rt = rcu_dereference(dn_rt_hash_table[hash].chain); rt != NULL;
d8d1f30b9   Changli Gao   net-next: remove ...
1430
  	    rt = rcu_dereference(rt->dst.dn_next)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1431
  		if ((rt->fl.fld_src == cb->src) &&
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1432
  		    (rt->fl.fld_dst == cb->dst) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1433
  		    (rt->fl.oif == 0) &&
47dcf0cb1   Thomas Graf   [NET]: Rethink ma...
1434
  		    (rt->fl.mark == skb->mark) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1435
  		    (rt->fl.iif == cb->iif)) {
d8d1f30b9   Changli Gao   net-next: remove ...
1436
  			dst_use(&rt->dst, jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1437
  			rcu_read_unlock();
adf30907d   Eric Dumazet   net: skb->dst acc...
1438
  			skb_dst_set(skb, (struct dst_entry *)rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1439
1440
1441
1442
1443
1444
1445
  			return 0;
  		}
  	}
  	rcu_read_unlock();
  
  	return dn_route_input_slow(skb);
  }
b6544c0b4   Jamal Hadi Salim   [NETLINK]: Correc...
1446
1447
  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
1448
  {
adf30907d   Eric Dumazet   net: skb->dst acc...
1449
  	struct dn_route *rt = (struct dn_route *)skb_dst(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1450
1451
  	struct rtmsg *r;
  	struct nlmsghdr *nlh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1452
  	unsigned char *b = skb_tail_pointer(skb);
e3703b3de   Thomas Graf   [RTNETLINK]: Add ...
1453
  	long expires;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1454

b6544c0b4   Jamal Hadi Salim   [NETLINK]: Correc...
1455
  	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*r), flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1456
  	r = NLMSG_DATA(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1457
1458
1459
1460
1461
  	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 ...
1462
  	RTA_PUT_U32(skb, RTA_TABLE, RT_TABLE_MAIN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
  	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);
  	if (rt->fl.fld_src) {
  		r->rtm_src_len = 16;
  		RTA_PUT(skb, RTA_SRC, 2, &rt->fl.fld_src);
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
1474
1475
  	if (rt->dst.dev)
  		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->dst.dev->ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1476
1477
1478
1479
1480
1481
1482
1483
  	/*
  	 * 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...
1484
  	if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1485
  		goto rtattr_failure;
d8d1f30b9   Changli Gao   net-next: remove ...
1486
1487
1488
  	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 ...
1489
  		goto rtattr_failure;
c75379676   David S. Miller   ipv4: Make rt->fl...
1490
  	if (dn_is_input_route(rt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1491
  		RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->fl.iif);
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1492
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1493
1494
1495
1496
  	return skb->len;
  
  nlmsg_failure:
  rtattr_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
1497
  	nlmsg_trim(skb, b);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1498
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1499
1500
1501
1502
1503
  }
  
  /*
   * This is called by both endnodes and routers now.
   */
fa34ddd73   Thomas Graf   [DECNet]: Use rtn...
1504
  static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1505
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1506
  	struct net *net = sock_net(in_skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1507
1508
1509
1510
1511
1512
1513
  	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;
  	struct flowi fl;
09ad9bc75   Octavian Purdila   net: use net_eq t...
1514
  	if (!net_eq(net, &init_net))
b854272b3   Denis V. Lunev   [NET]: Modify all...
1515
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1516
1517
1518
1519
1520
1521
  	memset(&fl, 0, sizeof(fl));
  	fl.proto = DNPROTO_NSP;
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (skb == NULL)
  		return -ENOBUFS;
459a98ed8   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1522
  	skb_reset_mac_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
  	cb = DN_SKB_CB(skb);
  
  	if (rta[RTA_SRC-1])
  		memcpy(&fl.fld_src, RTA_DATA(rta[RTA_SRC-1]), 2);
  	if (rta[RTA_DST-1])
  		memcpy(&fl.fld_dst, RTA_DATA(rta[RTA_DST-1]), 2);
  	if (rta[RTA_IIF-1])
  		memcpy(&fl.iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
  
  	if (fl.iif) {
  		struct net_device *dev;
881d966b4   Eric W. Biederman   [NET]: Make the d...
1534
  		if ((dev = dev_get_by_index(&init_net, fl.iif)) == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1535
1536
1537
1538
1539
1540
1541
1542
  			kfree_skb(skb);
  			return -ENODEV;
  		}
  		if (!dev->dn_ptr) {
  			dev_put(dev);
  			kfree_skb(skb);
  			return -ENODEV;
  		}
b98999dc3   YOSHIFUJI Hideaki   [DECNET]: Use hto...
1543
  		skb->protocol = htons(ETH_P_DNA_RT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1544
1545
1546
1547
1548
1549
1550
  		skb->dev = dev;
  		cb->src = fl.fld_src;
  		cb->dst = fl.fld_dst;
  		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...
1551
  		rt = (struct dn_route *)skb_dst(skb);
d8d1f30b9   Changli Gao   net-next: remove ...
1552
1553
  		if (!err && -rt->dst.error)
  			err = rt->dst.error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
  	} else {
  		int oif = 0;
  		if (rta[RTA_OIF - 1])
  			memcpy(&oif, RTA_DATA(rta[RTA_OIF - 1]), sizeof(int));
  		fl.oif = oif;
  		err = dn_route_output_key((struct dst_entry **)&rt, &fl, 0);
  	}
  
  	if (skb->dev)
  		dev_put(skb->dev);
  	skb->dev = NULL;
  	if (err)
  		goto out_free;
d8d1f30b9   Changli Gao   net-next: remove ...
1567
  	skb_dst_set(skb, &rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1568
1569
  	if (rtm->rtm_flags & RTM_F_NOTIFY)
  		rt->rt_flags |= RTCF_NOTIFY;
b6544c0b4   Jamal Hadi Salim   [NETLINK]: Correc...
1570
  	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
1571
1572
1573
1574
1575
1576
1577
  
  	if (err == 0)
  		goto out_free;
  	if (err < 0) {
  		err = -EMSGSIZE;
  		goto out_free;
  	}
97c53cacf   Denis V. Lunev   [NET]: Make rtnet...
1578
  	return rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
  
  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...
1591
  	struct net *net = sock_net(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1592
1593
1594
  	struct dn_route *rt;
  	int h, s_h;
  	int idx, s_idx;
09ad9bc75   Octavian Purdila   net: use net_eq t...
1595
  	if (!net_eq(net, &init_net))
b854272b3   Denis V. Lunev   [NET]: Modify all...
1596
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
  	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...
1610
  		for(rt = rcu_dereference_bh(dn_rt_hash_table[h].chain), idx = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1611
  			rt;
d8d1f30b9   Changli Gao   net-next: remove ...
1612
  			rt = rcu_dereference_bh(rt->dst.dn_next), idx++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1613
1614
  			if (idx < s_idx)
  				continue;
d8d1f30b9   Changli Gao   net-next: remove ...
1615
  			skb_dst_set(skb, dst_clone(&rt->dst));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1616
  			if (dn_rt_fill_info(skb, NETLINK_CB(cb->skb).pid,
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1617
  					cb->nlh->nlmsg_seq, RTM_NEWROUTE,
b6544c0b4   Jamal Hadi Salim   [NETLINK]: Correc...
1618
  					1, NLM_F_MULTI) <= 0) {
adf30907d   Eric Dumazet   net: skb->dst acc...
1619
  				skb_dst_drop(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1620
1621
1622
  				rcu_read_unlock_bh();
  				goto done;
  			}
adf30907d   Eric Dumazet   net: skb->dst acc...
1623
  			skb_dst_drop(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
  		}
  		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...
1646
  		rt = rcu_dereference_bh(dn_rt_hash_table[s->bucket].chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1647
1648
1649
1650
  		if (rt)
  			break;
  		rcu_read_unlock_bh();
  	}
a898def29   Paul E. McKenney   net: Add checking...
1651
  	return rt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1652
1653
1654
1655
  }
  
  static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_route *rt)
  {
0d89d7944   Eric Dumazet   [DECNET] ROUTE: f...
1656
  	struct dn_rt_cache_iter_state *s = seq->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1657

fc766e4c4   Eric Dumazet   decnet: RCU conve...
1658
1659
  	rt = rcu_dereference_bh(rt->dst.dn_next);
  	while (!rt) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1660
1661
1662
1663
  		rcu_read_unlock_bh();
  		if (--s->bucket < 0)
  			break;
  		rcu_read_lock_bh();
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1664
  		rt = rcu_dereference_bh(dn_rt_hash_table[s->bucket].chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1665
  	}
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1666
  	return rt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
  }
  
  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 ...
1700
  			rt->dst.dev ? rt->dst.dev->name : "*",
c4106aa88   Harvey Harrison   decnet: remove pr...
1701
1702
  			dn_addr2asc(le16_to_cpu(rt->rt_daddr), buf1),
  			dn_addr2asc(le16_to_cpu(rt->rt_saddr), buf2),
d8d1f30b9   Changli Gao   net-next: remove ...
1703
1704
1705
  			atomic_read(&rt->dst.__refcnt),
  			rt->dst.__use,
  			(int) dst_metric(&rt->dst, RTAX_RTT));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1706
  	return 0;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1707
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1708

56b3d975b   Philippe De Muyter   [NET]: Make all i...
1709
  static const struct seq_operations dn_rt_cache_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1710
1711
1712
1713
1714
1715
1716
1717
  	.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...
1718
1719
  	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
1720
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
1721
  static const struct file_operations dn_rt_cache_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
  	.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...
1734
1735
  	dn_dst_ops.kmem_cachep =
  		kmem_cache_create("dn_dst_cache", sizeof(struct dn_route), 0,
20c2df83d   Paul Mundt   mm: Remove slab d...
1736
  				  SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
fc66f95c6   Eric Dumazet   net dst: use a pe...
1737
  	dst_entries_init(&dn_dst_ops);
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1738
  	setup_timer(&dn_route_timer, dn_dst_check_expire, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1739
1740
  	dn_route_timer.expires = jiffies + decnet_dst_gc_interval * HZ;
  	add_timer(&dn_route_timer);
4481374ce   Jan Beulich   mm: replace vario...
1741
  	goal = totalram_pages >> (26 - PAGE_SHIFT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1742
1743
1744
  
  	for(order = 0; (1UL << order) < goal; order++)
  		/* NOTHING */;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
  	/*
  	 * 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
1761
1762
  
  	if (!dn_rt_hash_table)
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1763
1764
  		panic("Failed to allocate DECnet route cache hash table
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1765

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1766
1767
1768
1769
  	printk(KERN_INFO
  		"DECnet: Routing cache hash table of %u buckets, %ldKbytes
  ",
  		dn_rt_hash_mask,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1770
1771
1772
  		(long)(dn_rt_hash_mask*sizeof(struct dn_rt_hash_bucket))/1024);
  
  	dn_rt_hash_mask--;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1773
1774
1775
1776
  	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
1777

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

457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
1780
  	proc_net_fops_create(&init_net, "decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops);
fa34ddd73   Thomas Graf   [DECNet]: Use rtn...
1781
1782
1783
1784
1785
1786
1787
  
  #ifdef CONFIG_DECNET_ROUTER
  	rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, dn_fib_dump);
  #else
  	rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute,
  		      dn_cache_dump);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1788
1789
1790
1791
1792
1793
  }
  
  void __exit dn_route_cleanup(void)
  {
  	del_timer(&dn_route_timer);
  	dn_run_flush(0);
457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
1794
  	proc_net_remove(&init_net, "decnet_cache");
fc66f95c6   Eric Dumazet   net dst: use a pe...
1795
  	dst_entries_destroy(&dn_dst_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1796
  }