Update libslirp to 4.7.0 with our customizations

This commit is contained in:
Jasmine Iwanek
2023-02-06 12:46:37 -05:00
parent 0daee20e8b
commit 3b5cfe8bfc
50 changed files with 2071 additions and 441 deletions

View File

@@ -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) || \

View File

@@ -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

View File

@@ -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);

View File

@@ -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) {}

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)) { \

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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 */

View File

@@ -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");

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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 *);

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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) || \

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);
}
/*

View File

@@ -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;

View File

@@ -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))
/*

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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_ */

View File

@@ -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 \