rtnetlink.c 32.4 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
/*
 * INET		An implementation of the TCP/IP protocol suite for the LINUX
 *		operating system.  INET is implemented using the  BSD Socket
 *		interface as the means of communication with the user level.
 *
 *		Routing netlink socket interface: protocol independent part.
 *
 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 *
 *		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 (at your option) any later version.
 *
 *	Fixes:
 *	Vitaly E. Lavrov		RTA_OK arithmetics was wrong.
 */

#include <linux/errno.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/capability.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/security.h>
36
#include <linux/mutex.h>
37
#include <linux/if_addr.h>
38
#include <linux/nsproxy.h>
Linus Torvalds's avatar
Linus Torvalds committed
39
40
41
42
43
44
45
46
47
48
49
50
51
52

#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/string.h>

#include <linux/inet.h>
#include <linux/netdevice.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/arp.h>
#include <net/route.h>
#include <net/udp.h>
#include <net/sock.h>
#include <net/pkt_sched.h>
53
#include <net/fib_rules.h>
54
#include <net/rtnetlink.h>
Linus Torvalds's avatar
Linus Torvalds committed
55

56
57
58
59
60
61
struct rtnl_link
{
	rtnl_doit_func		doit;
	rtnl_dumpit_func	dumpit;
};

62
static DEFINE_MUTEX(rtnl_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
63
64
65

void rtnl_lock(void)
{
66
	mutex_lock(&rtnl_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
67
68
}

69
void __rtnl_unlock(void)
Linus Torvalds's avatar
Linus Torvalds committed
70
{
71
	mutex_unlock(&rtnl_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
72
}
73

Linus Torvalds's avatar
Linus Torvalds committed
74
75
void rtnl_unlock(void)
{
76
	mutex_unlock(&rtnl_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
77
78
79
	netdev_run_todo();
}

80
81
82
83
84
int rtnl_trylock(void)
{
	return mutex_trylock(&rtnl_mutex);
}

Adrian Bunk's avatar
Adrian Bunk committed
85
static struct rtnl_link *rtnl_msg_handlers[NPROTO];
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

static inline int rtm_msgindex(int msgtype)
{
	int msgindex = msgtype - RTM_BASE;

	/*
	 * msgindex < 0 implies someone tried to register a netlink
	 * control code. msgindex >= RTM_NR_MSGTYPES may indicate that
	 * the message type has not been added to linux/rtnetlink.h
	 */
	BUG_ON(msgindex < 0 || msgindex >= RTM_NR_MSGTYPES);

	return msgindex;
}

static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex)
{
	struct rtnl_link *tab;

	tab = rtnl_msg_handlers[protocol];
106
	if (tab == NULL || tab[msgindex].doit == NULL)
107
108
		tab = rtnl_msg_handlers[PF_UNSPEC];

109
	return tab ? tab[msgindex].doit : NULL;
110
111
112
113
114
115
116
}

static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex)
{
	struct rtnl_link *tab;

	tab = rtnl_msg_handlers[protocol];
117
	if (tab == NULL || tab[msgindex].dumpit == NULL)
118
119
		tab = rtnl_msg_handlers[PF_UNSPEC];

120
	return tab ? tab[msgindex].dumpit : NULL;
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
}

/**
 * __rtnl_register - Register a rtnetlink message type
 * @protocol: Protocol family or PF_UNSPEC
 * @msgtype: rtnetlink message type
 * @doit: Function pointer called for each request message
 * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
 *
 * Registers the specified function pointers (at least one of them has
 * to be non-NULL) to be called whenever a request message for the
 * specified protocol family and message type is received.
 *
 * The special protocol family PF_UNSPEC may be used to define fallback
 * function pointers for the case when no entry for the specific protocol
 * family exists.
 *
 * Returns 0 on success or a negative error code.
 */
int __rtnl_register(int protocol, int msgtype,
		    rtnl_doit_func doit, rtnl_dumpit_func dumpit)
{
	struct rtnl_link *tab;
	int msgindex;

	BUG_ON(protocol < 0 || protocol >= NPROTO);
	msgindex = rtm_msgindex(msgtype);

	tab = rtnl_msg_handlers[protocol];
	if (tab == NULL) {
		tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL);
		if (tab == NULL)
			return -ENOBUFS;

		rtnl_msg_handlers[protocol] = tab;
	}

	if (doit)
		tab[msgindex].doit = doit;

	if (dumpit)
		tab[msgindex].dumpit = dumpit;

	return 0;
}

EXPORT_SYMBOL_GPL(__rtnl_register);

/**
 * rtnl_register - Register a rtnetlink message type
 *
 * Identical to __rtnl_register() but panics on failure. This is useful
 * as failure of this function is very unlikely, it can only happen due
 * to lack of memory when allocating the chain to store all message
 * handlers for a protocol. Meant for use in init functions where lack
 * of memory implies no sense in continueing.
 */
void rtnl_register(int protocol, int msgtype,
		   rtnl_doit_func doit, rtnl_dumpit_func dumpit)
{
	if (__rtnl_register(protocol, msgtype, doit, dumpit) < 0)
		panic("Unable to register rtnetlink message handler, "
		      "protocol = %d, message type = %d\n",
		      protocol, msgtype);
}

EXPORT_SYMBOL_GPL(rtnl_register);

/**
 * rtnl_unregister - Unregister a rtnetlink message type
 * @protocol: Protocol family or PF_UNSPEC
 * @msgtype: rtnetlink message type
 *
 * Returns 0 on success or a negative error code.
 */
int rtnl_unregister(int protocol, int msgtype)
{
	int msgindex;

	BUG_ON(protocol < 0 || protocol >= NPROTO);
	msgindex = rtm_msgindex(msgtype);

	if (rtnl_msg_handlers[protocol] == NULL)
		return -ENOENT;

	rtnl_msg_handlers[protocol][msgindex].doit = NULL;
	rtnl_msg_handlers[protocol][msgindex].dumpit = NULL;

	return 0;
}

EXPORT_SYMBOL_GPL(rtnl_unregister);

/**
 * rtnl_unregister_all - Unregister all rtnetlink message type of a protocol
 * @protocol : Protocol family or PF_UNSPEC
 *
 * Identical to calling rtnl_unregster() for all registered message types
 * of a certain protocol family.
 */
void rtnl_unregister_all(int protocol)
{
	BUG_ON(protocol < 0 || protocol >= NPROTO);

	kfree(rtnl_msg_handlers[protocol]);
	rtnl_msg_handlers[protocol] = NULL;
}

EXPORT_SYMBOL_GPL(rtnl_unregister_all);
Linus Torvalds's avatar
Linus Torvalds committed
230

231
232
233
234
235
236
237
238
239
240
241
242
243
244
static LIST_HEAD(link_ops);

/**
 * __rtnl_link_register - Register rtnl_link_ops with rtnetlink.
 * @ops: struct rtnl_link_ops * to register
 *
 * The caller must hold the rtnl_mutex. This function should be used
 * by drivers that create devices during module initialization. It
 * must be called before registering the devices.
 *
 * Returns 0 on success or a negative error code.
 */
int __rtnl_link_register(struct rtnl_link_ops *ops)
{
245
246
247
	if (!ops->dellink)
		ops->dellink = unregister_netdevice;

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
	list_add_tail(&ops->list, &link_ops);
	return 0;
}

EXPORT_SYMBOL_GPL(__rtnl_link_register);

/**
 * rtnl_link_register - Register rtnl_link_ops with rtnetlink.
 * @ops: struct rtnl_link_ops * to register
 *
 * Returns 0 on success or a negative error code.
 */
int rtnl_link_register(struct rtnl_link_ops *ops)
{
	int err;

	rtnl_lock();
	err = __rtnl_link_register(ops);
	rtnl_unlock();
	return err;
}

EXPORT_SYMBOL_GPL(rtnl_link_register);

/**
 * __rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink.
 * @ops: struct rtnl_link_ops * to unregister
 *
276
 * The caller must hold the rtnl_mutex.
277
278
279
 */
void __rtnl_link_unregister(struct rtnl_link_ops *ops)
{
280
	struct net_device *dev, *n;
281
	struct net *net;
282

283
	for_each_net(net) {
284
restart:
285
		for_each_netdev_safe(net, dev, n) {
286
			if (dev->rtnl_link_ops == ops) {
287
				ops->dellink(dev);
288
289
				goto restart;
			}
290
		}
291
	}
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
	list_del(&ops->list);
}

EXPORT_SYMBOL_GPL(__rtnl_link_unregister);

/**
 * rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink.
 * @ops: struct rtnl_link_ops * to unregister
 */
void rtnl_link_unregister(struct rtnl_link_ops *ops)
{
	rtnl_lock();
	__rtnl_link_unregister(ops);
	rtnl_unlock();
}

EXPORT_SYMBOL_GPL(rtnl_link_unregister);

static const struct rtnl_link_ops *rtnl_link_ops_get(const char *kind)
{
	const struct rtnl_link_ops *ops;

	list_for_each_entry(ops, &link_ops, list) {
		if (!strcmp(ops->kind, kind))
			return ops;
	}
	return NULL;
}

static size_t rtnl_link_get_size(const struct net_device *dev)
{
	const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
	size_t size;

	if (!ops)
		return 0;

	size = nlmsg_total_size(sizeof(struct nlattr)) + /* IFLA_LINKINFO */
	       nlmsg_total_size(strlen(ops->kind) + 1);	 /* IFLA_INFO_KIND */

	if (ops->get_size)
		/* IFLA_INFO_DATA + nested data */
		size += nlmsg_total_size(sizeof(struct nlattr)) +
			ops->get_size(dev);

	if (ops->get_xstats_size)
		size += ops->get_xstats_size(dev);	/* IFLA_INFO_XSTATS */

	return size;
}

static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev)
{
	const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
	struct nlattr *linkinfo, *data;
	int err = -EMSGSIZE;

	linkinfo = nla_nest_start(skb, IFLA_LINKINFO);
	if (linkinfo == NULL)
		goto out;

	if (nla_put_string(skb, IFLA_INFO_KIND, ops->kind) < 0)
		goto err_cancel_link;
	if (ops->fill_xstats) {
		err = ops->fill_xstats(skb, dev);
		if (err < 0)
			goto err_cancel_link;
	}
	if (ops->fill_info) {
		data = nla_nest_start(skb, IFLA_INFO_DATA);
		if (data == NULL)
			goto err_cancel_link;
		err = ops->fill_info(skb, dev);
		if (err < 0)
			goto err_cancel_data;
		nla_nest_end(skb, data);
	}

	nla_nest_end(skb, linkinfo);
	return 0;

err_cancel_data:
	nla_nest_cancel(skb, data);
err_cancel_link:
	nla_nest_cancel(skb, linkinfo);
out:
	return err;
}

381
static const int rtm_min[RTM_NR_FAMILIES] =
Linus Torvalds's avatar
Linus Torvalds committed
382
{
383
384
385
	[RTM_FAM(RTM_NEWLINK)]      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
	[RTM_FAM(RTM_NEWADDR)]      = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
	[RTM_FAM(RTM_NEWROUTE)]     = NLMSG_LENGTH(sizeof(struct rtmsg)),
386
	[RTM_FAM(RTM_NEWRULE)]      = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)),
387
388
389
390
391
392
	[RTM_FAM(RTM_NEWQDISC)]     = NLMSG_LENGTH(sizeof(struct tcmsg)),
	[RTM_FAM(RTM_NEWTCLASS)]    = NLMSG_LENGTH(sizeof(struct tcmsg)),
	[RTM_FAM(RTM_NEWTFILTER)]   = NLMSG_LENGTH(sizeof(struct tcmsg)),
	[RTM_FAM(RTM_NEWACTION)]    = NLMSG_LENGTH(sizeof(struct tcamsg)),
	[RTM_FAM(RTM_GETMULTICAST)] = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
	[RTM_FAM(RTM_GETANYCAST)]   = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
Linus Torvalds's avatar
Linus Torvalds committed
393
394
};

395
static const int rta_max[RTM_NR_FAMILIES] =
Linus Torvalds's avatar
Linus Torvalds committed
396
{
397
398
399
	[RTM_FAM(RTM_NEWLINK)]      = IFLA_MAX,
	[RTM_FAM(RTM_NEWADDR)]      = IFA_MAX,
	[RTM_FAM(RTM_NEWROUTE)]     = RTA_MAX,
400
	[RTM_FAM(RTM_NEWRULE)]      = FRA_MAX,
401
402
403
404
	[RTM_FAM(RTM_NEWQDISC)]     = TCA_MAX,
	[RTM_FAM(RTM_NEWTCLASS)]    = TCA_MAX,
	[RTM_FAM(RTM_NEWTFILTER)]   = TCA_MAX,
	[RTM_FAM(RTM_NEWACTION)]    = TCAA_MAX,
Linus Torvalds's avatar
Linus Torvalds committed
405
406
407
408
409
410
411
412
413
414
415
};

void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
{
	struct rtattr *rta;
	int size = RTA_LENGTH(attrlen);

	rta = (struct rtattr*)skb_put(skb, RTA_ALIGN(size));
	rta->rta_type = attrtype;
	rta->rta_len = size;
	memcpy(RTA_DATA(rta), data, attrlen);
416
	memset(RTA_DATA(rta) + attrlen, 0, RTA_ALIGN(size) - size);
Linus Torvalds's avatar
Linus Torvalds committed
417
418
}

419
int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group, int echo)
Linus Torvalds's avatar
Linus Torvalds committed
420
{
421
	struct sock *rtnl = net->rtnl;
Linus Torvalds's avatar
Linus Torvalds committed
422
423
	int err = 0;

424
	NETLINK_CB(skb).dst_group = group;
Linus Torvalds's avatar
Linus Torvalds committed
425
426
427
428
429
430
431
432
	if (echo)
		atomic_inc(&skb->users);
	netlink_broadcast(rtnl, skb, pid, group, GFP_KERNEL);
	if (echo)
		err = netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
	return err;
}

433
int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid)
434
{
435
436
	struct sock *rtnl = net->rtnl;

437
438
439
	return nlmsg_unicast(rtnl, skb, pid);
}

440
int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
441
442
		struct nlmsghdr *nlh, gfp_t flags)
{
443
	struct sock *rtnl = net->rtnl;
444
445
446
447
448
449
450
451
	int report = 0;

	if (nlh)
		report = nlmsg_report(nlh);

	return nlmsg_notify(rtnl, skb, pid, group, report, flags);
}

452
void rtnl_set_sk_err(struct net *net, u32 group, int error)
453
{
454
455
	struct sock *rtnl = net->rtnl;

456
457
458
	netlink_set_err(rtnl, 0, group, error);
}

Linus Torvalds's avatar
Linus Torvalds committed
459
460
int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics)
{
461
462
463
464
465
466
467
468
469
470
471
472
	struct nlattr *mx;
	int i, valid = 0;

	mx = nla_nest_start(skb, RTA_METRICS);
	if (mx == NULL)
		return -ENOBUFS;

	for (i = 0; i < RTAX_MAX; i++) {
		if (metrics[i]) {
			valid++;
			NLA_PUT_U32(skb, i+1, metrics[i]);
		}
Linus Torvalds's avatar
Linus Torvalds committed
473
474
	}

475
476
477
478
	if (!valid) {
		nla_nest_cancel(skb, mx);
		return 0;
	}
479
480
481
482
483

	return nla_nest_end(skb, mx);

nla_put_failure:
	return nla_nest_cancel(skb, mx);
Linus Torvalds's avatar
Linus Torvalds committed
484
485
}

486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
		       u32 ts, u32 tsage, long expires, u32 error)
{
	struct rta_cacheinfo ci = {
		.rta_lastuse = jiffies_to_clock_t(jiffies - dst->lastuse),
		.rta_used = dst->__use,
		.rta_clntref = atomic_read(&(dst->__refcnt)),
		.rta_error = error,
		.rta_id =  id,
		.rta_ts = ts,
		.rta_tsage = tsage,
	};

	if (expires)
		ci.rta_expires = jiffies_to_clock_t(expires);

	return nla_put(skb, RTA_CACHEINFO, sizeof(ci), &ci);
}

EXPORT_SYMBOL_GPL(rtnl_put_cacheinfo);
Linus Torvalds's avatar
Linus Torvalds committed
506

507
static void set_operstate(struct net_device *dev, unsigned char transition)
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
{
	unsigned char operstate = dev->operstate;

	switch(transition) {
	case IF_OPER_UP:
		if ((operstate == IF_OPER_DORMANT ||
		     operstate == IF_OPER_UNKNOWN) &&
		    !netif_dormant(dev))
			operstate = IF_OPER_UP;
		break;

	case IF_OPER_DORMANT:
		if (operstate == IF_OPER_UP ||
		    operstate == IF_OPER_UNKNOWN)
			operstate = IF_OPER_DORMANT;
		break;
524
	}
525
526
527
528
529

	if (dev->operstate != operstate) {
		write_lock_bh(&dev_base_lock);
		dev->operstate = operstate;
		write_unlock_bh(&dev_base_lock);
530
531
		netdev_state_change(dev);
	}
532
533
}

534
535
static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
				 struct net_device_stats *b)
Linus Torvalds's avatar
Linus Torvalds committed
536
{
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
	a->rx_packets = b->rx_packets;
	a->tx_packets = b->tx_packets;
	a->rx_bytes = b->rx_bytes;
	a->tx_bytes = b->tx_bytes;
	a->rx_errors = b->rx_errors;
	a->tx_errors = b->tx_errors;
	a->rx_dropped = b->rx_dropped;
	a->tx_dropped = b->tx_dropped;

	a->multicast = b->multicast;
	a->collisions = b->collisions;

	a->rx_length_errors = b->rx_length_errors;
	a->rx_over_errors = b->rx_over_errors;
	a->rx_crc_errors = b->rx_crc_errors;
	a->rx_frame_errors = b->rx_frame_errors;
	a->rx_fifo_errors = b->rx_fifo_errors;
	a->rx_missed_errors = b->rx_missed_errors;

	a->tx_aborted_errors = b->tx_aborted_errors;
	a->tx_carrier_errors = b->tx_carrier_errors;
	a->tx_fifo_errors = b->tx_fifo_errors;
	a->tx_heartbeat_errors = b->tx_heartbeat_errors;
	a->tx_window_errors = b->tx_window_errors;

	a->rx_compressed = b->rx_compressed;
	a->tx_compressed = b->tx_compressed;
};
Linus Torvalds's avatar
Linus Torvalds committed
565

566
static inline size_t if_nlmsg_size(const struct net_device *dev)
567
568
569
570
571
572
573
574
575
576
577
578
579
580
{
	return NLMSG_ALIGN(sizeof(struct ifinfomsg))
	       + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
	       + nla_total_size(IFNAMSIZ) /* IFLA_QDISC */
	       + nla_total_size(sizeof(struct rtnl_link_ifmap))
	       + nla_total_size(sizeof(struct rtnl_link_stats))
	       + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
	       + nla_total_size(MAX_ADDR_LEN) /* IFLA_BROADCAST */
	       + nla_total_size(4) /* IFLA_TXQLEN */
	       + nla_total_size(4) /* IFLA_WEIGHT */
	       + nla_total_size(4) /* IFLA_MTU */
	       + nla_total_size(4) /* IFLA_LINK */
	       + nla_total_size(4) /* IFLA_MASTER */
	       + nla_total_size(1) /* IFLA_OPERSTATE */
581
582
	       + nla_total_size(1) /* IFLA_LINKMODE */
	       + rtnl_link_get_size(dev); /* IFLA_LINKINFO */
583
584
}

585
static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
586
587
			    int type, u32 pid, u32 seq, u32 change,
			    unsigned int flags)
588
589
590
{
	struct ifinfomsg *ifm;
	struct nlmsghdr *nlh;
Linus Torvalds's avatar
Linus Torvalds committed
591

592
593
	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
	if (nlh == NULL)
594
		return -EMSGSIZE;
Linus Torvalds's avatar
Linus Torvalds committed
595

596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
	ifm = nlmsg_data(nlh);
	ifm->ifi_family = AF_UNSPEC;
	ifm->__ifi_pad = 0;
	ifm->ifi_type = dev->type;
	ifm->ifi_index = dev->ifindex;
	ifm->ifi_flags = dev_get_flags(dev);
	ifm->ifi_change = change;

	NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
	NLA_PUT_U32(skb, IFLA_TXQLEN, dev->tx_queue_len);
	NLA_PUT_U8(skb, IFLA_OPERSTATE,
		   netif_running(dev) ? dev->operstate : IF_OPER_DOWN);
	NLA_PUT_U8(skb, IFLA_LINKMODE, dev->link_mode);
	NLA_PUT_U32(skb, IFLA_MTU, dev->mtu);

	if (dev->ifindex != dev->iflink)
		NLA_PUT_U32(skb, IFLA_LINK, dev->iflink);

	if (dev->master)
		NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex);
Linus Torvalds's avatar
Linus Torvalds committed
616

617
618
	if (dev->qdisc_sleeping)
		NLA_PUT_STRING(skb, IFLA_QDISC, dev->qdisc_sleeping->ops->id);
619

Linus Torvalds's avatar
Linus Torvalds committed
620
621
622
623
624
625
626
627
628
	if (1) {
		struct rtnl_link_ifmap map = {
			.mem_start   = dev->mem_start,
			.mem_end     = dev->mem_end,
			.base_addr   = dev->base_addr,
			.irq         = dev->irq,
			.dma         = dev->dma,
			.port        = dev->if_port,
		};
629
		NLA_PUT(skb, IFLA_MAP, sizeof(map), &map);
Linus Torvalds's avatar
Linus Torvalds committed
630
631
632
	}

	if (dev->addr_len) {
633
634
		NLA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
		NLA_PUT(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast);
Linus Torvalds's avatar
Linus Torvalds committed
635
636
637
	}

	if (dev->get_stats) {
638
		struct net_device_stats *stats = dev->get_stats(dev);
Linus Torvalds's avatar
Linus Torvalds committed
639
		if (stats) {
640
641
642
643
644
645
646
647
			struct nlattr *attr;

			attr = nla_reserve(skb, IFLA_STATS,
					   sizeof(struct rtnl_link_stats));
			if (attr == NULL)
				goto nla_put_failure;

			copy_rtnl_link_stats(nla_data(attr), stats);
Linus Torvalds's avatar
Linus Torvalds committed
648
649
650
		}
	}

651
652
653
654
655
	if (dev->rtnl_link_ops) {
		if (rtnl_link_fill(skb, dev) < 0)
			goto nla_put_failure;
	}

656
657
658
	return nlmsg_end(skb, nlh);

nla_put_failure:
659
660
	nlmsg_cancel(skb, nlh);
	return -EMSGSIZE;
Linus Torvalds's avatar
Linus Torvalds committed
661
662
}

663
static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
Linus Torvalds's avatar
Linus Torvalds committed
664
{
665
	struct net *net = skb->sk->sk_net;
Linus Torvalds's avatar
Linus Torvalds committed
666
667
668
669
	int idx;
	int s_idx = cb->args[0];
	struct net_device *dev;

670
	idx = 0;
671
	for_each_netdev(net, dev) {
Linus Torvalds's avatar
Linus Torvalds committed
672
		if (idx < s_idx)
673
			goto cont;
674
		if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
675
676
				     NETLINK_CB(cb->skb).pid,
				     cb->nlh->nlmsg_seq, 0, NLM_F_MULTI) <= 0)
Linus Torvalds's avatar
Linus Torvalds committed
677
			break;
678
679
cont:
		idx++;
Linus Torvalds's avatar
Linus Torvalds committed
680
681
682
683
684
685
	}
	cb->args[0] = idx;

	return skb->len;
}

686
const struct nla_policy ifla_policy[IFLA_MAX+1] = {
687
	[IFLA_IFNAME]		= { .type = NLA_STRING, .len = IFNAMSIZ-1 },
688
689
	[IFLA_ADDRESS]		= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
	[IFLA_BROADCAST]	= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
690
	[IFLA_MAP]		= { .len = sizeof(struct rtnl_link_ifmap) },
691
	[IFLA_MTU]		= { .type = NLA_U32 },
692
	[IFLA_LINK]		= { .type = NLA_U32 },
693
694
695
696
	[IFLA_TXQLEN]		= { .type = NLA_U32 },
	[IFLA_WEIGHT]		= { .type = NLA_U32 },
	[IFLA_OPERSTATE]	= { .type = NLA_U8 },
	[IFLA_LINKMODE]		= { .type = NLA_U8 },
697
	[IFLA_LINKINFO]		= { .type = NLA_NESTED },
698
	[IFLA_NET_NS_PID]	= { .type = NLA_U32 },
699
700
};

701
702
703
704
705
static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
	[IFLA_INFO_KIND]	= { .type = NLA_STRING },
	[IFLA_INFO_DATA]	= { .type = NLA_NESTED },
};

706
707
708
709
710
711
712
713
static struct net *get_net_ns_by_pid(pid_t pid)
{
	struct task_struct *tsk;
	struct net *net;

	/* Lookup the network namespace */
	net = ERR_PTR(-ESRCH);
	rcu_read_lock();
714
	tsk = find_task_by_vpid(pid);
715
	if (tsk) {
716
717
718
719
		struct nsproxy *nsproxy;
		nsproxy = task_nsproxy(tsk);
		if (nsproxy)
			net = get_net(nsproxy->net_ns);
720
721
722
723
724
	}
	rcu_read_unlock();
	return net;
}

725
static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
726
		      struct nlattr **tb, char *ifname, int modified)
Linus Torvalds's avatar
Linus Torvalds committed
727
{
728
	int send_addr_notify = 0;
729
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
730

731
732
733
734
735
736
737
738
739
740
741
742
743
744
	if (tb[IFLA_NET_NS_PID]) {
		struct net *net;
		net = get_net_ns_by_pid(nla_get_u32(tb[IFLA_NET_NS_PID]));
		if (IS_ERR(net)) {
			err = PTR_ERR(net);
			goto errout;
		}
		err = dev_change_net_namespace(dev, net, ifname);
		put_net(net);
		if (err)
			goto errout;
		modified = 1;
	}

745
	if (tb[IFLA_MAP]) {
Linus Torvalds's avatar
Linus Torvalds committed
746
747
748
749
750
		struct rtnl_link_ifmap *u_map;
		struct ifmap k_map;

		if (!dev->set_config) {
			err = -EOPNOTSUPP;
751
			goto errout;
Linus Torvalds's avatar
Linus Torvalds committed
752
753
754
755
		}

		if (!netif_device_present(dev)) {
			err = -ENODEV;
756
			goto errout;
Linus Torvalds's avatar
Linus Torvalds committed
757
758
		}

759
		u_map = nla_data(tb[IFLA_MAP]);
Linus Torvalds's avatar
Linus Torvalds committed
760
761
762
763
764
765
766
767
		k_map.mem_start = (unsigned long) u_map->mem_start;
		k_map.mem_end = (unsigned long) u_map->mem_end;
		k_map.base_addr = (unsigned short) u_map->base_addr;
		k_map.irq = (unsigned char) u_map->irq;
		k_map.dma = (unsigned char) u_map->dma;
		k_map.port = (unsigned char) u_map->port;

		err = dev->set_config(dev, &k_map);
768
		if (err < 0)
769
			goto errout;
Linus Torvalds's avatar
Linus Torvalds committed
770

771
		modified = 1;
Linus Torvalds's avatar
Linus Torvalds committed
772
773
	}

774
	if (tb[IFLA_ADDRESS]) {
775
776
777
		struct sockaddr *sa;
		int len;

Linus Torvalds's avatar
Linus Torvalds committed
778
779
		if (!dev->set_mac_address) {
			err = -EOPNOTSUPP;
780
			goto errout;
Linus Torvalds's avatar
Linus Torvalds committed
781
		}
782

Linus Torvalds's avatar
Linus Torvalds committed
783
784
		if (!netif_device_present(dev)) {
			err = -ENODEV;
785
			goto errout;
Linus Torvalds's avatar
Linus Torvalds committed
786
787
		}

788
789
790
791
		len = sizeof(sa_family_t) + dev->addr_len;
		sa = kmalloc(len, GFP_KERNEL);
		if (!sa) {
			err = -ENOMEM;
792
			goto errout;
793
794
		}
		sa->sa_family = dev->type;
795
		memcpy(sa->sa_data, nla_data(tb[IFLA_ADDRESS]),
796
797
798
		       dev->addr_len);
		err = dev->set_mac_address(dev, sa);
		kfree(sa);
Linus Torvalds's avatar
Linus Torvalds committed
799
		if (err)
800
			goto errout;
Linus Torvalds's avatar
Linus Torvalds committed
801
		send_addr_notify = 1;
802
		modified = 1;
Linus Torvalds's avatar
Linus Torvalds committed
803
804
	}

805
806
807
	if (tb[IFLA_MTU]) {
		err = dev_set_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
		if (err < 0)
808
			goto errout;
809
		modified = 1;
Linus Torvalds's avatar
Linus Torvalds committed
810
811
	}

812
813
814
815
816
	/*
	 * Interface selected by interface index but interface
	 * name provided implies that a name change has been
	 * requested.
	 */
817
	if (ifm->ifi_index > 0 && ifname[0]) {
818
819
		err = dev_change_name(dev, ifname);
		if (err < 0)
820
			goto errout;
821
		modified = 1;
Linus Torvalds's avatar
Linus Torvalds committed
822
823
	}

824
825
826
	if (tb[IFLA_BROADCAST]) {
		nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len);
		send_addr_notify = 1;
Linus Torvalds's avatar
Linus Torvalds committed
827
828
	}

829
830
831
832
833
834
835
836
837
	if (ifm->ifi_flags || ifm->ifi_change) {
		unsigned int flags = ifm->ifi_flags;

		/* bugwards compatibility: ifi_change == 0 is treated as ~0 */
		if (ifm->ifi_change)
			flags = (flags & ifm->ifi_change) |
				(dev->flags & ~ifm->ifi_change);
		dev_change_flags(dev, flags);
	}
Linus Torvalds's avatar
Linus Torvalds committed
838

839
840
	if (tb[IFLA_TXQLEN])
		dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
841

842
	if (tb[IFLA_OPERSTATE])
843
		set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
844

845
	if (tb[IFLA_LINKMODE]) {
846
847
848
		write_lock_bh(&dev_base_lock);
		dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
		write_unlock_bh(&dev_base_lock);
849
850
	}

Linus Torvalds's avatar
Linus Torvalds committed
851
852
	err = 0;

853
errout:
854
855
856
857
858
859
	if (err < 0 && modified && net_ratelimit())
		printk(KERN_WARNING "A link change request failed with "
		       "some changes comitted already. Interface %s may "
		       "have been left with an inconsistent configuration, "
		       "please check.\n", dev->name);

Linus Torvalds's avatar
Linus Torvalds committed
860
861
	if (send_addr_notify)
		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
862
863
	return err;
}
Linus Torvalds's avatar
Linus Torvalds committed
864

865
866
static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
867
	struct net *net = skb->sk->sk_net;
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
	struct ifinfomsg *ifm;
	struct net_device *dev;
	int err;
	struct nlattr *tb[IFLA_MAX+1];
	char ifname[IFNAMSIZ];

	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);
	if (err < 0)
		goto errout;

	if (tb[IFLA_IFNAME])
		nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
	else
		ifname[0] = '\0';

	err = -EINVAL;
	ifm = nlmsg_data(nlh);
	if (ifm->ifi_index > 0)
886
		dev = dev_get_by_index(net, ifm->ifi_index);
887
	else if (tb[IFLA_IFNAME])
888
		dev = dev_get_by_name(net, ifname);
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
	else
		goto errout;

	if (dev == NULL) {
		err = -ENODEV;
		goto errout;
	}

	if (tb[IFLA_ADDRESS] &&
	    nla_len(tb[IFLA_ADDRESS]) < dev->addr_len)
		goto errout_dev;

	if (tb[IFLA_BROADCAST] &&
	    nla_len(tb[IFLA_BROADCAST]) < dev->addr_len)
		goto errout_dev;

905
	err = do_setlink(dev, ifm, tb, ifname, 0);
906
errout_dev:
Linus Torvalds's avatar
Linus Torvalds committed
907
	dev_put(dev);
908
errout:
Linus Torvalds's avatar
Linus Torvalds committed
909
910
911
	return err;
}

912
913
static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
914
	struct net *net = skb->sk->sk_net;
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
	const struct rtnl_link_ops *ops;
	struct net_device *dev;
	struct ifinfomsg *ifm;
	char ifname[IFNAMSIZ];
	struct nlattr *tb[IFLA_MAX+1];
	int err;

	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);
	if (err < 0)
		return err;

	if (tb[IFLA_IFNAME])
		nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);

	ifm = nlmsg_data(nlh);
	if (ifm->ifi_index > 0)
931
		dev = __dev_get_by_index(net, ifm->ifi_index);
932
	else if (tb[IFLA_IFNAME])
933
		dev = __dev_get_by_name(net, ifname);
934
935
936
937
938
939
940
941
942
943
944
945
946
947
	else
		return -EINVAL;

	if (!dev)
		return -ENODEV;

	ops = dev->rtnl_link_ops;
	if (!ops)
		return -EOPNOTSUPP;

	ops->dellink(dev);
	return 0;
}

948
struct net_device *rtnl_create_link(struct net *net, char *ifname,
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
		const struct rtnl_link_ops *ops, struct nlattr *tb[])
{
	int err;
	struct net_device *dev;

	err = -ENOMEM;
	dev = alloc_netdev(ops->priv_size, ifname, ops->setup);
	if (!dev)
		goto err;

	if (strchr(dev->name, '%')) {
		err = dev_alloc_name(dev, dev->name);
		if (err < 0)
			goto err_free;
	}

965
	dev->nd_net = net;
966
967
968
969
970
971
972
973
974
975
976
977
978
	dev->rtnl_link_ops = ops;

	if (tb[IFLA_MTU])
		dev->mtu = nla_get_u32(tb[IFLA_MTU]);
	if (tb[IFLA_ADDRESS])
		memcpy(dev->dev_addr, nla_data(tb[IFLA_ADDRESS]),
				nla_len(tb[IFLA_ADDRESS]));
	if (tb[IFLA_BROADCAST])
		memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]),
				nla_len(tb[IFLA_BROADCAST]));
	if (tb[IFLA_TXQLEN])
		dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
	if (tb[IFLA_OPERSTATE])
979
		set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
980
981
982
983
984
985
986
987
988
989
990
	if (tb[IFLA_LINKMODE])
		dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);

	return dev;

err_free:
	free_netdev(dev);
err:
	return ERR_PTR(err);
}

991
992
static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
993
	struct net *net = skb->sk->sk_net;
994
995
996
997
998
999
1000
1001
1002
	const struct rtnl_link_ops *ops;
	struct net_device *dev;
	struct ifinfomsg *ifm;
	char kind[MODULE_NAME_LEN];
	char ifname[IFNAMSIZ];
	struct nlattr *tb[IFLA_MAX+1];
	struct nlattr *linkinfo[IFLA_INFO_MAX+1];
	int err;

1003
#ifdef CONFIG_KMOD
1004
replay:
1005
#endif
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);
	if (err < 0)
		return err;

	if (tb[IFLA_IFNAME])
		nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
	else
		ifname[0] = '\0';

	ifm = nlmsg_data(nlh);
	if (ifm->ifi_index > 0)
1017
		dev = __dev_get_by_index(net, ifm->ifi_index);
1018
	else if (ifname[0])
1019
		dev = __dev_get_by_name(net, ifname);
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
	else
		dev = NULL;

	if (tb[IFLA_LINKINFO]) {
		err = nla_parse_nested(linkinfo, IFLA_INFO_MAX,
				       tb[IFLA_LINKINFO], ifla_info_policy);
		if (err < 0)
			return err;
	} else
		memset(linkinfo, 0, sizeof(linkinfo));

	if (linkinfo[IFLA_INFO_KIND]) {
		nla_strlcpy(kind, linkinfo[IFLA_INFO_KIND], sizeof(kind));
		ops = rtnl_link_ops_get(kind);
	} else {
		kind[0] = '\0';
		ops = NULL;
	}

	if (1) {
		struct nlattr *attr[ops ? ops->maxtype + 1 : 0], **data = NULL;

		if (ops) {
			if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
				err = nla_parse_nested(attr, ops->maxtype,
						       linkinfo[IFLA_INFO_DATA],
						       ops->policy);
				if (err < 0)
					return err;
				data = attr;
			}
			if (ops->validate) {
				err = ops->validate(tb, data);
				if (err < 0)
					return err;
			}
		}

		if (dev) {
			int modified = 0;

			if (nlh->nlmsg_flags & NLM_F_EXCL)
				return -EEXIST;
			if (nlh->nlmsg_flags & NLM_F_REPLACE)
				return -EOPNOTSUPP;

			if (linkinfo[IFLA_INFO_DATA]) {
				if (!ops || ops != dev->rtnl_link_ops ||
				    !ops->changelink)
					return -EOPNOTSUPP;

				err = ops->changelink(dev, tb, data);
				if (err < 0)
					return err;
				modified = 1;
			}

			return do_setlink(dev, ifm, tb, ifname, modified);
		}

		if (!(nlh->nlmsg_flags & NLM_F_CREATE))
			return -ENODEV;

		if (ifm->ifi_index || ifm->ifi_flags || ifm->ifi_change)
			return -EOPNOTSUPP;
1085
		if (tb[IFLA_MAP] || tb[IFLA_MASTER] || tb[IFLA_PROTINFO])
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
			return -EOPNOTSUPP;

		if (!ops) {
#ifdef CONFIG_KMOD
			if (kind[0]) {
				__rtnl_unlock();
				request_module("rtnl-link-%s", kind);
				rtnl_lock();
				ops = rtnl_link_ops_get(kind);
				if (ops)
					goto replay;
			}
#endif
			return -EOPNOTSUPP;
		}

		if (!ifname[0])
			snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
1104

1105
		dev = rtnl_create_link(net, ifname, ops, tb);
1106
1107
1108
1109

		if (IS_ERR(dev))
			err = PTR_ERR(dev);
		else if (ops->newlink)
1110
1111
1112
			err = ops->newlink(dev, tb, data);
		else
			err = register_netdevice(dev);
1113
1114

		if (err < 0 && !IS_ERR(dev))
1115
1116
1117
1118
1119
			free_netdev(dev);
		return err;
	}
}

1120
static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
1121
{
1122
	struct net *net = skb->sk->sk_net;
1123
1124
1125
1126
	struct ifinfomsg *ifm;
	struct nlattr *tb[IFLA_MAX+1];
	struct net_device *dev = NULL;
	struct sk_buff *nskb;
1127
	int err;
1128

1129
1130
	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);
	if (err < 0)
1131
		return err;
1132
1133

	ifm = nlmsg_data(nlh);
1134
	if (ifm->ifi_index > 0) {
1135
		dev = dev_get_by_index(net, ifm->ifi_index);
1136
1137
1138
		if (dev == NULL)
			return -ENODEV;
	} else
1139
1140
		return -EINVAL;

1141
	nskb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL);
1142
1143
1144
1145
1146
	if (nskb == NULL) {
		err = -ENOBUFS;
		goto errout;
	}

1147
1148
	err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).pid,
			       nlh->nlmsg_seq, 0, 0);
1149
1150
1151
1152
1153
1154
	if (err < 0) {
		/* -EMSGSIZE implies BUG in if_nlmsg_size */
		WARN_ON(err == -EMSGSIZE);
		kfree_skb(nskb);
		goto errout;
	}
1155
	err = rtnl_unicast(nskb, net, NETLINK_CB(skb).pid);
1156
errout:
1157
1158
	dev_put(dev);

1159
	return err;
1160
1161
}

Adrian Bunk's avatar
Adrian Bunk committed
1162
static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
Linus Torvalds's avatar
Linus Torvalds committed
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
{
	int idx;
	int s_idx = cb->family;

	if (s_idx == 0)
		s_idx = 1;
	for (idx=1; idx<NPROTO; idx++) {
		int type = cb->nlh->nlmsg_type-RTM_BASE;
		if (idx < s_idx || idx == PF_PACKET)
			continue;
1173
1174
		if (rtnl_msg_handlers[idx] == NULL ||
		    rtnl_msg_handlers[idx][type].dumpit == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
1175
1176
1177
			continue;
		if (idx > s_idx)
			memset(&cb->args[0], 0, sizeof(cb->args));
1178
		if (rtnl_msg_handlers[idx][type].dumpit(skb, cb))
Linus Torvalds's avatar
Linus Torvalds committed
1179
1180
1181
1182
1183
1184
1185
1186
1187
			break;
	}
	cb->family = idx;

	return skb->len;
}

void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
{
1188
	struct net *net = dev->nd_net;
Linus Torvalds's avatar
Linus Torvalds committed
1189
	struct sk_buff *skb;
1190
	int err = -ENOBUFS;
Linus Torvalds's avatar
Linus Torvalds committed
1191

1192
	skb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL);
1193
1194
	if (skb == NULL)
		goto errout;
Linus Torvalds's avatar
Linus Torvalds committed
1195

1196
	err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0);
1197
1198
1199
1200
1201
1202
	if (err < 0) {
		/* -EMSGSIZE implies BUG in if_nlmsg_size() */
		WARN_ON(err == -EMSGSIZE);
		kfree_skb(skb);
		goto errout;
	}
1203
	err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
1204
1205
errout:
	if (err < 0)
1206
		rtnl_set_sk_err(net, RTNLGRP_LINK, err);
Linus Torvalds's avatar
Linus Torvalds committed
1207
1208
1209
1210
1211
1212
1213
1214
}

/* Protected by RTNL sempahore.  */
static struct rtattr **rta_buf;
static int rtattr_max;

/* Process one rtnetlink message. */

1215
static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
Linus Torvalds's avatar
Linus Torvalds committed
1216
{
1217
	struct net *net = skb->sk->sk_net;
1218
	rtnl_doit_func doit;
Linus Torvalds's avatar
Linus Torvalds committed
1219
1220
1221
1222
	int sz_idx, kind;
	int min_len;
	int family;
	int type;
1223
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
1224
1225
1226

	type = nlh->nlmsg_type;
	if (type > RTM_MAX)
1227
		return -EOPNOTSUPP;
Linus Torvalds's avatar
Linus Torvalds committed
1228
1229
1230
1231
1232
1233
1234
1235

	type -= RTM_BASE;

	/* All the messages must have at least 1 byte length */
	if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtgenmsg)))
		return 0;

	family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family;
1236
1237
	if (family >= NPROTO)
		return -EAFNOSUPPORT;
Linus Torvalds's avatar
Linus Torvalds committed
1238
1239
1240
1241

	sz_idx = type>>2;
	kind = type&3;

1242
1243
	if (kind != 2 && security_netlink_recv(skb, CAP_NET_ADMIN))
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
1244
1245

	if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
1246
		struct sock *rtnl;
1247
		rtnl_dumpit_func dumpit;
Linus Torvalds's avatar
Linus Torvalds committed
1248

1249
1250
		dumpit = rtnl_get_dumpit(family, type);
		if (dumpit == NULL)
1251
			return -EOPNOTSUPP;
1252

1253
		__rtnl_unlock();
1254
		rtnl = net->rtnl;
1255
1256
1257
		err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);
		rtnl_lock();
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
1258
1259
1260
1261
1262
1263
	}

	memset(rta_buf, 0, (rtattr_max * sizeof(struct rtattr *)));

	min_len = rtm_min[sz_idx];
	if (nlh->nlmsg_len < min_len)
1264
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1265
1266
1267
1268
1269
1270
1271
1272
1273

	if (nlh->nlmsg_len > min_len) {
		int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
		struct rtattr *attr = (void*)nlh + NLMSG_ALIGN(min_len);

		while (RTA_OK(attr, attrlen)) {
			unsigned flavor = attr->rta_type;
			if (flavor) {
				if (flavor > rta_max[sz_idx])
1274
					return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
1275
1276
1277
1278
1279
1280
				rta_buf[flavor-1] = attr;
			}
			attr = RTA_NEXT(attr, attrlen);
		}
	}

1281
1282
	doit = rtnl_get_doit(family, type);
	if (doit == NULL)
1283
		return -EOPNOTSUPP;
Linus Torvalds's avatar
Linus Torvalds committed
1284

1285
	return doit(skb, nlh, (void *)&rta_buf[0]);
Linus Torvalds's avatar
Linus Torvalds committed
1286
1287
}

1288
static void rtnetlink_rcv(struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
1289
{
1290
1291
1292
	rtnl_lock();
	netlink_rcv_skb(skb, &rtnetlink_rcv_msg);
	rtnl_unlock();
Linus Torvalds's avatar
Linus Torvalds committed
1293
1294
1295
1296
1297
}

static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
{
	struct net_device *dev = ptr;
1298

Linus Torvalds's avatar
Linus Torvalds committed
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
	switch (event) {
	case NETDEV_UNREGISTER:
		rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
		break;
	case NETDEV_REGISTER:
		rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
		break;
	case NETDEV_UP:
	case NETDEV_DOWN:
		rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
		break;
	case NETDEV_CHANGE:
	case NETDEV_GOING_DOWN:
		break;
	default:
		rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
		break;
	}
	return NOTIFY_DONE;
}

static struct notifier_block rtnetlink_dev_notifier = {
	.notifier_call	= rtnetlink_event,
};

1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337

static int rtnetlink_net_init(struct net *net)
{
	struct sock *sk;
	sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX,
				   rtnetlink_rcv, &rtnl_mutex, THIS_MODULE);
	if (!sk)
		return -ENOMEM;
	net->rtnl = sk;
	return 0;
}

static void rtnetlink_net_exit(struct net *net)
{
1338
1339
	netlink_kernel_release(net->rtnl);
	net->rtnl = NULL;
1340
1341
1342
1343
1344
1345
1346
}

static struct pernet_operations rtnetlink_net_ops = {
	.init = rtnetlink_net_init,
	.exit = rtnetlink_net_exit,
};

Linus Torvalds's avatar
Linus Torvalds committed
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
void __init rtnetlink_init(void)
{
	int i;

	rtattr_max = 0;
	for (i = 0; i < ARRAY_SIZE(rta_max); i++)
		if (rta_max[i] > rtattr_max)
			rtattr_max = rta_max[i];
	rta_buf = kmalloc(rtattr_max * sizeof(struct rtattr *), GFP_KERNEL);
	if (!rta_buf)
		panic("rtnetlink_init: cannot allocate rta_buf\n");

1359
	if (register_pernet_subsys(&rtnetlink_net_ops))
Linus Torvalds's avatar
Linus Torvalds committed
1360
		panic("rtnetlink_init: cannot initialize rtnetlink\n");
1361

Linus Torvalds's avatar
Linus Torvalds committed
1362
1363
	netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV);
	register_netdevice_notifier(&rtnetlink_dev_notifier);
1364
1365
1366

	rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo);
	rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL);
1367
1368
	rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL);
	rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL);
1369
1370
1371

	rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all);
	rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all);
Linus Torvalds's avatar
Linus Torvalds committed
1372
1373
1374
1375
1376
}

EXPORT_SYMBOL(__rta_fill);
EXPORT_SYMBOL(rtnetlink_put_metrics);
EXPORT_SYMBOL(rtnl_lock);
1377
EXPORT_SYMBOL(rtnl_trylock);
Linus Torvalds's avatar
Linus Torvalds committed
1378
EXPORT_SYMBOL(rtnl_unlock);
1379
EXPORT_SYMBOL(rtnl_unicast);
1380
1381
EXPORT_SYMBOL(rtnl_notify);
EXPORT_SYMBOL(rtnl_set_sk_err);
1382
1383
EXPORT_SYMBOL(rtnl_create_link);
EXPORT_SYMBOL(ifla_policy);