blob: 1bedd9d6cb4891ed6da18b6ac4cfc9c799db0114 [file] [log] [blame]
Rusty Eddy95421642015-10-21 17:22:13 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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
18import org.onlab.packet.Ethernet;
19import org.onlab.packet.IPv4;
Rusty Eddy4d5a92f2016-01-25 17:12:14 -080020import org.onlab.packet.Ip4Address;
Rusty Eddy95421642015-10-21 17:22:13 -070021import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.PIM;
24import org.onlab.packet.pim.PIMHello;
25import org.onlab.packet.pim.PIMHelloOption;
26import org.onosproject.incubator.net.intf.Interface;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080027import org.onosproject.net.flow.DefaultTrafficTreatment;
28import org.onosproject.net.flow.TrafficTreatment;
Rusty Eddy95421642015-10-21 17:22:13 -070029import org.onosproject.net.host.InterfaceIpAddress;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080030import org.onosproject.net.packet.DefaultOutboundPacket;
31import org.onosproject.net.packet.PacketService;
Rusty Eddy95421642015-10-21 17:22:13 -070032import org.slf4j.Logger;
Rusty Eddy95421642015-10-21 17:22:13 -070033
Jonathan Hart36fd31e2016-01-28 15:55:31 -080034import java.nio.ByteBuffer;
Jonathan Hart54119bb2016-02-06 18:48:27 -080035import java.util.Collection;
Rusty Eddy390498d2016-01-15 19:21:32 -080036import java.util.HashMap;
37import java.util.Map;
Jonathan Hart5af5f142016-01-28 18:45:27 -080038import java.util.Random;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080039import java.util.Set;
Jonathan Hart54119bb2016-02-06 18:48:27 -080040import java.util.stream.Collectors;
Rusty Eddy95421642015-10-21 17:22:13 -070041
Jonathan Hart5af5f142016-01-28 18:45:27 -080042import static com.google.common.base.Preconditions.checkArgument;
Rusty Eddy390498d2016-01-15 19:21:32 -080043import static com.google.common.base.Preconditions.checkNotNull;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080044import static org.slf4j.LoggerFactory.getLogger;
Rusty Eddy95421642015-10-21 17:22:13 -070045
46/**
Ray Milkeya059a702016-01-12 11:10:33 -080047 * PIM Interface represents an ONOS Interface with IP and MAC addresses for
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080048 * a given ConnectPoint.
Rusty Eddy95421642015-10-21 17:22:13 -070049 */
Jonathan Hart5af5f142016-01-28 18:45:27 -080050public final class PIMInterface {
Rusty Eddy95421642015-10-21 17:22:13 -070051
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080052 private final Logger log = getLogger(getClass());
Rusty Eddy95421642015-10-21 17:22:13 -070053
Jonathan Hart36fd31e2016-01-28 15:55:31 -080054 private final PacketService packetService;
55
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080056 private Interface onosInterface;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080057 private final TrafficTreatment outputTreatment;
Rusty Eddy95421642015-10-21 17:22:13 -070058
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080059 // Our hello opt holdtime
60 private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
Rusty Eddy95421642015-10-21 17:22:13 -070061
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080062 // Our hello opt prune delay
63 private int pruneDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
Rusty Eddy95421642015-10-21 17:22:13 -070064
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080065 // Neighbor priority
66 private int priority = PIMHelloOption.DEFAULT_PRIORITY;
Rusty Eddy95421642015-10-21 17:22:13 -070067
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080068 // Our current genid
Jonathan Hart5af5f142016-01-28 18:45:27 -080069 private final int generationId;
Rusty Eddy95421642015-10-21 17:22:13 -070070
Rusty Eddy390498d2016-01-15 19:21:32 -080071 // The IP address of the DR
Jonathan Hart5af5f142016-01-28 18:45:27 -080072 private IpAddress drIpaddress;
Rusty Eddy390498d2016-01-15 19:21:32 -080073
74 // A map of all our PIM neighbors keyed on our neighbors IP address
75 private Map<IpAddress, PIMNeighbor> pimNeighbors = new HashMap<>();
76
Rusty Eddy95421642015-10-21 17:22:13 -070077 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080078 * Create a PIMInterface from an ONOS Interface.
Charles Chan30ba4002015-11-05 14:45:16 -080079 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080080 * @param intf the ONOS Interface.
Jonathan Hart5af5f142016-01-28 18:45:27 -080081 * @param holdTime hold time
82 * @param priority priority
83 * @param propagationDelay propagation delay
84 * @param overrideInterval override interval
85 * @param packetService reference to the packet service
Rusty Eddy95421642015-10-21 17:22:13 -070086 */
Jonathan Hart5af5f142016-01-28 18:45:27 -080087 private PIMInterface(Interface intf,
88 short holdTime,
89 int priority,
90 short propagationDelay,
91 short overrideInterval,
92 PacketService packetService) {
93
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080094 onosInterface = intf;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080095 outputTreatment = createOutputTreatment();
Jonathan Hart5af5f142016-01-28 18:45:27 -080096 this.holdtime = holdTime;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080097 this.packetService = packetService;
Rusty Eddy390498d2016-01-15 19:21:32 -080098 IpAddress ourIp = getIpAddress();
99 MacAddress mac = intf.mac();
100
Jonathan Hart5af5f142016-01-28 18:45:27 -0800101 generationId = new Random().nextInt();
102
Rusty Eddy390498d2016-01-15 19:21:32 -0800103 // Create a PIM Neighbor to represent ourselves for DR election.
Jonathan Hart54119bb2016-02-06 18:48:27 -0800104 PIMNeighbor us = new PIMNeighbor(ourIp, mac, holdTime, 0, priority, generationId);
Rusty Eddy390498d2016-01-15 19:21:32 -0800105
106 pimNeighbors.put(ourIp, us);
107 drIpaddress = ourIp;
Rusty Eddy95421642015-10-21 17:22:13 -0700108 }
109
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800110 private TrafficTreatment createOutputTreatment() {
111 return DefaultTrafficTreatment.builder()
112 .setOutput(onosInterface.connectPoint().port())
113 .build();
114 }
115
Rusty Eddy95421642015-10-21 17:22:13 -0700116 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800117 * Return the ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -0700118 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800119 * @return ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -0700120 */
121 public Interface getInterface() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800122 return onosInterface;
123
Rusty Eddy95421642015-10-21 17:22:13 -0700124 }
125
126 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800127 * Set the ONOS Interface, it will override a previous value.
Rusty Eddy95421642015-10-21 17:22:13 -0700128 *
Jian Lidfba7392016-01-22 16:46:58 -0800129 * @param intf ONOS Interface
130 * @return PIM interface instance
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800131 */
132 public PIMInterface setInterface(Interface intf) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800133 onosInterface = intf;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800134 return this;
135 }
136
137 /**
138 * Get the set of IP Addresses associated with this interface.
139 *
140 * @return a set of Ip Addresses on this interface
141 */
142 public Set<InterfaceIpAddress> getIpAddresses() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800143 return onosInterface.ipAddresses();
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800144 }
145
146 /**
147 * Return a single "best" IP address.
148 *
149 * @return the choosen IP address or null if none
Rusty Eddy95421642015-10-21 17:22:13 -0700150 */
151 public IpAddress getIpAddress() {
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800152 if (onosInterface.ipAddresses().isEmpty()) {
Rusty Eddy95421642015-10-21 17:22:13 -0700153 return null;
154 }
155
Rusty Eddy95421642015-10-21 17:22:13 -0700156 IpAddress ipaddr = null;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800157 for (InterfaceIpAddress ifipaddr : onosInterface.ipAddresses()) {
Rusty Eddy95421642015-10-21 17:22:13 -0700158 ipaddr = ifipaddr.ipAddress();
159 break;
160 }
161 return ipaddr;
162 }
163
164 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800165 * Get the holdtime.
Rusty Eddy95421642015-10-21 17:22:13 -0700166 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800167 * @return the holdtime
168 */
169 public short getHoldtime() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800170 return holdtime;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800171 }
172
173 /**
174 * Get the prune delay.
175 *
176 * @return The prune delay
177 */
178 public int getPruneDelay() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800179 return pruneDelay;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800180 }
181
182 /**
183 * Get our hello priority.
184 *
185 * @return our priority
Rusty Eddy95421642015-10-21 17:22:13 -0700186 */
187 public int getPriority() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800188 return priority;
Rusty Eddy95421642015-10-21 17:22:13 -0700189 }
190
191 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800192 * Get our generation ID.
Rusty Eddy95421642015-10-21 17:22:13 -0700193 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800194 * @return our generation ID
Rusty Eddy95421642015-10-21 17:22:13 -0700195 */
Jonathan Hart5af5f142016-01-28 18:45:27 -0800196 public int getGenerationId() {
197 return generationId;
Rusty Eddy95421642015-10-21 17:22:13 -0700198 }
199
200 /**
Jonathan Hart54119bb2016-02-06 18:48:27 -0800201 * Gets the neighbors seen on this interface.
202 *
203 * @return PIM neighbors
204 */
205 public Collection<PIMNeighbor> getNeighbors() {
206 return pimNeighbors.values();
207 }
208
209 /**
210 * Checks whether any of our neighbors have expired, and cleans up their
211 * state if they have.
212 */
213 public void checkNeighborTimeouts() {
214 Set<PIMNeighbor> expired = pimNeighbors.values().stream()
215 // Don't time ourselves out!
216 .filter(neighbor -> !neighbor.ipAddress().equals(getIpAddress()))
217 .filter(neighbor -> neighbor.isExpired())
218 .collect(Collectors.toSet());
219
220 for (PIMNeighbor neighbor : expired) {
221 log.info("Timing out neighbor {}", neighbor);
222 pimNeighbors.remove(neighbor.ipAddress(), neighbor);
223 }
224 }
225
226 /**
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800227 * Multicast a hello message out our interface. This hello message is sent
228 * periodically during the normal PIM Neighbor refresh time, as well as a
229 * result of a newly created interface.
230 */
231 public void sendHello() {
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800232 // Create the base PIM Packet and mark it a hello packet
233 PIMPacket pimPacket = new PIMPacket(PIM.TYPE_HELLO);
234
235 // We need to set the source MAC and IPv4 addresses
236 pimPacket.setSrcMacAddr(onosInterface.mac());
237 pimPacket.setSrcIpAddress(Ip4Address.valueOf(getIpAddress().toOctets()));
238
239 // Create the hello message with options
240 PIMHello hello = new PIMHello();
241 hello.createDefaultOptions();
Jonathan Hart5af5f142016-01-28 18:45:27 -0800242 hello.addOption(PIMHelloOption.createHoldTime(holdtime));
243 hello.addOption(PIMHelloOption.createPriority(priority));
244 hello.addOption(PIMHelloOption.createGenID(generationId));
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800245
246 // Now set the hello option payload
247 pimPacket.setPIMPayload(hello);
248
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800249 packetService.emit(new DefaultOutboundPacket(
250 onosInterface.connectPoint().deviceId(),
251 outputTreatment,
252 ByteBuffer.wrap(pimPacket.getEthernet().serialize())));
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800253 }
254
255 /**
Rusty Eddy390498d2016-01-15 19:21:32 -0800256 * Process an incoming PIM Hello message. There are a few things going on in
257 * this method:
258 * <ul>
259 * <li>We <em>may</em> have to create a new neighbor if one does not already exist</li>
260 * <li>We <em>may</em> need to re-elect a new DR if new information is received</li>
261 * <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 -0800262 * <li>We will refresh the neighbor's timestamp</li>
Rusty Eddy390498d2016-01-15 19:21:32 -0800263 * </ul>
Rusty Eddy95421642015-10-21 17:22:13 -0700264 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800265 * @param ethPkt the Ethernet packet header
Rusty Eddy95421642015-10-21 17:22:13 -0700266 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800267 public void processHello(Ethernet ethPkt) {
Rusty Eddy95421642015-10-21 17:22:13 -0700268
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800269 // We'll need to save our neighbors MAC address
270 MacAddress nbrmac = ethPkt.getSourceMAC();
Rusty Eddy95421642015-10-21 17:22:13 -0700271
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800272 // And we'll need to save neighbors IP Address.
273 IPv4 iphdr = (IPv4) ethPkt.getPayload();
274 IpAddress srcip = IpAddress.valueOf(iphdr.getSourceAddress());
Rusty Eddy95421642015-10-21 17:22:13 -0700275
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800276 PIM pimhdr = (PIM) iphdr.getPayload();
277 if (pimhdr.getPimMsgType() != PIM.TYPE_HELLO) {
278 log.error("process Hello has received a non hello packet type: " + pimhdr.getPimMsgType());
Rusty Eddy95421642015-10-21 17:22:13 -0700279 return;
280 }
281
Rusty Eddy390498d2016-01-15 19:21:32 -0800282 // get the DR values for later calculation
283 PIMNeighbor dr = pimNeighbors.get(drIpaddress);
284 checkNotNull(dr);
285
286 IpAddress drip = drIpaddress;
Jonathan Hart54119bb2016-02-06 18:48:27 -0800287 int drpri = dr.priority();
Rusty Eddy390498d2016-01-15 19:21:32 -0800288
289 // Assume we do not need to run a DR election
290 boolean reElectDr = false;
291 boolean genidChanged = false;
292
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800293 PIMHello hello = (PIMHello) pimhdr.getPayload();
294
Rusty Eddy390498d2016-01-15 19:21:32 -0800295 // Determine if we already have a PIMNeighbor
296 PIMNeighbor nbr = pimNeighbors.getOrDefault(srcip, null);
Jonathan Hart54119bb2016-02-06 18:48:27 -0800297 PIMNeighbor newNbr = PIMNeighbor.createPimNeighbor(srcip, nbrmac, hello.getOptions().values());
298
Rusty Eddy390498d2016-01-15 19:21:32 -0800299 if (nbr == null) {
Jonathan Hart54119bb2016-02-06 18:48:27 -0800300 pimNeighbors.putIfAbsent(srcip, newNbr);
301 nbr = newNbr;
302 } else if (!nbr.equals(newNbr)) {
303 if (newNbr.holdtime() == 0) {
304 // Neighbor has shut down. Remove them and clean up
305 pimNeighbors.remove(srcip, nbr);
306 return;
307 } else {
308 // Neighbor has changed one of their options.
309 pimNeighbors.put(srcip, newNbr);
310 nbr = newNbr;
Rusty Eddy390498d2016-01-15 19:21:32 -0800311 }
Rusty Eddy95421642015-10-21 17:22:13 -0700312 }
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800313
Jonathan Hart54119bb2016-02-06 18:48:27 -0800314 // Refresh this neighbor's timestamp
Rusty Eddy390498d2016-01-15 19:21:32 -0800315 nbr.refreshTimestamp();
316
317 /*
Jonathan Hart5af5f142016-01-28 18:45:27 -0800318 * the election method will first determine if an election
Rusty Eddy390498d2016-01-15 19:21:32 -0800319 * needs to be run, if so it will run the election. The
320 * IP address of the DR will be returned. If the IP address
321 * of the DR is different from what we already have we know a
322 * new DR has been elected.
323 */
324 IpAddress electedIp = election(nbr, drip, drpri);
325 if (!drip.equals(electedIp)) {
326 // we have a new DR.
327 drIpaddress = electedIp;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800328 }
Rusty Eddy95421642015-10-21 17:22:13 -0700329 }
330
Rusty Eddy390498d2016-01-15 19:21:32 -0800331 // Run an election if we need to. Return the elected IP address.
Jonathan Hart5af5f142016-01-28 18:45:27 -0800332 private IpAddress election(PIMNeighbor nbr, IpAddress drIp, int drPriority) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800333
Jonathan Hart54119bb2016-02-06 18:48:27 -0800334 IpAddress nbrIp = nbr.ipAddress();
335 if (nbr.priority() > drPriority) {
Jonathan Hart5af5f142016-01-28 18:45:27 -0800336 return nbrIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800337 }
338
Jonathan Hart5af5f142016-01-28 18:45:27 -0800339 if (nbrIp.compareTo(drIp) > 0) {
340 return nbrIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800341 }
Jonathan Hart5af5f142016-01-28 18:45:27 -0800342 return drIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800343 }
344
Rusty Eddy95421642015-10-21 17:22:13 -0700345 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800346 * Process an incoming PIM JoinPrune message.
Rusty Eddy95421642015-10-21 17:22:13 -0700347 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800348 * @param ethPkt the Ethernet packet header.
Rusty Eddy95421642015-10-21 17:22:13 -0700349 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800350 public void processJoinPrune(Ethernet ethPkt) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800351 // TODO: add Join/Prune processing code.
Rusty Eddy95421642015-10-21 17:22:13 -0700352 }
Jonathan Hart5af5f142016-01-28 18:45:27 -0800353
354 /**
355 * Returns a builder for a PIM interface.
356 *
357 * @return PIM interface builder
358 */
359 public static Builder builder() {
360 return new Builder();
361 }
362
363 /**
364 * Builder for a PIM interface.
365 */
366 public static class Builder {
367 private Interface intf;
368 private PacketService packetService;
369 private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
370 private int priority = PIMHelloOption.DEFAULT_PRIORITY;
371 private short propagationDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
372 private short overrideInterval = PIMHelloOption.DEFAULT_OVERRIDEINTERVAL;
373
374 /**
375 * Uses the specified ONOS interface.
376 *
377 * @param intf ONOS interface
378 * @return this PIM interface builder
379 */
380 public Builder withInterface(Interface intf) {
381 this.intf = checkNotNull(intf);
382 return this;
383 }
384
385 /**
386 * Sets the reference to the packet service.
387 *
388 * @param packetService packet service
389 * @return this PIM interface builder
390 */
391 public Builder withPacketService(PacketService packetService) {
392 this.packetService = checkNotNull(packetService);
393 return this;
394 }
395
396 /**
397 * Uses the specified hold time.
398 *
399 * @param holdTime hold time in seconds
400 * @return this PIM interface builder
401 */
402 public Builder withHoldTime(short holdTime) {
403 this.holdtime = holdTime;
404 return this;
405 }
406
407 /**
408 * Uses the specified DR priority.
409 *
410 * @param priority DR priority
411 * @return this PIM interface builder
412 */
413 public Builder withPriority(int priority) {
414 this.priority = priority;
415 return this;
416 }
417
418 /**
419 * Uses the specified propagation delay.
420 *
421 * @param propagationDelay propagation delay in ms
422 * @return this PIM interface builder
423 */
424 public Builder withPropagationDelay(short propagationDelay) {
425 this.propagationDelay = propagationDelay;
426 return this;
427 }
428
429 /**
430 * Uses the specified override interval.
431 *
432 * @param overrideInterval override interval in ms
433 * @return this PIM interface builder
434 */
435 public Builder withOverrideInterval(short overrideInterval) {
436 this.overrideInterval = overrideInterval;
437 return this;
438 }
439
440 /**
441 * Builds the PIM interface.
442 *
443 * @return PIM interface
444 */
445 public PIMInterface build() {
446 checkArgument(intf != null, "Must provide an interface");
447 checkArgument(packetService != null, "Must provide a packet service");
448
449 return new PIMInterface(intf, holdtime, priority, propagationDelay,
450 overrideInterval, packetService);
451 }
452
453 }
Rusty Eddy95421642015-10-21 17:22:13 -0700454}