diff options
author | Kozlov Dmitry <xeb@mail.ru> | 2011-08-28 00:37:34 +0400 |
---|---|---|
committer | Kozlov Dmitry <xeb@mail.ru> | 2011-08-28 00:37:34 +0400 |
commit | 6459c6085f7324404717418448fa8c04ffd46c20 (patch) | |
tree | 9c610e3aa4497463caf0c534f725d059229e0db0 /accel-pppd | |
parent | 2ae178c12aced47699cbb252f6a67defb0d0bbe7 (diff) | |
download | accel-ppp-6459c6085f7324404717418448fa8c04ffd46c20.tar.gz accel-ppp-6459c6085f7324404717418448fa8c04ffd46c20.zip |
ipv6: initial dhcpv6 support
Diffstat (limited to 'accel-pppd')
-rw-r--r-- | accel-pppd/CMakeLists.txt | 2 | ||||
-rw-r--r-- | accel-pppd/accel-ppp.conf | 7 | ||||
-rw-r--r-- | accel-pppd/extra/ipv6pool.c | 2 | ||||
-rw-r--r-- | accel-pppd/ipdb.h | 3 | ||||
-rw-r--r-- | accel-pppd/ipv6/CMakeLists.txt | 7 | ||||
-rw-r--r-- | accel-pppd/ipv6/dhcpv6.c | 551 | ||||
-rw-r--r-- | accel-pppd/ipv6/dhcpv6.h | 175 | ||||
-rw-r--r-- | accel-pppd/ipv6/dhcpv6_packet.c | 438 | ||||
-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.c | 61 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_ipv6cp.c | 2 | ||||
-rw-r--r-- | accel-pppd/radius/radius.c | 8 | ||||
-rw-r--r-- | accel-pppd/radius/req.c | 2 |
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)) |