blob: 9113e013d825053eaab2c12d682b210cf81aa71e [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;
20import org.onlab.packet.Ethernet;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.IpPrefix;
23import org.onlab.packet.MacAddress;
24import org.onlab.packet.VlanId;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.incubator.net.intf.Interface;
27import org.onosproject.incubator.net.intf.InterfaceService;
28import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.flow.DefaultTrafficSelector;
30import org.onosproject.net.flow.DefaultTrafficTreatment;
31import org.onosproject.net.flow.TrafficSelector;
32import org.onosproject.net.flow.TrafficTreatment;
33import org.onosproject.net.intent.Constraint;
34import org.onosproject.net.intent.Key;
35import org.onosproject.net.intent.MultiPointToSinglePointIntent;
36import org.onosproject.net.intent.constraint.PartialFailureConstraint;
37import org.onosproject.routing.FibListener;
38import org.onosproject.routing.FibUpdate;
39import org.onosproject.routing.IntentSynchronizationService;
40import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
43import java.util.Collection;
44import java.util.HashSet;
45import java.util.Map;
46import java.util.Set;
47import java.util.concurrent.ConcurrentHashMap;
48
49import static com.google.common.base.Preconditions.checkArgument;
50
51/**
52 * FIB component of SDN-IP.
53 */
54public class SdnIpFib implements FibListener {
55 private Logger log = LoggerFactory.getLogger(getClass());
56
57 private static final int PRIORITY_OFFSET = 100;
58 private static final int PRIORITY_MULTIPLIER = 5;
59 protected static final ImmutableList<Constraint> CONSTRAINTS
60 = ImmutableList.of(new PartialFailureConstraint());
61
62 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
63
64 private final ApplicationId appId;
65 private final InterfaceService interfaceService;
66 private final IntentSynchronizationService intentSynchronizer;
67
68 /**
69 * Class constructor.
70 *
71 * @param appId application ID to use when generating intents
72 * @param interfaceService interface service
73 * @param intentSynchronizer intent synchronizer
74 */
75 public SdnIpFib(ApplicationId appId, InterfaceService interfaceService,
76 IntentSynchronizationService intentSynchronizer) {
77 routeIntents = new ConcurrentHashMap<>();
78
79 this.appId = appId;
80 this.interfaceService = interfaceService;
81 this.intentSynchronizer = intentSynchronizer;
82 }
83
84
85 @Override
Pingping Lin92ca4912015-11-19 16:41:54 -080086 public void update(Collection<FibUpdate> updates,
87 Collection<FibUpdate> withdraws) {
Jonathan Hart9a426f82015-09-03 15:43:13 +020088 int submitCount = 0, withdrawCount = 0;
89 //
90 // NOTE: Semantically, we MUST withdraw existing intents before
91 // submitting new intents.
92 //
93 synchronized (this) {
94 MultiPointToSinglePointIntent intent;
95
96 //
97 // Prepare the Intent batch operations for the intents to withdraw
98 //
99 for (FibUpdate withdraw : withdraws) {
100 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
101 "FibUpdate with wrong type in withdraws list");
102
103 IpPrefix prefix = withdraw.entry().prefix();
104 intent = routeIntents.remove(prefix);
105 if (intent == null) {
106 log.trace("SDN-IP No intent in routeIntents to delete " +
107 "for prefix: {}", prefix);
108 continue;
109 }
110 intentSynchronizer.withdraw(intent);
111 withdrawCount++;
112 }
113
114 //
115 // Prepare the Intent batch operations for the intents to submit
116 //
117 for (FibUpdate update : updates) {
118 checkArgument(update.type() == FibUpdate.Type.UPDATE,
119 "FibUpdate with wrong type in updates list");
120
121 IpPrefix prefix = update.entry().prefix();
122 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
123 update.entry().nextHopMac());
124
125 if (intent == null) {
126 // This preserves the old semantics - if an intent can't be
127 // generated, we don't do anything with that prefix. But
128 // perhaps we should withdraw the old intent anyway?
129 continue;
130 }
131
132 routeIntents.put(prefix, intent);
133 intentSynchronizer.submit(intent);
134 submitCount++;
135 }
136
137 log.debug("SDN-IP submitted {}/{}, withdrew = {}/{}", submitCount,
138 updates.size(), withdrawCount, withdraws.size());
139 }
140 }
141
142 /**
143 * Generates a route intent for a prefix, the next hop IP address, and
144 * the next hop MAC address.
145 * <p/>
146 * This method will find the egress interface for the intent.
147 * Intent will match dst IP prefix and rewrite dst MAC address at all other
148 * border switches, then forward packets according to dst MAC address.
149 *
150 * @param prefix IP prefix of the route to add
151 * @param nextHopIpAddress IP address of the next hop
152 * @param nextHopMacAddress MAC address of the next hop
153 * @return the generated intent, or null if no intent should be submitted
154 */
155 private MultiPointToSinglePointIntent generateRouteIntent(
156 IpPrefix prefix,
157 IpAddress nextHopIpAddress,
158 MacAddress nextHopMacAddress) {
159
160 // Find the attachment point (egress interface) of the next hop
Pingping Lin92ca4912015-11-19 16:41:54 -0800161 Interface egressInterface =
162 interfaceService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200163 if (egressInterface == null) {
164 log.warn("No outgoing interface found for {}",
165 nextHopIpAddress);
166 return null;
167 }
168
169 // Generate the intent itself
170 Set<ConnectPoint> ingressPorts = new HashSet<>();
171 ConnectPoint egressPort = egressInterface.connectPoint();
172 log.debug("Generating intent for prefix {}, next hop mac {}",
173 prefix, nextHopMacAddress);
174
175 for (Interface intf : interfaceService.getInterfaces()) {
176 // TODO this should be only peering interfaces
177 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
178 ConnectPoint srcPort = intf.connectPoint();
179 ingressPorts.add(srcPort);
180 }
181 }
182
183 // Match the destination IP prefix at the first hop
184 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
185 if (prefix.isIp4()) {
186 selector.matchEthType(Ethernet.TYPE_IPV4);
Pingping Lin92ca4912015-11-19 16:41:54 -0800187 // if it is default route, then we do not need match destination
188 // IP address
189 if (prefix.prefixLength() != 0) {
190 selector.matchIPDst(prefix);
191 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200192 } else {
193 selector.matchEthType(Ethernet.TYPE_IPV6);
Pingping Lin92ca4912015-11-19 16:41:54 -0800194 // if it is default route, then we do not need match destination
195 // IP address
196 if (prefix.prefixLength() != 0) {
197 selector.matchIPv6Dst(prefix);
198 }
199
Jonathan Hart9a426f82015-09-03 15:43:13 +0200200 }
201
202 // Rewrite the destination MAC address
203 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
204 .setEthDst(nextHopMacAddress);
205 if (!egressInterface.vlan().equals(VlanId.NONE)) {
206 treatment.setVlanId(egressInterface.vlan());
207 // If we set VLAN ID, we have to make sure a VLAN tag exists.
208 // TODO support no VLAN -> VLAN routing
209 selector.matchVlanId(VlanId.ANY);
210 }
211
212 int priority =
213 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
214 Key key = Key.of(prefix.toString(), appId);
215 return MultiPointToSinglePointIntent.builder()
216 .appId(appId)
217 .key(key)
218 .selector(selector.build())
219 .treatment(treatment.build())
220 .ingressPoints(ingressPorts)
221 .egressPoint(egressPort)
222 .priority(priority)
223 .constraints(CONSTRAINTS)
224 .build();
225 }
226
227}