Split out most of the logic from in_pcbbind() into a new function

called in_pcbbind_setup() that does everything except commit the
changes to the PCB. There should be no functional change here, but
in_pcbbind_setup() will be used by the soon-to-appear IP_SENDSRCADDR
control message implementation to check or allocate the source
address and port.

Discussed on:	-net
Approved by:	re
This commit is contained in:
Ian Dowse 2002-10-20 21:44:31 +00:00
parent 54f937adb1
commit 4b932371f4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=105565
2 changed files with 66 additions and 36 deletions

View File

@ -189,17 +189,56 @@ in_pcbbind(inp, nam, td)
struct sockaddr *nam;
struct thread *td;
{
register struct socket *so = inp->inp_socket;
int anonport, error;
if (inp->inp_lport != 0 || inp->inp_laddr.s_addr != INADDR_ANY)
return (EINVAL);
anonport = inp->inp_lport == 0 && (nam == NULL ||
((struct sockaddr_in *)nam)->sin_port == 0);
error = in_pcbbind_setup(inp, nam, &inp->inp_laddr.s_addr,
&inp->inp_lport, td);
if (error)
return (error);
if (in_pcbinshash(inp) != 0) {
inp->inp_laddr.s_addr = INADDR_ANY;
inp->inp_lport = 0;
return (EAGAIN);
}
if (anonport)
inp->inp_flags |= INP_ANONPORT;
return (0);
}
/*
* Set up a bind operation on a PCB, performing port allocation
* as required, but do not actually modify the PCB. Callers can
* either complete the bind by setting inp_laddr/inp_lport and
* calling in_pcbinshash(), or they can just use the resulting
* port and address to authorise the sending of a once-off packet.
*
* On error, the values of *laddrp and *lportp are not changed.
*/
int
in_pcbbind_setup(inp, nam, laddrp, lportp, td)
struct inpcb *inp;
struct sockaddr *nam;
in_addr_t *laddrp;
u_short *lportp;
struct thread *td;
{
struct socket *so = inp->inp_socket;
unsigned short *lastport;
struct sockaddr_in *sin;
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
struct in_addr laddr;
u_short lport = 0;
int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
int error, prison = 0;
if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */
return (EADDRNOTAVAIL);
if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
laddr.s_addr = *laddrp;
if (nam != NULL && laddr.s_addr != INADDR_ANY)
return (EINVAL);
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
wild = 1;
@ -218,7 +257,13 @@ in_pcbbind(inp, nam, td)
if (sin->sin_addr.s_addr != INADDR_ANY)
if (prison_ip(td->td_ucred, 0, &sin->sin_addr.s_addr))
return(EINVAL);
lport = sin->sin_port;
if (sin->sin_port != *lportp) {
/* Don't allow the port to change. */
if (*lportp != 0)
return (EINVAL);
lport = sin->sin_port;
}
/* NB: lport is left as 0 if the port isn't being changed. */
if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
/*
* Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
@ -235,6 +280,7 @@ in_pcbbind(inp, nam, td)
if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
return (EADDRNOTAVAIL);
}
laddr = sin->sin_addr;
if (lport) {
struct inpcb *t;
/* GROSS */
@ -284,28 +330,25 @@ in_pcbbind(inp, nam, td)
return (EADDRINUSE);
}
}
inp->inp_laddr = sin->sin_addr;
}
if (*lportp != 0)
lport = *lportp;
if (lport == 0) {
ushort first, last;
int count;
if (inp->inp_laddr.s_addr != INADDR_ANY)
if (prison_ip(td->td_ucred, 0, &inp->inp_laddr.s_addr )) {
inp->inp_laddr.s_addr = INADDR_ANY;
if (laddr.s_addr != INADDR_ANY)
if (prison_ip(td->td_ucred, 0, &laddr.s_addr))
return (EINVAL);
}
inp->inp_flags |= INP_ANONPORT;
if (inp->inp_flags & INP_HIGHPORT) {
first = ipport_hifirstauto; /* sysctl */
last = ipport_hilastauto;
lastport = &pcbinfo->lasthi;
} else if (inp->inp_flags & INP_LOWPORT) {
if (td && (error = suser_cred(td->td_ucred, PRISON_ROOT))) {
inp->inp_laddr.s_addr = INADDR_ANY;
if (td && (error = suser_cred(td->td_ucred,
PRISON_ROOT)) != 0)
return error;
}
first = ipport_lowfirstauto; /* 1023 */
last = ipport_lowlastauto; /* 600 */
lastport = &pcbinfo->lastlow;
@ -328,16 +371,14 @@ in_pcbbind(inp, nam, td)
count = first - last;
do {
if (count-- < 0) { /* completely used? */
inp->inp_laddr.s_addr = INADDR_ANY;
if (count-- < 0) /* completely used? */
return (EADDRNOTAVAIL);
}
--*lastport;
if (*lastport > first || *lastport < last)
*lastport = first;
lport = htons(*lastport);
} while (in_pcblookup_local(pcbinfo,
inp->inp_laddr, lport, wild));
} while (in_pcblookup_local(pcbinfo, laddr, lport,
wild));
} else {
/*
* counting up
@ -345,33 +386,20 @@ in_pcbbind(inp, nam, td)
count = last - first;
do {
if (count-- < 0) { /* completely used? */
/*
* Undo any address bind that may have
* occurred above.
*/
inp->inp_laddr.s_addr = INADDR_ANY;
if (count-- < 0) /* completely used? */
return (EADDRNOTAVAIL);
}
++*lastport;
if (*lastport < first || *lastport > last)
*lastport = first;
lport = htons(*lastport);
} while (in_pcblookup_local(pcbinfo,
inp->inp_laddr, lport, wild));
} while (in_pcblookup_local(pcbinfo, laddr, lport,
wild));
}
}
inp->inp_lport = lport;
if (prison_ip(td->td_ucred, 0, &inp->inp_laddr.s_addr)) {
inp->inp_laddr.s_addr = INADDR_ANY;
inp->inp_lport = 0;
if (prison_ip(td->td_ucred, 0, &laddr.s_addr))
return (EINVAL);
}
if (in_pcbinshash(inp) != 0) {
inp->inp_laddr.s_addr = INADDR_ANY;
inp->inp_lport = 0;
return (EAGAIN);
}
*laddrp = laddr.s_addr;
*lportp = lport;
return (0);
}

View File

@ -328,6 +328,8 @@ struct inpcb *
in_rtchange(struct inpcb *, int);
int in_pcballoc(struct socket *, struct inpcbinfo *, struct thread *);
int in_pcbbind(struct inpcb *, struct sockaddr *, struct thread *);
int in_pcbbind_setup(struct inpcb *, struct sockaddr *, in_addr_t *,
u_short *, struct thread *);
int in_pcbconnect(struct inpcb *, struct sockaddr *, struct thread *);
void in_pcbdetach(struct inpcb *);
void in_pcbdisconnect(struct inpcb *);