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