Blame view

lib/nlattr.c 12.2 KB
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
1
2
3
4
5
6
  /*
   * NETLINK      Netlink attributes
   *
   * 		Authors:	Thomas Graf <tgraf@suug.ch>
   * 				Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
   */
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/jiffies.h>
  #include <linux/netdevice.h>
  #include <linux/skbuff.h>
  #include <linux/string.h>
  #include <linux/types.h>
  #include <net/netlink.h>
  
  static u16 nla_attr_minlen[NLA_TYPE_MAX+1] __read_mostly = {
  	[NLA_U8]	= sizeof(u8),
  	[NLA_U16]	= sizeof(u16),
  	[NLA_U32]	= sizeof(u32),
  	[NLA_U64]	= sizeof(u64),
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
22
23
24
25
  	[NLA_NESTED]	= NLA_HDRLEN,
  };
  
  static int validate_nla(struct nlattr *nla, int maxtype,
ef7c79ed6   Patrick McHardy   [NETLINK]: Mark n...
26
  			const struct nla_policy *policy)
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
27
  {
ef7c79ed6   Patrick McHardy   [NETLINK]: Mark n...
28
  	const struct nla_policy *pt;
8f4c1f9b0   Thomas Graf   [NETLINK]: Introd...
29
  	int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
30

8f4c1f9b0   Thomas Graf   [NETLINK]: Introd...
31
  	if (type <= 0 || type > maxtype)
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
32
  		return 0;
8f4c1f9b0   Thomas Graf   [NETLINK]: Introd...
33
  	pt = &policy[type];
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
34
35
  
  	BUG_ON(pt->type > NLA_TYPE_MAX);
a5531a5d8   Thomas Graf   [NETLINK]: Improv...
36
37
38
39
40
  	switch (pt->type) {
  	case NLA_FLAG:
  		if (attrlen > 0)
  			return -ERANGE;
  		break;
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
41

a5531a5d8   Thomas Graf   [NETLINK]: Improv...
42
43
44
45
46
  	case NLA_NUL_STRING:
  		if (pt->len)
  			minlen = min_t(int, attrlen, pt->len + 1);
  		else
  			minlen = attrlen;
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
47

a5531a5d8   Thomas Graf   [NETLINK]: Improv...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  		if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL)
  			return -EINVAL;
  		/* fall through */
  
  	case NLA_STRING:
  		if (attrlen < 1)
  			return -ERANGE;
  
  		if (pt->len) {
  			char *buf = nla_data(nla);
  
  			if (buf[attrlen - 1] == '\0')
  				attrlen--;
  
  			if (attrlen > pt->len)
  				return -ERANGE;
  		}
  		break;
d30045a0b   Johannes Berg   [NETLINK]: introd...
66
67
68
69
  	case NLA_BINARY:
  		if (pt->len && attrlen > pt->len)
  			return -ERANGE;
  		break;
1092cb219   Patrick McHardy   [NETLINK]: attr: ...
70
71
72
73
74
75
76
77
78
79
80
  	case NLA_NESTED_COMPAT:
  		if (attrlen < pt->len)
  			return -ERANGE;
  		if (attrlen < NLA_ALIGN(pt->len))
  			break;
  		if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN)
  			return -ERANGE;
  		nla = nla_data(nla) + NLA_ALIGN(pt->len);
  		if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
  			return -ERANGE;
  		break;
ea5693ccc   Patrick McHardy   netlink: allow em...
81
82
83
84
85
86
  	case NLA_NESTED:
  		/* a nested attributes is allowed to be empty; if its not,
  		 * it must have a size of at least NLA_HDRLEN.
  		 */
  		if (attrlen == 0)
  			break;
a5531a5d8   Thomas Graf   [NETLINK]: Improv...
87
88
89
90
91
92
93
94
95
  	default:
  		if (pt->len)
  			minlen = pt->len;
  		else if (pt->type != NLA_UNSPEC)
  			minlen = nla_attr_minlen[pt->type];
  
  		if (attrlen < minlen)
  			return -ERANGE;
  	}
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  
  	return 0;
  }
  
  /**
   * nla_validate - Validate a stream of attributes
   * @head: head of attribute stream
   * @len: length of attribute stream
   * @maxtype: maximum attribute type to be expected
   * @policy: validation policy
   *
   * Validates all attributes in the specified attribute stream against the
   * specified policy. Attributes with a type exceeding maxtype will be
   * ignored. See documenation of struct nla_policy for more details.
   *
   * Returns 0 on success or a negative error code.
   */
  int nla_validate(struct nlattr *head, int len, int maxtype,
ef7c79ed6   Patrick McHardy   [NETLINK]: Mark n...
114
  		 const struct nla_policy *policy)
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  {
  	struct nlattr *nla;
  	int rem, err;
  
  	nla_for_each_attr(nla, head, len, rem) {
  		err = validate_nla(nla, maxtype, policy);
  		if (err < 0)
  			goto errout;
  	}
  
  	err = 0;
  errout:
  	return err;
  }
  
  /**
e487eb99c   Holger Eitzenberger   netlink: add nla_...
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
   * nla_policy_len - Determin the max. length of a policy
   * @policy: policy to use
   * @n: number of policies
   *
   * Determines the max. length of the policy.  It is currently used
   * to allocated Netlink buffers roughly the size of the actual
   * message.
   *
   * Returns 0 on success or a negative error code.
   */
  int
  nla_policy_len(const struct nla_policy *p, int n)
  {
  	int i, len = 0;
  
  	for (i = 0; i < n; i++) {
  		if (p->len)
  			len += nla_total_size(p->len);
  		else if (nla_attr_minlen[p->type])
  			len += nla_total_size(nla_attr_minlen[p->type]);
  	}
  
  	return len;
  }
  
  /**
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
157
158
159
160
161
   * nla_parse - Parse a stream of attributes into a tb buffer
   * @tb: destination array with maxtype+1 elements
   * @maxtype: maximum attribute type to be expected
   * @head: head of attribute stream
   * @len: length of attribute stream
10b595aff   Julius Volz   netlink: Fix some...
162
   * @policy: validation policy
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
163
164
165
166
167
168
169
170
171
   *
   * Parses a stream of attributes and stores a pointer to each attribute in
   * the tb array accessable via the attribute type. Attributes with a type
   * exceeding maxtype will be silently ignored for backwards compatibility
   * reasons. policy may be set to NULL if no validation is required.
   *
   * Returns 0 on success or a negative error code.
   */
  int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
ef7c79ed6   Patrick McHardy   [NETLINK]: Mark n...
172
  	      const struct nla_policy *policy)
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
173
174
175
176
177
178
179
  {
  	struct nlattr *nla;
  	int rem, err;
  
  	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
  
  	nla_for_each_attr(nla, head, len, rem) {
8f4c1f9b0   Thomas Graf   [NETLINK]: Introd...
180
  		u16 type = nla_type(nla);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
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
  
  		if (type > 0 && type <= maxtype) {
  			if (policy) {
  				err = validate_nla(nla, maxtype, policy);
  				if (err < 0)
  					goto errout;
  			}
  
  			tb[type] = nla;
  		}
  	}
  
  	if (unlikely(rem > 0))
  		printk(KERN_WARNING "netlink: %d bytes leftover after parsing "
  		       "attributes.
  ", rem);
  
  	err = 0;
  errout:
  	return err;
  }
  
  /**
   * nla_find - Find a specific attribute in a stream of attributes
   * @head: head of attribute stream
   * @len: length of attribute stream
   * @attrtype: type of attribute to look for
   *
   * Returns the first attribute in the stream matching the specified type.
   */
  struct nlattr *nla_find(struct nlattr *head, int len, int attrtype)
  {
  	struct nlattr *nla;
  	int rem;
  
  	nla_for_each_attr(nla, head, len, rem)
8f4c1f9b0   Thomas Graf   [NETLINK]: Introd...
217
  		if (nla_type(nla) == attrtype)
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
218
219
220
221
222
223
224
225
  			return nla;
  
  	return NULL;
  }
  
  /**
   * nla_strlcpy - Copy string attribute payload into a sized buffer
   * @dst: where to copy the string to
10b595aff   Julius Volz   netlink: Fix some...
226
   * @nla: attribute to copy the string from
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
   * @dstsize: size of destination buffer
   *
   * Copies at most dstsize - 1 bytes into the destination buffer.
   * The result is always a valid NUL-terminated string. Unlike
   * strlcpy the destination buffer is always padded out.
   *
   * Returns the length of the source buffer.
   */
  size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
  {
  	size_t srclen = nla_len(nla);
  	char *src = nla_data(nla);
  
  	if (srclen > 0 && src[srclen - 1] == '\0')
  		srclen--;
  
  	if (dstsize > 0) {
  		size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen;
  
  		memset(dst, 0, dstsize);
  		memcpy(dst, src, len);
  	}
  
  	return srclen;
  }
  
  /**
   * nla_memcpy - Copy a netlink attribute into another memory area
   * @dest: where to copy to memcpy
   * @src: netlink attribute to copy from
   * @count: size of the destination area
   *
   * Note: The number of bytes copied is limited by the length of
   *       attribute's payload. memcpy
   *
   * Returns the number of bytes copied.
   */
b057efd4d   Patrick McHardy   netlink: constify...
264
  int nla_memcpy(void *dest, const struct nlattr *src, int count)
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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
  {
  	int minlen = min_t(int, count, nla_len(src));
  
  	memcpy(dest, nla_data(src), minlen);
  
  	return minlen;
  }
  
  /**
   * nla_memcmp - Compare an attribute with sized memory area
   * @nla: netlink attribute
   * @data: memory area
   * @size: size of memory area
   */
  int nla_memcmp(const struct nlattr *nla, const void *data,
  			     size_t size)
  {
  	int d = nla_len(nla) - size;
  
  	if (d == 0)
  		d = memcmp(nla_data(nla), data, size);
  
  	return d;
  }
  
  /**
   * nla_strcmp - Compare a string attribute against a string
   * @nla: netlink string attribute
   * @str: another string
   */
  int nla_strcmp(const struct nlattr *nla, const char *str)
  {
  	int len = strlen(str) + 1;
  	int d = nla_len(nla) - len;
  
  	if (d == 0)
  		d = memcmp(nla_data(nla), str, len);
  
  	return d;
  }
908002161   Herbert Xu   nlattr: Fix build...
305
  #ifdef CONFIG_NET
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  /**
   * __nla_reserve - reserve room for attribute on the skb
   * @skb: socket buffer to reserve room on
   * @attrtype: attribute type
   * @attrlen: length of attribute payload
   *
   * Adds a netlink attribute header to a socket buffer and reserves
   * room for the payload but does not copy it.
   *
   * The caller is responsible to ensure that the skb provides enough
   * tailroom for the attribute header and payload.
   */
  struct nlattr *__nla_reserve(struct sk_buff *skb, int attrtype, int attrlen)
  {
  	struct nlattr *nla;
  
  	nla = (struct nlattr *) skb_put(skb, nla_total_size(attrlen));
  	nla->nla_type = attrtype;
  	nla->nla_len = nla_attr_size(attrlen);
  
  	memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen));
  
  	return nla;
  }
908002161   Herbert Xu   nlattr: Fix build...
330
  EXPORT_SYMBOL(__nla_reserve);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
331
332
  
  /**
fe4944e59   Thomas Graf   [NETLINK]: Extend...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
   * __nla_reserve_nohdr - reserve room for attribute without header
   * @skb: socket buffer to reserve room on
   * @attrlen: length of attribute payload
   *
   * Reserves room for attribute payload without a header.
   *
   * The caller is responsible to ensure that the skb provides enough
   * tailroom for the payload.
   */
  void *__nla_reserve_nohdr(struct sk_buff *skb, int attrlen)
  {
  	void *start;
  
  	start = skb_put(skb, NLA_ALIGN(attrlen));
  	memset(start, 0, NLA_ALIGN(attrlen));
  
  	return start;
  }
908002161   Herbert Xu   nlattr: Fix build...
351
  EXPORT_SYMBOL(__nla_reserve_nohdr);
fe4944e59   Thomas Graf   [NETLINK]: Extend...
352
353
  
  /**
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
   * nla_reserve - reserve room for attribute on the skb
   * @skb: socket buffer to reserve room on
   * @attrtype: attribute type
   * @attrlen: length of attribute payload
   *
   * Adds a netlink attribute header to a socket buffer and reserves
   * room for the payload but does not copy it.
   *
   * Returns NULL if the tailroom of the skb is insufficient to store
   * the attribute header and payload.
   */
  struct nlattr *nla_reserve(struct sk_buff *skb, int attrtype, int attrlen)
  {
  	if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen)))
  		return NULL;
  
  	return __nla_reserve(skb, attrtype, attrlen);
  }
908002161   Herbert Xu   nlattr: Fix build...
372
  EXPORT_SYMBOL(nla_reserve);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
373
374
  
  /**
10b595aff   Julius Volz   netlink: Fix some...
375
   * nla_reserve_nohdr - reserve room for attribute without header
fe4944e59   Thomas Graf   [NETLINK]: Extend...
376
   * @skb: socket buffer to reserve room on
10b595aff   Julius Volz   netlink: Fix some...
377
   * @attrlen: length of attribute payload
fe4944e59   Thomas Graf   [NETLINK]: Extend...
378
379
380
381
382
383
384
385
386
387
388
389
390
   *
   * Reserves room for attribute payload without a header.
   *
   * Returns NULL if the tailroom of the skb is insufficient to store
   * the attribute payload.
   */
  void *nla_reserve_nohdr(struct sk_buff *skb, int attrlen)
  {
  	if (unlikely(skb_tailroom(skb) < NLA_ALIGN(attrlen)))
  		return NULL;
  
  	return __nla_reserve_nohdr(skb, attrlen);
  }
908002161   Herbert Xu   nlattr: Fix build...
391
  EXPORT_SYMBOL(nla_reserve_nohdr);
fe4944e59   Thomas Graf   [NETLINK]: Extend...
392
393
  
  /**
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
   * __nla_put - Add a netlink attribute to a socket buffer
   * @skb: socket buffer to add attribute to
   * @attrtype: attribute type
   * @attrlen: length of attribute payload
   * @data: head of attribute payload
   *
   * The caller is responsible to ensure that the skb provides enough
   * tailroom for the attribute header and payload.
   */
  void __nla_put(struct sk_buff *skb, int attrtype, int attrlen,
  			     const void *data)
  {
  	struct nlattr *nla;
  
  	nla = __nla_reserve(skb, attrtype, attrlen);
  	memcpy(nla_data(nla), data, attrlen);
  }
908002161   Herbert Xu   nlattr: Fix build...
411
  EXPORT_SYMBOL(__nla_put);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
412

fe4944e59   Thomas Graf   [NETLINK]: Extend...
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
  /**
   * __nla_put_nohdr - Add a netlink attribute without header
   * @skb: socket buffer to add attribute to
   * @attrlen: length of attribute payload
   * @data: head of attribute payload
   *
   * The caller is responsible to ensure that the skb provides enough
   * tailroom for the attribute payload.
   */
  void __nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data)
  {
  	void *start;
  
  	start = __nla_reserve_nohdr(skb, attrlen);
  	memcpy(start, data, attrlen);
  }
908002161   Herbert Xu   nlattr: Fix build...
429
  EXPORT_SYMBOL(__nla_put_nohdr);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
430
431
432
433
434
435
436
437
  
  /**
   * nla_put - Add a netlink attribute to a socket buffer
   * @skb: socket buffer to add attribute to
   * @attrtype: attribute type
   * @attrlen: length of attribute payload
   * @data: head of attribute payload
   *
bc3ed28ca   Thomas Graf   netlink: Improve ...
438
   * Returns -EMSGSIZE if the tailroom of the skb is insufficient to store
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
439
440
441
442
443
   * the attribute header and payload.
   */
  int nla_put(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
  {
  	if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen)))
bc3ed28ca   Thomas Graf   netlink: Improve ...
444
  		return -EMSGSIZE;
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
445
446
447
448
  
  	__nla_put(skb, attrtype, attrlen, data);
  	return 0;
  }
908002161   Herbert Xu   nlattr: Fix build...
449
  EXPORT_SYMBOL(nla_put);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
450

fe4944e59   Thomas Graf   [NETLINK]: Extend...
451
452
453
454
455
456
  /**
   * nla_put_nohdr - Add a netlink attribute without header
   * @skb: socket buffer to add attribute to
   * @attrlen: length of attribute payload
   * @data: head of attribute payload
   *
bc3ed28ca   Thomas Graf   netlink: Improve ...
457
   * Returns -EMSGSIZE if the tailroom of the skb is insufficient to store
fe4944e59   Thomas Graf   [NETLINK]: Extend...
458
459
460
461
462
   * the attribute payload.
   */
  int nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data)
  {
  	if (unlikely(skb_tailroom(skb) < NLA_ALIGN(attrlen)))
bc3ed28ca   Thomas Graf   netlink: Improve ...
463
  		return -EMSGSIZE;
fe4944e59   Thomas Graf   [NETLINK]: Extend...
464
465
466
467
  
  	__nla_put_nohdr(skb, attrlen, data);
  	return 0;
  }
908002161   Herbert Xu   nlattr: Fix build...
468
  EXPORT_SYMBOL(nla_put_nohdr);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
469

01480e1cf   Patrick McHardy   [NETLINK]: Add nl...
470
471
472
473
474
475
  /**
   * nla_append - Add a netlink attribute without header or padding
   * @skb: socket buffer to add attribute to
   * @attrlen: length of attribute payload
   * @data: head of attribute payload
   *
bc3ed28ca   Thomas Graf   netlink: Improve ...
476
   * Returns -EMSGSIZE if the tailroom of the skb is insufficient to store
01480e1cf   Patrick McHardy   [NETLINK]: Add nl...
477
478
479
480
481
   * the attribute payload.
   */
  int nla_append(struct sk_buff *skb, int attrlen, const void *data)
  {
  	if (unlikely(skb_tailroom(skb) < NLA_ALIGN(attrlen)))
bc3ed28ca   Thomas Graf   netlink: Improve ...
482
  		return -EMSGSIZE;
01480e1cf   Patrick McHardy   [NETLINK]: Add nl...
483
484
485
486
  
  	memcpy(skb_put(skb, attrlen), data, attrlen);
  	return 0;
  }
908002161   Herbert Xu   nlattr: Fix build...
487
488
  EXPORT_SYMBOL(nla_append);
  #endif
01480e1cf   Patrick McHardy   [NETLINK]: Add nl...
489

bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
490
  EXPORT_SYMBOL(nla_validate);
e487eb99c   Holger Eitzenberger   netlink: add nla_...
491
  EXPORT_SYMBOL(nla_policy_len);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
492
493
494
  EXPORT_SYMBOL(nla_parse);
  EXPORT_SYMBOL(nla_find);
  EXPORT_SYMBOL(nla_strlcpy);
bfa83a9e0   Thomas Graf   [NETLINK]: Type-s...
495
496
497
  EXPORT_SYMBOL(nla_memcpy);
  EXPORT_SYMBOL(nla_memcmp);
  EXPORT_SYMBOL(nla_strcmp);