#include #include #include "inet/common/INETDefs.h" #include "inet/common/scenario/IScriptable.h" #include "inet/networklayer/ipv4/IPv4InterfaceData.h" #include "inet/networklayer/contract/ipv4/IPv4ControlInfo.h" #include "inet/networklayer/contract/IInterfaceTable.h" #include "inet/networklayer/ipv4/IIPv4RoutingTable.h" #include "inet/networklayer/ipv4/IGMPv2.h" namespace inet { enum StateKind { HOST_GROUP_STATE = 0x01, ROUTER_GROUP_STATE = 0x02, ROUTER_IF_STATE = 0x04, }; class INET_API TestIGMP : public IGMPv2, public IScriptable { private: std::ofstream out; cModule *node; // std::string currentEvent; // std::vector currentActions; // IGMPHostGroupState st; // IGMPRouterGroupState st2; // IGMPRouterState currentRouterGroupState; protected: typedef IPv4InterfaceData::IPv4AddressVector IPv4AddressVector; virtual void initialize(int stage) override; virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override; virtual void configureInterface(InterfaceEntry *ie) override; virtual void processIgmpMessage(IGMPMessage *msg) override; virtual void processHostGroupTimer(cMessage *msg) override; virtual void processQueryTimer(cMessage *msg) override; virtual void processLeaveTimer(cMessage *msg) override; virtual void processRexmtTimer(cMessage *msg) override; virtual void processCommand(const cXMLElement &node) override; virtual void sendToIP(IGMPMessage *msg, InterfaceEntry *ie, const IPv4Address& dest) override; private: void dumpMulticastGroups(const char* name, const char *ifname, IPv4AddressVector groups); void startEvent(const char *event, int stateMask, InterfaceEntry *ie, const IPv4Address *group = NULL); void endEvent(int stateMask, InterfaceEntry *ie, const IPv4Address *group = NULL); void printStates(int stateMask, InterfaceEntry *ie, const IPv4Address *group); }; Define_Module(TestIGMP); void TestIGMP::initialize(int stage) { if (stage == 0) { node = (cModule*)getOwner()->getOwner(); const char *filename = par("outputFile"); if (filename && (*filename)) { out.open(filename); if (out.fail()) throw cRuntimeError("Failed to open output file: %s", filename); } } IGMPv2::initialize(stage); } void TestIGMP::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) { const IPv4MulticastGroupInfo *info; if (signalID == NF_IPv4_MCAST_JOIN) { info = check_and_cast(obj); startEvent("join group", HOST_GROUP_STATE, info->ie, &info->groupAddress); IGMPv2::receiveSignal(source, signalID, obj, details); endEvent(HOST_GROUP_STATE, info->ie, &info->groupAddress); } else if (signalID == NF_IPv4_MCAST_LEAVE) { info = check_and_cast(obj); startEvent("leave group", HOST_GROUP_STATE, info->ie, &info->groupAddress); IGMPv2::receiveSignal(source, signalID, obj, details); endEvent(HOST_GROUP_STATE, info->ie, &info->groupAddress); } else { IGMPv2::receiveSignal(source, signalID, obj, details); } } void TestIGMP::configureInterface(InterfaceEntry *ie) { startEvent("configure interface", ROUTER_IF_STATE, ie); IGMPv2::configureInterface(ie); endEvent(ROUTER_IF_STATE, ie); } void TestIGMP::processIgmpMessage(IGMPMessage *msg) { IPv4ControlInfo *controlInfo = (IPv4ControlInfo *)msg->getControlInfo(); InterfaceEntry *ie = ift->getInterfaceById(controlInfo->getInterfaceId()); IPv4Address group = IPv4Address::UNSPECIFIED_ADDRESS; switch (msg->getType()) { case IGMP_MEMBERSHIP_QUERY: group = check_and_cast(msg)->getGroupAddress(); break; case IGMPV1_MEMBERSHIP_REPORT: group = check_and_cast(msg)->getGroupAddress(); break; case IGMPV2_MEMBERSHIP_REPORT: group = check_and_cast(msg)->getGroupAddress(); break; case IGMPV2_LEAVE_GROUP: group = check_and_cast(msg)->getGroupAddress(); break; } int stateMask = 0; if (rt->isMulticastForwardingEnabled()) stateMask |= ROUTER_IF_STATE; if (!group.isUnspecified()) stateMask |= HOST_GROUP_STATE; if (!group.isUnspecified() && rt->isMulticastForwardingEnabled()) stateMask |= ROUTER_GROUP_STATE; switch (msg->getType()) { case IGMP_MEMBERSHIP_QUERY: startEvent("query received", stateMask, ie, &group); IGMPv2::processIgmpMessage(msg); endEvent(stateMask, ie, &group); break; case IGMPV2_MEMBERSHIP_REPORT: startEvent("report received", stateMask, ie, &group); IGMPv2::processIgmpMessage(msg); endEvent(stateMask, ie, &group); break; case IGMPV2_LEAVE_GROUP: startEvent("leave received", stateMask, ie, &group); IGMPv2::processIgmpMessage(msg); endEvent(stateMask, ie, &group); break; default: IGMPv2::processIgmpMessage(msg); break; } } void TestIGMP::processHostGroupTimer(cMessage *msg) { IGMPHostTimerContext *ctx = (IGMPHostTimerContext*)msg->getContextPointer(); startEvent("timer expired", HOST_GROUP_STATE, ctx->ie, &ctx->hostGroup->groupAddr); IGMPv2::processHostGroupTimer(msg); endEvent(HOST_GROUP_STATE, ctx->ie, &ctx->hostGroup->groupAddr); } void TestIGMP::processQueryTimer(cMessage *msg) { InterfaceEntry *ie = (InterfaceEntry*)msg->getContextPointer(); RouterInterfaceData *routerData = getRouterInterfaceData(ie); const char *event = routerData && routerData->igmpRouterState == IGMP_RS_QUERIER ? "gen. query timer expired" : "other querier present timer expired"; startEvent(event, ROUTER_IF_STATE, ie); IGMPv2::processQueryTimer(msg); endEvent(ROUTER_IF_STATE, ie); } void TestIGMP::processLeaveTimer(cMessage *msg) { IGMPRouterTimerContext *ctx = (IGMPRouterTimerContext*)msg->getContextPointer(); InterfaceEntry *ie = ctx->ie; IPv4Address group = ctx->routerGroup->groupAddr; startEvent("timer expired", ROUTER_GROUP_STATE, ie, &group); IGMPv2::processLeaveTimer(msg); endEvent(ROUTER_GROUP_STATE, ie, &group); } void TestIGMP::processRexmtTimer(cMessage *msg) { IGMPRouterTimerContext *ctx = (IGMPRouterTimerContext*)msg->getContextPointer(); startEvent("rexmt timer expired", ROUTER_GROUP_STATE, ctx->ie, &ctx->routerGroup->groupAddr); IGMPv2::processRexmtTimer(msg); endEvent(ROUTER_GROUP_STATE, ctx->ie, &ctx->routerGroup->groupAddr); } void TestIGMP::sendToIP(IGMPMessage *msg, InterfaceEntry *ie, const IPv4Address& dest) { if (out.is_open()) { switch (msg->getType()) { case IGMP_MEMBERSHIP_QUERY: out << "send query"; break; case IGMPV1_MEMBERSHIP_REPORT: case IGMPV2_MEMBERSHIP_REPORT: out << "send report"; break; case IGMPV2_LEAVE_GROUP: out << "send leave"; break; } } IGMPv2::sendToIP(msg, ie, dest); } void TestIGMP::processCommand(const cXMLElement &node) { Enter_Method_Silent(); const char *tag = node.getTagName(); const char *ifname = node.getAttribute("ifname"); InterfaceEntry *ie = ifname ? ift->getInterfaceByName(ifname) : NULL; if (!strcmp(tag, "join")) { const char *group = node.getAttribute("group"); ie->ipv4Data()->joinMulticastGroup(IPv4Address(group)); } else if (!strcmp(tag, "leave")) { const char *group = node.getAttribute("group"); ie->ipv4Data()->leaveMulticastGroup(IPv4Address(group)); } else if (!strcmp(tag, "dump")) { const char *what = node.getAttribute("what"); if (!strcmp(what, "groups")) { if (!ie) throw cRuntimeError("'ifname' attribute is missing at XML node ", node.str().c_str()); IPv4AddressVector joinedGroups; const int count = ie->ipv4Data()->getNumOfJoinedMulticastGroups(); for (int i = 0; i < count; ++i) joinedGroups.push_back(ie->ipv4Data()->getJoinedMulticastGroup(i)); dumpMulticastGroups(what, ifname, joinedGroups); } else if (!strcmp(what, "listeners")) { if (!ie) throw cRuntimeError("'ifname' attribute is missing at XML node ", node.str().c_str()); IPv4AddressVector reportedGroups; const int count = ie->ipv4Data()->getNumOfReportedMulticastGroups(); for (int i = 0; i < count; ++i) reportedGroups.push_back(ie->ipv4Data()->getReportedMulticastGroup(i)); dumpMulticastGroups("listeners", ifname, reportedGroups); } } else if (!strcmp(tag, "disable")) { enabled = false; } else if (!strcmp(tag, "enable")) { enabled = true; } else if (!strcmp(tag, "send")) { const char *what = node.getAttribute("what"); if (!strcmp(what,"query")) { const char *groupAttr = node.getAttribute("group"); IPv4Address group = groupAttr ? IPv4Address(groupAttr) : IPv4Address::UNSPECIFIED_ADDRESS; const char *maxRespTimeAttr = node.getAttribute("maxRespTime"); double maxRespTime = maxRespTimeAttr ? atof(maxRespTimeAttr) : queryResponseInterval; sendQuery(ie, group, maxRespTime); } } } void TestIGMP::startEvent(const char * event, int stateMask, InterfaceEntry *ie, const IPv4Address *group) { if (out.is_open()) { out << "t=" << simTime() << " " << node->getFullName() << "/" << ie->getName(); if (group) out << "/" << *group; out << ":"; printStates(stateMask, ie, group); out << " --> " << event << " <"; } } void TestIGMP::endEvent(int stateMask, InterfaceEntry *ie, const IPv4Address *group) { if (out.is_open()) { out << "> -->"; printStates(stateMask, ie, group); out << "\n"; out.flush(); } } void TestIGMP::printStates(int stateMask, InterfaceEntry *ie, const IPv4Address *group) { if (stateMask & ROUTER_IF_STATE) { RouterInterfaceData *routerIfData = getRouterInterfaceData(ie); if (routerIfData) { switch (routerIfData->igmpRouterState) { case IGMP_RS_INITIAL: out << " INITIAL"; break; case IGMP_RS_QUERIER: out << " QUERIER"; break; case IGMP_RS_NON_QUERIER: out << " NON_QUERIER"; break; default: out << "???"; break; } } else out << " "; } if ((stateMask & ROUTER_GROUP_STATE) && group) { RouterGroupData *routerGroupData = getRouterGroupData(ie, *group); if (routerGroupData) { switch (routerGroupData->state) { case IGMP_RGS_NO_MEMBERS_PRESENT: out << " NO_MEMBERS_PRESENT"; break; case IGMP_RGS_MEMBERS_PRESENT: out << " MEMBERS_PRESENT"; break; case IGMP_RGS_V1_MEMBERS_PRESENT: out << " V1_MEMBERS_PRESENT"; break; case IGMP_RGS_CHECKING_MEMBERSHIP: out << " CHECKING_MEMBERSHIP"; break; default: out << "???"; break; } } else out << " NO_MEMBERS_PRESENT"; } if ((stateMask & HOST_GROUP_STATE) && group) { HostGroupData *hostGroupData = getHostGroupData(ie, *group); if (hostGroupData) { switch (hostGroupData->state) { case IGMP_HGS_NON_MEMBER: out << " NON_MEMBER"; break; case IGMP_HGS_DELAYING_MEMBER: out << " DELAYING_MEMBER"; break; case IGMP_HGS_IDLE_MEMBER: out << " IDLE_MEMBER"; break; default: out << "???"; break; } } else out << " NON_MEMBER"; } } void TestIGMP::dumpMulticastGroups(const char* name, const char *ifname, IPv4AddressVector groups) { if (!out.is_open()) return; out << "t=" << simTime() << " " << node->getFullName() << "/" << ifname << ": " << name << " = <"; sort(groups.begin(), groups.end()); for (IPv4AddressVector::iterator it = groups.begin(); it != groups.end(); ++it) out << (it == groups.begin()?"":",") << *it; out << ">\n"; } } // namespace inet