Module Name: src Committed By: elad Date: Thu Apr 23 16:42:56 UTC 2009
Modified Files: src/sys/netinet: in_pcb.c Log Message: Some changes to in_pcbbind(): - Extract guts to in_pcbbind_{addr,port}() - Put the port auto-assignment logic in in_pcbsetport(), which looks very similar to in6_pcbsetport() - Fix a bug where "sin" was passed to kauth(9) without being set to anything No objections on tech-...@. To generate a diff of this commit: cvs rdiff -u -r1.131 -r1.132 src/sys/netinet/in_pcb.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netinet/in_pcb.c diff -u src/sys/netinet/in_pcb.c:1.131 src/sys/netinet/in_pcb.c:1.132 --- src/sys/netinet/in_pcb.c:1.131 Tue Apr 14 21:25:20 2009 +++ src/sys/netinet/in_pcb.c Thu Apr 23 16:42:56 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: in_pcb.c,v 1.131 2009/04/14 21:25:20 elad Exp $ */ +/* $NetBSD: in_pcb.c,v 1.132 2009/04/23 16:42:56 elad Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -91,7 +91,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: in_pcb.c,v 1.131 2009/04/14 21:25:20 elad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: in_pcb.c,v 1.132 2009/04/23 16:42:56 elad Exp $"); #include "opt_inet.h" #include "opt_ipsec.h" @@ -111,6 +111,7 @@ #include <sys/proc.h> #include <sys/kauth.h> #include <sys/uidinfo.h> +#include <sys/domain.h> #include <net/if.h> #include <net/route.h> @@ -222,35 +223,92 @@ return (0); } -int -in_pcbbind(void *v, struct mbuf *nam, struct lwp *l) +static int +in_pcbsetport(struct in_addr *laddr, struct inpcb *inp, + struct sockaddr_in *sin, kauth_cred_t cred) { - struct in_ifaddr *ia = NULL; - struct inpcb *inp = v; - struct socket *so = inp->inp_socket; struct inpcbtable *table = inp->inp_table; - struct sockaddr_in *sin = NULL; /* XXXGCC */ + struct socket *so = inp->inp_socket; + int cnt; + u_int16_t mymin, mymax; + u_int16_t *lastport; u_int16_t lport = 0; - int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); - kauth_cred_t cred = l->l_cred; - if (inp->inp_af != AF_INET) - return (EINVAL); + if (inp->inp_flags & INP_LOWPORT) { +#ifndef IPNOPRIVPORTS + if (kauth_authorize_network(cred, + KAUTH_NETWORK_BIND, + KAUTH_REQ_NETWORK_BIND_PRIVPORT, so, + sin, NULL)) + return (EACCES); +#endif + mymin = lowportmin; + mymax = lowportmax; + lastport = &table->inpt_lastlow; + } else { + mymin = anonportmin; + mymax = anonportmax; + lastport = &table->inpt_lastport; + } + if (mymin > mymax) { /* sanity check */ + u_int16_t swp; + + swp = mymin; + mymin = mymax; + mymax = swp; + } + + lport = *lastport - 1; + for (cnt = mymax - mymin + 1; cnt; cnt--, lport--) { + if (lport < mymin || lport > mymax) + lport = mymax; + if (!in_pcblookup_port(table, inp->inp_laddr, + htons(lport), 1)) + goto found; + } + + return (EAGAIN); + + found: + inp->inp_flags |= INP_ANONPORT; + *lastport = lport; + lport = htons(lport); + inp->inp_lport = lport; + in_pcbstate(inp, INP_BOUND); - if (TAILQ_FIRST(&in_ifaddrhead) == 0) - return (EADDRNOTAVAIL); - if (inp->inp_lport || !in_nullhost(inp->inp_laddr)) - return (EINVAL); - if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) - wild = 1; - if (nam == 0) - goto noname; - sin = mtod(nam, struct sockaddr_in *); - if (nam->m_len != sizeof (*sin)) - return (EINVAL); + return (0); +} + +static int +in_pcbbind_addr(struct inpcb *inp, struct sockaddr_in *sin, kauth_cred_t cred) +{ if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); - lport = sin->sin_port; + + if (!in_nullhost(sin->sin_addr)) { + struct in_ifaddr *ia = NULL; + + INADDR_TO_IA(sin->sin_addr, ia); + /* check for broadcast addresses */ + if (ia == NULL) + ia = ifatoia(ifa_ifwithaddr(sintosa(sin))); + if (ia == NULL) + return (EADDRNOTAVAIL); + } + + inp->inp_laddr = sin->sin_addr; + + return (0); +} + +static int +in_pcbbind_port(struct inpcb *inp, struct sockaddr_in *sin, kauth_cred_t cred) +{ + struct inpcbtable *table = inp->inp_table; + struct socket *so = inp->inp_socket; + int reuseport = (so->so_options & SO_REUSEPORT); + int wild = 0; + if (IN_MULTICAST(sin->sin_addr.s_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; @@ -261,23 +319,27 @@ */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; - } else if (!in_nullhost(sin->sin_addr)) { - INADDR_TO_IA(sin->sin_addr, ia); - /* check for broadcast addresses */ - if (ia == NULL) - ia = ifatoia(ifa_ifwithaddr(sintosa(sin))); - if (ia == NULL) - return (EADDRNOTAVAIL); - } - if (lport) { + } + + if (sin->sin_port == 0) { + int error; + + error = in_pcbsetport(&inp->inp_laddr, inp, sin, cred); + if (error) + return (error); + } else { struct inpcb *t; #ifdef INET6 struct in6pcb *t6; struct in6_addr mapped; #endif + + if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) + wild = 1; + #ifndef IPNOPRIVPORTS /* GROSS */ - if (ntohs(lport) < IPPORT_RESERVED && + if (ntohs(sin->sin_port) < IPPORT_RESERVED && kauth_authorize_network(cred, KAUTH_NETWORK_BIND, KAUTH_REQ_NETWORK_BIND_PRIVPORT, so, sin, @@ -289,12 +351,12 @@ mapped.s6_addr16[5] = 0xffff; memcpy(&mapped.s6_addr32[3], &sin->sin_addr, sizeof(mapped.s6_addr32[3])); - t6 = in6_pcblookup_port(table, &mapped, lport, wild); + t6 = in6_pcblookup_port(table, &mapped, sin->sin_port, wild); if (t6 && (reuseport & t6->in6p_socket->so_options) == 0) return (EADDRINUSE); #endif if (so->so_uidinfo->ui_uid && !IN_MULTICAST(sin->sin_addr.s_addr)) { - t = in_pcblookup_port(table, sin->sin_addr, lport, 1); + t = in_pcblookup_port(table, sin->sin_addr, sin->sin_port, 1); /* * XXX: investigate ramifications of loosening this * restriction so that as long as both ports have @@ -308,63 +370,58 @@ return (EADDRINUSE); } } - t = in_pcblookup_port(table, sin->sin_addr, lport, wild); + t = in_pcblookup_port(table, sin->sin_addr, sin->sin_port, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return (EADDRINUSE); - } - inp->inp_laddr = sin->sin_addr; - -noname: - if (lport == 0) { - int cnt; - u_int16_t mymin, mymax; - u_int16_t *lastport; - if (inp->inp_flags & INP_LOWPORT) { -#ifndef IPNOPRIVPORTS - if (kauth_authorize_network(cred, - KAUTH_NETWORK_BIND, - KAUTH_REQ_NETWORK_BIND_PRIVPORT, so, - sin, NULL)) - return (EACCES); -#endif - mymin = lowportmin; - mymax = lowportmax; - lastport = &table->inpt_lastlow; - } else { - mymin = anonportmin; - mymax = anonportmax; - lastport = &table->inpt_lastport; - } - if (mymin > mymax) { /* sanity check */ - u_int16_t swp; - - swp = mymin; - mymin = mymax; - mymax = swp; - } - - lport = *lastport - 1; - for (cnt = mymax - mymin + 1; cnt; cnt--, lport--) { - if (lport < mymin || lport > mymax) - lport = mymax; - if (!in_pcblookup_port(table, inp->inp_laddr, - htons(lport), 1)) - goto found; - } - if (!in_nullhost(inp->inp_laddr)) - inp->inp_laddr.s_addr = INADDR_ANY; - return (EAGAIN); - found: - inp->inp_flags |= INP_ANONPORT; - *lastport = lport; - lport = htons(lport); + inp->inp_lport = sin->sin_port; + in_pcbstate(inp, INP_BOUND); } - inp->inp_lport = lport; + LIST_REMOVE(&inp->inp_head, inph_lhash); LIST_INSERT_HEAD(INPCBHASH_PORT(table, inp->inp_lport), &inp->inp_head, inph_lhash); - in_pcbstate(inp, INP_BOUND); + + return (0); +} + +int +in_pcbbind(void *v, struct mbuf *nam, struct lwp *l) +{ + struct inpcb *inp = v; + struct sockaddr_in *sin = NULL; /* XXXGCC */ + int error; + + if (inp->inp_af != AF_INET) + return (EINVAL); + + if (TAILQ_FIRST(&in_ifaddrhead) == 0) + return (EADDRNOTAVAIL); + if (inp->inp_lport || !in_nullhost(inp->inp_laddr)) + return (EINVAL); + + if (nam != NULL) { + sin = mtod(nam, struct sockaddr_in *); + if (nam->m_len != sizeof (*sin)) + return (EINVAL); + } else { + sin = (struct sockaddr_in *) + __UNCONST(inp->inp_socket->so_proto->pr_domain->dom_sa_any); + } + + /* Bind address. */ + error = in_pcbbind_addr(inp, sin, l->l_cred); + if (error) + return (error); + + /* Bind port. */ + error = in_pcbbind_port(inp, sin, l->l_cred); + if (error) { + inp->inp_laddr.s_addr = INADDR_ANY; + + return (error); + } + return (0); }