VirtualBox

Changeset 53350 in vbox


Ignore:
Timestamp:
Nov 19, 2014 1:05:41 PM (10 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
96956
Message:

NAT: Snapshot work in progress on Windows ICMP API ping proxy.
This commit adds new proxy code but doesn't hook it up yet.

Location:
trunk/src/VBox/Devices/Network/slirp
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Network/slirp/ip_icmp.h

    r53298 r53350  
    207207int icmpwin_init (PNATState);
    208208void icmpwin_finit (PNATState);
     209void icmpwin_ping(PNATState, struct mbuf *, int);
    209210#endif
    210211
  • trunk/src/VBox/Devices/Network/slirp/ip_icmpwin.c

    r53310 r53350  
    1919#include "ip_icmp.h"
    2020
     21#include <winternl.h>           /* for PIO_APC_ROUTINE &c */
    2122#include <iphlpapi.h>
    2223#include <icmpapi.h>
    2324
     25/*
     26 * A header of ICMP ECHO.  Intended for storage, unlike struct icmp
     27 * which is intended to be overlayed onto a buffer.
     28 */
     29struct icmp_echo {
     30    uint8_t  icmp_type;
     31    uint8_t  icmp_code;
     32    uint16_t icmp_cksum;
     33    uint16_t icmp_echo_id;
     34    uint16_t icmp_echo_seq;
     35};
     36
     37AssertCompileSize(struct icmp_echo, 8);
     38
     39
     40struct pong {
     41    PNATState pData;
     42
     43    struct ip reqiph;
     44    struct icmp_echo reqicmph;
     45
     46    size_t bufsize;
     47    uint8_t buf[1];
     48};
     49
     50
     51static VOID WINAPI icmpwin_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved);
     52static VOID WINAPI icmpwin_callback_old(void *ctx);
     53
     54static void icmpwin_callback(struct pong *pong);
     55
     56static struct mbuf *icmpwin_get_error(struct pong *pong, int type, int code);
     57static struct mbuf *icmpwin_get_mbuf(PNATState pData, size_t reqsize);
     58
     59
     60/*
     61 * On Windows XP and Windows Server 2003 IcmpSendEcho2() callback
     62 * is FARPROC, but starting from Vista it's PIO_APC_ROUTINE with
     63 * two extra arguments.  Callbacks use WINAPI (stdcall) calling
     64 * convention with callee responsible for popping the arguments,
     65 * so to avoid stack corruption we check windows version at run
     66 * time and provide correct callback.
     67 *
     68 * XXX: this is system-wide, but what about multiple NAT threads?
     69 */
     70static void *pfIcmpCallback;
     71
    2472
    2573int
    2674icmpwin_init(PNATState pData)
    2775{
     76    if (pfIcmpCallback == NULL)
     77    {
     78        OSVERSIONINFO osvi;
     79        int status;
     80
     81        ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
     82        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
     83        status = GetVersionEx(&osvi);
     84        if (status == 0)
     85            return 1;
     86
     87        if (osvi.dwMajorVersion >= 6)
     88            pfIcmpCallback = icmpwin_callback_apc;
     89        else
     90            pfIcmpCallback = icmpwin_callback_old;
     91    }
     92
    2893    pData->icmp_socket.sh = IcmpCreateFile();
    2994    pData->phEvents[VBOX_ICMP_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL);
     
    41106    RTMemFree(pData->pvIcmpBuffer);
    42107}
     108
     109
     110/*
     111 * Outgoing ping from guest.
     112 */
     113void
     114icmpwin_ping(PNATState pData, struct mbuf *m, int hlen)
     115{
     116    struct ip *ip = mtod(m, struct ip *);
     117    int icmplen = ip->ip_len - hlen;
     118    uint8_t ttl;
     119    size_t bufsize;
     120    struct pong *pong;
     121    IPAddr dst;
     122    IP_OPTION_INFORMATION opts;
     123    void *reqdata;
     124    size_t reqsize;
     125    int status;
     126
     127    ttl = ip->ip_ttl;
     128    AssertReturnVoid(ttl > 1); /* should've been dealt with in the caller */
     129    --ttl;
     130
     131    bufsize = sizeof(ICMP_ECHO_REPLY) + icmplen;
     132    pong = RTMemAlloc(RT_OFFSETOF(struct pong, buf) + bufsize);
     133    if (RT_UNLIKELY(pong == NULL))
     134        return;
     135
     136    pong->pData = pData;
     137    pong->bufsize = bufsize;
     138    m_copydata(m, 0, hlen, (caddr_t)&pong->reqiph);
     139    m_copydata(m, hlen, sizeof(struct icmp_echo), (caddr_t)&pong->reqicmph);
     140    AssertReturnVoid(pong->reqicmph.icmp_type == ICMP_ECHO);
     141
     142    reqsize = icmplen - sizeof(struct icmp_echo); /* just the payload */
     143    if (m->m_next == NULL)
     144    {
     145        /* already in single contiguous buffer */
     146        reqdata = mtod(m, char *) + sizeof(struct ip) + sizeof(struct icmp_echo);
     147    }
     148    else
     149    {
     150        /* use reply buffer as temporary storage */
     151        reqdata = pong->buf;
     152        m_copydata(m, sizeof(struct ip) + sizeof(struct icmp_echo),
     153                   reqsize, reqdata);
     154    }
     155
     156    dst = ip->ip_dst.s_addr;
     157
     158    opts.Ttl = ttl;
     159    opts.Tos = ip->ip_tos; /* affected by DisableUserTOSSetting key */
     160    opts.Flags = (ip->ip_off & IP_DF) != 0 ? IP_FLAG_DF : 0;
     161    opts.OptionsSize = 0;
     162    opts.OptionsData = 0;
     163
     164
     165    status = IcmpSendEcho2(pData->icmp_socket.sh, NULL,
     166                           pfIcmpCallback, pong,
     167                           dst, reqdata, (WORD)reqsize, &opts,
     168                           pong->buf, (DWORD)pong->bufsize,
     169                           5 * 1000 /* ms */);
     170
     171    if (RT_UNLIKELY(status != 0))
     172    {
     173        Log2(("NAT: IcmpSendEcho2: unexpected status %d\n", status));
     174    }
     175    else if ((status = GetLastError()) != ERROR_IO_PENDING)
     176    {
     177        int code;
     178
     179        Log2(("NAT: IcmpSendEcho2: error %d\n", status));
     180        switch (status) {
     181        case ERROR_NETWORK_UNREACHABLE:
     182            code = ICMP_UNREACH_NET;
     183            break;
     184        case ERROR_HOST_UNREACHABLE:
     185            code = ICMP_UNREACH_HOST;
     186            break;
     187        default:
     188            code = -1;
     189            break;
     190        }
     191
     192        if (code != -1)         /* send icmp error */
     193        {
     194            struct mbuf *em = icmpwin_get_error(pong, ICMP_UNREACH, code);
     195            if (em != NULL)
     196            {
     197                struct ip *eip = mtod(em, struct ip *);
     198                eip->ip_src = alias_addr;
     199                ip_output(pData, NULL, em);
     200            }
     201        }
     202    }
     203    else /* success */
     204    {
     205        Log2(("NAT: pong %p for ping %RTnaipv4 id 0x%04x seq %d len %d\n",
     206              pong, dst,
     207              RT_N2H_U16(pong->reqicmph.icmp_echo_id),
     208              RT_N2H_U16(pong->reqicmph.icmp_echo_seq),
     209              reqsize));
     210        pong = NULL;            /* callback owns it now */
     211    }
     212
     213    if (pong != NULL)
     214        RTMemFree(pong);
     215}
     216
     217
     218static VOID WINAPI
     219icmpwin_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved)
     220{
     221    struct pong *pong = (struct pong *)ctx;
     222
     223    if (pong != NULL)
     224    {
     225        icmpwin_callback(pong);
     226        RTMemFree(pong);
     227    }
     228}
     229
     230
     231static VOID WINAPI
     232icmpwin_callback_old(void *ctx)
     233{
     234    struct pong *pong = (struct pong *)ctx;
     235
     236    if (pong != NULL)
     237    {
     238        icmpwin_callback(pong);
     239        RTMemFree(pong);
     240    }
     241}
     242
     243
     244/*
     245 * Actual callback code for IcmpSendEcho2().  OS version specific
     246 * trampoline will free "pong" argument for us.
     247 */
     248static void
     249icmpwin_callback(struct pong *pong)
     250{
     251    PNATState pData;
     252    DWORD nreplies;
     253    ICMP_ECHO_REPLY *reply;
     254    struct mbuf *m;
     255    struct ip *ip;
     256    struct icmp_echo *icmp;
     257    size_t reqsize;
     258
     259    pData = pong->pData; /* to make slirp_state.h macro hackery work */
     260
     261    nreplies = IcmpParseReplies(pong->buf, (DWORD)pong->bufsize);
     262    if (nreplies == 0)
     263    {
     264        DWORD error = GetLastError();
     265        if (error == IP_REQ_TIMED_OUT)
     266            Log2(("NAT: ping %p timed out\n", (void *)pong));
     267        else
     268            Log2(("NAT: ping %p: IcmpParseReplies: error %d\n",
     269                  (void *)pong, error));
     270        return;
     271    }
     272
     273    reply = (ICMP_ECHO_REPLY *)pong->buf;
     274
     275    if (reply->Status == IP_SUCCESS)
     276    {
     277        if (reply->Options.OptionsSize != 0) /* don't do options */
     278            return;
     279
     280        /* need to remap &reply->Address ? */
     281        if (/* not a mapped loopback */ 1)
     282        {
     283            if (reply->Options.Ttl <= 1)
     284                return;
     285            --reply->Options.Ttl;
     286        }
     287
     288        reqsize = reply->DataSize;
     289        if (   (reply->Options.Flags & IP_FLAG_DF) != 0
     290            && sizeof(struct ip) + sizeof(struct icmp_echo) + reqsize > if_mtu)
     291            return;
     292
     293        m = icmpwin_get_mbuf(pData, reqsize);
     294        if (m == NULL)
     295            return;
     296
     297        ip = mtod(m, struct ip *);
     298        icmp = (struct icmp_echo *)(mtod(m, char *) + sizeof(*ip));
     299
     300        /* fill in ip (ip_output0() does the boilerplate for us) */
     301        ip->ip_tos = reply->Options.Tos;
     302        ip->ip_len = sizeof(*ip) + sizeof(*icmp) + reqsize;
     303        ip->ip_off = 0;
     304        ip->ip_ttl = reply->Options.Ttl;
     305        ip->ip_p = IPPROTO_ICMP;
     306        ip->ip_src.s_addr = reply->Address;
     307        ip->ip_dst = pong->reqiph.ip_src;
     308
     309        icmp->icmp_type = ICMP_ECHOREPLY;
     310        icmp->icmp_code = 0;
     311        icmp->icmp_cksum = 0;
     312        icmp->icmp_echo_id = pong->reqicmph.icmp_echo_id;
     313        icmp->icmp_echo_seq = pong->reqicmph.icmp_echo_seq;
     314
     315        m_append(pData, m, reqsize, reply->Data);
     316
     317        icmp->icmp_cksum = in_cksum_skip(m, sizeof(*icmp) + reqsize, sizeof(*ip));
     318    }
     319    else {
     320        uint8_t type, code;
     321
     322        switch (reply->Status) {
     323        case IP_DEST_NET_UNREACHABLE:
     324            type = ICMP_UNREACH; code = ICMP_UNREACH_NET;
     325            break;
     326        case IP_DEST_HOST_UNREACHABLE:
     327            type = ICMP_UNREACH; code = ICMP_UNREACH_HOST;
     328            break;
     329        case IP_DEST_PROT_UNREACHABLE:
     330            type = ICMP_UNREACH; code = ICMP_UNREACH_PROTOCOL;
     331            break;
     332        case IP_PACKET_TOO_BIG:
     333            type = ICMP_UNREACH; code = ICMP_UNREACH_NEEDFRAG;
     334            break;
     335        case IP_SOURCE_QUENCH:
     336            type = ICMP_SOURCEQUENCH; code = 0;
     337            break;
     338        case IP_TTL_EXPIRED_TRANSIT:
     339            type = ICMP_TIMXCEED; code = ICMP_TIMXCEED_INTRANS;
     340            break;
     341        case IP_TTL_EXPIRED_REASSEM:
     342            type = ICMP_TIMXCEED; code = ICMP_TIMXCEED_REASS;
     343            break;
     344        default:
     345            Log2(("NAT: ping reply status %d, dropped\n", reply->Status));
     346            return;
     347        }
     348
     349        Log2(("NAT: ping status %d -> type %d/code %d\n",
     350              reply->Status, type, code));
     351
     352        /*
     353         * XXX: we don't know the TTL of the request at the time this
     354         * ICMP error was generated (we can guess it was 1 for ttl
     355         * exceeded, but don't bother faking it).
     356         */
     357        m = icmpwin_get_error(pong, type, code);
     358        if (m == NULL)
     359            return;
     360
     361        ip = mtod(m, struct ip *);
     362
     363        ip->ip_tos = reply->Options.Tos;
     364        ip->ip_ttl = reply->Options.Ttl; /* XXX: decrement */
     365        ip->ip_src.s_addr = reply->Address;
     366    }
     367
     368    Assert(ip->ip_len == m_length(m, NULL));
     369    ip_output(pData, NULL, m);
     370}
     371
     372
     373/*
     374 * Prepare mbuf with ICMP error type/code.
     375 * IP source must be filled by the caller.
     376 */
     377static struct mbuf *
     378icmpwin_get_error(struct pong *pong, int type, int code)
     379{
     380    PNATState pData = pong->pData;
     381    struct mbuf *m;
     382    struct ip *ip;
     383    struct icmp_echo *icmp;
     384    size_t reqsize;
     385
     386    Log2(("NAT: ping error type %d/code %d\n", type, code));
     387
     388    reqsize = sizeof(pong->reqiph) + sizeof(pong->reqicmph);
     389
     390    m = icmpwin_get_mbuf(pData, reqsize);
     391    if (m == NULL)
     392        return NULL;
     393
     394    ip = mtod(m, struct ip *);
     395    icmp = (struct icmp_echo *)(mtod(m, char *) + sizeof(*ip));
     396
     397    ip->ip_tos = 0;
     398    ip->ip_len = sizeof(*ip) + sizeof(*icmp) + reqsize;
     399    ip->ip_off = 0;
     400    ip->ip_ttl = IPDEFTTL;
     401    ip->ip_p = IPPROTO_ICMP;
     402    ip->ip_src.s_addr = 0;      /* NB */
     403    ip->ip_dst = pong->reqiph.ip_src;
     404
     405    icmp->icmp_type = type;
     406    icmp->icmp_code = code;
     407    icmp->icmp_cksum = 0;
     408    icmp->icmp_echo_id = 0;
     409    icmp->icmp_echo_seq = 0;
     410
     411    m_append(pData, m, sizeof(pong->reqiph), (caddr_t)&pong->reqiph);
     412    m_append(pData, m, sizeof(pong->reqicmph), (caddr_t)&pong->reqicmph);
     413
     414    icmp->icmp_cksum = in_cksum_skip(m, sizeof(*icmp) + reqsize, sizeof(*ip));
     415
     416    return m;
     417}
     418
     419
     420/*
     421 * Replacing original simple slirp mbufs with real mbufs from freebsd
     422 * was a bit messy since assumption are different.  This leads to
     423 * rather ugly code at times.  Hide the gore here.
     424 */
     425static struct mbuf *
     426icmpwin_get_mbuf(PNATState pData, size_t reqsize)
     427{
     428    struct mbuf *m;
     429
     430    reqsize += if_maxlinkhdr;
     431    reqsize += sizeof(struct ip) + sizeof(struct icmp_echo);
     432
     433    if (reqsize <= MHLEN)
     434        /* good pings come in small packets */
     435        m = m_gethdr(pData, M_NOWAIT, MT_HEADER);
     436    else
     437        m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, slirp_size(pData));
     438
     439    if (m == NULL)
     440        return NULL;
     441
     442    m->m_flags |= M_SKIP_FIREWALL;
     443    m->m_data += if_maxlinkhdr; /* reserve leading space for ethernet header */
     444
     445    m->m_pkthdr.header = mtod(m, void *);
     446    m->m_len = sizeof(struct ip) + sizeof(struct icmp_echo);
     447
     448    return m;
     449}
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