/** * Calculates the number of segments required to represent the mbuf. * * @returns Number of segments. * @param pThis The instance. * @param pMBuf The mbuf. * @param pvFrame The frame pointer, optional. */ DECLINLINE(unsigned) vboxNetFltDarwinMBufCalcSGSegs(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame) { NOREF(pThis); /* * Count the buffers in the chain. */ unsigned cSegs = 0; for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur)) if (mbuf_len(pCur)) cSegs++; else if ( !cSegs && pvFrame && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf)) cSegs++; #ifdef PADD_RUNT_FRAMES_FROM_HOST /* * Add one buffer if the total is less than the ethernet minimum 60 bytes. * This may allocate a segment too much if the ethernet header is separated, * but that shouldn't harm us much. */ if (mbuf_pkthdr_len(pMBuf) < 60) cSegs++; #endif #ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE /* maximize the number of segments. */ cSegs = RT_MAX(VBOXNETFLT_DARWIN_MAX_SEGS - 1, cSegs); #endif return cSegs ? cSegs : 1; }
size_t mbuf_maxlen(const mbuf_t mbuf) { if (mbuf->m_flags & M_EXT) return (mbuf->m_ext.ext_size); return (&mbuf->m_dat[MLEN] - ((char *)mbuf_datastart(mbuf))); }
errno_t mbuf_align_32(mbuf_t mbuf, size_t len) { if ((mbuf->m_flags & M_EXT) != 0 && m_mclhasreference(mbuf)) return (ENOTSUP); mbuf->m_data = mbuf_datastart(mbuf); mbuf->m_data += ((mbuf_trailingspace(mbuf) - len) &~ (sizeof(u_int32_t) - 1)); return (0); }
errno_t mbuf_setdata(mbuf_t mbuf, void *data, size_t len) { size_t start = (size_t)((char *)mbuf_datastart(mbuf)); size_t maxlen = mbuf_maxlen(mbuf); if ((size_t)data < start || ((size_t)data) + len > start + maxlen) return (EINVAL); mbuf->m_data = data; mbuf->m_len = len; return (0); }
errno_t mbuf_adjustlen(mbuf_t m, int amount) { /* Verify m_len will be valid after adding amount */ if (amount > 0) { int used = (size_t)mbuf_data(m) - (size_t)mbuf_datastart(m) + m->m_len; if ((size_t)(amount + used) > mbuf_maxlen(m)) return (EINVAL); } else if (-amount > m->m_len) { return (EINVAL); } m->m_len += amount; return (0); }
errno_t kn_tcp_pkt_from_params(mbuf_t *data, u_int8_t tcph_flags, u_int32_t iph_saddr, u_int32_t iph_daddr, u_int16_t tcph_sport, u_int16_t tcph_dport, u_int32_t tcph_seq, u_int32_t tcph_ack, const char* payload, size_t payload_len) { int retval = 0; size_t tot_data_len, tot_buf_len, max_len; // mac osx thing.. to be safe, leave out 14 bytes for ethernet header. void *buf = NULL; struct ip* o_iph; struct tcphdr* o_tcph; u_int16_t csum; mbuf_csum_request_flags_t csum_flags = 0; boolean_t pkt_allocated = FALSE; tot_data_len = sizeof(struct ip) + sizeof(struct tcphdr) + payload_len; tot_buf_len = tot_data_len + ETHHDR_LEN; // allocate the packet retval = mbuf_allocpacket(MBUF_DONTWAIT, tot_buf_len, NULL, data); if (retval != 0) { kn_debug("mbuf_allocpacket returned error %d\n", retval); goto FAILURE; } else { pkt_allocated = TRUE; } max_len = mbuf_maxlen(*data); if (max_len < tot_buf_len) { kn_debug("no enough buffer space, try to request more.\n"); retval = mbuf_prepend(data, tot_buf_len - max_len, MBUF_DONTWAIT); if (retval != 0) { kn_debug("mbuf_prepend returned error %d\n", retval); goto FAILURE; } } mbuf_pkthdr_setlen(*data, tot_data_len); retval = mbuf_pkthdr_setrcvif(*data, NULL); if (retval != 0) { kn_debug("mbuf_pkthdr_setrcvif returned error %d\n", retval); goto FAILURE; } mbuf_setlen(*data, tot_data_len); retval = mbuf_setdata(*data, (mbuf_datastart(*data) + ETHHDR_LEN), tot_data_len); if (retval != 0) { kn_debug("mbuf_setdata returned error %d\n", retval); goto FAILURE; } buf = mbuf_data(*data); mbuf_pkthdr_setheader(*data, buf); o_iph = (struct ip*)buf; memset(o_iph, 0, sizeof(struct ip)); // setup IPv4 header o_iph->ip_hl = sizeof(struct ip) / 4; o_iph->ip_v = 4; o_iph->ip_tos = 0; o_iph->ip_id = 0; o_iph->ip_off = htons(IP_DF); o_iph->ip_p = IPPROTO_TCP; o_iph->ip_len = htons(tot_data_len); o_iph->ip_sum = 0; o_iph->ip_ttl = 64; o_iph->ip_src.s_addr = iph_saddr; o_iph->ip_dst.s_addr = iph_daddr; o_tcph = (struct tcphdr*)((char*)o_iph + sizeof(struct ip)); memset(o_tcph, 0, sizeof(struct tcphdr)); o_tcph->th_sport = tcph_sport; o_tcph->th_dport = tcph_dport; o_tcph->th_seq = tcph_seq; o_tcph->th_ack = tcph_ack; o_tcph->th_flags = tcph_flags; o_tcph->th_win = 0xffffU; o_tcph->th_off = sizeof(struct tcphdr) / 4; o_tcph->th_sum = 0; o_tcph->th_urp = 0; if (payload_len > 0) { memcpy((char*)o_tcph + sizeof(struct tcphdr), payload, payload_len); } mbuf_clear_csum_performed(*data); csum_flags |= MBUF_CSUM_REQ_IP; retval = mbuf_get_csum_requested(*data, &csum_flags, NULL); if (retval != 0) { kn_debug("mbuf_get_csum_requested returned error %d\n", retval); goto FAILURE; } /* calculate TCP checksum */ csum = kn_tcp_sum_calc(sizeof(struct tcphdr) + payload_len, (u_int16_t*)&o_iph->ip_src.s_addr, (u_int16_t*)&o_iph->ip_dst.s_addr, (u_int16_t*)o_tcph); o_tcph->th_sum = csum; return 0; FAILURE: if (pkt_allocated == TRUE) { mbuf_free(*data); } return retval; }
/** * Initializes a SG list from an mbuf. * * @returns Number of segments. * @param pThis The instance. * @param pMBuf The mbuf. * @param pSG The SG. * @param pvFrame The frame pointer, optional. * @param cSegs The number of segments allocated for the SG. * This should match the number in the mbuf exactly! * @param fSrc The source of the frame. */ DECLINLINE(void) vboxNetFltDarwinMBufToSG(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc) { NOREF(pThis); /* * Walk the chain and convert the buffers to segments. Works INTNETSG::cbTotal. */ unsigned iSeg = 0; IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/); for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur)) { size_t cbSeg = mbuf_len(pCur); if (cbSeg) { void *pvSeg = mbuf_data(pCur); /* deal with pvFrame */ if (!iSeg && pvFrame && pvFrame != pvSeg) { void *pvStart = mbuf_datastart(pMBuf); uintptr_t offSeg = (uintptr_t)pvSeg - (uintptr_t)pvStart; uintptr_t offSegEnd = offSeg + cbSeg; Assert(pvStart && pvSeg && offSeg < mbuf_maxlen(pMBuf) && offSegEnd <= mbuf_maxlen(pMBuf)); NOREF(offSegEnd); uintptr_t offFrame = (uintptr_t)pvFrame - (uintptr_t)pvStart; if (RT_LIKELY(offFrame < offSeg)) { pvSeg = pvFrame; cbSeg += offSeg - offFrame; } else AssertMsgFailed(("pvFrame=%p pvStart=%p pvSeg=%p offSeg=%p cbSeg=%#zx offSegEnd=%p offFrame=%p maxlen=%#zx\n", pvFrame, pvStart, pvSeg, offSeg, cbSeg, offSegEnd, offFrame, mbuf_maxlen(pMBuf))); pvFrame = NULL; } AssertBreak(iSeg < cSegs); pSG->cbTotal += cbSeg; pSG->aSegs[iSeg].cb = cbSeg; pSG->aSegs[iSeg].pv = pvSeg; pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS; iSeg++; } /* The pvFrame might be in a now empty buffer. */ else if ( !iSeg && pvFrame && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf)) { cbSeg = (uintptr_t)mbuf_datastart(pMBuf) + mbuf_maxlen(pMBuf) - (uintptr_t)pvFrame; pSG->cbTotal += cbSeg; pSG->aSegs[iSeg].cb = cbSeg; pSG->aSegs[iSeg].pv = pvFrame; pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS; iSeg++; pvFrame = NULL; } } Assert(iSeg && iSeg <= cSegs); pSG->cSegsUsed = iSeg; #ifdef PADD_RUNT_FRAMES_FROM_HOST /* * Add a trailer if the frame is too small. * * Since we're getting to the packet before it is framed, it has not * yet been padded. The current solution is to add a segment pointing * to a buffer containing all zeros and pray that works for all frames... */ if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST)) { AssertReturnVoid(iSeg < cSegs); static uint8_t const s_abZero[128] = {0}; pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS; pSG->aSegs[iSeg].pv = (void *)&s_abZero[0]; pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal; pSG->cbTotal = 60; pSG->cSegsUsed++; } #endif #ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE /* * Redistribute the segments. */ if (pSG->cSegsUsed < pSG->cSegsAlloc) { /* copy the segments to the end. */ int iSrc = pSG->cSegsUsed; int iDst = pSG->cSegsAlloc; while (iSrc > 0) { iDst--; iSrc--; pSG->aSegs[iDst] = pSG->aSegs[iSrc]; } /* create small segments from the start. */ pSG->cSegsUsed = pSG->cSegsAlloc; iSrc = iDst; iDst = 0; while ( iDst < iSrc && iDst < pSG->cSegsAlloc) { pSG->aSegs[iDst].Phys = NIL_RTHCPHYS; pSG->aSegs[iDst].pv = pSG->aSegs[iSrc].pv; pSG->aSegs[iDst].cb = RT_MIN(pSG->aSegs[iSrc].cb, VBOXNETFLT_DARWIN_TEST_SEG_SIZE); if (pSG->aSegs[iDst].cb != pSG->aSegs[iSrc].cb) { pSG->aSegs[iSrc].cb -= pSG->aSegs[iDst].cb; pSG->aSegs[iSrc].pv = (uint8_t *)pSG->aSegs[iSrc].pv + pSG->aSegs[iDst].cb; } else if (++iSrc >= pSG->cSegsAlloc) { pSG->cSegsUsed = iDst + 1; break; } iDst++; } } #endif AssertMsg(!pvFrame, ("pvFrame=%p pMBuf=%p iSeg=%d\n", pvFrame, pMBuf, iSeg)); }
errno_t firewire_inet_arp( ifnet_t ifp, u_short arpop, const struct sockaddr_dl* sender_hw, const struct sockaddr* sender_proto, const struct sockaddr_dl* target_hw, const struct sockaddr* target_proto) { mbuf_t m; errno_t result; register struct firewire_header *fwh; register IP1394_ARP *fwa; const struct sockaddr_in* sender_ip = (const struct sockaddr_in*)sender_proto; const struct sockaddr_in* target_ip = (const struct sockaddr_in*)target_proto; char *datap; IOFWInterface *fwIf = (IOFWInterface*)ifnet_softc(ifp); if(fwIf == NULL) return EINVAL; IOFireWireIP *fwIpObj = (IOFireWireIP*)fwIf->getController(); if(fwIpObj == NULL) return EINVAL; LCB *lcb = fwIpObj->getLcb(); if (target_ip == NULL) return EINVAL; if ((sender_ip && sender_ip->sin_family != AF_INET) || (target_ip && target_ip->sin_family != AF_INET)) return EAFNOSUPPORT; result = mbuf_gethdr(MBUF_DONTWAIT, MBUF_TYPE_DATA, &m); if (result != 0) return result; mbuf_setlen(m, sizeof(*fwa)); mbuf_pkthdr_setlen(m, sizeof(*fwa)); /* Move the data pointer in the mbuf to the end, aligned to 4 bytes */ datap = (char*)mbuf_datastart(m); datap += mbuf_trailingspace(m); datap -= (((u_long)datap) & 0x3); mbuf_setdata(m, datap, sizeof(*fwa)); fwa = (IP1394_ARP*)mbuf_data(m); bzero((caddr_t)fwa, sizeof(*fwa)); /* Prepend the ethernet header, we will send the raw frame */ result = mbuf_prepend(&m, sizeof(*fwh), MBUF_DONTWAIT); if(result != 0) return result; fwh = (struct firewire_header*)mbuf_data(m); fwh->fw_type = htons(FWTYPE_ARP); /* Fill out the arp packet */ fwa->hardwareType = htons(ARP_HDW_TYPE); fwa->protocolType = htons(FWTYPE_IP); fwa->hwAddrLen = sizeof(IP1394_HDW_ADDR); fwa->ipAddrLen = IPV4_ADDR_SIZE; fwa->opcode = htons(arpop); fwa->senderMaxRec = lcb->ownHardwareAddress.maxRec; fwa->sspd = lcb->ownHardwareAddress.spd; fwa->senderUnicastFifoHi = htons(lcb->ownHardwareAddress.unicastFifoHi); fwa->senderUnicastFifoLo = htonl(lcb->ownHardwareAddress.unicastFifoLo); /* Sender Hardware */ if (sender_hw != NULL) bcopy(CONST_LLADDR(sender_hw), &fwa->senderUniqueID, sizeof(fwa->senderUniqueID)); else ifnet_lladdr_copy_bytes(ifp, &fwa->senderUniqueID, FIREWIRE_ADDR_LEN); ifnet_lladdr_copy_bytes(ifp, fwh->fw_shost, sizeof(fwh->fw_shost)); /* Sender IP */ if (sender_ip != NULL) fwa->senderIpAddress = sender_ip->sin_addr.s_addr; else { ifaddr_t *addresses; struct sockaddr sa; if (ifnet_get_address_list_family(ifp, &addresses, AF_INET) == 0) { ifaddr_address( addresses[0], &sa, 16 ); fwa->senderIpAddress = ((UInt32)(sa.sa_data[5] & 0xFF)) << 24; fwa->senderIpAddress |= ((UInt32)(sa.sa_data[4] & 0xFF)) << 16; fwa->senderIpAddress |= ((UInt32)(sa.sa_data[3] & 0xFF)) << 8; fwa->senderIpAddress |= ((UInt32)(sa.sa_data[2] & 0xFF)); ifnet_free_address_list(addresses); } else { mbuf_free(m); return ENXIO; } } /* Target Hardware */ if (target_hw == 0) bcopy(fwbroadcastaddr, fwh->fw_dhost, sizeof(fwh->fw_dhost)); else bcopy(CONST_LLADDR(target_hw), fwh->fw_dhost, sizeof(fwh->fw_dhost)); /* Target IP */ fwa->targetIpAddress = target_ip->sin_addr.s_addr; ifnet_output_raw(ifp, PF_INET, m); return 0; }