/* * 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); }