blob: 2b9a2a54e3c8888f7ebf4351c80396828bdf587b [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
18import static org.slf4j.LoggerFactory.getLogger;
19
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.IpPrefix;
28import org.onlab.packet.IpAddress;
29import org.onlab.packet.Ip4Address;
30import org.onlab.packet.IPv4;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.packet.DefaultOutboundPacket;
39import org.onosproject.net.packet.InboundPacket;
40import org.onosproject.net.packet.OutboundPacket;
41import org.onosproject.net.packet.PacketContext;
42import org.onosproject.net.packet.PacketPriority;
43import org.onosproject.net.packet.PacketProcessor;
44import org.onosproject.net.packet.PacketService;
45import org.slf4j.Logger;
46
47/**
48 * WORK-IN-PROGRESS: The multicast forwarding application using intent framework.
49 */
50@Component(immediate = true)
51public class McastForwarding {
52
53 private final Logger log = getLogger(getClass());
54 private final IpPrefix mcast = IpPrefix.valueOf("224.0.0.0/4");
55
56 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
57 protected PacketService packetService;
58
59 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
60 protected CoreService coreService;
61
62 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
63 private McastRouteTable mrib;
64 private static ApplicationId appId;
65
66 /**
67 * Active MulticastForwardingIntent.
68 */
69 @Activate
70 public void activate() {
71 appId = coreService.registerApplication("org.onosproject.mfwd");
72
73 packetService.addProcessor(processor, PacketProcessor.director(2));
74
75 // Build a traffic selector for all multicast traffic
76 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
77 selector.matchEthType(Ethernet.TYPE_IPV4);
78 selector.matchIPDst(mcast);
79 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
80
81 mrib = McastRouteTable.getInstance();
82 log.info("Started");
83 }
84
85 /**
86 * Deactivate Multicast Forwarding Intent.
87 */
88 @Deactivate
89 public void deactivate() {
90 packetService.removeProcessor(processor);
91 processor = null;
92 log.info("Stopped");
93 }
94
95 /**
96 * Get the application ID, used by the McastIntentManager.
97 *
98 * @return the application ID
99 */
100 public static ApplicationId getAppId() {
101 return appId;
102 }
103
104 /**
105 * Packet processor responsible for forwarding packets along their paths.
106 */
107 private class ReactivePacketProcessor implements PacketProcessor {
108
109 /**
110 * Process incoming packets.
111 *
112 * @param context packet processing context
113 */
114 @Override
115 public void process(PacketContext context) {
116 // Stop processing if the packet has been handled, since we
117 // can't do any more to it.
118 if (context.isHandled()) {
119 return;
120 }
121
122 InboundPacket pkt = context.inPacket();
123 Ethernet ethPkt = pkt.parsed();
124
125 if (ethPkt == null) {
126 return;
127 }
128
129 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4 &&
130 ethPkt.getEtherType() != Ethernet.TYPE_IPV6) {
131 return;
132 }
133
134 if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) {
135 // Ignore ipv6 at the moment.
136 return;
137 }
138
139 IPv4 ip = (IPv4) ethPkt.getPayload();
140 IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
141 IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
142
143 log.debug("Packet ({}, {}) has been punted\n" +
144 "\tingress port: {}\n",
145 saddr.toString(),
146 gaddr.toString(),
147 context.inPacket().receivedFrom().toString());
148
149 if (!mcast.contains(gaddr)) {
150 // Yikes, this is a bad group address
151 return;
152 }
153
154 if (mcast.contains(saddr)) {
155 // Yikes, the source address is multicast
156 return;
157 }
158
159 IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
160 IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
161
162 /*
163 * Do a best match lookup on the (s, g) of the packet. If an entry does
164 * not exist create one and store it's incoming connect point.
165 *
166 * The connect point is deviceId / portId that the packet entered
167 * the SDN network. This differs from traditional mcast where the
168 * ingress port would be a specific device.
169 */
170 McastRoute entry = mrib.findBestMatch(spfx, gpfx);
Satish K8f3deb72015-11-21 13:35:17 +0530171 if (entry == null || entry.getSaddr().address().isZero()) {
alshabibeff00542015-09-23 13:22:33 -0700172
173 /*
174 * Create an entry that we can fast drop.
175 */
176 entry = mrib.addRoute(spfx, gpfx);
177 entry.addIngressPoint(context.inPacket().receivedFrom());
178 }
179
180 /*
181 * TODO: If we do not have an ingress or any egress connect points we
182 * should set up a fast drop entry.
183 */
184 if (entry.getIngressPoint() == null) {
185 return;
186 }
187
188 if (entry.getEgressPoints().isEmpty()) {
189 return;
190 }
191
192 /*
193 * This is odd, we should not have received a punted packet if an
194 * intent was installed unless the intent was not installed
195 * correctly. However, we are seeing packets get punted after
196 * the intent has been installed.
197 *
198 * Therefore we are going to forward the packets even if they
199 * should have already been forwarded by the intent fabric.
200 */
201 if (entry.getIntentKey() != null) {
202 return;
203 }
204
205 entry.setIntent();
206 McastIntentManager im = McastIntentManager.getInstance();
207 im.setIntent(entry);
208
209 entry.incrementPuntCount();
210
211 // Send the pack out each of the egress devices & port
212 forwardPacketToDst(context, entry);
213 }
214 }
215
216 /**
217 * Forward the packet to it's multicast destinations.
218 *
219 * @param context The packet context
220 * @param entry The multicast route entry matching this packet
221 */
222 private void forwardPacketToDst(PacketContext context, McastRoute entry) {
223
224 // Send the pack out each of the respective egress ports
Rusty Eddyddef8932015-09-25 01:15:53 +0000225 for (ConnectPoint egress : entry.getEgressConnectPoints()) {
alshabibeff00542015-09-23 13:22:33 -0700226 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
227 .setOutput(egress.port()).build();
228
229 OutboundPacket packet = new DefaultOutboundPacket(
230 egress.deviceId(),
231 treatment,
232 context.inPacket().unparsed());
233
234 packetService.emit(packet);
235 }
236 }
237}