slirp: Adding IPv6 support to Qemu -net user mode

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCgAGBQJW6DLrAAoJEOPlHOj7ay8dav8QAIbciLu4X3pFo6S9V6ScDvnq
 DtZXb2EFvjhref6eQhkBqdpkS4uFGOC4GfWo5k4h5V1Lkn17xEx8zvJJBllMFQEA
 pFCkpc+ejKrlNI705gWXglIYiVagiddMDTOJ/yg2/UriEdFhoRqf1giDigLnvitx
 /RdRQGX8UDd3E3DKT7tJiZdRxiPygMuk0lvi3RDUAUNufE/Z5XX5j6WpGffpV5ju
 gDzFIyC2+86mQKLuXobX3Fhyax78Ws7Nib/NJx4bw9itOacfAZFn76iI78Cr5k+4
 gFu4RHoXoYFqFDLnCxoycbGUZTTmiwn4voqbVo9/hfjsiEc8+SEeAULDemcc0wz3
 /l8EYAKa72baB6AjReQwGSMiOdV6GQYI3T5pZwXwlHsiiwijoUk8rGPT6HQqfdNF
 VOtD7bQkqz2W4QElwmvGwGVe1Dxea2Avb6w/holCPz8Y8TVmtGpQ2lMY78qYrA8t
 lH2qhydIKgklZijVk3Jti1Wnx69NZsyFFWep0EZZDuZDUfoq5rENqT/Xsfm2x6pp
 foRrdmR52KjhwkVi6/VhJe8aDBaoJDZw+8AaFf3qXmowzcav63tsi2f+GtquwUFf
 tcbC6dTGaAod7Pdo4WzZDIhiBVT4JnygksNOb0Fn4lBMDegm0WHDhFlxTKV5+pbQ
 rKgLlQmvYW2JwOWXjkfD
 =VX4A
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/thibault/tags/samuel-thibault' into staging

slirp: Adding IPv6 support to Qemu -net user mode

# gpg: Signature made Tue 15 Mar 2016 16:06:03 GMT using RSA key ID FB6B2F1D
# gpg: Good signature from "Samuel Thibault <samuel.thibault@gnu.org>"
# gpg:                 aka "Samuel Thibault <sthibault@debian.org>"
# gpg:                 aka "Samuel Thibault <samuel.thibault@inria.fr>"
# gpg:                 aka "Samuel Thibault <samuel.thibault@labri.fr>"
# gpg:                 aka "Samuel Thibault <samuel.thibault@ens-lyon.org>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 900C B024 B679 31D4 0F82  304B D017 8C76 7D06 9EE6
#      Subkey fingerprint: F632 74CD C630 0873 CB3D  29D9 E3E5 1CE8 FB6B 2F1D

* remotes/thibault/tags/samuel-thibault:
  slirp: Add IPv6 support to the TFTP code
  qapi-schema, qemu-options & slirp: Adding Qemu options for IPv6 addresses
  slirp: Adding IPv6 address for DNS relay
  slirp: Handle IPv6 in TCP functions
  slirp: Reindent after refactoring
  slirp: Generalizing and neutralizing various TCP functions before adding IPv6 stuff
  slirp: Factorizing tcpiphdr structure with an union
  slirp: Adding IPv6 UDP support
  slirp: Adding ICMPv6 error sending
  slirp: Fix ICMP error sending
  slirp: Adding IPv6, ICMPv6 Echo and NDP autoconfiguration

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-03-15 17:09:52 +00:00
commit a6cdb77f81
34 changed files with 1953 additions and 219 deletions

View File

@ -1050,6 +1050,37 @@ int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
OptsVisitor *ov = opts_visitor_new(opts);
Visitor *v = opts_get_visitor(ov);
{
/* Parse convenience option format ip6-net=fec0::0[/64] */
const char *ip6_net = qemu_opt_get(opts, "ip6-net");
if (ip6_net) {
char buf[strlen(ip6_net) + 1];
if (get_str_sep(buf, sizeof(buf), &ip6_net, '/') < 0) {
/* Default 64bit prefix length. */
qemu_opt_set(opts, "ip6-prefix", ip6_net, &error_abort);
qemu_opt_set_number(opts, "ip6-prefixlen", 64, &error_abort);
} else {
/* User-specified prefix length. */
unsigned long len;
int err;
qemu_opt_set(opts, "ip6-prefix", buf, &error_abort);
err = qemu_strtoul(ip6_net, NULL, 10, &len);
if (err) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"ip6-prefix", "a number");
} else {
qemu_opt_set_number(opts, "ip6-prefixlen", len,
&error_abort);
}
}
qemu_opt_unset(opts, "ip6-net");
}
}
if (is_netdev) {
visit_type_Netdev(v, NULL, (Netdev **)&object, &err);
} else {

View File

@ -36,6 +36,7 @@
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "slirp/libslirp.h"
#include "slirp/ip6.h"
#include "sysemu/char.h"
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
@ -135,10 +136,13 @@ static NetClientInfo net_slirp_info = {
static int net_slirp_init(NetClientState *peer, const char *model,
const char *name, int restricted,
const char *vnetwork, const char *vhost,
const char *vprefix6, int vprefix6_len,
const char *vhost6,
const char *vhostname, const char *tftp_export,
const char *bootfile, const char *vdhcp_start,
const char *vnameserver, const char *smb_export,
const char *vsmbserver, const char **dnssearch)
const char *vnameserver, const char *vnameserver6,
const char *smb_export, const char *vsmbserver,
const char **dnssearch)
{
/* default settings according to historic slirp */
struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
@ -146,6 +150,9 @@ static int net_slirp_init(NetClientState *peer, const char *model,
struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
struct in6_addr ip6_prefix;
struct in6_addr ip6_host;
struct in6_addr ip6_dns;
#ifndef _WIN32
struct in_addr smbsrv = { .s_addr = 0 };
#endif
@ -235,6 +242,64 @@ static int net_slirp_init(NetClientState *peer, const char *model,
}
#endif
#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
/* No inet_pton helper before Vista... */
if (vprefix6) {
/* Unsupported */
return -1;
}
memset(&ip6_prefix, 0, sizeof(ip6_prefix));
ip6_prefix.s6_addr[0] = 0xfe;
ip6_prefix.s6_addr[1] = 0xc0;
#else
if (!vprefix6) {
vprefix6 = "fec0::";
}
if (!inet_pton(AF_INET6, vprefix6, &ip6_prefix)) {
return -1;
}
#endif
if (!vprefix6_len) {
vprefix6_len = 64;
}
if (vprefix6_len < 0 || vprefix6_len > 126) {
return -1;
}
if (vhost6) {
#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
return -1;
#else
if (!inet_pton(AF_INET6, vhost6, &ip6_host)) {
return -1;
}
if (!in6_equal_net(&ip6_prefix, &ip6_host, vprefix6_len)) {
return -1;
}
#endif
} else {
ip6_host = ip6_prefix;
ip6_host.s6_addr[15] |= 2;
}
if (vnameserver6) {
#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
return -1;
#else
if (!inet_pton(AF_INET6, vnameserver6, &ip6_dns)) {
return -1;
}
if (!in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) {
return -1;
}
#endif
} else {
ip6_dns = ip6_prefix;
ip6_dns.s6_addr[15] |= 3;
}
nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
snprintf(nc->info_str, sizeof(nc->info_str),
@ -243,8 +308,10 @@ static int net_slirp_init(NetClientState *peer, const char *model,
s = DO_UPCAST(SlirpState, nc, nc);
s->slirp = slirp_init(restricted, net, mask, host, vhostname,
tftp_export, bootfile, dhcp, dns, dnssearch, s);
s->slirp = slirp_init(restricted, net, mask, host,
ip6_prefix, vprefix6_len, ip6_host,
vhostname, tftp_export, bootfile, dhcp,
dns, ip6_dns, dnssearch, s);
QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
for (config = slirp_configs; config; config = config->next) {
@ -761,8 +828,10 @@ int net_init_slirp(const NetClientOptions *opts, const char *name,
net_init_slirp_configs(user->guestfwd, 0);
ret = net_slirp_init(peer, "user", name, user->q_restrict, vnet,
user->host, user->hostname, user->tftp,
user->bootfile, user->dhcpstart, user->dns, user->smb,
user->host, user->ip6_prefix, user->ip6_prefixlen,
user->ip6_host, user->hostname, user->tftp,
user->bootfile, user->dhcpstart,
user->dns, user->ip6_dns, user->smb,
user->smbserver, dnssearch);
while (slirp_configs) {

View File

@ -2451,6 +2451,14 @@
# @dnssearch: #optional list of DNS suffixes to search, passed as DHCP option
# to the guest
#
# @ip6-prefix: #optional IPv6 network prefix (default is fec0::) (since 2.6)
#
# @ip6-prefixlen: #optional IPv6 network prefix length (default is 64) (since 2.6)
#
# @ip6-host: #optional guest-visible IPv6 address of the host (since 2.6)
#
# @ip6-dns: #optional guest-visible IPv6 address of the virtual nameserver (since 2.6)
#
# @smb: #optional root directory of the built-in SMB server
#
# @smbserver: #optional IP address of the built-in SMB server
@ -2474,6 +2482,10 @@
'*dhcpstart': 'str',
'*dns': 'str',
'*dnssearch': ['String'],
'*ip6-prefix': 'str',
'*ip6-prefixlen': 'int',
'*ip6-host': 'str',
'*ip6-dns': 'str',
'*smb': 'str',
'*smbserver': 'str',
'*hostfwd': ['String'],

View File

@ -1560,8 +1560,9 @@ DEF("smb", HAS_ARG, QEMU_OPTION_smb, "", QEMU_ARCH_ALL)
DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
#ifdef CONFIG_SLIRP
"-netdev user,id=str[,net=addr[/mask]][,host=addr][,restrict=on|off]\n"
" [,hostname=host][,dhcpstart=addr][,dns=addr][,dnssearch=domain][,tftp=dir]\n"
"-netdev user,id=str[,net=addr[/mask]][,host=addr][,ip6-net=addr[/int]]\n"
" [,ip6-host=addr][,restrict=on|off][,hostname=host][,dhcpstart=addr]\n"
" [,dns=addr][,ip6-dns=addr][,dnssearch=domain][,tftp=dir]\n"
" [,bootfile=f][,hostfwd=rule][,guestfwd=rule]"
#ifndef _WIN32
"[,smb=dir[,smbserver=addr]]\n"
@ -1718,6 +1719,14 @@ either in the form a.b.c.d or as number of valid top-most bits. Default is
Specify the guest-visible address of the host. Default is the 2nd IP in the
guest network, i.e. x.x.x.2.
@item ip6-net=@var{addr}[/@var{int}]
Set IPv6 network address the guest will see. Optionally specify the prefix
size, as number of valid top-most bits. Default is fec0::/64.
@item ip6-host=@var{addr}
Specify the guest-visible IPv6 address of the host. Default is the 2nd IPv6 in
the guest network, i.e. xxxx::2.
@item restrict=on|off
If this option is enabled, the guest will be isolated, i.e. it will not be
able to contact the host and no guest IP packets will be routed over the host
@ -1735,6 +1744,11 @@ Specify the guest-visible address of the virtual nameserver. The address must
be different from the host address. Default is the 3rd IP in the guest network,
i.e. x.x.x.3.
@item ip6-dns=@var{addr}
Specify the guest-visible address of the IPv6 virtual nameserver. The address
must be different from the host address. Default is the 3rd IP in the guest
network, i.e. xxxx::3.
@item dnssearch=@var{domain}
Provides an entry for the domain-search list sent by the built-in
DHCP server. More than one domain suffix can be transmitted by specifying

View File

@ -1,3 +1,5 @@
common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o
common-obj-y = cksum.o if.o ip_icmp.o ip6_icmp.o ip6_input.o ip6_output.o \
ip_input.o ip_output.o dnssearch.o
common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
common-obj-y += tcp_subr.o tcp_timer.o udp.o udp6.o bootp.o tftp.o arp_table.o \
ndp_table.o

View File

@ -138,3 +138,28 @@ cont:
REDUCE;
return (~sum & 0xffff);
}
int ip6_cksum(struct mbuf *m)
{
/* TODO: Optimize this by being able to pass the ip6_pseudohdr to cksum
* separately from the mbuf */
struct ip6 save_ip, *ip = mtod(m, struct ip6 *);
struct ip6_pseudohdr *ih = mtod(m, struct ip6_pseudohdr *);
int sum;
save_ip = *ip;
ih->ih_src = save_ip.ip_src;
ih->ih_dst = save_ip.ip_dst;
ih->ih_pl = htonl((uint32_t)ntohs(save_ip.ip_pl));
ih->ih_zero_hi = 0;
ih->ih_zero_lo = 0;
ih->ih_nh = save_ip.ip_nh;
sum = cksum(m, ((int)sizeof(struct ip6_pseudohdr))
+ ntohl(ih->ih_pl));
*ip = save_ip;
return sum;
}

View File

@ -194,7 +194,7 @@ void if_start(Slirp *slirp)
/* Try to send packet unless it already expired */
if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) {
/* Packet is delayed due to pending ARP resolution */
/* Packet is delayed due to pending ARP or NDP resolution */
continue;
}

View File

@ -17,7 +17,7 @@
#define IF_MRU 1500
#define IF_COMP IF_AUTOCOMP /* Flags for compression */
/* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
#define IF_MAXLINKHDR (2 + 14 + 40)
/* 2 for alignment, 14 for ethernet */
#define IF_MAXLINKHDR (2 + ETH_HLEN)
#endif

141
slirp/ip6.h Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright (c) 2013
* Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
*/
#ifndef SLIRP_IP6_H_
#define SLIRP_IP6_H_
#include "net/eth.h"
#define ALLNODES_MULTICAST { .s6_addr = \
{ 0xff, 0x02, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x01 } }
#define SOLICITED_NODE_PREFIX { .s6_addr = \
{ 0xff, 0x02, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x01,\
0xff, 0x00, 0x00, 0x00 } }
#define LINKLOCAL_ADDR { .s6_addr = \
{ 0xfe, 0x80, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x02 } }
static inline bool in6_equal(const struct in6_addr *a, const struct in6_addr *b)
{
return memcmp(a, b, sizeof(*a)) == 0;
}
static inline bool in6_equal_net(const struct in6_addr *a,
const struct in6_addr *b,
int prefix_len)
{
if (memcmp(a, b, prefix_len / 8) != 0) {
return 0;
}
if (prefix_len % 8 == 0) {
return 1;
}
return a->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8))
== b->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8));
}
static inline bool in6_equal_mach(const struct in6_addr *a,
const struct in6_addr *b,
int prefix_len)
{
if (memcmp(&(a->s6_addr[(prefix_len + 7) / 8]),
&(b->s6_addr[(prefix_len + 7) / 8]),
16 - (prefix_len + 7) / 8) != 0) {
return 0;
}
if (prefix_len % 8 == 0) {
return 1;
}
return (a->s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1))
== (b->s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1));
}
#define in6_equal_router(a)\
((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len)\
&& in6_equal_mach(a, &slirp->vhost_addr6, slirp->vprefix_len))\
|| (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64)\
&& in6_equal_mach(a, &slirp->vhost_addr6, 64)))
#define in6_equal_dns(a)\
((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len)\
&& in6_equal_mach(a, &slirp->vnameserver_addr6, slirp->vprefix_len))\
|| (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64)\
&& in6_equal_mach(a, &slirp->vnameserver_addr6, 64)))
#define in6_equal_host(a)\
(in6_equal_router(a) || in6_equal_dns(a))
#define in6_solicitednode_multicast(a)\
(in6_equal_net(a, &(struct in6_addr)SOLICITED_NODE_PREFIX, 104))
/* Compute emulated host MAC address from its ipv6 address */
static inline void in6_compute_ethaddr(struct in6_addr ip,
uint8_t eth[ETH_ALEN])
{
eth[0] = 0x52;
eth[1] = 0x56;
memcpy(&eth[2], &ip.s6_addr[16 - (ETH_ALEN - 2)], ETH_ALEN - 2);
}
/*
* Definitions for internet protocol version 6.
* Per RFC 2460, December 1998.
*/
#define IP6VERSION 6
#define IP6_HOP_LIMIT 255
/*
* Structure of an internet header, naked of options.
*/
struct ip6 {
#ifdef HOST_WORDS_BIGENDIAN
uint32_t
ip_v:4, /* version */
ip_tc_hi:4, /* traffic class */
ip_tc_lo:4,
ip_fl_hi:4, /* flow label */
ip_fl_lo:16;
#else
uint32_t
ip_tc_hi:4,
ip_v:4,
ip_fl_hi:4,
ip_tc_lo:4,
ip_fl_lo:16;
#endif
uint16_t ip_pl; /* payload length */
uint8_t ip_nh; /* next header */
uint8_t ip_hl; /* hop limit */
struct in6_addr ip_src, ip_dst; /* source and dest address */
} QEMU_PACKED;
/*
* IPv6 pseudo-header used by upper-layer protocols
*/
struct ip6_pseudohdr {
struct in6_addr ih_src; /* source internet address */
struct in6_addr ih_dst; /* destination internet address */
uint32_t ih_pl; /* upper-layer packet length */
uint16_t ih_zero_hi; /* zero */
uint8_t ih_zero_lo; /* zero */
uint8_t ih_nh; /* next header */
} QEMU_PACKED;
#endif

416
slirp/ip6_icmp.c Normal file
View File

@ -0,0 +1,416 @@
/*
* Copyright (c) 2013
* Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
*/
#include "qemu/osdep.h"
#include "slirp.h"
#include "ip6_icmp.h"
#include "qemu/timer.h"
#include "qemu/error-report.h"
#include "qemu/log.h"
#include <time.h>
#define NDP_Interval g_rand_int_range(slirp->grand, \
NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
static void ra_timer_handler(void *opaque)
{
Slirp *slirp = opaque;
timer_mod(slirp->ra_timer,
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
ndp_send_ra(slirp);
}
void icmp6_init(Slirp *slirp)
{
slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ra_timer_handler, slirp);
timer_mod(slirp->ra_timer,
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
}
void icmp6_cleanup(Slirp *slirp)
{
timer_del(slirp->ra_timer);
timer_free(slirp->ra_timer);
}
static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
struct icmp6 *icmp)
{
struct mbuf *t = m_get(slirp);
t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
memcpy(t->m_data, m->m_data, t->m_len);
/* IPv6 Packet */
struct ip6 *rip = mtod(t, struct ip6 *);
rip->ip_dst = ip->ip_src;
rip->ip_src = ip->ip_dst;
/* ICMPv6 packet */
t->m_data += sizeof(struct ip6);
struct icmp6 *ricmp = mtod(t, struct icmp6 *);
ricmp->icmp6_type = ICMP6_ECHO_REPLY;
ricmp->icmp6_cksum = 0;
/* Checksum */
t->m_data -= sizeof(struct ip6);
ricmp->icmp6_cksum = ip6_cksum(t);
ip6_output(NULL, t, 0);
}
void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
{
Slirp *slirp = m->slirp;
struct mbuf *t;
struct ip6 *ip = mtod(m, struct ip6 *);
DEBUG_CALL("icmp6_send_error");
DEBUG_ARGS((dfd, " type = %d, code = %d\n", type, code));
if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
/* TODO icmp error? */
return;
}
t = m_get(slirp);
/* IPv6 packet */
struct ip6 *rip = mtod(t, struct ip6 *);
rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
rip->ip_dst = ip->ip_src;
#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
char addrstr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
DEBUG_ARG("target = %s", addrstr);
#endif
rip->ip_nh = IPPROTO_ICMPV6;
const int error_data_len = min(m->m_len,
IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
/* ICMPv6 packet */
t->m_data += sizeof(struct ip6);
struct icmp6 *ricmp = mtod(t, struct icmp6 *);
ricmp->icmp6_type = type;
ricmp->icmp6_code = code;
ricmp->icmp6_cksum = 0;
switch (type) {
case ICMP6_UNREACH:
case ICMP6_TIMXCEED:
ricmp->icmp6_err.unused = 0;
break;
case ICMP6_TOOBIG:
ricmp->icmp6_err.mtu = htonl(IF_MTU);
break;
case ICMP6_PARAMPROB:
/* TODO: Handle this case */
break;
default:
g_assert_not_reached();
break;
}
t->m_data += ICMP6_ERROR_MINLEN;
memcpy(t->m_data, m->m_data, error_data_len);
/* Checksum */
t->m_data -= ICMP6_ERROR_MINLEN;
t->m_data -= sizeof(struct ip6);
ricmp->icmp6_cksum = ip6_cksum(t);
ip6_output(NULL, t, 0);
}
/*
* Send NDP Router Advertisement
*/
void ndp_send_ra(Slirp *slirp)
{
DEBUG_CALL("ndp_send_ra");
/* Build IPv6 packet */
struct mbuf *t = m_get(slirp);
struct ip6 *rip = mtod(t, struct ip6 *);
rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
rip->ip_nh = IPPROTO_ICMPV6;
rip->ip_pl = htons(ICMP6_NDP_RA_MINLEN
+ NDPOPT_LINKLAYER_LEN
+ NDPOPT_PREFIXINFO_LEN);
t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
/* Build ICMPv6 packet */
t->m_data += sizeof(struct ip6);
struct icmp6 *ricmp = mtod(t, struct icmp6 *);
ricmp->icmp6_type = ICMP6_NDP_RA;
ricmp->icmp6_code = 0;
ricmp->icmp6_cksum = 0;
/* NDP */
ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
ricmp->icmp6_nra.reserved = 0;
ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
/* Source link-layer address (NDP option) */
t->m_data += ICMP6_NDP_RA_MINLEN;
struct ndpopt *opt = mtod(t, struct ndpopt *);
opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
/* Prefix information (NDP option) */
t->m_data += NDPOPT_LINKLAYER_LEN;
struct ndpopt *opt2 = mtod(t, struct ndpopt *);
opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
opt2->ndpopt_prefixinfo.L = 1;
opt2->ndpopt_prefixinfo.A = 1;
opt2->ndpopt_prefixinfo.reserved1 = 0;
opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
opt2->ndpopt_prefixinfo.reserved2 = 0;
opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
/* ICMPv6 Checksum */
t->m_data -= NDPOPT_LINKLAYER_LEN;
t->m_data -= ICMP6_NDP_RA_MINLEN;
t->m_data -= sizeof(struct ip6);
ricmp->icmp6_cksum = ip6_cksum(t);
ip6_output(NULL, t, 0);
}
/*
* Send NDP Neighbor Solitication
*/
void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
{
DEBUG_CALL("ndp_send_ns");
#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
char addrstr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
DEBUG_ARG("target = %s", addrstr);
#endif
/* Build IPv6 packet */
struct mbuf *t = m_get(slirp);
struct ip6 *rip = mtod(t, struct ip6 *);
rip->ip_src = slirp->vhost_addr6;
rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
rip->ip_nh = IPPROTO_ICMPV6;
rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
/* Build ICMPv6 packet */
t->m_data += sizeof(struct ip6);
struct icmp6 *ricmp = mtod(t, struct icmp6 *);
ricmp->icmp6_type = ICMP6_NDP_NS;
ricmp->icmp6_code = 0;
ricmp->icmp6_cksum = 0;
/* NDP */
ricmp->icmp6_nns.reserved = 0;
ricmp->icmp6_nns.target = addr;
/* Build NDP option */
t->m_data += ICMP6_NDP_NS_MINLEN;
struct ndpopt *opt = mtod(t, struct ndpopt *);
opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
/* ICMPv6 Checksum */
t->m_data -= ICMP6_NDP_NA_MINLEN;
t->m_data -= sizeof(struct ip6);
ricmp->icmp6_cksum = ip6_cksum(t);
ip6_output(NULL, t, 1);
}
/*
* Send NDP Neighbor Advertisement
*/
static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
{
/* Build IPv6 packet */
struct mbuf *t = m_get(slirp);
struct ip6 *rip = mtod(t, struct ip6 *);
rip->ip_src = icmp->icmp6_nns.target;
if (IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
} else {
rip->ip_dst = ip->ip_src;
}
rip->ip_nh = IPPROTO_ICMPV6;
rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
+ NDPOPT_LINKLAYER_LEN);
t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
/* Build ICMPv6 packet */
t->m_data += sizeof(struct ip6);
struct icmp6 *ricmp = mtod(t, struct icmp6 *);
ricmp->icmp6_type = ICMP6_NDP_NA;
ricmp->icmp6_code = 0;
ricmp->icmp6_cksum = 0;
/* NDP */
ricmp->icmp6_nna.R = NDP_IsRouter;
ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
ricmp->icmp6_nna.O = 1;
ricmp->icmp6_nna.reserved_hi = 0;
ricmp->icmp6_nna.reserved_lo = 0;
ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
/* Build NDP option */
t->m_data += ICMP6_NDP_NA_MINLEN;
struct ndpopt *opt = mtod(t, struct ndpopt *);
opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
in6_compute_ethaddr(ricmp->icmp6_nna.target,
opt->ndpopt_linklayer);
/* ICMPv6 Checksum */
t->m_data -= ICMP6_NDP_NA_MINLEN;
t->m_data -= sizeof(struct ip6);
ricmp->icmp6_cksum = ip6_cksum(t);
ip6_output(NULL, t, 0);
}
/*
* Process a NDP message
*/
static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
struct icmp6 *icmp)
{
m->m_len += ETH_HLEN;
m->m_data -= ETH_HLEN;
struct ethhdr *eth = mtod(m, struct ethhdr *);
m->m_len -= ETH_HLEN;
m->m_data += ETH_HLEN;
switch (icmp->icmp6_type) {
case ICMP6_NDP_RS:
DEBUG_CALL(" type = Router Solicitation");
if (ip->ip_hl == 255
&& icmp->icmp6_code == 0
&& ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
/* Gratuitous NDP */
ndp_table_add(slirp, ip->ip_src, eth->h_source);
ndp_send_ra(slirp);
}
break;
case ICMP6_NDP_RA:
DEBUG_CALL(" type = Router Advertisement");
qemu_log_mask(LOG_GUEST_ERROR,
"Warning: guest sent NDP RA, but shouldn't");
break;
case ICMP6_NDP_NS:
DEBUG_CALL(" type = Neighbor Solicitation");
if (ip->ip_hl == 255
&& icmp->icmp6_code == 0
&& !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
&& ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
&& (!IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)
|| in6_solicitednode_multicast(&ip->ip_dst))) {
if (in6_equal_host(&icmp->icmp6_nns.target)) {
/* Gratuitous NDP */
ndp_table_add(slirp, ip->ip_src, eth->h_source);
ndp_send_na(slirp, ip, icmp);
}
}
break;
case ICMP6_NDP_NA:
DEBUG_CALL(" type = Neighbor Advertisement");
if (ip->ip_hl == 255
&& icmp->icmp6_code == 0
&& ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
&& !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
&& (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
|| icmp->icmp6_nna.S == 0)) {
ndp_table_add(slirp, ip->ip_src, eth->h_source);
}
break;
case ICMP6_NDP_REDIRECT:
DEBUG_CALL(" type = Redirect");
qemu_log_mask(LOG_GUEST_ERROR,
"Warning: guest sent NDP REDIRECT, but shouldn't");
break;
}
}
/*
* Process a received ICMPv6 message.
*/
void icmp6_input(struct mbuf *m)
{
struct icmp6 *icmp;
struct ip6 *ip = mtod(m, struct ip6 *);
Slirp *slirp = m->slirp;
int hlen = sizeof(struct ip6);
DEBUG_CALL("icmp6_input");
DEBUG_ARG("m = %lx", (long) m);
DEBUG_ARG("m_len = %d", m->m_len);
if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
goto end;
}
if (ip6_cksum(m)) {
goto end;
}
m->m_len -= hlen;
m->m_data += hlen;
icmp = mtod(m, struct icmp6 *);
m->m_len += hlen;
m->m_data -= hlen;
DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
switch (icmp->icmp6_type) {
case ICMP6_ECHO_REQUEST:
if (in6_equal_host(&ip->ip_dst)) {
icmp6_send_echoreply(m, slirp, ip, icmp);
} else {
/* TODO */
error_report("external icmpv6 not supported yet");
}
break;
case ICMP6_NDP_RS:
case ICMP6_NDP_RA:
case ICMP6_NDP_NS:
case ICMP6_NDP_NA:
case ICMP6_NDP_REDIRECT:
ndp_input(m, slirp, ip, icmp);
break;
case ICMP6_UNREACH:
case ICMP6_TOOBIG:
case ICMP6_TIMXCEED:
case ICMP6_PARAMPROB:
/* XXX? report error? close socket? */
default:
break;
}
end:
m_free(m);
}

213
slirp/ip6_icmp.h Normal file
View File

@ -0,0 +1,213 @@
/*
* Copyright (c) 2013
* Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
*/
#ifndef SLIRP_NETINET_ICMP6_H_
#define SLIRP_NETINET_ICMP6_H_
/*
* Interface Control Message Protocol version 6 Definitions.
* Per RFC 4443, March 2006.
*
* Network Discover Protocol Definitions.
* Per RFC 4861, September 2007.
*/
struct icmp6_echo { /* Echo Messages */
uint16_t id;
uint16_t seq_num;
};
union icmp6_error_body {
uint32_t unused;
uint32_t pointer;
uint32_t mtu;
};
/*
* NDP Messages
*/
struct ndp_rs { /* Router Solicitation Message */
uint32_t reserved;
};
struct ndp_ra { /* Router Advertisement Message */
uint8_t chl; /* Cur Hop Limit */
#ifdef HOST_WORDS_BIGENDIAN
uint8_t
M:1,
O:1,
reserved:6;
#else
uint8_t
reserved:6,
O:1,
M:1;
#endif
uint16_t lifetime; /* Router Lifetime */
uint32_t reach_time; /* Reachable Time */
uint32_t retrans_time; /* Retrans Timer */
} QEMU_PACKED;
struct ndp_ns { /* Neighbor Solicitation Message */
uint32_t reserved;
struct in6_addr target; /* Target Address */
} QEMU_PACKED;
struct ndp_na { /* Neighbor Advertisement Message */
#ifdef HOST_WORDS_BIGENDIAN
uint32_t
R:1, /* Router Flag */
S:1, /* Solicited Flag */
O:1, /* Override Flag */
reserved_hi:5,
reserved_lo:24;
#else
uint32_t
reserved_hi:5,
O:1,
S:1,
R:1,
reserved_lo:24;
#endif
struct in6_addr target; /* Target Address */
} QEMU_PACKED;
struct ndp_redirect {
uint32_t reserved;
struct in6_addr target; /* Target Address */
struct in6_addr dest; /* Destination Address */
} QEMU_PACKED;
/*
* Structure of an icmpv6 header.
*/
struct icmp6 {
uint8_t icmp6_type; /* type of message, see below */
uint8_t icmp6_code; /* type sub code */
uint16_t icmp6_cksum; /* ones complement cksum of struct */
union {
union icmp6_error_body error_body;
struct icmp6_echo echo;
struct ndp_rs ndp_rs;
struct ndp_ra ndp_ra;
struct ndp_ns ndp_ns;
struct ndp_na ndp_na;
struct ndp_redirect ndp_redirect;
} icmp6_body;
#define icmp6_err icmp6_body.error_body
#define icmp6_echo icmp6_body.echo
#define icmp6_nrs icmp6_body.ndp_rs
#define icmp6_nra icmp6_body.ndp_ra
#define icmp6_nns icmp6_body.ndp_ns
#define icmp6_nna icmp6_body.ndp_na
#define icmp6_redirect icmp6_body.ndp_redirect
} QEMU_PACKED;
#define ICMP6_MINLEN 4
#define ICMP6_ERROR_MINLEN 8
#define ICMP6_ECHO_MINLEN 8
#define ICMP6_NDP_RS_MINLEN 8
#define ICMP6_NDP_RA_MINLEN 16
#define ICMP6_NDP_NS_MINLEN 24
#define ICMP6_NDP_NA_MINLEN 24
#define ICMP6_NDP_REDIRECT_MINLEN 40
/*
* NDP Options
*/
struct ndpopt {
uint8_t ndpopt_type; /* Option type */
uint8_t ndpopt_len; /* /!\ In units of 8 octets */
union {
unsigned char linklayer_addr[6]; /* Source/Target Link-layer */
struct prefixinfo { /* Prefix Information */
uint8_t prefix_length;
#ifdef HOST_WORDS_BIGENDIAN
uint8_t L:1, A:1, reserved1:6;
#else
uint8_t reserved1:6, A:1, L:1;
#endif
uint32_t valid_lt; /* Valid Lifetime */
uint32_t pref_lt; /* Preferred Lifetime */
uint32_t reserved2;
struct in6_addr prefix;
} QEMU_PACKED prefixinfo;
} ndpopt_body;
#define ndpopt_linklayer ndpopt_body.linklayer_addr
#define ndpopt_prefixinfo ndpopt_body.prefixinfo
} QEMU_PACKED;
/* NDP options type */
#define NDPOPT_LINKLAYER_SOURCE 1 /* Source Link-Layer Address */
#define NDPOPT_LINKLAYER_TARGET 2 /* Target Link-Layer Address */
#define NDPOPT_PREFIX_INFO 3 /* Prefix Information */
/* NDP options size, in octets. */
#define NDPOPT_LINKLAYER_LEN 8
#define NDPOPT_PREFIXINFO_LEN 32
/*
* Definition of type and code field values.
* Per https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml
* Last Updated 2012-11-12
*/
/* Errors */
#define ICMP6_UNREACH 1 /* Destination Unreachable */
#define ICMP6_UNREACH_NO_ROUTE 0 /* no route to dest */
#define ICMP6_UNREACH_DEST_PROHIB 1 /* com with dest prohibited */
#define ICMP6_UNREACH_SCOPE 2 /* beyond scope of src addr */
#define ICMP6_UNREACH_ADDRESS 3 /* address unreachable */
#define ICMP6_UNREACH_PORT 4 /* port unreachable */
#define ICMP6_UNREACH_SRC_FAIL 5 /* src addr failed */
#define ICMP6_UNREACH_REJECT_ROUTE 6 /* reject route to dest */
#define ICMP6_UNREACH_SRC_HDR_ERROR 7 /* error in src routing header */
#define ICMP6_TOOBIG 2 /* Packet Too Big */
#define ICMP6_TIMXCEED 3 /* Time Exceeded */
#define ICMP6_TIMXCEED_INTRANS 0 /* hop limit exceeded in transit */
#define ICMP6_TIMXCEED_REASS 1 /* ttl=0 in reass */
#define ICMP6_PARAMPROB 4 /* Parameter Problem */
#define ICMP6_PARAMPROB_HDR_FIELD 0 /* err header field */
#define ICMP6_PARAMPROB_NXTHDR_TYPE 1 /* unrecognized Next Header type */
#define ICMP6_PARAMPROB_IPV6_OPT 2 /* unrecognized IPv6 option */
/* Informational Messages */
#define ICMP6_ECHO_REQUEST 128 /* Echo Request */
#define ICMP6_ECHO_REPLY 129 /* Echo Reply */
#define ICMP6_NDP_RS 133 /* Router Solicitation (NDP) */
#define ICMP6_NDP_RA 134 /* Router Advertisement (NDP) */
#define ICMP6_NDP_NS 135 /* Neighbor Solicitation (NDP) */
#define ICMP6_NDP_NA 136 /* Neighbor Advertisement (NDP) */
#define ICMP6_NDP_REDIRECT 137 /* Redirect Message (NDP) */
/*
* Router Configuration Variables (rfc4861#section-6)
*/
#define NDP_IsRouter 1
#define NDP_AdvSendAdvertisements 1
#define NDP_MaxRtrAdvInterval 600000
#define NDP_MinRtrAdvInterval ((NDP_MaxRtrAdvInterval >= 9) ? \
NDP_MaxRtrAdvInterval / 3 : \
NDP_MaxRtrAdvInterval)
#define NDP_AdvManagedFlag 0
#define NDP_AdvOtherConfigFlag 0
#define NDP_AdvLinkMTU 0
#define NDP_AdvReachableTime 0
#define NDP_AdvRetransTime 0
#define NDP_AdvCurHopLimit 64
#define NDP_AdvDefaultLifetime ((3 * NDP_MaxRtrAdvInterval) / 1000)
#define NDP_AdvValidLifetime 86400
#define NDP_AdvOnLinkFlag 1
#define NDP_AdvPrefLifetime 14400
#define NDP_AdvAutonomousFlag 1
void icmp6_init(Slirp *slirp);
void icmp6_cleanup(Slirp *slirp);
void icmp6_input(struct mbuf *);
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);
#endif

73
slirp/ip6_input.c Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2013
* Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
*/
#include "qemu/osdep.h"
#include "slirp.h"
#include "ip6_icmp.h"
/*
* 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)
{
icmp6_init(slirp);
}
void ip6_cleanup(Slirp *slirp)
{
icmp6_cleanup(slirp);
}
void ip6_input(struct mbuf *m)
{
struct ip6 *ip6;
DEBUG_CALL("ip6_input");
DEBUG_ARG("m = %lx", (long)m);
DEBUG_ARG("m_len = %d", m->m_len);
if (m->m_len < sizeof(struct ip6)) {
goto bad;
}
ip6 = mtod(m, struct ip6 *);
if (ip6->ip_v != IP6VERSION) {
goto bad;
}
if (ntohs(ip6->ip_pl) > IF_MTU) {
icmp6_send_error(m, ICMP6_TOOBIG, 0);
goto bad;
}
/* check ip_ttl for a correct ICMP reply */
if (ip6->ip_hl == 0) {
icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS);
goto bad;
}
/*
* Switch out to protocol's input routine.
*/
switch (ip6->ip_nh) {
case IPPROTO_TCP:
NTOHS(ip6->ip_pl);
tcp_input(m, sizeof(struct ip6), (struct socket *)NULL, AF_INET6);
break;
case IPPROTO_UDP:
udp6_input(m);
break;
case IPPROTO_ICMPV6:
icmp6_input(m);
break;
default:
m_free(m);
}
return;
bad:
m_free(m);
}

40
slirp/ip6_output.c Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2013
* Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "slirp.h"
/* Number of packets queued before we start sending
* (to prevent allocing too many mbufs) */
#define IF6_THRESH 10
/*
* IPv6 output. The packet in mbuf chain m contains a IP header
*/
int ip6_output(struct socket *so, struct mbuf *m, int fast)
{
struct ip6 *ip = mtod(m, struct ip6 *);
DEBUG_CALL("ip6_output");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("m = %lx", (long)m);
/* Fill IPv6 header */
ip->ip_v = IP6VERSION;
ip->ip_hl = IP6_HOP_LIMIT;
ip->ip_tc_hi = 0;
ip->ip_tc_lo = 0;
ip->ip_fl_hi = 0;
ip->ip_fl_lo = 0;
if (fast) {
if_encap(m->slirp, m);
} else {
if_output(so, m);
}
return 0;
}

View File

@ -38,7 +38,7 @@
/* Be nice and tell them it's just a pseudo-ping packet */
static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
/* list of actions for icmp_error() on RX of an icmp message */
/* list of actions for icmp_send_error() on RX of an icmp message */
static const int icmp_flush[19] = {
/* ECHO REPLY (0) */ 0,
1,
@ -101,7 +101,7 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen)
(struct sockaddr *)&addr, sizeof(addr)) == -1) {
DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n",
errno, strerror(errno)));
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
icmp_detach(so);
}
@ -189,7 +189,7 @@ icmp_input(struct mbuf *m, int hlen)
(struct sockaddr *)&addr, sizeof(addr)) == -1) {
DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
errno,strerror(errno)));
icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
udp_detach(so);
}
} /* if ip->ip_dst.s_addr == alias_addr.s_addr */
@ -235,7 +235,7 @@ end_error:
#define ICMP_MAXDATALEN (IP_MSS-28)
void
icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
const char *message)
{
unsigned hlen, shlen, s_ip_len;
@ -243,7 +243,7 @@ icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
register struct icmp *icp;
register struct mbuf *m;
DEBUG_CALL("icmp_error");
DEBUG_CALL("icmp_send_error");
DEBUG_ARG("msrc = %p", msrc);
DEBUG_ARG("msrc_len = %d", msrc->m_len);
@ -433,7 +433,7 @@ void icmp_receive(struct socket *so)
}
DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno,
strerror(errno)));
icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno));
icmp_send_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno));
} else {
icmp_reflect(so->so_m);
so->so_m = NULL; /* Don't m_free() it again! */

View File

@ -156,8 +156,8 @@ struct icmp {
void icmp_init(Slirp *slirp);
void icmp_cleanup(Slirp *slirp);
void icmp_input(struct mbuf *, int);
void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
const char *message);
void icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
const char *message);
void icmp_reflect(struct mbuf *);
void icmp_receive(struct socket *so);
void icmp_detach(struct socket *so);

View File

@ -132,9 +132,9 @@ ip_input(struct mbuf *m)
m_adj(m, ip->ip_len - m->m_len);
/* check ip_ttl for a correct ICMP reply */
if(ip->ip_ttl==0) {
icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
goto bad;
if (ip->ip_ttl == 0) {
icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl");
goto bad;
}
/*
@ -200,7 +200,7 @@ ip_input(struct mbuf *m)
*/
switch (ip->ip_p) {
case IPPROTO_TCP:
tcp_input(m, hlen, (struct socket *)NULL);
tcp_input(m, hlen, (struct socket *)NULL, AF_INET);
break;
case IPPROTO_UDP:
udp_input(m, hlen);
@ -637,7 +637,7 @@ typedef uint32_t n_time;
}
return (0);
bad:
icmp_error(m, type, code, 0, 0);
icmp_send_error(m, type, code, 0, 0);
return (1);
}

View File

@ -10,9 +10,11 @@ int get_dns_addr(struct in_addr *pdns_addr);
Slirp *slirp_init(int restricted, struct in_addr vnetwork,
struct in_addr vnetmask, struct in_addr vhost,
const char *vhostname, const char *tftp_path,
const char *bootfile, struct in_addr vdhcp_start,
struct in_addr vnameserver, const char **vdnssearch,
struct in6_addr vprefix_addr6, uint8_t vprefix_len,
struct in6_addr vhost6, const char *vhostname,
const char *tftp_path, const char *bootfile,
struct in_addr vdhcp_start, struct in_addr vnameserver,
struct in6_addr vnameserver6, const char **vdnssearch,
void *opaque);
void slirp_cleanup(Slirp *slirp);

View File

@ -22,9 +22,9 @@
/*
* Find a nice value for msize
* XXX if_maxlinkhdr already in mtu
*/
#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6)
#define SLIRP_MSIZE\
(offsetof(struct mbuf, m_dat) + IF_MAXLINKHDR + TCPIPHDR_DELTA + IF_MTU)
void
m_init(Slirp *slirp)

90
slirp/ndp_table.c Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2013
* Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "slirp.h"
void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr,
uint8_t ethaddr[ETH_ALEN])
{
NdpTable *ndp_table = &slirp->ndp_table;
int i;
DEBUG_CALL("ndp_table_add");
#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
char addrstr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN);
DEBUG_ARG("ip = %s", addrstr);
#endif
DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
ethaddr[0], ethaddr[1], ethaddr[2],
ethaddr[3], ethaddr[4], ethaddr[5]));
if (IN6_IS_ADDR_MULTICAST(&ip_addr) || IN6_IS_ADDR_UNSPECIFIED(&ip_addr)) {
/* Do not register multicast or unspecified addresses */
DEBUG_CALL(" abort: do not register multicast or unspecified address");
return;
}
/* Search for an entry */
for (i = 0; i < NDP_TABLE_SIZE; i++) {
if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) {
DEBUG_CALL(" already in table: update the entry");
/* Update the entry */
memcpy(ndp_table->table[i].eth_addr, ethaddr, ETH_ALEN);
return;
}
}
/* No entry found, create a new one */
DEBUG_CALL(" create new entry");
ndp_table->table[ndp_table->next_victim].ip_addr = ip_addr;
memcpy(ndp_table->table[ndp_table->next_victim].eth_addr,
ethaddr, ETH_ALEN);
ndp_table->next_victim = (ndp_table->next_victim + 1) % NDP_TABLE_SIZE;
}
bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr,
uint8_t out_ethaddr[ETH_ALEN])
{
NdpTable *ndp_table = &slirp->ndp_table;
int i;
DEBUG_CALL("ndp_table_search");
#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
char addrstr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN);
DEBUG_ARG("ip = %s", addrstr);
#endif
assert(!IN6_IS_ADDR_UNSPECIFIED(&ip_addr));
/* Multicast address: fec0::abcd:efgh/8 -> 33:33:ab:cd:ef:gh */
if (IN6_IS_ADDR_MULTICAST(&ip_addr)) {
out_ethaddr[0] = 0x33; out_ethaddr[1] = 0x33;
out_ethaddr[2] = ip_addr.s6_addr[12];
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_ARGS((dfd, " multicast addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]));
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_ARGS((dfd, " found hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]));
return 1;
}
}
DEBUG_CALL(" ip not found in table");
return 0;
}

View File

@ -201,19 +201,23 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id);
Slirp *slirp_init(int restricted, struct in_addr vnetwork,
struct in_addr vnetmask, struct in_addr vhost,
const char *vhostname, const char *tftp_path,
const char *bootfile, struct in_addr vdhcp_start,
struct in_addr vnameserver, const char **vdnssearch,
struct in6_addr vprefix_addr6, uint8_t vprefix_len,
struct in6_addr vhost6, const char *vhostname,
const char *tftp_path, const char *bootfile,
struct in_addr vdhcp_start, struct in_addr vnameserver,
struct in6_addr vnameserver6, const char **vdnssearch,
void *opaque)
{
Slirp *slirp = g_malloc0(sizeof(Slirp));
slirp_init_once();
slirp->grand = g_rand_new();
slirp->restricted = restricted;
if_init(slirp);
ip_init(slirp);
ip6_init(slirp);
/* Initialise mbufs *after* setting the MTU */
m_init(slirp);
@ -221,6 +225,9 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->vnetwork_addr = vnetwork;
slirp->vnetwork_mask = vnetmask;
slirp->vhost_addr = vhost;
slirp->vprefix_addr6 = vprefix_addr6;
slirp->vprefix_len = vprefix_len;
slirp->vhost_addr6 = vhost6;
if (vhostname) {
pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
vhostname);
@ -229,6 +236,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->bootp_filename = g_strdup(bootfile);
slirp->vdhcp_startaddr = vdhcp_start;
slirp->vnameserver_addr = vnameserver;
slirp->vnameserver_addr6 = vnameserver6;
if (vdnssearch) {
translate_dnssearch(slirp, vdnssearch);
@ -251,8 +259,11 @@ void slirp_cleanup(Slirp *slirp)
unregister_savevm(NULL, "slirp", slirp);
ip_cleanup(slirp);
ip6_cleanup(slirp);
m_cleanup(slirp);
g_rand_free(slirp->grand);
g_free(slirp->vdnssearch);
g_free(slirp->tftp_prefix);
g_free(slirp->bootp_filename);
@ -568,7 +579,8 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error)
/*
* Continue tcp_input
*/
tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
tcp_input((struct mbuf *)NULL, sizeof(struct ip), so,
so->so_ffamily);
/* continue; */
} else {
ret = sowrite(so);
@ -617,7 +629,8 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error)
}
}
tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
tcp_input((struct mbuf *)NULL, sizeof(struct ip), so,
so->so_ffamily);
} /* SS_ISFCONNECTING */
#endif
}
@ -744,21 +757,28 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
arp_input(slirp, pkt, pkt_len);
break;
case ETH_P_IP:
case ETH_P_IPV6:
m = m_get(slirp);
if (!m)
return;
/* Note: we add to align the IP header */
if (M_FREEROOM(m) < pkt_len + 2) {
m_inc(m, pkt_len + 2);
/* Note: we add 2 to align the IP header on 4 bytes,
* and add the margin for the tcpiphdr overhead */
if (M_FREEROOM(m) < pkt_len + TCPIPHDR_DELTA + 2) {
m_inc(m, pkt_len + TCPIPHDR_DELTA + 2);
}
m->m_len = pkt_len + 2;
memcpy(m->m_data + 2, pkt, pkt_len);
m->m_len = pkt_len + TCPIPHDR_DELTA + 2;
memcpy(m->m_data + TCPIPHDR_DELTA + 2, pkt, pkt_len);
m->m_data += 2 + ETH_HLEN;
m->m_len -= 2 + ETH_HLEN;
m->m_data += TCPIPHDR_DELTA + 2 + ETH_HLEN;
m->m_len -= TCPIPHDR_DELTA + 2 + ETH_HLEN;
ip_input(m);
if (proto == ETH_P_IP) {
ip_input(m);
} else if (proto == ETH_P_IPV6) {
ip6_input(m);
}
break;
default:
break;
}
@ -826,6 +846,31 @@ static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,
}
}
/* Prepare the IPv6 packet to be sent to the ethernet device. Returns 1 if no
* packet should be sent, 0 if the packet must be re-queued, 2 if the packet
* is ready to go.
*/
static int if_encap6(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,
uint8_t ethaddr[ETH_ALEN])
{
const struct ip6 *ip6h = mtod(ifm, const struct ip6 *);
if (!ndp_table_search(slirp, ip6h->ip_dst, ethaddr)) {
if (!ifm->resolution_requested) {
ndp_send_ns(slirp, ip6h->ip_dst);
ifm->resolution_requested = true;
ifm->expiration_date =
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL;
}
return 0;
} else {
eh->h_proto = htons(ETH_P_IPV6);
in6_compute_ethaddr(ip6h->ip_src, eh->h_source);
/* Send this */
return 2;
}
}
/* Output the IP packet to the ethernet device. Returns 0 if the packet must be
* re-queued.
*/
@ -849,9 +894,15 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
}
break;
case IP6VERSION:
ret = if_encap6(slirp, ifm, eh, ethaddr);
if (ret < 2) {
return ret;
}
break;
default:
/* Do not assert while we don't manage IP6VERSION */
/* assert(0); */
g_assert_not_reached();
break;
}

View File

@ -111,6 +111,8 @@ void free(void *ptr);
#include <sys/stropts.h>
#endif
#include <glib.h>
#include "debug.h"
#include "qemu/queue.h"
@ -119,12 +121,14 @@ void free(void *ptr);
#include "libslirp.h"
#include "ip.h"
#include "ip6.h"
#include "tcp.h"
#include "tcp_timer.h"
#include "tcp_var.h"
#include "tcpip.h"
#include "udp.h"
#include "ip_icmp.h"
#include "ip6_icmp.h"
#include "mbuf.h"
#include "sbuf.h"
#include "socket.h"
@ -176,6 +180,23 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]);
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 */
struct in6_addr ip_addr; /* sender IP address */
} QEMU_PACKED;
#define NDP_TABLE_SIZE 16
typedef struct NdpTable {
struct ndpentry table[NDP_TABLE_SIZE];
int next_victim;
} NdpTable;
void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr,
uint8_t ethaddr[ETH_ALEN]);
bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr,
uint8_t out_ethaddr[ETH_ALEN]);
struct Slirp {
QTAILQ_ENTRY(Slirp) entry;
u_int time_fasttimo;
@ -186,8 +207,12 @@ struct Slirp {
struct in_addr vnetwork_addr;
struct in_addr vnetwork_mask;
struct in_addr vhost_addr;
struct in6_addr vprefix_addr6;
uint8_t vprefix_len;
struct in6_addr vhost_addr6;
struct in_addr vdhcp_startaddr;
struct in_addr vnameserver_addr;
struct in6_addr vnameserver_addr6;
struct in_addr client_ipaddr;
char client_hostname[33];
@ -234,6 +259,10 @@ struct Slirp {
struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
ArpTable arp_table;
NdpTable ndp_table;
GRand *grand;
QEMUTimer *ra_timer;
void *opaque;
};
@ -276,6 +305,7 @@ int translate_dnssearch(Slirp *s, const char ** names);
/* cksum.c */
int cksum(struct mbuf *m, int len);
int ip6_cksum(struct mbuf *m);
/* if.c */
void if_init(Slirp *);
@ -291,8 +321,16 @@ void ip_stripoptions(register struct mbuf *, struct mbuf *);
/* ip_output.c */
int ip_output(struct socket *, struct mbuf *);
/* ip6_input.c */
void ip6_init(Slirp *);
void ip6_cleanup(Slirp *);
void ip6_input(struct mbuf *);
/* ip6_output */
int ip6_output(struct socket *, struct mbuf *, int fast);
/* tcp_input.c */
void tcp_input(register struct mbuf *, int, struct socket *);
void tcp_input(register struct mbuf *, int, struct socket *, unsigned short af);
int tcp_mss(register struct tcpcb *, u_int);
/* tcp_output.c */
@ -303,7 +341,8 @@ void tcp_setpersist(register struct tcpcb *);
void tcp_init(Slirp *);
void tcp_cleanup(Slirp *);
void tcp_template(struct tcpcb *);
void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int);
void tcp_respond(struct tcpcb *, register struct tcpiphdr *,
register struct mbuf *, tcp_seq, tcp_seq, int, unsigned short);
struct tcpcb * tcp_newtcpcb(struct socket *);
struct tcpcb * tcp_close(register struct tcpcb *);
void tcp_sockclosed(struct tcpcb *);

View File

@ -463,7 +463,7 @@ sorecvfrom(struct socket *so)
DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n",
errno,strerror(errno)));
icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno));
} else {
icmp_reflect(so->so_m);
so->so_m = NULL; /* Don't m_free() it again! */
@ -483,7 +483,18 @@ sorecvfrom(struct socket *so)
if (!m) {
return;
}
m->m_data += IF_MAXLINKHDR;
switch (so->so_ffamily) {
case AF_INET:
m->m_data += IF_MAXLINKHDR + sizeof(struct udpiphdr);
break;
case AF_INET6:
m->m_data += IF_MAXLINKHDR + sizeof(struct ip6)
+ sizeof(struct udphdr);
break;
default:
g_assert_not_reached();
break;
}
/*
* XXX Shouldn't FIONREAD packets destined for port 53,
@ -505,13 +516,37 @@ sorecvfrom(struct socket *so)
DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n",
m->m_len, errno,strerror(errno)));
if(m->m_len<0) {
u_char code=ICMP_UNREACH_PORT;
/* Report error as ICMP */
switch (so->so_lfamily) {
uint8_t code;
case AF_INET:
code = ICMP_UNREACH_PORT;
if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
if (errno == EHOSTUNREACH) {
code = ICMP_UNREACH_HOST;
} else if (errno == ENETUNREACH) {
code = ICMP_UNREACH_NET;
}
DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code));
icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
DEBUG_MISC((dfd, " rx error, tx icmp ICMP_UNREACH:%i\n", code));
icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno));
break;
case AF_INET6:
code = ICMP6_UNREACH_PORT;
if (errno == EHOSTUNREACH) {
code = ICMP6_UNREACH_ADDRESS;
} else if (errno == ENETUNREACH) {
code = ICMP6_UNREACH_NO_ROUTE;
}
DEBUG_MISC((dfd, " rx error, tx icmp6 ICMP_UNREACH:%i\n", code));
icmp6_send_error(so->so_m, ICMP6_UNREACH, code);
break;
default:
g_assert_not_reached();
break;
}
m_free(m);
} else {
/*
@ -541,7 +576,12 @@ sorecvfrom(struct socket *so)
(struct sockaddr_in *) &daddr,
so->so_iptos);
break;
case AF_INET6:
udp6_output(so, m, (struct sockaddr_in6 *) &saddr,
(struct sockaddr_in6 *) &daddr);
break;
default:
g_assert_not_reached();
break;
}
} /* rx error */
@ -731,6 +771,7 @@ void sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
{
Slirp *slirp = so->slirp;
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
switch (addr->ss_family) {
case AF_INET:
@ -751,6 +792,19 @@ void sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
ntohs(sin->sin_port), inet_ntoa(sin->sin_addr)));
break;
case AF_INET6:
if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6,
slirp->vprefix_len)) {
if (in6_equal(&so->so_faddr6, &slirp->vnameserver_addr6)) {
/*if (get_dns_addr(&addr) < 0) {*/ /* TODO */
sin6->sin6_addr = in6addr_loopback;
/*}*/
} else {
sin6->sin6_addr = in6addr_loopback;
}
}
break;
default:
break;
}
@ -760,6 +814,7 @@ void sotranslate_in(struct socket *so, struct sockaddr_storage *addr)
{
Slirp *slirp = so->slirp;
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
switch (addr->ss_family) {
case AF_INET:
@ -776,6 +831,16 @@ void sotranslate_in(struct socket *so, struct sockaddr_storage *addr)
}
break;
case AF_INET6:
if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6,
slirp->vprefix_len)) {
if (in6_equal(&sin6->sin6_addr, &in6addr_loopback)
|| !in6_equal(&so->so_faddr6, &slirp->vhost_addr6)) {
sin6->sin6_addr = so->so_faddr6;
}
}
break;
default:
break;
}
@ -797,6 +862,13 @@ void sotranslate_accept(struct socket *so)
}
break;
case AF_INET6:
if (in6_equal(&so->so_faddr6, &in6addr_any) ||
in6_equal(&so->so_faddr6, &in6addr_loopback)) {
so->so_faddr6 = slirp->vhost_addr6;
}
break;
default:
break;
}

View File

@ -34,17 +34,23 @@ struct socket {
union { /* foreign host */
struct sockaddr_storage ss;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} fhost;
#define so_faddr fhost.sin.sin_addr
#define so_fport fhost.sin.sin_port
#define so_faddr6 fhost.sin6.sin6_addr
#define so_fport6 fhost.sin6.sin6_port
#define so_ffamily fhost.ss.ss_family
union { /* local host */
struct sockaddr_storage ss;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} lhost;
#define so_laddr lhost.sin.sin_addr
#define so_lport lhost.sin.sin_port
#define so_laddr6 lhost.sin6.sin6_addr
#define so_lport6 lhost.sin6.sin6_port
#define so_lfamily lhost.ss.ss_family
uint8_t so_iptos; /* Type of service */
@ -102,6 +108,13 @@ static inline int sockaddr_equal(struct sockaddr_storage *a,
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;
return (in6_equal(&a6->sin6_addr, &b6->sin6_addr)
&& a6->sin6_port == b6->sin6_port);
}
default:
g_assert_not_reached();
}

View File

@ -106,6 +106,8 @@ struct tcphdr {
*/
#undef TCP_MSS
#define TCP_MSS 1460
#undef TCP6_MSS
#define TCP6_MSS 1440
#undef TCP_MAXWIN
#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */

View File

@ -214,9 +214,10 @@ present:
* protocol specification dated September, 1981 very closely.
*/
void
tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
{
struct ip save_ip, *ip;
struct ip save_ip, *ip;
struct ip6 save_ip6, *ip6;
register struct tcpiphdr *ti;
caddr_t optp = NULL;
int optlen = 0;
@ -230,6 +231,7 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
int ret;
struct sockaddr_storage lhost, fhost;
struct sockaddr_in *lhost4, *fhost4;
struct sockaddr_in6 *lhost6, *fhost6;
struct ex_list *ex_ptr;
Slirp *slirp;
@ -256,37 +258,83 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
}
slirp = m->slirp;
/*
* Get IP and TCP header together in first mbuf.
* Note: IP leaves IP header in first mbuf.
*/
ti = mtod(m, struct tcpiphdr *);
if (iphlen > sizeof(struct ip )) {
ip_stripoptions(m, (struct mbuf *)0);
iphlen=sizeof(struct ip );
ip = mtod(m, struct ip *);
ip6 = mtod(m, struct ip6 *);
switch (af) {
case AF_INET:
if (iphlen > sizeof(struct ip)) {
ip_stripoptions(m, (struct mbuf *)0);
iphlen = sizeof(struct ip);
}
/* XXX Check if too short */
/*
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.
*/
save_ip = *ip;
save_ip.ip_len += iphlen;
/*
* Get IP and TCP header together in first mbuf.
* Note: IP leaves IP header in first mbuf.
*/
m->m_data -= sizeof(struct tcpiphdr) - sizeof(struct ip)
- sizeof(struct tcphdr);
m->m_len += sizeof(struct tcpiphdr) - sizeof(struct ip)
- sizeof(struct tcphdr);
ti = mtod(m, struct tcpiphdr *);
/*
* Checksum extended TCP header and data.
*/
tlen = ip->ip_len;
tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
memset(&ti->ih_mbuf, 0 , sizeof(struct mbuf_ptr));
memset(&ti->ti, 0, sizeof(ti->ti));
ti->ti_x0 = 0;
ti->ti_src = save_ip.ip_src;
ti->ti_dst = save_ip.ip_dst;
ti->ti_pr = save_ip.ip_p;
ti->ti_len = htons((uint16_t)tlen);
break;
case AF_INET6:
/*
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.
*/
save_ip6 = *ip6;
/*
* Get IP and TCP header together in first mbuf.
* Note: IP leaves IP header in first mbuf.
*/
m->m_data -= sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+ sizeof(struct tcphdr));
m->m_len += sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+ sizeof(struct tcphdr));
ti = mtod(m, struct tcpiphdr *);
tlen = ip6->ip_pl;
tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
memset(&ti->ih_mbuf, 0 , sizeof(struct mbuf_ptr));
memset(&ti->ti, 0, sizeof(ti->ti));
ti->ti_x0 = 0;
ti->ti_src6 = save_ip6.ip_src;
ti->ti_dst6 = save_ip6.ip_dst;
ti->ti_nh6 = save_ip6.ip_nh;
ti->ti_len = htons((uint16_t)tlen);
break;
default:
g_assert_not_reached();
}
/* XXX Check if too short */
/*
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.
*/
ip=mtod(m, struct ip *);
save_ip = *ip;
save_ip.ip_len+= iphlen;
/*
* Checksum extended TCP header and data.
*/
tlen = ((struct ip *)ti)->ip_len;
tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
ti->ti_x1 = 0;
ti->ti_len = htons((uint16_t)tlen);
len = sizeof(struct ip ) + tlen;
if(cksum(m, len)) {
goto drop;
len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen);
if (cksum(m, len)) {
goto drop;
}
/*
@ -323,14 +371,28 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
* Locate pcb for segment.
*/
findso:
lhost.ss_family = AF_INET;
lhost4 = (struct sockaddr_in *) &lhost;
lhost4->sin_addr = ti->ti_src;
lhost4->sin_port = ti->ti_sport;
fhost.ss_family = AF_INET;
fhost4 = (struct sockaddr_in *) &fhost;
fhost4->sin_addr = ti->ti_dst;
fhost4->sin_port = ti->ti_dport;
lhost.ss_family = af;
fhost.ss_family = af;
switch (af) {
case AF_INET:
lhost4 = (struct sockaddr_in *) &lhost;
lhost4->sin_addr = ti->ti_src;
lhost4->sin_port = ti->ti_sport;
fhost4 = (struct sockaddr_in *) &fhost;
fhost4->sin_addr = ti->ti_dst;
fhost4->sin_port = ti->ti_dport;
break;
case AF_INET6:
lhost6 = (struct sockaddr_in6 *) &lhost;
lhost6->sin6_addr = ti->ti_src6;
lhost6->sin6_port = ti->ti_sport;
fhost6 = (struct sockaddr_in6 *) &fhost;
fhost6->sin6_addr = ti->ti_dst6;
fhost6->sin6_port = ti->ti_dport;
break;
default:
g_assert_not_reached();
}
so = solookup(&slirp->tcp_last_so, &slirp->tcb, &lhost, &fhost);
@ -380,8 +442,18 @@ findso:
so->lhost.ss = lhost;
so->fhost.ss = fhost;
if ((so->so_iptos = tcp_tos(so)) == 0)
so->so_iptos = ((struct ip *)ti)->ip_tos;
so->so_iptos = tcp_tos(so);
if (so->so_iptos == 0) {
switch (af) {
case AF_INET:
so->so_iptos = ((struct ip *)ti)->ip_tos;
break;
case AF_INET6:
break;
default:
g_assert_not_reached();
}
}
tp = sototcpcb(so);
tp->t_state = TCPS_LISTEN;
@ -560,8 +632,9 @@ findso:
* If this is destined for the control address, then flag to
* tcp_ctl once connected, otherwise connect
*/
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
if (af == AF_INET &&
(so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr &&
so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) {
/* May be an add exec */
@ -588,23 +661,56 @@ findso:
if ((tcp_fconnect(so, so->so_ffamily) == -1) &&
(errno != EINPROGRESS) && (errno != EWOULDBLOCK)
) {
u_char code=ICMP_UNREACH_NET;
uint8_t code;
DEBUG_MISC((dfd, " tcp fconnect errno = %d-%s\n",
errno,strerror(errno)));
if(errno == ECONNREFUSED) {
/* ACK the SYN, send RST to refuse the connection */
tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
TH_RST|TH_ACK);
tcp_respond(tp, ti, m, ti->ti_seq + 1, (tcp_seq) 0,
TH_RST | TH_ACK, af);
} else {
if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
switch (af) {
case AF_INET:
code = ICMP_UNREACH_NET;
if (errno == EHOSTUNREACH) {
code = ICMP_UNREACH_HOST;
}
break;
case AF_INET6:
code = ICMP6_UNREACH_NO_ROUTE;
if (errno == EHOSTUNREACH) {
code = ICMP6_UNREACH_ADDRESS;
}
break;
default:
g_assert_not_reached();
}
HTONL(ti->ti_seq); /* restore tcp header */
HTONL(ti->ti_ack);
HTONS(ti->ti_win);
HTONS(ti->ti_urp);
m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
*ip=save_ip;
icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno));
switch (af) {
case AF_INET:
m->m_data += sizeof(struct tcpiphdr) - sizeof(struct ip)
- sizeof(struct tcphdr);
m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct ip)
- sizeof(struct tcphdr);
*ip = save_ip;
icmp_send_error(m, ICMP_UNREACH, code, 0, strerror(errno));
break;
case AF_INET6:
m->m_data += sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+ sizeof(struct tcphdr));
m->m_len -= sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+ sizeof(struct tcphdr));
*ip6 = save_ip6;
icmp6_send_error(m, ICMP6_UNREACH, code);
break;
default:
g_assert_not_reached();
}
}
tcp_close(tp);
m_free(m);
@ -1276,11 +1382,11 @@ dropafterack:
dropwithreset:
/* reuses m if m!=NULL, m_free() unnecessary */
if (tiflags & TH_ACK)
tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST, af);
else {
if (tiflags & TH_SYN) ti->ti_len++;
tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
TH_RST|TH_ACK);
tcp_respond(tp, ti, m, ti->ti_seq + ti->ti_len, (tcp_seq) 0,
TH_RST | TH_ACK, af);
}
return;
@ -1471,7 +1577,19 @@ tcp_mss(struct tcpcb *tp, u_int offer)
DEBUG_ARG("tp = %p", tp);
DEBUG_ARG("offer = %d", offer);
mss = min(IF_MTU, IF_MRU) - sizeof(struct tcpiphdr);
switch (so->so_ffamily) {
case AF_INET:
mss = min(IF_MTU, IF_MRU) - sizeof(struct tcphdr)
+ sizeof(struct ip);
break;
case AF_INET6:
mss = min(IF_MTU, IF_MRU) - sizeof(struct tcphdr)
+ sizeof(struct ip6);
break;
default:
g_assert_not_reached();
}
if (offer)
mss = min(mss, offer);
mss = max(mss, 32);

View File

@ -61,7 +61,9 @@ tcp_output(struct tcpcb *tp)
register long len, win;
int off, flags, error;
register struct mbuf *m;
register struct tcpiphdr *ti;
register struct tcpiphdr *ti, tcpiph_save;
struct ip *ip;
struct ip6 *ip6;
u_char opt[MAX_TCPOPTLEN];
unsigned optlen, hdrlen;
int idle, sendalot;
@ -447,16 +449,45 @@ send:
* the template, but need a way to checksum without them.
*/
m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
tcpiph_save = *mtod(m, struct tcpiphdr *);
{
switch (so->so_ffamily) {
case AF_INET:
m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
- sizeof(struct ip);
m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
- sizeof(struct ip);
ip = mtod(m, struct ip *);
((struct ip *)ti)->ip_len = m->m_len;
ip->ip_len = m->m_len;
ip->ip_dst = tcpiph_save.ti_dst;
ip->ip_src = tcpiph_save.ti_src;
ip->ip_p = tcpiph_save.ti_pr;
((struct ip *)ti)->ip_ttl = IPDEFTTL;
((struct ip *)ti)->ip_tos = so->so_iptos;
ip->ip_ttl = IPDEFTTL;
ip->ip_tos = so->so_iptos;
error = ip_output(so, m);
break;
case AF_INET6:
m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
- sizeof(struct ip6);
m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
- sizeof(struct ip6);
ip6 = mtod(m, struct ip6 *);
ip6->ip_pl = tcpiph_save.ti_len;
ip6->ip_dst = tcpiph_save.ti_dst6;
ip6->ip_src = tcpiph_save.ti_src6;
ip6->ip_nh = tcpiph_save.ti_nh6;
error = ip6_output(so, m, 0);
break;
default:
g_assert_not_reached();
}
error = ip_output(so, m);
}
if (error) {
out:
return (error);

View File

@ -76,13 +76,30 @@ tcp_template(struct tcpcb *tp)
register struct tcpiphdr *n = &tp->t_template;
n->ti_mbuf = NULL;
n->ti_x1 = 0;
n->ti_pr = IPPROTO_TCP;
n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
n->ti_src = so->so_faddr;
n->ti_dst = so->so_laddr;
n->ti_sport = so->so_fport;
n->ti_dport = so->so_lport;
memset(&n->ti, 0, sizeof(n->ti));
n->ti_x0 = 0;
switch (so->so_ffamily) {
case AF_INET:
n->ti_pr = IPPROTO_TCP;
n->ti_len = htons(sizeof(struct tcphdr));
n->ti_src = so->so_faddr;
n->ti_dst = so->so_laddr;
n->ti_sport = so->so_fport;
n->ti_dport = so->so_lport;
break;
case AF_INET6:
n->ti_nh6 = IPPROTO_TCP;
n->ti_len = htons(sizeof(struct tcphdr));
n->ti_src6 = so->so_faddr6;
n->ti_dst6 = so->so_laddr6;
n->ti_sport = so->so_fport6;
n->ti_dport = so->so_lport6;
break;
default:
g_assert_not_reached();
}
n->ti_seq = 0;
n->ti_ack = 0;
@ -109,7 +126,7 @@ tcp_template(struct tcpcb *tp)
*/
void
tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
tcp_seq ack, tcp_seq seq, int flags)
tcp_seq ack, tcp_seq seq, int flags, unsigned short af)
{
register int tlen;
int win = 0;
@ -131,6 +148,7 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
m->m_data += IF_MAXLINKHDR;
*mtod(m, struct tcpiphdr *) = *ti;
ti = mtod(m, struct tcpiphdr *);
memset(&ti->ti, 0, sizeof(ti->ti));
flags = TH_ACK;
} else {
/*
@ -142,16 +160,26 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
m->m_len = sizeof (struct tcpiphdr);
tlen = 0;
#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
xchg(ti->ti_dport, ti->ti_sport, uint16_t);
switch (af) {
case AF_INET:
xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
xchg(ti->ti_dport, ti->ti_sport, uint16_t);
break;
case AF_INET6:
xchg(ti->ti_dst6, ti->ti_src6, struct in6_addr);
xchg(ti->ti_dport, ti->ti_sport, uint16_t);
break;
default:
g_assert_not_reached();
}
#undef xchg
}
ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
tlen += sizeof (struct tcpiphdr);
m->m_len = tlen;
ti->ti_mbuf = NULL;
ti->ti_x1 = 0;
ti->ti_mbuf = NULL;
ti->ti_x0 = 0;
ti->ti_seq = htonl(seq);
ti->ti_ack = htonl(ack);
ti->ti_x2 = 0;
@ -164,14 +192,49 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
ti->ti_urp = 0;
ti->ti_sum = 0;
ti->ti_sum = cksum(m, tlen);
((struct ip *)ti)->ip_len = tlen;
if(flags & TH_RST)
((struct ip *)ti)->ip_ttl = MAXTTL;
else
((struct ip *)ti)->ip_ttl = IPDEFTTL;
struct tcpiphdr tcpiph_save = *(mtod(m, struct tcpiphdr *));
struct ip *ip;
struct ip6 *ip6;
(void) ip_output((struct socket *)0, m);
switch (af) {
case AF_INET:
m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
- sizeof(struct ip);
m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
- sizeof(struct ip);
ip = mtod(m, struct ip *);
ip->ip_len = tlen;
ip->ip_dst = tcpiph_save.ti_dst;
ip->ip_src = tcpiph_save.ti_src;
ip->ip_p = tcpiph_save.ti_pr;
if (flags & TH_RST) {
ip->ip_ttl = MAXTTL;
} else {
ip->ip_ttl = IPDEFTTL;
}
ip_output(NULL, m);
break;
case AF_INET6:
m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
- sizeof(struct ip6);
m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
- sizeof(struct ip6);
ip6 = mtod(m, struct ip6 *);
ip6->ip_pl = tlen;
ip6->ip_dst = tcpiph_save.ti_dst6;
ip6->ip_src = tcpiph_save.ti_src6;
ip6->ip_nh = tcpiph_save.ti_nh6;
ip6_output(NULL, m, 0);
break;
default:
g_assert_not_reached();
}
}
/*
@ -190,7 +253,7 @@ tcp_newtcpcb(struct socket *so)
memset((char *) tp, 0, sizeof(struct tcpcb));
tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
tp->t_maxseg = TCP_MSS;
tp->t_maxseg = (so->so_ffamily == AF_INET) ? TCP_MSS : TCP6_MSS;
tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
tp->t_socket = so;
@ -375,8 +438,8 @@ void tcp_connect(struct socket *inso)
{
Slirp *slirp = inso->slirp;
struct socket *so;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(struct sockaddr_in);
struct sockaddr_storage addr;
socklen_t addrlen = sizeof(struct sockaddr_storage);
struct tcpcb *tp;
int s, opt;
@ -401,9 +464,8 @@ void tcp_connect(struct socket *inso)
free(so); /* NOT sofree */
return;
}
so->so_lfamily = AF_INET;
so->so_laddr = inso->so_laddr;
so->so_lport = inso->so_lport;
so->lhost = inso->lhost;
so->so_ffamily = inso->so_ffamily;
}
tcp_mss(sototcpcb(so), 0);
@ -419,7 +481,7 @@ void tcp_connect(struct socket *inso)
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
socket_set_nodelay(s);
so->fhost.sin = addr;
so->fhost.ss = addr;
sotranslate_accept(so);
/* Close the accept() socket, set right state */

View File

@ -278,7 +278,8 @@ tcp_timers(register struct tcpcb *tp, int timer)
* correspondent TCP to respond.
*/
tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
tp->rcv_nxt, tp->snd_una - 1, 0);
tp->rcv_nxt, tp->snd_una - 1, 0,
tp->t_socket->so_ffamily);
tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
} else
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;

View File

@ -37,15 +37,32 @@
* Tcp+ip header, after ip options removed.
*/
struct tcpiphdr {
struct ipovly ti_i; /* overlaid ip structure */
struct tcphdr ti_t; /* tcp header */
struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */
union {
struct {
struct in_addr ih_src; /* source internet address */
struct in_addr ih_dst; /* destination internet address */
uint8_t ih_x1; /* (unused) */
uint8_t ih_pr; /* protocol */
} ti_i4;
struct {
struct in6_addr ih_src;
struct in6_addr ih_dst;
uint8_t ih_x1;
uint8_t ih_nh;
} ti_i6;
} ti;
uint16_t ti_x0;
uint16_t ti_len; /* protocol length */
struct tcphdr ti_t; /* tcp header */
};
#define ti_mbuf ti_i.ih_mbuf.mptr
#define ti_x1 ti_i.ih_x1
#define ti_pr ti_i.ih_pr
#define ti_len ti_i.ih_len
#define ti_src ti_i.ih_src
#define ti_dst ti_i.ih_dst
#define ti_mbuf ih_mbuf.mptr
#define ti_pr ti.ti_i4.ih_pr
#define ti_src ti.ti_i4.ih_src
#define ti_dst ti.ti_i4.ih_dst
#define ti_src6 ti.ti_i6.ih_src
#define ti_dst6 ti.ti_i6.ih_dst
#define ti_nh6 ti.ti_i6.ih_nh
#define ti_sport ti_t.th_sport
#define ti_dport ti_t.th_dport
#define ti_seq ti_t.th_seq
@ -65,6 +82,13 @@ struct tcpiphdr {
#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T))
#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T))
/* 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) & ~3))
/*
* Just a clean way to get to the first byte
* of the packet

View File

@ -46,7 +46,8 @@ static void tftp_session_terminate(struct tftp_session *spt)
spt->slirp = NULL;
}
static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas,
struct tftp_t *tp)
{
struct tftp_session *spt;
int k;
@ -68,7 +69,7 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
found:
memset(spt, 0, sizeof(*spt));
memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
spt->client_addr = *srcsas;
spt->fd = -1;
spt->client_port = tp->udp.uh_sport;
spt->slirp = slirp;
@ -78,7 +79,8 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
return k;
}
static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
struct tftp_t *tp)
{
struct tftp_session *spt;
int k;
@ -87,7 +89,7 @@ static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
spt = &slirp->tftp_sessions[k];
if (tftp_session_in_use(spt)) {
if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
if (sockaddr_equal(&spt->client_addr, srcsas)) {
if (spt->client_port == tp->udp.uh_sport) {
return k;
}
@ -120,11 +122,53 @@ static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr,
return bytes_read;
}
static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt,
struct mbuf *m)
{
struct tftp_t *tp;
memset(m->m_data, 0, m->m_size);
m->m_data += IF_MAXLINKHDR;
if (spt->client_addr.ss_family == AF_INET6) {
m->m_data += sizeof(struct ip6);
} else {
m->m_data += sizeof(struct ip);
}
tp = (void *)m->m_data;
m->m_data += sizeof(struct udphdr);
return tp;
}
static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
struct tftp_t *recv_tp)
{
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;
da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr;
da6.sin6_port = spt->client_port;
udp6_output(NULL, m, &sa6, &da6);
} else {
struct sockaddr_in sa4, da4;
sa4.sin_addr = spt->slirp->vhost_addr;
sa4.sin_port = recv_tp->udp.uh_dport;
da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr;
da4.sin_port = spt->client_port;
udp_output(NULL, m, &sa4, &da4, IPTOS_LOWDELAY);
}
}
static int tftp_send_oack(struct tftp_session *spt,
const char *keys[], uint32_t values[], int nb,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
struct mbuf *m;
struct tftp_t *tp;
int i, n = 0;
@ -132,13 +176,9 @@ static int tftp_send_oack(struct tftp_session *spt,
m = m_get(spt->slirp);
if (!m)
return -1;
return -1;
memset(m->m_data, 0, m->m_size);
m->m_data += IF_MAXLINKHDR;
tp = (void *)m->m_data;
m->m_data += sizeof(struct udpiphdr);
tp = tftp_prep_mbuf_data(spt, m);
tp->tp_op = htons(TFTP_OACK);
for (i = 0; i < nb; i++) {
@ -148,15 +188,8 @@ static int tftp_send_oack(struct tftp_session *spt,
values[i]) + 1;
}
saddr.sin_addr = recv_tp->ip.ip_dst;
saddr.sin_port = recv_tp->udp.uh_dport;
daddr.sin_addr = spt->client_ip;
daddr.sin_port = spt->client_port;
m->m_len = sizeof(struct tftp_t) - 514 + n -
sizeof(struct ip) - sizeof(struct udphdr);
udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
m->m_len = sizeof(struct tftp_t) - 514 + n - sizeof(struct udphdr);
tftp_udp_output(spt, m, recv_tp);
return 0;
}
@ -165,7 +198,6 @@ static void tftp_send_error(struct tftp_session *spt,
uint16_t errorcode, const char *msg,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
struct mbuf *m;
struct tftp_t *tp;
@ -177,24 +209,15 @@ static void tftp_send_error(struct tftp_session *spt,
memset(m->m_data, 0, m->m_size);
m->m_data += IF_MAXLINKHDR;
tp = (void *)m->m_data;
m->m_data += sizeof(struct udpiphdr);
tp = tftp_prep_mbuf_data(spt, m);
tp->tp_op = htons(TFTP_ERROR);
tp->x.tp_error.tp_error_code = htons(errorcode);
pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
saddr.sin_addr = recv_tp->ip.ip_dst;
saddr.sin_port = recv_tp->udp.uh_dport;
daddr.sin_addr = spt->client_ip;
daddr.sin_port = spt->client_port;
m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
sizeof(struct ip) - sizeof(struct udphdr);
udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg)
- sizeof(struct udphdr);
tftp_udp_output(spt, m, recv_tp);
out:
tftp_session_terminate(spt);
@ -203,7 +226,6 @@ out:
static void tftp_send_next_block(struct tftp_session *spt,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
struct mbuf *m;
struct tftp_t *tp;
int nobytes;
@ -216,19 +238,11 @@ static void tftp_send_next_block(struct tftp_session *spt,
memset(m->m_data, 0, m->m_size);
m->m_data += IF_MAXLINKHDR;
tp = (void *)m->m_data;
m->m_data += sizeof(struct udpiphdr);
tp = tftp_prep_mbuf_data(spt, m);
tp->tp_op = htons(TFTP_DATA);
tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
saddr.sin_addr = recv_tp->ip.ip_dst;
saddr.sin_port = recv_tp->udp.uh_dport;
daddr.sin_addr = spt->client_ip;
daddr.sin_port = spt->client_port;
nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512);
if (nobytes < 0) {
@ -241,10 +255,8 @@ static void tftp_send_next_block(struct tftp_session *spt,
return;
}
m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
sizeof(struct ip) - sizeof(struct udphdr);
udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - sizeof(struct udphdr);
tftp_udp_output(spt, m, recv_tp);
if (nobytes == 512) {
tftp_session_update(spt);
@ -256,7 +268,8 @@ static void tftp_send_next_block(struct tftp_session *spt,
spt->block_nr++;
}
static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas,
struct tftp_t *tp, int pktlen)
{
struct tftp_session *spt;
int s, k;
@ -267,12 +280,12 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
int nb_options = 0;
/* check if a session already exists and if so terminate it */
s = tftp_session_find(slirp, tp);
s = tftp_session_find(slirp, srcsas, tp);
if (s >= 0) {
tftp_session_terminate(&slirp->tftp_sessions[s]);
}
s = tftp_session_allocate(slirp, tp);
s = tftp_session_allocate(slirp, srcsas, tp);
if (s < 0) {
return;
@ -397,11 +410,12 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
tftp_send_next_block(spt, tp);
}
static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas,
struct tftp_t *tp, int pktlen)
{
int s;
s = tftp_session_find(slirp, tp);
s = tftp_session_find(slirp, srcsas, tp);
if (s < 0) {
return;
@ -410,11 +424,12 @@ static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
tftp_send_next_block(&slirp->tftp_sessions[s], tp);
}
static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
struct tftp_t *tp, int pktlen)
{
int s;
s = tftp_session_find(slirp, tp);
s = tftp_session_find(slirp, srcsas, tp);
if (s < 0) {
return;
@ -423,21 +438,21 @@ static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
tftp_session_terminate(&slirp->tftp_sessions[s]);
}
void tftp_input(struct mbuf *m)
void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m)
{
struct tftp_t *tp = (struct tftp_t *)m->m_data;
switch(ntohs(tp->tp_op)) {
case TFTP_RRQ:
tftp_handle_rrq(m->slirp, tp, m->m_len);
tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len);
break;
case TFTP_ACK:
tftp_handle_ack(m->slirp, tp, m->m_len);
tftp_handle_ack(m->slirp, srcsas, tp, m->m_len);
break;
case TFTP_ERROR:
tftp_handle_error(m->slirp, tp, m->m_len);
tftp_handle_error(m->slirp, srcsas, tp, m->m_len);
break;
}
}

View File

@ -16,7 +16,6 @@
#define TFTP_FILENAME_MAX 512
struct tftp_t {
struct ip ip;
struct udphdr udp;
uint16_t tp_op;
union {
@ -30,20 +29,20 @@ struct tftp_t {
} tp_error;
char tp_buf[512 + 2];
} x;
};
} __attribute__((packed));
struct tftp_session {
Slirp *slirp;
char *filename;
int fd;
struct in_addr client_ip;
struct sockaddr_storage client_addr;
uint16_t client_port;
uint32_t block_nr;
int timestamp;
};
void tftp_input(struct mbuf *m);
void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m);
#endif

View File

@ -128,6 +128,11 @@ udp_input(register struct mbuf *m, int iphlen)
}
}
lhost.ss_family = AF_INET;
lhost4 = (struct sockaddr_in *) &lhost;
lhost4->sin_addr = ip->ip_src;
lhost4->sin_port = uh->uh_sport;
/*
* handle DHCP/BOOTP
*/
@ -143,7 +148,11 @@ udp_input(register struct mbuf *m, int iphlen)
*/
if (ntohs(uh->uh_dport) == TFTP_SERVER &&
ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
tftp_input(m);
m->m_data += iphlen;
m->m_len -= iphlen;
tftp_input(&lhost, m);
m->m_data -= iphlen;
m->m_len += iphlen;
goto bad;
}
@ -154,11 +163,6 @@ udp_input(register struct mbuf *m, int iphlen)
/*
* Locate pcb for datagram.
*/
lhost.ss_family = AF_INET;
lhost4 = (struct sockaddr_in *) &lhost;
lhost4->sin_addr = ip->ip_src;
lhost4->sin_port = uh->uh_sport;
so = solookup(&slirp->udp_last_so, &slirp->udb, &lhost, NULL);
if (so == NULL) {
@ -209,7 +213,8 @@ udp_input(register struct mbuf *m, int iphlen)
m->m_data -= iphlen;
*ip=save_ip;
DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0,
strerror(errno));
goto bad;
}

View File

@ -83,4 +83,9 @@ struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int udp_output(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr,
int iptos);
void udp6_input(register struct mbuf *);
int udp6_output(struct socket *so, struct mbuf *m,
struct sockaddr_in6 *saddr, struct sockaddr_in6 *daddr);
#endif

169
slirp/udp6.c Normal file
View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2013
* Guillaume Subiron
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "slirp.h"
#include "qemu/osdep.h"
#include "udp.h"
void udp6_input(struct mbuf *m)
{
Slirp *slirp = m->slirp;
struct ip6 *ip, save_ip;
struct udphdr *uh;
int iphlen = sizeof(struct ip6);
int len;
struct socket *so;
struct sockaddr_in6 lhost;
DEBUG_CALL("udp6_input");
DEBUG_ARG("m = %lx", (long)m);
if (slirp->restricted) {
goto bad;
}
ip = mtod(m, struct ip6 *);
m->m_len -= iphlen;
m->m_data += iphlen;
uh = mtod(m, struct udphdr *);
m->m_len += iphlen;
m->m_data -= iphlen;
if (ip6_cksum(m)) {
goto bad;
}
len = ntohs((uint16_t)uh->uh_ulen);
/*
* Make mbuf data length reflect UDP length.
* If not enough data to reflect UDP length, drop.
*/
if (ntohs(ip->ip_pl) != len) {
if (len > ntohs(ip->ip_pl)) {
goto bad;
}
m_adj(m, len - ntohs(ip->ip_pl));
ip->ip_pl = htons(len);
}
/*
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.
*/
save_ip = *ip;
/* Locate pcb for datagram. */
lhost.sin6_family = AF_INET6;
lhost.sin6_addr = ip->ip_src;
lhost.sin6_port = uh->uh_sport;
/* TODO handle DHCP/BOOTP */
/* handle TFTP */
if (ntohs(uh->uh_dport) == TFTP_SERVER &&
!memcmp(ip->ip_dst.s6_addr, slirp->vhost_addr6.s6_addr, 16)) {
m->m_data += iphlen;
m->m_len -= iphlen;
tftp_input((struct sockaddr_storage *)&lhost, m);
m->m_data -= iphlen;
m->m_len += iphlen;
goto bad;
}
so = solookup(&slirp->udp_last_so, &slirp->udb,
(struct sockaddr_storage *) &lhost, NULL);
if (so == NULL) {
/* If there's no socket for this packet, create one. */
so = socreate(slirp);
if (!so) {
goto bad;
}
if (udp_attach(so, AF_INET6) == -1) {
DEBUG_MISC((dfd, " udp6_attach errno = %d-%s\n",
errno, strerror(errno)));
sofree(so);
goto bad;
}
/* Setup fields */
so->so_lfamily = AF_INET6;
so->so_laddr6 = ip->ip_src;
so->so_lport6 = uh->uh_sport;
}
so->so_ffamily = AF_INET6;
so->so_faddr6 = ip->ip_dst; /* XXX */
so->so_fport6 = uh->uh_dport; /* XXX */
iphlen += sizeof(struct udphdr);
m->m_len -= iphlen;
m->m_data += iphlen;
/*
* Now we sendto() the packet.
*/
if (sosendto(so, m) == -1) {
m->m_len += iphlen;
m->m_data -= iphlen;
*ip = save_ip;
DEBUG_MISC((dfd, "udp tx errno = %d-%s\n", errno, strerror(errno)));
/* TODO: ICMPv6 error */
/*icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));*/
goto bad;
}
m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
/* restore the orig mbuf packet */
m->m_len += iphlen;
m->m_data -= iphlen;
*ip = save_ip;
so->so_m = m;
return;
bad:
m_free(m);
}
int udp6_output(struct socket *so, struct mbuf *m,
struct sockaddr_in6 *saddr, struct sockaddr_in6 *daddr)
{
struct ip6 *ip;
struct udphdr *uh;
DEBUG_CALL("udp6_output");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("m = %lx", (long)m);
/* adjust for header */
m->m_data -= sizeof(struct udphdr);
m->m_len += sizeof(struct udphdr);
uh = mtod(m, struct udphdr *);
m->m_data -= sizeof(struct ip6);
m->m_len += sizeof(struct ip6);
ip = mtod(m, struct ip6 *);
/* Build IP header */
ip->ip_pl = htons(m->m_len - sizeof(struct ip6));
ip->ip_nh = IPPROTO_UDP;
ip->ip_src = saddr->sin6_addr;
ip->ip_dst = daddr->sin6_addr;
/* Build UDP header */
uh->uh_sport = saddr->sin6_port;
uh->uh_dport = daddr->sin6_port;
uh->uh_ulen = ip->ip_pl;
uh->uh_sum = 0;
uh->uh_sum = ip6_cksum(m);
if (uh->uh_sum == 0) {
uh->uh_sum = 0xffff;
}
return ip6_output(so, m, 0);
}