passt: Introduce a DHCPv6 server
This implementation, similarly to the IPv4 DHCP one, hands out a single address, which is the same as the upstream address for the host. This avoids the need for address translation as long as the client runs a DHCPv6 client. The NDP "Managed" flag is now set in Router Advertisements. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
parent
6b1a9f0d34
commit
4aa8e54a30
6 changed files with 530 additions and 19 deletions
4
Makefile
4
Makefile
|
@ -2,8 +2,8 @@ CFLAGS += -Wall -Wextra -pedantic
|
||||||
|
|
||||||
all: passt qrap
|
all: passt qrap
|
||||||
|
|
||||||
passt: passt.c passt.h arp.c arp.h dhcp.c dhcp.h ndp.c ndp.h siphash.c siphash.h tap.c tap.h icmp.c icmp.h tcp.c tcp.h udp.c udp.h util.c util.h
|
passt: passt.c passt.h arp.c arp.h dhcp.c dhcp.h dhcpv6.c dhcpv6.h ndp.c ndp.h siphash.c siphash.h tap.c tap.h icmp.c icmp.h tcp.c tcp.h udp.c udp.h util.c util.h
|
||||||
$(CC) $(CFLAGS) passt.c arp.c dhcp.c ndp.c siphash.c tap.c icmp.c tcp.c udp.c util.c -o passt
|
$(CC) $(CFLAGS) passt.c arp.c dhcp.c dhcpv6.c ndp.c siphash.c tap.c icmp.c tcp.c udp.c util.c -o passt
|
||||||
|
|
||||||
qrap: qrap.c passt.h
|
qrap: qrap.c passt.h
|
||||||
$(CC) $(CFLAGS) qrap.c -o qrap
|
$(CC) $(CFLAGS) qrap.c -o qrap
|
||||||
|
|
21
README.md
21
README.md
|
@ -171,7 +171,7 @@ capabilities, as we don't need to create any interface.
|
||||||
_passt_ provides some minimalistic implementations of networking services that
|
_passt_ provides some minimalistic implementations of networking services that
|
||||||
can't practically run on the host:
|
can't practically run on the host:
|
||||||
|
|
||||||
* [ARP proxy](https://passt.top/passt/tree/arp.c), that resolve the address of
|
* [ARP proxy](https://passt.top/passt/tree/arp.c), that resolves the address of
|
||||||
the host (which is used as gateway) to the original MAC address of the host
|
the host (which is used as gateway) to the original MAC address of the host
|
||||||
* [DHCP server](https://passt.top/passt/tree/dhcp.c), a simple implementation
|
* [DHCP server](https://passt.top/passt/tree/dhcp.c), a simple implementation
|
||||||
handing out one single IPv4 address to the guest, namely, the same address as
|
handing out one single IPv4 address to the guest, namely, the same address as
|
||||||
|
@ -179,11 +179,10 @@ can't practically run on the host:
|
||||||
nameservers configured on the host
|
nameservers configured on the host
|
||||||
* [NDP proxy](https://passt.top/passt/tree/ndp.c), which can also assign prefix
|
* [NDP proxy](https://passt.top/passt/tree/ndp.c), which can also assign prefix
|
||||||
and nameserver using SLAAC
|
and nameserver using SLAAC
|
||||||
* _to be done_: DHCPv6 server: right now, the guest gets the same _prefix_ as
|
* [DHCPv6 server](https://passt.top/passt/tree/dhcpv6.c): a simple
|
||||||
the host, but not the same address, because the suffix is generated from the
|
implementation handing out one single IPv6 address to the guest, namely, the
|
||||||
MAC address of the virtual machine, so we currently have to translate packet
|
the same address as the first one configured for the upstream host interface,
|
||||||
addresses back and forth. With a DHCPv6 server, we could simply assign the
|
and passing the first nameserver configured on the host
|
||||||
host address to the guest
|
|
||||||
|
|
||||||
## Addresses
|
## Addresses
|
||||||
|
|
||||||
|
@ -192,11 +191,11 @@ interface of the host, and the same default gateway as the default gateway of
|
||||||
the host. Addresses are never translated.
|
the host. Addresses are never translated.
|
||||||
|
|
||||||
For IPv6, the guest is assigned, via SLAAC, the same prefix as the upstream
|
For IPv6, the guest is assigned, via SLAAC, the same prefix as the upstream
|
||||||
interface of the host, and the same default route as the default route of the
|
interface of the host, the same default route as the default route of the
|
||||||
host. This means that the guest will typically have a different address, and
|
host, and, if a DHCPv6 client is running on the guest, also the same address as
|
||||||
the destination address is translated for packets going to the guest. This will
|
the upstream address of the host. This means that, with a DHCPv6 client on the
|
||||||
be avoided in the future once a minimalistic DHCPv6 server is implemented in
|
guest, addresses don't need to be translated. Should the client use a different
|
||||||
_passt_.
|
address, the destination address is translated for packets going to the guest.
|
||||||
|
|
||||||
## Protocols
|
## Protocols
|
||||||
|
|
||||||
|
|
502
dhcpv6.c
Normal file
502
dhcpv6.c
Normal file
|
@ -0,0 +1,502 @@
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
/* PASST - Plug A Simple Socket Transport
|
||||||
|
*
|
||||||
|
* dhcpv6.c - Minimalistic DHCPv6 server for PASST
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Red Hat GmbH
|
||||||
|
* Author: Stefano Brivio <sbrivio@redhat.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <linux/ipv6.h>
|
||||||
|
#include <linux/udp.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "passt.h"
|
||||||
|
#include "tap.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct opt_hdr - DHCPv6 option header
|
||||||
|
* @t: Option type
|
||||||
|
* @l: Option length, network order
|
||||||
|
*/
|
||||||
|
struct opt_hdr {
|
||||||
|
uint16_t t;
|
||||||
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
# define OPT_CLIENTID 1
|
||||||
|
# define OPT_SERVERID 2
|
||||||
|
# define OPT_IA_NA 3
|
||||||
|
# define OPT_IA_TA 4
|
||||||
|
# define OPT_IAAADR 5
|
||||||
|
# define OPT_STATUS_CODE 13
|
||||||
|
# define STATUS_NOTONLINK 4
|
||||||
|
# define OPT_DNS_SERVERS 23
|
||||||
|
#else
|
||||||
|
# define OPT_CLIENTID __bswap_constant_16(1)
|
||||||
|
# define OPT_SERVERID __bswap_constant_16(2)
|
||||||
|
# define OPT_IA_NA __bswap_constant_16(3)
|
||||||
|
# define OPT_IA_TA __bswap_constant_16(4)
|
||||||
|
# define OPT_IAAADR __bswap_constant_16(5)
|
||||||
|
# define OPT_STATUS_CODE __bswap_constant_16(13)
|
||||||
|
# define STATUS_NOTONLINK __bswap_constant_16(4)
|
||||||
|
# define OPT_DNS_SERVERS __bswap_constant_16(23)
|
||||||
|
#endif
|
||||||
|
#define STR_NOTONLINK "Prefix not appropriate for link."
|
||||||
|
|
||||||
|
uint16_t l;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
# define OPT_SIZE_CONV(x) (x)
|
||||||
|
#else
|
||||||
|
# define OPT_SIZE_CONV(x) (__bswap_constant_16(x))
|
||||||
|
#endif
|
||||||
|
#define OPT_SIZE(x) OPT_SIZE_CONV(sizeof(struct opt_##x) - \
|
||||||
|
sizeof(struct opt_hdr))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct opt_client_id - DHCPv6 Client Identifier option
|
||||||
|
* @hdr: Option header
|
||||||
|
* @duid: Client DUID, up to 128 bytes (cf. RFC 8415, 11.1.)
|
||||||
|
*/
|
||||||
|
struct opt_client_id {
|
||||||
|
struct opt_hdr hdr;
|
||||||
|
uint8_t duid[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct opt_server_id - DHCPv6 Server Identifier option
|
||||||
|
* @hdr: Option header
|
||||||
|
* @duid_type: Type of server DUID, network order
|
||||||
|
* @duid_hw: IANA hardware type, network order
|
||||||
|
* @duid_time: Time reference, network order
|
||||||
|
* @duid_lladdr: Link-layer address (MAC address)
|
||||||
|
*/
|
||||||
|
struct opt_server_id {
|
||||||
|
struct opt_hdr hdr;
|
||||||
|
uint16_t duid_type;
|
||||||
|
#define DUID_TYPE_LLT 1
|
||||||
|
|
||||||
|
uint16_t duid_hw;
|
||||||
|
uint32_t duid_time;
|
||||||
|
uint8_t duid_lladdr[ETH_ALEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct opt_server_id server_id_const = {
|
||||||
|
{ OPT_SERVERID, OPT_SIZE(server_id) },
|
||||||
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
DUID_TYPE_LLT, ARPHRD_ETHER,
|
||||||
|
#else
|
||||||
|
__bswap_constant_16(DUID_TYPE_LLT), __bswap_constant_16(ARPHRD_ETHER),
|
||||||
|
#endif
|
||||||
|
0, { 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct opt_ia_na - Identity Association for Non-temporary Addresses Option
|
||||||
|
* @hdr: Option header
|
||||||
|
* @iaid: Unique identifier for IA_NA, network order
|
||||||
|
* @t1: Rebind interval for this server (always infinity)
|
||||||
|
* @t2: Rebind interval for any server (always infinity)
|
||||||
|
*/
|
||||||
|
struct opt_ia_na {
|
||||||
|
struct opt_hdr hdr;
|
||||||
|
uint32_t iaid;
|
||||||
|
uint32_t t1;
|
||||||
|
uint32_t t2;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct opt_ia_ta - Identity Association for Temporary Addresses Option
|
||||||
|
* @hdr: Option header
|
||||||
|
* @iaid: Unique identifier for IA_TA, network order
|
||||||
|
*/
|
||||||
|
struct opt_ia_ta {
|
||||||
|
struct opt_hdr hdr;
|
||||||
|
uint32_t iaid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct opt_ia_addr - IA Address Option
|
||||||
|
* @hdr: Option header
|
||||||
|
* @addr: Leased IPv6 address
|
||||||
|
* @pref_lifetime: Preferred lifetime, network order (always infinity)
|
||||||
|
* @valid_lifetime: Valid lifetime, network order (always infinity)
|
||||||
|
*/
|
||||||
|
struct opt_ia_addr {
|
||||||
|
struct opt_hdr hdr;
|
||||||
|
struct in6_addr addr;
|
||||||
|
uint32_t pref_lifetime;
|
||||||
|
uint32_t valid_lifetime;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct opt_status_code - Status Code Option (used for NotOnLink error only)
|
||||||
|
* @hdr: Option header
|
||||||
|
* @code: Numeric code for status, network order
|
||||||
|
* @status_msg: Text string suitable for display, not NULL-terminated
|
||||||
|
*/
|
||||||
|
struct opt_status_code {
|
||||||
|
struct opt_hdr hdr;
|
||||||
|
uint16_t code;
|
||||||
|
char status_msg[sizeof(STR_NOTONLINK) - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct opt_dns_servers - DNS Recursive Name Server option (RFC 3646)
|
||||||
|
* @hdr: Option header
|
||||||
|
* @addr: IPv6 DNS address
|
||||||
|
*/
|
||||||
|
struct opt_dns_servers {
|
||||||
|
struct opt_hdr hdr;
|
||||||
|
struct in6_addr addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct msg_hdr - DHCPv6 client/server message header
|
||||||
|
* @type: DHCP message type
|
||||||
|
* @xid: Transaction ID for message exchange
|
||||||
|
*/
|
||||||
|
struct msg_hdr {
|
||||||
|
uint32_t type:8;
|
||||||
|
#define TYPE_SOLICIT 1
|
||||||
|
#define TYPE_ADVERTISE 2
|
||||||
|
#define TYPE_REQUEST 3
|
||||||
|
#define TYPE_CONFIRM 4
|
||||||
|
#define TYPE_RENEW 5
|
||||||
|
#define TYPE_REBIND 6
|
||||||
|
#define TYPE_REPLY 7
|
||||||
|
#define TYPE_RELEASE 8
|
||||||
|
#define TYPE_DECLINE 9
|
||||||
|
#define TYPE_INFORMATION_REQUEST 11
|
||||||
|
|
||||||
|
uint32_t xid:24;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
static const struct udphdr uh_resp = {
|
||||||
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
547, 546, 0, 0,
|
||||||
|
#else
|
||||||
|
__bswap_constant_16(547), __bswap_constant_16(546), 0, 0,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct resp_t - Normal advertise and reply message
|
||||||
|
* @uh: UDP header
|
||||||
|
* @hdr: DHCP message header
|
||||||
|
* @server_id: Server Identifier option
|
||||||
|
* @ia_na: Non-temporary Address option
|
||||||
|
* @ia_addr: Address for IA_NA
|
||||||
|
* @dns_servers: DNS Recursive Name Server option
|
||||||
|
* @client_id: Client Identifier, variable length, must be at the end
|
||||||
|
*/
|
||||||
|
static struct resp_t {
|
||||||
|
struct udphdr uh;
|
||||||
|
struct msg_hdr hdr;
|
||||||
|
|
||||||
|
struct opt_server_id server_id;
|
||||||
|
struct opt_ia_na ia_na;
|
||||||
|
struct opt_ia_addr ia_addr;
|
||||||
|
struct opt_dns_servers dns_servers;
|
||||||
|
struct opt_client_id client_id;
|
||||||
|
} __attribute__((__packed__)) resp = {
|
||||||
|
uh_resp,
|
||||||
|
{ 0 },
|
||||||
|
server_id_const,
|
||||||
|
|
||||||
|
{ { OPT_IA_NA, OPT_SIZE_CONV(sizeof(struct opt_ia_na) +
|
||||||
|
sizeof(struct opt_ia_addr) -
|
||||||
|
sizeof(struct opt_hdr)) },
|
||||||
|
1, (uint32_t)~0U, (uint32_t)~0U
|
||||||
|
},
|
||||||
|
|
||||||
|
{ { OPT_IAAADR, OPT_SIZE(ia_addr) },
|
||||||
|
IN6ADDR_ANY_INIT, (uint32_t)~0U, (uint32_t)~0U
|
||||||
|
},
|
||||||
|
|
||||||
|
{ { OPT_DNS_SERVERS, OPT_SIZE(dns_servers), },
|
||||||
|
IN6ADDR_ANY_INIT
|
||||||
|
},
|
||||||
|
|
||||||
|
{ { OPT_CLIENTID, 0, },
|
||||||
|
{ 0 }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct opt_status_code sc_not_on_link = {
|
||||||
|
{ OPT_STATUS_CODE, OPT_SIZE(status_code), },
|
||||||
|
STATUS_NOTONLINK, STR_NOTONLINK
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct resp_not_on_link_t - NotOnLink error (mandated by RFC 8415, 18.3.2.)
|
||||||
|
* @uh: UDP header
|
||||||
|
* @hdr: DHCP message header
|
||||||
|
* @server_id: Server Identifier option
|
||||||
|
* @var: Payload: IA_NA from client, status code, client ID
|
||||||
|
*/
|
||||||
|
static struct resp_not_on_link_t {
|
||||||
|
struct udphdr uh;
|
||||||
|
struct msg_hdr hdr;
|
||||||
|
|
||||||
|
struct opt_server_id server_id;
|
||||||
|
|
||||||
|
uint8_t var[sizeof(struct opt_ia_na) + sizeof(struct opt_status_code) +
|
||||||
|
sizeof(struct opt_client_id)];
|
||||||
|
} __attribute__((__packed__)) resp_not_on_link = {
|
||||||
|
uh_resp,
|
||||||
|
{ TYPE_REPLY, 0 },
|
||||||
|
server_id_const,
|
||||||
|
{ 0, },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dhcpv6_opt() - Get option from DHCPv6 message
|
||||||
|
* @o: First option header to check
|
||||||
|
* @type: Option type to look up, network order
|
||||||
|
* @len: Remaining length, host order, modified on return
|
||||||
|
*
|
||||||
|
* Return: pointer to option header, or NULL on malformed or missing option
|
||||||
|
*/
|
||||||
|
static struct opt_hdr *dhcpv6_opt(struct opt_hdr *o, uint16_t type, size_t *len)
|
||||||
|
{
|
||||||
|
while (*len >= sizeof(struct opt_hdr)) {
|
||||||
|
if (ntohs(o->l) > *len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (o->t == type)
|
||||||
|
return o;
|
||||||
|
|
||||||
|
*len -= ntohs(o->l) + sizeof(struct opt_hdr);
|
||||||
|
o = (struct opt_hdr *)((uint8_t *)(o + 1) + ntohs(o->l));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dhcpv6_ia_notonlink() - Check if any IA contains non-appropriate addresses
|
||||||
|
* @o: First option header to check for IAs
|
||||||
|
* @len: Remaining message length, host order
|
||||||
|
* @addr: Address we want to lease to the client
|
||||||
|
*
|
||||||
|
* Return: pointer to non-appropriate IA_NA or IA_TA, if any, NULL otherwise
|
||||||
|
*/
|
||||||
|
static struct opt_hdr *dhcpv6_ia_notonlink(struct opt_hdr *o, size_t len,
|
||||||
|
struct in6_addr *addr)
|
||||||
|
{
|
||||||
|
struct opt_hdr *ia, *ia_addr;
|
||||||
|
struct in6_addr *req_addr;
|
||||||
|
size_t __len;
|
||||||
|
int ia_type;
|
||||||
|
|
||||||
|
ia_type = OPT_IA_NA;
|
||||||
|
ia_ta:
|
||||||
|
__len = len;
|
||||||
|
ia = o;
|
||||||
|
|
||||||
|
while ((ia = dhcpv6_opt(ia, ia_type, &__len))) {
|
||||||
|
size_t ia_len = ntohs(ia->l) - sizeof(struct opt_hdr);
|
||||||
|
|
||||||
|
if (ia_len > __len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (ia_type == OPT_IA_NA) {
|
||||||
|
struct opt_ia_na *opts = (struct opt_ia_na *)ia + 1;
|
||||||
|
|
||||||
|
ia_addr = (struct opt_hdr *)opts;
|
||||||
|
} else if (ia_type == OPT_IA_TA) {
|
||||||
|
struct opt_ia_ta *opts = (struct opt_ia_ta *)ia + 1;
|
||||||
|
|
||||||
|
ia_addr = (struct opt_hdr *)opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((ia_addr = dhcpv6_opt(ia_addr, OPT_IAAADR, &ia_len))) {
|
||||||
|
struct opt_ia_addr *next;
|
||||||
|
|
||||||
|
req_addr = (struct in6_addr *)(ia_addr + 1);
|
||||||
|
if (memcmp(addr, req_addr, sizeof(*addr)))
|
||||||
|
return ia;
|
||||||
|
|
||||||
|
next = (struct opt_ia_addr *)ia_addr + 1;
|
||||||
|
ia_addr = (struct opt_hdr *)next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ia_addr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ia = ia_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ia_type == OPT_IA_NA) {
|
||||||
|
ia_type = OPT_IA_TA;
|
||||||
|
goto ia_ta;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dhcpv6() - Check if this is a DHCPv6 message, reply as needed
|
||||||
|
* @c: Execution context
|
||||||
|
* @eh: Packet buffer, Ethernet header
|
||||||
|
* @len: Total L2 packet length
|
||||||
|
*
|
||||||
|
* Return: 0 if it's not a DHCPv6 message, 1 if handled, -1 on failure
|
||||||
|
*/
|
||||||
|
int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len)
|
||||||
|
{
|
||||||
|
struct ipv6hdr *ip6h = (struct ipv6hdr *)(eh + 1);
|
||||||
|
struct opt_hdr *ia, *bad_ia, *client_id, *server_id;
|
||||||
|
struct msg_hdr *mh;
|
||||||
|
struct udphdr *uh;
|
||||||
|
uint8_t proto;
|
||||||
|
size_t mlen;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
uh = (struct udphdr *)ipv6_l4hdr(ip6h, &proto);
|
||||||
|
if (!uh || proto != IPPROTO_UDP || uh->dest != htons(547))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!IN6_IS_ADDR_MULTICAST(&ip6h->daddr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mlen = len - ((intptr_t)uh - (intptr_t)eh) - sizeof(*uh);
|
||||||
|
|
||||||
|
if (mlen != ntohs(uh->len) - sizeof(*uh) ||
|
||||||
|
mlen < sizeof(struct msg_hdr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
c->addr6_guest = ip6h->saddr;
|
||||||
|
|
||||||
|
mh = (struct msg_hdr *)(uh + 1);
|
||||||
|
mlen -= sizeof(struct msg_hdr);
|
||||||
|
|
||||||
|
n = mlen;
|
||||||
|
client_id = dhcpv6_opt((struct opt_hdr *)(mh + 1), OPT_CLIENTID, &n);
|
||||||
|
if (!client_id || ntohs(client_id->l) > ntohs(OPT_SIZE(client_id)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
n = mlen;
|
||||||
|
server_id = dhcpv6_opt((struct opt_hdr *)(mh + 1), OPT_SERVERID, &n);
|
||||||
|
|
||||||
|
n = mlen;
|
||||||
|
ia = dhcpv6_opt((struct opt_hdr *)(mh + 1), OPT_IA_NA, &n);
|
||||||
|
if (ia && ntohs(ia->l) < ntohs(OPT_SIZE(ia_na)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
resp.hdr.type = TYPE_REPLY;
|
||||||
|
switch (mh->type) {
|
||||||
|
case TYPE_REQUEST:
|
||||||
|
case TYPE_RENEW:
|
||||||
|
if (!server_id ||
|
||||||
|
memcmp(&resp.server_id, server_id, sizeof(resp.server_id)))
|
||||||
|
return -1;
|
||||||
|
/* Falls through */
|
||||||
|
case TYPE_CONFIRM:
|
||||||
|
if (mh->type == TYPE_CONFIRM && server_id)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((bad_ia = dhcpv6_ia_notonlink((struct opt_hdr *)(mh + 1),
|
||||||
|
mlen, &c->addr6))) {
|
||||||
|
n = OPT_IA_NA ? sizeof(struct opt_ia_na) :
|
||||||
|
sizeof(struct opt_ia_ta);
|
||||||
|
memcpy(&resp_not_on_link.var, bad_ia, n);
|
||||||
|
|
||||||
|
memcpy(&resp_not_on_link.var + n, &sc_not_on_link,
|
||||||
|
sizeof(sc_not_on_link));
|
||||||
|
n += sizeof(sc_not_on_link);
|
||||||
|
|
||||||
|
memcpy(&resp_not_on_link.var + n, client_id,
|
||||||
|
sizeof(struct opt_hdr) + client_id->l);
|
||||||
|
n += sizeof(struct opt_hdr) + client_id->l;
|
||||||
|
|
||||||
|
n = offsetof(struct resp_not_on_link_t, var) + n;
|
||||||
|
resp_not_on_link.uh.len = htons(n);
|
||||||
|
|
||||||
|
resp_not_on_link.hdr.xid = mh->xid;
|
||||||
|
|
||||||
|
tap_ip_send(c, &c->gw6, IPPROTO_UDP,
|
||||||
|
(char *)&resp_not_on_link, n);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
info("DHCPv6: received REQUEST/RENEW/CONFIRM, sending REPLY");
|
||||||
|
break;
|
||||||
|
case TYPE_INFORMATION_REQUEST:
|
||||||
|
if (server_id &&
|
||||||
|
memcmp(&resp.server_id, server_id, sizeof(resp.server_id)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
n = mlen;
|
||||||
|
if (ia || dhcpv6_opt((struct opt_hdr *)(mh + 1), OPT_IA_TA, &n))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
info("DHCPv6: received INFORMATION_REQUEST, sending REPLY");
|
||||||
|
break;
|
||||||
|
case TYPE_REBIND:
|
||||||
|
if (!server_id ||
|
||||||
|
memcmp(&resp.server_id, server_id, sizeof(resp.server_id)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
info("DHCPv6: received REBIND, sending REPLY");
|
||||||
|
break;
|
||||||
|
case TYPE_SOLICIT:
|
||||||
|
if (server_id)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
resp.hdr.type = TYPE_ADVERTISE;
|
||||||
|
|
||||||
|
info("DHCPv6: received SOLICIT, sending ADVERTISE");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ia)
|
||||||
|
resp.ia_na.iaid = ((struct opt_ia_na *)ia)->iaid;
|
||||||
|
|
||||||
|
memcpy(&resp.client_id, client_id,
|
||||||
|
ntohs(client_id->l) + sizeof(struct opt_hdr));
|
||||||
|
resp.uh.len = htons(n = offsetof(struct resp_t, client_id) +
|
||||||
|
sizeof(struct opt_hdr) + ntohs(client_id->l));
|
||||||
|
|
||||||
|
resp.hdr.xid = mh->xid;
|
||||||
|
tap_ip_send(c, &c->gw6, IPPROTO_UDP, (char *)&resp, n);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dhcpv6() - Initialise DUID and addresses for DHCPv6 server
|
||||||
|
* @c: Execution context
|
||||||
|
*/
|
||||||
|
void dhcpv6_init(struct ctx *c)
|
||||||
|
{
|
||||||
|
struct tm y2k = { 0, 0, 0, 1, 0, 100, 0, 0, 0, 0, NULL };
|
||||||
|
uint32_t duid_time;
|
||||||
|
|
||||||
|
duid_time = htonl(difftime(time(NULL), mktime(&y2k)));
|
||||||
|
|
||||||
|
resp.server_id.duid_time = duid_time;
|
||||||
|
resp_not_on_link.server_id.duid_time = duid_time;
|
||||||
|
|
||||||
|
memcpy(resp.server_id.duid_lladdr, c->mac, sizeof(c->mac));
|
||||||
|
memcpy(resp_not_on_link.server_id.duid_lladdr, c->mac, sizeof(c->mac));
|
||||||
|
|
||||||
|
resp.ia_addr.addr = c->addr6;
|
||||||
|
resp.dns_servers.addr = c->dns6;
|
||||||
|
}
|
2
dhcpv6.h
Normal file
2
dhcpv6.h
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len);
|
||||||
|
void dhcpv6_init(struct ctx *c);
|
3
ndp.c
3
ndp.c
|
@ -80,13 +80,14 @@ int ndp(struct ctx *c, struct ethhdr *eh, size_t len)
|
||||||
ihr->icmp6_type = RA;
|
ihr->icmp6_type = RA;
|
||||||
ihr->icmp6_code = 0;
|
ihr->icmp6_code = 0;
|
||||||
ihr->icmp6_rt_lifetime = htons(3600);
|
ihr->icmp6_rt_lifetime = htons(3600);
|
||||||
|
ihr->icmp6_addrconf_managed = 1;
|
||||||
|
|
||||||
p = (unsigned char *)(ihr + 1);
|
p = (unsigned char *)(ihr + 1);
|
||||||
p += 8; /* reachable, retrans time */
|
p += 8; /* reachable, retrans time */
|
||||||
*p++ = 3; /* prefix */
|
*p++ = 3; /* prefix */
|
||||||
*p++ = 4; /* length */
|
*p++ = 4; /* length */
|
||||||
*p++ = 64; /* prefix length */
|
*p++ = 64; /* prefix length */
|
||||||
*p++ = 0xc0; /* flags: L, A */
|
*p++ = 0xc0; /* prefix flags: L, A */
|
||||||
*(uint32_t *)p = htonl(3600); /* lifetime */
|
*(uint32_t *)p = htonl(3600); /* lifetime */
|
||||||
p += 4;
|
p += 4;
|
||||||
*(uint32_t *)p = htonl(3600); /* preferred lifetime */
|
*(uint32_t *)p = htonl(3600); /* preferred lifetime */
|
||||||
|
|
17
passt.c
17
passt.c
|
@ -47,6 +47,7 @@
|
||||||
#include "arp.h"
|
#include "arp.h"
|
||||||
#include "dhcp.h"
|
#include "dhcp.h"
|
||||||
#include "ndp.h"
|
#include "ndp.h"
|
||||||
|
#include "dhcpv6.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "icmp.h"
|
#include "icmp.h"
|
||||||
#include "tcp.h"
|
#include "tcp.h"
|
||||||
|
@ -308,6 +309,9 @@ static void tap4_handler(struct ctx *c, char *in, size_t len)
|
||||||
char buf_d[BUFSIZ] __attribute((__unused__));
|
char buf_d[BUFSIZ] __attribute((__unused__));
|
||||||
char *l4h;
|
char *l4h;
|
||||||
|
|
||||||
|
if (!c->v4)
|
||||||
|
return;
|
||||||
|
|
||||||
if (arp(c, eh, len) || dhcp(c, eh, len))
|
if (arp(c, eh, len) || dhcp(c, eh, len))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -358,17 +362,17 @@ static void tap6_handler(struct ctx *c, char *in, size_t len)
|
||||||
uint8_t proto;
|
uint8_t proto;
|
||||||
char *l4h;
|
char *l4h;
|
||||||
|
|
||||||
|
if (!c->v6)
|
||||||
|
return;
|
||||||
|
|
||||||
if (len < sizeof(*eh) + sizeof(*ip6h))
|
if (len < sizeof(*eh) + sizeof(*ip6h))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ndp(c, eh, len))
|
if (ndp(c, eh, len) || dhcpv6(c, eh, len))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
l4h = ipv6_l4hdr(ip6h, &proto);
|
l4h = ipv6_l4hdr(ip6h, &proto);
|
||||||
|
|
||||||
/* TODO: Assign MAC address to guest so that, together with prefix
|
|
||||||
* assigned via NDP, address matches the one from the host.
|
|
||||||
*/
|
|
||||||
c->addr6_guest = ip6h->saddr;
|
c->addr6_guest = ip6h->saddr;
|
||||||
ip6h->saddr = c->addr6;
|
ip6h->saddr = c->addr6;
|
||||||
|
|
||||||
|
@ -559,6 +563,9 @@ int main(int argc, char **argv)
|
||||||
if (icmp_sock_init(&c) || tcp_sock_init(&c) || udp_sock_init(&c))
|
if (icmp_sock_init(&c) || tcp_sock_init(&c) || udp_sock_init(&c))
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
if (c.v6)
|
||||||
|
dhcpv6_init(&c);
|
||||||
|
|
||||||
if (c.v4) {
|
if (c.v4) {
|
||||||
info("ARP:");
|
info("ARP:");
|
||||||
info(" address: %02x:%02x:%02x:%02x:%02x:%02x from %s",
|
info(" address: %02x:%02x:%02x:%02x:%02x:%02x from %s",
|
||||||
|
@ -575,7 +582,7 @@ int main(int argc, char **argv)
|
||||||
inet_ntop(AF_INET, &c.dns4, buf4[3], sizeof(buf4[3])));
|
inet_ntop(AF_INET, &c.dns4, buf4[3], sizeof(buf4[3])));
|
||||||
}
|
}
|
||||||
if (c.v6) {
|
if (c.v6) {
|
||||||
info("NDP:");
|
info("NDP/DHCPv6:");
|
||||||
info(" assign: %s",
|
info(" assign: %s",
|
||||||
inet_ntop(AF_INET6, &c.addr6, buf6[0], sizeof(buf6[0])));
|
inet_ntop(AF_INET6, &c.addr6, buf6[0], sizeof(buf6[0])));
|
||||||
info(" router: %s",
|
info(" router: %s",
|
||||||
|
|
Loading…
Reference in a new issue