blob: 16d7da3e846040b35e6ff7b24c1fc665ac202bd3 [file] [log] [blame]
alshabibeff00542015-09-23 13:22:33 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
alshabibeff00542015-09-23 13:22:33 -07003 *
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());
alshabibeff00542015-09-23 13:22:33 -070070
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected PacketService packetService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected CoreService coreService;
76
Julian Lawrencefa790f62016-01-07 17:18:30 -080077 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Julian Lawrence51731882016-01-19 17:24:15 -080078 private IntentService intentService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Julian Lawrencefa790f62016-01-07 17:18:30 -080081 protected MulticastRouteService mcastRouteManager;
82
Julian Lawrence51731882016-01-19 17:24:15 -080083 protected McastIntentManager mcastIntentManager;
Julian Lawrencefa790f62016-01-07 17:18:30 -080084
alshabibeff00542015-09-23 13:22:33 -070085 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabibeff00542015-09-23 13:22:33 -070086 private static ApplicationId appId;
87
88 /**
89 * Active MulticastForwardingIntent.
90 */
91 @Activate
92 public void activate() {
93 appId = coreService.registerApplication("org.onosproject.mfwd");
94
Julian Lawrence51731882016-01-19 17:24:15 -080095 mcastIntentManager = new McastIntentManager();
96 mcastRouteManager.addListener(mcastIntentManager);
97
alshabibeff00542015-09-23 13:22:33 -070098 packetService.addProcessor(processor, PacketProcessor.director(2));
99
100 // Build a traffic selector for all multicast traffic
101 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
102 selector.matchEthType(Ethernet.TYPE_IPV4);
Charles Chanaedabfd2016-02-26 09:31:48 -0800103 selector.matchIPDst(IpPrefix.IPV4_MULTICAST_PREFIX);
Julian Lawrencefa790f62016-01-07 17:18:30 -0800104
alshabibeff00542015-09-23 13:22:33 -0700105 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
106
alshabibeff00542015-09-23 13:22:33 -0700107 log.info("Started");
108 }
109
110 /**
111 * Deactivate Multicast Forwarding Intent.
112 */
113 @Deactivate
114 public void deactivate() {
115 packetService.removeProcessor(processor);
Julian Lawrence51731882016-01-19 17:24:15 -0800116 mcastRouteManager.removeListener(mcastIntentManager);
117 mcastIntentManager.withdrawAllIntents();
alshabibeff00542015-09-23 13:22:33 -0700118 processor = null;
119 log.info("Stopped");
120 }
121
122 /**
123 * Get the application ID, used by the McastIntentManager.
124 *
125 * @return the application ID
126 */
127 public static ApplicationId getAppId() {
128 return appId;
129 }
130
131 /**
Julian Lawrence51731882016-01-19 17:24:15 -0800132 * Forward the packet to it's multicast destinations.
133 *
134 * @param context The packet context
135 * @param egressList The list of egress ports which the multicast packet is intended for.
136 */
137 private void forwardPacketToDst(PacketContext context, ArrayList<ConnectPoint> egressList) {
138
139 // Send the pack out each of the respective egress ports
140 for (ConnectPoint egress : egressList) {
141 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
142 .setOutput(egress.port()).build();
143
144 OutboundPacket packet = new DefaultOutboundPacket(
145 egress.deviceId(),
146 treatment,
147 context.inPacket().unparsed());
148
149 packetService.emit(packet);
150 }
151 }
152
153 public static McastRoute createStaticRoute(String source, String group) {
154 checkNotNull(source, "Must provide a source");
155 checkNotNull(group, "Must provide a group");
156 IpAddress ipSource = IpAddress.valueOf(source);
157 IpAddress ipGroup = IpAddress.valueOf(group);
158 return createStaticcreateRoute(ipSource, ipGroup);
159 }
160
161 public static McastRoute createStaticcreateRoute(IpAddress source, IpAddress group) {
162 checkNotNull(source, "Must provide a source");
163 checkNotNull(group, "Must provide a group");
164 McastRoute.Type type = McastRoute.Type.STATIC;
165 return new McastRoute(source, group, type);
166 }
167
168 /**
alshabibeff00542015-09-23 13:22:33 -0700169 * Packet processor responsible for forwarding packets along their paths.
170 */
alshabibeff00542015-09-23 13:22:33 -0700171
Julian Lawrence51731882016-01-19 17:24:15 -0800172 private class ReactivePacketProcessor implements PacketProcessor {
alshabibeff00542015-09-23 13:22:33 -0700173 /**
174 * Process incoming packets.
175 *
176 * @param context packet processing context
177 */
178 @Override
179 public void process(PacketContext context) {
180 // Stop processing if the packet has been handled, since we
181 // can't do any more to it.
182 if (context.isHandled()) {
183 return;
184 }
185
186 InboundPacket pkt = context.inPacket();
187 Ethernet ethPkt = pkt.parsed();
188
189 if (ethPkt == null) {
190 return;
191 }
192
193 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4 &&
194 ethPkt.getEtherType() != Ethernet.TYPE_IPV6) {
195 return;
196 }
197
198 if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) {
199 // Ignore ipv6 at the moment.
200 return;
201 }
202
203 IPv4 ip = (IPv4) ethPkt.getPayload();
alshabibeff00542015-09-23 13:22:33 -0700204 IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
Julian Lawrencefa790f62016-01-07 17:18:30 -0800205 IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
alshabibeff00542015-09-23 13:22:33 -0700206
207 log.debug("Packet ({}, {}) has been punted\n" +
208 "\tingress port: {}\n",
209 saddr.toString(),
210 gaddr.toString(),
211 context.inPacket().receivedFrom().toString());
212
Julian Lawrencefa790f62016-01-07 17:18:30 -0800213 // Don't allow PIM/IGMP packets to be handled here.
214 byte proto = ip.getProtocol();
215 if (proto == IPv4.PROTOCOL_PIM || proto == IPv4.PROTOCOL_IGMP) {
alshabibeff00542015-09-23 13:22:33 -0700216 return;
217 }
218
219 IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
220 IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
221
Julian Lawrencefa790f62016-01-07 17:18:30 -0800222 // TODO do we want to add a type for Mcast?
Thomas Vachuska61ab5e02016-01-11 13:55:15 -0800223 McastRoute mRoute = new McastRoute(saddr, gaddr, McastRoute.Type.STATIC);
alshabibeff00542015-09-23 13:22:33 -0700224
Julian Lawrencefa790f62016-01-07 17:18:30 -0800225 ConnectPoint ingress = mcastRouteManager.fetchSource(mRoute);
226
227 // An ingress port already exists. Log error.
228 if (ingress != null) {
229 log.error(McastForwarding.class.getSimpleName() + " received packet which already has a route.");
230 return;
231 } else {
232 //add ingress port
233 mcastRouteManager.addSource(mRoute, pkt.receivedFrom());
alshabibeff00542015-09-23 13:22:33 -0700234 }
235
Julian Lawrencefa790f62016-01-07 17:18:30 -0800236 ArrayList<ConnectPoint> egressList = (ArrayList<ConnectPoint>) mcastRouteManager.fetchSinks(mRoute);
237 //If there are no egress ports set return, otherwise forward the packets to their expected port.
Jon Hallcbd1b392017-01-18 20:15:44 -0800238 if (egressList.isEmpty()) {
alshabibeff00542015-09-23 13:22:33 -0700239 return;
240 }
241
alshabibeff00542015-09-23 13:22:33 -0700242 // Send the pack out each of the egress devices & port
Julian Lawrencefa790f62016-01-07 17:18:30 -0800243 forwardPacketToDst(context, egressList);
alshabibeff00542015-09-23 13:22:33 -0700244 }
Julian Lawrence51731882016-01-19 17:24:15 -0800245
alshabibeff00542015-09-23 13:22:33 -0700246 }
247
Julian Lawrence51731882016-01-19 17:24:15 -0800248 private class McastIntentManager implements McastListener {
alshabibeff00542015-09-23 13:22:33 -0700249
Julian Lawrence51731882016-01-19 17:24:15 -0800250 private Map<McastRoute, Key> intentHashMap;
alshabibeff00542015-09-23 13:22:33 -0700251
Julian Lawrence51731882016-01-19 17:24:15 -0800252 public McastIntentManager() {
253 intentHashMap = new HashMap<>();
alshabibeff00542015-09-23 13:22:33 -0700254 }
Julian Lawrencefa790f62016-01-07 17:18:30 -0800255
Julian Lawrence51731882016-01-19 17:24:15 -0800256 @Override
257 public void event(McastEvent event) {
258 McastRoute route = event.subject().route();
259 if (intentHashMap.containsKey(route)) {
260 withdrawIntent(intentHashMap.get(route));
261 }
262 Key routeKey = setIntent(route);
263 intentHashMap.put(route, routeKey);
264 }
Julian Lawrencefa790f62016-01-07 17:18:30 -0800265
Julian Lawrence51731882016-01-19 17:24:15 -0800266 private Key setIntent(McastRoute route) {
267
268 ConnectPoint ingressPoint = mcastRouteManager.fetchSource(route);
269 Set<ConnectPoint> egressPoints = new HashSet<>(mcastRouteManager.fetchSinks(route));
270
271 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
272 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
273
274 if (ingressPoint == null) {
275 log.warn("Can't set intent without an ingress or egress connect points");
276 return null;
277 }
278
279 selector.matchEthType(Ethernet.TYPE_IPV4)
280 .matchIPDst(route.group().toIpPrefix())
281 .matchIPSrc(route.source().toIpPrefix());
282
283 SinglePointToMultiPointIntent.Builder builder = SinglePointToMultiPointIntent.builder()
284 .appId(appId)
285 .selector(selector.build())
286 .treatment(treatment)
287 .ingressPoint(ingressPoint);
288
289 // allowing intent to be pushed without egress points means we can drop packets.
290 if (!egressPoints.isEmpty()) {
291 builder.egressPoints(egressPoints);
292 }
293
294 SinglePointToMultiPointIntent intent = builder.build();
295 intentService.submit(intent);
296
297 return intent.key();
298 }
299
300 public void withdrawAllIntents() {
301 for (Map.Entry<McastRoute, Key> entry : intentHashMap.entrySet()) {
302 withdrawIntent(entry.getValue());
303 }
304 intentHashMap.clear();
305 }
306
307 public void withdrawIntent(Key key) {
308 if (key == null) {
309 // nothing to withdraw
310 return;
311 }
312 Intent intent = intentService.getIntent(key);
313 intentService.withdraw(intent);
314 }
Julian Lawrencefa790f62016-01-07 17:18:30 -0800315 }
alshabibeff00542015-09-23 13:22:33 -0700316}