📄 phyint.c
字号:
}
void IGMPOtherQuerierTimer::action()
{
phyp->igmp_querier = 0;
phyp->verify_igmp_capabilities();
}
/* Send a Host Membership Query.
* It is a general query if the group is 0.
* Otherwise, it is a specific query sent in response
* to a receive leave group message.
* We're not quite to spec, as we don'y include a
* router alert option!
*/
void PhyInt::send_query(InAddr group)
{
InPkt *iphdr;
IgmpPkt *ig_pkt;
InAddr dest;
byte max_response;
int plen;
if (!IAmQuerier())
return;
// General queries
if (group == 0) {
dest = IGMP_ALL_SYSTEMS;
max_response = query_response_interval;
}
// Group-specific queries
else {
dest = group;
max_response = last_member_query_interval;
}
plen = sizeof(InPkt) + sizeof(IgmpPkt);
iphdr = (InPkt *) new byte[plen];
iphdr->i_vhlen = IHLVER;
iphdr->i_tos = 0;
iphdr->i_len = hton16(plen);
iphdr->i_id = 0;
iphdr->i_ffo = 0;
iphdr->i_ttl = 1;
iphdr->i_prot = PROT_IGMP;
iphdr->i_chksum = 0;
iphdr->i_src = hton32(my_addr);
iphdr->i_dest = hton32(dest);
iphdr->i_chksum = ~incksum((uns16 *) iphdr, sizeof(*iphdr));
ig_pkt = (IgmpPkt *) (iphdr+1);
ig_pkt->ig_type = IGMP_MEMBERSHIP_QUERY;
ig_pkt->ig_rsptim = max_response;
ig_pkt->ig_chksum = 0;
ig_pkt->ig_group = hton32(group);
ig_pkt->ig_chksum = ~incksum((uns16 *) ig_pkt, sizeof(*ig_pkt));
sys->sendpkt(iphdr, (int)index1());
delete [] ((byte *) iphdr);
}
/* Receive an IGMP packet. If it hasn't been
* associated with an interface by the kernel, we attempt
* to do so by looking at the source address.
* Then the packet is dispatched based on its IGMP
* type.
*/
void OSPF::rxigmp(int phyint, InPkt *pkt, int plen)
{
IgmpPkt *igmpkt;
int rcv_err;
int iphlen;
InAddr src;
PhyInt *phyp;
InAddr group;
rcv_err = 0;
iphlen = (pkt->i_vhlen & 0xf) << 2;
igmpkt = (IgmpPkt *) (((byte *) pkt) + iphlen);
src = ntoh32(pkt->i_src);
if (plen < ntoh16(pkt->i_len))
rcv_err = IGMP_RCV_SHORT;
else if (ntoh16(pkt->i_len) < (int) (iphlen + sizeof(IgmpPkt)))
rcv_err = IGMP_RCV_SHORT;
else if (incksum((uns16 *) igmpkt, sizeof(IgmpPkt)) != 0)
rcv_err = IGMP_RCV_XSUM;
else if (phyint == -1) {
SpfIfc *rip;
if ((rip = find_nbr_ifc(src)))
phyint = rip->if_phyint;
else
rcv_err = IGMP_RCV_NOIFC;
}
if (rcv_err) {
if (spflog(rcv_err, 3))
log(pkt);
return;
}
if (!(phyp = (PhyInt *)phyints.find((uns32) phyint, 0))) {
if (spflog(IGMP_RCV_NOIFC, 3))
log(pkt);
return;
}
group = ntoh32(igmpkt->ig_group);
if (spflog(IGMP_RCV, 1)) {
log(igmpkt->ig_type);
log(pkt);
}
// Dispatch on IGMP packet type
switch (igmpkt->ig_type) {
case IGMP_MEMBERSHIP_QUERY:
phyp->received_igmp_query(src, group, igmpkt->ig_rsptim);
break;
case IGMP_V1MEMBERSHIP_REPORT:
phyp->received_igmp_report(group, true);
break;
case IGMP_MEMBERSHIP_REPORT:
phyp->received_igmp_report(group, false);
break;
case IGMP_LEAVE_GROUP:
phyp->received_igmp_leave(group);
break;
default: // Unexpected packet type
break;
}
}
/* Constructor for a group membership entry.
*/
GroupMembership::GroupMembership(InAddr group, int phyint)
: AVLitem(group, phyint), leave_tim(this), exp_tim(this), v1_tim(this)
{
v1members = false;
}
/* Constructors for the group-membership timers.
*/
LeaveQueryTimer::LeaveQueryTimer(GroupMembership *g)
{
gentry = g;
}
GroupTimeoutTimer::GroupTimeoutTimer(GroupMembership *g)
{
gentry = g;
}
V1MemberTimer::V1MemberTimer(GroupMembership *g)
{
gentry = g;
}
/* Process a received IGMP Host Membership Query.
* Two things can result. We can let the send become
* the querier, as a result of having a lower IP address.
* Also, on queries for specific groups, we can
* reset the group membership timeout.
*/
void PhyInt::received_igmp_query(InAddr src, InAddr group, byte max_response)
{
int phyint=(int)index1();
GroupMembership *gentry=0;
// Ignore own queries
if (src == my_addr)
return;
// Is packet source the querier now?
if (!igmp_querier || src <= igmp_querier) {
stop_query_duties();
oqtim.stop();
if (igmp_querier != src && ospf->spflog(LOG_QUERIER, 4)) {
ospf->log(&src);
ospf->log(" Ifc ");
ospf->log(sys->phyname(phyint));
}
igmp_querier = src;
oqtim.start(other_querier_present_interval*Timer::SECOND, false);
}
if (!IAmQuerier() && group != 0 &&
(gentry = (GroupMembership *) ospf->local_membership.find(group,phyint))) {
int msec_tmo;
msec_tmo = last_member_query_count*max_response*100;
if (msec_tmo < gentry->exp_tim.milliseconds_to_firing()) {
gentry->exp_tim.stop();
gentry->exp_tim.start(msec_tmo);
}
}
}
/* Process a received Host Membership Report. If
* necessary, create a new group membership. In any case
* reset the membership timeouts.
*/
void PhyInt::received_igmp_report(InAddr group, bool v1)
{
GroupMembership *gentry;
int phyint = (int) index1();
if (LOCAL_SCOPE_MULTICAST(group))
return;
if (!(gentry = (GroupMembership *) ospf->local_membership.find(group,phyint))){
ospf->join_indication(group, phyint);
gentry = (GroupMembership *) ospf->local_membership.find(group,phyint);
}
if (v1) {
gentry->v1members = true;
gentry->v1_tim.stop();
gentry->v1_tim.start(group_membership_interval*Timer::SECOND, false);
}
gentry->leave_tim.stop();
gentry->exp_tim.stop();
gentry->exp_tim.start(group_membership_interval*Timer::SECOND, false);
}
/* Group membership has timed out. Destroy the membership entry
* and schedule a new group-membership-LSA.
*/
void GroupTimeoutTimer::action()
{
InAddr group=gentry->index1();
int phyint=gentry->index2();
gentry->leave_tim.stop();
gentry->exp_tim.stop();
gentry->v1_tim.stop();
ospf->leave_indication(group, phyint);
}
/* There are no longer an members speaking IGMP Version 1.
*/
void V1MemberTimer::action()
{
gentry->v1members = false;
}
/* Process a received leave message. If the membership
* exist, and isn't already in "Checking Membership" state,
* start the Last Member Query timer.
*/
void PhyInt::received_igmp_leave(InAddr group)
{
GroupMembership *gentry;
int phyint = (int) index1();
if (!IAmQuerier())
return;
if (!(gentry = (GroupMembership *) ospf->local_membership.find(group,phyint)))
return;
if (gentry->v1members)
return;
// Already doing leave processing?
if (gentry->leave_tim.is_running())
return;
// Start the leave timer, and send the first group-specific query
send_query(group);
if (last_member_query_count > 1) {
gentry->leave_tim.queries_remaining = last_member_query_count - 1;
gentry->leave_tim.start(last_member_query_interval*100, false);
}
}
/* Leave query timer. If more queries need to be sent, then do
* so. Otherwise set the expiration timer to something short.
*/
void LeaveQueryTimer::action()
{
PhyInt *phyp;
phyp = (PhyInt *)ospf->phyints.find(gentry->index2(), 0);
if (phyp)
phyp->send_query(gentry->index1());
if (--queries_remaining <= 0)
gentry->exp_tim.restart(phyp->last_member_query_interval*100);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -