diff options
author | Vladislav Grishenko <themiron@mail.ru> | 2020-01-10 05:29:14 +0500 |
---|---|---|
committer | Vladislav Grishenko <themiron@mail.ru> | 2020-01-10 05:42:16 +0500 |
commit | 8c9c91764069d3f6d3122c7897b05b0b93024246 (patch) | |
tree | cc6461cd7a5fcd2189d78dd33b9afae39e0146dd /accel-pppd/extra | |
parent | 385349450d42f6e1b92e08cabe97ed52cb740fab (diff) | |
download | accel-ppp-8c9c91764069d3f6d3122c7897b05b0b93024246.tar.gz accel-ppp-8c9c91764069d3f6d3122c7897b05b0b93024246.zip |
ipv6pool/radius: implement named ipv6 pools
default stateful ipv6 address & prefix radius attrs are per-rfc6911:
171 Delegated-IPv6-Prefix-Pool
172 Stateful-IPv6-Address-Pool
the single pool name from chap-secret file pool is shared for
ipv4/ipv6/ipv6 dp, new config syntax TBD.
per-proto pool names are still for ipv4 only, new config syntax TBD.
Diffstat (limited to 'accel-pppd/extra')
-rw-r--r-- | accel-pppd/extra/chap-secrets.c | 6 | ||||
-rw-r--r-- | accel-pppd/extra/ipv6pool.c | 304 |
2 files changed, 273 insertions, 37 deletions
diff --git a/accel-pppd/extra/chap-secrets.c b/accel-pppd/extra/chap-secrets.c index 92cfb42f..2d8593c9 100644 --- a/accel-pppd/extra/chap-secrets.c +++ b/accel-pppd/extra/chap-secrets.c @@ -313,6 +313,12 @@ static struct ipv4db_item_t *get_ip(struct ap_session *ses) if (ses->ipv4_pool_name) _free(ses->ipv4_pool_name); ses->ipv4_pool_name = _strdup(pd->pool); + if (ses->ipv6_pool_name) + _free(ses->ipv6_pool_name); + ses->ipv6_pool_name = _strdup(pd->pool); + if (ses->ipv6_dppool_name) + _free(ses->ipv6_dppool_name); + ses->ipv6_dppool_name = _strdup(pd->pool); return NULL; } else if (!pd->ip.peer_addr) return NULL; diff --git a/accel-pppd/extra/ipv6pool.c b/accel-pppd/extra/ipv6pool.c index 4f29a280..f00dbcfa 100644 --- a/accel-pppd/extra/ipv6pool.c +++ b/accel-pppd/extra/ipv6pool.c @@ -8,31 +8,60 @@ #include <arpa/inet.h> #include <endian.h> +#include "events.h" #include "ipdb.h" #include "list.h" #include "log.h" #include "spinlock.h" +#ifdef RADIUS +#include "radius.h" +#endif + #include "memdebug.h" +enum ippool_type +{ + IPPOOL_ADDRESS, + IPPOOL_PREFIX +}; + +struct ippool_t +{ + struct list_head entry; + char *name; + struct list_head gw_list; + struct list_head items; + struct ippool_t *next; + spinlock_t lock; +}; struct ippool_item_t { struct list_head entry; + struct ippool_t *pool; struct ipv6db_item_t it; }; struct dppool_item_t { struct list_head entry; + struct ippool_t *pool; struct ipv6db_prefix_t it; }; -static LIST_HEAD(ippool); -static LIST_HEAD(dppool); -static spinlock_t pool_lock; +#ifdef RADIUS +static int conf_vendor = 0; +static int conf_dppool_attr = 171; // Delegated-IPv6-Prefix-Pool +static int conf_ippool_attr = 172; // Stateful-IPv6-Address-Pool +#endif + +static LIST_HEAD(ippool_list); +static LIST_HEAD(dppool_list); static struct ipdb_t ipdb; static struct in6_addr conf_gw_addr; +static struct ippool_t *def_ippool; +static struct ippool_t *def_dppool; static void in6_addr_add(struct in6_addr *res, const struct in6_addr *arg) { @@ -59,7 +88,40 @@ static int in6_addr_cmp(const struct in6_addr *n1, const struct in6_addr *n2) return 0; } -static void generate_ippool(struct in6_addr *addr, int mask, int prefix_len) +static struct ippool_t *create_pool(enum ippool_type type, char *name) +{ + struct ippool_t *pool = malloc(sizeof(*pool)); + struct list_head *pool_list = (type == IPPOOL_PREFIX) ? &dppool_list : &ippool_list; + + memset(pool, 0, sizeof(*pool)); + pool->name = name; + + INIT_LIST_HEAD(&pool->items); + spinlock_init(&pool->lock); + + if (name) + list_add_tail(&pool->entry, pool_list); + + return pool; +} + +static struct ippool_t *find_pool(enum ippool_type type, char *name, int create) +{ + struct ippool_t *pool; + struct list_head *pool_list = (type == IPPOOL_PREFIX) ? &dppool_list : &ippool_list; + + list_for_each_entry(pool, pool_list, entry) { + if (!strcmp(pool->name, name)) + return pool; + } + + if (create) + return create_pool(type, name); + + return NULL; +} + +static void generate_ippool(struct ippool_t *pool, struct in6_addr *addr, int mask, int prefix_len) { struct ippool_item_t *it; struct ipv6db_addr_t *a; @@ -84,6 +146,7 @@ static void generate_ippool(struct in6_addr *addr, int mask, int prefix_len) while (in6_addr_cmp(&ip, &end) <= 0) { it = malloc(sizeof(*it)); memset(it, 0, sizeof(*it)); + it->pool = pool; it->it.owner = &ipdb; INIT_LIST_HEAD(&it->it.addr_list); a = malloc(sizeof(*a)); @@ -91,12 +154,12 @@ static void generate_ippool(struct in6_addr *addr, int mask, int prefix_len) memcpy(&a->addr, &ip, sizeof(ip)); a->prefix_len = prefix_len; list_add_tail(&a->entry, &it->it.addr_list); - list_add_tail(&it->entry, &ippool); + list_add_tail(&it->entry, &pool->items); in6_addr_add(&ip, &step); } } -static void generate_dppool(struct in6_addr *addr, int mask, int prefix_len) +static void generate_dppool(struct ippool_t *pool, struct in6_addr *addr, int mask, int prefix_len) { struct dppool_item_t *it; struct in6_addr ip, end, step; @@ -121,6 +184,7 @@ static void generate_dppool(struct in6_addr *addr, int mask, int prefix_len) while (in6_addr_cmp(&ip, &end) <= 0) { it = malloc(sizeof(*it)); memset(it, 0, sizeof(*it)); + it->pool = pool; it->it.owner = &ipdb; INIT_LIST_HEAD(&it->it.prefix_list); a = malloc(sizeof(*a)); @@ -128,13 +192,12 @@ static void generate_dppool(struct in6_addr *addr, int mask, int prefix_len) memcpy(&a->addr, &ip, sizeof(ip)); a->prefix_len = prefix_len; list_add_tail(&a->entry, &it->it.prefix_list); - list_add_tail(&it->entry, &dppool); + list_add_tail(&it->entry, &pool->items); in6_addr_add(&ip, &step); } } - -static void add_prefix(int type, const char *_val) +static void add_prefix(enum ippool_type type, struct ippool_t *pool, const char *_val) { char *val = _strdup(_val); char *ptr1, *ptr2; @@ -169,10 +232,10 @@ static void add_prefix(int type, const char *_val) if (prefix_len > 128 || prefix_len < mask) goto err; - if (type) - generate_dppool(&addr, mask, prefix_len); + if (type == IPPOOL_PREFIX) + generate_dppool(pool, &addr, mask, prefix_len); else - generate_ippool(&addr, mask, prefix_len); + generate_ippool(pool, &addr, mask, prefix_len); _free(val); return; @@ -186,14 +249,24 @@ static struct ipv6db_item_t *get_ip(struct ap_session *ses) { struct ippool_item_t *it; struct ipv6db_addr_t *a; + struct ippool_t *pool; + + if (ses->ipv6_pool_name) + pool = find_pool(IPPOOL_ADDRESS, ses->ipv6_pool_name, 0); + else + pool = def_ippool; + + if (!pool) + return NULL; - spin_lock(&pool_lock); - if (!list_empty(&ippool)) { - it = list_entry(ippool.next, typeof(*it), entry); +again: + spin_lock(&pool->lock); + if (!list_empty(&pool->items)) { + it = list_entry(pool->items.next, typeof(*it), entry); list_del(&it->entry); } else it = NULL; - spin_unlock(&pool_lock); + spin_unlock(&pool->lock); if (it) { a = list_entry(it->it.addr_list.next, typeof(*a), entry); @@ -206,6 +279,9 @@ static struct ipv6db_item_t *get_ip(struct ap_session *ses) } return &it->it; + } else if (pool->next) { + pool = pool->next; + goto again; } return NULL; @@ -215,33 +291,47 @@ static void put_ip(struct ap_session *ses, struct ipv6db_item_t *it) { struct ippool_item_t *pit = container_of(it, typeof(*pit), it); - spin_lock(&pool_lock); - list_add_tail(&pit->entry, &ippool); - spin_unlock(&pool_lock); + spin_lock(&pit->pool->lock); + list_add_tail(&pit->entry, &pit->pool->items); + spin_unlock(&pit->pool->lock); } static struct ipv6db_prefix_t *get_dp(struct ap_session *ses) { struct dppool_item_t *it; + struct ippool_t *pool; - spin_lock(&pool_lock); - if (!list_empty(&dppool)) { - it = list_entry(dppool.next, typeof(*it), entry); + if (ses->ipv6_pool_name) + pool = find_pool(IPPOOL_PREFIX, ses->ipv6_dppool_name, 0); + else + pool = def_dppool; + +again: + spin_lock(&pool->lock); + if (!list_empty(&pool->items)) { + it = list_entry(pool->items.next, typeof(*it), entry); list_del(&it->entry); } else it = NULL; - spin_unlock(&pool_lock); + spin_unlock(&pool->lock); - return it ? &it->it : NULL; + if (it) + return &it->it; + else if (pool->next) { + pool = pool->next; + goto again; + } + + return NULL; } static void put_dp(struct ap_session *ses, struct ipv6db_prefix_t *it) { struct dppool_item_t *pit = container_of(it, typeof(*pit), it); - spin_lock(&pool_lock); - list_add_tail(&pit->entry, &dppool); - spin_unlock(&pool_lock); + spin_lock(&pit->pool->lock); + list_add_tail(&pit->entry, &pit->pool->items); + spin_unlock(&pit->pool->lock); } static struct ipdb_t ipdb = { @@ -251,28 +341,168 @@ static struct ipdb_t ipdb = { .put_ipv6_prefix = put_dp, }; -static void ippool_init(void) +#ifdef RADIUS +static void ev_radius_access_accept(struct ev_radius_t *ev) +{ + struct rad_attr_t *attr; + struct ap_session *ses = ev->ses; + + list_for_each_entry(attr, &ev->reply->attrs, entry) { + if (attr->attr->type != ATTR_TYPE_STRING) + continue; + if (attr->vendor && attr->vendor->id != conf_vendor) + continue; + if (!attr->vendor && conf_vendor) + continue; + + if (conf_dppool_attr && conf_dppool_attr == attr->attr->id) { + if (ses->ipv6_dppool_name) + _free(ses->ipv6_dppool_name); + ses->ipv6_dppool_name = _strdup(attr->val.string); + } else + if (conf_ippool_attr && conf_ippool_attr == attr->attr->id) { + if (ses->ipv6_pool_name) + _free(ses->ipv6_pool_name); + ses->ipv6_pool_name = _strdup(attr->val.string); + } + } +} + +static int parse_attr_opt(const char *opt) +{ + struct rad_dict_attr_t *attr; + struct rad_dict_vendor_t *vendor; + + if (conf_vendor) + vendor = rad_dict_find_vendor_id(conf_vendor); + else + vendor = NULL; + + if (conf_vendor) { + if (vendor) + attr = rad_dict_find_vendor_attr(vendor, opt); + else + attr = NULL; + } else + attr = rad_dict_find_attr(opt); + + if (attr) + return attr->id; + + return atoi(opt); +} + +static int parse_vendor_opt(const char *opt) +{ + struct rad_dict_vendor_t *vendor; + + vendor = rad_dict_find_vendor_name(opt); + if (vendor) + return vendor->id; + + return atoi(opt); +} +#endif + +static void parse_options(enum ippool_type type, const char *opt, char **pool_name, struct ippool_t **next) +{ + char *ptr1, *ptr2, *tmp; + + ptr1 = strstr(opt, ",name="); + if (ptr1) { + ptr1 += sizeof(",name=") - 1; + ptr2 = strchrnul(ptr1, ','); + *pool_name = _strndup(ptr1, ptr2 - ptr1); + } + + ptr1 = strstr(opt, ",next="); + if (ptr1) { + ptr1 += sizeof(",next=") - 1; + ptr2 = strchrnul(ptr1, ','); + if (*ptr2 == ',') { + tmp = alloca(ptr2 - ptr1 + 1); + strncpy(tmp, ptr1, ptr2 - ptr1); + ptr1 = tmp; + } + *next = find_pool(type, ptr1, 0); + if (!(*next)) + log_error("ipv6_pool: %s: next pool not found\n", opt); + } +} + +static void ippool_init1(void) +{ + ipdb_register(&ipdb); +} + +static void ippool_init2(void) { struct conf_sect_t *s = conf_get_section("ipv6-pool"); struct conf_option_t *opt; - - spinlock_init(&pool_lock); + struct ippool_t *pool, *next; + char *pool_name, *val; + enum ippool_type type; +#ifdef RADIUS + int dppool_attr = 0, ippool_attr = 0; +#endif if (!s) return; + def_ippool = create_pool(IPPOOL_ADDRESS, NULL); + def_dppool = create_pool(IPPOOL_PREFIX, NULL); + list_for_each_entry(opt, &s->items, entry) { +#ifdef RADIUS + if (triton_module_loaded("radius")) { + if (!strcmp(opt->name, "vendor")) { + conf_vendor = parse_vendor_opt(opt->val); + continue; + } + if (!strcmp(opt->name, "attr-prefix")) { + dppool_attr = parse_attr_opt(opt->val); + continue; + } + if (!strcmp(opt->name, "attr-address")) { + ippool_attr = parse_attr_opt(opt->val); + continue; + } + } +#endif if (!strcmp(opt->name, "gw-ip6-address")) { if (inet_pton(AF_INET6, opt->val, &conf_gw_addr) == 0) log_error("ipv6_pool: failed to parse gw-ip6-address\n"); - } else if (!strcmp(opt->name, "delegate")) - add_prefix(1, opt->val); - else - add_prefix(0, opt->name); + continue; + } else if (!strcmp(opt->name, "delegate")) { + type = IPPOOL_PREFIX; + val = opt->val; + pool = def_dppool; + } else { + type = IPPOOL_ADDRESS; + val = opt->name; + pool = def_ippool; + } + + pool_name = NULL; + parse_options(type, opt->raw, &pool_name, &next); + + if (pool_name) + pool = find_pool(type, pool_name, 1); + add_prefix(type, pool, val); + + pool->next = next; } - ipdb_register(&ipdb); +#ifdef RADIUS + if (triton_module_loaded("radius")) { + if (conf_vendor || dppool_attr) + conf_dppool_attr = dppool_attr; + if (conf_vendor || ippool_attr) + conf_ippool_attr = ippool_attr; + triton_event_register_handler(EV_RADIUS_ACCESS_ACCEPT, (triton_event_func)ev_radius_access_accept); + } +#endif } -DEFINE_INIT(51, ippool_init); - +DEFINE_INIT(51, ippool_init1); +DEFINE_INIT2(52, ippool_init2); |