diff options
author | Dmitry Kozlov <xeb@mail.ru> | 2016-11-29 22:19:52 +0300 |
---|---|---|
committer | Dmitry Kozlov <xeb@mail.ru> | 2016-11-29 22:19:52 +0300 |
commit | 8557ca019041374d43712506eecc21479f83b9e6 (patch) | |
tree | 92bb6ffd66129da92250e54d7f143aeed1f78cdb | |
parent | 01a985316d5240e7a2ba9ba683493cc015282d8a (diff) | |
download | accel-ppp-8557ca019041374d43712506eecc21479f83b9e6.tar.gz accel-ppp-8557ca019041374d43712506eecc21479f83b9e6.zip |
ipoe: apply offer-delay rules to arp initiated sessions
-rw-r--r-- | accel-pppd/ctrl/ipoe/arp.c | 34 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 121 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.h | 4 | ||||
-rw-r--r-- | drivers/ipoe/ipoe.c | 7 |
4 files changed, 143 insertions, 23 deletions
diff --git a/accel-pppd/ctrl/ipoe/arp.c b/accel-pppd/ctrl/ipoe/arp.c index 4818c3b5..36b03447 100644 --- a/accel-pppd/ctrl/ipoe/arp.c +++ b/accel-pppd/ctrl/ipoe/arp.c @@ -39,6 +39,8 @@ struct arp_tree { static mempool_t arp_pool; static mempool_t arp_hdr_pool; +static uint8_t bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + #define HASH_BITS 0xff static struct arp_tree *arp_tree; @@ -63,6 +65,12 @@ static void arp_ctx_read(struct _arphdr *ah) ah2.ar_op = htons(ARPOP_REPLY); pthread_mutex_lock(&ipoe->lock); + if (ah->ar_op == htons(ARPOP_REPLY)) { + ipoe_serv_recv_arp(ipoe, ah); + pthread_mutex_unlock(&ipoe->lock); + goto out; + } + list_for_each_entry(ses, &ipoe->sessions, entry) { if (ses->yiaddr == ah->ar_spa) { ses1 = ses; @@ -80,8 +88,14 @@ static void arp_ctx_read(struct _arphdr *ah) break; } - if (!ses1 || (ses1->ses.state != AP_STATE_ACTIVE) || - (ses2 && ses2->ses.state != AP_STATE_ACTIVE)) { + if (!ses1 && ipoe->opt_up) { + ipoe_serv_recv_arp(ipoe, ah); + pthread_mutex_unlock(&ipoe->lock); + goto out; + } + + if (!ipoe->opt_arp || !ses1 || ses1->arph || + (ses2 && ses2->ses.state != AP_STATE_ACTIVE)) { pthread_mutex_unlock(&ipoe->lock); goto out; } @@ -112,7 +126,7 @@ out: mempool_free(ah); } -void arp_send(int ifindex, struct _arphdr *arph) +void arp_send(int ifindex, struct _arphdr *arph, int broadcast) { struct sockaddr_ll dst; @@ -120,7 +134,10 @@ void arp_send(int ifindex, struct _arphdr *arph) dst.sll_family = AF_PACKET; dst.sll_ifindex = ifindex; dst.sll_protocol = htons(ETH_P_ARP); - memcpy(dst.sll_addr, arph->ar_tha, ETH_ALEN); + if (broadcast) + memcpy(dst.sll_addr, bc_addr, ETH_ALEN); + else + memcpy(dst.sll_addr, arph->ar_tha, ETH_ALEN); arph->ar_op = htons(ARPOP_REPLY); @@ -151,8 +168,13 @@ static int arp_read(struct triton_md_handler_t *h) if (r < sizeof(*ah)) continue; - if (ah->ar_op != htons(ARPOP_REQUEST)) - continue; + if (ah->ar_op != htons(ARPOP_REQUEST)) { + if (ah->ar_op != htons(ARPOP_REPLY)) + continue; + + if (memcmp(src.sll_addr, bc_addr, ETH_ALEN)) + continue; + } if (ah->ar_pln != 4) continue; diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index 5a150082..a969894c 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -7,6 +7,7 @@ #include <fcntl.h> #include <assert.h> #include <time.h> +#include <limits.h> #include <arpa/inet.h> #include <netinet/in.h> #include <net/ethernet.h> @@ -14,6 +15,7 @@ #include <sys/socket.h> #include <sys/ioctl.h> #include <linux/if.h> +#include <linux/if_arp.h> #include <linux/route.h> #include <pcre.h> @@ -76,6 +78,12 @@ struct disc_item { struct timespec ts; }; +struct arp_item { + struct list_head entry; + struct timespec ts; + struct _arphdr arph; +}; + struct delay { struct list_head entry; unsigned int conn_cnt; @@ -166,6 +174,7 @@ static unsigned int stat_delayed_offer; static mempool_t ses_pool; static mempool_t disc_item_pool; +static mempool_t arp_item_pool; static mempool_t req_item_pool; static int connlimit_loaded; @@ -200,6 +209,7 @@ static int ipoe_rad_send_auth_request(struct rad_plugin_t *rad, struct rad_packe static int ipoe_rad_send_acct_request(struct rad_plugin_t *rad, struct rad_packet_t *pack); static void ipoe_session_create_auto(struct ipoe_serv *serv); static void ipoe_serv_timeout(struct triton_timer_t *t); +static struct ipoe_session *ipoe_session_create_up(struct ipoe_serv *serv, struct ethhdr *eth, struct iphdr *iph, struct _arphdr *arph); static void ipoe_ctx_switch(struct triton_context_t *ctx, void *arg) { @@ -727,7 +737,7 @@ static void send_arp_reply(struct ipoe_serv *serv, struct _arphdr *arph) memcpy(arph->ar_sha, serv->hwaddr, ETH_ALEN); arph->ar_tpa = arph->ar_spa; arph->ar_spa = tpa; - arp_send(serv->ifindex, arph); + arp_send(serv->ifindex, arph, 1); } static void __ipoe_session_start(struct ipoe_session *ses) @@ -1449,36 +1459,79 @@ static void ipoe_ses_recv_dhcpv4_request(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; + int delay, delay1 = INT_MAX, delay2 = INT_MAX, offer_delay; clock_gettime(CLOCK_MONOTONIC, &ts); while (!list_empty(&serv->disc_list)) { - d = list_entry(serv->disc_list.next, typeof(*d), entry); + struct disc_item *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; + delay1 = delay; + break; } __ipoe_recv_dhcpv4(serv->dhcpv4, d->pack, 1); + dhcpv4_packet_free(d->pack); list_del(&d->entry); - dhcpv4_packet_free(d->pack); mempool_free(d); __sync_sub_and_fetch(&stat_delayed_offer, 1); } - triton_timer_del(t); + while (!list_empty(&serv->arp_list)) { + struct arp_item *d = list_entry(serv->arp_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) { + delay2 = delay; + break; + } + + ipoe_session_create_up(serv, NULL, NULL, &d->arph); + + list_del(&d->entry); + mempool_free(d); + + __sync_sub_and_fetch(&stat_delayed_offer, 1); + } + + if (list_empty(&serv->disc_list) && list_empty(&serv->arp_list)) + triton_timer_del(t); + else { + delay = delay1 < delay2 ? delay1 : delay2; + delay = offer_delay - delay; + t->expire_tv.tv_sec = delay / 1000; + t->expire_tv.tv_usec = (delay % 1000) * 1000; + triton_timer_mod(t, 0); + } +} + +static void ipoe_serv_add_disc_arp(struct ipoe_serv *serv, struct _arphdr *arph, int offer_delay) +{ + struct arp_item *d = mempool_alloc(arp_item_pool); + + if (!d) + return; + + __sync_add_and_fetch(&stat_delayed_offer, 1); + + memcpy(&d->arph, arph, sizeof(*arph)); + clock_gettime(CLOCK_MONOTONIC, &d->ts); + list_add_tail(&d->entry, &serv->arp_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_add_disc(struct ipoe_serv *serv, struct dhcpv4_packet *pack, int offer_delay) @@ -1988,6 +2041,39 @@ void ipoe_recv_up(int ifindex, struct ethhdr *eth, struct iphdr *iph, struct _ar pthread_mutex_unlock(&serv_lock); } +void ipoe_serv_recv_arp(struct ipoe_serv *serv, struct _arphdr *arph) +{ + struct arp_item *d; + + if (arph->ar_op == htons(ARPOP_REQUEST)) { + int offer_delay = get_offer_delay(); + + if (offer_delay == -1) + return; + + list_for_each_entry(d, &serv->arp_list, entry) { + if (d->arph.ar_spa == arph->ar_spa) + return; + } + + if (offer_delay) + ipoe_serv_add_disc_arp(serv, arph, offer_delay); + else + ipoe_session_create_up(serv, NULL, NULL, arph); + } else { + list_for_each_entry(d, &serv->arp_list, entry) { + if (d->arph.ar_spa == arph->ar_tpa) { + list_del(&d->entry); + mempool_free(d); + + __sync_sub_and_fetch(&stat_delayed_offer, 1); + + break; + } + } + } +} + #ifdef RADIUS static void ev_radius_access_accept(struct ev_radius_t *ev) { @@ -2173,6 +2259,13 @@ static void ipoe_serv_release(struct ipoe_serv *serv) __sync_sub_and_fetch(&stat_delayed_offer, 1); } + while (!list_empty(&serv->arp_list)) { + struct arp_item *d = list_entry(serv->arp_list.next, typeof(*d), entry); + list_del(&d->entry); + mempool_free(d); + __sync_sub_and_fetch(&stat_delayed_offer, 1); + } + while (!list_empty(&serv->req_list)) { struct request_item *r = list_first_entry(&serv->req_list, typeof(*r), entry); list_del(&r->entry); @@ -2637,10 +2730,10 @@ static void add_interface(const char *ifname, int ifindex, const char *opt, int if (!serv->dhcpv4_relay && serv->opt_dhcpv4 && opt_relay) serv->dhcpv4_relay = dhcpv4_relay_create(opt_relay, opt_giaddr, &serv->ctx, (triton_event_func)ipoe_recv_dhcpv4_relay); - if (serv->arp && !conf_arp) { + if (serv->arp && !opt_arp && !opt_up) { arpd_stop(serv->arp); serv->arp = NULL; - } else if (!serv->arp && conf_arp) + } else if (!serv->arp && (opt_arp || opt_up)) serv->arp = arpd_start(serv); serv->opt_up = opt_up; @@ -2744,6 +2837,7 @@ static void add_interface(const char *ifname, int ifindex, const char *opt, int serv->active = 1; INIT_LIST_HEAD(&serv->sessions); INIT_LIST_HEAD(&serv->disc_list); + INIT_LIST_HEAD(&serv->arp_list); INIT_LIST_HEAD(&serv->req_list); memcpy(serv->hwaddr, hwaddr, ETH_ALEN); serv->disc_timer.expire = ipoe_serv_disc_timer; @@ -3563,6 +3657,7 @@ static void ipoe_init(void) { ses_pool = mempool_create(sizeof(struct ipoe_session)); disc_item_pool = mempool_create(sizeof(struct disc_item)); + arp_item_pool = mempool_create(sizeof(struct arp_item)); req_item_pool = mempool_create(sizeof(struct request_item)); uc_pool = mempool_create(sizeof(struct unit_cache)); diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h index e398648e..f3fc3b7c 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.h +++ b/accel-pppd/ctrl/ipoe/ipoe.h @@ -41,6 +41,7 @@ struct ipoe_serv { struct dhcpv4_relay *dhcpv4_relay; void *arp; struct list_head disc_list; + struct list_head arp_list; struct list_head req_list; struct triton_timer_t disc_timer; struct triton_timer_t timer; @@ -133,6 +134,7 @@ void ipoe_recv_up(int ifindex, struct ethhdr *eth, struct iphdr *iph, struct _ar struct ipoe_session *ipoe_session_alloc(const char *ifname); struct ipoe_serv *ipoe_find_serv(const char *ifname); +void ipoe_serv_recv_arp(struct ipoe_serv *s, struct _arphdr *arph); void ipoe_nl_add_interface(int ifindex, uint8_t mode); void ipoe_nl_del_interface(int ifindex); @@ -148,7 +150,7 @@ void ipoe_nl_del_net(uint32_t addr); void *arpd_start(struct ipoe_serv *ipoe); void arpd_stop(void *arp); -void arp_send(int ifindex, struct _arphdr *arph); +void arp_send(int ifindex, struct _arphdr *arph, int bc); #endif diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index c2ff3ba7..f1addcc8 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -703,7 +703,6 @@ static rx_handler_result_t ipoe_recv(struct sk_buff **pskb) struct net_device *out = NULL; struct ipoe_session *ses = NULL; struct iphdr *iph = NULL; - struct _arphdr *arph = NULL; struct ethhdr *eth = eth_hdr(skb); int noff; struct net_device_stats *stats; @@ -724,7 +723,9 @@ static rx_handler_result_t ipoe_recv(struct sk_buff **pskb) iph = ip_hdr(skb); saddr = iph->saddr; - } else if (likely(skb->protocol == htons(ETH_P_ARP))) { + } /*else if (likely(skb->protocol == htons(ETH_P_ARP))) { + struct _arphdr *arph = NULL; + noff = skb_network_offset(skb); if (skb->len < sizeof(*arph)) @@ -745,7 +746,7 @@ static rx_handler_result_t ipoe_recv(struct sk_buff **pskb) return RX_HANDLER_PASS; saddr = arph->ar_sip; - } else + }*/ else return RX_HANDLER_PASS; if (!saddr || saddr == 0xffffffff) |