aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv4/raw.c104
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
84struct 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
83static struct raw_hashinfo raw_v4_hashinfo = { 93static 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
423static int raw_probe_proto_opt(struct flowi4 *fl4, struct msghdr *msg) 433static 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
453static 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
470static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 484static 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);