diff options
-rw-r--r-- | net/ipv4/raw.c | 104 |
1 files changed, 61 insertions, 43 deletions
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index ee8fa4bf3b73..43385a9fa441 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c | |||
@@ -79,6 +79,16 @@ | |||
79 | #include <linux/netfilter.h> | 79 | #include <linux/netfilter.h> |
80 | #include <linux/netfilter_ipv4.h> | 80 | #include <linux/netfilter_ipv4.h> |
81 | #include <linux/compat.h> | 81 | #include <linux/compat.h> |
82 | #include <linux/uio.h> | ||
83 | |||
84 | struct raw_frag_vec { | ||
85 | struct iovec *iov; | ||
86 | union { | ||
87 | struct icmphdr icmph; | ||
88 | char c[1]; | ||
89 | } hdr; | ||
90 | int hlen; | ||
91 | }; | ||
82 | 92 | ||
83 | static struct raw_hashinfo raw_v4_hashinfo = { | 93 | static struct raw_hashinfo raw_v4_hashinfo = { |
84 | .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock), | 94 | .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock), |
@@ -420,53 +430,57 @@ error: | |||
420 | return err; | 430 | return err; |
421 | } | 431 | } |
422 | 432 | ||
423 | static int raw_probe_proto_opt(struct flowi4 *fl4, struct msghdr *msg) | 433 | static int raw_probe_proto_opt(struct raw_frag_vec *rfv, struct flowi4 *fl4) |
424 | { | 434 | { |
425 | struct iovec *iov; | 435 | int err; |
426 | u8 __user *type = NULL; | ||
427 | u8 __user *code = NULL; | ||
428 | int probed = 0; | ||
429 | unsigned int i; | ||
430 | 436 | ||
431 | if (!msg->msg_iov) | 437 | if (fl4->flowi4_proto != IPPROTO_ICMP) |
432 | return 0; | 438 | return 0; |
433 | 439 | ||
434 | for (i = 0; i < msg->msg_iovlen; i++) { | 440 | /* We only need the first two bytes. */ |
435 | iov = &msg->msg_iov[i]; | 441 | rfv->hlen = 2; |
436 | if (!iov) | 442 | |
437 | continue; | 443 | err = memcpy_fromiovec(rfv->hdr.c, rfv->iov, rfv->hlen); |
438 | 444 | if (err) | |
439 | switch (fl4->flowi4_proto) { | 445 | return err; |
440 | case IPPROTO_ICMP: | 446 | |
441 | /* check if one-byte field is readable or not. */ | 447 | fl4->fl4_icmp_type = rfv->hdr.icmph.type; |
442 | if (iov->iov_base && iov->iov_len < 1) | 448 | fl4->fl4_icmp_code = rfv->hdr.icmph.code; |
443 | break; | 449 | |
444 | |||
445 | if (!type) { | ||
446 | type = iov->iov_base; | ||
447 | /* check if code field is readable or not. */ | ||
448 | if (iov->iov_len > 1) | ||
449 | code = type + 1; | ||
450 | } else if (!code) | ||
451 | code = iov->iov_base; | ||
452 | |||
453 | if (type && code) { | ||
454 | if (get_user(fl4->fl4_icmp_type, type) || | ||
455 | get_user(fl4->fl4_icmp_code, code)) | ||
456 | return -EFAULT; | ||
457 | probed = 1; | ||
458 | } | ||
459 | break; | ||
460 | default: | ||
461 | probed = 1; | ||
462 | break; | ||
463 | } | ||
464 | if (probed) | ||
465 | break; | ||
466 | } | ||
467 | return 0; | 450 | return 0; |
468 | } | 451 | } |
469 | 452 | ||
453 | static int raw_getfrag(void *from, char *to, int offset, int len, int odd, | ||
454 | struct sk_buff *skb) | ||
455 | { | ||
456 | struct raw_frag_vec *rfv = from; | ||
457 | |||
458 | if (offset < rfv->hlen) { | ||
459 | int copy = min(rfv->hlen - offset, len); | ||
460 | |||
461 | if (skb->ip_summed == CHECKSUM_PARTIAL) | ||
462 | memcpy(to, rfv->hdr.c + offset, copy); | ||
463 | else | ||
464 | skb->csum = csum_block_add( | ||
465 | skb->csum, | ||
466 | csum_partial_copy_nocheck(rfv->hdr.c + offset, | ||
467 | to, copy, 0), | ||
468 | odd); | ||
469 | |||
470 | odd = 0; | ||
471 | offset += copy; | ||
472 | to += copy; | ||
473 | len -= copy; | ||
474 | |||
475 | if (!len) | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | offset -= rfv->hlen; | ||
480 | |||
481 | return ip_generic_getfrag(rfv->iov, to, offset, len, odd, skb); | ||
482 | } | ||
483 | |||
470 | static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | 484 | static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, |
471 | size_t len) | 485 | size_t len) |
472 | { | 486 | { |
@@ -480,6 +494,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
480 | u8 tos; | 494 | u8 tos; |
481 | int err; | 495 | int err; |
482 | struct ip_options_data opt_copy; | 496 | struct ip_options_data opt_copy; |
497 | struct raw_frag_vec rfv; | ||
483 | 498 | ||
484 | err = -EMSGSIZE; | 499 | err = -EMSGSIZE; |
485 | if (len > 0xFFFF) | 500 | if (len > 0xFFFF) |
@@ -585,7 +600,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
585 | daddr, saddr, 0, 0); | 600 | daddr, saddr, 0, 0); |
586 | 601 | ||
587 | if (!inet->hdrincl) { | 602 | if (!inet->hdrincl) { |
588 | err = raw_probe_proto_opt(&fl4, msg); | 603 | rfv.iov = msg->msg_iov; |
604 | rfv.hlen = 0; | ||
605 | |||
606 | err = raw_probe_proto_opt(&rfv, &fl4); | ||
589 | if (err) | 607 | if (err) |
590 | goto done; | 608 | goto done; |
591 | } | 609 | } |
@@ -616,8 +634,8 @@ back_from_confirm: | |||
616 | if (!ipc.addr) | 634 | if (!ipc.addr) |
617 | ipc.addr = fl4.daddr; | 635 | ipc.addr = fl4.daddr; |
618 | lock_sock(sk); | 636 | lock_sock(sk); |
619 | err = ip_append_data(sk, &fl4, ip_generic_getfrag, | 637 | err = ip_append_data(sk, &fl4, raw_getfrag, |
620 | msg->msg_iov, len, 0, | 638 | &rfv, len, 0, |
621 | &ipc, &rt, msg->msg_flags); | 639 | &ipc, &rt, msg->msg_flags); |
622 | if (err) | 640 | if (err) |
623 | ip_flush_pending_frames(sk); | 641 | ip_flush_pending_frames(sk); |