dn_route.c 47.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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
46

Linus Torvalds's avatar
Linus Torvalds committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    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.
*******************************************************************************/

#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>
69
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
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>
80
#include <linux/export.h>
Linus Torvalds's avatar
Linus Torvalds committed
81
#include <asm/errno.h>
82
#include <net/net_namespace.h>
83
#include <net/netlink.h>
Linus Torvalds's avatar
Linus Torvalds committed
84
85
86
#include <net/neighbour.h>
#include <net/dst.h>
#include <net/flow.h>
87
#include <net/fib_rules.h>
Linus Torvalds's avatar
Linus Torvalds committed
88
89
90
91
92
93
94
95
96
#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
{
97
	struct dn_route __rcu *chain;
Linus Torvalds's avatar
Linus Torvalds committed
98
	spinlock_t lock;
99
};
Linus Torvalds's avatar
Linus Torvalds committed
100
101
102
103
104
105
106
107
108
109
110
111

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;

112
static int dn_dst_gc(struct dst_ops *ops);
Linus Torvalds's avatar
Linus Torvalds committed
113
static struct dst_entry *dn_dst_check(struct dst_entry *, __u32);
114
static unsigned int dn_dst_default_advmss(const struct dst_entry *dst);
115
static unsigned int dn_dst_mtu(const struct dst_entry *dst);
116
static void dn_dst_destroy(struct dst_entry *);
117
static void dn_dst_ifdown(struct dst_entry *, struct net_device *dev, int how);
Linus Torvalds's avatar
Linus Torvalds committed
118
119
static struct dst_entry *dn_dst_negative_advice(struct dst_entry *);
static void dn_dst_link_failure(struct sk_buff *);
120
121
122
123
static void dn_dst_update_pmtu(struct dst_entry *dst, struct sock *sk,
			       struct sk_buff *skb , u32 mtu);
static void dn_dst_redirect(struct dst_entry *dst, struct sock *sk,
			    struct sk_buff *skb);
124
125
126
static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst,
					     struct sk_buff *skb,
					     const void *daddr);
Linus Torvalds's avatar
Linus Torvalds committed
127
128
129
130
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;
131
static unsigned int dn_rt_hash_mask;
Linus Torvalds's avatar
Linus Torvalds committed
132
133

static struct timer_list dn_route_timer;
134
static DEFINE_TIMER(dn_rt_flush_timer, dn_run_flush, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
135
136
137
138
139
140
141
int decnet_dst_gc_interval = 2;

static struct dst_ops dn_dst_ops = {
	.family =		PF_DECnet,
	.gc_thresh =		128,
	.gc =			dn_dst_gc,
	.check =		dn_dst_check,
142
	.default_advmss =	dn_dst_default_advmss,
143
	.mtu =			dn_dst_mtu,
144
145
	.cow_metrics =		dst_cow_metrics_generic,
	.destroy =		dn_dst_destroy,
146
	.ifdown =		dn_dst_ifdown,
Linus Torvalds's avatar
Linus Torvalds committed
147
148
149
	.negative_advice =	dn_dst_negative_advice,
	.link_failure =		dn_dst_link_failure,
	.update_pmtu =		dn_dst_update_pmtu,
150
	.redirect =		dn_dst_redirect,
151
	.neigh_lookup =		dn_dst_neigh_lookup,
Linus Torvalds's avatar
Linus Torvalds committed
152
153
};

154
155
static void dn_dst_destroy(struct dst_entry *dst)
{
156
157
158
159
	struct dn_route *rt = (struct dn_route *) dst;

	if (rt->n)
		neigh_release(rt->n);
160
161
162
	dst_destroy_metrics_generic(dst);
}

163
164
165
166
167
168
169
170
171
172
173
174
175
176
static void dn_dst_ifdown(struct dst_entry *dst, struct net_device *dev, int how)
{
	if (how) {
		struct dn_route *rt = (struct dn_route *) dst;
		struct neighbour *n = rt->n;

		if (n && n->dev == dev) {
			n->dev = dev_net(dev)->loopback_dev;
			dev_hold(n->dev);
			dev_put(dev);
		}
	}
}

177
static __inline__ unsigned int dn_hash(__le16 src, __le16 dst)
Linus Torvalds's avatar
Linus Torvalds committed
178
{
179
	__u16 tmp = (__u16 __force)(src ^ dst);
Linus Torvalds's avatar
Linus Torvalds committed
180
181
182
	tmp ^= (tmp >> 3);
	tmp ^= (tmp >> 5);
	tmp ^= (tmp >> 10);
183
	return dn_rt_hash_mask & (unsigned int)tmp;
Linus Torvalds's avatar
Linus Torvalds committed
184
185
186
187
}

static inline void dnrt_free(struct dn_route *rt)
{
188
	call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free);
Linus Torvalds's avatar
Linus Torvalds committed
189
190
191
192
}

static inline void dnrt_drop(struct dn_route *rt)
{
193
194
	dst_release(&rt->dst);
	call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free);
Linus Torvalds's avatar
Linus Torvalds committed
195
196
197
198
199
}

static void dn_dst_check_expire(unsigned long dummy)
{
	int i;
200
201
	struct dn_route *rt;
	struct dn_route __rcu **rtp;
Linus Torvalds's avatar
Linus Torvalds committed
202
203
204
	unsigned long now = jiffies;
	unsigned long expire = 120 * HZ;

205
	for (i = 0; i <= dn_rt_hash_mask; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
206
207
208
		rtp = &dn_rt_hash_table[i].chain;

		spin_lock(&dn_rt_hash_table[i].lock);
209
210
		while ((rt = rcu_dereference_protected(*rtp,
						lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
211
212
213
			if (atomic_read(&rt->dst.__refcnt) ||
					(now - rt->dst.lastuse) < expire) {
				rtp = &rt->dst.dn_next;
Linus Torvalds's avatar
Linus Torvalds committed
214
215
				continue;
			}
216
217
			*rtp = rt->dst.dn_next;
			rt->dst.dn_next = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
218
219
220
221
222
223
224
225
226
227
228
			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);
}

229
static int dn_dst_gc(struct dst_ops *ops)
Linus Torvalds's avatar
Linus Torvalds committed
230
{
231
232
	struct dn_route *rt;
	struct dn_route __rcu **rtp;
Linus Torvalds's avatar
Linus Torvalds committed
233
234
235
236
	int i;
	unsigned long now = jiffies;
	unsigned long expire = 10 * HZ;

237
	for (i = 0; i <= dn_rt_hash_mask; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
238
239
240
241

		spin_lock_bh(&dn_rt_hash_table[i].lock);
		rtp = &dn_rt_hash_table[i].chain;

242
243
		while ((rt = rcu_dereference_protected(*rtp,
						lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
244
245
246
			if (atomic_read(&rt->dst.__refcnt) ||
					(now - rt->dst.lastuse) < expire) {
				rtp = &rt->dst.dn_next;
Linus Torvalds's avatar
Linus Torvalds committed
247
248
				continue;
			}
249
250
			*rtp = rt->dst.dn_next;
			rt->dst.dn_next = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
			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).
 */
270
271
static void dn_dst_update_pmtu(struct dst_entry *dst, struct sock *sk,
			       struct sk_buff *skb, u32 mtu)
Linus Torvalds's avatar
Linus Torvalds committed
272
{
273
274
	struct dn_route *rt = (struct dn_route *) dst;
	struct neighbour *n = rt->n;
Linus Torvalds's avatar
Linus Torvalds committed
275
	u32 min_mtu = 230;
276
277
278
	struct dn_dev *dn;

	dn = n ? rcu_dereference_raw(n->dev->dn_ptr) : NULL;
Linus Torvalds's avatar
Linus Torvalds committed
279
280
281
282
283
284

	if (dn && dn->use_long == 0)
		min_mtu -= 6;
	else
		min_mtu -= 21;

285
	if (dst_metric(dst, RTAX_MTU) > mtu && mtu >= min_mtu) {
Linus Torvalds's avatar
Linus Torvalds committed
286
		if (!(dst_metric_locked(dst, RTAX_MTU))) {
287
			dst_metric_set(dst, RTAX_MTU, mtu);
Linus Torvalds's avatar
Linus Torvalds committed
288
289
290
291
			dst_set_expires(dst, dn_rt_mtu_expires);
		}
		if (!(dst_metric_locked(dst, RTAX_ADVMSS))) {
			u32 mss = mtu - DN_MAX_NSP_DATA_HEADER;
292
293
			u32 existing_mss = dst_metric_raw(dst, RTAX_ADVMSS);
			if (!existing_mss || existing_mss > mss)
294
				dst_metric_set(dst, RTAX_ADVMSS, mss);
Linus Torvalds's avatar
Linus Torvalds committed
295
296
297
298
		}
	}
}

299
300
static void dn_dst_redirect(struct dst_entry *dst, struct sock *sk,
			    struct sk_buff *skb)
301
302
303
{
}

304
/*
Linus Torvalds's avatar
Linus Torvalds committed
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
 * 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)
{
}

322
static inline int compare_keys(struct flowidn *fl1, struct flowidn *fl2)
Linus Torvalds's avatar
Linus Torvalds committed
323
{
324
325
326
327
328
329
	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;
Linus Torvalds's avatar
Linus Torvalds committed
330
331
}

332
static int dn_insert_route(struct dn_route *rt, unsigned int hash, struct dn_route **rp)
Linus Torvalds's avatar
Linus Torvalds committed
333
{
334
335
	struct dn_route *rth;
	struct dn_route __rcu **rthp;
Linus Torvalds's avatar
Linus Torvalds committed
336
337
338
339
340
	unsigned long now = jiffies;

	rthp = &dn_rt_hash_table[hash].chain;

	spin_lock_bh(&dn_rt_hash_table[hash].lock);
341
342
	while ((rth = rcu_dereference_protected(*rthp,
						lockdep_is_held(&dn_rt_hash_table[hash].lock))) != NULL) {
343
		if (compare_keys(&rth->fld, &rt->fld)) {
Linus Torvalds's avatar
Linus Torvalds committed
344
			/* Put it first */
345
346
			*rthp = rth->dst.dn_next;
			rcu_assign_pointer(rth->dst.dn_next,
Linus Torvalds's avatar
Linus Torvalds committed
347
348
349
					   dn_rt_hash_table[hash].chain);
			rcu_assign_pointer(dn_rt_hash_table[hash].chain, rth);

350
			dst_use(&rth->dst, now);
Linus Torvalds's avatar
Linus Torvalds committed
351
352
353
354
355
356
			spin_unlock_bh(&dn_rt_hash_table[hash].lock);

			dnrt_drop(rt);
			*rp = rth;
			return 0;
		}
357
		rthp = &rth->dst.dn_next;
Linus Torvalds's avatar
Linus Torvalds committed
358
359
	}

360
	rcu_assign_pointer(rt->dst.dn_next, dn_rt_hash_table[hash].chain);
Linus Torvalds's avatar
Linus Torvalds committed
361
	rcu_assign_pointer(dn_rt_hash_table[hash].chain, rt);
362

363
	dst_use(&rt->dst, now);
Linus Torvalds's avatar
Linus Torvalds committed
364
365
366
367
368
	spin_unlock_bh(&dn_rt_hash_table[hash].lock);
	*rp = rt;
	return 0;
}

Roel Kluin's avatar
Roel Kluin committed
369
static void dn_run_flush(unsigned long dummy)
Linus Torvalds's avatar
Linus Torvalds committed
370
371
372
373
{
	int i;
	struct dn_route *rt, *next;

374
	for (i = 0; i < dn_rt_hash_mask; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
375
376
		spin_lock_bh(&dn_rt_hash_table[i].lock);

377
		if ((rt = xchg((struct dn_route **)&dn_rt_hash_table[i].chain, NULL)) == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
378
379
			goto nothing_to_declare;

380
381
382
		for(; rt; rt = next) {
			next = rcu_dereference_raw(rt->dst.dn_next);
			RCU_INIT_POINTER(rt->dst.dn_next, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
			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;
436
437
	__le16 *src;
	__le16 *dst;
Linus Torvalds's avatar
Linus Torvalds committed
438
439

	/* Add back headers */
440
	skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds's avatar
Linus Torvalds committed
441
442
443
444
445
446
447
448
449

	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;

450
	dst = (__le16 *)ptr;
Linus Torvalds's avatar
Linus Torvalds committed
451
	ptr += 2;
452
	src = (__le16 *)ptr;
Linus Torvalds's avatar
Linus Torvalds committed
453
454
455
	ptr += 2;
	*ptr = 0; /* Zero hop count */

456
	swap(*src, *dst);
Linus Torvalds's avatar
Linus Torvalds committed
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475

	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 */
476
	skb_push(skb, skb->data - skb_network_header(skb));
Linus Torvalds's avatar
Linus Torvalds committed
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514

	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
 */
515
static int dn_route_rx_packet(struct sock *sk, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
516
{
517
	struct dn_skb_cb *cb;
Linus Torvalds's avatar
Linus Torvalds committed
518
519
520
521
522
	int err;

	if ((err = dn_route_input(skb)) == 0)
		return dst_input(skb);

523
	cb = DN_SKB_CB(skb);
Linus Torvalds's avatar
Linus Torvalds committed
524
525
	if (decnet_debug_level & 4) {
		char *devname = skb->dev ? skb->dev->name : "???";
526

Linus Torvalds's avatar
Linus Torvalds committed
527
528
		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\n",
529
			(int)cb->rt_flags, devname, skb->len,
530
			le16_to_cpu(cb->src), le16_to_cpu(cb->dst),
Linus Torvalds's avatar
Linus Torvalds committed
531
532
533
534
			err, skb->pkt_type);
	}

	if ((skb->pkt_type == PACKET_HOST) && (cb->rt_flags & DN_RT_F_RQR)) {
535
536
537
538
539
		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);
Linus Torvalds's avatar
Linus Torvalds committed
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
		}
	}

	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);
556
	skb_reset_transport_header(skb);
Linus Torvalds's avatar
Linus Torvalds committed
557

558
559
	/* Destination info */
	ptr += 2;
560
	cb->dst = dn_eth2dn(ptr);
561
562
563
	if (memcmp(ptr, dn_hiord_addr, 4) != 0)
		goto drop_it;
	ptr += 6;
Linus Torvalds's avatar
Linus Torvalds committed
564
565


566
567
	/* Source info */
	ptr += 2;
568
	cb->src = dn_eth2dn(ptr);
569
570
571
572
573
574
	if (memcmp(ptr, dn_hiord_addr, 4) != 0)
		goto drop_it;
	ptr += 6;
	/* Other junk */
	ptr++;
	cb->hops = *ptr++; /* Visit Count */
Linus Torvalds's avatar
Linus Torvalds committed
575

576
577
	return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING,
		       &init_net, NULL, skb, skb->dev, NULL,
578
		       dn_route_rx_packet);
Linus Torvalds's avatar
Linus Torvalds committed
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595

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);
596
	skb_reset_transport_header(skb);
Linus Torvalds's avatar
Linus Torvalds committed
597

598
	cb->dst = *(__le16 *)ptr;
599
600
601
602
	ptr += 2;
	cb->src = *(__le16 *)ptr;
	ptr += 2;
	cb->hops = *ptr & 0x3f;
Linus Torvalds's avatar
Linus Torvalds committed
603

604
605
	return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING,
		       &init_net, NULL, skb, skb->dev, NULL,
606
		       dn_route_rx_packet);
Linus Torvalds's avatar
Linus Torvalds committed
607
608

drop_it:
609
610
	kfree_skb(skb);
	return NET_RX_DROP;
Linus Torvalds's avatar
Linus Torvalds committed
611
612
}

613
static int dn_route_discard(struct sock *sk, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
614
615
616
617
618
619
620
621
622
{
	/*
	 * I know we drop the packet here, but thats considered success in
	 * this case
	 */
	kfree_skb(skb);
	return NET_RX_SUCCESS;
}

623
static int dn_route_ptp_hello(struct sock *sk, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
624
625
626
627
628
629
{
	dn_dev_hello(skb);
	dn_neigh_pointopoint_hello(skb);
	return NET_RX_SUCCESS;
}

David S. Miller's avatar
David S. Miller committed
630
int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
Linus Torvalds's avatar
Linus Torvalds committed
631
632
633
{
	struct dn_skb_cb *cb;
	unsigned char flags = 0;
634
	__u16 len = le16_to_cpu(*(__le16 *)skb->data);
635
	struct dn_dev *dn = rcu_dereference(dev->dn_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
636
637
	unsigned char padlen = 0;

638
	if (!net_eq(dev_net(dev), &init_net))
639
640
		goto dump_it;

Linus Torvalds's avatar
Linus Torvalds committed
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
	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;
	}

674
	skb_reset_network_header(skb);
Linus Torvalds's avatar
Linus Torvalds committed
675
676
677
678
679
680
681
682
683
684

	/*
	 * Weed out future version DECnet
	 */
	if (flags & DN_RT_F_VER)
		goto dump_it;

	cb->rt_flags = flags;

	if (decnet_debug_level & 1)
685
		printk(KERN_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
686
			"dn_route_rcv: got 0x%02x from %s [%d %d %d]\n",
687
			(int)flags, (dev) ? dev->name : "???", len, skb->len,
Linus Torvalds's avatar
Linus Torvalds committed
688
689
			padlen);

690
	if (flags & DN_RT_PKT_CNTL) {
Herbert Xu's avatar
Herbert Xu committed
691
		if (unlikely(skb_linearize(skb)))
Linus Torvalds's avatar
Linus Torvalds committed
692
693
			goto dump_it;

694
695
696
697
698
699
700
		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;
Linus Torvalds's avatar
Linus Torvalds committed
701
702
703
704
705
		}

		if (dn->parms.state != DN_DEV_S_RU)
			goto dump_it;

706
707
708
		switch (flags & DN_RT_CNTL_MSK) {
		case DN_RT_PKT_HELO:
			return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
709
				       &init_net, NULL, skb, skb->dev, NULL,
710
711
712
713
714
				       dn_route_ptp_hello);

		case DN_RT_PKT_L1RT:
		case DN_RT_PKT_L2RT:
			return NF_HOOK(NFPROTO_DECNET, NF_DN_ROUTE,
715
				       &init_net, NULL, skb, skb->dev, NULL,
716
717
718
				       dn_route_discard);
		case DN_RT_PKT_ERTH:
			return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
719
				       &init_net, NULL, skb, skb->dev, NULL,
720
721
722
723
				       dn_neigh_router_hello);

		case DN_RT_PKT_EEDH:
			return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
724
				       &init_net, NULL, skb, skb->dev, NULL,
725
				       dn_neigh_endnode_hello);
726
727
		}
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
728
729
730
731
732
		if (dn->parms.state != DN_DEV_S_RU)
			goto dump_it;

		skb_pull(skb, 1); /* Pull flags */

733
734
735
736
737
		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);
Linus Torvalds's avatar
Linus Torvalds committed
738
		}
739
	}
Linus Torvalds's avatar
Linus Torvalds committed
740
741
742
743
744
745
746

dump_it:
	kfree_skb(skb);
out:
	return NET_RX_DROP;
}

747
static int dn_output(struct sock *sk, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
748
{
Eric Dumazet's avatar
Eric Dumazet committed
749
	struct dst_entry *dst = skb_dst(skb);
Linus Torvalds's avatar
Linus Torvalds committed
750
751
752
753
754
755
	struct dn_route *rt = (struct dn_route *)dst;
	struct net_device *dev = dst->dev;
	struct dn_skb_cb *cb = DN_SKB_CB(skb);

	int err = -EINVAL;

756
	if (rt->n == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
		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;

773
774
	return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT,
		       &init_net, sk, skb, NULL, dev,
775
		       dn_to_neigh_output);
Linus Torvalds's avatar
Linus Torvalds committed
776
777

error:
778
	net_dbg_ratelimited("dn_output: This should not happen\n");
Linus Torvalds's avatar
Linus Torvalds committed
779
780
781
782
783
784
785
786
787

	kfree_skb(skb);

	return err;
}

static int dn_forward(struct sk_buff *skb)
{
	struct dn_skb_cb *cb = DN_SKB_CB(skb);
Eric Dumazet's avatar
Eric Dumazet committed
788
	struct dst_entry *dst = skb_dst(skb);
789
	struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
790
791
792
793
794
795
796
797
798
799
	struct dn_route *rt;
	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 */
Eric Dumazet's avatar
Eric Dumazet committed
800
	rt = (struct dn_route *)skb_dst(skb);
Linus Torvalds's avatar
Linus Torvalds committed
801
	header_len = dn_db->use_long ? 21 : 6;
802
	if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+header_len))
Linus Torvalds's avatar
Linus Torvalds committed
803
804
805
806
807
808
809
810
		goto drop;

	/*
	 * Hop count exceeded.
	 */
	if (++cb->hops > 30)
		goto drop;

811
	skb->dev = rt->dst.dev;
Linus Torvalds's avatar
Linus Torvalds committed
812
813
814
815
816
817
818
819
820
821

	/*
	 * 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;

822
823
	return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD,
		       &init_net, NULL, skb, dev, skb->dev,
824
		       dn_to_neigh_output);
Linus Torvalds's avatar
Linus Torvalds committed
825
826
827
828
829
830
831
832
833
834

drop:
	kfree_skb(skb);
	return NET_RX_DROP;
}

/*
 * Used to catch bugs. This should never normally get
 * called.
 */
835
836
837
838
839
840
841
842
843
844
845
846
static int dn_rt_bug_sk(struct sock *sk, struct sk_buff *skb)
{
	struct dn_skb_cb *cb = DN_SKB_CB(skb);

	net_dbg_ratelimited("dn_rt_bug: skb from:%04x to:%04x\n",
			    le16_to_cpu(cb->src), le16_to_cpu(cb->dst));

	kfree_skb(skb);

	return NET_RX_DROP;
}

Linus Torvalds's avatar
Linus Torvalds committed
847
848
static int dn_rt_bug(struct sk_buff *skb)
{
849
	struct dn_skb_cb *cb = DN_SKB_CB(skb);
Linus Torvalds's avatar
Linus Torvalds committed
850

851
852
	net_dbg_ratelimited("dn_rt_bug: skb from:%04x to:%04x\n",
			    le16_to_cpu(cb->src), le16_to_cpu(cb->dst));
Linus Torvalds's avatar
Linus Torvalds committed
853
854
855

	kfree_skb(skb);

856
	return NET_RX_DROP;
Linus Torvalds's avatar
Linus Torvalds committed
857
858
}

859
860
861
862
863
static unsigned int dn_dst_default_advmss(const struct dst_entry *dst)
{
	return dn_mss_from_pmtu(dst->dev, dst_mtu(dst));
}

864
static unsigned int dn_dst_mtu(const struct dst_entry *dst)
865
{
866
867
868
	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);

	return mtu ? : dst->dev->mtu;
869
870
}

871
872
873
static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst,
					     struct sk_buff *skb,
					     const void *daddr)
874
875
876
877
{
	return __neigh_lookup_errno(&dn_neigh_table, daddr, dst->dev);
}

Linus Torvalds's avatar
Linus Torvalds committed
878
879
880
static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
{
	struct dn_fib_info *fi = res->fi;
881
	struct net_device *dev = rt->dst.dev;
882
	unsigned int mss_metric;
Linus Torvalds's avatar
Linus Torvalds committed
883
884
885
886
887
888
	struct neighbour *n;

	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);
889
		dst_init_metrics(&rt->dst, fi->fib_metrics, true);
Linus Torvalds's avatar
Linus Torvalds committed
890
891
892
	}
	rt->rt_type = res->type;

893
	if (dev != NULL && rt->n == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
894
895
896
		n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev);
		if (IS_ERR(n))
			return PTR_ERR(n);
897
		rt->n = n;
Linus Torvalds's avatar
Linus Torvalds committed
898
899
	}

900
	if (dst_metric(&rt->dst, RTAX_MTU) > rt->dst.dev->mtu)
901
		dst_metric_set(&rt->dst, RTAX_MTU, rt->dst.dev->mtu);
902
903
	mss_metric = dst_metric_raw(&rt->dst, RTAX_ADVMSS);
	if (mss_metric) {
904
		unsigned int mss = dn_mss_from_pmtu(dev, dst_mtu(&rt->dst));
905
		if (mss_metric > mss)
906
907
			dst_metric_set(&rt->dst, RTAX_ADVMSS, mss);
	}
Linus Torvalds's avatar
Linus Torvalds committed
908
909
910
	return 0;
}

911
static inline int dn_match_addr(__le16 addr1, __le16 addr2)
Linus Torvalds's avatar
Linus Torvalds committed
912
{
913
	__u16 tmp = le16_to_cpu(addr1) ^ le16_to_cpu(addr2);
Linus Torvalds's avatar
Linus Torvalds committed
914
915
916
917
918
919
920
921
	int match = 16;
	while(tmp) {
		tmp >>= 1;
		match--;
	}
	return match;
}

922
static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope)
Linus Torvalds's avatar
Linus Torvalds committed
923
{
924
	__le16 saddr = 0;
925
	struct dn_dev *dn_db;
Linus Torvalds's avatar
Linus Torvalds committed
926
927
928
929
	struct dn_ifaddr *ifa;
	int best_match = 0;
	int ret;

930
931
932
933
934
	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)) {
Linus Torvalds's avatar
Linus Torvalds committed
935
936
937
938
939
940
941
942
943
944
945
946
		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;
	}
947
	rcu_read_unlock();
Linus Torvalds's avatar
Linus Torvalds committed
948
949
950
951

	return saddr;
}

952
static inline __le16 __dn_fib_res_prefsrc(struct dn_fib_res *res)
Linus Torvalds's avatar
Linus Torvalds committed
953
954
955
956
{
	return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope);
}

957
static inline __le16 dn_fib_rules_map_destination(__le16 daddr, struct dn_fib_res *res)
Linus Torvalds's avatar
Linus Torvalds committed
958
{
959
	__le16 mask = dnet_make_mask(res->prefixlen);
Linus Torvalds's avatar
Linus Torvalds committed
960
961
962
	return (daddr&~mask)|res->fi->fib_nh->nh_gw;
}

963
static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *oldflp, int try_hard)
Linus Torvalds's avatar
Linus Torvalds committed
964
{
965
966
967
968
969
	struct flowidn fld = {
		.daddr = oldflp->daddr,
		.saddr = oldflp->saddr,
		.flowidn_scope = RT_SCOPE_UNIVERSE,
		.flowidn_mark = oldflp->flowidn_mark,
970
		.flowidn_iif = LOOPBACK_IFINDEX,
971
		.flowidn_oif = oldflp->flowidn_oif,
972
	};
Linus Torvalds's avatar
Linus Torvalds committed
973
	struct dn_route *rt = NULL;
974
	struct net_device *dev_out = NULL, *dev;
Linus Torvalds's avatar
Linus Torvalds committed
975
	struct neighbour *neigh = NULL;
976
977
	unsigned int hash;
	unsigned int flags = 0;
Linus Torvalds's avatar
Linus Torvalds committed
978
979
980
	struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST };
	int err;
	int free_res = 0;
981
	__le16 gateway = 0;
Linus Torvalds's avatar
Linus Torvalds committed
982
983
984
985

	if (decnet_debug_level & 16)
		printk(KERN_DEBUG
		       "dn_route_output_slow: dst=%04x src=%04x mark=%d"
986
987
		       " iif=%d oif=%d\n", le16_to_cpu(oldflp->daddr),
		       le16_to_cpu(oldflp->saddr),
988
		       oldflp->flowidn_mark, LOOPBACK_IFINDEX,
989
		       oldflp->flowidn_oif);
Linus Torvalds's avatar
Linus Torvalds committed
990
991

	/* If we have an output interface, verify its a DECnet device */
992
993
	if (oldflp->flowidn_oif) {
		dev_out = dev_get_by_index(&init_net, oldflp->flowidn_oif);
Linus Torvalds's avatar
Linus Torvalds committed
994
995
996
997
998
999
1000
		err = -ENODEV;
		if (dev_out && dev_out->dn_ptr == NULL) {
			dev_put(dev_out);
			dev_out = NULL;
		}
		if (dev_out == NULL)
			goto out;