Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/ip_output.c
Show First 20 Lines • Show All 949 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* IP socket option processing. | * IP socket option processing. | ||||
*/ | */ | ||||
int | int | ||||
ip_ctloutput(struct socket *so, struct sockopt *sopt) | ip_ctloutput(struct socket *so, struct sockopt *sopt) | ||||
{ | { | ||||
struct inpcb *inp = sotoinpcb(so); | struct inpcb *inp; | ||||
struct mbuf *options; | |||||
int error, optval; | int error, optval; | ||||
bool rlock = false; | |||||
bool wlock = false; | |||||
#ifdef RSS | #ifdef RSS | ||||
uint32_t rss_bucket; | uint32_t rss_bucket; | ||||
int retval; | int retval; | ||||
#endif | #endif | ||||
SOCK_LOCK(so); | |||||
inp = sotoinpcb(so); | |||||
if (inp == NULL) { | |||||
SOCK_UNLOCK(so); | |||||
return ENOTCONN; | |||||
} | |||||
in_pcbref(inp); | |||||
SOCK_UNLOCK(so); | |||||
error = optval = 0; | error = optval = 0; | ||||
if (sopt->sopt_level != IPPROTO_IP) { | if (sopt->sopt_level != IPPROTO_IP) { | ||||
error = EINVAL; | error = EINVAL; | ||||
if (sopt->sopt_level == SOL_SOCKET && | if (sopt->sopt_level == SOL_SOCKET && | ||||
sopt->sopt_dir == SOPT_SET) { | sopt->sopt_dir == SOPT_SET) { | ||||
switch (sopt->sopt_name) { | switch (sopt->sopt_name) { | ||||
case SO_REUSEADDR: | case SO_REUSEADDR: | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
wlock = true; | |||||
if ((so->so_options & SO_REUSEADDR) != 0) | if ((so->so_options & SO_REUSEADDR) != 0) | ||||
inp->inp_flags2 |= INP_REUSEADDR; | inp->inp_flags2 |= INP_REUSEADDR; | ||||
else | else | ||||
inp->inp_flags2 &= ~INP_REUSEADDR; | inp->inp_flags2 &= ~INP_REUSEADDR; | ||||
INP_WUNLOCK(inp); | |||||
error = 0; | error = 0; | ||||
break; | break; | ||||
case SO_REUSEPORT: | case SO_REUSEPORT: | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
wlock = true; | |||||
if ((so->so_options & SO_REUSEPORT) != 0) | if ((so->so_options & SO_REUSEPORT) != 0) | ||||
inp->inp_flags2 |= INP_REUSEPORT; | inp->inp_flags2 |= INP_REUSEPORT; | ||||
else | else | ||||
inp->inp_flags2 &= ~INP_REUSEPORT; | inp->inp_flags2 &= ~INP_REUSEPORT; | ||||
INP_WUNLOCK(inp); | |||||
error = 0; | error = 0; | ||||
break; | break; | ||||
case SO_SETFIB: | case SO_SETFIB: | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
wlock = true; | |||||
inp->inp_inc.inc_fibnum = so->so_fibnum; | inp->inp_inc.inc_fibnum = so->so_fibnum; | ||||
INP_WUNLOCK(inp); | |||||
error = 0; | error = 0; | ||||
break; | break; | ||||
case SO_MAX_PACING_RATE: | case SO_MAX_PACING_RATE: | ||||
#ifdef RATELIMIT | #ifdef RATELIMIT | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
wlock = true; | |||||
inp->inp_flags2 |= INP_RATE_LIMIT_CHANGED; | inp->inp_flags2 |= INP_RATE_LIMIT_CHANGED; | ||||
INP_WUNLOCK(inp); | |||||
error = 0; | error = 0; | ||||
#else | #else | ||||
error = EOPNOTSUPP; | error = EOPNOTSUPP; | ||||
#endif | #endif | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (!wlock) | |||||
INP_WLOCK(inp); | |||||
if (!in_pcbrele_wlocked(inp)) | |||||
INP_WUNLOCK(inp); | |||||
return (error); | return (error); | ||||
} | } | ||||
switch (sopt->sopt_dir) { | switch (sopt->sopt_dir) { | ||||
case SOPT_SET: | case SOPT_SET: | ||||
switch (sopt->sopt_name) { | switch (sopt->sopt_name) { | ||||
case IP_OPTIONS: | case IP_OPTIONS: | ||||
#ifdef notyet | #ifdef notyet | ||||
Show All 13 Lines | #endif | ||||
m->m_len = sopt->sopt_valsize; | m->m_len = sopt->sopt_valsize; | ||||
error = sooptcopyin(sopt, mtod(m, char *), m->m_len, | error = sooptcopyin(sopt, mtod(m, char *), m->m_len, | ||||
m->m_len); | m->m_len); | ||||
if (error) { | if (error) { | ||||
m_free(m); | m_free(m); | ||||
break; | break; | ||||
} | } | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
wlock = true; | |||||
error = ip_pcbopts(inp, sopt->sopt_name, m); | error = ip_pcbopts(inp, sopt->sopt_name, m); | ||||
INP_WUNLOCK(inp); | break; | ||||
return (error); | |||||
} | } | ||||
case IP_BINDANY: | case IP_BINDANY: | ||||
if (sopt->sopt_td != NULL) { | if (sopt->sopt_td != NULL) { | ||||
error = priv_check(sopt->sopt_td, | error = priv_check(sopt->sopt_td, | ||||
PRIV_NETINET_BINDANY); | PRIV_NETINET_BINDANY); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
Show All 37 Lines | #endif | ||||
if (optval >= 0 && optval <= MAXTTL) | if (optval >= 0 && optval <= MAXTTL) | ||||
inp->inp_ip_minttl = optval; | inp->inp_ip_minttl = optval; | ||||
else | else | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
#define OPTSET(bit) do { \ | #define OPTSET(bit) do { \ | ||||
INP_WLOCK(inp); \ | INP_WLOCK(inp); \ | ||||
wlock = true; \ | |||||
if (optval) \ | if (optval) \ | ||||
inp->inp_flags |= bit; \ | inp->inp_flags |= bit; \ | ||||
else \ | else \ | ||||
inp->inp_flags &= ~bit; \ | inp->inp_flags &= ~bit; \ | ||||
INP_WUNLOCK(inp); \ | |||||
} while (0) | } while (0) | ||||
#define OPTSET2(bit, val) do { \ | #define OPTSET2(bit, val) do { \ | ||||
INP_WLOCK(inp); \ | INP_WLOCK(inp); \ | ||||
wlock = true; \ | |||||
if (val) \ | if (val) \ | ||||
inp->inp_flags2 |= bit; \ | inp->inp_flags2 |= bit; \ | ||||
else \ | else \ | ||||
inp->inp_flags2 &= ~bit; \ | inp->inp_flags2 &= ~bit; \ | ||||
INP_WUNLOCK(inp); \ | |||||
} while (0) | } while (0) | ||||
case IP_RECVOPTS: | case IP_RECVOPTS: | ||||
OPTSET(INP_RECVOPTS); | OPTSET(INP_RECVOPTS); | ||||
break; | break; | ||||
case IP_RECVRETOPTS: | case IP_RECVRETOPTS: | ||||
OPTSET(INP_RECVRETOPTS); | OPTSET(INP_RECVRETOPTS); | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | #undef OPTSET2 | ||||
case IP_PORTRANGE: | case IP_PORTRANGE: | ||||
error = sooptcopyin(sopt, &optval, sizeof optval, | error = sooptcopyin(sopt, &optval, sizeof optval, | ||||
sizeof optval); | sizeof optval); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
wlock = true; | |||||
switch (optval) { | switch (optval) { | ||||
case IP_PORTRANGE_DEFAULT: | case IP_PORTRANGE_DEFAULT: | ||||
inp->inp_flags &= ~(INP_LOWPORT); | inp->inp_flags &= ~(INP_LOWPORT); | ||||
inp->inp_flags &= ~(INP_HIGHPORT); | inp->inp_flags &= ~(INP_HIGHPORT); | ||||
break; | break; | ||||
case IP_PORTRANGE_HIGH: | case IP_PORTRANGE_HIGH: | ||||
inp->inp_flags &= ~(INP_LOWPORT); | inp->inp_flags &= ~(INP_LOWPORT); | ||||
inp->inp_flags |= INP_HIGHPORT; | inp->inp_flags |= INP_HIGHPORT; | ||||
break; | break; | ||||
case IP_PORTRANGE_LOW: | case IP_PORTRANGE_LOW: | ||||
inp->inp_flags &= ~(INP_HIGHPORT); | inp->inp_flags &= ~(INP_HIGHPORT); | ||||
inp->inp_flags |= INP_LOWPORT; | inp->inp_flags |= INP_LOWPORT; | ||||
break; | break; | ||||
default: | default: | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
INP_WUNLOCK(inp); | |||||
break; | break; | ||||
#if defined(IPSEC) || defined(IPSEC_SUPPORT) | #if defined(IPSEC) || defined(IPSEC_SUPPORT) | ||||
case IP_IPSEC_POLICY: | case IP_IPSEC_POLICY: | ||||
if (IPSEC_ENABLED(ipv4)) { | if (IPSEC_ENABLED(ipv4)) { | ||||
INP_WLOCK(inp); | |||||
wlock = true; | |||||
error = IPSEC_PCBCTL(ipv4, inp, sopt); | error = IPSEC_PCBCTL(ipv4, inp, sopt); | ||||
if (error) | |||||
wlock = false; | |||||
break; | break; | ||||
} | } | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
#endif /* IPSEC */ | #endif /* IPSEC */ | ||||
default: | default: | ||||
error = ENOPROTOOPT; | error = ENOPROTOOPT; | ||||
break; | break; | ||||
} | } | ||||
break; | break; | ||||
case SOPT_GET: | case SOPT_GET: | ||||
switch (sopt->sopt_name) { | switch (sopt->sopt_name) { | ||||
case IP_OPTIONS: | case IP_OPTIONS: | ||||
case IP_RETOPTS: | case IP_RETOPTS: | ||||
if (inp->inp_options) | if (inp->inp_options) { | ||||
ae: Is it necessary to acquire wlock for getsockopt? | |||||
Not Done Inline ActionsFor some but not all. I'll take a second pass at getsockopt. jason_eggnet.com: For some but not all. I'll take a second pass at getsockopt. | |||||
options = malloc(sopt->sopt_valsize, M_TEMP, M_WAITOK|M_ZERO); | |||||
INP_RLOCK(inp); | |||||
bcopy(inp->inp_options, options, min(inp->inp_options->m_len, sopt->sopt_valsize)); | |||||
if (!in_pcbrele_rlocked(inp)) | |||||
INP_RUNLOCK(inp); | |||||
error = sooptcopyout(sopt, | error = sooptcopyout(sopt, | ||||
mtod(inp->inp_options, | mtod(options, | ||||
char *), | char *), | ||||
inp->inp_options->m_len); | options->m_len); | ||||
else | return (error); | ||||
} else | |||||
sopt->sopt_valsize = 0; | sopt->sopt_valsize = 0; | ||||
break; | break; | ||||
case IP_TOS: | case IP_TOS: | ||||
case IP_TTL: | case IP_TTL: | ||||
case IP_MINTTL: | case IP_MINTTL: | ||||
case IP_RECVOPTS: | case IP_RECVOPTS: | ||||
case IP_RECVRETOPTS: | case IP_RECVRETOPTS: | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | #define OPTBIT2(bit) (inp->inp_flags2 & bit ? 1 : 0) | ||||
optval = OPTBIT(INP_RECVTTL); | optval = OPTBIT(INP_RECVTTL); | ||||
break; | break; | ||||
case IP_RECVIF: | case IP_RECVIF: | ||||
optval = OPTBIT(INP_RECVIF); | optval = OPTBIT(INP_RECVIF); | ||||
break; | break; | ||||
case IP_PORTRANGE: | case IP_PORTRANGE: | ||||
INP_RLOCK(inp); | |||||
rlock = true; | |||||
if (inp->inp_flags & INP_HIGHPORT) | if (inp->inp_flags & INP_HIGHPORT) | ||||
optval = IP_PORTRANGE_HIGH; | optval = IP_PORTRANGE_HIGH; | ||||
else if (inp->inp_flags & INP_LOWPORT) | else if (inp->inp_flags & INP_LOWPORT) | ||||
optval = IP_PORTRANGE_LOW; | optval = IP_PORTRANGE_LOW; | ||||
else | else | ||||
optval = 0; | optval = 0; | ||||
break; | break; | ||||
Show All 15 Lines | #define OPTBIT2(bit) (inp->inp_flags2 & bit ? 1 : 0) | ||||
case IP_FLOWTYPE: | case IP_FLOWTYPE: | ||||
optval = inp->inp_flowtype; | optval = inp->inp_flowtype; | ||||
break; | break; | ||||
case IP_RECVFLOWID: | case IP_RECVFLOWID: | ||||
optval = OPTBIT2(INP_RECVFLOWID); | optval = OPTBIT2(INP_RECVFLOWID); | ||||
break; | break; | ||||
#ifdef RSS | #ifdef RSS | ||||
case IP_RSSBUCKETID: | case IP_RSSBUCKETID: | ||||
INP_RLOCK(inp); | |||||
rlock = true; | |||||
retval = rss_hash2bucket(inp->inp_flowid, | retval = rss_hash2bucket(inp->inp_flowid, | ||||
inp->inp_flowtype, | inp->inp_flowtype, | ||||
&rss_bucket); | &rss_bucket); | ||||
if (retval == 0) | if (retval == 0) | ||||
optval = rss_bucket; | optval = rss_bucket; | ||||
else | else | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
case IP_RECVRSSBUCKETID: | case IP_RECVRSSBUCKETID: | ||||
optval = OPTBIT2(INP_RECVRSSBUCKETID); | optval = OPTBIT2(INP_RECVRSSBUCKETID); | ||||
break; | break; | ||||
#endif | #endif | ||||
case IP_BINDMULTI: | case IP_BINDMULTI: | ||||
optval = OPTBIT2(INP_BINDMULTI); | optval = OPTBIT2(INP_BINDMULTI); | ||||
break; | break; | ||||
} | } | ||||
if (!rlock) | |||||
INP_RLOCK(inp); | |||||
if (!in_pcbrele_rlocked(inp)) | |||||
INP_RUNLOCK(inp); | |||||
error = sooptcopyout(sopt, &optval, sizeof optval); | error = sooptcopyout(sopt, &optval, sizeof optval); | ||||
break; | return (error); | ||||
/* | /* | ||||
* Multicast socket options are processed by the in_mcast | * Multicast socket options are processed by the in_mcast | ||||
* module. | * module. | ||||
*/ | */ | ||||
case IP_MULTICAST_IF: | case IP_MULTICAST_IF: | ||||
case IP_MULTICAST_VIF: | case IP_MULTICAST_VIF: | ||||
case IP_MULTICAST_TTL: | case IP_MULTICAST_TTL: | ||||
case IP_MULTICAST_LOOP: | case IP_MULTICAST_LOOP: | ||||
case IP_MSFILTER: | case IP_MSFILTER: | ||||
error = inp_getmoptions(inp, sopt); | error = inp_getmoptions(inp, sopt); | ||||
break; | break; | ||||
#if defined(IPSEC) || defined(IPSEC_SUPPORT) | #if defined(IPSEC) || defined(IPSEC_SUPPORT) | ||||
case IP_IPSEC_POLICY: | case IP_IPSEC_POLICY: | ||||
if (IPSEC_ENABLED(ipv4)) { | if (IPSEC_ENABLED(ipv4)) { | ||||
INP_WLOCK(inp); | |||||
error = IPSEC_PCBCTL(ipv4, inp, sopt); | error = IPSEC_PCBCTL(ipv4, inp, sopt); | ||||
INP_WUNLOCK(inp); | |||||
break; | break; | ||||
} | } | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
#endif /* IPSEC */ | #endif /* IPSEC */ | ||||
default: | default: | ||||
error = ENOPROTOOPT; | error = ENOPROTOOPT; | ||||
break; | break; | ||||
} | } | ||||
break; | break; | ||||
} | |||||
if (wlock && !in_pcbrele_wlocked(inp)) | |||||
INP_WUNLOCK(inp); | |||||
else { | |||||
if (!rlock) | |||||
INP_RLOCK(inp); | |||||
if (!in_pcbrele_rlocked(inp)) | |||||
INP_RUNLOCK(inp); | |||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Routine called from ip_output() to loop back a copy of an IP multicast | * Routine called from ip_output() to loop back a copy of an IP multicast | ||||
* packet to the input queue of a specified interface. Note that this | * packet to the input queue of a specified interface. Note that this | ||||
* calls the output routine of the loopback "driver", but with an interface | * calls the output routine of the loopback "driver", but with an interface | ||||
Show All 35 Lines |
Is it necessary to acquire wlock for getsockopt?