--- ./pppd/plugins/rp-pppoe/discovery.c~ 2005-03-22 11:22:32 +0100 +++ ./pppd/plugins/rp-pppoe/discovery.c 2007-05-28 10:20:03 +0200 @@ -351,18 +351,49 @@ continue; } -#ifdef USE_BPF /* If it's not a Discovery packet, loop again */ - if (etherType(&packet) != Eth_PPPOE_Discovery) continue; -#endif + if (etherType(&packet) == Eth_PPPOE_Session) + goto reset_packet; + if (etherType(&packet) != Eth_PPPOE_Discovery) + continue; if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } - /* If it's not for us, loop again */ - if (!packetIsForMe(conn, &packet)) continue; + /* If it's not for us, we should not have received it. So we will send + * a PADT in the following situations : + * - invalid MAC destination received + * => possibly means the hardware was replaced + * - a session is known and it does not match the sessin received + * => means pppd has been restarted / machine rebooted + * - no session is known yet and the packet is not a PADO + * => means and older session was not killed + * After that we we loop again. + */ + if (!packetIsForMe(conn, &packet) || + (conn->session && packet.session && packet.session != conn->session) || + (packet.code != CODE_PADO && packet.code != CODE_PADT)) { + PPPoEConnection *tmp_conn; + reset_packet: + tmp_conn = (PPPoEConnection *)calloc(1, sizeof(PPPoEConnection)); + if (!tmp_conn) + continue; + memcpy(tmp_conn, conn, sizeof(*tmp_conn)); + + memcpy(tmp_conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); + memcpy(conn->myEth, packet.ethHdr.h_dest, ETH_ALEN); + tmp_conn->session = packet.session; + tmp_conn->cookie.type = 0; + tmp_conn->relayId.type = 0; + sendPADT(tmp_conn, NULL); + free(tmp_conn); + /* The previous packet was ignored. Let's resend it. */ + sleep(1); + sendPADI(conn); + continue; + } if (packet.code == CODE_PADO) { if (BROADCAST(packet.ethHdr.h_source)) { @@ -528,10 +559,12 @@ continue; } -#ifdef USE_BPF /* If it's not a Discovery packet, loop again */ - if (etherType(&packet) != Eth_PPPOE_Discovery) continue; -#endif + if (etherType(&packet) == Eth_PPPOE_Session) + goto reset_packet; + if (etherType(&packet) != Eth_PPPOE_Discovery) + continue; + if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); @@ -541,8 +574,40 @@ /* 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; + /* If it's not for us, we should not have received it. So we will send + * a PADT in the following situations : + * - invalid MAC destination received + * => possibly means our hardware was replaced + * - a session is known and it does not match the session received + * => means pppd has been restarted / machine rebooted + * - no session is known yet and the packet is not a PADS nor a PADO + * => means and older session was not properly killed + * - it was a data packet or anything we don't expect at this stage + * => probably that pppd was restarted during a live session + * After that we we loop again. + */ + if (!packetIsForMe(conn, &packet) || + (conn->session && packet.session && packet.session != conn->session) || + (packet.code != CODE_PADS && packet.code != CODE_PADO && + packet.code != CODE_PADT)) { + PPPoEConnection *tmp_conn; + reset_packet: + tmp_conn = (PPPoEConnection *)calloc(1, sizeof(PPPoEConnection)); + if (!tmp_conn) + continue; + memcpy(tmp_conn, conn, sizeof(*tmp_conn)); + memcpy(tmp_conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); + memcpy(conn->myEth, packet.ethHdr.h_dest, ETH_ALEN); + tmp_conn->session = packet.session; + tmp_conn->cookie.type = 0; + tmp_conn->relayId.type = 0; + sendPADT(tmp_conn, NULL); + free(tmp_conn); + /* The previous packet has been dropped. Let's resend it. */ + sleep(1); + sendPADR(conn); + continue; + } /* Is it PADS? */ if (packet.code == CODE_PADS) { @@ -586,8 +651,10 @@ return; } + /* Note: to support session killing, it is important to listen to all + protocols, namely Eth_PPPOE_Discovery AND Eth_PPPOE_Session. */ conn->discoverySocket = - openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); + openInterface(conn->ifName, 0, conn->myEth); /* Skip discovery? */ if (conn->skipDiscovery) { --- ./pppd/plugins/rp-pppoe/if.c~ 2001-12-14 03:55:20 +0100 +++ ./pppd/plugins/rp-pppoe/if.c 2007-05-28 10:16:43 +0200 @@ -408,7 +408,7 @@ *%FUNCTION: openInterface *%ARGUMENTS: * ifname -- name of interface -* type -- Ethernet frame type +* type -- Ethernet frame type. 0 means any type. * hwaddr -- if non-NULL, set to the hardware address *%RETURNS: * A raw socket for talking to the Ethernet card. Exits on error. @@ -439,6 +439,9 @@ stype = SOCK_PACKET; #endif + if (!type) + type = ETH_P_ALL; + if ((fd = socket(domain, stype, htons(type))) < 0) { /* Give a more helpful message for the common error case */ if (errno == EPERM) {