blob: ca82052050e2735238ea9313220f83a583460025 [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;
Rusty Eddy390498d2016-01-15 19:21:32 -080035import java.util.HashMap;
36import java.util.Map;
Jonathan Hart5af5f142016-01-28 18:45:27 -080037import java.util.Random;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080038import java.util.Set;
Rusty Eddy95421642015-10-21 17:22:13 -070039
Jonathan Hart5af5f142016-01-28 18:45:27 -080040import static com.google.common.base.Preconditions.checkArgument;
Rusty Eddy390498d2016-01-15 19:21:32 -080041import static com.google.common.base.Preconditions.checkNotNull;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080042import static org.slf4j.LoggerFactory.getLogger;
Rusty Eddy95421642015-10-21 17:22:13 -070043
44/**
Ray Milkeya059a702016-01-12 11:10:33 -080045 * PIM Interface represents an ONOS Interface with IP and MAC addresses for
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080046 * a given ConnectPoint.
Rusty Eddy95421642015-10-21 17:22:13 -070047 */
Jonathan Hart5af5f142016-01-28 18:45:27 -080048public final class PIMInterface {
Rusty Eddy95421642015-10-21 17:22:13 -070049
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080050 private final Logger log = getLogger(getClass());
Rusty Eddy95421642015-10-21 17:22:13 -070051
Jonathan Hart36fd31e2016-01-28 15:55:31 -080052 private final PacketService packetService;
53
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080054 private Interface onosInterface;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080055 private final TrafficTreatment outputTreatment;
Rusty Eddy95421642015-10-21 17:22:13 -070056
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080057 // Our hello opt holdtime
58 private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
Rusty Eddy95421642015-10-21 17:22:13 -070059
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080060 // Our hello opt prune delay
61 private int pruneDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
Rusty Eddy95421642015-10-21 17:22:13 -070062
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080063 // Neighbor priority
64 private int priority = PIMHelloOption.DEFAULT_PRIORITY;
Rusty Eddy95421642015-10-21 17:22:13 -070065
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080066 // Our current genid
Jonathan Hart5af5f142016-01-28 18:45:27 -080067 private final int generationId;
Rusty Eddy95421642015-10-21 17:22:13 -070068
Rusty Eddy390498d2016-01-15 19:21:32 -080069 // The IP address of the DR
Jonathan Hart5af5f142016-01-28 18:45:27 -080070 private IpAddress drIpaddress;
Rusty Eddy390498d2016-01-15 19:21:32 -080071
72 // A map of all our PIM neighbors keyed on our neighbors IP address
73 private Map<IpAddress, PIMNeighbor> pimNeighbors = new HashMap<>();
74
Rusty Eddy95421642015-10-21 17:22:13 -070075 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080076 * Create a PIMInterface from an ONOS Interface.
Charles Chan30ba4002015-11-05 14:45:16 -080077 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080078 * @param intf the ONOS Interface.
Jonathan Hart5af5f142016-01-28 18:45:27 -080079 * @param holdTime hold time
80 * @param priority priority
81 * @param propagationDelay propagation delay
82 * @param overrideInterval override interval
83 * @param packetService reference to the packet service
Rusty Eddy95421642015-10-21 17:22:13 -070084 */
Jonathan Hart5af5f142016-01-28 18:45:27 -080085 private PIMInterface(Interface intf,
86 short holdTime,
87 int priority,
88 short propagationDelay,
89 short overrideInterval,
90 PacketService packetService) {
91
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080092 onosInterface = intf;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080093 outputTreatment = createOutputTreatment();
Jonathan Hart5af5f142016-01-28 18:45:27 -080094 this.holdtime = holdTime;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080095 this.packetService = packetService;
Rusty Eddy390498d2016-01-15 19:21:32 -080096 IpAddress ourIp = getIpAddress();
97 MacAddress mac = intf.mac();
98
Jonathan Hart5af5f142016-01-28 18:45:27 -080099 generationId = new Random().nextInt();
100
Rusty Eddy390498d2016-01-15 19:21:32 -0800101 // Create a PIM Neighbor to represent ourselves for DR election.
102 PIMNeighbor us = new PIMNeighbor(ourIp, mac);
103
104 // Priority and IP address are all we need to DR election.
105 us.setPriority(priority);
106
107 pimNeighbors.put(ourIp, us);
108 drIpaddress = ourIp;
Rusty Eddy95421642015-10-21 17:22:13 -0700109 }
110
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800111 private TrafficTreatment createOutputTreatment() {
112 return DefaultTrafficTreatment.builder()
113 .setOutput(onosInterface.connectPoint().port())
114 .build();
115 }
116
Rusty Eddy95421642015-10-21 17:22:13 -0700117 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800118 * Return the ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -0700119 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800120 * @return ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -0700121 */
122 public Interface getInterface() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800123 return onosInterface;
124
Rusty Eddy95421642015-10-21 17:22:13 -0700125 }
126
127 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800128 * Set the ONOS Interface, it will override a previous value.
Rusty Eddy95421642015-10-21 17:22:13 -0700129 *
Jian Lidfba7392016-01-22 16:46:58 -0800130 * @param intf ONOS Interface
131 * @return PIM interface instance
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800132 */
133 public PIMInterface setInterface(Interface intf) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800134 onosInterface = intf;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800135 return this;
136 }
137
138 /**
139 * Get the set of IP Addresses associated with this interface.
140 *
141 * @return a set of Ip Addresses on this interface
142 */
143 public Set<InterfaceIpAddress> getIpAddresses() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800144 return onosInterface.ipAddresses();
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800145 }
146
147 /**
148 * Return a single "best" IP address.
149 *
150 * @return the choosen IP address or null if none
Rusty Eddy95421642015-10-21 17:22:13 -0700151 */
152 public IpAddress getIpAddress() {
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800153 if (onosInterface.ipAddresses().isEmpty()) {
Rusty Eddy95421642015-10-21 17:22:13 -0700154 return null;
155 }
156
Rusty Eddy95421642015-10-21 17:22:13 -0700157 IpAddress ipaddr = null;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800158 for (InterfaceIpAddress ifipaddr : onosInterface.ipAddresses()) {
Rusty Eddy95421642015-10-21 17:22:13 -0700159 ipaddr = ifipaddr.ipAddress();
160 break;
161 }
162 return ipaddr;
163 }
164
165 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800166 * Get the holdtime.
Rusty Eddy95421642015-10-21 17:22:13 -0700167 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800168 * @return the holdtime
169 */
170 public short getHoldtime() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800171 return holdtime;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800172 }
173
174 /**
175 * Get the prune delay.
176 *
177 * @return The prune delay
178 */
179 public int getPruneDelay() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800180 return pruneDelay;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800181 }
182
183 /**
184 * Get our hello priority.
185 *
186 * @return our priority
Rusty Eddy95421642015-10-21 17:22:13 -0700187 */
188 public int getPriority() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800189 return priority;
Rusty Eddy95421642015-10-21 17:22:13 -0700190 }
191
192 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800193 * Get our generation ID.
Rusty Eddy95421642015-10-21 17:22:13 -0700194 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800195 * @return our generation ID
Rusty Eddy95421642015-10-21 17:22:13 -0700196 */
Jonathan Hart5af5f142016-01-28 18:45:27 -0800197 public int getGenerationId() {
198 return generationId;
Rusty Eddy95421642015-10-21 17:22:13 -0700199 }
200
201 /**
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800202 * Multicast a hello message out our interface. This hello message is sent
203 * periodically during the normal PIM Neighbor refresh time, as well as a
204 * result of a newly created interface.
205 */
206 public void sendHello() {
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800207 // Create the base PIM Packet and mark it a hello packet
208 PIMPacket pimPacket = new PIMPacket(PIM.TYPE_HELLO);
209
210 // We need to set the source MAC and IPv4 addresses
211 pimPacket.setSrcMacAddr(onosInterface.mac());
212 pimPacket.setSrcIpAddress(Ip4Address.valueOf(getIpAddress().toOctets()));
213
214 // Create the hello message with options
215 PIMHello hello = new PIMHello();
216 hello.createDefaultOptions();
Jonathan Hart5af5f142016-01-28 18:45:27 -0800217 hello.addOption(PIMHelloOption.createHoldTime(holdtime));
218 hello.addOption(PIMHelloOption.createPriority(priority));
219 hello.addOption(PIMHelloOption.createGenID(generationId));
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800220
221 // Now set the hello option payload
222 pimPacket.setPIMPayload(hello);
223
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800224 packetService.emit(new DefaultOutboundPacket(
225 onosInterface.connectPoint().deviceId(),
226 outputTreatment,
227 ByteBuffer.wrap(pimPacket.getEthernet().serialize())));
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800228 }
229
230 /**
Rusty Eddy390498d2016-01-15 19:21:32 -0800231 * Process an incoming PIM Hello message. There are a few things going on in
232 * this method:
233 * <ul>
234 * <li>We <em>may</em> have to create a new neighbor if one does not already exist</li>
235 * <li>We <em>may</em> need to re-elect a new DR if new information is received</li>
236 * <li>We <em>may</em> need to send an existing neighbor all joins if the genid changed</li>
237 * <li>We will refresh the neighbors timestamp</li>
238 * </ul>
Rusty Eddy95421642015-10-21 17:22:13 -0700239 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800240 * @param ethPkt the Ethernet packet header
Rusty Eddy95421642015-10-21 17:22:13 -0700241 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800242 public void processHello(Ethernet ethPkt) {
Rusty Eddy95421642015-10-21 17:22:13 -0700243
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800244 // We'll need to save our neighbors MAC address
245 MacAddress nbrmac = ethPkt.getSourceMAC();
Rusty Eddy95421642015-10-21 17:22:13 -0700246
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800247 // And we'll need to save neighbors IP Address.
248 IPv4 iphdr = (IPv4) ethPkt.getPayload();
249 IpAddress srcip = IpAddress.valueOf(iphdr.getSourceAddress());
Rusty Eddy95421642015-10-21 17:22:13 -0700250
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800251 PIM pimhdr = (PIM) iphdr.getPayload();
252 if (pimhdr.getPimMsgType() != PIM.TYPE_HELLO) {
253 log.error("process Hello has received a non hello packet type: " + pimhdr.getPimMsgType());
Rusty Eddy95421642015-10-21 17:22:13 -0700254 return;
255 }
256
Rusty Eddy390498d2016-01-15 19:21:32 -0800257 // get the DR values for later calculation
258 PIMNeighbor dr = pimNeighbors.get(drIpaddress);
259 checkNotNull(dr);
260
261 IpAddress drip = drIpaddress;
262 int drpri = dr.getPriority();
263
264 // Assume we do not need to run a DR election
265 boolean reElectDr = false;
266 boolean genidChanged = false;
267
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800268 PIMHello hello = (PIMHello) pimhdr.getPayload();
269
Rusty Eddy390498d2016-01-15 19:21:32 -0800270 // Determine if we already have a PIMNeighbor
271 PIMNeighbor nbr = pimNeighbors.getOrDefault(srcip, null);
272 if (nbr == null) {
273 nbr = new PIMNeighbor(srcip, hello.getOptions());
274 checkNotNull(nbr);
275 } else {
276 Integer previousGenid = nbr.getGenid();
277 nbr.addOptions(hello.getOptions());
278 if (previousGenid != nbr.getGenid()) {
279 genidChanged = true;
280 }
Rusty Eddy95421642015-10-21 17:22:13 -0700281 }
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800282
Rusty Eddy390498d2016-01-15 19:21:32 -0800283 // Refresh this neighbors timestamp
284 nbr.refreshTimestamp();
285
286 /*
Jonathan Hart5af5f142016-01-28 18:45:27 -0800287 * the election method will first determine if an election
Rusty Eddy390498d2016-01-15 19:21:32 -0800288 * needs to be run, if so it will run the election. The
289 * IP address of the DR will be returned. If the IP address
290 * of the DR is different from what we already have we know a
291 * new DR has been elected.
292 */
293 IpAddress electedIp = election(nbr, drip, drpri);
294 if (!drip.equals(electedIp)) {
295 // we have a new DR.
296 drIpaddress = electedIp;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800297 }
Rusty Eddy95421642015-10-21 17:22:13 -0700298 }
299
Rusty Eddy390498d2016-01-15 19:21:32 -0800300 // Run an election if we need to. Return the elected IP address.
Jonathan Hart5af5f142016-01-28 18:45:27 -0800301 private IpAddress election(PIMNeighbor nbr, IpAddress drIp, int drPriority) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800302
Jonathan Hart5af5f142016-01-28 18:45:27 -0800303 IpAddress nbrIp = nbr.getIpaddr();
304 if (nbr.getPriority() > drPriority) {
305 return nbrIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800306 }
307
Jonathan Hart5af5f142016-01-28 18:45:27 -0800308 if (nbrIp.compareTo(drIp) > 0) {
309 return nbrIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800310 }
Jonathan Hart5af5f142016-01-28 18:45:27 -0800311 return drIp;
Rusty Eddy390498d2016-01-15 19:21:32 -0800312 }
313
Rusty Eddy95421642015-10-21 17:22:13 -0700314 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800315 * Process an incoming PIM JoinPrune message.
Rusty Eddy95421642015-10-21 17:22:13 -0700316 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800317 * @param ethPkt the Ethernet packet header.
Rusty Eddy95421642015-10-21 17:22:13 -0700318 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800319 public void processJoinPrune(Ethernet ethPkt) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800320 // TODO: add Join/Prune processing code.
Rusty Eddy95421642015-10-21 17:22:13 -0700321 }
Jonathan Hart5af5f142016-01-28 18:45:27 -0800322
323 /**
324 * Returns a builder for a PIM interface.
325 *
326 * @return PIM interface builder
327 */
328 public static Builder builder() {
329 return new Builder();
330 }
331
332 /**
333 * Builder for a PIM interface.
334 */
335 public static class Builder {
336 private Interface intf;
337 private PacketService packetService;
338 private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
339 private int priority = PIMHelloOption.DEFAULT_PRIORITY;
340 private short propagationDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
341 private short overrideInterval = PIMHelloOption.DEFAULT_OVERRIDEINTERVAL;
342
343 /**
344 * Uses the specified ONOS interface.
345 *
346 * @param intf ONOS interface
347 * @return this PIM interface builder
348 */
349 public Builder withInterface(Interface intf) {
350 this.intf = checkNotNull(intf);
351 return this;
352 }
353
354 /**
355 * Sets the reference to the packet service.
356 *
357 * @param packetService packet service
358 * @return this PIM interface builder
359 */
360 public Builder withPacketService(PacketService packetService) {
361 this.packetService = checkNotNull(packetService);
362 return this;
363 }
364
365 /**
366 * Uses the specified hold time.
367 *
368 * @param holdTime hold time in seconds
369 * @return this PIM interface builder
370 */
371 public Builder withHoldTime(short holdTime) {
372 this.holdtime = holdTime;
373 return this;
374 }
375
376 /**
377 * Uses the specified DR priority.
378 *
379 * @param priority DR priority
380 * @return this PIM interface builder
381 */
382 public Builder withPriority(int priority) {
383 this.priority = priority;
384 return this;
385 }
386
387 /**
388 * Uses the specified propagation delay.
389 *
390 * @param propagationDelay propagation delay in ms
391 * @return this PIM interface builder
392 */
393 public Builder withPropagationDelay(short propagationDelay) {
394 this.propagationDelay = propagationDelay;
395 return this;
396 }
397
398 /**
399 * Uses the specified override interval.
400 *
401 * @param overrideInterval override interval in ms
402 * @return this PIM interface builder
403 */
404 public Builder withOverrideInterval(short overrideInterval) {
405 this.overrideInterval = overrideInterval;
406 return this;
407 }
408
409 /**
410 * Builds the PIM interface.
411 *
412 * @return PIM interface
413 */
414 public PIMInterface build() {
415 checkArgument(intf != null, "Must provide an interface");
416 checkArgument(packetService != null, "Must provide a packet service");
417
418 return new PIMInterface(intf, holdtime, priority, propagationDelay,
419 overrideInterval, packetService);
420 }
421
422 }
Rusty Eddy95421642015-10-21 17:22:13 -0700423}