|
From: Chad M. <cmm...@us...> - 2005-07-12 00:01:12
|
Update of /cvsroot/seq/showeq/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8196/src Modified Files: Tag: pre_5_0_beta packet.cpp packet.h packetcapture.cpp packetstream.cpp packetstream.h Log Message: Cleaned up pcap handling a bit Cleaned up packet dispatch a bit CRC errors should only happen on EQ ports now so it won't spam people using teamspeak. Locked down on client port in code so we only process packets for the port we're looking at. will help with dual boxing crashes. Index: packetstream.cpp =================================================================== RCS file: /cvsroot/seq/showeq/src/packetstream.cpp,v retrieving revision 1.1.6.11 retrieving revision 1.1.6.12 diff -u -d -r1.1.6.11 -r1.1.6.12 --- packetstream.cpp 19 Jun 2005 16:20:55 -0000 1.1.6.11 +++ packetstream.cpp 12 Jul 2005 00:00:58 -0000 1.1.6.12 @@ -71,6 +71,7 @@ m_fragment(streamid), m_sessionId(0), m_sessionKey(0), + m_sessionClientPort(0), m_maxLength(0), m_decodeKey(0), m_validKey(true) @@ -161,6 +162,9 @@ m_fragment.reset(); m_arqSeqExp = 0; m_arqSeqFound = false; + m_sessionClientPort = 0; + m_sessionId = 0; + m_sessionKey = 0; } //////////////////////////////////////////////////// @@ -248,8 +252,13 @@ // check if the cache has grown large enough that we should give up // on seeing the current serverArqSeqExp - // this should really only kick in for people with pathetic - // network cards that missed the packet. + // + // If people see this a lot, they either have pathetic network cards, or + // are having problems keeping up with packets (slow computer? Too much + // net traffic?). Some possible solutions to this are to turn on session + // tracking to filter out more PF_PACKET packets from getting passed out of + // the kernel and to up the socket receive buffer sizes. See FAQ for + // more information. if (m_cache.size() >= m_arqSeqGiveUp) { // ok, if the expected server arq sequence isn't here yet, give up @@ -467,6 +476,9 @@ { emit numPacket(++m_packetCount, (int)m_streamid); + // Packet is ours now. Logging needs to know this later on. + packet.setSessionKey(getSessionKey()); + // Only accept packets if we've been initialized unless they are // initialization packets! if (packet.getNetOpCode() != OP_SessionRequest && @@ -484,6 +496,44 @@ return; } + // Only accept packets that correspond to our latched client port, if + // it is set. This helps filter out multiple sessions on the same physical + // host when two eq clients zone at the same time. The first one will win. + if (m_sessionClientPort != 0 && + ((dir() == DIR_Server && m_sessionClientPort != packet.getDestPort()) || + (dir() == DIR_Client && m_sessionClientPort != packet.getSourcePort()))) + { +#if (defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1)) || (defined(PACKET_SESSION_DIAG) && PACKET_SESSION_DIAG > 1) + seqDebug("discarding packet %s:%d ==>%s:%d netopcode=%04x size=%d. Multiple sessions on the same box? Ignoring all but one of them. Latched client port %d. Session tracking %s.", + (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), + (const char*)packet.getIPv4DestA(), packet.getDestPort(), + packet.getNetOpCode(), packet.payloadLength(), + m_sessionClientPort, + (m_session_tracking_enabled == 2 ? "locked on" : + (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); +#endif + return; + } + + // Only accept packets that pass the EQ protocol-level CRC check. This helps + // weed out non-EQ packets that we might see. +#ifdef APPLY_CRC_CHECK + if (packet.hasCRC()) + { + uint16_t calcedCRC = calculateCRC(packet); + + if (calcedCRC != packet.crc()) + { + seqWarn("INVALID PACKET: Bad CRC [%s:%d -> %s:%d] netOp %04x seq %04x len %d crc (%04x != %04x)", + (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), + (const char*)packet.getIPv4DestA(), packet.getDestPort(), + packet.getNetOpCode(), packet.arqSeq(), packet.getUDPPayloadLength(), + packet.crc(), calcedCRC); + return; + } + } +#endif /* APPLY_CRC_CHECK */ + // Decode the packet first if (! packet.decode(m_maxLength)) { @@ -894,6 +944,14 @@ m_arqSeqExp = 0; m_arqSeqFound = true; + + if (m_session_tracking_enabled) + { + // Save off client port for the stream so we can match against it + // later. SessionRequest should always be an outer protocol packet + // so we can cast it to EQUDPIPPacketFormat to get the ip headers. + m_sessionClientPort = ((EQUDPIPPacketFormat&) packet).getSourcePort(); + } } break; case OP_SessionResponse: @@ -952,6 +1010,11 @@ // Session tracking if (m_session_tracking_enabled) { + // Save off client port for the stream so we can match against it + // later. SessionRequest should always be an outer protocol packet + // so we can cast it to EQUDPIPPacketFormat to get the ip headers. + m_sessionClientPort = ((EQUDPIPPacketFormat&) packet).getDestPort(); + // If this is the world server talking to us, reset session tracking if // it is on so we unlatch the client in case of getting kicked. if (m_streamid == world2client) @@ -1005,9 +1068,11 @@ { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); + + m_sessionClientPort = 0; } - emit closing(); + emit closing(m_sessionId, m_streamid); } break; case OP_Ack: @@ -1015,7 +1080,7 @@ case OP_AckAfterDisconnect: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) - seqDebug("EQPacket: no-op on for net opcode %04x seq %04x, stream %s (%d)", + seqDebug("EQPacket: no-op on ACK for net opcode %04x seq %04x, stream %s (%d)", packet.getNetOpCode(), eqntohuint16(packet.payload()), EQStreamStr[m_streamid], m_streamid); #endif @@ -1026,7 +1091,7 @@ case OP_SessionStatResponse: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) - seqDebug("EQPacket: no-op on for net opcode %04x, stream %s (%d)", + seqDebug("EQPacket: no-op on stats for net opcode %04x, stream %s (%d)", packet.getNetOpCode(), EQStreamStr[m_streamid], m_streamid); #endif } @@ -1058,6 +1123,25 @@ } /////////////////////////////////////////////////////////////// +// Process a session disconnect if it is for us +void EQPacketStream::close(uint32_t sessionId, EQStreamID streamId, + uint8_t sessionTracking) +{ + if (sessionId == m_sessionId) + { + // Close is for us + reset(); + setSessionTracking(sessionTracking); + +#ifdef PACKET_SESSION_DIAG + seqInfo("EQPacket: SessionDisconnected received on stream %s (%d). Closing session %u on stream %s (%d).", + EQStreamStr[streamId], streamId, sessionId, + EQStreamStr[m_streamid], m_streamid); +#endif + } +} + +/////////////////////////////////////////////////////////////// // Calculate the CRC on the given packet using this stream's key uint16_t EQPacketStream::calculateCRC(EQProtocolPacket& packet) { Index: packet.h =================================================================== RCS file: /cvsroot/seq/showeq/src/packet.h,v retrieving revision 1.39.6.4 retrieving revision 1.39.6.5 diff -u -d -r1.39.6.4 -r1.39.6.5 --- packet.h 10 May 2005 16:12:19 -0000 1.39.6.4 +++ packet.h 12 Jul 2005 00:00:57 -0000 1.39.6.5 @@ -105,7 +105,7 @@ uint32_t sessionKey); protected slots: - void closeStream(); + void closeStream(uint32_t sessionId, EQStreamID streamId); void lockOnClient(in_port_t serverPort, in_port_t clientPort); signals: @@ -173,6 +173,7 @@ EQPacketOPCodeDB* m_worldOPCodeDB; EQPacketOPCodeDB* m_zoneOPCodeDB; + void connectStream(EQPacketStream* stream); void dispatchPacket (int size, unsigned char *buffer); void dispatchPacket(EQUDPIPPacketFormat& packet); protected slots: Index: packet.cpp =================================================================== RCS file: /cvsroot/seq/showeq/src/packet.cpp,v retrieving revision 1.62.6.9 retrieving revision 1.62.6.10 diff -u -d -r1.62.6.9 -r1.62.6.10 --- packet.cpp 10 May 2005 16:12:14 -0000 1.62.6.9 +++ packet.cpp 12 Jul 2005 00:00:56 -0000 1.62.6.10 @@ -143,174 +143,25 @@ m_client2WorldStream = new EQPacketStream(client2world, DIR_Client, m_arqSeqGiveUp, *m_worldOPCodeDB, this, "client2world"); - connect(m_client2WorldStream, - SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), - this, - SIGNAL(rawWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); - connect(m_client2WorldStream, - SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), - this, - SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); - connect(m_client2WorldStream, - SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool)), - this, - SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool))); - connect(m_client2WorldStream, - SIGNAL(cacheSize(int, int)), - this, - SIGNAL(cacheSize(int, int))); - connect(m_client2WorldStream, - SIGNAL(seqReceive(int, int)), - this, - SIGNAL(seqReceive(int, int))); - connect(m_client2WorldStream, - SIGNAL(seqExpect(int, int)), - this, - SIGNAL(seqExpect(int, int))); - connect(m_client2WorldStream, - SIGNAL(numPacket(int, int)), - this, - SIGNAL(numPacket(int, int))); - connect(m_client2WorldStream, - SIGNAL(sessionKey(uint32_t, EQStreamID, uint32_t)), - this, - SLOT(dispatchSessionKey(uint32_t, EQStreamID, uint32_t))); - + connectStream(m_client2WorldStream); + // Setup world -> client stream m_world2ClientStream = new EQPacketStream(world2client, DIR_Server, m_arqSeqGiveUp, *m_worldOPCodeDB, this, "world2client"); - connect(m_world2ClientStream, - SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), - this, - SIGNAL(rawWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); - connect(m_world2ClientStream, - SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), - this, - SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); - connect(m_world2ClientStream, - SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool)), - this, - SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool))); - connect(m_world2ClientStream, - SIGNAL(cacheSize(int, int)), - this, - SIGNAL(cacheSize(int, int))); - connect(m_world2ClientStream, - SIGNAL(seqReceive(int, int)), - this, - SIGNAL(seqReceive(int, int))); - connect(m_world2ClientStream, - SIGNAL(seqExpect(int, int)), - this, - SIGNAL(seqExpect(int, int))); - connect(m_world2ClientStream, - SIGNAL(numPacket(int, int)), - this, - SIGNAL(numPacket(int, int))); - connect(m_world2ClientStream, - SIGNAL(sessionTrackingChanged(uint8_t)), - this, - SIGNAL(sessionTrackingChanged(uint8_t))); - connect(m_world2ClientStream, - SIGNAL(sessionKey(uint32_t, EQStreamID, uint32_t)), - this, - SLOT(dispatchSessionKey(uint32_t, EQStreamID, uint32_t))); + connectStream(m_world2ClientStream); // Setup client -> zone stream m_client2ZoneStream = new EQPacketStream(client2zone, DIR_Client, m_arqSeqGiveUp, *m_zoneOPCodeDB, this, "client2zone"); - connect(m_client2ZoneStream, - SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), - this, - SIGNAL(rawZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); - connect(m_client2ZoneStream, - SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), - this, - SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); - connect(m_client2ZoneStream, - SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*,bool)), - this, - SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*,bool))); - connect(m_client2ZoneStream, - SIGNAL(cacheSize(int, int)), - this, - SIGNAL(cacheSize(int, int))); - connect(m_client2ZoneStream, - SIGNAL(seqReceive(int, int)), - this, - SIGNAL(seqReceive(int, int))); - connect(m_client2ZoneStream, - SIGNAL(seqExpect(int, int)), - this, - SIGNAL(seqExpect(int, int))); - connect(m_client2ZoneStream, - SIGNAL(numPacket(int, int)), - this, - SIGNAL(numPacket(int, int))); - connect(m_client2ZoneStream, - SIGNAL(sessionTrackingChanged(uint8_t)), - this, - SIGNAL(sessionTrackingChanged(uint8_t))); - connect(m_client2ZoneStream, - SIGNAL(closing()), - this, - SLOT(closeStream())); - connect(m_client2ZoneStream, - SIGNAL(sessionKey(uint32_t, EQStreamID, uint32_t)), - this, - SLOT(dispatchSessionKey(uint32_t, EQStreamID, uint32_t))); + connectStream(m_client2ZoneStream); // Setup zone -> client stream m_zone2ClientStream = new EQPacketStream(zone2client, DIR_Server, m_arqSeqGiveUp, *m_zoneOPCodeDB, this, "zone2client"); - connect(m_zone2ClientStream, - SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), - this, - SIGNAL(rawZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); - connect(m_zone2ClientStream, - SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), - this, - SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); - connect(m_zone2ClientStream, - SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool)), - this, - SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool))); - connect(m_zone2ClientStream, - SIGNAL(cacheSize(int, int)), - this, - SIGNAL(cacheSize(int, int))); - connect(m_zone2ClientStream, - SIGNAL(seqReceive(int, int)), - this, - SIGNAL(seqReceive(int, int))); - connect(m_zone2ClientStream, - SIGNAL(seqExpect(int, int)), - this, - SIGNAL(seqExpect(int, int))); - connect(m_zone2ClientStream, - SIGNAL(numPacket(int, int)), - this, - SIGNAL(numPacket(int, int))); - // Zone to client stream specific signals (session tracking non-sense) - connect(m_zone2ClientStream, - SIGNAL(sessionTrackingChanged(uint8_t)), - this, - SIGNAL(sessionTrackingChanged(uint8_t))); - connect(m_zone2ClientStream, - SIGNAL(lockOnClient(in_port_t, in_port_t)), - this, - SLOT(lockOnClient(in_port_t, in_port_t))); - connect(m_zone2ClientStream, - SIGNAL(closing()), - this, - SLOT(closeStream())); - connect(m_zone2ClientStream, - SIGNAL(sessionKey(uint32_t, EQStreamID, uint32_t)), - this, - SLOT(dispatchSessionKey(uint32_t, EQStreamID, uint32_t))); + connectStream(m_zone2ClientStream); // Initialize convenient streams array m_streams[client2world] = m_client2WorldStream; @@ -622,6 +473,96 @@ m_busy_decoding = false; } +///////////////////////////////////////////////////////// +// Connect the given stream's signals to the proper slots +void EQPacket::connectStream(EQPacketStream* stream) +{ + // Packet logging + switch (stream->streamID()) + { + case zone2client: + case client2zone: + { + // Zone server stream + connect(stream, + SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), + this, + SIGNAL(rawZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); + + connect(stream, + SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), + this, + SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); + + connect(stream, + SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool)), + this, + SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool))); + } + break; + case world2client: + case client2world: + { + // World server stream + connect(stream, + SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), + this, + SIGNAL(rawWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); + + connect(stream, + SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), + this, + SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); + + connect(stream, + SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool)), + this, + SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool))); + } + break; + default : + { + return; + } + } + + // Debugging + connect(stream, + SIGNAL(cacheSize(int, int)), + this, + SIGNAL(cacheSize(int, int))); + connect(stream, + SIGNAL(seqReceive(int, int)), + this, + SIGNAL(seqReceive(int, int))); + connect(stream, + SIGNAL(seqExpect(int, int)), + this, + SIGNAL(seqExpect(int, int))); + connect(stream, + SIGNAL(numPacket(int, int)), + this, + SIGNAL(numPacket(int, int))); + + // Session handling + connect(stream, + SIGNAL(sessionTrackingChanged(uint8_t)), + this, + SIGNAL(sessionTrackingChanged(uint8_t))); + connect(stream, + SIGNAL(lockOnClient(in_port_t, in_port_t)), + this, + SLOT(lockOnClient(in_port_t, in_port_t))); + connect(stream, + SIGNAL(closing(uint32_t, EQStreamID)), + this, + SLOT(closeStream(uint32_t, EQStreamID))); + connect(stream, + SIGNAL(sessionKey(uint32_t, EQStreamID, uint32_t)), + this, + SLOT(dispatchSessionKey(uint32_t, EQStreamID, uint32_t))); +} + //////////////////////////////////////////////////// // This function decides the fate of the Everquest packet // and dispatches it to the correct packet stream for handling function @@ -630,7 +571,6 @@ #ifdef DEBUG_PACKET debug ("EQPacket::dispatchPacket()"); #endif /* DEBUG_PACKET */ - /* Setup variables */ // Create an object to parse the packet EQUDPIPPacketFormat packet(buffer, size, false); @@ -644,7 +584,7 @@ void EQPacket::dispatchPacket(EQUDPIPPacketFormat& packet) { - /* Client Detection */ + // Detect client by world server port traffic... if (m_detectingClient && packet.getSourcePort() == WorldServerGeneralPort) { m_ip = packet.getIPv4DestA(); @@ -661,118 +601,93 @@ emit clientChanged(m_client_addr); seqInfo("Client Detected: %s", (const char*)m_ip); } - /* end client detection */ - /* Find the stream we are sending this to */ - EQPacketStream* stream; - - /* Chat and Login Server Packets, Discard for now */ + // Dispatch based on known streams if ((packet.getDestPort() == ChatServerPort) || (packet.getSourcePort() == ChatServerPort)) + { + // Drop chat server traffic return; - - if ((packet.getDestPort() == WorldServerChatPort) || + } + else if ((packet.getDestPort() == WorldServerChatPort) || (packet.getSourcePort() == WorldServerChatPort)) + { + // Drop cross-server chat traffic return; - - if ((packet.getDestPort() == WorldServerChat2Port) || + } + else if ((packet.getDestPort() == WorldServerChat2Port) || (packet.getSourcePort() == WorldServerChat2Port)) + { + // Drop email and cross-game chat traffic return; - - if (((packet.getDestPort() >= LoginServerMinPort) && - (packet.getDestPort() <= LoginServerMaxPort)) || + } + else if (((packet.getDestPort() >= LoginServerMinPort) && + (packet.getDestPort() <= LoginServerMaxPort)) || (packet.getSourcePort() >= LoginServerMinPort) && (packet.getSourcePort() <= LoginServerMaxPort)) - return; - - if (packet.getIPv4SourceN() == m_client_addr) - { - if (packet.getDestPort() == WorldServerGeneralPort) - stream = m_client2WorldStream; - else - stream = m_client2ZoneStream; - } - else if (packet.getIPv4DestN() == m_client_addr) - { - if (packet.getSourcePort() == WorldServerGeneralPort) - stream = m_world2ClientStream; - else - stream = m_zone2ClientStream; - } - else { + // Drop login server traffic return; } - - // Fill in the session id of the packet in case people need it and only - // have the packet. This is crappy, but logging doesn't have access to - // the stream the packet came from. - packet.setSessionKey(stream->getSessionKey()); - -#ifdef APPLY_CRC_CHECK - // Check CRC. Have to ask the stream to do it, since the packet doesn't know - // it's sessionKey yet. - if (packet.hasCRC()) + else if (packet.getDestPort() == WorldServerGeneralPort || + packet.getSourcePort() == WorldServerGeneralPort) { - uint16_t calcedCRC = stream->calculateCRC(packet); - - if (calcedCRC != packet.crc() && packet.getSessionKey() != 0) + // World server traffic. Dispatch it. + if (packet.getIPv4SourceN() == m_client_addr) { - seqWarn("INVALID PACKET: Bad CRC [%s:%d -> %s:%d] netOp %04x seq %04x len %d crc (%04x != %04x)", - (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), - (const char*)packet.getIPv4DestA(), packet.getDestPort(), - packet.getNetOpCode(), - packet.arqSeq(), - packet.getUDPPayloadLength(), - packet.crc(), calcedCRC); - return; + m_client2WorldStream->handlePacket(packet); + } + else + { + m_world2ClientStream->handlePacket(packet); } } -#endif /* APPLY_CRC_CHECK */ - - /* discard pure ack/req packets */ - if (packet.getNetOpCode() == OP_KeepAlive || - packet.getNetOpCode() == OP_SessionStatRequest || - packet.getNetOpCode() == OP_SessionStatResponse || - packet.getNetOpCode() == OP_AckFuture || - packet.getNetOpCode() == OP_AckAfterDisconnect || - packet.getNetOpCode() == OP_Ack) + else { -#if defined(PACKET_PROCESS_DIAG) - seqDebug("discarding packet %s:%d ==>%s:%d netopcode=%04x size=%d", - (const char*)packet.getIPv4SourceA(), packet.getSourcePort(), - (const char*)packet.getIPv4DestA(), packet.getDestPort(), - packet.getNetOpCode(), size); -#endif - return; + // Anything else we assume is zone server traffic. + if (packet.getIPv4SourceN() == m_client_addr) + { + m_client2ZoneStream->handlePacket(packet); + } + else + { + m_zone2ClientStream->handlePacket(packet); + } } - - // Send it to the stream handler. - stream->handlePacket(packet); - - return; } /* end dispatchPacket() */ //////////////////////////////////////////////////// // Handle zone2client stream closing -void EQPacket::closeStream() +void EQPacket::closeStream(uint32_t sessionId, EQStreamID streamId) { - // reseting the pcap filter to a non-exclusive form allows us to beat - // the race condition between timer and processing the zoneServerInfo - if(m_playbackPackets == PLAYBACK_OFF || m_playbackPackets == PLAYBACK_FORMAT_TCPDUMP) + // If this is the zone server session closing, reset the pcap filter to + // a non-exclusive form + if ((streamId == zone2client || streamId == client2zone) && + (m_playbackPackets == PLAYBACK_OFF || + m_playbackPackets == PLAYBACK_FORMAT_TCPDUMP)) { m_packetCapture->setFilter(m_device, m_ip, m_realtime, IP_ADDRESS_TYPE, 0, 0); emit filterChanged(); } - seqInfo("EQPacket: SEQClosing detected, awaiting next zone session, pcap filter: EQ Client %s", + // Pass the close onto the streams + m_client2WorldStream->close(sessionId, streamId, m_session_tracking); + m_world2ClientStream->close(sessionId, streamId, m_session_tracking); + m_client2ZoneStream->close(sessionId, streamId, m_session_tracking); + m_zone2ClientStream->close(sessionId, streamId, m_session_tracking); + + // If we just closed the zone server session, unlatch the client port + if (streamId == zone2client || streamId == client2zone) + { + m_clientPort = 0; + m_serverPort = 0; + + emit clientPortLatched(m_clientPort); + + seqInfo("EQPacket: SessionDisconnect detected, awaiting next zone session, pcap filter: EQ Client %s", (const char*)m_ip); - - // we'll be waiting for a new SEQStart for ALL streams - // it seems we only ever see a proper closing sequence from the zone server - // so reset all packet sequence caches - resetEQPacket(); + } } //////////////////////////////////////////////////// @@ -808,12 +723,12 @@ // Wanted this message even if we're running on playback... if (m_mac.length() == 17) { - seqInfo("EQPacket: SEQStart detected, pcap filter: EQ Client %s, Client port %d. Server port %d", + seqInfo("EQPacket: SessionRequest detected, pcap filter: EQ Client %s, Client port %d. Server port %d", (const char*)m_mac, m_clientPort, m_serverPort); } else { - seqInfo("EQPacket: SEQStart detected, pcap filter: EQ Client %s, Client port %d. Server port %d", + seqInfo("EQPacket: SessionRequest detected, pcap filter: EQ Client %s, Client port %d. Server port %d", (const char*)m_ip, m_clientPort, m_serverPort); } Index: packetstream.h =================================================================== RCS file: /cvsroot/seq/showeq/src/packetstream.h,v retrieving revision 1.1.6.2 retrieving revision 1.1.6.3 diff -u -d -r1.1.6.2 -r1.1.6.3 --- packetstream.h 9 Feb 2005 07:07:39 -0000 1.1.6.2 +++ packetstream.h 12 Jul 2005 00:00:58 -0000 1.1.6.3 @@ -76,6 +76,7 @@ const QObject* receiver, const char* member); void receiveSessionKey(uint32_t sessionId, EQStreamID streamid, uint32_t sessionKey); + void close(uint32_t sessionId, EQStreamID streamid, uint8_t sessionTracking); uint16_t calculateCRC(EQProtocolPacket& packet); uint32_t getSessionKey() const { return m_sessionKey; } @@ -92,7 +93,7 @@ bool unknown); // this signals stream closure - void closing(); + void closing(uint32_t sessionId, EQStreamID streamId); // this signals a change in the session tracking state void sessionTrackingChanged(uint8_t); @@ -137,6 +138,7 @@ // Session info uint32_t m_sessionId; uint32_t m_sessionKey; + in_port_t m_sessionClientPort; uint32_t m_maxLength; // encryption Index: packetcapture.cpp =================================================================== RCS file: /cvsroot/seq/showeq/src/packetcapture.cpp,v retrieving revision 1.2.4.5 retrieving revision 1.2.4.6 diff -u -d -r1.2.4.5 -r1.2.4.6 --- packetcapture.cpp 19 Jun 2005 16:20:55 -0000 1.2.4.5 +++ packetcapture.cpp 12 Jul 2005 00:00:58 -0000 1.2.4.6 @@ -4,7 +4,7 @@ * ShowEQ Distributed under GPL * http://www.sourceforge.net/projects/seq * - * Copyright 2000-2003 by the respective ShowEQ Developers + * Copyright 2000-2005 by the respective ShowEQ Developers * Portions Copyright 2001-2003 Zaphod (do...@us...). */ @@ -33,25 +33,31 @@ PacketCaptureThread::~PacketCaptureThread() { - // Drop the packets we have lying around - pthread_mutex_lock (&m_pcache_mutex); + if (m_pcache_pcap) + { + // Turn off pcap + pcap_close(m_pcache_pcap); + } - struct packetCache *pc = m_pcache_first; - struct packetCache* freeMe = NULL; + // Drop the packets we have lying around + pthread_mutex_lock (&m_pcache_mutex); - while (pc) - { - freeMe = pc; - pc = pc->next; + struct packetCache *pc = m_pcache_first; + struct packetCache* freeMe = NULL; - free(freeMe); - } + while (pc) + { + freeMe = pc; + pc = pc->next; - m_pcache_first = NULL; - m_pcache_last = NULL; - m_pcache_closed = true; + free(freeMe); + } - pthread_mutex_unlock (&m_pcache_mutex); + m_pcache_first = NULL; + m_pcache_last = NULL; + m_pcache_closed = true; + + pthread_mutex_unlock (&m_pcache_mutex); } void PacketCaptureThread::setPlaybackSpeed(int playbackSpeed) @@ -75,108 +81,124 @@ } } -void PacketCaptureThread::start(const char *device, const char *host, bool realtime, uint8_t address_type) +void PacketCaptureThread::start(const char *device, const char *host, + bool realtime, uint8_t address_type) { - - char ebuf[256]; // pcap error buffer + char ebuf[PCAP_ERRBUF_SIZE]; // pcap error buffer char filter_buf[256]; // pcap filter buffer struct bpf_program bpp; struct sched_param sp; + bpf_u_int32 mask; // sniff device netmask + bpf_u_int32 net; // sniff device ip seqInfo("Initializing Packet Capture Thread: "); m_pcache_closed = false; - // create pcap style filter expressions - if (address_type == IP_ADDRESS_TYPE) - { - if (strcmp(host, AUTOMATIC_CLIENT_IP) == 0) - { - seqInfo("Filtering packets on device %s, searching for EQ client...", device); - sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800"); - } - else - { - seqInfo("Filtering packets on device %s, IP host %s", device, host); - sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and host %s and ether proto 0x0800", host); - } - } - - else if (address_type == MAC_ADDRESS_TYPE) - { - seqInfo("Filtering packets on device %s, MAC host %s", device, host); - sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and ether host %s and ether proto 0x0800", host); - } + // Fetch the netmask for the device to use later with the filter. + if (pcap_lookupnet(device, &net, &mask, ebuf) == -1) + { + // Couldn't find net mask. Just leave it open. + seqWarn("Couldn't determine netmask of device %s. Using 0.0.0.0. Error was %s", + device, ebuf); + } - else - { - seqFatal("pcap_error:filter_string: unknown address_type (%d)", address_type); - exit(0); - } + // create pcap style filter expressions + if (address_type == IP_ADDRESS_TYPE) + { + if (strcmp(host, AUTOMATIC_CLIENT_IP) == 0) + { + seqInfo("Filtering packets on device %s, searching for EQ client...", device); + sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800"); + } + else + { + seqInfo("Filtering packets on device %s, IP host %s", device, host); + sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and host %s and ether proto 0x0800", host); + } + } + else if (address_type == MAC_ADDRESS_TYPE) + { + seqInfo("Filtering packets on device %s, MAC host %s", device, host); + sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and ether host %s and ether proto 0x0800", host); + } + else + { + seqFatal("pcap_error:filter_string: unknown address_type (%d)", address_type); + exit(0); + } - /* A word about pcap_open_live() from the docs - ** to_ms specifies the read timeout in milliseconds. The - ** read timeout is used to arrange that the read not necessarily - ** return immediately when a packet is seen, but that it wait - ** for some amount of time to allow more packets to arrive and - ** to read multiple packets from the OS kernel in one operation. - ** Not all platforms support a read timeout; on platforms that - ** don't, the read timeout is ignored. - ** - ** In Linux 2.4.x with the to_ms set to 0 we get packets immediatly, - ** and thats what we need in this application, so don't change it!! - ** - ** a race condition exists between this thread and the main thread - ** any artificial delay in getting packets can cause filtering problems - ** and cause us to miss new stream when the player zones. - */ - // initialize the pcap object - m_pcache_pcap = pcap_open_live((char *) device, BUFSIZ, true, 0, ebuf); + /* A word about pcap_open_live() from the docs + ** to_ms specifies the read timeout in milliseconds. The + ** read timeout is used to arrange that the read not necessarily + ** return immediately when a packet is seen, but that it wait + ** for some amount of time to allow more packets to arrive and + ** to read multiple packets from the OS kernel in one operation. + ** Not all platforms support a read timeout; on platforms that + ** don't, the read timeout is ignored. + ** + ** In Linux 2.4.x with the to_ms set to 0 we get packets immediatly, + ** and thats what we need in this application, so don't change it!! + ** + ** a race condition exists between this thread and the main thread + ** any artificial delay in getting packets can cause filtering problems + ** and cause us to miss new stream when the player zones. + */ + // initialize the pcap object + m_pcache_pcap = pcap_open_live((char *) device, BUFSIZ, true, 0, ebuf); #ifdef __FreeBSD__ - // if we're on FreeBSD, we need to call ioctl on the file descriptor - // with BIOCIMMEDIATE to get the kernel Berkeley Packet Filter device - // to return packets to us immediately, rather than holding them in - // it's internal buffer... if we don't do this, we end up getting 32K - // worth of packets all at once, at long intervals -- if someone - // knows a less hacky way of doing this, I'd love to hear about it. - // the problem here is that libpcap doesn't expose an API to do this - // in any way - int fd = *((int*)m_pcache_pcap); - int temp = 1; - if ( ioctl( fd, BIOCIMMEDIATE, &temp ) < 0 ) - seqWarn("PCAP couldn't set immediate mode on BSD" ); + // if we're on FreeBSD, we need to call ioctl on the file descriptor + // with BIOCIMMEDIATE to get the kernel Berkeley Packet Filter device + // to return packets to us immediately, rather than holding them in + // it's internal buffer... if we don't do this, we end up getting 32K + // worth of packets all at once, at long intervals -- if someone + // knows a less hacky way of doing this, I'd love to hear about it. + // the problem here is that libpcap doesn't expose an API to do this + // in any way + int fd = *((int*)m_pcache_pcap); + int temp = 1; + if ( ioctl( fd, BIOCIMMEDIATE, &temp ) < 0 ) + { + seqWarn("PCAP couldn't set immediate mode on BSD" ); + } #endif - if (!m_pcache_pcap) - { - seqWarn("pcap_error:pcap_open_live(%s): %s", device, ebuf); - if ((getuid() != 0) && (geteuid() != 0)) - seqWarn("Make sure you are running ShowEQ as root."); - exit(0); - } + if (!m_pcache_pcap) + { + seqWarn("pcap_error:pcap_open_live(%s): %s", device, ebuf); + if ((getuid() != 0) && (geteuid() != 0)) + { + seqWarn("Make sure you are running ShowEQ as root."); + } + exit(0); + } - if (pcap_compile(m_pcache_pcap, &bpp, filter_buf, 1, 0) == -1) - { - pcap_perror (m_pcache_pcap, "pcap_error:pcap_compile"); - exit(0); - } + if (pcap_compile(m_pcache_pcap, &bpp, filter_buf, 1, net) == -1) + { + pcap_perror (m_pcache_pcap, "pcap_error:pcap_compile"); + exit(0); + } - if (pcap_setfilter (m_pcache_pcap, &bpp) == -1) - { - pcap_perror (m_pcache_pcap, "pcap_error:pcap_setfilter"); - exit(0); - } + if (pcap_setfilter (m_pcache_pcap, &bpp) == -1) + { + pcap_perror (m_pcache_pcap, "pcap_error:pcap_setfilter"); + exit(0); + } - m_pcache_first = m_pcache_last = NULL; + pcap_freecode(&bpp); - pthread_mutex_init (&m_pcache_mutex, NULL); - pthread_create (&m_tid, NULL, loop, (void*)this); + m_pcache_first = m_pcache_last = NULL; - if (realtime) - { - memset (&sp, 0, sizeof (sp)); - sp.sched_priority = 1; - if (pthread_setschedparam (m_tid, SCHED_RR, &sp) != 0) - seqWarn("Failed to set capture thread realtime."); - } + pthread_mutex_init (&m_pcache_mutex, NULL); + pthread_create (&m_tid, NULL, loop, (void*)this); + + if (realtime) + { + memset (&sp, 0, sizeof (sp)); + sp.sched_priority = 1; + if (pthread_setschedparam (m_tid, SCHED_RR, &sp) != 0) + { + seqWarn("Failed to set capture thread realtime."); + } + } } //------------------------------------------------------------------------ @@ -214,8 +236,9 @@ void PacketCaptureThread::stop() { - // close the pcap session - pcap_close(m_pcache_pcap); + // close the pcap session + pcap_close(m_pcache_pcap); + m_pcache_pcap = NULL; } void* PacketCaptureThread::loop (void *param) @@ -226,8 +249,8 @@ } void PacketCaptureThread::packetCallBack(u_char * param, - const struct pcap_pkthdr *ph, - const u_char *data) + const struct pcap_pkthdr *ph, + const u_char *data) { struct packetCache *pc; PacketCaptureThread* myThis = (PacketCaptureThread*)param; @@ -339,13 +362,17 @@ if (! myThis->m_pcache_closed) { - if (myThis->m_pcache_last) - myThis->m_pcache_last->next = pc; + if (myThis->m_pcache_last) + { + myThis->m_pcache_last->next = pc; + } - myThis->m_pcache_last = pc; + myThis->m_pcache_last = pc; - if (!myThis->m_pcache_first) - myThis->m_pcache_first = pc; + if (!myThis->m_pcache_first) + { + myThis->m_pcache_first = pc; + } } else { @@ -391,35 +418,73 @@ bool realtime, uint8_t address_type, uint16_t zone_port, - uint16_t client_port - ) + uint16_t client_port) { char filter_buf[256]; // pcap filter buffer + char ebuf[PCAP_ERRBUF_SIZE]; struct bpf_program bpp; struct sched_param sp; + bpf_u_int32 mask; // sniff device netmask + bpf_u_int32 net; // sniff device ip + + // Fetch the netmask for the device to use later with the filter + if (pcap_lookupnet(device, &net, &mask, ebuf) == -1) + { + // Couldn't find net mask. Just leave it open. + seqWarn("Couldn't determine netmask of device %s. Using 0.0.0.0. Error was %s", + device, ebuf); + } /* Listen to World Server or the specified Zone Server */ if (address_type == IP_ADDRESS_TYPE && client_port) - sprintf (filter_buf, "(udp[0:2] = 9000 or udp[2:2] = 9000 or udp[0:2] = 9876 or udp[0:2] = %d or udp[2:2] = %d) and host %s and ether proto 0x0800", client_port, client_port, hostname); + { + // Restrict to client port and ip, plus world streams. + sprintf(filter_buf, + "(udp[0:2] = 9000 or udp[2:2] = 9000 or udp[0:2] = 9876 or udp[0:2] = %d or udp[2:2] = %d) and host %s and ether proto 0x0800", + client_port, client_port, hostname); + } else if (address_type == IP_ADDRESS_TYPE && zone_port) - sprintf (filter_buf, "(udp[0:2] = 9000 or udp[2:2] = 9000 or udp[0:2] = 9876 or udp[0:2] = %d or udp[2:2] = %d) and host %s and ether proto 0x0800", zone_port, zone_port, hostname); + { + // Restrict to zone port and world streams. + sprintf(filter_buf, + "(udp[0:2] = 9000 or udp[2:2] = 9000 or udp[0:2] = 9876 or udp[0:2] = %d or udp[2:2] = %d) and host %s and ether proto 0x0800", + zone_port, zone_port, hostname); + } else if (address_type == MAC_ADDRESS_TYPE && client_port) - sprintf (filter_buf, "(udp[0:2] = 9000 or udp[2:2] = 9000 or udp[0:2] = 9876 or udp[0:2] = %d or udp[2:2] = %d) and ether host %s and ether proto 0x0800", client_port, client_port, hostname); + { + // Restrict to client port and world streams. + sprintf(filter_buf, + "(udp[0:2] = 9000 or udp[2:2] = 9000 or udp[0:2] = 9876 or udp[0:2] = %d or udp[2:2] = %d) and ether host %s and ether proto 0x0800", + client_port, client_port, hostname); + } else if (address_type == MAC_ADDRESS_TYPE && zone_port) - sprintf (filter_buf, "(udp[0:2] = 9000 or udp[2:2] = 9000 or udp[0:2] = 9876 or udp[0:2] = %d or udp[2:2] = %d) and ether host %s and ether proto 0x0800", zone_port, zone_port, hostname); + { + // Restrict to zone port and world streams. + sprintf(filter_buf, + "(udp[0:2] = 9000 or udp[2:2] = 9000 or udp[0:2] = 9876 or udp[0:2] = %d or udp[2:2] = %d) and ether host %s and ether proto 0x0800", + zone_port, zone_port, hostname); + } else if (hostname != NULL && !client_port && !zone_port) - sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800 and host %s", hostname); + { + // Leave wide open. + sprintf(filter_buf, + "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800 and host %s", + hostname); + } else { - seqInfo("Filtering packets on device %s, searching for EQ client...", device); - sprintf (filter_buf, "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800"); + // Not even a hostname. Leave really wide open! + seqInfo("Filtering packets on device %s, searching for EQ client...", + device); + sprintf(filter_buf, + "udp[0:2] > 1024 and udp[2:2] > 1024 and ether proto 0x0800"); } - if (pcap_compile (m_pcache_pcap, &bpp, filter_buf, 1, 0) == -1) + if (pcap_compile (m_pcache_pcap, &bpp, filter_buf, 1, net) == -1) { - seqWarn("%s",filter_buf); - pcap_perror(m_pcache_pcap, "pcap_error:pcap_compile_error"); - exit (0); + seqWarn("%s",filter_buf); + pcap_perror(m_pcache_pcap, "pcap_error:pcap_compile_error"); + exit(0); } if (pcap_setfilter (m_pcache_pcap, &bpp) == -1) @@ -428,14 +493,18 @@ exit (0); } + pcap_freecode(&bpp); + seqDebug("PCAP Filter Set: %s", filter_buf); if (realtime) { - memset (&sp, 0, sizeof (sp)); - sp.sched_priority = 1; - if (pthread_setschedparam (m_tid, SCHED_RR, &sp) != 0) - seqWarn("Failed to set capture thread realtime."); + memset (&sp, 0, sizeof (sp)); + sp.sched_priority = 1; + if (pthread_setschedparam (m_tid, SCHED_RR, &sp) != 0) + { + seqWarn("Failed to set capture thread realtime."); + } } m_pcapFilter = filter_buf; |