int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst) { NOREF(pvIfData); int rc = VINF_SUCCESS; ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis); if (pIfNet) { /* * Create a mbuf for the gather list and push it onto the wire. * * Note! If the interface is in the promiscuous mode we need to send the * packet down the stack so it reaches the driver and Berkeley * Packet Filter (see @bugref{5817}). */ if ((fDst & INTNETTRUNKDIR_WIRE) || vboxNetFltDarwinIsPromiscuous(pThis)) { mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG); if (pMBuf) { errno_t err = ifnet_output_raw(pIfNet, PF_LINK, pMBuf); if (err) rc = RTErrConvertFromErrno(err); } else rc = VERR_NO_MEMORY; } /* * Create a mbuf for the gather list and push it onto the host stack. */ if (fDst & INTNETTRUNKDIR_HOST) { mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG); if (pMBuf) { /* This is what IONetworkInterface::inputPacket does. */ unsigned const cbEthHdr = 14; mbuf_pkthdr_setheader(pMBuf, mbuf_data(pMBuf)); mbuf_pkthdr_setlen(pMBuf, mbuf_pkthdr_len(pMBuf) - cbEthHdr); mbuf_setdata(pMBuf, (uint8_t *)mbuf_data(pMBuf) + cbEthHdr, mbuf_len(pMBuf) - cbEthHdr); mbuf_pkthdr_setrcvif(pMBuf, pIfNet); /* will crash without this. */ errno_t err = ifnet_input(pIfNet, pMBuf, NULL); if (err) rc = RTErrConvertFromErrno(err); } else rc = VERR_NO_MEMORY; } vboxNetFltDarwinReleaseIfNet(pThis, pIfNet); } return rc; }
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; }
/* * Decapsulate. Does the real work and is called from in_gre_input() * (above) or ipv4_infilter(), Returns an mbuf back if packet is not * yet processed, and NULL if it needs no further processing. * proto is the protocol number of the "calling" foo_input() routine. */ mbuf_t in_gre_input(mbuf_t m, int hlen) { struct greip *gip; struct gre_softc *sc; u_int16_t flags; //static u_int32_t af; //u_int8_t proto; //proto = ((struct ip *)mbuf_data(m))->ip_p; if ((sc = gre_lookup(m, IPPROTO_GRE)) == NULL) { /* No matching tunnel or tunnel is down. */ return m; } /* from here on, we increased the sc->sc_refcnt, so do remember to decrease it before return */ if (mbuf_len(m) < sizeof(struct greip)) { mbuf_pullup(&m, sizeof(struct greip)); if (m == NULL) goto done; } gip = mbuf_data(m); //switch (proto) { // case IPPROTO_GRE: hlen += sizeof(struct gre_h); /* process GRE flags as packet can be of variable len */ flags = ntohs(gip->gi_flags); /* Checksum & Offset are present */ if ((flags & GRE_CP) | (flags & GRE_RP)) hlen += 4; /* We don't support routing fields (variable length) */ if (flags & GRE_RP) goto done; if (flags & GRE_KP) hlen += 4; if (flags & GRE_SP) hlen += 4; switch (ntohs(gip->gi_ptype)) { /* ethertypes */ case WCCP_PROTOCOL_TYPE: if (sc->wccp_ver == WCCP_V2) hlen += 4; /* FALLTHROUGH */ case ETHERTYPE_IP: //af = AF_INET; break; case ETHERTYPE_IPV6: //af = AF_INET6; break; //case ETHERTYPE_AT: // af = AF_APPLETALK; // break; default: /* Others not yet supported. */ goto done; } // break; // default: /* Others not yet supported. */ // goto done; //} if (hlen > mbuf_pkthdr_len(m)) { /* not a valid GRE packet */ mbuf_freem(m); m = NULL; goto done; } /* Unlike NetBSD, in FreeBSD(as well as Darwin) m_adj() adjusts mbuf_pkthdr_len(m) as well */ mbuf_adj(m, hlen); mbuf_pkthdr_setrcvif(m, sc->sc_ifp); mbuf_pkthdr_setheader(m, NULL); //mbuf_pkthdr_setheader(m, &af); /* it's ugly... */ struct ifnet_stat_increment_param incs; bzero(&incs, sizeof(incs)); incs.packets_in = 1; incs.bytes_in = mbuf_pkthdr_len(m); ifnet_input(sc->sc_ifp, m, &incs); m = NULL; /* ifnet_input() has freed the mbuf */ done: /* since we got sc->sc_refcnt add by one, we decrease it when done */ gre_sc_release(sc); return m; }
/** * Internal worker that create a darwin mbuf for a (scatter/)gather list. * * @returns Pointer to the mbuf. * @param pThis The instance. * @param pSG The (scatter/)gather list. */ static mbuf_t vboxNetFltDarwinMBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG) { /// @todo future? mbuf_how_t How = preemption enabled ? MBUF_DONTWAIT : MBUF_WAITOK; mbuf_how_t How = MBUF_WAITOK; /* * We need some way of getting back to our instance data when * the mbuf is freed, so use pvUserData for this. * -- this is not relevant anylonger! -- */ Assert(!pSG->pvUserData || pSG->pvUserData == pThis); Assert(!pSG->pvUserData2); pSG->pvUserData = pThis; /* * Allocate a packet and copy over the data. * * Using mbuf_attachcluster() here would've been nice but there are two * issues with it: (1) it's 10.5.x only, and (2) the documentation indicates * that it's not supposed to be used for really external buffers. The 2nd * point might be argued against considering that the only m_clattach user * is mallocs memory for the ext mbuf and not doing what's stated in the docs. * However, it's hard to tell if these m_clattach buffers actually makes it * to the NICs or not, and even if they did, the NIC would need the physical * addresses for the pages they contain and might end up copying the data * to a new mbuf anyway. * * So, in the end it's better to just do it the simple way that will work * 100%, even if it involves some extra work (alloc + copy) we really wished * to avoid. * * Note. We can't make use of the physical addresses on darwin because the * way the mbuf / cluster stuff works (see mbuf_data_to_physical and * mcl_to_paddr). */ mbuf_t pPkt = NULL; errno_t err = mbuf_allocpacket(How, pSG->cbTotal, NULL, &pPkt); if (!err) { /* Skip zero sized memory buffers (paranoia). */ mbuf_t pCur = pPkt; while (pCur && !mbuf_maxlen(pCur)) pCur = mbuf_next(pCur); Assert(pCur); /* Set the required packet header attributes. */ mbuf_pkthdr_setlen(pPkt, pSG->cbTotal); mbuf_pkthdr_setheader(pPkt, mbuf_data(pCur)); /* Special case the single buffer copy. */ if ( mbuf_next(pCur) && mbuf_maxlen(pCur) >= pSG->cbTotal) { mbuf_setlen(pCur, pSG->cbTotal); IntNetSgRead(pSG, mbuf_data(pCur)); } else { /* Multi buffer copying. */ size_t cbLeft = pSG->cbTotal; size_t offSrc = 0; while (cbLeft > 0 && pCur) { size_t cb = mbuf_maxlen(pCur); if (cb > cbLeft) cb = cbLeft; mbuf_setlen(pCur, cb); IntNetSgReadEx(pSG, offSrc, cb, mbuf_data(pCur)); /* advance */ offSrc += cb; cbLeft -= cb; pCur = mbuf_next(pCur); } Assert(cbLeft == 0); } if (!err) { /* * Tag the packet and return successfully. */ PVBOXNETFLTTAG pTagData; err = mbuf_tag_allocate(pPkt, g_idTag, 0 /* type */, sizeof(VBOXNETFLTTAG) /* tag len */, How, (void **)&pTagData); if (!err) { Assert(pSG->aSegs[0].cb >= sizeof(pTagData->EthHdr)); memcpy(&pTagData->EthHdr, pSG->aSegs[0].pv, sizeof(pTagData->EthHdr)); return pPkt; } /* bailout: */ AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err)); } mbuf_freem(pPkt); } else AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err)); pSG->pvUserData = NULL; return NULL; }
/* * input routine for IPPRPOTO_MOBILE * This is a little bit diffrent from the other modes, as the * encapsulating header was not prepended, but instead inserted * between IP header and payload */ mbuf_t in_mobile_input(mbuf_t m, int hlen) { #ifdef DEBUG printf("%s: got packet\n", __FUNCTION__); #endif struct ip *ip; struct mobip_h *mip; struct gre_softc *sc; int msiz; if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) { /* No matching tunnel or tunnel is down. */ return m; } /* from here on, we increased the sc->sc_refcnt, so do remember to decrease it before return */ if (mbuf_len(m) < sizeof(*mip)) { mbuf_pullup(&m, sizeof(*mip)); if (m == NULL) goto done; } ip = mbuf_data(m); mip = mbuf_data(m); if (ntohs(mip->mh.proto) & MOB_H_SBIT) { msiz = MOB_H_SIZ_L; mip->mi.ip_src.s_addr = mip->mh.osrc; } else msiz = MOB_H_SIZ_S; if (mbuf_len(m) < (ip->ip_hl << 2) + msiz) { mbuf_pullup(&m, (ip->ip_hl << 2) + msiz); if (m == NULL) goto done; ip = mbuf_data(m); mip = mbuf_data(m); } mip->mi.ip_dst.s_addr = mip->mh.odst; mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8); if (gre_in_cksum((u_int16_t *)&mip->mh, msiz) != 0) { mbuf_freem(m); m = NULL; goto done; } bcopy((caddr_t)(ip) + (ip->ip_hl << 2) + msiz, (caddr_t)(ip) + (ip->ip_hl << 2), mbuf_len(m) - msiz - (ip->ip_hl << 2)); mbuf_setdata(m, mbuf_data(m), mbuf_len(m) - msiz); mbuf_pkthdr_adjustlen(m, - msiz); /* * On FreeBSD, rip_input() supplies us with ip->ip_len * already converted into host byteorder and also decreases * it by the lengh of IP header, however, ip_input() expects * that this field is in the original format (network byteorder * and full size of IP packet), so that adjust accordingly. */ ip->ip_len = htons(ip->ip_len + sizeof(struct ip) - msiz); ip->ip_sum = 0; ip->ip_sum = in_cksum(m, (ip->ip_hl << 2)); mbuf_pkthdr_setrcvif(m, sc->sc_ifp); mbuf_pkthdr_setheader(m, NULL); struct ifnet_stat_increment_param incs; bzero(&incs, sizeof(incs)); incs.packets_in = 1; incs.bytes_in = mbuf_pkthdr_len(m); ifnet_input(sc->sc_ifp, m, &incs); m = NULL; /* ifnet_input() has freed the mbuf */ done: /* since we got sc->sc_refcnt add by one, we decrease it when done */ gre_sc_release(sc); return m; }