blob: 1140c3a8f7a368ad1fe7a0df61e412aa78e9687d [file] [log] [blame]
alshabibeff00542015-09-23 13:22:33 -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.mfwd.impl;
17
18import org.apache.felix.scr.annotations.Service;
19import org.onlab.packet.IpPrefix;
Julian Lawrence14c73182015-10-08 18:31:52 -070020
alshabibeff00542015-09-23 13:22:33 -070021import java.util.Map;
22import java.util.concurrent.ConcurrentHashMap;
23import static com.google.common.base.Preconditions.checkNotNull;
24
25/**
26 * The Mcast Route Table holds all multicast state for the controller.
27 *
28 * State for IPv4 and IPv6 are maintained. The tables are sets of McastRouteGroup
29 * structures that represent (*, G) state with a series of egress ConnectPoints.
30 * Each (*, G) may also have a set of (S, G) that may have there own set of
31 * ingress and egress ConnectPoints.
32 *
33 * TODO: perhaps should probably create two separate singleton for IPv4 and IPv6 respectively.
34 */
35@Service(value = org.onosproject.mfwd.impl.McastRouteTable.class)
36public final class McastRouteTable {
37
38 /*
39 * Create a map of the McastGroups indexed by the multicast group prefix.
40 * We may choose to change the map data structure in to some form a radix trie
41 * depending on the type of real world usage we see.
42 */
43 private final Map<IpPrefix, McastRouteGroup> mrib4;
44 private final Map<IpPrefix, McastRouteGroup> mrib6;
45 private static McastRouteTable instance = null;
46
47 private Boolean ipv6Enabled = false;
48
49 /**
50 * Create the two v4 & v6 tables.
51 */
52 private McastRouteTable() {
53 mrib4 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
54 if (ipv6Enabled) {
55 mrib6 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
56 } else {
57 mrib6 = null;
58 }
59 }
60
61 /**
62 * Get the single instance of this multicast group address.
63 *
64 * @return the multicast route table
65 */
66 public static McastRouteTable getInstance() {
67 if (instance == null) {
68 instance = new McastRouteTable();
69 }
70 return instance;
71 }
72
73 /**
74 * Get the IPv4 MRIB.
75 *
76 * @return the IPv4 MRIB
77 */
78 public Map<IpPrefix, McastRouteGroup> getMrib4() {
79 return mrib4;
80 }
81
82 /**
83 * Get the IPv6 MRIB.
84 *
85 * @return Return the set of prefix keyed McastGroups
86 */
87 public Map<IpPrefix, McastRouteGroup> getMrib6() {
88 return mrib6;
89 }
90
91 /**
92 * Save the McastRouteGroup in the address family appropriate mrib.
93 *
94 * @param group The McastRouteGroup to save
95 */
96 private void storeGroup(McastRouteGroup group) {
97 if (group.isIp4()) {
98 mrib4.put(group.getGaddr(), group);
99 } else if (group.isIp6() && ipv6Enabled) {
100 mrib6.put(group.getGaddr(), group);
101 }
102 }
103
104 /**
105 * Remove the group.
106 *
107 * @param group the group to be removed
108 */
109 private void removeGroup(McastRouteGroup group) {
110 IpPrefix gpfx = group.getGaddr();
111 if (gpfx.isIp4()) {
112 mrib4.remove(gpfx);
113 } else if (gpfx.isIp6() && ipv6Enabled) {
114 mrib6.remove(gpfx);
115 }
116 }
117
118 /**
119 * Add a multicast route to the MRIB. This function will.
120 *
121 * @param saddr source address * or x.x.x.x or x.x.x.x/y
122 * @param gaddr group address x.x.x.x or x.x.x.x/y
123 * @return the multicast route
124 */
125 public McastRouteBase addRoute(String saddr, String gaddr) {
126 IpPrefix gpfx = IpPrefix.valueOf(gaddr);
127 IpPrefix spfx = IpPrefix.valueOf(0, 0);
128 if (saddr != null && !saddr.equals("*")) {
129 spfx = IpPrefix.valueOf(saddr);
130 }
131 return addRoute(spfx, gpfx);
132 }
133
134 /**
135 * Add a multicast route to the MRIB. This function will store either
136 * (S, G) or (*, G) in the mrib if an entry does not already exist. If
137 * an entry does exist it is returned to the caller.
138 *
139 * Every (S, G) is stored as part of it's parent group entry which also represents
140 * (*, G) routes. In the case of a (S, G) we will also create the (*, G) entry if needed
141 * then save the (S, G) to the (*, G).
142 *
143 * @param spfx the source prefix
144 * @param gpfx the group prefix
145 * @return the resulting McastRouteSource or McastRouteGroup accordingly.
146 */
147 public McastRouteBase addRoute(IpPrefix spfx, IpPrefix gpfx) {
148
149 /**
150 * If a group route (*, g) does not exist we will need to make so we
151 * can start attaching our sources to the group entry.
152 */
153 McastRouteGroup group = findMcastGroup(gpfx);
154 if (group == null) {
155 group = new McastRouteGroup(gpfx);
156
157 // Save it for later
158 if (gpfx.isIp4()) {
159 this.mrib4.put(gpfx, group);
160 } else if (gpfx.isIp6() && ipv6Enabled) {
Rusty Eddyddef8932015-09-25 01:15:53 +0000161 this.mrib6.put(gpfx, group);
alshabibeff00542015-09-23 13:22:33 -0700162 }
163 }
164
165 /**
166 * If the source prefix length is 0 then we have our (*, g) entry, we can
167 * just return now.
168 */
169 if (spfx.prefixLength() == 0) {
170 return group;
171 }
172
173 // See if the source already exists. If so just return it.
174 McastRouteSource source = group.findSource(spfx);
175 if (source != null) {
176 return source;
177 }
178
179 /**
180 * We have the group but no source. We need to create the source then add it
181 * to the group.
182 */
183 source = new McastRouteSource(spfx, gpfx);
184
185 // Have the source save it's parent
186 source.setGroup(group);
187
188 // Save this source as part of this group
189 group.addSource(source);
190
191 return source;
192 }
193
194 /**
Julian Lawrence14c73182015-10-08 18:31:52 -0700195 * Delete a specific egress from the MRIB.
196 *
197 * @param saddr source address * or x.x.x.x or x.x.x.x/y
198 * @param gaddr group address x.x.x.x or x.x.x.x/y
199 * @param egress group address x.x.x.x or x.x.x.x/y
200 * @return boolean if egress was deleted
201 */
202 public boolean removeEgress(String saddr, String gaddr, String egress) {
203
204 IpPrefix gpfx = IpPrefix.valueOf(gaddr);
205 IpPrefix spfx = IpPrefix.valueOf(0, 0);
206 if (saddr != null && !saddr.equals("*")) {
207 spfx = IpPrefix.valueOf(saddr);
208 }
209
210 McastRouteSource src = (McastRouteSource) findBestMatch(spfx, gpfx);
211 boolean removed = src.removeEgressPoint(egress);
212 if (removed) {
213 src.setIntent();
214 }
215 return removed;
216 }
217
218 /**
alshabibeff00542015-09-23 13:22:33 -0700219 * Delete a multicast route from the MRIB.
220 *
221 * @param saddr source address * or x.x.x.x or x.x.x.x/y
222 * @param gaddr group address x.x.x.x or x.x.x.x/y
223 */
224 public void removeRoute(String saddr, String gaddr) {
225 IpPrefix gpfx = IpPrefix.valueOf(gaddr);
226 IpPrefix spfx = IpPrefix.valueOf(0, 0);
227 if (saddr != null && !saddr.equals("*")) {
228 spfx = IpPrefix.valueOf(saddr);
229 }
230 removeRoute(spfx, gpfx);
231 }
232
233 /**
234 * Remove a multicast route.
235 *
236 * @param spfx the source prefix
237 * @param gpfx the group prefix
238 */
239 public void removeRoute(IpPrefix spfx, IpPrefix gpfx) {
240
241 /**
242 * If a group route (*, g) does not exist we will need to make so we
243 * can start attaching our sources to the group entry.
244 */
245 McastRouteGroup group = findMcastGroup(gpfx);
246 if (group == null) {
247 // The group does not exist, we can't remove it.
248 return;
249 }
250
251 /**
252 * If the source prefix length is 0 then we have a (*, g) entry, which
253 * means we will remove this group and all of it's sources. We will
254 * also withdraw it's intent if need be.
255 */
256 if (spfx.prefixLength() > 0) {
257 group.removeSource(spfx);
258
259 /*
260 * Now a little house keeping. If this group has no more sources
261 * nor egress connectPoints git rid of it.
262 */
263 if (group.getSources().size() == 0 &&
264 group.getEgressPoints().size() == 0) {
265 removeGroup(group);
266 }
267
268 } else {
269 // Group remove has been explicitly requested.
270 group.removeSources();
271 group.withdrawIntent();
272 removeGroup(group);
273 }
274 }
275
276 /**
277 * Find the specific multicast group entry.
278 *
279 * @param group the group address
280 * @return McastRouteGroup the multicast (*, G) group route
281 */
282 public McastRouteGroup findMcastGroup(IpPrefix group) {
283 McastRouteGroup g = null;
284 if (group.isIp4()) {
285 g = mrib4.get(group);
286 } else if (group.isIp6() && ipv6Enabled) {
Rusty Eddyddef8932015-09-25 01:15:53 +0000287 g = mrib6.get(group);
alshabibeff00542015-09-23 13:22:33 -0700288 }
289 return g;
290 }
291
292 /**
293 * Find the multicast (S, G) entry if it exists.
294 *
295 * @param saddr the source address
296 * @param gaddr the group address
297 * @return The multicast source route entry if it exists, null if it does not.
298 */
299 public McastRouteSource findMcastSource(IpPrefix saddr, IpPrefix gaddr) {
300 McastRouteGroup grp = findMcastGroup(checkNotNull(gaddr));
301 if (grp == null) {
302 return null;
303 }
304 return grp.findSource(saddr);
305 }
306
307 /**
308 * This will first look up a Group entry. If no group entry was found null will
309 * be returned. If the group entry has been found we will then look up the (s, g) entry.
310 * If the (s, g) entry has been found, that will be returned. If no (s, g) was found
311 * the (*, g) group entry will be returned.
312 *
313 * @param saddr the source address
314 * @param gaddr the group address
315 * @return return the best matching McastRouteSource or McastRouteGroup
316 */
317 public McastRoute findBestMatch(IpPrefix saddr, IpPrefix gaddr) {
318 McastRouteGroup grp = this.findMcastGroup(checkNotNull(gaddr));
319 if (grp == null) {
320 return null;
321 }
322
323 // Found a group now look for a source
324 McastRouteSource src = grp.findSource(checkNotNull(saddr));
325 if (src == null) {
326 return grp;
327 }
328
329 return src;
330 }
331
332 /**
333 * Print out the multicast route table in it's entirety.
334 *
335 * TODO: Eventually we will have to implement paging and how to handle large tables.
336 * @return String
337 */
338 public String printMcastRouteTable() {
339 String out = this.toString() + "\n";
340
341 for (McastRouteGroup grp : mrib4.values()) {
342 out += grp.toString() + "\n";
343 for (McastRouteSource src : grp.getSources().values()) {
344 out += src.toString() + "\n";
345 }
346 }
347 return out;
348 }
349
350 /**
351 * Print out a summary of groups in the MRIB.
352 *
353 * @return String
354 */
355 public String toString() {
356 String out = "Mcast Route Table: ";
357 out += mrib4.size() + " IPv4 Multicast Groups\n";
358 if (ipv6Enabled) {
359 out += mrib6.size() + " IPv6 Multicast Groups\n";
360 }
361 return out;
362 }
363}