VirtualBox

Changeset 49861 in vbox for trunk/src


Ignore:
Timestamp:
Dec 10, 2013 4:52:28 AM (11 years ago)
Author:
vboxsync
Message:

Use either IP_HDRINCL or OS-specific socket option to deal with IPv4 DF flag.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/NetworkServices/NAT/pxping.c

    r49782 r49861  
    7878struct pxping {
    7979    SOCKET sock4;
     80
     81#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
     82#   define DF_WITH_IP_HDRINCL
     83    int hdrincl;
     84#else
     85    int df;
     86#endif
    8087    int ttl;
    8188    int tos;
    82     int df;
    8389
    8490    SOCKET sock6;
     
    261267    g_pxping.sock4 = sock4;
    262268    if (g_pxping.sock4 != INVALID_SOCKET) {
     269#ifdef DF_WITH_IP_HDRINCL
     270        g_pxping.hdrincl = -1;
     271#else
     272        g_pxping.df = -1;
     273#endif
    263274        g_pxping.ttl = -1;
    264275        g_pxping.tos = 0;
    265         g_pxping.df = -1;
     276
     277#ifdef RT_OS_LINUX
     278        {
     279            const int dont = IP_PMTUDISC_DONT;
     280            status = setsockopt(sock4, IPPROTO_IP, IP_MTU_DISCOVER,
     281                                &dont, sizeof(dont));
     282            if (status != 0) {
     283                perror("IP_MTU_DISCOVER");
     284            }
     285        }
     286#endif /* RT_OS_LINUX */
    266287
    267288        g_pxping.pmhdl4.callback = pxping_pmgr_pump;
     
    415436    struct pxping *pxping = (struct pxping *)arg;
    416437    struct ping_pcb *pcb;
     438#ifdef DF_WITH_IP_HDRINCL
     439    struct ip_hdr iph_orig;
     440#endif
     441    struct icmp_echo_hdr icmph_orig;
    417442    struct ip_hdr *iph;
    418443    struct icmp_echo_hdr *icmph;
    419     int ttl, tos;
     444    int df, ttl, tos;
    420445    u32_t sum;
    421446    u16_t iphlen;
    422     u16_t id, seq;
    423447    int status;
    424448
     449    iphlen = ip_current_header_tot_len();
     450    if (iphlen != IP_HLEN) {    /* we don't do options */
     451        pbuf_free(p);
     452        return;
     453    }
     454
    425455    iph = (/* UNCONST */ struct ip_hdr *)ip_current_header();
    426     iphlen = ip_current_header_tot_len();
    427 
    428456    icmph = (struct icmp_echo_hdr *)p->payload;
    429 
    430     id  = icmph->id;
    431     seq = icmph->seqno;
    432457
    433458    pcb = pxping_pcb_for_request(pxping, 0,
    434459                                 ipX_current_src_addr(),
    435460                                 ipX_current_dest_addr(),
    436                                  id);
     461                                 icmph->id);
    437462    if (pcb == NULL) {
    438463        pbuf_free(p);
     
    442467    pxping_pcb_debug_print(pcb); /* XXX */
    443468    printf(" seq %d len %u ttl %d\n",
    444            ntohs(seq), (unsigned int)p->tot_len,
     469           ntohs(icmph->seqno), (unsigned int)p->tot_len,
    445470           IPH_TTL(iph));
    446471
    447472    ttl = IPH_TTL(iph);
    448473    if (!pcb->is_mapped) {
    449         if (ttl == 1) {
    450             pbuf_header(p, iphlen); /* back to IP header */
    451             icmp_time_exceeded(p, ICMP_TE_TTL);
     474        if (RT_UNLIKELY(ttl == 1)) {
     475            status = pbuf_header(p, iphlen); /* back to IP header */
     476            if (RT_LIKELY(status == 0)) {
     477                icmp_time_exceeded(p, ICMP_TE_TTL);
     478            }
    452479            pbuf_free(p);
    453480            return;
     
    456483    }
    457484
    458     /* rewrite ICMP echo header */
    459     sum = (u16_t)~icmph->chksum;
    460     sum += chksum_update_16(&icmph->id, pcb->host_id);
    461     sum = FOLD_U32T(sum);
    462     icmph->chksum = ~sum;
    463 
    464     if (ttl != pxping->ttl) {
    465         status = setsockopt(pxping->sock4, IPPROTO_IP, IP_TTL,
    466                             (char *)&ttl, sizeof(ttl));
    467         if (status == 0) {
    468             pxping->ttl = ttl;
     485    /*
     486     * OS X doesn't provide a socket option to control fragmentation.
     487     * Solaris doesn't provide IP_DONTFRAG on all releases we support.
     488     * In this case we have to use IP_HDRINCL.  We don't want to use
     489     * it always since it doesn't handle fragmentation (but that's ok
     490     * for DF) and Windows doesn't do automatic source address
     491     * selection with IP_HDRINCL.
     492     */
     493    df = (IPH_OFFSET(iph) & PP_HTONS(IP_DF)) != 0;
     494
     495#ifdef DF_WITH_IP_HDRINCL
     496    if (df != pxping->hdrincl) {
     497        status = setsockopt(pxping->sock4, IPPROTO_IP, IP_HDRINCL,
     498                            &df, sizeof(df));
     499        if (RT_LIKELY(status == 0)) {
     500            pxping->hdrincl = df;
    469501        }
    470502        else {
    471             perror("IP_TTL");
    472         }
    473     }
    474 
    475     tos = IPH_TOS(iph);
    476     if (tos != pxping->tos) {
    477         status = setsockopt(pxping->sock4, IPPROTO_IP, IP_TOS,
    478                             (char *)&tos, sizeof(tos));
    479         if (status == 0) {
    480             pxping->tos = tos;
     503            perror("IP_HDRINCL");
     504        }
     505    }
     506
     507    if (pxping->hdrincl) {
     508        status = pbuf_header(p, iphlen); /* back to IP header */
     509        if (RT_UNLIKELY(status != 0)) {
     510            pbuf_free(p);
     511            return;
     512        }
     513
     514        /* we will overwrite IP header, save original for ICMP errors */
     515        memcpy(&iph_orig, iph, iphlen);
     516
     517        if (g_proxy_options->src4 != NULL) {
     518            memcpy(&iph->src, &g_proxy_options->src4->sin_addr,
     519                   sizeof(g_proxy_options->src4->sin_addr));
    481520        }
    482521        else {
    483             perror("IP_TOS");
    484         }
    485     }
    486 
    487 #if 0
    488 #if /*defined(RT_OS_LINUX) ||*/ defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS)
     522            /* let the kernel select suitable source address */
     523            memset(&iph->src, 0, sizeof(iph->src));
     524        }
     525
     526        IPH_TTL_SET(iph, ttl);  /* already decremented */
     527        IPH_ID_SET(iph, 0);     /* kernel will set one */
     528#ifdef RT_OS_DARWIN
     529        /* wants ip_offset and ip_len fields in host order */
     530        IPH_OFFSET_SET(iph, ntohs(IPH_OFFSET(iph)));
     531        IPH_LEN_SET(iph, ntohs(IPH_LEN(iph)));
     532#endif
     533        IPH_CHKSUM_SET(iph, 0); /* kernel will recalculate */
     534    }
     535    else /* !pxping->hdrincl */
     536#endif   /* DF_WITH_IP_HDRINCL */
    489537    {
    490         const int df_flag = IPH_OFFSET(iph) & PP_HTONS(IP_DF);
    491 
     538#if !defined(DF_WITH_IP_HDRINCL)
     539        /* control DF flag via setsockopt(2) */
     540#define USE_DF_OPTION(_Optname)                         \
     541        const int dfopt = _Optname;                     \
     542        const char * const dfoptname = #_Optname;
    492543#if   defined(RT_OS_LINUX)
    493         const char * const dfoptname = "IP_MTU_DISCOVER";
    494         const int dfopt = IP_MTU_DISCOVER;
    495         int df = df_flag ? IP_PMTUDISC_PROBE : IP_PMTUDISC_DONT;
    496 #elif defined(RT_OS_SOLARIS)
    497         const char * const dfoptname = "IP_DONTFRAG";
    498         const int dfopt = IP_DONTFRAG;
    499         int df = !!df_flag;
     544        USE_DF_OPTION(IP_MTU_DISCOVER);
     545        df = df ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
     546#elif defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
     547        USE_DF_OPTION(IP_DONTFRAG);
    500548#elif defined(RT_OS_WINDOWS)
    501         const char * const dfoptname = "IP_DONTFRAGMENT";
    502         const int dfopt = IP_DONTFRAGMENT;
    503         DWORD df = !!df_flag;
     549        USE_DF_OPTION(IP_DONTFRAGMENT);
    504550#endif
    505551        if (df != pxping->df) {
    506552            status = setsockopt(pxping->sock4, IPPROTO_IP, dfopt,
    507553                                (char *)&df, sizeof(df));
    508             if (status == 0) {
     554            if (RT_LIKELY(status == 0)) {
    509555                pxping->df = df;
    510556            }
     
    513559            }
    514560        }
    515     }
    516 #endif /* don't fragment */
    517 #endif /* 0 */
    518 
    519     proxy_sendto(pxping->sock4, p,
    520                  &pcb->peer.sin, sizeof(pcb->peer.sin));
     561#endif /* !DF_WITH_IP_HDRINCL */
     562
     563        if (ttl != pxping->ttl) {
     564            status = setsockopt(pxping->sock4, IPPROTO_IP, IP_TTL,
     565                                (char *)&ttl, sizeof(ttl));
     566            if (RT_LIKELY(status == 0)) {
     567                pxping->ttl = ttl;
     568            }
     569            else {
     570                perror("IP_TTL");
     571            }
     572        }
     573
     574        tos = IPH_TOS(iph);
     575        if (tos != pxping->tos) {
     576            status = setsockopt(pxping->sock4, IPPROTO_IP, IP_TOS,
     577                                (char *)&tos, sizeof(tos));
     578            if (RT_LIKELY(status == 0)) {
     579                pxping->tos = tos;
     580            }
     581            else {
     582                perror("IP_TOS");
     583            }
     584        }
     585    }
     586
     587    /* rewrite ICMP echo header */
     588    memcpy(&icmph_orig, icmph, sizeof(*icmph));
     589    sum = (u16_t)~icmph->chksum;
     590    sum += chksum_update_16(&icmph->id, pcb->host_id);
     591    sum = FOLD_U32T(sum);
     592    icmph->chksum = ~sum;
     593
     594    status = proxy_sendto(pxping->sock4, p,
     595                          &pcb->peer.sin, sizeof(pcb->peer.sin));
     596    if (status != 0) {
     597        int error = -status;
     598        DPRINTF(("%s: sendto errno %d\n", __func__, error));
     599
     600#ifdef DF_WITH_IP_HDRINCL
     601        if (pxping->hdrincl) {
     602            /* restore original IP header */
     603            memcpy(iph, &iph_orig, iphlen);
     604        }
     605        else
     606#endif
     607        {
     608            status = pbuf_header(p, iphlen); /* back to IP header */
     609            if (RT_UNLIKELY(status != 0)) {
     610                pbuf_free(p);
     611                return;
     612            }
     613        }
     614
     615        /* restore original ICMP header */
     616        memcpy(icmph, &icmph_orig, sizeof(*icmph));
     617
     618        /*
     619         * Some ICMP errors may be generated by the kernel and we read
     620         * them from the socket and forward them normally, hence the
     621         * ifdefs below.
     622         */
     623        switch (error) {
     624
     625#if !( defined(RT_OS_SOLARIS)                                   \
     626    || (defined(RT_OS_LINUX) && !defined(DF_WITH_IP_HDRINCL))   \
     627    )
     628        case EMSGSIZE:
     629            icmp_dest_unreach(p, ICMP_DUR_FRAG);
     630            break;
     631#endif
     632
     633        case ENETDOWN:
     634        case ENETUNREACH:
     635            icmp_dest_unreach(p, ICMP_DUR_NET);
     636            break;
     637
     638        case EHOSTDOWN:
     639        case EHOSTUNREACH:
     640            icmp_dest_unreach(p, ICMP_DUR_HOST);
     641            break;
     642        }
     643    }
    521644
    522645    pbuf_free(p);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette