blob: 1185e6898ea2fa141644b9074a5dffe8ce30c7bd [file] [log] [blame]
Rusty Eddy95421642015-10-21 17:22:13 -07001/*
Rusty Eddybcad55b2016-02-11 18:56:09 -08002 * Copyright 2015, 2016 Open Networking Laboratory
Rusty Eddy95421642015-10-21 17:22:13 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.pim.impl;
17
Jonathan Hart7f4bc522016-02-20 11:32:43 -080018import com.google.common.collect.ImmutableList;
Rusty Eddy95421642015-10-21 17:22:13 -070019import org.onlab.packet.Ethernet;
20import org.onlab.packet.IPv4;
Rusty Eddy4d5a92f2016-01-25 17:12:14 -080021import org.onlab.packet.Ip4Address;
Rusty Eddy95421642015-10-21 17:22:13 -070022import org.onlab.packet.IpAddress;
Rusty Eddybcad55b2016-02-11 18:56:09 -080023import org.onlab.packet.IpPrefix;
Rusty Eddy95421642015-10-21 17:22:13 -070024import org.onlab.packet.MacAddress;
25import org.onlab.packet.PIM;
Jonathan Hart00cddda2016-02-16 10:30:37 -080026import org.onlab.packet.pim.PIMAddrUnicast;
Rusty Eddy95421642015-10-21 17:22:13 -070027import org.onlab.packet.pim.PIMHello;
28import org.onlab.packet.pim.PIMHelloOption;
Rusty Eddybcad55b2016-02-11 18:56:09 -080029import org.onlab.packet.pim.PIMJoinPrune;
30import org.onlab.packet.pim.PIMJoinPruneGroup;
Rusty Eddy95421642015-10-21 17:22:13 -070031import org.onosproject.incubator.net.intf.Interface;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080032import org.onosproject.net.flow.DefaultTrafficTreatment;
33import org.onosproject.net.flow.TrafficTreatment;
Rusty Eddy95421642015-10-21 17:22:13 -070034import org.onosproject.net.host.InterfaceIpAddress;
Jonathan Hart00cddda2016-02-16 10:30:37 -080035import org.onosproject.net.mcast.McastRoute;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080036import org.onosproject.net.packet.DefaultOutboundPacket;
37import org.onosproject.net.packet.PacketService;
Rusty Eddy95421642015-10-21 17:22:13 -070038import org.slf4j.Logger;
Rusty Eddy95421642015-10-21 17:22:13 -070039
Jonathan Hart36fd31e2016-01-28 15:55:31 -080040import java.nio.ByteBuffer;
Jonathan Hart54119bb2016-02-06 18:48:27 -080041import java.util.Collection;
Jonathan Hart00cddda2016-02-16 10:30:37 -080042import java.util.List;
Rusty Eddy390498d2016-01-15 19:21:32 -080043import java.util.Map;
Jonathan Hart5af5f142016-01-28 18:45:27 -080044import java.util.Random;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080045import java.util.Set;
Jonathan Hart00cddda2016-02-16 10:30:37 -080046import java.util.concurrent.ConcurrentHashMap;
Jonathan Hart6be70952016-02-12 21:11:26 -080047import java.util.concurrent.TimeUnit;
Jonathan Hart54119bb2016-02-06 18:48:27 -080048import java.util.stream.Collectors;
Rusty Eddy95421642015-10-21 17:22:13 -070049
Jonathan Hart5af5f142016-01-28 18:45:27 -080050import static com.google.common.base.Preconditions.checkArgument;
Rusty Eddy390498d2016-01-15 19:21:32 -080051import static com.google.common.base.Preconditions.checkNotNull;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080052import static org.slf4j.LoggerFactory.getLogger;
Rusty Eddy95421642015-10-21 17:22:13 -070053
54/**
Ray Milkeya059a702016-01-12 11:10:33 -080055 * PIM Interface represents an ONOS Interface with IP and MAC addresses for
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080056 * a given ConnectPoint.
Rusty Eddy95421642015-10-21 17:22:13 -070057 */
Jonathan Hartfbfe2a82016-03-29 11:36:33 -070058public final class PimInterface {
Rusty Eddy95421642015-10-21 17:22:13 -070059
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080060 private final Logger log = getLogger(getClass());
Rusty Eddy95421642015-10-21 17:22:13 -070061
Jonathan Hart00cddda2016-02-16 10:30:37 -080062 private static final int JOIN_PERIOD = 60;
63 private static final double HOLD_TIME_MULTIPLIER = 3.5;
64
Jonathan Hart36fd31e2016-01-28 15:55:31 -080065 private final PacketService packetService;
66
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080067 private Interface onosInterface;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080068 private final TrafficTreatment outputTreatment;
Rusty Eddy95421642015-10-21 17:22:13 -070069
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080070 // Our hello opt holdtime
71 private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
Rusty Eddy95421642015-10-21 17:22:13 -070072
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080073 // Our hello opt prune delay
74 private int pruneDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
Rusty Eddy95421642015-10-21 17:22:13 -070075
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080076 // Neighbor priority
77 private int priority = PIMHelloOption.DEFAULT_PRIORITY;
Rusty Eddy95421642015-10-21 17:22:13 -070078
Jonathan Hart6be70952016-02-12 21:11:26 -080079 private final int helloInterval;
80
81 private long lastHello;
82
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080083 // Our current genid
Jonathan Hart5af5f142016-01-28 18:45:27 -080084 private final int generationId;
Rusty Eddy95421642015-10-21 17:22:13 -070085
Rusty Eddy390498d2016-01-15 19:21:32 -080086 // The IP address of the DR
Jonathan Hart5af5f142016-01-28 18:45:27 -080087 private IpAddress drIpaddress;
Rusty Eddy390498d2016-01-15 19:21:32 -080088
89 // A map of all our PIM neighbors keyed on our neighbors IP address
Jonathan Hartfbfe2a82016-03-29 11:36:33 -070090 private Map<IpAddress, PimNeighbor> pimNeighbors = new ConcurrentHashMap<>();
Rusty Eddy390498d2016-01-15 19:21:32 -080091
Jonathan Hart00cddda2016-02-16 10:30:37 -080092 private Map<McastRoute, RouteData> routes = new ConcurrentHashMap<>();
93
Rusty Eddy95421642015-10-21 17:22:13 -070094 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080095 * Create a PIMInterface from an ONOS Interface.
Charles Chan30ba4002015-11-05 14:45:16 -080096 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080097 * @param intf the ONOS Interface.
Jonathan Hart5af5f142016-01-28 18:45:27 -080098 * @param holdTime hold time
99 * @param priority priority
100 * @param propagationDelay propagation delay
101 * @param overrideInterval override interval
102 * @param packetService reference to the packet service
Rusty Eddy95421642015-10-21 17:22:13 -0700103 */
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700104 private PimInterface(Interface intf,
Jonathan Hart6be70952016-02-12 21:11:26 -0800105 int helloInterval,
106 short holdTime,
107 int priority,
108 short propagationDelay,
109 short overrideInterval,
110 PacketService packetService) {
Jonathan Hart5af5f142016-01-28 18:45:27 -0800111
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800112 onosInterface = intf;
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800113 outputTreatment = createOutputTreatment();
Jonathan Hart6be70952016-02-12 21:11:26 -0800114 this.helloInterval = helloInterval;
Jonathan Hart5af5f142016-01-28 18:45:27 -0800115 this.holdtime = holdTime;
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800116 this.packetService = packetService;
Rusty Eddy390498d2016-01-15 19:21:32 -0800117 IpAddress ourIp = getIpAddress();
118 MacAddress mac = intf.mac();
119
Jonathan Hart6be70952016-02-12 21:11:26 -0800120 lastHello = 0;
121
Jonathan Hart5af5f142016-01-28 18:45:27 -0800122 generationId = new Random().nextInt();
123
Rusty Eddy390498d2016-01-15 19:21:32 -0800124 // Create a PIM Neighbor to represent ourselves for DR election.
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700125 PimNeighbor us = new PimNeighbor(ourIp, mac, holdTime, 0, priority, generationId);
Rusty Eddy390498d2016-01-15 19:21:32 -0800126
127 pimNeighbors.put(ourIp, us);
128 drIpaddress = ourIp;
Rusty Eddy95421642015-10-21 17:22:13 -0700129 }
130
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800131 private TrafficTreatment createOutputTreatment() {
132 return DefaultTrafficTreatment.builder()
133 .setOutput(onosInterface.connectPoint().port())
134 .build();
135 }
136
Rusty Eddy95421642015-10-21 17:22:13 -0700137 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800138 * Return the ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -0700139 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800140 * @return ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -0700141 */
142 public Interface getInterface() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800143 return onosInterface;
144
Rusty Eddy95421642015-10-21 17:22:13 -0700145 }
146
147 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800148 * Set the ONOS Interface, it will override a previous value.
Rusty Eddy95421642015-10-21 17:22:13 -0700149 *
Jian Lidfba7392016-01-22 16:46:58 -0800150 * @param intf ONOS Interface
151 * @return PIM interface instance
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800152 */
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700153 public PimInterface setInterface(Interface intf) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800154 onosInterface = intf;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800155 return this;
156 }
157
158 /**
159 * Get the set of IP Addresses associated with this interface.
160 *
161 * @return a set of Ip Addresses on this interface
162 */
Jonathan Hart00cddda2016-02-16 10:30:37 -0800163 public List<InterfaceIpAddress> getIpAddresses() {
164 return onosInterface.ipAddressesList();
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800165 }
166
167 /**
168 * Return a single "best" IP address.
169 *
170 * @return the choosen IP address or null if none
Rusty Eddy95421642015-10-21 17:22:13 -0700171 */
172 public IpAddress getIpAddress() {
Jonathan Hart00cddda2016-02-16 10:30:37 -0800173 if (onosInterface.ipAddressesList().isEmpty()) {
Rusty Eddy95421642015-10-21 17:22:13 -0700174 return null;
175 }
176
Rusty Eddy95421642015-10-21 17:22:13 -0700177 IpAddress ipaddr = null;
Jonathan Hart00cddda2016-02-16 10:30:37 -0800178 for (InterfaceIpAddress ifipaddr : onosInterface.ipAddressesList()) {
Rusty Eddy95421642015-10-21 17:22:13 -0700179 ipaddr = ifipaddr.ipAddress();
180 break;
181 }
182 return ipaddr;
183 }
184
185 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800186 * Get the holdtime.
Rusty Eddy95421642015-10-21 17:22:13 -0700187 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800188 * @return the holdtime
189 */
190 public short getHoldtime() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800191 return holdtime;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800192 }
193
194 /**
195 * Get the prune delay.
196 *
197 * @return The prune delay
198 */
199 public int getPruneDelay() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800200 return pruneDelay;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800201 }
202
203 /**
204 * Get our hello priority.
205 *
206 * @return our priority
Rusty Eddy95421642015-10-21 17:22:13 -0700207 */
208 public int getPriority() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800209 return priority;
Rusty Eddy95421642015-10-21 17:22:13 -0700210 }
211
212 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800213 * Get our generation ID.
Rusty Eddy95421642015-10-21 17:22:13 -0700214 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800215 * @return our generation ID
Rusty Eddy95421642015-10-21 17:22:13 -0700216 */
Jonathan Hart5af5f142016-01-28 18:45:27 -0800217 public int getGenerationId() {
218 return generationId;
Rusty Eddy95421642015-10-21 17:22:13 -0700219 }
220
221 /**
Jonathan Hart54119bb2016-02-06 18:48:27 -0800222 * Gets the neighbors seen on this interface.
223 *
224 * @return PIM neighbors
225 */
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700226 public Collection<PimNeighbor> getNeighbors() {
Jonathan Hart7f4bc522016-02-20 11:32:43 -0800227 return ImmutableList.copyOf(pimNeighbors.values());
Jonathan Hart54119bb2016-02-06 18:48:27 -0800228 }
229
Jonathan Hart00cddda2016-02-16 10:30:37 -0800230 public Collection<McastRoute> getRoutes() {
231 return routes.keySet();
232 }
233
Jonathan Hart54119bb2016-02-06 18:48:27 -0800234 /**
235 * Checks whether any of our neighbors have expired, and cleans up their
236 * state if they have.
237 */
238 public void checkNeighborTimeouts() {
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700239 Set<PimNeighbor> expired = pimNeighbors.values().stream()
Jonathan Hart54119bb2016-02-06 18:48:27 -0800240 // Don't time ourselves out!
241 .filter(neighbor -> !neighbor.ipAddress().equals(getIpAddress()))
242 .filter(neighbor -> neighbor.isExpired())
243 .collect(Collectors.toSet());
244
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700245 for (PimNeighbor neighbor : expired) {
Jonathan Hart54119bb2016-02-06 18:48:27 -0800246 log.info("Timing out neighbor {}", neighbor);
247 pimNeighbors.remove(neighbor.ipAddress(), neighbor);
248 }
249 }
250
251 /**
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800252 * Multicast a hello message out our interface. This hello message is sent
253 * periodically during the normal PIM Neighbor refresh time, as well as a
254 * result of a newly created interface.
255 */
256 public void sendHello() {
Jonathan Hart6be70952016-02-12 21:11:26 -0800257 if (lastHello + TimeUnit.SECONDS.toMillis(helloInterval) >
258 System.currentTimeMillis()) {
259 return;
260 }
261
262 lastHello = System.currentTimeMillis();
263
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800264 // Create the base PIM Packet and mark it a hello packet
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700265 PimPacket pimPacket = new PimPacket(PIM.TYPE_HELLO);
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800266
267 // We need to set the source MAC and IPv4 addresses
268 pimPacket.setSrcMacAddr(onosInterface.mac());
269 pimPacket.setSrcIpAddress(Ip4Address.valueOf(getIpAddress().toOctets()));
270
271 // Create the hello message with options
272 PIMHello hello = new PIMHello();
273 hello.createDefaultOptions();
Jonathan Hart5af5f142016-01-28 18:45:27 -0800274 hello.addOption(PIMHelloOption.createHoldTime(holdtime));
275 hello.addOption(PIMHelloOption.createPriority(priority));
276 hello.addOption(PIMHelloOption.createGenID(generationId));
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800277
278 // Now set the hello option payload
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700279 pimPacket.setPimPayload(hello);
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800280
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800281 packetService.emit(new DefaultOutboundPacket(
282 onosInterface.connectPoint().deviceId(),
283 outputTreatment,
284 ByteBuffer.wrap(pimPacket.getEthernet().serialize())));
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800285 }
286
287 /**
Rusty Eddy390498d2016-01-15 19:21:32 -0800288 * Process an incoming PIM Hello message. There are a few things going on in
289 * this method:
290 * <ul>
291 * <li>We <em>may</em> have to create a new neighbor if one does not already exist</li>
292 * <li>We <em>may</em> need to re-elect a new DR if new information is received</li>
293 * <li>We <em>may</em> need to send an existing neighbor all joins if the genid changed</li>
Jonathan Hart54119bb2016-02-06 18:48:27 -0800294 * <li>We will refresh the neighbor's timestamp</li>
Rusty Eddy390498d2016-01-15 19:21:32 -0800295 * </ul>
Rusty Eddy95421642015-10-21 17:22:13 -0700296 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800297 * @param ethPkt the Ethernet packet header
Rusty Eddy95421642015-10-21 17:22:13 -0700298 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800299 public void processHello(Ethernet ethPkt) {
Jonathan Hart7f4bc522016-02-20 11:32:43 -0800300 if (log.isTraceEnabled()) {
301 log.trace("Received a PIM hello packet");
302 }
Rusty Eddy95421642015-10-21 17:22:13 -0700303
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800304 // We'll need to save our neighbors MAC address
305 MacAddress nbrmac = ethPkt.getSourceMAC();
Rusty Eddy95421642015-10-21 17:22:13 -0700306
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800307 // And we'll need to save neighbors IP Address.
308 IPv4 iphdr = (IPv4) ethPkt.getPayload();
309 IpAddress srcip = IpAddress.valueOf(iphdr.getSourceAddress());
Rusty Eddy95421642015-10-21 17:22:13 -0700310
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800311 PIM pimhdr = (PIM) iphdr.getPayload();
312 if (pimhdr.getPimMsgType() != PIM.TYPE_HELLO) {
313 log.error("process Hello has received a non hello packet type: " + pimhdr.getPimMsgType());
Rusty Eddy95421642015-10-21 17:22:13 -0700314 return;
315 }
316
Rusty Eddy390498d2016-01-15 19:21:32 -0800317 // get the DR values for later calculation
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700318 PimNeighbor dr = pimNeighbors.get(drIpaddress);
Rusty Eddy390498d2016-01-15 19:21:32 -0800319 checkNotNull(dr);
320
321 IpAddress drip = drIpaddress;
Jonathan Hart54119bb2016-02-06 18:48:27 -0800322 int drpri = dr.priority();
Rusty Eddy390498d2016-01-15 19:21:32 -0800323
324 // Assume we do not need to run a DR election
325 boolean reElectDr = false;
326 boolean genidChanged = false;
327
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800328 PIMHello hello = (PIMHello) pimhdr.getPayload();
329
Rusty Eddy390498d2016-01-15 19:21:32 -0800330 // Determine if we already have a PIMNeighbor
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700331 PimNeighbor nbr = pimNeighbors.getOrDefault(srcip, null);
332 PimNeighbor newNbr = PimNeighbor.createPimNeighbor(srcip, nbrmac, hello.getOptions().values());
Jonathan Hart54119bb2016-02-06 18:48:27 -0800333
Rusty Eddy390498d2016-01-15 19:21:32 -0800334 if (nbr == null) {
Jonathan Hart54119bb2016-02-06 18:48:27 -0800335 pimNeighbors.putIfAbsent(srcip, newNbr);
336 nbr = newNbr;
337 } else if (!nbr.equals(newNbr)) {
338 if (newNbr.holdtime() == 0) {
339 // Neighbor has shut down. Remove them and clean up
340 pimNeighbors.remove(srcip, nbr);
341 return;
342 } else {
343 // Neighbor has changed one of their options.
344 pimNeighbors.put(srcip, newNbr);
345 nbr = newNbr;
Rusty Eddy390498d2016-01-15 19:21:32 -0800346 }
Rusty Eddy95421642015-10-21 17:22:13 -0700347 }
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800348
Jonathan Hart54119bb2016-02-06 18:48:27 -0800349 // Refresh this neighbor's timestamp
Rusty Eddy390498d2016-01-15 19:21:32 -0800350 nbr.refreshTimestamp();
351
352 /*
Jonathan Hart5af5f142016-01-28 18:45:27 -0800353 * the election method will first determine if an election
Rusty Eddy390498d2016-01-15 19:21:32 -0800354 * needs to be run, if so it will run the election. The
355 * IP address of the DR will be returned. If the IP address
356 * of the DR is different from what we already have we know a
357 * new DR has been elected.
358 */
359 IpAddress electedIp = election(nbr, drip, drpri);
360 if (!drip.equals(electedIp)) {
361 // we have a new DR.
362 drIpaddress = electedIp;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800363 }
Rusty Eddy95421642015-10-21 17:22:13 -0700364 }
365
Rusty Eddy390498d2016-01-15 19:21:32 -0800366 // Run an election if we need to. Return the elected IP address.
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700367 private IpAddress election(PimNeighbor nbr, IpAddress drIp, int drPriority) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800368
Jonathan Hart54119bb2016-02-06 18:48:27 -0800369 IpAddress nbrIp = nbr.ipAddress();
370 if (nbr.priority() > drPriority) {
Jonathan Hart5af5f142016-01-28 18:45:27 -0800371 return nbrIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800372 }
373
Jonathan Hart5af5f142016-01-28 18:45:27 -0800374 if (nbrIp.compareTo(drIp) > 0) {
375 return nbrIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800376 }
Jonathan Hart5af5f142016-01-28 18:45:27 -0800377 return drIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800378 }
379
Rusty Eddy95421642015-10-21 17:22:13 -0700380 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800381 * Process an incoming PIM JoinPrune message.
Rusty Eddy95421642015-10-21 17:22:13 -0700382 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800383 * @param ethPkt the Ethernet packet header.
Rusty Eddy95421642015-10-21 17:22:13 -0700384 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800385 public void processJoinPrune(Ethernet ethPkt) {
Rusty Eddybcad55b2016-02-11 18:56:09 -0800386
387 IPv4 ip = (IPv4) ethPkt.getPayload();
388 checkNotNull(ip);
389
390 PIM pim = (PIM) ip.getPayload();
391 checkNotNull(pim);
392
393 PIMJoinPrune jpHdr = (PIMJoinPrune) pim.getPayload();
394 checkNotNull(jpHdr);
395
396 /*
397 * The Join/Prune messages are grouped by Group address. We'll walk each group address
398 * where we will possibly have to walk a list of source address for the joins and prunes.
399 */
400 Collection<PIMJoinPruneGroup> jpgs = jpHdr.getJoinPrunes();
401 for (PIMJoinPruneGroup jpg : jpgs) {
402 IpPrefix gpfx = jpg.getGroup();
403
404 // Walk the joins first.
405 for (IpPrefix spfx : jpg.getJoins().values()) {
406
407 // We may need
408
409
410 }
411
412 for (IpPrefix spfx : jpg.getPrunes().values()) {
413
414 // TODO: this is where we many need to remove multi-cast state and possibly intents.
415
416 }
417 }
418
Rusty Eddy95421642015-10-21 17:22:13 -0700419 }
Jonathan Hart5af5f142016-01-28 18:45:27 -0800420
Jonathan Hart00cddda2016-02-16 10:30:37 -0800421 public void addRoute(McastRoute route, IpAddress nextHop, MacAddress nextHopMac) {
422 RouteData data = new RouteData(nextHop, nextHopMac);
423 routes.put(route, data);
424
425 sendJoinPrune(route, data, true);
426 }
427
428 public void removeRoute(McastRoute route) {
429 RouteData data = routes.remove(route);
430
431 if (data != null) {
432 sendJoinPrune(route, data, false);
433 }
434 }
435
436 public void sendJoins() {
437 routes.entrySet().forEach(entry -> {
438 if (entry.getValue().timestamp + TimeUnit.SECONDS.toMillis(JOIN_PERIOD) >
439 System.currentTimeMillis()) {
440 return;
441 }
442
443 sendJoinPrune(entry.getKey(), entry.getValue(), true);
444 });
445 }
446
447 private void sendJoinPrune(McastRoute route, RouteData data, boolean join) {
448 PIMJoinPrune jp = new PIMJoinPrune();
449
450 jp.addJoinPrune(route.source().toIpPrefix(), route.group().toIpPrefix(), join);
451 jp.setHoldTime(join ? (short) Math.floor(JOIN_PERIOD * HOLD_TIME_MULTIPLIER) : 0);
452 jp.setUpstreamAddr(new PIMAddrUnicast(data.ipAddress.toString()));
453
454 PIM pim = new PIM();
455 pim.setPIMType(PIM.TYPE_JOIN_PRUNE_REQUEST);
456 pim.setPayload(jp);
457
458 IPv4 ipv4 = new IPv4();
459 ipv4.setDestinationAddress(PIM.PIM_ADDRESS.getIp4Address().toInt());
460 ipv4.setSourceAddress(getIpAddress().getIp4Address().toInt());
461 ipv4.setProtocol(IPv4.PROTOCOL_PIM);
462 ipv4.setTtl((byte) 1);
463 ipv4.setDiffServ((byte) 0xc0);
464 ipv4.setPayload(pim);
465
466 Ethernet eth = new Ethernet();
467 eth.setSourceMACAddress(onosInterface.mac());
468 eth.setDestinationMACAddress(MacAddress.valueOf("01:00:5E:00:00:0d"));
469 eth.setEtherType(Ethernet.TYPE_IPV4);
470 eth.setPayload(ipv4);
471
472 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
473 .setOutput(onosInterface.connectPoint().port())
474 .build();
475
476 packetService.emit(new DefaultOutboundPacket(onosInterface.connectPoint().deviceId(),
477 treatment, ByteBuffer.wrap(eth.serialize())));
478
479 data.timestamp = System.currentTimeMillis();
480 }
481
Jonathan Hart5af5f142016-01-28 18:45:27 -0800482 /**
483 * Returns a builder for a PIM interface.
484 *
485 * @return PIM interface builder
486 */
487 public static Builder builder() {
488 return new Builder();
489 }
490
491 /**
492 * Builder for a PIM interface.
493 */
494 public static class Builder {
495 private Interface intf;
496 private PacketService packetService;
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700497 private int helloInterval = PimInterfaceManager.DEFAULT_HELLO_INTERVAL;
Jonathan Hart5af5f142016-01-28 18:45:27 -0800498 private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
499 private int priority = PIMHelloOption.DEFAULT_PRIORITY;
500 private short propagationDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
501 private short overrideInterval = PIMHelloOption.DEFAULT_OVERRIDEINTERVAL;
502
503 /**
504 * Uses the specified ONOS interface.
505 *
506 * @param intf ONOS interface
507 * @return this PIM interface builder
508 */
509 public Builder withInterface(Interface intf) {
510 this.intf = checkNotNull(intf);
511 return this;
512 }
513
514 /**
515 * Sets the reference to the packet service.
516 *
517 * @param packetService packet service
518 * @return this PIM interface builder
519 */
520 public Builder withPacketService(PacketService packetService) {
521 this.packetService = checkNotNull(packetService);
522 return this;
523 }
524
525 /**
Jonathan Hart6be70952016-02-12 21:11:26 -0800526 * Users the specified hello interval.
527 *
528 * @param helloInterval hello interval in seconds
529 * @return this PIM interface builder
530 */
531 public Builder withHelloInterval(int helloInterval) {
532 this.helloInterval = helloInterval;
533 return this;
534 }
535
536 /**
Jonathan Hart5af5f142016-01-28 18:45:27 -0800537 * Uses the specified hold time.
538 *
539 * @param holdTime hold time in seconds
540 * @return this PIM interface builder
541 */
542 public Builder withHoldTime(short holdTime) {
543 this.holdtime = holdTime;
544 return this;
545 }
546
547 /**
548 * Uses the specified DR priority.
549 *
550 * @param priority DR priority
551 * @return this PIM interface builder
552 */
553 public Builder withPriority(int priority) {
554 this.priority = priority;
555 return this;
556 }
557
558 /**
559 * Uses the specified propagation delay.
560 *
561 * @param propagationDelay propagation delay in ms
562 * @return this PIM interface builder
563 */
564 public Builder withPropagationDelay(short propagationDelay) {
565 this.propagationDelay = propagationDelay;
566 return this;
567 }
568
569 /**
570 * Uses the specified override interval.
571 *
572 * @param overrideInterval override interval in ms
573 * @return this PIM interface builder
574 */
575 public Builder withOverrideInterval(short overrideInterval) {
576 this.overrideInterval = overrideInterval;
577 return this;
578 }
579
580 /**
581 * Builds the PIM interface.
582 *
583 * @return PIM interface
584 */
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700585 public PimInterface build() {
Jonathan Hart5af5f142016-01-28 18:45:27 -0800586 checkArgument(intf != null, "Must provide an interface");
587 checkArgument(packetService != null, "Must provide a packet service");
588
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700589 return new PimInterface(intf, helloInterval, holdtime, priority,
Jonathan Hart6be70952016-02-12 21:11:26 -0800590 propagationDelay, overrideInterval, packetService);
Jonathan Hart5af5f142016-01-28 18:45:27 -0800591 }
592
593 }
Jonathan Hart00cddda2016-02-16 10:30:37 -0800594
595 private static class RouteData {
596 public final IpAddress ipAddress;
597 public final MacAddress macAddress;
598 public long timestamp;
599
600 public RouteData(IpAddress ip, MacAddress mac) {
601 this.ipAddress = ip;
602 this.macAddress = mac;
603 timestamp = System.currentTimeMillis();
604 }
605 }
Rusty Eddy95421642015-10-21 17:22:13 -0700606}