Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.24 447 lines 11 kB view raw
1/* 2 * NETLINK Netlink attributes 3 * 4 * Authors: Thomas Graf <tgraf@suug.ch> 5 * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> 6 */ 7 8#include <linux/module.h> 9#include <linux/kernel.h> 10#include <linux/errno.h> 11#include <linux/jiffies.h> 12#include <linux/netdevice.h> 13#include <linux/skbuff.h> 14#include <linux/string.h> 15#include <linux/types.h> 16#include <net/netlink.h> 17 18static u16 nla_attr_minlen[NLA_TYPE_MAX+1] __read_mostly = { 19 [NLA_U8] = sizeof(u8), 20 [NLA_U16] = sizeof(u16), 21 [NLA_U32] = sizeof(u32), 22 [NLA_U64] = sizeof(u64), 23 [NLA_NESTED] = NLA_HDRLEN, 24}; 25 26static int validate_nla(struct nlattr *nla, int maxtype, 27 const struct nla_policy *policy) 28{ 29 const struct nla_policy *pt; 30 int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla); 31 32 if (type <= 0 || type > maxtype) 33 return 0; 34 35 pt = &policy[type]; 36 37 BUG_ON(pt->type > NLA_TYPE_MAX); 38 39 switch (pt->type) { 40 case NLA_FLAG: 41 if (attrlen > 0) 42 return -ERANGE; 43 break; 44 45 case NLA_NUL_STRING: 46 if (pt->len) 47 minlen = min_t(int, attrlen, pt->len + 1); 48 else 49 minlen = attrlen; 50 51 if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL) 52 return -EINVAL; 53 /* fall through */ 54 55 case NLA_STRING: 56 if (attrlen < 1) 57 return -ERANGE; 58 59 if (pt->len) { 60 char *buf = nla_data(nla); 61 62 if (buf[attrlen - 1] == '\0') 63 attrlen--; 64 65 if (attrlen > pt->len) 66 return -ERANGE; 67 } 68 break; 69 70 case NLA_BINARY: 71 if (pt->len && attrlen > pt->len) 72 return -ERANGE; 73 break; 74 75 case NLA_NESTED_COMPAT: 76 if (attrlen < pt->len) 77 return -ERANGE; 78 if (attrlen < NLA_ALIGN(pt->len)) 79 break; 80 if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN) 81 return -ERANGE; 82 nla = nla_data(nla) + NLA_ALIGN(pt->len); 83 if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla)) 84 return -ERANGE; 85 break; 86 default: 87 if (pt->len) 88 minlen = pt->len; 89 else if (pt->type != NLA_UNSPEC) 90 minlen = nla_attr_minlen[pt->type]; 91 92 if (attrlen < minlen) 93 return -ERANGE; 94 } 95 96 return 0; 97} 98 99/** 100 * nla_validate - Validate a stream of attributes 101 * @head: head of attribute stream 102 * @len: length of attribute stream 103 * @maxtype: maximum attribute type to be expected 104 * @policy: validation policy 105 * 106 * Validates all attributes in the specified attribute stream against the 107 * specified policy. Attributes with a type exceeding maxtype will be 108 * ignored. See documenation of struct nla_policy for more details. 109 * 110 * Returns 0 on success or a negative error code. 111 */ 112int nla_validate(struct nlattr *head, int len, int maxtype, 113 const struct nla_policy *policy) 114{ 115 struct nlattr *nla; 116 int rem, err; 117 118 nla_for_each_attr(nla, head, len, rem) { 119 err = validate_nla(nla, maxtype, policy); 120 if (err < 0) 121 goto errout; 122 } 123 124 err = 0; 125errout: 126 return err; 127} 128 129/** 130 * nla_parse - Parse a stream of attributes into a tb buffer 131 * @tb: destination array with maxtype+1 elements 132 * @maxtype: maximum attribute type to be expected 133 * @head: head of attribute stream 134 * @len: length of attribute stream 135 * 136 * Parses a stream of attributes and stores a pointer to each attribute in 137 * the tb array accessable via the attribute type. Attributes with a type 138 * exceeding maxtype will be silently ignored for backwards compatibility 139 * reasons. policy may be set to NULL if no validation is required. 140 * 141 * Returns 0 on success or a negative error code. 142 */ 143int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, 144 const struct nla_policy *policy) 145{ 146 struct nlattr *nla; 147 int rem, err; 148 149 memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); 150 151 nla_for_each_attr(nla, head, len, rem) { 152 u16 type = nla_type(nla); 153 154 if (type > 0 && type <= maxtype) { 155 if (policy) { 156 err = validate_nla(nla, maxtype, policy); 157 if (err < 0) 158 goto errout; 159 } 160 161 tb[type] = nla; 162 } 163 } 164 165 if (unlikely(rem > 0)) 166 printk(KERN_WARNING "netlink: %d bytes leftover after parsing " 167 "attributes.\n", rem); 168 169 err = 0; 170errout: 171 return err; 172} 173 174/** 175 * nla_find - Find a specific attribute in a stream of attributes 176 * @head: head of attribute stream 177 * @len: length of attribute stream 178 * @attrtype: type of attribute to look for 179 * 180 * Returns the first attribute in the stream matching the specified type. 181 */ 182struct nlattr *nla_find(struct nlattr *head, int len, int attrtype) 183{ 184 struct nlattr *nla; 185 int rem; 186 187 nla_for_each_attr(nla, head, len, rem) 188 if (nla_type(nla) == attrtype) 189 return nla; 190 191 return NULL; 192} 193 194/** 195 * nla_strlcpy - Copy string attribute payload into a sized buffer 196 * @dst: where to copy the string to 197 * @src: attribute to copy the string from 198 * @dstsize: size of destination buffer 199 * 200 * Copies at most dstsize - 1 bytes into the destination buffer. 201 * The result is always a valid NUL-terminated string. Unlike 202 * strlcpy the destination buffer is always padded out. 203 * 204 * Returns the length of the source buffer. 205 */ 206size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) 207{ 208 size_t srclen = nla_len(nla); 209 char *src = nla_data(nla); 210 211 if (srclen > 0 && src[srclen - 1] == '\0') 212 srclen--; 213 214 if (dstsize > 0) { 215 size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; 216 217 memset(dst, 0, dstsize); 218 memcpy(dst, src, len); 219 } 220 221 return srclen; 222} 223 224/** 225 * nla_memcpy - Copy a netlink attribute into another memory area 226 * @dest: where to copy to memcpy 227 * @src: netlink attribute to copy from 228 * @count: size of the destination area 229 * 230 * Note: The number of bytes copied is limited by the length of 231 * attribute's payload. memcpy 232 * 233 * Returns the number of bytes copied. 234 */ 235int nla_memcpy(void *dest, struct nlattr *src, int count) 236{ 237 int minlen = min_t(int, count, nla_len(src)); 238 239 memcpy(dest, nla_data(src), minlen); 240 241 return minlen; 242} 243 244/** 245 * nla_memcmp - Compare an attribute with sized memory area 246 * @nla: netlink attribute 247 * @data: memory area 248 * @size: size of memory area 249 */ 250int nla_memcmp(const struct nlattr *nla, const void *data, 251 size_t size) 252{ 253 int d = nla_len(nla) - size; 254 255 if (d == 0) 256 d = memcmp(nla_data(nla), data, size); 257 258 return d; 259} 260 261/** 262 * nla_strcmp - Compare a string attribute against a string 263 * @nla: netlink string attribute 264 * @str: another string 265 */ 266int nla_strcmp(const struct nlattr *nla, const char *str) 267{ 268 int len = strlen(str) + 1; 269 int d = nla_len(nla) - len; 270 271 if (d == 0) 272 d = memcmp(nla_data(nla), str, len); 273 274 return d; 275} 276 277/** 278 * __nla_reserve - reserve room for attribute on the skb 279 * @skb: socket buffer to reserve room on 280 * @attrtype: attribute type 281 * @attrlen: length of attribute payload 282 * 283 * Adds a netlink attribute header to a socket buffer and reserves 284 * room for the payload but does not copy it. 285 * 286 * The caller is responsible to ensure that the skb provides enough 287 * tailroom for the attribute header and payload. 288 */ 289struct nlattr *__nla_reserve(struct sk_buff *skb, int attrtype, int attrlen) 290{ 291 struct nlattr *nla; 292 293 nla = (struct nlattr *) skb_put(skb, nla_total_size(attrlen)); 294 nla->nla_type = attrtype; 295 nla->nla_len = nla_attr_size(attrlen); 296 297 memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); 298 299 return nla; 300} 301 302/** 303 * __nla_reserve_nohdr - reserve room for attribute without header 304 * @skb: socket buffer to reserve room on 305 * @attrlen: length of attribute payload 306 * 307 * Reserves room for attribute payload without a header. 308 * 309 * The caller is responsible to ensure that the skb provides enough 310 * tailroom for the payload. 311 */ 312void *__nla_reserve_nohdr(struct sk_buff *skb, int attrlen) 313{ 314 void *start; 315 316 start = skb_put(skb, NLA_ALIGN(attrlen)); 317 memset(start, 0, NLA_ALIGN(attrlen)); 318 319 return start; 320} 321 322/** 323 * nla_reserve - reserve room for attribute on the skb 324 * @skb: socket buffer to reserve room on 325 * @attrtype: attribute type 326 * @attrlen: length of attribute payload 327 * 328 * Adds a netlink attribute header to a socket buffer and reserves 329 * room for the payload but does not copy it. 330 * 331 * Returns NULL if the tailroom of the skb is insufficient to store 332 * the attribute header and payload. 333 */ 334struct nlattr *nla_reserve(struct sk_buff *skb, int attrtype, int attrlen) 335{ 336 if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen))) 337 return NULL; 338 339 return __nla_reserve(skb, attrtype, attrlen); 340} 341 342/** 343 * nla_reserve - reserve room for attribute without header 344 * @skb: socket buffer to reserve room on 345 * @len: length of attribute payload 346 * 347 * Reserves room for attribute payload without a header. 348 * 349 * Returns NULL if the tailroom of the skb is insufficient to store 350 * the attribute payload. 351 */ 352void *nla_reserve_nohdr(struct sk_buff *skb, int attrlen) 353{ 354 if (unlikely(skb_tailroom(skb) < NLA_ALIGN(attrlen))) 355 return NULL; 356 357 return __nla_reserve_nohdr(skb, attrlen); 358} 359 360/** 361 * __nla_put - Add a netlink attribute to a socket buffer 362 * @skb: socket buffer to add attribute to 363 * @attrtype: attribute type 364 * @attrlen: length of attribute payload 365 * @data: head of attribute payload 366 * 367 * The caller is responsible to ensure that the skb provides enough 368 * tailroom for the attribute header and payload. 369 */ 370void __nla_put(struct sk_buff *skb, int attrtype, int attrlen, 371 const void *data) 372{ 373 struct nlattr *nla; 374 375 nla = __nla_reserve(skb, attrtype, attrlen); 376 memcpy(nla_data(nla), data, attrlen); 377} 378 379/** 380 * __nla_put_nohdr - Add a netlink attribute without header 381 * @skb: socket buffer to add attribute to 382 * @attrlen: length of attribute payload 383 * @data: head of attribute payload 384 * 385 * The caller is responsible to ensure that the skb provides enough 386 * tailroom for the attribute payload. 387 */ 388void __nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data) 389{ 390 void *start; 391 392 start = __nla_reserve_nohdr(skb, attrlen); 393 memcpy(start, data, attrlen); 394} 395 396/** 397 * nla_put - Add a netlink attribute to a socket buffer 398 * @skb: socket buffer to add attribute to 399 * @attrtype: attribute type 400 * @attrlen: length of attribute payload 401 * @data: head of attribute payload 402 * 403 * Returns -1 if the tailroom of the skb is insufficient to store 404 * the attribute header and payload. 405 */ 406int nla_put(struct sk_buff *skb, int attrtype, int attrlen, const void *data) 407{ 408 if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen))) 409 return -1; 410 411 __nla_put(skb, attrtype, attrlen, data); 412 return 0; 413} 414 415/** 416 * nla_put_nohdr - Add a netlink attribute without header 417 * @skb: socket buffer to add attribute to 418 * @attrlen: length of attribute payload 419 * @data: head of attribute payload 420 * 421 * Returns -1 if the tailroom of the skb is insufficient to store 422 * the attribute payload. 423 */ 424int nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data) 425{ 426 if (unlikely(skb_tailroom(skb) < NLA_ALIGN(attrlen))) 427 return -1; 428 429 __nla_put_nohdr(skb, attrlen, data); 430 return 0; 431} 432 433EXPORT_SYMBOL(nla_validate); 434EXPORT_SYMBOL(nla_parse); 435EXPORT_SYMBOL(nla_find); 436EXPORT_SYMBOL(nla_strlcpy); 437EXPORT_SYMBOL(__nla_reserve); 438EXPORT_SYMBOL(__nla_reserve_nohdr); 439EXPORT_SYMBOL(nla_reserve); 440EXPORT_SYMBOL(nla_reserve_nohdr); 441EXPORT_SYMBOL(__nla_put); 442EXPORT_SYMBOL(__nla_put_nohdr); 443EXPORT_SYMBOL(nla_put); 444EXPORT_SYMBOL(nla_put_nohdr); 445EXPORT_SYMBOL(nla_memcpy); 446EXPORT_SYMBOL(nla_memcmp); 447EXPORT_SYMBOL(nla_strcmp);