aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java
blob: 1140c3a8f7a368ad1fe7a0df61e412aa78e9687d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/*
 * Copyright 2015 Open Networking Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onosproject.mfwd.impl;

import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.IpPrefix;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * The Mcast Route Table holds all multicast state for the controller.
 *
 * State for IPv4 and IPv6 are maintained.  The tables are sets of McastRouteGroup
 * structures that represent (*, G) state with a series of egress ConnectPoints.
 * Each (*, G) may also have a set of (S, G) that may have there own set of
 * ingress and egress ConnectPoints.
 *
 * TODO: perhaps should probably create two separate singleton for IPv4 and IPv6 respectively.
 */
@Service(value = org.onosproject.mfwd.impl.McastRouteTable.class)
public final class McastRouteTable {

    /*
     * Create a map of the McastGroups indexed by the multicast group prefix.
     * We may choose to change the map data structure in to some form a radix trie
     * depending on the type of real world usage we see.
     */
    private final Map<IpPrefix, McastRouteGroup> mrib4;
    private final Map<IpPrefix, McastRouteGroup> mrib6;
    private static McastRouteTable instance = null;

    private Boolean ipv6Enabled = false;

    /**
     * Create the two v4 & v6 tables.
     */
    private McastRouteTable() {
        mrib4 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
        if (ipv6Enabled) {
            mrib6 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
        } else {
            mrib6 = null;
        }
    }

    /**
     * Get the single instance of this multicast group address.
     *
     * @return the multicast route table
     */
    public static McastRouteTable getInstance() {
        if (instance == null) {
            instance = new McastRouteTable();
        }
        return instance;
    }

    /**
     * Get the IPv4 MRIB.
     *
     * @return the IPv4 MRIB
     */
    public Map<IpPrefix, McastRouteGroup> getMrib4() {
        return mrib4;
    }

    /**
     * Get the IPv6 MRIB.
     *
     * @return Return the set of prefix keyed McastGroups
     */
    public Map<IpPrefix, McastRouteGroup> getMrib6() {
        return mrib6;
    }

    /**
     * Save the McastRouteGroup in the address family appropriate mrib.
     *
     * @param group The McastRouteGroup to save
     */
    private void storeGroup(McastRouteGroup group) {
        if (group.isIp4()) {
            mrib4.put(group.getGaddr(), group);
        } else if (group.isIp6() && ipv6Enabled) {
            mrib6.put(group.getGaddr(), group);
        }
    }

    /**
     * Remove the group.
     *
     * @param group the group to be removed
     */
    private void removeGroup(McastRouteGroup group) {
        IpPrefix gpfx = group.getGaddr();
        if (gpfx.isIp4()) {
            mrib4.remove(gpfx);
        } else if (gpfx.isIp6() && ipv6Enabled) {
            mrib6.remove(gpfx);
        }
    }

    /**
     * Add a multicast route to the MRIB.  This function will.
     *
     * @param saddr source address * or x.x.x.x or x.x.x.x/y
     * @param gaddr group address x.x.x.x or x.x.x.x/y
     * @return the multicast route
     */
    public McastRouteBase addRoute(String saddr, String gaddr) {
        IpPrefix gpfx = IpPrefix.valueOf(gaddr);
        IpPrefix spfx = IpPrefix.valueOf(0, 0);
        if (saddr != null && !saddr.equals("*")) {
            spfx = IpPrefix.valueOf(saddr);
        }
        return addRoute(spfx, gpfx);
    }

    /**
     * Add a multicast route to the MRIB.  This function will store either
     * (S, G) or (*, G) in the mrib if an entry does not already exist. If
     * an entry does exist it is returned to the caller.
     *
     * Every (S, G) is stored as part of it's parent group entry which also represents
     * (*, G) routes.  In the case of a (S, G) we will also create the (*, G) entry if needed
     * then save the (S, G) to the (*, G).
     *
     * @param spfx the source prefix
     * @param gpfx the group prefix
     * @return the resulting McastRouteSource or McastRouteGroup accordingly.
     */
    public McastRouteBase addRoute(IpPrefix spfx, IpPrefix gpfx) {

        /**
         * If a group route (*, g) does not exist we will need to make so we
         * can start attaching our sources to the group entry.
         */
        McastRouteGroup group = findMcastGroup(gpfx);
        if (group == null) {
            group = new McastRouteGroup(gpfx);

            // Save it for later
            if (gpfx.isIp4()) {
                this.mrib4.put(gpfx, group);
            } else if (gpfx.isIp6() && ipv6Enabled) {
                this.mrib6.put(gpfx, group);
            }
        }

        /**
         * If the source prefix length is 0 then we have our (*, g) entry, we can
         * just return now.
         */
        if (spfx.prefixLength() == 0) {
            return group;
        }

        // See if the source already exists.  If so just return it.
        McastRouteSource source = group.findSource(spfx);
        if (source != null) {
            return source;
        }

        /**
         * We have the group but no source.  We need to create the source then add it
         * to the group.
         */
        source = new McastRouteSource(spfx, gpfx);

        // Have the source save it's parent
        source.setGroup(group);

        // Save this source as part of this group
        group.addSource(source);

        return source;
    }

    /**
     * Delete a specific egress from the MRIB.
     *
     * @param saddr source address * or x.x.x.x or x.x.x.x/y
     * @param gaddr group address x.x.x.x or x.x.x.x/y
     * @param egress group address x.x.x.x or x.x.x.x/y
     * @return boolean if egress was deleted
     */
    public boolean removeEgress(String saddr, String gaddr, String egress) {

        IpPrefix gpfx = IpPrefix.valueOf(gaddr);
        IpPrefix spfx = IpPrefix.valueOf(0, 0);
        if (saddr != null && !saddr.equals("*")) {
            spfx = IpPrefix.valueOf(saddr);
        }

        McastRouteSource src = (McastRouteSource) findBestMatch(spfx, gpfx);
        boolean removed = src.removeEgressPoint(egress);
        if (removed) {
            src.setIntent();
        }
        return removed;
    }

    /**
     * Delete a multicast route from the MRIB.
     *
     * @param saddr source address * or x.x.x.x or x.x.x.x/y
     * @param gaddr group address x.x.x.x or x.x.x.x/y
     */
    public void removeRoute(String saddr, String gaddr) {
        IpPrefix gpfx = IpPrefix.valueOf(gaddr);
        IpPrefix spfx = IpPrefix.valueOf(0, 0);
        if (saddr != null && !saddr.equals("*")) {
            spfx = IpPrefix.valueOf(saddr);
        }
        removeRoute(spfx, gpfx);
    }

    /**
     * Remove a multicast route.
     *
     * @param spfx the source prefix
     * @param gpfx the group prefix
     */
    public void removeRoute(IpPrefix spfx, IpPrefix gpfx) {

        /**
         * If a group route (*, g) does not exist we will need to make so we
         * can start attaching our sources to the group entry.
         */
        McastRouteGroup group = findMcastGroup(gpfx);
        if (group == null) {
            // The group does not exist, we can't remove it.
            return;
        }

        /**
         * If the source prefix length is 0 then we have a (*, g) entry, which
         * means we will remove this group and all of it's sources. We will
         * also withdraw it's intent if need be.
         */
        if (spfx.prefixLength() > 0) {
            group.removeSource(spfx);

            /*
             * Now a little house keeping. If this group has no more sources
             * nor egress connectPoints git rid of it.
             */
            if (group.getSources().size() == 0 &&
                    group.getEgressPoints().size() == 0) {
                removeGroup(group);
            }

        } else {
            // Group remove has been explicitly requested.
            group.removeSources();
            group.withdrawIntent();
            removeGroup(group);
        }
    }

    /**
     * Find the specific multicast group entry.
     *
     * @param group the group address
     * @return McastRouteGroup the multicast (*, G) group route
     */
    public McastRouteGroup findMcastGroup(IpPrefix group) {
        McastRouteGroup g = null;
        if (group.isIp4()) {
            g = mrib4.get(group);
        } else if (group.isIp6() && ipv6Enabled) {
            g = mrib6.get(group);
        }
        return g;
    }

    /**
     * Find the multicast (S, G) entry if it exists.
     *
     * @param saddr the source address
     * @param gaddr the group address
     * @return The multicast source route entry if it exists, null if it does not.
     */
    public McastRouteSource findMcastSource(IpPrefix saddr, IpPrefix gaddr) {
        McastRouteGroup grp = findMcastGroup(checkNotNull(gaddr));
        if (grp == null) {
            return null;
        }
        return grp.findSource(saddr);
    }

    /**
     * This will first look up a Group entry. If no group entry was found null will
     * be returned. If the group entry has been found we will then look up the (s, g) entry.
     * If the (s, g) entry has been found, that will be returned.  If no (s, g) was found
     * the (*, g) group entry will be returned.
     *
     * @param saddr the source address
     * @param gaddr the group address
     * @return return the best matching McastRouteSource or McastRouteGroup
     */
    public McastRoute findBestMatch(IpPrefix saddr, IpPrefix gaddr) {
        McastRouteGroup grp = this.findMcastGroup(checkNotNull(gaddr));
        if (grp == null) {
            return null;
        }

        // Found a group now look for a source
        McastRouteSource src = grp.findSource(checkNotNull(saddr));
        if (src == null) {
            return grp;
        }

        return src;
    }

    /**
     * Print out the multicast route table in it's entirety.
     *
     * TODO: Eventually we will have to implement paging and how to handle large tables.
     * @return String
     */
    public String printMcastRouteTable() {
        String out = this.toString() + "\n";

        for (McastRouteGroup grp : mrib4.values()) {
            out += grp.toString() + "\n";
            for (McastRouteSource src : grp.getSources().values()) {
                out += src.toString() + "\n";
            }
        }
        return out;
    }

    /**
     * Print out a summary of groups in the MRIB.
     *
     * @return String
     */
    public String toString() {
        String out = "Mcast Route Table: ";
        out += mrib4.size() + " IPv4 Multicast Groups\n";
        if (ipv6Enabled) {
            out += mrib6.size() + " IPv6 Multicast Groups\n";
        }
        return out;
    }
}