blob: c0001bdc68a5dfa6636412174bbc5194b8d1bcdf [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
86 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
87 int submitCount = 0, withdrawCount = 0;
88 //
89 // NOTE: Semantically, we MUST withdraw existing intents before
90 // submitting new intents.
91 //
92 synchronized (this) {
93 MultiPointToSinglePointIntent intent;
94
95 //
96 // Prepare the Intent batch operations for the intents to withdraw
97 //
98 for (FibUpdate withdraw : withdraws) {
99 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
100 "FibUpdate with wrong type in withdraws list");
101
102 IpPrefix prefix = withdraw.entry().prefix();
103 intent = routeIntents.remove(prefix);
104 if (intent == null) {
105 log.trace("SDN-IP No intent in routeIntents to delete " +
106 "for prefix: {}", prefix);
107 continue;
108 }
109 intentSynchronizer.withdraw(intent);
110 withdrawCount++;
111 }
112
113 //
114 // Prepare the Intent batch operations for the intents to submit
115 //
116 for (FibUpdate update : updates) {
117 checkArgument(update.type() == FibUpdate.Type.UPDATE,
118 "FibUpdate with wrong type in updates list");
119
120 IpPrefix prefix = update.entry().prefix();
121 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
122 update.entry().nextHopMac());
123
124 if (intent == null) {
125 // This preserves the old semantics - if an intent can't be
126 // generated, we don't do anything with that prefix. But
127 // perhaps we should withdraw the old intent anyway?
128 continue;
129 }
130
131 routeIntents.put(prefix, intent);
132 intentSynchronizer.submit(intent);
133 submitCount++;
134 }
135
136 log.debug("SDN-IP submitted {}/{}, withdrew = {}/{}", submitCount,
137 updates.size(), withdrawCount, withdraws.size());
138 }
139 }
140
141 /**
142 * Generates a route intent for a prefix, the next hop IP address, and
143 * the next hop MAC address.
144 * <p/>
145 * This method will find the egress interface for the intent.
146 * Intent will match dst IP prefix and rewrite dst MAC address at all other
147 * border switches, then forward packets according to dst MAC address.
148 *
149 * @param prefix IP prefix of the route to add
150 * @param nextHopIpAddress IP address of the next hop
151 * @param nextHopMacAddress MAC address of the next hop
152 * @return the generated intent, or null if no intent should be submitted
153 */
154 private MultiPointToSinglePointIntent generateRouteIntent(
155 IpPrefix prefix,
156 IpAddress nextHopIpAddress,
157 MacAddress nextHopMacAddress) {
158
159 // Find the attachment point (egress interface) of the next hop
160 Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
161 if (egressInterface == null) {
162 log.warn("No outgoing interface found for {}",
163 nextHopIpAddress);
164 return null;
165 }
166
167 // Generate the intent itself
168 Set<ConnectPoint> ingressPorts = new HashSet<>();
169 ConnectPoint egressPort = egressInterface.connectPoint();
170 log.debug("Generating intent for prefix {}, next hop mac {}",
171 prefix, nextHopMacAddress);
172
173 for (Interface intf : interfaceService.getInterfaces()) {
174 // TODO this should be only peering interfaces
175 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
176 ConnectPoint srcPort = intf.connectPoint();
177 ingressPorts.add(srcPort);
178 }
179 }
180
181 // Match the destination IP prefix at the first hop
182 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
183 if (prefix.isIp4()) {
184 selector.matchEthType(Ethernet.TYPE_IPV4);
185 selector.matchIPDst(prefix);
186 } else {
187 selector.matchEthType(Ethernet.TYPE_IPV6);
188 selector.matchIPv6Dst(prefix);
189 }
190
191 // Rewrite the destination MAC address
192 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
193 .setEthDst(nextHopMacAddress);
194 if (!egressInterface.vlan().equals(VlanId.NONE)) {
195 treatment.setVlanId(egressInterface.vlan());
196 // If we set VLAN ID, we have to make sure a VLAN tag exists.
197 // TODO support no VLAN -> VLAN routing
198 selector.matchVlanId(VlanId.ANY);
199 }
200
201 int priority =
202 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
203 Key key = Key.of(prefix.toString(), appId);
204 return MultiPointToSinglePointIntent.builder()
205 .appId(appId)
206 .key(key)
207 .selector(selector.build())
208 .treatment(treatment.build())
209 .ingressPoints(ingressPorts)
210 .egressPoint(egressPort)
211 .priority(priority)
212 .constraints(CONSTRAINTS)
213 .build();
214 }
215
216}