blob: f8856e62e8f7fec2b20455a0759ef6462e8469b3 [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
alshabibeff00542015-09-23 13:22:33 -070018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabibeff00542015-09-23 13:22:33 -070023import org.onlab.packet.Ethernet;
alshabibeff00542015-09-23 13:22:33 -070024import org.onlab.packet.IPv4;
Thomas Vachuska61ab5e02016-01-11 13:55:15 -080025import org.onlab.packet.Ip4Address;
26import org.onlab.packet.IpAddress;
27import org.onlab.packet.IpPrefix;
alshabibeff00542015-09-23 13:22:33 -070028import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.flow.DefaultTrafficSelector;
32import org.onosproject.net.flow.DefaultTrafficTreatment;
33import org.onosproject.net.flow.TrafficSelector;
34import org.onosproject.net.flow.TrafficTreatment;
Julian Lawrence51731882016-01-19 17:24:15 -080035import org.onosproject.net.intent.Intent;
36import org.onosproject.net.intent.IntentService;
37import org.onosproject.net.intent.Key;
38import org.onosproject.net.intent.SinglePointToMultiPointIntent;
Julian Lawrencefa790f62016-01-07 17:18:30 -080039import org.onosproject.net.mcast.McastRoute;
Julian Lawrence51731882016-01-19 17:24:15 -080040import org.onosproject.net.mcast.McastListener;
41import org.onosproject.net.mcast.McastEvent;
Thomas Vachuska61ab5e02016-01-11 13:55:15 -080042import org.onosproject.net.mcast.MulticastRouteService;
alshabibeff00542015-09-23 13:22:33 -070043import org.onosproject.net.packet.DefaultOutboundPacket;
44import org.onosproject.net.packet.InboundPacket;
45import org.onosproject.net.packet.OutboundPacket;
46import org.onosproject.net.packet.PacketContext;
47import org.onosproject.net.packet.PacketPriority;
48import org.onosproject.net.packet.PacketProcessor;
49import org.onosproject.net.packet.PacketService;
50import org.slf4j.Logger;
51
Julian Lawrencefa790f62016-01-07 17:18:30 -080052import java.util.ArrayList;
Julian Lawrence51731882016-01-19 17:24:15 -080053import java.util.HashMap;
54import java.util.HashSet;
55import java.util.Map;
56import java.util.Set;
Julian Lawrencefa790f62016-01-07 17:18:30 -080057
Thomas Vachuska61ab5e02016-01-11 13:55:15 -080058import static com.google.common.base.Preconditions.checkNotNull;
59import static org.slf4j.LoggerFactory.getLogger;
60
alshabibeff00542015-09-23 13:22:33 -070061/**
Julian Lawrencefa790f62016-01-07 17:18:30 -080062 * The multicast forwarding component. This component is responsible for
63 * handling live multicast traffic by modifying multicast state and forwarding
64 * packets that do not yet have state installed.
alshabibeff00542015-09-23 13:22:33 -070065 */
66@Component(immediate = true)
67public class McastForwarding {
68
69 private final Logger log = getLogger(getClass());
70 private final IpPrefix mcast = IpPrefix.valueOf("224.0.0.0/4");
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected PacketService packetService;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected CoreService coreService;
77
Julian Lawrencefa790f62016-01-07 17:18:30 -080078 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Julian Lawrence51731882016-01-19 17:24:15 -080079 private IntentService intentService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Julian Lawrencefa790f62016-01-07 17:18:30 -080082 protected MulticastRouteService mcastRouteManager;
83
Julian Lawrence51731882016-01-19 17:24:15 -080084 protected McastIntentManager mcastIntentManager;
Julian Lawrencefa790f62016-01-07 17:18:30 -080085
alshabibeff00542015-09-23 13:22:33 -070086 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabibeff00542015-09-23 13:22:33 -070087 private static ApplicationId appId;
88
89 /**
90 * Active MulticastForwardingIntent.
91 */
92 @Activate
93 public void activate() {
94 appId = coreService.registerApplication("org.onosproject.mfwd");
95
Julian Lawrence51731882016-01-19 17:24:15 -080096 mcastIntentManager = new McastIntentManager();
97 mcastRouteManager.addListener(mcastIntentManager);
98
alshabibeff00542015-09-23 13:22:33 -070099 packetService.addProcessor(processor, PacketProcessor.director(2));
100
101 // Build a traffic selector for all multicast traffic
102 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
103 selector.matchEthType(Ethernet.TYPE_IPV4);
104 selector.matchIPDst(mcast);
Julian Lawrencefa790f62016-01-07 17:18:30 -0800105
alshabibeff00542015-09-23 13:22:33 -0700106 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
107
alshabibeff00542015-09-23 13:22:33 -0700108 log.info("Started");
109 }
110
111 /**
112 * Deactivate Multicast Forwarding Intent.
113 */
114 @Deactivate
115 public void deactivate() {
116 packetService.removeProcessor(processor);
Julian Lawrence51731882016-01-19 17:24:15 -0800117 mcastRouteManager.removeListener(mcastIntentManager);
118 mcastIntentManager.withdrawAllIntents();
alshabibeff00542015-09-23 13:22:33 -0700119 processor = null;
120 log.info("Stopped");
121 }
122
123 /**
124 * Get the application ID, used by the McastIntentManager.
125 *
126 * @return the application ID
127 */
128 public static ApplicationId getAppId() {
129 return appId;
130 }
131
132 /**
Julian Lawrence51731882016-01-19 17:24:15 -0800133 * Forward the packet to it's multicast destinations.
134 *
135 * @param context The packet context
136 * @param egressList The list of egress ports which the multicast packet is intended for.
137 */
138 private void forwardPacketToDst(PacketContext context, ArrayList<ConnectPoint> egressList) {
139
140 // Send the pack out each of the respective egress ports
141 for (ConnectPoint egress : egressList) {
142 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
143 .setOutput(egress.port()).build();
144
145 OutboundPacket packet = new DefaultOutboundPacket(
146 egress.deviceId(),
147 treatment,
148 context.inPacket().unparsed());
149
150 packetService.emit(packet);
151 }
152 }
153
154 public static McastRoute createStaticRoute(String source, String group) {
155 checkNotNull(source, "Must provide a source");
156 checkNotNull(group, "Must provide a group");
157 IpAddress ipSource = IpAddress.valueOf(source);
158 IpAddress ipGroup = IpAddress.valueOf(group);
159 return createStaticcreateRoute(ipSource, ipGroup);
160 }
161
162 public static McastRoute createStaticcreateRoute(IpAddress source, IpAddress group) {
163 checkNotNull(source, "Must provide a source");
164 checkNotNull(group, "Must provide a group");
165 McastRoute.Type type = McastRoute.Type.STATIC;
166 return new McastRoute(source, group, type);
167 }
168
169 /**
alshabibeff00542015-09-23 13:22:33 -0700170 * Packet processor responsible for forwarding packets along their paths.
171 */
alshabibeff00542015-09-23 13:22:33 -0700172
Julian Lawrence51731882016-01-19 17:24:15 -0800173 private class ReactivePacketProcessor implements PacketProcessor {
alshabibeff00542015-09-23 13:22:33 -0700174 /**
175 * Process incoming packets.
176 *
177 * @param context packet processing context
178 */
179 @Override
180 public void process(PacketContext context) {
181 // Stop processing if the packet has been handled, since we
182 // can't do any more to it.
183 if (context.isHandled()) {
184 return;
185 }
186
187 InboundPacket pkt = context.inPacket();
188 Ethernet ethPkt = pkt.parsed();
189
190 if (ethPkt == null) {
191 return;
192 }
193
194 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4 &&
195 ethPkt.getEtherType() != Ethernet.TYPE_IPV6) {
196 return;
197 }
198
199 if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) {
200 // Ignore ipv6 at the moment.
201 return;
202 }
203
204 IPv4 ip = (IPv4) ethPkt.getPayload();
alshabibeff00542015-09-23 13:22:33 -0700205 IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
Julian Lawrencefa790f62016-01-07 17:18:30 -0800206 IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
alshabibeff00542015-09-23 13:22:33 -0700207
208 log.debug("Packet ({}, {}) has been punted\n" +
209 "\tingress port: {}\n",
210 saddr.toString(),
211 gaddr.toString(),
212 context.inPacket().receivedFrom().toString());
213
Julian Lawrencefa790f62016-01-07 17:18:30 -0800214 // Don't allow PIM/IGMP packets to be handled here.
215 byte proto = ip.getProtocol();
216 if (proto == IPv4.PROTOCOL_PIM || proto == IPv4.PROTOCOL_IGMP) {
alshabibeff00542015-09-23 13:22:33 -0700217 return;
218 }
219
220 IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
221 IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
222
Julian Lawrencefa790f62016-01-07 17:18:30 -0800223 // TODO do we want to add a type for Mcast?
Thomas Vachuska61ab5e02016-01-11 13:55:15 -0800224 McastRoute mRoute = new McastRoute(saddr, gaddr, McastRoute.Type.STATIC);
alshabibeff00542015-09-23 13:22:33 -0700225
Julian Lawrencefa790f62016-01-07 17:18:30 -0800226 ConnectPoint ingress = mcastRouteManager.fetchSource(mRoute);
227
228 // An ingress port already exists. Log error.
229 if (ingress != null) {
230 log.error(McastForwarding.class.getSimpleName() + " received packet which already has a route.");
231 return;
232 } else {
233 //add ingress port
234 mcastRouteManager.addSource(mRoute, pkt.receivedFrom());
alshabibeff00542015-09-23 13:22:33 -0700235 }
236
Julian Lawrencefa790f62016-01-07 17:18:30 -0800237 ArrayList<ConnectPoint> egressList = (ArrayList<ConnectPoint>) mcastRouteManager.fetchSinks(mRoute);
238 //If there are no egress ports set return, otherwise forward the packets to their expected port.
239 if (egressList.size() == 0) {
alshabibeff00542015-09-23 13:22:33 -0700240 return;
241 }
242
alshabibeff00542015-09-23 13:22:33 -0700243 // Send the pack out each of the egress devices & port
Julian Lawrencefa790f62016-01-07 17:18:30 -0800244 forwardPacketToDst(context, egressList);
alshabibeff00542015-09-23 13:22:33 -0700245 }
Julian Lawrence51731882016-01-19 17:24:15 -0800246
alshabibeff00542015-09-23 13:22:33 -0700247 }
248
Julian Lawrence51731882016-01-19 17:24:15 -0800249 private class McastIntentManager implements McastListener {
alshabibeff00542015-09-23 13:22:33 -0700250
Julian Lawrence51731882016-01-19 17:24:15 -0800251 private Map<McastRoute, Key> intentHashMap;
alshabibeff00542015-09-23 13:22:33 -0700252
Julian Lawrence51731882016-01-19 17:24:15 -0800253 public McastIntentManager() {
254 intentHashMap = new HashMap<>();
alshabibeff00542015-09-23 13:22:33 -0700255 }
Julian Lawrencefa790f62016-01-07 17:18:30 -0800256
Julian Lawrence51731882016-01-19 17:24:15 -0800257 @Override
258 public void event(McastEvent event) {
259 McastRoute route = event.subject().route();
260 if (intentHashMap.containsKey(route)) {
261 withdrawIntent(intentHashMap.get(route));
262 }
263 Key routeKey = setIntent(route);
264 intentHashMap.put(route, routeKey);
265 }
Julian Lawrencefa790f62016-01-07 17:18:30 -0800266
Julian Lawrence51731882016-01-19 17:24:15 -0800267 private Key setIntent(McastRoute route) {
268
269 ConnectPoint ingressPoint = mcastRouteManager.fetchSource(route);
270 Set<ConnectPoint> egressPoints = new HashSet<>(mcastRouteManager.fetchSinks(route));
271
272 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
273 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
274
275 if (ingressPoint == null) {
276 log.warn("Can't set intent without an ingress or egress connect points");
277 return null;
278 }
279
280 selector.matchEthType(Ethernet.TYPE_IPV4)
281 .matchIPDst(route.group().toIpPrefix())
282 .matchIPSrc(route.source().toIpPrefix());
283
284 SinglePointToMultiPointIntent.Builder builder = SinglePointToMultiPointIntent.builder()
285 .appId(appId)
286 .selector(selector.build())
287 .treatment(treatment)
288 .ingressPoint(ingressPoint);
289
290 // allowing intent to be pushed without egress points means we can drop packets.
291 if (!egressPoints.isEmpty()) {
292 builder.egressPoints(egressPoints);
293 }
294
295 SinglePointToMultiPointIntent intent = builder.build();
296 intentService.submit(intent);
297
298 return intent.key();
299 }
300
301 public void withdrawAllIntents() {
302 for (Map.Entry<McastRoute, Key> entry : intentHashMap.entrySet()) {
303 withdrawIntent(entry.getValue());
304 }
305 intentHashMap.clear();
306 }
307
308 public void withdrawIntent(Key key) {
309 if (key == null) {
310 // nothing to withdraw
311 return;
312 }
313 Intent intent = intentService.getIntent(key);
314 intentService.withdraw(intent);
315 }
Julian Lawrencefa790f62016-01-07 17:18:30 -0800316 }
alshabibeff00542015-09-23 13:22:33 -0700317}