Ejemplo n.º 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);
}
Ejemplo n.º 2
0
/*
 * IPsec input callback, called by the transform callback. Takes care of
 * filtering and other sanity checks on the processed packet.
 */
int
ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff,
    struct m_tag *mt)
{
	int af, sproto;
	u_char prot;

#if NBPFILTER > 0
	struct ifnet *encif;
#endif

#ifdef INET
	struct ip *ip, ipn;
#endif /* INET */

#ifdef INET6
	struct ip6_hdr *ip6, ip6n;
#endif /* INET6 */
	struct m_tag *mtag;
	struct tdb_ident *tdbi;

	af = tdbp->tdb_dst.sa.sa_family;
	sproto = tdbp->tdb_sproto;

	tdbp->tdb_last_used = time_second;

	/* Sanity check */
	if (m == NULL) {
		/* The called routine will print a message if necessary */
		IPSEC_ISTAT(espstat.esps_badkcr, ahstat.ahs_badkcr,
		    ipcompstat.ipcomps_badkcr);
		return EINVAL;
	}

#ifdef INET
	/* Fix IPv4 header */
	if (af == AF_INET) {
		if ((m->m_len < skip) && ((m = m_pullup(m, skip)) == NULL)) {
			DPRINTF(("ipsec_common_input_cb(): processing failed "
			    "for SA %s/%08x\n", ipsp_address(tdbp->tdb_dst),
			    ntohl(tdbp->tdb_spi)));
			IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops,
			    ipcompstat.ipcomps_hdrops);
			return ENOBUFS;
		}

		ip = mtod(m, struct ip *);
		ip->ip_len = htons(m->m_pkthdr.len);
		ip->ip_sum = 0;
		ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
		prot = ip->ip_p;

		/* IP-in-IP encapsulation */
		if (prot == IPPROTO_IPIP) {
			if (m->m_pkthdr.len - skip < sizeof(struct ip)) {
				m_freem(m);
				IPSEC_ISTAT(espstat.esps_hdrops,
				    ahstat.ahs_hdrops,
				    ipcompstat.ipcomps_hdrops);
				return EINVAL;
			}
			/* ipn will now contain the inner IPv4 header */
			m_copydata(m, skip, sizeof(struct ip),
			    (caddr_t) &ipn);

			/*
			 * Check that the inner source address is the same as
			 * the proxy address, if available.
			 */
			if ((tdbp->tdb_proxy.sa.sa_family == AF_INET &&
			    tdbp->tdb_proxy.sin.sin_addr.s_addr !=
			    INADDR_ANY &&
			    ipn.ip_src.s_addr !=
			    tdbp->tdb_proxy.sin.sin_addr.s_addr) ||
			    (tdbp->tdb_proxy.sa.sa_family != AF_INET &&
				tdbp->tdb_proxy.sa.sa_family != 0)) {
#if ENCDEBUG
				char addr[INET_ADDRSTRLEN];
#endif

				DPRINTF(("ipsec_common_input_cb(): inner "
				    "source address %s doesn't correspond to "
				    "expected proxy source %s, SA %s/%08x\n",
				    inet_ntop(AF_INET, &ipn.ip_src,
					addr, sizeof(addr)),
				    ipsp_address(tdbp->tdb_proxy),
				    ipsp_address(tdbp->tdb_dst),
				    ntohl(tdbp->tdb_spi)));

				m_freem(m);
				IPSEC_ISTAT(espstat.esps_pdrops,
				    ahstat.ahs_pdrops,
				    ipcompstat.ipcomps_pdrops);
				return EACCES;
			}
		}

#ifdef INET6
		/* IPv6-in-IP encapsulation. */
		if (prot == IPPROTO_IPV6) {
			if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) {
				m_freem(m);
				IPSEC_ISTAT(espstat.esps_hdrops,
				    ahstat.ahs_hdrops,
				    ipcompstat.ipcomps_hdrops);
				return EINVAL;
			}
			/* ip6n will now contain the inner IPv6 header. */
			m_copydata(m, skip, sizeof(struct ip6_hdr),
			    (caddr_t) &ip6n);

			/*
			 * Check that the inner source address is the same as
			 * the proxy address, if available.
			 */
			if ((tdbp->tdb_proxy.sa.sa_family == AF_INET6 &&
			    !IN6_IS_ADDR_UNSPECIFIED(&tdbp->tdb_proxy.sin6.sin6_addr) &&
			    !IN6_ARE_ADDR_EQUAL(&ip6n.ip6_src,
				&tdbp->tdb_proxy.sin6.sin6_addr)) ||
			    (tdbp->tdb_proxy.sa.sa_family != AF_INET6 &&
				tdbp->tdb_proxy.sa.sa_family != 0)) {
#if ENCDEBUG
				char addr[INET6_ADDRSTRLEN];
#endif

				DPRINTF(("ipsec_common_input_cb(): inner "
				    "source address %s doesn't correspond to "
				    "expected proxy source %s, SA %s/%08x\n",
				    inet_ntop(AF_INET6, &ip6n.ip6_src,
					addr, sizeof(addr)),
				    ipsp_address(tdbp->tdb_proxy),
				    ipsp_address(tdbp->tdb_dst),
				    ntohl(tdbp->tdb_spi)));

				m_freem(m);
				IPSEC_ISTAT(espstat.esps_pdrops,
				    ahstat.ahs_pdrops,
				    ipcompstat.ipcomps_pdrops);
				return EACCES;
			}
		}
#endif /* INET6 */
	}
#endif /* INET */

#ifdef INET6
	/* Fix IPv6 header */
	if (af == AF_INET6)
	{
		if (m->m_len < sizeof(struct ip6_hdr) &&
		    (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {

			DPRINTF(("ipsec_common_input_cb(): processing failed "
			    "for SA %s/%08x\n", ipsp_address(tdbp->tdb_dst),
			    ntohl(tdbp->tdb_spi)));

			IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops,
			    ipcompstat.ipcomps_hdrops);
			return EACCES;
		}

		ip6 = mtod(m, struct ip6_hdr *);
		ip6->ip6_plen = htons(m->m_pkthdr.len - skip);

		/* Save protocol */
		m_copydata(m, protoff, 1, (caddr_t) &prot);

#ifdef INET
		/* IP-in-IP encapsulation */
		if (prot == IPPROTO_IPIP) {
			if (m->m_pkthdr.len - skip < sizeof(struct ip)) {
				m_freem(m);
				IPSEC_ISTAT(espstat.esps_hdrops,
				    ahstat.ahs_hdrops,
				    ipcompstat.ipcomps_hdrops);
				return EINVAL;
			}
			/* ipn will now contain the inner IPv4 header */
			m_copydata(m, skip, sizeof(struct ip), (caddr_t) &ipn);

			/*
			 * Check that the inner source address is the same as
			 * the proxy address, if available.
			 */
			if ((tdbp->tdb_proxy.sa.sa_family == AF_INET &&
			    tdbp->tdb_proxy.sin.sin_addr.s_addr !=
			    INADDR_ANY &&
			    ipn.ip_src.s_addr !=
				tdbp->tdb_proxy.sin.sin_addr.s_addr) ||
			    (tdbp->tdb_proxy.sa.sa_family != AF_INET &&
				tdbp->tdb_proxy.sa.sa_family != 0)) {
#if ENCDEBUG
				char addr[INET_ADDRSTRLEN];
#endif

				DPRINTF(("ipsec_common_input_cb(): inner "
				    "source address %s doesn't correspond to "
				    "expected proxy source %s, SA %s/%08x\n",
				    inet_ntop(AF_INET, &ipn.ip_src,
					addr, sizeof(addr)),
				    ipsp_address(tdbp->tdb_proxy),
				    ipsp_address(tdbp->tdb_dst),
				    ntohl(tdbp->tdb_spi)));

				m_freem(m);
				IPSEC_ISTAT(espstat.esps_pdrops,
				    ahstat.ahs_pdrops,
				    ipcompstat.ipcomps_pdrops);
				return EACCES;
			}
		}
#endif /* INET */

		/* IPv6-in-IP encapsulation */
		if (prot == IPPROTO_IPV6) {
			if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) {
				m_freem(m);
				IPSEC_ISTAT(espstat.esps_hdrops,
				    ahstat.ahs_hdrops,
				    ipcompstat.ipcomps_hdrops);
				return EINVAL;
			}
			/* ip6n will now contain the inner IPv6 header. */
			m_copydata(m, skip, sizeof(struct ip6_hdr),
			    (caddr_t) &ip6n);

			/*
			 * Check that the inner source address is the same as
			 * the proxy address, if available.
			 */
			if ((tdbp->tdb_proxy.sa.sa_family == AF_INET6 &&
			    !IN6_IS_ADDR_UNSPECIFIED(&tdbp->tdb_proxy.sin6.sin6_addr) &&
			    !IN6_ARE_ADDR_EQUAL(&ip6n.ip6_src,
				&tdbp->tdb_proxy.sin6.sin6_addr)) ||
			    (tdbp->tdb_proxy.sa.sa_family != AF_INET6 &&
				tdbp->tdb_proxy.sa.sa_family != 0)) {
#if ENCDEBUG
				char addr[INET6_ADDRSTRLEN];
#endif

				DPRINTF(("ipsec_common_input_cb(): inner "
				    "source address %s doesn't correspond to "
				    "expected proxy source %s, SA %s/%08x\n",
				    inet_ntop(AF_INET6, &ip6n.ip6_src,
					addr, sizeof(addr)),
				    ipsp_address(tdbp->tdb_proxy),
				    ipsp_address(tdbp->tdb_dst),
				    ntohl(tdbp->tdb_spi)));

				m_freem(m);
				IPSEC_ISTAT(espstat.esps_pdrops,
				    ahstat.ahs_pdrops,
				    ipcompstat.ipcomps_pdrops);
				return EACCES;
			}
		}
	}
#endif /* INET6 */

	/*
	 * Fix TCP/UDP checksum of UDP encapsulated transport mode ESP packet.
	 * (RFC3948 3.1.2)
	 */
	if ((af == AF_INET || af == AF_INET6) &&
	    (tdbp->tdb_flags & TDBF_UDPENCAP) &&
	    (tdbp->tdb_flags & TDBF_TUNNELING) == 0) {
		u_int16_t cksum;

		switch (prot) {
		case IPPROTO_UDP:
			if (m->m_pkthdr.len < skip + sizeof(struct udphdr)) {
				m_freem(m);
				IPSEC_ISTAT(espstat.esps_hdrops,
				    ahstat.ahs_hdrops,
				    ipcompstat.ipcomps_hdrops);
				return EINVAL;
			}
			cksum = 0;
			m_copyback(m, skip + offsetof(struct udphdr, uh_sum),
			    sizeof(cksum), &cksum, M_NOWAIT);
#ifdef INET6
			if (af == AF_INET6) {
				cksum = in6_cksum(m, IPPROTO_UDP, skip,
				    m->m_pkthdr.len - skip);
				m_copyback(m, skip + offsetof(struct udphdr,
				    uh_sum), sizeof(cksum), &cksum, M_NOWAIT);
			}
#endif
			break;
		case IPPROTO_TCP:
			if (m->m_pkthdr.len < skip + sizeof(struct tcphdr)) {
				m_freem(m);
				IPSEC_ISTAT(espstat.esps_hdrops,
				    ahstat.ahs_hdrops,
				    ipcompstat.ipcomps_hdrops);
				return EINVAL;
			}
			cksum = 0;
			m_copyback(m, skip + offsetof(struct tcphdr, th_sum),
			    sizeof(cksum), &cksum, M_NOWAIT);
			if (af == AF_INET)
				cksum = in4_cksum(m, IPPROTO_TCP, skip,
				    m->m_pkthdr.len - skip);
#ifdef INET6
			else if (af == AF_INET6)
				cksum = in6_cksum(m, IPPROTO_TCP, skip,
				    m->m_pkthdr.len - skip);
#endif
			m_copyback(m, skip + offsetof(struct tcphdr, th_sum),
			    sizeof(cksum), &cksum, M_NOWAIT);
			break;
		}
Ejemplo n.º 3
0
void
udp_input(struct mbuf *m, ...)
{
	struct ip *ip;
	struct udphdr *uh;
	struct inpcb *inp = NULL;
	struct mbuf *opts = NULL;
	struct ip save_ip;
	int iphlen, len;
	va_list ap;
	u_int16_t savesum;
	union {
		struct sockaddr sa;
		struct sockaddr_in sin;
#ifdef INET6
		struct sockaddr_in6 sin6;
#endif /* INET6 */
	} srcsa, dstsa;
#ifdef INET6
	struct ip6_hdr *ip6;
#endif /* INET6 */
#ifdef IPSEC
	struct m_tag *mtag;
	struct tdb_ident *tdbi;
	struct tdb *tdb;
	int error, s;
#endif /* IPSEC */

	va_start(ap, m);
	iphlen = va_arg(ap, int);
	va_end(ap);

	udpstat.udps_ipackets++;

	switch (mtod(m, struct ip *)->ip_v) {
	case 4:
		ip = mtod(m, struct ip *);
#ifdef INET6
		ip6 = NULL;
#endif /* INET6 */
		srcsa.sa.sa_family = AF_INET;
		break;
#ifdef INET6
	case 6:
		ip = NULL;
		ip6 = mtod(m, struct ip6_hdr *);
		srcsa.sa.sa_family = AF_INET6;
		break;
#endif /* INET6 */
	default:
		goto bad;
	}

	IP6_EXTHDR_GET(uh, struct udphdr *, m, iphlen, sizeof(struct udphdr));
	if (!uh) {
		udpstat.udps_hdrops++;
		return;
	}

	/* Check for illegal destination port 0 */
	if (uh->uh_dport == 0) {
		udpstat.udps_noport++;
		goto bad;
	}

	/*
	 * Make mbuf data length reflect UDP length.
	 * If not enough data to reflect UDP length, drop.
	 */
	len = ntohs((u_int16_t)uh->uh_ulen);
	if (ip) {
		if (m->m_pkthdr.len - iphlen != len) {
			if (len > (m->m_pkthdr.len - iphlen) ||
			    len < sizeof(struct udphdr)) {
				udpstat.udps_badlen++;
				goto bad;
			}
			m_adj(m, len - (m->m_pkthdr.len - iphlen));
		}
	}
#ifdef INET6
	else if (ip6) {
		/* jumbograms */
		if (len == 0 && m->m_pkthdr.len - iphlen > 0xffff)
			len = m->m_pkthdr.len - iphlen;
		if (len != m->m_pkthdr.len - iphlen) {
			udpstat.udps_badlen++;
			goto bad;
		}
	}
#endif
	else /* shouldn't happen */
		goto bad;

	/*
	 * Save a copy of the IP header in case we want restore it
	 * for sending an ICMP error message in response.
	 */
	if (ip)
		save_ip = *ip;

	/*
	 * Checksum extended UDP header and data.
	 * from W.R.Stevens: check incoming udp cksums even if
	 *	udpcksum is not set.
	 */
	savesum = uh->uh_sum;
#ifdef INET6
	if (ip6) {
		/* Be proactive about malicious use of IPv4 mapped address */
		if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) ||
		    IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) {
			/* XXX stat */
			goto bad;
		}

		/*
		 * In IPv6, the UDP checksum is ALWAYS used.
		 */
		if (uh->uh_sum == 0) {
			udpstat.udps_nosum++;
			goto bad;
		}
		if ((m->m_pkthdr.csum_flags & M_UDP_CSUM_IN_OK) == 0) {
			if (m->m_pkthdr.csum_flags & M_UDP_CSUM_IN_BAD) {
				udpstat.udps_badsum++;
				udpstat.udps_inhwcsum++;
				goto bad;
			}

			if ((uh->uh_sum = in6_cksum(m, IPPROTO_UDP,
			    iphlen, len))) {
				udpstat.udps_badsum++;
				goto bad;
			}
		} else {
			m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_IN_OK;
			udpstat.udps_inhwcsum++;
		}
	} else
#endif /* INET6 */
	if (uh->uh_sum) {
		if ((m->m_pkthdr.csum_flags & M_UDP_CSUM_IN_OK) == 0) {
			if (m->m_pkthdr.csum_flags & M_UDP_CSUM_IN_BAD) {
				udpstat.udps_badsum++;
				udpstat.udps_inhwcsum++;
				m_freem(m);
				return;
			}

			if ((uh->uh_sum = in4_cksum(m, IPPROTO_UDP,
			    iphlen, len))) {
				udpstat.udps_badsum++;
				m_freem(m);
				return;
			}
		} else {
			m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_IN_OK;
			udpstat.udps_inhwcsum++;
		}
	} else
		udpstat.udps_nosum++;

#ifdef IPSEC
	if (udpencap_enable && udpencap_port &&
	    uh->uh_dport == htons(udpencap_port)) {
		u_int32_t spi;
		int skip = iphlen + sizeof(struct udphdr);

		if (m->m_pkthdr.len - skip < sizeof(u_int32_t)) {
			/* packet too short */
			m_freem(m);
			return;
		}
		m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
		/*
		 * decapsulate if the SPI is not zero, otherwise pass
		 * to userland
		 */
		if (spi != 0) {
			if ((m = m_pullup2(m, skip)) == NULL) {
				udpstat.udps_hdrops++;
				return;
			}

			/* remove the UDP header */
			bcopy(mtod(m, u_char *),
			    mtod(m, u_char *) + sizeof(struct udphdr), iphlen);
			m_adj(m, sizeof(struct udphdr));
			skip -= sizeof(struct udphdr);

			espstat.esps_udpencin++;
			ipsec_common_input(m, skip, offsetof(struct ip, ip_p),
			    srcsa.sa.sa_family, IPPROTO_ESP, 1);
			return;
		}
	}
Ejemplo n.º 4
0
Archivo: ndisc.c Proyecto: jlanza/umip
static int ndisc_send_unspec(int oif, const struct in6_addr *dest,
			     uint8_t *hdr, int hdrlen, struct iovec *optv,
			     size_t optvlen)
{
	struct {
		struct ip6_hdr ip;
		struct icmp6_hdr icmp;
		uint8_t data[1500];
	} frame;

	struct msghdr msgh;
	struct cmsghdr *cmsg;
	struct in6_pktinfo *pinfo;
	struct sockaddr_in6 dst;
	char cbuf[CMSG_SPACE(sizeof(*pinfo))];
	struct iovec iov;
	uint8_t *data = (uint8_t *)(&frame.icmp);
	int type, fd, ret, remlen, datalen = 0, written = 0, v = 1;

	if (hdr == NULL || hdrlen < 0 ||
	    (size_t)hdrlen < sizeof(struct icmp6_hdr) ||
	    (size_t)hdrlen > (sizeof(frame) - sizeof(struct ip6_hdr)))
		return -EINVAL;

	if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)) < 0)
		return -1;

	if (setsockopt(fd, IPPROTO_IPV6, IP_HDRINCL, &v, sizeof(v)) < 0) {
		dbg("cannot set IP_HDRINCL: %s\n", strerror(errno));
		close(fd);
		return -errno;
	}

	memset(&frame, 0, sizeof(frame));
	memset(&dst, 0, sizeof(dst));

	dst.sin6_addr = *dest;

	/* Copy ICMPv6 header and update various length values */
	memcpy(data, hdr, hdrlen);
	data += hdrlen;
	datalen += hdrlen;
	remlen = sizeof(frame) - sizeof(struct ip6_hdr) - hdrlen;

	/* Prepare for csum: write trailing options by linearizing iov */
	if ((iov_linearize(data, remlen, optv, optvlen, &written) != 0) ||
	    (written & 0x1)) /* Ensure length is even for csum() */
		return -1;
	datalen += written;

	/* Fill in the IPv6 header */
	frame.ip.ip6_vfc = 0x60;
	frame.ip.ip6_plen = htons(datalen);
	frame.ip.ip6_nxt = IPPROTO_ICMPV6;
	frame.ip.ip6_hlim = 255;
	frame.ip.ip6_dst = *dest;
	/* all other fields are already set to zero */

	frame.icmp.icmp6_cksum = in6_cksum(&in6addr_any, dest, &frame.icmp,
					   datalen, IPPROTO_ICMPV6);

	iov.iov_base = &frame;
	iov.iov_len = sizeof(frame.ip) + datalen;

	dst.sin6_family = AF_INET6;
	msgh.msg_name = &dst;
	msgh.msg_namelen = sizeof(dst);
	msgh.msg_iov = &iov;
	msgh.msg_iovlen = 1;
	msgh.msg_flags = 0;

	memset(cbuf, 0, CMSG_SPACE(sizeof(*pinfo)));
	cmsg = (struct cmsghdr *)cbuf;
	pinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
	pinfo->ipi6_ifindex = oif;

	cmsg->cmsg_len = CMSG_LEN(sizeof(*pinfo));
	cmsg->cmsg_level = IPPROTO_IPV6;
	cmsg->cmsg_type = IPV6_PKTINFO;
	msgh.msg_control = cmsg;
	msgh.msg_controllen = cmsg->cmsg_len;

	ret = sendmsg(fd, &msgh, 0);
	if (ret < 0)
		dbg("sendmsg: %s\n", strerror(errno));

	close(fd);
	type = hdr[0];
	if (type == ND_NEIGHBOR_SOLICIT) {
		statistics_inc(&mipl_stat, MIPL_STATISTICS_OUT_NS_UNSPEC);
	} else if (type == ND_ROUTER_SOLICIT) {
		statistics_inc(&mipl_stat, MIPL_STATISTICS_OUT_RS_UNSPEC);
	}
	return ret;
}
Ejemplo n.º 5
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);
}
Ejemplo n.º 6
0
int
udp6_input(struct mbuf **mp, int *offp, int proto)
{
	struct mbuf *m = *mp;
	struct ip6_hdr *ip6;
	struct udphdr *uh;
	struct inpcb *in6p;
	struct  mbuf *opts = NULL;
	int off = *offp;
	int plen, ulen;
	struct sockaddr_in6 udp_in6;
	struct socket *so;
	struct inpcbinfo *pcbinfo = &udbinfo[0];

	IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE);

	ip6 = mtod(m, struct ip6_hdr *);

	if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
		/* XXX send icmp6 host/port unreach? */
		m_freem(m);
		return IPPROTO_DONE;
	}

	udp_stat.udps_ipackets++;

	plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6);
	uh = (struct udphdr *)((caddr_t)ip6 + off);
	ulen = ntohs((u_short)uh->uh_ulen);

	if (plen != ulen) {
		udp_stat.udps_badlen++;
		goto bad;
	}

	/*
	 * Checksum extended UDP header and data.
	 */
	if (uh->uh_sum == 0)
		udp_stat.udps_nosum++;
	else if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) {
		udp_stat.udps_badsum++;
		goto bad;
	}

	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
		struct	inpcb *last, *marker;

		/*
		 * Deliver a multicast datagram to all sockets
		 * for which the local and remote addresses and ports match
		 * those of the incoming datagram.  This allows more than
		 * one process to receive multicasts on the same port.
		 * (This really ought to be done for unicast datagrams as
		 * well, but that would cause problems with existing
		 * applications that open both address-specific sockets and
		 * a wildcard socket listening to the same port -- they would
		 * end up receiving duplicates of every unicast datagram.
		 * Those applications open the multiple sockets to overcome an
		 * inadequacy of the UDP socket interface, but for backwards
		 * compatibility we avoid the problem here rather than
		 * fixing the interface.  Maybe 4.5BSD will remedy this?)
		 */

		/*
		 * In a case that laddr should be set to the link-local
		 * address (this happens in RIPng), the multicast address
		 * specified in the received packet does not match with
		 * laddr. To cure this situation, the matching is relaxed
		 * if the receiving interface is the same as one specified
		 * in the socket and if the destination multicast address
		 * matches one of the multicast groups specified in the socket.
		 */

		/*
		 * Construct sockaddr format source address.
		 */
		init_sin6(&udp_in6, m); /* general init */
		udp_in6.sin6_port = uh->uh_sport;
		/*
		 * KAME note: traditionally we dropped udpiphdr from mbuf here.
		 * We need udphdr for IPsec processing so we do that later.
		 */

		/*
		 * Locate pcb(s) for datagram.
		 * (Algorithm copied from raw_intr().)
		 */
		last = NULL;

		marker = in_pcbmarker(mycpuid);

		GET_PCBINFO_TOKEN(pcbinfo);

		LIST_INSERT_HEAD(&pcbinfo->pcblisthead, marker, inp_list);
		while ((in6p = LIST_NEXT(marker, inp_list)) != NULL) {
			LIST_REMOVE(marker, inp_list);
			LIST_INSERT_AFTER(in6p, marker, inp_list);

			if (in6p->inp_flags & INP_PLACEMARKER)
				continue;
			if (!INP_ISIPV6(in6p))
				continue;
			if (in6p->in6p_lport != uh->uh_dport)
				continue;
			if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) {
				if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr,
							&ip6->ip6_dst) &&
				    !in6_mcmatch(in6p, &ip6->ip6_dst,
						 m->m_pkthdr.rcvif))
					continue;
			}
			if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
				if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr,
							&ip6->ip6_src) ||
				   in6p->in6p_fport != uh->uh_sport)
					continue;
			}

			if (last != NULL) {
				struct mbuf *n;

#ifdef IPSEC
				/*
				 * Check AH/ESP integrity.
				 */
				if (ipsec6_in_reject_so(m, last->inp_socket))
					ipsec6stat.in_polvio++;
					/* do not inject data into pcb */
				else
#endif /* IPSEC */
#ifdef FAST_IPSEC
				/*
				 * Check AH/ESP integrity.
				 */
				if (ipsec6_in_reject(m, last))
					;
				else
#endif /* FAST_IPSEC */
				if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
					/*
					 * KAME NOTE: do not
					 * m_copy(m, offset, ...) above.
					 * ssb_appendaddr() expects M_PKTHDR,
					 * and m_copy() will copy M_PKTHDR
					 * only if offset is 0.
					 */
					so = last->in6p_socket;
					if ((last->in6p_flags & IN6P_CONTROLOPTS) ||
					    (so->so_options & SO_TIMESTAMP)) {
						ip6_savecontrol(last, &opts,
								ip6, n);
					}
					m_adj(n, off + sizeof(struct udphdr));
					lwkt_gettoken(&so->so_rcv.ssb_token);
					if (ssb_appendaddr(&so->so_rcv,
						    (struct sockaddr *)&udp_in6,
						    n, opts) == 0) {
						m_freem(n);
						if (opts)
							m_freem(opts);
						udp_stat.udps_fullsock++;
					} else {
						sorwakeup(so);
					}
					lwkt_reltoken(&so->so_rcv.ssb_token);
					opts = NULL;
				}
			}
			last = in6p;
			/*
			 * Don't look for additional matches if this one does
			 * not have either the SO_REUSEPORT or SO_REUSEADDR
			 * socket options set.  This heuristic avoids searching
			 * through all pcbs in the common case of a non-shared
			 * port.  It assumes that an application will never
			 * clear these options after setting them.
			 */
			if ((last->in6p_socket->so_options &
			     (SO_REUSEPORT | SO_REUSEADDR)) == 0)
				break;
		}
		LIST_REMOVE(marker, inp_list);

		REL_PCBINFO_TOKEN(pcbinfo);

		if (last == NULL) {
			/*
			 * No matching pcb found; discard datagram.
			 * (No need to send an ICMP Port Unreachable
			 * for a broadcast or multicast datgram.)
			 */
			udp_stat.udps_noport++;
			udp_stat.udps_noportmcast++;
			goto bad;
		}
#ifdef IPSEC
		/*
		 * Check AH/ESP integrity.
		 */
		if (ipsec6_in_reject_so(m, last->inp_socket)) {
			ipsec6stat.in_polvio++;
			goto bad;
		}
#endif /* IPSEC */
#ifdef FAST_IPSEC
		/*
		 * Check AH/ESP integrity.
		 */
		if (ipsec6_in_reject(m, last)) {
			goto bad;
		}
#endif /* FAST_IPSEC */
		if (last->in6p_flags & IN6P_CONTROLOPTS
		    || last->in6p_socket->so_options & SO_TIMESTAMP)
			ip6_savecontrol(last, &opts, ip6, m);

		m_adj(m, off + sizeof(struct udphdr));
		so = last->in6p_socket;
		lwkt_gettoken(&so->so_rcv.ssb_token);
		if (ssb_appendaddr(&so->so_rcv, (struct sockaddr *)&udp_in6,
				   m, opts) == 0) {
			udp_stat.udps_fullsock++;
			lwkt_reltoken(&so->so_rcv.ssb_token);
			goto bad;
		}
		sorwakeup(so);
		lwkt_reltoken(&so->so_rcv.ssb_token);
		return IPPROTO_DONE;
	}
	/*
	 * Locate pcb for datagram.
	 */
	in6p = in6_pcblookup_hash(pcbinfo, &ip6->ip6_src, uh->uh_sport,
				  &ip6->ip6_dst, uh->uh_dport, 1,
				  m->m_pkthdr.rcvif);
	if (in6p == NULL) {
		if (log_in_vain) {
			char buf[INET6_ADDRSTRLEN];

			strcpy(buf, ip6_sprintf(&ip6->ip6_dst));
			log(LOG_INFO,
			    "Connection attempt to UDP [%s]:%d from [%s]:%d\n",
			    buf, ntohs(uh->uh_dport),
			    ip6_sprintf(&ip6->ip6_src), ntohs(uh->uh_sport));
		}
		udp_stat.udps_noport++;
		if (m->m_flags & M_MCAST) {
			kprintf("UDP6: M_MCAST is set in a unicast packet.\n");
			udp_stat.udps_noportmcast++;
			goto bad;
		}
		icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0);
		return IPPROTO_DONE;
	}
#ifdef IPSEC
	/*
	 * Check AH/ESP integrity.
	 */
	if (ipsec6_in_reject_so(m, in6p->in6p_socket)) {
		ipsec6stat.in_polvio++;
		goto bad;
	}
#endif /* IPSEC */
#ifdef FAST_IPSEC
	/*
	 * Check AH/ESP integrity.
	 */
	if (ipsec6_in_reject(m, in6p)) {
		goto bad;
	}
#endif /* FAST_IPSEC */

	/*
	 * Construct sockaddr format source address.
	 * Stuff source address and datagram in user buffer.
	 */
	init_sin6(&udp_in6, m); /* general init */
	udp_in6.sin6_port = uh->uh_sport;
	if (in6p->in6p_flags & IN6P_CONTROLOPTS
	    || in6p->in6p_socket->so_options & SO_TIMESTAMP)
		ip6_savecontrol(in6p, &opts, ip6, m);
	m_adj(m, off + sizeof(struct udphdr));
	so = in6p->in6p_socket;
	lwkt_gettoken(&so->so_rcv.ssb_token);
	if (ssb_appendaddr(&so->so_rcv, (struct sockaddr *)&udp_in6,
			   m, opts) == 0) {
		udp_stat.udps_fullsock++;
		lwkt_reltoken(&so->so_rcv.ssb_token);
		goto bad;
	}
	sorwakeup(so);
	lwkt_reltoken(&so->so_rcv.ssb_token);
	return IPPROTO_DONE;
bad:
	if (m)
		m_freem(m);
	if (opts)
		m_freem(opts);
	return IPPROTO_DONE;
}
Ejemplo n.º 7
0
static int
build_udp_packet_v6(uint8_t *buf, int payloadlen, int *off)
{
	struct ether_header *eh = (struct ether_header *)buf;
	struct ip6_hdr *ip6 = (struct ip6_hdr *)(eh + 1);
	struct ip6_frag *ip6f = (struct ip6_frag *)(ip6 + 1);
	struct udphdr *udp;
	char *data;
	int plen, pktsize = 0;
	static int ident = 0;

	plen = min(payloadlen - *off, MAXPAYLOADLEN);
	pktsize += plen;

	/* Just fake an address */
	eh->ether_shost[0] = 0x01;
	eh->ether_shost[1] = 0xbd;
	eh->ether_shost[2] = 0xbc;
	eh->ether_shost[3] = 0x4d;
	eh->ether_shost[4] = 0xfb;
	eh->ether_shost[5] = 0xfb;
	memcpy(eh->ether_dhost, tapif_ether, ETHER_ADDR_LEN);
	eh->ether_type = htons(ETHERTYPE_IPV6);
	pktsize += sizeof(*eh);

	ip6->ip6_flow = 0;
	ip6->ip6_nxt = IPPROTO_UDP;
	ip6->ip6_hlim = 0xff;
	ip6->ip6_vfc |= IPV6_VERSION;
	ip6->ip6_dst = tapif_ip6;
	ip6->ip6_src = ip6_src;
	pktsize += sizeof(*ip6);

	if (*off == 0) {
		if (plen == payloadlen && !force_fragment) {
			ip6->ip6_plen = htons(plen + sizeof(*udp));
			udp = (struct udphdr *)(ip6 + 1);
		} else {
			ip6->ip6_plen = htons(plen + sizeof(*udp) + sizeof(*ip6f));
			udp = (struct udphdr *)(ip6f + 1);
		}

		udp->uh_sport = htons(src_port);
		udp->uh_dport = htons(SERVER_PORT);
		udp->uh_ulen = htons(payloadlen + sizeof(*udp));
		pktsize += sizeof(*udp);

		data = (char *)(udp + 1);
		memcpy(data, payload, payloadlen);

		udp->uh_sum = 0;
		udp->uh_sum = in6_cksum((u_int8_t *)ip6, IPPROTO_UDP,
		     (char *)udp - (char *)ip6, payloadlen + sizeof(*udp));

		if (plen == payloadlen && !force_fragment)
			goto out;
	} else
		ip6->ip6_plen = htons(plen + sizeof(*ip6f));

	ip6->ip6_nxt = IPPROTO_FRAGMENT;

	ip6f->ip6f_nxt = IPPROTO_UDP;
	ip6f->ip6f_reserved = 0;
	if (*off == 0)
		ip6f->ip6f_offlg = 0;
	else {
		ip6f->ip6f_offlg = sizeof(*udp) + *off;
		assert((ip6f->ip6f_offlg & 0x7) == 0);
		ip6f->ip6f_offlg = htons(ip6f->ip6f_offlg);
	}
	if (*off + plen < payloadlen)
		ip6f->ip6f_offlg |= IP6F_MORE_FRAG;

	if (*off == 0)
		ident++;
	ip6f->ip6f_ident = ident;

	pktsize += sizeof(*ip6f);

	if (*off != 0)
		data = (char *)(ip6f + 1);
	memcpy(data, &payload[*off], plen);

out:
	*off += plen;
	return (pktsize);
}
Ejemplo n.º 8
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);
}