blob: 454c431c47156a2918bf5b32ffcd45da3ea92c82 [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;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080037import java.util.Set;
Rusty Eddy95421642015-10-21 17:22:13 -070038
Rusty Eddy390498d2016-01-15 19:21:32 -080039import static com.google.common.base.Preconditions.checkNotNull;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080040import static org.slf4j.LoggerFactory.getLogger;
Rusty Eddy95421642015-10-21 17:22:13 -070041
42/**
Ray Milkeya059a702016-01-12 11:10:33 -080043 * PIM Interface represents an ONOS Interface with IP and MAC addresses for
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080044 * a given ConnectPoint.
Rusty Eddy95421642015-10-21 17:22:13 -070045 */
46public class PIMInterface {
Rusty Eddy95421642015-10-21 17:22:13 -070047
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080048 private final Logger log = getLogger(getClass());
Rusty Eddy95421642015-10-21 17:22:13 -070049
Jonathan Hart36fd31e2016-01-28 15:55:31 -080050 private final PacketService packetService;
51
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080052 private Interface onosInterface;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080053 private final TrafficTreatment outputTreatment;
Rusty Eddy95421642015-10-21 17:22:13 -070054
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080055 // Our hello opt holdtime
56 private short holdtime = PIMHelloOption.DEFAULT_HOLDTIME;
Rusty Eddy95421642015-10-21 17:22:13 -070057
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080058 // Our hello opt prune delay
59 private int pruneDelay = PIMHelloOption.DEFAULT_PRUNEDELAY;
Rusty Eddy95421642015-10-21 17:22:13 -070060
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080061 // Neighbor priority
62 private int priority = PIMHelloOption.DEFAULT_PRIORITY;
Rusty Eddy95421642015-10-21 17:22:13 -070063
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080064 // Our current genid
65 private int genid = PIMHelloOption.DEFAULT_GENID; // Needs to be assigned.
Rusty Eddy95421642015-10-21 17:22:13 -070066
Rusty Eddy390498d2016-01-15 19:21:32 -080067 // The IP address of the DR
68 IpAddress drIpaddress;
69
70 // A map of all our PIM neighbors keyed on our neighbors IP address
71 private Map<IpAddress, PIMNeighbor> pimNeighbors = new HashMap<>();
72
Rusty Eddy95421642015-10-21 17:22:13 -070073 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080074 * Create a PIMInterface from an ONOS Interface.
Charles Chan30ba4002015-11-05 14:45:16 -080075 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080076 * @param intf the ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -070077 */
Jonathan Hart36fd31e2016-01-28 15:55:31 -080078 public PIMInterface(Interface intf, PacketService packetService) {
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080079 onosInterface = intf;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080080 outputTreatment = createOutputTreatment();
81 this.packetService = packetService;
Rusty Eddy390498d2016-01-15 19:21:32 -080082 IpAddress ourIp = getIpAddress();
83 MacAddress mac = intf.mac();
84
85 // Create a PIM Neighbor to represent ourselves for DR election.
86 PIMNeighbor us = new PIMNeighbor(ourIp, mac);
87
88 // Priority and IP address are all we need to DR election.
89 us.setPriority(priority);
90
91 pimNeighbors.put(ourIp, us);
92 drIpaddress = ourIp;
Rusty Eddy95421642015-10-21 17:22:13 -070093 }
94
Jonathan Hart36fd31e2016-01-28 15:55:31 -080095 private TrafficTreatment createOutputTreatment() {
96 return DefaultTrafficTreatment.builder()
97 .setOutput(onosInterface.connectPoint().port())
98 .build();
99 }
100
Rusty Eddy95421642015-10-21 17:22:13 -0700101 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800102 * Return the ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -0700103 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800104 * @return ONOS Interface.
Rusty Eddy95421642015-10-21 17:22:13 -0700105 */
106 public Interface getInterface() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800107 return onosInterface;
108
Rusty Eddy95421642015-10-21 17:22:13 -0700109 }
110
111 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800112 * Set the ONOS Interface, it will override a previous value.
Rusty Eddy95421642015-10-21 17:22:13 -0700113 *
Jian Lidfba7392016-01-22 16:46:58 -0800114 * @param intf ONOS Interface
115 * @return PIM interface instance
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800116 */
117 public PIMInterface setInterface(Interface intf) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800118 onosInterface = intf;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800119 return this;
120 }
121
122 /**
123 * Get the set of IP Addresses associated with this interface.
124 *
125 * @return a set of Ip Addresses on this interface
126 */
127 public Set<InterfaceIpAddress> getIpAddresses() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800128 return onosInterface.ipAddresses();
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800129 }
130
131 /**
132 * Return a single "best" IP address.
133 *
134 * @return the choosen IP address or null if none
Rusty Eddy95421642015-10-21 17:22:13 -0700135 */
136 public IpAddress getIpAddress() {
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800137 if (onosInterface.ipAddresses().isEmpty()) {
Rusty Eddy95421642015-10-21 17:22:13 -0700138 return null;
139 }
140
Rusty Eddy95421642015-10-21 17:22:13 -0700141 IpAddress ipaddr = null;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800142 for (InterfaceIpAddress ifipaddr : onosInterface.ipAddresses()) {
Rusty Eddy95421642015-10-21 17:22:13 -0700143 ipaddr = ifipaddr.ipAddress();
144 break;
145 }
146 return ipaddr;
147 }
148
149 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800150 * Get the holdtime.
Rusty Eddy95421642015-10-21 17:22:13 -0700151 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800152 * @return the holdtime
153 */
154 public short getHoldtime() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800155 return holdtime;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800156 }
157
158 /**
159 * Get the prune delay.
160 *
161 * @return The prune delay
162 */
163 public int getPruneDelay() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800164 return pruneDelay;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800165 }
166
167 /**
168 * Get our hello priority.
169 *
170 * @return our priority
Rusty Eddy95421642015-10-21 17:22:13 -0700171 */
172 public int getPriority() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800173 return priority;
Rusty Eddy95421642015-10-21 17:22:13 -0700174 }
175
176 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800177 * Get our generation ID.
Rusty Eddy95421642015-10-21 17:22:13 -0700178 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800179 * @return our generation ID
Rusty Eddy95421642015-10-21 17:22:13 -0700180 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800181 public int getGenid() {
Rusty Eddy390498d2016-01-15 19:21:32 -0800182 return genid;
Rusty Eddy95421642015-10-21 17:22:13 -0700183 }
184
185 /**
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800186 * Multicast a hello message out our interface. This hello message is sent
187 * periodically during the normal PIM Neighbor refresh time, as well as a
188 * result of a newly created interface.
189 */
190 public void sendHello() {
191
192 // Create the base PIM Packet and mark it a hello packet
193 PIMPacket pimPacket = new PIMPacket(PIM.TYPE_HELLO);
194
195 // We need to set the source MAC and IPv4 addresses
196 pimPacket.setSrcMacAddr(onosInterface.mac());
197 pimPacket.setSrcIpAddress(Ip4Address.valueOf(getIpAddress().toOctets()));
198
199 // Create the hello message with options
200 PIMHello hello = new PIMHello();
201 hello.createDefaultOptions();
202
203 // Now set the hello option payload
204 pimPacket.setPIMPayload(hello);
205
Jonathan Hart36fd31e2016-01-28 15:55:31 -0800206 packetService.emit(new DefaultOutboundPacket(
207 onosInterface.connectPoint().deviceId(),
208 outputTreatment,
209 ByteBuffer.wrap(pimPacket.getEthernet().serialize())));
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800210 }
211
212 /**
Rusty Eddy390498d2016-01-15 19:21:32 -0800213 * Process an incoming PIM Hello message. There are a few things going on in
214 * this method:
215 * <ul>
216 * <li>We <em>may</em> have to create a new neighbor if one does not already exist</li>
217 * <li>We <em>may</em> need to re-elect a new DR if new information is received</li>
218 * <li>We <em>may</em> need to send an existing neighbor all joins if the genid changed</li>
219 * <li>We will refresh the neighbors timestamp</li>
220 * </ul>
Rusty Eddy95421642015-10-21 17:22:13 -0700221 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800222 * @param ethPkt the Ethernet packet header
Rusty Eddy95421642015-10-21 17:22:13 -0700223 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800224 public void processHello(Ethernet ethPkt) {
Rusty Eddy95421642015-10-21 17:22:13 -0700225
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800226 // We'll need to save our neighbors MAC address
227 MacAddress nbrmac = ethPkt.getSourceMAC();
Rusty Eddy95421642015-10-21 17:22:13 -0700228
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800229 // And we'll need to save neighbors IP Address.
230 IPv4 iphdr = (IPv4) ethPkt.getPayload();
231 IpAddress srcip = IpAddress.valueOf(iphdr.getSourceAddress());
Rusty Eddy95421642015-10-21 17:22:13 -0700232
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800233 PIM pimhdr = (PIM) iphdr.getPayload();
234 if (pimhdr.getPimMsgType() != PIM.TYPE_HELLO) {
235 log.error("process Hello has received a non hello packet type: " + pimhdr.getPimMsgType());
Rusty Eddy95421642015-10-21 17:22:13 -0700236 return;
237 }
238
Rusty Eddy390498d2016-01-15 19:21:32 -0800239 // get the DR values for later calculation
240 PIMNeighbor dr = pimNeighbors.get(drIpaddress);
241 checkNotNull(dr);
242
243 IpAddress drip = drIpaddress;
244 int drpri = dr.getPriority();
245
246 // Assume we do not need to run a DR election
247 boolean reElectDr = false;
248 boolean genidChanged = false;
249
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800250 PIMHello hello = (PIMHello) pimhdr.getPayload();
251
Rusty Eddy390498d2016-01-15 19:21:32 -0800252 // Determine if we already have a PIMNeighbor
253 PIMNeighbor nbr = pimNeighbors.getOrDefault(srcip, null);
254 if (nbr == null) {
255 nbr = new PIMNeighbor(srcip, hello.getOptions());
256 checkNotNull(nbr);
257 } else {
258 Integer previousGenid = nbr.getGenid();
259 nbr.addOptions(hello.getOptions());
260 if (previousGenid != nbr.getGenid()) {
261 genidChanged = true;
262 }
Rusty Eddy95421642015-10-21 17:22:13 -0700263 }
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800264
Rusty Eddy390498d2016-01-15 19:21:32 -0800265 // Refresh this neighbors timestamp
266 nbr.refreshTimestamp();
267
268 /*
269 * the election method will frist determine if an election
270 * needs to be run, if so it will run the election. The
271 * IP address of the DR will be returned. If the IP address
272 * of the DR is different from what we already have we know a
273 * new DR has been elected.
274 */
275 IpAddress electedIp = election(nbr, drip, drpri);
276 if (!drip.equals(electedIp)) {
277 // we have a new DR.
278 drIpaddress = electedIp;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800279 }
Rusty Eddy95421642015-10-21 17:22:13 -0700280 }
281
Rusty Eddy390498d2016-01-15 19:21:32 -0800282 // Run an election if we need to. Return the elected IP address.
283 private IpAddress election(PIMNeighbor nbr, IpAddress drip, int drpri) {
284
285 IpAddress nbrip = nbr.getIpaddr();
286 if (nbr.getPriority() > drpri) {
287 return nbrip;
288 }
289
290 if (nbrip.compareTo(drip) > 0) {
291 return nbrip;
292 }
293 return drip;
294 }
295
Rusty Eddy95421642015-10-21 17:22:13 -0700296 /**
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800297 * Process an incoming PIM JoinPrune message.
Rusty Eddy95421642015-10-21 17:22:13 -0700298 *
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800299 * @param ethPkt the Ethernet packet header.
Rusty Eddy95421642015-10-21 17:22:13 -0700300 */
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800301 public void processJoinPrune(Ethernet ethPkt) {
Rusty Eddy390498d2016-01-15 19:21:32 -0800302 // TODO: add Join/Prune processing code.
Rusty Eddy95421642015-10-21 17:22:13 -0700303 }
Rusty Eddy95421642015-10-21 17:22:13 -0700304}