blob: ac9baabd11a99c55cff80959e48fadba42edd689 [file] [log] [blame]
Jonathan Hart9a426f82015-09-03 15:43:13 +02001/*
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 */
16
17package org.onosproject.sdnip;
18
19import com.google.common.collect.ImmutableList;
Jonathan Hart365335e2015-12-10 11:09:53 -080020import 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;
Jonathan Hart9a426f82015-09-03 15:43:13 +020025import 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;
Jonathan Hart365335e2015-12-10 11:09:53 -080031import org.onosproject.core.CoreService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020032import 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;
Jonathan Hart365335e2015-12-10 11:09:53 -080046import org.onosproject.routing.RoutingService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020047import 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 SDN-IP.
60 */
Jonathan Hart365335e2015-12-10 11:09:53 -080061@Component(immediate = true)
62public class SdnIpFib {
Jonathan Hart9a426f82015-09-03 15:43:13 +020063 private Logger log = LoggerFactory.getLogger(getClass());
64
Jonathan Hart365335e2015-12-10 11:09:53 -080065 @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 private final InternalFibListener fibListener = new InternalFibListener();
78
Jonathan Hart9a426f82015-09-03 15:43:13 +020079 private static final int PRIORITY_OFFSET = 100;
80 private static final int PRIORITY_MULTIPLIER = 5;
81 protected static final ImmutableList<Constraint> CONSTRAINTS
82 = ImmutableList.of(new PartialFailureConstraint());
83
Jonathan Hart365335e2015-12-10 11:09:53 -080084 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents
85 = new ConcurrentHashMap<>();
Jonathan Hart9a426f82015-09-03 15:43:13 +020086
Jonathan Hart365335e2015-12-10 11:09:53 -080087 private ApplicationId appId;
Jonathan Hart9a426f82015-09-03 15:43:13 +020088
Jonathan Hart365335e2015-12-10 11:09:53 -080089 @Activate
90 public void activate() {
91 appId = coreService.getAppId(SdnIp.SDN_IP_APP);
Jonathan Hart9a426f82015-09-03 15:43:13 +020092
Jonathan Hart365335e2015-12-10 11:09:53 -080093 routingService.addFibListener(fibListener);
94 routingService.start();
Jonathan Hart9a426f82015-09-03 15:43:13 +020095 }
96
Jonathan Hart365335e2015-12-10 11:09:53 -080097 @Deactivate
98 public void deactivate() {
99 // TODO remove listener
100 routingService.stop();
101 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200102
Jonathan Hart365335e2015-12-10 11:09:53 -0800103 private void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200104 int submitCount = 0, withdrawCount = 0;
105 //
106 // NOTE: Semantically, we MUST withdraw existing intents before
107 // submitting new intents.
108 //
109 synchronized (this) {
110 MultiPointToSinglePointIntent intent;
111
112 //
113 // Prepare the Intent batch operations for the intents to withdraw
114 //
115 for (FibUpdate withdraw : withdraws) {
116 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
117 "FibUpdate with wrong type in withdraws list");
118
119 IpPrefix prefix = withdraw.entry().prefix();
120 intent = routeIntents.remove(prefix);
121 if (intent == null) {
122 log.trace("SDN-IP No intent in routeIntents to delete " +
123 "for prefix: {}", prefix);
124 continue;
125 }
126 intentSynchronizer.withdraw(intent);
127 withdrawCount++;
128 }
129
130 //
131 // Prepare the Intent batch operations for the intents to submit
132 //
133 for (FibUpdate update : updates) {
134 checkArgument(update.type() == FibUpdate.Type.UPDATE,
135 "FibUpdate with wrong type in updates list");
136
137 IpPrefix prefix = update.entry().prefix();
138 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
139 update.entry().nextHopMac());
140
141 if (intent == null) {
142 // This preserves the old semantics - if an intent can't be
143 // generated, we don't do anything with that prefix. But
144 // perhaps we should withdraw the old intent anyway?
145 continue;
146 }
147
148 routeIntents.put(prefix, intent);
149 intentSynchronizer.submit(intent);
150 submitCount++;
151 }
152
153 log.debug("SDN-IP submitted {}/{}, withdrew = {}/{}", submitCount,
154 updates.size(), withdrawCount, withdraws.size());
155 }
156 }
157
158 /**
159 * Generates a route intent for a prefix, the next hop IP address, and
160 * the next hop MAC address.
161 * <p/>
162 * This method will find the egress interface for the intent.
163 * Intent will match dst IP prefix and rewrite dst MAC address at all other
164 * border switches, then forward packets according to dst MAC address.
165 *
166 * @param prefix IP prefix of the route to add
167 * @param nextHopIpAddress IP address of the next hop
168 * @param nextHopMacAddress MAC address of the next hop
169 * @return the generated intent, or null if no intent should be submitted
170 */
171 private MultiPointToSinglePointIntent generateRouteIntent(
172 IpPrefix prefix,
173 IpAddress nextHopIpAddress,
174 MacAddress nextHopMacAddress) {
175
176 // Find the attachment point (egress interface) of the next hop
Pingping Lin92ca4912015-11-19 16:41:54 -0800177 Interface egressInterface =
178 interfaceService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200179 if (egressInterface == null) {
180 log.warn("No outgoing interface found for {}",
181 nextHopIpAddress);
182 return null;
183 }
184
185 // Generate the intent itself
186 Set<ConnectPoint> ingressPorts = new HashSet<>();
187 ConnectPoint egressPort = egressInterface.connectPoint();
188 log.debug("Generating intent for prefix {}, next hop mac {}",
189 prefix, nextHopMacAddress);
190
191 for (Interface intf : interfaceService.getInterfaces()) {
192 // TODO this should be only peering interfaces
193 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
194 ConnectPoint srcPort = intf.connectPoint();
195 ingressPorts.add(srcPort);
196 }
197 }
198
199 // Match the destination IP prefix at the first hop
200 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
201 if (prefix.isIp4()) {
202 selector.matchEthType(Ethernet.TYPE_IPV4);
Pingping Lin92ca4912015-11-19 16:41:54 -0800203 // if it is default route, then we do not need match destination
204 // IP address
205 if (prefix.prefixLength() != 0) {
206 selector.matchIPDst(prefix);
207 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200208 } else {
209 selector.matchEthType(Ethernet.TYPE_IPV6);
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.matchIPv6Dst(prefix);
214 }
215
Jonathan Hart9a426f82015-09-03 15:43:13 +0200216 }
217
218 // Rewrite the destination MAC address
219 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
220 .setEthDst(nextHopMacAddress);
221 if (!egressInterface.vlan().equals(VlanId.NONE)) {
222 treatment.setVlanId(egressInterface.vlan());
223 // If we set VLAN ID, we have to make sure a VLAN tag exists.
224 // TODO support no VLAN -> VLAN routing
225 selector.matchVlanId(VlanId.ANY);
226 }
227
228 int priority =
229 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
230 Key key = Key.of(prefix.toString(), appId);
231 return MultiPointToSinglePointIntent.builder()
232 .appId(appId)
233 .key(key)
234 .selector(selector.build())
235 .treatment(treatment.build())
236 .ingressPoints(ingressPorts)
237 .egressPoint(egressPort)
238 .priority(priority)
239 .constraints(CONSTRAINTS)
240 .build();
241 }
242
Jonathan Hart365335e2015-12-10 11:09:53 -0800243 private class InternalFibListener implements FibListener {
244 @Override
245 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
246 SdnIpFib.this.update(updates, withdraws);
247 }
248 }
249
Jonathan Hart9a426f82015-09-03 15:43:13 +0200250}