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:
commit
a6cdb77f81
31
net/net.c
31
net/net.c
@ -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 {
|
||||
|
81
net/slirp.c
81
net/slirp.c
@ -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) {
|
||||
|
@ -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'],
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
141
slirp/ip6.h
Normal 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(ð[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
416
slirp/ip6_icmp.c
Normal 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
213
slirp/ip6_icmp.h
Normal 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
73
slirp/ip6_input.c
Normal 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
40
slirp/ip6_output.c
Normal 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;
|
||||
}
|
@ -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! */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
90
slirp/ndp_table.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 *);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
112
slirp/tcp_subr.c
112
slirp/tcp_subr.c
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
133
slirp/tftp.c
133
slirp/tftp.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
19
slirp/udp.c
19
slirp/udp.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
169
slirp/udp6.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user