sync with OpenBSD -current

This commit is contained in:
purplerain 2023-12-08 04:20:46 +00:00
parent f7289b5181
commit 880da5e235
Signed by: purplerain
GPG key ID: F42C07F07E2E35B7
21 changed files with 651 additions and 157 deletions

View file

@ -1,4 +1,4 @@
/* $OpenBSD: in_pcb.c,v 1.281 2023/12/03 20:24:17 bluhm Exp $ */
/* $OpenBSD: in_pcb.c,v 1.282 2023/12/07 16:08:30 bluhm Exp $ */
/* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */
/*
@ -131,6 +131,13 @@ uint64_t in_pcbhash(struct inpcbtable *, u_int,
const struct in_addr *, u_short, const struct in_addr *, u_short);
uint64_t in_pcblhash(struct inpcbtable *, u_int, u_short);
struct inpcb *in_pcblookup_lock(struct inpcbtable *, struct in_addr, u_int,
struct in_addr, u_int, u_int, int);
int in_pcbaddrisavail_lock(struct inpcb *, struct sockaddr_in *, int,
struct proc *, int);
int in_pcbpickport(u_int16_t *, const void *, int, const struct inpcb *,
struct proc *);
/*
* in_pcb is used for inet and inet6. in6_pcb only contains special
* IPv6 cases. So the internet initializer is used for both domains.
@ -269,9 +276,8 @@ in_pcballoc(struct socket *so, struct inpcbtable *table, int wait)
}
int
in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p)
in_pcbbind_locked(struct inpcb *inp, struct mbuf *nam, struct proc *p)
{
struct inpcbtable *table = inp->inp_table;
struct socket *so = inp->inp_socket;
u_int16_t lport = 0;
int wild = 0;
@ -297,7 +303,8 @@ in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p)
if ((error = in6_nam2sin6(nam, &sin6)))
return (error);
if ((error = in6_pcbaddrisavail(inp, sin6, wild, p)))
if ((error = in6_pcbaddrisavail_lock(inp, sin6, wild,
p, IN_PCBLOCK_HOLD)))
return (error);
laddr = &sin6->sin6_addr;
lport = sin6->sin6_port;
@ -313,7 +320,8 @@ in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p)
if ((error = in_nam2sin(nam, &sin)))
return (error);
if ((error = in_pcbaddrisavail(inp, sin, wild, p)))
if ((error = in_pcbaddrisavail_lock(inp, sin, wild,
p, IN_PCBLOCK_HOLD)))
return (error);
laddr = &sin->sin_addr;
lport = sin->sin_port;
@ -337,16 +345,28 @@ in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p)
inp->inp_laddr = *(struct in_addr *)laddr;
}
inp->inp_lport = lport;
mtx_enter(&table->inpt_mtx);
in_pcbrehash(inp);
mtx_leave(&table->inpt_mtx);
return (0);
}
int
in_pcbaddrisavail(struct inpcb *inp, struct sockaddr_in *sin, int wild,
struct proc *p)
in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p)
{
struct inpcbtable *table = inp->inp_table;
int error;
/* keep lookup, modification, and rehash in sync */
mtx_enter(&table->inpt_mtx);
error = in_pcbbind_locked(inp, nam, p);
mtx_leave(&table->inpt_mtx);
return error;
}
int
in_pcbaddrisavail_lock(struct inpcb *inp, struct sockaddr_in *sin, int wild,
struct proc *p, int lock)
{
struct socket *so = inp->inp_socket;
struct inpcbtable *table = inp->inp_table;
@ -393,19 +413,21 @@ in_pcbaddrisavail(struct inpcb *inp, struct sockaddr_in *sin, int wild,
int error = 0;
if (so->so_euid && !IN_MULTICAST(sin->sin_addr.s_addr)) {
t = in_pcblookup_local(table, &sin->sin_addr, lport,
INPLOOKUP_WILDCARD, inp->inp_rtableid);
t = in_pcblookup_local_lock(table, &sin->sin_addr,
lport, INPLOOKUP_WILDCARD, inp->inp_rtableid, lock);
if (t && (so->so_euid != t->inp_socket->so_euid))
error = EADDRINUSE;
in_pcbunref(t);
if (lock == IN_PCBLOCK_GRAB)
in_pcbunref(t);
if (error)
return (error);
}
t = in_pcblookup_local(table, &sin->sin_addr, lport,
wild, inp->inp_rtableid);
t = in_pcblookup_local_lock(table, &sin->sin_addr, lport,
wild, inp->inp_rtableid, lock);
if (t && (reuseport & t->inp_socket->so_options) == 0)
error = EADDRINUSE;
in_pcbunref(t);
if (lock == IN_PCBLOCK_GRAB)
in_pcbunref(t);
if (error)
return (error);
}
@ -413,6 +435,13 @@ in_pcbaddrisavail(struct inpcb *inp, struct sockaddr_in *sin, int wild,
return (0);
}
int
in_pcbaddrisavail(struct inpcb *inp, struct sockaddr_in *sin, int wild,
struct proc *p)
{
return in_pcbaddrisavail_lock(inp, sin, wild, p, IN_PCBLOCK_GRAB);
}
int
in_pcbpickport(u_int16_t *lport, const void *laddr, int wild,
const struct inpcb *inp, struct proc *p)
@ -423,6 +452,8 @@ in_pcbpickport(u_int16_t *lport, const void *laddr, int wild,
u_int16_t first, last, lower, higher, candidate, localport;
int count;
MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
if (inp->inp_flags & INP_HIGHPORT) {
first = ipport_hifirstauto; /* sysctl */
last = ipport_hilastauto;
@ -451,9 +482,7 @@ in_pcbpickport(u_int16_t *lport, const void *laddr, int wild,
count = higher - lower;
candidate = lower + arc4random_uniform(count);
t = NULL;
do {
in_pcbunref(t);
do {
if (count-- < 0) /* completely used? */
return (EADDRNOTAVAIL);
@ -462,8 +491,8 @@ in_pcbpickport(u_int16_t *lport, const void *laddr, int wild,
candidate = lower;
localport = htons(candidate);
} while (in_baddynamic(candidate, so->so_proto->pr_protocol));
t = in_pcblookup_local(table, laddr, localport, wild,
inp->inp_rtableid);
t = in_pcblookup_local_lock(table, laddr, localport, wild,
inp->inp_rtableid, IN_PCBLOCK_HOLD);
} while (t != NULL);
*lport = localport;
@ -498,10 +527,13 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam)
if (error)
return (error);
t = in_pcblookup(inp->inp_table, sin->sin_addr, sin->sin_port,
ina, inp->inp_lport, inp->inp_rtableid);
/* keep lookup, modification, and rehash in sync */
mtx_enter(&table->inpt_mtx);
t = in_pcblookup_lock(inp->inp_table, sin->sin_addr, sin->sin_port,
ina, inp->inp_lport, inp->inp_rtableid, IN_PCBLOCK_HOLD);
if (t != NULL) {
in_pcbunref(t);
mtx_leave(&table->inpt_mtx);
return (EADDRINUSE);
}
@ -509,15 +541,17 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam)
if (inp->inp_laddr.s_addr == INADDR_ANY) {
if (inp->inp_lport == 0) {
error = in_pcbbind(inp, NULL, curproc);
if (error)
error = in_pcbbind_locked(inp, NULL, curproc);
if (error) {
mtx_leave(&table->inpt_mtx);
return (error);
t = in_pcblookup(inp->inp_table, sin->sin_addr,
}
t = in_pcblookup_lock(inp->inp_table, sin->sin_addr,
sin->sin_port, ina, inp->inp_lport,
inp->inp_rtableid);
inp->inp_rtableid, IN_PCBLOCK_HOLD);
if (t != NULL) {
inp->inp_lport = 0;
in_pcbunref(t);
mtx_leave(&table->inpt_mtx);
return (EADDRINUSE);
}
}
@ -525,8 +559,8 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam)
}
inp->inp_faddr = sin->sin_addr;
inp->inp_fport = sin->sin_port;
mtx_enter(&table->inpt_mtx);
in_pcbrehash(inp);
mtx_leave(&table->inpt_mtx);
#if NSTOEPLITZ > 0
@ -539,6 +573,11 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam)
void
in_pcbdisconnect(struct inpcb *inp)
{
/*
* XXXSMP pf lock sleeps, so we cannot use table->inpt_mtx
* to keep inp_pf_sk in sync with pcb. Use net lock for now.
*/
NET_ASSERT_LOCKED_EXCLUSIVE();
#if NPF > 0
if (inp->inp_pf_sk) {
pf_remove_divert_state(inp->inp_pf_sk);
@ -576,6 +615,12 @@ in_pcbdetach(struct inpcb *inp)
} else
#endif
ip_freemoptions(inp->inp_moptions);
/*
* XXXSMP pf lock sleeps, so we cannot use table->inpt_mtx
* to keep inp_pf_sk in sync with pcb. Use net lock for now.
*/
NET_ASSERT_LOCKED_EXCLUSIVE();
#if NPF > 0
if (inp->inp_pf_sk) {
pf_remove_divert_state(inp->inp_pf_sk);
@ -791,8 +836,8 @@ in_rtchange(struct inpcb *inp, int errno)
}
struct inpcb *
in_pcblookup_local(struct inpcbtable *table, const void *laddrp,
u_int lport_arg, int flags, u_int rtable)
in_pcblookup_local_lock(struct inpcbtable *table, const void *laddrp,
u_int lport_arg, int flags, u_int rtable, int lock)
{
struct inpcb *inp, *match = NULL;
int matchwild = 3, wildcard;
@ -808,7 +853,12 @@ in_pcblookup_local(struct inpcbtable *table, const void *laddrp,
rdomain = rtable_l2(rtable);
lhash = in_pcblhash(table, rdomain, lport);
mtx_enter(&table->inpt_mtx);
if (lock == IN_PCBLOCK_GRAB) {
mtx_enter(&table->inpt_mtx);
} else {
KASSERT(lock == IN_PCBLOCK_HOLD);
MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
}
head = &table->inpt_lhashtbl[lhash & table->inpt_lmask];
LIST_FOREACH(inp, head, inp_lhash) {
if (rtable_l2(inp->inp_rtableid) != rdomain)
@ -859,8 +909,10 @@ in_pcblookup_local(struct inpcbtable *table, const void *laddrp,
break;
}
}
in_pcbref(match);
mtx_leave(&table->inpt_mtx);
if (lock == IN_PCBLOCK_GRAB) {
in_pcbref(match);
mtx_leave(&table->inpt_mtx);
}
return (match);
}
@ -1029,10 +1081,6 @@ in_pcbselsrc(struct in_addr *insrc, struct sockaddr_in *sin,
void
in_pcbrehash(struct inpcb *inp)
{
struct inpcbtable *table = inp->inp_table;
MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
LIST_REMOVE(inp, inp_lhash);
LIST_REMOVE(inp, inp_hash);
in_pcbhash_insert(inp);
@ -1154,8 +1202,8 @@ int in_pcbnotifymiss = 0;
* After those two lookups no other are necessary.
*/
struct inpcb *
in_pcblookup(struct inpcbtable *table, struct in_addr faddr,
u_int fport, struct in_addr laddr, u_int lport, u_int rtable)
in_pcblookup_lock(struct inpcbtable *table, struct in_addr faddr,
u_int fport, struct in_addr laddr, u_int lport, u_int rtable, int lock)
{
struct inpcb *inp;
uint64_t hash;
@ -1164,11 +1212,18 @@ in_pcblookup(struct inpcbtable *table, struct in_addr faddr,
rdomain = rtable_l2(rtable);
hash = in_pcbhash(table, rdomain, &faddr, fport, &laddr, lport);
mtx_enter(&table->inpt_mtx);
if (lock == IN_PCBLOCK_GRAB) {
mtx_enter(&table->inpt_mtx);
} else {
KASSERT(lock == IN_PCBLOCK_HOLD);
MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
}
inp = in_pcbhash_lookup(table, hash, rdomain,
&faddr, fport, &laddr, lport);
in_pcbref(inp);
mtx_leave(&table->inpt_mtx);
if (lock == IN_PCBLOCK_GRAB) {
in_pcbref(inp);
mtx_leave(&table->inpt_mtx);
}
#ifdef DIAGNOSTIC
if (inp == NULL && in_pcbnotifymiss) {
@ -1180,6 +1235,14 @@ in_pcblookup(struct inpcbtable *table, struct in_addr faddr,
return (inp);
}
struct inpcb *
in_pcblookup(struct inpcbtable *table, struct in_addr faddr,
u_int fport, struct in_addr laddr, u_int lport, u_int rtable)
{
return in_pcblookup_lock(table, faddr, fport, laddr, lport, rtable,
IN_PCBLOCK_GRAB);
}
/*
* The in(6)_pcblookup_listen functions are used to locate listening
* sockets quickly. This are sockets with unspecified foreign address