diff options
Diffstat (limited to 'net/ipv4/ip_options.c')
-rw-r--r-- | net/ipv4/ip_options.c | 49 |
1 files changed, 34 insertions, 15 deletions
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 7e02ba584079..bce4e875193b 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c | |||
@@ -489,23 +489,18 @@ void ip_options_undo(struct ip_options * opt) | |||
489 | } | 489 | } |
490 | } | 490 | } |
491 | 491 | ||
492 | int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, int user) | 492 | static struct ip_options *ip_options_get_alloc(const int optlen) |
493 | { | 493 | { |
494 | struct ip_options *opt; | 494 | struct ip_options *opt = kmalloc(sizeof(*opt) + ((optlen + 3) & ~3), |
495 | GFP_KERNEL); | ||
496 | if (opt) | ||
497 | memset(opt, 0, sizeof(*opt)); | ||
498 | return opt; | ||
499 | } | ||
495 | 500 | ||
496 | opt = kmalloc(sizeof(struct ip_options)+((optlen+3)&~3), GFP_KERNEL); | 501 | static int ip_options_get_finish(struct ip_options **optp, |
497 | if (!opt) | 502 | struct ip_options *opt, int optlen) |
498 | return -ENOMEM; | 503 | { |
499 | memset(opt, 0, sizeof(struct ip_options)); | ||
500 | if (optlen) { | ||
501 | if (user) { | ||
502 | if (copy_from_user(opt->__data, data, optlen)) { | ||
503 | kfree(opt); | ||
504 | return -EFAULT; | ||
505 | } | ||
506 | } else | ||
507 | memcpy(opt->__data, data, optlen); | ||
508 | } | ||
509 | while (optlen & 3) | 504 | while (optlen & 3) |
510 | opt->__data[optlen++] = IPOPT_END; | 505 | opt->__data[optlen++] = IPOPT_END; |
511 | opt->optlen = optlen; | 506 | opt->optlen = optlen; |
@@ -521,6 +516,30 @@ int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, in | |||
521 | return 0; | 516 | return 0; |
522 | } | 517 | } |
523 | 518 | ||
519 | int ip_options_get_from_user(struct ip_options **optp, unsigned char __user *data, int optlen) | ||
520 | { | ||
521 | struct ip_options *opt = ip_options_get_alloc(optlen); | ||
522 | |||
523 | if (!opt) | ||
524 | return -ENOMEM; | ||
525 | if (optlen && copy_from_user(opt->__data, data, optlen)) { | ||
526 | kfree(opt); | ||
527 | return -EFAULT; | ||
528 | } | ||
529 | return ip_options_get_finish(optp, opt, optlen); | ||
530 | } | ||
531 | |||
532 | int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen) | ||
533 | { | ||
534 | struct ip_options *opt = ip_options_get_alloc(optlen); | ||
535 | |||
536 | if (!opt) | ||
537 | return -ENOMEM; | ||
538 | if (optlen) | ||
539 | memcpy(opt->__data, data, optlen); | ||
540 | return ip_options_get_finish(optp, opt, optlen); | ||
541 | } | ||
542 | |||
524 | void ip_forward_options(struct sk_buff *skb) | 543 | void ip_forward_options(struct sk_buff *skb) |
525 | { | 544 | { |
526 | struct ip_options * opt = &(IPCB(skb)->opt); | 545 | struct ip_options * opt = &(IPCB(skb)->opt); |