blob: cee28702d8f2fa76dbf037476e3c81c6ceb2880f [file] [log] [blame]
alshabibeff00542015-09-23 13:22:33 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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
Ray Milkey4f7e3632019-02-19 15:35:20 -080018import org.onosproject.net.FilteredConnectPoint;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import org.osgi.service.component.annotations.Activate;
20import org.osgi.service.component.annotations.Component;
21import org.osgi.service.component.annotations.Deactivate;
22import org.osgi.service.component.annotations.Reference;
23import org.osgi.service.component.annotations.ReferenceCardinality;
alshabibeff00542015-09-23 13:22:33 -070024import org.onlab.packet.Ethernet;
alshabibeff00542015-09-23 13:22:33 -070025import org.onlab.packet.IPv4;
Thomas Vachuska61ab5e02016-01-11 13:55:15 -080026import org.onlab.packet.Ip4Address;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
alshabibeff00542015-09-23 13:22:33 -070029import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
31import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.flow.DefaultTrafficSelector;
33import org.onosproject.net.flow.DefaultTrafficTreatment;
34import org.onosproject.net.flow.TrafficSelector;
35import org.onosproject.net.flow.TrafficTreatment;
Julian Lawrence51731882016-01-19 17:24:15 -080036import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentService;
38import org.onosproject.net.intent.Key;
39import org.onosproject.net.intent.SinglePointToMultiPointIntent;
Julian Lawrencefa790f62016-01-07 17:18:30 -080040import org.onosproject.net.mcast.McastRoute;
Julian Lawrence51731882016-01-19 17:24:15 -080041import org.onosproject.net.mcast.McastListener;
42import org.onosproject.net.mcast.McastEvent;
Thomas Vachuska61ab5e02016-01-11 13:55:15 -080043import org.onosproject.net.mcast.MulticastRouteService;
alshabibeff00542015-09-23 13:22:33 -070044import org.onosproject.net.packet.DefaultOutboundPacket;
45import org.onosproject.net.packet.InboundPacket;
46import org.onosproject.net.packet.OutboundPacket;
47import org.onosproject.net.packet.PacketContext;
48import org.onosproject.net.packet.PacketPriority;
49import org.onosproject.net.packet.PacketProcessor;
50import org.onosproject.net.packet.PacketService;
51import org.slf4j.Logger;
52
Julian Lawrencefa790f62016-01-07 17:18:30 -080053import java.util.ArrayList;
Julian Lawrence51731882016-01-19 17:24:15 -080054import java.util.HashMap;
55import java.util.HashSet;
56import java.util.Map;
57import java.util.Set;
Julian Lawrencefa790f62016-01-07 17:18:30 -080058
Thomas Vachuska61ab5e02016-01-11 13:55:15 -080059import static com.google.common.base.Preconditions.checkNotNull;
60import static org.slf4j.LoggerFactory.getLogger;
61
alshabibeff00542015-09-23 13:22:33 -070062/**
Julian Lawrencefa790f62016-01-07 17:18:30 -080063 * The multicast forwarding component. This component is responsible for
64 * handling live multicast traffic by modifying multicast state and forwarding
65 * packets that do not yet have state installed.
alshabibeff00542015-09-23 13:22:33 -070066 */
67@Component(immediate = true)
68public class McastForwarding {
69
70 private final Logger log = getLogger(getClass());
alshabibeff00542015-09-23 13:22:33 -070071
Ray Milkeyd84f89b2018-08-17 14:54:17 -070072 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibeff00542015-09-23 13:22:33 -070073 protected PacketService packetService;
74
Ray Milkeyd84f89b2018-08-17 14:54:17 -070075 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibeff00542015-09-23 13:22:33 -070076 protected CoreService coreService;
77
Ray Milkeyd84f89b2018-08-17 14:54:17 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Julian Lawrence51731882016-01-19 17:24:15 -080079 private IntentService intentService;
80
Ray Milkeyd84f89b2018-08-17 14:54:17 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY)
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);
Charles Chanaedabfd2016-02-26 09:31:48 -0800104 selector.matchIPDst(IpPrefix.IPV4_MULTICAST_PREFIX);
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.
Jon Hallcbd1b392017-01-18 20:15:44 -0800239 if (egressList.isEmpty()) {
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);
Ray Milkey4f7e3632019-02-19 15:35:20 -0800270 Set<FilteredConnectPoint> filteredEgressPoints = new HashSet<>();
271 mcastRouteManager.fetchSinks(route).iterator()
272 .forEachRemaining(point -> filteredEgressPoints.add(new FilteredConnectPoint(point)));
273
Julian Lawrence51731882016-01-19 17:24:15 -0800274
275 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
276 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
277
278 if (ingressPoint == null) {
279 log.warn("Can't set intent without an ingress or egress connect points");
280 return null;
281 }
282
283 selector.matchEthType(Ethernet.TYPE_IPV4)
284 .matchIPDst(route.group().toIpPrefix())
285 .matchIPSrc(route.source().toIpPrefix());
286
287 SinglePointToMultiPointIntent.Builder builder = SinglePointToMultiPointIntent.builder()
288 .appId(appId)
289 .selector(selector.build())
290 .treatment(treatment)
Ray Milkey4f7e3632019-02-19 15:35:20 -0800291 .filteredIngressPoint(new FilteredConnectPoint(ingressPoint));
Julian Lawrence51731882016-01-19 17:24:15 -0800292
293 // allowing intent to be pushed without egress points means we can drop packets.
Ray Milkey4f7e3632019-02-19 15:35:20 -0800294 if (!filteredEgressPoints.isEmpty()) {
295 builder.filteredEgressPoints(filteredEgressPoints);
Julian Lawrence51731882016-01-19 17:24:15 -0800296 }
297
298 SinglePointToMultiPointIntent intent = builder.build();
299 intentService.submit(intent);
300
301 return intent.key();
302 }
303
304 public void withdrawAllIntents() {
305 for (Map.Entry<McastRoute, Key> entry : intentHashMap.entrySet()) {
306 withdrawIntent(entry.getValue());
307 }
308 intentHashMap.clear();
309 }
310
311 public void withdrawIntent(Key key) {
312 if (key == null) {
313 // nothing to withdraw
314 return;
315 }
316 Intent intent = intentService.getIntent(key);
317 intentService.withdraw(intent);
318 }
Julian Lawrencefa790f62016-01-07 17:18:30 -0800319 }
alshabibeff00542015-09-23 13:22:33 -0700320}