diff options
-rw-r--r-- | include/net/ip.h | 5 | ||||
-rw-r--r-- | net/ipv4/ip_options.c | 49 | ||||
-rw-r--r-- | net/ipv4/ip_sockglue.c | 4 |
3 files changed, 40 insertions, 18 deletions
diff --git a/include/net/ip.h b/include/net/ip.h index 7623e414a5fb..e4563bbee6ea 100644 --- a/include/net/ip.h +++ b/include/net/ip.h | |||
@@ -335,7 +335,10 @@ extern void ip_options_build(struct sk_buff *skb, struct ip_options *opt, u32 da | |||
335 | extern int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb); | 335 | extern int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb); |
336 | extern void ip_options_fragment(struct sk_buff *skb); | 336 | extern void ip_options_fragment(struct sk_buff *skb); |
337 | extern int ip_options_compile(struct ip_options *opt, struct sk_buff *skb); | 337 | extern int ip_options_compile(struct ip_options *opt, struct sk_buff *skb); |
338 | extern int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, int user); | 338 | extern int ip_options_get(struct ip_options **optp, |
339 | unsigned char *data, int optlen); | ||
340 | extern int ip_options_get_from_user(struct ip_options **optp, | ||
341 | unsigned char __user *data, int optlen); | ||
339 | extern void ip_options_undo(struct ip_options * opt); | 342 | extern void ip_options_undo(struct ip_options * opt); |
340 | extern void ip_forward_options(struct sk_buff *skb); | 343 | extern void ip_forward_options(struct sk_buff *skb); |
341 | extern int ip_options_rcv_srr(struct sk_buff *skb); | 344 | extern int ip_options_rcv_srr(struct sk_buff *skb); |
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); |
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index aca088b3707a..2f0b47da5b37 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -153,7 +153,7 @@ int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc) | |||
153 | switch (cmsg->cmsg_type) { | 153 | switch (cmsg->cmsg_type) { |
154 | case IP_RETOPTS: | 154 | case IP_RETOPTS: |
155 | err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); | 155 | err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); |
156 | err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40, 0); | 156 | err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40); |
157 | if (err) | 157 | if (err) |
158 | return err; | 158 | return err; |
159 | break; | 159 | break; |
@@ -425,7 +425,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, | |||
425 | struct ip_options * opt = NULL; | 425 | struct ip_options * opt = NULL; |
426 | if (optlen > 40 || optlen < 0) | 426 | if (optlen > 40 || optlen < 0) |
427 | goto e_inval; | 427 | goto e_inval; |
428 | err = ip_options_get(&opt, optval, optlen, 1); | 428 | err = ip_options_get_from_user(&opt, optval, optlen); |
429 | if (err) | 429 | if (err) |
430 | break; | 430 | break; |
431 | if (sk->sk_type == SOCK_STREAM) { | 431 | if (sk->sk_type == SOCK_STREAM) { |