Пример #1
0
int
udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6,
	    struct mbuf *control, struct thread *td)
{
	u_int32_t ulen = m->m_pkthdr.len;
	u_int32_t plen = sizeof(struct udphdr) + ulen;
	struct ip6_hdr *ip6;
	struct udphdr *udp6;
	struct in6_addr *laddr, *faddr;
	u_short fport;
	int error = 0;
	struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts;
	int priv;
	int af = AF_INET6, hlen = sizeof(struct ip6_hdr);
	int flags;
	struct sockaddr_in6 tmp;

	priv = !priv_check(td, PRIV_ROOT);	/* 1 if privileged, 0 if not */
	if (control) {
		if ((error = ip6_setpktoptions(control, &opt,
		    in6p->in6p_outputopts, 
		    IPPROTO_UDP, priv)) != 0)
			goto release;
		in6p->in6p_outputopts = &opt;
	}

	if (addr6) {
		/*
		 * IPv4 version of udp_output calls in_pcbconnect in this case,
		 * which needs splnet and affects performance.
		 * Since we saw no essential reason for calling in_pcbconnect,
		 * we get rid of such kind of logic, and call in6_selectsrc
		 * and in6_pcbsetport in order to fill in the local address
		 * and the local port.
		 */
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6;
		if (sin6->sin6_port == 0) {
			error = EADDRNOTAVAIL;
			goto release;
		}

		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
			/* how about ::ffff:0.0.0.0 case? */
			error = EISCONN;
			goto release;
		}
		if (!prison_remote_ip(td, addr6)) {
			error = EAFNOSUPPORT; /* IPv4 only jail */
			goto release;
		}

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

		faddr = &sin6->sin6_addr;
		fport = sin6->sin6_port; /* allow 0 port */

		if (IN6_IS_ADDR_V4MAPPED(faddr)) {
			if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY)) {
				/*
				 * I believe we should explicitly discard the
				 * packet when mapped addresses are disabled,
				 * rather than send the packet as an IPv6 one.
				 * If we chose the latter approach, the packet
				 * might be sent out on the wire based on the
				 * default route, the situation which we'd
				 * probably want to avoid.
				 * (20010421 [email protected])
				 */
				error = EINVAL;
				goto release;
			} else
				af = AF_INET;
		}

		/* KAME hack: embed scopeid */
		if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) {
			error = EINVAL;
			goto release;
		}

		if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
			laddr = in6_selectsrc(sin6, in6p->in6p_outputopts,
					      in6p->in6p_moptions,
					      &in6p->in6p_route,
					      &in6p->in6p_laddr, &error, NULL);
		} else
			laddr = &in6p->in6p_laddr;	/* XXX */
		if (laddr == NULL) {
			if (error == 0)
				error = EADDRNOTAVAIL;
			goto release;
		}
		if (in6p->in6p_lport == 0 &&
		    (error = in6_pcbsetport(laddr, in6p, td)) != 0)
			goto release;
	} else {
		if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
			error = ENOTCONN;
			goto release;
		}
		if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_faddr)) {
			if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY)) {
				/*
				 * XXX: this case would happen when the
				 * application sets the V6ONLY flag after
				 * connecting the foreign address.
				 * Such applications should be fixed,
				 * so we bark here.
				 */
				log(LOG_INFO, "udp6_output: IPV6_V6ONLY "
				    "option was set for a connected socket\n");
				error = EINVAL;
				goto release;
			} else
				af = AF_INET;
		}
		laddr = &in6p->in6p_laddr;
		faddr = &in6p->in6p_faddr;
		fport = in6p->in6p_fport;
	}

	if (af == AF_INET)
		hlen = sizeof(struct ip);

	/*
	 * Calculate data length and get a mbuf
	 * for UDP and IP6 headers.
	 */
	M_PREPEND(m, hlen + sizeof(struct udphdr), MB_DONTWAIT);
	if (m == NULL) {
		error = ENOBUFS;
		goto release;
	}

	/*
	 * Stuff checksum and output datagram.
	 */
	udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
	udp6->uh_sport = in6p->in6p_lport; /* lport is always set in the PCB */
	udp6->uh_dport = fport;
	if (plen <= 0xffff)
		udp6->uh_ulen = htons((u_short)plen);
	else
		udp6->uh_ulen = 0;
	udp6->uh_sum = 0;

	switch (af) {
	case AF_INET6:
		ip6 = mtod(m, struct ip6_hdr *);
		ip6->ip6_flow	= in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK;
		ip6->ip6_vfc 	&= ~IPV6_VERSION_MASK;
		ip6->ip6_vfc 	|= IPV6_VERSION;
#if 0				/* ip6_plen will be filled in ip6_output. */
		ip6->ip6_plen	= htons((u_short)plen);
#endif
		ip6->ip6_nxt	= IPPROTO_UDP;
		ip6->ip6_hlim	= in6_selecthlim(in6p,
						 in6p->in6p_route.ro_rt ?
						 in6p->in6p_route.ro_rt->rt_ifp : NULL);
		ip6->ip6_src	= *laddr;
		ip6->ip6_dst	= *faddr;

		if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP,
				sizeof(struct ip6_hdr), plen)) == 0) {
			udp6->uh_sum = 0xffff;
		}

		flags = 0;

		udp6stat.udp6s_opackets++;
		error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route,
		    flags, in6p->in6p_moptions, NULL, in6p);
		break;
	case AF_INET:
		error = EAFNOSUPPORT;
		goto release;
	}
	goto releaseopt;

release:
	m_freem(m);

releaseopt:
	if (control) {
		ip6_clearpktopts(in6p->in6p_outputopts, -1);
		in6p->in6p_outputopts = stickyopt;
		m_freem(control);
	}
	return (error);
}
Пример #2
0
/*
 * Connect from a socket to a specified address.
 * Both address and port must be specified in argument sin6.
 * Eventually, flow labels will have to be dealt with here, as well.
 *
 * If don't have a local address for this socket yet,
 * then pick one.
 *
 * I believe this has to be called at splnet().
 */
int
in6_pcbconnect(struct inpcb *inp, struct mbuf *nam)
{
	struct in6_addr *in6a = NULL;
	struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
	struct ifnet *ifp = NULL;	/* outgoing interface */
	int error = 0;
	struct sockaddr_in6 tmp;

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

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

	/* reject IPv4 mapped address, we have no support for it */
	if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
		return EADDRNOTAVAIL;

	/* sanity check for mapped address case */
	if (IN6_IS_ADDR_V4MAPPED(&inp->inp_laddr6))
		return EINVAL;

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

	/* KAME hack: embed scopeid */
	if (in6_embedscope(&sin6->sin6_addr, sin6, inp, &ifp) != 0)
		return EINVAL;
	/* this must be cleared for ifa_ifwithaddr() */
	sin6->sin6_scope_id = 0;

	/* Source address selection. */
	/*
	 * 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, inp->inp_outputopts6,
	    inp->inp_moptions6, &inp->inp_route6, &inp->inp_laddr6,
	    &error);
	if (in6a == 0) {
		if (error == 0)
			error = EADDRNOTAVAIL;
		return (error);
	}

	if (inp->inp_route6.ro_rt)
		ifp = inp->inp_route6.ro_rt->rt_ifp;

	inp->inp_ipv6.ip6_hlim = (u_int8_t)in6_selecthlim(inp, ifp);

	if (in_pcblookup(inp->inp_table, &sin6->sin6_addr, sin6->sin6_port,
	    IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) ? in6a : &inp->inp_laddr6,
	    inp->inp_lport, INPLOOKUP_IPV6)) {
		return (EADDRINUSE);
	}
	if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) {
		if (inp->inp_lport == 0)
			(void)in6_pcbbind(inp, NULL, curproc);
		inp->inp_laddr6 = *in6a;
	}
	inp->inp_faddr6 = sin6->sin6_addr;
	inp->inp_fport = sin6->sin6_port;
	inp->inp_flowinfo &= ~IPV6_FLOWLABEL_MASK;
	if (ip6_auto_flowlabel)
		inp->inp_flowinfo |=
		    (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK);
	in_pcbrehash(inp);
	return (0);
}
Пример #3
0
udp6_output(struct in6pcb *in6p, struct mbuf *m, struct mbuf *addr6,
	struct mbuf *control)
#endif
{
	u_int32_t plen = sizeof(struct udphdr) + m->m_pkthdr.len;
	struct ip6_hdr *ip6;
	struct udphdr *udp6;
	struct in6_addr *laddr6 = NULL, *faddr6 = NULL;
	struct sockaddr_in6 *fsa6 = NULL;
	struct ifnet *oifp = NULL;
	int scope_ambiguous = 0;
#ifndef __FreeBSD__ 
	struct sockaddr_in6 lsa6_mapped; /* XXX ugly */
#endif
	u_int16_t fport;
	int error = 0;
	struct ip6_pktopts *optp, opt;
	int priv;
	int af = AF_INET6, hlen = sizeof(struct ip6_hdr);
#ifdef INET
#if defined(__NetBSD__)
	struct ip *ip;
	struct udpiphdr *ui;
#endif
#endif
	int flags = 0;
	struct sockaddr_in6 tmp;
#if defined(__OpenBSD__)
	struct proc *p = curproc;	/* XXX */
#endif

	priv = 0;
#if defined(__NetBSD__)
	if (p && !suser(p->p_ucred, &p->p_acflag))
		priv = 1;
#elif defined(__FreeBSD__)
	if (p && !suser(p))
		priv = 1;
#else
	if ((in6p->in6p_socket->so_state & SS_PRIV) != 0)
		priv = 1;
#endif

	if (addr6) {
#ifdef __FreeBSD__
		/* addr6 has been validated in udp6_send(). */
		fsa6 = (struct sockaddr_in6 *)addr6;
#else
		fsa6 = mtod(addr6, struct sockaddr_in6 *);

		if (addr6->m_len != sizeof(*fsa6))
			return (EINVAL);

		if (fsa6->sin6_family != AF_INET6)
			return (EAFNOSUPPORT);
#endif

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

		/*
		 * Application should provide a proper zone ID or the use of
		 * default zone IDs should be enabled.  Unfortunately, some
		 * applications do not behave as it should, so we need a
		 * workaround.  Even if an appropriate ID is not determined,
		 * we'll see if we can determine the outgoing interface.  If we
		 * can, determine the zone ID based on the interface below.
		 */
		if (fsa6->sin6_scope_id == 0 && !ip6_use_defzone)
			scope_ambiguous = 1;
		if ((error = sa6_embedscope(fsa6, ip6_use_defzone)) != 0)
			return (error);
	}

	if (control) {
		if ((error = ip6_setpktopts(control, &opt,
		    in6p->in6p_outputopts, priv, IPPROTO_UDP)) != 0)
			goto release;
		optp = &opt;
	} else
		optp = in6p->in6p_outputopts;
		

	if (fsa6) {
		faddr6 = &fsa6->sin6_addr;

		/*
		 * IPv4 version of udp_output calls in_pcbconnect in this case,
		 * which needs splnet and affects performance.
		 * Since we saw no essential reason for calling in_pcbconnect,
		 * we get rid of such kind of logic, and call in6_selectsrc
		 * and in6_pcbsetport in order to fill in the local address
		 * and the local port.
		 */
		if (fsa6->sin6_port == 0) {
			error = EADDRNOTAVAIL;
			goto release;
		}

		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
			/* how about ::ffff:0.0.0.0 case? */
			error = EISCONN;
			goto release;
		}

		fport = fsa6->sin6_port; /* allow 0 port */

		if (IN6_IS_ADDR_V4MAPPED(faddr6)) {
#ifdef __OpenBSD__		/* does not support mapped addresses */
			if (1)
#else
			if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY))
#endif
			{
				/*
				 * I believe we should explicitly discard the
				 * packet when mapped addresses are disabled,
				 * rather than send the packet as an IPv6 one.
				 * If we chose the latter approach, the packet
				 * might be sent out on the wire based on the
				 * default route, the situation which we'd
				 * probably want to avoid.
				 * (20010421 [email protected])
				 */
				error = EINVAL;
				goto release;
			}
			if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
			    !IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr)) {
				/*
				 * when remote addr is an IPv4-mapped address,
				 * local addr should not be an IPv6 address,
				 * since you cannot determine how to map IPv6
				 * source address to IPv4.
				 */
				error = EINVAL;
				goto release;
			}

			af = AF_INET;
		}

		if (!IN6_IS_ADDR_V4MAPPED(faddr6)) {
			laddr6 = in6_selectsrc(fsa6, optp,
			    in6p->in6p_moptions, &in6p->in6p_route,
			    &in6p->in6p_laddr, &oifp, &error);
			if (oifp && scope_ambiguous &&
			    (error = in6_setscope(&fsa6->sin6_addr,
			    oifp, NULL))) {
				goto release;
			}
		} else {
#ifndef __FreeBSD__
			/*
			 * XXX: freebsd[34] does not have in_selectsrc, but
			 * we can omit the whole part because freebsd4 calls
			 * udp_output() directly in this case, and thus we'll
			 * never see this path.
			 */
			if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) {
				struct sockaddr_in *sinp, sin_dst;

				bzero(&sin_dst, sizeof(sin_dst));
				sin_dst.sin_family = AF_INET;
				sin_dst.sin_len = sizeof(sin_dst);
				bcopy(&fsa6->sin6_addr.s6_addr[12],
				      &sin_dst.sin_addr,
				      sizeof(sin_dst.sin_addr));
				sinp = in_selectsrc(&sin_dst,
						    (struct route *)&in6p->in6p_route,
						    in6p->in6p_socket->so_options,
						    NULL, &error);
				if (sinp == NULL) {
					if (error == 0)
						error = EADDRNOTAVAIL;
					goto release;
				}
				bzero(&lsa6_mapped, sizeof(lsa6_mapped));
				lsa6_mapped.sin6_family = AF_INET6;
				lsa6_mapped.sin6_len = sizeof(lsa6_mapped);
				/* ugly */
				lsa6_mapped.sin6_addr.s6_addr16[5] = 0xffff;
				bcopy(&sinp->sin_addr,
				      &lsa6_mapped.sin6_addr.s6_addr[12],
				      sizeof(sinp->sin_addr));
				laddr6 = &lsa6_mapped.sin6_addr;
			} else
#endif /* !freebsd */
			{
				laddr6 = &in6p->in6p_laddr;
			}
		}
		if (laddr6 == NULL) {
			if (error == 0)
				error = EADDRNOTAVAIL;
			goto release;
		}
		if (in6p->in6p_lport == 0 &&
		    (error = in6_pcbsetport(laddr6, in6p,
#ifdef __FreeBSD__
			p->td_ucred
#else
			p
#endif
		     )) != 0)
			goto release;
	} else {
		if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
			error = ENOTCONN;
			goto release;
		}
		if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_faddr)) {
#ifdef __OpenBSD__		/* does not support mapped addresses */
			if (1)
#else
			if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY))
#endif
			{
				/*
				 * XXX: this case would happen when the
				 * application sets the V6ONLY flag after
				 * connecting the foreign address.
				 * Such applications should be fixed,
				 * so we bark here.
				 */
				log(LOG_INFO, "udp6_output: IPV6_V6ONLY "
				    "option was set for a connected socket\n");
				error = EINVAL;
				goto release;
			} else
				af = AF_INET;
		}
		laddr6 = &in6p->in6p_laddr;
		faddr6 = &in6p->in6p_faddr;
		fport = in6p->in6p_fport;
	}

	if (af == AF_INET)
		hlen = sizeof(struct ip);

	/*
	 * Calculate data length and get a mbuf
	 * for UDP and IP6 headers.
	 */
	M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT);
	if (m == 0) {
		error = ENOBUFS;
		goto release;
	}

	/*
	 * Stuff checksum and output datagram.
	 */
	udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
	udp6->uh_sport = in6p->in6p_lport; /* lport is always set in the PCB */
	udp6->uh_dport = fport;
	if (plen <= 0xffff)
		udp6->uh_ulen = htons((u_int16_t)plen);
	else
		udp6->uh_ulen = 0;
	udp6->uh_sum = 0;

	switch (af) {
	case AF_INET6:
		ip6 = mtod(m, struct ip6_hdr *);
		ip6->ip6_flow	= in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK;
		ip6->ip6_vfc 	&= ~IPV6_VERSION_MASK;
		ip6->ip6_vfc 	|= IPV6_VERSION;
#if 0				/* ip6_plen will be filled in ip6_output. */
		ip6->ip6_plen	= htons((u_int16_t)plen);
#endif
		ip6->ip6_nxt	= IPPROTO_UDP;
		ip6->ip6_hlim	= in6_selecthlim(in6p, oifp);
		ip6->ip6_src	= *laddr6;
		ip6->ip6_dst	= *faddr6;

		if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP,
				sizeof(struct ip6_hdr), plen)) == 0) {
			udp6->uh_sum = 0xffff;
		}

		udp6stat.udp6s_opackets++;
#ifdef IPSEC
		if (ipsec_setsocket(m, in6p->in6p_socket) != 0) {
			error = ENOBUFS;
			goto release;
		}
#endif /* IPSEC */
#ifdef __FreeBSD__
		error = ip6_output(m, optp, &in6p->in6p_route,
		    flags, in6p->in6p_moptions, NULL, NULL);
#elif defined(__NetBSD__)
		error = ip6_output(m, optp, &in6p->in6p_route,
		    flags, in6p->in6p_moptions, in6p->in6p_socket, NULL);
#else
		error = ip6_output(m, optp, &in6p->in6p_route,
		    flags, in6p->in6p_moptions, NULL);
#endif

		break;
	case AF_INET:
#if defined(INET) && defined(__NetBSD__)
		/* can't transmit jumbogram over IPv4 */
		if (plen > 0xffff) {
			error = EMSGSIZE;
			goto release;
		}

		ip = mtod(m, struct ip *);
		ui = (struct udpiphdr *)ip;
		bzero(ui->ui_x1, sizeof ui->ui_x1);
		ui->ui_pr = IPPROTO_UDP;
		ui->ui_len = htons(plen);
		bcopy(&laddr6->s6_addr[12], &ui->ui_src, sizeof(ui->ui_src));
		ui->ui_ulen = ui->ui_len;

#ifdef  __NetBSD__
		flags = (in6p->in6p_socket->so_options &
			 (SO_DONTROUTE | SO_BROADCAST));
		bcopy(&fsa6->sin6_addr.s6_addr[12],
		      &ui->ui_dst, sizeof(ui->ui_dst));
		udp6->uh_sum = in_cksum(m, hlen + plen);
#endif
		if (udp6->uh_sum == 0)
			udp6->uh_sum = 0xffff;

		ip->ip_len = hlen + plen;
		ip->ip_ttl = in6_selecthlim(in6p, NULL); /* XXX */
		ip->ip_tos = 0;	/* XXX */

		ip->ip_len = hlen + plen; /* XXX */

		udpstat.udps_opackets++;
#ifdef IPSEC
		(void)ipsec_setsocket(m, NULL);	/* XXX */
#endif /* IPSEC */
#ifdef __NetBSD__
		error = ip_output(m, NULL, &in6p->in6p_route, flags /* XXX */);
#endif
		break;
#else
		error = EAFNOSUPPORT;
		goto release;
#endif
	}
	goto releaseopt;

release:
	m_freem(m);

releaseopt:
	if (control) {
		ip6_clearpktopts(&opt, -1);
		m_freem(control);
	}
	return (error);
}
Пример #4
0
static int
udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6,
    struct mbuf *control, struct thread *td)
{
	u_int32_t ulen = m->m_pkthdr.len;
	u_int32_t plen = sizeof(struct udphdr) + ulen;
	struct ip6_hdr *ip6;
	struct udphdr *udp6;
	struct in6_addr *laddr, *faddr, in6a;
	struct sockaddr_in6 *sin6 = NULL;
	int cscov_partial = 0;
	int scope_ambiguous = 0;
	u_short fport;
	int error = 0;
	uint8_t nxt;
	uint16_t cscov = 0;
	struct ip6_pktopts *optp, opt;
	int af = AF_INET6, hlen = sizeof(struct ip6_hdr);
	int flags;
	struct sockaddr_in6 tmp;

	INP_WLOCK_ASSERT(inp);
	INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);

	if (addr6) {
		/* addr6 has been validated in udp6_send(). */
		sin6 = (struct sockaddr_in6 *)addr6;

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

		/*
		 * Application should provide a proper zone ID or the use of
		 * default zone IDs should be enabled.  Unfortunately, some
		 * applications do not behave as it should, so we need a
		 * workaround.  Even if an appropriate ID is not determined,
		 * we'll see if we can determine the outgoing interface.  If we
		 * can, determine the zone ID based on the interface below.
		 */
		if (sin6->sin6_scope_id == 0 && !V_ip6_use_defzone)
			scope_ambiguous = 1;
		if ((error = sa6_embedscope(sin6, V_ip6_use_defzone)) != 0)
			return (error);
	}

	nxt = (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDP) ?
	    IPPROTO_UDP : IPPROTO_UDPLITE;
	if (control) {
		if ((error = ip6_setpktopts(control, &opt,
		    inp->in6p_outputopts, td->td_ucred, nxt)) != 0)
			goto release;
		optp = &opt;
	} else
		optp = inp->in6p_outputopts;

	if (sin6) {
		faddr = &sin6->sin6_addr;

		/*
		 * Since we saw no essential reason for calling in_pcbconnect,
		 * we get rid of such kind of logic, and call in6_selectsrc
		 * and in6_pcbsetport in order to fill in the local address
		 * and the local port.
		 */
		if (sin6->sin6_port == 0) {
			error = EADDRNOTAVAIL;
			goto release;
		}

		if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
			/* how about ::ffff:0.0.0.0 case? */
			error = EISCONN;
			goto release;
		}

		fport = sin6->sin6_port; /* allow 0 port */

		if (IN6_IS_ADDR_V4MAPPED(faddr)) {
			if ((inp->inp_flags & IN6P_IPV6_V6ONLY)) {
				/*
				 * I believe we should explicitly discard the
				 * packet when mapped addresses are disabled,
				 * rather than send the packet as an IPv6 one.
				 * If we chose the latter approach, the packet
				 * might be sent out on the wire based on the
				 * default route, the situation which we'd
				 * probably want to avoid.
				 * (20010421 [email protected])
				 */
				error = EINVAL;
				goto release;
			}
			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) &&
			    !IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) {
				/*
				 * when remote addr is an IPv4-mapped address,
				 * local addr should not be an IPv6 address,
				 * since you cannot determine how to map IPv6
				 * source address to IPv4.
				 */
				error = EINVAL;
				goto release;
			}

			af = AF_INET;
		}

		if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
			error = in6_selectsrc_socket(sin6, optp, inp,
			    td->td_ucred, scope_ambiguous, &in6a, NULL);
			if (error)
				goto release;
			laddr = &in6a;
		} else
			laddr = &inp->in6p_laddr;	/* XXX */
		if (laddr == NULL) {
			if (error == 0)
				error = EADDRNOTAVAIL;
			goto release;
		}
		if (inp->inp_lport == 0 &&
		    (error = in6_pcbsetport(laddr, inp, td->td_ucred)) != 0) {
			/* Undo an address bind that may have occurred. */
			inp->in6p_laddr = in6addr_any;
			goto release;
		}
	} else {
		if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
			error = ENOTCONN;
			goto release;
		}
		if (IN6_IS_ADDR_V4MAPPED(&inp->in6p_faddr)) {
			if ((inp->inp_flags & IN6P_IPV6_V6ONLY)) {
				/*
				 * XXX: this case would happen when the
				 * application sets the V6ONLY flag after
				 * connecting the foreign address.
				 * Such applications should be fixed,
				 * so we bark here.
				 */
				log(LOG_INFO, "udp6_output: IPV6_V6ONLY "
				    "option was set for a connected socket\n");
				error = EINVAL;
				goto release;
			} else
				af = AF_INET;
		}
		laddr = &inp->in6p_laddr;
		faddr = &inp->in6p_faddr;
		fport = inp->inp_fport;
	}

	if (af == AF_INET)
		hlen = sizeof(struct ip);

	/*
	 * Calculate data length and get a mbuf
	 * for UDP and IP6 headers.
	 */
	M_PREPEND(m, hlen + sizeof(struct udphdr), M_NOWAIT);
	if (m == NULL) {
		error = ENOBUFS;
		goto release;
	}

	/*
	 * Stuff checksum and output datagram.
	 */
	udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
	udp6->uh_sport = inp->inp_lport; /* lport is always set in the PCB */
	udp6->uh_dport = fport;
	if (nxt == IPPROTO_UDPLITE) {
		struct udpcb *up;

		up = intoudpcb(inp);
		cscov = up->u_txcslen;
		if (cscov >= plen)
			cscov = 0;
		udp6->uh_ulen = htons(cscov);
		/*
		 * For UDP-Lite, checksum coverage length of zero means
		 * the entire UDPLite packet is covered by the checksum.
		 */
		cscov_partial = (cscov == 0) ? 0 : 1;
	} else if (plen <= 0xffff)
		udp6->uh_ulen = htons((u_short)plen);
	else
		udp6->uh_ulen = 0;
	udp6->uh_sum = 0;

	switch (af) {
	case AF_INET6:
		ip6 = mtod(m, struct ip6_hdr *);
		ip6->ip6_flow	= inp->inp_flow & IPV6_FLOWINFO_MASK;
		ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
		ip6->ip6_vfc	|= IPV6_VERSION;
		ip6->ip6_plen	= htons((u_short)plen);
		ip6->ip6_nxt	= nxt;
		ip6->ip6_hlim	= in6_selecthlim(inp, NULL);
		ip6->ip6_src	= *laddr;
		ip6->ip6_dst	= *faddr;

		if (cscov_partial) {
			if ((udp6->uh_sum = in6_cksum_partial(m, nxt,
			    sizeof(struct ip6_hdr), plen, cscov)) == 0)
				udp6->uh_sum = 0xffff;
		} else {
			udp6->uh_sum = in6_cksum_pseudo(ip6, plen, nxt, 0);
			m->m_pkthdr.csum_flags = CSUM_UDP_IPV6;
			m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
		}

#ifdef	RSS
		{
			uint32_t hash_val, hash_type;
			uint8_t pr;

			pr = inp->inp_socket->so_proto->pr_protocol;
			/*
			 * Calculate an appropriate RSS hash for UDP and
			 * UDP Lite.
			 *
			 * The called function will take care of figuring out
			 * whether a 2-tuple or 4-tuple hash is required based
			 * on the currently configured scheme.
			 *
			 * Later later on connected socket values should be
			 * cached in the inpcb and reused, rather than constantly
			 * re-calculating it.
			 *
			 * UDP Lite is a different protocol number and will
			 * likely end up being hashed as a 2-tuple until
			 * RSS / NICs grow UDP Lite protocol awareness.
			 */
			if (rss_proto_software_hash_v6(faddr, laddr, fport,
			    inp->inp_lport, pr, &hash_val, &hash_type) == 0) {
				m->m_pkthdr.flowid = hash_val;
				M_HASHTYPE_SET(m, hash_type);
			}
		}
#endif
		flags = 0;
#ifdef	RSS
		/*
		 * Don't override with the inp cached flowid.
		 *
		 * Until the whole UDP path is vetted, it may actually
		 * be incorrect.
		 */
		flags |= IP_NODEFAULTFLOWID;
#endif

		UDP_PROBE(send, NULL, inp, ip6, inp, udp6);
		UDPSTAT_INC(udps_opackets);
		error = ip6_output(m, optp, &inp->inp_route6, flags,
		    inp->in6p_moptions, NULL, inp);
		break;
	case AF_INET:
		error = EAFNOSUPPORT;
		goto release;
	}
	goto releaseopt;

release:
	m_freem(m);

releaseopt:
	if (control) {
		ip6_clearpktopts(&opt, -1);
		m_freem(control);
	}
	return (error);
}
Пример #5
0
int
udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6,
	    struct mbuf *control, struct thread *td)
{
	u_int32_t ulen = m->m_pkthdr.len;
	u_int32_t plen = sizeof(struct udphdr) + ulen;
	struct ip6_hdr *ip6;
	struct udphdr *udp6;
	struct in6_addr *laddr, *faddr;
	u_short fport;
	int error = 0;
	struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts;
	int priv;
	int hlen = sizeof(struct ip6_hdr);
	struct sockaddr_in6 tmp;

	priv = !priv_check(td, PRIV_ROOT);	/* 1 if privileged, 0 if not */
	if (control) {
		if ((error = ip6_setpktoptions(control, &opt,
		    in6p->in6p_outputopts, 
		    IPPROTO_UDP, priv)) != 0)
			goto release;
		in6p->in6p_outputopts = &opt;
	}

	if (addr6) {
		/*
		 * IPv4 version of udp_output calls in_pcbconnect in this case,
		 * which needs splnet and affects performance.
		 * Since we saw no essential reason for calling in_pcbconnect,
		 * we get rid of such kind of logic, and call in6_selectsrc
		 * and in6_pcbsetlport in order to fill in the local address
		 * and the local port.
		 */
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6;

		/* Caller should have rejected the v4-mapped address */
		KASSERT(!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr),
		    ("v4-mapped address"));

		if (sin6->sin6_port == 0) {
			error = EADDRNOTAVAIL;
			goto release;
		}

		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
			/* how about ::ffff:0.0.0.0 case? */
			error = EISCONN;
			goto release;
		}
		if (!prison_remote_ip(td, addr6)) {
			error = EAFNOSUPPORT; /* IPv4 only jail */
			goto release;
		}

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

		faddr = &sin6->sin6_addr;
		fport = sin6->sin6_port; /* allow 0 port */

		/* KAME hack: embed scopeid */
		if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) {
			error = EINVAL;
			goto release;
		}

		laddr = in6_selectsrc(sin6, in6p->in6p_outputopts,
				      in6p->in6p_moptions,
				      &in6p->in6p_route,
				      &in6p->in6p_laddr, &error, NULL);
		if (laddr == NULL) {
			if (error == 0)
				error = EADDRNOTAVAIL;
			goto release;
		}
		if (in6p->in6p_lport == 0 &&
		    (error = in6_pcbsetlport(laddr, in6p, td)) != 0)
			goto release;
	} else {
		if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
			error = ENOTCONN;
			goto release;
		}

		/* Connection to v4-mapped address should have been rejected */
		KASSERT(!IN6_IS_ADDR_V4MAPPED(&in6p->in6p_faddr),
		    ("bound to v4-mapped address"));

		laddr = &in6p->in6p_laddr;
		faddr = &in6p->in6p_faddr;
		fport = in6p->in6p_fport;
	}

	/*
	 * Calculate data length and get a mbuf
	 * for UDP and IP6 headers.
	 */
	M_PREPEND(m, hlen + sizeof(struct udphdr), M_NOWAIT);
	if (m == NULL) {
		error = ENOBUFS;
		goto release;
	}

	/*
	 * Stuff checksum and output datagram.
	 */
	udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
	udp6->uh_sport = in6p->in6p_lport; /* lport is always set in the PCB */
	udp6->uh_dport = fport;
	if (plen <= 0xffff)
		udp6->uh_ulen = htons((u_short)plen);
	else
		udp6->uh_ulen = 0;
	udp6->uh_sum = 0;

	ip6 = mtod(m, struct ip6_hdr *);
	ip6->ip6_flow	= in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK;
	ip6->ip6_vfc 	&= ~IPV6_VERSION_MASK;
	ip6->ip6_vfc 	|= IPV6_VERSION;
#if 0				/* ip6_plen will be filled in ip6_output. */
	ip6->ip6_plen	= htons((u_short)plen);
#endif
	ip6->ip6_nxt	= IPPROTO_UDP;
	ip6->ip6_hlim	= in6_selecthlim(in6p,
					 in6p->in6p_route.ro_rt ?
					 in6p->in6p_route.ro_rt->rt_ifp : NULL);
	ip6->ip6_src	= *laddr;
	ip6->ip6_dst	= *faddr;

	if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP,
			sizeof(struct ip6_hdr), plen)) == 0) {
		udp6->uh_sum = 0xffff;
	}

	udp6stat.udp6s_opackets++;
	error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, 0,
	    in6p->in6p_moptions, NULL, in6p);
	goto releaseopt;

release:
	m_freem(m);

releaseopt:
	if (control) {
		ip6_clearpktopts(in6p->in6p_outputopts, -1);
		in6p->in6p_outputopts = stickyopt;
		m_freem(control);
	}
	return (error);
}
Пример #6
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);
}