Example #1
0
/*
 * Bind a PF_PACKET socket to a network interface.
 *
 * The default operation of this bind() is to place the socket (and thus the
 * network interface) into promiscuous mode. It is then up to the application
 * to turn that down by issuing the relevant ioctls, if desired.
 */
static int
sdpfp_bind(sock_lower_handle_t handle, struct sockaddr *addr,
    socklen_t addrlen, struct cred *cred)
{
	struct sockaddr_ll *addr_ll, *sol;
	mac_client_handle_t mch;
	struct pfpsock *ps;
	mac_handle_t mh;
	int error;

	ps = (struct pfpsock *)handle;
	if (ps->ps_bound)
		return (EINVAL);

	if (addrlen < sizeof (struct sockaddr_ll) || addr == NULL)
		return (EINVAL);

	addr_ll = (struct sockaddr_ll *)addr;

	error = pfp_open_index(addr_ll->sll_ifindex, &mh, &mch, cred);
	if (error != 0)
		return (error);
	/*
	 * Ensure that each socket is only bound once.
	 */
	mutex_enter(&ps->ps_lock);
	if (ps->ps_mh != 0) {
		mutex_exit(&ps->ps_lock);
		pfp_close(mh, mch);
		return (EADDRINUSE);
	}
	ps->ps_mh = mh;
	ps->ps_mch = mch;
	mutex_exit(&ps->ps_lock);

	/*
	 * Cache all of the information from bind so that it's in an easy
	 * place to get at when packets are received.
	 */
	sol = &ps->ps_sock;
	sol->sll_family = AF_PACKET;
	sol->sll_ifindex = addr_ll->sll_ifindex;
	sol->sll_protocol = addr_ll->sll_protocol;
	sol->sll_halen = mac_addr_len(ps->ps_mh);
	mac_unicast_primary_get(ps->ps_mh, sol->sll_addr);
	mac_sdu_get(ps->ps_mh, NULL, &ps->ps_max_sdu);
	ps->ps_linkid = addr_ll->sll_ifindex;

	error = mac_promisc_add(ps->ps_mch, MAC_CLIENT_PROMISC_ALL,
	    pfp_packet, ps, &ps->ps_phd, MAC_PROMISC_FLAGS_VLAN_TAG_STRIP);
	if (error == 0) {
		ps->ps_promisc = MAC_CLIENT_PROMISC_ALL;
		ps->ps_bound = B_TRUE;
	}

	return (error);
}
/* ARGSUSED */
static int
sdpfp_senduio(sock_lower_handle_t handle, struct uio *uiop,
    struct nmsghdr *msg, struct cred *cred)
{
	struct sockaddr_ll *sol;
	mac_client_handle_t mch;
	struct pfpsock *ps;
	boolean_t new_open;
	mac_handle_t mh;
	size_t mpsize;
	uint_t maxsdu;
	mblk_t *mp0;
	mblk_t *mp;
	int error;

	mp = NULL;
	mp0 = NULL;
	new_open = B_FALSE;
	ps = (struct pfpsock *)handle;
	mh = ps->ps_mh;
	mch = ps->ps_mch;
	maxsdu = ps->ps_max_sdu;

	sol = (struct sockaddr_ll *)msg->msg_name;
	if (sol == NULL) {
		/*
		 * If no sockaddr_ll has been provided with the send call,
		 * use the one constructed when the socket was bound to an
		 * interface and fail if it hasn't been bound.
		 */
		if (!ps->ps_bound) {
			ks_stats.kp_send_unbound.value.ui64++;
			return (EPROTO);
		}
		sol = (struct sockaddr_ll *)&ps->ps_sock;
	} else {
		/*
		 * Verify the sockaddr_ll message passed down before using
		 * it to send a packet out with. If it refers to an interface
		 * that has not been bound, it is necessary to open it.
		 */
		struct sockaddr_ll *sll;

		if (msg->msg_namelen < sizeof (struct sockaddr_ll)) {
			ks_stats.kp_send_short_msg.value.ui64++;
			return (EINVAL);
		}

		if (sol->sll_family != AF_PACKET) {
			ks_stats.kp_send_wrong_family.value.ui64++;
			return (EAFNOSUPPORT);
		}

		sll = (struct sockaddr_ll *)&ps->ps_sock;
		if (sol->sll_ifindex != sll->sll_ifindex) {
			error = pfp_open_index(sol->sll_ifindex, &mh, &mch,
			    cred);
			if (error != 0) {
				ks_stats.kp_send_open_fail.value.ui64++;
				return (error);
			}
			mac_sdu_get(mh, NULL, &maxsdu);
			new_open = B_TRUE;
		}
	}

	mpsize = uiop->uio_resid;
	if (mpsize > maxsdu) {
		ks_stats.kp_send_too_big.value.ui64++;
		error = EMSGSIZE;
		goto done;
	}

	if ((mp = allocb(mpsize, BPRI_HI)) == NULL) {
		ks_stats.kp_send_alloc_fail.value.ui64++;
		error = ENOBUFS;
		goto done;
	}

	mp->b_wptr = mp->b_rptr + mpsize;
	error = uiomove(mp->b_rptr, mpsize, UIO_WRITE, uiop);
	if (error != 0) {
		ks_stats.kp_send_uiomove_fail.value.ui64++;
		goto done;
	}

	if (ps->ps_type == SOCK_DGRAM) {
		mp0 = mac_header(mh, sol->sll_addr, sol->sll_protocol, mp, 0);
		if (mp0 == NULL) {
			ks_stats.kp_send_no_memory.value.ui64++;
			error = ENOBUFS;
			goto done;
		}
		linkb(mp0, mp);
		mp = mp0;
	}

	/*
	 * As this is sending datagrams and no promise is made about
	 * how or if a packet will be sent/delivered, no effort is to
	 * be expended in recovering from a situation where the packet
	 * cannot be sent - it is just dropped.
	 */
	error = mac_tx(mch, mp, 0, MAC_DROP_ON_NO_DESC, NULL);
	if (error == 0) {
		mp = NULL;
		ks_stats.kp_send_ok.value.ui64++;
	} else {
		ks_stats.kp_send_failed.value.ui64++;
	}

done:

	if (new_open) {
		ASSERT(mch != ps->ps_mch);
		ASSERT(mh != ps->ps_mh);
		pfp_close(mh, mch);
	}
	if (mp != NULL)
		freemsg(mp);

	return (error);

}