277 lines
7.5 KiB
Diff
277 lines
7.5 KiB
Diff
From 6dd23d5e7f9301237d6a9b96fbbb7b9f6bbcf464 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB?= <spike@lmu.name>
|
|
Date: Tue, 5 Jan 2021 12:28:03 +0100
|
|
Subject: [PATCH 12/14] Don't hang when starting pppoe connection without
|
|
server response
|
|
|
|
waitForPADO is part of PPPoE discovery procedure. This function ignores
|
|
timeout parameter and uses it only for delay in select(). If any pppoe
|
|
garbage packet recived, recive retried with waiting next packet with
|
|
SAME timeout value. In case of no response from server, continuous flow
|
|
PPPoE discovery packets in ethernet segment waitForPADO never return.
|
|
|
|
This actually occurs when pppoe server temporary fails and other clients
|
|
trying to recover connection.
|
|
|
|
So pppoe server did not reply because PADI request is sent for a long
|
|
time. New PADI should be sent but pppd only waits forever.
|
|
|
|
Gentoo-bug: https://bugs.gentoo.org/340267
|
|
---
|
|
pppd/plugins/pppoe/discovery.c | 158 ++++++++++++++-------------
|
|
pppd/plugins/pppoe/pppoe-discovery.c | 8 +-
|
|
2 files changed, 89 insertions(+), 77 deletions(-)
|
|
|
|
diff --git a/pppd/plugins/pppoe/discovery.c b/pppd/plugins/pppoe/discovery.c
|
|
index 23089df..6923a2c 100644
|
|
--- a/pppd/plugins/pppoe/discovery.c
|
|
+++ b/pppd/plugins/pppoe/discovery.c
|
|
@@ -39,6 +39,7 @@ static char const RCSID[] =
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
+#include <time.h>
|
|
|
|
/* Calculate time remaining until *exp, return 0 if now >= *exp */
|
|
static int time_left(struct timeval *diff, struct timeval *exp)
|
|
@@ -246,6 +247,79 @@ parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data,
|
|
}
|
|
}
|
|
|
|
+/***********************************************************************
|
|
+*%FUNCTION: recvPacketForMe
|
|
+*%ARGUMENTS:
|
|
+* packet -- output parameter
|
|
+* len -- output parameter length
|
|
+* conn -- connection
|
|
+* start -- operation startup timestamp
|
|
+* timeout -- how long to wait (in seconds)
|
|
+*%RETURNS:
|
|
+* -1: error
|
|
+* 0: timed out
|
|
+* 1: packet received
|
|
+*%DESCRIPTION:
|
|
+* receive and filter junk packets
|
|
+***********************************************************************/
|
|
+
|
|
+static int
|
|
+recvPacketForMe(PPPoEPacket *packet, int *len, PPPoEConnection *conn, time_t start, int timeout)
|
|
+{
|
|
+ fd_set readable;
|
|
+ int r;
|
|
+ struct timeval tv;
|
|
+ time_t now;
|
|
+ int time_remain;
|
|
+
|
|
+ do {
|
|
+ time(&now);
|
|
+ time_remain = timeout - (int)difftime(now, start);
|
|
+ if (time_remain <= 0) return 0; /* Timed out */
|
|
+
|
|
+ if (BPF_BUFFER_IS_EMPTY) {
|
|
+ tv.tv_sec = time_remain;
|
|
+ tv.tv_usec = 0;
|
|
+
|
|
+ FD_ZERO(&readable);
|
|
+ FD_SET(conn->discoverySocket, &readable);
|
|
+
|
|
+ r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
|
|
+ if (r < 0)
|
|
+ {
|
|
+ if (errno == EINTR && !got_sigterm)
|
|
+ {
|
|
+ continue; /* interrupted, so retry */
|
|
+ } else
|
|
+ {
|
|
+ error("pppoe: recvPacketForMe: select: %m");
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (r == 0) return 0; /* Timed out */
|
|
+ }
|
|
+
|
|
+ /* Get the packet */
|
|
+ receivePacket(conn->discoverySocket, packet, len);
|
|
+
|
|
+ /* Check length */
|
|
+ if (ntohs(packet->length) + HDR_SIZE > *len) {
|
|
+ error("Bogus PPPoE length field (%u)",
|
|
+ (unsigned int) ntohs(packet->length));
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+#ifdef USE_BPF
|
|
+ /* If it's not a Discovery packet, loop again */
|
|
+ if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
|
|
+#endif
|
|
+ /* If it's not for us, loop again */
|
|
+ }while ( ! packetIsForMe(conn, packet));
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
/***********************************************************************
|
|
*%FUNCTION: sendPADI
|
|
*%ARGUMENTS:
|
|
@@ -336,13 +410,12 @@ sendPADI(PPPoEConnection *conn)
|
|
void
|
|
waitForPADO(PPPoEConnection *conn, int timeout)
|
|
{
|
|
- fd_set readable;
|
|
int r;
|
|
- struct timeval tv;
|
|
struct timeval expire_at;
|
|
|
|
PPPoEPacket packet;
|
|
int len;
|
|
+ time_t start;
|
|
|
|
struct PacketCriteria pc;
|
|
pc.conn = conn;
|
|
@@ -359,43 +432,10 @@ waitForPADO(PPPoEConnection *conn, int timeout)
|
|
}
|
|
expire_at.tv_sec += timeout;
|
|
|
|
+ time(&start);
|
|
do {
|
|
- if (BPF_BUFFER_IS_EMPTY) {
|
|
- if (!time_left(&tv, &expire_at))
|
|
- return; /* Timed out */
|
|
-
|
|
- FD_ZERO(&readable);
|
|
- FD_SET(conn->discoverySocket, &readable);
|
|
-
|
|
- while(1) {
|
|
- r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
|
|
- if (r >= 0 || errno != EINTR || got_sigterm) break;
|
|
- }
|
|
- if (r < 0) {
|
|
- error("select (waitForPADO): %m");
|
|
- return;
|
|
- }
|
|
- if (r == 0)
|
|
- return; /* Timed out */
|
|
- }
|
|
-
|
|
- /* Get the packet */
|
|
- receivePacket(conn->discoverySocket, &packet, &len);
|
|
-
|
|
- /* Check length */
|
|
- if (ntohs(packet.length) + HDR_SIZE > len) {
|
|
- error("Bogus PPPoE length field (%u)",
|
|
- (unsigned int) ntohs(packet.length));
|
|
- continue;
|
|
- }
|
|
-
|
|
-#ifdef USE_BPF
|
|
- /* If it's not a Discovery packet, loop again */
|
|
- if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
|
|
-#endif
|
|
-
|
|
- /* If it's not for us, loop again */
|
|
- if (!packetIsForMe(conn, &packet)) continue;
|
|
+ r = recvPacketForMe(&packet, &len, conn, start, timeout);
|
|
+ if (r<=0) return; /* Timed out or error */
|
|
|
|
if (packet.code == CODE_PADO) {
|
|
if (NOT_UNICAST(packet.ethHdr.h_source)) {
|
|
@@ -525,13 +565,12 @@ sendPADR(PPPoEConnection *conn)
|
|
static void
|
|
waitForPADS(PPPoEConnection *conn, int timeout)
|
|
{
|
|
- fd_set readable;
|
|
int r;
|
|
- struct timeval tv;
|
|
struct timeval expire_at;
|
|
|
|
PPPoEPacket packet;
|
|
int len;
|
|
+ time_t start;
|
|
|
|
if (get_time(&expire_at) < 0) {
|
|
error("get_time (waitForPADS): %m");
|
|
@@ -539,48 +578,15 @@ waitForPADS(PPPoEConnection *conn, int timeout)
|
|
}
|
|
expire_at.tv_sec += timeout;
|
|
|
|
+ time(&start);
|
|
conn->error = 0;
|
|
do {
|
|
- if (BPF_BUFFER_IS_EMPTY) {
|
|
- if (!time_left(&tv, &expire_at))
|
|
- return; /* Timed out */
|
|
-
|
|
- FD_ZERO(&readable);
|
|
- FD_SET(conn->discoverySocket, &readable);
|
|
-
|
|
- while(1) {
|
|
- r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
|
|
- if (r >= 0 || errno != EINTR || got_sigterm) break;
|
|
- }
|
|
- if (r < 0) {
|
|
- error("select (waitForPADS): %m");
|
|
- return;
|
|
- }
|
|
- if (r == 0)
|
|
- return; /* Timed out */
|
|
- }
|
|
-
|
|
- /* Get the packet */
|
|
- receivePacket(conn->discoverySocket, &packet, &len);
|
|
-
|
|
- /* Check length */
|
|
- if (ntohs(packet.length) + HDR_SIZE > len) {
|
|
- error("Bogus PPPoE length field (%u)",
|
|
- (unsigned int) ntohs(packet.length));
|
|
- continue;
|
|
- }
|
|
-
|
|
-#ifdef USE_BPF
|
|
- /* If it's not a Discovery packet, loop again */
|
|
- if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
|
|
-#endif
|
|
+ r = recvPacketForMe(&packet, &len, conn, start, timeout);
|
|
+ if (r<=0) return; /* Timed out or error */
|
|
|
|
/* If it's not from the AC, it's not for me */
|
|
if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;
|
|
|
|
- /* If it's not for us, loop again */
|
|
- if (!packetIsForMe(conn, &packet)) continue;
|
|
-
|
|
/* Is it PADS? */
|
|
if (packet.code == CODE_PADS) {
|
|
/* Parse for goodies */
|
|
diff --git a/pppd/plugins/pppoe/pppoe-discovery.c b/pppd/plugins/pppoe/pppoe-discovery.c
|
|
index e41d286..1730f29 100644
|
|
--- a/pppd/plugins/pppoe/pppoe-discovery.c
|
|
+++ b/pppd/plugins/pppoe/pppoe-discovery.c
|
|
@@ -518,6 +518,8 @@ waitForPADO(PPPoEConnection *conn, int timeout)
|
|
struct timeval tv;
|
|
PPPoEPacket packet;
|
|
int len;
|
|
+ time_t start, now;
|
|
+ int time_remain;
|
|
|
|
struct PacketCriteria pc;
|
|
pc.conn = conn;
|
|
@@ -527,9 +529,13 @@ waitForPADO(PPPoEConnection *conn, int timeout)
|
|
pc.seenServiceName = 0;
|
|
conn->error = 0;
|
|
|
|
+ time(&start);
|
|
do {
|
|
+ time(&now);
|
|
+ time_remain = timeout - (int)difftime(now, start);
|
|
+ if (time_remain <= 0) return; /* Timed out */
|
|
if (BPF_BUFFER_IS_EMPTY) {
|
|
- tv.tv_sec = timeout;
|
|
+ tv.tv_sec = time_remain;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&readable);
|
|
--
|
|
2.30.0
|
|
|