Beispiel #1
0
/*
 * Check if protocol type doesn't have a further header and do IPSEC
 * decryption or reject right now.  Protocols with further headers get
 * their IPSEC treatment within the protocol specific processing.
 * Called from ip_input().
 * 1 = drop packet, 0 = continue processing packet.
 */
int
ip_ipsec_input(struct mbuf *m, int nxt)
{
	/*
	 * enforce IPsec policy checking if we are seeing last header.
	 * note that we do not visit this with protocols with pcb layer
	 * code - like udp/tcp/raw ip.
	 */
	if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0)
		return (ipsec4_in_reject(m, NULL));
	return (0);
}
Beispiel #2
0
static int
udp_mcast_input(struct udp_mcast_arg *arg)
{
	struct inpcb *inp = arg->inp;
	struct inpcb *last = arg->last;
	struct ip *ip = arg->ip;
	struct mbuf *m = arg->m;

	if (check_multicast_membership(ip, inp, m) < 0)
		return ERESTART; /* caller continue */

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

#ifdef IPSEC
		/* check AH/ESP integrity. */
		if (ipsec4_in_reject_so(m, last->inp_socket))
			ipsecstat.in_polvio++;
			/* do not inject data to pcb */
		else
#endif /*IPSEC*/
#ifdef FAST_IPSEC
		/* check AH/ESP integrity. */
		if (ipsec4_in_reject(m, last))
			;
		else
#endif /*FAST_IPSEC*/
		if ((n = m_copypacket(m, M_NOWAIT)) != NULL)
			udp_append(last, ip, n,
			    arg->iphlen + sizeof(struct udphdr),
			    arg->udp_in);
	}
	arg->last = last = inp;

	/*
	 * 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->inp_socket->so_options &
	    (SO_REUSEPORT | SO_REUSEADDR)))
		return EJUSTRETURN; /* caller stop */
	return 0;
}
Beispiel #3
0
int
udp_input(struct mbuf **mp, int *offp, int proto)
{
	struct sockaddr_in udp_in = { sizeof udp_in, AF_INET };
	int iphlen;
	struct ip *ip;
	struct udphdr *uh;
	struct inpcb *inp;
	struct mbuf *m;
	struct mbuf *opts = NULL;
	int len, off;
	struct ip save_ip;
	struct inpcbinfo *pcbinfo = &udbinfo[mycpuid];

	off = *offp;
	m = *mp;
	*mp = NULL;

	iphlen = off;
	udp_stat.udps_ipackets++;

	/*
	 * Strip IP options, if any; should skip this,
	 * make available to user, and use on returned packets,
	 * but we don't yet have a way to check the checksum
	 * with options still present.
	 */
	if (iphlen > sizeof(struct ip)) {
		ip_stripoptions(m);
		iphlen = sizeof(struct ip);
	}

	/*
	 * IP and UDP headers are together in first mbuf.
	 * Already checked and pulled up in ip_demux().
	 */
	KASSERT(m->m_len >= iphlen + sizeof(struct udphdr),
	    ("UDP header not in one mbuf"));

	ip = mtod(m, struct ip *);
	uh = (struct udphdr *)((caddr_t)ip + iphlen);

	/* destination port of 0 is illegal, based on RFC768. */
	if (uh->uh_dport == 0)
		goto bad;

	/*
	 * Make mbuf data length reflect UDP length.
	 * If not enough data to reflect UDP length, drop.
	 */
	len = ntohs((u_short)uh->uh_ulen);
	if (ip->ip_len != len) {
		if (len > ip->ip_len || len < sizeof(struct udphdr)) {
			udp_stat.udps_badlen++;
			goto bad;
		}
		m_adj(m, len - ip->ip_len);
		/* ip->ip_len = len; */
	}
	/*
	 * Save a copy of the IP header in case we want restore it
	 * for sending an ICMP error message in response.
	 */
	save_ip = *ip;

	/*
	 * Checksum extended UDP header and data.
	 */
	if (uh->uh_sum) {
		if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
			if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
				uh->uh_sum = m->m_pkthdr.csum_data;
			else
				uh->uh_sum = in_pseudo(ip->ip_src.s_addr,
				    ip->ip_dst.s_addr, htonl((u_short)len +
				    m->m_pkthdr.csum_data + IPPROTO_UDP));
			uh->uh_sum ^= 0xffff;
		} else {
			char b[9];

			bcopy(((struct ipovly *)ip)->ih_x1, b, 9);
			bzero(((struct ipovly *)ip)->ih_x1, 9);
			((struct ipovly *)ip)->ih_len = uh->uh_ulen;
			uh->uh_sum = in_cksum(m, len + sizeof(struct ip));
			bcopy(b, ((struct ipovly *)ip)->ih_x1, 9);
		}
		if (uh->uh_sum) {
			udp_stat.udps_badsum++;
			m_freem(m);
			return(IPPROTO_DONE);
		}
	} else
		udp_stat.udps_nosum++;

	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
	    in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
	    	struct inpcbhead *connhead;
		struct inpcontainer *ic, *ic_marker;
		struct inpcontainerhead *ichead;
		struct udp_mcast_arg arg;
		struct inpcb *last;
		int error;

		/*
		 * Deliver a multicast or broadcast 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 multi/broadcasts 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?)
		 */

		/*
		 * Construct sockaddr format source address.
		 */
		udp_in.sin_port = uh->uh_sport;
		udp_in.sin_addr = ip->ip_src;
		arg.udp_in = &udp_in;
		/*
		 * Locate pcb(s) for datagram.
		 * (Algorithm copied from raw_intr().)
		 */
		last = NULL;
		arg.iphlen = iphlen;

		connhead = &pcbinfo->hashbase[
		    INP_PCBCONNHASH(ip->ip_src.s_addr, uh->uh_sport,
		    ip->ip_dst.s_addr, uh->uh_dport, pcbinfo->hashmask)];
		LIST_FOREACH(inp, connhead, inp_hash) {
#ifdef INET6
			if (!INP_ISIPV4(inp))
				continue;
#endif
			if (!in_hosteq(inp->inp_faddr, ip->ip_src) ||
			    !in_hosteq(inp->inp_laddr, ip->ip_dst) ||
			    inp->inp_fport != uh->uh_sport ||
			    inp->inp_lport != uh->uh_dport)
				continue;

			arg.inp = inp;
			arg.last = last;
			arg.ip = ip;
			arg.m = m;

			error = udp_mcast_input(&arg);
			if (error == ERESTART)
				continue;
			last = arg.last;

			if (error == EJUSTRETURN)
				goto done;
		}

		ichead = &pcbinfo->wildcardhashbase[
		    INP_PCBWILDCARDHASH(uh->uh_dport,
		    pcbinfo->wildcardhashmask)];
		ic_marker = in_pcbcontainer_marker(mycpuid);

		GET_PCBINFO_TOKEN(pcbinfo);
		LIST_INSERT_HEAD(ichead, ic_marker, ic_list);
		while ((ic = LIST_NEXT(ic_marker, ic_list)) != NULL) {
			LIST_REMOVE(ic_marker, ic_list);
			LIST_INSERT_AFTER(ic, ic_marker, ic_list);

			inp = ic->ic_inp;
			if (inp->inp_flags & INP_PLACEMARKER)
				continue;
#ifdef INET6
			if (!INP_ISIPV4(inp))
				continue;
#endif
			if (inp->inp_lport != uh->uh_dport)
				continue;
			if (inp->inp_laddr.s_addr != INADDR_ANY &&
			    inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
				continue;

			arg.inp = inp;
			arg.last = last;
			arg.ip = ip;
			arg.m = m;

			error = udp_mcast_input(&arg);
			if (error == ERESTART)
				continue;
			last = arg.last;

			if (error == EJUSTRETURN)
				break;
		}
		LIST_REMOVE(ic_marker, ic_list);
		REL_PCBINFO_TOKEN(pcbinfo);
done:
		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_noportbcast++;
			goto bad;
		}
#ifdef IPSEC
		/* check AH/ESP integrity. */
		if (ipsec4_in_reject_so(m, last->inp_socket)) {
			ipsecstat.in_polvio++;
			goto bad;
		}
#endif /*IPSEC*/
#ifdef FAST_IPSEC
		/* check AH/ESP integrity. */
		if (ipsec4_in_reject(m, last))
			goto bad;
#endif /*FAST_IPSEC*/
		udp_append(last, ip, m, iphlen + sizeof(struct udphdr),
		    &udp_in);
		return(IPPROTO_DONE);
	}
	/*
	 * Locate pcb for datagram.
	 */
	inp = in_pcblookup_pkthash(pcbinfo, ip->ip_src, uh->uh_sport,
	    ip->ip_dst, uh->uh_dport, TRUE, m->m_pkthdr.rcvif,
	    udp_reuseport_ext ? m : NULL);
	if (inp == NULL) {
		if (log_in_vain) {
			char buf[sizeof "aaa.bbb.ccc.ddd"];

			strcpy(buf, inet_ntoa(ip->ip_dst));
			log(LOG_INFO,
			    "Connection attempt to UDP %s:%d from %s:%d\n",
			    buf, ntohs(uh->uh_dport), inet_ntoa(ip->ip_src),
			    ntohs(uh->uh_sport));
		}
		udp_stat.udps_noport++;
		if (m->m_flags & (M_BCAST | M_MCAST)) {
			udp_stat.udps_noportbcast++;
			goto bad;
		}
		if (blackhole)
			goto bad;
#ifdef ICMP_BANDLIM
		if (badport_bandlim(BANDLIM_ICMP_UNREACH) < 0)
			goto bad;
#endif
		*ip = save_ip;
		ip->ip_len += iphlen;
		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
		return(IPPROTO_DONE);
	}
	KASSERT(INP_ISIPV4(inp), ("not inet inpcb"));
#ifdef IPSEC
	if (ipsec4_in_reject_so(m, inp->inp_socket)) {
		ipsecstat.in_polvio++;
		goto bad;
	}
#endif /*IPSEC*/
#ifdef FAST_IPSEC
	if (ipsec4_in_reject(m, inp))
		goto bad;
#endif /*FAST_IPSEC*/
	/*
	 * Check the minimum TTL for socket.
	 */
	if (ip->ip_ttl < inp->inp_ip_minttl)
		goto bad;

	/*
	 * Construct sockaddr format source address.
	 * Stuff source address and datagram in user buffer.
	 */
	udp_in.sin_port = uh->uh_sport;
	udp_in.sin_addr = ip->ip_src;
	if ((inp->inp_flags & INP_CONTROLOPTS) ||
	    (inp->inp_socket->so_options & SO_TIMESTAMP))
		ip_savecontrol(inp, &opts, ip, m);
	m_adj(m, iphlen + sizeof(struct udphdr));

	lwkt_gettoken(&inp->inp_socket->so_rcv.ssb_token);
	if (ssb_appendaddr(&inp->inp_socket->so_rcv,
	    (struct sockaddr *)&udp_in, m, opts) == 0) {
		lwkt_reltoken(&inp->inp_socket->so_rcv.ssb_token);
		udp_stat.udps_fullsock++;
		goto bad;
	}
	lwkt_reltoken(&inp->inp_socket->so_rcv.ssb_token);
	sorwakeup(inp->inp_socket);
	return(IPPROTO_DONE);
bad:
	m_freem(m);
	if (opts)
		m_freem(opts);
	return(IPPROTO_DONE);
}
Beispiel #4
0
/*
 * Subroutine of udp_input(), which appends the provided mbuf chain to the
 * passed pcb/socket.  The caller must provide a sockaddr_in via udp_in that
 * contains the source address.  If the socket ends up being an IPv6 socket,
 * udp_append() will convert to a sockaddr_in6 before passing the address
 * into the socket code.
 */
static void
udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
    struct sockaddr_in *udp_in)
{
	struct sockaddr *append_sa;
	struct socket *so;
	struct mbuf *opts = 0;
#ifdef INET6
	struct sockaddr_in6 udp_in6;
#endif
	struct udpcb *up;

	INP_LOCK_ASSERT(inp);

	/*
	 * Engage the tunneling protocol.
	 */
	up = intoudpcb(inp);
	if (up->u_tun_func != NULL) {
		(*up->u_tun_func)(n, off, inp);
		return;
	}

	if (n == NULL)
		return;

	off += sizeof(struct udphdr);

#ifdef IPSEC
	/* Check AH/ESP integrity. */
	if (ipsec4_in_reject(n, inp)) {
		m_freem(n);
		IPSECSTAT_INC(ips_in_polvio);
		return;
	}
#ifdef IPSEC_NAT_T
	up = intoudpcb(inp);
	KASSERT(up != NULL, ("%s: udpcb NULL", __func__));
	if (up->u_flags & UF_ESPINUDP_ALL) {	/* IPSec UDP encaps. */
		n = udp4_espdecap(inp, n, off);
		if (n == NULL)				/* Consumed. */
			return;
	}
#endif /* IPSEC_NAT_T */
#endif /* IPSEC */
#ifdef MAC
	if (mac_inpcb_check_deliver(inp, n) != 0) {
		m_freem(n);
		return;
	}
#endif /* MAC */
	if (inp->inp_flags & INP_CONTROLOPTS ||
	    inp->inp_socket->so_options & (SO_TIMESTAMP | SO_BINTIME)) {
#ifdef INET6
		if (inp->inp_vflag & INP_IPV6)
			(void)ip6_savecontrol_v4(inp, n, &opts, NULL);
		else
#endif /* INET6 */
			ip_savecontrol(inp, &opts, ip, n);
	}
#ifdef INET6
	if (inp->inp_vflag & INP_IPV6) {
		bzero(&udp_in6, sizeof(udp_in6));
		udp_in6.sin6_len = sizeof(udp_in6);
		udp_in6.sin6_family = AF_INET6;
		in6_sin_2_v4mapsin6(udp_in, &udp_in6);
		append_sa = (struct sockaddr *)&udp_in6;
	} else
#endif /* INET6 */
		append_sa = (struct sockaddr *)udp_in;
	m_adj(n, off);

	so = inp->inp_socket;
	SOCKBUF_LOCK(&so->so_rcv);
	if (sbappendaddr_locked(&so->so_rcv, append_sa, n, opts) == 0) {
		SOCKBUF_UNLOCK(&so->so_rcv);
		m_freem(n);
		if (opts)
			m_freem(opts);
		UDPSTAT_INC(udps_fullsock);
	} else
		sorwakeup_locked(so);
}
Beispiel #5
0
/*
 * Check if this packet has an active SA and needs to be dropped instead
 * of forwarded.
 * Called from ip_forward().
 * 1 = drop packet, 0 = forward packet.
 */
int
ip_ipsec_fwd(struct mbuf *m)
{

	return (ipsec4_in_reject(m, NULL));
}