Exemplo n.º 1
0
static
void
process(int fd)
{
	struct sockaddr from;
	socklen_t fromlen;
	int cc;
	union {
		char	buf[MAXPACKETSIZE+1];
		struct	rip rip;
	} inbuf;

	for (;;) {
		fromlen = sizeof(from);
		cc = recvfrom(fd, &inbuf, sizeof(inbuf), 0, &from, &fromlen);
		if (cc <= 0) {
			if (cc < 0 && errno != EWOULDBLOCK)
				perror("recvfrom");
			break;
		}
		if (fromlen != sizeof (struct sockaddr_in)) {
			break;
		}
		rip_input(&from, &inbuf.rip, cc);
	}
}
Exemplo n.º 2
0
/*
 * De-encapsulate a packet and feed it back through ip input (this
 * routine is called whenever IP gets a packet with proto type
 * IPPROTO_GRE and a local destination address).
 * This really is simple
 */
void
gre_input(struct mbuf *m, int off)
{
	int proto;

	proto = (mtod(m, struct ip *))->ip_p;

	m = gre_input2(m, off, proto);

	/*
	 * If no matching tunnel that is up is found. We inject
	 * the mbuf to raw ip socket to see if anyone picks it up.
	 */
	if (m != NULL)
		rip_input(m, off);
}
Exemplo n.º 3
0
void
process(int fd, int pkt_type)
{
	struct sockaddr from;
	int fromlen = sizeof (from), cc, omask;
	struct ipx *ipxdp = (struct ipx *)packet;

	cc = recvfrom(fd, packet, sizeof (packet), 0, &from, &fromlen);
	if (cc <= 0) {
		if (cc < 0 && errno != EINTR)
			syslog(LOG_ERR, "recvfrom: %m");
		return;
	}
	if (tracepackets > 1 && ftrace) {
	    fprintf(ftrace,"rcv %d bytes on %s ", 
		    cc, ipxdp_ntoa(&ipxdp->ipx_dna));
	    fprintf(ftrace," from %s\n", ipxdp_ntoa(&ipxdp->ipx_sna));
	}
	
	if (noteremoterequests && 
	    !ipx_neteqnn(ipxdp->ipx_sna.x_net, ipx_zeronet) &&
	    !ipx_neteq(ipxdp->ipx_sna, ipxdp->ipx_dna))
	{
		syslog(LOG_ERR,
		       "net of interface (%s) != net on ether (%s)!\n",
		       ipxdp_nettoa(ipxdp->ipx_dna.x_net),
		       ipxdp_nettoa(ipxdp->ipx_sna.x_net));
	}
			
	/* We get the IPX header in front of the RIF packet*/
	cc -= sizeof (struct ipx);
#define	mask(s)	(1<<((s)-1))
	omask = sigblock(mask(SIGALRM));
	switch(pkt_type) {
		case SAP_PKT: sap_input(&from, cc);
				break;
		case RIP_PKT: rip_input(&from, cc);
				break;
	}
	sigsetmask(omask);
}
Exemplo n.º 4
0
/*
 * De-encapsulate a packet and feed it back through ip input (this
 * routine is called whenever IP gets a packet with proto type
 * IPPROTO_GRE and a local destination address).
 * This really is simple
 */
int
gre_input(struct mbuf **mp, int *offp, int proto)
{
	struct mbuf *m;
	int ret, off;

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

	proto = (mtod(m, struct ip *))->ip_p;

	ret = gre_input2(m, off, proto);
	/*
	 * ret == 0 : packet not processed, meaning that
	 * no matching tunnel that is up is found.
	 * we inject it to raw ip socket to see if anyone picks it up.
	 */
	if (ret == 0) {
		*mp = m;
		rip_input(mp, offp, proto);
	}
	return(IPPROTO_DONE);
}
Exemplo n.º 5
0
Arquivo: igmp.c Projeto: AoLaD/rtems
void
igmp_input(struct mbuf *m, int iphlen)
{
	register struct igmp *igmp;
	register struct ip *ip;
	register int igmplen;
	register struct ifnet *ifp = m->m_pkthdr.rcvif;
	register int minlen;
	register struct in_multi *inm;
	register struct in_ifaddr *ia;
	struct in_multistep step;
	struct router_info *rti;
	
	int timer; /** timer value in the igmp query header **/

	++igmpstat.igps_rcv_total;

	ip = mtod(m, struct ip *);
	igmplen = ip->ip_len;

	/*
	 * Validate lengths
	 */
	if (igmplen < IGMP_MINLEN) {
		++igmpstat.igps_rcv_tooshort;
		m_freem(m);
		return;
	}
	minlen = iphlen + IGMP_MINLEN;
	if ((m->m_flags & M_EXT || m->m_len < minlen) &&
	    (m = m_pullup(m, minlen)) == 0) {
		++igmpstat.igps_rcv_tooshort;
		return;
	}

	/*
	 * Validate checksum
	 */
	m->m_data += iphlen;
	m->m_len -= iphlen;
	igmp = mtod(m, struct igmp *);
	if (in_cksum(m, igmplen)) {
		++igmpstat.igps_rcv_badsum;
		m_freem(m);
		return;
	}
	m->m_data -= iphlen;
	m->m_len += iphlen;

	ip = mtod(m, struct ip *);
	timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
	rti = find_rti(ifp);

	/*
	 * In the IGMPv2 specification, there are 3 states and a flag.
	 *
	 * In Non-Member state, we simply don't have a membership record.
	 * In Delaying Member state, our timer is running (inm->inm_timer)
	 * In Idle Member state, our timer is not running (inm->inm_timer==0)
	 *
	 * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if
	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
	 * if I sent the last report.
	 */
	switch (igmp->igmp_type) {

	case IGMP_MEMBERSHIP_QUERY:
		++igmpstat.igps_rcv_queries;

		if (ifp->if_flags & IFF_LOOPBACK)
			break;

		if (igmp->igmp_code == 0) {
			/*
			 * Old router.  Remember that the querier on this
			 * interface is old, and set the timer to the
			 * value in RFC 1112.
			 */

			rti->rti_type = IGMP_V1_ROUTER;
			rti->rti_time = 0;

			timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ;

			if (ip->ip_dst.s_addr != igmp_all_hosts_group ||
			    igmp->igmp_group.s_addr != 0) {
				++igmpstat.igps_rcv_badqueries;
				m_freem(m);
				return;
			}
		} else {
			/*
			 * New router.  Simply do the new validity check.
			 */
			
			if (igmp->igmp_group.s_addr != 0 &&
			    !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
				++igmpstat.igps_rcv_badqueries;
				m_freem(m);
				return;
			}
		}

		/*
		 * - Start the timers in all of our membership records
		 *   that the query applies to for the interface on
		 *   which the query arrived excl. those that belong
		 *   to the "all-hosts" group (224.0.0.1).
		 * - Restart any timer that is already running but has
		 *   a value longer than the requested timeout.
		 * - Use the value specified in the query message as
		 *   the maximum timeout.
		 */
		IN_FIRST_MULTI(step, inm);
		while (inm != NULL) {
			if (inm->inm_ifp == ifp &&
			    inm->inm_addr.s_addr != igmp_all_hosts_group &&
			    (igmp->igmp_group.s_addr == 0 ||
			     igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) {
				if (inm->inm_timer == 0 ||
				    inm->inm_timer > timer) {
					inm->inm_timer =
						IGMP_RANDOM_DELAY(timer);
					igmp_timers_are_running = 1;
				}
			}
			IN_NEXT_MULTI(step, inm);
		}

		break;

	case IGMP_V1_MEMBERSHIP_REPORT:
	case IGMP_V2_MEMBERSHIP_REPORT:
		/*
		 * For fast leave to work, we have to know that we are the
		 * last person to send a report for this group.  Reports
		 * can potentially get looped back if we are a multicast
		 * router, so discard reports sourced by me.
		 */
		IFP_TO_IA(ifp, ia);
		if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr)
			break;

		++igmpstat.igps_rcv_reports;

		if (ifp->if_flags & IFF_LOOPBACK)
			break;

		if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
			++igmpstat.igps_rcv_badreports;
			m_freem(m);
			return;
		}

		/*
		 * KLUDGE: if the IP source address of the report has an
		 * unspecified (i.e., zero) subnet number, as is allowed for
		 * a booting host, replace it with the correct subnet number
		 * so that a process-level multicast routing demon can
		 * determine which subnet it arrived from.  This is necessary
		 * to compensate for the lack of any way for a process to
		 * determine the arrival interface of an incoming packet.
		 */
		if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0)
			if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);

		/*
		 * If we belong to the group being reported, stop
		 * our timer for that group.
		 */
		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);

		if (inm != NULL) {
			inm->inm_timer = 0;
			++igmpstat.igps_rcv_ourreports;

			inm->inm_state = IGMP_OTHERMEMBER;
		}

		break;
	}

	/*
	 * Pass all valid IGMP packets up to any process(es) listening
	 * on a raw IGMP socket.
	 */
	rip_input(m, iphlen);
}
Exemplo n.º 6
0
/*
 * ipsec_common_input() gets called when we receive an IPsec-protected packet
 * in IPv4 or IPv6. All it does is find the right TDB and call the appropriate
 * transform. The callback takes care of further processing (like ingress
 * filtering).
 */
int
ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto,
    int udpencap)
{
#define IPSEC_ISTAT(x,y,z) (sproto == IPPROTO_ESP ? (x)++ : \
			    sproto == IPPROTO_AH ? (y)++ : (z)++)

	union sockaddr_union dst_address;
	struct timeval tv;
	struct tdb *tdbp;
	struct ifnet *encif;
	u_int32_t spi;
	u_int16_t cpi;
	int s, error;

	IPSEC_ISTAT(espstat.esps_input, ahstat.ahs_input,
	    ipcompstat.ipcomps_input);

	if (m == 0) {
		DPRINTF(("ipsec_common_input(): NULL packet received\n"));
		IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops,
		    ipcompstat.ipcomps_hdrops);
		return EINVAL;
	}

	if ((sproto == IPPROTO_ESP && !esp_enable) ||
	    (sproto == IPPROTO_AH && !ah_enable) ||
#if NPF > 0
	    (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) ||
#endif
	    (sproto == IPPROTO_IPCOMP && !ipcomp_enable)) {
		switch (af) {
#ifdef INET
		case AF_INET:
			rip_input(m, skip, sproto);
			break;
#endif /* INET */
#ifdef INET6
		case AF_INET6:
			rip6_input(&m, &skip, sproto);
			break;
#endif /* INET6 */
		default:
			DPRINTF(("ipsec_common_input(): unsupported protocol "
			    "family %d\n", af));
			m_freem(m);
			IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf,
			    ipcompstat.ipcomps_nopf);
			return EPFNOSUPPORT;
		}
		return 0;
	}
	if ((sproto == IPPROTO_IPCOMP) && (m->m_flags & M_COMP)) {
		m_freem(m);
		ipcompstat.ipcomps_pdrops++;
		DPRINTF(("ipsec_common_input(): repeated decompression\n"));
		return EINVAL;
	}

	if (m->m_pkthdr.len - skip < 2 * sizeof(u_int32_t)) {
		m_freem(m);
		IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops,
		    ipcompstat.ipcomps_hdrops);
		DPRINTF(("ipsec_common_input(): packet too small\n"));
		return EINVAL;
	}

	/* Retrieve the SPI from the relevant IPsec header */
	if (sproto == IPPROTO_ESP)
		m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
	else if (sproto == IPPROTO_AH)
		m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t),
		    (caddr_t) &spi);
	else if (sproto == IPPROTO_IPCOMP) {
		m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t),
		    (caddr_t) &cpi);
		spi = ntohl(htons(cpi));
	}

	/*
	 * Find tunnel control block and (indirectly) call the appropriate
	 * kernel crypto routine. The resulting mbuf chain is a valid
	 * IP packet ready to go through input processing.
	 */

	memset(&dst_address, 0, sizeof(dst_address));
	dst_address.sa.sa_family = af;

	switch (af) {
#ifdef INET
	case AF_INET:
		dst_address.sin.sin_len = sizeof(struct sockaddr_in);
		m_copydata(m, offsetof(struct ip, ip_dst),
		    sizeof(struct in_addr),
		    (caddr_t) &(dst_address.sin.sin_addr));
		break;
#endif /* INET */

#ifdef INET6
	case AF_INET6:
		dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6);
		m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
		    sizeof(struct in6_addr),
		    (caddr_t) &(dst_address.sin6.sin6_addr));
		in6_recoverscope(&dst_address.sin6, &dst_address.sin6.sin6_addr,
		    NULL);
		break;
#endif /* INET6 */

	default:
		DPRINTF(("ipsec_common_input(): unsupported protocol "
		    "family %d\n", af));
		m_freem(m);
		IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf,
		    ipcompstat.ipcomps_nopf);
		return EPFNOSUPPORT;
	}

	s = splsoftnet();
	tdbp = gettdb(rtable_l2(m->m_pkthdr.ph_rtableid),
	    spi, &dst_address, sproto);
	if (tdbp == NULL) {
		splx(s);
		DPRINTF(("ipsec_common_input(): could not find SA for "
		    "packet to %s, spi %08x\n",
		    ipsp_address(dst_address), ntohl(spi)));
		m_freem(m);
		IPSEC_ISTAT(espstat.esps_notdb, ahstat.ahs_notdb,
		    ipcompstat.ipcomps_notdb);
		return ENOENT;
	}

	if (tdbp->tdb_flags & TDBF_INVALID) {
		splx(s);
		DPRINTF(("ipsec_common_input(): attempted to use invalid SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto));
		m_freem(m);
		IPSEC_ISTAT(espstat.esps_invalid, ahstat.ahs_invalid,
		    ipcompstat.ipcomps_invalid);
		return EINVAL;
	}

	if (udpencap && !(tdbp->tdb_flags & TDBF_UDPENCAP)) {
		splx(s);
		DPRINTF(("ipsec_common_input(): attempted to use non-udpencap SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto));
		m_freem(m);
		espstat.esps_udpinval++;
		return EINVAL;
	}

	if (tdbp->tdb_xform == NULL) {
		splx(s);
		DPRINTF(("ipsec_common_input(): attempted to use uninitialized SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto));
		m_freem(m);
		IPSEC_ISTAT(espstat.esps_noxform, ahstat.ahs_noxform,
		    ipcompstat.ipcomps_noxform);
		return ENXIO;
	}

	if (sproto != IPPROTO_IPCOMP) {
		if ((encif = enc_getif(tdbp->tdb_rdomain,
		    tdbp->tdb_tap)) == NULL) {
			splx(s);
			DPRINTF(("ipsec_common_input(): "
			    "no enc%u interface for SA %s/%08x/%u\n",
			    tdbp->tdb_tap, ipsp_address(dst_address),
			    ntohl(spi), tdbp->tdb_sproto));
			m_freem(m);

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

		/* XXX This conflicts with the scoped nature of IPv6 */
		m->m_pkthdr.rcvif = encif;
	}

	/* Register first use, setup expiration timer. */
	if (tdbp->tdb_first_use == 0) {
		tdbp->tdb_first_use = time_second;

		tv.tv_usec = 0;

		tv.tv_sec = tdbp->tdb_exp_first_use + tdbp->tdb_first_use;
		if (tdbp->tdb_flags & TDBF_FIRSTUSE)
			timeout_add(&tdbp->tdb_first_tmo, hzto(&tv));

		tv.tv_sec = tdbp->tdb_first_use + tdbp->tdb_soft_first_use;
		if (tdbp->tdb_flags & TDBF_SOFT_FIRSTUSE)
			timeout_add(&tdbp->tdb_sfirst_tmo, hzto(&tv));
	}

	/*
	 * Call appropriate transform and return -- callback takes care of
	 * everything else.
	 */
	error = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff);
	splx(s);
	return error;
}
Exemplo n.º 7
0
/* Query all of the listed hosts */
static void
query_loop(char *argv[], int argc, int soc)
{
#define	NA0 (OMSG.rip_auths[0])
#define	NA2 (OMSG.rip_auths[2])
	struct seen {
		struct seen *next;
		struct in_addr addr;
	} *seen, *sp;
	int answered = 0;
	int cc;
	fd_set bits;
	struct timeval now, delay;
	struct sockaddr_in from;
	MD5_CTX md5_ctx;
	struct msghdr msg;
	uint_t	ifindex;
	struct iovec iov;
	uint8_t ancillary_data[CONTROL_BUFSIZE];


	OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST;
	if (ripv2) {
		OMSG.rip_vers = RIPv2;
		if (auth_type == RIP_AUTH_PW) {
			OMSG.rip_nets[1] = OMSG.rip_nets[0];
			NA0.a_family = RIP_AF_AUTH;
			NA0.a_type = RIP_AUTH_PW;
			(void) memcpy(NA0.au.au_pw, passwd, RIP_AUTH_PW_LEN);
			omsg_len += sizeof (OMSG.rip_nets[0]);

		} else if (auth_type == RIP_AUTH_MD5) {
			OMSG.rip_nets[1] = OMSG.rip_nets[0];
			NA0.a_family = RIP_AF_AUTH;
			NA0.a_type = RIP_AUTH_MD5;
			NA0.au.a_md5.md5_keyid = (int8_t)keyid;
			NA0.au.a_md5.md5_auth_len = RIP_AUTH_MD5_LEN;
			NA0.au.a_md5.md5_seqno = 0;
			cc = (char *)&NA2-(char *)&OMSG;
			NA0.au.a_md5.md5_pkt_len = htons(cc);
			NA2.a_family = RIP_AF_AUTH;
			NA2.a_type = RIP_AUTH_TRAILER;
			MD5Init(&md5_ctx);
			MD5Update(&md5_ctx, (uchar_t *)&OMSG, cc+4);
			MD5Update(&md5_ctx,
			    (uchar_t *)passwd, RIP_AUTH_MD5_LEN);
			MD5Final(NA2.au.au_pw, &md5_ctx);
			omsg_len += 2*sizeof (OMSG.rip_nets[0]);
		}

	} else {
		OMSG.rip_vers = RIPv1;
		OMSG.rip_nets[0].n_mask = 0;
	}

	/* ask the first (valid) host */
	seen = NULL;
	while (0 > out(*argv++, soc)) {
		if (*argv == NULL)
			exit(EXIT_FAILURE);
		answered++;
	}

	iov.iov_base = &imsg_buf;
	iov.iov_len = sizeof (imsg_buf);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_name = &from;
	msg.msg_control = &ancillary_data;

	(void) FD_ZERO(&bits);
	FD_SET(soc, &bits);
	for (;;) {
		delay.tv_sec = 0;
		delay.tv_usec = STIME;
		cc = select(soc+1, &bits, 0, 0, &delay);
		if (cc > 0) {
			msg.msg_namelen = sizeof (from);
			msg.msg_controllen = sizeof (ancillary_data);
			cc = recvmsg(soc, &msg, 0);
			if (cc < 0) {
				perror("rtquery: recvmsg");
				exit(EXIT_FAILURE);
			}

			/* avoid looping on high traffic */
			if (answered > argc + 200)
				break;

			/*
			 * count the distinct responding hosts.
			 * You cannot match responding hosts with
			 * addresses to which queries were transmitted,
			 * because a router might respond with a
			 * different source address.
			 */
			for (sp = seen; sp != NULL; sp = sp->next) {
				if (sp->addr.s_addr == from.sin_addr.s_addr)
					break;
			}
			if (sp == NULL) {
				sp = malloc(sizeof (*sp));
				if (sp != NULL) {
					sp->addr = from.sin_addr;
					sp->next = seen;
					seen = sp;
				} else {
					perror("rtquery: malloc");
				}
				answered++;
			}

			ifindex = incoming_interface(&msg);
			rip_input(&from, cc, ifindex);
			continue;
		}

		if (cc < 0) {
			if (errno == EINTR)
				continue;
			perror("rtquery: select");
			exit(EXIT_FAILURE);
		}

		/*
		 * After a pause in responses, probe another host.
		 * This reduces the intermingling of answers.
		 */
		while (*argv != NULL && 0 > out(*argv++, soc))
			answered++;

		/*
		 * continue until no more packets arrive
		 * or we have heard from all hosts
		 */
		if (answered >= argc)
			break;

		/* or until we have waited a long time */
		if (gettimeofday(&now, 0) < 0) {
			perror("rtquery: gettimeofday");
			exit(EXIT_FAILURE);
		}
		if (sent.tv_sec + wtime <= now.tv_sec)
			break;
	}

	/* fail if there was no answer */
	exit(answered >= argc ? EXIT_SUCCESS : EXIT_FAILURE);
}
Exemplo n.º 8
0
int main(int argc, char *argv[])
{
	int ch, cc, count=0, bits;
	struct sockaddr from;
	struct sigaction sigact;
	socklen_t fromlen = sizeof(from);
	int size = 32*1024;
	struct timeval shorttime;

	while ((ch = getopt(argc, argv, "n")) != EOF)
		switch (ch) {
		case 'n':
			nflag++;
			break;
		case '?':
		default:
			goto usage;
		}
	argv += optind;

	if (!*argv) {
usage:		printf("usage: ripquery [-n] hosts...\n");
		exit(1);
	}

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
		perror("socket");
		exit(2);
	}
	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0)
		perror("setsockopt SO_RCVBUF");

	while (*argv) {
		query(*argv++);
		count++;
	}

	/*
	 * Listen for returning packets;
	 * may be more than one packet per host.
	 */
	bits = 1 << s;
	memset(&shorttime, 0, sizeof(shorttime));
	shorttime.tv_usec = STIME;
	memset(&sigact, 0, sizeof(sigact));
	sigact.sa_handler = timeout;
	/*sigact.sa_flags = 0;*/		/* no restart */
	if (sigaction(SIGALRM, &sigact, (struct sigaction *)NULL) == -1)
		perror("sigaction");
	alarm(WTIME);
	while ((count > 0 && !timedout) ||
	    select(20, (fd_set *)&bits, NULL, NULL, &shorttime) > 0) {
		cc = recvfrom(s, packet, sizeof (packet), 0,
		  &from, &fromlen);
		if (cc <= 0) {
			if (cc < 0) {
				if (errno == EINTR)
					continue;
				perror("recvfrom");
				(void) close(s);
				exit(1);
			}
			continue;
		}
		rip_input(&from, cc);
		count--;
	}
	exit (count > 0 ? count : 0);
}
Exemplo n.º 9
0
void
encap4_input(struct mbuf *m, int off)
{
	int proto;
	struct ip *ip;
	struct sockaddr_in s, d;
	const struct protosw *psw;
	struct encaptab *ep, *match;
	int prio, matchprio;

#ifndef __APPLE__
	va_start(ap, m);
	off = va_arg(ap, int);
	proto = va_arg(ap, int);
	va_end(ap);
#endif

	/* Expect 32-bit aligned data pointer on strict-align platforms */
	MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);

	ip = mtod(m, struct ip *);
#ifdef __APPLE__
	proto = ip->ip_p;
#endif

	bzero(&s, sizeof(s));
	s.sin_family = AF_INET;
	s.sin_len = sizeof(struct sockaddr_in);
	s.sin_addr = ip->ip_src;
	bzero(&d, sizeof(d));
	d.sin_family = AF_INET;
	d.sin_len = sizeof(struct sockaddr_in);
	d.sin_addr = ip->ip_dst;

	match = NULL;
	matchprio = 0;
	for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) {
		if (ep->af != AF_INET)
			continue;
		if (ep->proto >= 0 && ep->proto != proto)
			continue;
		if (ep->func)
			prio = (*ep->func)(m, off, proto, ep->arg);
		else {
			/*
			 * it's inbound traffic, we need to match in reverse
			 * order
			 */
			prio = mask_match(ep, (struct sockaddr *)&d,
			    (struct sockaddr *)&s);
		}

		/*
		 * We prioritize the matches by using bit length of the
		 * matches.  mask_match() and user-supplied matching function
		 * should return the bit length of the matches (for example,
		 * if both src/dst are matched for IPv4, 64 should be returned).
		 * 0 or negative return value means "it did not match".
		 *
		 * The question is, since we have two "mask" portion, we
		 * cannot really define total order between entries.
		 * For example, which of these should be preferred?
		 * mask_match() returns 48 (32 + 16) for both of them.
		 *	src=3ffe::/16, dst=3ffe:501::/32
		 *	src=3ffe:501::/32, dst=3ffe::/16
		 *
		 * We need to loop through all the possible candidates
		 * to get the best match - the search takes O(n) for
		 * n attachments (i.e. interfaces).
		 */
		if (prio <= 0)
			continue;
		if (prio > matchprio) {
			matchprio = prio;
			match = ep;
		}
	}

	if (match) {
		/* found a match, "match" has the best one */
		psw = (const struct protosw *)match->psw;
		if (psw && psw->pr_input) {
			encap_fillarg(m, match);
			(*psw->pr_input)(m, off);
		} else
			m_freem(m);
		return;
	}

	/* last resort: inject to raw socket */
	rip_input(m, off);
}