Exemplo n.º 1
0
/* m->m_data  points at ip packet header
 * m->m_len   length ip packet
 * ip->ip_len length data (IPDU)
 */
void
udp_input(PNATState pData, register struct mbuf *m, int iphlen)
{
    register struct ip *ip;
    register struct udphdr *uh;
    int len;
    struct ip save_ip;
    struct socket *so;
    int ret;
    int ttl, tos;

    LogFlowFunc(("ENTER: m = %p, iphlen = %d\n", m, iphlen));
    ip = mtod(m, struct ip *);
    Log2(("%RTnaipv4 iphlen = %d\n", ip->ip_dst, iphlen));

    udpstat.udps_ipackets++;

    /*
     * Strip IP options, if any; should skip this,
     * make available to user, and use on returned packets,
     * but we don't yet have a way to check the checksum
     * with options still present.
     */
    if (iphlen > sizeof(struct ip))
    {
        ip_stripoptions(m, (struct mbuf *)0);
        iphlen = sizeof(struct ip);
    }

    /*
     * Get IP and UDP header together in first mbuf.
     */
    ip = mtod(m, struct ip *);
    uh = (struct udphdr *)((caddr_t)ip + iphlen);

    /*
     * Make mbuf data length reflect UDP length.
     * If not enough data to reflect UDP length, drop.
     */
    len = RT_N2H_U16((u_int16_t)uh->uh_ulen);
    Assert(ip->ip_len + iphlen == (ssize_t)m_length(m, NULL));

    if (ip->ip_len != len)
    {
        if (len > ip->ip_len)
        {
            udpstat.udps_badlen++;
            Log3(("NAT: IP(id: %hd) has bad size\n", ip->ip_id));
            goto bad_free_mbuf;
        }
        m_adj(m, len - ip->ip_len);
        ip->ip_len = len;
    }

    /*
     * Save a copy of the IP header in case we want restore it
     * for sending an ICMP error message in response.
     */
    save_ip = *ip;
    save_ip.ip_len+= iphlen;         /* tcp_input subtracts this */

    /*
     * Checksum extended UDP header and data.
     */
    if (udpcksum && uh->uh_sum)
    {
        memset(((struct ipovly *)ip)->ih_x1, 0, 9);
        ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
#if 0
        /* keep uh_sum for ICMP reply */
        uh->uh_sum = cksum(m, len + sizeof (struct ip));
        if (uh->uh_sum)
        {

#endif
            if (cksum(m, len + iphlen))
            {
                udpstat.udps_badsum++;
                Log3(("NAT: IP(id: %hd) has bad (udp) cksum\n", ip->ip_id));
                goto bad_free_mbuf;
            }
        }
#if 0
    }
#endif

    /*
     *  handle DHCP/BOOTP
     */
    if (uh->uh_dport == RT_H2N_U16_C(BOOTP_SERVER))
    {
        bootp_input(pData, m);
        goto done_free_mbuf;
    }

    LogFunc(("uh src: %RTnaipv4:%d, dst: %RTnaipv4:%d\n",
             ip->ip_src.s_addr, RT_N2H_U16(uh->uh_sport),
             ip->ip_dst.s_addr, RT_N2H_U16(uh->uh_dport)));

    /*
     * handle DNS host resolver without creating a socket
     */
    if (   pData->fUseHostResolver
        && uh->uh_dport == RT_H2N_U16_C(53)
        && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS))
    {
        struct sockaddr_in dst, src;

        src.sin_addr.s_addr = ip->ip_dst.s_addr;
        src.sin_port = uh->uh_dport;
        dst.sin_addr.s_addr = ip->ip_src.s_addr;
        dst.sin_port = uh->uh_sport;

        m_adj(m, sizeof(struct udpiphdr));

        m = hostresolver(pData, m, ip->ip_src.s_addr, uh->uh_sport);
        if (m == NULL)
            goto done_free_mbuf;

        slirpMbufTagService(pData, m, CTL_DNS);

        udp_output2(pData, NULL, m, &src, &dst, IPTOS_LOWDELAY);
        LogFlowFuncLeave();
        return;
    }

    /*
     *  handle TFTP
     */
    if (   uh->uh_dport == RT_H2N_U16_C(TFTP_SERVER)
        && CTL_CHECK(ip->ip_dst.s_addr, CTL_TFTP))
    {
        if (pData->pvTftpSessions)
            slirpTftpInput(pData, m);
        goto done_free_mbuf;
    }

    /*
     * XXX: DNS proxy currently relies on the fact that each socket
     * only serves one request.
     */
    if (   pData->fUseDnsProxy
        && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS)
        && (uh->uh_dport == RT_H2N_U16_C(53)))
    {
        so = NULL;
        goto new_socket;
    }

    /*
     * Locate pcb for datagram.
     */
    so = udp_last_so;
    if (   so->so_lport != uh->uh_sport
        || so->so_laddr.s_addr != ip->ip_src.s_addr)
    {
        struct socket *tmp;

        for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next)
        {
            if (   tmp->so_lport        == uh->uh_sport
                && tmp->so_laddr.s_addr == ip->ip_src.s_addr)
            {
                so = tmp;
                break;
            }
        }
        if (tmp == &udb)
            so = NULL;
        else
        {
            udpstat.udpps_pcbcachemiss++;
            udp_last_so = so;
        }
    }

  new_socket:
    if (so == NULL)
    {
        /*
         * If there's no socket for this packet,
         * create one
         */
        if ((so = socreate()) == NULL)
        {
            Log2(("NAT: IP(id: %hd) failed to create socket\n", ip->ip_id));
            goto bad_free_mbuf;
        }

        /*
         * Setup fields
         */
        so->so_laddr = ip->ip_src;
        so->so_lport = uh->uh_sport;
        so->so_iptos = ip->ip_tos;

        if (udp_attach(pData, so) <= 0)
        {
            Log2(("NAT: IP(id: %hd) udp_attach errno = %d (%s)\n",
                        ip->ip_id, errno, strerror(errno)));
            sofree(pData, so);
            goto bad_free_mbuf;
        }

        /* udp_last_so = so; */
        /*
         * XXXXX Here, check if it's in udpexec_list,
         * and if it is, do the fork_exec() etc.
         */
    }

    so->so_faddr = ip->ip_dst;   /* XXX */
    so->so_fport = uh->uh_dport; /* XXX */
    Assert(so->so_type == IPPROTO_UDP);

    /*
     * DNS proxy
     */
    if (   pData->fUseDnsProxy
        && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS)
        && (uh->uh_dport == RT_H2N_U16_C(53)))
    {
        dnsproxy_query(pData, so, m, iphlen);
        goto done_free_mbuf;
    }

    iphlen += sizeof(struct udphdr);
    m->m_len -= iphlen;
    m->m_data += iphlen;

    ttl = ip->ip_ttl = save_ip.ip_ttl;
    if (ttl != so->so_sottl) {
        ret = setsockopt(so->s, IPPROTO_IP, IP_TTL,
                         (char *)&ttl, sizeof(ttl));
        if (RT_LIKELY(ret == 0))
            so->so_sottl = ttl;
    }

    tos = save_ip.ip_tos;
    if (tos != so->so_sotos) {
        ret = setsockopt(so->s, IPPROTO_IP, IP_TOS,
                         (char *)&tos, sizeof(tos));
        if (RT_LIKELY(ret == 0))
            so->so_sotos = tos;
    }

    {
        /*
         * Different OSes have different socket options for DF.  We
         * can't use IP_HDRINCL here as it's only valid for SOCK_RAW.
         */
#     define USE_DF_OPTION(_Optname) \
        const int dfopt = _Optname
#if   defined(IP_MTU_DISCOVER)
        USE_DF_OPTION(IP_MTU_DISCOVER);
#elif defined(IP_DONTFRAG)      /* Solaris 11+, FreeBSD */
        USE_DF_OPTION(IP_DONTFRAG);
#elif defined(IP_DONTFRAGMENT)  /* Windows */
        USE_DF_OPTION(IP_DONTFRAGMENT);
#else
        USE_DF_OPTION(0);
#endif
        if (dfopt) {
            int df = (save_ip.ip_off & IP_DF) != 0;
#if defined(IP_MTU_DISCOVER)
            df = df ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
#endif
            if (df != so->so_sodf) {
                ret = setsockopt(so->s, IPPROTO_IP, dfopt,
                                 (char *)&df, sizeof(df));
                if (RT_LIKELY(ret == 0))
                    so->so_sodf = df;
            }
        }
    }

    if (   sosendto(pData, so, m) == -1
        && (   !soIgnorableErrorCode(errno)
            && errno != ENOTCONN))
    {
        m->m_len += iphlen;
        m->m_data -= iphlen;
        *ip = save_ip;
        Log2(("NAT: UDP tx errno = %d (%s) on sent to %RTnaipv4\n",
              errno, strerror(errno), ip->ip_dst));
        icmp_error(pData, m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
        so->so_m = NULL;
        LogFlowFuncLeave();
        return;
    }

    if (so->so_m)
        m_freem(pData, so->so_m);   /* used for ICMP if error on sorecvfrom */

    /* restore the orig mbuf packet */
    m->m_len += iphlen;
    m->m_data -= iphlen;
    *ip = save_ip;
    so->so_m = m;         /* ICMP backup */
    LogFlowFuncLeave();
    return;

bad_free_mbuf:
    Log2(("NAT: UDP(id: %hd) datagram to %RTnaipv4 with size(%d) claimed as bad\n",
        ip->ip_id, &ip->ip_dst, ip->ip_len));

done_free_mbuf:
    /* some services like bootp(built-in), dns(buildt-in) and dhcp don't need sockets
     * and create new m'buffers to send them to guest, so we'll free their incomming
     * buffers here.
     */
    if (m != NULL)
        m_freem(pData, m);
    LogFlowFuncLeave();
    return;
}
Exemplo n.º 2
0
/* m->m_data  points at ip packet header
 * m->m_len   length ip packet
 * ip->ip_len length data (IPDU)
 */
void
udp_input(PNATState pData, register struct mbuf *m, int iphlen)
{
    register struct ip *ip;
    register struct udphdr *uh;
    int len;
    struct ip save_ip;
    struct socket *so;
    int ret;
    int ttl;

    LogFlowFunc(("ENTER: m = %p, iphlen = %d\n", m, iphlen));
    ip = mtod(m, struct ip *);
    Log2(("%RTnaipv4 iphlen = %d\n", ip->ip_dst, iphlen));

    udpstat.udps_ipackets++;

    /*
     * Strip IP options, if any; should skip this,
     * make available to user, and use on returned packets,
     * but we don't yet have a way to check the checksum
     * with options still present.
     */
    if (iphlen > sizeof(struct ip))
    {
        ip_stripoptions(m, (struct mbuf *)0);
        iphlen = sizeof(struct ip);
    }

    /*
     * Get IP and UDP header together in first mbuf.
     */
    ip = mtod(m, struct ip *);
    uh = (struct udphdr *)((caddr_t)ip + iphlen);

    /*
     * Make mbuf data length reflect UDP length.
     * If not enough data to reflect UDP length, drop.
     */
    len = RT_N2H_U16((u_int16_t)uh->uh_ulen);
    Assert((ip->ip_len == len));
    Assert((ip->ip_len + iphlen == m_length(m, NULL)));

    if (ip->ip_len != len)
    {
        if (len > ip->ip_len)
        {
            udpstat.udps_badlen++;
            Log3(("NAT: IP(id: %hd) has bad size\n", ip->ip_id));
        }
        m_adj(m, len - ip->ip_len);
        ip->ip_len = len;
    }

    /*
     * Save a copy of the IP header in case we want restore it
     * for sending an ICMP error message in response.
     */
    save_ip = *ip;
    save_ip.ip_len+= iphlen;         /* tcp_input subtracts this */

    /*
     * Checksum extended UDP header and data.
     */
    if (udpcksum && uh->uh_sum)
    {
        memset(((struct ipovly *)ip)->ih_x1, 0, 9);
        ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
#if 0
        /* keep uh_sum for ICMP reply */
        uh->uh_sum = cksum(m, len + sizeof (struct ip));
        if (uh->uh_sum)
        {

#endif
            if (cksum(m, len + iphlen))
            {
                udpstat.udps_badsum++;
                Log3(("NAT: IP(id: %hd) has bad (udp) cksum\n", ip->ip_id));
                goto bad_free_mbuf;
            }
        }
#if 0
    }
#endif

    /*
     *  handle DHCP/BOOTP
     */
    if (uh->uh_dport == RT_H2N_U16_C(BOOTP_SERVER))
    {
        bootp_input(pData, m);
        goto done_free_mbuf;
    }

    LogFunc(("uh src: %RTnaipv4:%d, dst: %RTnaipv4:%d\n", ip->ip_src, RT_H2N_U16_C(uh->uh_sport), ip->ip_dst, RT_H2N_U16_C(uh->uh_dport)));
    if (   pData->fUseHostResolver
        && uh->uh_dport == RT_H2N_U16_C(53)
        && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS))
    {
        struct sockaddr_in dst, src;
        src.sin_addr.s_addr = ip->ip_dst.s_addr;
        src.sin_port = uh->uh_dport;
        dst.sin_addr.s_addr = ip->ip_src.s_addr;
        dst.sin_port = uh->uh_sport;

        slirpMbufTagService(pData, m, CTL_DNS);
        /* udp_output2() expects a pointer to the body of UDP packet. */
        m->m_data += sizeof(struct udpiphdr);
        m->m_len -= sizeof(struct udpiphdr);
        udp_output2(pData, NULL, m, &src, &dst, IPTOS_LOWDELAY);
        LogFlowFuncLeave();
        return;
    }
    /*
     *  handle TFTP
     */
    if (   uh->uh_dport == RT_H2N_U16_C(TFTP_SERVER)
        && CTL_CHECK(ip->ip_dst.s_addr, CTL_TFTP))
    {
        if (pData->pvTftpSessions)
            slirpTftpInput(pData, m);
        goto done_free_mbuf;
    }

    /*
     * Locate pcb for datagram.
     */
    so = udp_last_so;
    if (   so->so_lport != uh->uh_sport
        || so->so_laddr.s_addr != ip->ip_src.s_addr)
    {
        struct socket *tmp;

        for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next)
        {
            if (   tmp->so_lport        == uh->uh_sport
                && tmp->so_laddr.s_addr == ip->ip_src.s_addr)
            {
                so = tmp;
                break;
            }
        }
        if (tmp == &udb)
            so = NULL;
        else
        {
            udpstat.udpps_pcbcachemiss++;
            udp_last_so = so;
        }
    }

    if (so == NULL)
    {
        /*
         * If there's no socket for this packet,
         * create one
         */
        if ((so = socreate()) == NULL)
        {
            Log2(("NAT: IP(id: %hd) failed to create socket\n", ip->ip_id));
            goto bad_free_mbuf;
        }
        if (udp_attach(pData, so) <= 0)
        {
            Log2(("NAT: IP(id: %hd) udp_attach errno = %d (%s)\n",
                        ip->ip_id, errno, strerror(errno)));
            sofree(pData, so);
            goto bad_free_mbuf;
        }

        /*
         * Setup fields
         */
        /* udp_last_so = so; */
        so->so_laddr = ip->ip_src;
        so->so_lport = uh->uh_sport;

        so->so_iptos = ip->ip_tos;

        /*
         * XXXXX Here, check if it's in udpexec_list,
         * and if it is, do the fork_exec() etc.
         */
    }

    so->so_faddr = ip->ip_dst;   /* XXX */
    so->so_fport = uh->uh_dport; /* XXX */
    Assert(so->so_type == IPPROTO_UDP);

    /*
     * DNS proxy
     */
    if (   pData->fUseDnsProxy
        && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS)
        && (uh->uh_dport == RT_H2N_U16_C(53)))
    {
        dnsproxy_query(pData, so, m, iphlen);
        goto done_free_mbuf;
    }

    iphlen += sizeof(struct udphdr);
    m->m_len -= iphlen;
    m->m_data += iphlen;

    ttl = ip->ip_ttl = save_ip.ip_ttl;
    ret = setsockopt(so->s, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof(ttl));
    if (ret < 0)
        LogRel(("NAT: Error (%s) occurred while setting TTL(%d) attribute "
                "of IP packet to socket %R[natsock]\n", strerror(errno), ip->ip_ttl, so));

    if (   sosendto(pData, so, m) == -1
        && (   !soIgnorableErrorCode(errno)
            && errno != ENOTCONN))
    {
        m->m_len += iphlen;
        m->m_data -= iphlen;
        *ip = save_ip;
        Log2(("NAT: UDP tx errno = %d (%s) on sent to %RTnaipv4\n",
              errno, strerror(errno), ip->ip_dst));
#if 0
        /* ICMP_SOURCEQUENCH haven't got any effect, the idea here
         * inform guest about the exosting NAT resources with assumption that
         * that guest reduce traffic. But it doesn't work
         */
        if(    errno == EAGAIN
            || errno == EWOULDBLOCK
            || errno == EINPROGRESS
            || errno == ENOTCONN)
            icmp_error(pData, m, ICMP_SOURCEQUENCH, 0, 1, strerror(errno));
        else
#endif
            icmp_error(pData, m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
        so->so_m = NULL;
        LogFlowFuncLeave();
        return;
    }

    if (so->so_m)
        m_freem(pData, so->so_m);   /* used for ICMP if error on sorecvfrom */

    /* restore the orig mbuf packet */
    m->m_len += iphlen;
    m->m_data -= iphlen;
    *ip = save_ip;
    so->so_m = m;         /* ICMP backup */
    LogFlowFuncLeave();
    return;

bad_free_mbuf:
    Log2(("NAT: UDP(id: %hd) datagram to %RTnaipv4 with size(%d) claimed as bad\n",
        ip->ip_id, &ip->ip_dst, ip->ip_len));

done_free_mbuf:
    /* some services like bootp(built-in), dns(buildt-in) and dhcp don't need sockets
     * and create new m'buffers to send them to guest, so we'll free their incomming
     * buffers here.
     */
    m_freem(pData, m);
    LogFlowFuncLeave();
    return;
}