blob: a4bd14c76f01b1c2bfb26857777dd3c103d9ca92 [file] [log] [blame]
Jonathan Hart9a426f82015-09-03 15:43:13 +02001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Jonathan Hart9a426f82015-09-03 15:43:13 +02003 *
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 */
16
17package org.onosproject.sdnip;
18
19import com.google.common.collect.ImmutableList;
Jonathan Hartb14221c2016-03-07 09:55:50 -080020import com.google.common.collect.Sets;
Jonathan Hart365335e2015-12-10 11:09:53 -080021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart9a426f82015-09-03 15:43:13 +020026import org.onlab.packet.Ethernet;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
29import org.onlab.packet.MacAddress;
30import org.onlab.packet.VlanId;
31import org.onosproject.core.ApplicationId;
Jonathan Hart365335e2015-12-10 11:09:53 -080032import org.onosproject.core.CoreService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020033import org.onosproject.incubator.net.intf.Interface;
Jonathan Hartb14221c2016-03-07 09:55:50 -080034import org.onosproject.incubator.net.intf.InterfaceEvent;
35import org.onosproject.incubator.net.intf.InterfaceListener;
Jonathan Hart9a426f82015-09-03 15:43:13 +020036import org.onosproject.incubator.net.intf.InterfaceService;
37import org.onosproject.net.ConnectPoint;
38import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.flow.TrafficTreatment;
42import org.onosproject.net.intent.Constraint;
43import org.onosproject.net.intent.Key;
44import org.onosproject.net.intent.MultiPointToSinglePointIntent;
45import org.onosproject.net.intent.constraint.PartialFailureConstraint;
46import org.onosproject.routing.FibListener;
47import org.onosproject.routing.FibUpdate;
48import org.onosproject.routing.IntentSynchronizationService;
Jonathan Hart365335e2015-12-10 11:09:53 -080049import org.onosproject.routing.RoutingService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020050import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
53import java.util.Collection;
54import java.util.HashSet;
55import java.util.Map;
56import java.util.Set;
57import java.util.concurrent.ConcurrentHashMap;
58
59import static com.google.common.base.Preconditions.checkArgument;
60
61/**
62 * FIB component of SDN-IP.
63 */
Jonathan Hartc22e8472015-11-17 18:25:45 -080064@Component(immediate = true, enabled = false)
Jonathan Hart365335e2015-12-10 11:09:53 -080065public class SdnIpFib {
Jonathan Hart9a426f82015-09-03 15:43:13 +020066 private Logger log = LoggerFactory.getLogger(getClass());
67
Jonathan Hart365335e2015-12-10 11:09:53 -080068 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected InterfaceService interfaceService;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected IntentSynchronizationService intentSynchronizer;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected CoreService coreService;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected RoutingService routingService;
79
80 private final InternalFibListener fibListener = new InternalFibListener();
Jonathan Hartb14221c2016-03-07 09:55:50 -080081 private final InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
Jonathan Hart365335e2015-12-10 11:09:53 -080082
Jonathan Hart9a426f82015-09-03 15:43:13 +020083 private static final int PRIORITY_OFFSET = 100;
84 private static final int PRIORITY_MULTIPLIER = 5;
85 protected static final ImmutableList<Constraint> CONSTRAINTS
86 = ImmutableList.of(new PartialFailureConstraint());
87
Jonathan Hart365335e2015-12-10 11:09:53 -080088 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents
89 = new ConcurrentHashMap<>();
Jonathan Hart9a426f82015-09-03 15:43:13 +020090
Jonathan Hart365335e2015-12-10 11:09:53 -080091 private ApplicationId appId;
Jonathan Hart9a426f82015-09-03 15:43:13 +020092
Jonathan Hart365335e2015-12-10 11:09:53 -080093 @Activate
94 public void activate() {
95 appId = coreService.getAppId(SdnIp.SDN_IP_APP);
Jonathan Hart9a426f82015-09-03 15:43:13 +020096
Jonathan Hartb14221c2016-03-07 09:55:50 -080097 interfaceService.addListener(interfaceListener);
98
Jonathan Hart365335e2015-12-10 11:09:53 -080099 routingService.addFibListener(fibListener);
100 routingService.start();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200101 }
102
Jonathan Hart365335e2015-12-10 11:09:53 -0800103 @Deactivate
104 public void deactivate() {
Jonathan Hartb14221c2016-03-07 09:55:50 -0800105 interfaceService.removeListener(interfaceListener);
Jonathan Hart365335e2015-12-10 11:09:53 -0800106 // TODO remove listener
107 routingService.stop();
108 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200109
Jonathan Hart365335e2015-12-10 11:09:53 -0800110 private void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200111 int submitCount = 0, withdrawCount = 0;
112 //
113 // NOTE: Semantically, we MUST withdraw existing intents before
114 // submitting new intents.
115 //
116 synchronized (this) {
117 MultiPointToSinglePointIntent intent;
118
119 //
120 // Prepare the Intent batch operations for the intents to withdraw
121 //
122 for (FibUpdate withdraw : withdraws) {
123 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
124 "FibUpdate with wrong type in withdraws list");
125
126 IpPrefix prefix = withdraw.entry().prefix();
127 intent = routeIntents.remove(prefix);
128 if (intent == null) {
129 log.trace("SDN-IP No intent in routeIntents to delete " +
130 "for prefix: {}", prefix);
131 continue;
132 }
133 intentSynchronizer.withdraw(intent);
134 withdrawCount++;
135 }
136
137 //
138 // Prepare the Intent batch operations for the intents to submit
139 //
140 for (FibUpdate update : updates) {
141 checkArgument(update.type() == FibUpdate.Type.UPDATE,
142 "FibUpdate with wrong type in updates list");
143
144 IpPrefix prefix = update.entry().prefix();
145 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
146 update.entry().nextHopMac());
147
148 if (intent == null) {
149 // This preserves the old semantics - if an intent can't be
150 // generated, we don't do anything with that prefix. But
151 // perhaps we should withdraw the old intent anyway?
152 continue;
153 }
154
155 routeIntents.put(prefix, intent);
156 intentSynchronizer.submit(intent);
157 submitCount++;
158 }
159
160 log.debug("SDN-IP submitted {}/{}, withdrew = {}/{}", submitCount,
161 updates.size(), withdrawCount, withdraws.size());
162 }
163 }
164
165 /**
166 * Generates a route intent for a prefix, the next hop IP address, and
167 * the next hop MAC address.
168 * <p/>
169 * This method will find the egress interface for the intent.
170 * Intent will match dst IP prefix and rewrite dst MAC address at all other
171 * border switches, then forward packets according to dst MAC address.
172 *
173 * @param prefix IP prefix of the route to add
174 * @param nextHopIpAddress IP address of the next hop
175 * @param nextHopMacAddress MAC address of the next hop
176 * @return the generated intent, or null if no intent should be submitted
177 */
178 private MultiPointToSinglePointIntent generateRouteIntent(
179 IpPrefix prefix,
180 IpAddress nextHopIpAddress,
181 MacAddress nextHopMacAddress) {
182
183 // Find the attachment point (egress interface) of the next hop
Pingping Lin92ca4912015-11-19 16:41:54 -0800184 Interface egressInterface =
185 interfaceService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200186 if (egressInterface == null) {
187 log.warn("No outgoing interface found for {}",
188 nextHopIpAddress);
189 return null;
190 }
191
192 // Generate the intent itself
193 Set<ConnectPoint> ingressPorts = new HashSet<>();
194 ConnectPoint egressPort = egressInterface.connectPoint();
195 log.debug("Generating intent for prefix {}, next hop mac {}",
196 prefix, nextHopMacAddress);
197
198 for (Interface intf : interfaceService.getInterfaces()) {
199 // TODO this should be only peering interfaces
200 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
201 ConnectPoint srcPort = intf.connectPoint();
202 ingressPorts.add(srcPort);
203 }
204 }
205
206 // Match the destination IP prefix at the first hop
207 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
208 if (prefix.isIp4()) {
209 selector.matchEthType(Ethernet.TYPE_IPV4);
Pingping Lin92ca4912015-11-19 16:41:54 -0800210 // if it is default route, then we do not need match destination
211 // IP address
212 if (prefix.prefixLength() != 0) {
213 selector.matchIPDst(prefix);
214 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200215 } else {
216 selector.matchEthType(Ethernet.TYPE_IPV6);
Pingping Lin92ca4912015-11-19 16:41:54 -0800217 // if it is default route, then we do not need match destination
218 // IP address
219 if (prefix.prefixLength() != 0) {
220 selector.matchIPv6Dst(prefix);
221 }
222
Jonathan Hart9a426f82015-09-03 15:43:13 +0200223 }
224
225 // Rewrite the destination MAC address
226 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
227 .setEthDst(nextHopMacAddress);
228 if (!egressInterface.vlan().equals(VlanId.NONE)) {
229 treatment.setVlanId(egressInterface.vlan());
230 // If we set VLAN ID, we have to make sure a VLAN tag exists.
231 // TODO support no VLAN -> VLAN routing
232 selector.matchVlanId(VlanId.ANY);
233 }
234
235 int priority =
236 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
237 Key key = Key.of(prefix.toString(), appId);
238 return MultiPointToSinglePointIntent.builder()
239 .appId(appId)
240 .key(key)
241 .selector(selector.build())
242 .treatment(treatment.build())
243 .ingressPoints(ingressPorts)
244 .egressPoint(egressPort)
245 .priority(priority)
246 .constraints(CONSTRAINTS)
247 .build();
248 }
249
Jonathan Hartb14221c2016-03-07 09:55:50 -0800250 private void updateInterface(Interface intf) {
251 synchronized (this) {
252 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry : routeIntents.entrySet()) {
253 MultiPointToSinglePointIntent intent = entry.getValue();
254 Set<ConnectPoint> ingress = Sets.newHashSet(intent.ingressPoints());
255 ingress.add(intf.connectPoint());
256
257 MultiPointToSinglePointIntent newIntent =
258 MultiPointToSinglePointIntent.builder(intent)
259 .ingressPoints(ingress)
260 .build();
261
262 routeIntents.put(entry.getKey(), newIntent);
263 intentSynchronizer.submit(newIntent);
264 }
265 }
266 }
267
268 private void removeInterface(Interface intf) {
269 synchronized (this) {
270 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry : routeIntents.entrySet()) {
271 MultiPointToSinglePointIntent intent = entry.getValue();
272 if (intent.egressPoint().equals(intf.connectPoint())) {
273 // This intent just lost its head. Remove it and let
274 // higher layer routing reroute.
275 intentSynchronizer.withdraw(routeIntents.remove(entry.getKey()));
276 } else {
277 if (intent.ingressPoints().contains(intf.connectPoint())) {
278
279 Set<ConnectPoint> ingress = Sets.newHashSet(intent.ingressPoints());
280 ingress.remove(intf.connectPoint());
281
282 MultiPointToSinglePointIntent newIntent =
283 MultiPointToSinglePointIntent.builder(intent)
284 .ingressPoints(ingress)
285 .build();
286
287 routeIntents.put(entry.getKey(), newIntent);
288 intentSynchronizer.submit(newIntent);
289 }
290 }
291 }
292 }
293 }
294
Jonathan Hart365335e2015-12-10 11:09:53 -0800295 private class InternalFibListener implements FibListener {
296 @Override
297 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
298 SdnIpFib.this.update(updates, withdraws);
299 }
300 }
301
Jonathan Hartb14221c2016-03-07 09:55:50 -0800302 private class InternalInterfaceListener implements InterfaceListener {
303
304 @Override
305 public void event(InterfaceEvent event) {
306 switch (event.type()) {
307 case INTERFACE_ADDED:
308 updateInterface(event.subject());
309 break;
310 case INTERFACE_UPDATED:
311 break;
312 case INTERFACE_REMOVED:
313 removeInterface(event.subject());
314 break;
315 default:
316 break;
317 }
318 }
319 }
320
Jonathan Hart9a426f82015-09-03 15:43:13 +0200321}