Beispiel #1
0
void
in6_pcbdetach(struct in6pcb *in6p)
{
	struct socket *so = in6p->in6p_socket;
	int s;

	if (in6p->in6p_af != AF_INET6)
		return;

#if defined(IPSEC) || defined(FAST_IPSEC)
	ipsec6_delete_pcbpolicy(in6p);
#endif /* IPSEC */
	so->so_pcb = 0;
	if (in6p->in6p_options)
		m_freem(in6p->in6p_options);
	if (in6p->in6p_outputopts != NULL) {
		ip6_clearpktopts(in6p->in6p_outputopts, -1);
		free(in6p->in6p_outputopts, M_IP6OPT);
	}
	rtcache_free(&in6p->in6p_route);
	ip6_freemoptions(in6p->in6p_moptions);
	s = splnet();
	in6_pcbstate(in6p, IN6P_ATTACHED);
	LIST_REMOVE(&in6p->in6p_head, inph_lhash);
	CIRCLEQ_REMOVE(&in6p->in6p_table->inpt_queue, &in6p->in6p_head,
	    inph_queue);
	pool_put(&in6pcb_pool, in6p);
	splx(s);
	sofree(so);				/* drops the socket's lock */
	mutex_enter(softnet_lock);		/* reacquire it */
}
Beispiel #2
0
void
in6_pcbdisconnect(struct in6pcb *in6p)
{
	bzero((void *)&in6p->in6p_faddr, sizeof(in6p->in6p_faddr));
	in6p->in6p_fport = 0;
	in6_pcbstate(in6p, IN6P_BOUND);
	in6p->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK;
#if defined(IPSEC) || defined(FAST_IPSEC)
	ipsec_pcbdisconn(in6p->in6p_sp);
#endif
	if (in6p->in6p_socket->so_state & SS_NOFDREF)
		in6_pcbdetach(in6p);
}
int
in6_pcballoc(struct socket *so, void *v)
{
	struct inpcbtable *table = v;
	struct in6pcb *in6p;
	int s;
#if defined(IPSEC)
	int error;
#endif

	s = splnet();
	in6p = pool_get(&in6pcb_pool, PR_NOWAIT);
	splx(s);
	if (in6p == NULL)
		return (ENOBUFS);
	memset((void *)in6p, 0, sizeof(*in6p));
	in6p->in6p_af = AF_INET6;
	in6p->in6p_table = table;
	in6p->in6p_socket = so;
	in6p->in6p_hops = -1;	/* use kernel default */
	in6p->in6p_icmp6filt = NULL;
	in6p->in6p_portalgo = PORTALGO_DEFAULT;
	in6p->in6p_bindportonsend = false;
#if defined(IPSEC)
	error = ipsec_init_pcbpolicy(so, &in6p->in6p_sp);
	if (error != 0) {
		s = splnet();
		pool_put(&in6pcb_pool, in6p);
		splx(s);
		return error;
	}
#endif /* IPSEC */
	s = splnet();
	CIRCLEQ_INSERT_HEAD(&table->inpt_queue, (struct inpcb_hdr*)in6p,
	    inph_queue);
	LIST_INSERT_HEAD(IN6PCBHASH_PORT(table, in6p->in6p_lport),
	    &in6p->in6p_head, inph_lhash);
	in6_pcbstate(in6p, IN6P_ATTACHED);
	splx(s);
	if (ip6_v6only)
		in6p->in6p_flags |= IN6P_IPV6_V6ONLY;
	so->so_pcb = (void *)in6p;
	return (0);
}
/*
 * Bind port from sin6 to in6p.
 */
static int
in6_pcbbind_port(struct in6pcb *in6p, struct sockaddr_in6 *sin6, struct lwp *l)
{
	struct inpcbtable *table = in6p->in6p_table;
	struct socket *so = in6p->in6p_socket;
	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
	int error;

	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
	   ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
	    (so->so_options & SO_ACCEPTCONN) == 0))
		wild = 1;

	if (sin6->sin6_port != 0) {
		enum kauth_network_req req;

#ifndef IPNOPRIVPORTS
		if (ntohs(sin6->sin6_port) < IPV6PORT_RESERVED)
			req = KAUTH_REQ_NETWORK_BIND_PRIVPORT;
		else
#endif /* IPNOPRIVPORTS */
			req = KAUTH_REQ_NETWORK_BIND_PORT;

		error = kauth_authorize_network(l->l_cred, KAUTH_NETWORK_BIND,
		    req, so, sin6, NULL);
		if (error)
			return (EACCES);
	}

	if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
		/*
		 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
		 * allow compepte duplication of binding if
		 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
		 * and a multicast address is bound on both
		 * new and duplicated sockets.
		 */
		if (so->so_options & SO_REUSEADDR)
			reuseport = SO_REUSEADDR|SO_REUSEPORT;
	}

	if (sin6->sin6_port != 0) {
		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
#ifdef INET
			struct inpcb *t;
			struct vestigial_inpcb vestige;

			t = in_pcblookup_port(table,
			    *(struct in_addr *)&sin6->sin6_addr.s6_addr32[3],
			    sin6->sin6_port, wild, &vestige);
			if (t && (reuseport & t->inp_socket->so_options) == 0)
				return (EADDRINUSE);
			if (!t
			    && vestige.valid
			    && !(reuseport && vestige.reuse_port))
			    return EADDRINUSE;
#else
			return (EADDRNOTAVAIL);
#endif
		}

		{
			struct in6pcb *t;
			struct vestigial_inpcb vestige;

			t = in6_pcblookup_port(table, &sin6->sin6_addr,
			    sin6->sin6_port, wild, &vestige);
			if (t && (reuseport & t->in6p_socket->so_options) == 0)
				return (EADDRINUSE);
			if (!t
			    && vestige.valid
			    && !(reuseport && vestige.reuse_port))
			    return EADDRINUSE;
		}
	}

	if (sin6->sin6_port == 0) {
		int e;
		e = in6_pcbsetport(sin6, in6p, l);
		if (e != 0)
			return (e);
	} else {
		in6p->in6p_lport = sin6->sin6_port;
		in6_pcbstate(in6p, IN6P_BOUND);
	}

	LIST_REMOVE(&in6p->in6p_head, inph_lhash);
	LIST_INSERT_HEAD(IN6PCBHASH_PORT(table, in6p->in6p_lport),
	    &in6p->in6p_head, inph_lhash);

	return (0);
}
Beispiel #5
0
/*
 * Connect from a socket to a specified address.
 * Both address and port must be specified in argument sin6.
 * If don't have a local address for this socket yet,
 * then pick one.
 */
int
in6_pcbconnect(void *v, struct mbuf *nam, struct lwp *l)
{
	struct rtentry *rt;
	struct in6pcb *in6p = v;
	struct in6_addr *in6a = NULL;
	struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
	struct ifnet *ifp = NULL;	/* outgoing interface */
	int error = 0;
	int scope_ambiguous = 0;
#ifdef INET
	struct in6_addr mapped;
#endif
	struct sockaddr_in6 tmp;

	(void)&in6a;				/* XXX fool gcc */

	if (in6p->in6p_af != AF_INET6)
		return (EINVAL);

	if (nam->m_len != sizeof(*sin6))
		return (EINVAL);
	if (sin6->sin6_family != AF_INET6)
		return (EAFNOSUPPORT);
	if (sin6->sin6_port == 0)
		return (EADDRNOTAVAIL);

	if (sin6->sin6_scope_id == 0 && !ip6_use_defzone)
		scope_ambiguous = 1;
	if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
		return(error);

	/* sanity check for mapped address case */
	if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
		if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0)
			return EINVAL;
		if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr))
			in6p->in6p_laddr.s6_addr16[5] = htons(0xffff);
		if (!IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr))
			return EINVAL;
	} else
	{
		if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr))
			return EINVAL;
	}

	/* protect *sin6 from overwrites */
	tmp = *sin6;
	sin6 = &tmp;

	/* Source address selection. */
	if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr) &&
	    in6p->in6p_laddr.s6_addr32[3] == 0) {
#ifdef INET
		struct sockaddr_in sin, *sinp;

		bzero(&sin, sizeof(sin));
		sin.sin_len = sizeof(sin);
		sin.sin_family = AF_INET;
		bcopy(&sin6->sin6_addr.s6_addr32[3], &sin.sin_addr,
			sizeof(sin.sin_addr));
		sinp = in_selectsrc(&sin, &in6p->in6p_route,
			in6p->in6p_socket->so_options, NULL, &error);
		if (sinp == 0) {
			if (error == 0)
				error = EADDRNOTAVAIL;
			return (error);
		}
		bzero(&mapped, sizeof(mapped));
		mapped.s6_addr16[5] = htons(0xffff);
		bcopy(&sinp->sin_addr, &mapped.s6_addr32[3], sizeof(sinp->sin_addr));
		in6a = &mapped;
#else
		return EADDRNOTAVAIL;
#endif
	} else {
		/*
		 * XXX: in6_selectsrc might replace the bound local address
		 * with the address specified by setsockopt(IPV6_PKTINFO).
		 * Is it the intended behavior?
		 */
		in6a = in6_selectsrc(sin6, in6p->in6p_outputopts,
				     in6p->in6p_moptions,
				     &in6p->in6p_route,
				     &in6p->in6p_laddr, &ifp, &error);
		if (ifp && scope_ambiguous &&
		    (error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) {
			return(error);
		}

		if (in6a == 0) {
			if (error == 0)
				error = EADDRNOTAVAIL;
			return (error);
		}
	}
	if (ifp == NULL && (rt = rtcache_validate(&in6p->in6p_route)) != NULL)
		ifp = rt->rt_ifp;

	in6p->in6p_ip6.ip6_hlim = (u_int8_t)in6_selecthlim(in6p, ifp);

	if (in6_pcblookup_connect(in6p->in6p_table, &sin6->sin6_addr,
	    sin6->sin6_port,
	    IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) ? in6a : &in6p->in6p_laddr,
	    in6p->in6p_lport, 0))
		return (EADDRINUSE);
	if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) ||
	    (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr) &&
	     in6p->in6p_laddr.s6_addr32[3] == 0))
	{
		if (in6p->in6p_lport == 0) {
			error = in6_pcbbind(in6p, (struct mbuf *)0, l);
			if (error != 0)
				return error;
		}
		in6p->in6p_laddr = *in6a;
	}
	in6p->in6p_faddr = sin6->sin6_addr;
	in6p->in6p_fport = sin6->sin6_port;
	in6_pcbstate(in6p, IN6P_CONNECTED);
	in6p->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK;
	if (ip6_auto_flowlabel)
		in6p->in6p_flowinfo |=
		    (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK);
#if defined(IPSEC) || defined(FAST_IPSEC)
	if (in6p->in6p_socket->so_type == SOCK_STREAM)
		ipsec_pcbconn(in6p->in6p_sp);
#endif
	return (0);
}
Beispiel #6
0
int
in6_pcbbind(void *v, struct mbuf *nam, struct lwp *l)
{
	struct in6pcb *in6p = v;
	struct socket *so = in6p->in6p_socket;
	struct inpcbtable *table = in6p->in6p_table;
	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL;
	u_int16_t lport = 0;
	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);

	if (in6p->in6p_af != AF_INET6)
		return (EINVAL);

	if (in6p->in6p_lport || !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr))
		return (EINVAL);
	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
	   ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
	    (so->so_options & SO_ACCEPTCONN) == 0))
		wild = 1;
	if (nam) {
		int error;

		sin6 = mtod(nam, struct sockaddr_in6 *);
		if (nam->m_len != sizeof(*sin6))
			return (EINVAL);
		/*
		 * We should check the family, but old programs
		 * incorrectly fail to intialize it.
		 */
		if (sin6->sin6_family != AF_INET6)
			return (EAFNOSUPPORT);

#ifndef INET
		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
			return (EADDRNOTAVAIL);
#endif

		if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0)
			return (error);

		lport = sin6->sin6_port;
		if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
			/*
			 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
			 * allow compepte duplication of binding if
			 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
			 * and a multicast address is bound on both
			 * new and duplicated sockets.
			 */
			if (so->so_options & SO_REUSEADDR)
				reuseport = SO_REUSEADDR|SO_REUSEPORT;
		} else if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
			if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0)
				return (EINVAL);
			if (sin6->sin6_addr.s6_addr32[3]) {
				struct sockaddr_in sin;

				bzero(&sin, sizeof(sin));
				sin.sin_len = sizeof(sin);
				sin.sin_family = AF_INET;
				bcopy(&sin6->sin6_addr.s6_addr32[3],
				    &sin.sin_addr, sizeof(sin.sin_addr));
				if (ifa_ifwithaddr((struct sockaddr *)&sin) == 0)
					return EADDRNOTAVAIL;
			}
		} else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
			struct ifaddr *ia = NULL;

			sin6->sin6_port = 0;		/* yech... */
			if ((in6p->in6p_flags & IN6P_FAITH) == 0 &&
			    (ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0)
				return (EADDRNOTAVAIL);

			/*
			 * bind to an anycast address might accidentally
			 * cause sending a packet with an anycast source
			 * address, so we forbid it.
			 *
			 * We should allow to bind to a deprecated address,
			 * since the application dare to use it.
			 * But, can we assume that they are careful enough
			 * to check if the address is deprecated or not?
			 * Maybe, as a safeguard, we should have a setsockopt
			 * flag to control the bind(2) behavior against
			 * deprecated addresses (default: forbid bind(2)).
			 */
			if (ia &&
			    ((struct in6_ifaddr *)ia)->ia6_flags &
			    (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED))
				return (EADDRNOTAVAIL);
		}
		if (lport) {
#ifndef IPNOPRIVPORTS
			int priv;

			/*
			 * NOTE: all operating systems use suser() for
			 * privilege check!  do not rewrite it into SS_PRIV.
			 */
			priv = (l && !kauth_authorize_generic(l->l_cred,
			    KAUTH_GENERIC_ISSUSER, NULL)) ? 1 : 0;
			/* GROSS */
			if (ntohs(lport) < IPV6PORT_RESERVED && !priv)
				return (EACCES);
#endif

			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
#ifdef INET
				struct inpcb *t;

				t = in_pcblookup_port(table,
				    *(struct in_addr *)&sin6->sin6_addr.s6_addr32[3],
				    lport, wild);
				if (t && (reuseport & t->inp_socket->so_options) == 0)
					return (EADDRINUSE);
#else
				return (EADDRNOTAVAIL);
#endif
			}

			{
				struct in6pcb *t;

				t = in6_pcblookup_port(table, &sin6->sin6_addr,
				    lport, wild);
				if (t && (reuseport & t->in6p_socket->so_options) == 0)
					return (EADDRINUSE);
			}
		}
		in6p->in6p_laddr = sin6->sin6_addr;
	}

	if (lport == 0) {
		int e;
		e = in6_pcbsetport(&in6p->in6p_laddr, in6p, l);
		if (e != 0)
			return (e);
	} else {
		in6p->in6p_lport = lport;
		in6_pcbstate(in6p, IN6P_BOUND);
	}

	LIST_REMOVE(&in6p->in6p_head, inph_lhash);
	LIST_INSERT_HEAD(IN6PCBHASH_PORT(table, in6p->in6p_lport),
	    &in6p->in6p_head, inph_lhash);

#if 0
	in6p->in6p_flowinfo = 0;	/* XXX */
#endif
	return (0);
}