/********************************************************************** *%FUNCTION: dumpPacket *%ARGUMENTS: * fp -- file to dump to * packet -- a PPPoE packet * dir -- either SENT or RCVD *%RETURNS: * Nothing *%DESCRIPTION: * Dumps the PPPoE packet to fp in an easy-to-read format ***********************************************************************/ void dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir) { int len = ntohs(packet->length); /* Sheesh... printing times is a pain... */ struct timeval tv; time_t now; int millisec; struct tm *lt; char timebuf[256]; UINT16_t type = etherType(packet); if (!fp) return; gettimeofday(&tv, NULL); now = (time_t) tv.tv_sec; millisec = tv.tv_usec / 1000; lt = localtime(&now); strftime(timebuf, 256, "%H:%M:%S", lt); fprintf(fp, "%s.%03d %s PPPoE ", timebuf, millisec, dir); if (type == Eth_PPPOE_Discovery) { fprintf(fp, "Discovery (%x) ", (unsigned) type); } else if (type == Eth_PPPOE_Session) { fprintf(fp, "Session (%x) ", (unsigned) type); } else { fprintf(fp, "Unknown (%x) ", (unsigned) type); } switch(packet->code) { case CODE_PADI: fprintf(fp, "PADI "); break; case CODE_PADO: fprintf(fp, "PADO "); break; case CODE_PADR: fprintf(fp, "PADR "); break; case CODE_PADS: fprintf(fp, "PADS "); break; case CODE_PADT: fprintf(fp, "PADT "); break; case CODE_SESS: fprintf(fp, "SESS "); break; } fprintf(fp, "sess-id %d length %d\n", (int) ntohs(packet->session), len); /* Ugly... I apologize... */ fprintf(fp, "SourceAddr %02x:%02x:%02x:%02x:%02x:%02x " "DestAddr %02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned) packet->ethHdr.h_source[0], (unsigned) packet->ethHdr.h_source[1], (unsigned) packet->ethHdr.h_source[2], (unsigned) packet->ethHdr.h_source[3], (unsigned) packet->ethHdr.h_source[4], (unsigned) packet->ethHdr.h_source[5], (unsigned) packet->ethHdr.h_dest[0], (unsigned) packet->ethHdr.h_dest[1], (unsigned) packet->ethHdr.h_dest[2], (unsigned) packet->ethHdr.h_dest[3], (unsigned) packet->ethHdr.h_dest[4], (unsigned) packet->ethHdr.h_dest[5]); dumpHex(fp, packet->payload, ntohs(packet->length)); }
/********************************************************************** *%FUNCTION: waitForPADS *%ARGUMENTS: * conn -- PPPoE connection info * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADS packet and copies useful information ***********************************************************************/ void waitForPADS(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; PPPoEPacket packet; int len; do { if (BPF_BUFFER_IS_EMPTY) { tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&readable); FD_SET(conn->discoverySocket, &readable); while(1) { r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); if (r >= 0 || errno != EINTR) break; } if (r < 0) { fatalSys("select (waitForPADS)"); } if (r == 0) return; } /* Get the packet */ receivePacket(conn->discoverySocket, &packet, &len); /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "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 (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } /* 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 */ parsePacket(&packet, parsePADSTags, conn); conn->discoveryState = STATE_SESSION; break; } } while (conn->discoveryState != STATE_SESSION); /* Don't bother with ntohs; we'll just end up converting it back... */ conn->session = packet.session; syslog(LOG_INFO, "PPP session is %d", (int) ntohs(conn->session)); /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */ if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) { syslog(LOG_ERR, "Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session)); } }
/********************************************************************** *%FUNCTION: waitForPADO *%ARGUMENTS: * conn -- PPPoEConnection structure * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADO packet and copies useful information ***********************************************************************/ void waitForPADO(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; PPPoEPacket packet; int len; struct PacketCriteria pc; pc.conn = conn; pc.acNameOK = (conn->acName) ? 0 : 1; pc.serviceNameOK = (conn->serviceName) ? 0 : 1; pc.seenACName = 0; pc.seenServiceName = 0; do { if (BPF_BUFFER_IS_EMPTY) { tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&readable); FD_SET(conn->discoverySocket, &readable); while(1) { r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); if (r >= 0 || errno != EINTR) break; } if (r < 0) { fatalSys("select (waitForPADO)"); } if (r == 0) return; /* Timed out */ } /* Get the packet */ receivePacket(conn->discoverySocket, &packet, &len); /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "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 (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 (packet.code == CODE_PADO) { if (NOT_UNICAST(packet.ethHdr.h_source)) { printErr("Ignoring PADO packet from non-unicast MAC address"); continue; } parsePacket(&packet, parsePADOTags, &pc); if (!pc.seenACName) { printErr("Ignoring PADO packet with no AC-Name tag"); continue; } if (!pc.seenServiceName) { printErr("Ignoring PADO packet with no Service-Name tag"); continue; } conn->numPADOs++; if (conn->printACNames) { printf("--------------------------------------------------\n"); } if (pc.acNameOK && pc.serviceNameOK) { memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); if (conn->printACNames) { printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned) conn->peerEth[0], (unsigned) conn->peerEth[1], (unsigned) conn->peerEth[2], (unsigned) conn->peerEth[3], (unsigned) conn->peerEth[4], (unsigned) conn->peerEth[5]); continue; } conn->discoveryState = STATE_RECEIVED_PADO; break; } } } while (conn->discoveryState != STATE_RECEIVED_PADO); }
/********************************************************************** *%FUNCTION: waitForPADS *%ARGUMENTS: * conn -- PPPoE connection info * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADS packet and copies useful information ***********************************************************************/ static void waitForPADS(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; struct timeval expire_at; PPPoEPacket packet; int len; if (gettimeofday(&expire_at, NULL) < 0) { error("gettimeofday (waitForPADS): %m"); return; } expire_at.tv_sec += timeout; 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) 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 /* 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 */ if (parsePacket(&packet, parsePADSTags, conn) < 0) return; if (conn->error) return; conn->discoveryState = STATE_SESSION; break; } } while (conn->discoveryState != STATE_SESSION); /* Don't bother with ntohs; we'll just end up converting it back... */ conn->session = packet.session; info("PPP session is %d", (int) ntohs(conn->session)); /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */ if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) { error("Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session)); } }
/********************************************************************** *%FUNCTION: waitForPADO *%ARGUMENTS: * conn -- PPPoEConnection structure * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADO packet and copies useful information ***********************************************************************/ void waitForPADO(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; struct timeval expire_at; PPPoEPacket packet; int len; struct PacketCriteria pc; pc.conn = conn; pc.acNameOK = (conn->acName) ? 0 : 1; pc.serviceNameOK = (conn->serviceName) ? 0 : 1; pc.seenACName = 0; pc.seenServiceName = 0; conn->seenMaxPayload = 0; conn->error = 0; if (gettimeofday(&expire_at, NULL) < 0) { error("gettimeofday (waitForPADO): %m"); return; } expire_at.tv_sec += timeout; 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) 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; if (packet.code == CODE_PADO) { if (NOT_UNICAST(packet.ethHdr.h_source)) { error("Ignoring PADO packet from non-unicast MAC address"); continue; } if (conn->req_peer && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) { warn("Ignoring PADO packet from wrong MAC address"); continue; } if (parsePacket(&packet, parsePADOTags, &pc) < 0) return; if (conn->error) return; if (!pc.seenACName) { error("Ignoring PADO packet with no AC-Name tag"); continue; } if (!pc.seenServiceName) { error("Ignoring PADO packet with no Service-Name tag"); continue; } conn->numPADOs++; if (pc.acNameOK && pc.serviceNameOK) { memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); conn->discoveryState = STATE_RECEIVED_PADO; break; } } } while (conn->discoveryState != STATE_RECEIVED_PADO); }
/********************************************************************** *%FUNCTION: waitForPADO *%ARGUMENTS: * conn -- PPPoEConnection structure * timeout -- how long to wait (in seconds) *%RETURNS: * Nothing *%DESCRIPTION: * Waits for a PADO packet and copies useful information ***********************************************************************/ static void waitForPADO(PPPoEConnection *conn, int timeout) { fd_set readable; int r; struct timeval tv; struct timeval expire_at; struct timeval now; PPPoEPacket packet; int len; struct PacketCriteria pc; pc.conn = conn; #ifdef PLUGIN conn->seenMaxPayload = 0; #endif if (gettimeofday(&expire_at, NULL) < 0) { fatalSys("gettimeofday (waitForPADO)"); } expire_at.tv_sec += timeout; do { #ifdef PLUGIN if (got_sigterm || got_sighup) return; #endif if (BPF_BUFFER_IS_EMPTY) { if (gettimeofday(&now, NULL) < 0) { fatalSys("gettimeofday (waitForPADO)"); } tv.tv_sec = expire_at.tv_sec - now.tv_sec; tv.tv_usec = expire_at.tv_usec - now.tv_usec; if (tv.tv_usec < 0) { tv.tv_usec += 1000000; if (tv.tv_sec) { tv.tv_sec--; } else { /* Timed out */ return; } } if (tv.tv_sec <= 0 && tv.tv_usec <= 0) { /* Timed out */ return; } FD_ZERO(&readable); FD_SET(conn->discoverySocket, &readable); while(1) { r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); if (r >= 0 || errno != EINTR) break; #ifdef PLUGIN if (got_sigterm || got_sighup) return; #endif } if (r < 0) { fatalSys("select (waitForPADO)"); } if (r == 0) { /* Timed out */ return; } } /* Get the packet */ receivePacket(conn->discoverySocket, &packet, &len); /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "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 #ifdef DEBUGGING_ENABLED if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } #endif /* If it's not for us, loop again */ if (!packetIsForMe(conn, &packet)) continue; if (packet.code == CODE_PADO) { if (BROADCAST(packet.ethHdr.h_source)) { printErr("Ignoring PADO packet from broadcast MAC address"); continue; } #ifdef PLUGIN if (conn->req_peer && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) { warn("Ignoring PADO packet from wrong MAC address"); continue; } #endif pc.gotError = 0; pc.seenACName = 0; pc.seenServiceName = 0; pc.acNameOK = (conn->acName) ? 0 : 1; pc.serviceNameOK = (conn->serviceName) ? 0 : 1; parsePacket(&packet, parsePADOTags, &pc); if (pc.gotError) { printErr("Error in PADO packet"); continue; } if (!pc.seenACName) { printErr("Ignoring PADO packet with no AC-Name tag"); continue; } if (!pc.seenServiceName) { printErr("Ignoring PADO packet with no Service-Name tag"); continue; } conn->numPADOs++; if (pc.acNameOK && pc.serviceNameOK) { memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); if (conn->printACNames) { printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned) conn->peerEth[0], (unsigned) conn->peerEth[1], (unsigned) conn->peerEth[2], (unsigned) conn->peerEth[3], (unsigned) conn->peerEth[4], (unsigned) conn->peerEth[5]); printf("--------------------------------------------------\n"); continue; } conn->discoveryState = STATE_RECEIVED_PADO; break; } } } while (conn->discoveryState != STATE_RECEIVED_PADO); }