summaryrefslogtreecommitdiff
path: root/accel-pppd
diff options
context:
space:
mode:
authorKozlov Dmitry <xeb@mail.ru>2011-08-28 00:37:34 +0400
committerKozlov Dmitry <xeb@mail.ru>2011-08-28 00:37:34 +0400
commit6459c6085f7324404717418448fa8c04ffd46c20 (patch)
tree9c610e3aa4497463caf0c534f725d059229e0db0 /accel-pppd
parent2ae178c12aced47699cbb252f6a67defb0d0bbe7 (diff)
downloadaccel-ppp-6459c6085f7324404717418448fa8c04ffd46c20.tar.gz
accel-ppp-6459c6085f7324404717418448fa8c04ffd46c20.zip
ipv6: initial dhcpv6 support
Diffstat (limited to 'accel-pppd')
-rw-r--r--accel-pppd/CMakeLists.txt2
-rw-r--r--accel-pppd/accel-ppp.conf7
-rw-r--r--accel-pppd/extra/ipv6pool.c2
-rw-r--r--accel-pppd/ipdb.h3
-rw-r--r--accel-pppd/ipv6/CMakeLists.txt7
-rw-r--r--accel-pppd/ipv6/dhcpv6.c551
-rw-r--r--accel-pppd/ipv6/dhcpv6.h175
-rw-r--r--accel-pppd/ipv6/dhcpv6_packet.c438
-rw-r--r--accel-pppd/ipv6/nd.c (renamed from accel-pppd/ppp/ipv6_nd.c)34
-rw-r--r--accel-pppd/ppp/ipv6cp_opt_intfid.c61
-rw-r--r--accel-pppd/ppp/ppp_ipv6cp.c2
-rw-r--r--accel-pppd/radius/radius.c8
-rw-r--r--accel-pppd/radius/req.c2
13 files changed, 1248 insertions, 44 deletions
diff --git a/accel-pppd/CMakeLists.txt b/accel-pppd/CMakeLists.txt
index 807f039c..cdc2d3ea 100644
--- a/accel-pppd/CMakeLists.txt
+++ b/accel-pppd/CMakeLists.txt
@@ -52,6 +52,7 @@ ADD_SUBDIRECTORY(ctrl)
ADD_SUBDIRECTORY(auth)
ADD_SUBDIRECTORY(logs)
ADD_SUBDIRECTORY(extra)
+ADD_SUBDIRECTORY(ipv6)
ADD_EXECUTABLE(accel-pppd
ppp/ppp.c
@@ -69,7 +70,6 @@ ADD_EXECUTABLE(accel-pppd
ppp/ppp_ipv6cp.c
ppp/ppp_ccp.c
ppp/ccp_mppe.c
- ppp/ipv6_nd.c
cli/std_cmd.c
cli/show_sessions.c
diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index 3470abab..d7e4814d 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -16,6 +16,8 @@ sigchld
pppd_compat
#shaper_tbf
#chap-secrets
+#ipv6_nd
+#ipv6_dhcp
[core]
log-error=/var/log/accel-ppp/core.log
@@ -32,7 +34,7 @@ mru=1400
#single-session=replace
#mppe=require
ipv4=require
-ipv6=allow
+ipv6=deny
ipv6-intf-id=0:0:0:1
ipv6-peer-intf-id=0:0:0:2
ipv6-accept-peer-intf-id=1
@@ -149,3 +151,6 @@ tcp=127.0.0.1:2001
[snmp]
master=0
agent-name=accel-ppp
+
+[dhcpv6]
+verbose=1
diff --git a/accel-pppd/extra/ipv6pool.c b/accel-pppd/extra/ipv6pool.c
index 8fe8ce2f..e9fb52d5 100644
--- a/accel-pppd/extra/ipv6pool.c
+++ b/accel-pppd/extra/ipv6pool.c
@@ -40,7 +40,6 @@ static void generate_pool(struct in6_addr *addr, int mask, int prefix_len)
it = malloc(sizeof(*it));
it->it.owner = &ipdb;
INIT_LIST_HEAD(&it->it.addr_list);
- INIT_LIST_HEAD(&it->it.route_list);
a = malloc(sizeof(*a));
memset(a, 0, sizeof(*a));
*(uint64_t *)a->addr.s6_addr = htobe64(ip);
@@ -104,6 +103,7 @@ static struct ipv6db_item_t *get_ip(struct ppp_t *ppp)
it = list_entry(ippool.next, typeof(*it), entry);
list_del(&it->entry);
it->it.intf_id = 0;
+ it->it.peer_intf_id = 0;
} else
it = NULL;
spin_unlock(&pool_lock);
diff --git a/accel-pppd/ipdb.h b/accel-pppd/ipdb.h
index 69d54792..1fcaf616 100644
--- a/accel-pppd/ipdb.h
+++ b/accel-pppd/ipdb.h
@@ -18,14 +18,15 @@ struct ipv6db_addr_t
struct list_head entry;
struct in6_addr addr;
int prefix_len;
+ int flag_auto:1;
};
struct ipv6db_item_t
{
struct ipdb_t *owner;
uint64_t intf_id;
+ uint64_t peer_intf_id;
struct list_head addr_list;
- struct list_head route_list;
};
diff --git a/accel-pppd/ipv6/CMakeLists.txt b/accel-pppd/ipv6/CMakeLists.txt
new file mode 100644
index 00000000..9f8c3d1d
--- /dev/null
+++ b/accel-pppd/ipv6/CMakeLists.txt
@@ -0,0 +1,7 @@
+ADD_LIBRARY(ipv6_dhcp SHARED dhcpv6.c dhcpv6_packet.c)
+ADD_LIBRARY(ipv6_nd SHARED nd.c)
+
+INSTALL(TARGETS ipv6_dhcp ipv6_nd
+ LIBRARY DESTINATION lib/accel-ppp
+)
+
diff --git a/accel-pppd/ipv6/dhcpv6.c b/accel-pppd/ipv6/dhcpv6.c
new file mode 100644
index 00000000..c431fbee
--- /dev/null
+++ b/accel-pppd/ipv6/dhcpv6.c
@@ -0,0 +1,551 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pthread.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "triton.h"
+#include "mempool.h"
+#include "log.h"
+#include "ppp.h"
+#include "ipdb.h"
+#include "events.h"
+
+#include "memdebug.h"
+
+#include "dhcpv6.h"
+
+#define BUF_SIZE 65536
+#define MAX_DNS_COUNT 3
+
+static int conf_verbose;
+static int conf_pref_lifetime = -1;
+static int conf_valid_lifetime = -1;
+static struct in6_addr conf_dns[MAX_DNS_COUNT];
+static int conf_dns_count;
+static void *conf_dnssl;
+static int conf_dnssl_size;
+
+struct dhcpv6_pd
+{
+ struct ppp_pd_t pd;
+ struct dhcpv6_opt_clientid *clientid;
+ struct dhcpv6_opt_serverid serverid;
+ uint32_t iaid;
+};
+
+static struct triton_md_handler_t dhcpv6_hnd;
+static struct triton_context_t dhcpv6_ctx;
+
+static uint8_t *buf;
+static void *pd_key;
+
+static void ev_ppp_started(struct ppp_t *ppp)
+{
+ struct ipv6_mreq mreq;
+ struct dhcpv6_pd *pd;
+ time_t t, t0;
+ struct tm tm;
+
+ if (!ppp->ipv6)
+ return;
+
+ time(&t);
+ localtime_r(&t, &tm);
+
+ tm.tm_year = 100;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+
+ t0 = mktime(&tm);
+
+ pd = _malloc(sizeof(*pd));
+ memset(pd, 0, sizeof(*pd));
+
+ pd->pd.key = &pd_key;
+
+ pd->serverid.hdr.code = htons(D6_OPTION_SERVERID);
+ pd->serverid.hdr.len = htons(16);
+ pd->serverid.duid.type = htons(DUID_LLT);
+ pd->serverid.duid.u.llt.htype = htons(27);
+ pd->serverid.duid.u.llt.time = htonl(t - t0);
+ *(uint64_t *)pd->serverid.duid.u.llt.addr = ppp->ipv6->intf_id;
+
+ list_add_tail(&pd->pd.entry, &ppp->pd_list);
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.ipv6mr_interface = ppp->ifindex;
+ mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xff020000);
+ mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x010002);
+
+ if (setsockopt(dhcpv6_hnd.fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) {
+ log_ppp_error("dhcpv6: failed to join to All_DHCP_Relay_Agents_and_Servers\n");
+ return;
+ }
+}
+
+static struct dhcpv6_pd *find_pd(struct ppp_t *ppp)
+{
+ struct ppp_pd_t *pd;
+
+ list_for_each_entry(pd, &ppp->pd_list, entry) {
+ if (pd->key == &pd_key)
+ return container_of(pd, struct dhcpv6_pd, pd);
+ }
+
+ return NULL;
+}
+
+static void ev_ppp_finished(struct ppp_t *ppp)
+{
+ struct dhcpv6_pd *pd = find_pd(ppp);
+
+ if (!pd)
+ return;
+
+ list_del(&pd->pd.entry);
+
+ if (pd->clientid)
+ _free(pd->clientid);
+
+ _free(pd);
+}
+
+static void dhcpv6_send(struct dhcpv6_packet *reply)
+{
+ struct sockaddr_in6 addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(DHCPV6_CLIENT_PORT);
+ addr.sin6_addr.s6_addr32[0] = htons(0xfe80);
+ *(uint64_t *)(addr.sin6_addr.s6_addr + 8) = reply->ppp->ipv6->peer_intf_id;
+ addr.sin6_scope_id = reply->ppp->ifindex;
+
+ sendto(dhcpv6_hnd.fd, reply->hdr, reply->endptr - (void *)reply->hdr, 0, (struct sockaddr *)&addr, sizeof(addr));
+ printf("sendto: %s %i\n", strerror(errno), errno);
+}
+
+static void build_addr(struct ipv6db_addr_t *a, uint64_t intf_id, struct in6_addr *addr)
+{
+ memcpy(addr, &a->addr, sizeof(*addr));
+
+ if (a->prefix_len <= 64)
+ *(uint64_t *)(addr->s6_addr + 8) = intf_id;
+ else
+ *(uint64_t *)(addr->s6_addr + 8) |= intf_id & ((1 << (128 - a->prefix_len)) - 1);
+}
+
+static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, int code)
+{
+ struct dhcpv6_packet *reply;
+ struct dhcpv6_option *opt, *opt1, *opt2, *opt3, *opt4;
+ struct dhcpv6_opt_ia_na *ia_na;
+ struct dhcpv6_opt_ia_addr *ia_addr;
+ struct dhcpv6_opt_status *status;
+ struct ipv6db_addr_t *a;
+ struct in6_addr addr, *addr_ptr;
+ int i, j, f = 0, f1;
+ uint16_t *ptr;
+
+ reply = dhcpv6_packet_alloc_reply(req, code);
+ if (!reply)
+ return;
+
+ list_for_each_entry(opt, &req->opt_list, entry) {
+ if (ntohs(opt->hdr->code) == D6_OPTION_IA_NA) {
+ opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_NA, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
+ memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
+ if (list_empty(&req->ppp->ipv6->addr_list) || f) {
+ opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_STATUS_CODE, sizeof(struct dhcpv6_opt_status) - sizeof(struct dhcpv6_opt_hdr));
+ status = (struct dhcpv6_opt_status *)opt3->hdr;
+ status->code = htons(D6_STATUS_NoAddrsAvail);
+ } else {
+ if (code == D6_REPLY) {
+ ia_na = (struct dhcpv6_opt_ia_na *)opt->hdr;
+ pd->iaid = ia_na->iaid;
+ }
+
+ f = 1;
+
+ list_for_each_entry(a, &req->ppp->ipv6->addr_list, entry) {
+ opt2 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAADDR, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
+ ia_addr = (struct dhcpv6_opt_ia_addr *)opt2->hdr;
+
+ build_addr(a, req->ppp->ipv6->peer_intf_id, &ia_addr->addr);
+
+ ia_addr->pref_lifetime = htonl(conf_pref_lifetime);
+ ia_addr->valid_lifetime = htonl(conf_valid_lifetime);
+ }
+
+ list_for_each_entry(opt2, &opt->opt_list, entry) {
+ if (ntohs(opt2->hdr->code) == D6_OPTION_IAADDR) {
+ ia_addr = (struct dhcpv6_opt_ia_addr *)opt2->hdr;
+ f1 = 0;
+ list_for_each_entry(a, &req->ppp->ipv6->addr_list, entry) {
+ build_addr(a, req->ppp->ipv6->peer_intf_id, &addr);
+ if (memcmp(&addr, &ia_addr->addr, sizeof(addr)))
+ continue;
+ f1 = 1;
+ break;
+ }
+
+ if (!f1) {
+ opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAADDR, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
+ memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
+ opt4 = dhcpv6_nested_option_alloc(reply, opt3, D6_OPTION_STATUS_CODE, sizeof(struct dhcpv6_opt_status) - sizeof(struct dhcpv6_opt_hdr));
+ status = (struct dhcpv6_opt_status *)opt4->hdr;
+ status->code = htons(D6_STATUS_NotOnLink);
+ }
+ }
+ }
+ }
+ } else if (ntohs(opt->hdr->code) == D6_OPTION_IA_TA) {
+ opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_TA, sizeof(struct dhcpv6_opt_ia_ta) - sizeof(struct dhcpv6_opt_hdr));
+ memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
+ opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_STATUS_CODE, sizeof(struct dhcpv6_opt_status) - sizeof(struct dhcpv6_opt_hdr));
+ status = (struct dhcpv6_opt_status *)opt3->hdr;
+ status->code = htons(D6_STATUS_NoAddrsAvail);
+ } else if (ntohs(opt->hdr->code) == D6_OPTION_ORO) {
+ for (i = ntohs(opt->hdr->len) / 2, ptr = (uint16_t *)opt->hdr->data; i; i--, ptr++) {
+ if (ntohs(*ptr) == D6_OPTION_DNS_SERVERS) {
+ opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DNS_SERVERS, conf_dns_count * sizeof(addr));
+ for (j = 0, addr_ptr = (struct in6_addr *)opt1->hdr->data; j < conf_dns_count; j++, addr_ptr++)
+ memcpy(addr_ptr, conf_dns + j, sizeof(addr));
+ } else if (ntohs(*ptr) == D6_OPTION_DOMAIN_LIST) {
+ opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DOMAIN_LIST, conf_dnssl_size);
+ memcpy(opt1->hdr->data, conf_dnssl, conf_dnssl_size);
+ }
+ }
+ }
+ }
+
+ opt3 = dhcpv6_option_alloc(reply, D6_OPTION_STATUS_CODE, sizeof(struct dhcpv6_opt_status) - sizeof(struct dhcpv6_opt_hdr));
+ status = (struct dhcpv6_opt_status *)opt3->hdr;
+ status->code = htons(D6_STATUS_Success);
+
+ if (conf_verbose) {
+ log_ppp_info2("send ");
+ dhcpv6_packet_print(reply, log_ppp_info2);
+ }
+
+ dhcpv6_send(reply);
+
+ dhcpv6_packet_free(reply);
+}
+
+static void dhcpv6_recv_solicit(struct dhcpv6_packet *req)
+{
+ struct dhcpv6_pd *pd = find_pd(req->ppp);
+
+ if (!pd)
+ return;
+
+ if (!req->clientid) {
+ log_ppp_error("dhcpv6: no Client-ID option\n");
+ return;
+ }
+
+ if (req->serverid) {
+ log_ppp_error("dhcpv6: unexpected Server-ID option\n");
+ return;
+ }
+
+ req->serverid = &pd->serverid;
+
+ if (!pd->clientid) {
+ pd->clientid = _malloc(sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
+ memcpy(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
+ } else if (pd->clientid->hdr.len != req->clientid->hdr.len || memcmp(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len))) {
+ log_ppp_warn("dhcpv6: Client-ID option was changed\n");
+ return;
+ }
+
+ dhcpv6_send_reply(req, pd, D6_ADVERTISE);
+}
+
+static void dhcpv6_recv_request(struct dhcpv6_packet *req)
+{
+ struct dhcpv6_pd *pd = find_pd(req->ppp);
+
+ if (!pd)
+ return;
+
+ if (!req->clientid) {
+ log_ppp_error("dhcpv6: no Client-ID option\n");
+ return;
+ }
+
+ if (!req->serverid) {
+ log_ppp_error("dhcpv6: no Server-ID option\n");
+ return;
+ }
+
+ if (!pd->clientid) {
+ pd->clientid = _malloc(sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
+ memcpy(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
+ } else if (pd->clientid->hdr.len != req->clientid->hdr.len || memcmp(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len))) {
+ log_ppp_warn("dhcpv6: Client-ID option was changed\n");
+ return;
+ }
+
+ dhcpv6_send_reply(req, pd, D6_REPLY);
+}
+
+static void dhcpv6_recv_renew(struct dhcpv6_packet *pkt)
+{
+
+}
+
+static void dhcpv6_recv_rebind(struct dhcpv6_packet *pkt)
+{
+
+}
+
+static void dhcpv6_recv_release(struct dhcpv6_packet *pkt)
+{
+
+}
+
+static void dhcpv6_recv_decline(struct dhcpv6_packet *pkt)
+{
+
+}
+
+static void dhcpv6_recv_packet(struct dhcpv6_packet *pkt)
+{
+ if (conf_verbose) {
+ log_ppp_info2("recv ");
+ dhcpv6_packet_print(pkt, log_ppp_info2);
+ }
+
+ switch (pkt->hdr->type) {
+ case D6_SOLICIT:
+ dhcpv6_recv_solicit(pkt);
+ break;
+ case D6_REQUEST:
+ dhcpv6_recv_request(pkt);
+ break;
+ case D6_RENEW:
+ dhcpv6_recv_renew(pkt);
+ break;
+ case D6_REBIND:
+ dhcpv6_recv_rebind(pkt);
+ break;
+ case D6_RELEASE:
+ dhcpv6_recv_release(pkt);
+ break;
+ case D6_DECLINE:
+ dhcpv6_recv_decline(pkt);
+ break;
+ }
+
+ dhcpv6_packet_free(pkt);
+}
+
+static int dhcpv6_read(struct triton_md_handler_t *h)
+{
+ int n;
+ struct sockaddr_in6 addr;
+ socklen_t len = sizeof(addr);
+ struct dhcpv6_packet *pkt;
+ struct ppp_t *ppp;
+
+ while (1) {
+ n = recvfrom(h->fd, buf, BUF_SIZE, 0, &addr, &len);
+ if (n == -1) {
+ if (errno == EAGAIN)
+ return 0;
+ log_error("dhcpv6: read: %s\n", strerror(errno));
+ }
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr))
+ continue;
+
+ if (addr.sin6_port != ntohs(DHCPV6_CLIENT_PORT))
+ continue;
+
+ pkt = dhcpv6_packet_parse(buf, n);
+ if (!pkt || !pkt->clientid) {
+ continue;
+ }
+
+ pthread_rwlock_rdlock(&ppp_lock);
+ list_for_each_entry(ppp, &ppp_list, entry) {
+ if (ppp->state != PPP_STATE_ACTIVE)
+ continue;
+
+ if (!ppp->ipv6)
+ continue;
+
+ if (ppp->ifindex != addr.sin6_scope_id)
+ continue;
+
+ if (ppp->ipv6->peer_intf_id != *(uint64_t *)(addr.sin6_addr.s6_addr + 8))
+ continue;
+
+ pkt->ppp = ppp;
+
+ triton_context_call(ppp->ctrl->ctx, (triton_event_func)dhcpv6_recv_packet, pkt);
+ break;
+ }
+ pthread_rwlock_unlock(&ppp_lock);
+ }
+
+ return 0;
+}
+
+static void dhcpv6_close(struct triton_context_t *ctx)
+{
+ triton_md_unregister_handler(&dhcpv6_hnd);
+ close(dhcpv6_hnd.fd);
+ triton_context_unregister(ctx);
+}
+
+static struct triton_md_handler_t dhcpv6_hnd = {
+ .read = dhcpv6_read,
+};
+
+static struct triton_context_t dhcpv6_ctx = {
+ .close = dhcpv6_close,
+};
+
+static void add_dnssl(const char *val)
+{
+ int n = strlen(val);
+ const char *ptr;
+ uint8_t *buf;
+
+ if (val[n - 1] == '.')
+ n++;
+ else
+ n += 2;
+
+ if (n > 255) {
+ log_error("dnsv6: dnssl '%s' is too long\n", val);
+ return;
+ }
+
+ if (!conf_dnssl)
+ conf_dnssl = _malloc(n);
+ else
+ conf_dnssl = _realloc(conf_dnssl, conf_dnssl_size + n);
+
+ buf = conf_dnssl + conf_dnssl_size;
+
+ while (1) {
+ ptr = strchr(val, '.');
+ if (!ptr)
+ ptr = strchr(val, 0);
+ if (ptr - val > 63) {
+ log_error("dnsv6: dnssl '%s' is invalid\n", val);
+ return;
+ }
+ *buf = ptr - val;
+ memcpy(buf + 1, val, ptr - val);
+ buf += 1 + (ptr - val);
+ val = ptr + 1;
+ if (!*ptr || !*val) {
+ *buf = 0;
+ break;
+ }
+ }
+
+ conf_dnssl_size += n;
+}
+
+static void load_dns(void)
+{
+ struct conf_sect_t *s = conf_get_section("dnsv6");
+ struct conf_option_t *opt;
+
+ if (!s)
+ return;
+
+ conf_dns_count = 0;
+
+ if (conf_dnssl)
+ _free(conf_dnssl);
+ conf_dnssl = NULL;
+ conf_dnssl_size = 0;
+
+ list_for_each_entry(opt, &s->items, entry) {
+ if (!strcmp(opt->name, "dnssl")) {
+ add_dnssl(opt->val);
+ continue;
+ }
+
+ if (!strcmp(opt->name, "dns") || !opt->val) {
+ if (conf_dns_count == MAX_DNS_COUNT)
+ continue;
+
+ if (inet_pton(AF_INET6, opt->val ? opt->val : opt->name, &conf_dns[conf_dns_count]) == 0) {
+ log_error("dnsv6: faild to parse '%s'\n", opt->name);
+ continue;
+ }
+ conf_dns_count++;
+ }
+ }
+}
+
+static void load_config(void)
+{
+ const char *opt;
+
+ opt = conf_get_opt("dhcpv6", "verbose");
+ if (opt)
+ conf_verbose = atoi(opt);
+
+ load_dns();
+}
+
+static void init(void)
+{
+ struct sockaddr_in6 addr;
+ int sock;
+
+ load_config();
+
+ sock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (!sock) {
+ log_error("dhcpv6: socket: %s\n", strerror(errno));
+ return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(DHCPV6_SERV_PORT);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) {
+ log_error("dhcpv6: bind: %s\n", strerror(errno));
+ close(sock);
+ return;
+ }
+
+ fcntl(sock, F_SETFL, O_NONBLOCK);
+
+ dhcpv6_hnd.fd = sock;
+
+ buf = malloc(BUF_SIZE);
+
+ triton_context_register(&dhcpv6_ctx, NULL);
+ triton_md_register_handler(&dhcpv6_ctx, &dhcpv6_hnd);
+ triton_md_enable_handler(&dhcpv6_hnd, MD_MODE_READ);
+ triton_context_wakeup(&dhcpv6_ctx);
+
+ triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
+ triton_event_register_handler(EV_PPP_STARTED, (triton_event_func)ev_ppp_started);
+ triton_event_register_handler(EV_PPP_FINISHED, (triton_event_func)ev_ppp_finished);
+}
+
+DEFINE_INIT(10, init);
diff --git a/accel-pppd/ipv6/dhcpv6.h b/accel-pppd/ipv6/dhcpv6.h
new file mode 100644
index 00000000..6c5d164c
--- /dev/null
+++ b/accel-pppd/ipv6/dhcpv6.h
@@ -0,0 +1,175 @@
+#ifndef __DHCPV6_H
+#define __DHCPV6_H
+
+#include <stdint.h>
+#include <netinet/in.h>
+
+#include "list.h"
+
+#define __packed __attribute__((packed))
+
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERV_PORT 547
+
+#define D6_OPTION_CLIENTID 1
+#define D6_OPTION_SERVERID 2
+#define D6_OPTION_IA_NA 3
+#define D6_OPTION_IA_TA 4
+#define D6_OPTION_IAADDR 5
+#define D6_OPTION_ORO 6
+#define D6_OPTION_PREFERENCE 7
+#define D6_OPTION_ELAPSED_TIME 8
+#define D6_OPTION_RELAY_MSG 9
+#define D6_OPTION_AUTH 11
+#define D6_OPTION_UNICAST 12
+#define D6_OPTION_STATUS_CODE 13
+#define D6_OPTION_RAPID_COMMIT 14
+#define D6_OPTION_USER_CLASS 15
+#define D6_OPTION_VENDOR_CLASS 16
+#define D6_OPTION_VENDOR_SPECIFIC 17
+#define D6_OPTION_INTERFACE_ID 18
+#define D6_OPTION_RECONF_MSG 19
+#define D6_OPTION_RECONF_ACCEPT 20
+#define D6_OPTION_DNS_SERVERS 23
+#define D6_OPTION_DOMAIN_LIST 24
+
+#define D6_SOLICIT 1
+#define D6_ADVERTISE 2
+#define D6_REQUEST 3
+#define D6_CONFIRM 4
+#define D6_RENEW 5
+#define D6_REBIND 6
+#define D6_REPLY 7
+#define D6_RELEASE 8
+#define D6_DECLINE 9
+#define D6_RECONFIGURE 10
+#define D6_INFORMATION_REQUEST 11
+#define D6_RELAY_FORM 12
+#define D6_RELAY_REPL 13
+
+#define D6_STATUS_Success 0
+#define D6_STATUS_UnspecFail 1
+#define D6_STATUS_NoAddrsAvail 2
+#define D6_STATUS_NoBinding 3
+#define D6_STATUS_NotOnLink 4
+#define D6_STATUS_UseMulticast 5
+
+#define DUID_LLT 1
+#define DUID_EN 2
+#define DUID_LL 3
+
+struct dhcpv6_opt_hdr
+{
+ uint16_t code;
+ uint16_t len;
+ uint8_t data[0];
+} __packed;
+
+struct dhcpv6_msg_hdr
+{
+ uint32_t type:8;
+ uint32_t trans_id:24;
+ uint8_t data[0];
+} __packed;
+
+struct dhcpv6_duid
+{
+ uint16_t type;
+ union {
+ struct {
+ uint16_t htype;
+ uint32_t time;
+ uint8_t addr[0];
+ } __packed llt;
+ struct {
+ uint32_t enterprise;
+ uint8_t id[0];
+ } en;
+ struct {
+ uint16_t htype;
+ uint8_t addr[0];
+ } ll;
+ uint8_t raw[0];
+ } u;
+} __packed;
+
+struct dhcpv6_opt_clientid
+{
+ struct dhcpv6_opt_hdr hdr;
+ struct dhcpv6_duid duid;
+} __packed;
+
+struct dhcpv6_opt_serverid
+{
+ struct dhcpv6_opt_hdr hdr;
+ struct dhcpv6_duid duid;
+} __packed;
+
+struct dhcpv6_opt_ia_na
+{
+ struct dhcpv6_opt_hdr hdr;
+ uint32_t iaid;
+ uint32_t T1;
+ uint32_t T2;
+} __packed;
+
+struct dhcpv6_opt_ia_ta
+{
+ struct dhcpv6_opt_hdr hdr;
+ uint32_t iaid;
+} __packed;
+
+
+struct dhcpv6_opt_ia_addr
+{
+ struct dhcpv6_opt_hdr hdr;
+ struct in6_addr addr;
+ uint32_t pref_lifetime;
+ uint32_t valid_lifetime;
+} __packed;
+
+struct dhcpv6_opt_oro
+{
+ struct dhcpv6_opt_hdr hdr;
+ uint16_t opt[0];
+} __packed;
+
+struct dhcpv6_opt_status
+{
+ struct dhcpv6_opt_hdr hdr;
+ uint16_t code;
+ char msg[0];
+} __packed;
+
+
+struct dhcpv6_option
+{
+ struct list_head entry;
+
+ struct dhcpv6_opt_hdr *hdr;
+
+ struct dhcpv6_option *parent;
+ struct list_head opt_list;
+};
+
+struct ppp_t;
+struct dhcpv6_packet
+{
+ struct ppp_t *ppp;
+
+ struct dhcpv6_msg_hdr *hdr;
+ struct dhcpv6_opt_clientid *clientid;
+ struct dhcpv6_opt_serverid *serverid;
+
+ struct list_head opt_list;
+ void *endptr;
+};
+
+struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size);
+void dhcpv6_packet_free(struct dhcpv6_packet *pkt);
+void dhcpv6_packet_print(struct dhcpv6_packet *pkt, void (*print)(const char *fmt, ...));
+struct dhcpv6_packet *dhcpv6_packet_alloc_reply(struct dhcpv6_packet *req, int type);
+struct dhcpv6_option *dhcpv6_option_alloc(struct dhcpv6_packet *pkt, int code, int len);
+struct dhcpv6_option *dhcpv6_nested_option_alloc(struct dhcpv6_packet *pkt, struct dhcpv6_option *opt, int code, int len);
+
+#endif
diff --git a/accel-pppd/ipv6/dhcpv6_packet.c b/accel-pppd/ipv6/dhcpv6_packet.c
new file mode 100644
index 00000000..9a709963
--- /dev/null
+++ b/accel-pppd/ipv6/dhcpv6_packet.c
@@ -0,0 +1,438 @@
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "log.h"
+#include "memdebug.h"
+
+#include "dhcpv6.h"
+
+#define BUF_SIZE 4096
+
+struct dict_option {
+ int code;
+ const char *name;
+ int recv;
+ int len;
+ void (*print)(struct dhcpv6_option *, void (*)(const char *fmt, ...));
+};
+
+static void print_clientid(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_ia_na(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_ia_ta(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_ia_addr(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_oro(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_uint8(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_time(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_ipv6addr(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_ipv6addr_array(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_status(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_uint64(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_reconf(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+static void print_dnssl(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
+
+static struct dict_option known_options[] = {
+ { D6_OPTION_CLIENTID, "Client-ID", 1, 0, print_clientid },
+ { D6_OPTION_SERVERID, "Server-ID", 0, 0, print_clientid },
+ { D6_OPTION_IA_NA, "IA-NA", 1, sizeof(struct dhcpv6_opt_ia_na), print_ia_na },
+ { D6_OPTION_IA_TA, "IA-TA", 1, sizeof(struct dhcpv6_opt_ia_ta), print_ia_ta },
+ { D6_OPTION_IAADDR, "IA-Addr", 1, sizeof(struct dhcpv6_opt_ia_addr), print_ia_addr },
+ { D6_OPTION_ORO, "Option-Request", 1, 0, print_oro },
+ { D6_OPTION_PREFERENCE, "Preference", 0, 0, print_uint8 },
+ { D6_OPTION_ELAPSED_TIME, "Elapsed-Time", 1, 0, print_time },
+ { D6_OPTION_RELAY_MSG, "Relay-Message", 1, 0 },
+ { D6_OPTION_AUTH, "Auth", 1, 0 },
+ { D6_OPTION_PREFERENCE, "Server-Unicast", 0, 0, print_ipv6addr },
+ { D6_OPTION_STATUS_CODE, "Status", 0, 0, print_status },
+ { D6_OPTION_RAPID_COMMIT, "Rapid-Commit", 1, 0 },
+ { D6_OPTION_USER_CLASS, "User-Class", 1, 0 },
+ { D6_OPTION_VENDOR_CLASS, "Vendor-Class", 1, 0, print_uint64 },
+ { D6_OPTION_VENDOR_SPECIFIC, "Vendor-Specific", 1, 0, print_uint64 },
+ { D6_OPTION_INTERFACE_ID, "Interface-ID", 1, 0, print_uint64 },
+ { D6_OPTION_RECONF_MSG, "Reconfigure", 0, 0, print_reconf },
+ { D6_OPTION_RECONF_ACCEPT, "Reconfigure-Accept", 1, 0 },
+ { D6_OPTION_DNS_SERVERS, "DNS", 1, 0, print_ipv6addr_array },
+ { D6_OPTION_DOMAIN_LIST, "DNSSL", 1, 0, print_dnssl },
+ { 0 }
+};
+
+static void *parse_option(void *ptr, void *endptr, struct list_head *opt_list)
+{
+ struct dict_option *dopt;
+ struct dhcpv6_opt_hdr *opth = ptr;
+ struct dhcpv6_option *opt;
+
+ if (ptr + sizeof(*opth) + ntohs(opth->len) > endptr) {
+ log_warn("dhcpv6: invalid packet received\n");
+ return NULL;
+ }
+
+ opt = _malloc(sizeof(*opt));
+ if (!opt) {
+ log_emerg("out of memory\n");
+ return NULL;
+ }
+
+ memset(opt, 0, sizeof(*opt));
+ INIT_LIST_HEAD(&opt->opt_list);
+ opt->hdr = ptr;
+ list_add_tail(&opt->entry, opt_list);
+
+ for (dopt = known_options; dopt->code; dopt++) {
+ if (dopt->code)
+ break;
+ }
+
+ if (dopt->len) {
+ endptr = ptr + sizeof(*opth) + ntohs(opth->len);
+ ptr += sizeof(*opth) + dopt->len;
+ while (ptr < endptr) {
+ ptr = parse_option(ptr, endptr, &opt->opt_list);
+ if (!ptr)
+ return NULL;
+ }
+ } else
+ ptr = ptr + sizeof(*opth) + ntohs(opth->len);
+
+ return ptr;
+}
+
+struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
+{
+ struct dhcpv6_packet *pkt;
+ struct dhcpv6_opt_hdr *opth;
+ void *ptr, *endptr;
+
+ pkt = _malloc(sizeof(*pkt));
+ if (!pkt) {
+ log_emerg("out of memory\n");
+ return NULL;
+ }
+
+ memset(pkt, 0, sizeof(*pkt));
+ INIT_LIST_HEAD(&pkt->opt_list);
+
+ pkt->hdr = _malloc(size);
+ if (!pkt->hdr) {
+ log_emerg("out of memory\n");
+ _free(pkt);
+ return NULL;
+ }
+
+ memcpy(pkt->hdr, buf, size);
+
+ ptr = pkt->hdr->data;
+ endptr = ((void *)pkt->hdr) + size;
+
+ while (ptr < endptr) {
+ opth = ptr;
+ if (opth->code == htons(D6_OPTION_CLIENTID))
+ pkt->clientid = ptr;
+ else if (opth->code == htons(D6_OPTION_SERVERID))
+ pkt->serverid = ptr;
+ ptr = parse_option(ptr, endptr, &pkt->opt_list);
+ if (!ptr) {
+ dhcpv6_packet_free(pkt);
+ return NULL;
+ }
+ }
+
+ return pkt;
+}
+
+struct dhcpv6_option *dhcpv6_option_alloc(struct dhcpv6_packet *pkt, int code, int len)
+{
+ struct dhcpv6_option *opt;
+
+ opt = _malloc(sizeof(*opt));
+ if (!opt) {
+ log_emerg("out of memory\n");
+ return NULL;
+ }
+
+ memset(opt, 0, sizeof(*opt));
+ INIT_LIST_HEAD(&opt->opt_list);
+
+ opt->hdr = pkt->endptr;
+ opt->hdr->code = htons(code);
+ opt->hdr->len = htons(len);
+
+ pkt->endptr += sizeof(struct dhcpv6_opt_hdr) + len;
+
+ list_add_tail(&opt->entry, &pkt->opt_list);
+
+ return opt;
+}
+
+struct dhcpv6_option *dhcpv6_nested_option_alloc(struct dhcpv6_packet *pkt, struct dhcpv6_option *popt, int code, int len)
+{
+ struct dhcpv6_option *opt;
+
+ opt = _malloc(sizeof(*opt));
+ if (!opt) {
+ log_emerg("out of memory\n");
+ return NULL;
+ }
+
+ memset(opt, 0, sizeof(*opt));
+ INIT_LIST_HEAD(&opt->opt_list);
+ opt->parent = popt;
+
+ opt->hdr = pkt->endptr;
+ opt->hdr->code = htons(code);
+ opt->hdr->len = htons(len);
+
+ list_add_tail(&opt->entry, &popt->opt_list);
+
+ pkt->endptr += sizeof(struct dhcpv6_opt_hdr) + len;
+
+ while (popt) {
+ popt->hdr->len = htons(ntohs(popt->hdr->len) + sizeof(struct dhcpv6_opt_hdr) + len);
+ popt = popt->parent;
+ }
+
+ return opt;
+}
+
+
+struct dhcpv6_packet *dhcpv6_packet_alloc_reply(struct dhcpv6_packet *req, int type)
+{
+ struct dhcpv6_packet *pkt = _malloc(sizeof(*pkt));
+ struct dhcpv6_option *opt;
+
+ if (!pkt) {
+ log_emerg("out of memory\n");
+ return NULL;
+ }
+
+ memset(pkt, 0, sizeof(*pkt));
+ INIT_LIST_HEAD(&pkt->opt_list);
+ pkt->ppp = req->ppp;
+
+ pkt->hdr = _malloc(BUF_SIZE);
+ if (!pkt->hdr) {
+ log_emerg("out of memory\n");
+ _free(pkt);
+ return NULL;
+ }
+
+ pkt->hdr->type = type;
+ pkt->hdr->trans_id = req->hdr->trans_id;
+
+ pkt->endptr = pkt->hdr->data;
+
+ opt = dhcpv6_option_alloc(pkt, D6_OPTION_SERVERID, ntohs(req->serverid->hdr.len));
+ memcpy(opt->hdr, req->serverid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->serverid->hdr.len));
+
+ opt = dhcpv6_option_alloc(pkt, D6_OPTION_CLIENTID, ntohs(req->clientid->hdr.len));
+ memcpy(opt->hdr, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
+
+ return pkt;
+}
+
+static void free_options(struct list_head *opt_list)
+{
+ struct dhcpv6_option *opt;
+
+ while (!list_empty(opt_list)) {
+ opt = list_entry(opt_list->next, typeof(*opt), entry);
+ list_del(&opt->entry);
+ free_options(&opt->opt_list);
+ _free(opt);
+ }
+}
+
+void dhcpv6_packet_free(struct dhcpv6_packet *pkt)
+{
+ free_options(&pkt->opt_list);
+ _free(pkt->hdr);
+ _free(pkt);
+}
+
+static void print_options(struct list_head *opt_list, int level, void (*print)(const char *fmt, ...))
+{
+ struct dhcpv6_option *opt;
+ struct dict_option *dopt;
+ const char l_open[] = {'<', '{', '('};
+ const char l_close[] = {'>', '}', ')'};
+
+ if (level >= sizeof(l_open))
+ level = sizeof(l_open) - 1;
+
+ list_for_each_entry(opt, opt_list, entry) {
+ for (dopt = known_options; dopt->code; dopt++) {
+ if (htons(dopt->code) == opt->hdr->code)
+ break;
+ }
+ if (dopt->code) {
+ print(" %c%s", l_open[level], dopt->name);
+ if (dopt->print)
+ dopt->print(opt, print);
+
+ print_options(&opt->opt_list, level + 1, print);
+
+ print("%c", l_close[level]);
+ } else
+ print(" %cOption %i%c", l_open[level], ntohs(opt->hdr->code), l_close[level]);
+ }
+}
+
+void dhcpv6_packet_print(struct dhcpv6_packet *pkt, void (*print)(const char *fmt, ...))
+{
+ static const char *type_name[] = {
+ "Solicit",
+ "Advertise",
+ "Request",
+ "Confirt",
+ "Renew",
+ "Rebind",
+ "Reply",
+ "Release",
+ "Decline",
+ "Reconfigure",
+ "Information-Request",
+ "Relay-Form",
+ "Relay-Reply"
+ };
+
+ print("[DHCPv6 ");
+
+ if (pkt->hdr->type == 0 || pkt->hdr->type > 13)
+ print("Unknown");
+ else
+ print("%s", type_name[pkt->hdr->type - 1]);
+
+ print(" XID=%x", pkt->hdr->trans_id);
+
+ print_options(&pkt->opt_list, 0, print);
+
+ print("]\n");
+}
+
+static void print_clientid(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ int i;
+ struct dhcpv6_opt_clientid *o = (struct dhcpv6_opt_clientid *)opt->hdr;
+
+ print(" %i:", htons(o->duid.type));
+
+ for (i = 0; i < ntohs(o->hdr.len) - 2; i++)
+ print("%02x", o->duid.u.raw[i]);
+}
+
+static void print_ia_na(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ struct dhcpv6_opt_ia_na *o = (struct dhcpv6_opt_ia_na *)opt->hdr;
+
+ print(" %x T1=%i T2=%i", o->iaid, ntohl(o->T1), ntohl(o->T2));
+}
+
+static void print_ia_ta(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ struct dhcpv6_opt_ia_ta *o = (struct dhcpv6_opt_ia_ta *)opt->hdr;
+
+ print(" %x", o->iaid);
+}
+
+static void print_ia_addr(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ struct dhcpv6_opt_ia_addr *o = (struct dhcpv6_opt_ia_addr *)opt->hdr;
+ char str[50];
+
+ inet_ntop(AF_INET6, &o->addr, str, sizeof(str));
+ print(" %s pref_lifetime=%i valid_lifetime=%i", str, ntohl(o->pref_lifetime), ntohl(o->valid_lifetime));
+}
+
+static void print_oro(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ uint16_t *ptr = (uint16_t *)opt->hdr->data;
+ uint16_t *end_ptr = ptr + ntohs(opt->hdr->len)/2;
+ struct dict_option *dopt;
+ int f = 0;
+
+ for (; ptr < end_ptr; ptr++) {
+ if (f)
+ print(",");
+ else
+ print(" ");
+
+ for (dopt = known_options; dopt->code; dopt++) {
+ if (ntohs(*ptr) == dopt->code)
+ break;
+ }
+
+ if (dopt->code)
+ print("%s", dopt->name);
+ else
+ print("%i", ntohs(*ptr));
+
+ f = 1;
+ }
+}
+
+static void print_uint8(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ print(" %i", *(uint8_t *)opt->hdr->data);
+}
+
+static void print_time(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ print(" %u", *(uint32_t *)opt->hdr->data);
+}
+
+static void print_ipv6addr(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ char str[50];
+
+ inet_ntop(AF_INET6, opt->hdr->data, str, sizeof(str));
+
+ print(" %s", str);
+}
+
+static void print_ipv6addr_array(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ char str[50];
+ int i;
+ int f = 0;
+ struct in6_addr *addr = (struct in6_addr *)opt->hdr->data;
+
+ for (i = ntohs(opt->hdr->len) / sizeof(*addr); i; i--, addr++) {
+ inet_ntop(AF_INET6, addr, str, sizeof(str));
+ print("%c%s", f ? ',' : ' ', str);
+ f = 1;
+ }
+}
+
+static void print_status(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ struct dhcpv6_opt_status *o = (struct dhcpv6_opt_status *)opt->hdr;
+ static char *status_name[] = {
+ "Success",
+ "UnspecFail",
+ "NoAddrsAvail",
+ "NoBindings",
+ "NotOnLink",
+ "UseMulticast"
+ };
+
+ if (ntohs(o->code) < 0 || ntohs(o->code) > 5)
+ print(" %u", ntohs(o->code));
+ else
+ print(" %s", status_name[ntohs(o->code)]);
+}
+
+static void print_uint64(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+ print(" %llu", *(uint64_t *)opt->hdr->data);
+}
+
+static void print_reconf(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+
+}
+
+static void print_dnssl(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
+
+}
+
diff --git a/accel-pppd/ppp/ipv6_nd.c b/accel-pppd/ipv6/nd.c
index 6524c78a..67ba102e 100644
--- a/accel-pppd/ppp/ipv6_nd.c
+++ b/accel-pppd/ipv6/nd.c
@@ -30,6 +30,7 @@ static struct in6_addr conf_dns[MAX_DNS_COUNT];
static int conf_dns_count;
static void *conf_dnssl;
static int conf_dnssl_size;
+static int conf_managed;
#undef ND_OPT_ROUTE_INFORMATION
#define ND_OPT_ROUTE_INFORMATION 24
@@ -84,7 +85,7 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ad
void *buf = mempool_alloc(buf_pool), *endptr;
struct nd_router_advert *adv = buf;
struct nd_opt_prefix_info *pinfo;
- struct nd_opt_route_info_local *rinfo;
+ //struct nd_opt_route_info_local *rinfo;
struct nd_opt_rdnss_info_local *rdnssinfo;
struct in6_addr *rdnss_addr;
struct nd_opt_dnssl_info_local *dnsslinfo;
@@ -101,23 +102,28 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ad
adv->nd_ra_type = ND_ROUTER_ADVERT;
adv->nd_ra_curhoplimit = 64;
adv->nd_ra_router_lifetime = htons(conf_router_lifetime);
+ adv->nd_ra_flags_reserved =
+ conf_managed ? ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER : 0;
//adv->nd_ra_reachable = 0;
//adv->nd_ra_retransmit = 0;
pinfo = (struct nd_opt_prefix_info *)(adv + 1);
list_for_each_entry(a, &h->ppp->ipv6->addr_list, entry) {
+ if (a->prefix_len == 128)
+ continue;
+
memset(pinfo, 0, sizeof(*pinfo));
pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
pinfo->nd_opt_pi_len = 4;
pinfo->nd_opt_pi_prefix_len = a->prefix_len;
- pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO;
+ pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK | ((a->flag_auto || !conf_managed) ? ND_OPT_PI_FLAG_AUTO : 0);
pinfo->nd_opt_pi_valid_time = 0xffffffff;
pinfo->nd_opt_pi_preferred_time = 0xffffffff;
memcpy(&pinfo->nd_opt_pi_prefix, &a->addr, 8);
pinfo++;
}
- rinfo = (struct nd_opt_route_info_local *)pinfo;
+ /*rinfo = (struct nd_opt_route_info_local *)pinfo;
list_for_each_entry(a, &h->ppp->ipv6->route_list, entry) {
memset(rinfo, 0, sizeof(*rinfo));
rinfo->nd_opt_ri_type = ND_OPT_ROUTE_INFORMATION;
@@ -126,10 +132,10 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ad
rinfo->nd_opt_ri_lifetime = 0xffffffff;
memcpy(&rinfo->nd_opt_ri_prefix, &a->addr, 8);
rinfo++;
- }
+ }*/
if (conf_dns_count) {
- rdnssinfo = (struct nd_opt_rdnss_info_local *)rinfo;
+ rdnssinfo = (struct nd_opt_rdnss_info_local *)pinfo;
memset(rdnssinfo, 0, sizeof(*rdnssinfo));
rdnssinfo->nd_opt_rdnssi_type = ND_OPT_RDNSS_INFORMATION;
rdnssinfo->nd_opt_rdnssi_len = 1 + 2 * conf_dns_count;
@@ -140,7 +146,7 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ad
rdnss_addr++;
}
} else
- rdnss_addr = (struct in6_addr *)rinfo;
+ rdnss_addr = (struct in6_addr *)pinfo;
if (conf_dnssl) {
dnsslinfo = (struct nd_opt_dnssl_info_local *)rdnss_addr;
@@ -228,7 +234,7 @@ static int ipv6_nd_read(struct triton_md_handler_t *_h)
return 0;
}
-int ppp_ipv6_nd_start(struct ppp_t *ppp, uint64_t intf_id)
+static int ipv6_nd_start(struct ppp_t *ppp)
{
int sock;
struct icmp6_filter filter;
@@ -247,7 +253,7 @@ int ppp_ipv6_nd_start(struct ppp_t *ppp, uint64_t intf_id)
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_addr.s6_addr32[0] = htons(0xfe80);
- *(uint64_t *)(addr.sin6_addr.s6_addr + 8) = intf_id;
+ *(uint64_t *)(addr.sin6_addr.s6_addr + 8) = ppp->ipv6->intf_id;
addr.sin6_scope_id = ppp->ifindex;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) {
@@ -311,6 +317,8 @@ int ppp_ipv6_nd_start(struct ppp_t *ppp, uint64_t intf_id)
triton_md_register_handler(ppp->ctrl->ctx, &h->hnd);
triton_md_enable_handler(&h->hnd, MD_MODE_READ);
+ triton_timer_add(ppp->ctrl->ctx, &h->timer, 0);
+
return 0;
out_err:
@@ -332,12 +340,10 @@ static struct ipv6_nd_handler_t *find_pd(struct ppp_t *ppp)
static void ev_ppp_started(struct ppp_t *ppp)
{
- struct ipv6_nd_handler_t *h = find_pd(ppp);
-
- if (!h)
+ if (!ppp->ipv6)
return;
-
- triton_timer_add(ppp->ctrl->ctx, &h->timer, 0);
+
+ ipv6_nd_start(ppp);
}
static void ev_ppp_finishing(struct ppp_t *ppp)
@@ -441,6 +447,8 @@ static void init(void)
buf_pool = mempool_create(BUF_SIZE);
load_config();
+
+ conf_managed = triton_module_loaded("ipv6_dhcp");
triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
triton_event_register_handler(EV_PPP_STARTED, (triton_event_func)ev_ppp_started);
diff --git a/accel-pppd/ppp/ipv6cp_opt_intfid.c b/accel-pppd/ppp/ipv6cp_opt_intfid.c
index 610f0dc3..cc7ce364 100644
--- a/accel-pppd/ppp/ipv6cp_opt_intfid.c
+++ b/accel-pppd/ppp/ipv6cp_opt_intfid.c
@@ -50,8 +50,8 @@ static void ipaddr_print(void (*print)(const char *fmt,...),struct ipv6cp_option
struct ipaddr_option_t
{
struct ipv6cp_option_t opt;
- uint64_t intf_id;
int started:1;
+ struct ppp_t *ppp;
};
static struct ipv6cp_option_handler_t ipaddr_opt_hnd =
@@ -72,16 +72,8 @@ static struct ipv6cp_option_t *ipaddr_init(struct ppp_ipv6cp_t *ipv6cp)
ipaddr_opt->opt.id = CI_INTFID;
ipaddr_opt->opt.len = 10;
+ ipaddr_opt->ppp = ipv6cp->ppp;
- switch (conf_intf_id) {
- case INTF_ID_FIXED:
- ipaddr_opt->intf_id = conf_intf_id_val;
- break;
- case INTF_ID_RANDOM:
- read(urandom_fd, &ipaddr_opt->intf_id, 8);
- break;
- }
-
return &ipaddr_opt->opt;
}
@@ -127,6 +119,23 @@ out:
return r;
}
+static uint64_t generate_intf_id(struct ppp_t *ppp)
+{
+ uint64_t id = 0;
+
+ switch (conf_intf_id) {
+ case INTF_ID_FIXED:
+ return conf_intf_id_val;
+ break;
+ //case INTF_ID_RANDOM:
+ default:
+ read(urandom_fd, &id, 8);
+ break;
+ }
+
+ return id;
+}
+
static uint64_t generate_peer_intf_id(struct ppp_t *ppp)
{
char str[4];
@@ -166,6 +175,9 @@ static int alloc_ip(struct ppp_t *ppp)
log_ppp_warn("ppp: no free IPv6 address\n");
return IPV6CP_OPT_CLOSE;
}
+
+ if (!ppp->ipv6->intf_id)
+ ppp->ipv6->intf_id = generate_intf_id(ppp);
if (conf_check_exists && check_exists(ppp))
return IPV6CP_OPT_FAIL;
@@ -187,7 +199,7 @@ static int ipaddr_send_conf_req(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_optio
opt64->hdr.id = CI_INTFID;
opt64->hdr.len = 10;
- opt64->val = ipaddr_opt->intf_id;
+ opt64->val = ipv6cp->ppp->ipv6->intf_id;
return 10;
}
@@ -197,7 +209,7 @@ static int ipaddr_send_conf_nak(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_optio
struct ipv6cp_opt64_t *opt64 = (struct ipv6cp_opt64_t *)ptr;
opt64->hdr.id = CI_INTFID;
opt64->hdr.len = 10;
- opt64->val = ipv6cp->ppp->ipv6->intf_id;
+ opt64->val = ipv6cp->ppp->ipv6->peer_intf_id;
return 10;
}
@@ -219,15 +231,15 @@ static int ipaddr_recv_conf_req(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_optio
}
if (conf_accept_peer_intf_id && opt64->val)
- ipv6cp->ppp->ipv6->intf_id = opt64->val;
+ ipv6cp->ppp->ipv6->peer_intf_id = opt64->val;
- if (opt64->val && ipv6cp->ppp->ipv6->intf_id == opt64->val && opt64->val != ipaddr_opt->intf_id) {
+ if (opt64->val && ipv6cp->ppp->ipv6->peer_intf_id == opt64->val && opt64->val != ipv6cp->ppp->ipv6->intf_id) {
ipv6cp->delay_ack = ccp_ipcp_started(ipv6cp->ppp);
goto ack;
}
- ipv6cp->ppp->ipv6->intf_id = generate_peer_intf_id(ipv6cp->ppp);
- if (!ipv6cp->ppp->ipv6->intf_id)
+ ipv6cp->ppp->ipv6->peer_intf_id = generate_peer_intf_id(ipv6cp->ppp);
+ if (!ipv6cp->ppp->ipv6->peer_intf_id)
return IPV6CP_OPT_TERMACK;
return IPV6CP_OPT_NAK;
@@ -251,7 +263,7 @@ ack:
memset(&ifr6, 0, sizeof(ifr6));
ifr6.ifr6_addr.s6_addr32[0] = htons(0xfe80);
- *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ipaddr_opt->intf_id;
+ *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ipv6cp->ppp->ipv6->intf_id;
ifr6.ifr6_prefixlen = 64;
ifr6.ifr6_ifindex = ipv6cp->ppp->ifindex;
@@ -261,7 +273,15 @@ ack:
}
list_for_each_entry(a, &ipv6cp->ppp->ipv6->addr_list, entry) {
- memcpy(ifr6.ifr6_addr.s6_addr, a->addr.s6_addr, 8);
+ if (a->prefix_len == 128)
+ continue;
+
+ memcpy(ifr6.ifr6_addr.s6_addr, a->addr.s6_addr, 16);
+
+ if (a->prefix_len <= 64)
+ *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ipv6cp->ppp->ipv6->intf_id;
+ else
+ *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) |= ipv6cp->ppp->ipv6->intf_id & ((1 << (128 - a->prefix_len)) - 1);
if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) {
log_ppp_error("ppp:ipv6cp: ioctl(SIOCSIFADDR): %s\n", strerror(errno));
@@ -269,9 +289,6 @@ ack:
}
}
- if (ppp_ipv6_nd_start(ipv6cp->ppp, ipaddr_opt->intf_id))
- return IPV6CP_OPT_REJ;
-
return IPV6CP_OPT_ACK;
}
@@ -284,7 +301,7 @@ static void ipaddr_print(void (*print)(const char *fmt,...), struct ipv6cp_optio
if (ptr)
*(uint64_t *)(a.s6_addr + 8) = opt64->val;
else
- *(uint64_t *)(a.s6_addr + 8) = ipaddr_opt->intf_id;
+ *(uint64_t *)(a.s6_addr + 8) = ipaddr_opt->ppp->ipv6->intf_id;
print("<addr %x:%x:%x:%x>", ntohs(a.s6_addr16[4]), ntohs(a.s6_addr16[5]), ntohs(a.s6_addr16[6]), ntohs(a.s6_addr16[7]));
}
diff --git a/accel-pppd/ppp/ppp_ipv6cp.c b/accel-pppd/ppp/ppp_ipv6cp.c
index 1a9a334b..816abc42 100644
--- a/accel-pppd/ppp/ppp_ipv6cp.c
+++ b/accel-pppd/ppp/ppp_ipv6cp.c
@@ -30,7 +30,7 @@ struct recv_opt_t
#define START_TIMEOUT 60
-static int conf_ipv6 = IPV6_ALLOW;
+static int conf_ipv6 = IPV6_DENY;
static LIST_HEAD(option_handlers);
static struct ppp_layer_t ipv6cp_layer;
diff --git a/accel-pppd/radius/radius.c b/accel-pppd/radius/radius.c
index 22e4cca9..94f6447d 100644
--- a/accel-pppd/radius/radius.c
+++ b/accel-pppd/radius/radius.c
@@ -122,7 +122,7 @@ int rad_proc_attrs(struct rad_req_t *req)
req->rpd->termination_action = attr->val.integer;
break;
case Framed_Interface_Id:
- req->rpd->ipv6_addr.intf_id = attr->val.ifid;
+ req->rpd->ipv6_addr.peer_intf_id = attr->val.ifid;
break;
case Framed_IPv6_Prefix:
a = _malloc(sizeof(*a));
@@ -185,7 +185,10 @@ static struct ipv4db_item_t *get_ipv4(struct ppp_t *ppp)
static struct ipv6db_item_t *get_ipv6(struct ppp_t *ppp)
{
struct radius_pd_t *rpd = find_pd(ppp);
-
+
+ rpd->ipv6_addr.owner = &ipdb;
+ rpd->ipv6_addr.intf_id = 0;
+
if (!list_empty(&rpd->ipv6_addr.addr_list))
return &rpd->ipv6_addr;
@@ -218,7 +221,6 @@ static void ppp_starting(struct ppp_t *ppp)
pthread_mutex_init(&rpd->lock, NULL);
INIT_LIST_HEAD(&rpd->plugin_list);
INIT_LIST_HEAD(&rpd->ipv6_addr.addr_list);
- INIT_LIST_HEAD(&rpd->ipv6_addr.route_list);
list_add_tail(&rpd->pd.entry, &ppp->pd_list);
pthread_rwlock_wrlock(&sessions_lock);
diff --git a/accel-pppd/radius/req.c b/accel-pppd/radius/req.c
index c9b91494..3c4a38b2 100644
--- a/accel-pppd/radius/req.c
+++ b/accel-pppd/radius/req.c
@@ -139,7 +139,7 @@ int rad_req_acct_fill(struct rad_req_t *req)
return -1;
}
if (req->rpd->ppp->ipv6) {
- if (rad_packet_add_ifid(req->pack, NULL, "Framed-Interface-Id", req->rpd->ppp->ipv6->intf_id))
+ if (rad_packet_add_ifid(req->pack, NULL, "Framed-Interface-Id", req->rpd->ppp->ipv6->peer_intf_id))
return -1;
list_for_each_entry(a, &req->rpd->ppp->ipv6->addr_list, entry) {
if (rad_packet_add_ipv6prefix(req->pack, NULL, "Framed-IPv6-Prefix", &a->addr, a->prefix_len))