2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* INET An implementation of the TCP/IP protocol suite for the LINUX
|
|
|
|
* operating system. INET is implemented using the BSD Socket
|
|
|
|
* interface as the means of communication with the user level.
|
|
|
|
*
|
|
|
|
* The IP to API glue.
|
2007-02-09 21:24:47 +07:00
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Authors: see ip.c
|
|
|
|
*
|
|
|
|
* Fixes:
|
|
|
|
* Many : Split from ip.c , see ip.c for history.
|
|
|
|
* Martin Mares : TOS setting fixed.
|
2007-02-09 21:24:47 +07:00
|
|
|
* Alan Cox : Fixed a couple of oopses in Martin's
|
2005-04-17 05:20:36 +07:00
|
|
|
* TOS tweaks.
|
|
|
|
* Mike McLagan : Routing by source
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/ip.h>
|
|
|
|
#include <linux/icmp.h>
|
2005-12-27 11:43:12 +07:00
|
|
|
#include <linux/inetdevice.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/netdevice.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <net/sock.h>
|
|
|
|
#include <net/ip.h>
|
|
|
|
#include <net/icmp.h>
|
2005-12-14 14:26:10 +07:00
|
|
|
#include <net/tcp_states.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/udp.h>
|
|
|
|
#include <linux/igmp.h>
|
|
|
|
#include <linux/netfilter.h>
|
|
|
|
#include <linux/route.h>
|
|
|
|
#include <linux/mroute.h>
|
2011-10-22 11:07:47 +07:00
|
|
|
#include <net/inet_ecn.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <net/route.h>
|
|
|
|
#include <net/xfrm.h>
|
2008-04-27 15:06:07 +07:00
|
|
|
#include <net/compat.h>
|
2015-01-06 04:56:17 +07:00
|
|
|
#include <net/checksum.h>
|
2011-12-10 16:48:31 +07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <net/transp_v6.h>
|
|
|
|
#endif
|
2012-06-28 17:59:11 +07:00
|
|
|
#include <net/ip_fib.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#include <linux/errqueue.h>
|
2016-12-25 02:46:01 +07:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* SOL_IP control messages.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
|
|
|
|
{
|
ipv4: PKTINFO doesnt need dst reference
Le lundi 07 novembre 2011 à 15:33 +0100, Eric Dumazet a écrit :
> At least, in recent kernels we dont change dst->refcnt in forwarding
> patch (usinf NOREF skb->dst)
>
> One particular point is the atomic_inc(dst->refcnt) we have to perform
> when queuing an UDP packet if socket asked PKTINFO stuff (for example a
> typical DNS server has to setup this option)
>
> I have one patch somewhere that stores the information in skb->cb[] and
> avoid the atomic_{inc|dec}(dst->refcnt).
>
OK I found it, I did some extra tests and believe its ready.
[PATCH net-next] ipv4: IP_PKTINFO doesnt need dst reference
When a socket uses IP_PKTINFO notifications, we currently force a dst
reference for each received skb. Reader has to access dst to get needed
information (rt_iif & rt_spec_dst) and must release dst reference.
We also forced a dst reference if skb was put in socket backlog, even
without IP_PKTINFO handling. This happens under stress/load.
We can instead store the needed information in skb->cb[], so that only
softirq handler really access dst, improving cache hit ratios.
This removes two atomic operations per packet, and false sharing as
well.
On a benchmark using a mono threaded receiver (doing only recvmsg()
calls), I can reach 720.000 pps instead of 570.000 pps.
IP_PKTINFO is typically used by DNS servers, and any multihomed aware
UDP application.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2011-11-09 14:24:35 +07:00
|
|
|
struct in_pktinfo info = *PKTINFO_SKB_CB(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-04-21 12:47:35 +07:00
|
|
|
info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
|
|
|
|
{
|
2007-04-21 12:47:35 +07:00
|
|
|
int ttl = ip_hdr(skb)->ttl;
|
2005-04-17 05:20:36 +07:00
|
|
|
put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
|
|
|
|
{
|
2007-04-21 12:47:35 +07:00
|
|
|
put_cmsg(msg, SOL_IP, IP_TOS, 1, &ip_hdr(skb)->tos);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
if (IPCB(skb)->opt.optlen == 0)
|
|
|
|
return;
|
|
|
|
|
2007-04-21 12:47:35 +07:00
|
|
|
put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen,
|
|
|
|
ip_hdr(skb) + 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
unsigned char optbuf[sizeof(struct ip_options) + 40];
|
2012-04-15 08:34:41 +07:00
|
|
|
struct ip_options *opt = (struct ip_options *)optbuf;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (IPCB(skb)->opt.optlen == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ip_options_echo(opt, skb)) {
|
|
|
|
msg->msg_flags |= MSG_CTRUNC;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ip_options_undo(opt);
|
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
|
|
|
|
}
|
|
|
|
|
2016-11-02 22:02:16 +07:00
|
|
|
static void ip_cmsg_recv_fragsize(struct msghdr *msg, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
|
|
|
if (IPCB(skb)->frag_max_size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
val = IPCB(skb)->frag_max_size;
|
|
|
|
put_cmsg(msg, SOL_IP, IP_RECVFRAGSIZE, sizeof(val), &val);
|
|
|
|
}
|
|
|
|
|
2015-01-06 04:56:17 +07:00
|
|
|
static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
|
2016-10-24 08:03:06 +07:00
|
|
|
int tlen, int offset)
|
2015-01-06 04:56:17 +07:00
|
|
|
{
|
|
|
|
__wsum csum = skb->csum;
|
|
|
|
|
|
|
|
if (skb->ip_summed != CHECKSUM_COMPLETE)
|
|
|
|
return;
|
|
|
|
|
2017-02-21 15:33:18 +07:00
|
|
|
if (offset != 0) {
|
|
|
|
int tend_off = skb_transport_offset(skb) + tlen;
|
|
|
|
csum = csum_sub(csum, skb_checksum(skb, tend_off, offset, 0));
|
|
|
|
}
|
2015-01-06 04:56:17 +07:00
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
|
|
|
|
}
|
|
|
|
|
[SECURITY]: TCP/UDP getpeersec
This patch implements an application of the LSM-IPSec networking
controls whereby an application can determine the label of the
security association its TCP or UDP sockets are currently connected to
via getsockopt and the auxiliary data mechanism of recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of an IPSec security association a particular TCP or
UDP socket is using. The application can then use this security
context to determine the security context for processing on behalf of
the peer at the other end of this connection. In the case of UDP, the
security context is for each individual packet. An example
application is the inetd daemon, which could be modified to start
daemons running at security contexts dependent on the remote client.
Patch design approach:
- Design for TCP
The patch enables the SELinux LSM to set the peer security context for
a socket based on the security context of the IPSec security
association. The application may retrieve this context using
getsockopt. When called, the kernel determines if the socket is a
connected (TCP_ESTABLISHED) TCP socket and, if so, uses the dst_entry
cache on the socket to retrieve the security associations. If a
security association has a security context, the context string is
returned, as for UNIX domain sockets.
- Design for UDP
Unlike TCP, UDP is connectionless. This requires a somewhat different
API to retrieve the peer security context. With TCP, the peer
security context stays the same throughout the connection, thus it can
be retrieved at any time between when the connection is established
and when it is torn down. With UDP, each read/write can have
different peer and thus the security context might change every time.
As a result the security context retrieval must be done TOGETHER with
the packet retrieval.
The solution is to build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message).
Patch implementation details:
- Implementation for TCP
The security context can be retrieved by applications using getsockopt
with the existing SO_PEERSEC flag. As an example (ignoring error
checking):
getsockopt(sockfd, SOL_SOCKET, SO_PEERSEC, optbuf, &optlen);
printf("Socket peer context is: %s\n", optbuf);
The SELinux function, selinux_socket_getpeersec, is extended to check
for labeled security associations for connected (TCP_ESTABLISHED ==
sk->sk_state) TCP sockets only. If so, the socket has a dst_cache of
struct dst_entry values that may refer to security associations. If
these have security associations with security contexts, the security
context is returned.
getsockopt returns a buffer that contains a security context string or
the buffer is unmodified.
- Implementation for UDP
To retrieve the security context, the application first indicates to
the kernel such desire by setting the IP_PASSSEC option via
getsockopt. Then the application retrieves the security context using
the auxiliary data mechanism.
An example server application for UDP should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_IP, IP_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_IP &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
ip_setsockopt is enhanced with a new socket option IP_PASSSEC to allow
a server socket to receive security context of the peer. A new
ancillary message type SCM_SECURITY.
When the packet is received we get the security context from the
sec_path pointer which is contained in the sk_buff, and copy it to the
ancillary message space. An additional LSM hook,
selinux_socket_getpeersec_udp, is defined to retrieve the security
context from the SELinux space. The existing function,
selinux_socket_getpeersec does not suit our purpose, because the
security context is copied directly to user space, rather than to
kernel space.
Testing:
We have tested the patch by setting up TCP and UDP connections between
applications on two machines using the IPSec policies that result in
labeled security associations being built. For TCP, we can then
extract the peer security context using getsockopt on either end. For
UDP, the receiving end can retrieve the security context using the
auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 13:41:23 +07:00
|
|
|
static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
char *secdata;
|
2006-08-03 04:12:06 +07:00
|
|
|
u32 seclen, secid;
|
[SECURITY]: TCP/UDP getpeersec
This patch implements an application of the LSM-IPSec networking
controls whereby an application can determine the label of the
security association its TCP or UDP sockets are currently connected to
via getsockopt and the auxiliary data mechanism of recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of an IPSec security association a particular TCP or
UDP socket is using. The application can then use this security
context to determine the security context for processing on behalf of
the peer at the other end of this connection. In the case of UDP, the
security context is for each individual packet. An example
application is the inetd daemon, which could be modified to start
daemons running at security contexts dependent on the remote client.
Patch design approach:
- Design for TCP
The patch enables the SELinux LSM to set the peer security context for
a socket based on the security context of the IPSec security
association. The application may retrieve this context using
getsockopt. When called, the kernel determines if the socket is a
connected (TCP_ESTABLISHED) TCP socket and, if so, uses the dst_entry
cache on the socket to retrieve the security associations. If a
security association has a security context, the context string is
returned, as for UNIX domain sockets.
- Design for UDP
Unlike TCP, UDP is connectionless. This requires a somewhat different
API to retrieve the peer security context. With TCP, the peer
security context stays the same throughout the connection, thus it can
be retrieved at any time between when the connection is established
and when it is torn down. With UDP, each read/write can have
different peer and thus the security context might change every time.
As a result the security context retrieval must be done TOGETHER with
the packet retrieval.
The solution is to build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message).
Patch implementation details:
- Implementation for TCP
The security context can be retrieved by applications using getsockopt
with the existing SO_PEERSEC flag. As an example (ignoring error
checking):
getsockopt(sockfd, SOL_SOCKET, SO_PEERSEC, optbuf, &optlen);
printf("Socket peer context is: %s\n", optbuf);
The SELinux function, selinux_socket_getpeersec, is extended to check
for labeled security associations for connected (TCP_ESTABLISHED ==
sk->sk_state) TCP sockets only. If so, the socket has a dst_cache of
struct dst_entry values that may refer to security associations. If
these have security associations with security contexts, the security
context is returned.
getsockopt returns a buffer that contains a security context string or
the buffer is unmodified.
- Implementation for UDP
To retrieve the security context, the application first indicates to
the kernel such desire by setting the IP_PASSSEC option via
getsockopt. Then the application retrieves the security context using
the auxiliary data mechanism.
An example server application for UDP should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_IP, IP_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_IP &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
ip_setsockopt is enhanced with a new socket option IP_PASSSEC to allow
a server socket to receive security context of the peer. A new
ancillary message type SCM_SECURITY.
When the packet is received we get the security context from the
sec_path pointer which is contained in the sk_buff, and copy it to the
ancillary message space. An additional LSM hook,
selinux_socket_getpeersec_udp, is defined to retrieve the security
context from the SELinux space. The existing function,
selinux_socket_getpeersec does not suit our purpose, because the
security context is copied directly to user space, rather than to
kernel space.
Testing:
We have tested the patch by setting up TCP and UDP connections between
applications on two machines using the IPSec policies that result in
labeled security associations being built. For TCP, we can then
extract the peer security context using getsockopt on either end. For
UDP, the receiving end can retrieve the security context using the
auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 13:41:23 +07:00
|
|
|
int err;
|
|
|
|
|
2006-08-03 04:12:06 +07:00
|
|
|
err = security_socket_getpeersec_dgram(NULL, skb, &secid);
|
|
|
|
if (err)
|
|
|
|
return;
|
|
|
|
|
|
|
|
err = security_secid_to_secctx(secid, &secdata, &seclen);
|
[SECURITY]: TCP/UDP getpeersec
This patch implements an application of the LSM-IPSec networking
controls whereby an application can determine the label of the
security association its TCP or UDP sockets are currently connected to
via getsockopt and the auxiliary data mechanism of recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of an IPSec security association a particular TCP or
UDP socket is using. The application can then use this security
context to determine the security context for processing on behalf of
the peer at the other end of this connection. In the case of UDP, the
security context is for each individual packet. An example
application is the inetd daemon, which could be modified to start
daemons running at security contexts dependent on the remote client.
Patch design approach:
- Design for TCP
The patch enables the SELinux LSM to set the peer security context for
a socket based on the security context of the IPSec security
association. The application may retrieve this context using
getsockopt. When called, the kernel determines if the socket is a
connected (TCP_ESTABLISHED) TCP socket and, if so, uses the dst_entry
cache on the socket to retrieve the security associations. If a
security association has a security context, the context string is
returned, as for UNIX domain sockets.
- Design for UDP
Unlike TCP, UDP is connectionless. This requires a somewhat different
API to retrieve the peer security context. With TCP, the peer
security context stays the same throughout the connection, thus it can
be retrieved at any time between when the connection is established
and when it is torn down. With UDP, each read/write can have
different peer and thus the security context might change every time.
As a result the security context retrieval must be done TOGETHER with
the packet retrieval.
The solution is to build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message).
Patch implementation details:
- Implementation for TCP
The security context can be retrieved by applications using getsockopt
with the existing SO_PEERSEC flag. As an example (ignoring error
checking):
getsockopt(sockfd, SOL_SOCKET, SO_PEERSEC, optbuf, &optlen);
printf("Socket peer context is: %s\n", optbuf);
The SELinux function, selinux_socket_getpeersec, is extended to check
for labeled security associations for connected (TCP_ESTABLISHED ==
sk->sk_state) TCP sockets only. If so, the socket has a dst_cache of
struct dst_entry values that may refer to security associations. If
these have security associations with security contexts, the security
context is returned.
getsockopt returns a buffer that contains a security context string or
the buffer is unmodified.
- Implementation for UDP
To retrieve the security context, the application first indicates to
the kernel such desire by setting the IP_PASSSEC option via
getsockopt. Then the application retrieves the security context using
the auxiliary data mechanism.
An example server application for UDP should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_IP, IP_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_IP &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
ip_setsockopt is enhanced with a new socket option IP_PASSSEC to allow
a server socket to receive security context of the peer. A new
ancillary message type SCM_SECURITY.
When the packet is received we get the security context from the
sec_path pointer which is contained in the sk_buff, and copy it to the
ancillary message space. An additional LSM hook,
selinux_socket_getpeersec_udp, is defined to retrieve the security
context from the SELinux space. The existing function,
selinux_socket_getpeersec does not suit our purpose, because the
security context is copied directly to user space, rather than to
kernel space.
Testing:
We have tested the patch by setting up TCP and UDP connections between
applications on two machines using the IPSec policies that result in
labeled security associations being built. For TCP, we can then
extract the peer security context using getsockopt on either end. For
UDP, the receiving end can retrieve the security context using the
auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 13:41:23 +07:00
|
|
|
if (err)
|
|
|
|
return;
|
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
|
2006-08-03 04:12:06 +07:00
|
|
|
security_release_secctx(secdata, seclen);
|
[SECURITY]: TCP/UDP getpeersec
This patch implements an application of the LSM-IPSec networking
controls whereby an application can determine the label of the
security association its TCP or UDP sockets are currently connected to
via getsockopt and the auxiliary data mechanism of recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of an IPSec security association a particular TCP or
UDP socket is using. The application can then use this security
context to determine the security context for processing on behalf of
the peer at the other end of this connection. In the case of UDP, the
security context is for each individual packet. An example
application is the inetd daemon, which could be modified to start
daemons running at security contexts dependent on the remote client.
Patch design approach:
- Design for TCP
The patch enables the SELinux LSM to set the peer security context for
a socket based on the security context of the IPSec security
association. The application may retrieve this context using
getsockopt. When called, the kernel determines if the socket is a
connected (TCP_ESTABLISHED) TCP socket and, if so, uses the dst_entry
cache on the socket to retrieve the security associations. If a
security association has a security context, the context string is
returned, as for UNIX domain sockets.
- Design for UDP
Unlike TCP, UDP is connectionless. This requires a somewhat different
API to retrieve the peer security context. With TCP, the peer
security context stays the same throughout the connection, thus it can
be retrieved at any time between when the connection is established
and when it is torn down. With UDP, each read/write can have
different peer and thus the security context might change every time.
As a result the security context retrieval must be done TOGETHER with
the packet retrieval.
The solution is to build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message).
Patch implementation details:
- Implementation for TCP
The security context can be retrieved by applications using getsockopt
with the existing SO_PEERSEC flag. As an example (ignoring error
checking):
getsockopt(sockfd, SOL_SOCKET, SO_PEERSEC, optbuf, &optlen);
printf("Socket peer context is: %s\n", optbuf);
The SELinux function, selinux_socket_getpeersec, is extended to check
for labeled security associations for connected (TCP_ESTABLISHED ==
sk->sk_state) TCP sockets only. If so, the socket has a dst_cache of
struct dst_entry values that may refer to security associations. If
these have security associations with security contexts, the security
context is returned.
getsockopt returns a buffer that contains a security context string or
the buffer is unmodified.
- Implementation for UDP
To retrieve the security context, the application first indicates to
the kernel such desire by setting the IP_PASSSEC option via
getsockopt. Then the application retrieves the security context using
the auxiliary data mechanism.
An example server application for UDP should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_IP, IP_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_IP &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
ip_setsockopt is enhanced with a new socket option IP_PASSSEC to allow
a server socket to receive security context of the peer. A new
ancillary message type SCM_SECURITY.
When the packet is received we get the security context from the
sec_path pointer which is contained in the sk_buff, and copy it to the
ancillary message space. An additional LSM hook,
selinux_socket_getpeersec_udp, is defined to retrieve the security
context from the SELinux space. The existing function,
selinux_socket_getpeersec does not suit our purpose, because the
security context is copied directly to user space, rather than to
kernel space.
Testing:
We have tested the patch by setting up TCP and UDP connections between
applications on two machines using the IPSec policies that result in
labeled security associations being built. For TCP, we can then
extract the peer security context using getsockopt on either end. For
UDP, the receiving end can retrieve the security context using the
auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 13:41:23 +07:00
|
|
|
}
|
|
|
|
|
2008-11-20 16:54:27 +07:00
|
|
|
static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
|
2008-11-17 10:32:39 +07:00
|
|
|
{
|
|
|
|
struct sockaddr_in sin;
|
2011-04-22 11:53:02 +07:00
|
|
|
const struct iphdr *iph = ip_hdr(skb);
|
2008-11-20 16:54:27 +07:00
|
|
|
__be16 *ports = (__be16 *)skb_transport_header(skb);
|
2008-11-17 10:32:39 +07:00
|
|
|
|
2016-12-23 06:19:16 +07:00
|
|
|
if (skb_transport_offset(skb) + 4 > (int)skb->len)
|
2008-11-17 10:32:39 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* All current transport protocols have the port numbers in the
|
|
|
|
* first four bytes of the transport header and this function is
|
|
|
|
* written with this assumption in mind.
|
|
|
|
*/
|
|
|
|
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
sin.sin_addr.s_addr = iph->daddr;
|
|
|
|
sin.sin_port = ports[1];
|
|
|
|
memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
|
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2016-11-04 17:28:58 +07:00
|
|
|
void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk,
|
|
|
|
struct sk_buff *skb, int tlen, int offset)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2016-11-04 17:28:58 +07:00
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
2012-04-15 12:58:06 +07:00
|
|
|
unsigned int flags = inet->cmsg_flags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Ordered by supposed usage frequency */
|
2015-01-06 04:56:15 +07:00
|
|
|
if (flags & IP_CMSG_PKTINFO) {
|
2005-04-17 05:20:36 +07:00
|
|
|
ip_cmsg_recv_pktinfo(msg, skb);
|
|
|
|
|
2015-01-06 04:56:15 +07:00
|
|
|
flags &= ~IP_CMSG_PKTINFO;
|
|
|
|
if (!flags)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & IP_CMSG_TTL) {
|
2005-04-17 05:20:36 +07:00
|
|
|
ip_cmsg_recv_ttl(msg, skb);
|
|
|
|
|
2015-01-06 04:56:15 +07:00
|
|
|
flags &= ~IP_CMSG_TTL;
|
|
|
|
if (!flags)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & IP_CMSG_TOS) {
|
2005-04-17 05:20:36 +07:00
|
|
|
ip_cmsg_recv_tos(msg, skb);
|
|
|
|
|
2015-01-06 04:56:15 +07:00
|
|
|
flags &= ~IP_CMSG_TOS;
|
|
|
|
if (!flags)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & IP_CMSG_RECVOPTS) {
|
2005-04-17 05:20:36 +07:00
|
|
|
ip_cmsg_recv_opts(msg, skb);
|
|
|
|
|
2015-01-06 04:56:15 +07:00
|
|
|
flags &= ~IP_CMSG_RECVOPTS;
|
|
|
|
if (!flags)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & IP_CMSG_RETOPTS) {
|
2005-04-17 05:20:36 +07:00
|
|
|
ip_cmsg_recv_retopts(msg, skb);
|
[SECURITY]: TCP/UDP getpeersec
This patch implements an application of the LSM-IPSec networking
controls whereby an application can determine the label of the
security association its TCP or UDP sockets are currently connected to
via getsockopt and the auxiliary data mechanism of recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of an IPSec security association a particular TCP or
UDP socket is using. The application can then use this security
context to determine the security context for processing on behalf of
the peer at the other end of this connection. In the case of UDP, the
security context is for each individual packet. An example
application is the inetd daemon, which could be modified to start
daemons running at security contexts dependent on the remote client.
Patch design approach:
- Design for TCP
The patch enables the SELinux LSM to set the peer security context for
a socket based on the security context of the IPSec security
association. The application may retrieve this context using
getsockopt. When called, the kernel determines if the socket is a
connected (TCP_ESTABLISHED) TCP socket and, if so, uses the dst_entry
cache on the socket to retrieve the security associations. If a
security association has a security context, the context string is
returned, as for UNIX domain sockets.
- Design for UDP
Unlike TCP, UDP is connectionless. This requires a somewhat different
API to retrieve the peer security context. With TCP, the peer
security context stays the same throughout the connection, thus it can
be retrieved at any time between when the connection is established
and when it is torn down. With UDP, each read/write can have
different peer and thus the security context might change every time.
As a result the security context retrieval must be done TOGETHER with
the packet retrieval.
The solution is to build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message).
Patch implementation details:
- Implementation for TCP
The security context can be retrieved by applications using getsockopt
with the existing SO_PEERSEC flag. As an example (ignoring error
checking):
getsockopt(sockfd, SOL_SOCKET, SO_PEERSEC, optbuf, &optlen);
printf("Socket peer context is: %s\n", optbuf);
The SELinux function, selinux_socket_getpeersec, is extended to check
for labeled security associations for connected (TCP_ESTABLISHED ==
sk->sk_state) TCP sockets only. If so, the socket has a dst_cache of
struct dst_entry values that may refer to security associations. If
these have security associations with security contexts, the security
context is returned.
getsockopt returns a buffer that contains a security context string or
the buffer is unmodified.
- Implementation for UDP
To retrieve the security context, the application first indicates to
the kernel such desire by setting the IP_PASSSEC option via
getsockopt. Then the application retrieves the security context using
the auxiliary data mechanism.
An example server application for UDP should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_IP, IP_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_IP &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
ip_setsockopt is enhanced with a new socket option IP_PASSSEC to allow
a server socket to receive security context of the peer. A new
ancillary message type SCM_SECURITY.
When the packet is received we get the security context from the
sec_path pointer which is contained in the sk_buff, and copy it to the
ancillary message space. An additional LSM hook,
selinux_socket_getpeersec_udp, is defined to retrieve the security
context from the SELinux space. The existing function,
selinux_socket_getpeersec does not suit our purpose, because the
security context is copied directly to user space, rather than to
kernel space.
Testing:
We have tested the patch by setting up TCP and UDP connections between
applications on two machines using the IPSec policies that result in
labeled security associations being built. For TCP, we can then
extract the peer security context using getsockopt on either end. For
UDP, the receiving end can retrieve the security context using the
auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 13:41:23 +07:00
|
|
|
|
2015-01-06 04:56:15 +07:00
|
|
|
flags &= ~IP_CMSG_RETOPTS;
|
|
|
|
if (!flags)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & IP_CMSG_PASSSEC) {
|
[SECURITY]: TCP/UDP getpeersec
This patch implements an application of the LSM-IPSec networking
controls whereby an application can determine the label of the
security association its TCP or UDP sockets are currently connected to
via getsockopt and the auxiliary data mechanism of recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of an IPSec security association a particular TCP or
UDP socket is using. The application can then use this security
context to determine the security context for processing on behalf of
the peer at the other end of this connection. In the case of UDP, the
security context is for each individual packet. An example
application is the inetd daemon, which could be modified to start
daemons running at security contexts dependent on the remote client.
Patch design approach:
- Design for TCP
The patch enables the SELinux LSM to set the peer security context for
a socket based on the security context of the IPSec security
association. The application may retrieve this context using
getsockopt. When called, the kernel determines if the socket is a
connected (TCP_ESTABLISHED) TCP socket and, if so, uses the dst_entry
cache on the socket to retrieve the security associations. If a
security association has a security context, the context string is
returned, as for UNIX domain sockets.
- Design for UDP
Unlike TCP, UDP is connectionless. This requires a somewhat different
API to retrieve the peer security context. With TCP, the peer
security context stays the same throughout the connection, thus it can
be retrieved at any time between when the connection is established
and when it is torn down. With UDP, each read/write can have
different peer and thus the security context might change every time.
As a result the security context retrieval must be done TOGETHER with
the packet retrieval.
The solution is to build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message).
Patch implementation details:
- Implementation for TCP
The security context can be retrieved by applications using getsockopt
with the existing SO_PEERSEC flag. As an example (ignoring error
checking):
getsockopt(sockfd, SOL_SOCKET, SO_PEERSEC, optbuf, &optlen);
printf("Socket peer context is: %s\n", optbuf);
The SELinux function, selinux_socket_getpeersec, is extended to check
for labeled security associations for connected (TCP_ESTABLISHED ==
sk->sk_state) TCP sockets only. If so, the socket has a dst_cache of
struct dst_entry values that may refer to security associations. If
these have security associations with security contexts, the security
context is returned.
getsockopt returns a buffer that contains a security context string or
the buffer is unmodified.
- Implementation for UDP
To retrieve the security context, the application first indicates to
the kernel such desire by setting the IP_PASSSEC option via
getsockopt. Then the application retrieves the security context using
the auxiliary data mechanism.
An example server application for UDP should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_IP, IP_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_IP &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
ip_setsockopt is enhanced with a new socket option IP_PASSSEC to allow
a server socket to receive security context of the peer. A new
ancillary message type SCM_SECURITY.
When the packet is received we get the security context from the
sec_path pointer which is contained in the sk_buff, and copy it to the
ancillary message space. An additional LSM hook,
selinux_socket_getpeersec_udp, is defined to retrieve the security
context from the SELinux space. The existing function,
selinux_socket_getpeersec does not suit our purpose, because the
security context is copied directly to user space, rather than to
kernel space.
Testing:
We have tested the patch by setting up TCP and UDP connections between
applications on two machines using the IPSec policies that result in
labeled security associations being built. For TCP, we can then
extract the peer security context using getsockopt on either end. For
UDP, the receiving end can retrieve the security context using the
auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 13:41:23 +07:00
|
|
|
ip_cmsg_recv_security(msg, skb);
|
2008-11-17 10:32:39 +07:00
|
|
|
|
2015-01-06 04:56:15 +07:00
|
|
|
flags &= ~IP_CMSG_PASSSEC;
|
|
|
|
if (!flags)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-06 04:56:17 +07:00
|
|
|
if (flags & IP_CMSG_ORIGDSTADDR) {
|
2008-11-17 10:32:39 +07:00
|
|
|
ip_cmsg_recv_dstaddr(msg, skb);
|
|
|
|
|
2015-01-06 04:56:17 +07:00
|
|
|
flags &= ~IP_CMSG_ORIGDSTADDR;
|
|
|
|
if (!flags)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & IP_CMSG_CHECKSUM)
|
2016-10-24 08:03:06 +07:00
|
|
|
ip_cmsg_recv_checksum(msg, skb, tlen, offset);
|
2016-11-02 22:02:16 +07:00
|
|
|
|
|
|
|
if (flags & IP_CMSG_RECVFRAGSIZE)
|
|
|
|
ip_cmsg_recv_fragsize(msg, skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2015-01-06 04:56:16 +07:00
|
|
|
EXPORT_SYMBOL(ip_cmsg_recv_offset);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2016-04-03 10:08:10 +07:00
|
|
|
int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
|
2014-02-19 03:38:08 +07:00
|
|
|
bool allow_ipv6)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2013-09-24 20:43:08 +07:00
|
|
|
int err, val;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct cmsghdr *cmsg;
|
2016-04-03 10:08:10 +07:00
|
|
|
struct net *net = sock_net(sk);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-12-11 10:22:04 +07:00
|
|
|
for_each_cmsghdr(cmsg, msg) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!CMSG_OK(msg, cmsg))
|
|
|
|
return -EINVAL;
|
2014-11-11 08:54:25 +07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
2014-02-19 03:38:08 +07:00
|
|
|
if (allow_ipv6 &&
|
|
|
|
cmsg->cmsg_level == SOL_IPV6 &&
|
|
|
|
cmsg->cmsg_type == IPV6_PKTINFO) {
|
|
|
|
struct in6_pktinfo *src_info;
|
|
|
|
|
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
|
|
|
|
return -EINVAL;
|
|
|
|
src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
|
|
|
|
if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
|
|
|
|
return -EINVAL;
|
|
|
|
ipc->oif = src_info->ipi6_ifindex;
|
|
|
|
ipc->addr = src_info->ipi6_addr.s6_addr32[3];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
2016-04-03 10:08:10 +07:00
|
|
|
if (cmsg->cmsg_level == SOL_SOCKET) {
|
2016-05-13 20:14:37 +07:00
|
|
|
err = __sock_cmsg_send(sk, msg, cmsg, &ipc->sockc);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2016-04-03 10:08:10 +07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (cmsg->cmsg_level != SOL_IP)
|
|
|
|
continue;
|
|
|
|
switch (cmsg->cmsg_type) {
|
|
|
|
case IP_RETOPTS:
|
2017-01-03 19:42:17 +07:00
|
|
|
err = cmsg->cmsg_len - sizeof(struct cmsghdr);
|
2016-02-04 21:23:28 +07:00
|
|
|
|
|
|
|
/* Our caller is responsible for freeing ipc->opt */
|
2009-06-02 14:42:16 +07:00
|
|
|
err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg),
|
|
|
|
err < 40 ? err : 40);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
case IP_PKTINFO:
|
|
|
|
{
|
|
|
|
struct in_pktinfo *info;
|
|
|
|
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
|
|
|
|
return -EINVAL;
|
|
|
|
info = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
|
|
|
ipc->oif = info->ipi_ifindex;
|
|
|
|
ipc->addr = info->ipi_spec_dst.s_addr;
|
|
|
|
break;
|
|
|
|
}
|
2013-09-24 20:43:08 +07:00
|
|
|
case IP_TTL:
|
|
|
|
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
|
|
|
|
return -EINVAL;
|
|
|
|
val = *(int *)CMSG_DATA(cmsg);
|
|
|
|
if (val < 1 || val > 255)
|
|
|
|
return -EINVAL;
|
|
|
|
ipc->ttl = val;
|
|
|
|
break;
|
|
|
|
case IP_TOS:
|
2016-09-08 11:52:56 +07:00
|
|
|
if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
|
|
|
|
val = *(int *)CMSG_DATA(cmsg);
|
|
|
|
else if (cmsg->cmsg_len == CMSG_LEN(sizeof(u8)))
|
|
|
|
val = *(u8 *)CMSG_DATA(cmsg);
|
|
|
|
else
|
2013-09-24 20:43:08 +07:00
|
|
|
return -EINVAL;
|
|
|
|
if (val < 0 || val > 255)
|
|
|
|
return -EINVAL;
|
|
|
|
ipc->tos = val;
|
|
|
|
ipc->priority = rt_tos2priority(ipc->tos);
|
|
|
|
break;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Special input handler for packets caught by router alert option.
|
|
|
|
They are selected only by protocol field, and then processed likely
|
|
|
|
local ones; but only if someone wants them! Otherwise, router
|
|
|
|
not running rsvpd will kill RSVP.
|
|
|
|
|
|
|
|
It is user level problem, what it will make with them.
|
|
|
|
I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
|
|
|
|
but receiver should be enough clever f.e. to forward mtrace requests,
|
|
|
|
sent to multicast group to reach destination designated router.
|
|
|
|
*/
|
2010-10-25 10:32:44 +07:00
|
|
|
struct ip_ra_chain __rcu *ip_ra_chain;
|
2010-06-07 10:12:08 +07:00
|
|
|
|
2010-06-09 23:21:07 +07:00
|
|
|
|
|
|
|
static void ip_ra_destroy_rcu(struct rcu_head *head)
|
2010-06-07 10:12:08 +07:00
|
|
|
{
|
2010-06-09 23:21:07 +07:00
|
|
|
struct ip_ra_chain *ra = container_of(head, struct ip_ra_chain, rcu);
|
|
|
|
|
|
|
|
sock_put(ra->saved_sk);
|
|
|
|
kfree(ra);
|
2010-06-07 10:12:08 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-06-02 14:42:16 +07:00
|
|
|
int ip_ra_control(struct sock *sk, unsigned char on,
|
|
|
|
void (*destructor)(struct sock *))
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-10-25 10:32:44 +07:00
|
|
|
struct ip_ra_chain *ra, *new_ra;
|
|
|
|
struct ip_ra_chain __rcu **rap;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-10-15 13:30:45 +07:00
|
|
|
if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
|
|
|
|
|
2010-10-25 10:32:44 +07:00
|
|
|
for (rap = &ip_ra_chain;
|
2017-04-29 00:04:29 +07:00
|
|
|
(ra = rtnl_dereference(*rap)) != NULL;
|
2010-10-25 10:32:44 +07:00
|
|
|
rap = &ra->next) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ra->sk == sk) {
|
|
|
|
if (on) {
|
2005-11-09 00:41:34 +07:00
|
|
|
kfree(new_ra);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EADDRINUSE;
|
|
|
|
}
|
2010-06-09 23:21:07 +07:00
|
|
|
/* dont let ip_call_ra_chain() use sk again */
|
|
|
|
ra->sk = NULL;
|
2014-09-09 22:11:41 +07:00
|
|
|
RCU_INIT_POINTER(*rap, ra->next);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (ra->destructor)
|
|
|
|
ra->destructor(sk);
|
2010-06-09 23:21:07 +07:00
|
|
|
/*
|
|
|
|
* Delay sock_put(sk) and kfree(ra) after one rcu grace
|
|
|
|
* period. This guarantee ip_call_ra_chain() dont need
|
|
|
|
* to mess with socket refcounts.
|
|
|
|
*/
|
|
|
|
ra->saved_sk = sk;
|
|
|
|
call_rcu(&ra->rcu, ip_ra_destroy_rcu);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2017-04-29 00:04:29 +07:00
|
|
|
if (!new_ra)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENOBUFS;
|
|
|
|
new_ra->sk = sk;
|
|
|
|
new_ra->destructor = destructor;
|
|
|
|
|
2014-09-09 22:11:41 +07:00
|
|
|
RCU_INIT_POINTER(new_ra->next, ra);
|
2010-06-07 10:12:08 +07:00
|
|
|
rcu_assign_pointer(*rap, new_ra);
|
2005-04-17 05:20:36 +07:00
|
|
|
sock_hold(sk);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-09 21:24:47 +07:00
|
|
|
void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
|
2006-09-28 08:34:21 +07:00
|
|
|
__be16 port, u32 info, u8 *payload)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct sock_exterr_skb *serr;
|
|
|
|
|
|
|
|
skb = skb_clone(skb, GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2007-02-09 21:24:47 +07:00
|
|
|
serr = SKB_EXT_ERR(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
serr->ee.ee_errno = err;
|
|
|
|
serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
|
2007-03-14 00:43:18 +07:00
|
|
|
serr->ee.ee_type = icmp_hdr(skb)->type;
|
|
|
|
serr->ee.ee_code = icmp_hdr(skb)->code;
|
2005-04-17 05:20:36 +07:00
|
|
|
serr->ee.ee_pad = 0;
|
|
|
|
serr->ee.ee_info = info;
|
|
|
|
serr->ee.ee_data = 0;
|
2007-03-14 00:43:18 +07:00
|
|
|
serr->addr_offset = (u8 *)&(((struct iphdr *)(icmp_hdr(skb) + 1))->daddr) -
|
2007-04-11 10:50:43 +07:00
|
|
|
skb_network_header(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
serr->port = port;
|
|
|
|
|
2015-04-03 15:17:27 +07:00
|
|
|
if (skb_pull(skb, payload - skb->data)) {
|
2007-03-14 03:10:43 +07:00
|
|
|
skb_reset_transport_header(skb);
|
|
|
|
if (sock_queue_err_skb(sk, skb) == 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
kfree_skb(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-09-28 08:33:40 +07:00
|
|
|
void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
|
|
struct sock_exterr_skb *serr;
|
|
|
|
struct iphdr *iph;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
if (!inet->recverr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2007-03-11 05:15:25 +07:00
|
|
|
skb_put(skb, sizeof(struct iphdr));
|
|
|
|
skb_reset_network_header(skb);
|
2007-04-21 12:47:35 +07:00
|
|
|
iph = ip_hdr(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
iph->daddr = daddr;
|
|
|
|
|
2007-02-09 21:24:47 +07:00
|
|
|
serr = SKB_EXT_ERR(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
serr->ee.ee_errno = err;
|
|
|
|
serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
|
2007-02-09 21:24:47 +07:00
|
|
|
serr->ee.ee_type = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
serr->ee.ee_code = 0;
|
|
|
|
serr->ee.ee_pad = 0;
|
|
|
|
serr->ee.ee_info = info;
|
|
|
|
serr->ee.ee_data = 0;
|
2007-04-11 10:50:43 +07:00
|
|
|
serr->addr_offset = (u8 *)&iph->daddr - skb_network_header(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
serr->port = port;
|
|
|
|
|
2007-04-20 10:29:13 +07:00
|
|
|
__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
|
2007-03-14 03:10:43 +07:00
|
|
|
skb_reset_transport_header(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (sock_queue_err_skb(sk, skb))
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2015-06-23 12:34:39 +07:00
|
|
|
/* For some errors we have valid addr_offset even with zero payload and
|
|
|
|
* zero port. Also, addr_offset should be supported if port is set.
|
|
|
|
*/
|
|
|
|
static inline bool ipv4_datagram_support_addr(struct sock_exterr_skb *serr)
|
|
|
|
{
|
|
|
|
return serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
|
|
|
|
serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL || serr->port;
|
|
|
|
}
|
|
|
|
|
2015-03-08 08:33:22 +07:00
|
|
|
/* IPv4 supports cmsg on all imcp errors and some timestamps
|
|
|
|
*
|
|
|
|
* Timestamp code paths do not initialize the fields expected by cmsg:
|
|
|
|
* the PKTINFO fields in skb->cb[]. Fill those in here.
|
|
|
|
*/
|
|
|
|
static bool ipv4_datagram_support_cmsg(const struct sock *sk,
|
|
|
|
struct sk_buff *skb,
|
|
|
|
int ee_origin)
|
net-timestamp: allow reading recv cmsg on errqueue with origin tstamp
Allow reading of timestamps and cmsg at the same time on all relevant
socket families. One use is to correlate timestamps with egress
device, by asking for cmsg IP_PKTINFO.
on AF_INET sockets, call the relevant function (ip_cmsg_recv). To
avoid changing legacy expectations, only do so if the caller sets a
new timestamping flag SOF_TIMESTAMPING_OPT_CMSG.
on AF_INET6 sockets, IPV6_PKTINFO and all other recv cmsg are already
returned for all origins. only change is to set ifindex, which is
not initialized for all error origins.
In both cases, only generate the pktinfo message if an ifindex is
known. This is not the case for ACK timestamps.
The difference between the protocol families is probably a historical
accident as a result of the different conditions for generating cmsg
in the relevant ip(v6)_recv_error function:
ipv4: if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
ipv6: if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
At one time, this was the same test bar for the ICMP/ICMP6
distinction. This is no longer true.
Signed-off-by: Willem de Bruijn <willemb@google.com>
----
Changes
v1 -> v2
large rewrite
- integrate with existing pktinfo cmsg generation code
- on ipv4: only send with new flag, to maintain legacy behavior
- on ipv6: send at most a single pktinfo cmsg
- on ipv6: initialize fields if not yet initialized
The recv cmsg interfaces are also relevant to the discussion of
whether looping packet headers is problematic. For v6, cmsgs that
identify many headers are already returned. This patch expands
that to v4. If it sounds reasonable, I will follow with patches
1. request timestamps without payload with SOF_TIMESTAMPING_OPT_TSONLY
(http://patchwork.ozlabs.org/patch/366967/)
2. sysctl to conditionally drop all timestamps that have payload or
cmsg from users without CAP_NET_RAW.
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-12-01 10:22:34 +07:00
|
|
|
{
|
2015-03-08 08:33:22 +07:00
|
|
|
struct in_pktinfo *info;
|
|
|
|
|
|
|
|
if (ee_origin == SO_EE_ORIGIN_ICMP)
|
|
|
|
return true;
|
net-timestamp: allow reading recv cmsg on errqueue with origin tstamp
Allow reading of timestamps and cmsg at the same time on all relevant
socket families. One use is to correlate timestamps with egress
device, by asking for cmsg IP_PKTINFO.
on AF_INET sockets, call the relevant function (ip_cmsg_recv). To
avoid changing legacy expectations, only do so if the caller sets a
new timestamping flag SOF_TIMESTAMPING_OPT_CMSG.
on AF_INET6 sockets, IPV6_PKTINFO and all other recv cmsg are already
returned for all origins. only change is to set ifindex, which is
not initialized for all error origins.
In both cases, only generate the pktinfo message if an ifindex is
known. This is not the case for ACK timestamps.
The difference between the protocol families is probably a historical
accident as a result of the different conditions for generating cmsg
in the relevant ip(v6)_recv_error function:
ipv4: if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
ipv6: if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
At one time, this was the same test bar for the ICMP/ICMP6
distinction. This is no longer true.
Signed-off-by: Willem de Bruijn <willemb@google.com>
----
Changes
v1 -> v2
large rewrite
- integrate with existing pktinfo cmsg generation code
- on ipv4: only send with new flag, to maintain legacy behavior
- on ipv6: send at most a single pktinfo cmsg
- on ipv6: initialize fields if not yet initialized
The recv cmsg interfaces are also relevant to the discussion of
whether looping packet headers is problematic. For v6, cmsgs that
identify many headers are already returned. This patch expands
that to v4. If it sounds reasonable, I will follow with patches
1. request timestamps without payload with SOF_TIMESTAMPING_OPT_TSONLY
(http://patchwork.ozlabs.org/patch/366967/)
2. sysctl to conditionally drop all timestamps that have payload or
cmsg from users without CAP_NET_RAW.
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-12-01 10:22:34 +07:00
|
|
|
|
2015-03-08 08:33:22 +07:00
|
|
|
if (ee_origin == SO_EE_ORIGIN_LOCAL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Support IP_PKTINFO on tstamp packets if requested, to correlate
|
2017-04-13 06:24:35 +07:00
|
|
|
* timestamp with egress dev. Not possible for packets without iif
|
2015-03-08 08:33:22 +07:00
|
|
|
* or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
|
|
|
|
*/
|
2017-04-13 06:24:35 +07:00
|
|
|
info = PKTINFO_SKB_CB(skb);
|
|
|
|
if (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG) ||
|
|
|
|
!info->ipi_ifindex)
|
net-timestamp: allow reading recv cmsg on errqueue with origin tstamp
Allow reading of timestamps and cmsg at the same time on all relevant
socket families. One use is to correlate timestamps with egress
device, by asking for cmsg IP_PKTINFO.
on AF_INET sockets, call the relevant function (ip_cmsg_recv). To
avoid changing legacy expectations, only do so if the caller sets a
new timestamping flag SOF_TIMESTAMPING_OPT_CMSG.
on AF_INET6 sockets, IPV6_PKTINFO and all other recv cmsg are already
returned for all origins. only change is to set ifindex, which is
not initialized for all error origins.
In both cases, only generate the pktinfo message if an ifindex is
known. This is not the case for ACK timestamps.
The difference between the protocol families is probably a historical
accident as a result of the different conditions for generating cmsg
in the relevant ip(v6)_recv_error function:
ipv4: if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
ipv6: if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
At one time, this was the same test bar for the ICMP/ICMP6
distinction. This is no longer true.
Signed-off-by: Willem de Bruijn <willemb@google.com>
----
Changes
v1 -> v2
large rewrite
- integrate with existing pktinfo cmsg generation code
- on ipv4: only send with new flag, to maintain legacy behavior
- on ipv6: send at most a single pktinfo cmsg
- on ipv6: initialize fields if not yet initialized
The recv cmsg interfaces are also relevant to the discussion of
whether looping packet headers is problematic. For v6, cmsgs that
identify many headers are already returned. This patch expands
that to v4. If it sounds reasonable, I will follow with patches
1. request timestamps without payload with SOF_TIMESTAMPING_OPT_TSONLY
(http://patchwork.ozlabs.org/patch/366967/)
2. sysctl to conditionally drop all timestamps that have payload or
cmsg from users without CAP_NET_RAW.
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-12-01 10:22:34 +07:00
|
|
|
return false;
|
|
|
|
|
|
|
|
info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-02-09 21:24:47 +07:00
|
|
|
/*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Handle MSG_ERRQUEUE
|
|
|
|
*/
|
2013-11-23 06:46:12 +07:00
|
|
|
int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct sock_exterr_skb *serr;
|
2014-09-01 08:30:27 +07:00
|
|
|
struct sk_buff *skb;
|
2014-01-18 04:53:15 +07:00
|
|
|
DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct {
|
|
|
|
struct sock_extended_err ee;
|
|
|
|
struct sockaddr_in offender;
|
|
|
|
} errhdr;
|
|
|
|
int err;
|
|
|
|
int copied;
|
|
|
|
|
2014-12-01 10:22:33 +07:00
|
|
|
WARN_ON_ONCE(sk->sk_family == AF_INET6);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EAGAIN;
|
2014-09-01 08:30:27 +07:00
|
|
|
skb = sock_dequeue_err_skb(sk);
|
2015-04-03 15:17:26 +07:00
|
|
|
if (!skb)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
copied = skb->len;
|
|
|
|
if (copied > len) {
|
|
|
|
msg->msg_flags |= MSG_TRUNC;
|
|
|
|
copied = len;
|
|
|
|
}
|
2014-11-06 04:46:40 +07:00
|
|
|
err = skb_copy_datagram_msg(skb, 0, msg, copied);
|
2016-04-22 12:27:32 +07:00
|
|
|
if (unlikely(err)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return err;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
sock_recv_timestamp(msg, sk, skb);
|
|
|
|
|
|
|
|
serr = SKB_EXT_ERR(skb);
|
|
|
|
|
2015-06-23 12:34:39 +07:00
|
|
|
if (sin && ipv4_datagram_support_addr(serr)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
sin->sin_family = AF_INET;
|
2007-04-11 10:50:43 +07:00
|
|
|
sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
|
|
|
|
serr->addr_offset);
|
2005-04-17 05:20:36 +07:00
|
|
|
sin->sin_port = serr->port;
|
|
|
|
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
|
2013-11-23 06:46:12 +07:00
|
|
|
*addr_len = sizeof(*sin);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
|
|
|
|
sin = &errhdr.offender;
|
2015-01-16 01:18:40 +07:00
|
|
|
memset(sin, 0, sizeof(*sin));
|
net-timestamp: allow reading recv cmsg on errqueue with origin tstamp
Allow reading of timestamps and cmsg at the same time on all relevant
socket families. One use is to correlate timestamps with egress
device, by asking for cmsg IP_PKTINFO.
on AF_INET sockets, call the relevant function (ip_cmsg_recv). To
avoid changing legacy expectations, only do so if the caller sets a
new timestamping flag SOF_TIMESTAMPING_OPT_CMSG.
on AF_INET6 sockets, IPV6_PKTINFO and all other recv cmsg are already
returned for all origins. only change is to set ifindex, which is
not initialized for all error origins.
In both cases, only generate the pktinfo message if an ifindex is
known. This is not the case for ACK timestamps.
The difference between the protocol families is probably a historical
accident as a result of the different conditions for generating cmsg
in the relevant ip(v6)_recv_error function:
ipv4: if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
ipv6: if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
At one time, this was the same test bar for the ICMP/ICMP6
distinction. This is no longer true.
Signed-off-by: Willem de Bruijn <willemb@google.com>
----
Changes
v1 -> v2
large rewrite
- integrate with existing pktinfo cmsg generation code
- on ipv4: only send with new flag, to maintain legacy behavior
- on ipv6: send at most a single pktinfo cmsg
- on ipv6: initialize fields if not yet initialized
The recv cmsg interfaces are also relevant to the discussion of
whether looping packet headers is problematic. For v6, cmsgs that
identify many headers are already returned. This patch expands
that to v4. If it sounds reasonable, I will follow with patches
1. request timestamps without payload with SOF_TIMESTAMPING_OPT_TSONLY
(http://patchwork.ozlabs.org/patch/366967/)
2. sysctl to conditionally drop all timestamps that have payload or
cmsg from users without CAP_NET_RAW.
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-12-01 10:22:34 +07:00
|
|
|
|
2015-03-08 08:33:22 +07:00
|
|
|
if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
sin->sin_family = AF_INET;
|
2007-04-21 12:47:35 +07:00
|
|
|
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
|
2015-01-16 01:18:40 +07:00
|
|
|
if (inet_sk(sk)->cmsg_flags)
|
2005-04-17 05:20:36 +07:00
|
|
|
ip_cmsg_recv(msg, skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
|
|
|
|
|
|
|
|
/* Now we could try to dump offended packet options */
|
|
|
|
|
|
|
|
msg->msg_flags |= MSG_ERRQUEUE;
|
|
|
|
err = copied;
|
|
|
|
|
2016-04-22 12:27:32 +07:00
|
|
|
consume_skb(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-06-02 14:42:16 +07:00
|
|
|
* Socket option code for IP. This is the end of the line after any
|
|
|
|
* TCP,UDP etc options on an IP socket.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2015-03-19 00:50:42 +07:00
|
|
|
static bool setsockopt_needs_rtnl(int optname)
|
|
|
|
{
|
|
|
|
switch (optname) {
|
|
|
|
case IP_ADD_MEMBERSHIP:
|
|
|
|
case IP_ADD_SOURCE_MEMBERSHIP:
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
case IP_BLOCK_SOURCE:
|
2015-03-19 00:50:42 +07:00
|
|
|
case IP_DROP_MEMBERSHIP:
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
case IP_DROP_SOURCE_MEMBERSHIP:
|
|
|
|
case IP_MSFILTER:
|
|
|
|
case IP_UNBLOCK_SOURCE:
|
|
|
|
case MCAST_BLOCK_SOURCE:
|
|
|
|
case MCAST_MSFILTER:
|
2015-03-19 00:50:42 +07:00
|
|
|
case MCAST_JOIN_GROUP:
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
case MCAST_JOIN_SOURCE_GROUP:
|
2015-03-19 00:50:42 +07:00
|
|
|
case MCAST_LEAVE_GROUP:
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
case MCAST_LEAVE_SOURCE_GROUP:
|
|
|
|
case MCAST_UNBLOCK_SOURCE:
|
2017-04-13 02:32:13 +07:00
|
|
|
case IP_ROUTER_ALERT:
|
2015-03-19 00:50:42 +07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-03-21 13:45:21 +07:00
|
|
|
static int do_ip_setsockopt(struct sock *sk, int level,
|
2009-10-01 06:12:20 +07:00
|
|
|
int optname, char __user *optval, unsigned int optlen)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
2016-02-09 04:29:22 +07:00
|
|
|
struct net *net = sock_net(sk);
|
2008-11-03 15:27:11 +07:00
|
|
|
int val = 0, err;
|
2015-03-19 00:50:42 +07:00
|
|
|
bool needs_rtnl = setsockopt_needs_rtnl(optname);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-11-11 18:20:01 +07:00
|
|
|
switch (optname) {
|
|
|
|
case IP_PKTINFO:
|
|
|
|
case IP_RECVTTL:
|
|
|
|
case IP_RECVOPTS:
|
|
|
|
case IP_RECVTOS:
|
|
|
|
case IP_RETOPTS:
|
|
|
|
case IP_TOS:
|
|
|
|
case IP_TTL:
|
|
|
|
case IP_HDRINCL:
|
|
|
|
case IP_MTU_DISCOVER:
|
|
|
|
case IP_RECVERR:
|
|
|
|
case IP_ROUTER_ALERT:
|
|
|
|
case IP_FREEBIND:
|
|
|
|
case IP_PASSSEC:
|
|
|
|
case IP_TRANSPARENT:
|
|
|
|
case IP_MINTTL:
|
|
|
|
case IP_NODEFRAG:
|
inet: add IP_BIND_ADDRESS_NO_PORT to overcome bind(0) limitations
When an application needs to force a source IP on an active TCP socket
it has to use bind(IP, port=x).
As most applications do not want to deal with already used ports, x is
often set to 0, meaning the kernel is in charge to find an available
port.
But kernel does not know yet if this socket is going to be a listener or
be connected.
It has very limited choices (no full knowledge of final 4-tuple for a
connect())
With limited ephemeral port range (about 32K ports), it is very easy to
fill the space.
This patch adds a new SOL_IP socket option, asking kernel to ignore
the 0 port provided by application in bind(IP, port=0) and only
remember the given IP address.
The port will be automatically chosen at connect() time, in a way
that allows sharing a source port as long as the 4-tuples are unique.
This new feature is available for both IPv4 and IPv6 (Thanks Neal)
Tested:
Wrote a test program and checked its behavior on IPv4 and IPv6.
strace(1) shows sequences of bind(IP=127.0.0.2, port=0) followed by
connect().
Also getsockname() show that the port is still 0 right after bind()
but properly allocated after connect().
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 5
setsockopt(5, SOL_IP, IP_BIND_ADDRESS_NO_PORT, [1], 4) = 0
bind(5, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.2")}, 16) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.2")}, [16]) = 0
connect(5, {sa_family=AF_INET, sin_port=htons(53174), sin_addr=inet_addr("127.0.0.3")}, 16) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(38050), sin_addr=inet_addr("127.0.0.2")}, [16]) = 0
IPv6 test :
socket(PF_INET6, SOCK_STREAM, IPPROTO_IP) = 7
setsockopt(7, SOL_IP, IP_BIND_ADDRESS_NO_PORT, [1], 4) = 0
bind(7, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(7, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
connect(7, {sa_family=AF_INET6, sin6_port=htons(57300), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(7, {sa_family=AF_INET6, sin6_port=htons(60964), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
I was able to bind()/connect() a million concurrent IPv4 sockets,
instead of ~32000 before patch.
lpaa23:~# ulimit -n 1000010
lpaa23:~# ./bind --connect --num-flows=1000000 &
1000000 sockets
lpaa23:~# grep TCP /proc/net/sockstat
TCP: inuse 2000063 orphan 0 tw 47 alloc 2000157 mem 66
Check that a given source port is indeed used by many different
connections :
lpaa23:~# ss -t src :40000 | head -10
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 127.0.0.2:40000 127.0.202.33:44983
ESTAB 0 0 127.0.0.2:40000 127.2.27.240:44983
ESTAB 0 0 127.0.0.2:40000 127.2.98.5:44983
ESTAB 0 0 127.0.0.2:40000 127.0.124.196:44983
ESTAB 0 0 127.0.0.2:40000 127.2.139.38:44983
ESTAB 0 0 127.0.0.2:40000 127.1.59.80:44983
ESTAB 0 0 127.0.0.2:40000 127.3.6.228:44983
ESTAB 0 0 127.0.0.2:40000 127.0.38.53:44983
ESTAB 0 0 127.0.0.2:40000 127.1.197.10:44983
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-06-07 11:17:57 +07:00
|
|
|
case IP_BIND_ADDRESS_NO_PORT:
|
2012-11-11 18:20:01 +07:00
|
|
|
case IP_UNICAST_IF:
|
|
|
|
case IP_MULTICAST_TTL:
|
|
|
|
case IP_MULTICAST_ALL:
|
|
|
|
case IP_MULTICAST_LOOP:
|
|
|
|
case IP_RECVORIGDSTADDR:
|
2015-01-06 04:56:17 +07:00
|
|
|
case IP_CHECKSUM:
|
2016-11-02 22:02:16 +07:00
|
|
|
case IP_RECVFRAGSIZE:
|
2005-04-17 05:20:36 +07:00
|
|
|
if (optlen >= sizeof(int)) {
|
|
|
|
if (get_user(val, (int __user *) optval))
|
|
|
|
return -EFAULT;
|
|
|
|
} else if (optlen >= sizeof(char)) {
|
|
|
|
unsigned char ucval;
|
|
|
|
|
|
|
|
if (get_user(ucval, (unsigned char __user *) optval))
|
|
|
|
return -EFAULT;
|
|
|
|
val = (int) ucval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If optlen==0, it is equivalent to val == 0 */
|
|
|
|
|
2007-11-06 12:32:31 +07:00
|
|
|
if (ip_mroute_opt(optname))
|
2008-11-03 15:27:11 +07:00
|
|
|
return ip_mroute_setsockopt(sk, optname, optval, optlen);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
err = 0;
|
2015-03-19 00:50:42 +07:00
|
|
|
if (needs_rtnl)
|
|
|
|
rtnl_lock();
|
2005-04-17 05:20:36 +07:00
|
|
|
lock_sock(sk);
|
|
|
|
|
|
|
|
switch (optname) {
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_OPTIONS:
|
|
|
|
{
|
2011-04-21 16:45:37 +07:00
|
|
|
struct ip_options_rcu *old, *opt = NULL;
|
|
|
|
|
2009-10-23 12:59:21 +07:00
|
|
|
if (optlen > 40)
|
2007-03-09 11:44:43 +07:00
|
|
|
goto e_inval;
|
2008-03-26 00:26:21 +07:00
|
|
|
err = ip_options_get_from_user(sock_net(sk), &opt,
|
2008-03-25 05:31:00 +07:00
|
|
|
optval, optlen);
|
2007-03-09 11:44:43 +07:00
|
|
|
if (err)
|
|
|
|
break;
|
2011-04-21 16:45:37 +07:00
|
|
|
old = rcu_dereference_protected(inet->inet_opt,
|
2016-04-05 22:10:15 +07:00
|
|
|
lockdep_sock_is_held(sk));
|
2007-03-09 11:44:43 +07:00
|
|
|
if (inet->is_icsk) {
|
|
|
|
struct inet_connection_sock *icsk = inet_csk(sk);
|
2011-12-10 16:48:31 +07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
2007-03-09 11:44:43 +07:00
|
|
|
if (sk->sk_family == PF_INET ||
|
|
|
|
(!((1 << sk->sk_state) &
|
|
|
|
(TCPF_LISTEN | TCPF_CLOSE)) &&
|
2009-10-15 13:30:45 +07:00
|
|
|
inet->inet_daddr != LOOPBACK4_IPV6)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
2011-04-21 16:45:37 +07:00
|
|
|
if (old)
|
|
|
|
icsk->icsk_ext_hdr_len -= old->opt.optlen;
|
2007-03-09 11:44:43 +07:00
|
|
|
if (opt)
|
2011-04-21 16:45:37 +07:00
|
|
|
icsk->icsk_ext_hdr_len += opt->opt.optlen;
|
2007-03-09 11:44:43 +07:00
|
|
|
icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
|
2011-12-10 16:48:31 +07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2011-04-21 16:45:37 +07:00
|
|
|
rcu_assign_pointer(inet->inet_opt, opt);
|
|
|
|
if (old)
|
2012-01-07 08:08:33 +07:00
|
|
|
kfree_rcu(old, rcu);
|
2007-03-09 11:44:43 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IP_PKTINFO:
|
|
|
|
if (val)
|
|
|
|
inet->cmsg_flags |= IP_CMSG_PKTINFO;
|
|
|
|
else
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
|
|
|
|
break;
|
|
|
|
case IP_RECVTTL:
|
|
|
|
if (val)
|
|
|
|
inet->cmsg_flags |= IP_CMSG_TTL;
|
|
|
|
else
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_TTL;
|
|
|
|
break;
|
|
|
|
case IP_RECVTOS:
|
|
|
|
if (val)
|
|
|
|
inet->cmsg_flags |= IP_CMSG_TOS;
|
|
|
|
else
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_TOS;
|
|
|
|
break;
|
|
|
|
case IP_RECVOPTS:
|
|
|
|
if (val)
|
|
|
|
inet->cmsg_flags |= IP_CMSG_RECVOPTS;
|
|
|
|
else
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
|
|
|
|
break;
|
|
|
|
case IP_RETOPTS:
|
|
|
|
if (val)
|
|
|
|
inet->cmsg_flags |= IP_CMSG_RETOPTS;
|
|
|
|
else
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
|
|
|
|
break;
|
|
|
|
case IP_PASSSEC:
|
|
|
|
if (val)
|
|
|
|
inet->cmsg_flags |= IP_CMSG_PASSSEC;
|
|
|
|
else
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
|
|
|
|
break;
|
2008-11-17 10:32:39 +07:00
|
|
|
case IP_RECVORIGDSTADDR:
|
|
|
|
if (val)
|
|
|
|
inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
|
|
|
|
else
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
|
|
|
|
break;
|
2015-01-06 04:56:17 +07:00
|
|
|
case IP_CHECKSUM:
|
|
|
|
if (val) {
|
|
|
|
if (!(inet->cmsg_flags & IP_CMSG_CHECKSUM)) {
|
|
|
|
inet_inc_convert_csum(sk);
|
|
|
|
inet->cmsg_flags |= IP_CMSG_CHECKSUM;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (inet->cmsg_flags & IP_CMSG_CHECKSUM) {
|
|
|
|
inet_dec_convert_csum(sk);
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_CHECKSUM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2016-11-02 22:02:16 +07:00
|
|
|
case IP_RECVFRAGSIZE:
|
|
|
|
if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
|
|
|
|
goto e_inval;
|
|
|
|
if (val)
|
|
|
|
inet->cmsg_flags |= IP_CMSG_RECVFRAGSIZE;
|
|
|
|
else
|
|
|
|
inet->cmsg_flags &= ~IP_CMSG_RECVFRAGSIZE;
|
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_TOS: /* This sets both TOS and Precedence */
|
|
|
|
if (sk->sk_type == SOCK_STREAM) {
|
2011-10-22 11:07:47 +07:00
|
|
|
val &= ~INET_ECN_MASK;
|
|
|
|
val |= inet->tos & INET_ECN_MASK;
|
2007-03-09 11:44:43 +07:00
|
|
|
}
|
|
|
|
if (inet->tos != val) {
|
|
|
|
inet->tos = val;
|
|
|
|
sk->sk_priority = rt_tos2priority(val);
|
|
|
|
sk_dst_reset(sk);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IP_TTL:
|
2009-06-02 14:42:16 +07:00
|
|
|
if (optlen < 1)
|
2007-03-09 11:44:43 +07:00
|
|
|
goto e_inval;
|
2013-01-08 04:17:00 +07:00
|
|
|
if (val != -1 && (val < 1 || val > 255))
|
2007-03-09 11:44:43 +07:00
|
|
|
goto e_inval;
|
|
|
|
inet->uc_ttl = val;
|
|
|
|
break;
|
|
|
|
case IP_HDRINCL:
|
|
|
|
if (sk->sk_type != SOCK_RAW) {
|
|
|
|
err = -ENOPROTOOPT;
|
[SECURITY]: TCP/UDP getpeersec
This patch implements an application of the LSM-IPSec networking
controls whereby an application can determine the label of the
security association its TCP or UDP sockets are currently connected to
via getsockopt and the auxiliary data mechanism of recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of an IPSec security association a particular TCP or
UDP socket is using. The application can then use this security
context to determine the security context for processing on behalf of
the peer at the other end of this connection. In the case of UDP, the
security context is for each individual packet. An example
application is the inetd daemon, which could be modified to start
daemons running at security contexts dependent on the remote client.
Patch design approach:
- Design for TCP
The patch enables the SELinux LSM to set the peer security context for
a socket based on the security context of the IPSec security
association. The application may retrieve this context using
getsockopt. When called, the kernel determines if the socket is a
connected (TCP_ESTABLISHED) TCP socket and, if so, uses the dst_entry
cache on the socket to retrieve the security associations. If a
security association has a security context, the context string is
returned, as for UNIX domain sockets.
- Design for UDP
Unlike TCP, UDP is connectionless. This requires a somewhat different
API to retrieve the peer security context. With TCP, the peer
security context stays the same throughout the connection, thus it can
be retrieved at any time between when the connection is established
and when it is torn down. With UDP, each read/write can have
different peer and thus the security context might change every time.
As a result the security context retrieval must be done TOGETHER with
the packet retrieval.
The solution is to build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message).
Patch implementation details:
- Implementation for TCP
The security context can be retrieved by applications using getsockopt
with the existing SO_PEERSEC flag. As an example (ignoring error
checking):
getsockopt(sockfd, SOL_SOCKET, SO_PEERSEC, optbuf, &optlen);
printf("Socket peer context is: %s\n", optbuf);
The SELinux function, selinux_socket_getpeersec, is extended to check
for labeled security associations for connected (TCP_ESTABLISHED ==
sk->sk_state) TCP sockets only. If so, the socket has a dst_cache of
struct dst_entry values that may refer to security associations. If
these have security associations with security contexts, the security
context is returned.
getsockopt returns a buffer that contains a security context string or
the buffer is unmodified.
- Implementation for UDP
To retrieve the security context, the application first indicates to
the kernel such desire by setting the IP_PASSSEC option via
getsockopt. Then the application retrieves the security context using
the auxiliary data mechanism.
An example server application for UDP should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_IP, IP_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_IP &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
ip_setsockopt is enhanced with a new socket option IP_PASSSEC to allow
a server socket to receive security context of the peer. A new
ancillary message type SCM_SECURITY.
When the packet is received we get the security context from the
sec_path pointer which is contained in the sk_buff, and copy it to the
ancillary message space. An additional LSM hook,
selinux_socket_getpeersec_udp, is defined to retrieve the security
context from the SELinux space. The existing function,
selinux_socket_getpeersec does not suit our purpose, because the
security context is copied directly to user space, rather than to
kernel space.
Testing:
We have tested the patch by setting up TCP and UDP connections between
applications on two machines using the IPSec policies that result in
labeled security associations being built. For TCP, we can then
extract the peer security context using getsockopt on either end. For
UDP, the receiving end can retrieve the security context using the
auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-03-21 13:41:23 +07:00
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
}
|
|
|
|
inet->hdrincl = val ? 1 : 0;
|
|
|
|
break;
|
2010-06-15 08:07:31 +07:00
|
|
|
case IP_NODEFRAG:
|
|
|
|
if (sk->sk_type != SOCK_RAW) {
|
|
|
|
err = -ENOPROTOOPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
inet->nodefrag = val ? 1 : 0;
|
|
|
|
break;
|
inet: add IP_BIND_ADDRESS_NO_PORT to overcome bind(0) limitations
When an application needs to force a source IP on an active TCP socket
it has to use bind(IP, port=x).
As most applications do not want to deal with already used ports, x is
often set to 0, meaning the kernel is in charge to find an available
port.
But kernel does not know yet if this socket is going to be a listener or
be connected.
It has very limited choices (no full knowledge of final 4-tuple for a
connect())
With limited ephemeral port range (about 32K ports), it is very easy to
fill the space.
This patch adds a new SOL_IP socket option, asking kernel to ignore
the 0 port provided by application in bind(IP, port=0) and only
remember the given IP address.
The port will be automatically chosen at connect() time, in a way
that allows sharing a source port as long as the 4-tuples are unique.
This new feature is available for both IPv4 and IPv6 (Thanks Neal)
Tested:
Wrote a test program and checked its behavior on IPv4 and IPv6.
strace(1) shows sequences of bind(IP=127.0.0.2, port=0) followed by
connect().
Also getsockname() show that the port is still 0 right after bind()
but properly allocated after connect().
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 5
setsockopt(5, SOL_IP, IP_BIND_ADDRESS_NO_PORT, [1], 4) = 0
bind(5, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.2")}, 16) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.2")}, [16]) = 0
connect(5, {sa_family=AF_INET, sin_port=htons(53174), sin_addr=inet_addr("127.0.0.3")}, 16) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(38050), sin_addr=inet_addr("127.0.0.2")}, [16]) = 0
IPv6 test :
socket(PF_INET6, SOCK_STREAM, IPPROTO_IP) = 7
setsockopt(7, SOL_IP, IP_BIND_ADDRESS_NO_PORT, [1], 4) = 0
bind(7, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(7, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
connect(7, {sa_family=AF_INET6, sin6_port=htons(57300), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(7, {sa_family=AF_INET6, sin6_port=htons(60964), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
I was able to bind()/connect() a million concurrent IPv4 sockets,
instead of ~32000 before patch.
lpaa23:~# ulimit -n 1000010
lpaa23:~# ./bind --connect --num-flows=1000000 &
1000000 sockets
lpaa23:~# grep TCP /proc/net/sockstat
TCP: inuse 2000063 orphan 0 tw 47 alloc 2000157 mem 66
Check that a given source port is indeed used by many different
connections :
lpaa23:~# ss -t src :40000 | head -10
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 127.0.0.2:40000 127.0.202.33:44983
ESTAB 0 0 127.0.0.2:40000 127.2.27.240:44983
ESTAB 0 0 127.0.0.2:40000 127.2.98.5:44983
ESTAB 0 0 127.0.0.2:40000 127.0.124.196:44983
ESTAB 0 0 127.0.0.2:40000 127.2.139.38:44983
ESTAB 0 0 127.0.0.2:40000 127.1.59.80:44983
ESTAB 0 0 127.0.0.2:40000 127.3.6.228:44983
ESTAB 0 0 127.0.0.2:40000 127.0.38.53:44983
ESTAB 0 0 127.0.0.2:40000 127.1.197.10:44983
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-06-07 11:17:57 +07:00
|
|
|
case IP_BIND_ADDRESS_NO_PORT:
|
|
|
|
inet->bind_address_no_port = val ? 1 : 0;
|
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_MTU_DISCOVER:
|
2014-02-26 07:20:42 +07:00
|
|
|
if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
|
2007-03-09 11:44:43 +07:00
|
|
|
goto e_inval;
|
|
|
|
inet->pmtudisc = val;
|
|
|
|
break;
|
|
|
|
case IP_RECVERR:
|
|
|
|
inet->recverr = !!val;
|
|
|
|
if (!val)
|
|
|
|
skb_queue_purge(&sk->sk_error_queue);
|
|
|
|
break;
|
|
|
|
case IP_MULTICAST_TTL:
|
|
|
|
if (sk->sk_type == SOCK_STREAM)
|
|
|
|
goto e_inval;
|
2009-06-02 14:42:16 +07:00
|
|
|
if (optlen < 1)
|
2007-03-09 11:44:43 +07:00
|
|
|
goto e_inval;
|
2008-11-03 15:27:11 +07:00
|
|
|
if (val == -1)
|
2007-03-09 11:44:43 +07:00
|
|
|
val = 1;
|
|
|
|
if (val < 0 || val > 255)
|
|
|
|
goto e_inval;
|
|
|
|
inet->mc_ttl = val;
|
|
|
|
break;
|
|
|
|
case IP_MULTICAST_LOOP:
|
2009-06-02 14:42:16 +07:00
|
|
|
if (optlen < 1)
|
2007-03-09 11:44:43 +07:00
|
|
|
goto e_inval;
|
|
|
|
inet->mc_loop = !!val;
|
|
|
|
break;
|
2012-02-08 16:11:07 +07:00
|
|
|
case IP_UNICAST_IF:
|
|
|
|
{
|
|
|
|
struct net_device *dev = NULL;
|
|
|
|
int ifindex;
|
|
|
|
|
|
|
|
if (optlen != sizeof(int))
|
|
|
|
goto e_inval;
|
|
|
|
|
|
|
|
ifindex = (__force int)ntohl((__force __be32)val);
|
|
|
|
if (ifindex == 0) {
|
|
|
|
inet->uc_index = 0;
|
|
|
|
err = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = dev_get_by_index(sock_net(sk), ifindex);
|
|
|
|
err = -EADDRNOTAVAIL;
|
|
|
|
if (!dev)
|
|
|
|
break;
|
|
|
|
dev_put(dev);
|
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
if (sk->sk_bound_dev_if)
|
|
|
|
break;
|
|
|
|
|
|
|
|
inet->uc_index = ifindex;
|
|
|
|
err = 0;
|
|
|
|
break;
|
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_MULTICAST_IF:
|
|
|
|
{
|
|
|
|
struct ip_mreqn mreq;
|
|
|
|
struct net_device *dev = NULL;
|
2016-12-30 06:39:37 +07:00
|
|
|
int midx;
|
2007-03-09 11:44:43 +07:00
|
|
|
|
|
|
|
if (sk->sk_type == SOCK_STREAM)
|
|
|
|
goto e_inval;
|
|
|
|
/*
|
|
|
|
* Check the arguments are allowable
|
|
|
|
*/
|
|
|
|
|
2009-09-22 22:41:10 +07:00
|
|
|
if (optlen < sizeof(struct in_addr))
|
|
|
|
goto e_inval;
|
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
err = -EFAULT;
|
|
|
|
if (optlen >= sizeof(struct ip_mreqn)) {
|
2008-11-03 15:27:11 +07:00
|
|
|
if (copy_from_user(&mreq, optval, sizeof(mreq)))
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
} else {
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
2012-05-04 05:37:45 +07:00
|
|
|
if (optlen >= sizeof(struct ip_mreq)) {
|
|
|
|
if (copy_from_user(&mreq, optval,
|
|
|
|
sizeof(struct ip_mreq)))
|
|
|
|
break;
|
|
|
|
} else if (optlen >= sizeof(struct in_addr)) {
|
|
|
|
if (copy_from_user(&mreq.imr_address, optval,
|
|
|
|
sizeof(struct in_addr)))
|
|
|
|
break;
|
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!mreq.imr_ifindex) {
|
2008-03-18 12:44:53 +07:00
|
|
|
if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
|
2007-03-09 11:44:43 +07:00
|
|
|
inet->mc_index = 0;
|
|
|
|
inet->mc_addr = 0;
|
|
|
|
err = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
2008-03-26 00:26:21 +07:00
|
|
|
dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
|
2009-10-19 13:41:58 +07:00
|
|
|
if (dev)
|
2007-03-09 11:44:43 +07:00
|
|
|
mreq.imr_ifindex = dev->ifindex;
|
|
|
|
} else
|
2009-10-19 13:41:58 +07:00
|
|
|
dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
err = -EADDRNOTAVAIL;
|
|
|
|
if (!dev)
|
|
|
|
break;
|
2016-12-30 06:39:37 +07:00
|
|
|
|
|
|
|
midx = l3mdev_master_ifindex(dev);
|
|
|
|
|
2009-10-19 13:41:58 +07:00
|
|
|
dev_put(dev);
|
2007-03-09 11:44:43 +07:00
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
if (sk->sk_bound_dev_if &&
|
2016-12-30 06:39:37 +07:00
|
|
|
mreq.imr_ifindex != sk->sk_bound_dev_if &&
|
|
|
|
(!midx || midx != sk->sk_bound_dev_if))
|
2007-03-09 11:44:43 +07:00
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
inet->mc_index = mreq.imr_ifindex;
|
|
|
|
inet->mc_addr = mreq.imr_address.s_addr;
|
|
|
|
err = 0;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_ADD_MEMBERSHIP:
|
|
|
|
case IP_DROP_MEMBERSHIP:
|
|
|
|
{
|
|
|
|
struct ip_mreqn mreq;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-08-25 12:16:39 +07:00
|
|
|
err = -EPROTO;
|
|
|
|
if (inet_sk(sk)->is_icsk)
|
|
|
|
break;
|
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (optlen < sizeof(struct ip_mreq))
|
|
|
|
goto e_inval;
|
|
|
|
err = -EFAULT;
|
|
|
|
if (optlen >= sizeof(struct ip_mreqn)) {
|
2008-11-03 15:27:11 +07:00
|
|
|
if (copy_from_user(&mreq, optval, sizeof(mreq)))
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
} else {
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
2008-11-03 15:27:11 +07:00
|
|
|
if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq)))
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (optname == IP_ADD_MEMBERSHIP)
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
err = ip_mc_join_group(sk, &mreq);
|
2007-03-09 11:44:43 +07:00
|
|
|
else
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
err = ip_mc_leave_group(sk, &mreq);
|
2007-03-09 11:44:43 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IP_MSFILTER:
|
|
|
|
{
|
|
|
|
struct ip_msfilter *msf;
|
|
|
|
|
|
|
|
if (optlen < IP_MSFILTER_SIZE(0))
|
|
|
|
goto e_inval;
|
|
|
|
if (optlen > sysctl_optmem_max) {
|
|
|
|
err = -ENOBUFS;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
msf = kmalloc(optlen, GFP_KERNEL);
|
2007-10-09 15:59:42 +07:00
|
|
|
if (!msf) {
|
2007-03-09 11:44:43 +07:00
|
|
|
err = -ENOBUFS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
err = -EFAULT;
|
|
|
|
if (copy_from_user(msf, optval, optlen)) {
|
|
|
|
kfree(msf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* numsrc >= (1G-4) overflow in 32 bits */
|
|
|
|
if (msf->imsf_numsrc >= 0x3ffffffcU ||
|
2016-02-09 04:29:22 +07:00
|
|
|
msf->imsf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
|
2007-03-09 11:44:43 +07:00
|
|
|
kfree(msf);
|
|
|
|
err = -ENOBUFS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
|
|
|
|
kfree(msf);
|
|
|
|
err = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
err = ip_mc_msfilter(sk, msf, 0);
|
|
|
|
kfree(msf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IP_BLOCK_SOURCE:
|
|
|
|
case IP_UNBLOCK_SOURCE:
|
|
|
|
case IP_ADD_SOURCE_MEMBERSHIP:
|
|
|
|
case IP_DROP_SOURCE_MEMBERSHIP:
|
|
|
|
{
|
|
|
|
struct ip_mreq_source mreqs;
|
|
|
|
int omode, add;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (optlen != sizeof(struct ip_mreq_source))
|
|
|
|
goto e_inval;
|
|
|
|
if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
if (optname == IP_BLOCK_SOURCE) {
|
|
|
|
omode = MCAST_EXCLUDE;
|
|
|
|
add = 1;
|
|
|
|
} else if (optname == IP_UNBLOCK_SOURCE) {
|
|
|
|
omode = MCAST_EXCLUDE;
|
|
|
|
add = 0;
|
|
|
|
} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
|
|
|
|
struct ip_mreqn mreq;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
|
|
|
|
mreq.imr_address.s_addr = mreqs.imr_interface;
|
|
|
|
mreq.imr_ifindex = 0;
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
err = ip_mc_join_group(sk, &mreq);
|
2007-03-09 11:44:43 +07:00
|
|
|
if (err && err != -EADDRINUSE)
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
omode = MCAST_INCLUDE;
|
|
|
|
add = 1;
|
|
|
|
} else /* IP_DROP_SOURCE_MEMBERSHIP */ {
|
|
|
|
omode = MCAST_INCLUDE;
|
|
|
|
add = 0;
|
|
|
|
}
|
|
|
|
err = ip_mc_source(add, omode, sk, &mreqs, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MCAST_JOIN_GROUP:
|
|
|
|
case MCAST_LEAVE_GROUP:
|
|
|
|
{
|
|
|
|
struct group_req greq;
|
|
|
|
struct sockaddr_in *psin;
|
|
|
|
struct ip_mreqn mreq;
|
|
|
|
|
|
|
|
if (optlen < sizeof(struct group_req))
|
|
|
|
goto e_inval;
|
|
|
|
err = -EFAULT;
|
|
|
|
if (copy_from_user(&greq, optval, sizeof(greq)))
|
|
|
|
break;
|
|
|
|
psin = (struct sockaddr_in *)&greq.gr_group;
|
|
|
|
if (psin->sin_family != AF_INET)
|
|
|
|
goto e_inval;
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
|
|
|
mreq.imr_multiaddr = psin->sin_addr;
|
|
|
|
mreq.imr_ifindex = greq.gr_interface;
|
|
|
|
|
|
|
|
if (optname == MCAST_JOIN_GROUP)
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
err = ip_mc_join_group(sk, &mreq);
|
2007-03-09 11:44:43 +07:00
|
|
|
else
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
err = ip_mc_leave_group(sk, &mreq);
|
2007-03-09 11:44:43 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MCAST_JOIN_SOURCE_GROUP:
|
|
|
|
case MCAST_LEAVE_SOURCE_GROUP:
|
|
|
|
case MCAST_BLOCK_SOURCE:
|
|
|
|
case MCAST_UNBLOCK_SOURCE:
|
|
|
|
{
|
|
|
|
struct group_source_req greqs;
|
|
|
|
struct ip_mreq_source mreqs;
|
|
|
|
struct sockaddr_in *psin;
|
|
|
|
int omode, add;
|
|
|
|
|
|
|
|
if (optlen != sizeof(struct group_source_req))
|
|
|
|
goto e_inval;
|
|
|
|
if (copy_from_user(&greqs, optval, sizeof(greqs))) {
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
if (greqs.gsr_group.ss_family != AF_INET ||
|
|
|
|
greqs.gsr_source.ss_family != AF_INET) {
|
|
|
|
err = -EADDRNOTAVAIL;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
psin = (struct sockaddr_in *)&greqs.gsr_group;
|
|
|
|
mreqs.imr_multiaddr = psin->sin_addr.s_addr;
|
|
|
|
psin = (struct sockaddr_in *)&greqs.gsr_source;
|
|
|
|
mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
|
|
|
|
mreqs.imr_interface = 0; /* use index for mc_source */
|
|
|
|
|
|
|
|
if (optname == MCAST_BLOCK_SOURCE) {
|
|
|
|
omode = MCAST_EXCLUDE;
|
|
|
|
add = 1;
|
|
|
|
} else if (optname == MCAST_UNBLOCK_SOURCE) {
|
|
|
|
omode = MCAST_EXCLUDE;
|
|
|
|
add = 0;
|
|
|
|
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
|
2005-04-17 05:20:36 +07:00
|
|
|
struct ip_mreqn mreq;
|
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
psin = (struct sockaddr_in *)&greqs.gsr_group;
|
2005-04-17 05:20:36 +07:00
|
|
|
mreq.imr_multiaddr = psin->sin_addr;
|
2007-03-09 11:44:43 +07:00
|
|
|
mreq.imr_address.s_addr = 0;
|
|
|
|
mreq.imr_ifindex = greqs.gsr_interface;
|
ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}
in favor of their inner __ ones, which doesn't grab rtnl.
As these functions need to operate on a locked socket, we can't be
grabbing rtnl by then. It's too late and doing so causes reversed
locking.
So this patch:
- move rtnl handling to callers instead while already fixing some
reversed locking situations, like on vxlan and ipvs code.
- renames __ ones to not have the __ mark:
__ip_mc_{join,leave}_group -> ip_mc_{join,leave}_group
__ipv6_sock_mc_{join,drop} -> ipv6_sock_mc_{join,drop}
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-19 00:50:43 +07:00
|
|
|
err = ip_mc_join_group(sk, &mreq);
|
2007-03-09 11:44:43 +07:00
|
|
|
if (err && err != -EADDRINUSE)
|
|
|
|
break;
|
|
|
|
greqs.gsr_interface = mreq.imr_ifindex;
|
|
|
|
omode = MCAST_INCLUDE;
|
|
|
|
add = 1;
|
|
|
|
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
|
|
|
|
omode = MCAST_INCLUDE;
|
|
|
|
add = 0;
|
|
|
|
}
|
|
|
|
err = ip_mc_source(add, omode, sk, &mreqs,
|
|
|
|
greqs.gsr_interface);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MCAST_MSFILTER:
|
|
|
|
{
|
|
|
|
struct sockaddr_in *psin;
|
|
|
|
struct ip_msfilter *msf = NULL;
|
|
|
|
struct group_filter *gsf = NULL;
|
|
|
|
int msize, i, ifindex;
|
|
|
|
|
|
|
|
if (optlen < GROUP_FILTER_SIZE(0))
|
|
|
|
goto e_inval;
|
|
|
|
if (optlen > sysctl_optmem_max) {
|
|
|
|
err = -ENOBUFS;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
2008-11-03 15:27:11 +07:00
|
|
|
gsf = kmalloc(optlen, GFP_KERNEL);
|
2007-10-09 15:59:42 +07:00
|
|
|
if (!gsf) {
|
2007-03-09 11:44:43 +07:00
|
|
|
err = -ENOBUFS;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
err = -EFAULT;
|
2009-06-02 14:42:16 +07:00
|
|
|
if (copy_from_user(gsf, optval, optlen))
|
2007-03-09 11:44:43 +07:00
|
|
|
goto mc_msf_out;
|
2009-06-02 14:42:16 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
/* numsrc >= (4G-140)/128 overflow in 32 bits */
|
|
|
|
if (gsf->gf_numsrc >= 0x1ffffff ||
|
2016-02-09 04:29:22 +07:00
|
|
|
gsf->gf_numsrc > net->ipv4.sysctl_igmp_max_msf) {
|
2007-03-09 11:44:43 +07:00
|
|
|
err = -ENOBUFS;
|
|
|
|
goto mc_msf_out;
|
|
|
|
}
|
|
|
|
if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto mc_msf_out;
|
|
|
|
}
|
|
|
|
msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
|
2008-11-03 15:27:11 +07:00
|
|
|
msf = kmalloc(msize, GFP_KERNEL);
|
2007-10-09 15:59:42 +07:00
|
|
|
if (!msf) {
|
2007-03-09 11:44:43 +07:00
|
|
|
err = -ENOBUFS;
|
|
|
|
goto mc_msf_out;
|
|
|
|
}
|
|
|
|
ifindex = gsf->gf_interface;
|
|
|
|
psin = (struct sockaddr_in *)&gsf->gf_group;
|
|
|
|
if (psin->sin_family != AF_INET) {
|
2005-04-17 05:20:36 +07:00
|
|
|
err = -EADDRNOTAVAIL;
|
2007-03-09 11:44:43 +07:00
|
|
|
goto mc_msf_out;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
msf->imsf_multiaddr = psin->sin_addr.s_addr;
|
|
|
|
msf->imsf_interface = 0;
|
|
|
|
msf->imsf_fmode = gsf->gf_fmode;
|
|
|
|
msf->imsf_numsrc = gsf->gf_numsrc;
|
|
|
|
err = -EADDRNOTAVAIL;
|
2009-06-02 14:42:16 +07:00
|
|
|
for (i = 0; i < gsf->gf_numsrc; ++i) {
|
2007-03-09 11:44:43 +07:00
|
|
|
psin = (struct sockaddr_in *)&gsf->gf_slist[i];
|
2007-02-09 21:24:47 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (psin->sin_family != AF_INET)
|
|
|
|
goto mc_msf_out;
|
|
|
|
msf->imsf_slist[i] = psin->sin_addr.s_addr;
|
|
|
|
}
|
|
|
|
kfree(gsf);
|
|
|
|
gsf = NULL;
|
|
|
|
|
|
|
|
err = ip_mc_msfilter(sk, msf, ifindex);
|
2009-06-02 14:42:16 +07:00
|
|
|
mc_msf_out:
|
2007-03-09 11:44:43 +07:00
|
|
|
kfree(msf);
|
|
|
|
kfree(gsf);
|
|
|
|
break;
|
|
|
|
}
|
2009-05-28 14:00:46 +07:00
|
|
|
case IP_MULTICAST_ALL:
|
|
|
|
if (optlen < 1)
|
|
|
|
goto e_inval;
|
|
|
|
if (val != 0 && val != 1)
|
|
|
|
goto e_inval;
|
|
|
|
inet->mc_all = val;
|
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_ROUTER_ALERT:
|
|
|
|
err = ip_ra_control(sk, val ? 1 : 0, NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_FREEBIND:
|
2009-06-02 14:42:16 +07:00
|
|
|
if (optlen < 1)
|
2007-03-09 11:44:43 +07:00
|
|
|
goto e_inval;
|
|
|
|
inet->freebind = !!val;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_IPSEC_POLICY:
|
|
|
|
case IP_XFRM_POLICY:
|
|
|
|
err = -EPERM;
|
net: Allow userns root to control ipv4
Allow an unpriviled user who has created a user namespace, and then
created a network namespace to effectively use the new network
namespace, by reducing capable(CAP_NET_ADMIN) and
capable(CAP_NET_RAW) calls to be ns_capable(net->user_ns,
CAP_NET_ADMIN), or capable(net->user_ns, CAP_NET_RAW) calls.
Settings that merely control a single network device are allowed.
Either the network device is a logical network device where
restrictions make no difference or the network device is hardware NIC
that has been explicity moved from the initial network namespace.
In general policy and network stack state changes are allowed
while resource control is left unchanged.
Allow creating raw sockets.
Allow the SIOCSARP ioctl to control the arp cache.
Allow the SIOCSIFFLAG ioctl to allow setting network device flags.
Allow the SIOCSIFADDR ioctl to allow setting a netdevice ipv4 address.
Allow the SIOCSIFBRDADDR ioctl to allow setting a netdevice ipv4 broadcast address.
Allow the SIOCSIFDSTADDR ioctl to allow setting a netdevice ipv4 destination address.
Allow the SIOCSIFNETMASK ioctl to allow setting a netdevice ipv4 netmask.
Allow the SIOCADDRT and SIOCDELRT ioctls to allow adding and deleting ipv4 routes.
Allow the SIOCADDTUNNEL, SIOCCHGTUNNEL and SIOCDELTUNNEL ioctls for
adding, changing and deleting gre tunnels.
Allow the SIOCADDTUNNEL, SIOCCHGTUNNEL and SIOCDELTUNNEL ioctls for
adding, changing and deleting ipip tunnels.
Allow the SIOCADDTUNNEL, SIOCCHGTUNNEL and SIOCDELTUNNEL ioctls for
adding, changing and deleting ipsec virtual tunnel interfaces.
Allow setting the MRT_INIT, MRT_DONE, MRT_ADD_VIF, MRT_DEL_VIF, MRT_ADD_MFC,
MRT_DEL_MFC, MRT_ASSERT, MRT_PIM, MRT_TABLE socket options on multicast routing
sockets.
Allow setting and receiving IPOPT_CIPSO, IP_OPT_SEC, IP_OPT_SID and
arbitrary ip options.
Allow setting IP_SEC_POLICY/IP_XFRM_POLICY ipv4 socket option.
Allow setting the IP_TRANSPARENT ipv4 socket option.
Allow setting the TCP_REPAIR socket option.
Allow setting the TCP_CONGESTION socket option.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-16 10:03:05 +07:00
|
|
|
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
err = xfrm_user_policy(sk, optname, optval, optlen);
|
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-10-01 21:30:02 +07:00
|
|
|
case IP_TRANSPARENT:
|
net: Allow userns root to control ipv4
Allow an unpriviled user who has created a user namespace, and then
created a network namespace to effectively use the new network
namespace, by reducing capable(CAP_NET_ADMIN) and
capable(CAP_NET_RAW) calls to be ns_capable(net->user_ns,
CAP_NET_ADMIN), or capable(net->user_ns, CAP_NET_RAW) calls.
Settings that merely control a single network device are allowed.
Either the network device is a logical network device where
restrictions make no difference or the network device is hardware NIC
that has been explicity moved from the initial network namespace.
In general policy and network stack state changes are allowed
while resource control is left unchanged.
Allow creating raw sockets.
Allow the SIOCSARP ioctl to control the arp cache.
Allow the SIOCSIFFLAG ioctl to allow setting network device flags.
Allow the SIOCSIFADDR ioctl to allow setting a netdevice ipv4 address.
Allow the SIOCSIFBRDADDR ioctl to allow setting a netdevice ipv4 broadcast address.
Allow the SIOCSIFDSTADDR ioctl to allow setting a netdevice ipv4 destination address.
Allow the SIOCSIFNETMASK ioctl to allow setting a netdevice ipv4 netmask.
Allow the SIOCADDRT and SIOCDELRT ioctls to allow adding and deleting ipv4 routes.
Allow the SIOCADDTUNNEL, SIOCCHGTUNNEL and SIOCDELTUNNEL ioctls for
adding, changing and deleting gre tunnels.
Allow the SIOCADDTUNNEL, SIOCCHGTUNNEL and SIOCDELTUNNEL ioctls for
adding, changing and deleting ipip tunnels.
Allow the SIOCADDTUNNEL, SIOCCHGTUNNEL and SIOCDELTUNNEL ioctls for
adding, changing and deleting ipsec virtual tunnel interfaces.
Allow setting the MRT_INIT, MRT_DONE, MRT_ADD_VIF, MRT_DEL_VIF, MRT_ADD_MFC,
MRT_DEL_MFC, MRT_ASSERT, MRT_PIM, MRT_TABLE socket options on multicast routing
sockets.
Allow setting and receiving IPOPT_CIPSO, IP_OPT_SEC, IP_OPT_SID and
arbitrary ip options.
Allow setting IP_SEC_POLICY/IP_XFRM_POLICY ipv4 socket option.
Allow setting the IP_TRANSPARENT ipv4 socket option.
Allow setting the TCP_REPAIR socket option.
Allow setting the TCP_CONGESTION socket option.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-16 10:03:05 +07:00
|
|
|
if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
|
|
|
|
!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
|
2008-10-01 21:30:02 +07:00
|
|
|
err = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (optlen < 1)
|
|
|
|
goto e_inval;
|
|
|
|
inet->transparent = !!val;
|
|
|
|
break;
|
|
|
|
|
2010-01-12 07:28:01 +07:00
|
|
|
case IP_MINTTL:
|
|
|
|
if (optlen < 1)
|
|
|
|
goto e_inval;
|
|
|
|
if (val < 0 || val > 255)
|
|
|
|
goto e_inval;
|
|
|
|
inet->min_ttl = val;
|
|
|
|
break;
|
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
default:
|
|
|
|
err = -ENOPROTOOPT;
|
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
release_sock(sk);
|
2015-03-19 00:50:42 +07:00
|
|
|
if (needs_rtnl)
|
|
|
|
rtnl_unlock();
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
|
|
|
|
e_inval:
|
|
|
|
release_sock(sk);
|
2015-03-19 00:50:42 +07:00
|
|
|
if (needs_rtnl)
|
|
|
|
rtnl_unlock();
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-04-29 05:31:51 +07:00
|
|
|
/**
|
net-timestamp: allow reading recv cmsg on errqueue with origin tstamp
Allow reading of timestamps and cmsg at the same time on all relevant
socket families. One use is to correlate timestamps with egress
device, by asking for cmsg IP_PKTINFO.
on AF_INET sockets, call the relevant function (ip_cmsg_recv). To
avoid changing legacy expectations, only do so if the caller sets a
new timestamping flag SOF_TIMESTAMPING_OPT_CMSG.
on AF_INET6 sockets, IPV6_PKTINFO and all other recv cmsg are already
returned for all origins. only change is to set ifindex, which is
not initialized for all error origins.
In both cases, only generate the pktinfo message if an ifindex is
known. This is not the case for ACK timestamps.
The difference between the protocol families is probably a historical
accident as a result of the different conditions for generating cmsg
in the relevant ip(v6)_recv_error function:
ipv4: if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
ipv6: if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
At one time, this was the same test bar for the ICMP/ICMP6
distinction. This is no longer true.
Signed-off-by: Willem de Bruijn <willemb@google.com>
----
Changes
v1 -> v2
large rewrite
- integrate with existing pktinfo cmsg generation code
- on ipv4: only send with new flag, to maintain legacy behavior
- on ipv6: send at most a single pktinfo cmsg
- on ipv6: initialize fields if not yet initialized
The recv cmsg interfaces are also relevant to the discussion of
whether looping packet headers is problematic. For v6, cmsgs that
identify many headers are already returned. This patch expands
that to v4. If it sounds reasonable, I will follow with patches
1. request timestamps without payload with SOF_TIMESTAMPING_OPT_TSONLY
(http://patchwork.ozlabs.org/patch/366967/)
2. sysctl to conditionally drop all timestamps that have payload or
cmsg from users without CAP_NET_RAW.
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-12-01 10:22:34 +07:00
|
|
|
* ipv4_pktinfo_prepare - transfer some info from rtable to skb
|
2010-04-29 05:31:51 +07:00
|
|
|
* @sk: socket
|
|
|
|
* @skb: buffer
|
|
|
|
*
|
2012-06-28 17:59:11 +07:00
|
|
|
* To support IP_CMSG_PKTINFO option, we store rt_iif and specific
|
|
|
|
* destination in skb->cb[] before dst drop.
|
2013-12-09 03:15:44 +07:00
|
|
|
* This way, receiver doesn't make cache line misses to read rtable.
|
2010-04-29 05:31:51 +07:00
|
|
|
*/
|
2013-10-07 23:01:40 +07:00
|
|
|
void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
|
2010-04-29 05:31:51 +07:00
|
|
|
{
|
ipv4: PKTINFO doesnt need dst reference
Le lundi 07 novembre 2011 à 15:33 +0100, Eric Dumazet a écrit :
> At least, in recent kernels we dont change dst->refcnt in forwarding
> patch (usinf NOREF skb->dst)
>
> One particular point is the atomic_inc(dst->refcnt) we have to perform
> when queuing an UDP packet if socket asked PKTINFO stuff (for example a
> typical DNS server has to setup this option)
>
> I have one patch somewhere that stores the information in skb->cb[] and
> avoid the atomic_{inc|dec}(dst->refcnt).
>
OK I found it, I did some extra tests and believe its ready.
[PATCH net-next] ipv4: IP_PKTINFO doesnt need dst reference
When a socket uses IP_PKTINFO notifications, we currently force a dst
reference for each received skb. Reader has to access dst to get needed
information (rt_iif & rt_spec_dst) and must release dst reference.
We also forced a dst reference if skb was put in socket backlog, even
without IP_PKTINFO handling. This happens under stress/load.
We can instead store the needed information in skb->cb[], so that only
softirq handler really access dst, improving cache hit ratios.
This removes two atomic operations per packet, and false sharing as
well.
On a benchmark using a mono threaded receiver (doing only recvmsg()
calls), I can reach 720.000 pps instead of 570.000 pps.
IP_PKTINFO is typically used by DNS servers, and any multihomed aware
UDP application.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2011-11-09 14:24:35 +07:00
|
|
|
struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
|
2014-01-20 09:43:08 +07:00
|
|
|
bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) ||
|
|
|
|
ipv6_sk_rxinfo(sk);
|
ipv4: PKTINFO doesnt need dst reference
Le lundi 07 novembre 2011 à 15:33 +0100, Eric Dumazet a écrit :
> At least, in recent kernels we dont change dst->refcnt in forwarding
> patch (usinf NOREF skb->dst)
>
> One particular point is the atomic_inc(dst->refcnt) we have to perform
> when queuing an UDP packet if socket asked PKTINFO stuff (for example a
> typical DNS server has to setup this option)
>
> I have one patch somewhere that stores the information in skb->cb[] and
> avoid the atomic_{inc|dec}(dst->refcnt).
>
OK I found it, I did some extra tests and believe its ready.
[PATCH net-next] ipv4: IP_PKTINFO doesnt need dst reference
When a socket uses IP_PKTINFO notifications, we currently force a dst
reference for each received skb. Reader has to access dst to get needed
information (rt_iif & rt_spec_dst) and must release dst reference.
We also forced a dst reference if skb was put in socket backlog, even
without IP_PKTINFO handling. This happens under stress/load.
We can instead store the needed information in skb->cb[], so that only
softirq handler really access dst, improving cache hit ratios.
This removes two atomic operations per packet, and false sharing as
well.
On a benchmark using a mono threaded receiver (doing only recvmsg()
calls), I can reach 720.000 pps instead of 570.000 pps.
IP_PKTINFO is typically used by DNS servers, and any multihomed aware
UDP application.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2011-11-09 14:24:35 +07:00
|
|
|
|
2014-01-20 09:43:08 +07:00
|
|
|
if (prepare && skb_rtable(skb)) {
|
2016-05-11 01:19:51 +07:00
|
|
|
/* skb->cb is overloaded: prior to this point it is IP{6}CB
|
|
|
|
* which has interface index (iif) as the first member of the
|
|
|
|
* underlying inet{6}_skb_parm struct. This code then overlays
|
|
|
|
* PKTINFO_SKB_CB and in_pktinfo also has iif as the first
|
2016-12-29 15:45:04 +07:00
|
|
|
* element so the iif is picked up from the prior IPCB. If iif
|
|
|
|
* is the loopback interface, then return the sending interface
|
|
|
|
* (e.g., process binds socket to eth0 for Tx which is
|
|
|
|
* redirected to loopback in the rtable/dst).
|
2016-05-11 01:19:51 +07:00
|
|
|
*/
|
2016-12-29 15:45:04 +07:00
|
|
|
if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX)
|
|
|
|
pktinfo->ipi_ifindex = inet_iif(skb);
|
|
|
|
|
2012-06-28 17:59:11 +07:00
|
|
|
pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
|
ipv4: PKTINFO doesnt need dst reference
Le lundi 07 novembre 2011 à 15:33 +0100, Eric Dumazet a écrit :
> At least, in recent kernels we dont change dst->refcnt in forwarding
> patch (usinf NOREF skb->dst)
>
> One particular point is the atomic_inc(dst->refcnt) we have to perform
> when queuing an UDP packet if socket asked PKTINFO stuff (for example a
> typical DNS server has to setup this option)
>
> I have one patch somewhere that stores the information in skb->cb[] and
> avoid the atomic_{inc|dec}(dst->refcnt).
>
OK I found it, I did some extra tests and believe its ready.
[PATCH net-next] ipv4: IP_PKTINFO doesnt need dst reference
When a socket uses IP_PKTINFO notifications, we currently force a dst
reference for each received skb. Reader has to access dst to get needed
information (rt_iif & rt_spec_dst) and must release dst reference.
We also forced a dst reference if skb was put in socket backlog, even
without IP_PKTINFO handling. This happens under stress/load.
We can instead store the needed information in skb->cb[], so that only
softirq handler really access dst, improving cache hit ratios.
This removes two atomic operations per packet, and false sharing as
well.
On a benchmark using a mono threaded receiver (doing only recvmsg()
calls), I can reach 720.000 pps instead of 570.000 pps.
IP_PKTINFO is typically used by DNS servers, and any multihomed aware
UDP application.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2011-11-09 14:24:35 +07:00
|
|
|
} else {
|
|
|
|
pktinfo->ipi_ifindex = 0;
|
|
|
|
pktinfo->ipi_spec_dst.s_addr = 0;
|
|
|
|
}
|
2017-02-05 02:16:52 +07:00
|
|
|
/* We need to keep the dst for __ip_options_echo()
|
|
|
|
* We could restrict the test to opt.ts_needtime || opt.srr,
|
|
|
|
* but the following is good enough as IP options are not often used.
|
|
|
|
*/
|
|
|
|
if (unlikely(IPCB(skb)->opt.optlen))
|
|
|
|
skb_dst_force(skb);
|
|
|
|
else
|
|
|
|
skb_dst_drop(skb);
|
2010-04-29 05:31:51 +07:00
|
|
|
}
|
|
|
|
|
2006-03-21 13:45:21 +07:00
|
|
|
int ip_setsockopt(struct sock *sk, int level,
|
2009-10-01 06:12:20 +07:00
|
|
|
int optname, char __user *optval, unsigned int optlen)
|
2006-03-21 13:45:21 +07:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (level != SOL_IP)
|
|
|
|
return -ENOPROTOOPT;
|
|
|
|
|
|
|
|
err = do_ip_setsockopt(sk, level, optname, optval, optlen);
|
|
|
|
#ifdef CONFIG_NETFILTER
|
|
|
|
/* we need to exclude all possible ENOPROTOOPTs except default case */
|
|
|
|
if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
|
2007-11-06 12:32:31 +07:00
|
|
|
optname != IP_IPSEC_POLICY &&
|
|
|
|
optname != IP_XFRM_POLICY &&
|
|
|
|
!ip_mroute_opt(optname)) {
|
2006-03-21 13:45:21 +07:00
|
|
|
lock_sock(sk);
|
|
|
|
err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
|
|
|
|
release_sock(sk);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return err;
|
|
|
|
}
|
2009-06-02 14:42:16 +07:00
|
|
|
EXPORT_SYMBOL(ip_setsockopt);
|
2006-03-21 13:45:21 +07:00
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
2006-03-21 13:48:35 +07:00
|
|
|
int compat_ip_setsockopt(struct sock *sk, int level, int optname,
|
2009-10-01 06:12:20 +07:00
|
|
|
char __user *optval, unsigned int optlen)
|
2006-03-21 13:45:21 +07:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (level != SOL_IP)
|
|
|
|
return -ENOPROTOOPT;
|
|
|
|
|
2008-04-27 15:06:07 +07:00
|
|
|
if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
|
|
|
|
return compat_mc_setsockopt(sk, level, optname, optval, optlen,
|
|
|
|
ip_setsockopt);
|
|
|
|
|
2006-03-21 13:45:21 +07:00
|
|
|
err = do_ip_setsockopt(sk, level, optname, optval, optlen);
|
|
|
|
#ifdef CONFIG_NETFILTER
|
|
|
|
/* we need to exclude all possible ENOPROTOOPTs except default case */
|
|
|
|
if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
|
2007-11-06 12:32:31 +07:00
|
|
|
optname != IP_IPSEC_POLICY &&
|
|
|
|
optname != IP_XFRM_POLICY &&
|
|
|
|
!ip_mroute_opt(optname)) {
|
2006-03-21 13:45:21 +07:00
|
|
|
lock_sock(sk);
|
2006-03-21 13:48:35 +07:00
|
|
|
err = compat_nf_setsockopt(sk, PF_INET, optname,
|
|
|
|
optval, optlen);
|
2006-03-21 13:45:21 +07:00
|
|
|
release_sock(sk);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return err;
|
|
|
|
}
|
2006-03-21 13:48:35 +07:00
|
|
|
EXPORT_SYMBOL(compat_ip_setsockopt);
|
2006-03-21 13:45:21 +07:00
|
|
|
#endif
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2009-06-02 14:42:16 +07:00
|
|
|
* Get the options. Note for future reference. The GET of IP options gets
|
|
|
|
* the _received_ ones. The set sets the _sent_ ones.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
|
2015-11-04 06:41:16 +07:00
|
|
|
static bool getsockopt_needs_rtnl(int optname)
|
|
|
|
{
|
|
|
|
switch (optname) {
|
|
|
|
case IP_MSFILTER:
|
|
|
|
case MCAST_MSFILTER:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-03-21 13:45:21 +07:00
|
|
|
static int do_ip_getsockopt(struct sock *sk, int level, int optname,
|
2012-04-15 12:58:06 +07:00
|
|
|
char __user *optval, int __user *optlen, unsigned int flags)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
2015-11-04 06:41:16 +07:00
|
|
|
bool needs_rtnl = getsockopt_needs_rtnl(optname);
|
|
|
|
int val, err = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
int len;
|
2007-02-09 21:24:47 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (level != SOL_IP)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2007-11-06 12:32:31 +07:00
|
|
|
if (ip_mroute_opt(optname))
|
2008-11-03 15:27:11 +07:00
|
|
|
return ip_mroute_getsockopt(sk, optname, optval, optlen);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-03 15:27:11 +07:00
|
|
|
if (get_user(len, optlen))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
2007-03-09 11:44:43 +07:00
|
|
|
if (len < 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
2007-02-09 21:24:47 +07:00
|
|
|
|
2015-11-04 06:41:16 +07:00
|
|
|
if (needs_rtnl)
|
|
|
|
rtnl_lock();
|
2005-04-17 05:20:36 +07:00
|
|
|
lock_sock(sk);
|
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
switch (optname) {
|
|
|
|
case IP_OPTIONS:
|
|
|
|
{
|
|
|
|
unsigned char optbuf[sizeof(struct ip_options)+40];
|
2011-04-21 16:45:37 +07:00
|
|
|
struct ip_options *opt = (struct ip_options *)optbuf;
|
|
|
|
struct ip_options_rcu *inet_opt;
|
|
|
|
|
|
|
|
inet_opt = rcu_dereference_protected(inet->inet_opt,
|
2016-04-05 22:10:15 +07:00
|
|
|
lockdep_sock_is_held(sk));
|
2007-03-09 11:44:43 +07:00
|
|
|
opt->optlen = 0;
|
2011-04-21 16:45:37 +07:00
|
|
|
if (inet_opt)
|
|
|
|
memcpy(optbuf, &inet_opt->opt,
|
|
|
|
sizeof(struct ip_options) +
|
|
|
|
inet_opt->opt.optlen);
|
2007-03-09 11:44:43 +07:00
|
|
|
release_sock(sk);
|
|
|
|
|
|
|
|
if (opt->optlen == 0)
|
|
|
|
return put_user(0, optlen);
|
|
|
|
|
|
|
|
ip_options_undo(opt);
|
|
|
|
|
|
|
|
len = min_t(unsigned int, len, opt->optlen);
|
|
|
|
if (put_user(len, optlen))
|
|
|
|
return -EFAULT;
|
|
|
|
if (copy_to_user(optval, opt->__data, len))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
case IP_PKTINFO:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
|
|
|
|
break;
|
|
|
|
case IP_RECVTTL:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
|
|
|
|
break;
|
|
|
|
case IP_RECVTOS:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
|
|
|
|
break;
|
|
|
|
case IP_RECVOPTS:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
|
|
|
|
break;
|
|
|
|
case IP_RETOPTS:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
|
|
|
|
break;
|
|
|
|
case IP_PASSSEC:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
|
|
|
|
break;
|
2008-11-17 10:32:39 +07:00
|
|
|
case IP_RECVORIGDSTADDR:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
|
|
|
|
break;
|
2015-01-06 04:56:17 +07:00
|
|
|
case IP_CHECKSUM:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0;
|
|
|
|
break;
|
2016-11-02 22:02:16 +07:00
|
|
|
case IP_RECVFRAGSIZE:
|
|
|
|
val = (inet->cmsg_flags & IP_CMSG_RECVFRAGSIZE) != 0;
|
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_TOS:
|
|
|
|
val = inet->tos;
|
|
|
|
break;
|
|
|
|
case IP_TTL:
|
2016-02-15 17:11:27 +07:00
|
|
|
{
|
|
|
|
struct net *net = sock_net(sk);
|
2007-03-09 11:44:43 +07:00
|
|
|
val = (inet->uc_ttl == -1 ?
|
2016-02-15 17:11:27 +07:00
|
|
|
net->ipv4.sysctl_ip_default_ttl :
|
2007-03-09 11:44:43 +07:00
|
|
|
inet->uc_ttl);
|
|
|
|
break;
|
2016-02-15 17:11:27 +07:00
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_HDRINCL:
|
|
|
|
val = inet->hdrincl;
|
|
|
|
break;
|
2010-09-11 03:26:56 +07:00
|
|
|
case IP_NODEFRAG:
|
|
|
|
val = inet->nodefrag;
|
|
|
|
break;
|
inet: add IP_BIND_ADDRESS_NO_PORT to overcome bind(0) limitations
When an application needs to force a source IP on an active TCP socket
it has to use bind(IP, port=x).
As most applications do not want to deal with already used ports, x is
often set to 0, meaning the kernel is in charge to find an available
port.
But kernel does not know yet if this socket is going to be a listener or
be connected.
It has very limited choices (no full knowledge of final 4-tuple for a
connect())
With limited ephemeral port range (about 32K ports), it is very easy to
fill the space.
This patch adds a new SOL_IP socket option, asking kernel to ignore
the 0 port provided by application in bind(IP, port=0) and only
remember the given IP address.
The port will be automatically chosen at connect() time, in a way
that allows sharing a source port as long as the 4-tuples are unique.
This new feature is available for both IPv4 and IPv6 (Thanks Neal)
Tested:
Wrote a test program and checked its behavior on IPv4 and IPv6.
strace(1) shows sequences of bind(IP=127.0.0.2, port=0) followed by
connect().
Also getsockname() show that the port is still 0 right after bind()
but properly allocated after connect().
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 5
setsockopt(5, SOL_IP, IP_BIND_ADDRESS_NO_PORT, [1], 4) = 0
bind(5, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.2")}, 16) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.2")}, [16]) = 0
connect(5, {sa_family=AF_INET, sin_port=htons(53174), sin_addr=inet_addr("127.0.0.3")}, 16) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(38050), sin_addr=inet_addr("127.0.0.2")}, [16]) = 0
IPv6 test :
socket(PF_INET6, SOCK_STREAM, IPPROTO_IP) = 7
setsockopt(7, SOL_IP, IP_BIND_ADDRESS_NO_PORT, [1], 4) = 0
bind(7, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(7, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
connect(7, {sa_family=AF_INET6, sin6_port=htons(57300), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(7, {sa_family=AF_INET6, sin6_port=htons(60964), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
I was able to bind()/connect() a million concurrent IPv4 sockets,
instead of ~32000 before patch.
lpaa23:~# ulimit -n 1000010
lpaa23:~# ./bind --connect --num-flows=1000000 &
1000000 sockets
lpaa23:~# grep TCP /proc/net/sockstat
TCP: inuse 2000063 orphan 0 tw 47 alloc 2000157 mem 66
Check that a given source port is indeed used by many different
connections :
lpaa23:~# ss -t src :40000 | head -10
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 127.0.0.2:40000 127.0.202.33:44983
ESTAB 0 0 127.0.0.2:40000 127.2.27.240:44983
ESTAB 0 0 127.0.0.2:40000 127.2.98.5:44983
ESTAB 0 0 127.0.0.2:40000 127.0.124.196:44983
ESTAB 0 0 127.0.0.2:40000 127.2.139.38:44983
ESTAB 0 0 127.0.0.2:40000 127.1.59.80:44983
ESTAB 0 0 127.0.0.2:40000 127.3.6.228:44983
ESTAB 0 0 127.0.0.2:40000 127.0.38.53:44983
ESTAB 0 0 127.0.0.2:40000 127.1.197.10:44983
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-06-07 11:17:57 +07:00
|
|
|
case IP_BIND_ADDRESS_NO_PORT:
|
|
|
|
val = inet->bind_address_no_port;
|
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_MTU_DISCOVER:
|
|
|
|
val = inet->pmtudisc;
|
|
|
|
break;
|
|
|
|
case IP_MTU:
|
|
|
|
{
|
|
|
|
struct dst_entry *dst;
|
|
|
|
val = 0;
|
|
|
|
dst = sk_dst_get(sk);
|
|
|
|
if (dst) {
|
|
|
|
val = dst_mtu(dst);
|
|
|
|
dst_release(dst);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
if (!val) {
|
2005-04-17 05:20:36 +07:00
|
|
|
release_sock(sk);
|
2007-03-09 11:44:43 +07:00
|
|
|
return -ENOTCONN;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IP_RECVERR:
|
|
|
|
val = inet->recverr;
|
|
|
|
break;
|
|
|
|
case IP_MULTICAST_TTL:
|
|
|
|
val = inet->mc_ttl;
|
|
|
|
break;
|
|
|
|
case IP_MULTICAST_LOOP:
|
|
|
|
val = inet->mc_loop;
|
|
|
|
break;
|
2012-02-08 16:11:07 +07:00
|
|
|
case IP_UNICAST_IF:
|
|
|
|
val = (__force int)htonl((__u32) inet->uc_index);
|
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_MULTICAST_IF:
|
|
|
|
{
|
|
|
|
struct in_addr addr;
|
|
|
|
len = min_t(unsigned int, len, sizeof(struct in_addr));
|
|
|
|
addr.s_addr = inet->mc_addr;
|
|
|
|
release_sock(sk);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (put_user(len, optlen))
|
|
|
|
return -EFAULT;
|
|
|
|
if (copy_to_user(optval, &addr, len))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
case IP_MSFILTER:
|
|
|
|
{
|
|
|
|
struct ip_msfilter msf;
|
|
|
|
|
|
|
|
if (len < IP_MSFILTER_SIZE(0)) {
|
2015-11-04 06:41:16 +07:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
|
2015-11-04 06:41:16 +07:00
|
|
|
err = -EFAULT;
|
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
err = ip_mc_msfget(sk, &msf,
|
|
|
|
(struct ip_msfilter __user *)optval, optlen);
|
2015-11-04 06:41:16 +07:00
|
|
|
goto out;
|
2007-03-09 11:44:43 +07:00
|
|
|
}
|
|
|
|
case MCAST_MSFILTER:
|
|
|
|
{
|
|
|
|
struct group_filter gsf;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (len < GROUP_FILTER_SIZE(0)) {
|
2015-11-04 06:41:16 +07:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2007-03-09 11:44:43 +07:00
|
|
|
}
|
|
|
|
if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
|
2015-11-04 06:41:16 +07:00
|
|
|
err = -EFAULT;
|
|
|
|
goto out;
|
2007-03-09 11:44:43 +07:00
|
|
|
}
|
|
|
|
err = ip_mc_gsfget(sk, &gsf,
|
2009-06-02 14:42:16 +07:00
|
|
|
(struct group_filter __user *)optval,
|
|
|
|
optlen);
|
2015-11-04 06:41:16 +07:00
|
|
|
goto out;
|
2007-03-09 11:44:43 +07:00
|
|
|
}
|
2009-05-28 14:00:46 +07:00
|
|
|
case IP_MULTICAST_ALL:
|
|
|
|
val = inet->mc_all;
|
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
case IP_PKTOPTIONS:
|
|
|
|
{
|
|
|
|
struct msghdr msg;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
release_sock(sk);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (sk->sk_type != SOCK_STREAM)
|
|
|
|
return -ENOPROTOOPT;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2014-07-27 17:29:07 +07:00
|
|
|
msg.msg_control = (__force void *) optval;
|
2007-03-09 11:44:43 +07:00
|
|
|
msg.msg_controllen = len;
|
2011-08-08 12:31:07 +07:00
|
|
|
msg.msg_flags = flags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-03-09 11:44:43 +07:00
|
|
|
if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
|
|
|
|
struct in_pktinfo info;
|
|
|
|
|
2009-10-15 13:30:45 +07:00
|
|
|
info.ipi_addr.s_addr = inet->inet_rcv_saddr;
|
|
|
|
info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr;
|
2007-03-09 11:44:43 +07:00
|
|
|
info.ipi_ifindex = inet->mc_index;
|
|
|
|
put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
if (inet->cmsg_flags & IP_CMSG_TTL) {
|
|
|
|
int hlim = inet->mc_ttl;
|
|
|
|
put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
|
|
|
|
}
|
2012-02-09 16:35:49 +07:00
|
|
|
if (inet->cmsg_flags & IP_CMSG_TOS) {
|
|
|
|
int tos = inet->rcv_tos;
|
|
|
|
put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
|
|
|
|
}
|
2007-03-09 11:44:43 +07:00
|
|
|
len -= msg.msg_controllen;
|
|
|
|
return put_user(len, optlen);
|
|
|
|
}
|
|
|
|
case IP_FREEBIND:
|
|
|
|
val = inet->freebind;
|
|
|
|
break;
|
2008-10-01 21:30:02 +07:00
|
|
|
case IP_TRANSPARENT:
|
|
|
|
val = inet->transparent;
|
|
|
|
break;
|
2010-01-12 07:28:01 +07:00
|
|
|
case IP_MINTTL:
|
|
|
|
val = inet->min_ttl;
|
|
|
|
break;
|
2007-03-09 11:44:43 +07:00
|
|
|
default:
|
|
|
|
release_sock(sk);
|
|
|
|
return -ENOPROTOOPT;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
release_sock(sk);
|
2007-02-09 21:24:47 +07:00
|
|
|
|
2009-06-02 14:42:16 +07:00
|
|
|
if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned char ucval = (unsigned char)val;
|
|
|
|
len = 1;
|
2007-03-09 11:44:43 +07:00
|
|
|
if (put_user(len, optlen))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
2008-11-03 15:27:11 +07:00
|
|
|
if (copy_to_user(optval, &ucval, 1))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
|
|
|
} else {
|
|
|
|
len = min_t(unsigned int, sizeof(int), len);
|
2007-03-09 11:44:43 +07:00
|
|
|
if (put_user(len, optlen))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
2008-11-03 15:27:11 +07:00
|
|
|
if (copy_to_user(optval, &val, len))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
return 0;
|
2015-11-04 06:41:16 +07:00
|
|
|
|
|
|
|
out:
|
|
|
|
release_sock(sk);
|
|
|
|
if (needs_rtnl)
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-03-21 13:45:21 +07:00
|
|
|
int ip_getsockopt(struct sock *sk, int level,
|
2007-03-09 11:44:43 +07:00
|
|
|
int optname, char __user *optval, int __user *optlen)
|
2006-03-21 13:45:21 +07:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2011-08-08 12:31:07 +07:00
|
|
|
err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
|
2006-03-21 13:45:21 +07:00
|
|
|
#ifdef CONFIG_NETFILTER
|
|
|
|
/* we need to exclude all possible ENOPROTOOPTs except default case */
|
2007-11-06 12:32:31 +07:00
|
|
|
if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
|
|
|
|
!ip_mroute_opt(optname)) {
|
2007-02-09 21:24:47 +07:00
|
|
|
int len;
|
2006-03-21 13:45:21 +07:00
|
|
|
|
2008-11-03 15:27:11 +07:00
|
|
|
if (get_user(len, optlen))
|
2006-03-21 13:45:21 +07:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
lock_sock(sk);
|
|
|
|
err = nf_getsockopt(sk, PF_INET, optname, optval,
|
|
|
|
&len);
|
|
|
|
release_sock(sk);
|
|
|
|
if (err >= 0)
|
|
|
|
err = put_user(len, optlen);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return err;
|
|
|
|
}
|
2009-06-02 14:42:16 +07:00
|
|
|
EXPORT_SYMBOL(ip_getsockopt);
|
2006-03-21 13:45:21 +07:00
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
2006-03-21 13:48:35 +07:00
|
|
|
int compat_ip_getsockopt(struct sock *sk, int level, int optname,
|
|
|
|
char __user *optval, int __user *optlen)
|
2006-03-21 13:45:21 +07:00
|
|
|
{
|
2008-04-29 17:23:22 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (optname == MCAST_MSFILTER)
|
|
|
|
return compat_mc_getsockopt(sk, level, optname, optval, optlen,
|
|
|
|
ip_getsockopt);
|
|
|
|
|
2011-08-08 12:31:07 +07:00
|
|
|
err = do_ip_getsockopt(sk, level, optname, optval, optlen,
|
|
|
|
MSG_CMSG_COMPAT);
|
2008-04-29 17:23:22 +07:00
|
|
|
|
2006-03-21 13:45:21 +07:00
|
|
|
#ifdef CONFIG_NETFILTER
|
|
|
|
/* we need to exclude all possible ENOPROTOOPTs except default case */
|
2007-11-06 12:32:31 +07:00
|
|
|
if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
|
|
|
|
!ip_mroute_opt(optname)) {
|
2007-02-09 21:24:47 +07:00
|
|
|
int len;
|
2006-03-21 13:45:21 +07:00
|
|
|
|
2006-03-21 13:48:35 +07:00
|
|
|
if (get_user(len, optlen))
|
2006-03-21 13:45:21 +07:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
lock_sock(sk);
|
2006-03-21 13:48:35 +07:00
|
|
|
err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
|
2006-03-21 13:45:21 +07:00
|
|
|
release_sock(sk);
|
|
|
|
if (err >= 0)
|
|
|
|
err = put_user(len, optlen);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return err;
|
|
|
|
}
|
2006-03-21 13:48:35 +07:00
|
|
|
EXPORT_SYMBOL(compat_ip_getsockopt);
|
2006-03-21 13:45:21 +07:00
|
|
|
#endif
|