Blame view

net/decnet/dn_dev.c 32.3 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
  /*
   * 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 Device Layer
   *
   * Authors:     Steve Whitehouse <SteveW@ACM.org>
   *              Eduardo Marcelo Serrat <emserrat@geocities.com>
   *
   * Changes:
   *          Steve Whitehouse : Devices now see incoming frames so they
   *                             can mark on who it came from.
   *          Steve Whitehouse : Fixed bug in creating neighbours. Each neighbour
   *                             can now have a device specific setup func.
   *          Steve Whitehouse : Added /proc/sys/net/decnet/conf/<dev>/
   *          Steve Whitehouse : Fixed bug which sometimes killed timer
   *          Steve Whitehouse : Multiple ifaddr support
   *          Steve Whitehouse : SIOCGIFCONF is now a compile time option
   *          Steve Whitehouse : /proc/sys/net/decnet/conf/<sys>/forwarding
   *          Steve Whitehouse : Removed timer1 - it's a user space issue now
   *         Patrick Caulfield : Fixed router hello message format
   *          Steve Whitehouse : Got rid of constant sizes for blksize for
   *                             devices. All mtu based now.
   */
4fc268d24   Randy Dunlap   [PATCH] capable/c...
26
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
32
33
34
35
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/init.h>
  #include <linux/net.h>
  #include <linux/netdevice.h>
  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
  #include <linux/timer.h>
  #include <linux/string.h>
1823730fb   Thomas Graf   [IPv4]: Move inte...
36
  #include <linux/if_addr.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
  #include <linux/if_arp.h>
  #include <linux/if_ether.h>
  #include <linux/skbuff.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
  #include <linux/sysctl.h>
  #include <linux/notifier.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
42
  #include <linux/slab.h>
b5c5c36d3   Himangi Saraogi   dn_dev: Use time_...
43
  #include <linux/jiffies.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  #include <asm/uaccess.h>
457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
45
  #include <net/net_namespace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
  #include <net/neighbour.h>
  #include <net/dst.h>
  #include <net/flow.h>
a8731cbf6   Steven Whitehouse   [DECNET]: Covert ...
49
  #include <net/fib_rules.h>
a6f01cace   Thomas Graf   [DECNET] address:...
50
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  #include <net/dn.h>
  #include <net/dn_dev.h>
  #include <net/dn_route.h>
  #include <net/dn_neigh.h>
  #include <net/dn_fib.h>
  
  #define DN_IFREQ_SIZE (sizeof(struct ifreq) - sizeof(struct sockaddr) + sizeof(struct sockaddr_dn))
  
  static char dn_rt_all_end_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x04,0x00,0x00};
  static char dn_rt_all_rt_mcast[ETH_ALEN]  = {0xAB,0x00,0x00,0x03,0x00,0x00};
  static char dn_hiord[ETH_ALEN]            = {0xAA,0x00,0x04,0x00,0x00,0x00};
  static unsigned char dn_eco_version[3]    = {0x02,0x00,0x00};
  
  extern struct neigh_table dn_neigh_table;
  
  /*
   * decnet_address is kept in network order.
   */
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
69
  __le16 decnet_address = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70

e5c140a34   stephen hemminger   decnet: convert d...
71
  static DEFINE_SPINLOCK(dndev_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  static struct net_device *decnet_default_device;
e041c6834   Alan Stern   [PATCH] Notifier ...
73
  static BLOCKING_NOTIFIER_HEAD(dnaddr_chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
  
  static struct dn_dev *dn_dev_create(struct net_device *dev, int *err);
  static void dn_dev_delete(struct net_device *dev);
b020b942c   Thomas Graf   [DECNET] address:...
77
  static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  
  static int dn_eth_up(struct net_device *);
  static void dn_eth_down(struct net_device *);
  static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa);
  static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa);
  
  static struct dn_dev_parms dn_dev_list[] =  {
  {
  	.type =		ARPHRD_ETHER, /* Ethernet */
  	.mode =		DN_DEV_BCAST,
  	.state =	DN_DEV_S_RU,
  	.t2 =		1,
  	.t3 =		10,
  	.name =		"ethernet",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
97
98
99
100
101
102
  	.up =		dn_eth_up,
  	.down = 	dn_eth_down,
  	.timer3 =	dn_send_brd_hello,
  },
  {
  	.type =		ARPHRD_IPGRE, /* DECnet tunneled over GRE in IP */
  	.mode =		DN_DEV_BCAST,
  	.state =	DN_DEV_S_RU,
  	.t2 =		1,
  	.t3 =		10,
  	.name =		"ipgre",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
106
107
108
109
110
111
112
  	.timer3 =	dn_send_brd_hello,
  },
  #if 0
  {
  	.type =		ARPHRD_X25, /* Bog standard X.25 */
  	.mode =		DN_DEV_UCAST,
  	.state =	DN_DEV_S_DS,
  	.t2 =		1,
  	.t3 =		120,
  	.name =		"x25",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
117
118
119
120
121
122
123
  	.timer3 =	dn_send_ptp_hello,
  },
  #endif
  #if 0
  {
  	.type =		ARPHRD_PPP, /* DECnet over PPP */
  	.mode =		DN_DEV_BCAST,
  	.state =	DN_DEV_S_RU,
  	.t2 =		1,
  	.t3 =		10,
  	.name =		"ppp",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
131
132
133
  	.timer3 =	dn_send_brd_hello,
  },
  #endif
  {
  	.type =		ARPHRD_DDCMP, /* DECnet over DDCMP */
  	.mode =		DN_DEV_UCAST,
  	.state =	DN_DEV_S_DS,
  	.t2 =		1,
  	.t3 =		120,
  	.name =		"ddcmp",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
139
140
141
142
  	.timer3 =	dn_send_ptp_hello,
  },
  {
  	.type =		ARPHRD_LOOPBACK, /* Loopback interface - always last */
  	.mode =		DN_DEV_BCAST,
  	.state =	DN_DEV_S_RU,
  	.t2 =		1,
  	.t3 =		10,
  	.name =		"loopback",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
  	.timer3 =	dn_send_brd_hello,
  }
  };
8b14a5367   Denis Cheng   [NET]: all net/ c...
146
  #define DN_DEV_LIST_SIZE ARRAY_SIZE(dn_dev_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147

8fa0b315f   David S. Miller   decnet: Fix compi...
148
  #define DN_DEV_PARMS_OFFSET(x) offsetof(struct dn_dev_parms, x)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
154
155
156
157
158
  
  #ifdef CONFIG_SYSCTL
  
  static int min_t2[] = { 1 };
  static int max_t2[] = { 60 }; /* No max specified, but this seems sensible */
  static int min_t3[] = { 1 };
  static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MULT or T3MULT */
  
  static int min_priority[1];
  static int max_priority[] = { 127 }; /* From DECnet spec */
fe2c6338f   Joe Perches   net: Convert uses...
159
  static int dn_forwarding_proc(struct ctl_table *, int,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  			void __user *, size_t *, loff_t *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
  static struct dn_dev_sysctl_table {
  	struct ctl_table_header *sysctl_header;
fe2c6338f   Joe Perches   net: Convert uses...
163
  	struct ctl_table dn_dev_vars[5];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
  } dn_dev_sysctl = {
  	NULL,
  	{
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
  		.procname = "forwarding",
  		.data = (void *)DN_DEV_PARMS_OFFSET(forwarding),
  		.maxlen = sizeof(int),
  		.mode = 0644,
  		.proc_handler = dn_forwarding_proc,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
  	},
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
  		.procname = "priority",
  		.data = (void *)DN_DEV_PARMS_OFFSET(priority),
  		.maxlen = sizeof(int),
  		.mode = 0644,
  		.proc_handler = proc_dointvec_minmax,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
183
  		.extra1 = &min_priority,
  		.extra2 = &max_priority
  	},
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
188
  		.procname = "t2",
  		.data = (void *)DN_DEV_PARMS_OFFSET(t2),
  		.maxlen = sizeof(int),
  		.mode = 0644,
  		.proc_handler = proc_dointvec_minmax,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
  		.extra1 = &min_t2,
  		.extra2 = &max_t2
  	},
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
197
  		.procname = "t3",
  		.data = (void *)DN_DEV_PARMS_OFFSET(t3),
  		.maxlen = sizeof(int),
  		.mode = 0644,
  		.proc_handler = proc_dointvec_minmax,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
  		.extra1 = &min_t3,
  		.extra2 = &max_t3
  	},
  	{0}
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
207
208
  };
  
  static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms *parms)
  {
  	struct dn_dev_sysctl_table *t;
  	int i;
9bdcc88fa   Eric W. Biederman   net decnet: Conve...
209
  	char path[sizeof("net/decnet/conf/") + IFNAMSIZ];
3151a9ab0   Pavel Emelyanov   [DECNET]: Switch ...
210

c66b721a5   Arnaldo Carvalho de Melo   [DECNET]: Use kme...
211
  	t = kmemdup(&dn_dev_sysctl, sizeof(*t), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
  	if (t == NULL)
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
  	for(i = 0; i < ARRAY_SIZE(t->dn_dev_vars) - 1; i++) {
  		long offset = (long)t->dn_dev_vars[i].data;
  		t->dn_dev_vars[i].data = ((char *)parms) + offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  	}
9bdcc88fa   Eric W. Biederman   net decnet: Conve...
218
219
  	snprintf(path, sizeof(path), "net/decnet/conf/%s",
  		dev? dev->name : parms->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
  	t->dn_dev_vars[0].extra1 = (void *)dev;
9bdcc88fa   Eric W. Biederman   net decnet: Conve...
222
  	t->sysctl_header = register_net_sysctl(&init_net, path, t->dn_dev_vars);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
226
227
228
229
230
231
232
233
  	if (t->sysctl_header == NULL)
  		kfree(t);
  	else
  		parms->sysctl = t;
  }
  
  static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
  {
  	if (parms->sysctl) {
  		struct dn_dev_sysctl_table *t = parms->sysctl;
  		parms->sysctl = NULL;
5dd3df105   Eric W. Biederman   net: Move all of ...
234
  		unregister_net_sysctl_table(t->sysctl_header);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
237
  		kfree(t);
  	}
  }
fe2c6338f   Joe Perches   net: Convert uses...
238
  static int dn_forwarding_proc(struct ctl_table *table, int write,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
244
245
246
247
248
249
  				void __user *buffer,
  				size_t *lenp, loff_t *ppos)
  {
  #ifdef CONFIG_DECNET_ROUTER
  	struct net_device *dev = table->extra1;
  	struct dn_dev *dn_db;
  	int err;
  	int tmp, old;
  
  	if (table->extra1 == NULL)
  		return -EINVAL;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
250
  	dn_db = rcu_dereference_raw(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  	old = dn_db->parms.forwarding;
8d65af789   Alexey Dobriyan   sysctl: remove "s...
252
  	err = proc_dointvec(table, write, buffer, lenp, ppos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  
  	if ((err >= 0) && write) {
  		if (dn_db->parms.forwarding < 0)
  			dn_db->parms.forwarding = 0;
  		if (dn_db->parms.forwarding > 2)
  			dn_db->parms.forwarding = 2;
  		/*
  		 * What an ugly hack this is... its works, just. It
  		 * would be nice if sysctl/proc were just that little
  		 * bit more flexible so I don't have to write a special
  		 * routine, or suffer hacks like this - SJW
  		 */
  		tmp = dn_db->parms.forwarding;
  		dn_db->parms.forwarding = old;
  		if (dn_db->parms.down)
  			dn_db->parms.down(dev);
  		dn_db->parms.forwarding = tmp;
  		if (dn_db->parms.up)
  			dn_db->parms.up(dev);
  	}
  
  	return err;
  #else
  	return -EINVAL;
  #endif
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  #else /* CONFIG_SYSCTL */
  static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
  {
  }
  static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms *parms)
  {
  }
  
  #endif /* CONFIG_SYSCTL */
  
  static inline __u16 mtu2blksize(struct net_device *dev)
  {
  	u32 blksize = dev->mtu;
  	if (blksize > 0xffff)
  		blksize = 0xffff;
  
  	if (dev->type == ARPHRD_ETHER ||
  	    dev->type == ARPHRD_PPP ||
  	    dev->type == ARPHRD_IPGRE ||
  	    dev->type == ARPHRD_LOOPBACK)
  		blksize -= 2;
  
  	return (__u16)blksize;
  }
  
  static struct dn_ifaddr *dn_dev_alloc_ifa(void)
  {
  	struct dn_ifaddr *ifa;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
307
  	ifa = kzalloc(sizeof(*ifa), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
  
  	return ifa;
  }
fc766e4c4   Eric Dumazet   decnet: RCU conve...
311
  static void dn_dev_free_ifa(struct dn_ifaddr *ifa)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  {
1e547757e   Lai Jiangshan   net,rcu: convert ...
313
  	kfree_rcu(ifa, rcu);
fc766e4c4   Eric Dumazet   decnet: RCU conve...
314
315
316
317
318
  }
  
  static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr __rcu **ifap, int destroy)
  {
  	struct dn_ifaddr *ifa1 = rtnl_dereference(*ifap);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
323
324
325
326
  	unsigned char mac_addr[6];
  	struct net_device *dev = dn_db->dev;
  
  	ASSERT_RTNL();
  
  	*ifap = ifa1->ifa_next;
  
  	if (dn_db->dev->type == ARPHRD_ETHER) {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
327
  		if (ifa1->ifa_local != dn_eth2dn(dev->dev_addr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
  			dn_dn2eth(mac_addr, ifa1->ifa_local);
22bedad3c   Jiri Pirko   net: convert mult...
329
  			dev_mc_del(dev, mac_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  		}
  	}
b020b942c   Thomas Graf   [DECNET] address:...
332
  	dn_ifaddr_notify(RTM_DELADDR, ifa1);
e041c6834   Alan Stern   [PATCH] Notifier ...
333
  	blocking_notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  	if (destroy) {
  		dn_dev_free_ifa(ifa1);
  
  		if (dn_db->ifa_list == NULL)
  			dn_dev_delete(dn_db->dev);
  	}
  }
  
  static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa)
  {
  	struct net_device *dev = dn_db->dev;
  	struct dn_ifaddr *ifa1;
  	unsigned char mac_addr[6];
  
  	ASSERT_RTNL();
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
349
  	/* Check for duplicates */
fc766e4c4   Eric Dumazet   decnet: RCU conve...
350
351
352
  	for (ifa1 = rtnl_dereference(dn_db->ifa_list);
  	     ifa1 != NULL;
  	     ifa1 = rtnl_dereference(ifa1->ifa_next)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
  		if (ifa1->ifa_local == ifa->ifa_local)
  			return -EEXIST;
  	}
  
  	if (dev->type == ARPHRD_ETHER) {
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
358
  		if (ifa->ifa_local != dn_eth2dn(dev->dev_addr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
  			dn_dn2eth(mac_addr, ifa->ifa_local);
22bedad3c   Jiri Pirko   net: convert mult...
360
  			dev_mc_add(dev, mac_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
  		}
  	}
  
  	ifa->ifa_next = dn_db->ifa_list;
cf778b00e   Eric Dumazet   net: reintroduce ...
365
  	rcu_assign_pointer(dn_db->ifa_list, ifa);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366

b020b942c   Thomas Graf   [DECNET] address:...
367
  	dn_ifaddr_notify(RTM_NEWADDR, ifa);
e041c6834   Alan Stern   [PATCH] Notifier ...
368
  	blocking_notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
372
373
374
  
  	return 0;
  }
  
  static int dn_dev_set_ifa(struct net_device *dev, struct dn_ifaddr *ifa)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
375
  	struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
  	int rv;
  
  	if (dn_db == NULL) {
  		int err;
  		dn_db = dn_dev_create(dev, &err);
  		if (dn_db == NULL)
  			return err;
  	}
  
  	ifa->ifa_dev = dn_db;
  
  	if (dev->flags & IFF_LOOPBACK)
  		ifa->ifa_scope = RT_SCOPE_HOST;
  
  	rv = dn_dev_insert_ifa(dn_db, ifa);
  	if (rv)
  		dn_dev_free_ifa(ifa);
  	return rv;
  }
  
  
  int dn_dev_ioctl(unsigned int cmd, void __user *arg)
  {
  	char buffer[DN_IFREQ_SIZE];
  	struct ifreq *ifr = (struct ifreq *)buffer;
  	struct sockaddr_dn *sdn = (struct sockaddr_dn *)&ifr->ifr_addr;
  	struct dn_dev *dn_db;
  	struct net_device *dev;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
404
405
  	struct dn_ifaddr *ifa = NULL;
  	struct dn_ifaddr __rcu **ifap = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
  	int ret = 0;
  
  	if (copy_from_user(ifr, arg, DN_IFREQ_SIZE))
  		return -EFAULT;
  	ifr->ifr_name[IFNAMSIZ-1] = 0;
881d966b4   Eric W. Biederman   [NET]: Make the d...
411
  	dev_load(&init_net, ifr->ifr_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412

06f8fe11b   Joe Perches   decnet: Reduce sw...
413
414
415
416
417
418
419
  	switch (cmd) {
  	case SIOCGIFADDR:
  		break;
  	case SIOCSIFADDR:
  		if (!capable(CAP_NET_ADMIN))
  			return -EACCES;
  		if (sdn->sdn_family != AF_DECnet)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  			return -EINVAL;
06f8fe11b   Joe Perches   decnet: Reduce sw...
421
422
423
  		break;
  	default:
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
426
  	}
  
  	rtnl_lock();
881d966b4   Eric W. Biederman   [NET]: Make the d...
427
  	if ((dev = __dev_get_by_name(&init_net, ifr->ifr_name)) == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
430
  		ret = -ENODEV;
  		goto done;
  	}
fc766e4c4   Eric Dumazet   decnet: RCU conve...
431
432
433
434
  	if ((dn_db = rtnl_dereference(dev->dn_ptr)) != NULL) {
  		for (ifap = &dn_db->ifa_list;
  		     (ifa = rtnl_dereference(*ifap)) != NULL;
  		     ifap = &ifa->ifa_next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
437
438
439
440
441
442
  			if (strcmp(ifr->ifr_name, ifa->ifa_label) == 0)
  				break;
  	}
  
  	if (ifa == NULL && cmd != SIOCSIFADDR) {
  		ret = -EADDRNOTAVAIL;
  		goto done;
  	}
06f8fe11b   Joe Perches   decnet: Reduce sw...
443
444
445
446
447
448
449
450
451
452
  	switch (cmd) {
  	case SIOCGIFADDR:
  		*((__le16 *)sdn->sdn_nodeaddr) = ifa->ifa_local;
  		goto rarok;
  
  	case SIOCSIFADDR:
  		if (!ifa) {
  			if ((ifa = dn_dev_alloc_ifa()) == NULL) {
  				ret = -ENOBUFS;
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
  			}
06f8fe11b   Joe Perches   decnet: Reduce sw...
454
455
456
457
458
459
  			memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
  		} else {
  			if (ifa->ifa_local == dn_saddr2dn(sdn))
  				break;
  			dn_dev_del_ifa(dn_db, ifap, 0);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460

06f8fe11b   Joe Perches   decnet: Reduce sw...
461
  		ifa->ifa_local = ifa->ifa_address = dn_saddr2dn(sdn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462

06f8fe11b   Joe Perches   decnet: Reduce sw...
463
  		ret = dn_dev_set_ifa(dev, ifa);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
466
467
468
469
470
471
472
473
474
475
476
477
  	}
  done:
  	rtnl_unlock();
  
  	return ret;
  rarok:
  	if (copy_to_user(arg, ifr, DN_IFREQ_SIZE))
  		ret = -EFAULT;
  	goto done;
  }
  
  struct net_device *dn_dev_get_default(void)
  {
  	struct net_device *dev;
e5c140a34   stephen hemminger   decnet: convert d...
478
479
  
  	spin_lock(&dndev_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
483
484
485
486
  	dev = decnet_default_device;
  	if (dev) {
  		if (dev->dn_ptr)
  			dev_hold(dev);
  		else
  			dev = NULL;
  	}
e5c140a34   stephen hemminger   decnet: convert d...
487
  	spin_unlock(&dndev_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
490
491
492
493
494
495
496
  	return dev;
  }
  
  int dn_dev_set_default(struct net_device *dev, int force)
  {
  	struct net_device *old = NULL;
  	int rv = -EBUSY;
  	if (!dev->dn_ptr)
  		return -ENODEV;
e5c140a34   stephen hemminger   decnet: convert d...
497
498
  
  	spin_lock(&dndev_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
501
502
503
  	if (force || decnet_default_device == NULL) {
  		old = decnet_default_device;
  		decnet_default_device = dev;
  		rv = 0;
  	}
e5c140a34   stephen hemminger   decnet: convert d...
504
  	spin_unlock(&dndev_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
  	if (old)
6a57b2ee4   Patrick Caulfield   [DECNET]: Fix ref...
506
  		dev_put(old);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
508
509
510
511
  	return rv;
  }
  
  static void dn_dev_check_default(struct net_device *dev)
  {
e5c140a34   stephen hemminger   decnet: convert d...
512
  	spin_lock(&dndev_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
517
  	if (dev == decnet_default_device) {
  		decnet_default_device = NULL;
  	} else {
  		dev = NULL;
  	}
e5c140a34   stephen hemminger   decnet: convert d...
518
  	spin_unlock(&dndev_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
  	if (dev)
  		dev_put(dev);
  }
b4d745db1   Eric Dumazet   decnet: avoid tou...
522
523
524
  /*
   * Called with RTNL
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
527
528
  static struct dn_dev *dn_dev_by_index(int ifindex)
  {
  	struct net_device *dev;
  	struct dn_dev *dn_dev = NULL;
b4d745db1   Eric Dumazet   decnet: avoid tou...
529
530
531
  
  	dev = __dev_get_by_index(&init_net, ifindex);
  	if (dev)
fc766e4c4   Eric Dumazet   decnet: RCU conve...
532
  		dn_dev = rtnl_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
  
  	return dn_dev;
  }
ef7c79ed6   Patrick McHardy   [NETLINK]: Mark n...
536
  static const struct nla_policy dn_ifa_policy[IFA_MAX+1] = {
4a89c2562   Thomas Graf   [DECNET] address:...
537
538
539
540
  	[IFA_ADDRESS]		= { .type = NLA_U16 },
  	[IFA_LOCAL]		= { .type = NLA_U16 },
  	[IFA_LABEL]		= { .type = NLA_STRING,
  				    .len = IFNAMSIZ - 1 },
9a32b8604   Jiri Pirko   dn_dev: add suppo...
541
  	[IFA_FLAGS]		= { .type = NLA_U32 },
4a89c2562   Thomas Graf   [DECNET] address:...
542
  };
661d2967b   Thomas Graf   rtnetlink: Remove...
543
  static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
545
  	struct net *net = sock_net(skb->sk);
4a89c2562   Thomas Graf   [DECNET] address:...
546
  	struct nlattr *tb[IFA_MAX+1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
  	struct dn_dev *dn_db;
4a89c2562   Thomas Graf   [DECNET] address:...
548
  	struct ifaddrmsg *ifm;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
549
550
  	struct dn_ifaddr *ifa;
  	struct dn_ifaddr __rcu **ifap;
b854272b3   Denis V. Lunev   [NET]: Modify all...
551
  	int err = -EINVAL;
90f62cf30   Eric W. Biederman   net: Use netlink_...
552
  	if (!netlink_capable(skb, CAP_NET_ADMIN))
dfc47ef86   Eric W. Biederman   net: Push capable...
553
  		return -EPERM;
09ad9bc75   Octavian Purdila   net: use net_eq t...
554
  	if (!net_eq(net, &init_net))
b854272b3   Denis V. Lunev   [NET]: Modify all...
555
  		goto errout;
4a89c2562   Thomas Graf   [DECNET] address:...
556
557
558
559
  
  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy);
  	if (err < 0)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560

3ccd86241   Pavel Emelyanov   [DECNET]: dn_nl_d...
561
  	err = -ENODEV;
4a89c2562   Thomas Graf   [DECNET] address:...
562
  	ifm = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
  	if ((dn_db = dn_dev_by_index(ifm->ifa_index)) == NULL)
4a89c2562   Thomas Graf   [DECNET] address:...
564
  		goto errout;
3ccd86241   Pavel Emelyanov   [DECNET]: dn_nl_d...
565
  	err = -EADDRNOTAVAIL;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
566
567
568
  	for (ifap = &dn_db->ifa_list;
  	     (ifa = rtnl_dereference(*ifap)) != NULL;
  	     ifap = &ifa->ifa_next) {
4a89c2562   Thomas Graf   [DECNET] address:...
569
570
571
  		if (tb[IFA_LOCAL] &&
  		    nla_memcmp(tb[IFA_LOCAL], &ifa->ifa_local, 2))
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572

4a89c2562   Thomas Graf   [DECNET] address:...
573
  		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
  			continue;
  
  		dn_dev_del_ifa(dn_db, ifap, 1);
  		return 0;
  	}
4a89c2562   Thomas Graf   [DECNET] address:...
579
580
  errout:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
  }
661d2967b   Thomas Graf   rtnetlink: Remove...
582
  static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
584
  	struct net *net = sock_net(skb->sk);
4a89c2562   Thomas Graf   [DECNET] address:...
585
  	struct nlattr *tb[IFA_MAX+1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
  	struct net_device *dev;
  	struct dn_dev *dn_db;
4a89c2562   Thomas Graf   [DECNET] address:...
588
  	struct ifaddrmsg *ifm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
  	struct dn_ifaddr *ifa;
4a89c2562   Thomas Graf   [DECNET] address:...
590
  	int err;
90f62cf30   Eric W. Biederman   net: Use netlink_...
591
  	if (!netlink_capable(skb, CAP_NET_ADMIN))
dfc47ef86   Eric W. Biederman   net: Push capable...
592
  		return -EPERM;
09ad9bc75   Octavian Purdila   net: use net_eq t...
593
  	if (!net_eq(net, &init_net))
b854272b3   Denis V. Lunev   [NET]: Modify all...
594
  		return -EINVAL;
4a89c2562   Thomas Graf   [DECNET] address:...
595
596
597
  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598

4a89c2562   Thomas Graf   [DECNET] address:...
599
  	if (tb[IFA_LOCAL] == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
  		return -EINVAL;
4a89c2562   Thomas Graf   [DECNET] address:...
601
  	ifm = nlmsg_data(nlh);
881d966b4   Eric W. Biederman   [NET]: Make the d...
602
  	if ((dev = __dev_get_by_index(&init_net, ifm->ifa_index)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
  		return -ENODEV;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
604
  	if ((dn_db = rtnl_dereference(dev->dn_ptr)) == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
  		dn_db = dn_dev_create(dev, &err);
  		if (!dn_db)
  			return err;
  	}
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
609

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
  	if ((ifa = dn_dev_alloc_ifa()) == NULL)
  		return -ENOBUFS;
4a89c2562   Thomas Graf   [DECNET] address:...
612
613
614
615
616
  	if (tb[IFA_ADDRESS] == NULL)
  		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
  
  	ifa->ifa_local = nla_get_le16(tb[IFA_LOCAL]);
  	ifa->ifa_address = nla_get_le16(tb[IFA_ADDRESS]);
9a32b8604   Jiri Pirko   dn_dev: add suppo...
617
618
  	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
  					 ifm->ifa_flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
  	ifa->ifa_scope = ifm->ifa_scope;
  	ifa->ifa_dev = dn_db;
4a89c2562   Thomas Graf   [DECNET] address:...
621
622
623
  
  	if (tb[IFA_LABEL])
  		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
  	else
  		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
4a89c2562   Thomas Graf   [DECNET] address:...
626
627
  	err = dn_dev_insert_ifa(dn_db, ifa);
  	if (err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
  		dn_dev_free_ifa(ifa);
4a89c2562   Thomas Graf   [DECNET] address:...
629
630
  
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
  }
a6f01cace   Thomas Graf   [DECNET] address:...
632
633
634
635
636
  static inline size_t dn_ifaddr_nlmsg_size(void)
  {
  	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
  	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
  	       + nla_total_size(2) /* IFA_ADDRESS */
9a32b8604   Jiri Pirko   dn_dev: add suppo...
637
638
  	       + nla_total_size(2) /* IFA_LOCAL */
  	       + nla_total_size(4); /* IFA_FLAGS */
a6f01cace   Thomas Graf   [DECNET] address:...
639
  }
4a89c2562   Thomas Graf   [DECNET] address:...
640
  static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
15e473046   Eric W. Biederman   netlink: Rename p...
641
  			     u32 portid, u32 seq, int event, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
  {
  	struct ifaddrmsg *ifm;
  	struct nlmsghdr *nlh;
9a32b8604   Jiri Pirko   dn_dev: add suppo...
645
  	u32 ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646

15e473046   Eric W. Biederman   netlink: Rename p...
647
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
4a89c2562   Thomas Graf   [DECNET] address:...
648
  	if (nlh == NULL)
26932566a   Patrick McHardy   [NETLINK]: Don't ...
649
  		return -EMSGSIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650

4a89c2562   Thomas Graf   [DECNET] address:...
651
  	ifm = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
653
  	ifm->ifa_family = AF_DECnet;
  	ifm->ifa_prefixlen = 16;
9a32b8604   Jiri Pirko   dn_dev: add suppo...
654
  	ifm->ifa_flags = ifa_flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
  	ifm->ifa_scope = ifa->ifa_scope;
  	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
4a89c2562   Thomas Graf   [DECNET] address:...
657

b21dddb9d   David S. Miller   decnet: Stop usin...
658
659
660
661
662
  	if ((ifa->ifa_address &&
  	     nla_put_le16(skb, IFA_ADDRESS, ifa->ifa_address)) ||
  	    (ifa->ifa_local &&
  	     nla_put_le16(skb, IFA_LOCAL, ifa->ifa_local)) ||
  	    (ifa->ifa_label[0] &&
9a32b8604   Jiri Pirko   dn_dev: add suppo...
663
664
  	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
  	     nla_put_u32(skb, IFA_FLAGS, ifa_flags))
b21dddb9d   David S. Miller   decnet: Stop usin...
665
  		goto nla_put_failure;
053c095a8   Johannes Berg   netlink: make nlm...
666
667
  	nlmsg_end(skb, nlh);
  	return 0;
4a89c2562   Thomas Graf   [DECNET] address:...
668
669
  
  nla_put_failure:
26932566a   Patrick McHardy   [NETLINK]: Don't ...
670
671
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
  }
b020b942c   Thomas Graf   [DECNET] address:...
673
  static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
  {
  	struct sk_buff *skb;
dc738dd83   Thomas Graf   [DECNET]: Convert...
676
  	int err = -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677

a6f01cace   Thomas Graf   [DECNET] address:...
678
  	skb = alloc_skb(dn_ifaddr_nlmsg_size(), GFP_KERNEL);
dc738dd83   Thomas Graf   [DECNET]: Convert...
679
680
  	if (skb == NULL)
  		goto errout;
4a89c2562   Thomas Graf   [DECNET] address:...
681
  	err = dn_nl_fill_ifaddr(skb, ifa, 0, 0, event, 0);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
682
683
684
685
686
687
  	if (err < 0) {
  		/* -EMSGSIZE implies BUG in dn_ifaddr_nlmsg_size() */
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(skb);
  		goto errout;
  	}
1ce85fe40   Pablo Neira Ayuso   netlink: change n...
688
689
  	rtnl_notify(skb, &init_net, 0, RTNLGRP_DECnet_IFADDR, NULL, GFP_KERNEL);
  	return;
dc738dd83   Thomas Graf   [DECNET]: Convert...
690
691
  errout:
  	if (err < 0)
97c53cacf   Denis V. Lunev   [NET]: Make rtnet...
692
  		rtnl_set_sk_err(&init_net, RTNLGRP_DECnet_IFADDR, err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
  }
4a89c2562   Thomas Graf   [DECNET] address:...
694
  static int dn_nl_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
696
  	struct net *net = sock_net(skb->sk);
4a89c2562   Thomas Graf   [DECNET] address:...
697
  	int idx, dn_idx = 0, skip_ndevs, skip_naddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
699
700
  	struct net_device *dev;
  	struct dn_dev *dn_db;
  	struct dn_ifaddr *ifa;
09ad9bc75   Octavian Purdila   net: use net_eq t...
701
  	if (!net_eq(net, &init_net))
b854272b3   Denis V. Lunev   [NET]: Modify all...
702
  		return 0;
4a89c2562   Thomas Graf   [DECNET] address:...
703
704
  	skip_ndevs = cb->args[0];
  	skip_naddr = cb->args[1];
7562f876c   Pavel Emelianov   [NET]: Rework dev...
705
  	idx = 0;
e67f88dd1   Eric Dumazet   net: dont hold rt...
706
707
  	rcu_read_lock();
  	for_each_netdev_rcu(&init_net, dev) {
4a89c2562   Thomas Graf   [DECNET] address:...
708
  		if (idx < skip_ndevs)
7562f876c   Pavel Emelianov   [NET]: Rework dev...
709
  			goto cont;
4a89c2562   Thomas Graf   [DECNET] address:...
710
711
712
713
714
  		else if (idx > skip_ndevs) {
  			/* Only skip over addresses for first dev dumped
  			 * in this iteration (idx == skip_ndevs) */
  			skip_naddr = 0;
  		}
e67f88dd1   Eric Dumazet   net: dont hold rt...
715
  		if ((dn_db = rcu_dereference(dev->dn_ptr)) == NULL)
7562f876c   Pavel Emelianov   [NET]: Rework dev...
716
  			goto cont;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717

e67f88dd1   Eric Dumazet   net: dont hold rt...
718
719
  		for (ifa = rcu_dereference(dn_db->ifa_list), dn_idx = 0; ifa;
  		     ifa = rcu_dereference(ifa->ifa_next), dn_idx++) {
4a89c2562   Thomas Graf   [DECNET] address:...
720
  			if (dn_idx < skip_naddr)
a2221f308   Patrick McHardy   [DECNET]: Fix int...
721
  				continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722

15e473046   Eric W. Biederman   netlink: Rename p...
723
  			if (dn_nl_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).portid,
4a89c2562   Thomas Graf   [DECNET] address:...
724
725
  					      cb->nlh->nlmsg_seq, RTM_NEWADDR,
  					      NLM_F_MULTI) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
727
  				goto done;
  		}
7562f876c   Pavel Emelianov   [NET]: Rework dev...
728
729
  cont:
  		idx++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
  	}
  done:
e67f88dd1   Eric Dumazet   net: dont hold rt...
732
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
735
736
737
  	cb->args[0] = idx;
  	cb->args[1] = dn_idx;
  
  	return skb->len;
  }
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
738
  static int dn_dev_get_first(struct net_device *dev, __le16 *addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
740
  	struct dn_dev *dn_db;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
  	struct dn_ifaddr *ifa;
  	int rv = -ENODEV;
41bdecf17   stephen hemminger   decnet: add RTNL ...
743

fc766e4c4   Eric Dumazet   decnet: RCU conve...
744
745
  	rcu_read_lock();
  	dn_db = rcu_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
747
  	if (dn_db == NULL)
  		goto out;
41bdecf17   stephen hemminger   decnet: add RTNL ...
748

fc766e4c4   Eric Dumazet   decnet: RCU conve...
749
  	ifa = rcu_dereference(dn_db->ifa_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
751
752
753
754
  	if (ifa != NULL) {
  		*addr = ifa->ifa_local;
  		rv = 0;
  	}
  out:
fc766e4c4   Eric Dumazet   decnet: RCU conve...
755
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
  	return rv;
  }
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
758
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
760
761
762
763
764
765
766
767
   * Find a default address to bind to.
   *
   * This is one of those areas where the initial VMS concepts don't really
   * map onto the Linux concepts, and since we introduced multiple addresses
   * per interface we have to cope with slightly odd ways of finding out what
   * "our address" really is. Mostly it's not a problem; for this we just guess
   * a sensible default. Eventually the routing code will take care of all the
   * nasties for us I hope.
   */
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
768
  int dn_dev_bind_default(__le16 *addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
772
773
774
  {
  	struct net_device *dev;
  	int rv;
  	dev = dn_dev_get_default();
  last_chance:
  	if (dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775
  		rv = dn_dev_get_first(dev, addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
  		dev_put(dev);
2774c7aba   Eric W. Biederman   [NET]: Make the l...
777
  		if (rv == 0 || dev == init_net.loopback_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
  			return rv;
  	}
2774c7aba   Eric W. Biederman   [NET]: Make the l...
780
  	dev = init_net.loopback_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
782
783
784
785
786
  	dev_hold(dev);
  	goto last_chance;
  }
  
  static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa)
  {
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
787
788
789
  	struct endnode_hello_message *msg;
  	struct sk_buff *skb = NULL;
  	__le16 *pktlen;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
790
  	struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
792
  	if ((skb = dn_alloc_skb(NULL, sizeof(*msg), GFP_ATOMIC)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
  		return;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
794
  	skb->dev = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
796
  	msg = (struct endnode_hello_message *)skb_put(skb,sizeof(*msg));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
798
799
  	msg->msgflg  = 0x0D;
  	memcpy(msg->tiver, dn_eco_version, 3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
  	dn_dn2eth(msg->id, ifa->ifa_local);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
801
  	msg->iinfo   = DN_RT_INFO_ENDN;
c4106aa88   Harvey Harrison   decnet: remove pr...
802
  	msg->blksize = cpu_to_le16(mtu2blksize(dev));
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
803
804
805
  	msg->area    = 0x00;
  	memset(msg->seed, 0, 8);
  	memcpy(msg->neighbor, dn_hiord, ETH_ALEN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806
807
808
809
810
  
  	if (dn_db->router) {
  		struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
  		dn_dn2eth(msg->neighbor, dn->addr);
  	}
c4106aa88   Harvey Harrison   decnet: remove pr...
811
  	msg->timer   = cpu_to_le16((unsigned short)dn_db->parms.t3);
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
812
813
814
815
816
  	msg->mpd     = 0x00;
  	msg->datalen = 0x02;
  	memset(msg->data, 0xAA, 2);
  
  	pktlen = (__le16 *)skb_push(skb,2);
c4106aa88   Harvey Harrison   decnet: remove pr...
817
  	*pktlen = cpu_to_le16(skb->len - 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818

c1d2bbe1c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
819
  	skb_reset_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
821
822
823
824
825
826
827
828
829
  
  	dn_rt_finish_output(skb, dn_rt_all_rt_mcast, msg->id);
  }
  
  
  #define DRDELAY (5 * HZ)
  
  static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db, struct dn_ifaddr *ifa)
  {
  	/* First check time since device went up */
b5c5c36d3   Himangi Saraogi   dn_dev: Use time_...
830
  	if (time_before(jiffies, dn_db->uptime + DRDELAY))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
832
833
834
835
836
837
838
839
840
841
842
843
  		return 0;
  
  	/* If there is no router, then yes... */
  	if (!dn_db->router)
  		return 1;
  
  	/* otherwise only if we have a higher priority or.. */
  	if (dn->priority < dn_db->parms.priority)
  		return 1;
  
  	/* if we have equal priority and a higher node number */
  	if (dn->priority != dn_db->parms.priority)
  		return 0;
c4106aa88   Harvey Harrison   decnet: remove pr...
844
  	if (le16_to_cpu(dn->addr) < le16_to_cpu(ifa->ifa_local))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
846
847
848
849
850
851
852
  		return 1;
  
  	return 0;
  }
  
  static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa)
  {
  	int n;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
853
  	struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
855
856
857
858
  	struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
  	struct sk_buff *skb;
  	size_t size;
  	unsigned char *ptr;
  	unsigned char *i1, *i2;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
859
  	__le16 *pktlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
  	char *src;
  
  	if (mtu2blksize(dev) < (26 + 7))
  		return;
  
  	n = mtu2blksize(dev) - 26;
  	n /= 7;
  
  	if (n > 32)
  		n = 32;
  
  	size = 2 + 26 + 7 * n;
  
  	if ((skb = dn_alloc_skb(NULL, size, GFP_ATOMIC)) == NULL)
  		return;
  
  	skb->dev = dev;
  	ptr = skb_put(skb, size);
  
  	*ptr++ = DN_RT_PKT_CNTL | DN_RT_PKT_ERTH;
  	*ptr++ = 2; /* ECO */
  	*ptr++ = 0;
  	*ptr++ = 0;
  	dn_dn2eth(ptr, ifa->ifa_local);
  	src = ptr;
  	ptr += ETH_ALEN;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
886
  	*ptr++ = dn_db->parms.forwarding == 1 ?
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  			DN_RT_INFO_L1RT : DN_RT_INFO_L2RT;
c4106aa88   Harvey Harrison   decnet: remove pr...
888
  	*((__le16 *)ptr) = cpu_to_le16(mtu2blksize(dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
  	ptr += 2;
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
890
  	*ptr++ = dn_db->parms.priority; /* Priority */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891
  	*ptr++ = 0; /* Area: Reserved */
c4106aa88   Harvey Harrison   decnet: remove pr...
892
  	*((__le16 *)ptr) = cpu_to_le16((unsigned short)dn_db->parms.t3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
898
899
900
901
902
903
904
905
  	ptr += 2;
  	*ptr++ = 0; /* MPD: Reserved */
  	i1 = ptr++;
  	memset(ptr, 0, 7); /* Name: Reserved */
  	ptr += 7;
  	i2 = ptr++;
  
  	n = dn_neigh_elist(dev, ptr, n);
  
  	*i2 = 7 * n;
  	*i1 = 8 + *i2;
  
  	skb_trim(skb, (27 + *i2));
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
906
  	pktlen = (__le16 *)skb_push(skb, 2);
c4106aa88   Harvey Harrison   decnet: remove pr...
907
  	*pktlen = cpu_to_le16(skb->len - 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908

c1d2bbe1c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
909
  	skb_reset_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910
911
912
913
914
915
916
917
918
919
920
921
922
  
  	if (dn_am_i_a_router(dn, dn_db, ifa)) {
  		struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
  		if (skb2) {
  			dn_rt_finish_output(skb2, dn_rt_all_end_mcast, src);
  		}
  	}
  
  	dn_rt_finish_output(skb, dn_rt_all_rt_mcast, src);
  }
  
  static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
923
  	struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
  
  	if (dn_db->parms.forwarding == 0)
  		dn_send_endnode_hello(dev, ifa);
  	else
  		dn_send_router_hello(dev, ifa);
  }
  
  static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa)
  {
  	int tdlen = 16;
  	int size = dev->hard_header_len + 2 + 4 + tdlen;
  	struct sk_buff *skb = dn_alloc_skb(NULL, size, GFP_ATOMIC);
  	int i;
  	unsigned char *ptr;
  	char src[ETH_ALEN];
  
  	if (skb == NULL)
  		return ;
  
  	skb->dev = dev;
  	skb_push(skb, dev->hard_header_len);
  	ptr = skb_put(skb, 2 + 4 + tdlen);
  
  	*ptr++ = DN_RT_PKT_HELO;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
948
  	*((__le16 *)ptr) = ifa->ifa_local;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
950
951
952
953
954
955
956
957
958
959
960
  	ptr += 2;
  	*ptr++ = tdlen;
  
  	for(i = 0; i < tdlen; i++)
  		*ptr++ = 0252;
  
  	dn_dn2eth(src, ifa->ifa_local);
  	dn_rt_finish_output(skb, dn_rt_all_rt_mcast, src);
  }
  
  static int dn_eth_up(struct net_device *dev)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
961
  	struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
963
  
  	if (dn_db->parms.forwarding == 0)
22bedad3c   Jiri Pirko   net: convert mult...
964
  		dev_mc_add(dev, dn_rt_all_end_mcast);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965
  	else
22bedad3c   Jiri Pirko   net: convert mult...
966
  		dev_mc_add(dev, dn_rt_all_rt_mcast);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
969
970
971
972
973
974
  	dn_db->use_long = 1;
  
  	return 0;
  }
  
  static void dn_eth_down(struct net_device *dev)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
975
  	struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
976
977
  
  	if (dn_db->parms.forwarding == 0)
22bedad3c   Jiri Pirko   net: convert mult...
978
  		dev_mc_del(dev, dn_rt_all_end_mcast);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
979
  	else
22bedad3c   Jiri Pirko   net: convert mult...
980
  		dev_mc_del(dev, dn_rt_all_rt_mcast);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
982
983
984
985
986
987
  }
  
  static void dn_dev_set_timer(struct net_device *dev);
  
  static void dn_dev_timer_func(unsigned long arg)
  {
  	struct net_device *dev = (struct net_device *)arg;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
988
  	struct dn_dev *dn_db;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
  	struct dn_ifaddr *ifa;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
990
991
  	rcu_read_lock();
  	dn_db = rcu_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
993
  	if (dn_db->t3 <= dn_db->parms.t2) {
  		if (dn_db->parms.timer3) {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
994
995
996
  			for (ifa = rcu_dereference(dn_db->ifa_list);
  			     ifa;
  			     ifa = rcu_dereference(ifa->ifa_next)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
997
998
999
1000
1001
1002
1003
1004
  				if (!(ifa->ifa_flags & IFA_F_SECONDARY))
  					dn_db->parms.timer3(dev, ifa);
  			}
  		}
  		dn_db->t3 = dn_db->parms.t3;
  	} else {
  		dn_db->t3 -= dn_db->parms.t2;
  	}
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1005
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1006
1007
1008
1009
1010
  	dn_dev_set_timer(dev);
  }
  
  static void dn_dev_set_timer(struct net_device *dev)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1011
  	struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
  
  	if (dn_db->parms.t2 > dn_db->parms.t3)
  		dn_db->parms.t2 = dn_db->parms.t3;
  
  	dn_db->timer.data = (unsigned long)dev;
  	dn_db->timer.function = dn_dev_timer_func;
  	dn_db->timer.expires = jiffies + (dn_db->parms.t2 * HZ);
  
  	add_timer(&dn_db->timer);
  }
5eaa65b24   Roel Kluin   net: Make static
1022
  static struct dn_dev *dn_dev_create(struct net_device *dev, int *err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
  {
  	int i;
  	struct dn_dev_parms *p = dn_dev_list;
  	struct dn_dev *dn_db;
  
  	for(i = 0; i < DN_DEV_LIST_SIZE; i++, p++) {
  		if (p->type == dev->type)
  			break;
  	}
  
  	*err = -ENODEV;
  	if (i == DN_DEV_LIST_SIZE)
  		return NULL;
  
  	*err = -ENOBUFS;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
1038
  	if ((dn_db = kzalloc(sizeof(struct dn_dev), GFP_ATOMIC)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1040
  	memcpy(&dn_db->parms, p, sizeof(struct dn_dev_parms));
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1041

cf778b00e   Eric Dumazet   net: reintroduce ...
1042
  	rcu_assign_pointer(dev->dn_ptr, dn_db);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1043
1044
1045
1046
  	dn_db->dev = dev;
  	init_timer(&dn_db->timer);
  
  	dn_db->uptime = jiffies;
95743deb3   Eric W. Biederman   [DECNET]: Handle ...
1047
1048
1049
  
  	dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table);
  	if (!dn_db->neigh_parms) {
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
1050
  		RCU_INIT_POINTER(dev->dn_ptr, NULL);
95743deb3   Eric W. Biederman   [DECNET]: Handle ...
1051
1052
1053
  		kfree(dn_db);
  		return NULL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054
1055
  	if (dn_db->parms.up) {
  		if (dn_db->parms.up(dev) < 0) {
95743deb3   Eric W. Biederman   [DECNET]: Handle ...
1056
  			neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
1058
1059
1060
1061
  			dev->dn_ptr = NULL;
  			kfree(dn_db);
  			return NULL;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
  	dn_dev_sysctl_register(dev, &dn_db->parms);
  
  	dn_dev_set_timer(dev);
  
  	*err = 0;
  	return dn_db;
  }
  
  
  /*
   * This processes a device up event. We only start up
   * the loopback device & ethernet devices with correct
b595076a1   Uwe Kleine-König   tree-wide: fix co...
1074
   * MAC addresses automatically. Others must be started
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
   * specifically.
   *
   * FIXME: How should we configure the loopback address ? If we could dispense
   * with using decnet_address here and for autobind, it will be one less thing
   * for users to worry about setting up.
   */
  
  void dn_dev_up(struct net_device *dev)
  {
  	struct dn_ifaddr *ifa;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
1085
  	__le16 addr = decnet_address;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
  	int maybe_default = 0;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1087
  	struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
  
  	if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK))
  		return;
  
  	/*
  	 * Need to ensure that loopback device has a dn_db attached to it
  	 * to allow creation of neighbours against it, even though it might
  	 * not have a local address of its own. Might as well do the same for
  	 * all autoconfigured interfaces.
  	 */
  	if (dn_db == NULL) {
  		int err;
  		dn_db = dn_dev_create(dev, &err);
  		if (dn_db == NULL)
  			return;
  	}
  
  	if (dev->type == ARPHRD_ETHER) {
  		if (memcmp(dev->dev_addr, dn_hiord, 4) != 0)
  			return;
c4ea94ab3   Steven Whitehouse   [DECnet]: Endian ...
1108
  		addr = dn_eth2dn(dev->dev_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
  		maybe_default = 1;
  	}
  
  	if (addr == 0)
  		return;
  
  	if ((ifa = dn_dev_alloc_ifa()) == NULL)
  		return;
  
  	ifa->ifa_local = ifa->ifa_address = addr;
  	ifa->ifa_flags = 0;
  	ifa->ifa_scope = RT_SCOPE_UNIVERSE;
  	strcpy(ifa->ifa_label, dev->name);
  
  	dn_dev_set_ifa(dev, ifa);
  
  	/*
  	 * Automagically set the default device to the first automatically
  	 * configured ethernet card in the system.
  	 */
  	if (maybe_default) {
  		dev_hold(dev);
  		if (dn_dev_set_default(dev, 0))
  			dev_put(dev);
  	}
  }
  
  static void dn_dev_delete(struct net_device *dev)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1138
  	struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
  
  	if (dn_db == NULL)
  		return;
  
  	del_timer_sync(&dn_db->timer);
  	dn_dev_sysctl_unregister(&dn_db->parms);
  	dn_dev_check_default(dev);
  	neigh_ifdown(&dn_neigh_table, dev);
  
  	if (dn_db->parms.down)
  		dn_db->parms.down(dev);
  
  	dev->dn_ptr = NULL;
  
  	neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms);
  	neigh_ifdown(&dn_neigh_table, dev);
  
  	if (dn_db->router)
  		neigh_release(dn_db->router);
  	if (dn_db->peer)
  		neigh_release(dn_db->peer);
  
  	kfree(dn_db);
  }
  
  void dn_dev_down(struct net_device *dev)
  {
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1166
  	struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1167
1168
1169
1170
  	struct dn_ifaddr *ifa;
  
  	if (dn_db == NULL)
  		return;
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1171
  	while ((ifa = rtnl_dereference(dn_db->ifa_list)) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1172
1173
1174
1175
1176
1177
1178
1179
1180
  		dn_dev_del_ifa(dn_db, &dn_db->ifa_list, 0);
  		dn_dev_free_ifa(ifa);
  	}
  
  	dn_dev_delete(dev);
  }
  
  void dn_dev_init_pkt(struct sk_buff *skb)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1181
1182
1183
1184
  }
  
  void dn_dev_veri_pkt(struct sk_buff *skb)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1185
1186
1187
1188
  }
  
  void dn_dev_hello(struct sk_buff *skb)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1189
1190
1191
1192
1193
1194
1195
  }
  
  void dn_dev_devices_off(void)
  {
  	struct net_device *dev;
  
  	rtnl_lock();
881d966b4   Eric W. Biederman   [NET]: Make the d...
1196
  	for_each_netdev(&init_net, dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
  		dn_dev_down(dev);
  	rtnl_unlock();
  
  }
  
  void dn_dev_devices_on(void)
  {
  	struct net_device *dev;
  
  	rtnl_lock();
881d966b4   Eric W. Biederman   [NET]: Make the d...
1207
  	for_each_netdev(&init_net, dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1208
1209
1210
1211
1212
1213
1214
1215
  		if (dev->flags & IFF_UP)
  			dn_dev_up(dev);
  	}
  	rtnl_unlock();
  }
  
  int register_dnaddr_notifier(struct notifier_block *nb)
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
1216
  	return blocking_notifier_chain_register(&dnaddr_chain, nb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1217
1218
1219
1220
  }
  
  int unregister_dnaddr_notifier(struct notifier_block *nb)
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
1221
  	return blocking_notifier_chain_unregister(&dnaddr_chain, nb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1222
1223
1224
  }
  
  #ifdef CONFIG_PROC_FS
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1225
  static inline int is_dn_dev(struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1226
  {
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1227
  	return dev->dn_ptr != NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1228
  }
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1229
  static void *dn_dev_seq_start(struct seq_file *seq, loff_t *pos)
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1230
  	__acquires(RCU)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1231
  {
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1232
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1233
  	struct net_device *dev;
fa918602b   stephen hemminger   decnet: use RCU t...
1234
  	rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1235

7562f876c   Pavel Emelianov   [NET]: Rework dev...
1236
1237
1238
1239
  	if (*pos == 0)
  		return SEQ_START_TOKEN;
  
  	i = 1;
fa918602b   stephen hemminger   decnet: use RCU t...
1240
  	for_each_netdev_rcu(&init_net, dev) {
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1241
1242
1243
1244
1245
  		if (!is_dn_dev(dev))
  			continue;
  
  		if (i++ == *pos)
  			return dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1246
  	}
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1247
1248
  
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1249
1250
1251
1252
  }
  
  static void *dn_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1253
  	struct net_device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1254

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1255
  	++*pos;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1256

ea1107338   Joe Perches   net: Remove casts...
1257
  	dev = v;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1258
  	if (v == SEQ_START_TOKEN)
881d966b4   Eric W. Biederman   [NET]: Make the d...
1259
  		dev = net_device_entry(&init_net.dev_base_head);
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1260

fa918602b   stephen hemminger   decnet: use RCU t...
1261
  	for_each_netdev_continue_rcu(&init_net, dev) {
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1262
1263
1264
1265
1266
1267
1268
  		if (!is_dn_dev(dev))
  			continue;
  
  		return dev;
  	}
  
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1269
1270
1271
  }
  
  static void dn_dev_seq_stop(struct seq_file *seq, void *v)
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1272
  	__releases(RCU)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1273
  {
fa918602b   stephen hemminger   decnet: use RCU t...
1274
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1275
1276
1277
1278
  }
  
  static char *dn_type2asc(char type)
  {
06f8fe11b   Joe Perches   decnet: Reduce sw...
1279
1280
1281
1282
1283
1284
1285
  	switch (type) {
  	case DN_DEV_BCAST:
  		return "B";
  	case DN_DEV_UCAST:
  		return "U";
  	case DN_DEV_MPOINT:
  		return "M";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1286
1287
1288
1289
1290
1291
1292
1293
  	}
  
  	return "?";
  }
  
  static int dn_dev_seq_show(struct seq_file *seq, void *v)
  {
  	if (v == SEQ_START_TOKEN)
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1294
1295
  		seq_puts(seq, "Name     Flags T1   Timer1 T3   Timer3 BlkSize Pri State DevType    Router Peer
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1296
1297
1298
1299
  	else {
  		struct net_device *dev = v;
  		char peer_buf[DN_ASCBUF_LEN];
  		char router_buf[DN_ASCBUF_LEN];
fc766e4c4   Eric Dumazet   decnet: RCU conve...
1300
  		struct dn_dev *dn_db = rcu_dereference(dev->dn_ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1301

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1302
  		seq_printf(seq, "%-8s %1s     %04u %04u   %04lu %04lu"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1303
1304
  				"   %04hu    %03d %02x    %-10s %-7s %-7s
  ",
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1305
1306
1307
  				dev->name ? dev->name : "???",
  				dn_type2asc(dn_db->parms.mode),
  				0, 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1308
1309
1310
1311
  				dn_db->t3, dn_db->parms.t3,
  				mtu2blksize(dev),
  				dn_db->parms.priority,
  				dn_db->parms.state, dn_db->parms.name,
c4106aa88   Harvey Harrison   decnet: remove pr...
1312
1313
  				dn_db->router ? dn_addr2asc(le16_to_cpu(*(__le16 *)dn_db->router->primary_key), router_buf) : "",
  				dn_db->peer ? dn_addr2asc(le16_to_cpu(*(__le16 *)dn_db->peer->primary_key), peer_buf) : "");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1314
1315
1316
  	}
  	return 0;
  }
56b3d975b   Philippe De Muyter   [NET]: Make all i...
1317
  static const struct seq_operations dn_dev_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
  	.start	= dn_dev_seq_start,
  	.next	= dn_dev_seq_next,
  	.stop	= dn_dev_seq_stop,
  	.show	= dn_dev_seq_show,
  };
  
  static int dn_dev_seq_open(struct inode *inode, struct file *file)
  {
  	return seq_open(file, &dn_dev_seq_ops);
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
1328
  static const struct file_operations dn_dev_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1329
1330
1331
1332
1333
1334
1335
1336
  	.owner	 = THIS_MODULE,
  	.open	 = dn_dev_seq_open,
  	.read	 = seq_read,
  	.llseek	 = seq_lseek,
  	.release = seq_release,
  };
  
  #endif /* CONFIG_PROC_FS */
4e058063f   Alexey Dobriyan   [DECNET]: "addr" ...
1337
  static int addr[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1338
1339
1340
1341
1342
  module_param_array(addr, int, NULL, 0444);
  MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
  
  void __init dn_dev_init(void)
  {
429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1343
1344
1345
1346
  	if (addr[0] > 63 || addr[0] < 0) {
  		printk(KERN_ERR "DECnet: Area must be between 0 and 63");
  		return;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1347

429eb0fae   YOSHIFUJI Hideaki   [NET] DECNET: Fix...
1348
1349
1350
1351
  	if (addr[1] > 1023 || addr[1] < 0) {
  		printk(KERN_ERR "DECnet: Node must be between 0 and 1023");
  		return;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1352

c4106aa88   Harvey Harrison   decnet: remove pr...
1353
  	decnet_address = cpu_to_le16((addr[0] << 10) | addr[1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1354
1355
  
  	dn_dev_devices_on();
c7ac8679b   Greg Rose   rtnetlink: Comput...
1356
1357
1358
  	rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL, NULL);
  	rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, NULL);
  	rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1359

d4beaa66a   Gao feng   net: proc: change...
1360
  	proc_create("decnet_dev", S_IRUGO, init_net.proc_net, &dn_dev_seq_fops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
  
  #ifdef CONFIG_SYSCTL
  	{
  		int i;
  		for(i = 0; i < DN_DEV_LIST_SIZE; i++)
  			dn_dev_sysctl_register(NULL, &dn_dev_list[i]);
  	}
  #endif /* CONFIG_SYSCTL */
  }
  
  void __exit dn_dev_cleanup(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1373
1374
1375
1376
1377
1378
1379
  #ifdef CONFIG_SYSCTL
  	{
  		int i;
  		for(i = 0; i < DN_DEV_LIST_SIZE; i++)
  			dn_dev_sysctl_unregister(&dn_dev_list[i]);
  	}
  #endif /* CONFIG_SYSCTL */
ece31ffd5   Gao feng   net: proc: change...
1380
  	remove_proc_entry("decnet_dev", init_net.proc_net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1381
1382
1383
  
  	dn_dev_devices_off();
  }