/* * 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_ioctl(sock_lower_handle_t handle, int cmd, intptr_t arg, int mod, int32_t *rval, struct cred *cr) { #if defined(_SYSCALL32) struct timeval32 tival; #else struct timeval tival; #endif mac_client_promisc_type_t mtype; datalink_id_t linkid; struct lifreq lifreq; struct ifreq ifreq; struct pfpsock *ps; mac_handle_t mh; timespec_t tv; int error; switch (cmd) { /* * ioctls that work on "struct lifreq" */ case SIOCSLIFFLAGS : case SIOCGLIFINDEX : case SIOCGLIFFLAGS : case SIOCGLIFMTU : error = pfp_lifreq_getlinkid(arg, &lifreq, &linkid); if (error != 0) return (error); break; /* * ioctls that work on "struct ifreq". * Not all of these have a "struct lifreq" partner, for example * SIOCGIFHWADDR, for the simple reason that the logical interface * does not have a hardware address. */ case SIOCSIFFLAGS : case SIOCGIFINDEX : case SIOCGIFFLAGS : case SIOCGIFMTU : case SIOCGIFHWADDR : error = pfp_ifreq_getlinkid(arg, &ifreq, &linkid); if (error != 0) return (error); break; } error = mac_open_by_linkid(linkid, &mh); if (error != 0) return (error); ps = (struct pfpsock *)handle; switch (cmd) { case SIOCGLIFINDEX : lifreq.lifr_index = linkid; break; case SIOCGIFINDEX : ifreq.ifr_index = linkid; break; case SIOCGIFFLAGS : ifreq.ifr_flags = IFF_RUNNING; if (ps->ps_promisc == MAC_CLIENT_PROMISC_ALL) ifreq.ifr_flags |= IFF_PROMISC; break; case SIOCGLIFFLAGS : lifreq.lifr_flags = IFF_RUNNING; if (ps->ps_promisc == MAC_CLIENT_PROMISC_ALL) lifreq.lifr_flags |= IFF_PROMISC; break; case SIOCSIFFLAGS : if (linkid != ps->ps_linkid) { error = EINVAL; } else { if ((ifreq.ifr_flags & IFF_PROMISC) != 0) mtype = MAC_CLIENT_PROMISC_ALL; else mtype = MAC_CLIENT_PROMISC_FILTERED; error = pfp_set_promisc(ps, mtype); } break; case SIOCSLIFFLAGS : if (linkid != ps->ps_linkid) { error = EINVAL; } else { if ((lifreq.lifr_flags & IFF_PROMISC) != 0) mtype = MAC_CLIENT_PROMISC_ALL; else mtype = MAC_CLIENT_PROMISC_FILTERED; error = pfp_set_promisc(ps, mtype); } break; case SIOCGIFMTU : mac_sdu_get(mh, NULL, &ifreq.ifr_mtu); break; case SIOCGLIFMTU : mac_sdu_get(mh, NULL, &lifreq.lifr_mtu); break; case SIOCGIFHWADDR : mac_unicast_primary_get(mh, (uint8_t *)ifreq.ifr_addr.sa_data); ifreq.ifr_addr.sa_family = pfp_dl_to_arphrd(mac_type(mh)); break; case SIOCGSTAMP : (void) gethrestime(&tv); tival.tv_sec = (time_t)tv.tv_sec; tival.tv_usec = tv.tv_nsec / 1000; error = ddi_copyout(&tival, (void *)arg, sizeof (tival), 0); break; default : break; } mac_close(mh); if (error == 0) { /* * Only the "GET" ioctls need to copy data back to userace. */ switch (cmd) { case SIOCGLIFINDEX : case SIOCGLIFFLAGS : case SIOCGLIFMTU : error = ddi_copyout(&lifreq, (void *)arg, sizeof (lifreq), 0); break; case SIOCGIFINDEX : case SIOCGIFFLAGS : case SIOCGIFMTU : case SIOCGIFHWADDR : error = ddi_copyout(&ifreq, (void *)arg, sizeof (ifreq), 0); break; default : break; } } 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); }
/* ARGSUSED */ int vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid, vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr, int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid, int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag, cred_t *credp) { vnic_t *vnic; mac_register_t *mac; int err; boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0); char vnic_name[MAXNAMELEN]; const mac_info_t *minfop; uint32_t req_hwgrp_flag = B_FALSE; *diag = VNIC_IOC_DIAG_NONE; rw_enter(&vnic_lock, RW_WRITER); /* does a VNIC with the same id already exist? */ err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), (mod_hash_val_t *)&vnic); if (err == 0) { rw_exit(&vnic_lock); return (EEXIST); } vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP); if (vnic == NULL) { rw_exit(&vnic_lock); return (ENOMEM); } bzero(vnic, sizeof (*vnic)); vnic->vn_id = vnic_id; vnic->vn_link_id = linkid; vnic->vn_vrid = vrid; vnic->vn_af = af; if (!is_anchor) { if (linkid == DATALINK_INVALID_LINKID) { err = EINVAL; goto bail; } /* * Open the lower MAC and assign its initial bandwidth and * MAC address. We do this here during VNIC creation and * do not wait until the upper MAC client open so that we * can validate the VNIC creation parameters (bandwidth, * MAC address, etc) and reserve a factory MAC address if * one was requested. */ err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh); if (err != 0) goto bail; /* * VNIC(vlan) over VNICs(vlans) is not supported. */ if (mac_is_vnic(vnic->vn_lower_mh)) { err = EINVAL; goto bail; } /* only ethernet support for now */ minfop = mac_info(vnic->vn_lower_mh); if (minfop->mi_nativemedia != DL_ETHER) { err = ENOTSUP; goto bail; } (void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL, NULL); err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch, vnic_name, MAC_OPEN_FLAGS_IS_VNIC); if (err != 0) goto bail; /* assign a MAC address to the VNIC */ err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot, mac_prefix_len, mac_len, mac_addr, flags, diag, vid, req_hwgrp_flag); if (err != 0) { vnic->vn_muh = NULL; if (diag != NULL && req_hwgrp_flag) *diag = VNIC_IOC_DIAG_NO_HWRINGS; goto bail; } /* register to receive notification from underlying MAC */ vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb, vnic); *vnic_addr_type = vnic->vn_addr_type; vnic->vn_addr_len = *mac_len; vnic->vn_vid = vid; bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len); if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) vnic->vn_slot_id = *mac_slot; /* * Set the initial VNIC capabilities. If the VNIC is created * over MACs which does not support nactive vlan, disable * VNIC's hardware checksum capability if its VID is not 0, * since the underlying MAC would get the hardware checksum * offset wrong in case of VLAN packets. */ if (vid == 0 || !mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL)) { if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM, &vnic->vn_hcksum_txflags)) vnic->vn_hcksum_txflags = 0; } else { vnic->vn_hcksum_txflags = 0; } } /* register with the MAC module */ if ((mac = mac_alloc(MAC_VERSION)) == NULL) goto bail; mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; mac->m_driver = vnic; mac->m_dip = vnic_get_dip(); mac->m_instance = (uint_t)-1; mac->m_src_addr = vnic->vn_addr; mac->m_callbacks = &vnic_m_callbacks; if (!is_anchor) { /* * If this is a VNIC based VLAN, then we check for the * margin unless it has been created with the force * flag. If we are configuring a VLAN over an etherstub, * we don't check the margin even if force is not set. */ if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) { if (vid != VLAN_ID_NONE) vnic->vn_force = B_TRUE; /* * As the current margin size of the underlying mac is * used to determine the margin size of the VNIC * itself, request the underlying mac not to change * to a smaller margin size. */ err = mac_margin_add(vnic->vn_lower_mh, &vnic->vn_margin, B_TRUE); ASSERT(err == 0); } else { vnic->vn_margin = VLAN_TAGSZ; err = mac_margin_add(vnic->vn_lower_mh, &vnic->vn_margin, B_FALSE); if (err != 0) { mac_free(mac); if (diag != NULL) *diag = VNIC_IOC_DIAG_MACMARGIN_INVALID; goto bail; } } mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu, &mac->m_max_sdu); err = mac_mtu_add(vnic->vn_lower_mh, &mac->m_max_sdu, B_FALSE); if (err != 0) { VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); mac_free(mac); if (diag != NULL) *diag = VNIC_IOC_DIAG_MACMTU_INVALID; goto bail; } vnic->vn_mtu = mac->m_max_sdu; } else { vnic->vn_margin = VLAN_TAGSZ; mac->m_min_sdu = 1; mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU; vnic->vn_mtu = ANCHOR_VNIC_MAX_MTU; } mac->m_margin = vnic->vn_margin; err = mac_register(mac, &vnic->vn_mh); mac_free(mac); if (err != 0) { if (!is_anchor) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); } goto bail; } /* Set the VNIC's MAC in the client */ if (!is_anchor) { mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp); if (mrp != NULL) { if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 || (mrp->mrp_mask & MRP_TX_RINGS) != 0) { req_hwgrp_flag = B_TRUE; } err = mac_client_set_resources(vnic->vn_mch, mrp); if (err != 0) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); (void) mac_unregister(vnic->vn_mh); goto bail; } } } err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp)); if (err != 0) { VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); if (!is_anchor) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); } (void) mac_unregister(vnic->vn_mh); goto bail; } /* add new VNIC to hash table */ err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id), (mod_hash_val_t)vnic); ASSERT(err == 0); vnic_count++; /* * Now that we've enabled this VNIC, we should go through and update the * link state by setting it to our parents. */ vnic->vn_enabled = B_TRUE; if (is_anchor) { mac_link_update(vnic->vn_mh, LINK_STATE_UP); } else { mac_link_update(vnic->vn_mh, mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE)); } rw_exit(&vnic_lock); return (0); bail: rw_exit(&vnic_lock); if (!is_anchor) { if (vnic->vn_mnh != NULL) (void) mac_notify_remove(vnic->vn_mnh, B_TRUE); if (vnic->vn_muh != NULL) (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh); if (vnic->vn_mch != NULL) mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC); if (vnic->vn_lower_mh != NULL) mac_close(vnic->vn_lower_mh); } kmem_cache_free(vnic_cache, vnic); return (err); }
static boolean_t xnbo_open_mac(xnb_t *xnbp, char *mac) { xnbo_t *xnbop = xnbp->xnb_flavour_data; int err; const mac_info_t *mi; void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *, boolean_t); struct ether_addr ea; uint_t max_sdu; mac_diag_t diag; if ((err = mac_open_by_linkname(mac, &xnbop->o_mh)) != 0) { cmn_err(CE_WARN, "xnbo_open_mac: " "cannot open mac for link %s (%d)", mac, err); return (B_FALSE); } ASSERT(xnbop->o_mh != NULL); mi = mac_info(xnbop->o_mh); ASSERT(mi != NULL); if (mi->mi_media != DL_ETHER) { cmn_err(CE_WARN, "xnbo_open_mac: " "device is not DL_ETHER (%d)", mi->mi_media); i_xnbo_close_mac(xnbp, B_TRUE); return (B_FALSE); } if (mi->mi_media != mi->mi_nativemedia) { cmn_err(CE_WARN, "xnbo_open_mac: " "device media and native media mismatch (%d != %d)", mi->mi_media, mi->mi_nativemedia); i_xnbo_close_mac(xnbp, B_TRUE); return (B_FALSE); } mac_sdu_get(xnbop->o_mh, NULL, &max_sdu); if (max_sdu > XNBMAXPKT) { cmn_err(CE_WARN, "xnbo_open_mac: mac device SDU too big (%d)", max_sdu); i_xnbo_close_mac(xnbp, B_TRUE); return (B_FALSE); } /* * MAC_OPEN_FLAGS_MULTI_PRIMARY is relevant when we are migrating a * guest on the localhost itself. In this case we would have the MAC * client open for the guest being migrated *and* also for the * migrated guest (i.e. the former will be active till the migration * is complete when the latter will be activated). This flag states * that it is OK for mac_unicast_add to add the primary MAC unicast * address multiple times. */ if (mac_client_open(xnbop->o_mh, &xnbop->o_mch, NULL, MAC_OPEN_FLAGS_USE_DATALINK_NAME | MAC_OPEN_FLAGS_MULTI_PRIMARY) != 0) { cmn_err(CE_WARN, "xnbo_open_mac: " "error (%d) opening mac client", err); i_xnbo_close_mac(xnbp, B_TRUE); return (B_FALSE); } if (xnbop->o_need_rx_filter) rx_fn = xnbo_from_mac_filter; else rx_fn = xnbo_from_mac; err = mac_unicast_add_set_rx(xnbop->o_mch, NULL, MAC_UNICAST_PRIMARY, &xnbop->o_mah, 0, &diag, xnbop->o_multicast_control ? rx_fn : NULL, xnbp); if (err != 0) { cmn_err(CE_WARN, "xnbo_open_mac: failed to get the primary " "MAC address of %s: %d", mac, err); i_xnbo_close_mac(xnbp, B_TRUE); return (B_FALSE); } if (!xnbop->o_multicast_control) { err = mac_promisc_add(xnbop->o_mch, MAC_CLIENT_PROMISC_ALL, rx_fn, xnbp, &xnbop->o_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP | MAC_PROMISC_FLAGS_VLAN_TAG_STRIP); if (err != 0) { cmn_err(CE_WARN, "xnbo_open_mac: " "cannot enable promiscuous mode of %s: %d", mac, err); i_xnbo_close_mac(xnbp, B_TRUE); return (B_FALSE); } xnbop->o_promiscuous = B_TRUE; } if (xnbop->o_need_setphysaddr) { err = mac_unicast_primary_set(xnbop->o_mh, xnbp->xnb_mac_addr); /* Warn, but continue on. */ if (err != 0) { bcopy(xnbp->xnb_mac_addr, ea.ether_addr_octet, ETHERADDRL); cmn_err(CE_WARN, "xnbo_open_mac: " "cannot set MAC address of %s to " "%s: %d", mac, ether_sprintf(&ea), err); } } if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM, &xnbop->o_hcksum_capab)) xnbop->o_hcksum_capab = 0; xnbop->o_running = B_TRUE; return (B_TRUE); }
static void mac_bpf_sdu_get(uintptr_t mhandle, uint_t *mtup) { mac_sdu_get((mac_handle_t)mhandle, NULL, mtup); }
/* * DL_INFO_REQ */ static void proto_info_req(dld_str_t *dsp, mblk_t *mp) { dl_info_ack_wrapper_t *dlwp; dl_info_ack_t *dlp; dl_qos_cl_sel1_t *selp; dl_qos_cl_range1_t *rangep; uint8_t *addr; uint8_t *brdcst_addr; uint_t addr_length; uint_t sap_length; mac_info_t minfo; mac_info_t *minfop; queue_t *q = dsp->ds_wq; /* * Swap the request message for one large enough to contain the * wrapper structure defined above. */ if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t), M_PCPROTO, 0)) == NULL) return; bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t)); dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr; dlp = &(dlwp->dl_info); ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr); dlp->dl_primitive = DL_INFO_ACK; /* * Set up the sub-structure pointers. */ addr = dlwp->dl_addr; brdcst_addr = dlwp->dl_brdcst_addr; rangep = &(dlwp->dl_qos_range1); selp = &(dlwp->dl_qos_sel1); /* * This driver supports only version 2 connectionless DLPI provider * nodes. */ dlp->dl_service_mode = DL_CLDLS; dlp->dl_version = DL_VERSION_2; /* * Set the style of the provider */ dlp->dl_provider_style = dsp->ds_style; ASSERT(dlp->dl_provider_style == DL_STYLE1 || dlp->dl_provider_style == DL_STYLE2); /* * Set the current DLPI state. */ dlp->dl_current_state = dsp->ds_dlstate; /* * Gratuitously set the media type. This is to deal with modules * that assume the media type is known prior to DL_ATTACH_REQ * being completed. */ dlp->dl_mac_type = DL_ETHER; /* * If the stream is not at least attached we try to retrieve the * mac_info using mac_info_get() */ if (dsp->ds_dlstate == DL_UNATTACHED || dsp->ds_dlstate == DL_ATTACH_PENDING || dsp->ds_dlstate == DL_DETACH_PENDING) { if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) { /* * Cannot find mac_info. giving up. */ goto done; } minfop = &minfo; } else { minfop = (mac_info_t *)dsp->ds_mip; /* We can only get the sdu if we're attached. */ mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu); } /* * Set the media type (properly this time). */ if (dsp->ds_native) dlp->dl_mac_type = minfop->mi_nativemedia; else dlp->dl_mac_type = minfop->mi_media; /* * Set the DLSAP length. We only support 16 bit values and they * appear after the MAC address portion of DLSAP addresses. */ sap_length = sizeof (uint16_t); dlp->dl_sap_length = NEG(sap_length); addr_length = minfop->mi_addr_length; /* * Copy in the media broadcast address. */ if (minfop->mi_brdcst_addr != NULL) { dlp->dl_brdcst_addr_offset = (uintptr_t)brdcst_addr - (uintptr_t)dlp; bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); dlp->dl_brdcst_addr_length = addr_length; } /* Only VLAN links and links that have a normal tag mode support QOS. */ if ((dsp->ds_mch != NULL && mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE) || (dsp->ds_dlp != NULL && dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_NORMAL)) { dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); rangep->dl_qos_type = DL_QOS_CL_RANGE1; rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; rangep->dl_protection.dl_min = DL_UNKNOWN; rangep->dl_protection.dl_max = DL_UNKNOWN; rangep->dl_residual_error = DL_UNKNOWN; /* * Specify the supported range of priorities. */ rangep->dl_priority.dl_min = 0; rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); selp->dl_qos_type = DL_QOS_CL_SEL1; selp->dl_trans_delay = DL_UNKNOWN; selp->dl_protection = DL_UNKNOWN; selp->dl_residual_error = DL_UNKNOWN; /* * Specify the current priority (which can be changed by * the DL_UDQOS_REQ primitive). */ selp->dl_priority = dsp->ds_pri; } dlp->dl_addr_length = addr_length + sizeof (uint16_t); if (dsp->ds_dlstate == DL_IDLE) { /* * The stream is bound. Therefore we can formulate a valid * DLSAP address. */ dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; if (addr_length > 0) mac_unicast_primary_get(dsp->ds_mh, addr); *(uint16_t *)(addr + addr_length) = dsp->ds_sap; } done: IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0); IMPLY(dlp->dl_qos_range_offset != 0, dlp->dl_qos_range_length != 0); IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0); IMPLY(dlp->dl_brdcst_addr_offset != 0, dlp->dl_brdcst_addr_length != 0); qreply(q, mp); }
/* * DL_UINTDATA_REQ */ void proto_unitdata_req(dld_str_t *dsp, mblk_t *mp) { queue_t *q = dsp->ds_wq; dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr; off_t off; size_t len, size; const uint8_t *addr; uint16_t sap; uint_t addr_length; mblk_t *bp, *payload; uint32_t start, stuff, end, value, flags; t_uscalar_t dl_err; uint_t max_sdu; if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0); return; } mutex_enter(&dsp->ds_lock); if (dsp->ds_dlstate != DL_IDLE) { mutex_exit(&dsp->ds_lock); dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0); return; } DLD_DATATHR_INC(dsp); mutex_exit(&dsp->ds_lock); addr_length = dsp->ds_mip->mi_addr_length; off = dlp->dl_dest_addr_offset; len = dlp->dl_dest_addr_length; if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { dl_err = DL_BADPRIM; goto failed; } if (len != addr_length + sizeof (uint16_t)) { dl_err = DL_BADADDR; goto failed; } addr = mp->b_rptr + off; sap = *(uint16_t *)(mp->b_rptr + off + addr_length); /* * Check the length of the packet and the block types. */ size = 0; payload = mp->b_cont; for (bp = payload; bp != NULL; bp = bp->b_cont) { if (DB_TYPE(bp) != M_DATA) goto baddata; size += MBLKL(bp); } mac_sdu_get(dsp->ds_mh, NULL, &max_sdu); if (size > max_sdu) goto baddata; /* * Build a packet header. */ if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max, &payload)) == NULL) { dl_err = DL_BADADDR; goto failed; } /* * We no longer need the M_PROTO header, so free it. */ freeb(mp); /* * Transfer the checksum offload information if it is present. */ hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value, &flags); (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0); /* * Link the payload onto the new header. */ ASSERT(bp->b_cont == NULL); bp->b_cont = payload; /* * No lock can be held across modules and putnext()'s, * which can happen here with the call from DLD_TX(). */ if (DLD_TX(dsp, bp, 0, 0) != NULL) { /* flow-controlled */ DLD_SETQFULL(dsp); } DLD_DATATHR_DCR(dsp); return; failed: dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); DLD_DATATHR_DCR(dsp); return; baddata: dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); DLD_DATATHR_DCR(dsp); }
/* ARGSUSED */ static int sdpfp_ioctl(sock_lower_handle_t handle, int cmd, intptr_t arg, int mod, int32_t *rval, struct cred *cr) { struct timeval tival; mac_client_promisc_type_t mtype; struct sockaddr_dl *sock; datalink_id_t linkid; struct lifreq lifreq; struct ifreq ifreq; struct pfpsock *ps; mac_handle_t mh; int error; ps = (struct pfpsock *)handle; switch (cmd) { /* * ioctls that work on "struct lifreq" */ case SIOCSLIFFLAGS : case SIOCGLIFINDEX : case SIOCGLIFFLAGS : case SIOCGLIFMTU : case SIOCGLIFHWADDR : error = pfp_lifreq_getlinkid(arg, &lifreq, &linkid, mod); if (error != 0) return (error); break; /* * ioctls that work on "struct ifreq". * Not all of these have a "struct lifreq" partner, for example * SIOCGIFHWADDR, for the simple reason that the logical interface * does not have a hardware address. */ case SIOCSIFFLAGS : case SIOCGIFINDEX : case SIOCGIFFLAGS : case SIOCGIFMTU : case SIOCGIFHWADDR : error = pfp_ifreq_getlinkid(arg, &ifreq, &linkid, mod); if (error != 0) return (error); break; case SIOCGSTAMP : tival.tv_sec = (time_t)ps->ps_timestamp.tv_sec; tival.tv_usec = ps->ps_timestamp.tv_nsec / 1000; if (get_udatamodel() == DATAMODEL_NATIVE) { error = ddi_copyout(&tival, (void *)arg, sizeof (tival), mod); } #ifdef _SYSCALL32_IMPL else { struct timeval32 tv32; TIMEVAL_TO_TIMEVAL32(&tv32, &tival); error = ddi_copyout(&tv32, (void *)arg, sizeof (tv32), mod); } #endif return (error); } error = mac_open_by_linkid(linkid, &mh); if (error != 0) return (error); switch (cmd) { case SIOCGLIFINDEX : lifreq.lifr_index = linkid; break; case SIOCGIFINDEX : ifreq.ifr_index = linkid; break; case SIOCGIFFLAGS : ifreq.ifr_flags = IFF_RUNNING; if (ps->ps_promisc == MAC_CLIENT_PROMISC_ALL) ifreq.ifr_flags |= IFF_PROMISC; break; case SIOCGLIFFLAGS : lifreq.lifr_flags = IFF_RUNNING; if (ps->ps_promisc == MAC_CLIENT_PROMISC_ALL) lifreq.lifr_flags |= IFF_PROMISC; break; case SIOCSIFFLAGS : if (linkid != ps->ps_linkid) { error = EINVAL; } else { if ((ifreq.ifr_flags & IFF_PROMISC) != 0) mtype = MAC_CLIENT_PROMISC_ALL; else mtype = MAC_CLIENT_PROMISC_FILTERED; error = pfp_set_promisc(ps, mtype); } break; case SIOCSLIFFLAGS : if (linkid != ps->ps_linkid) { error = EINVAL; } else { if ((lifreq.lifr_flags & IFF_PROMISC) != 0) mtype = MAC_CLIENT_PROMISC_ALL; else mtype = MAC_CLIENT_PROMISC_FILTERED; error = pfp_set_promisc(ps, mtype); } break; case SIOCGIFMTU : mac_sdu_get(mh, NULL, &ifreq.ifr_mtu); break; case SIOCGLIFMTU : mac_sdu_get(mh, NULL, &lifreq.lifr_mtu); break; case SIOCGIFHWADDR : if (mac_addr_len(mh) > sizeof (ifreq.ifr_addr.sa_data)) { error = EPFNOSUPPORT; break; } if (mac_addr_len(mh) == 0) { (void) memset(ifreq.ifr_addr.sa_data, 0, sizeof (ifreq.ifr_addr.sa_data)); } else { mac_unicast_primary_get(mh, (uint8_t *)ifreq.ifr_addr.sa_data); } /* * The behaviour here in setting sa_family is consistent * with what applications such as tcpdump would expect * for a Linux PF_PACKET socket. */ ifreq.ifr_addr.sa_family = pfp_dl_to_arphrd(mac_type(mh)); break; case SIOCGLIFHWADDR : lifreq.lifr_type = 0; sock = (struct sockaddr_dl *)&lifreq.lifr_addr; if (mac_addr_len(mh) > sizeof (sock->sdl_data)) { error = EPFNOSUPPORT; break; } /* * Fill in the sockaddr_dl with link layer details. Of note, * the index is returned as 0 for a couple of reasons: * (1) there is no public API that uses or requires it * (2) the MAC index is currently 32bits and sdl_index is 16. */ sock->sdl_family = AF_LINK; sock->sdl_index = 0; sock->sdl_type = mac_type(mh); sock->sdl_nlen = 0; sock->sdl_alen = mac_addr_len(mh); sock->sdl_slen = 0; if (mac_addr_len(mh) == 0) { (void) memset(sock->sdl_data, 0, sizeof (sock->sdl_data)); } else { mac_unicast_primary_get(mh, (uint8_t *)sock->sdl_data); } break; default : break; } mac_close(mh); if (error == 0) { /* * Only the "GET" ioctls need to copy data back to userace. */ switch (cmd) { case SIOCGLIFINDEX : case SIOCGLIFFLAGS : case SIOCGLIFMTU : case SIOCGLIFHWADDR : error = ddi_copyout(&lifreq, (void *)arg, sizeof (lifreq), mod); break; case SIOCGIFINDEX : case SIOCGIFFLAGS : case SIOCGIFMTU : case SIOCGIFHWADDR : error = ddi_copyout(&ifreq, (void *)arg, sizeof (ifreq), mod); break; default : break; } } return (error); }