From 64e5c04c0d9aa4b46235c5f0da45d22abf9cce1c Mon Sep 17 00:00:00 2001
From: Dmitry Kozlov <xeb@mail.ru>
Date: Sat, 19 Mar 2016 20:17:02 +0300
Subject: ipoe: send arp reply for sessions started by arp request

---
 accel-pppd/ctrl/ipoe/arp.c  | 14 ++++++++++++++
 accel-pppd/ctrl/ipoe/ipoe.c | 32 +++++++++++++++++++++++++-------
 accel-pppd/ctrl/ipoe/ipoe.h | 26 ++++++++++++++------------
 drivers/ipoe/ipoe.c         |  7 +++++++
 4 files changed, 60 insertions(+), 19 deletions(-)

diff --git a/accel-pppd/ctrl/ipoe/arp.c b/accel-pppd/ctrl/ipoe/arp.c
index 2bf5a951..58533582 100644
--- a/accel-pppd/ctrl/ipoe/arp.c
+++ b/accel-pppd/ctrl/ipoe/arp.c
@@ -112,6 +112,20 @@ out:
 	mempool_free(ah);
 }
 
+void arp_send(int ifindex, struct _arphdr *arph)
+{
+	struct sockaddr_ll dst;
+
+	memset(&dst, 0, sizeof(dst));
+	dst.sll_family = AF_PACKET;
+	dst.sll_ifindex = ifindex;
+	dst.sll_protocol = htons(ETH_P_ARP);
+
+	arph->ar_op = htons(ARPOP_REPLY);
+
+	sendto(arp_hnd.fd, arph, sizeof(*arph), MSG_DONTWAIT, (struct sockaddr *)&dst, sizeof(dst));
+}
+
 static int arp_read(struct triton_md_handler_t *h)
 {
 	int r, i;
diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c
index 12bb2ba7..8068983d 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.c
+++ b/accel-pppd/ctrl/ipoe/ipoe.c
@@ -800,23 +800,25 @@ static void __ipoe_session_start(struct ipoe_session *ses)
 		ses->timer.expire_tv.tv_sec = conf_offer_timeout;
 		triton_timer_add(&ses->ctx, &ses->timer, 0);
 	} else {
-		if (!ses->siaddr)
+		if (!ses->router)
 			find_gw_addr(ses);
 
-		if (!ses->siaddr)
-			ses->siaddr = ses->serv->opt_src;
+		if (!ses->router)
+			ses->router = ses->serv->opt_src;
 
-		if (!ses->siaddr)
+		if (!ses->router)
 			ses->siaddr = iproute_get(ses->yiaddr, NULL);
 
-		if (!ses->siaddr) {
-			log_ppp_error("can't determine local address\n");
+		if (!ses->router) {
+			log_ppp_error("can't determine router address\n");
 			ap_session_terminate(&ses->ses, TERM_NAS_ERROR, 1);
 			return;
 		}
 
 		if (ses->ses.ipv4 && !ses->ses.ipv4->addr)
-			ses->ses.ipv4->addr = ses->siaddr;
+			ses->ses.ipv4->addr = ses->router;
+
+		ses->siaddr = ses->router;
 
 		__ipoe_session_activate(ses);
 	}
@@ -903,6 +905,14 @@ static void __ipoe_session_activate(struct ipoe_session *ses)
 
 		dhcpv4_packet_free(ses->dhcpv4_request);
 		ses->dhcpv4_request = NULL;
+	} else if (ses->arph) {
+		if (ses->arph->ar_tpa == ses->router) {
+			memcpy(ses->arph->ar_tha, ses->serv->hwaddr, ETH_ALEN);
+			arp_send(ses->serv->ifindex, ses->arph);
+		}
+
+		_free(ses->arph);
+		ses->arph = NULL;
 	}
 
 	ses->timer.expire = ipoe_session_timeout;
@@ -1015,6 +1025,9 @@ static void ipoe_session_free(struct ipoe_session *ses)
 	if (ses->dhcpv4_relay_reply)
 		dhcpv4_packet_free(ses->dhcpv4_relay_reply);
 
+	if (ses->arph)
+		_free(ses->arph);
+
 	if (ses->ctrl.called_station_id && ses->ctrl.called_station_id != ses->ses.ifname)
 		_free(ses->ctrl.called_station_id);
 
@@ -1806,6 +1819,11 @@ static struct ipoe_session *ipoe_session_create_up(struct ipoe_serv *serv, struc
 	if (serv->timer.tpd)
 		triton_timer_del(&serv->timer);
 
+	if (arph) {
+		ses->arph = _malloc(sizeof(*arph));
+		memcpy(ses->arph, arph, sizeof(*arph));
+	}
+
 	triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_start, ses);
 
 	triton_context_wakeup(&ses->ctx);
diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h
index 52b3c8da..c8b03b9c 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.h
+++ b/accel-pppd/ctrl/ipoe/ipoe.h
@@ -18,6 +18,18 @@
 #define ETH_ALEN 6
 #endif
 
+struct _arphdr {
+	__be16 ar_hrd;
+	__be16 ar_pro;
+	__u8   ar_hln;
+	__u8   ar_pln;
+	__be16 ar_op;
+	__u8   ar_sha[ETH_ALEN];
+	__be32 ar_spa;
+	__u8   ar_tha[ETH_ALEN];
+	__be32 ar_tpa;
+} __packed;
+
 struct ipoe_serv {
 	struct list_head entry;
 	struct triton_context_t ctx;
@@ -82,6 +94,7 @@ struct ipoe_session {
 	uint8_t *data;
 	struct dhcpv4_packet *dhcpv4_request;
 	struct dhcpv4_packet *dhcpv4_relay_reply;
+	struct _arphdr *arph;
 	int relay_retransmit;
 	int ifindex;
 	char *username;
@@ -106,18 +119,6 @@ struct ipoe_session_info {
 	uint32_t peer_addr;
 };
 
-struct _arphdr {
-	__be16 ar_hrd;
-	__be16 ar_pro;
-	__u8   ar_hln;
-	__u8   ar_pln;
-	__be16 ar_op;
-	__u8   ar_sha[ETH_ALEN];
-	__be32 ar_spa;
-	__u8   ar_tha[ETH_ALEN];
-	__be32 ar_tpa;
-} __packed;
-
 #ifdef USE_LUA
 char *ipoe_lua_get_username(struct ipoe_session *, const char *func);
 int ipoe_lua_make_vlan_name(const char *func, const char *parent, int svid, int cvid, char *name);
@@ -146,6 +147,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);
 
 #endif
 
diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c
index 5bafde6a..059b1397 100644
--- a/drivers/ipoe/ipoe.c
+++ b/drivers/ipoe/ipoe.c
@@ -736,9 +736,16 @@ static rx_handler_result_t ipoe_recv(struct sk_buff **pskb)
 			return RX_HANDLER_PASS;
 
 		arph = (struct _arphdr *)skb_network_header(skb);
+
 		if (arph->ar_op != htons(ARPOP_REQUEST))
 			return RX_HANDLER_PASS;
 
+		if (arph->ar_hrd != htons(ARPHRD_ETHER))
+			return RX_HANDLER_PASS;
+
+		if (arph->ar_pro != htons(ETH_P_IP))
+			return RX_HANDLER_PASS;
+
 		saddr = arph->ar_sip;
 	} else
 		return RX_HANDLER_PASS;
-- 
cgit v1.2.3