blob: b3707a8252437eb49a25a4c78e5c933ec466b97b [file] [log] [blame]
Hyunsun Moonb974fca2016-06-30 21:20:39 -07001/*
2* Copyright 2016-present 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.openstacknetworking.switching;
18
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onlab.packet.Ethernet;
25import org.onlab.packet.Ip4Address;
26import org.onlab.packet.IpAddress;
27import org.onosproject.net.Device;
28import org.onosproject.net.DeviceId;
29import org.onosproject.net.Host;
30import org.onosproject.net.PortNumber;
31import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
32import org.onosproject.net.device.DeviceService;
33import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
37import org.onosproject.net.flow.instructions.ExtensionTreatment;
38import org.onosproject.net.flow.instructions.ExtensionPropertyException;
39import org.onosproject.net.flowobjective.DefaultForwardingObjective;
40import org.onosproject.net.flowobjective.FlowObjectiveService;
41import org.onosproject.net.flowobjective.ForwardingObjective;
42import org.onosproject.openstacknode.OpenstackNodeService;
43import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45
46import java.util.Objects;
47import java.util.Optional;
48
49import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
sangho6032f342016-07-07 14:32:03 +090050import static org.onosproject.openstacknetworking.Constants.*;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070051
52/**
53 * Populates switching flow rules.
54 */
55@Component(immediate = true)
56public final class OpenstackSwitchingRulePopulator extends AbstractVmHandler {
57
58 private final Logger log = LoggerFactory.getLogger(getClass());
59
60 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
61 protected DeviceService deviceService;
62
63 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 protected FlowObjectiveService flowObjectiveService;
65
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected OpenstackNodeService nodeService;
68
69
70 private static final String TUNNEL_DST = "tunnelDst";
71
72 @Activate
73 protected void activate() {
74 super.activate();
75 }
76
77 @Deactivate
78 protected void deactivate() {
79 super.deactivate();
80 }
81
82 private void populateSwitchingRules(Host host) {
83 populateFlowRulesForTunnelTag(host);
84 populateFlowRulesForTrafficToSameCnode(host);
85 populateFlowRulesForTrafficToDifferentCnode(host);
86
87 log.debug("Populated switching rule for {}", host);
88 }
89
90 private void populateFlowRulesForTunnelTag(Host host) {
91 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
92 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
93
94 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
95 .matchInPort(host.location().port());
96
97 tBuilder.setTunnelId(Long.valueOf(getVni(host)));
98
99 ForwardingObjective fo = DefaultForwardingObjective.builder()
100 .withSelector(sBuilder.build())
101 .withTreatment(tBuilder.build())
102 .withPriority(TUNNELTAG_RULE_PRIORITY)
103 .withFlag(ForwardingObjective.Flag.SPECIFIC)
104 .fromApp(appId)
105 .add();
106
107 flowObjectiveService.forward(host.location().deviceId(), fo);
108 }
109
110 private void populateFlowRulesForTrafficToSameCnode(Host host) {
111 //For L2 Switching Case
112 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
113 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
114
115 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
116 .matchIPDst(getIp(host).toIpPrefix())
117 .matchTunnelId(Long.valueOf(getVni(host)));
118
119 tBuilder.setOutput(host.location().port());
120
121 ForwardingObjective fo = DefaultForwardingObjective.builder()
122 .withSelector(sBuilder.build())
123 .withTreatment(tBuilder.build())
124 .withPriority(SWITCHING_RULE_PRIORITY)
125 .withFlag(ForwardingObjective.Flag.SPECIFIC)
126 .fromApp(appId)
127 .add();
128
129 flowObjectiveService.forward(host.location().deviceId(), fo);
130 }
131
132 private void populateFlowRulesForTrafficToDifferentCnode(Host host) {
133 Ip4Address localVmIp = getIp(host);
134 DeviceId localDeviceId = host.location().deviceId();
135 Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
136
137 if (!localDataIp.isPresent()) {
138 log.debug("Failed to get data IP for device {}",
139 host.location().deviceId());
140 return;
141 }
142
143 String vni = getVni(host);
144 getVmsInDifferentCnode(host).forEach(remoteVm -> {
145 Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
146 if (remoteDataIp.isPresent()) {
147 setVxLanFlowRule(vni,
148 localDeviceId,
149 remoteDataIp.get().getIp4Address(),
150 getIp(remoteVm));
151
152 setVxLanFlowRule(vni,
153 remoteVm.location().deviceId(),
154 localDataIp.get().getIp4Address(),
155 localVmIp);
156 }
157 });
158 }
159
160 private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
161 Ip4Address vmIp) {
162 Optional<PortNumber> tunnelPort = nodeService.tunnelPort(deviceId);
163 if (!tunnelPort.isPresent()) {
164 log.warn("Failed to get tunnel port from {}", deviceId);
165 return;
166 }
167
168 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
169 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
170
171 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
172 .matchTunnelId(Long.parseLong(vni))
173 .matchIPDst(vmIp.toIpPrefix());
174 tBuilder.extension(buildNiciraExtenstion(deviceId, remoteIp), deviceId)
175 .setOutput(tunnelPort.get());
176
177 ForwardingObjective fo = DefaultForwardingObjective.builder()
178 .withSelector(sBuilder.build())
179 .withTreatment(tBuilder.build())
180 .withPriority(SWITCHING_RULE_PRIORITY)
181 .withFlag(ForwardingObjective.Flag.SPECIFIC)
182 .fromApp(appId)
183 .add();
184
185 flowObjectiveService.forward(deviceId, fo);
186 }
187
188 private void removeSwitchingRules(Host host) {
189 removeFlowRuleForTunnelTag(host);
190 removeFlowRuleForVMsInSameCnode(host);
191 removeFlowRuleForVMsInDiffrentCnode(host);
192
193 log.debug("Removed switching rule for {}", host);
194 }
195
196 private void removeFlowRuleForTunnelTag(Host host) {
197 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
198 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
199
200 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
201 .matchInPort(host.location().port());
202
203 ForwardingObjective fo = DefaultForwardingObjective.builder()
204 .withSelector(sBuilder.build())
205 .withTreatment(tBuilder.build())
206 .withPriority(TUNNELTAG_RULE_PRIORITY)
207 .withFlag(ForwardingObjective.Flag.SPECIFIC)
208 .fromApp(appId)
209 .remove();
210
211 flowObjectiveService.forward(host.location().deviceId(), fo);
212 }
213
214 private void removeFlowRuleForVMsInSameCnode(Host host) {
215 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
216 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
217 .matchIPDst(getIp(host).toIpPrefix())
218 .matchTunnelId(Long.valueOf(getVni(host)));
219
220 ForwardingObjective fo = DefaultForwardingObjective.builder()
221 .withSelector(sBuilder.build())
222 .withTreatment(DefaultTrafficTreatment.builder().build())
223 .withFlag(ForwardingObjective.Flag.SPECIFIC)
224 .withPriority(SWITCHING_RULE_PRIORITY)
225 .fromApp(appId)
226 .remove();
227
228 flowObjectiveService.forward(host.location().deviceId(), fo);
229 }
230
231 private void removeFlowRuleForVMsInDiffrentCnode(Host host) {
232 DeviceId deviceId = host.location().deviceId();
233 final boolean anyPortRemainedInSameCnode = hostService.getConnectedHosts(deviceId)
234 .stream()
235 .filter(this::isValidHost)
236 .anyMatch(h -> Objects.equals(getVni(h), getVni(host)));
237
238 getVmsInDifferentCnode(host).forEach(h -> {
239 removeVxLanFlowRule(h.location().deviceId(), getIp(host), getVni(host));
240 if (!anyPortRemainedInSameCnode) {
241 removeVxLanFlowRule(deviceId, getIp(h), getVni(host));
242 }
243 });
244 }
245
246 private void removeVxLanFlowRule(DeviceId deviceId, Ip4Address vmIp, String vni) {
247 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
248
249 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
250 .matchTunnelId(Long.valueOf(vni))
251 .matchIPDst(vmIp.toIpPrefix());
252
253 ForwardingObjective fo = DefaultForwardingObjective.builder()
254 .withSelector(sBuilder.build())
255 .withTreatment(DefaultTrafficTreatment.builder().build())
256 .withFlag(ForwardingObjective.Flag.SPECIFIC)
257 .withPriority(SWITCHING_RULE_PRIORITY)
258 .fromApp(appId)
259 .remove();
260
261 flowObjectiveService.forward(deviceId, fo);
262 }
263
264 private ExtensionTreatment buildNiciraExtenstion(DeviceId deviceId, Ip4Address remoteIp) {
265 Device device = deviceService.getDevice(deviceId);
266 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
267 log.error("The extension treatment is not supported");
268 return null;
269 }
270
271 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
272 ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
273 try {
274 treatment.setPropertyValue(TUNNEL_DST, remoteIp);
275 return treatment;
276 } catch (ExtensionPropertyException e) {
277 log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
278 return null;
279 }
280 }
281
282 @Override
283 protected void hostDetected(Host host) {
284 populateSwitchingRules(host);
285 log.info("Added new virtual machine to switching service {}", host);
286 }
287
288 @Override
289 protected void hostRemoved(Host host) {
290 removeSwitchingRules(host);
291 log.info("Removed virtual machine from switching service {}", host);
292 }
293}