/* * Packets from the mac device come here. We pass them to the peer if * the destination mac address matches or it's a multicast/broadcast * address. */ static void xnbo_from_mac_filter(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t loopback) { _NOTE(ARGUNUSED(loopback)); xnb_t *xnbp = arg; xnbo_t *xnbop = xnbp->xnb_flavour_data; mblk_t *next, *keep, *keep_head, *free, *free_head; keep = keep_head = free = free_head = NULL; #define ADD(list, bp) \ if (list != NULL) \ list->b_next = bp; \ else \ list##_head = bp; \ list = bp; for (; mp != NULL; mp = next) { mac_header_info_t hdr_info; next = mp->b_next; mp->b_next = NULL; if (mac_header_info(xnbop->o_mh, mp, &hdr_info) != 0) { ADD(free, mp); continue; } if ((hdr_info.mhi_dsttype == MAC_ADDRTYPE_BROADCAST) || (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST)) { ADD(keep, mp); continue; } if (bcmp(hdr_info.mhi_daddr, xnbp->xnb_mac_addr, sizeof (xnbp->xnb_mac_addr)) == 0) { ADD(keep, mp); continue; } ADD(free, mp); } #undef ADD if (keep_head != NULL) xnbo_from_mac(xnbp, mrh, keep_head, B_FALSE); if (free_head != NULL) freemsgchain(free_head); }
/* ARGSUSED */ static void pfp_packet(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t flag) { struct T_unitdata_ind *tunit; struct sockaddr_ll *sll; struct sockaddr_ll *sol; mac_header_info_t hdr; struct pfpsock *ps; size_t tusz; mblk_t *mp0; int error; if (mp == NULL) return; ps = arg; if (ps->ps_flow_ctrld) { ps->ps_flow_ctrl_drops++; ps->ps_stats.tp_drops++; ks_stats.kp_recv_flow_cntrld.value.ui64++; freemsg(mp); return; } if (mac_header_info(ps->ps_mh, mp, &hdr) != 0) { /* * Can't decode the packet header information so drop it. */ ps->ps_stats.tp_drops++; ks_stats.kp_recv_mac_hdr_fail.value.ui64++; freemsg(mp); return; } if (mac_type(ps->ps_mh) == DL_ETHER && hdr.mhi_bindsap == ETHERTYPE_VLAN) { struct ether_vlan_header *evhp; struct ether_vlan_header evh; hdr.mhi_hdrsize = sizeof (struct ether_vlan_header); hdr.mhi_istagged = B_TRUE; if (MBLKL(mp) >= sizeof (*evhp)) { evhp = (struct ether_vlan_header *)mp->b_rptr; } else { int sz = sizeof (*evhp); char *s = (char *)&evh; mblk_t *tmp; int len; for (tmp = mp; sz > 0 && tmp != NULL; tmp = tmp->b_cont) { len = min(sz, MBLKL(tmp)); bcopy(tmp->b_rptr, s, len); sz -= len; } evhp = &evh; } hdr.mhi_tci = ntohs(evhp->ether_tci); hdr.mhi_bindsap = ntohs(evhp->ether_type); } if ((ps->ps_proto != 0) && (ps->ps_proto != hdr.mhi_bindsap)) { /* * The packet is not of interest to this socket so * drop it on the floor. Here the SAP is being used * as a very course filter. */ ps->ps_stats.tp_drops++; ks_stats.kp_recv_bad_proto.value.ui64++; freemsg(mp); return; } /* * This field is not often set, even for ethernet, * by mac_header_info, so compute it if it is 0. */ if (hdr.mhi_pktsize == 0) hdr.mhi_pktsize = msgdsize(mp); /* * If a BPF filter is present, pass the raw packet into that. * A failed match will result in zero being returned, indicating * that this socket is not interested in the packet. */ if (ps->ps_bpf.bf_len != 0) { uchar_t *buffer; int buflen; buflen = MBLKL(mp); if (hdr.mhi_pktsize == buflen) { buffer = mp->b_rptr; } else { buflen = 0; buffer = (uchar_t *)mp; } rw_enter(&ps->ps_bpflock, RW_READER); if (bpf_filter(ps->ps_bpf.bf_insns, buffer, hdr.mhi_pktsize, buflen) == 0) { rw_exit(&ps->ps_bpflock); ps->ps_stats.tp_drops++; ks_stats.kp_recv_filtered.value.ui64++; freemsg(mp); return; } rw_exit(&ps->ps_bpflock); } if (ps->ps_type == SOCK_DGRAM) { /* * SOCK_DGRAM socket expect a "layer 3" packet, so advance * past the link layer header. */ mp->b_rptr += hdr.mhi_hdrsize; hdr.mhi_pktsize -= hdr.mhi_hdrsize; } tusz = sizeof (struct T_unitdata_ind) + sizeof (struct sockaddr_ll); if (ps->ps_auxdata) { tusz += _TPI_ALIGN_TOPT(sizeof (struct tpacket_auxdata)); tusz += _TPI_ALIGN_TOPT(sizeof (struct T_opthdr)); } /* * It is tempting to think that this could be optimised by having * the base mblk_t allocated and hung off the pfpsock structure, * except that then another one would need to be allocated for the * sockaddr_ll that is included. Even creating a template to copy * from is of questionable value, as read-write from one structure * to the other is going to be slower than all of the initialisation. */ mp0 = allocb(tusz, BPRI_HI); if (mp0 == NULL) { ps->ps_stats.tp_drops++; ks_stats.kp_recv_alloc_fail.value.ui64++; freemsg(mp); return; } (void) memset(mp0->b_rptr, 0, tusz); mp0->b_datap->db_type = M_PROTO; mp0->b_wptr = mp0->b_rptr + tusz; tunit = (struct T_unitdata_ind *)mp0->b_rptr; tunit->PRIM_type = T_UNITDATA_IND; tunit->SRC_length = sizeof (struct sockaddr); tunit->SRC_offset = sizeof (*tunit); sol = (struct sockaddr_ll *)&ps->ps_sock; sll = (struct sockaddr_ll *)(mp0->b_rptr + sizeof (*tunit)); sll->sll_ifindex = sol->sll_ifindex; sll->sll_hatype = (uint16_t)hdr.mhi_origsap; sll->sll_halen = sol->sll_halen; if (hdr.mhi_saddr != NULL) (void) memcpy(sll->sll_addr, hdr.mhi_saddr, sll->sll_halen); switch (hdr.mhi_dsttype) { case MAC_ADDRTYPE_MULTICAST : sll->sll_pkttype = PACKET_MULTICAST; break; case MAC_ADDRTYPE_BROADCAST : sll->sll_pkttype = PACKET_BROADCAST; break; case MAC_ADDRTYPE_UNICAST : if (memcmp(sol->sll_addr, hdr.mhi_daddr, sol->sll_halen) == 0) sll->sll_pkttype = PACKET_HOST; else sll->sll_pkttype = PACKET_OTHERHOST; break; } if (ps->ps_auxdata) { struct tpacket_auxdata *aux; struct T_opthdr *topt; tunit->OPT_offset = _TPI_ALIGN_TOPT(tunit->SRC_offset + sizeof (struct sockaddr_ll)); tunit->OPT_length = _TPI_ALIGN_TOPT(sizeof (struct T_opthdr)) + _TPI_ALIGN_TOPT(sizeof (struct tpacket_auxdata)); topt = (struct T_opthdr *)(mp0->b_rptr + tunit->OPT_offset); aux = (struct tpacket_auxdata *) ((char *)topt + _TPI_ALIGN_TOPT(sizeof (*topt))); topt->len = tunit->OPT_length; topt->level = SOL_PACKET; topt->name = PACKET_AUXDATA; topt->status = 0; /* * libpcap doesn't seem to use any other field, * so it isn't clear how they should be filled in. */ aux->tp_vlan_vci = hdr.mhi_tci; } linkb(mp0, mp); ps->ps_upcalls->su_recv(ps->ps_upper, mp0, hdr.mhi_pktsize, 0, &error, NULL); if (error == 0) { ps->ps_stats.tp_packets++; ks_stats.kp_recv_ok.value.ui64++; } else { mutex_enter(&ps->ps_lock); if (error == ENOSPC) { ps->ps_upcalls->su_recv(ps->ps_upper, NULL, 0, 0, &error, NULL); if (error == ENOSPC) ps->ps_flow_ctrld = B_TRUE; } mutex_exit(&ps->ps_lock); ps->ps_stats.tp_drops++; ks_stats.kp_recv_fail.value.ui64++; } }