/* * Copyright 2014-2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in reliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.pim.impl; import org.jboss.netty.util.Timeout; import org.jboss.netty.util.TimerTask; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.pim.PIMHello; import org.onlab.packet.pim.PIMHelloOption; import org.onosproject.net.ConnectPoint; import org.slf4j.Logger; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; /** * PIMNeighbor represents all the PIM routers that have sent us * hello messages, or that possibly have been statically configured. */ public class PIMNeighbor { private final Logger log = getLogger(getClass()); // The primary address of this PIM neighbor private IpAddress primaryAddr; // The MacAddress of this neighbor private MacAddress macAddress; // The ConnectPoint this PIM neighbor is connected to. private ConnectPoint connectPoint; // Is this neighbor us? private boolean isThisUs = false; // The option values this neighbor has sent us. private int priority = 0; private int genId = 0; private short holdtime = 0; // Is this pim neighbor the DR? private boolean isDr = false; // Timeout for this neighbor private volatile Timeout timeout; // A back pointer the neighbors list this neighbor belongs to. private PIMInterface pimInterface; /** * Construct this neighbor from the address and connect point. * * @param ipaddr IP Address of neighbor * @param macaddr MAC Address of the neighbor * @param pimInterface The PIMInterface of this neighbor */ public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, PIMInterface pimInterface) { this.macAddress = macaddr; this.primaryAddr = ipaddr; this.pimInterface = pimInterface; this.resetTimeout(); } /** * Get the primary address of this neighbor. * * @return the primary IP address. */ public IpAddress getPrimaryAddr() { return primaryAddr; } /** * Set the primary address of this neighbor. * * @param primaryAddr the address we'll use when sending hello messages */ public void setPrimaryAddr(IpAddress primaryAddr) { this.primaryAddr = primaryAddr; } /** * Get the priority this neighbor has advertised to us. * * @return the priority */ public int getPriority() { return priority; } /** * Set the priority for this neighbor. * * @param priority This neighbors priority. */ public void setPriority(int priority) { this.priority = priority; } /** * Get the generation ID. * * @return the generation ID. */ public int getGenId() { return genId; } /** * Set the generation ID. * * @param genId the generation ID. */ public void setGenId(int genId) { this.genId = genId; } /** * Get the holdtime for this neighbor. * * @return the holdtime */ public short getHoldtime() { return holdtime; } /** * Set the holdtime for this neighbor. * * @param holdtime the holdtime. */ public void setholdtime(short holdtime) { this.holdtime = holdtime; } /** * Is this neighbor the designated router on this connect point? * * @return true if so, false if not. */ public boolean isDr() { return isDr; } /** * Set this router as the designated router on this connect point. * * @param isDr True is this neighbor is the DR false otherwise */ public void setIsDr(boolean isDr) { this.isDr = isDr; } /** * The ConnectPoint this neighbor is connected to. * * @return the ConnectPoint */ public PIMInterface getPimInterface() { return pimInterface; } /** * We have received a fresh hello from this neighbor, now we need to process it. * Depending on the values received in the the hello options may force a * re-election process. * * We will also refresh the timeout for this neighbor. * * @param hello copy of the hello we'll be able to extract options from. */ public void refresh(PIMHello hello) { checkNotNull(hello); boolean reelect = false; for (PIMHelloOption opt : hello.getOptions().values()) { int len = opt.getOptLength(); ByteBuffer bb = ByteBuffer.wrap(opt.getValue()); switch (opt.getOptType()) { case PIMHelloOption.OPT_GENID: int newid = bb.getInt(); if (this.genId != newid) { // We have a newly rebooted neighbor, this is where we would // send them our joins. this.genId = newid; } break; case PIMHelloOption.OPT_PRIORITY: int newpri = bb.getInt(); if (this.priority != newpri) { // The priorities have changed. We may need to re-elect a new DR? if (this.isDr || pimInterface.getDesignatedRouter().getPriority() < priority) { reelect = true; } this.priority = newpri; } break; case PIMHelloOption.OPT_HOLDTIME: short holdtime = bb.getShort(); if (this.holdtime != holdtime) { this.holdtime = holdtime; if (holdtime == 0) { // We have a neighbor going down. We can remove all joins // we have learned from them. log.debug("PIM Neighbor has timed out: {}", this.primaryAddr.toString()); return; } } break; case PIMHelloOption.OPT_PRUNEDELAY: case PIMHelloOption.OPT_ADDRLIST: // TODO: implement prune delay and addr list. Fall through for now. default: log.debug("PIM Hello option type: {} not yet supported or unknown.", opt.getOptType()); break; } } if (reelect) { pimInterface.electDR(this); } // Reset the next timeout timer this.resetTimeout(); } /* --------------------------------------- Timer functions -------------------------- */ /** * Restart the timeout task for this neighbor. */ private void resetTimeout() { if (this.holdtime == 0) { // Prepare to die. log.debug("shutting down timer for nbr {}", this.primaryAddr.toString()); if (this.timeout != null) { this.timeout.cancel(); this.timeout = null; } return; } // Cancel the existing timeout and start a fresh new one. if (this.timeout != null) { this.timeout.cancel(); } this.timeout = PIMTimer.getTimer().newTimeout(new NeighborTimeoutTask(this), holdtime, TimeUnit.SECONDS); } /** * The task to run when a neighbor timeout expires. */ private final class NeighborTimeoutTask implements TimerTask { PIMNeighbor nbr; NeighborTimeoutTask(PIMNeighbor nbr) { this.nbr = nbr; } @Override public void run(Timeout timeout) throws Exception { log.debug("PIM Neighbor {} has timed out: ", nbr.toString()); nbr.pimInterface.removeNeighbor(nbr); } } /** * Stop the timeout timer. * * This happens when we remove the neighbor. */ private final void stopTimeout() { this.timeout.cancel(); this.timeout = null; } @Override public String toString() { String out = ""; if (this.isDr) { out += "*NBR:"; } else { out += "NBR:"; } out += "\tIP: " + this.primaryAddr.toString(); out += "\tPr: " + String.valueOf(this.priority); out += "\tHoldTime: " + String.valueOf(this.holdtime); out += "\tGenID: " + String.valueOf(this.genId) + "\n"; return out; } }