Esempio n. 1
0
static int
pcap_netmap_dispatch(pcap_t *p, int cnt, pcap_handler cb, u_char *user)
{
	int ret;
	struct pcap_netmap *pn = p->priv;
	struct nm_desc *d = pn->d;
	struct pollfd pfd = { .fd = p->fd, .events = POLLIN, .revents = 0 };

	pn->cb = cb;
	pn->cb_arg = user;

	for (;;) {
		if (p->break_loop) {
			p->break_loop = 0;
			return PCAP_ERROR_BREAK;
		}
		/* nm_dispatch won't run forever */

		ret = nm_dispatch((void *)d, cnt, (void *)pcap_netmap_filter, (void *)p);
		if (ret != 0)
			break;
		errno = 0;
		ret = poll(&pfd, 1, p->opt.timeout);
	}
	return ret;
}


/* XXX need to check the NIOCTXSYNC/poll */
static int
pcap_netmap_inject(pcap_t *p, const void *buf, size_t size)
{
	struct pcap_netmap *pn = p->priv;
	struct nm_desc *d = pn->d;

	return nm_inject(d, buf, size);
}


static int
pcap_netmap_ioctl(pcap_t *p, u_long what, uint32_t *if_flags)
{
	struct pcap_netmap *pn = p->priv;
	struct nm_desc *d = pn->d;
	struct ifreq ifr;
	int error, fd = d->fd;

#ifdef linux
	fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		fprintf(stderr, "Error: cannot get device control socket.\n");
		return -1;
	}
#endif /* linux */
	bzero(&ifr, sizeof(ifr));
	strncpy(ifr.ifr_name, d->req.nr_name, sizeof(ifr.ifr_name));
	switch (what) {
	case SIOCSIFFLAGS:
		/*
		 * The flags we pass in are 32-bit and unsigned.
		 *
		 * On most if not all UN*Xes, ifr_flags is 16-bit and
		 * signed, and the result of assigning a longer
		 * unsigned value to a shorter signed value is
		 * implementation-defined (even if, in practice, it'll
		 * do what's intended on all platforms we support
		 * result of assigning a 32-bit unsigned value).
		 * So we mask out the upper 16 bits.
		 */
		ifr.ifr_flags = *if_flags & 0xffff;
#ifdef __FreeBSD__
		/*
		 * In FreeBSD, we need to set the high-order flags,
		 * as we're using IFF_PPROMISC, which is in those bits.
		 *
		 * XXX - DragonFly BSD?
		 */
		ifr.ifr_flagshigh = *if_flags >> 16;
#endif /* __FreeBSD__ */
		break;
	}
	error = ioctl(fd, what, &ifr);
	if (!error) {
		switch (what) {
		case SIOCGIFFLAGS:
			/*
			 * The flags we return are 32-bit.
			 *
			 * On most if not all UN*Xes, ifr_flags is
			 * 16-bit and signed, and will get sign-
			 * extended, so that the upper 16 bits of
			 * those flags will be forced on.  So we
			 * mask out the upper 16 bits of the
			 * sign-extended value.
			 */
			*if_flags = ifr.ifr_flags & 0xffff;
#ifdef __FreeBSD__
			/*
			 * In FreeBSD, we need to return the
			 * high-order flags, as we're using
			 * IFF_PPROMISC, which is in those bits.
			 *
			 * XXX - DragonFly BSD?
			 */
			*if_flags |= (ifr.ifr_flagshigh << 16);
#endif /* __FreeBSD__ */
		}
	}
#ifdef linux
	close(fd);
#endif /* linux */
	return error ? -1 : 0;
}


static void
pcap_netmap_close(pcap_t *p)
{
	struct pcap_netmap *pn = p->priv;
	struct nm_desc *d = pn->d;
	uint32_t if_flags = 0;

	if (pn->must_clear_promisc) {
		pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
		if (if_flags & IFF_PPROMISC) {
			if_flags &= ~IFF_PPROMISC;
			pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
		}
	}
	nm_close(d);
	pcap_cleanup_live_common(p);
}


static int
pcap_netmap_activate(pcap_t *p)
{
	struct pcap_netmap *pn = p->priv;
	struct nm_desc *d;
	uint32_t if_flags = 0;

	d = nm_open(p->opt.device, NULL, 0, NULL);
	if (d == NULL) {
		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
		    errno, "netmap open: cannot access %s",
		    p->opt.device);
		pcap_cleanup_live_common(p);
		return (PCAP_ERROR);
	}
	if (0)
	    fprintf(stderr, "%s device %s priv %p fd %d ports %d..%d\n",
		__FUNCTION__, p->opt.device, d, d->fd,
		d->first_rx_ring, d->last_rx_ring);
	pn->d = d;
	p->fd = d->fd;

	/*
	 * Turn a negative snapshot value (invalid), a snapshot value of
	 * 0 (unspecified), or a value bigger than the normal maximum
	 * value, into the maximum allowed value.
	 *
	 * If some application really *needs* a bigger snapshot
	 * length, we should just increase MAXIMUM_SNAPLEN.
	 */
	if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
		p->snapshot = MAXIMUM_SNAPLEN;

	if (p->opt.promisc && !(d->req.nr_ringid & NETMAP_SW_RING)) {
		pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
		if (!(if_flags & IFF_PPROMISC)) {
			pn->must_clear_promisc = 1;
			if_flags |= IFF_PPROMISC;
			pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
		}
	}
	p->linktype = DLT_EN10MB;
	p->selectable_fd = p->fd;
	p->read_op = pcap_netmap_dispatch;
	p->inject_op = pcap_netmap_inject,
	p->setfilter_op = install_bpf_program;
	p->setdirection_op = NULL;
	p->set_datalink_op = NULL;
	p->getnonblock_op = pcap_getnonblock_fd;
	p->setnonblock_op = pcap_setnonblock_fd;
	p->stats_op = pcap_netmap_stats;
	p->cleanup_op = pcap_netmap_close;

	return (0);
}


pcap_t *
pcap_netmap_create(const char *device, char *ebuf, int *is_ours)
{
	pcap_t *p;

	*is_ours = (!strncmp(device, "netmap:", 7) || !strncmp(device, "vale", 4));
	if (! *is_ours)
		return NULL;
	p = pcap_create_common(ebuf, sizeof (struct pcap_netmap));
	if (p == NULL)
		return (NULL);
	p->activate_op = pcap_netmap_activate;
	return (p);
}
Esempio n. 2
0
static int
pcap_netmap_dispatch(pcap_t *p, int cnt, pcap_handler cb, u_char *user)
{
	int ret;
	struct pcap_netmap *pn = NM_PRIV(p);
	struct nm_desc *d = pn->d;
	struct pollfd pfd = { .fd = p->fd, .events = POLLIN, .revents = 0 };

	pn->cb = cb;
	pn->cb_arg = user;

	for (;;) {
		if (p->break_loop) {
			p->break_loop = 0;
			return PCAP_ERROR_BREAK;
		}
		/* nm_dispatch won't run forever */

		ret = nm_dispatch((void *)d, cnt, (void *)pcap_netmap_filter, (void *)p);
		if (ret != 0)
			break;
		errno = 0;
		ret = poll(&pfd, 1, p->the_timeout);
	}
	return ret;
}


/* XXX need to check the NIOCTXSYNC/poll */
static int
pcap_netmap_inject(pcap_t *p, const void *buf, size_t size)
{
	struct nm_desc *d = NM_PRIV(p)->d;

	return nm_inject(d, buf, size);
}


static int
pcap_netmap_ioctl(pcap_t *p, u_long what, uint32_t *if_flags)
{
	struct pcap_netmap *pn = NM_PRIV(p);
	struct nm_desc *d = pn->d;
	struct ifreq ifr;
	int error, fd = d->fd;

#ifdef linux
	fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		fprintf(stderr, "Error: cannot get device control socket.\n");
		return -1;
	}
#endif /* linux */
	bzero(&ifr, sizeof(ifr));
	strncpy(ifr.ifr_name, d->req.nr_name, sizeof(ifr.ifr_name));
	switch (what) {
	case SIOCSIFFLAGS:
		ifr.ifr_flags = *if_flags;
#ifdef __FreeBSD__
		ifr.ifr_flagshigh = *if_flags >> 16;
#endif /* __FreeBSD__ */
		break;
	}
	error = ioctl(fd, what, &ifr);
	if (!error) {
		switch (what) {
		case SIOCGIFFLAGS:
			*if_flags = ifr.ifr_flags;
#ifdef __FreeBSD__
			*if_flags |= (ifr.ifr_flagshigh << 16);
#endif /* __FreeBSD__ */
		}
	}
#ifdef linux
	close(fd);
#endif /* linux */
	return error ? -1 : 0;
}


static void
pcap_netmap_close(pcap_t *p)
{
	struct pcap_netmap *pn = NM_PRIV(p);
	struct nm_desc *d = pn->d;
	uint32_t if_flags = 0;

	if (pn->must_clear_promisc) {
		pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
		if (if_flags & IFF_PPROMISC) {
			if_flags &= ~IFF_PPROMISC;
			pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
		}
	}
	nm_close(d);
#ifdef HAVE_NO_PRIV
	free(pn);
	SET_PRIV(p, NULL); // unnecessary
#endif
	pcap_cleanup_live_common(p);
}


static int
pcap_netmap_activate(pcap_t *p)
{
	struct pcap_netmap *pn = NM_PRIV(p);
	struct nm_desc *d = nm_open(p->opt.source, NULL, 0, NULL);
	uint32_t if_flags = 0;

	if (d == NULL) {
		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
			"netmap open: cannot access %s: %s\n",
			p->opt.source, pcap_strerror(errno));
#ifdef HAVE_NO_PRIV
		free(pn);
		SET_PRIV(p, NULL); // unnecessary
#endif
		pcap_cleanup_live_common(p);
		return (PCAP_ERROR);
	}
	if (0)
	    fprintf(stderr, "%s device %s priv %p fd %d ports %d..%d\n",
		__FUNCTION__, p->opt.source, d, d->fd,
		d->first_rx_ring, d->last_rx_ring);
	pn->d = d;
	p->fd = d->fd;
	if (p->opt.promisc && !(d->req.nr_ringid & NETMAP_SW_RING)) {
		pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
		if (!(if_flags & IFF_PPROMISC)) {
			pn->must_clear_promisc = 1;
			if_flags |= IFF_PPROMISC;
			pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
		}
	}
	p->linktype = DLT_EN10MB;
	p->selectable_fd = p->fd;
	p->read_op = pcap_netmap_dispatch;
	p->inject_op = pcap_netmap_inject,
	p->setfilter_op = install_bpf_program;
	p->setdirection_op = NULL;
	p->set_datalink_op = NULL;
	p->getnonblock_op = pcap_getnonblock_fd;
	p->setnonblock_op = pcap_setnonblock_fd;
	p->stats_op = pcap_netmap_stats;
	p->cleanup_op = pcap_netmap_close;

	return (0);
}


pcap_t *
pcap_netmap_create(const char *device, char *ebuf, int *is_ours)
{
	pcap_t *p;

	*is_ours = (!strncmp(device, "netmap:", 7) || !strncmp(device, "vale", 4));
	if (! *is_ours)
		return NULL;
#ifdef HAVE_NO_PRIV
	{
		void *pn = calloc(1, sizeof(struct pcap_netmap));
		if (pn == NULL)
			return NULL;
		p = pcap_create_common(device, ebuf);
		if (p == NULL) {
			free(pn);
			return NULL;
		}
		SET_PRIV(p, pn);
	}
#else
	p = pcap_create_common(device, ebuf, sizeof (struct pcap_netmap));
	if (p == NULL)
		return (NULL);
#endif
	p->activate_op = pcap_netmap_activate;
	return (p);
}