blob: 3ade4197ea59642b0db33431352680a8533c628e [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 */
alshabib79e52872015-12-07 16:01:01 -080016package org.onosproject.igmp;
alshabibeff00542015-09-23 13:22:33 -070017
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;
alshabib79e52872015-12-07 16:01:01 -080021import org.apache.felix.scr.annotations.Property;
alshabibeff00542015-09-23 13:22:33 -070022import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib03adb492016-02-01 17:25:00 -080024import org.onlab.packet.EthType;
alshabibeff00542015-09-23 13:22:33 -070025import org.onlab.packet.Ethernet;
alshabib03adb492016-02-01 17:25:00 -080026import org.onlab.packet.IGMP;
alshabibeff00542015-09-23 13:22:33 -070027import org.onlab.packet.IPv4;
28import org.onlab.packet.Ip4Address;
29import org.onlab.packet.IpAddress;
30import org.onlab.packet.IpPrefix;
alshabibeff00542015-09-23 13:22:33 -070031import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
alshabib79e52872015-12-07 16:01:01 -080033import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.DeviceId;
alshabib03adb492016-02-01 17:25:00 -080035import org.onosproject.net.Port;
36import org.onosproject.net.PortNumber;
alshabibfe69e9a2016-02-11 17:31:36 -080037import org.onosproject.net.config.ConfigFactory;
38import org.onosproject.net.config.NetworkConfigEvent;
39import org.onosproject.net.config.NetworkConfigListener;
alshabib79e52872015-12-07 16:01:01 -080040import org.onosproject.net.config.NetworkConfigRegistry;
alshabibfe69e9a2016-02-11 17:31:36 -080041import org.onosproject.net.config.basics.SubjectFactories;
alshabib03adb492016-02-01 17:25:00 -080042import org.onosproject.net.device.DeviceEvent;
43import org.onosproject.net.device.DeviceListener;
44import org.onosproject.net.device.DeviceService;
45import org.onosproject.net.flow.DefaultTrafficTreatment;
46import org.onosproject.net.flow.criteria.Criteria;
47import org.onosproject.net.flowobjective.DefaultFilteringObjective;
48import org.onosproject.net.flowobjective.FilteringObjective;
49import org.onosproject.net.flowobjective.FlowObjectiveService;
50import org.onosproject.net.flowobjective.Objective;
51import org.onosproject.net.flowobjective.ObjectiveContext;
52import org.onosproject.net.flowobjective.ObjectiveError;
alshabib79e52872015-12-07 16:01:01 -080053import org.onosproject.net.mcast.McastRoute;
54import org.onosproject.net.mcast.MulticastRouteService;
alshabibeff00542015-09-23 13:22:33 -070055import org.onosproject.net.packet.InboundPacket;
56import org.onosproject.net.packet.PacketContext;
alshabibeff00542015-09-23 13:22:33 -070057import org.onosproject.net.packet.PacketProcessor;
58import org.onosproject.net.packet.PacketService;
alshabib03adb492016-02-01 17:25:00 -080059import org.onosproject.olt.AccessDeviceConfig;
60import org.onosproject.olt.AccessDeviceData;
alshabibeff00542015-09-23 13:22:33 -070061import org.slf4j.Logger;
62
alshabibfe69e9a2016-02-11 17:31:36 -080063import java.util.List;
alshabib03adb492016-02-01 17:25:00 -080064import java.util.Map;
65import java.util.concurrent.ConcurrentHashMap;
66
67import static org.slf4j.LoggerFactory.getLogger;
alshabib79e52872015-12-07 16:01:01 -080068
alshabibeff00542015-09-23 13:22:33 -070069/**
70 * Internet Group Management Protocol.
71 */
72@Component(immediate = true)
alshabib79e52872015-12-07 16:01:01 -080073public class IgmpSnoop {
alshabibeff00542015-09-23 13:22:33 -070074 private final Logger log = getLogger(getClass());
75
alshabib79e52872015-12-07 16:01:01 -080076 private static final String DEFAULT_MCAST_ADDR = "224.0.0.0/4";
77
78 @Property(name = "multicastAddress",
79 label = "Define the multicast base raneg to listen to")
80 private String multicastAddress = DEFAULT_MCAST_ADDR;
81
alshabibeff00542015-09-23 13:22:33 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib03adb492016-02-01 17:25:00 -080083 protected FlowObjectiveService flowObjectiveService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibeff00542015-09-23 13:22:33 -070086 protected PacketService packetService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected CoreService coreService;
90
alshabib79e52872015-12-07 16:01:01 -080091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected NetworkConfigRegistry networkConfig;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected MulticastRouteService multicastService;
96
alshabib03adb492016-02-01 17:25:00 -080097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected DeviceService deviceService;
99
100 private Map<DeviceId, AccessDeviceData> oltData = new ConcurrentHashMap<>();
101
102 private DeviceListener deviceListener = new InternalDeviceListener();
alshabib79e52872015-12-07 16:01:01 -0800103 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
alshabibeff00542015-09-23 13:22:33 -0700104 private static ApplicationId appId;
105
alshabibfe69e9a2016-02-11 17:31:36 -0800106 private InternalNetworkConfigListener configListener =
107 new InternalNetworkConfigListener();
108
109 private static final Class<AccessDeviceConfig> CONFIG_CLASS =
110 AccessDeviceConfig.class;
111
112 private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory =
113 new ConfigFactory<DeviceId, AccessDeviceConfig>(
114 SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") {
115 @Override
116 public AccessDeviceConfig createConfig() {
117 return new AccessDeviceConfig();
118 }
119 };
120
121
alshabibeff00542015-09-23 13:22:33 -0700122 @Activate
123 public void activate() {
124 appId = coreService.registerApplication("org.onosproject.igmp");
125
126 packetService.addProcessor(processor, PacketProcessor.director(1));
127
alshabibfe69e9a2016-02-11 17:31:36 -0800128 networkConfig.registerConfigFactory(configFactory);
129 networkConfig.addListener(configListener);
130
alshabib03adb492016-02-01 17:25:00 -0800131 networkConfig.getSubjects(DeviceId.class, AccessDeviceConfig.class).forEach(
alshabib79e52872015-12-07 16:01:01 -0800132 subject -> {
alshabib03adb492016-02-01 17:25:00 -0800133 AccessDeviceConfig config = networkConfig.getConfig(subject,
134 AccessDeviceConfig.class);
alshabib79e52872015-12-07 16:01:01 -0800135 if (config != null) {
alshabib03adb492016-02-01 17:25:00 -0800136 AccessDeviceData data = config.getOlt();
137 oltData.put(data.deviceId(), data);
alshabibfe69e9a2016-02-11 17:31:36 -0800138
alshabib79e52872015-12-07 16:01:01 -0800139 }
140 }
141 );
alshabibeff00542015-09-23 13:22:33 -0700142
alshabibfe69e9a2016-02-11 17:31:36 -0800143 oltData.keySet().stream()
144 .flatMap(did -> deviceService.getPorts(did).stream())
145 .filter(p -> !oltData.get(p.element().id()).uplink().equals(p.number()))
146 .filter(p -> p.isEnabled())
147 .forEach(p -> processFilterObjective((DeviceId) p.element().id(), p, false));
148
alshabib03adb492016-02-01 17:25:00 -0800149 deviceService.addListener(deviceListener);
150
alshabibeff00542015-09-23 13:22:33 -0700151 log.info("Started");
152 }
153
154 @Deactivate
155 public void deactivate() {
156 packetService.removeProcessor(processor);
157 processor = null;
alshabib03adb492016-02-01 17:25:00 -0800158 deviceService.removeListener(deviceListener);
alshabibfe69e9a2016-02-11 17:31:36 -0800159 networkConfig.removeListener(configListener);
160 networkConfig.unregisterConfigFactory(configFactory);
alshabibeff00542015-09-23 13:22:33 -0700161 log.info("Stopped");
162 }
163
alshabib03adb492016-02-01 17:25:00 -0800164 private void processFilterObjective(DeviceId devId, Port port, boolean remove) {
alshabib79e52872015-12-07 16:01:01 -0800165
alshabib03adb492016-02-01 17:25:00 -0800166 //TODO migrate to packet requests when packet service uses filtering objectives
167 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
168
169 builder = remove ? builder.deny() : builder.permit();
170
171 FilteringObjective igmp = builder
172 .withKey(Criteria.matchInPort(port.number()))
173 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
174 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
175 .withMeta(DefaultTrafficTreatment.builder()
176 .setOutput(PortNumber.CONTROLLER).build())
177 .fromApp(appId)
178 .withPriority(1000)
179 .add(new ObjectiveContext() {
180 @Override
181 public void onSuccess(Objective objective) {
182 log.info("Igmp filter for {} on {} installed.",
183 devId, port);
184 }
185
186 @Override
187 public void onError(Objective objective, ObjectiveError error) {
188 log.info("Igmp filter for {} on {} failed because {}.",
189 devId, port, error);
190 }
191 });
192
193 flowObjectiveService.filter(devId, igmp);
194 }
195
196 private void processQuery(IGMP pkt, ConnectPoint location) {
197 pkt.getGroups().forEach(group -> group.getSources().forEach(src -> {
198
199 McastRoute route = new McastRoute(src,
200 group.getGaddr(),
201 McastRoute.Type.IGMP);
202 multicastService.add(route);
203 multicastService.addSink(route, location);
204
205 }));
alshabib79e52872015-12-07 16:01:01 -0800206 }
207
alshabibeff00542015-09-23 13:22:33 -0700208 /**
209 * Packet processor responsible for handling IGMP packets.
210 */
alshabib79e52872015-12-07 16:01:01 -0800211 private class IgmpPacketProcessor implements PacketProcessor {
alshabibeff00542015-09-23 13:22:33 -0700212
213 @Override
214 public void process(PacketContext context) {
215 // Stop processing if the packet has been handled, since we
216 // can't do any more to it.
217 if (context.isHandled()) {
218 return;
219 }
220
221 InboundPacket pkt = context.inPacket();
222 Ethernet ethPkt = pkt.parsed();
223 if (ethPkt == null) {
224 return;
225 }
226
227 /*
228 * IPv6 MLD packets are handled by ICMP6. We'll only deal
229 * with IPv4.
230 */
231 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
232 return;
233 }
234
235 IPv4 ip = (IPv4) ethPkt.getPayload();
236 IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
237 IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
alshabib79e52872015-12-07 16:01:01 -0800238 log.debug("Packet ({}, {}) -> ingress port: {}", saddr, gaddr,
239 context.inPacket().receivedFrom());
240
alshabibeff00542015-09-23 13:22:33 -0700241
242 if (ip.getProtocol() != IPv4.PROTOCOL_IGMP) {
Rusty Eddy158d5d82015-10-12 16:59:04 -0700243 log.debug("IGMP Picked up a non IGMP packet.");
alshabibeff00542015-09-23 13:22:33 -0700244 return;
245 }
246
alshabib79e52872015-12-07 16:01:01 -0800247 IpPrefix mcast = IpPrefix.valueOf(DEFAULT_MCAST_ADDR);
alshabibeff00542015-09-23 13:22:33 -0700248 if (!mcast.contains(gaddr)) {
Rusty Eddy158d5d82015-10-12 16:59:04 -0700249 log.debug("IGMP Picked up a non multicast packet.");
alshabibeff00542015-09-23 13:22:33 -0700250 return;
251 }
252
253 if (mcast.contains(saddr)) {
Rusty Eddy158d5d82015-10-12 16:59:04 -0700254 log.debug("IGMP Picked up a packet with a multicast source address.");
alshabibeff00542015-09-23 13:22:33 -0700255 return;
256 }
alshabibeff00542015-09-23 13:22:33 -0700257
258 IGMP igmp = (IGMP) ip.getPayload();
259 switch (igmp.getIgmpType()) {
260
261 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
262 IGMPProcessMembership.processMembership(igmp, pkt.receivedFrom());
263 break;
264
265 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
alshabib79e52872015-12-07 16:01:01 -0800266 processQuery(igmp, pkt.receivedFrom());
alshabibeff00542015-09-23 13:22:33 -0700267 break;
268
269 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
270 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
271 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
272 log.debug("IGMP version 1 & 2 message types are not currently supported. Message type: " +
alshabib79e52872015-12-07 16:01:01 -0800273 igmp.getIgmpType());
alshabibeff00542015-09-23 13:22:33 -0700274 break;
275
276 default:
277 log.debug("Unkown IGMP message type: " + igmp.getIgmpType());
278 break;
279 }
280 }
281 }
alshabib79e52872015-12-07 16:01:01 -0800282
alshabib79e52872015-12-07 16:01:01 -0800283
alshabib03adb492016-02-01 17:25:00 -0800284 private class InternalDeviceListener implements DeviceListener {
285 @Override
286 public void event(DeviceEvent event) {
287 switch (event.type()) {
288
289 case DEVICE_ADDED:
290 case DEVICE_UPDATED:
291 case DEVICE_REMOVED:
292 case DEVICE_SUSPENDED:
293 case DEVICE_AVAILABILITY_CHANGED:
294 case PORT_STATS_UPDATED:
295 break;
296 case PORT_ADDED:
297 if (event.port().isEnabled()) {
298 processFilterObjective(event.subject().id(), event.port(), false);
299 }
300 break;
301 case PORT_UPDATED:
302 if (event.port().isEnabled()) {
303 processFilterObjective(event.subject().id(), event.port(), false);
304 } else {
305 processFilterObjective(event.subject().id(), event.port(), true);
306 }
307 break;
308 case PORT_REMOVED:
alshabibfe69e9a2016-02-11 17:31:36 -0800309 processFilterObjective(event.subject().id(), event.port(), true);
alshabib03adb492016-02-01 17:25:00 -0800310 break;
311 default:
312 log.warn("Unknown device event {}", event.type());
313 break;
314 }
315
316 }
317
318 @Override
319 public boolean isRelevant(DeviceEvent event) {
320 return oltData.containsKey(event.subject().id());
321 }
alshabib79e52872015-12-07 16:01:01 -0800322 }
alshabibfe69e9a2016-02-11 17:31:36 -0800323
324 private class InternalNetworkConfigListener implements NetworkConfigListener {
325 @Override
326 public void event(NetworkConfigEvent event) {
327 switch (event.type()) {
328
329 case CONFIG_ADDED:
330 case CONFIG_UPDATED:
331 if (event.configClass().equals(CONFIG_CLASS)) {
332 AccessDeviceConfig config =
333 networkConfig.getConfig((DeviceId) event.subject(), CONFIG_CLASS);
334 if (config != null) {
335 oltData.put(config.getOlt().deviceId(), config.getOlt());
336 provisionDefaultFlows((DeviceId) event.subject());
337 }
338 }
339 break;
340 case CONFIG_UNREGISTERED:
341 case CONFIG_REMOVED:
342 default:
343 break;
344 }
345 }
346 }
347
348 private void provisionDefaultFlows(DeviceId deviceId) {
349 List<Port> ports = deviceService.getPorts(deviceId);
350
351 ports.stream()
352 .filter(p -> !oltData.get(p.element().id()).uplink().equals(p.number()))
353 .filter(p -> p.isEnabled())
354 .forEach(p -> processFilterObjective((DeviceId) p.element().id(), p, false));
355
356 }
alshabibeff00542015-09-23 13:22:33 -0700357}