diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/core/datagram.c | 26 | ||||
-rw-r--r-- | net/core/sock.c | 13 | ||||
-rw-r--r-- | net/ipv4/udp.c | 4 | ||||
-rw-r--r-- | net/ipv6/udp.c | 4 | ||||
-rw-r--r-- | net/unix/af_unix.c | 50 |
5 files changed, 76 insertions, 21 deletions
diff --git a/net/core/datagram.c b/net/core/datagram.c index 68bbf9f65cb0..d3cf12f62c8f 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
@@ -132,6 +132,8 @@ out_noerr: | |||
132 | * __skb_recv_datagram - Receive a datagram skbuff | 132 | * __skb_recv_datagram - Receive a datagram skbuff |
133 | * @sk: socket | 133 | * @sk: socket |
134 | * @flags: MSG_ flags | 134 | * @flags: MSG_ flags |
135 | * @off: an offset in bytes to peek skb from. Returns an offset | ||
136 | * within an skb where data actually starts | ||
135 | * @peeked: returns non-zero if this packet has been seen before | 137 | * @peeked: returns non-zero if this packet has been seen before |
136 | * @err: error code returned | 138 | * @err: error code returned |
137 | * | 139 | * |
@@ -158,7 +160,7 @@ out_noerr: | |||
158 | * the standard around please. | 160 | * the standard around please. |
159 | */ | 161 | */ |
160 | struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, | 162 | struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, |
161 | int *peeked, int *err) | 163 | int *peeked, int *off, int *err) |
162 | { | 164 | { |
163 | struct sk_buff *skb; | 165 | struct sk_buff *skb; |
164 | long timeo; | 166 | long timeo; |
@@ -180,21 +182,25 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, | |||
180 | * However, this function was correct in any case. 8) | 182 | * However, this function was correct in any case. 8) |
181 | */ | 183 | */ |
182 | unsigned long cpu_flags; | 184 | unsigned long cpu_flags; |
185 | struct sk_buff_head *queue = &sk->sk_receive_queue; | ||
183 | 186 | ||
184 | spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags); | 187 | spin_lock_irqsave(&queue->lock, cpu_flags); |
185 | skb = skb_peek(&sk->sk_receive_queue); | 188 | skb_queue_walk(queue, skb) { |
186 | if (skb) { | ||
187 | *peeked = skb->peeked; | 189 | *peeked = skb->peeked; |
188 | if (flags & MSG_PEEK) { | 190 | if (flags & MSG_PEEK) { |
191 | if (*off >= skb->len) { | ||
192 | *off -= skb->len; | ||
193 | continue; | ||
194 | } | ||
189 | skb->peeked = 1; | 195 | skb->peeked = 1; |
190 | atomic_inc(&skb->users); | 196 | atomic_inc(&skb->users); |
191 | } else | 197 | } else |
192 | __skb_unlink(skb, &sk->sk_receive_queue); | 198 | __skb_unlink(skb, queue); |
193 | } | ||
194 | spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags); | ||
195 | 199 | ||
196 | if (skb) | 200 | spin_unlock_irqrestore(&queue->lock, cpu_flags); |
197 | return skb; | 201 | return skb; |
202 | } | ||
203 | spin_unlock_irqrestore(&queue->lock, cpu_flags); | ||
198 | 204 | ||
199 | /* User doesn't want to wait */ | 205 | /* User doesn't want to wait */ |
200 | error = -EAGAIN; | 206 | error = -EAGAIN; |
@@ -214,10 +220,10 @@ EXPORT_SYMBOL(__skb_recv_datagram); | |||
214 | struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, | 220 | struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, |
215 | int noblock, int *err) | 221 | int noblock, int *err) |
216 | { | 222 | { |
217 | int peeked; | 223 | int peeked, off = 0; |
218 | 224 | ||
219 | return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), | 225 | return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), |
220 | &peeked, err); | 226 | &peeked, &off, err); |
221 | } | 227 | } |
222 | EXPORT_SYMBOL(skb_recv_datagram); | 228 | EXPORT_SYMBOL(skb_recv_datagram); |
223 | 229 | ||
diff --git a/net/core/sock.c b/net/core/sock.c index 02f8dfe320b7..19942d4bb6e6 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -793,6 +793,12 @@ set_rcvbuf: | |||
793 | sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool); | 793 | sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool); |
794 | break; | 794 | break; |
795 | 795 | ||
796 | case SO_PEEK_OFF: | ||
797 | if (sock->ops->set_peek_off) | ||
798 | sock->ops->set_peek_off(sk, val); | ||
799 | else | ||
800 | ret = -EOPNOTSUPP; | ||
801 | break; | ||
796 | default: | 802 | default: |
797 | ret = -ENOPROTOOPT; | 803 | ret = -ENOPROTOOPT; |
798 | break; | 804 | break; |
@@ -1018,6 +1024,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname, | |||
1018 | v.val = !!sock_flag(sk, SOCK_WIFI_STATUS); | 1024 | v.val = !!sock_flag(sk, SOCK_WIFI_STATUS); |
1019 | break; | 1025 | break; |
1020 | 1026 | ||
1027 | case SO_PEEK_OFF: | ||
1028 | if (!sock->ops->set_peek_off) | ||
1029 | return -EOPNOTSUPP; | ||
1030 | |||
1031 | v.val = sk->sk_peek_off; | ||
1032 | break; | ||
1021 | default: | 1033 | default: |
1022 | return -ENOPROTOOPT; | 1034 | return -ENOPROTOOPT; |
1023 | } | 1035 | } |
@@ -2092,6 +2104,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) | |||
2092 | 2104 | ||
2093 | sk->sk_sndmsg_page = NULL; | 2105 | sk->sk_sndmsg_page = NULL; |
2094 | sk->sk_sndmsg_off = 0; | 2106 | sk->sk_sndmsg_off = 0; |
2107 | sk->sk_peek_off = -1; | ||
2095 | 2108 | ||
2096 | sk->sk_peer_pid = NULL; | 2109 | sk->sk_peer_pid = NULL; |
2097 | sk->sk_peer_cred = NULL; | 2110 | sk->sk_peer_cred = NULL; |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index cd99f1a0f59f..7c41ab84e72e 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -1167,7 +1167,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
1167 | struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; | 1167 | struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; |
1168 | struct sk_buff *skb; | 1168 | struct sk_buff *skb; |
1169 | unsigned int ulen, copied; | 1169 | unsigned int ulen, copied; |
1170 | int peeked; | 1170 | int peeked, off = 0; |
1171 | int err; | 1171 | int err; |
1172 | int is_udplite = IS_UDPLITE(sk); | 1172 | int is_udplite = IS_UDPLITE(sk); |
1173 | bool slow; | 1173 | bool slow; |
@@ -1183,7 +1183,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
1183 | 1183 | ||
1184 | try_again: | 1184 | try_again: |
1185 | skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), | 1185 | skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), |
1186 | &peeked, &err); | 1186 | &peeked, &off, &err); |
1187 | if (!skb) | 1187 | if (!skb) |
1188 | goto out; | 1188 | goto out; |
1189 | 1189 | ||
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 8aebf8f90436..37b0699e95e5 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -342,7 +342,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
342 | struct inet_sock *inet = inet_sk(sk); | 342 | struct inet_sock *inet = inet_sk(sk); |
343 | struct sk_buff *skb; | 343 | struct sk_buff *skb; |
344 | unsigned int ulen, copied; | 344 | unsigned int ulen, copied; |
345 | int peeked; | 345 | int peeked, off = 0; |
346 | int err; | 346 | int err; |
347 | int is_udplite = IS_UDPLITE(sk); | 347 | int is_udplite = IS_UDPLITE(sk); |
348 | int is_udp4; | 348 | int is_udp4; |
@@ -359,7 +359,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
359 | 359 | ||
360 | try_again: | 360 | try_again: |
361 | skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), | 361 | skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), |
362 | &peeked, &err); | 362 | &peeked, &off, &err); |
363 | if (!skb) | 363 | if (!skb) |
364 | goto out; | 364 | goto out; |
365 | 365 | ||
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 85d3bb7490aa..0be4d24f6ae8 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -530,6 +530,16 @@ static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *, | |||
530 | static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *, | 530 | static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *, |
531 | struct msghdr *, size_t, int); | 531 | struct msghdr *, size_t, int); |
532 | 532 | ||
533 | static void unix_set_peek_off(struct sock *sk, int val) | ||
534 | { | ||
535 | struct unix_sock *u = unix_sk(sk); | ||
536 | |||
537 | mutex_lock(&u->readlock); | ||
538 | sk->sk_peek_off = val; | ||
539 | mutex_unlock(&u->readlock); | ||
540 | } | ||
541 | |||
542 | |||
533 | static const struct proto_ops unix_stream_ops = { | 543 | static const struct proto_ops unix_stream_ops = { |
534 | .family = PF_UNIX, | 544 | .family = PF_UNIX, |
535 | .owner = THIS_MODULE, | 545 | .owner = THIS_MODULE, |
@@ -549,6 +559,7 @@ static const struct proto_ops unix_stream_ops = { | |||
549 | .recvmsg = unix_stream_recvmsg, | 559 | .recvmsg = unix_stream_recvmsg, |
550 | .mmap = sock_no_mmap, | 560 | .mmap = sock_no_mmap, |
551 | .sendpage = sock_no_sendpage, | 561 | .sendpage = sock_no_sendpage, |
562 | .set_peek_off = unix_set_peek_off, | ||
552 | }; | 563 | }; |
553 | 564 | ||
554 | static const struct proto_ops unix_dgram_ops = { | 565 | static const struct proto_ops unix_dgram_ops = { |
@@ -570,6 +581,7 @@ static const struct proto_ops unix_dgram_ops = { | |||
570 | .recvmsg = unix_dgram_recvmsg, | 581 | .recvmsg = unix_dgram_recvmsg, |
571 | .mmap = sock_no_mmap, | 582 | .mmap = sock_no_mmap, |
572 | .sendpage = sock_no_sendpage, | 583 | .sendpage = sock_no_sendpage, |
584 | .set_peek_off = unix_set_peek_off, | ||
573 | }; | 585 | }; |
574 | 586 | ||
575 | static const struct proto_ops unix_seqpacket_ops = { | 587 | static const struct proto_ops unix_seqpacket_ops = { |
@@ -591,6 +603,7 @@ static const struct proto_ops unix_seqpacket_ops = { | |||
591 | .recvmsg = unix_seqpacket_recvmsg, | 603 | .recvmsg = unix_seqpacket_recvmsg, |
592 | .mmap = sock_no_mmap, | 604 | .mmap = sock_no_mmap, |
593 | .sendpage = sock_no_sendpage, | 605 | .sendpage = sock_no_sendpage, |
606 | .set_peek_off = unix_set_peek_off, | ||
594 | }; | 607 | }; |
595 | 608 | ||
596 | static struct proto unix_proto = { | 609 | static struct proto unix_proto = { |
@@ -1756,6 +1769,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1756 | int noblock = flags & MSG_DONTWAIT; | 1769 | int noblock = flags & MSG_DONTWAIT; |
1757 | struct sk_buff *skb; | 1770 | struct sk_buff *skb; |
1758 | int err; | 1771 | int err; |
1772 | int peeked, skip; | ||
1759 | 1773 | ||
1760 | err = -EOPNOTSUPP; | 1774 | err = -EOPNOTSUPP; |
1761 | if (flags&MSG_OOB) | 1775 | if (flags&MSG_OOB) |
@@ -1769,7 +1783,9 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1769 | goto out; | 1783 | goto out; |
1770 | } | 1784 | } |
1771 | 1785 | ||
1772 | skb = skb_recv_datagram(sk, flags, noblock, &err); | 1786 | skip = sk_peek_offset(sk, flags); |
1787 | |||
1788 | skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err); | ||
1773 | if (!skb) { | 1789 | if (!skb) { |
1774 | unix_state_lock(sk); | 1790 | unix_state_lock(sk); |
1775 | /* Signal EOF on disconnected non-blocking SEQPACKET socket. */ | 1791 | /* Signal EOF on disconnected non-blocking SEQPACKET socket. */ |
@@ -1786,12 +1802,12 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1786 | if (msg->msg_name) | 1802 | if (msg->msg_name) |
1787 | unix_copy_addr(msg, skb->sk); | 1803 | unix_copy_addr(msg, skb->sk); |
1788 | 1804 | ||
1789 | if (size > skb->len) | 1805 | if (size > skb->len - skip) |
1790 | size = skb->len; | 1806 | size = skb->len - skip; |
1791 | else if (size < skb->len) | 1807 | else if (size < skb->len - skip) |
1792 | msg->msg_flags |= MSG_TRUNC; | 1808 | msg->msg_flags |= MSG_TRUNC; |
1793 | 1809 | ||
1794 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size); | 1810 | err = skb_copy_datagram_iovec(skb, skip, msg->msg_iov, size); |
1795 | if (err) | 1811 | if (err) |
1796 | goto out_free; | 1812 | goto out_free; |
1797 | 1813 | ||
@@ -1808,6 +1824,8 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1808 | if (!(flags & MSG_PEEK)) { | 1824 | if (!(flags & MSG_PEEK)) { |
1809 | if (UNIXCB(skb).fp) | 1825 | if (UNIXCB(skb).fp) |
1810 | unix_detach_fds(siocb->scm, skb); | 1826 | unix_detach_fds(siocb->scm, skb); |
1827 | |||
1828 | sk_peek_offset_bwd(sk, skb->len); | ||
1811 | } else { | 1829 | } else { |
1812 | /* It is questionable: on PEEK we could: | 1830 | /* It is questionable: on PEEK we could: |
1813 | - do not return fds - good, but too simple 8) | 1831 | - do not return fds - good, but too simple 8) |
@@ -1821,6 +1839,9 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1821 | clearly however! | 1839 | clearly however! |
1822 | 1840 | ||
1823 | */ | 1841 | */ |
1842 | |||
1843 | sk_peek_offset_fwd(sk, size); | ||
1844 | |||
1824 | if (UNIXCB(skb).fp) | 1845 | if (UNIXCB(skb).fp) |
1825 | siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp); | 1846 | siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp); |
1826 | } | 1847 | } |
@@ -1884,6 +1905,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1884 | int target; | 1905 | int target; |
1885 | int err = 0; | 1906 | int err = 0; |
1886 | long timeo; | 1907 | long timeo; |
1908 | int skip; | ||
1887 | 1909 | ||
1888 | err = -EINVAL; | 1910 | err = -EINVAL; |
1889 | if (sk->sk_state != TCP_ESTABLISHED) | 1911 | if (sk->sk_state != TCP_ESTABLISHED) |
@@ -1913,12 +1935,15 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1913 | goto out; | 1935 | goto out; |
1914 | } | 1936 | } |
1915 | 1937 | ||
1938 | skip = sk_peek_offset(sk, flags); | ||
1939 | |||
1916 | do { | 1940 | do { |
1917 | int chunk; | 1941 | int chunk; |
1918 | struct sk_buff *skb; | 1942 | struct sk_buff *skb; |
1919 | 1943 | ||
1920 | unix_state_lock(sk); | 1944 | unix_state_lock(sk); |
1921 | skb = skb_peek(&sk->sk_receive_queue); | 1945 | skb = skb_peek(&sk->sk_receive_queue); |
1946 | again: | ||
1922 | if (skb == NULL) { | 1947 | if (skb == NULL) { |
1923 | unix_sk(sk)->recursion_level = 0; | 1948 | unix_sk(sk)->recursion_level = 0; |
1924 | if (copied >= target) | 1949 | if (copied >= target) |
@@ -1953,6 +1978,13 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1953 | unix_state_unlock(sk); | 1978 | unix_state_unlock(sk); |
1954 | break; | 1979 | break; |
1955 | } | 1980 | } |
1981 | |||
1982 | if (skip >= skb->len) { | ||
1983 | skip -= skb->len; | ||
1984 | skb = skb_peek_next(skb, &sk->sk_receive_queue); | ||
1985 | goto again; | ||
1986 | } | ||
1987 | |||
1956 | unix_state_unlock(sk); | 1988 | unix_state_unlock(sk); |
1957 | 1989 | ||
1958 | if (check_creds) { | 1990 | if (check_creds) { |
@@ -1972,8 +2004,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1972 | sunaddr = NULL; | 2004 | sunaddr = NULL; |
1973 | } | 2005 | } |
1974 | 2006 | ||
1975 | chunk = min_t(unsigned int, skb->len, size); | 2007 | chunk = min_t(unsigned int, skb->len - skip, size); |
1976 | if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { | 2008 | if (memcpy_toiovec(msg->msg_iov, skb->data + skip, chunk)) { |
1977 | if (copied == 0) | 2009 | if (copied == 0) |
1978 | copied = -EFAULT; | 2010 | copied = -EFAULT; |
1979 | break; | 2011 | break; |
@@ -1985,6 +2017,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1985 | if (!(flags & MSG_PEEK)) { | 2017 | if (!(flags & MSG_PEEK)) { |
1986 | skb_pull(skb, chunk); | 2018 | skb_pull(skb, chunk); |
1987 | 2019 | ||
2020 | sk_peek_offset_bwd(sk, chunk); | ||
2021 | |||
1988 | if (UNIXCB(skb).fp) | 2022 | if (UNIXCB(skb).fp) |
1989 | unix_detach_fds(siocb->scm, skb); | 2023 | unix_detach_fds(siocb->scm, skb); |
1990 | 2024 | ||
@@ -2002,6 +2036,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
2002 | if (UNIXCB(skb).fp) | 2036 | if (UNIXCB(skb).fp) |
2003 | siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp); | 2037 | siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp); |
2004 | 2038 | ||
2039 | sk_peek_offset_fwd(sk, chunk); | ||
2040 | |||
2005 | break; | 2041 | break; |
2006 | } | 2042 | } |
2007 | } while (size); | 2043 | } while (size); |