Update libslirp to 4.7.0 with our customizations
This commit is contained in:
@@ -7,9 +7,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define SLIRP_MAJOR_VERSION 4
|
||||
#define SLIRP_MINOR_VERSION 3
|
||||
#define SLIRP_MICRO_VERSION 1
|
||||
#define SLIRP_VERSION_STRING "4.3.1-git-86Box"
|
||||
#define SLIRP_MINOR_VERSION 7
|
||||
#define SLIRP_MICRO_VERSION 0
|
||||
#define SLIRP_VERSION_STRING "4.7.0-86Box"
|
||||
|
||||
#define SLIRP_CHECK_VERSION(major,minor,micro) \
|
||||
(SLIRP_MAJOR_VERSION > (major) || \
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <in6addr.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
@@ -31,8 +32,10 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Opaque structure containing the slirp state */
|
||||
typedef struct Slirp Slirp;
|
||||
|
||||
/* Flags passed to SlirpAddPollCb and to be returned by SlirpGetREventsCb. */
|
||||
enum {
|
||||
SLIRP_POLL_IN = 1 << 0,
|
||||
SLIRP_POLL_OUT = 1 << 1,
|
||||
@@ -47,38 +50,58 @@ typedef void (*SlirpTimerCb)(void *opaque);
|
||||
typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque);
|
||||
typedef int (*SlirpGetREventsCb)(int idx, void *opaque);
|
||||
|
||||
typedef enum SlirpTimerId {
|
||||
SLIRP_TIMER_RA,
|
||||
SLIRP_TIMER_NUM,
|
||||
} SlirpTimerId;
|
||||
|
||||
/*
|
||||
* Callbacks from slirp
|
||||
* Callbacks from slirp, to be set by the application.
|
||||
*
|
||||
* The opaque parameter is set to the opaque pointer given in the slirp_new /
|
||||
* slirp_init call.
|
||||
*/
|
||||
typedef struct SlirpCb {
|
||||
/*
|
||||
* Send an ethernet frame to the guest network. The opaque
|
||||
* parameter is the one given to slirp_init(). The function
|
||||
* doesn't need to send all the data and may return <len (no
|
||||
* buffering is done on libslirp side, so the data will be dropped
|
||||
* in this case). <0 reports an IO error.
|
||||
* Send an ethernet frame to the guest network. The opaque parameter is the
|
||||
* one given to slirp_init(). If the guest is not ready to receive a frame,
|
||||
* the function can just drop the data. TCP will then handle retransmissions
|
||||
* at a lower pace.
|
||||
* <0 reports an IO error.
|
||||
*/
|
||||
SlirpWriteCb send_packet;
|
||||
/* Print a message for an error due to guest misbehavior. */
|
||||
void (*guest_error)(const char *msg, void *opaque);
|
||||
/* Return the virtual clock value in nanoseconds */
|
||||
int64_t (*clock_get_ns)(void *opaque);
|
||||
/* Create a new timer with the given callback and opaque data */
|
||||
/* Create a new timer with the given callback and opaque data. Not
|
||||
* needed if timer_new_opaque is provided. */
|
||||
void *(*timer_new)(SlirpTimerCb cb, void *cb_opaque, void *opaque);
|
||||
/* Remove and free a timer */
|
||||
void (*timer_free)(void *timer, void *opaque);
|
||||
/* Modify a timer to expire at @expire_time */
|
||||
/* Modify a timer to expire at @expire_time (ms) */
|
||||
void (*timer_mod)(void *timer, int64_t expire_time, void *opaque);
|
||||
/* Register a fd for future polling */
|
||||
void (*register_poll_fd)(int fd, void *opaque);
|
||||
/* Unregister a fd */
|
||||
void (*unregister_poll_fd)(int fd, void *opaque);
|
||||
/* Kick the io-thread, to signal that new events may be processed */
|
||||
/* Kick the io-thread, to signal that new events may be processed because some TCP buffer
|
||||
* can now receive more data, i.e. slirp_socket_can_recv will return 1. */
|
||||
void (*notify)(void *opaque);
|
||||
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 4 begin
|
||||
*/
|
||||
|
||||
/* Initialization has completed and a Slirp* has been created. */
|
||||
void (*init_completed)(Slirp *slirp, void *opaque);
|
||||
/* Create a new timer. When the timer fires, the application passes
|
||||
* the SlirpTimerId and cb_opaque to slirp_handle_timer. */
|
||||
void *(*timer_new_opaque)(SlirpTimerId id, void *cb_opaque, void *opaque);
|
||||
} SlirpCb;
|
||||
|
||||
#define SLIRP_CONFIG_VERSION_MIN 1
|
||||
#define SLIRP_CONFIG_VERSION_MAX 3
|
||||
#define SLIRP_CONFIG_VERSION_MAX 4
|
||||
|
||||
typedef struct SlirpConfig {
|
||||
/* Version must be provided */
|
||||
@@ -124,8 +147,13 @@ typedef struct SlirpConfig {
|
||||
* Fields introduced in SlirpConfig version 3 begin
|
||||
*/
|
||||
bool disable_dns; /* slirp will not redirect/serve any DNS packet */
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 4 begin
|
||||
*/
|
||||
bool disable_dhcp; /* slirp will not reply to any DHCP requests */
|
||||
} SlirpConfig;
|
||||
|
||||
/* Create a new instance of a slirp stack */
|
||||
Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks,
|
||||
void *opaque);
|
||||
/* slirp_init is deprecated in favor of slirp_new */
|
||||
@@ -139,44 +167,103 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
|
||||
struct in6_addr vnameserver6, const char **vdnssearch,
|
||||
const char *vdomainname, const SlirpCb *callbacks,
|
||||
void *opaque);
|
||||
/* Shut down an instance of a slirp stack */
|
||||
void slirp_cleanup(Slirp *slirp);
|
||||
|
||||
/* This is called by the application when it is about to sleep through poll().
|
||||
* *timeout is set to the amount of virtual time (in ms) that the application intends to
|
||||
* wait (UINT32_MAX if infinite). slirp_pollfds_fill updates it according to
|
||||
* e.g. TCP timers, so the application knows it should sleep a smaller amount of
|
||||
* time. slirp_pollfds_fill calls add_poll for each file descriptor
|
||||
* that should be monitored along the sleep. The opaque pointer is passed as
|
||||
* such to add_poll, and add_poll returns an index. */
|
||||
void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout,
|
||||
SlirpAddPollCb add_poll, void *opaque);
|
||||
|
||||
/* This is called by the application after sleeping, to report which file
|
||||
* descriptors are available. slirp_pollfds_poll calls get_revents on each file
|
||||
* descriptor, giving it the index that add_poll returned during the
|
||||
* slirp_pollfds_fill call, to know whether the descriptor is available for
|
||||
* read/write/etc. (SLIRP_POLL_*)
|
||||
* select_error should be passed 1 if poll() returned an error. */
|
||||
void slirp_pollfds_poll(Slirp *slirp, int select_error,
|
||||
SlirpGetREventsCb get_revents, void *opaque);
|
||||
|
||||
/* This is called by the application when the guest emits a packet on the
|
||||
* guest network, to be interpreted by slirp. */
|
||||
void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
|
||||
|
||||
/* This is called by the application when a timer expires, if it provides
|
||||
* the timer_new_opaque callback. It is not needed if the application only
|
||||
* uses timer_new. */
|
||||
void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque);
|
||||
|
||||
/* These set up / remove port forwarding between a host port in the real world
|
||||
* and the guest network. */
|
||||
int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
int host_port, struct in_addr guest_addr, int guest_port);
|
||||
int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
int host_port);
|
||||
|
||||
#define SLIRP_HOSTFWD_UDP 1
|
||||
#define SLIRP_HOSTFWD_V6ONLY 2
|
||||
int slirp_add_hostxfwd(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
const struct sockaddr *gaddr, socklen_t gaddrlen,
|
||||
int flags);
|
||||
int slirp_remove_hostxfwd(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
int flags);
|
||||
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* command running on the host */
|
||||
int slirp_add_exec(Slirp *slirp, const char *cmdline,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* Unix port on the host */
|
||||
int slirp_add_unix(Slirp *slirp, const char *unixsock,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* callback that will receive the data coming from the port */
|
||||
int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
/* remove entries added by slirp_add_exec, slirp_add_unix or slirp_add_guestfwd */
|
||||
|
||||
/* TODO: rather identify a guestfwd through an opaque pointer instead of through
|
||||
* the guest_addr */
|
||||
|
||||
/* This is called by the application for a guestfwd, to determine how much data
|
||||
* can be received by the forwarded port through a call to slirp_socket_recv. */
|
||||
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
/* This is called by the application for a guestfwd, to provide the data to be
|
||||
* sent on the forwarded port */
|
||||
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
|
||||
const uint8_t *buf, int size);
|
||||
|
||||
/* Remove entries added by slirp_add_exec, slirp_add_unix or slirp_add_guestfwd */
|
||||
int slirp_remove_guestfwd(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
|
||||
/* Return a human-readable state of the slirp stack */
|
||||
char *slirp_connection_info(Slirp *slirp);
|
||||
|
||||
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
|
||||
const uint8_t *buf, int size);
|
||||
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
/* Return a human-readable state of the NDP/ARP tables */
|
||||
char *slirp_neighbor_info(Slirp *slirp);
|
||||
|
||||
/* Save the slirp state through the write_cb. The opaque pointer is passed as
|
||||
* such to the write_cb. */
|
||||
void slirp_state_save(Slirp *s, SlirpWriteCb write_cb, void *opaque);
|
||||
|
||||
/* Returns the version of the slirp state, to be saved along the state */
|
||||
int slirp_state_version(void);
|
||||
|
||||
/* Load the slirp state through the read_cb. The opaque pointer is passed as
|
||||
* such to the read_cb. The version should be given as it was obtained from
|
||||
* slirp_state_version when slirp_state_save was called. */
|
||||
int slirp_state_load(Slirp *s, int version_id, SlirpReadCb read_cb,
|
||||
void *opaque);
|
||||
|
||||
int slirp_state_version(void);
|
||||
|
||||
/* Return the version of the slirp implementation */
|
||||
const char *slirp_version_string(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -124,6 +124,8 @@ extern gboolean g_spawn_async_with_fds(const gchar *working_directory, gchar **a
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
gpointer user_data, GPid *child_pid, gint stdin_fd,
|
||||
gint stdout_fd, gint stderr_fd, GError **error);
|
||||
extern gboolean g_str_has_prefix (const gchar *str,
|
||||
const gchar *prefix);
|
||||
extern GString *g_string_new(gchar *base);
|
||||
extern gchar *g_string_free(GString *string, gboolean free_segment);
|
||||
extern gchar *g_strstr_len(const gchar *haystack, gssize haystack_len, const gchar *needle);
|
||||
|
@@ -493,15 +493,11 @@ const netdrv_t net_slirp_drv = {
|
||||
/* Stubs to stand in for the parts of libslirp we skip compiling. */
|
||||
void ncsi_input(void *slirp, const uint8_t *pkt, int pkt_len) {}
|
||||
void ip6_init(void *slirp) {}
|
||||
void ip6_cleanup(void *slirp) {}
|
||||
void ip6_input(void *m) {}
|
||||
int ip6_output(void *so, void *m, int fast) { return 0; }
|
||||
void in6_compute_ethaddr(struct in6_addr ip, uint8_t *eth) {}
|
||||
bool in6_equal(const void *a, const void *b) { return 0; }
|
||||
const struct in6_addr in6addr_any = { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
|
||||
const struct in6_addr in6addr_loopback = { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
|
||||
int udp6_output(void *so, void *m, void *saddr, void *daddr) { return 0; }
|
||||
void icmp6_send_error(void *m, uint8_t type, uint8_t code) {}
|
||||
void ndp_send_ns(void *slirp, struct in6_addr addr) {}
|
||||
void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr,
|
||||
uint8_t ethaddr[6]) {} /* IPv6 */
|
||||
bool ndp_table_search(void *slirp, struct in6_addr ip_addr, uint8_t *out_ethaddr) { return 0; }
|
||||
void tftp_input(void *srcsas, void *m) {}
|
||||
|
@@ -13,17 +13,22 @@
|
||||
# Copyright 2020-2021 David Hrdlička.
|
||||
#
|
||||
|
||||
add_library(slirp STATIC arp_table.c bootp.c cksum.c dnssearch.c if.c
|
||||
add_library(slirp STATIC arp_table.c bootp.c cksum.c dhcpv6.c dnssearch.c if.c
|
||||
ip_icmp.c ip_input.c ip_output.c
|
||||
ip6_icmp.c ip6_input.c ip6_output.c
|
||||
mbuf.c misc.c sbuf.c slirp.c socket.c
|
||||
tcp_input.c tcp_output.c tcp_subr.c tcp_timer.c
|
||||
udp.c
|
||||
udp.c udp6.c
|
||||
util.c version.c)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(slirp wsock32 iphlpapi)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(slirp resolv)
|
||||
endif()
|
||||
|
||||
# tinyglib conflicts with the real GLib used by Qt, let's just be safe
|
||||
if(QT AND UNIX AND NOT APPLE)
|
||||
set(SLIRP_TINYGLIB OFF)
|
||||
|
@@ -34,11 +34,16 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr,
|
||||
~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr;
|
||||
ArpTable *arptbl = &slirp->arp_table;
|
||||
int i;
|
||||
/*
|
||||
char ethaddr_str[ETH_ADDRSTRLEN];
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
|
||||
DEBUG_CALL("arp_table_add");
|
||||
DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){ .s_addr = ip_addr }));
|
||||
DEBUG_ARG("hw addr = %02x:%02x:%02x:%02x:%02x:%02x", ethaddr[0], ethaddr[1],
|
||||
ethaddr[2], ethaddr[3], ethaddr[4], ethaddr[5]);
|
||||
DEBUG_ARG("ip = %s", inet_ntop(AF_INET, &(struct in_addr){ .s_addr = ip_addr },
|
||||
addr, sizeof(addr)));
|
||||
DEBUG_ARG("hw addr = %s", slirp_ether_ntoa(ethaddr, ethaddr_str,
|
||||
sizeof(ethaddr_str)));
|
||||
*/
|
||||
|
||||
if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) {
|
||||
/* Do not register broadcast addresses */
|
||||
@@ -67,9 +72,14 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
|
||||
~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr;
|
||||
ArpTable *arptbl = &slirp->arp_table;
|
||||
int i;
|
||||
/*
|
||||
char ethaddr_str[ETH_ADDRSTRLEN];
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
|
||||
DEBUG_CALL("arp_table_search");
|
||||
DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){ .s_addr = ip_addr }));
|
||||
DEBUG_ARG("ip = %s", inet_ntop(AF_INET, &(struct in_addr){ .s_addr = ip_addr },
|
||||
addr, sizeof(addr)));
|
||||
*/
|
||||
|
||||
/* If broadcast address */
|
||||
if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) {
|
||||
@@ -81,9 +91,11 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
|
||||
for (i = 0; i < ARP_TABLE_SIZE; i++) {
|
||||
if (arptbl->table[i].ar_sip == ip_addr) {
|
||||
memcpy(out_ethaddr, arptbl->table[i].ar_sha, ETH_ALEN);
|
||||
DEBUG_ARG("found hw addr = %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
|
||||
out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]);
|
||||
/*
|
||||
DEBUG_ARG("found hw addr = %s",
|
||||
slirp_ether_ntoa(out_ethaddr, ethaddr_str,
|
||||
sizeof(ethaddr_str)));
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@@ -34,6 +34,8 @@
|
||||
|
||||
#define LEASE_TIME (24 * 3600)
|
||||
|
||||
#define UEFI_HTTP_VENDOR_CLASS_ID "HTTPClient"
|
||||
|
||||
static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
|
||||
|
||||
#define DPRINTF(fmt, ...) DEBUG_CALL(fmt, ##__VA_ARGS__)
|
||||
@@ -92,21 +94,22 @@ found:
|
||||
return bc;
|
||||
}
|
||||
|
||||
static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
|
||||
static void dhcp_decode(const struct bootp_t *bp,
|
||||
const uint8_t *bp_end,
|
||||
int *pmsg_type,
|
||||
struct in_addr *preq_addr)
|
||||
{
|
||||
const uint8_t *p, *p_end;
|
||||
const uint8_t *p;
|
||||
int len, tag;
|
||||
|
||||
*pmsg_type = 0;
|
||||
preq_addr->s_addr = htonl(0L);
|
||||
|
||||
p = bp->bp_vend;
|
||||
p_end = p + DHCP_OPT_LEN;
|
||||
if (memcmp(p, rfc1533_cookie, 4) != 0)
|
||||
return;
|
||||
p += 4;
|
||||
while (p < p_end) {
|
||||
while (p < bp_end) {
|
||||
tag = p[0];
|
||||
if (tag == RFC1533_PAD) {
|
||||
p++;
|
||||
@@ -114,10 +117,10 @@ static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
|
||||
break;
|
||||
} else {
|
||||
p++;
|
||||
if (p >= p_end)
|
||||
if (p >= bp_end)
|
||||
break;
|
||||
len = *p++;
|
||||
if (p + len > p_end) {
|
||||
if (p + len > bp_end) {
|
||||
break;
|
||||
}
|
||||
DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
|
||||
@@ -144,7 +147,9 @@ static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
|
||||
}
|
||||
}
|
||||
|
||||
static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
|
||||
static void bootp_reply(Slirp *slirp,
|
||||
const struct bootp_t *bp,
|
||||
const uint8_t *bp_end)
|
||||
{
|
||||
BOOTPClient *bc = NULL;
|
||||
struct mbuf *m;
|
||||
@@ -157,7 +162,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
|
||||
uint8_t client_ethaddr[ETH_ALEN];
|
||||
|
||||
/* extract exact DHCP msg type */
|
||||
dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
|
||||
dhcp_decode(bp, bp_end, &dhcp_msg_type, &preq_addr);
|
||||
DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
|
||||
if (preq_addr.s_addr != htonl(0L))
|
||||
DPRINTF(" req_addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr));
|
||||
@@ -179,9 +184,10 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
|
||||
return;
|
||||
}
|
||||
m->m_data += IF_MAXLINKHDR;
|
||||
m_inc(m, sizeof(struct bootp_t) + DHCP_OPT_LEN);
|
||||
rbp = (struct bootp_t *)m->m_data;
|
||||
m->m_data += sizeof(struct udpiphdr);
|
||||
memset(rbp, 0, sizeof(struct bootp_t));
|
||||
memset(rbp, 0, sizeof(struct bootp_t) + DHCP_OPT_LEN);
|
||||
|
||||
if (dhcp_msg_type == DHCPDISCOVER) {
|
||||
if (preq_addr.s_addr != htonl(0L)) {
|
||||
@@ -235,7 +241,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
|
||||
rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
|
||||
|
||||
q = rbp->bp_vend;
|
||||
end = (uint8_t *)&rbp[1];
|
||||
end = rbp->bp_vend + DHCP_OPT_LEN;
|
||||
memcpy(q, rfc1533_cookie, 4);
|
||||
q += 4;
|
||||
|
||||
@@ -336,6 +342,27 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
|
||||
q += val;
|
||||
}
|
||||
}
|
||||
|
||||
/* this allows to support UEFI HTTP boot: according to the UEFI
|
||||
specification, DHCP server must send vendor class identifier option
|
||||
set to "HTTPClient" string, when responding to DHCP requests as part
|
||||
of the UEFI HTTP boot
|
||||
|
||||
we assume that, if the bootfile parameter was configured as an http
|
||||
URL, the user intends to perform UEFI HTTP boot, so send this option
|
||||
automatically */
|
||||
if (slirp->bootp_filename && g_str_has_prefix(slirp->bootp_filename, "http://")) {
|
||||
val = strlen(UEFI_HTTP_VENDOR_CLASS_ID);
|
||||
if (q + val + 2 >= end) {
|
||||
g_warning("DHCP packet size exceeded, "
|
||||
"omitting vendor class id option.");
|
||||
} else {
|
||||
*q++ = RFC2132_VENDOR_CLASS_ID;
|
||||
*q++ = val;
|
||||
memcpy(q, UEFI_HTTP_VENDOR_CLASS_ID, val);
|
||||
q += val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
static const char nak_msg[] = "requested address not available";
|
||||
|
||||
@@ -351,19 +378,21 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
|
||||
q += sizeof(nak_msg) - 1;
|
||||
}
|
||||
assert(q < end);
|
||||
*q = RFC1533_END;
|
||||
*q++ = RFC1533_END;
|
||||
|
||||
daddr.sin_addr.s_addr = 0xffffffffu;
|
||||
|
||||
m->m_len = sizeof(struct bootp_t) - sizeof(struct ip) - sizeof(struct udphdr);
|
||||
assert(q <= end);
|
||||
|
||||
m->m_len = sizeof(struct bootp_t) + (end - rbp->bp_vend) - sizeof(struct ip) - sizeof(struct udphdr);
|
||||
udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
|
||||
}
|
||||
|
||||
void bootp_input(struct mbuf *m)
|
||||
{
|
||||
struct bootp_t *bp = mtod(m, struct bootp_t *);
|
||||
struct bootp_t *bp = mtod_check(m, sizeof(struct bootp_t));
|
||||
|
||||
if (bp->bp_op == BOOTP_REQUEST) {
|
||||
bootp_reply(m->slirp, bp);
|
||||
if (!m->slirp->disable_dhcp && bp && bp->bp_op == BOOTP_REQUEST) {
|
||||
bootp_reply(m->slirp, bp, m_end(m));
|
||||
}
|
||||
}
|
||||
|
@@ -71,6 +71,7 @@
|
||||
#define RFC2132_MAX_SIZE 57
|
||||
#define RFC2132_RENEWAL_TIME 58
|
||||
#define RFC2132_REBIND_TIME 59
|
||||
#define RFC2132_VENDOR_CLASS_ID 60
|
||||
#define RFC2132_TFTP_SERVER_NAME 66
|
||||
|
||||
#define DHCPDISCOVER 1
|
||||
@@ -114,7 +115,7 @@ struct bootp_t {
|
||||
uint8_t bp_hwaddr[16];
|
||||
uint8_t bp_sname[64];
|
||||
char bp_file[128];
|
||||
uint8_t bp_vend[DHCP_OPT_LEN];
|
||||
uint8_t bp_vend[];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@@ -47,7 +47,7 @@
|
||||
{ \
|
||||
l_util.l = sum; \
|
||||
sum = l_util.s[0] + l_util.s[1]; \
|
||||
(void)ADDCARRY(sum); \
|
||||
ADDCARRY(sum); \
|
||||
}
|
||||
|
||||
int cksum(struct mbuf *m, int len)
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#define DBG_MISC (1 << 1)
|
||||
#define DBG_ERROR (1 << 2)
|
||||
#define DBG_TFTP (1 << 3)
|
||||
#define DBG_VERBOSE_CALL (1 << 4)
|
||||
|
||||
extern int slirp_debug;
|
||||
|
||||
@@ -20,6 +21,13 @@ extern int slirp_debug;
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DEBUG_VERBOSE_CALL(fmt, ...) \
|
||||
do { \
|
||||
if (G_UNLIKELY(slirp_debug & DBG_VERBOSE_CALL)) { \
|
||||
g_debug(fmt "...", ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DEBUG_ARG(fmt, ...) \
|
||||
do { \
|
||||
if (G_UNLIKELY(slirp_debug & DBG_CALL)) { \
|
||||
|
@@ -41,6 +41,8 @@ void if_init(Slirp *slirp)
|
||||
void if_output(struct socket *so, struct mbuf *ifm)
|
||||
{
|
||||
Slirp *slirp = ifm->slirp;
|
||||
M_DUP_DEBUG(slirp, ifm, 0, 0);
|
||||
|
||||
struct mbuf *ifq;
|
||||
int on_fastq = 1;
|
||||
|
||||
@@ -54,7 +56,7 @@ void if_output(struct socket *so, struct mbuf *ifm)
|
||||
* XXX Shouldn't need this, gotta change dtom() etc.
|
||||
*/
|
||||
if (ifm->m_flags & M_USEDLIST) {
|
||||
remque(ifm);
|
||||
slirp_remque(ifm);
|
||||
ifm->m_flags &= ~M_USEDLIST;
|
||||
}
|
||||
|
||||
@@ -68,7 +70,8 @@ void if_output(struct socket *so, struct mbuf *ifm)
|
||||
*/
|
||||
if (so) {
|
||||
for (ifq = (struct mbuf *)slirp->if_batchq.qh_rlink;
|
||||
(struct quehead *)ifq != &slirp->if_batchq; ifq = ifq->ifq_prev) {
|
||||
(struct slirp_quehead *)ifq != &slirp->if_batchq;
|
||||
ifq = ifq->ifq_prev) {
|
||||
if (so == ifq->ifq_so) {
|
||||
/* A match! */
|
||||
ifm->ifq_so = so;
|
||||
@@ -98,7 +101,7 @@ void if_output(struct socket *so, struct mbuf *ifm)
|
||||
/* Create a new doubly linked list for this session */
|
||||
ifm->ifq_so = so;
|
||||
ifs_init(ifm);
|
||||
insque(ifm, ifq);
|
||||
slirp_insque(ifm, ifq);
|
||||
|
||||
diddit:
|
||||
if (so) {
|
||||
@@ -115,10 +118,10 @@ diddit:
|
||||
if (on_fastq &&
|
||||
((so->so_nqueued >= 6) && (so->so_nqueued - so->so_queued) >= 3)) {
|
||||
/* Remove from current queue... */
|
||||
remque(ifm->ifs_next);
|
||||
slirp_remque(ifm->ifs_next);
|
||||
|
||||
/* ...And insert in the new. That'll teach ya! */
|
||||
insque(ifm->ifs_next, &slirp->if_batchq);
|
||||
slirp_insque(ifm->ifs_next, &slirp->if_batchq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +146,7 @@ void if_start(Slirp *slirp)
|
||||
bool from_batchq = false;
|
||||
struct mbuf *ifm, *ifm_next, *ifqt;
|
||||
|
||||
DEBUG_CALL("if_start");
|
||||
DEBUG_VERBOSE_CALL("if_start");
|
||||
|
||||
if (slirp->if_start_busy) {
|
||||
return;
|
||||
@@ -169,12 +172,12 @@ void if_start(Slirp *slirp)
|
||||
ifm = ifm_next;
|
||||
|
||||
ifm_next = ifm->ifq_next;
|
||||
if ((struct quehead *)ifm_next == &slirp->if_fastq) {
|
||||
if ((struct slirp_quehead *)ifm_next == &slirp->if_fastq) {
|
||||
/* No more packets in fastq, switch to batchq */
|
||||
ifm_next = batch_head;
|
||||
from_batchq = true;
|
||||
}
|
||||
if ((struct quehead *)ifm_next == &slirp->if_batchq) {
|
||||
if ((struct slirp_quehead *)ifm_next == &slirp->if_batchq) {
|
||||
/* end of batchq */
|
||||
ifm_next = NULL;
|
||||
}
|
||||
@@ -187,13 +190,13 @@ void if_start(Slirp *slirp)
|
||||
|
||||
/* Remove it from the queue */
|
||||
ifqt = ifm->ifq_prev;
|
||||
remque(ifm);
|
||||
slirp_remque(ifm);
|
||||
|
||||
/* If there are more packets for this session, re-queue them */
|
||||
if (ifm->ifs_next != ifm) {
|
||||
struct mbuf *next = ifm->ifs_next;
|
||||
|
||||
insque(next, ifqt);
|
||||
slirp_insque(next, ifqt);
|
||||
ifs_remque(ifm);
|
||||
if (!from_batchq) {
|
||||
ifm_next = next;
|
||||
|
@@ -75,7 +75,7 @@ typedef uint32_t n_long; /* long as received from the net */
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ip {
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER)
|
||||
uint8_t ip_v : 4, /* version */
|
||||
ip_hl : 4; /* header length */
|
||||
#else
|
||||
@@ -147,7 +147,7 @@ struct ip_timestamp {
|
||||
uint8_t ipt_code; /* IPOPT_TS */
|
||||
uint8_t ipt_len; /* size of structure (variable) */
|
||||
uint8_t ipt_ptr; /* index of current entry */
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER)
|
||||
uint8_t ipt_oflw : 4, /* overflow counter */
|
||||
ipt_flg : 4; /* flags, see below */
|
||||
#else
|
||||
@@ -190,22 +190,28 @@ struct ip_timestamp {
|
||||
|
||||
#define IP_MSS 576 /* default maximum segment size */
|
||||
|
||||
#if GLIB_SIZEOF_VOID_P == 4
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
#if GLIB_SIZEOF_VOID_P == 4
|
||||
struct mbuf_ptr {
|
||||
struct mbuf *mptr;
|
||||
uint32_t dummy;
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
#else
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct mbuf_ptr {
|
||||
struct mbuf *mptr;
|
||||
} SLIRP_PACKED;
|
||||
#endif
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
#endif
|
||||
struct qlink {
|
||||
void *next, *prev;
|
||||
};
|
||||
|
@@ -176,7 +176,7 @@ static inline void in6_compute_ethaddr(struct in6_addr ip,
|
||||
* Structure of an internet header, naked of options.
|
||||
*/
|
||||
struct ip6 {
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER)
|
||||
uint32_t ip_v : 4, /* version */
|
||||
ip_tc_hi : 4, /* traffic class */
|
||||
ip_tc_lo : 4, ip_fl_hi : 4, /* flow label */
|
||||
|
@@ -10,25 +10,14 @@
|
||||
#define NDP_Interval \
|
||||
g_rand_int_range(slirp->grand, NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
|
||||
|
||||
static void ra_timer_handler(void *opaque)
|
||||
{
|
||||
Slirp *slirp = opaque;
|
||||
|
||||
slirp->cb->timer_mod(slirp->ra_timer,
|
||||
slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS +
|
||||
NDP_Interval,
|
||||
slirp->opaque);
|
||||
ndp_send_ra(slirp);
|
||||
}
|
||||
|
||||
void icmp6_init(Slirp *slirp)
|
||||
void icmp6_post_init(Slirp *slirp)
|
||||
{
|
||||
if (!slirp->in6_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
slirp->ra_timer =
|
||||
slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque);
|
||||
slirp_timer_new(slirp, SLIRP_TIMER_RA, NULL);
|
||||
slirp->cb->timer_mod(slirp->ra_timer,
|
||||
slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS +
|
||||
NDP_Interval,
|
||||
@@ -69,7 +58,7 @@ static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
|
||||
ip6_output(NULL, t, 0);
|
||||
}
|
||||
|
||||
void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
|
||||
void icmp6_forward_error(struct mbuf *m, uint8_t type, uint8_t code, struct in6_addr *src)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
struct mbuf *t;
|
||||
@@ -88,7 +77,7 @@ void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
|
||||
|
||||
/* IPv6 packet */
|
||||
struct ip6 *rip = mtod(t, struct ip6 *);
|
||||
rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
|
||||
rip->ip_src = *src;
|
||||
rip->ip_dst = ip->ip_src;
|
||||
inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
|
||||
DEBUG_ARG("target = %s", addrstr);
|
||||
@@ -131,10 +120,16 @@ void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
|
||||
ip6_output(NULL, t, 0);
|
||||
}
|
||||
|
||||
void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
|
||||
{
|
||||
struct in6_addr src = LINKLOCAL_ADDR;
|
||||
icmp6_forward_error(m, type, code, &src);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send NDP Router Advertisement
|
||||
*/
|
||||
void ndp_send_ra(Slirp *slirp)
|
||||
static void ndp_send_ra(Slirp *slirp)
|
||||
{
|
||||
DEBUG_CALL("ndp_send_ra");
|
||||
|
||||
@@ -213,6 +208,15 @@ void ndp_send_ra(Slirp *slirp)
|
||||
ip6_output(NULL, t, 0);
|
||||
}
|
||||
|
||||
void ra_timer_handler(Slirp *slirp, void *unused)
|
||||
{
|
||||
slirp->cb->timer_mod(slirp->ra_timer,
|
||||
slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS +
|
||||
NDP_Interval,
|
||||
slirp->opaque);
|
||||
ndp_send_ra(slirp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send NDP Neighbor Solitication
|
||||
*/
|
||||
@@ -315,6 +319,8 @@ static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
|
||||
static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
|
||||
struct icmp6 *icmp)
|
||||
{
|
||||
g_assert(M_ROOMBEFORE(m) >= ETH_HLEN);
|
||||
|
||||
m->m_len += ETH_HLEN;
|
||||
m->m_data -= ETH_HLEN;
|
||||
struct ethhdr *eth = mtod(m, struct ethhdr *);
|
||||
@@ -377,9 +383,12 @@ static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
|
||||
*/
|
||||
void icmp6_input(struct mbuf *m)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
/* NDP reads the ethernet header for gratuitous NDP */
|
||||
M_DUP_DEBUG(slirp, m, 1, ETH_HLEN);
|
||||
|
||||
struct icmp6 *icmp;
|
||||
struct ip6 *ip = mtod(m, struct ip6 *);
|
||||
Slirp *slirp = m->slirp;
|
||||
int hlen = sizeof(struct ip6);
|
||||
|
||||
DEBUG_CALL("icmp6_input");
|
||||
|
@@ -35,7 +35,7 @@ struct ndp_rs { /* Router Solicitation Message */
|
||||
|
||||
struct ndp_ra { /* Router Advertisement Message */
|
||||
uint8_t chl; /* Cur Hop Limit */
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER)
|
||||
uint8_t M : 1, O : 1, reserved : 6;
|
||||
#else
|
||||
uint8_t reserved : 6, O : 1, M : 1;
|
||||
@@ -55,7 +55,7 @@ struct ndp_ns { /* Neighbor Solicitation Message */
|
||||
G_STATIC_ASSERT(sizeof(struct ndp_ns) == 20);
|
||||
|
||||
struct ndp_na { /* Neighbor Advertisement Message */
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER)
|
||||
uint32_t R : 1, /* Router Flag */
|
||||
S : 1, /* Solicited Flag */
|
||||
O : 1, /* Override Flag */
|
||||
@@ -124,9 +124,12 @@ struct ndpopt {
|
||||
union {
|
||||
unsigned char linklayer_addr[6]; /* Source/Target Link-layer */
|
||||
#define ndpopt_linklayer ndpopt_body.linklayer_addr
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct prefixinfo { /* Prefix Information */
|
||||
uint8_t prefix_length;
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER)
|
||||
uint8_t L : 1, A : 1, reserved1 : 6;
|
||||
#else
|
||||
uint8_t reserved1 : 6, A : 1, L : 1;
|
||||
@@ -136,12 +139,21 @@ struct ndpopt {
|
||||
uint32_t reserved2;
|
||||
struct in6_addr prefix;
|
||||
} SLIRP_PACKED prefixinfo;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
#define ndpopt_prefixinfo ndpopt_body.prefixinfo
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct rdnss {
|
||||
uint16_t reserved;
|
||||
uint32_t lifetime;
|
||||
struct in6_addr addr;
|
||||
} SLIRP_PACKED rdnss;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
#define ndpopt_rdnss ndpopt_body.rdnss
|
||||
} ndpopt_body;
|
||||
} SLIRP_PACKED;
|
||||
@@ -215,11 +227,12 @@ struct ndpopt {
|
||||
#define NDP_AdvPrefLifetime 14400
|
||||
#define NDP_AdvAutonomousFlag 1
|
||||
|
||||
void icmp6_init(Slirp *slirp);
|
||||
void icmp6_post_init(Slirp *slirp);
|
||||
void icmp6_cleanup(Slirp *slirp);
|
||||
void icmp6_input(struct mbuf *);
|
||||
void icmp6_forward_error(struct mbuf *m, uint8_t type, uint8_t code, struct in6_addr *src);
|
||||
void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code);
|
||||
void ndp_send_ra(Slirp *slirp);
|
||||
void ndp_send_ns(Slirp *slirp, struct in6_addr addr);
|
||||
void ra_timer_handler(Slirp *slirp, void *unused);
|
||||
|
||||
#endif
|
||||
|
@@ -11,9 +11,9 @@
|
||||
* IP initialization: fill in IP protocol switch table.
|
||||
* All protocols not implemented in kernel go to raw IP protocol handler.
|
||||
*/
|
||||
void ip6_init(Slirp *slirp)
|
||||
void ip6_post_init(Slirp *slirp)
|
||||
{
|
||||
icmp6_init(slirp);
|
||||
icmp6_post_init(slirp);
|
||||
}
|
||||
|
||||
void ip6_cleanup(Slirp *slirp)
|
||||
@@ -23,8 +23,11 @@ void ip6_cleanup(Slirp *slirp)
|
||||
|
||||
void ip6_input(struct mbuf *m)
|
||||
{
|
||||
struct ip6 *ip6;
|
||||
Slirp *slirp = m->slirp;
|
||||
/* NDP reads the ethernet header for gratuitous NDP */
|
||||
M_DUP_DEBUG(slirp, m, 1, TCPIPHDR_DELTA + 2 + ETH_HLEN);
|
||||
|
||||
struct ip6 *ip6;
|
||||
|
||||
if (!slirp->in6_enabled) {
|
||||
goto bad;
|
||||
|
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
int ip6_output(struct socket *so, struct mbuf *m, int fast)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
M_DUP_DEBUG(slirp, m, 0, 0);
|
||||
|
||||
struct ip6 *ip = mtod(m, struct ip6 *);
|
||||
|
||||
DEBUG_CALL("ip6_output");
|
||||
@@ -30,7 +33,10 @@ int ip6_output(struct socket *so, struct mbuf *m, int fast)
|
||||
ip->ip_fl_lo = 0;
|
||||
|
||||
if (fast) {
|
||||
/* We cannot fast-send non-multicast, we'd need a NDP NS */
|
||||
assert(IN6_IS_ADDR_MULTICAST(&ip->ip_dst));
|
||||
if_encap(m->slirp, m);
|
||||
m_free(m);
|
||||
} else {
|
||||
if_output(so, m);
|
||||
}
|
||||
|
@@ -85,13 +85,41 @@ void icmp_cleanup(Slirp *slirp)
|
||||
|
||||
static int icmp_send(struct socket *so, struct mbuf *m, int hlen)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
M_DUP_DEBUG(slirp, m, 0, 0);
|
||||
|
||||
struct ip *ip = mtod(m, struct ip *);
|
||||
struct sockaddr_in addr;
|
||||
|
||||
/*
|
||||
* The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent
|
||||
* between host OSes. On Linux, only the ICMP header and payload is
|
||||
* included. On macOS/Darwin, the socket acts like a raw socket and
|
||||
* includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP
|
||||
* sockets aren't supported at all, so we treat them like raw sockets. It
|
||||
* isn't possible to detect this difference at runtime, so we must use an
|
||||
* #ifdef to determine if we need to remove the IP header.
|
||||
*/
|
||||
#ifdef CONFIG_BSD
|
||||
so->so_type = IPPROTO_IP;
|
||||
#else
|
||||
so->so_type = IPPROTO_ICMP;
|
||||
#endif
|
||||
|
||||
so->s = slirp_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
if (so->s == -1) {
|
||||
if (errno == EAFNOSUPPORT
|
||||
|| errno == EPROTONOSUPPORT
|
||||
|| errno == EACCES) {
|
||||
/* Kernel doesn't support or allow ping sockets. */
|
||||
so->so_type = IPPROTO_IP;
|
||||
so->s = slirp_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
}
|
||||
}
|
||||
if (so->s == -1) {
|
||||
return -1;
|
||||
}
|
||||
so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque);
|
||||
|
||||
if (slirp_bind_outbound(so, AF_INET) != 0) {
|
||||
// bind failed - close socket
|
||||
@@ -104,14 +132,13 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen)
|
||||
so->so_faddr = ip->ip_dst;
|
||||
so->so_laddr = ip->ip_src;
|
||||
so->so_iptos = ip->ip_tos;
|
||||
so->so_type = IPPROTO_ICMP;
|
||||
so->so_state = SS_ISFCONNECTED;
|
||||
so->so_expire = curtime + SO_EXPIRE;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr = so->so_faddr;
|
||||
|
||||
insque(so, &so->slirp->icmp);
|
||||
slirp_insque(so, &so->slirp->icmp);
|
||||
|
||||
if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0,
|
||||
(struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
@@ -136,10 +163,12 @@ void icmp_detach(struct socket *so)
|
||||
*/
|
||||
void icmp_input(struct mbuf *m, int hlen)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
M_DUP_DEBUG(slirp, m, 0, 0);
|
||||
|
||||
register struct icmp *icp;
|
||||
register struct ip *ip = mtod(m, struct ip *);
|
||||
int icmplen = ip->ip_len;
|
||||
Slirp *slirp = m->slirp;
|
||||
|
||||
DEBUG_CALL("icmp_input");
|
||||
DEBUG_ARG("m = %p", m);
|
||||
@@ -176,10 +205,17 @@ void icmp_input(struct mbuf *m, int hlen)
|
||||
} else {
|
||||
struct socket *so;
|
||||
struct sockaddr_storage addr;
|
||||
so = socreate(slirp);
|
||||
int ttl;
|
||||
|
||||
so = socreate(slirp, IPPROTO_ICMP);
|
||||
if (icmp_send(so, m, hlen) == 0) {
|
||||
/* We could send this as ICMP, good! */
|
||||
return;
|
||||
}
|
||||
|
||||
/* We could not send this as ICMP, try to send it on UDP echo
|
||||
* service (7), wishfully hoping that it is open there. */
|
||||
|
||||
if (udp_attach(so, AF_INET) == -1) {
|
||||
DEBUG_MISC("icmp_input udp_attach errno = %d-%s", errno,
|
||||
strerror(errno));
|
||||
@@ -195,7 +231,6 @@ void icmp_input(struct mbuf *m, int hlen)
|
||||
so->so_laddr = ip->ip_src;
|
||||
so->so_lport = htons(9);
|
||||
so->so_iptos = ip->ip_tos;
|
||||
so->so_type = IPPROTO_ICMP;
|
||||
so->so_state = SS_ISFCONNECTED;
|
||||
|
||||
/* Send the packet */
|
||||
@@ -207,6 +242,19 @@ void icmp_input(struct mbuf *m, int hlen)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for TTL
|
||||
*/
|
||||
ttl = ip->ip_ttl-1;
|
||||
if (ttl <= 0) {
|
||||
DEBUG_MISC("udp ttl exceeded");
|
||||
icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0,
|
||||
NULL);
|
||||
udp_detach(so);
|
||||
break;
|
||||
}
|
||||
setsockopt(so->s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
|
||||
|
||||
if (sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
|
||||
(struct sockaddr *)&addr, sockaddr_size(&addr)) == -1) {
|
||||
DEBUG_MISC("icmp_input udp sendto tx errno = %d-%s", errno,
|
||||
@@ -230,7 +278,7 @@ void icmp_input(struct mbuf *m, int hlen)
|
||||
|
||||
default:
|
||||
m_free(m);
|
||||
} /* swith */
|
||||
} /* switch */
|
||||
|
||||
end_error:
|
||||
/* m is m_free()'d xor put in a socket xor or given to ip_send */
|
||||
@@ -256,8 +304,8 @@ end_error:
|
||||
*/
|
||||
|
||||
#define ICMP_MAXDATALEN (IP_MSS - 28)
|
||||
void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
|
||||
const char *message)
|
||||
void icmp_forward_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
|
||||
const char *message, struct in_addr *src)
|
||||
{
|
||||
unsigned hlen, shlen, s_ip_len;
|
||||
register struct ip *ip;
|
||||
@@ -276,10 +324,12 @@ void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
|
||||
goto end_error;
|
||||
ip = mtod(msrc, struct ip *);
|
||||
if (slirp_debug & DBG_MISC) {
|
||||
char bufa[20], bufb[20];
|
||||
slirp_pstrcpy(bufa, sizeof(bufa), inet_ntoa(ip->ip_src));
|
||||
slirp_pstrcpy(bufb, sizeof(bufb), inet_ntoa(ip->ip_dst));
|
||||
DEBUG_MISC(" %.16s to %.16s", bufa, bufb);
|
||||
char addr_src[INET_ADDRSTRLEN];
|
||||
char addr_dst[INET_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET, &ip->ip_src, addr_src, sizeof(addr_src));
|
||||
inet_ntop(AF_INET, &ip->ip_dst, addr_dst, sizeof(addr_dst));
|
||||
DEBUG_MISC(" %.16s to %.16s", addr_src, addr_dst);
|
||||
}
|
||||
if (ip->ip_off & IP_OFFMASK)
|
||||
goto end_error; /* Only reply to fragment 0 */
|
||||
@@ -372,15 +422,21 @@ void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
|
||||
ip->ip_ttl = MAXTTL;
|
||||
ip->ip_p = IPPROTO_ICMP;
|
||||
ip->ip_dst = ip->ip_src; /* ip addresses */
|
||||
ip->ip_src = m->slirp->vhost_addr;
|
||||
ip->ip_src = *src;
|
||||
|
||||
(void)ip_output((struct socket *)NULL, m);
|
||||
ip_output((struct socket *)NULL, m);
|
||||
|
||||
end_error:
|
||||
return;
|
||||
}
|
||||
#undef ICMP_MAXDATALEN
|
||||
|
||||
void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
|
||||
const char *message)
|
||||
{
|
||||
icmp_forward_error(msrc, type, code, minsize, message, &msrc->slirp->vhost_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reflect the ip packet back to the source
|
||||
*/
|
||||
@@ -428,7 +484,7 @@ void icmp_reflect(struct mbuf *m)
|
||||
ip->ip_src = icmp_dst;
|
||||
}
|
||||
|
||||
(void)ip_output((struct socket *)NULL, m);
|
||||
ip_output((struct socket *)NULL, m);
|
||||
}
|
||||
|
||||
void icmp_receive(struct socket *so)
|
||||
@@ -446,31 +502,24 @@ void icmp_receive(struct socket *so)
|
||||
|
||||
id = icp->icmp_id;
|
||||
len = recv(so->s, icp, M_ROOM(m), 0);
|
||||
/*
|
||||
* The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent
|
||||
* between host OSes. On Linux, only the ICMP header and payload is
|
||||
* included. On macOS/Darwin, the socket acts like a raw socket and
|
||||
* includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP
|
||||
* sockets aren't supported at all, so we treat them like raw sockets. It
|
||||
* isn't possible to detect this difference at runtime, so we must use an
|
||||
* #ifdef to determine if we need to remove the IP header.
|
||||
*/
|
||||
#ifdef CONFIG_BSD
|
||||
if (len >= sizeof(struct ip)) {
|
||||
struct ip *inner_ip = mtod(m, struct ip *);
|
||||
int inner_hlen = inner_ip->ip_hl << 2;
|
||||
if (inner_hlen > len) {
|
||||
|
||||
if (so->so_type == IPPROTO_IP) {
|
||||
if (len >= sizeof(struct ip)) {
|
||||
struct ip *inner_ip = mtod(m, struct ip *);
|
||||
int inner_hlen = inner_ip->ip_hl << 2;
|
||||
if (inner_hlen > len) {
|
||||
len = -1;
|
||||
errno = -EINVAL;
|
||||
} else {
|
||||
len -= inner_hlen;
|
||||
memmove(icp, (unsigned char *)icp + inner_hlen, len);
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
errno = -EINVAL;
|
||||
} else {
|
||||
len -= inner_hlen;
|
||||
memmove(icp, (unsigned char *)icp + inner_hlen, len);
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
errno = -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
icp->icmp_id = id;
|
||||
|
||||
m->m_data -= hlen;
|
||||
|
@@ -157,6 +157,8 @@ struct icmp {
|
||||
void icmp_init(Slirp *slirp);
|
||||
void icmp_cleanup(Slirp *slirp);
|
||||
void icmp_input(struct mbuf *, int);
|
||||
void icmp_forward_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
|
||||
const char *message, struct in_addr *src);
|
||||
void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
|
||||
const char *message);
|
||||
void icmp_reflect(struct mbuf *);
|
||||
|
@@ -71,6 +71,8 @@ void ip_cleanup(Slirp *slirp)
|
||||
void ip_input(struct mbuf *m)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
M_DUP_DEBUG(slirp, m, 0, TCPIPHDR_DELTA);
|
||||
|
||||
register struct ip *ip;
|
||||
int hlen;
|
||||
|
||||
@@ -251,7 +253,7 @@ static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)
|
||||
goto dropfrag;
|
||||
}
|
||||
fp = mtod(t, struct ipq *);
|
||||
insque(&fp->ip_link, &slirp->ipq.ip_link);
|
||||
slirp_insque(&fp->ip_link, &slirp->ipq.ip_link);
|
||||
fp->ipq_ttl = IPFRAGTTL;
|
||||
fp->ipq_p = ip->ip_p;
|
||||
fp->ipq_id = ip->ip_id;
|
||||
@@ -348,7 +350,7 @@ insert:
|
||||
/*
|
||||
* If the fragments concatenated to an mbuf that's bigger than the total
|
||||
* size of the fragment and the mbuf was not already using an m_ext buffer,
|
||||
* then an m_ext buffer was alloced. But fp->ipq_next points to the old
|
||||
* then an m_ext buffer was allocated. But fp->ipq_next points to the old
|
||||
* buffer (in the mbuf), so we must point ip into the new buffer.
|
||||
*/
|
||||
if (m->m_flags & M_EXT) {
|
||||
@@ -360,8 +362,8 @@ insert:
|
||||
ip->ip_tos &= ~1;
|
||||
ip->ip_src = fp->ipq_src;
|
||||
ip->ip_dst = fp->ipq_dst;
|
||||
remque(&fp->ip_link);
|
||||
(void)m_free(dtom(slirp, fp));
|
||||
slirp_remque(&fp->ip_link);
|
||||
m_free(dtom(slirp, fp));
|
||||
m->m_len += (ip->ip_hl << 2);
|
||||
m->m_data -= (ip->ip_hl << 2);
|
||||
|
||||
@@ -386,13 +388,13 @@ static void ip_freef(Slirp *slirp, struct ipq *fp)
|
||||
ip_deq(q);
|
||||
m_free(dtom(slirp, q));
|
||||
}
|
||||
remque(&fp->ip_link);
|
||||
(void)m_free(dtom(slirp, fp));
|
||||
slirp_remque(&fp->ip_link);
|
||||
m_free(dtom(slirp, fp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Put an ip fragment on a reassembly chain.
|
||||
* Like insque, but pointers in middle of structure.
|
||||
* Like slirp_insque, but pointers in middle of structure.
|
||||
*/
|
||||
static void ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev)
|
||||
{
|
||||
@@ -405,7 +407,7 @@ static void ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev)
|
||||
}
|
||||
|
||||
/*
|
||||
* To ip_enq as remque is to insque.
|
||||
* To ip_enq as slirp_remque is to slirp_insque.
|
||||
*/
|
||||
static void ip_deq(register struct ipasfrag *p)
|
||||
{
|
||||
|
@@ -51,6 +51,8 @@
|
||||
int ip_output(struct socket *so, struct mbuf *m0)
|
||||
{
|
||||
Slirp *slirp = m0->slirp;
|
||||
M_DUP_DEBUG(slirp, m0, 0, 0);
|
||||
|
||||
register struct ip *ip;
|
||||
register struct mbuf *m = m0;
|
||||
register int hlen = sizeof(struct ip);
|
||||
|
@@ -7,9 +7,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define SLIRP_MAJOR_VERSION 4
|
||||
#define SLIRP_MINOR_VERSION 3
|
||||
#define SLIRP_MICRO_VERSION 1
|
||||
#define SLIRP_VERSION_STRING "4.3.1-git-86Box"
|
||||
#define SLIRP_MINOR_VERSION 7
|
||||
#define SLIRP_MICRO_VERSION 0
|
||||
#define SLIRP_VERSION_STRING "4.7.0-86Box"
|
||||
|
||||
#define SLIRP_CHECK_VERSION(major,minor,micro) \
|
||||
(SLIRP_MAJOR_VERSION > (major) || \
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <in6addr.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
@@ -31,8 +32,10 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Opaque structure containing the slirp state */
|
||||
typedef struct Slirp Slirp;
|
||||
|
||||
/* Flags passed to SlirpAddPollCb and to be returned by SlirpGetREventsCb. */
|
||||
enum {
|
||||
SLIRP_POLL_IN = 1 << 0,
|
||||
SLIRP_POLL_OUT = 1 << 1,
|
||||
@@ -47,38 +50,58 @@ typedef void (*SlirpTimerCb)(void *opaque);
|
||||
typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque);
|
||||
typedef int (*SlirpGetREventsCb)(int idx, void *opaque);
|
||||
|
||||
typedef enum SlirpTimerId {
|
||||
SLIRP_TIMER_RA,
|
||||
SLIRP_TIMER_NUM,
|
||||
} SlirpTimerId;
|
||||
|
||||
/*
|
||||
* Callbacks from slirp
|
||||
* Callbacks from slirp, to be set by the application.
|
||||
*
|
||||
* The opaque parameter is set to the opaque pointer given in the slirp_new /
|
||||
* slirp_init call.
|
||||
*/
|
||||
typedef struct SlirpCb {
|
||||
/*
|
||||
* Send an ethernet frame to the guest network. The opaque
|
||||
* parameter is the one given to slirp_init(). The function
|
||||
* doesn't need to send all the data and may return <len (no
|
||||
* buffering is done on libslirp side, so the data will be dropped
|
||||
* in this case). <0 reports an IO error.
|
||||
* Send an ethernet frame to the guest network. The opaque parameter is the
|
||||
* one given to slirp_init(). If the guest is not ready to receive a frame,
|
||||
* the function can just drop the data. TCP will then handle retransmissions
|
||||
* at a lower pace.
|
||||
* <0 reports an IO error.
|
||||
*/
|
||||
SlirpWriteCb send_packet;
|
||||
/* Print a message for an error due to guest misbehavior. */
|
||||
void (*guest_error)(const char *msg, void *opaque);
|
||||
/* Return the virtual clock value in nanoseconds */
|
||||
int64_t (*clock_get_ns)(void *opaque);
|
||||
/* Create a new timer with the given callback and opaque data */
|
||||
/* Create a new timer with the given callback and opaque data. Not
|
||||
* needed if timer_new_opaque is provided. */
|
||||
void *(*timer_new)(SlirpTimerCb cb, void *cb_opaque, void *opaque);
|
||||
/* Remove and free a timer */
|
||||
void (*timer_free)(void *timer, void *opaque);
|
||||
/* Modify a timer to expire at @expire_time */
|
||||
/* Modify a timer to expire at @expire_time (ms) */
|
||||
void (*timer_mod)(void *timer, int64_t expire_time, void *opaque);
|
||||
/* Register a fd for future polling */
|
||||
void (*register_poll_fd)(int fd, void *opaque);
|
||||
/* Unregister a fd */
|
||||
void (*unregister_poll_fd)(int fd, void *opaque);
|
||||
/* Kick the io-thread, to signal that new events may be processed */
|
||||
/* Kick the io-thread, to signal that new events may be processed because some TCP buffer
|
||||
* can now receive more data, i.e. slirp_socket_can_recv will return 1. */
|
||||
void (*notify)(void *opaque);
|
||||
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 4 begin
|
||||
*/
|
||||
|
||||
/* Initialization has completed and a Slirp* has been created. */
|
||||
void (*init_completed)(Slirp *slirp, void *opaque);
|
||||
/* Create a new timer. When the timer fires, the application passes
|
||||
* the SlirpTimerId and cb_opaque to slirp_handle_timer. */
|
||||
void *(*timer_new_opaque)(SlirpTimerId id, void *cb_opaque, void *opaque);
|
||||
} SlirpCb;
|
||||
|
||||
#define SLIRP_CONFIG_VERSION_MIN 1
|
||||
#define SLIRP_CONFIG_VERSION_MAX 3
|
||||
#define SLIRP_CONFIG_VERSION_MAX 4
|
||||
|
||||
typedef struct SlirpConfig {
|
||||
/* Version must be provided */
|
||||
@@ -124,8 +147,13 @@ typedef struct SlirpConfig {
|
||||
* Fields introduced in SlirpConfig version 3 begin
|
||||
*/
|
||||
bool disable_dns; /* slirp will not redirect/serve any DNS packet */
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 4 begin
|
||||
*/
|
||||
bool disable_dhcp; /* slirp will not reply to any DHCP requests */
|
||||
} SlirpConfig;
|
||||
|
||||
/* Create a new instance of a slirp stack */
|
||||
Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks,
|
||||
void *opaque);
|
||||
/* slirp_init is deprecated in favor of slirp_new */
|
||||
@@ -139,44 +167,103 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
|
||||
struct in6_addr vnameserver6, const char **vdnssearch,
|
||||
const char *vdomainname, const SlirpCb *callbacks,
|
||||
void *opaque);
|
||||
/* Shut down an instance of a slirp stack */
|
||||
void slirp_cleanup(Slirp *slirp);
|
||||
|
||||
/* This is called by the application when it is about to sleep through poll().
|
||||
* *timeout is set to the amount of virtual time (in ms) that the application intends to
|
||||
* wait (UINT32_MAX if infinite). slirp_pollfds_fill updates it according to
|
||||
* e.g. TCP timers, so the application knows it should sleep a smaller amount of
|
||||
* time. slirp_pollfds_fill calls add_poll for each file descriptor
|
||||
* that should be monitored along the sleep. The opaque pointer is passed as
|
||||
* such to add_poll, and add_poll returns an index. */
|
||||
void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout,
|
||||
SlirpAddPollCb add_poll, void *opaque);
|
||||
|
||||
/* This is called by the application after sleeping, to report which file
|
||||
* descriptors are available. slirp_pollfds_poll calls get_revents on each file
|
||||
* descriptor, giving it the index that add_poll returned during the
|
||||
* slirp_pollfds_fill call, to know whether the descriptor is available for
|
||||
* read/write/etc. (SLIRP_POLL_*)
|
||||
* select_error should be passed 1 if poll() returned an error. */
|
||||
void slirp_pollfds_poll(Slirp *slirp, int select_error,
|
||||
SlirpGetREventsCb get_revents, void *opaque);
|
||||
|
||||
/* This is called by the application when the guest emits a packet on the
|
||||
* guest network, to be interpreted by slirp. */
|
||||
void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
|
||||
|
||||
/* This is called by the application when a timer expires, if it provides
|
||||
* the timer_new_opaque callback. It is not needed if the application only
|
||||
* uses timer_new. */
|
||||
void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque);
|
||||
|
||||
/* These set up / remove port forwarding between a host port in the real world
|
||||
* and the guest network. */
|
||||
int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
int host_port, struct in_addr guest_addr, int guest_port);
|
||||
int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
int host_port);
|
||||
|
||||
#define SLIRP_HOSTFWD_UDP 1
|
||||
#define SLIRP_HOSTFWD_V6ONLY 2
|
||||
int slirp_add_hostxfwd(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
const struct sockaddr *gaddr, socklen_t gaddrlen,
|
||||
int flags);
|
||||
int slirp_remove_hostxfwd(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
int flags);
|
||||
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* command running on the host */
|
||||
int slirp_add_exec(Slirp *slirp, const char *cmdline,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* Unix port on the host */
|
||||
int slirp_add_unix(Slirp *slirp, const char *unixsock,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* callback that will receive the data coming from the port */
|
||||
int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
/* remove entries added by slirp_add_exec, slirp_add_unix or slirp_add_guestfwd */
|
||||
|
||||
/* TODO: rather identify a guestfwd through an opaque pointer instead of through
|
||||
* the guest_addr */
|
||||
|
||||
/* This is called by the application for a guestfwd, to determine how much data
|
||||
* can be received by the forwarded port through a call to slirp_socket_recv. */
|
||||
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
/* This is called by the application for a guestfwd, to provide the data to be
|
||||
* sent on the forwarded port */
|
||||
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
|
||||
const uint8_t *buf, int size);
|
||||
|
||||
/* Remove entries added by slirp_add_exec, slirp_add_unix or slirp_add_guestfwd */
|
||||
int slirp_remove_guestfwd(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
|
||||
/* Return a human-readable state of the slirp stack */
|
||||
char *slirp_connection_info(Slirp *slirp);
|
||||
|
||||
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
|
||||
const uint8_t *buf, int size);
|
||||
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
/* Return a human-readable state of the NDP/ARP tables */
|
||||
char *slirp_neighbor_info(Slirp *slirp);
|
||||
|
||||
/* Save the slirp state through the write_cb. The opaque pointer is passed as
|
||||
* such to the write_cb. */
|
||||
void slirp_state_save(Slirp *s, SlirpWriteCb write_cb, void *opaque);
|
||||
|
||||
/* Returns the version of the slirp state, to be saved along the state */
|
||||
int slirp_state_version(void);
|
||||
|
||||
/* Load the slirp state through the read_cb. The opaque pointer is passed as
|
||||
* such to the read_cb. The version should be given as it was obtained from
|
||||
* slirp_state_version when slirp_state_save was called. */
|
||||
int slirp_state_load(Slirp *s, int version_id, SlirpReadCb read_cb,
|
||||
void *opaque);
|
||||
|
||||
int slirp_state_version(void);
|
||||
|
||||
/* Return the version of the slirp implementation */
|
||||
const char *slirp_version_string(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -30,12 +30,12 @@ void m_init(Slirp *slirp)
|
||||
slirp->m_usedlist.qh_link = slirp->m_usedlist.qh_rlink = &slirp->m_usedlist;
|
||||
}
|
||||
|
||||
void m_cleanup(Slirp *slirp)
|
||||
static void m_cleanup_list(struct slirp_quehead *list_head)
|
||||
{
|
||||
struct mbuf *m, *next;
|
||||
|
||||
m = (struct mbuf *)slirp->m_usedlist.qh_link;
|
||||
while ((struct quehead *)m != &slirp->m_usedlist) {
|
||||
m = (struct mbuf *)list_head->qh_link;
|
||||
while ((struct slirp_quehead *)m != list_head) {
|
||||
next = m->m_next;
|
||||
if (m->m_flags & M_EXT) {
|
||||
g_free(m->m_ext);
|
||||
@@ -43,12 +43,16 @@ void m_cleanup(Slirp *slirp)
|
||||
g_free(m);
|
||||
m = next;
|
||||
}
|
||||
m = (struct mbuf *)slirp->m_freelist.qh_link;
|
||||
while ((struct quehead *)m != &slirp->m_freelist) {
|
||||
next = m->m_next;
|
||||
g_free(m);
|
||||
m = next;
|
||||
}
|
||||
list_head->qh_link = list_head;
|
||||
list_head->qh_rlink = list_head;
|
||||
}
|
||||
|
||||
void m_cleanup(Slirp *slirp)
|
||||
{
|
||||
m_cleanup_list(&slirp->m_usedlist);
|
||||
m_cleanup_list(&slirp->m_freelist);
|
||||
m_cleanup_list(&slirp->if_batchq);
|
||||
m_cleanup_list(&slirp->if_fastq);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -66,19 +70,19 @@ struct mbuf *m_get(Slirp *slirp)
|
||||
|
||||
DEBUG_CALL("m_get");
|
||||
|
||||
if (slirp->m_freelist.qh_link == &slirp->m_freelist) {
|
||||
if (MBUF_DEBUG || slirp->m_freelist.qh_link == &slirp->m_freelist) {
|
||||
m = g_malloc(SLIRP_MSIZE(slirp->if_mtu));
|
||||
slirp->mbuf_alloced++;
|
||||
if (slirp->mbuf_alloced > MBUF_THRESH)
|
||||
if (MBUF_DEBUG || slirp->mbuf_alloced > MBUF_THRESH)
|
||||
flags = M_DOFREE;
|
||||
m->slirp = slirp;
|
||||
} else {
|
||||
m = (struct mbuf *)slirp->m_freelist.qh_link;
|
||||
remque(m);
|
||||
slirp_remque(m);
|
||||
}
|
||||
|
||||
/* Insert it in the used list */
|
||||
insque(m, &slirp->m_usedlist);
|
||||
slirp_insque(m, &slirp->m_usedlist);
|
||||
m->m_flags = (flags | M_USEDLIST);
|
||||
|
||||
/* Initialise it */
|
||||
@@ -101,11 +105,12 @@ void m_free(struct mbuf *m)
|
||||
if (m) {
|
||||
/* Remove from m_usedlist */
|
||||
if (m->m_flags & M_USEDLIST)
|
||||
remque(m);
|
||||
slirp_remque(m);
|
||||
|
||||
/* If it's M_EXT, free() it */
|
||||
if (m->m_flags & M_EXT) {
|
||||
g_free(m->m_ext);
|
||||
m->m_flags &= ~M_EXT;
|
||||
}
|
||||
/*
|
||||
* Either free() it or put it on the free list
|
||||
@@ -114,7 +119,7 @@ void m_free(struct mbuf *m)
|
||||
m->slirp->mbuf_alloced--;
|
||||
g_free(m);
|
||||
} else if ((m->m_flags & M_FREELIST) == 0) {
|
||||
insque(m, &m->slirp->m_freelist);
|
||||
slirp_insque(m, &m->slirp->m_freelist);
|
||||
m->m_flags = M_FREELIST; /* Clobber other flags */
|
||||
}
|
||||
} /* if(m) */
|
||||
@@ -209,7 +214,7 @@ struct mbuf *dtom(Slirp *slirp, void *dat)
|
||||
|
||||
/* bug corrected for M_EXT buffers */
|
||||
for (m = (struct mbuf *)slirp->m_usedlist.qh_link;
|
||||
(struct quehead *)m != &slirp->m_usedlist; m = m->m_next) {
|
||||
(struct slirp_quehead *)m != &slirp->m_usedlist; m = m->m_next) {
|
||||
if (m->m_flags & M_EXT) {
|
||||
if ((char *)dat >= m->m_ext && (char *)dat < (m->m_ext + m->m_size))
|
||||
return m;
|
||||
@@ -223,3 +228,55 @@ struct mbuf *dtom(Slirp *slirp, void *dat)
|
||||
|
||||
return (struct mbuf *)0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Duplicate the mbuf
|
||||
*
|
||||
* copy_header specifies whether the bytes before m_data should also be copied.
|
||||
* header_size specifies how many bytes are to be reserved before m_data.
|
||||
*/
|
||||
struct mbuf *m_dup(Slirp *slirp, struct mbuf *m,
|
||||
bool copy_header,
|
||||
size_t header_size)
|
||||
{
|
||||
struct mbuf *n;
|
||||
int mcopy_result;
|
||||
|
||||
/* The previous mbuf was supposed to have it already, we can check it along
|
||||
* the way */
|
||||
assert(M_ROOMBEFORE(m) >= header_size);
|
||||
|
||||
n = m_get(slirp);
|
||||
m_inc(n, m->m_len + header_size);
|
||||
|
||||
if (copy_header) {
|
||||
m->m_len += header_size;
|
||||
m->m_data -= header_size;
|
||||
mcopy_result = m_copy(n, m, 0, m->m_len + header_size);
|
||||
n->m_data += header_size;
|
||||
m->m_len -= header_size;
|
||||
m->m_data += header_size;
|
||||
} else {
|
||||
n->m_data += header_size;
|
||||
mcopy_result = m_copy(n, m, 0, m->m_len);
|
||||
}
|
||||
g_assert(mcopy_result == 0);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void *mtod_check(struct mbuf *m, size_t len)
|
||||
{
|
||||
if (m->m_len >= len) {
|
||||
return m->m_data;
|
||||
}
|
||||
|
||||
DEBUG_ERROR("mtod failed");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *m_end(struct mbuf *m)
|
||||
{
|
||||
return m->m_data + m->m_len;
|
||||
}
|
||||
|
@@ -73,6 +73,13 @@
|
||||
*/
|
||||
#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len)
|
||||
|
||||
/*
|
||||
* How much free room there is before m_data
|
||||
*/
|
||||
#define M_ROOMBEFORE(m) \
|
||||
(((m)->m_flags & M_EXT) ? (m)->m_data - (m)->m_ext \
|
||||
: (m)->m_data - (m)->m_dat)
|
||||
|
||||
struct mbuf {
|
||||
/* XXX should union some of these! */
|
||||
/* header at beginning of each mbuf: */
|
||||
@@ -117,11 +124,69 @@ void m_cat(register struct mbuf *, register struct mbuf *);
|
||||
void m_inc(struct mbuf *, int);
|
||||
void m_adj(struct mbuf *, int);
|
||||
int m_copy(struct mbuf *, struct mbuf *, int, int);
|
||||
struct mbuf *m_dup(Slirp *slirp, struct mbuf *m, bool copy_header, size_t header_size);
|
||||
struct mbuf *dtom(Slirp *, void *);
|
||||
void *mtod_check(struct mbuf *, size_t len);
|
||||
void *m_end(struct mbuf *);
|
||||
|
||||
static inline void ifs_init(struct mbuf *ifm)
|
||||
{
|
||||
ifm->ifs_next = ifm->ifs_prev = ifm;
|
||||
}
|
||||
|
||||
#ifdef SLIRP_DEBUG
|
||||
# define MBUF_DEBUG 1
|
||||
#else
|
||||
# ifdef HAVE_VALGRIND
|
||||
# include <valgrind/valgrind.h>
|
||||
# define MBUF_DEBUG RUNNING_ON_VALGRIND
|
||||
# else
|
||||
# define MBUF_DEBUG 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When a function is given an mbuf as well as the responsibility to free it, we
|
||||
* want valgrind etc. to properly identify the new responsible for the
|
||||
* free. Achieve this by making a new copy. For instance:
|
||||
*
|
||||
* f0(void) {
|
||||
* struct mbuf *m = m_get(slirp);
|
||||
* [...]
|
||||
* switch (something) {
|
||||
* case 1:
|
||||
* f1(m);
|
||||
* break;
|
||||
* case 2:
|
||||
* f2(m);
|
||||
* break;
|
||||
* [...]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* f1(struct mbuf *m) {
|
||||
* M_DUP_DEBUG(m->slirp, m);
|
||||
* [...]
|
||||
* m_free(m); // but author of f1 might be forgetting this
|
||||
* }
|
||||
*
|
||||
* f0 transfers the freeing responsibility to f1, f2, etc. Without the
|
||||
* M_DUP_DEBUG call in f1, valgrind would tell us that it is f0 where the buffer
|
||||
* was allocated, but it's difficult to know whether a leak is actually in f0,
|
||||
* or in f1, or in f2, etc. Duplicating the mbuf in M_DUP_DEBUG each time the
|
||||
* responsibility is transferred allows to immediately know where the leak
|
||||
* actually is.
|
||||
*/
|
||||
#define M_DUP_DEBUG(slirp, m, copy_header, header_size) do { \
|
||||
if (MBUF_DEBUG) { \
|
||||
struct mbuf *__n; \
|
||||
__n = m_dup((slirp), (m), (copy_header), (header_size)); \
|
||||
m_free(m); \
|
||||
(m) = __n; \
|
||||
} else { \
|
||||
(void) (slirp); (void) (copy_header); \
|
||||
g_assert(M_ROOMBEFORE(m) >= (header_size)); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
@@ -15,22 +15,26 @@ extern gsize g_strlcpy(gchar* dest, const gchar* src, gsize dest_size);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern inline void insque(void *a, void *b)
|
||||
#ifdef __APPLE__
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
extern inline void slirp_insque(void *a, void *b)
|
||||
{
|
||||
register struct quehead *element = (struct quehead *)a;
|
||||
register struct quehead *head = (struct quehead *)b;
|
||||
register struct slirp_quehead *element = (struct slirp_quehead *)a;
|
||||
register struct slirp_quehead *head = (struct slirp_quehead *)b;
|
||||
element->qh_link = head->qh_link;
|
||||
head->qh_link = (struct quehead *)element;
|
||||
element->qh_rlink = (struct quehead *)head;
|
||||
((struct quehead *)(element->qh_link))->qh_rlink =
|
||||
(struct quehead *)element;
|
||||
head->qh_link = (struct slirp_quehead *)element;
|
||||
element->qh_rlink = (struct slirp_quehead *)head;
|
||||
((struct slirp_quehead *)(element->qh_link))->qh_rlink =
|
||||
(struct slirp_quehead *)element;
|
||||
}
|
||||
|
||||
extern inline void remque(void *a)
|
||||
extern inline void slirp_remque(void *a)
|
||||
{
|
||||
register struct quehead *element = (struct quehead *)a;
|
||||
((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
|
||||
((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
|
||||
register struct slirp_quehead *element = (struct slirp_quehead *)a;
|
||||
((struct slirp_quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
|
||||
((struct slirp_quehead *)(element->qh_rlink))->qh_link = element->qh_link;
|
||||
element->qh_rlink = NULL;
|
||||
}
|
||||
|
||||
@@ -89,7 +93,7 @@ static int slirp_socketpair_with_oob(int sv[2])
|
||||
struct sockaddr_in addr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = 0,
|
||||
.sin_addr.s_addr = INADDR_ANY,
|
||||
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
|
||||
};
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
int ret, s;
|
||||
@@ -143,11 +147,21 @@ static void fork_exec_child_setup(gpointer data)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
setsid();
|
||||
|
||||
/* Unblock all signals and leave our exec()-ee to block what it wants */
|
||||
sigset_t ss;
|
||||
sigemptyset(&ss);
|
||||
sigprocmask(SIG_SETMASK, &ss, NULL);
|
||||
|
||||
/* POSIX is obnoxious about SIGCHLD specifically across exec() */
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2, 58, 0)
|
||||
typedef struct SlirpGSpawnFds {
|
||||
@@ -197,7 +211,9 @@ g_spawn_async_with_fds_slirp(const gchar *working_directory, gchar **argv,
|
||||
#define g_spawn_async_with_fds(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) \
|
||||
g_spawn_async_with_fds_slirp(wd, argv, env, f, c, d, p, ifd, ofd, efd, err)
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
int fork_exec(struct socket *so, const char *ex)
|
||||
{
|
||||
@@ -300,6 +316,7 @@ char *slirp_connection_info(Slirp *slirp)
|
||||
uint16_t dst_port;
|
||||
struct socket *so;
|
||||
const char *state;
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
char buf[20];
|
||||
|
||||
g_string_append_printf(str,
|
||||
@@ -329,10 +346,11 @@ char *slirp_connection_info(Slirp *slirp)
|
||||
}
|
||||
slirp_fmt0(buf, sizeof(buf), " TCP[%s]", state);
|
||||
g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s,
|
||||
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
|
||||
"*",
|
||||
src.sin_addr.s_addr ?
|
||||
inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*",
|
||||
ntohs(src.sin_port));
|
||||
g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr),
|
||||
g_string_append_printf(str, "%15s %5d %5d %5d\n",
|
||||
inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)),
|
||||
ntohs(dst_port), so->so_rcv.sb_cc,
|
||||
so->so_snd.sb_cc);
|
||||
}
|
||||
@@ -353,10 +371,11 @@ char *slirp_connection_info(Slirp *slirp)
|
||||
dst_port = so->so_fport;
|
||||
}
|
||||
g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s,
|
||||
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
|
||||
"*",
|
||||
src.sin_addr.s_addr ?
|
||||
inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*",
|
||||
ntohs(src.sin_port));
|
||||
g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr),
|
||||
g_string_append_printf(str, "%15s %5d %5d %5d\n",
|
||||
inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)),
|
||||
ntohs(dst_port), so->so_rcv.sb_cc,
|
||||
so->so_snd.sb_cc);
|
||||
}
|
||||
@@ -367,15 +386,58 @@ char *slirp_connection_info(Slirp *slirp)
|
||||
src.sin_addr = so->so_laddr;
|
||||
dst_addr = so->so_faddr;
|
||||
g_string_append_printf(str, "%-19s %3d %15s - ", buf, so->s,
|
||||
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) :
|
||||
"*");
|
||||
g_string_append_printf(str, "%15s - %5d %5d\n", inet_ntoa(dst_addr),
|
||||
src.sin_addr.s_addr ?
|
||||
inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*");
|
||||
g_string_append_printf(str, "%15s - %5d %5d\n",
|
||||
inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)),
|
||||
so->so_rcv.sb_cc, so->so_snd.sb_cc);
|
||||
}
|
||||
|
||||
return g_string_free(str, false);
|
||||
}
|
||||
|
||||
char *slirp_neighbor_info(Slirp *slirp)
|
||||
{
|
||||
GString *str = g_string_new(NULL);
|
||||
ArpTable *arp_table = &slirp->arp_table;
|
||||
NdpTable *ndp_table = &slirp->ndp_table;
|
||||
char ip_addr[INET6_ADDRSTRLEN];
|
||||
char eth_addr[ETH_ADDRSTRLEN];
|
||||
const char *ip;
|
||||
|
||||
g_string_append_printf(str, " %5s %-17s %s\n",
|
||||
"Table", "MacAddr", "IP Address");
|
||||
|
||||
for (int i = 0; i < ARP_TABLE_SIZE; ++i) {
|
||||
struct in_addr addr;
|
||||
addr.s_addr = arp_table->table[i].ar_sip;
|
||||
if (!addr.s_addr) {
|
||||
continue;
|
||||
}
|
||||
ip = inet_ntop(AF_INET, &addr, ip_addr, sizeof(ip_addr));
|
||||
g_assert(ip != NULL);
|
||||
g_string_append_printf(str, " %5s %-17s %s\n", "ARP",
|
||||
slirp_ether_ntoa(arp_table->table[i].ar_sha,
|
||||
eth_addr, sizeof(eth_addr)),
|
||||
ip);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NDP_TABLE_SIZE; ++i) {
|
||||
if (in6_zero(&ndp_table->table[i].ip_addr)) {
|
||||
continue;
|
||||
}
|
||||
ip = inet_ntop(AF_INET6, &ndp_table->table[i].ip_addr, ip_addr,
|
||||
sizeof(ip_addr));
|
||||
g_assert(ip != NULL);
|
||||
g_string_append_printf(str, " %5s %-17s %s\n", "NDP",
|
||||
slirp_ether_ntoa(ndp_table->table[i].eth_addr,
|
||||
eth_addr, sizeof(eth_addr)),
|
||||
ip);
|
||||
}
|
||||
|
||||
return g_string_free(str, false);
|
||||
}
|
||||
|
||||
int slirp_bind_outbound(struct socket *so, unsigned short af)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@@ -40,6 +40,9 @@
|
||||
#define __be32 uint32_t
|
||||
#define __be16 uint16_t
|
||||
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_pkt_hdr {
|
||||
unsigned char mc_id; /* Management controller ID */
|
||||
unsigned char revision; /* NCSI version - 0x01 */
|
||||
@@ -49,64 +52,118 @@ struct ncsi_pkt_hdr {
|
||||
unsigned char channel; /* Network controller ID */
|
||||
__be16 length; /* Payload length */
|
||||
__be32 reserved1[2]; /* Reserved */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_pkt_hdr {
|
||||
struct ncsi_pkt_hdr common; /* Common NCSI packet header */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_pkt_hdr {
|
||||
struct ncsi_pkt_hdr common; /* Common NCSI packet header */
|
||||
__be16 code; /* Response code */
|
||||
__be16 reason; /* Response reason */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_aen_pkt_hdr {
|
||||
struct ncsi_pkt_hdr common; /* Common NCSI packet header */
|
||||
unsigned char reserved2[3]; /* Reserved */
|
||||
unsigned char type; /* AEN packet type */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* NCSI common command packet */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[26];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Select Package */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_sp_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
unsigned char reserved[3]; /* Reserved */
|
||||
unsigned char hw_arbitration; /* HW arbitration */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Disable Channel */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_dc_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
unsigned char reserved[3]; /* Reserved */
|
||||
unsigned char ald; /* Allow link down */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Reset Channel */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_rc_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
__be32 reserved; /* Reserved */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* AEN Enable */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_ae_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
unsigned char reserved[3]; /* Reserved */
|
||||
@@ -114,18 +171,30 @@ struct ncsi_cmd_ae_pkt {
|
||||
__be32 mode; /* AEN working mode */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[18];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Set Link */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_sl_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
__be32 mode; /* Link working mode */
|
||||
__be32 oem_mode; /* OEM link mode */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[18];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Set VLAN Filter */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_svf_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
__be16 reserved; /* Reserved */
|
||||
@@ -135,18 +204,30 @@ struct ncsi_cmd_svf_pkt {
|
||||
unsigned char enable; /* Enable or disable */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[14];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Enable VLAN */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_ev_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
unsigned char reserved[3]; /* Reserved */
|
||||
unsigned char mode; /* VLAN filter mode */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Set MAC Address */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_sma_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
unsigned char mac[6]; /* MAC address */
|
||||
@@ -154,23 +235,38 @@ struct ncsi_cmd_sma_pkt {
|
||||
unsigned char at_e; /* Addr type and operation */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[18];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Enable Broadcast Filter */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_ebf_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
__be32 mode; /* Filter mode */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Enable Global Multicast Filter */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_cmd_egmf_pkt {
|
||||
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
|
||||
__be32 mode; /* Global MC mode */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Set NCSI Flow Control */
|
||||
struct ncsi_cmd_snfc_pkt {
|
||||
@@ -179,9 +275,15 @@ struct ncsi_cmd_snfc_pkt {
|
||||
unsigned char mode; /* Flow control mode */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get Link Status */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gls_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
__be32 status; /* Link status */
|
||||
@@ -189,9 +291,15 @@ struct ncsi_rsp_gls_pkt {
|
||||
__be32 oem_status; /* OEM link status */
|
||||
__be32 checksum;
|
||||
unsigned char pad[10];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get Version ID */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gvi_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
__be32 ncsi_version; /* NCSI version */
|
||||
@@ -202,9 +310,15 @@ struct ncsi_rsp_gvi_pkt {
|
||||
__be16 pci_ids[4]; /* PCI IDs */
|
||||
__be32 mf_id; /* Manufacture ID */
|
||||
__be32 checksum;
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get Capabilities */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gc_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
__be32 cap; /* Capabilities */
|
||||
@@ -220,9 +334,15 @@ struct ncsi_rsp_gc_pkt {
|
||||
unsigned char vlan_mode; /* VLAN mode */
|
||||
unsigned char channel_cnt; /* Channel count */
|
||||
__be32 checksum; /* Checksum */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get Parameters */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gp_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
unsigned char mac_cnt; /* Number of MAC addr */
|
||||
@@ -241,9 +361,15 @@ struct ncsi_rsp_gp_pkt {
|
||||
unsigned char mac[6]; /* Supported MAC addr */
|
||||
__be16 vlan; /* Supported VLAN tags */
|
||||
__be32 checksum; /* Checksum */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get Controller Packet Statistics */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gcps_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
__be32 cnt_hi; /* Counter cleared */
|
||||
@@ -288,9 +414,15 @@ struct ncsi_rsp_gcps_pkt {
|
||||
__be32 rx_runt_pkts; /* Rx error runt packets */
|
||||
__be32 rx_jabber_pkts; /* Rx error jabber packets */
|
||||
__be32 checksum; /* Checksum */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get NCSI Statistics */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gns_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
__be32 rx_cmds; /* Rx NCSI commands */
|
||||
@@ -301,9 +433,15 @@ struct ncsi_rsp_gns_pkt {
|
||||
__be32 tx_pkts; /* Tx NCSI packets */
|
||||
__be32 tx_aen_pkts; /* Tx AEN packets */
|
||||
__be32 checksum; /* Checksum */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get NCSI Pass-through Statistics */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gnpts_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
__be32 tx_pkts; /* Tx packets */
|
||||
@@ -316,45 +454,78 @@ struct ncsi_rsp_gnpts_pkt {
|
||||
__be32 rx_us_err; /* Rx undersize errors */
|
||||
__be32 rx_os_err; /* Rx oversize errors */
|
||||
__be32 checksum; /* Checksum */
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get package status */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gps_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
__be32 status; /* Hardware arbitration status */
|
||||
__be32 checksum;
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Get package UUID */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_rsp_gpuuid_pkt {
|
||||
struct ncsi_rsp_pkt_hdr rsp; /* Response header */
|
||||
unsigned char uuid[16]; /* UUID */
|
||||
__be32 checksum;
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* AEN: Link State Change */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_aen_lsc_pkt {
|
||||
struct ncsi_aen_pkt_hdr aen; /* AEN header */
|
||||
__be32 status; /* Link status */
|
||||
__be32 oem_status; /* OEM link status */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[14];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* AEN: Configuration Required */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_aen_cr_pkt {
|
||||
struct ncsi_aen_pkt_hdr aen; /* AEN header */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[22];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* AEN: Host Network Controller Driver Status Change */
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct ncsi_aen_hncdsc_pkt {
|
||||
struct ncsi_aen_pkt_hdr aen; /* AEN header */
|
||||
__be32 status; /* Status */
|
||||
__be32 checksum; /* Checksum */
|
||||
unsigned char pad[18];
|
||||
};
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* NCSI packet revision */
|
||||
#define NCSI_PKT_REVISION 0x01
|
||||
|
@@ -38,7 +38,7 @@
|
||||
|
||||
#include "ncsi-pkt.h"
|
||||
|
||||
static uint32_t ncsi_calculate_checksum(uint16_t *data, int len)
|
||||
static uint32_t ncsi_calculate_checksum(uint8_t *data, int len)
|
||||
{
|
||||
uint32_t checksum = 0;
|
||||
int i;
|
||||
@@ -47,8 +47,8 @@ static uint32_t ncsi_calculate_checksum(uint16_t *data, int len)
|
||||
* 32-bit unsigned sum of the NC-SI packet header and NC-SI packet
|
||||
* payload interpreted as a series of 16-bit unsigned integer values.
|
||||
*/
|
||||
for (i = 0; i < len / 2; i++) {
|
||||
checksum += htons(data[i]);
|
||||
for (i = 0; i < len; i += 2) {
|
||||
checksum += (((uint16_t) data[i]) << 8) + data[i+1];
|
||||
}
|
||||
|
||||
checksum = (~checksum + 1);
|
||||
@@ -148,6 +148,10 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
|
||||
uint32_t checksum;
|
||||
uint32_t *pchecksum;
|
||||
|
||||
if (pkt_len < ETH_HLEN + sizeof(struct ncsi_pkt_hdr)) {
|
||||
return; /* packet too short */
|
||||
}
|
||||
|
||||
memset(ncsi_reply, 0, sizeof(ncsi_reply));
|
||||
|
||||
memset(reh->h_dest, 0xff, ETH_ALEN);
|
||||
@@ -184,7 +188,7 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
|
||||
}
|
||||
|
||||
/* Add the optional checksum at the end of the frame. */
|
||||
checksum = ncsi_calculate_checksum((uint16_t *)rnh, ncsi_rsp_len);
|
||||
checksum = ncsi_calculate_checksum((uint8_t *)rnh, ncsi_rsp_len);
|
||||
pchecksum = (uint32_t *)((void *)rnh + ncsi_rsp_len);
|
||||
*pchecksum = htonl(checksum);
|
||||
ncsi_rsp_len += 4;
|
||||
|
@@ -12,13 +12,14 @@ void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr,
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
NdpTable *ndp_table = &slirp->ndp_table;
|
||||
int i;
|
||||
char ethaddr_str[ETH_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN);
|
||||
|
||||
DEBUG_CALL("ndp_table_add");
|
||||
DEBUG_ARG("ip = %s", addrstr);
|
||||
DEBUG_ARG("hw addr = %02x:%02x:%02x:%02x:%02x:%02x", ethaddr[0], ethaddr[1],
|
||||
ethaddr[2], ethaddr[3], ethaddr[4], ethaddr[5]);
|
||||
DEBUG_ARG("hw addr = %s", slirp_ether_ntoa(ethaddr, ethaddr_str,
|
||||
sizeof(ethaddr_str)));
|
||||
|
||||
if (IN6_IS_ADDR_MULTICAST(&ip_addr) || in6_zero(&ip_addr)) {
|
||||
/* Do not register multicast or unspecified addresses */
|
||||
@@ -38,6 +39,10 @@ void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr,
|
||||
|
||||
/* No entry found, create a new one */
|
||||
DEBUG_CALL(" create new entry");
|
||||
/* Save the first entry, it is the guest. */
|
||||
if (in6_zero(&ndp_table->guest_in6_addr)) {
|
||||
ndp_table->guest_in6_addr = ip_addr;
|
||||
}
|
||||
ndp_table->table[ndp_table->next_victim].ip_addr = ip_addr;
|
||||
memcpy(ndp_table->table[ndp_table->next_victim].eth_addr, ethaddr,
|
||||
ETH_ALEN);
|
||||
@@ -50,13 +55,19 @@ bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr,
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
NdpTable *ndp_table = &slirp->ndp_table;
|
||||
int i;
|
||||
char ethaddr_str[ETH_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN);
|
||||
|
||||
DEBUG_CALL("ndp_table_search");
|
||||
DEBUG_ARG("ip = %s", addrstr);
|
||||
|
||||
assert(!in6_zero(&ip_addr));
|
||||
/* If unspecified address */
|
||||
if (in6_zero(&ip_addr)) {
|
||||
/* return Ethernet broadcast address */
|
||||
memset(out_ethaddr, 0xff, ETH_ALEN);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Multicast address: fec0::abcd:efgh/8 -> 33:33:ab:cd:ef:gh */
|
||||
if (IN6_IS_ADDR_MULTICAST(&ip_addr)) {
|
||||
@@ -66,18 +77,18 @@ bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr,
|
||||
out_ethaddr[3] = ip_addr.s6_addr[13];
|
||||
out_ethaddr[4] = ip_addr.s6_addr[14];
|
||||
out_ethaddr[5] = ip_addr.s6_addr[15];
|
||||
DEBUG_ARG("multicast addr = %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
|
||||
out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]);
|
||||
DEBUG_ARG("multicast addr = %s",
|
||||
slirp_ether_ntoa(out_ethaddr, ethaddr_str,
|
||||
sizeof(ethaddr_str)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < NDP_TABLE_SIZE; i++) {
|
||||
if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) {
|
||||
memcpy(out_ethaddr, ndp_table->table[i].eth_addr, ETH_ALEN);
|
||||
DEBUG_ARG("found hw addr = %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
|
||||
out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]);
|
||||
DEBUG_ARG("found hw addr = %s",
|
||||
slirp_ether_ntoa(out_ethaddr, ethaddr_str,
|
||||
sizeof(ethaddr_str)));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ void sbappend(struct socket *so, struct mbuf *m)
|
||||
if (so->so_urgc) {
|
||||
sbappendsb(&so->so_rcv, m);
|
||||
m_free(m);
|
||||
(void)sosendoob(so);
|
||||
sosendoob(so);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -34,6 +34,37 @@
|
||||
#undef if_mtu
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#define INITIAL_DNS_ADDR_BUF_SIZE 32 * 1024
|
||||
#define REALLOC_RETRIES 5
|
||||
|
||||
// Broadcast site local DNS resolvers. We do not use these because they are
|
||||
// highly unlikely to be valid.
|
||||
// https://www.ietf.org/proceedings/52/I-D/draft-ietf-ipngwg-dns-discovery-03.txt
|
||||
static const struct in6_addr SITE_LOCAL_DNS_BROADCAST_ADDRS[] = {
|
||||
{
|
||||
{{
|
||||
0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
}}
|
||||
},
|
||||
{
|
||||
{{
|
||||
0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
|
||||
}}
|
||||
},
|
||||
{
|
||||
{{
|
||||
0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
}}
|
||||
},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
int slirp_debug;
|
||||
|
||||
/* Define to 1 if you want KEEPALIVE timers */
|
||||
@@ -51,20 +82,17 @@ static const uint8_t special_ethaddr[ETH_ALEN] = { 0x52, 0x55, 0x00,
|
||||
unsigned curtime;
|
||||
|
||||
static struct in_addr dns_addr;
|
||||
#ifndef _WIN32
|
||||
static struct in6_addr dns6_addr;
|
||||
#endif
|
||||
static uint32_t dns6_scope_id;
|
||||
static unsigned dns_addr_time;
|
||||
#ifndef _WIN32
|
||||
static unsigned dns6_addr_time;
|
||||
#endif
|
||||
|
||||
#define TIMEOUT_FAST 2 /* milliseconds */
|
||||
#define TIMEOUT_SLOW 499 /* milliseconds */
|
||||
/* for the aging of certain requests like DNS */
|
||||
#define TIMEOUT_DEFAULT 1000 /* milliseconds */
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32)
|
||||
|
||||
int get_dns_addr(struct in_addr *pdns_addr)
|
||||
{
|
||||
@@ -111,8 +139,108 @@ int get_dns_addr(struct in_addr *pdns_addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_site_local_dns_broadcast(struct in6_addr *address)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < G_N_ELEMENTS(SITE_LOCAL_DNS_BROADCAST_ADDRS); i++) {
|
||||
if (in6_equal(address, &SITE_LOCAL_DNS_BROADCAST_ADDRS[i])) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_dns_v6_address(struct in6_addr address)
|
||||
{
|
||||
char address_str[INET6_ADDRSTRLEN] = "";
|
||||
if (inet_ntop(AF_INET6, &address, address_str, INET6_ADDRSTRLEN)
|
||||
== NULL) {
|
||||
DEBUG_ERROR("Failed to stringify IPv6 address for logging.");
|
||||
return;
|
||||
}
|
||||
DEBUG_CALL("IPv6 DNS server found: %s", address_str);
|
||||
}
|
||||
|
||||
// Gets the first valid DNS resolver with an IPv6 address.
|
||||
// Ignores any site local broadcast DNS servers, as these
|
||||
// are on deprecated addresses and not generally expected
|
||||
// to work. Further details at:
|
||||
// https://www.ietf.org/proceedings/52/I-D/draft-ietf-ipngwg-dns-discovery-03.txt
|
||||
int get_ipv6_dns_server(struct in6_addr *dns_server_address, uint32_t *scope_id)
|
||||
{
|
||||
PIP_ADAPTER_ADDRESSES addresses = NULL;
|
||||
PIP_ADAPTER_ADDRESSES address = NULL;
|
||||
IP_ADAPTER_DNS_SERVER_ADDRESS *dns_server = NULL;
|
||||
struct sockaddr_in6 *dns_v6_addr = NULL;
|
||||
|
||||
ULONG buf_size = INITIAL_DNS_ADDR_BUF_SIZE;
|
||||
DWORD res = ERROR_BUFFER_OVERFLOW;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < REALLOC_RETRIES; i++) {
|
||||
// If non null, we hit buffer overflow, free it so we can try again.
|
||||
if (addresses != NULL) {
|
||||
g_free(addresses);
|
||||
}
|
||||
|
||||
addresses = g_malloc(buf_size);
|
||||
res = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL,
|
||||
addresses, &buf_size);
|
||||
|
||||
if (res != ERROR_BUFFER_OVERFLOW) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res != NO_ERROR) {
|
||||
DEBUG_ERROR("Failed to get IPv6 DNS addresses due to error %lX", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
address = addresses;
|
||||
for (address = addresses; address != NULL; address = address->Next) {
|
||||
for (dns_server = address->FirstDnsServerAddress;
|
||||
dns_server != NULL;
|
||||
dns_server = dns_server->Next) {
|
||||
|
||||
if (dns_server->Address.lpSockaddr->sa_family != AF_INET6) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dns_v6_addr = (struct sockaddr_in6 *)dns_server->Address.lpSockaddr;
|
||||
if (is_site_local_dns_broadcast(&dns_v6_addr->sin6_addr) == 0) {
|
||||
print_dns_v6_address(dns_v6_addr->sin6_addr);
|
||||
*dns_server_address = dns_v6_addr->sin6_addr;
|
||||
*scope_id = dns_v6_addr->sin6_scope_id;
|
||||
|
||||
g_free(addresses);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_ERROR("No IPv6 DNS servers found.\n");
|
||||
|
||||
failure:
|
||||
g_free(addresses);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
|
||||
{
|
||||
if (!in6_zero(&dns6_addr) && (curtime - dns6_addr_time) < TIMEOUT_DEFAULT) {
|
||||
*pdns6_addr = dns6_addr;
|
||||
*scope_id = dns6_scope_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (get_ipv6_dns_server(pdns6_addr, scope_id) == 0) {
|
||||
dns6_addr = *pdns6_addr;
|
||||
dns6_addr_time = curtime;
|
||||
dns6_scope_id = *scope_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -121,7 +249,122 @@ static void winsock_cleanup(void)
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include <resolv.h>
|
||||
|
||||
static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,
|
||||
socklen_t addrlen, unsigned *cached_time)
|
||||
{
|
||||
if (curtime - *cached_time < TIMEOUT_DEFAULT) {
|
||||
memcpy(pdns_addr, cached_addr, addrlen);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_dns_addr_libresolv(int af, void *pdns_addr, void *cached_addr,
|
||||
socklen_t addrlen,
|
||||
uint32_t *scope_id, uint32_t *cached_scope_id,
|
||||
unsigned *cached_time)
|
||||
{
|
||||
struct __res_state state;
|
||||
union res_sockaddr_union servers[NI_MAXSERV];
|
||||
int count;
|
||||
int found;
|
||||
void *addr;
|
||||
|
||||
// we only support IPv4 and IPv4, we assume it's one or the other
|
||||
assert(af == AF_INET || af == AF_INET6);
|
||||
|
||||
if (res_ninit(&state) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
count = res_getservers(&state, servers, NI_MAXSERV);
|
||||
found = 0;
|
||||
DEBUG_MISC("IP address of your DNS(s):");
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (af == servers[i].sin.sin_family) {
|
||||
found++;
|
||||
}
|
||||
if (af == AF_INET) {
|
||||
addr = &servers[i].sin.sin_addr;
|
||||
} else { // af == AF_INET6
|
||||
addr = &servers[i].sin6.sin6_addr;
|
||||
}
|
||||
|
||||
// we use the first found entry
|
||||
if (found == 1) {
|
||||
memcpy(pdns_addr, addr, addrlen);
|
||||
memcpy(cached_addr, addr, addrlen);
|
||||
if (scope_id) {
|
||||
*scope_id = 0;
|
||||
}
|
||||
if (cached_scope_id) {
|
||||
*cached_scope_id = 0;
|
||||
}
|
||||
*cached_time = curtime;
|
||||
}
|
||||
|
||||
if (found > 3) {
|
||||
DEBUG_MISC(" (more)");
|
||||
break;
|
||||
} else if (slirp_debug & DBG_MISC) {
|
||||
char s[INET6_ADDRSTRLEN];
|
||||
const char *res = inet_ntop(af, addr, s, sizeof(s));
|
||||
if (!res) {
|
||||
res = " (string conversion error)";
|
||||
}
|
||||
DEBUG_MISC(" %s", res);
|
||||
}
|
||||
}
|
||||
|
||||
res_ndestroy(&state);
|
||||
if (!found)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_dns_addr(struct in_addr *pdns_addr)
|
||||
{
|
||||
if (dns_addr.s_addr != 0) {
|
||||
int ret;
|
||||
ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr),
|
||||
&dns_addr_time);
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return get_dns_addr_libresolv(AF_INET, pdns_addr, &dns_addr,
|
||||
sizeof(dns_addr), NULL, NULL, &dns_addr_time);
|
||||
}
|
||||
|
||||
int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
|
||||
{
|
||||
if (!in6_zero(&dns6_addr)) {
|
||||
int ret;
|
||||
ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr),
|
||||
&dns6_addr_time);
|
||||
if (ret == 0) {
|
||||
*scope_id = dns6_scope_id;
|
||||
}
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return get_dns_addr_libresolv(AF_INET6, pdns6_addr, &dns6_addr,
|
||||
sizeof(dns6_addr),
|
||||
scope_id, &dns6_scope_id, &dns6_addr_time);
|
||||
}
|
||||
|
||||
#else // !defined(_WIN32) && !defined(__APPLE__)
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
#define RESOLV_CONF_PATH "/boot/system/settings/network/resolv.conf"
|
||||
#else
|
||||
#define RESOLV_CONF_PATH "/etc/resolv.conf"
|
||||
#endif
|
||||
|
||||
static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,
|
||||
socklen_t addrlen, struct stat *cached_stat,
|
||||
@@ -133,7 +376,7 @@ static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,
|
||||
return 0;
|
||||
}
|
||||
old_stat = *cached_stat;
|
||||
if (stat("/etc/resolv.conf", cached_stat) != 0) {
|
||||
if (stat(RESOLV_CONF_PATH, cached_stat) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (cached_stat->st_dev == old_stat.st_dev &&
|
||||
@@ -147,17 +390,22 @@ static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,
|
||||
}
|
||||
|
||||
static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr,
|
||||
socklen_t addrlen, uint32_t *scope_id,
|
||||
socklen_t addrlen,
|
||||
uint32_t *scope_id, uint32_t *cached_scope_id,
|
||||
unsigned *cached_time)
|
||||
{
|
||||
char buff[512];
|
||||
char buff2[257];
|
||||
FILE *f;
|
||||
int found = 0;
|
||||
void *tmp_addr = alloca(addrlen);
|
||||
union {
|
||||
struct in_addr dns_addr;
|
||||
struct in6_addr dns6_addr;
|
||||
} tmp_addr;
|
||||
unsigned if_index;
|
||||
|
||||
f = fopen("/etc/resolv.conf", "r");
|
||||
assert(sizeof(tmp_addr) >= addrlen);
|
||||
f = fopen(RESOLV_CONF_PATH, "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
@@ -172,16 +420,19 @@ static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr,
|
||||
if_index = 0;
|
||||
}
|
||||
|
||||
if (!inet_pton(af, buff2, tmp_addr)) {
|
||||
if (!inet_pton(af, buff2, &tmp_addr)) {
|
||||
continue;
|
||||
}
|
||||
/* If it's the first one, set it to dns_addr */
|
||||
if (!found) {
|
||||
memcpy(pdns_addr, tmp_addr, addrlen);
|
||||
memcpy(cached_addr, tmp_addr, addrlen);
|
||||
memcpy(pdns_addr, &tmp_addr, addrlen);
|
||||
memcpy(cached_addr, &tmp_addr, addrlen);
|
||||
if (scope_id) {
|
||||
*scope_id = if_index;
|
||||
}
|
||||
if (cached_scope_id) {
|
||||
*cached_scope_id = if_index;
|
||||
}
|
||||
*cached_time = curtime;
|
||||
}
|
||||
|
||||
@@ -190,7 +441,7 @@ static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr,
|
||||
break;
|
||||
} else if (slirp_debug & DBG_MISC) {
|
||||
char s[INET6_ADDRSTRLEN];
|
||||
const char *res = inet_ntop(af, tmp_addr, s, sizeof(s));
|
||||
const char *res = inet_ntop(af, &tmp_addr, s, sizeof(s));
|
||||
if (!res) {
|
||||
res = " (string conversion error)";
|
||||
}
|
||||
@@ -217,7 +468,8 @@ int get_dns_addr(struct in_addr *pdns_addr)
|
||||
}
|
||||
}
|
||||
return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr,
|
||||
sizeof(dns_addr), NULL, &dns_addr_time);
|
||||
sizeof(dns_addr),
|
||||
NULL, NULL, &dns_addr_time);
|
||||
}
|
||||
|
||||
int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
|
||||
@@ -228,13 +480,16 @@ int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
|
||||
int ret;
|
||||
ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr),
|
||||
&dns6_addr_stat, &dns6_addr_time);
|
||||
if (ret == 0) {
|
||||
*scope_id = dns6_scope_id;
|
||||
}
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr,
|
||||
sizeof(dns6_addr), scope_id,
|
||||
&dns6_addr_time);
|
||||
sizeof(dns6_addr),
|
||||
scope_id, &dns6_scope_id, &dns6_addr_time);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -267,11 +522,49 @@ static void slirp_init_once(void)
|
||||
{ "misc", DBG_MISC },
|
||||
{ "error", DBG_ERROR },
|
||||
{ "tftp", DBG_TFTP },
|
||||
{ "verbose_call", DBG_VERBOSE_CALL },
|
||||
};
|
||||
slirp_debug = g_parse_debug_string(debug, keys, G_N_ELEMENTS(keys));
|
||||
}
|
||||
}
|
||||
|
||||
static void ra_timer_handler_cb(void *opaque)
|
||||
{
|
||||
Slirp *slirp = opaque;
|
||||
|
||||
return ra_timer_handler(slirp, NULL);
|
||||
}
|
||||
|
||||
void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque)
|
||||
{
|
||||
// g_return_if_fail(id >= 0 && id < SLIRP_TIMER_NUM);
|
||||
|
||||
switch (id) {
|
||||
case SLIRP_TIMER_RA:
|
||||
return ra_timer_handler(slirp, cb_opaque);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque)
|
||||
{
|
||||
g_return_val_if_fail(id >= 0 && id < SLIRP_TIMER_NUM, NULL);
|
||||
|
||||
if (slirp->cfg_version >= 4 && slirp->cb->timer_new_opaque) {
|
||||
return slirp->cb->timer_new_opaque(id, cb_opaque, slirp->opaque);
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case SLIRP_TIMER_RA:
|
||||
g_return_val_if_fail(cb_opaque == NULL, NULL);
|
||||
return slirp->cb->timer_new(ra_timer_handler_cb, slirp, slirp->opaque);
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque)
|
||||
{
|
||||
Slirp *slirp;
|
||||
@@ -291,6 +584,7 @@ Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque)
|
||||
|
||||
slirp_init_once();
|
||||
|
||||
slirp->cfg_version = cfg->version;
|
||||
slirp->opaque = opaque;
|
||||
slirp->cb = callbacks;
|
||||
slirp->grand = g_rand_new();
|
||||
@@ -301,7 +595,6 @@ Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque)
|
||||
|
||||
if_init(slirp);
|
||||
ip_init(slirp);
|
||||
ip6_init(slirp);
|
||||
|
||||
m_init(slirp);
|
||||
|
||||
@@ -345,6 +638,17 @@ Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque)
|
||||
slirp->disable_dns = false;
|
||||
}
|
||||
|
||||
if (cfg->version >= 4) {
|
||||
slirp->disable_dhcp = cfg->disable_dhcp;
|
||||
} else {
|
||||
slirp->disable_dhcp = false;
|
||||
}
|
||||
|
||||
if (slirp->cfg_version >= 4 && slirp->cb->init_completed) {
|
||||
slirp->cb->init_completed(slirp, slirp->opaque);
|
||||
}
|
||||
|
||||
ip6_post_init(slirp);
|
||||
return slirp;
|
||||
}
|
||||
|
||||
@@ -501,7 +805,10 @@ void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout,
|
||||
|
||||
/*
|
||||
* Set for reading (and urgent data) if we are connected, can
|
||||
* receive more, and we have room for it XXX /2 ?
|
||||
* receive more, and we have room for it.
|
||||
*
|
||||
* If sb is already half full, we will wait for the guest to consume it,
|
||||
* and notify again in sbdrop() when the sb becomes less than half full.
|
||||
*/
|
||||
if (CONN_CANFRCV(so) &&
|
||||
(so->so_snd.sb_cc < (so->so_snd.sb_datalen / 2))) {
|
||||
@@ -623,10 +930,16 @@ void slirp_pollfds_poll(Slirp *slirp, int select_error,
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
/*
|
||||
* Check for URG data
|
||||
* This will soread as well, so no need to
|
||||
* test for SLIRP_POLL_IN below if this succeeds
|
||||
* test for SLIRP_POLL_IN below if this succeeds.
|
||||
*
|
||||
* This is however disabled on MacOS, which apparently always
|
||||
* reports data as PRI when it is the last data of the
|
||||
* connection. We would then report it out of band, which the guest
|
||||
* would most probably not be ready for.
|
||||
*/
|
||||
if (revents & SLIRP_POLL_PRI) {
|
||||
ret = sorecvoob(so);
|
||||
@@ -639,8 +952,10 @@ void slirp_pollfds_poll(Slirp *slirp, int select_error,
|
||||
/*
|
||||
* Check sockets for reading
|
||||
*/
|
||||
else if (revents &
|
||||
(SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR)) {
|
||||
else
|
||||
#endif
|
||||
if (revents &
|
||||
(SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR | SLIRP_POLL_PRI)) {
|
||||
/*
|
||||
* Check for incoming connections
|
||||
*/
|
||||
@@ -763,6 +1078,10 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
|
||||
return;
|
||||
}
|
||||
|
||||
if (pkt_len < ETH_HLEN + sizeof(struct slirp_arphdr)) {
|
||||
return; /* packet too short */
|
||||
}
|
||||
|
||||
ar_op = ntohs(ah->ar_op);
|
||||
switch (ar_op) {
|
||||
case ARPOP_REQUEST:
|
||||
@@ -953,6 +1272,7 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
|
||||
uint8_t ethaddr[ETH_ALEN];
|
||||
const struct ip *iph = (const struct ip *)ifm->m_data;
|
||||
int ret;
|
||||
// char ethaddr_str[ETH_ADDRSTRLEN];
|
||||
|
||||
if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
|
||||
return 1;
|
||||
@@ -978,19 +1298,18 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
|
||||
}
|
||||
|
||||
memcpy(eh->h_dest, ethaddr, ETH_ALEN);
|
||||
DEBUG_ARG("src = %02x:%02x:%02x:%02x:%02x:%02x", eh->h_source[0],
|
||||
eh->h_source[1], eh->h_source[2], eh->h_source[3],
|
||||
eh->h_source[4], eh->h_source[5]);
|
||||
DEBUG_ARG("dst = %02x:%02x:%02x:%02x:%02x:%02x", eh->h_dest[0],
|
||||
eh->h_dest[1], eh->h_dest[2], eh->h_dest[3], eh->h_dest[4],
|
||||
eh->h_dest[5]);
|
||||
/*
|
||||
DEBUG_ARG("src = %s", slirp_ether_ntoa(eh->h_source, ethaddr_str,
|
||||
sizeof(ethaddr_str)));
|
||||
DEBUG_ARG("dst = %s", slirp_ether_ntoa(eh->h_dest, ethaddr_str,
|
||||
sizeof(ethaddr_str)));
|
||||
*/
|
||||
memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len);
|
||||
slirp_send_packet_all(slirp, buf, ifm->m_len + ETH_HLEN);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Drop host forwarding rule, return 0 if found. */
|
||||
/* TODO: IPv6 */
|
||||
int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
int host_port)
|
||||
{
|
||||
@@ -1004,7 +1323,10 @@ int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
addr_len = sizeof(addr);
|
||||
if ((so->so_state & SS_HOSTFWD) &&
|
||||
getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&
|
||||
addr.sin_addr.s_addr == host_addr.s_addr && addr.sin_port == port) {
|
||||
addr_len == sizeof(addr) &&
|
||||
addr.sin_family == AF_INET &&
|
||||
addr.sin_addr.s_addr == host_addr.s_addr &&
|
||||
addr.sin_port == port) {
|
||||
so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);
|
||||
closesocket(so->s);
|
||||
sofree(so);
|
||||
@@ -1015,7 +1337,6 @@ int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO: IPv6 */
|
||||
int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
int host_port, struct in_addr guest_addr, int guest_port)
|
||||
{
|
||||
@@ -1034,6 +1355,83 @@ int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int slirp_remove_hostxfwd(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
int flags)
|
||||
{
|
||||
struct socket *so;
|
||||
struct socket *head = (flags & SLIRP_HOSTFWD_UDP ? &slirp->udb : &slirp->tcb);
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addr_len;
|
||||
|
||||
for (so = head->so_next; so != head; so = so->so_next) {
|
||||
addr_len = sizeof(addr);
|
||||
if ((so->so_state & SS_HOSTFWD) &&
|
||||
getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&
|
||||
sockaddr_equal(&addr, (const struct sockaddr_storage *) haddr)) {
|
||||
so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);
|
||||
closesocket(so->s);
|
||||
sofree(so);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int slirp_add_hostxfwd(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
const struct sockaddr *gaddr, socklen_t gaddrlen,
|
||||
int flags)
|
||||
{
|
||||
struct sockaddr_in gdhcp_addr;
|
||||
int fwd_flags = SS_HOSTFWD;
|
||||
|
||||
if (flags & SLIRP_HOSTFWD_V6ONLY)
|
||||
fwd_flags |= SS_HOSTFWD_V6ONLY;
|
||||
|
||||
if (gaddr->sa_family == AF_INET) {
|
||||
const struct sockaddr_in *gaddr_in = (const struct sockaddr_in *) gaddr;
|
||||
|
||||
if (gaddrlen < sizeof(struct sockaddr_in)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!gaddr_in->sin_addr.s_addr) {
|
||||
gdhcp_addr = *gaddr_in;
|
||||
gdhcp_addr.sin_addr = slirp->vdhcp_startaddr;
|
||||
gaddr = (struct sockaddr *) &gdhcp_addr;
|
||||
gaddrlen = sizeof(gdhcp_addr);
|
||||
}
|
||||
} else {
|
||||
if (gaddrlen < sizeof(struct sockaddr_in6)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Libslirp currently only provides a stateless DHCPv6 server, thus
|
||||
* we can't translate "addr-any" to the guest here. Instead, we defer
|
||||
* performing the translation to when it's needed. See
|
||||
* soassign_guest_addr_if_needed().
|
||||
*/
|
||||
}
|
||||
|
||||
if (flags & SLIRP_HOSTFWD_UDP) {
|
||||
if (!udpx_listen(slirp, haddr, haddrlen,
|
||||
gaddr, gaddrlen,
|
||||
fwd_flags))
|
||||
return -1;
|
||||
} else {
|
||||
if (!tcpx_listen(slirp, haddr, haddrlen,
|
||||
gaddr, gaddrlen,
|
||||
fwd_flags))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: IPv6 */
|
||||
static bool check_guestfwd(Slirp *slirp, struct in_addr *guest_addr,
|
||||
int guest_port)
|
||||
@@ -1158,6 +1556,8 @@ size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
|
||||
}
|
||||
|
||||
if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen / 2)) {
|
||||
/* If the sb is already half full, we will wait for the guest to consume it,
|
||||
* and notify again in sbdrop() when the sb becomes less than half full. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
/* as defined in sdkddkver.h */
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0600 /* Vista */
|
||||
#define _WIN32_WINNT 0x0600 /* Windows Vista */
|
||||
#endif
|
||||
/* reduces the number of implicitly included headers */
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
@@ -20,10 +20,8 @@
|
||||
#include <iphlpapi.h>
|
||||
|
||||
#else
|
||||
#if !defined(__HAIKU__)
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/uio.h>
|
||||
@@ -37,12 +35,6 @@
|
||||
#include <sys/filio.h>
|
||||
#endif
|
||||
|
||||
/* Avoid conflicting with the libc insque() and remque(), which
|
||||
have different prototypes. */
|
||||
#define insque slirp_insque
|
||||
#define remque slirp_remque
|
||||
#define quehead slirp_quehead
|
||||
|
||||
#include "debug.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -88,9 +80,9 @@ struct slirp_arphdr {
|
||||
/*
|
||||
* Ethernet looks like this : This bit is variable sized however...
|
||||
*/
|
||||
unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
|
||||
uint8_t ar_sha[ETH_ALEN]; /* sender hardware address */
|
||||
uint32_t ar_sip; /* sender IP address */
|
||||
unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
|
||||
uint8_t ar_tha[ETH_ALEN]; /* target hardware address */
|
||||
uint32_t ar_tip; /* target IP address */
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
@@ -111,7 +103,7 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
|
||||
uint8_t out_ethaddr[ETH_ALEN]);
|
||||
|
||||
struct ndpentry {
|
||||
unsigned char eth_addr[ETH_ALEN]; /* sender hardware address */
|
||||
uint8_t eth_addr[ETH_ALEN]; /* sender hardware address */
|
||||
struct in6_addr ip_addr; /* sender IP address */
|
||||
};
|
||||
|
||||
@@ -119,6 +111,12 @@ struct ndpentry {
|
||||
|
||||
typedef struct NdpTable {
|
||||
struct ndpentry table[NDP_TABLE_SIZE];
|
||||
/*
|
||||
* The table is a cache with old entries overwritten when the table fills.
|
||||
* Preserve the first entry: it is the guest, which is needed for lazy
|
||||
* hostfwd guest address assignment.
|
||||
*/
|
||||
struct in6_addr guest_in6_addr;
|
||||
int next_victim;
|
||||
} NdpTable;
|
||||
|
||||
@@ -128,6 +126,8 @@ bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr,
|
||||
uint8_t out_ethaddr[ETH_ALEN]);
|
||||
|
||||
struct Slirp {
|
||||
int cfg_version;
|
||||
|
||||
unsigned time_fasttimo;
|
||||
unsigned last_slowtimo;
|
||||
bool do_slowtimo;
|
||||
@@ -141,6 +141,7 @@ struct Slirp {
|
||||
struct in6_addr vprefix_addr6;
|
||||
uint8_t vprefix_len;
|
||||
struct in6_addr vhost_addr6;
|
||||
bool disable_dhcp; /* slirp will not reply to any DHCP requests */
|
||||
struct in_addr vdhcp_startaddr;
|
||||
struct in_addr vnameserver_addr;
|
||||
struct in6_addr vnameserver_addr6;
|
||||
@@ -157,13 +158,13 @@ struct Slirp {
|
||||
bool disable_host_loopback;
|
||||
|
||||
/* mbuf states */
|
||||
struct quehead m_freelist;
|
||||
struct quehead m_usedlist;
|
||||
struct slirp_quehead m_freelist;
|
||||
struct slirp_quehead m_usedlist;
|
||||
int mbuf_alloced;
|
||||
|
||||
/* if states */
|
||||
struct quehead if_fastq; /* fast queue (for interactive data) */
|
||||
struct quehead if_batchq; /* queue for non-interactive data */
|
||||
struct slirp_quehead if_fastq; /* fast queue (for interactive data) */
|
||||
struct slirp_quehead if_batchq; /* queue for non-interactive data */
|
||||
bool if_start_busy; /* avoid if_start recursion */
|
||||
|
||||
/* ip states */
|
||||
@@ -251,7 +252,7 @@ void ip_stripoptions(register struct mbuf *, struct mbuf *);
|
||||
int ip_output(struct socket *, struct mbuf *);
|
||||
|
||||
/* ip6_input.c */
|
||||
void ip6_init(Slirp *);
|
||||
void ip6_post_init(Slirp *);
|
||||
void ip6_cleanup(Slirp *);
|
||||
void ip6_input(struct mbuf *);
|
||||
|
||||
@@ -287,5 +288,6 @@ struct socket *slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
|
||||
void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len);
|
||||
void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque);
|
||||
|
||||
#endif
|
||||
|
@@ -8,6 +8,9 @@
|
||||
#ifdef __sun__
|
||||
#include <sys/filio.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <linux/errqueue.h>
|
||||
#endif
|
||||
|
||||
static void sofcantrcvmore(struct socket *so);
|
||||
static void sofcantsendmore(struct socket *so);
|
||||
@@ -38,15 +41,17 @@ struct socket *solookup(struct socket **last, struct socket *head,
|
||||
/*
|
||||
* Create a new socket, initialise the fields
|
||||
* It is the responsibility of the caller to
|
||||
* insque() it into the correct linked-list
|
||||
* slirp_insque() it into the correct linked-list
|
||||
*/
|
||||
struct socket *socreate(Slirp *slirp)
|
||||
struct socket *socreate(Slirp *slirp, int type)
|
||||
{
|
||||
struct socket *so = g_new(struct socket, 1);
|
||||
|
||||
memset(so, 0, sizeof(struct socket));
|
||||
so->so_type = type;
|
||||
so->so_state = SS_NOFDREF;
|
||||
so->s = -1;
|
||||
so->s_aux = -1;
|
||||
so->slirp = slirp;
|
||||
so->pollfds_idx = -1;
|
||||
|
||||
@@ -56,11 +61,11 @@ struct socket *socreate(Slirp *slirp)
|
||||
/*
|
||||
* Remove references to so from the given message queue.
|
||||
*/
|
||||
static void soqfree(struct socket *so, struct quehead *qh)
|
||||
static void soqfree(struct socket *so, struct slirp_quehead *qh)
|
||||
{
|
||||
struct mbuf *ifq;
|
||||
|
||||
for (ifq = (struct mbuf *)qh->qh_link; (struct quehead *)ifq != qh;
|
||||
for (ifq = (struct mbuf *)qh->qh_link; (struct slirp_quehead *)ifq != qh;
|
||||
ifq = ifq->ifq_next) {
|
||||
if (ifq->ifq_so == so) {
|
||||
struct mbuf *ifm;
|
||||
@@ -73,12 +78,16 @@ static void soqfree(struct socket *so, struct quehead *qh)
|
||||
}
|
||||
|
||||
/*
|
||||
* remque and free a socket, clobber cache
|
||||
* slirp_remque and free a socket, clobber cache
|
||||
*/
|
||||
void sofree(struct socket *so)
|
||||
{
|
||||
Slirp *slirp = so->slirp;
|
||||
|
||||
if (so->s_aux != -1) {
|
||||
closesocket(so->s_aux);
|
||||
}
|
||||
|
||||
soqfree(so, &slirp->if_fastq);
|
||||
soqfree(so, &slirp->if_batchq);
|
||||
|
||||
@@ -92,7 +101,7 @@ void sofree(struct socket *so)
|
||||
m_free(so->so_m);
|
||||
|
||||
if (so->so_next && so->so_prev)
|
||||
remque(so); /* crashes if so is not in a queue */
|
||||
slirp_remque(so); /* crashes if so is not in a queue */
|
||||
|
||||
if (so->so_tcpcb) {
|
||||
g_free(so->so_tcpcb);
|
||||
@@ -208,8 +217,8 @@ int soread(struct socket *so)
|
||||
errno, strerror(errno));
|
||||
sofcantrcvmore(so);
|
||||
|
||||
if (err == ECONNRESET || err == ECONNREFUSED || err == ENOTCONN ||
|
||||
err == EPIPE) {
|
||||
if (err == ECONNABORTED || err == ECONNRESET || err == ECONNREFUSED ||
|
||||
err == ENOTCONN || err == EPIPE) {
|
||||
tcp_drop(sototcpcb(so), err);
|
||||
} else {
|
||||
tcp_sockclosed(sototcpcb(so));
|
||||
@@ -336,8 +345,8 @@ int sosendoob(struct socket *so)
|
||||
DEBUG_ARG("so = %p", so);
|
||||
DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc);
|
||||
|
||||
if (so->so_urgc > 2048)
|
||||
so->so_urgc = 2048; /* XXXX */
|
||||
if (so->so_urgc > sizeof(buff))
|
||||
so->so_urgc = sizeof(buff); /* XXXX */
|
||||
|
||||
if (sb->sb_rptr < sb->sb_wptr) {
|
||||
/* We can send it directly */
|
||||
@@ -349,7 +358,7 @@ int sosendoob(struct socket *so)
|
||||
* we must copy all data to a linear buffer then
|
||||
* send it all
|
||||
*/
|
||||
uint32_t urgc = so->so_urgc;
|
||||
uint32_t urgc = so->so_urgc; /* Amount of room left in buff */
|
||||
int len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
|
||||
if (len > urgc) {
|
||||
len = urgc;
|
||||
@@ -357,6 +366,7 @@ int sosendoob(struct socket *so)
|
||||
memcpy(buff, sb->sb_rptr, len);
|
||||
urgc -= len;
|
||||
if (urgc) {
|
||||
/* We still have some room for the rest */
|
||||
n = sb->sb_wptr - sb->sb_data;
|
||||
if (n > urgc) {
|
||||
n = urgc;
|
||||
@@ -365,7 +375,7 @@ int sosendoob(struct socket *so)
|
||||
len += n;
|
||||
}
|
||||
n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
|
||||
#ifdef DEBUG
|
||||
#ifdef SLIRP_DEBUG
|
||||
if (n != len) {
|
||||
DEBUG_ERROR("Didn't send all data urgently XXXXX");
|
||||
}
|
||||
@@ -493,12 +503,67 @@ void sorecvfrom(struct socket *so)
|
||||
struct sockaddr_storage addr;
|
||||
struct sockaddr_storage saddr, daddr;
|
||||
socklen_t addrlen = sizeof(struct sockaddr_storage);
|
||||
char buff[256];
|
||||
|
||||
#ifdef __linux__
|
||||
ssize_t size;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char control[1024];
|
||||
|
||||
/* First look for errors */
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_name = &saddr;
|
||||
msg.msg_namelen = sizeof(saddr);
|
||||
msg.msg_control = control;
|
||||
msg.msg_controllen = sizeof(control);
|
||||
iov.iov_base = buff;
|
||||
iov.iov_len = sizeof(buff);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
size = recvmsg(so->s, &msg, MSG_ERRQUEUE);
|
||||
if (size >= 0) {
|
||||
struct cmsghdr *cmsg;
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
|
||||
if (cmsg->cmsg_level == IPPROTO_IP &&
|
||||
cmsg->cmsg_type == IP_RECVERR) {
|
||||
struct sock_extended_err *ee =
|
||||
(struct sock_extended_err *) CMSG_DATA(cmsg);
|
||||
|
||||
if (ee->ee_origin == SO_EE_ORIGIN_ICMP) {
|
||||
/* Got an ICMP error, forward it */
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
sin = (struct sockaddr_in *) SO_EE_OFFENDER(ee);
|
||||
icmp_forward_error(so->so_m, ee->ee_type, ee->ee_code,
|
||||
0, NULL, &sin->sin_addr);
|
||||
}
|
||||
}
|
||||
else if (cmsg->cmsg_level == IPPROTO_IPV6 &&
|
||||
cmsg->cmsg_type == IPV6_RECVERR) {
|
||||
struct sock_extended_err *ee =
|
||||
(struct sock_extended_err *) CMSG_DATA(cmsg);
|
||||
|
||||
if (ee->ee_origin == SO_EE_ORIGIN_ICMP6) {
|
||||
/* Got an ICMPv6 error, forward it */
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = (struct sockaddr_in6 *) SO_EE_OFFENDER(ee);
|
||||
icmp6_forward_error(so->so_m, ee->ee_type, ee->ee_code,
|
||||
&sin6->sin6_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_CALL("sorecvfrom");
|
||||
DEBUG_ARG("so = %p", so);
|
||||
|
||||
if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
|
||||
char buff[256];
|
||||
int len;
|
||||
|
||||
len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen);
|
||||
@@ -533,9 +598,6 @@ void sorecvfrom(struct socket *so)
|
||||
DEBUG_MISC(" ioctlsocket errno = %d-%s\n", errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m = m_get(so->slirp);
|
||||
if (!m) {
|
||||
@@ -624,6 +686,28 @@ void sorecvfrom(struct socket *so)
|
||||
*/
|
||||
saddr = addr;
|
||||
sotranslate_in(so, &saddr);
|
||||
|
||||
/* Perform lazy guest IP address resolution if needed. */
|
||||
if (so->so_state & SS_HOSTFWD) {
|
||||
if (soassign_guest_addr_if_needed(so) < 0) {
|
||||
DEBUG_MISC(" guest address not available yet");
|
||||
switch (so->so_lfamily) {
|
||||
case AF_INET:
|
||||
icmp_send_error(so->so_m, ICMP_UNREACH,
|
||||
ICMP_UNREACH_HOST, 0,
|
||||
"guest address not available yet");
|
||||
break;
|
||||
case AF_INET6:
|
||||
icmp6_send_error(so->so_m, ICMP6_UNREACH,
|
||||
ICMP6_UNREACH_ADDRESS);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
m_free(m);
|
||||
return;
|
||||
}
|
||||
}
|
||||
daddr = so->lhost.ss;
|
||||
|
||||
switch (so->so_ffamily) {
|
||||
@@ -679,32 +763,67 @@ int sosendto(struct socket *so, struct mbuf *m)
|
||||
|
||||
/*
|
||||
* Listen for incoming TCP connections
|
||||
* On failure errno contains the reason.
|
||||
*/
|
||||
struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
|
||||
uint32_t laddr, unsigned lport, int flags)
|
||||
struct socket *tcpx_listen(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
const struct sockaddr *laddr, socklen_t laddrlen,
|
||||
int flags)
|
||||
{
|
||||
/* TODO: IPv6 */
|
||||
struct sockaddr_in addr;
|
||||
struct socket *so;
|
||||
int s, opt = 1;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
memset(&addr, 0, addrlen);
|
||||
socklen_t addrlen;
|
||||
|
||||
DEBUG_CALL("tcp_listen");
|
||||
DEBUG_ARG("haddr = %s", inet_ntoa((struct in_addr){ .s_addr = haddr }));
|
||||
DEBUG_ARG("hport = %d", ntohs(hport));
|
||||
DEBUG_ARG("laddr = %s", inet_ntoa((struct in_addr){ .s_addr = laddr }));
|
||||
DEBUG_ARG("lport = %d", ntohs(lport));
|
||||
DEBUG_CALL("tcpx_listen");
|
||||
/* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
char portstr[6];
|
||||
int ret;
|
||||
switch (haddr->sa_family) {
|
||||
case AF_INET:
|
||||
case AF_INET6:
|
||||
ret = getnameinfo(haddr, haddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
|
||||
g_assert(ret == 0);
|
||||
DEBUG_ARG("hfamily = INET");
|
||||
DEBUG_ARG("haddr = %s", addrstr);
|
||||
DEBUG_ARG("hport = %s", portstr);
|
||||
break;
|
||||
#ifndef _WIN32
|
||||
case AF_UNIX:
|
||||
DEBUG_ARG("hfamily = UNIX");
|
||||
DEBUG_ARG("hpath = %s", ((struct sockaddr_un *) haddr)->sun_path);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
switch (laddr->sa_family) {
|
||||
case AF_INET:
|
||||
case AF_INET6:
|
||||
ret = getnameinfo(laddr, laddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
|
||||
g_assert(ret == 0);
|
||||
DEBUG_ARG("laddr = %s", addrstr);
|
||||
DEBUG_ARG("lport = %s", portstr);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
DEBUG_ARG("flags = %x", flags);
|
||||
|
||||
so = socreate(slirp);
|
||||
/*
|
||||
* SS_HOSTFWD sockets can be accepted multiple times, so they can't be
|
||||
* SS_FACCEPTONCE. Also, SS_HOSTFWD connections can be accepted and
|
||||
* immediately closed if the guest address isn't available yet, which is
|
||||
* incompatible with the "accept once" concept. Correct code will never
|
||||
* request both, so disallow their combination by assertion.
|
||||
*/
|
||||
g_assert(!((flags & SS_HOSTFWD) && (flags & SS_FACCEPTONCE)));
|
||||
|
||||
so = socreate(slirp, IPPROTO_TCP);
|
||||
|
||||
/* Don't tcp_attach... we don't need so_snd nor so_rcv */
|
||||
if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) {
|
||||
g_free(so);
|
||||
return NULL;
|
||||
}
|
||||
insque(so, &slirp->tcb);
|
||||
so->so_tcpcb = tcp_newtcpcb(so);
|
||||
slirp_insque(so, &slirp->tcb);
|
||||
|
||||
/*
|
||||
* SS_FACCEPTONCE sockets must time out.
|
||||
@@ -714,20 +833,16 @@ struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
|
||||
|
||||
so->so_state &= SS_PERSISTENT_MASK;
|
||||
so->so_state |= (SS_FACCEPTCONN | flags);
|
||||
so->so_lfamily = AF_INET;
|
||||
so->so_lport = lport; /* Kept in network format */
|
||||
so->so_laddr.s_addr = laddr; /* Ditto */
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = haddr;
|
||||
addr.sin_port = hport;
|
||||
sockaddr_copy(&so->lhost.sa, sizeof(so->lhost), laddr, laddrlen);
|
||||
|
||||
if (((s = slirp_socket(AF_INET, SOCK_STREAM, 0)) < 0) ||
|
||||
s = slirp_socket(haddr->sa_family, SOCK_STREAM, 0);
|
||||
if ((s < 0) ||
|
||||
(haddr->sa_family == AF_INET6 && slirp_socket_set_v6only(s, (flags & SS_HOSTFWD_V6ONLY) != 0) < 0) ||
|
||||
(slirp_socket_set_fast_reuse(s) < 0) ||
|
||||
(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) ||
|
||||
(bind(s, haddr, haddrlen) < 0) ||
|
||||
(listen(s, 1) < 0)) {
|
||||
int tmperrno = errno; /* Don't clobber the real reason we failed */
|
||||
|
||||
if (s >= 0) {
|
||||
closesocket(s);
|
||||
}
|
||||
@@ -741,22 +856,34 @@ struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
|
||||
return NULL;
|
||||
}
|
||||
setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
|
||||
opt = 1;
|
||||
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int));
|
||||
slirp_socket_set_nodelay(s);
|
||||
|
||||
getsockname(s, (struct sockaddr *)&addr, &addrlen);
|
||||
so->so_ffamily = AF_INET;
|
||||
so->so_fport = addr.sin_port;
|
||||
if (addr.sin_addr.s_addr == 0 ||
|
||||
addr.sin_addr.s_addr == loopback_addr.s_addr)
|
||||
so->so_faddr = slirp->vhost_addr;
|
||||
else
|
||||
so->so_faddr = addr.sin_addr;
|
||||
addrlen = sizeof(so->fhost);
|
||||
getsockname(s, &so->fhost.sa, &addrlen);
|
||||
sotranslate_accept(so);
|
||||
|
||||
so->s = s;
|
||||
return so;
|
||||
}
|
||||
|
||||
struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
|
||||
uint32_t laddr, unsigned lport, int flags)
|
||||
{
|
||||
struct sockaddr_in hsa, lsa;
|
||||
|
||||
memset(&hsa, 0, sizeof(hsa));
|
||||
hsa.sin_family = AF_INET;
|
||||
hsa.sin_addr.s_addr = haddr;
|
||||
hsa.sin_port = hport;
|
||||
|
||||
memset(&lsa, 0, sizeof(lsa));
|
||||
lsa.sin_family = AF_INET;
|
||||
lsa.sin_addr.s_addr = laddr;
|
||||
lsa.sin_port = lport;
|
||||
|
||||
return tcpx_listen(slirp, (const struct sockaddr *) &hsa, sizeof(hsa), (struct sockaddr *) &lsa, sizeof(lsa), flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Various session state calls
|
||||
* XXX Should be #define's
|
||||
@@ -941,6 +1068,108 @@ void sotranslate_accept(struct socket *so)
|
||||
}
|
||||
break;
|
||||
|
||||
case AF_UNIX: {
|
||||
/* Translate Unix socket to random ephemeral source port. We obtain
|
||||
* this source port by binding to port 0 so that the OS allocates a
|
||||
* port for us. If this fails, we fall back to choosing a random port
|
||||
* with a random number generator. */
|
||||
int s;
|
||||
struct sockaddr_in in_addr;
|
||||
struct sockaddr_in6 in6_addr;
|
||||
socklen_t in_addr_len;
|
||||
|
||||
if (so->slirp->in_enabled) {
|
||||
so->so_ffamily = AF_INET;
|
||||
so->so_faddr = slirp->vhost_addr;
|
||||
so->so_fport = 0;
|
||||
|
||||
switch (so->so_type) {
|
||||
case IPPROTO_TCP:
|
||||
s = slirp_socket(PF_INET, SOCK_STREAM, 0);
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
s = slirp_socket(PF_INET, SOCK_DGRAM, 0);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
if (s < 0) {
|
||||
g_error("Ephemeral slirp_socket() allocation failed");
|
||||
goto unix2inet_cont;
|
||||
}
|
||||
memset(&in_addr, 0, sizeof(in_addr));
|
||||
in_addr.sin_family = AF_INET;
|
||||
in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
in_addr.sin_port = htons(0);
|
||||
if (bind(s, (struct sockaddr *) &in_addr, sizeof(in_addr))) {
|
||||
g_error("Ephemeral bind() failed");
|
||||
closesocket(s);
|
||||
goto unix2inet_cont;
|
||||
}
|
||||
in_addr_len = sizeof(in_addr);
|
||||
if (getsockname(s, (struct sockaddr *) &in_addr, &in_addr_len)) {
|
||||
g_error("Ephemeral getsockname() failed");
|
||||
closesocket(s);
|
||||
goto unix2inet_cont;
|
||||
}
|
||||
so->s_aux = s;
|
||||
so->so_fport = in_addr.sin_port;
|
||||
|
||||
unix2inet_cont:
|
||||
if (!so->so_fport) {
|
||||
g_warning("Falling back to random port allocation");
|
||||
so->so_fport = htons(g_rand_int_range(slirp->grand, 49152, 65536));
|
||||
}
|
||||
} else if (so->slirp->in6_enabled) {
|
||||
so->so_ffamily = AF_INET6;
|
||||
so->so_faddr6 = slirp->vhost_addr6;
|
||||
so->so_fport6 = 0;
|
||||
|
||||
switch (so->so_type) {
|
||||
case IPPROTO_TCP:
|
||||
s = slirp_socket(PF_INET6, SOCK_STREAM, 0);
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
s = slirp_socket(PF_INET6, SOCK_DGRAM, 0);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
if (s < 0) {
|
||||
g_error("Ephemeral slirp_socket() allocation failed");
|
||||
goto unix2inet6_cont;
|
||||
}
|
||||
memset(&in6_addr, 0, sizeof(in6_addr));
|
||||
in6_addr.sin6_family = AF_INET6;
|
||||
in6_addr.sin6_addr = in6addr_loopback;
|
||||
in6_addr.sin6_port = htons(0);
|
||||
if (bind(s, (struct sockaddr *) &in6_addr, sizeof(in6_addr))) {
|
||||
g_error("Ephemeral bind() failed");
|
||||
closesocket(s);
|
||||
goto unix2inet6_cont;
|
||||
}
|
||||
in_addr_len = sizeof(in6_addr);
|
||||
if (getsockname(s, (struct sockaddr *) &in6_addr, &in_addr_len)) {
|
||||
g_error("Ephemeral getsockname() failed");
|
||||
closesocket(s);
|
||||
goto unix2inet6_cont;
|
||||
}
|
||||
so->s_aux = s;
|
||||
so->so_fport6 = in6_addr.sin6_port;
|
||||
|
||||
unix2inet6_cont:
|
||||
if (!so->so_fport6) {
|
||||
g_warning("Falling back to random port allocation");
|
||||
so->so_fport6 = htons(g_rand_int_range(slirp->grand, 49152, 65536));
|
||||
}
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
break;
|
||||
} /* case AF_UNIX */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -952,3 +1181,53 @@ void sodrop(struct socket *s, int num)
|
||||
s->slirp->cb->notify(s->slirp->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate "addr-any" in so->lhost to the guest's actual address.
|
||||
* Returns 0 for success, or -1 if the guest doesn't have an address yet
|
||||
* with errno set to EHOSTUNREACH.
|
||||
*
|
||||
* The guest address is taken from the first entry in the ARP table for IPv4
|
||||
* and the first entry in the NDP table for IPv6.
|
||||
* Note: The IPv4 path isn't exercised yet as all hostfwd "" guest translations
|
||||
* are handled immediately by using slirp->vdhcp_startaddr.
|
||||
*/
|
||||
int soassign_guest_addr_if_needed(struct socket *so)
|
||||
{
|
||||
Slirp *slirp = so->slirp;
|
||||
/* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
char portstr[6];
|
||||
|
||||
g_assert(so->so_state & SS_HOSTFWD);
|
||||
|
||||
switch (so->so_ffamily) {
|
||||
case AF_INET:
|
||||
if (so->so_laddr.s_addr == INADDR_ANY) {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
if (in6_zero(&so->so_laddr6)) {
|
||||
int ret;
|
||||
if (in6_zero(&slirp->ndp_table.guest_in6_addr)) {
|
||||
errno = EHOSTUNREACH;
|
||||
return -1;
|
||||
}
|
||||
so->so_laddr6 = slirp->ndp_table.guest_in6_addr;
|
||||
ret = getnameinfo((const struct sockaddr *) &so->lhost.ss,
|
||||
sizeof(so->lhost.ss), addrstr, sizeof(addrstr),
|
||||
portstr, sizeof(portstr),
|
||||
NI_NUMERICHOST|NI_NUMERICSERV);
|
||||
g_assert(ret == 0);
|
||||
DEBUG_MISC("%s: new ip = [%s]:%s", __func__, addrstr, portstr);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -6,16 +6,31 @@
|
||||
#ifndef SLIRP_SOCKET_H
|
||||
#define SLIRP_SOCKET_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "sbuf.h"
|
||||
|
||||
#define SO_EXPIRE 240000
|
||||
#define SO_EXPIREFAST 10000
|
||||
|
||||
/* Helps unify some in/in6 routines. */
|
||||
union in4or6_addr {
|
||||
struct in_addr addr4;
|
||||
struct in6_addr addr6;
|
||||
};
|
||||
typedef union in4or6_addr in4or6_addr;
|
||||
|
||||
/*
|
||||
* Our socket structure
|
||||
*/
|
||||
|
||||
union slirp_sockaddr {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in6 sin6;
|
||||
@@ -25,6 +40,8 @@ struct socket {
|
||||
struct socket *so_next, *so_prev; /* For a linked list of sockets */
|
||||
|
||||
int s; /* The actual socket */
|
||||
int s_aux; /* An auxiliary socket for miscellaneous use. Currently used to
|
||||
* reserve OS ports in UNIX-to-inet translation. */
|
||||
struct gfwd_list *guestfwd;
|
||||
|
||||
int pollfds_idx; /* GPollFD GArray index */
|
||||
@@ -55,7 +72,8 @@ struct socket {
|
||||
uint8_t so_iptos; /* Type of service */
|
||||
uint8_t so_emu; /* Is the socket emulated? */
|
||||
|
||||
uint8_t so_type; /* Type of socket, UDP or TCP */
|
||||
uint8_t so_type; /* Protocol of the socket. May be 0 if loading old
|
||||
* states. */
|
||||
int32_t so_state; /* internal state flags SS_*, below */
|
||||
|
||||
struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */
|
||||
@@ -97,9 +115,10 @@ struct socket {
|
||||
#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */
|
||||
#define SS_INCOMING \
|
||||
0x2000 /* Connection was initiated by a host on the internet */
|
||||
#define SS_HOSTFWD_V6ONLY 0x4000 /* Only bind on v6 addresses */
|
||||
|
||||
static inline int sockaddr_equal(struct sockaddr_storage *a,
|
||||
struct sockaddr_storage *b)
|
||||
static inline int sockaddr_equal(const struct sockaddr_storage *a,
|
||||
const struct sockaddr_storage *b)
|
||||
{
|
||||
if (a->ss_family != b->ss_family) {
|
||||
return 0;
|
||||
@@ -107,17 +126,24 @@ static inline int sockaddr_equal(struct sockaddr_storage *a,
|
||||
|
||||
switch (a->ss_family) {
|
||||
case AF_INET: {
|
||||
struct sockaddr_in *a4 = (struct sockaddr_in *)a;
|
||||
struct sockaddr_in *b4 = (struct sockaddr_in *)b;
|
||||
const struct sockaddr_in *a4 = (const struct sockaddr_in *)a;
|
||||
const struct sockaddr_in *b4 = (const struct sockaddr_in *)b;
|
||||
return a4->sin_addr.s_addr == b4->sin_addr.s_addr &&
|
||||
a4->sin_port == b4->sin_port;
|
||||
}
|
||||
case AF_INET6: {
|
||||
struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)a;
|
||||
struct sockaddr_in6 *b6 = (struct sockaddr_in6 *)b;
|
||||
const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *)a;
|
||||
const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *)b;
|
||||
return (in6_equal(&a6->sin6_addr, &b6->sin6_addr) &&
|
||||
a6->sin6_port == b6->sin6_port);
|
||||
}
|
||||
#ifndef _WIN32
|
||||
case AF_UNIX: {
|
||||
const struct sockaddr_un *aun = (const struct sockaddr_un *)a;
|
||||
const struct sockaddr_un *bun = (const struct sockaddr_un *)b;
|
||||
return strncmp(aun->sun_path, bun->sun_path, sizeof(aun->sun_path)) == 0;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
@@ -125,21 +151,33 @@ static inline int sockaddr_equal(struct sockaddr_storage *a,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline socklen_t sockaddr_size(struct sockaddr_storage *a)
|
||||
static inline socklen_t sockaddr_size(const struct sockaddr_storage *a)
|
||||
{
|
||||
switch (a->ss_family) {
|
||||
case AF_INET:
|
||||
return sizeof(struct sockaddr_in);
|
||||
case AF_INET6:
|
||||
return sizeof(struct sockaddr_in6);
|
||||
#ifndef _WIN32
|
||||
case AF_UNIX:
|
||||
return sizeof(struct sockaddr_un);
|
||||
#endif
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sockaddr_copy(struct sockaddr *dst, socklen_t dstlen, const struct sockaddr *src, socklen_t srclen)
|
||||
{
|
||||
socklen_t len = sockaddr_size((const struct sockaddr_storage *) src);
|
||||
g_assert(len <= srclen);
|
||||
g_assert(len <= dstlen);
|
||||
memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
struct socket *solookup(struct socket **, struct socket *,
|
||||
struct sockaddr_storage *, struct sockaddr_storage *);
|
||||
struct socket *socreate(Slirp *);
|
||||
struct socket *socreate(Slirp *, int);
|
||||
void sofree(struct socket *);
|
||||
int soread(struct socket *);
|
||||
int sorecvoob(struct socket *);
|
||||
@@ -148,6 +186,10 @@ int sowrite(struct socket *);
|
||||
void sorecvfrom(struct socket *);
|
||||
int sosendto(struct socket *, struct mbuf *);
|
||||
struct socket *tcp_listen(Slirp *, uint32_t, unsigned, uint32_t, unsigned, int);
|
||||
struct socket *tcpx_listen(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
const struct sockaddr *laddr, socklen_t laddrlen,
|
||||
int flags);
|
||||
void soisfconnecting(register struct socket *);
|
||||
void soisfconnected(register struct socket *);
|
||||
void sofwdrain(struct socket *);
|
||||
@@ -159,6 +201,6 @@ int sotranslate_out(struct socket *, struct sockaddr_storage *);
|
||||
void sotranslate_in(struct socket *, struct sockaddr_storage *);
|
||||
void sotranslate_accept(struct socket *);
|
||||
void sodrop(struct socket *, int num);
|
||||
|
||||
int soassign_guest_addr_if_needed(struct socket *so);
|
||||
|
||||
#endif /* SLIRP_SOCKET_H */
|
||||
|
@@ -344,7 +344,7 @@ int slirp_state_load(Slirp *slirp, int version_id, SlirpReadCb read_cb,
|
||||
|
||||
while (slirp_istream_read_u8(&f)) {
|
||||
int ret;
|
||||
struct socket *so = socreate(slirp);
|
||||
struct socket *so = socreate(slirp, -1);
|
||||
|
||||
ret =
|
||||
slirp_vmstate_load_state(&f, &vmstate_slirp_socket, so, version_id);
|
||||
|
@@ -55,7 +55,7 @@ struct tcphdr {
|
||||
uint16_t th_dport; /* destination port */
|
||||
tcp_seq th_seq; /* sequence number */
|
||||
tcp_seq th_ack; /* acknowledgement number */
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER)
|
||||
uint8_t th_off : 4, /* data offset */
|
||||
th_x2 : 4; /* (unused) */
|
||||
#else
|
||||
|
@@ -146,14 +146,14 @@ static int tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti,
|
||||
}
|
||||
q = tcpiphdr_next(q);
|
||||
m = tcpiphdr_prev(q)->ti_mbuf;
|
||||
remque(tcpiphdr2qlink(tcpiphdr_prev(q)));
|
||||
slirp_remque(tcpiphdr2qlink(tcpiphdr_prev(q)));
|
||||
m_free(m);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stick new segment in its place.
|
||||
*/
|
||||
insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q)));
|
||||
slirp_insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q)));
|
||||
|
||||
present:
|
||||
/*
|
||||
@@ -170,7 +170,7 @@ present:
|
||||
do {
|
||||
tp->rcv_nxt += ti->ti_len;
|
||||
flags = ti->ti_flags & TH_FIN;
|
||||
remque(tcpiphdr2qlink(ti));
|
||||
slirp_remque(tcpiphdr2qlink(ti));
|
||||
m = ti->ti_mbuf;
|
||||
ti = tcpiphdr_next(ti);
|
||||
if (so->so_state & SS_FCANTSENDMORE)
|
||||
@@ -215,6 +215,9 @@ void tcp_input(struct mbuf *m, int iphlen, struct socket *inso,
|
||||
DEBUG_CALL("tcp_input");
|
||||
DEBUG_ARG("m = %p iphlen = %2d inso = %p", m, iphlen, inso);
|
||||
|
||||
memset(&lhost, 0, sizeof(struct sockaddr_storage));
|
||||
memset(&fhost, 0, sizeof(struct sockaddr_storage));
|
||||
|
||||
/*
|
||||
* If called with m == 0, then we're continuing the connect
|
||||
*/
|
||||
@@ -233,6 +236,16 @@ void tcp_input(struct mbuf *m, int iphlen, struct socket *inso,
|
||||
goto cont_conn;
|
||||
}
|
||||
slirp = m->slirp;
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
M_DUP_DEBUG(slirp, m, 0,
|
||||
sizeof(struct tcpiphdr) - sizeof(struct ip) - sizeof(struct tcphdr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
M_DUP_DEBUG(slirp, m, 0,
|
||||
sizeof(struct tcpiphdr) - sizeof(struct ip6) - sizeof(struct tcphdr));
|
||||
break;
|
||||
}
|
||||
|
||||
ip = mtod(m, struct ip *);
|
||||
ip6 = mtod(m, struct ip6 *);
|
||||
@@ -407,7 +420,7 @@ findso:
|
||||
if ((tiflags & (TH_SYN | TH_FIN | TH_RST | TH_URG | TH_ACK)) != TH_SYN)
|
||||
goto dropwithreset;
|
||||
|
||||
so = socreate(slirp);
|
||||
so = socreate(slirp, IPPROTO_TCP);
|
||||
tcp_attach(so);
|
||||
|
||||
sbreserve(&so->so_snd, TCP_SNDSPACE);
|
||||
@@ -524,7 +537,7 @@ findso:
|
||||
* we don't need this.. XXX???
|
||||
*/
|
||||
if (so->so_snd.sb_cc)
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -775,7 +788,7 @@ findso:
|
||||
soisfconnected(so);
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
|
||||
(void)tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
|
||||
tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
|
||||
/*
|
||||
* if we didn't have to retransmit the SYN,
|
||||
* use its rtt as our initial srtt & rtt var.
|
||||
@@ -977,7 +990,7 @@ findso:
|
||||
soisfconnected(so);
|
||||
}
|
||||
|
||||
(void)tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
|
||||
tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
|
||||
tp->snd_wl1 = ti->ti_seq - 1;
|
||||
/* Avoid ack processing; snd_una==ti_ack => dup ack */
|
||||
goto synrx_to_est;
|
||||
@@ -1040,7 +1053,7 @@ findso:
|
||||
tp->t_rtt = 0;
|
||||
tp->snd_nxt = ti->ti_ack;
|
||||
tp->snd_cwnd = tp->t_maxseg;
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
tp->snd_cwnd =
|
||||
tp->snd_ssthresh + tp->t_maxseg * tp->t_dupacks;
|
||||
if (SEQ_GT(onxt, tp->snd_nxt))
|
||||
@@ -1048,7 +1061,7 @@ findso:
|
||||
goto drop;
|
||||
} else if (tp->t_dupacks > TCPREXMTTHRESH) {
|
||||
tp->snd_cwnd += tp->t_maxseg;
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
goto drop;
|
||||
}
|
||||
} else
|
||||
@@ -1332,7 +1345,7 @@ dodata:
|
||||
* Return any desired output.
|
||||
*/
|
||||
if (needoutput || (tp->t_flags & TF_ACKNOW)) {
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -1345,7 +1358,7 @@ dropafterack:
|
||||
goto drop;
|
||||
m_free(m);
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
return;
|
||||
|
||||
dropwithreset:
|
||||
@@ -1399,7 +1412,7 @@ static void tcp_dooptions(struct tcpcb *tp, uint8_t *cp, int cnt,
|
||||
continue;
|
||||
memcpy((char *)&mss, (char *)cp + 2, sizeof(mss));
|
||||
NTOHS(mss);
|
||||
(void)tcp_mss(tp, mss); /* sets t_maxseg */
|
||||
tcp_mss(tp, mss); /* sets t_maxseg */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -303,7 +303,7 @@ struct tcpcb *tcp_drop(struct tcpcb *tp, int err)
|
||||
|
||||
if (TCPS_HAVERCVDSYN(tp->t_state)) {
|
||||
tp->t_state = TCPS_CLOSED;
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
}
|
||||
return (tcp_close(tp));
|
||||
}
|
||||
@@ -329,7 +329,7 @@ struct tcpcb *tcp_close(struct tcpcb *tp)
|
||||
while (!tcpfrag_list_end(t, tp)) {
|
||||
t = tcpiphdr_next(t);
|
||||
m = tcpiphdr_prev(t)->ti_mbuf;
|
||||
remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
|
||||
slirp_remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
|
||||
m_free(m);
|
||||
}
|
||||
g_free(tp);
|
||||
@@ -421,7 +421,7 @@ int tcp_fconnect(struct socket *so, unsigned short af)
|
||||
struct sockaddr_storage addr;
|
||||
|
||||
slirp_set_nonblock(s);
|
||||
so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque);
|
||||
so->slirp->cb->register_poll_fd(s, so->slirp->opaque);
|
||||
slirp_socket_set_fast_reuse(s);
|
||||
opt = 1;
|
||||
setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));
|
||||
@@ -464,12 +464,54 @@ void tcp_connect(struct socket *inso)
|
||||
Slirp *slirp = inso->slirp;
|
||||
struct socket *so;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen = sizeof(struct sockaddr_storage);
|
||||
socklen_t addrlen;
|
||||
struct tcpcb *tp;
|
||||
int s, opt;
|
||||
int s, opt, ret;
|
||||
/* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
char portstr[6];
|
||||
|
||||
DEBUG_CALL("tcp_connect");
|
||||
DEBUG_ARG("inso = %p", inso);
|
||||
switch (inso->lhost.ss.ss_family) {
|
||||
case AF_INET:
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
ret = getnameinfo((const struct sockaddr *) &inso->lhost.ss, addrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
|
||||
g_assert(ret == 0);
|
||||
DEBUG_ARG("ip = [%s]:%s", addrstr, portstr);
|
||||
DEBUG_ARG("so_state = 0x%x", inso->so_state);
|
||||
|
||||
/* Perform lazy guest IP address resolution if needed. */
|
||||
if (inso->so_state & SS_HOSTFWD) {
|
||||
/*
|
||||
* We can only reject the connection request by accepting it and
|
||||
* then immediately closing it. Note that SS_FACCEPTONCE sockets can't
|
||||
* get here.
|
||||
*/
|
||||
if (soassign_guest_addr_if_needed(inso) < 0) {
|
||||
/*
|
||||
* Guest address isn't available yet. We could either try to defer
|
||||
* completing this connection request until the guest address is
|
||||
* available, or punt. It's easier to punt. Otherwise we need to
|
||||
* complicate the mechanism by which we're called to defer calling
|
||||
* us again until the guest address is available.
|
||||
*/
|
||||
DEBUG_MISC(" guest address not available yet");
|
||||
addrlen = sizeof(addr);
|
||||
s = accept(inso->s, (struct sockaddr *)&addr, &addrlen);
|
||||
if (s >= 0) {
|
||||
closesocket(s);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's an SS_ACCEPTONCE socket, no need to socreate()
|
||||
@@ -479,7 +521,7 @@ void tcp_connect(struct socket *inso)
|
||||
/* FACCEPTONCE already have a tcpcb */
|
||||
so = inso;
|
||||
} else {
|
||||
so = socreate(slirp);
|
||||
so = socreate(slirp, IPPROTO_TCP);
|
||||
tcp_attach(so);
|
||||
so->lhost = inso->lhost;
|
||||
so->so_ffamily = inso->so_ffamily;
|
||||
@@ -487,13 +529,14 @@ void tcp_connect(struct socket *inso)
|
||||
|
||||
tcp_mss(sototcpcb(so), 0);
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
s = accept(inso->s, (struct sockaddr *)&addr, &addrlen);
|
||||
if (s < 0) {
|
||||
tcp_close(sototcpcb(so)); /* This will sofree() as well */
|
||||
return;
|
||||
}
|
||||
slirp_set_nonblock(s);
|
||||
so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque);
|
||||
so->slirp->cb->register_poll_fd(s, so->slirp->opaque);
|
||||
slirp_socket_set_fast_reuse(s);
|
||||
opt = 1;
|
||||
setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
|
||||
@@ -534,7 +577,7 @@ void tcp_connect(struct socket *inso)
|
||||
void tcp_attach(struct socket *so)
|
||||
{
|
||||
so->so_tcpcb = tcp_newtcpcb(so);
|
||||
insque(so, &so->slirp->tcb);
|
||||
slirp_insque(so, &so->slirp->tcb);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -52,7 +52,7 @@ void tcp_fasttimo(Slirp *slirp)
|
||||
(tp->t_flags & TF_DELACK)) {
|
||||
tp->t_flags &= ~TF_DELACK;
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer)
|
||||
tp->snd_ssthresh = win * tp->t_maxseg;
|
||||
tp->t_dupacks = 0;
|
||||
}
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
break;
|
||||
|
||||
/*
|
||||
@@ -243,7 +243,7 @@ static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer)
|
||||
case TCPT_PERSIST:
|
||||
tcp_setpersist(tp);
|
||||
tp->t_force = 1;
|
||||
(void)tcp_output(tp);
|
||||
tcp_output(tp);
|
||||
tp->t_force = 0;
|
||||
break;
|
||||
|
||||
|
@@ -88,8 +88,8 @@ struct tcpiphdr {
|
||||
/* This is the difference between the size of a tcpiphdr structure, and the
|
||||
* size of actual ip+tcp headers, rounded up since we need to align data. */
|
||||
#define TCPIPHDR_DELTA \
|
||||
(MAX(0, (sizeof(struct tcpiphdr) - sizeof(struct ip) - \
|
||||
sizeof(struct tcphdr) + 3) & \
|
||||
(MAX(0, ((int) sizeof(struct tcpiphdr) - (int) sizeof(struct ip) - \
|
||||
(int) sizeof(struct tcphdr) + 3) & \
|
||||
~3))
|
||||
|
||||
/*
|
||||
|
@@ -50,7 +50,7 @@ static void tftp_session_terminate(struct tftp_session *spt)
|
||||
}
|
||||
|
||||
static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas,
|
||||
struct tftp_t *tp)
|
||||
struct tftphdr *hdr)
|
||||
{
|
||||
struct tftp_session *spt;
|
||||
int k;
|
||||
@@ -75,7 +75,7 @@ found:
|
||||
memcpy(&spt->client_addr, srcsas, sockaddr_size(srcsas));
|
||||
spt->fd = -1;
|
||||
spt->block_size = 512;
|
||||
spt->client_port = tp->udp.uh_sport;
|
||||
spt->client_port = hdr->udp.uh_sport;
|
||||
spt->slirp = slirp;
|
||||
|
||||
tftp_session_update(spt);
|
||||
@@ -84,7 +84,7 @@ found:
|
||||
}
|
||||
|
||||
static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
|
||||
struct tftp_t *tp)
|
||||
struct tftphdr *hdr)
|
||||
{
|
||||
struct tftp_session *spt;
|
||||
int k;
|
||||
@@ -94,7 +94,7 @@ static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
|
||||
|
||||
if (tftp_session_in_use(spt)) {
|
||||
if (sockaddr_equal(&spt->client_addr, srcsas)) {
|
||||
if (spt->client_port == tp->udp.uh_sport) {
|
||||
if (spt->client_port == hdr->udp.uh_sport) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
@@ -148,13 +148,13 @@ static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt,
|
||||
}
|
||||
|
||||
static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
|
||||
struct tftp_t *recv_tp)
|
||||
struct tftphdr *hdr)
|
||||
{
|
||||
if (spt->client_addr.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 sa6, da6;
|
||||
|
||||
sa6.sin6_addr = spt->slirp->vhost_addr6;
|
||||
sa6.sin6_port = recv_tp->udp.uh_dport;
|
||||
sa6.sin6_port = hdr->udp.uh_dport;
|
||||
da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr;
|
||||
da6.sin6_port = spt->client_port;
|
||||
|
||||
@@ -163,7 +163,7 @@ static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
|
||||
struct sockaddr_in sa4, da4;
|
||||
|
||||
sa4.sin_addr = spt->slirp->vhost_addr;
|
||||
sa4.sin_port = recv_tp->udp.uh_dport;
|
||||
sa4.sin_port = hdr->udp.uh_dport;
|
||||
da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr;
|
||||
da4.sin_port = spt->client_port;
|
||||
|
||||
@@ -185,14 +185,14 @@ static int tftp_send_oack(struct tftp_session *spt, const char *keys[],
|
||||
|
||||
tp = tftp_prep_mbuf_data(spt, m);
|
||||
|
||||
tp->tp_op = htons(TFTP_OACK);
|
||||
tp->hdr.tp_op = htons(TFTP_OACK);
|
||||
for (i = 0; i < nb; i++) {
|
||||
n += slirp_fmt0(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", keys[i]);
|
||||
n += slirp_fmt0(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", values[i]);
|
||||
}
|
||||
|
||||
m->m_len = G_SIZEOF_MEMBER(struct tftp_t, tp_op) + n;
|
||||
tftp_udp_output(spt, m, recv_tp);
|
||||
m->m_len = G_SIZEOF_MEMBER(struct tftp_t, hdr.tp_op) + n;
|
||||
tftp_udp_output(spt, m, &recv_tp->hdr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -213,21 +213,21 @@ static void tftp_send_error(struct tftp_session *spt, uint16_t errorcode,
|
||||
|
||||
tp = tftp_prep_mbuf_data(spt, m);
|
||||
|
||||
tp->tp_op = htons(TFTP_ERROR);
|
||||
tp->hdr.tp_op = htons(TFTP_ERROR);
|
||||
tp->x.tp_error.tp_error_code = htons(errorcode);
|
||||
slirp_pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg),
|
||||
msg);
|
||||
|
||||
m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX + 2) + 3 +
|
||||
strlen(msg) - sizeof(struct udphdr);
|
||||
tftp_udp_output(spt, m, recv_tp);
|
||||
tftp_udp_output(spt, m, &recv_tp->hdr);
|
||||
|
||||
out:
|
||||
tftp_session_terminate(spt);
|
||||
}
|
||||
|
||||
static void tftp_send_next_block(struct tftp_session *spt,
|
||||
struct tftp_t *recv_tp)
|
||||
struct tftphdr *hdr)
|
||||
{
|
||||
struct mbuf *m;
|
||||
struct tftp_t *tp;
|
||||
@@ -241,7 +241,7 @@ static void tftp_send_next_block(struct tftp_session *spt,
|
||||
|
||||
tp = tftp_prep_mbuf_data(spt, m);
|
||||
|
||||
tp->tp_op = htons(TFTP_DATA);
|
||||
tp->hdr.tp_op = htons(TFTP_DATA);
|
||||
tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
|
||||
|
||||
nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf,
|
||||
@@ -259,7 +259,7 @@ static void tftp_send_next_block(struct tftp_session *spt,
|
||||
|
||||
m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX - nobytes) -
|
||||
sizeof(struct udphdr);
|
||||
tftp_udp_output(spt, m, recv_tp);
|
||||
tftp_udp_output(spt, m, hdr);
|
||||
|
||||
if (nobytes == spt->block_size) {
|
||||
tftp_session_update(spt);
|
||||
@@ -282,12 +282,12 @@ static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas,
|
||||
int nb_options = 0;
|
||||
|
||||
/* check if a session already exists and if so terminate it */
|
||||
s = tftp_session_find(slirp, srcsas, tp);
|
||||
s = tftp_session_find(slirp, srcsas, &tp->hdr);
|
||||
if (s >= 0) {
|
||||
tftp_session_terminate(&slirp->tftp_sessions[s]);
|
||||
}
|
||||
|
||||
s = tftp_session_allocate(slirp, srcsas, tp);
|
||||
s = tftp_session_allocate(slirp, srcsas, &tp->hdr);
|
||||
|
||||
if (s < 0) {
|
||||
return;
|
||||
@@ -413,29 +413,29 @@ static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas,
|
||||
}
|
||||
|
||||
spt->block_nr = 0;
|
||||
tftp_send_next_block(spt, tp);
|
||||
tftp_send_next_block(spt, &tp->hdr);
|
||||
}
|
||||
|
||||
static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas,
|
||||
struct tftp_t *tp, int pktlen)
|
||||
struct tftphdr *hdr)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = tftp_session_find(slirp, srcsas, tp);
|
||||
s = tftp_session_find(slirp, srcsas, hdr);
|
||||
|
||||
if (s < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tftp_send_next_block(&slirp->tftp_sessions[s], tp);
|
||||
tftp_send_next_block(&slirp->tftp_sessions[s], hdr);
|
||||
}
|
||||
|
||||
static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
|
||||
struct tftp_t *tp, int pktlen)
|
||||
struct tftphdr *hdr)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = tftp_session_find(slirp, srcsas, tp);
|
||||
s = tftp_session_find(slirp, srcsas, hdr);
|
||||
|
||||
if (s < 0) {
|
||||
return;
|
||||
@@ -446,19 +446,25 @@ static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
|
||||
|
||||
void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m)
|
||||
{
|
||||
struct tftp_t *tp = (struct tftp_t *)m->m_data;
|
||||
struct tftphdr *hdr = mtod_check(m, sizeof(struct tftphdr));
|
||||
|
||||
switch (ntohs(tp->tp_op)) {
|
||||
if (hdr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ntohs(hdr->tp_op)) {
|
||||
case TFTP_RRQ:
|
||||
tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len);
|
||||
tftp_handle_rrq(m->slirp, srcsas,
|
||||
mtod(m, struct tftp_t *),
|
||||
m->m_len);
|
||||
break;
|
||||
|
||||
case TFTP_ACK:
|
||||
tftp_handle_ack(m->slirp, srcsas, tp, m->m_len);
|
||||
tftp_handle_ack(m->slirp, srcsas, hdr);
|
||||
break;
|
||||
|
||||
case TFTP_ERROR:
|
||||
tftp_handle_error(m->slirp, srcsas, tp, m->m_len);
|
||||
tftp_handle_error(m->slirp, srcsas, hdr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -23,9 +23,19 @@
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct tftp_t {
|
||||
struct tftphdr {
|
||||
struct udphdr udp;
|
||||
uint16_t tp_op;
|
||||
} SLIRP_PACKED;
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined (__clang__)
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
struct tftp_t {
|
||||
struct tftphdr hdr;
|
||||
union {
|
||||
struct {
|
||||
uint16_t tp_block_nr;
|
||||
|
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
#include <tinyglib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Must be a function, as libslirp redefines it as a macro. */
|
||||
gboolean
|
||||
@@ -28,6 +29,17 @@ g_spawn_async_with_fds(const gchar *working_directory, gchar **argv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Implementation borrowed from GLib itself. */
|
||||
gboolean
|
||||
g_str_has_prefix (const gchar *str,
|
||||
const gchar *prefix)
|
||||
{
|
||||
g_return_val_if_fail (str != NULL, false);
|
||||
g_return_val_if_fail (prefix != NULL, false);
|
||||
|
||||
return strncmp (str, prefix, strlen (prefix)) == 0;
|
||||
}
|
||||
|
||||
/* Needs bounds checking, but not really used by libslirp. */
|
||||
GString *
|
||||
g_string_new(gchar *base)
|
||||
|
@@ -67,6 +67,8 @@ void udp_cleanup(Slirp *slirp)
|
||||
void udp_input(register struct mbuf *m, int iphlen)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
M_DUP_DEBUG(slirp, m, 0, 0);
|
||||
|
||||
register struct ip *ip;
|
||||
register struct udphdr *uh;
|
||||
int len;
|
||||
@@ -74,6 +76,7 @@ void udp_input(register struct mbuf *m, int iphlen)
|
||||
struct socket *so;
|
||||
struct sockaddr_storage lhost;
|
||||
struct sockaddr_in *lhost4;
|
||||
int ttl;
|
||||
|
||||
DEBUG_CALL("udp_input");
|
||||
DEBUG_ARG("m = %p", m);
|
||||
@@ -93,7 +96,10 @@ void udp_input(register struct mbuf *m, int iphlen)
|
||||
/*
|
||||
* Get IP and UDP header together in first mbuf.
|
||||
*/
|
||||
ip = mtod(m, struct ip *);
|
||||
ip = mtod_check(m, iphlen + sizeof(struct udphdr));
|
||||
if (ip == NULL) {
|
||||
goto bad;
|
||||
}
|
||||
uh = (struct udphdr *)((char *)ip + iphlen);
|
||||
|
||||
/*
|
||||
@@ -171,7 +177,7 @@ void udp_input(register struct mbuf *m, int iphlen)
|
||||
* If there's no socket for this packet,
|
||||
* create one
|
||||
*/
|
||||
so = socreate(slirp);
|
||||
so = socreate(slirp, IPPROTO_UDP);
|
||||
if (udp_attach(so, AF_INET) == -1) {
|
||||
DEBUG_MISC(" udp_attach errno = %d-%s", errno, strerror(errno));
|
||||
sofree(so);
|
||||
@@ -202,6 +208,20 @@ void udp_input(register struct mbuf *m, int iphlen)
|
||||
m->m_len -= iphlen;
|
||||
m->m_data += iphlen;
|
||||
|
||||
/*
|
||||
* Check for TTL
|
||||
*/
|
||||
ttl = save_ip.ip_ttl-1;
|
||||
if (ttl <= 0) {
|
||||
m->m_len += iphlen;
|
||||
m->m_data -= iphlen;
|
||||
*ip = save_ip;
|
||||
DEBUG_MISC("udp ttl exceeded");
|
||||
icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, NULL);
|
||||
goto bad;
|
||||
}
|
||||
setsockopt(so->s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
|
||||
|
||||
/*
|
||||
* Now we sendto() the packet.
|
||||
*/
|
||||
@@ -230,14 +250,21 @@ bad:
|
||||
int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr,
|
||||
struct sockaddr_in *daddr, int iptos)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
// char addr[INET_ADDRSTRLEN];
|
||||
|
||||
M_DUP_DEBUG(slirp, m, 0, sizeof(struct udpiphdr));
|
||||
|
||||
register struct udpiphdr *ui;
|
||||
int error = 0;
|
||||
|
||||
/*
|
||||
DEBUG_CALL("udp_output");
|
||||
DEBUG_ARG("so = %p", so);
|
||||
DEBUG_ARG("m = %p", m);
|
||||
DEBUG_ARG("saddr = %s", inet_ntoa(saddr->sin_addr));
|
||||
DEBUG_ARG("daddr = %s", inet_ntoa(daddr->sin_addr));
|
||||
DEBUG_ARG("saddr = %s", inet_ntop(AF_INET, &saddr->sin_addr, addr, sizeof(addr)));
|
||||
DEBUG_ARG("daddr = %s", inet_ntop(AF_INET, &daddr->sin_addr, addr, sizeof(addr)));
|
||||
*/
|
||||
|
||||
/*
|
||||
* Adjust for header
|
||||
@@ -288,9 +315,27 @@ int udp_attach(struct socket *so, unsigned short af)
|
||||
so->s = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
{
|
||||
int opt = 1;
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
setsockopt(so->s, IPPROTO_IP, IP_RECVERR, &opt, sizeof(opt));
|
||||
break;
|
||||
case AF_INET6:
|
||||
setsockopt(so->s, IPPROTO_IPV6, IPV6_RECVERR, &opt, sizeof(opt));
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
so->so_expire = curtime + SO_EXPIRE;
|
||||
insque(so, &so->slirp->udb);
|
||||
slirp_insque(so, &so->slirp->udb);
|
||||
}
|
||||
so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque);
|
||||
return (so->s);
|
||||
}
|
||||
|
||||
@@ -321,45 +366,64 @@ static uint8_t udp_tos(struct socket *so)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
|
||||
uint32_t laddr, unsigned lport, int flags)
|
||||
struct socket *udpx_listen(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
const struct sockaddr *laddr, socklen_t laddrlen,
|
||||
int flags)
|
||||
{
|
||||
/* TODO: IPv6 */
|
||||
struct sockaddr_in addr;
|
||||
struct socket *so;
|
||||
socklen_t addrlen = sizeof(struct sockaddr_in);
|
||||
socklen_t addrlen;
|
||||
int save_errno;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
so = socreate(slirp);
|
||||
so->s = slirp_socket(AF_INET, SOCK_DGRAM, 0);
|
||||
so = socreate(slirp, IPPROTO_UDP);
|
||||
so->s = slirp_socket(haddr->sa_family, SOCK_DGRAM, 0);
|
||||
if (so->s < 0) {
|
||||
save_errno = errno;
|
||||
sofree(so);
|
||||
errno = save_errno;
|
||||
return NULL;
|
||||
}
|
||||
if (haddr->sa_family == AF_INET6)
|
||||
slirp_socket_set_v6only(so->s, (flags & SS_HOSTFWD_V6ONLY) != 0);
|
||||
so->so_expire = curtime + SO_EXPIRE;
|
||||
insque(so, &slirp->udb);
|
||||
slirp_insque(so, &slirp->udb);
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = haddr;
|
||||
addr.sin_port = hport;
|
||||
|
||||
if (bind(so->s, (struct sockaddr *)&addr, addrlen) < 0) {
|
||||
if (bind(so->s, haddr, haddrlen) < 0) {
|
||||
save_errno = errno;
|
||||
udp_detach(so);
|
||||
errno = save_errno;
|
||||
return NULL;
|
||||
}
|
||||
slirp_socket_set_fast_reuse(so->s);
|
||||
|
||||
getsockname(so->s, (struct sockaddr *)&addr, &addrlen);
|
||||
so->fhost.sin = addr;
|
||||
addrlen = sizeof(so->fhost);
|
||||
getsockname(so->s, &so->fhost.sa, &addrlen);
|
||||
sotranslate_accept(so);
|
||||
so->so_lfamily = AF_INET;
|
||||
so->so_lport = lport;
|
||||
so->so_laddr.s_addr = laddr;
|
||||
|
||||
sockaddr_copy(&so->lhost.sa, sizeof(so->lhost), laddr, laddrlen);
|
||||
|
||||
if (flags != SS_FACCEPTONCE)
|
||||
so->so_expire = 0;
|
||||
|
||||
so->so_state &= SS_PERSISTENT_MASK;
|
||||
so->so_state |= SS_ISFCONNECTED | flags;
|
||||
|
||||
return so;
|
||||
}
|
||||
|
||||
struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
|
||||
uint32_t laddr, unsigned lport, int flags)
|
||||
{
|
||||
struct sockaddr_in hsa, lsa;
|
||||
|
||||
memset(&hsa, 0, sizeof(hsa));
|
||||
hsa.sin_family = AF_INET;
|
||||
hsa.sin_addr.s_addr = haddr;
|
||||
hsa.sin_port = hport;
|
||||
|
||||
memset(&lsa, 0, sizeof(lsa));
|
||||
lsa.sin_family = AF_INET;
|
||||
lsa.sin_addr.s_addr = laddr;
|
||||
lsa.sin_port = lport;
|
||||
|
||||
return udpx_listen(slirp, (const struct sockaddr *) &hsa, sizeof(hsa), (struct sockaddr *) &lsa, sizeof(lsa), flags);
|
||||
}
|
||||
|
@@ -34,6 +34,8 @@
|
||||
#ifndef UDP_H
|
||||
#define UDP_H
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#define UDP_TTL 0x60
|
||||
#define UDP_UDPDATALEN 16192
|
||||
|
||||
@@ -80,6 +82,10 @@ void udp_input(register struct mbuf *, int);
|
||||
int udp_attach(struct socket *, unsigned short af);
|
||||
void udp_detach(struct socket *);
|
||||
struct socket *udp_listen(Slirp *, uint32_t, unsigned, uint32_t, unsigned, int);
|
||||
struct socket *udpx_listen(Slirp *,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
const struct sockaddr *laddr, socklen_t laddrlen,
|
||||
int flags);
|
||||
int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr,
|
||||
struct sockaddr_in *daddr, int iptos);
|
||||
|
||||
|
@@ -11,12 +11,15 @@
|
||||
void udp6_input(struct mbuf *m)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
M_DUP_DEBUG(slirp, m, 0, 0);
|
||||
|
||||
struct ip6 *ip, save_ip;
|
||||
struct udphdr *uh;
|
||||
int iphlen = sizeof(struct ip6);
|
||||
int len;
|
||||
struct socket *so;
|
||||
struct sockaddr_in6 lhost;
|
||||
int hop_limit;
|
||||
|
||||
DEBUG_CALL("udp6_input");
|
||||
DEBUG_ARG("m = %p", m);
|
||||
@@ -28,7 +31,10 @@ void udp6_input(struct mbuf *m)
|
||||
ip = mtod(m, struct ip6 *);
|
||||
m->m_len -= iphlen;
|
||||
m->m_data += iphlen;
|
||||
uh = mtod(m, struct udphdr *);
|
||||
uh = mtod_check(m, sizeof(struct udphdr));
|
||||
if (uh == NULL) {
|
||||
goto bad;
|
||||
}
|
||||
m->m_len += iphlen;
|
||||
m->m_data -= iphlen;
|
||||
|
||||
@@ -89,7 +95,7 @@ void udp6_input(struct mbuf *m)
|
||||
|
||||
if (so == NULL) {
|
||||
/* If there's no socket for this packet, create one. */
|
||||
so = socreate(slirp);
|
||||
so = socreate(slirp, IPPROTO_UDP);
|
||||
if (udp_attach(so, AF_INET6) == -1) {
|
||||
DEBUG_MISC(" udp6_attach errno = %d-%s", errno, strerror(errno));
|
||||
sofree(so);
|
||||
@@ -110,6 +116,20 @@ void udp6_input(struct mbuf *m)
|
||||
m->m_len -= iphlen;
|
||||
m->m_data += iphlen;
|
||||
|
||||
/*
|
||||
* Check for TTL
|
||||
*/
|
||||
hop_limit = save_ip.ip_hl-1;
|
||||
if (hop_limit <= 0) {
|
||||
m->m_len += iphlen;
|
||||
m->m_data -= iphlen;
|
||||
*ip = save_ip;
|
||||
DEBUG_MISC("udp ttl exceeded");
|
||||
icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS);
|
||||
goto bad;
|
||||
}
|
||||
setsockopt(so->s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, sizeof(hop_limit));
|
||||
|
||||
/*
|
||||
* Now we sendto() the packet.
|
||||
*/
|
||||
@@ -138,6 +158,9 @@ bad:
|
||||
int udp6_output(struct socket *so, struct mbuf *m, struct sockaddr_in6 *saddr,
|
||||
struct sockaddr_in6 *daddr)
|
||||
{
|
||||
Slirp *slirp = m->slirp;
|
||||
M_DUP_DEBUG(slirp, m, 0, sizeof(struct ip6) + sizeof(struct udphdr));
|
||||
|
||||
struct ip6 *ip;
|
||||
struct udphdr *uh;
|
||||
|
||||
|
@@ -72,6 +72,7 @@ static void slirp_set_cloexec(int fd)
|
||||
|
||||
/*
|
||||
* Opens a socket with FD_CLOEXEC set
|
||||
* On failure errno contains the reason.
|
||||
*/
|
||||
int slirp_socket(int domain, int type, int protocol)
|
||||
{
|
||||
@@ -366,6 +367,7 @@ void slirp_pstrcpy(char *buf, int buf_size, const char *str)
|
||||
*q = '\0';
|
||||
}
|
||||
|
||||
G_GNUC_PRINTF(3, 0)
|
||||
static int slirp_vsnprintf(char *str, size_t size,
|
||||
const char *format, va_list args)
|
||||
{
|
||||
@@ -427,3 +429,14 @@ int slirp_fmt0(char *str, size_t size, const char *format, ...)
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
const char *slirp_ether_ntoa(const uint8_t *addr, char *out_str,
|
||||
size_t out_str_size)
|
||||
{
|
||||
assert(out_str_size >= ETH_ADDRSTRLEN);
|
||||
|
||||
slirp_fmt0(out_str, out_str_size, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
|
||||
|
||||
return out_str;
|
||||
}
|
||||
|
@@ -30,9 +30,6 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <inttypes.h>
|
||||
@@ -40,7 +37,9 @@
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -49,7 +48,7 @@
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define SLIRP_PACKED
|
||||
#elif defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) && !defined(__clang__)
|
||||
#define SLIRP_PACKED __attribute__((gcc_struct, packed))
|
||||
#define SLIRP_PACKED __attribute__((gcc_struct, packed))
|
||||
#else
|
||||
#define SLIRP_PACKED __attribute__((packed))
|
||||
#endif
|
||||
@@ -59,7 +58,8 @@
|
||||
#endif
|
||||
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)));
|
||||
#define container_of(ptr, type, member) \
|
||||
((type *) (((char *)(ptr)) - offsetof(type, member)));
|
||||
#endif
|
||||
|
||||
#ifndef G_SIZEOF_MEMBER
|
||||
@@ -83,6 +83,7 @@ struct iovec {
|
||||
#define SCALE_MS 1000000
|
||||
|
||||
#define ETH_ALEN 6
|
||||
#define ETH_ADDRSTRLEN 18 /* "xx:xx:xx:xx:xx:xx", with trailing NUL */
|
||||
#define ETH_HLEN 14
|
||||
#define ETH_P_IP (0x0800) /* Internet Protocol packet */
|
||||
#define ETH_P_ARP (0x0806) /* Address Resolution packet */
|
||||
@@ -159,6 +160,11 @@ int slirp_inet_aton(const char *cp, struct in_addr *ia);
|
||||
int slirp_socket(int domain, int type, int protocol);
|
||||
void slirp_set_nonblock(int fd);
|
||||
|
||||
static inline int slirp_socket_set_v6only(int fd, int v)
|
||||
{
|
||||
return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v, sizeof(v));
|
||||
}
|
||||
|
||||
static inline int slirp_socket_set_nodelay(int fd)
|
||||
{
|
||||
int v = 1;
|
||||
@@ -185,4 +191,11 @@ void slirp_pstrcpy(char *buf, int buf_size, const char *str);
|
||||
int slirp_fmt(char *str, size_t size, const char *format, ...) G_GNUC_PRINTF(3, 4);
|
||||
int slirp_fmt0(char *str, size_t size, const char *format, ...) G_GNUC_PRINTF(3, 4);
|
||||
|
||||
/*
|
||||
* Pretty print a MAC address into out_str.
|
||||
* As a convenience returns out_str.
|
||||
*/
|
||||
const char *slirp_ether_ntoa(const uint8_t *addr, char *out_str,
|
||||
size_t out_str_len);
|
||||
|
||||
#endif
|
||||
|
@@ -175,6 +175,9 @@ enum VMStateFlags {
|
||||
* VMStateField.struct_version_id to tell which version of the
|
||||
* structure we are referencing to use. */
|
||||
VMS_VSTRUCT = 0x8000,
|
||||
|
||||
/* Marker for end of list */
|
||||
VMS_END = 0x10000
|
||||
};
|
||||
|
||||
struct VMStateField {
|
||||
@@ -216,10 +219,17 @@ extern const VMStateInfo slirp_vmstate_info_nullptr;
|
||||
extern const VMStateInfo slirp_vmstate_info_buffer;
|
||||
extern const VMStateInfo slirp_vmstate_info_tmp;
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define type_check_array(t1, t2, n) ((t1(*)[n])0 - (t2 *)0)
|
||||
#define type_check_pointer(t1, t2) ((t1 **)0 - (t2 *)0)
|
||||
#define typeof_field(type, field) typeof(((type *)0)->field)
|
||||
#define type_check(t1, t2) ((t1 *)0 - (t2 *)0)
|
||||
#else
|
||||
#define type_check_array(t1, t2, n) 0
|
||||
#define type_check_pointer(t1, t2) 0
|
||||
#define typeof_field(type, field) (((type *)0)->field)
|
||||
#define type_check(t1, t2) 0
|
||||
#endif
|
||||
|
||||
#define vmstate_offset_value(_state, _field, _type) \
|
||||
(offsetof(_state, _field) + type_check(_type, typeof_field(_state, _field)))
|
||||
@@ -388,6 +398,7 @@ extern const VMStateInfo slirp_vmstate_info_tmp;
|
||||
|
||||
#define VMSTATE_END_OF_LIST() \
|
||||
{ \
|
||||
.flags = VMS_END, \
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* VMSTATE_H_ */
|
||||
|
@@ -637,16 +637,17 @@ SCSIOBJ := scsi.o scsi_device.o \
|
||||
scsi_ncr5380.o scsi_ncr53c8xx.o \
|
||||
scsi_pcscsi.o scsi_spock.o
|
||||
|
||||
SLIRPOBJ := arp_table.o bootp.o cksum.o dnssearch.o if.o \
|
||||
ip_icmp.o ip_input.o ip_output.o \
|
||||
mbuf.o misc.o sbuf.o slirp.o socket.o \
|
||||
tcp_input.o tcp_output.o tcp_subr.o tcp_timer.o \
|
||||
udp.o \
|
||||
util.o version.o \
|
||||
SLIRPOBJ := net_slirp.o tinyglib.o \
|
||||
arp_table.o bootp.o cksum.o dhcpv6.o dnssearch.o if.o \
|
||||
ip_icmp.o ip_input.o ip_output.o \
|
||||
ip6_icmp.o ip6_input.o ip6_output.o \
|
||||
mbuf.o misc.o sbuf.o slirp.o socket.o \
|
||||
tcp_input.o tcp_output.o tcp_subr.o tcp_timer.o \
|
||||
udp.o udp6.o \
|
||||
util.o version.o \
|
||||
|
||||
NETOBJ := network.o \
|
||||
net_pcap.o \
|
||||
net_slirp.o tinyglib.o \
|
||||
${SLIRPOBJ} \
|
||||
net_dp8390.o net_3c501.o \
|
||||
net_3c503.o net_ne2000.o \
|
||||
|
Reference in New Issue
Block a user