DevINIP.cpp revision a7d402dcc23137e7b9527be6de80400043a5fbf4
157093a77f2752732368338110cb50fa6cd7717fvboxsync/* $Id$ */
157093a77f2752732368338110cb50fa6cd7717fvboxsync/** @file
157093a77f2752732368338110cb50fa6cd7717fvboxsync * DevINIP - Internal Network IP stack device/service.
157093a77f2752732368338110cb50fa6cd7717fvboxsync */
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync/*
157093a77f2752732368338110cb50fa6cd7717fvboxsync * Copyright (C) 2007-2009 Sun Microsystems, Inc.
157093a77f2752732368338110cb50fa6cd7717fvboxsync *
157093a77f2752732368338110cb50fa6cd7717fvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
157093a77f2752732368338110cb50fa6cd7717fvboxsync * available from http://www.virtualbox.org. This file is free software;
157093a77f2752732368338110cb50fa6cd7717fvboxsync * you can redistribute it and/or modify it under the terms of the GNU
157093a77f2752732368338110cb50fa6cd7717fvboxsync * General Public License (GPL) as published by the Free Software
157093a77f2752732368338110cb50fa6cd7717fvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
157093a77f2752732368338110cb50fa6cd7717fvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
157093a77f2752732368338110cb50fa6cd7717fvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
157093a77f2752732368338110cb50fa6cd7717fvboxsync *
157093a77f2752732368338110cb50fa6cd7717fvboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
157093a77f2752732368338110cb50fa6cd7717fvboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
157093a77f2752732368338110cb50fa6cd7717fvboxsync * additional information or have any questions.
157093a77f2752732368338110cb50fa6cd7717fvboxsync */
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync/*******************************************************************************
157093a77f2752732368338110cb50fa6cd7717fvboxsync* Header Files *
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync*******************************************************************************/
157093a77f2752732368338110cb50fa6cd7717fvboxsync#define LOG_GROUP LOG_GROUP_DEV_INIP
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync#include <iprt/cdefs.h> /* include early to allow RT_C_DECLS_BEGIN hack */
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <iprt/mem.h> /* include anything of ours that the lwip headers use. */
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <iprt/semaphore.h>
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync#include <iprt/thread.h>
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <iprt/alloca.h>
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync/* All lwip header files are not C++ safe. So hack around this. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsyncRT_C_DECLS_BEGIN
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/sys.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/stats.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/mem.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/memp.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/pbuf.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/netif.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "ipv4/lwip/ip.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/udp.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/tcp.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/tcpip.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "lwip/sockets.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "netif/etharp.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsyncRT_C_DECLS_END
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <VBox/pdmdev.h>
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <VBox/pdmnetifs.h>
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <VBox/tm.h>
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <iprt/assert.h>
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <iprt/string.h>
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include <iprt/uuid.h>
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync#include "../Builtins.h"
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync/*******************************************************************************
157093a77f2752732368338110cb50fa6cd7717fvboxsync* Macros and Defines *
157093a77f2752732368338110cb50fa6cd7717fvboxsync*******************************************************************************/
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync/** Maximum frame size this device can handle. */
157093a77f2752732368338110cb50fa6cd7717fvboxsync#define DEVINIP_MAX_FRAME 1514
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync/*******************************************************************************
157093a77f2752732368338110cb50fa6cd7717fvboxsync* Structures and Typedefs *
157093a77f2752732368338110cb50fa6cd7717fvboxsync*******************************************************************************/
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync/**
157093a77f2752732368338110cb50fa6cd7717fvboxsync * Internal Network IP stack device instance data.
157093a77f2752732368338110cb50fa6cd7717fvboxsync *
157093a77f2752732368338110cb50fa6cd7717fvboxsync * @implements PDMIBASE
157093a77f2752732368338110cb50fa6cd7717fvboxsync * @implements PDMINETWORKDOWN
157093a77f2752732368338110cb50fa6cd7717fvboxsync */
157093a77f2752732368338110cb50fa6cd7717fvboxsynctypedef struct DEVINTNETIP
157093a77f2752732368338110cb50fa6cd7717fvboxsync{
157093a77f2752732368338110cb50fa6cd7717fvboxsync /** The base interface for LUN\#0. */
157093a77f2752732368338110cb50fa6cd7717fvboxsync PDMIBASE IBase;
157093a77f2752732368338110cb50fa6cd7717fvboxsync /** The network port this device provides (LUN\#0). */
157093a77f2752732368338110cb50fa6cd7717fvboxsync PDMINETWORKDOWN INetworkDown;
157093a77f2752732368338110cb50fa6cd7717fvboxsync /** The base interface of the network driver below us. */
157093a77f2752732368338110cb50fa6cd7717fvboxsync PPDMIBASE pDrvBase;
157093a77f2752732368338110cb50fa6cd7717fvboxsync /** The connector of the network driver below us. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync PPDMINETWORKUP pDrv;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** Pointer to the device instance. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync PPDMDEVINSR3 pDevIns;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** MAC adress. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync RTMAC MAC;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** Static IP address of the interface. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync char *pszIP;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** Netmask of the interface. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync char *pszNetmask;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** Gateway for the interface. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync char *pszGateway;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** lwIP network interface description. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync struct netif IntNetIF;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** lwIP ARP timer. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync PTMTIMERR3 ARPTimer;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** lwIP TCP fast timer. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync PTMTIMERR3 TCPFastTimer;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** lwIP TCP slow timer. */
d1bbcc83a0c9ccb22e216e9346143179b0c8a554vboxsync PTMTIMERR3 TCPSlowTimer;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** lwIP semaphore to coordinate TCPIP init/terminate. */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync sys_sem_t LWIPTcpInitSem;
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync /** hack: get linking right. remove this eventually, once the device
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync * provides a proper interface to all IP stack functions. */
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync const void *pLinkHack;
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync} DEVINTNETIP, *PDEVINTNETIP;
63ff009f4d6033ac5e3557c9c5ec0329ed5c0a19vboxsync
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync/*******************************************************************************
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync* Global Variables *
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync*******************************************************************************/
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync/**
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync * Pointer to the (only) instance data in this device.
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync */
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsyncstatic PDEVINTNETIP g_pDevINIPData = NULL;
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync/*
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync * really ugly hack to avoid linking problems on unix style platforms
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync * using .a libraries for now.
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync */
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsyncstatic const PFNRT g_pDevINILinkHack[] =
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync{
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync (PFNRT)lwip_socket,
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync (PFNRT)lwip_close,
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync (PFNRT)lwip_setsockopt,
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync (PFNRT)lwip_recv,
59c7f47943ae42abc3d4e1c6d87b789b668b1260vboxsync (PFNRT)lwip_send,
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync (PFNRT)lwip_select
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync};
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync/*******************************************************************************
157093a77f2752732368338110cb50fa6cd7717fvboxsync* Internal Functions *
157093a77f2752732368338110cb50fa6cd7717fvboxsync*******************************************************************************/
157093a77f2752732368338110cb50fa6cd7717fvboxsyncstatic DECLCALLBACK(void) devINIPARPTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer);
157093a77f2752732368338110cb50fa6cd7717fvboxsyncstatic DECLCALLBACK(void) devINIPTCPFastTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer);
157093a77f2752732368338110cb50fa6cd7717fvboxsyncstatic DECLCALLBACK(void) devINIPTCPSlowTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer);
157093a77f2752732368338110cb50fa6cd7717fvboxsyncstatic DECLCALLBACK(err_t) devINIPOutput(struct netif *netif, struct pbuf *p,
157093a77f2752732368338110cb50fa6cd7717fvboxsync struct ip_addr *ipaddr);
157093a77f2752732368338110cb50fa6cd7717fvboxsyncstatic DECLCALLBACK(err_t) devINIPOutputRaw(struct netif *netif,
157093a77f2752732368338110cb50fa6cd7717fvboxsync struct pbuf *p);
157093a77f2752732368338110cb50fa6cd7717fvboxsyncstatic DECLCALLBACK(err_t) devINIPInterface(struct netif *netif);
157093a77f2752732368338110cb50fa6cd7717fvboxsync
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync/**
157093a77f2752732368338110cb50fa6cd7717fvboxsync * ARP cache timeout handling for lwIP.
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync *
157093a77f2752732368338110cb50fa6cd7717fvboxsync * @param pDevIns Device instance.
157093a77f2752732368338110cb50fa6cd7717fvboxsync * @param pTimer Pointer to timer.
157093a77f2752732368338110cb50fa6cd7717fvboxsync */
157093a77f2752732368338110cb50fa6cd7717fvboxsyncstatic DECLCALLBACK(void) devINIPARPTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
157093a77f2752732368338110cb50fa6cd7717fvboxsync{
157093a77f2752732368338110cb50fa6cd7717fvboxsync PDEVINTNETIP pThis = (PDEVINTNETIP)pvUser;
157093a77f2752732368338110cb50fa6cd7717fvboxsync LogFlow(("%s: pDevIns=%p pTimer=%p\n", __FUNCTION__, pDevIns, pTimer));
157093a77f2752732368338110cb50fa6cd7717fvboxsync lwip_etharp_tmr();
157093a77f2752732368338110cb50fa6cd7717fvboxsync TMTimerSetMillies(pThis->ARPTimer, ARP_TMR_INTERVAL);
157093a77f2752732368338110cb50fa6cd7717fvboxsync LogFlow(("%s: return\n", __FUNCTION__));
157093a77f2752732368338110cb50fa6cd7717fvboxsync}
157093a77f2752732368338110cb50fa6cd7717fvboxsync
157093a77f2752732368338110cb50fa6cd7717fvboxsync/**
157093a77f2752732368338110cb50fa6cd7717fvboxsync * TCP fast timer handling for lwIP.
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync *
157093a77f2752732368338110cb50fa6cd7717fvboxsync * @param pDevIns Device instance.
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync * @param pTimer Pointer to timer.
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsync */
2ac3892cdc8b16a0dee55e8b4510b8ecea83c95fvboxsyncstatic DECLCALLBACK(void) devINIPTCPFastTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
157093a77f2752732368338110cb50fa6cd7717fvboxsync{
157093a77f2752732368338110cb50fa6cd7717fvboxsync PDEVINTNETIP pThis = (PDEVINTNETIP)pvUser;
157093a77f2752732368338110cb50fa6cd7717fvboxsync LogFlow(("%s: pDevIns=%p pTimer=%p\n", __FUNCTION__, pDevIns, pTimer));
lwip_tcp_fasttmr();
TMTimerSetMillies(pThis->TCPFastTimer, TCP_FAST_INTERVAL);
LogFlow(("%s: return\n", __FUNCTION__));
}
/**
* TCP slow timer handling for lwIP.
*
* @param pDevIns Device instance.
* @param pTimer Pointer to timer.
*/
static DECLCALLBACK(void) devINIPTCPSlowTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
PDEVINTNETIP pThis = (PDEVINTNETIP)pvUser;
LogFlow(("%s: pDevIns=%p pTimer=%p\n", __FUNCTION__, pDevIns, pTimer));
lwip_tcp_slowtmr();
TMTimerSetMillies(pThis->TCPSlowTimer, TCP_SLOW_INTERVAL);
LogFlow(("%s: return\n", __FUNCTION__));
}
/**
* Output a TCP/IP packet on the interface. Uses the generic lwIP ARP
* code to resolve the address and call the link-level packet function.
*
* @returns lwIP error code
* @param netif Interface on which to send IP packet.
* @param p Packet data.
* @param ipaddr Destination IP address.
*/
static DECLCALLBACK(err_t) devINIPOutput(struct netif *netif, struct pbuf *p,
struct ip_addr *ipaddr)
{
err_t lrc;
LogFlow(("%s: netif=%p p=%p ipaddr=%#04x\n", __FUNCTION__, netif, p,
ipaddr->addr));
lrc = lwip_etharp_output(netif, ipaddr, p);
LogFlow(("%s: return %d\n", __FUNCTION__, lrc));
return lrc;
}
/**
* Output a raw packet on the interface.
*
* @returns lwIP error code
* @param netif Interface on which to send frame.
* @param p Frame data.
*/
static DECLCALLBACK(err_t) devINIPOutputRaw(struct netif *netif,
struct pbuf *p)
{
int rc = VINF_SUCCESS;
LogFlow(("%s: netif=%p p=%p\n", __FUNCTION__, netif, p));
Assert(g_pDevINIPData);
Assert(g_pDevINIPData->pDrv);
/* Silently ignore packets being sent while lwIP isn't set up. */
if (g_pDevINIPData)
{
PPDMSCATTERGATHER pSgBuf;
rc = g_pDevINIPData->pDrv->pfnAllocBuf(g_pDevINIPData->pDrv, DEVINIP_MAX_FRAME, NULL /*pGso*/, &pSgBuf);
if (RT_SUCCESS(rc))
{
#if ETH_PAD_SIZE
lwip_pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
uint8_t *pbBuf = pSgBuf ? (uint8_t *)pSgBuf->aSegs[0].pvSeg : NULL;
size_t cbBuf = 0;
for (struct pbuf *q = p; q != NULL; q = q->next)
{
if (cbBuf + q->len <= DEVINIP_MAX_FRAME)
{
if (RT_LIKELY(pbBuf))
{
memcpy(pbBuf, q->payload, q->len);
pbBuf += q->len;
}
cbBuf += q->len;
}
else
{
LogRel(("INIP: exceeded frame size\n"));
break;
}
}
if (cbBuf)
rc = g_pDevINIPData->pDrv->pfnSendBuf(g_pDevINIPData->pDrv, pSgBuf, false);
else
rc = g_pDevINIPData->pDrv->pfnFreeBuf(g_pDevINIPData->pDrv, pSgBuf);
#if ETH_PAD_SIZE
lwip_pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
}
}
err_t lrc = ERR_OK;
if (RT_FAILURE(rc))
lrc = ERR_IF;
LogFlow(("%s: return %d (vbox: %Rrc)\n", __FUNCTION__, rc, lrc));
return lrc;
}
/**
* Implements the ethernet interface backend initialization for lwIP.
*
* @returns lwIP error code
* @param netif Interface to configure.
*/
static DECLCALLBACK(err_t) devINIPInterface(struct netif *netif)
{
LogFlow(("%s: netif=%p\n", __FUNCTION__, netif));
Assert(g_pDevINIPData != NULL);
netif->state = g_pDevINIPData;
netif->hwaddr_len = sizeof(g_pDevINIPData->MAC);
memcpy(netif->hwaddr, &g_pDevINIPData->MAC, sizeof(g_pDevINIPData->MAC));
netif->mtu = DEVINIP_MAX_FRAME;
netif->flags = NETIF_FLAG_BROADCAST;
netif->output = devINIPOutput;
netif->linkoutput = devINIPOutputRaw;
lwip_etharp_init();
TMTimerSetMillies(g_pDevINIPData->ARPTimer, ARP_TMR_INTERVAL);
LogFlow(("%s: success\n", __FUNCTION__));
return ERR_OK;
}
/**
* Wait until data can be received.
*
* @returns VBox status code. VINF_SUCCESS means there is at least one receive descriptor available.
* @param pInterface PDM network port interface pointer.
* @param cMillies Number of milliseconds to wait. 0 means return immediately.
*/
static DECLCALLBACK(int) devINIPNetworkDown_WaitInputAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
{
LogFlow(("%s: pInterface=%p\n", __FUNCTION__, pInterface));
LogFlow(("%s: return VINF_SUCCESS\n", __FUNCTION__));
return VINF_SUCCESS;
}
/**
* Receive data and pass it to lwIP for processing.
*
* @returns VBox status code
* @param pInterface PDM network port interface pointer.
* @param pvBuf Pointer to frame data.
* @param cb Frame size.
*/
static DECLCALLBACK(int) devINIPNetworkDown_Input(PPDMINETWORKDOWN pInterface,
const void *pvBuf, size_t cb)
{
const uint8_t *pbBuf = (const uint8_t *)pvBuf;
size_t len = cb;
const struct eth_hdr *ethhdr;
struct pbuf *p, *q;
int rc = VINF_SUCCESS;
LogFlow(("%s: pInterface=%p pvBuf=%p cb=%lu\n", __FUNCTION__, pInterface,
pvBuf, cb));
Assert(g_pDevINIPData);
Assert(g_pDevINIPData->pDrv);
/* Silently ignore packets being received while lwIP isn't set up. */
if (!g_pDevINIPData)
goto out;
#if ETH_PAD_SIZE
len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif
/* We allocate a pbuf chain of pbufs from the pool. */
p = lwip_pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL)
{
#if ETH_PAD_SIZE
lwip_pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
for (q = p; q != NULL; q = q->next)
{
/* Fill the buffers, and clean out unused buffer space. */
memcpy(q->payload, pbBuf, RT_MIN(cb, q->len));
pbBuf += RT_MIN(cb, q->len);
if (q->len > cb)
memset(((uint8_t *)q->payload) + cb, '\0', q->len - cb);
cb -= RT_MIN(cb, q->len);
}
ethhdr = (const struct eth_hdr *)p->payload;
struct netif *iface = &g_pDevINIPData->IntNetIF;
err_t lrc;
switch (htons(ethhdr->type))
{
case ETHTYPE_IP: /* IP packet */
lwip_pbuf_header(p, -(ssize_t)sizeof(struct eth_hdr));
lrc = iface->input(p, iface);
if (lrc)
rc = VERR_NET_IO_ERROR;
break;
case ETHTYPE_ARP: /* ARP packet */
lwip_etharp_arp_input(iface, (struct eth_addr *)iface->hwaddr, p);
break;
default:
lwip_pbuf_free(p);
}
}
out:
LogFlow(("%s: return %Rrc\n", __FUNCTION__, rc));
return rc;
}
/**
* @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
*/
static DECLCALLBACK(void) devINIPNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
{
NOREF(pInterface);
}
/**
* Signals the end of lwIP TCPIP initialization.
*
* @param arg opaque argument, here the pointer to the semaphore.
*/
static DECLCALLBACK(void) devINIPTcpipInitDone(void *arg)
{
sys_sem_t *sem = (sys_sem_t *)arg;
lwip_sys_sem_signal(*sem);
}
/* -=-=-=-=- PDMIBASE -=-=-=-=- */
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) devINIPQueryInterface(PPDMIBASE pInterface,
const char *pszIID)
{
PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
return NULL;
}
/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
/**
* Destruct a device instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(int) devINIPDestruct(PPDMDEVINS pDevIns)
{
PDEVINTNETIP pThis = PDMINS_2_DATA(pDevIns, PDEVINTNETIP);
LogFlow(("%s: pDevIns=%p\n", __FUNCTION__, pDevIns));
PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
if (g_pDevINIPData != NULL)
{
netif_set_down(&pThis->IntNetIF);
netif_remove(&pThis->IntNetIF);
tcpip_terminate();
lwip_sys_sem_wait(pThis->LWIPTcpInitSem);
lwip_sys_sem_free(pThis->LWIPTcpInitSem);
}
if (pThis->pszIP)
MMR3HeapFree(pThis->pszIP);
if (pThis->pszNetmask)
MMR3HeapFree(pThis->pszNetmask);
if (pThis->pszGateway)
MMR3HeapFree(pThis->pszGateway);
LogFlow(("%s: success\n", __FUNCTION__));
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
static DECLCALLBACK(int) devINIPConstruct(PPDMDEVINS pDevIns, int iInstance,
PCFGMNODE pCfg)
{
PDEVINTNETIP pThis = PDMINS_2_DATA(pDevIns, PDEVINTNETIP);
int rc = VINF_SUCCESS;
LogFlow(("%s: pDevIns=%p iInstance=%d pCfg=%p\n", __FUNCTION__,
pDevIns, iInstance, pCfg));
Assert(iInstance == 0);
PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
/*
* Validate the config.
*/
if (!CFGMR3AreValuesValid(pCfg, "MAC\0IP\0Netmask\0Gateway\0"))
{
rc = PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("Unknown Internal Networking IP configuration option"));
goto out;
}
/*
* Init the static parts.
*/
pThis->pszIP = NULL;
pThis->pszNetmask = NULL;
pThis->pszGateway = NULL;
/* Pointer to device instance */
pThis->pDevIns = pDevIns;
/* IBase */
pThis->IBase.pfnQueryInterface = devINIPQueryInterface;
/* INetworkDown */
pThis->INetworkDown.pfnWaitReceiveAvail = devINIPNetworkDown_WaitInputAvail;
pThis->INetworkDown.pfnReceive = devINIPNetworkDown_Input;
pThis->INetworkDown.pfnXmitPending = devINIPNetworkDown_XmitPending;
/*
* Get the configuration settings.
*/
rc = CFGMR3QueryBytes(pCfg, "MAC", &pThis->MAC, sizeof(pThis->MAC));
if (rc == VERR_CFGM_NOT_BYTES)
{
char szMAC[64];
rc = CFGMR3QueryString(pCfg, "MAC", &szMAC[0], sizeof(szMAC));
if (RT_SUCCESS(rc))
{
char *macStr = &szMAC[0];
char *pMac = (char *)&pThis->MAC;
for (uint32_t i = 0; i < 6; i++)
{
if ( !*macStr || !*(macStr + 1)
|| *macStr == ':' || *(macStr + 1) == ':')
{
rc = PDMDEV_SET_ERROR(pDevIns,
VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("Configuration error: Invalid \"MAC\" value"));
goto out;
}
char c1 = *macStr++ - '0';
if (c1 > 9)
c1 -= 7;
char c2 = *macStr++ - '0';
if (c2 > 9)
c2 -= 7;
*pMac++ = ((c1 & 0x0f) << 4) | (c2 & 0x0f);
if (i != 5 && *macStr == ':')
macStr++;
}
}
}
if (RT_FAILURE(rc))
{
PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to get the \"MAC\" value"));
goto out;
}
rc = CFGMR3QueryStringAlloc(pCfg, "IP", &pThis->pszIP);
if (RT_FAILURE(rc))
{
PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to get the \"IP\" value"));
goto out;
}
rc = CFGMR3QueryStringAlloc(pCfg, "Netmask", &pThis->pszNetmask);
if (RT_FAILURE(rc))
{
PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to get the \"Netmask\" value"));
goto out;
}
rc = CFGMR3QueryStringAlloc(pCfg, "Gateway", &pThis->pszGateway);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
rc = VINF_SUCCESS;
if (RT_FAILURE(rc))
{
PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to get the \"Gateway\" value"));
goto out;
}
/*
* Attach driver and query the network connector interface.
*/
rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase,
"Network Port");
if (RT_FAILURE(rc))
{
pThis->pDrvBase = NULL;
pThis->pDrv = NULL;
goto out;
}
else
{
pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
if (!pThis->pDrv)
{
AssertMsgFailed(("Failed to obtain the PDMINETWORKUP interface!\n"));
rc = VERR_PDM_MISSING_INTERFACE_BELOW;
goto out;
}
}
struct ip_addr ipaddr, netmask, gw;
struct in_addr ip;
if (!inet_aton(pThis->pszIP, &ip))
{
rc = PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("Configuration error: Invalid \"IP\" value"));
goto out;
}
memcpy(&ipaddr, &ip, sizeof(ipaddr));
if (!inet_aton(pThis->pszNetmask, &ip))
{
rc = PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("Configuration error: Invalid \"Netmask\" value"));
goto out;
}
memcpy(&netmask, &ip, sizeof(netmask));
if (pThis->pszGateway)
{
if (!inet_aton(pThis->pszGateway, &ip))
{
rc = PDMDEV_SET_ERROR(pDevIns,
VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("Configuration error: Invalid \"Gateway\" value"));
goto out;
}
memcpy(&gw, &ip, sizeof(gw));
}
else
{
inet_aton(pThis->pszIP, &ip);
memcpy(&gw, &ip, sizeof(gw));
}
/*
* Initialize lwIP.
*/
lwip_stats_init();
lwip_sys_init();
#if MEM_LIBC_MALLOC == 0
lwip_mem_init();
#endif
lwip_memp_init();
lwip_pbuf_init();
lwip_netif_init();
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, devINIPARPTimer, pThis,
TMTIMER_FLAGS_NO_CRIT_SECT, "lwIP ARP", &pThis->ARPTimer);
if (RT_FAILURE(rc))
goto out;
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, devINIPTCPFastTimer, pThis,
TMTIMER_FLAGS_NO_CRIT_SECT, "lwIP fast TCP", &pThis->TCPFastTimer);
if (RT_FAILURE(rc))
goto out;
TMTimerSetMillies(pThis->TCPFastTimer, TCP_FAST_INTERVAL);
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, devINIPTCPSlowTimer, pThis,
TMTIMER_FLAGS_NO_CRIT_SECT, "lwIP slow TCP", &pThis->TCPSlowTimer);
if (RT_FAILURE(rc))
goto out;
TMTimerSetMillies(pThis->TCPFastTimer, TCP_SLOW_INTERVAL);
pThis->LWIPTcpInitSem = lwip_sys_sem_new(0);
{
lwip_tcpip_init(devINIPTcpipInitDone, &pThis->LWIPTcpInitSem);
lwip_sys_sem_wait(pThis->LWIPTcpInitSem);
}
/*
* Set up global pointer to interface data.
*/
g_pDevINIPData = pThis;
struct netif *ret;
pThis->IntNetIF.name[0] = 'I';
pThis->IntNetIF.name[1] = 'N';
ret = netif_add(&pThis->IntNetIF, &ipaddr, &netmask, &gw, NULL,
devINIPInterface, lwip_tcpip_input);
if (!ret)
{
rc = VERR_NET_NO_NETWORK;
goto out;
}
lwip_netif_set_default(&pThis->IntNetIF);
lwip_netif_set_up(&pThis->IntNetIF);
/* link hack */
pThis->pLinkHack = g_pDevINILinkHack;
out:
LogFlow(("%s: return %Rrc\n", __FUNCTION__, rc));
return rc;
}
/**
* Query whether lwIP is initialized or not. Since there is only a single
* instance of this device ever for a VM, it can be a global function.
*
* @returns True if lwIP is initialized.
*/
bool DevINIPConfigured(void)
{
return g_pDevINIPData != NULL;
}
/**
* Internal network IP stack device registration record.
*/
const PDMDEVREG g_DeviceINIP =
{
/* u32Version */
PDM_DEVREG_VERSION,
/* szName */
"IntNetIP",
/* szRCMod/szR0Mod */
"",
"",
/* pszDescription */
"Internal Network IP stack device",
/* fFlags */
PDM_DEVREG_FLAGS_DEFAULT_BITS,
/* fClass. As this is used by the storage devices, it must come earlier. */
PDM_DEVREG_CLASS_VMM_DEV,
/* cMaxInstances */
1,
/* cbInstance */
sizeof(DEVINTNETIP),
/* pfnConstruct */
devINIPConstruct,
/* pfnDestruct */
devINIPDestruct,
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
PDM_DEVREG_VERSION
};