/* * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance 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.onlab.packet.Ethernet; import org.onlab.packet.IPv4; import org.onlab.packet.Ip4Address; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.PIM; import org.onlab.packet.pim.PIMHello; import org.onlab.packet.pim.PIMHelloOption; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.net.ConnectPoint; import org.onosproject.net.host.InterfaceIpAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.HashMap; import java.util.Map; import static com.google.common.base.Preconditions.checkNotNull; /** * The PIM Interface is a wrapper around a ConnectPoint and used to provide * hello options values when "talking" with PIM other PIM routers. */ public class PIMInterface { private static Logger log = LoggerFactory.getLogger("PIMInterfaces"); // Interface from the interface subsystem private Interface theInterface; // The list of PIM neighbors adjacent to this interface private Map neighbors = new HashMap<>(); // The designatedRouter for this LAN private PIMNeighbor designatedRouter; // The priority we use on this ConnectPoint. private int priority = PIMHelloOption.DEFAULT_PRIORITY; // The holdtime we are sending out. private int holdtime = PIMHelloOption.DEFAULT_HOLDTIME; // Then generation ID we are sending out. 0 means we need to generate a new random ID private int genid = PIMHelloOption.DEFAULT_GENID; // Our default prune delay private int prunedelay = PIMHelloOption.DEFAULT_PRUNEDELAY; /** * Create a PIMInterface. * * @param intf the network interface configuration */ public PIMInterface(Interface intf) { log.debug("Adding an interface: " + intf.toString() + "\n"); this.theInterface = intf; // Send a hello to let our neighbors know we are alive sendHello(); } /** * Get the PIM Interface. * * @return the PIM Interface */ public Interface getInterface() { return theInterface; } /** * Getter for our IP address. * * @return our IP address. */ public IpAddress getIpAddress() { if (theInterface.ipAddresses().isEmpty()) { return null; } // We will just assume the first interface on the list IpAddress ipaddr = null; for (InterfaceIpAddress ifipaddr : theInterface.ipAddresses()) { ipaddr = ifipaddr.ipAddress(); break; } return ipaddr; } /** * Get our priority. * * @return our priority. */ public int getPriority() { return this.priority; } /** * Get the designated router on this connection. * * @return the PIMNeighbor representing the DR */ public PIMNeighbor getDesignatedRouter() { return designatedRouter; } /** * Are we the DR on this CP? * * @return true if we are, false if not */ public boolean areWeDr() { return (designatedRouter != null && designatedRouter.getPrimaryAddr().equals(this.getIpAddress())); } /** * Return a collection of PIM Neighbors. * * @return the collection of PIM Neighbors */ public Collection getNeighbors() { return this.neighbors.values(); } /** * Find the neighbor with the given IP address on this CP. * * @param ipaddr the IP address of the neighbor we are interested in * @return the pim neighbor if it exists */ public PIMNeighbor findNeighbor(IpAddress ipaddr) { PIMNeighbor nbr = neighbors.get(ipaddr); return nbr; } /** * Add a new PIM neighbor to this list. * * @param nbr the neighbor to be added. */ public void addNeighbor(PIMNeighbor nbr) { if (neighbors.containsKey(nbr.getPrimaryAddr())) { log.debug("We are adding a neighbor that already exists: {}", nbr.toString()); neighbors.remove(nbr.getPrimaryAddr()); } neighbors.put(nbr.getPrimaryAddr(), nbr); } /** * Remove the neighbor from our neighbor list. * * @param ipaddr the IP address of the neighbor to remove */ public void removeNeighbor(IpAddress ipaddr) { if (neighbors.containsKey(ipaddr)) { neighbors.remove(ipaddr); } this.electDR(); } /** * Remove the given neighbor from the neighbor list. * * @param nbr the nbr to be removed. */ public void removeNeighbor(PIMNeighbor nbr) { neighbors.remove(nbr.getPrimaryAddr(), nbr); this.electDR(); } /** * Elect a new DR on this ConnectPoint. * * @return the PIM Neighbor that wins */ public PIMNeighbor electDR() { for (PIMNeighbor nbr : this.neighbors.values()) { if (this.designatedRouter == null) { this.designatedRouter = nbr; continue; } if (nbr.getPriority() > this.designatedRouter.getPriority()) { this.designatedRouter = nbr; continue; } // We could sort in ascending order if (this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) { this.designatedRouter = nbr; continue; } } return this.designatedRouter; } /** * Elect a new DR given the new neighbor. * * @param nbr the new neighbor to use in DR election. * @return the PIM Neighbor that wins DR election */ public PIMNeighbor electDR(PIMNeighbor nbr) { // Make sure I have if (this.designatedRouter == null || this.designatedRouter.getPriority() < nbr.getPriority() || this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) { this.designatedRouter = nbr; } return this.designatedRouter; } /** * Find or create a pim neighbor with a given ip address and connect point. * * @param ipaddr of the pim neighbor * @param mac The mac address of our sending neighbor * @return an existing or new PIM neighbor */ public PIMNeighbor findOrCreate(IpAddress ipaddr, MacAddress mac) { PIMNeighbor nbr = this.findNeighbor(ipaddr); if (nbr == null) { nbr = new PIMNeighbor(ipaddr, mac, this); this.addNeighbor(nbr); this.electDR(nbr); } return nbr; } /** * Process a hello packet received on this Interface. * * @param ethPkt the ethernet packet containing the hello message * @param cp the ConnectPoint of this interface */ public void processHello(Ethernet ethPkt, ConnectPoint cp) { checkNotNull(ethPkt); checkNotNull(cp); MacAddress srcmac = ethPkt.getSourceMAC(); IPv4 ip = (IPv4) ethPkt.getPayload(); Ip4Address srcip = Ip4Address.valueOf(ip.getSourceAddress()); PIM pim = (PIM) ip.getPayload(); checkNotNull(pim); PIMHello hello = (PIMHello) pim.getPayload(); checkNotNull(hello); PIMNeighbor nbr = this.findOrCreate(srcip, srcmac); if (nbr == null) { log.error("Could not create a neighbor for: {1}", srcip.toString()); return; } ConnectPoint icp = theInterface.connectPoint(); checkNotNull(icp); if (!cp.equals(icp)) { log.error("PIM Hello message received from {} on incorrect interface {}", nbr.getPrimaryAddr(), this.toString()); return; } nbr.refresh(hello); } /** * Send a hello packet from this interface. */ public void sendHello() { PIM pim = new PIM(); PIMHello hello = new PIMHello(); // Create a PIM Hello pim = new PIM(); pim.setVersion((byte) 2); pim.setPIMType((byte) PIM.TYPE_HELLO); pim.setChecksum((short) 0); hello = new PIMHello(); hello.createDefaultOptions(); pim.setPayload(hello); hello.setParent(pim); log.debug("Sending hello: \n"); PIMPacketHandler.getInstance().sendPacket(pim, this); } /** * prints the connectPointNeighbors list with each neighbor list. * * @return string of neighbors. */ public String printNeighbors() { String out = "PIM Neighbors Table: \n"; for (PIMNeighbor nbr : this.neighbors.values()) { out += "\t" + nbr.toString(); } return out; } @Override public String toString() { IpAddress ipaddr = this.getIpAddress(); String out = "PIM Neighbors: "; if (ipaddr != null) { out += "IP: " + ipaddr.toString(); } else { out += "IP: *Null*"; } out += "\tPR: " + String.valueOf(this.priority) + "\n"; return out; } }