summaryrefslogtreecommitdiff
path: root/accel-pppd/ctrl/ipoe
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/ctrl/ipoe')
-rw-r--r--accel-pppd/ctrl/ipoe/arp.c34
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.c121
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.h4
3 files changed, 139 insertions, 20 deletions
diff --git a/accel-pppd/ctrl/ipoe/arp.c b/accel-pppd/ctrl/ipoe/arp.c
index 4818c3b..36b0344 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 5a15008..a969894 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 e398648..f3fc3b7 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