/* $Id$ */
/** @file
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*
* This code is based on:
*
*
* Copyright (c) 2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <slirp.h>
#include <libslirp.h>
/** Entry in the table of known DHCP clients. */
typedef struct
{
bool allocated;
int number;
} BOOTPClient;
/** Number of DHCP clients supported by NAT. */
/* XXX: only DHCP is supported */
{
/*@todo magic validation */
q += 4; /*magic*/
while(*q != RFC1533_END)
{
if (*q == RFC1533_PAD)
{
q++;
continue;
}
if (*q == tag)
return q;
q++;
len = *q;
q += 1 + len;
}
return NULL;
}
{
int i;
for (i = 0; i < NB_ADDR; i++)
{
if (!bootp_clients[i].allocated)
{
bc = &bootp_clients[i];
return bc;
}
}
LogFlowFunc(("LEAVE: NULL\n"));
return NULL;
}
{
if (!bc)
return NULL;
return bc;
}
{
unsigned i;
for (i = 0; i < NB_ADDR; i++)
{
{
return VINF_SUCCESS;
}
}
return VERR_NOT_FOUND;
}
/*
* from RFC 2131 4.3.1
* Field DHCPOFFER DHCPACK DHCPNAK
* ----- --------- ------- -------
* 'op' BOOTREPLY BOOTREPLY BOOTREPLY
* 'htype' (From "Assigned Numbers" RFC)
* 'hlen' (Hardware address length in octets)
* 'hops' 0 0 0
* 'xid' 'xid' from client 'xid' from client 'xid' from client
* DHCPDISCOVER DHCPREQUEST DHCPREQUEST
* message message message
* 'secs' 0 0 0
* 'ciaddr' 0 'ciaddr' from 0
* DHCPREQUEST or 0
* 'yiaddr' IP address offered IP address 0
* to client assigned to client
* 'siaddr' IP address of next IP address of next 0
* bootstrap server bootstrap server
* 'flags' 'flags' from 'flags' from 'flags' from
* client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
* message message message
* 'giaddr' 'giaddr' from 'giaddr' from 'giaddr' from
* client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
* message message message
* 'chaddr' 'chaddr' from 'chaddr' from 'chaddr' from
* client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
* message message message
* 'sname' Server host name Server host name (unused)
* or options or options
* 'file' Client boot file Client boot file (unused)
* name or options name or options
* 'options' options options
*
* Option DHCPOFFER DHCPACK DHCPNAK
* ------ --------- ------- -------
* Requested IP address MUST NOT MUST NOT MUST NOT
* IP address lease time MUST MUST (DHCPREQUEST) MUST NOT
* MUST NOT (DHCPINFORM)
* Use 'file'/'sname' fields MAY MAY MUST NOT
* DHCP message type DHCPOFFER DHCPACK DHCPNAK
* Parameter request list MUST NOT MUST NOT MUST NOT
* Message SHOULD SHOULD SHOULD
* Client identifier MUST NOT MUST NOT MAY
* Vendor class identifier MAY MAY MAY
* Server identifier MUST MUST MUST
* Maximum message size MUST NOT MUST NOT MUST NOT
* All others MAY MAY MUST NOT
*/
{
int i;
for (i = 0; i < NB_ADDR; i++)
{
&& bootp_clients[i].allocated != 0)
{
bc = &bootp_clients[i];
return bc;
}
}
LogFlowFunc(("LEAVE: NULL\n"));
return NULL;
}
static struct mbuf *dhcp_create_msg(PNATState pData, struct bootp_t *bp, struct mbuf *m, uint8_t type)
{
uint8_t *q;
#if 0 /*check flags*/
#endif
q += 4;
*q++ = RFC2132_MSG_TYPE;
*q++ = 1;
*q++ = type;
return m;
}
{
uint8_t *q;
int val;
int added = 0;
q += 7; /* !cookie rfc 2132 + TYPE*/
/*DHCP Offer specific*/
/*
*/
if (bootp_filename)
if (fDhcpRequest)
{
}
do { \
}while(0)
/* appending another value to tag, calculates len of whole block*/
do { \
(q) += (len); \
}while(0)
{
}
{
q_dns_header = q;
{
continue; /* first value with head we've ingected before */
}
}
{
{
continue;
/* never meet valid separator here in RFC1533*/
if (added != 0)
else
added = 1;
}
}
if (*slirp_hostname)
{
}
/* Temporary fix: do not pollute ARP cache from BOOTP because it may result
in network loss due to cache entry override w/ invalid MAC address. */
//slirp_arp_cache_update_or_add(pData, rbp->bp_yiaddr.s_addr, bc->macaddr);
}
{
return 7;
}
static int dhcp_send_ack(PNATState pData, struct bootp_t *bp, BOOTPClient *bc, struct mbuf *m, int fDhcpRequest)
{
return offReply;
}
{
return offReply;
}
/**
* decoding client messages RFC2131 (4.3.6)
* ---------------------------------------------------------------------
* | |INIT-REBOOT |SELECTING |RENEWING |REBINDING |
* ---------------------------------------------------------------------
* |server-ip |MUST NOT |MUST |MUST NOT |MUST NOT |
* |requested-ip |MUST |MUST |MUST NOT |MUST NOT |
* |ciaddr |zero |zero |IP address |IP address|
* ---------------------------------------------------------------------
*
*/
enum DHCP_REQUEST_STATES
{
};
{
int offReply;
/* need to understand which type of request we get */
{
/* selecting */
if (!bc)
{
LogRel(("NAT: DHCP no IP was allocated\n"));
return -1;
}
if ( !req_ip
{
LogRel(("NAT: Invalid SELECTING request\n"));
return -1; /* silently ignored */
}
#if 0
/* DSL xid in request differ from offer */
#endif
}
else
{
{
/* init-reboot */
}
else
{
/* table 4 of rfc2131 */
else
}
}
/*?? renewing ??*/
switch (dhcp_stat)
{
case RENEWING:
/**
* decoding client messages RFC2131 (4.3.6)
* ------------------------------
* | |RENEWING |
* ------------------------------
* |server-ip |MUST NOT |
* |requested-ip |MUST NOT |
* |ciaddr |IP address |
* ------------------------------
*/
if ( server_ip
|| req_ip
{
LogRel(("NAT: invalid RENEWING dhcp request\n"));
return -1; /* silent ignorance */
}
{
/*if it already here well just do ack, we aren't aware of dhcp time expiration*/
}
else
{
{
return offReply;
}
if (!bc)
{
LogRel(("NAT: can't alloc address. RENEW has been silently ignored.\n"));
return -1;
}
}
break;
case INIT_REBOOT:
/**
* decoding client messages RFC2131 (4.3.6)
* ------------------------------
* | |INIT-REBOOT |
* ------------------------------
* |server-ip |MUST NOT |
* |requested-ip |MUST |
* |ciaddr |zero |
* ------------------------------
*
*/
if ( server_ip
|| !req_ip
{
LogRel(("NAT: Invalid INIT-REBOOT dhcp request\n"));
return -1; /* silently ignored */
}
{
return offReply;
}
/* find_addr() got some result? */
if (!bc)
{
if (!bc)
{
LogRel(("NAT: can't alloc address. RENEW has been silently ignored\n"));
return -1;
}
}
break;
case NONE:
LogRel(("NAT: REBINDING state isn't impemented\n"));
LogRel(("NAT: SELECTING state isn't impemented\n"));
return -1;
default:
break;
}
return offReply;
}
static int dhcp_decode_discover(PNATState pData, struct bootp_t *bp, int fDhcpDiscover, struct mbuf *m)
{
int offReply;
if (fDhcpDiscover)
{
if (!bc)
{
if (!bc)
{
LogRel(("NAT: DHCP no IP address left\n"));
Log(("no address left\n"));
return -1;
}
}
return offReply;
}
else
{
if (!bc)
{
LogRel(("NAT: DHCP Inform was ignored no boot client was found\n"));
return -1;
}
return offReply;
}
return -1;
}
{
LogRel(("NAT: %s %RTnaipv4\n",
return 0;
}
/**
* fields for discovering t
* Field DHCPDISCOVER DHCPREQUEST DHCPDECLINE,
* DHCPINFORM DHCPRELEASE
* ----- ------------ ----------- -----------
* 'op' BOOTREQUEST BOOTREQUEST BOOTREQUEST
* 'htype' (From "Assigned Numbers" RFC)
* 'hlen' (Hardware address length in octets)
* 'hops' 0 0 0
* 'xid' selected by client 'xid' from server selected by
* DHCPOFFER message client
* 'secs' 0 or seconds since 0 or seconds since 0
* DHCP process started DHCP process started
* 'flags' Set 'BROADCAST' Set 'BROADCAST' 0
* flag if client flag if client
* requires broadcast requires broadcast
* reply reply
* 'ciaddr' 0 (DHCPDISCOVER) 0 or client's 0 (DHCPDECLINE)
* client's network address client's network
* (DHCPINFORM) (DHCPRELEASE)
* 'yiaddr' 0 0 0
* 'siaddr' 0 0 0
* 'giaddr' 0 0 0
* 'chaddr' client's hardware client's hardware client's hardware
* address address address
* 'sname' options, if options, if (unused)
* indicated in indicated in
* option; otherwise option; otherwise
* unused unused
* 'file' options, if options, if (unused)
* indicated in indicated in
* option; otherwise option; otherwise
* unused unused
* 'options' options options (unused)
* Requested IP address MAY MUST (in MUST
* (DISCOVER) SELECTING or (DHCPDECLINE),
* MUST NOT INIT-REBOOT) MUST NOT
* (INFORM) MUST NOT (in (DHCPRELEASE)
* BOUND or
* RENEWING)
* IP address lease time MAY MAY MUST NOT
* (DISCOVER)
* MUST NOT
* (INFORM)
* Use 'file'/'sname' fields MAY MAY MAY
* DHCP message type DHCPDISCOVER/ DHCPREQUEST DHCPDECLINE/
* DHCPINFORM DHCPRELEASE
* Client identifier MAY MAY MAY
* Vendor class identifier MAY MAY MUST NOT
* Server identifier MUST NOT MUST (after MUST
* SELECTING)
* MUST NOT (after
* INIT-REBOOT,
* BOUND, RENEWING
* or REBINDING)
* Parameter request list MAY MAY MUST NOT
* Maximum message size MAY MAY MUST NOT
* Message SHOULD NOT SHOULD NOT SHOULD
* Site-specific MAY MAY MUST NOT
* All others MAY MAY MUST NOT
*
*/
{
int rc;
int fDhcpDiscover = 0;
if (size < 5)
return;
return;
/* note: pu8RawDhcpObject doesn't point to parameter buf */
if (!pu8RawDhcpObject)
return;
/**
* We're going update dns list at least once per DHCP transaction (!not on every operation
* within transaction), assuming that transaction can't be longer than 1 min.
*
* @note: if we have notification update (HAVE_NOTIFICATION_FOR_DNS_UPDATE)
* provided by host, we don't need implicitly re-initialize dns list.
*
* @note: NATState::fUseHostResolver became (r89055) the flag signalling that Slirp
* wasn't able to fetch fresh host DNS info and fall down to use host-resolver, on one
* of the previous attempts to proxy dns requests to Host's name-resolving API
*
* @note: Checking NATState::fUseHostResolver == true, we want to try restore behaviour initialy
* wanted by user ASAP (P here when host serialize its configuration in files parsed by Slirp).
*/
&& ( pData->dnsLastUpdate == 0
|| pData->fUseHostResolver))
{
{
if (parameter_list[i] == RFC1533_DNS)
{
break;
}
}
}
if (!m)
{
LogRel(("NAT: can't alocate memory for response!\n"));
return;
}
switch (*(pu8RawDhcpObject + 2))
{
case DHCPDISCOVER:
fDhcpDiscover = 1;
/* fall through */
case DHCPINFORM:
if (rc > 0)
goto reply;
break;
case DHCPREQUEST:
if (rc > 0)
goto reply;
break;
case DHCPRELEASE:
/* no reply required */
break;
case DHCPDECLINE:
/* note: pu8RawDhcpObject doesn't point to DHCP header, now it's expected it points
* to Dhcp Option RFC2132_REQ_ADDR
*/
if (!pu8RawDhcpObject)
{
Log(("NAT: RFC2132_REQ_ADDR not found\n"));
break;
}
if (RT_FAILURE(rc))
{
/* Not registered */
if (!bc)
{
LogRel(("NAT: can't allocate bootp client object\n"));
break;
}
}
/* no response required */
break;
default:
AssertMsgFailed(("unsupported DHCP message type"));
}
/* silently ignore */
return;
}
{
int nack;
Assert((m));
q += offReply;
*q++ = RFC1533_END; /* end of message */
- sizeof(struct ip)
- sizeof(struct udphdr);
+ sizeof(struct ip);
|| nack != 0)
else
}
{
}
{
int i;
return VERR_INVALID_PARAMETER;
for (i = 0; i < NB_ADDR; i++)
{
if ( bootp_clients[i].allocated
{
return VINF_SUCCESS;
}
}
*pip = INADDR_ANY;
return VERR_NOT_FOUND;
}
{
int i;
for (i = 0; i < NB_ADDR; i++)
{
if ( bootp_clients[i].allocated
{
return VINF_SUCCESS;
}
}
return VERR_NOT_FOUND;
}
/*
* Initialize dhcp server
* @returns 0 - if initialization is ok, non-zero otherwise
*/
{
if (!pData->pbootp_clients)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}