diff options
-rw-r--r-- | net/netlink/af_netlink.c | 135 |
1 files changed, 129 insertions, 6 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6560635fd25c..90504a0e42ab 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -116,6 +116,11 @@ static bool netlink_skb_is_mmaped(const struct sk_buff *skb) | |||
116 | return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED; | 116 | return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED; |
117 | } | 117 | } |
118 | 118 | ||
119 | static bool netlink_tx_is_mmaped(struct sock *sk) | ||
120 | { | ||
121 | return nlk_sk(sk)->tx_ring.pg_vec != NULL; | ||
122 | } | ||
123 | |||
119 | static __pure struct page *pgvec_to_page(const void *addr) | 124 | static __pure struct page *pgvec_to_page(const void *addr) |
120 | { | 125 | { |
121 | if (is_vmalloc_addr(addr)) | 126 | if (is_vmalloc_addr(addr)) |
@@ -438,6 +443,9 @@ static unsigned int netlink_poll(struct file *file, struct socket *sock, | |||
438 | struct netlink_sock *nlk = nlk_sk(sk); | 443 | struct netlink_sock *nlk = nlk_sk(sk); |
439 | unsigned int mask; | 444 | unsigned int mask; |
440 | 445 | ||
446 | if (nlk->cb != NULL && nlk->rx_ring.pg_vec != NULL) | ||
447 | netlink_dump(sk); | ||
448 | |||
441 | mask = datagram_poll(file, sock, wait); | 449 | mask = datagram_poll(file, sock, wait); |
442 | 450 | ||
443 | spin_lock_bh(&sk->sk_receive_queue.lock); | 451 | spin_lock_bh(&sk->sk_receive_queue.lock); |
@@ -483,10 +491,110 @@ static void netlink_ring_setup_skb(struct sk_buff *skb, struct sock *sk, | |||
483 | NETLINK_CB(skb).flags |= NETLINK_SKB_MMAPED; | 491 | NETLINK_CB(skb).flags |= NETLINK_SKB_MMAPED; |
484 | NETLINK_CB(skb).sk = sk; | 492 | NETLINK_CB(skb).sk = sk; |
485 | } | 493 | } |
494 | |||
495 | static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg, | ||
496 | u32 dst_portid, u32 dst_group, | ||
497 | struct sock_iocb *siocb) | ||
498 | { | ||
499 | struct netlink_sock *nlk = nlk_sk(sk); | ||
500 | struct netlink_ring *ring; | ||
501 | struct nl_mmap_hdr *hdr; | ||
502 | struct sk_buff *skb; | ||
503 | unsigned int maxlen; | ||
504 | bool excl = true; | ||
505 | int err = 0, len = 0; | ||
506 | |||
507 | /* Netlink messages are validated by the receiver before processing. | ||
508 | * In order to avoid userspace changing the contents of the message | ||
509 | * after validation, the socket and the ring may only be used by a | ||
510 | * single process, otherwise we fall back to copying. | ||
511 | */ | ||
512 | if (atomic_long_read(&sk->sk_socket->file->f_count) > 2 || | ||
513 | atomic_read(&nlk->mapped) > 1) | ||
514 | excl = false; | ||
515 | |||
516 | mutex_lock(&nlk->pg_vec_lock); | ||
517 | |||
518 | ring = &nlk->tx_ring; | ||
519 | maxlen = ring->frame_size - NL_MMAP_HDRLEN; | ||
520 | |||
521 | do { | ||
522 | hdr = netlink_current_frame(ring, NL_MMAP_STATUS_VALID); | ||
523 | if (hdr == NULL) { | ||
524 | if (!(msg->msg_flags & MSG_DONTWAIT) && | ||
525 | atomic_read(&nlk->tx_ring.pending)) | ||
526 | schedule(); | ||
527 | continue; | ||
528 | } | ||
529 | if (hdr->nm_len > maxlen) { | ||
530 | err = -EINVAL; | ||
531 | goto out; | ||
532 | } | ||
533 | |||
534 | netlink_frame_flush_dcache(hdr); | ||
535 | |||
536 | if (likely(dst_portid == 0 && dst_group == 0 && excl)) { | ||
537 | skb = alloc_skb_head(GFP_KERNEL); | ||
538 | if (skb == NULL) { | ||
539 | err = -ENOBUFS; | ||
540 | goto out; | ||
541 | } | ||
542 | sock_hold(sk); | ||
543 | netlink_ring_setup_skb(skb, sk, ring, hdr); | ||
544 | NETLINK_CB(skb).flags |= NETLINK_SKB_TX; | ||
545 | __skb_put(skb, hdr->nm_len); | ||
546 | netlink_set_status(hdr, NL_MMAP_STATUS_RESERVED); | ||
547 | atomic_inc(&ring->pending); | ||
548 | } else { | ||
549 | skb = alloc_skb(hdr->nm_len, GFP_KERNEL); | ||
550 | if (skb == NULL) { | ||
551 | err = -ENOBUFS; | ||
552 | goto out; | ||
553 | } | ||
554 | __skb_put(skb, hdr->nm_len); | ||
555 | memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, hdr->nm_len); | ||
556 | netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); | ||
557 | } | ||
558 | |||
559 | netlink_increment_head(ring); | ||
560 | |||
561 | NETLINK_CB(skb).portid = nlk->portid; | ||
562 | NETLINK_CB(skb).dst_group = dst_group; | ||
563 | NETLINK_CB(skb).creds = siocb->scm->creds; | ||
564 | |||
565 | err = security_netlink_send(sk, skb); | ||
566 | if (err) { | ||
567 | kfree_skb(skb); | ||
568 | goto out; | ||
569 | } | ||
570 | |||
571 | if (unlikely(dst_group)) { | ||
572 | atomic_inc(&skb->users); | ||
573 | netlink_broadcast(sk, skb, dst_portid, dst_group, | ||
574 | GFP_KERNEL); | ||
575 | } | ||
576 | err = netlink_unicast(sk, skb, dst_portid, | ||
577 | msg->msg_flags & MSG_DONTWAIT); | ||
578 | if (err < 0) | ||
579 | goto out; | ||
580 | len += err; | ||
581 | |||
582 | } while (hdr != NULL || | ||
583 | (!(msg->msg_flags & MSG_DONTWAIT) && | ||
584 | atomic_read(&nlk->tx_ring.pending))); | ||
585 | |||
586 | if (len > 0) | ||
587 | err = len; | ||
588 | out: | ||
589 | mutex_unlock(&nlk->pg_vec_lock); | ||
590 | return err; | ||
591 | } | ||
486 | #else /* CONFIG_NETLINK_MMAP */ | 592 | #else /* CONFIG_NETLINK_MMAP */ |
487 | #define netlink_skb_is_mmaped(skb) false | 593 | #define netlink_skb_is_mmaped(skb) false |
594 | #define netlink_tx_is_mmaped(sk) false | ||
488 | #define netlink_mmap sock_no_mmap | 595 | #define netlink_mmap sock_no_mmap |
489 | #define netlink_poll datagram_poll | 596 | #define netlink_poll datagram_poll |
597 | #define netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, siocb) 0 | ||
490 | #endif /* CONFIG_NETLINK_MMAP */ | 598 | #endif /* CONFIG_NETLINK_MMAP */ |
491 | 599 | ||
492 | static void netlink_destroy_callback(struct netlink_callback *cb) | 600 | static void netlink_destroy_callback(struct netlink_callback *cb) |
@@ -517,11 +625,16 @@ static void netlink_skb_destructor(struct sk_buff *skb) | |||
517 | hdr = netlink_mmap_hdr(skb); | 625 | hdr = netlink_mmap_hdr(skb); |
518 | sk = NETLINK_CB(skb).sk; | 626 | sk = NETLINK_CB(skb).sk; |
519 | 627 | ||
520 | if (!(NETLINK_CB(skb).flags & NETLINK_SKB_DELIVERED)) { | 628 | if (NETLINK_CB(skb).flags & NETLINK_SKB_TX) { |
521 | hdr->nm_len = 0; | 629 | netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); |
522 | netlink_set_status(hdr, NL_MMAP_STATUS_VALID); | 630 | ring = &nlk_sk(sk)->tx_ring; |
631 | } else { | ||
632 | if (!(NETLINK_CB(skb).flags & NETLINK_SKB_DELIVERED)) { | ||
633 | hdr->nm_len = 0; | ||
634 | netlink_set_status(hdr, NL_MMAP_STATUS_VALID); | ||
635 | } | ||
636 | ring = &nlk_sk(sk)->rx_ring; | ||
523 | } | 637 | } |
524 | ring = &nlk_sk(sk)->rx_ring; | ||
525 | 638 | ||
526 | WARN_ON(atomic_read(&ring->pending) == 0); | 639 | WARN_ON(atomic_read(&ring->pending) == 0); |
527 | atomic_dec(&ring->pending); | 640 | atomic_dec(&ring->pending); |
@@ -1230,8 +1343,9 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb, | |||
1230 | 1343 | ||
1231 | nlk = nlk_sk(sk); | 1344 | nlk = nlk_sk(sk); |
1232 | 1345 | ||
1233 | if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || | 1346 | if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || |
1234 | test_bit(NETLINK_CONGESTED, &nlk->state)) { | 1347 | test_bit(NETLINK_CONGESTED, &nlk->state)) && |
1348 | !netlink_skb_is_mmaped(skb)) { | ||
1235 | DECLARE_WAITQUEUE(wait, current); | 1349 | DECLARE_WAITQUEUE(wait, current); |
1236 | if (!*timeo) { | 1350 | if (!*timeo) { |
1237 | if (!ssk || netlink_is_kernel(ssk)) | 1351 | if (!ssk || netlink_is_kernel(ssk)) |
@@ -1291,6 +1405,8 @@ static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation) | |||
1291 | int delta; | 1405 | int delta; |
1292 | 1406 | ||
1293 | WARN_ON(skb->sk != NULL); | 1407 | WARN_ON(skb->sk != NULL); |
1408 | if (netlink_skb_is_mmaped(skb)) | ||
1409 | return skb; | ||
1294 | 1410 | ||
1295 | delta = skb->end - skb->tail; | 1411 | delta = skb->end - skb->tail; |
1296 | if (delta * 2 < skb->truesize) | 1412 | if (delta * 2 < skb->truesize) |
@@ -1815,6 +1931,13 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
1815 | goto out; | 1931 | goto out; |
1816 | } | 1932 | } |
1817 | 1933 | ||
1934 | if (netlink_tx_is_mmaped(sk) && | ||
1935 | msg->msg_iov->iov_base == NULL) { | ||
1936 | err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, | ||
1937 | siocb); | ||
1938 | goto out; | ||
1939 | } | ||
1940 | |||
1818 | err = -EMSGSIZE; | 1941 | err = -EMSGSIZE; |
1819 | if (len > sk->sk_sndbuf - 32) | 1942 | if (len > sk->sk_sndbuf - 32) |
1820 | goto out; | 1943 | goto out; |