diff options
author | iedowse <iedowse@FreeBSD.org> | 2002-10-20 21:44:31 +0000 |
---|---|---|
committer | iedowse <iedowse@FreeBSD.org> | 2002-10-20 21:44:31 +0000 |
commit | 1b97e2dc441e3e2d9bc1df9086655dd77899b3fd (patch) | |
tree | ceaf77dd17b34312c742dc161a676427954f2c37 /sys/netinet | |
parent | 25eef24259f2674c89d56fc85b39a13ded23749f (diff) | |
download | FreeBSD-src-1b97e2dc441e3e2d9bc1df9086655dd77899b3fd.zip FreeBSD-src-1b97e2dc441e3e2d9bc1df9086655dd77899b3fd.tar.gz |
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
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/in_pcb.c | 100 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 2 |
2 files changed, 66 insertions, 36 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 8411fe8..5ea538a 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -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); } diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index f08151b..7f26114 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -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 *); |