Ejemplo n.º 1
0
/**
 * Initiates a new List Exported Devices request.
 *
 * @returns VBox status code.
 */
int USBProxyBackendUsbIp::startListExportedDevicesReq()
{
    int rc = VINF_SUCCESS;

    /*
     * Reset the current state and reconnect in case we were called in the middle
     * of another transfer (which should not happen).
     */
    Assert(m->enmRecvState == kUsbIpRecvState_None);
    if (m->enmRecvState != kUsbIpRecvState_None)
        rc = reconnect();

    if (RT_SUCCESS(rc))
    {
        /* Send of the request. */
        UsbIpReqDevList ReqDevList;
        ReqDevList.u16Version = RT_H2N_U16(USBIP_VERSION);
        ReqDevList.u16Cmd     = RT_H2N_U16(USBIP_INDICATOR_REQ | USBIP_REQ_RET_DEVLIST);
        ReqDevList.u32Status  = RT_H2N_U32(0);
        rc = RTTcpWrite(m->hSocket, &ReqDevList, sizeof(ReqDevList));
        if (RT_SUCCESS(rc))
            advanceState(kUsbIpRecvState_Hdr);
    }

    return rc;
}
Ejemplo n.º 2
0
Archivo: tftp.c Proyecto: ryenus/vbox
DECLINLINE(int) tftpSendError(PNATState pData,
                              PTFTPSESSION pTftpSession,
                              uint16_t errorcode,
                              const char *msg,
                              PCTFTPIPHDR pcTftpIpHeaderRecv)
{
    struct mbuf *m = NULL;
    PTFTPIPHDR pTftpIpHeader = NULL;

    LogFlowFunc(("ENTER: errorcode: %RX16, msg: %s\n", errorcode, msg));
    m = slirpTftpMbufAlloc(pData);
    if (!m)
    {
        LogFlowFunc(("LEAVE: Can't allocate mbuf\n"));
        return -1;
    }

    m->m_data += if_maxlinkhdr;
    m->m_len = sizeof(TFTPIPHDR)
             + strlen(msg) + 1; /* ending zero */
    m->m_pkthdr.header = mtod(m, void *);
    pTftpIpHeader = mtod(m, PTFTPIPHDR);

    pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_ERROR);
    pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(errorcode);

    m_copyback(pData, m, sizeof(TFTPIPHDR), strlen(msg) + 1 /* copy ending zerro*/, (c_caddr_t)msg);

    tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);

    tftpSessionTerminate(pTftpSession);

    LogFlowFuncLeave();
    return 0;
}
Ejemplo n.º 3
0
/**
 * @note This function will free m!
 */
int udp_output(PNATState pData, struct socket *so, struct mbuf *m,
               struct sockaddr_in *addr)
{
    struct sockaddr_in saddr, daddr;

    Assert(so->so_type == IPPROTO_UDP);
    LogFlowFunc(("ENTER: so = %R[natsock], m = %p, saddr = %RTnaipv4\n", so, m, addr->sin_addr.s_addr));

    if (so->so_laddr.s_addr == INADDR_ANY)
    {
        if (pData->guest_addr_guess.s_addr != INADDR_ANY)
        {
            LogRel2(("NAT: port-forward: using %RTnaipv4 for %R[natsock]\n",
                     pData->guest_addr_guess.s_addr, so));
            so->so_laddr = pData->guest_addr_guess;
        }
        else
        {
            LogRel2(("NAT: port-forward: guest address unknown for %R[natsock]\n", so));
            m_freem(pData, m);
            return 0;
        }
    }

    saddr = *addr;
    if ((so->so_faddr.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
    {
        saddr.sin_addr.s_addr = so->so_faddr.s_addr;
        if (slirpIsWideCasting(pData, so->so_faddr.s_addr))
        {
            /**
             * We haven't got real firewall but have got its submodule libalias.
             */
            m->m_flags |= M_SKIP_FIREWALL;
            /**
             * udp/137 port is Name Service in NetBIOS protocol. for some reasons Windows guest rejects
             * accept data from non-aliased server.
             */
            if (   (so->so_fport == so->so_lport)
                && (so->so_fport == RT_H2N_U16(137)))
                saddr.sin_addr.s_addr = alias_addr.s_addr;
            else
                saddr.sin_addr.s_addr = addr->sin_addr.s_addr;
            so->so_faddr.s_addr = addr->sin_addr.s_addr;
        }
    }

    /* Any UDP packet to the loopback address must be translated to be from
     * the forwarding address, i.e. 10.0.2.2. */
    if (   (saddr.sin_addr.s_addr & RT_H2N_U32_C(IN_CLASSA_NET))
        == RT_H2N_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
        saddr.sin_addr.s_addr = alias_addr.s_addr;

    daddr.sin_addr = so->so_laddr;
    daddr.sin_port = so->so_lport;

    return udp_output2(pData, so, m, &saddr, &daddr, so->so_iptos);
}
Ejemplo n.º 4
0
/**
 * Output a UDP packet.
 *
 * @note This function will finally free m!
 */
int udp_output2(PNATState pData, struct socket *so, struct mbuf *m,
                struct sockaddr_in *saddr, struct sockaddr_in *daddr,
                int iptos)
{
    register struct udpiphdr *ui;
    int error;
    int mlen = 0;

    LogFlowFunc(("ENTER: so = %R[natsock], m = %p, saddr = %RTnaipv4, daddr = %RTnaipv4\n",
                 so, m, saddr->sin_addr.s_addr, daddr->sin_addr.s_addr));

    /* in case of built-in service so might be NULL */
    if (so) Assert(so->so_type == IPPROTO_UDP);

    /*
     * Adjust for header
     */
    m->m_data -= sizeof(struct udpiphdr);
    m->m_len += sizeof(struct udpiphdr);
    mlen = m_length(m, NULL);

    /*
     * Fill in mbuf with extended UDP header
     * and addresses and length put into network format.
     */
    ui = mtod(m, struct udpiphdr *);
    memset(ui->ui_x1, 0, 9);
    ui->ui_pr = IPPROTO_UDP;
    ui->ui_len = RT_H2N_U16((uint16_t)(mlen - sizeof(struct ip)));
    /* XXXXX Check for from-one-location sockets, or from-any-location sockets */
    ui->ui_src = saddr->sin_addr;
    ui->ui_dst = daddr->sin_addr;
    ui->ui_sport = saddr->sin_port;
    ui->ui_dport = daddr->sin_port;
    ui->ui_ulen = ui->ui_len;

    /*
     * Stuff checksum and output datagram.
     */
    ui->ui_sum = 0;
    if (udpcksum)
    {
        if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ mlen)) == 0)
            ui->ui_sum = 0xffff;
    }
    ((struct ip *)ui)->ip_len = mlen;
    ((struct ip *)ui)->ip_ttl = ip_defttl;
    ((struct ip *)ui)->ip_tos = iptos;

    udpstat.udps_opackets++;

    error = ip_output(pData, so, m);

    return error;
}
Ejemplo n.º 5
0
/**
 * @note This function will free m!
 */
int udp_output(PNATState pData, struct socket *so, struct mbuf *m,
               struct sockaddr_in *addr)
{
    struct sockaddr_in saddr, daddr;
#ifdef VBOX_WITH_NAT_UDP_SOCKET_CLONE
    struct socket *pSocketClone = NULL;
#endif
    Assert(so->so_type == IPPROTO_UDP);
    LogFlowFunc(("ENTER: so = %R[natsock], m = %p, saddr = %RTnaipv4\n",
                 so, (long)m, addr->sin_addr.s_addr));

    saddr = *addr;
    if ((so->so_faddr.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
    {
        saddr.sin_addr.s_addr = so->so_faddr.s_addr;
        if (slirpIsWideCasting(pData, so->so_faddr.s_addr))
        {
            /**
             * We haven't got real firewall but have got its submodule libalias.
             */
            m->m_flags |= M_SKIP_FIREWALL;
            /**
             * udp/137 port is Name Service in NetBIOS protocol. for some reasons Windows guest rejects
             * accept data from non-aliased server.
             */
            if (   (so->so_fport == so->so_lport)
                && (so->so_fport == RT_H2N_U16(137)))
                saddr.sin_addr.s_addr = alias_addr.s_addr;
            else
                saddr.sin_addr.s_addr = addr->sin_addr.s_addr;
            /* we shouldn't override initial socket */
#ifdef VBOX_WITH_NAT_UDP_SOCKET_CLONE
            if (so->so_cCloneCounter)
                pSocketClone = soLookUpClonedUDPSocket(pData, so, addr->sin_addr.s_addr);
            if (!pSocketClone)
                pSocketClone = soCloneUDPSocketWithForegnAddr(pData, false, so, addr->sin_addr.s_addr);
            Assert((pSocketClone));
            so = pSocketClone;
#else
            so->so_faddr.s_addr = addr->sin_addr.s_addr;
#endif
        }
    }

    /* Any UDP packet to the loopback address must be translated to be from
     * the forwarding address, i.e. 10.0.2.2. */
    if (   (saddr.sin_addr.s_addr & RT_H2N_U32_C(IN_CLASSA_NET))
        == RT_H2N_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
        saddr.sin_addr.s_addr = alias_addr.s_addr;

    daddr.sin_addr = so->so_laddr;
    daddr.sin_port = so->so_lport;

    return udp_output2(pData, so, m, &saddr, &daddr, so->so_iptos);
}
Ejemplo n.º 6
0
static int tftp_send_data(PNATState pData,
                          struct tftp_session *spt,
                          u_int16_t block_nr,
                          struct tftp_t *recv_tp)
{
    struct sockaddr_in saddr, daddr;
    struct mbuf *m;
    struct tftp_t *tp;
    int nobytes;

    if (block_nr < 1)
        return -1;

    m = slirpTftpMbufAlloc(pData);
    if (!m)
        return -1;

    m->m_data += if_maxlinkhdr;
    m->m_pkthdr.header = mtod(m, void *);
    tp = mtod(m, void *);
    m->m_data += sizeof(struct udpiphdr);

    tp->tp_op = RT_H2N_U16_C(TFTP_DATA);
    tp->x.tp_data.tp_block_nr = RT_H2N_U16(block_nr);

    saddr.sin_addr = recv_tp->ip.ip_dst;
    saddr.sin_port = recv_tp->udp.uh_dport;

    daddr.sin_addr = spt->client_ip;
    daddr.sin_port = spt->client_port;

    nobytes = tftp_read_data(pData, spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
    if (nobytes < 0)
    {
        m_freem(pData, m);
        /* send "file not found" error back */
        tftp_send_error(pData, spt, 1, "File not found", tp);
        return -1;
    }

    m->m_len = sizeof(struct tftp_t)
             - (512 - nobytes)
             - sizeof(struct ip)
             - sizeof(struct udphdr);

    udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);

    if (nobytes == 512)
        tftp_session_update(pData, spt);
    else
        tftp_session_terminate(spt);

    return 0;
}
Ejemplo n.º 7
0
Archivo: tftp.c Proyecto: ryenus/vbox
static int tftpSendData(PNATState pData,
                          PTFTPSESSION pTftpSession,
                          uint16_t u16Block,
                          PCTFTPIPHDR pcTftpIpHeaderRecv)
{
    struct mbuf *m;
    PTFTPIPHDR pTftpIpHeader;
    int cbRead = 0;
    int rc = VINF_SUCCESS;

    if (u16Block == pTftpSession->cTftpAck)
        pTftpSession->cTftpAck++;
    else
    {
        tftpSendError(pData, pTftpSession, 6, "ACK is wrong", pcTftpIpHeaderRecv);
        tftpSessionTerminate(pTftpSession);
        return -1;
    }

    m = slirpTftpMbufAlloc(pData);
    if (!m)
        return -1;

    m->m_data += if_maxlinkhdr;
    m->m_pkthdr.header = mtod(m, void *);
    pTftpIpHeader = mtod(m, PTFTPIPHDR);
    m->m_len = sizeof(TFTPIPHDR);

    pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_DATA);
    pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(pTftpSession->cTftpAck);

    rc = tftpReadDataBlock(pData, pTftpSession, (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t), &cbRead);

    if (RT_SUCCESS(rc))
    {
        pTftpSession->cbTransfered += cbRead;
        m->m_len += cbRead;
        tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
        if (cbRead > 0)
            tftpSessionUpdate(pData, pTftpSession);
        else
            tftpSessionTerminate(pTftpSession);
    }
    else
    {
        m_freem(pData, m);
        tftpSendError(pData, pTftpSession, 1, "File not found", pcTftpIpHeaderRecv);
        /* send "file not found" error back */
        return -1;
    }

    return 0;
}
Ejemplo n.º 8
0
static int tftp_send_error(PNATState pData,
                           struct tftp_session *spt,
                           u_int16_t errorcode, const char *msg,
                           struct tftp_t *recv_tp)
{
    struct sockaddr_in saddr, daddr;
    struct mbuf *m;
    struct tftp_t *tp;
    int nobytes;

    m = slirpTftpMbufAlloc(pData);
    if (!m)
        return -1;

    m->m_data += if_maxlinkhdr;
    m->m_pkthdr.header = mtod(m, void *);
    tp = (void *)m->m_data;
    m->m_data += sizeof(struct udpiphdr);

    tp->tp_op = RT_H2N_U16_C(TFTP_ERROR);
    tp->x.tp_error.tp_error_code = RT_H2N_U16(errorcode);
    strcpy((char *)tp->x.tp_error.tp_msg, msg);

    saddr.sin_addr = recv_tp->ip.ip_dst;
    saddr.sin_port = recv_tp->udp.uh_dport;

    daddr.sin_addr = spt->client_ip;
    daddr.sin_port = spt->client_port;

    nobytes = 2;

    m->m_len = sizeof(struct tftp_t)
             - 514
             + 3
             + strlen(msg)
             - sizeof(struct ip)
             - sizeof(struct udphdr);

    udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);

    tftp_session_terminate(spt);

    return 0;
}
Ejemplo n.º 9
0
/* struct tcpiphdr * */
void
tcp_template(struct tcpcb *tp)
{
    struct socket *so = tp->t_socket;
    register struct tcpiphdr *n = &tp->t_template;

    memset(n->ti_x1, 0, 9);
    n->ti_pr = IPPROTO_TCP;
    n->ti_len = RT_H2N_U16(sizeof (struct tcpiphdr) - sizeof (struct ip));
    n->ti_src = so->so_faddr;
    n->ti_dst = so->so_laddr;
    n->ti_sport = so->so_fport;
    n->ti_dport = so->so_lport;

    n->ti_seq = 0;
    n->ti_ack = 0;
    n->ti_x2 = 0;
    n->ti_off = 5;
    n->ti_flags = 0;
    n->ti_win = 0;
    n->ti_sum = 0;
    n->ti_urp = 0;
}
Ejemplo n.º 10
0
/* This function will free m0! */
int
ip_output0(PNATState pData, struct socket *so, struct mbuf *m0, int urg)
{
    register struct ip *ip;
    register struct mbuf *m = m0;
    register int hlen = sizeof(struct ip);
    int len, off, error = 0;
    struct ethhdr *eh = NULL;
    uint8_t eth_dst[ETH_ALEN];
    int rc = 1;

    STAM_PROFILE_START(&pData->StatIP_output, a);

#ifdef LOG_ENABLED
    LogFlowFunc(("ip_output: so = %R[natsock], m0 = %lx\n", so, (long)m0));
#else
    NOREF(so);
#endif

    M_ASSERTPKTHDR(m);
    Assert(m->m_pkthdr.header);

#if 0 /* We do no options */
    if (opt)
    {
        m = ip_insertoptions(m, opt, &len);
        hlen = len;
    }
#endif
    ip = mtod(m, struct ip *);
    LogFunc(("ip(src:%RTnaipv4, dst:%RTnaipv4)\n", ip->ip_src, ip->ip_dst));
    /*
     * Fill in IP header.
     */
    ip->ip_v = IPVERSION;
    ip->ip_off &= IP_DF;
    ip->ip_id = RT_H2N_U16(ip_currid++);
    ip->ip_hl = hlen >> 2;
    ipstat.ips_localout++;

    /* Current TCP/IP stack hasn't routing information at
     * all so we need to calculate destination ethernet address
     */
    rc = rt_lookup_in_cache(pData, ip->ip_dst.s_addr, eth_dst);
    if (RT_FAILURE(rc))
        goto exit_drop_package;

    eh = (struct ethhdr *)(m->m_data - ETH_HLEN);
    /*
     * If small enough for interface, can just send directly.
     */
    if ((u_int16_t)ip->ip_len <= if_mtu)
    {
        ip->ip_len = RT_H2N_U16((u_int16_t)ip->ip_len);
        ip->ip_off = RT_H2N_U16((u_int16_t)ip->ip_off);
        ip->ip_sum = 0;
        ip->ip_sum = cksum(m, hlen);

        if (!(m->m_flags & M_SKIP_FIREWALL)){
            struct m_tag *t;
            STAM_PROFILE_START(&pData->StatALIAS_output, b);
            if ((t = m_tag_find(m, PACKET_TAG_ALIAS, NULL)) != 0)
                rc = LibAliasOut((struct libalias *)&t[1], mtod(m, char *),
                                 m_length(m, NULL));
            else
                rc = LibAliasOut(pData->proxy_alias, mtod(m, char *),
                                 m_length(m, NULL));

            if (rc == PKT_ALIAS_IGNORED)
            {
                Log(("NAT: packet was droppped\n"));
                goto exit_drop_package;
            }
            STAM_PROFILE_STOP(&pData->StatALIAS_output, b);
        }
Ejemplo n.º 11
0
Archivo: tftp.c Proyecto: ryenus/vbox
DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader)
{
    int rc = VINF_SUCCESS;
    char *pszTftpRRQRaw;
    size_t idxTftpRRQRaw = 0;
    int cbTftpRRQRaw = 0;
    int fWithArg = 0;
    int idxOptionArg = 0;
    AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER);
    AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
    AssertReturn(RT_N2H_U16(pcTftpIpHeader->u16TftpOpType) == TFTP_RRQ, VERR_INVALID_PARAMETER);
    LogFlowFunc(("pTftpSession:%p, pcTftpIpHeader:%p\n", pTftpSession, pcTftpIpHeader));
    pszTftpRRQRaw = (char *)&pcTftpIpHeader->Core;
    cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_OFFSETOF(TFTPIPHDR, Core);
    while(cbTftpRRQRaw)
    {
        idxTftpRRQRaw = RTStrNLen(pszTftpRRQRaw, 512 - idxTftpRRQRaw) + 1;
        if (RTStrNLen((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX) == 0)
        {
            rc = RTStrCopy((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX, pszTftpRRQRaw);
            if (RT_FAILURE(rc))
            {
                LogFlowFuncLeaveRC(rc);
                AssertRCReturn(rc,rc);
            }
        }
        else if (pTftpSession->enmTftpFmt == TFTPFMT_NONE)
        {
            int idxFmt = 0;
            rc = tftpFindTransferFormatIdxbyName(&idxFmt, pszTftpRRQRaw);
            if (RT_FAILURE(rc))
            {
                LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR);
                return VERR_INTERNAL_ERROR;
            }
            AssertReturn(   g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NONE
                         && g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NOT_FMT, VERR_INTERNAL_ERROR);
            pTftpSession->enmTftpFmt = g_TftpTransferFmtDesc[idxFmt].enmType;
        }
        else if (fWithArg)
        {
            if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName))
            {
                rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionBlkSize);
                if (pTftpSession->OptionBlkSize.u64Value > UINT16_MAX)
                    rc = VERR_INVALID_PARAMETER;
            }

            if (   RT_SUCCESS(rc)
                && !RTStrICmp("tsize", g_TftpDesc[idxOptionArg].pszName))
                rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTSize);

            /* @todo: we don't use timeout, but its value in the range 0-255 */
            if (   RT_SUCCESS(rc)
                && !RTStrICmp("timeout", g_TftpDesc[idxOptionArg].pszName))
                rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTimeout);

            /* @todo: unknown option detection */
            if (RT_FAILURE(rc))
            {
                LogFlowFuncLeaveRC(rc);
                AssertRCReturn(rc,rc);
            }
            fWithArg = 0;
            idxOptionArg = 0;
        }
        else
        {
            rc = tftpFindOptionIdxbyName(&idxOptionArg, pszTftpRRQRaw);
            if (RT_SUCCESS(rc))
                fWithArg = 1;
            else
            {
                LogFlowFuncLeaveRC(rc);
                AssertRCReturn(rc,rc);
            }
        }
        pszTftpRRQRaw += idxTftpRRQRaw;
        cbTftpRRQRaw -= idxTftpRRQRaw;
    }

    LogFlowFuncLeaveRC(rc);
    return rc;
}
Ejemplo n.º 12
0
/*
 * Tcp output routine: figure out what should be sent and send it.
 */
int
tcp_output(PNATState pData, register struct tcpcb *tp)
{
    register struct socket *so = tp->t_socket;
    register long len, win;
    int off, flags, error;
    register struct mbuf *m = NULL;
    register struct tcpiphdr *ti;
    u_char opt[MAX_TCPOPTLEN];
    unsigned optlen, hdrlen;
    int idle, sendalot;
    int size = 0;

    LogFlowFunc(("ENTER: tcp_output: tp = %R[tcpcb793]\n", tp));

    /*
     * Determine length of data that should be transmitted,
     * and flags that will be used.
     * If there is some data or critical controls (SYN, RST)
     * to send, then transmit; otherwise, investigate further.
     */
    idle = (tp->snd_max == tp->snd_una);
    if (idle && tp->t_idle >= tp->t_rxtcur)
        /*
         * We have been idle for "a while" and no acks are
         * expected to clock out any data we send --
         * slow start to get ack "clock" running again.
         */
        tp->snd_cwnd = tp->t_maxseg;

again:
    sendalot = 0;
    off = tp->snd_nxt - tp->snd_una;
    win = min(tp->snd_wnd, tp->snd_cwnd);

    flags = tcp_outflags[tp->t_state];

    Log2((" --- tcp_output flags = 0x%x\n", flags));

    /*
     * If in persist timeout with window of 0, send 1 byte.
     * Otherwise, if window is small but nonzero
     * and timer expired, we will send what we can
     * and go to transmit state.
     */
    if (tp->t_force)
    {
        if (win == 0)
        {
            /*
             * If we still have some data to send, then
             * clear the FIN bit.  Usually this would
             * happen below when it realizes that we
             * aren't sending all the data.  However,
             * if we have exactly 1 byte of unset data,
             * then it won't clear the FIN bit below,
             * and if we are in persist state, we wind
             * up sending the packet without recording
             * that we sent the FIN bit.
             *
             * We can't just blindly clear the FIN bit,
             * because if we don't have any more data
             * to send then the probe will be the FIN
             * itself.
             */
            if (off < SBUF_LEN(&so->so_snd))
                flags &= ~TH_FIN;
            win = 1;
        }
        else
        {
            tp->t_timer[TCPT_PERSIST] = 0;
            tp->t_rxtshift = 0;
        }
    }

    len = min(SBUF_LEN(&so->so_snd), win) - off;
    if (len < 0)
    {
        /*
         * If FIN has been sent but not acked,
         * but we haven't been called to retransmit,
         * len will be -1.  Otherwise, window shrank
         * after we sent into it.  If window shrank to 0,
         * cancel pending retransmit and pull snd_nxt
         * back to (closed) window.  We will enter persist
         * state below.  If the window didn't close completely,
         * just wait for an ACK.
         */
        len = 0;
        if (win == 0)
        {
            tp->t_timer[TCPT_REXMT] = 0;
            tp->snd_nxt = tp->snd_una;
        }
    }
    if (len > tp->t_maxseg)
    {
        len = tp->t_maxseg;
        sendalot = 1;
    }
    if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + SBUF_LEN(&so->so_snd)))
        flags &= ~TH_FIN;

    win = sbspace(&so->so_rcv);

    /*
     * Sender silly window avoidance.  If connection is idle
     * and can send all data, a maximum segment,
     * at least a maximum default-size segment do it,
     * or are forced, do it; otherwise don't bother.
     * If peer's buffer is tiny, then send
     * when window is at least half open.
     * If retransmitting (possibly after persist timer forced us
     * to send into a small window), then must resend.
     */
    if (len)
    {
        if (len == tp->t_maxseg)
            goto send;
        if ((1 || idle || tp->t_flags & TF_NODELAY) &&
                len + off >= SBUF_LEN(&so->so_snd))
            goto send;
        if (tp->t_force)
            goto send;
        if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
            goto send;
        if (SEQ_LT(tp->snd_nxt, tp->snd_max))
            goto send;
    }

    /*
     * Compare available window to amount of window
     * known to peer (as advertised window less
     * next expected input).  If the difference is at least two
     * max size segments, or at least 50% of the maximum possible
     * window, then want to send a window update to peer.
     */
    if (win > 0)
    {
        /*
         * "adv" is the amount we can increase the window,
         * taking into account that we are limited by
         * TCP_MAXWIN << tp->rcv_scale.
         */
        long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale);
        if (SEQ_GT(tp->rcv_adv, tp->rcv_nxt))
            adv -= tp->rcv_adv - tp->rcv_nxt;

        if (adv >= (long) (2 * tp->t_maxseg))
            goto send;
        if (2 * adv >= (long) SBUF_SIZE(&so->so_rcv))
            goto send;
    }

    /*
     * Send if we owe peer an ACK.
     */
    if (tp->t_flags & TF_ACKNOW)
        goto send;
    if (flags & (TH_SYN|TH_RST))
        goto send;
    if (SEQ_GT(tp->snd_up, tp->snd_una))
        goto send;
    /*
     * If our state indicates that FIN should be sent
     * and we have not yet done so, or we're retransmitting the FIN,
     * then we need to send.
     */
    if (   flags & TH_FIN
        && ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
        goto send;

    /*
     * TCP window updates are not reliable, rather a polling protocol
     * using ``persist'' packets is used to insure receipt of window
     * updates.  The three ``states'' for the output side are:
     *      idle                    not doing retransmits or persists
     *      persisting              to move a small or zero window
     *      (re)transmitting        and thereby not persisting
     *
     * tp->t_timer[TCPT_PERSIST]
     *      is set when we are in persist state.
     * tp->t_force
     *      is set when we are called to send a persist packet.
     * tp->t_timer[TCPT_REXMT]
     *      is set when we are retransmitting
     * The output side is idle when both timers are zero.
     *
     * If send window is too small, there is data to transmit, and no
     * retransmit or persist is pending, then go to persist state.
     * If nothing happens soon, send when timer expires:
     * if window is nonzero, transmit what we can,
     * otherwise force out a byte.
     */
    if (   SBUF_LEN(&so->so_snd)
        && tp->t_timer[TCPT_REXMT] == 0
        && tp->t_timer[TCPT_PERSIST] == 0)
    {
        tp->t_rxtshift = 0;
        tcp_setpersist(tp);
    }

    /*
     * No reason to send a segment, just return.
     */
    tcpstat.tcps_didnuttin++;

    LogFlowFuncLeave();
    return (0);

send:
    LogFlowFunc(("send\n"));
    /*
     * Before ESTABLISHED, force sending of initial options
     * unless TCP set not to do any options.
     * NOTE: we assume that the IP/TCP header plus TCP options
     * always fit in a single mbuf, leaving room for a maximum
     * link header, i.e.
     *      max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
     */
    optlen = 0;
    hdrlen = sizeof (struct tcpiphdr);
    if (flags & TH_SYN)
    {
        tp->snd_nxt = tp->iss;
        if ((tp->t_flags & TF_NOOPT) == 0)
        {
            u_int16_t mss;

            opt[0] = TCPOPT_MAXSEG;
            opt[1] = 4;
            mss = RT_H2N_U16((u_int16_t) tcp_mss(pData, tp, 0));
            memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
            optlen = 4;

#if 0
            if (   (tp->t_flags & TF_REQ_SCALE)
                && (   (flags & TH_ACK) == 0
                    || (tp->t_flags & TF_RCVD_SCALE)))
            {
                *((u_int32_t *) (opt + optlen)) = RT_H2N_U32(  TCPOPT_NOP << 24
                                                             | TCPOPT_WINDOW << 16
                                                             | TCPOLEN_WINDOW << 8
                                                             | tp->request_r_scale);
                optlen += 4;
            }
#endif
        }
    }

    /*
     * Send a timestamp and echo-reply if this is a SYN and our side
     * wants to use timestamps (TF_REQ_TSTMP is set) or both our side
     * and our peer have sent timestamps in our SYN's.
     */
#if 0
    if (   (tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP
        && (flags & TH_RST) == 0
        && (   (flags & (TH_SYN|TH_ACK)) == TH_SYN
            || (tp->t_flags & TF_RCVD_TSTMP)))
    {
        u_int32_t *lp = (u_int32_t *)(opt + optlen);

        /* Form timestamp option as shown in appendix A of RFC 1323. */
        *lp++ = RT_H2N_U32_C(TCPOPT_TSTAMP_HDR);
        *lp++ = RT_H2N_U32(tcp_now);
        *lp   = RT_H2N_U32(tp->ts_recent);
        optlen += TCPOLEN_TSTAMP_APPA;
    }
#endif
    hdrlen += optlen;

    /*
     * Adjust data length if insertion of options will
     * bump the packet length beyond the t_maxseg length.
     */
    if (len > tp->t_maxseg - optlen)
    {
        len = tp->t_maxseg - optlen;
        sendalot = 1;
    }

    /*
     * Grab a header mbuf, attaching a copy of data to
     * be transmitted, and initialize the header from
     * the template for sends on this connection.
     */
    if (len)
    {
        if (tp->t_force && len == 1)
            tcpstat.tcps_sndprobe++;
        else if (SEQ_LT(tp->snd_nxt, tp->snd_max))
        {
            tcpstat.tcps_sndrexmitpack++;
            tcpstat.tcps_sndrexmitbyte += len;
        }
        else
        {
            tcpstat.tcps_sndpack++;
            tcpstat.tcps_sndbyte += len;
        }

        size = MCLBYTES;
        if ((len + hdrlen + ETH_HLEN) < MSIZE)
            size = MCLBYTES;
        else if ((len + hdrlen + ETH_HLEN) < MCLBYTES)
            size = MCLBYTES;
        else if((len + hdrlen + ETH_HLEN) < MJUM9BYTES)
            size = MJUM9BYTES;
        else if ((len + hdrlen + ETH_HLEN) < MJUM16BYTES)
            size = MJUM16BYTES;
        else
            AssertMsgFailed(("Unsupported size"));
        m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, size);
        if (m == NULL)
        {
/*          error = ENOBUFS; */
            error = 1;
            goto out;
        }
        m->m_data += if_maxlinkhdr;
        m->m_pkthdr.header = mtod(m, void *);
        m->m_len = hdrlen;

        /*
         * This will always succeed, since we make sure our mbufs
         * are big enough to hold one MSS packet + header + ... etc.
         */
#if 0
        if (len <= MHLEN - hdrlen - max_linkhdr)
        {
#endif
            sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen);
            m->m_len += len;
#if 0
        }
        else
        {
            m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len);
            if (m->m_next == 0)
                len = 0;
        }
#endif
        /*
         * If we're sending everything we've got, set PUSH.
         * (This will keep happy those implementations which only
         * give data to the user when a buffer fills or
         * a PUSH comes in.)
         */
        if (off + len == SBUF_LEN(&so->so_snd))
            flags |= TH_PUSH;
    }
    else
    {
/*
 * This function deals only with the hex-group IPv6 address syntax
 * proper (with possible embedded IPv4).
 */
DECLHIDDEN(int) rtNetStrToIPv6AddrBase(const char *pcszAddr, PRTNETADDRIPV6 pAddrResult,
                                       char **ppszNext)
{
    RTNETADDRIPV6 ipv6;
    RTNETADDRIPV4 ipv4;
    const char *pcszPos;
    char *pszNext;
    int iGroup;
    uint16_t u16;
    int rc;

    memset(&ipv6, 0, sizeof(ipv6));

    pcszPos = pcszAddr;

    if (pcszPos[0] == ':') /* compressed zero run at the beginning? */
    {
        if (pcszPos[1] != ':')
            return VERR_PARSE_ERROR;

        pcszPos += 2;           /* skip over "::" */
        pszNext = (/* UNCONST */ char *)pcszPos;
        iGroup = 1;
    }
    else
    {
        /*
         * Scan forward until we either get complete address or find
         * "::" compressed zero run.
         */
        for (iGroup = 0; iGroup < 8; ++iGroup)
        {
            /* check for embedded IPv4 at the end */
            if (iGroup == 6)
            {
                rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
                if (rc == VINF_SUCCESS)
                {
                    ipv6.au32[3] = ipv4.au32[0];
                    iGroup = 8; /* filled 6 and 7 */
                    break;      /* we are done */
                }
            }

            rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
            if (RT_FAILURE(rc))
                return VERR_PARSE_ERROR;

            ipv6.au16[iGroup] = RT_H2N_U16(u16);

            if (iGroup == 7)
                pcszPos = pszNext;
            else
            {
                /* skip the colon that delimits this group */
                if (*pszNext != ':')
                    return VERR_PARSE_ERROR;
                pcszPos = pszNext + 1;

                /* compressed zero run? */
                if (*pcszPos == ':')
                {
                    ++pcszPos;    /* skip over :: */
                    pszNext += 2; /* skip over :: (in case we are done) */
                    iGroup += 2;  /* current field and the zero in the next */
                    break;
                }
            }
        }
    }

    if (iGroup != 8)
    {
        /*
         * iGroup is the first group that can be filled by the part of
         * the address after "::".
         */
        RTNETADDRIPV6 ipv6Tail;
        const int iMaybeStart = iGroup;
        int j;

        memset(&ipv6Tail, 0, sizeof(ipv6Tail));

        /*
         * We try to accept longest match; we'll shift if necessary.
         * Unlike the first loop, a failure to parse a group doesn't
         * mean invalid address.
         */
        for (; iGroup < 8; ++iGroup)
        {
            /* check for embedded IPv4 at the end */
            if (iGroup <= 6)
            {
                rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
                if (rc == VINF_SUCCESS)
                {
                    ipv6Tail.au16[iGroup]     = ipv4.au16[0];
                    ipv6Tail.au16[iGroup + 1] = ipv4.au16[1];
                    iGroup = iGroup + 2; /* these two are done */
                    break;               /* the rest is trailer */
                }
            }

            rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
            if (RT_FAILURE(rc))
                break;

            ipv6Tail.au16[iGroup] = RT_H2N_U16(u16);

            if (iGroup == 7)
                pcszPos = pszNext;
            else
            {
                if (*pszNext != ':')
                {
                    ++iGroup;   /* this one is done */
                    break;      /* the rest is trailer */
                }

                pcszPos = pszNext + 1;
            }
        }

        for (j = 7, --iGroup; iGroup >= iMaybeStart; --j, --iGroup)
            ipv6.au16[j] = ipv6Tail.au16[iGroup];
    }

    if (pAddrResult != NULL)
        memcpy(pAddrResult, &ipv6, sizeof(ipv6));
    if (ppszNext != NULL)
        *ppszNext = pszNext;
    return VINF_SUCCESS;
}