diff options
Diffstat (limited to 'accel-pppd/ctrl/ipoe/ipoe.c')
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 254 |
1 files changed, 244 insertions, 10 deletions
diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index 94f50fd..f4701d1 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -79,11 +79,14 @@ static int conf_lease_timeout = 660; static int conf_verbose; static const char *conf_agent_remote_id; static int conf_proto; +static LIST_HEAD(conf_offer_delay); static unsigned int stat_starting; static unsigned int stat_active; +static unsigned int stat_delayed_offer; static mempool_t ses_pool; +static mempool_t disc_item_pool; static LIST_HEAD(serv_list); @@ -117,6 +120,19 @@ struct gw_addr { int mask1; }; +struct disc_item { + struct list_head entry; + struct dhcpv4_packet *pack; + struct timespec ts; +}; + +struct delay { + struct list_head entry; + unsigned int conn_cnt; + int delay; +}; + + static pthread_mutex_t uc_lock = PTHREAD_MUTEX_INITIALIZER; static LIST_HEAD(uc_list); static int uc_size; @@ -132,6 +148,8 @@ static void ipoe_drop_sessions(struct ipoe_serv *serv, struct ipoe_session *skip static void ipoe_serv_close(struct triton_context_t *ctx); static void __ipoe_session_activate(struct ipoe_session *ses); static void ipoe_ses_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack); +static void __ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack, int force); +static int get_offer_delay(); static struct ipoe_session *ipoe_session_lookup(struct ipoe_serv *serv, struct dhcpv4_packet *pack, struct ipoe_session **opt82_ses) { @@ -183,7 +201,7 @@ static struct ipoe_session *ipoe_session_lookup(struct ipoe_serv *serv, struct d if (opt82_match && opt82_ses) *opt82_ses = ses; - if (memcmp(pack->hdr->chaddr, ses->hwaddr, 6)) + if (memcmp(pack->hdr->chaddr, ses->hwaddr, ETH_ALEN)) continue; res = ses; @@ -385,6 +403,8 @@ static void ipoe_session_start(struct ipoe_session *ses) struct ifreq ifr; struct unit_cache *uc; + __sync_add_and_fetch(&stat_starting, 1); + if (!ses->ses.username) { strncpy(ses->ses.ifname, ses->serv->ifname, AP_IFNAME_LEN); @@ -692,6 +712,10 @@ static void __ipoe_session_activate(struct ipoe_session *ses) if (ses->l4_redirect) ipoe_change_l4_redirect(ses, 0); + __sync_sub_and_fetch(&stat_starting, 1); + __sync_add_and_fetch(&stat_active, 1); + ses->started = 1; + ap_session_activate(&ses->ses); if (ses->dhcpv4_request) { @@ -786,6 +810,11 @@ static void ipoe_session_free(struct ipoe_session *ses) { struct unit_cache *uc; + if (ses->started) + __sync_sub_and_fetch(&stat_active, 1); + else + __sync_sub_and_fetch(&stat_starting, 1); + if (ses->timer.tpd) triton_timer_del(&ses->timer); @@ -1056,10 +1085,88 @@ static void ipoe_ses_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packe } } -static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack) +static void ipoe_serv_disc_timer(struct triton_timer_t *t) +{ + struct ipoe_serv *serv = container_of(t, typeof(*serv), disc_timer); + struct disc_item *d; + struct timespec ts; + int delay, offer_delay; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + while (!list_empty(&serv->disc_list)) { + d = list_entry(serv->disc_list.next, typeof(*d), entry); + + delay = (ts.tv_sec - d->ts.tv_sec) * 1000 + (ts.tv_nsec - d->ts.tv_nsec) / 1000000; + offer_delay = get_offer_delay(); + + if (delay < offer_delay - 1) { + delay = offer_delay - delay; + t->expire_tv.tv_sec = delay / 1000; + t->expire_tv.tv_usec = (delay % 1000) * 1000; + triton_timer_mod(t, 0); + return; + } + + __ipoe_recv_dhcpv4(serv->dhcpv4, d->pack, 1); + + list_del(&d->entry); + dhcpv4_packet_free(d->pack); + mempool_free(d); + + __sync_sub_and_fetch(&stat_delayed_offer, 1); + } + + triton_timer_del(t); +} + +static void ipoe_serv_add_disc(struct ipoe_serv *serv, struct dhcpv4_packet *pack, int offer_delay) +{ + struct disc_item *d = mempool_alloc(disc_item_pool); + + if (!d) + return; + + __sync_add_and_fetch(&stat_delayed_offer, 1); + + dhcpv4_packet_ref(pack); + d->pack = pack; + clock_gettime(CLOCK_MONOTONIC, &d->ts); + list_add_tail(&d->entry, &serv->disc_list); + + if (!serv->disc_timer.tpd) { + serv->disc_timer.expire_tv.tv_sec = offer_delay / 1000; + serv->disc_timer.expire_tv.tv_usec = (offer_delay % 1000) * 1000; + triton_timer_add(&serv->ctx, &serv->disc_timer, 0); + } +} + +static void ipoe_serv_check_disc(struct ipoe_serv *serv, struct dhcpv4_packet *pack) +{ + struct disc_item *d; + + list_for_each_entry(d, &serv->disc_list, entry) { + if (d->pack->hdr->xid != pack->hdr->xid) + continue; + + if (memcmp(d->pack->hdr->chaddr, pack->hdr->chaddr, ETH_ALEN)) + continue; + + list_del(&d->entry); + dhcpv4_packet_free(d->pack); + mempool_free(d); + + __sync_sub_and_fetch(&stat_delayed_offer, 1); + + break; + } +} + +static void __ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack, int force) { struct ipoe_serv *serv = container_of(dhcpv4->ctx, typeof(*serv), ctx); struct ipoe_session *ses, *opt82_ses; + int offer_delay; //struct dhcpv4_packet *reply; if (ap_shutdown) @@ -1078,6 +1185,15 @@ static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p triton_context_call(&opt82_ses->ctx, (triton_event_func)__ipoe_session_terminate, &opt82_ses->ses); } + offer_delay = get_offer_delay(); + if (offer_delay == -1) + goto out; + + if (offer_delay && !force) { + ipoe_serv_add_disc(serv, pack, offer_delay); + goto out; + } + ses = ipoe_session_create_dhcpv4(serv, pack); if (ses) { dhcpv4_packet_ref(pack); @@ -1117,17 +1233,20 @@ static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p } } } else if (pack->msg_type == DHCPREQUEST) { + ipoe_serv_check_disc(serv, pack); + ses = ipoe_session_lookup(serv, pack, &opt82_ses); if (!ses) { if (conf_verbose) { - log_info2("recv "); - dhcpv4_print_packet(pack, 0, log_info2); + log_debug("recv "); + dhcpv4_print_packet(pack, 0, log_debug); } - - if (serv->opt_shared == 0) + + if (serv->opt_shared == 0) { ipoe_drop_sessions(serv, NULL); - else if (opt82_ses) { + //dhcpv4_send_nak(dhcpv4, pack); + } else if (opt82_ses) { if (conf_verbose) { log_switch(dhcpv4->ctx, &opt82_ses->ses); log_ppp_warn("mac change detected\n"); @@ -1135,11 +1254,10 @@ static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p triton_context_call(&opt82_ses->ctx, (triton_event_func)__ipoe_session_terminate, &opt82_ses->ses); } - - dhcpv4_send_nak(dhcpv4, pack); } else { if (pack->hdr->ciaddr == ses->yiaddr && pack->hdr->xid != ses->xid) ses->xid = pack->hdr->xid; + if ((pack->server_id && (pack->server_id != ses->siaddr || pack->request_ip != ses->yiaddr)) || (pack->hdr->ciaddr && (pack->hdr->xid != ses->xid || pack->hdr->ciaddr != ses->yiaddr)) || (opt82_ses && ses != opt82_ses) || (!opt82_ses && pack->relay_agent)) { @@ -1154,7 +1272,8 @@ static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p if (pack->server_id == ses->siaddr) dhcpv4_send_nak(dhcpv4, pack); - else if (ses->serv->dhcpv4_relay) + + if (ses->serv->dhcpv4_relay) dhcpv4_relay_send(ses->serv->dhcpv4_relay, pack, 0, ses->serv->ifname, conf_agent_remote_id); triton_context_call(&ses->ctx, (triton_event_func)__ipoe_session_terminate, &ses->ses); @@ -1190,6 +1309,11 @@ out: pthread_mutex_unlock(&serv->lock); } +static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack) +{ + __ipoe_recv_dhcpv4(dhcpv4, pack, 0); +} + static int parse_dhcpv4_mask(uint32_t mask) { int i; @@ -1489,6 +1613,17 @@ static void ipoe_serv_close(struct triton_context_t *ctx) if (serv->arp) arpd_stop(serv->arp); + while (!list_empty(&serv->disc_list)) { + struct disc_item *d = list_entry(serv->disc_list.next, typeof(*d), entry); + list_del(&d->entry); + dhcpv4_packet_free(d->pack); + mempool_free(d); + __sync_sub_and_fetch(&stat_delayed_offer, 1); + } + + if (serv->disc_timer.tpd) + triton_timer_del(&serv->disc_timer); + triton_context_unregister(ctx); _free(serv->ifname); @@ -1520,6 +1655,7 @@ static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, cli_send(client, "ipoe:\r\n"); cli_sendv(client," starting: %u\r\n", stat_starting); cli_sendv(client," active: %u\r\n", stat_active); + cli_sendv(client," delayed: %u\r\n", stat_delayed_offer); return CLI_CMD_OK; } @@ -1568,6 +1704,24 @@ struct ipoe_serv *ipoe_find_serv(const char *ifname) return NULL; } +static int get_offer_delay() +{ + struct delay *r, *prev = NULL; + + list_for_each_entry(r, &conf_offer_delay, entry) { + if (!prev || stat_active >= r->conn_cnt) { + prev = r; + continue; + } + break; + } + + if (prev) + return prev->delay; + + return 0; +} + static void add_interface(const char *ifname, int ifindex, const char *opt) { char *str0 = NULL, *str, *ptr1, *ptr2; @@ -1735,7 +1889,9 @@ static void add_interface(const char *ifname, int ifindex, const char *opt) serv->active = 1; INIT_LIST_HEAD(&serv->sessions); INIT_LIST_HEAD(&serv->addr_list); + INIT_LIST_HEAD(&serv->disc_list); memcpy(serv->hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + serv->disc_timer.expire = ipoe_serv_disc_timer; pthread_mutex_init(&serv->lock, NULL); triton_context_register(&serv->ctx, NULL); @@ -1994,6 +2150,81 @@ static void load_radius_attrs(void) } #endif +static void strip(char *str) +{ + char *ptr = str; + char *endptr = strchr(str, 0); + while (1) { + ptr = strchr(ptr, ' '); + if (ptr) + memmove(ptr, ptr + 1, endptr - ptr - 1); + else + break; + } +} + +int parse_offer_delay(const char *str) +{ + char *str1; + char *ptr1, *ptr2, *ptr3, *endptr; + struct delay *r; + + while (!list_empty(&conf_offer_delay)) { + r = list_entry(conf_offer_delay.next, typeof(*r), entry); + list_del(&r->entry); + _free(r); + } + + if (!str) + return 0; + + str1 = _strdup(str); + strip(str1); + + ptr1 = str1; + + while (1) { + ptr2 = strchr(ptr1, ','); + if (ptr2) + *ptr2 = 0; + ptr3 = strchr(ptr1, ':'); + if (ptr3) + *ptr3 = 0; + + r = _malloc(sizeof(*r)); + memset(r, 0, sizeof(*r)); + + r->delay = strtol(ptr1, &endptr, 10); + if (*endptr) + goto out_err; + + if (list_empty(&conf_offer_delay)) + r->conn_cnt = 0; + else { + if (!ptr3) + goto out_err; + r->conn_cnt = strtol(ptr3 + 1, &endptr, 10); + if (*endptr) + goto out_err; + } + + list_add_tail(&r->entry, &conf_offer_delay); + + if (!ptr2) + break; + + ptr1 = ptr2 + 1; + } + + _free(str1); + return 0; + +out_err: + _free(str1); + log_error("ipoe: failed to parse offer-delay\n"); + return -1; +} + static void load_config(void) { const char *opt; @@ -2150,6 +2381,8 @@ static void load_config(void) if (triton_module_loaded("radius")) load_radius_attrs(); #endif + + parse_offer_delay(conf_get_opt("ipoe", "offer-delay")); load_interfaces(s); load_local_nets(s); @@ -2167,6 +2400,7 @@ static struct triton_timer_t l4_redirect_timer = { static void ipoe_init(void) { ses_pool = mempool_create(sizeof(struct ipoe_session)); + disc_item_pool = mempool_create(sizeof(struct disc_item)); uc_pool = mempool_create(sizeof(struct unit_cache)); triton_context_register(&l4_redirect_ctx, NULL); |