blob: 9cd228de7495c0a8ce4b4db7721effd9d4134564 [file] [log] [blame]
Andreas Papazoisa9964ea2016-01-08 15:58:22 +02001/*
2 * Copyright 2014-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 */
16
17package org.onosproject.sdxl3;
18
19import com.google.common.collect.ImmutableList;
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;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.IpAddress;
27import org.onlab.packet.IpPrefix;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.VlanId;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.incubator.net.intf.Interface;
33import org.onosproject.incubator.net.intf.InterfaceService;
34import org.onosproject.net.ConnectPoint;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.intent.Constraint;
40import org.onosproject.net.intent.Key;
41import org.onosproject.net.intent.MultiPointToSinglePointIntent;
42import org.onosproject.net.intent.constraint.PartialFailureConstraint;
43import org.onosproject.routing.FibListener;
44import org.onosproject.routing.FibUpdate;
45import org.onosproject.routing.IntentSynchronizationService;
46import org.onosproject.routing.RoutingService;
47import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
49
50import java.util.Collection;
51import java.util.HashSet;
52import java.util.Map;
53import java.util.Set;
54import java.util.concurrent.ConcurrentHashMap;
55
56import static com.google.common.base.Preconditions.checkArgument;
57
58/**
59 * FIB component of SDX-L3.
60 */
61@Component(immediate = true, enabled = false)
62public class SdxL3Fib {
63 private Logger log = LoggerFactory.getLogger(getClass());
64
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected InterfaceService interfaceService;
67
68 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected IntentSynchronizationService intentSynchronizer;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected CoreService coreService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected RoutingService routingService;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andreas Papazoisc2c45012016-01-20 14:26:11 +020078 protected SdxL3PeerService peerService;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020079
80 private final InternalFibListener fibListener = new InternalFibListener();
81
82 private static final int PRIORITY_OFFSET = 100;
83 private static final int PRIORITY_MULTIPLIER = 5;
84 protected static final ImmutableList<Constraint> CONSTRAINTS
85 = ImmutableList.of(new PartialFailureConstraint());
86
87 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents
88 = new ConcurrentHashMap<>();
89
90 private ApplicationId appId;
91
92 @Activate
93 public void activate() {
94 appId = coreService.getAppId(SdxL3.SDX_L3_APP);
95
96 routingService.addFibListener(fibListener);
97 routingService.start();
98 }
99
100 @Deactivate
101 public void deactivate() {
102 // TODO remove listener
103 routingService.stop();
104 }
105
106 private void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
107 int submitCount = 0, withdrawCount = 0;
108 //
109 // NOTE: Semantically, we MUST withdraw existing intents before
110 // submitting new intents.
111 //
112 synchronized (this) {
113 MultiPointToSinglePointIntent intent;
114
115 //
116 // Prepare the Intent batch operations for the intents to withdraw
117 //
118 for (FibUpdate withdraw : withdraws) {
119 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
120 "FibUpdate with wrong type in withdraws list");
121
122 IpPrefix prefix = withdraw.entry().prefix();
123 intent = routeIntents.remove(prefix);
124 if (intent == null) {
125 log.trace("SDX-L3 No intent in routeIntents to delete " +
126 "for prefix: {}", prefix);
127 continue;
128 }
129 intentSynchronizer.withdraw(intent);
130 withdrawCount++;
131 }
132
133 //
134 // Prepare the Intent batch operations for the intents to submit
135 //
136 for (FibUpdate update : updates) {
137 checkArgument(update.type() == FibUpdate.Type.UPDATE,
138 "FibUpdate with wrong type in updates list");
139
140 IpPrefix prefix = update.entry().prefix();
141 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
142 update.entry().nextHopMac());
143
144 if (intent == null) {
145 // This preserves the old semantics - if an intent can't be
146 // generated, we don't do anything with that prefix. But
147 // perhaps we should withdraw the old intent anyway?
148 continue;
149 }
150
151 routeIntents.put(prefix, intent);
152 intentSynchronizer.submit(intent);
153 submitCount++;
154 }
155
156 log.debug("SDX-L3 submitted {}/{}, withdrew = {}/{}", submitCount,
157 updates.size(), withdrawCount, withdraws.size());
158 }
159 }
160
161 /**
162 * Generates a route intent for a prefix, the next hop IP address, and
163 * the next hop MAC address.
164 * <p/>
165 * This method will find the egress interface for the intent.
166 * Intent will match dst IP prefix and rewrite dst MAC address at all other
167 * border switches, then forward packets according to dst MAC address.
168 *
169 * @param prefix IP prefix of the route to add
170 * @param nextHopIpAddress IP address of the next hop
171 * @param nextHopMacAddress MAC address of the next hop
172 * @return the generated intent, or null if no intent should be submitted
173 */
174 private MultiPointToSinglePointIntent generateRouteIntent(
175 IpPrefix prefix,
176 IpAddress nextHopIpAddress,
177 MacAddress nextHopMacAddress) {
178
179 // Find the attachment point (egress interface) of the next hop
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200180 Interface egressInterface = peerService.getInterfaceForPeer(nextHopIpAddress);
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200181
182 if (egressInterface == null) {
183 log.warn("No outgoing interface found for {}",
184 nextHopIpAddress);
185 return null;
186 }
187
188 // Generate the intent itself
189 Set<ConnectPoint> ingressPorts = new HashSet<>();
190 ConnectPoint egressPort = egressInterface.connectPoint();
191 log.debug("Generating intent for prefix {}, next hop mac {}",
192 prefix, nextHopMacAddress);
193
194 for (Interface intf : interfaceService.getInterfaces()) {
195 // TODO this should be only peering interfaces
196 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
197 ConnectPoint srcPort = intf.connectPoint();
198 ingressPorts.add(srcPort);
199 }
200 }
201
202 // Match the destination IP prefix at the first hop
203 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
204 if (prefix.isIp4()) {
205 selector.matchEthType(Ethernet.TYPE_IPV4);
206 // if it is default route, then we do not need match destination
207 // IP address
208 if (prefix.prefixLength() != 0) {
209 selector.matchIPDst(prefix);
210 }
211 } else {
212 selector.matchEthType(Ethernet.TYPE_IPV6);
213 // if it is default route, then we do not need match destination
214 // IP address
215 if (prefix.prefixLength() != 0) {
216 selector.matchIPv6Dst(prefix);
217 }
218
219 }
220
221 // Rewrite the destination MAC address
222 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
223 .setEthDst(nextHopMacAddress);
224 if (!egressInterface.vlan().equals(VlanId.NONE)) {
225 treatment.setVlanId(egressInterface.vlan());
226 // If we set VLAN ID, we have to make sure a VLAN tag exists.
227 // TODO support no VLAN -> VLAN routing
228 selector.matchVlanId(VlanId.ANY);
229 }
230
231 int priority =
232 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
233 Key key = Key.of(prefix.toString(), appId);
234 return MultiPointToSinglePointIntent.builder()
235 .appId(appId)
236 .key(key)
237 .selector(selector.build())
238 .treatment(treatment.build())
239 .ingressPoints(ingressPorts)
240 .egressPoint(egressPort)
241 .priority(priority)
242 .constraints(CONSTRAINTS)
243 .build();
244 }
245
246 private class InternalFibListener implements FibListener {
247 @Override
248 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
249 SdxL3Fib.this.update(updates, withdraws);
250 }
251 }
252
253}