blob: c0164475763cda0d13f0122149ef1e4ed209d5fa [file] [log] [blame]
Daniel Park81a61a12016-02-26 08:24:44 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Daniel Park81a61a12016-02-26 08:24:44 +09003 *
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 */
Hyunsun Moon05400872017-02-07 17:11:25 +090016package org.onosproject.openstacknetworking.impl;
Daniel Park81a61a12016-02-26 08:24:44 +090017
Jian Li60312252018-05-10 18:40:32 +090018import com.google.common.base.Strings;
19import com.google.common.collect.Maps;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
Jian Li60312252018-05-10 18:40:32 +090023import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070025import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Daniel Park81a61a12016-02-26 08:24:44 +090027import org.onlab.packet.ARP;
Jian Li60312252018-05-10 18:40:32 +090028import org.onlab.packet.EthType;
Daniel Park81a61a12016-02-26 08:24:44 +090029import org.onlab.packet.Ethernet;
Daniel Park81a61a12016-02-26 08:24:44 +090030import org.onlab.packet.Ip4Address;
31import org.onlab.packet.IpAddress;
32import org.onlab.packet.MacAddress;
Jian Li60312252018-05-10 18:40:32 +090033import org.onlab.util.Tools;
34import org.onosproject.cfg.ComponentConfigService;
35import org.onosproject.cluster.ClusterService;
36import org.onosproject.cluster.LeadershipService;
37import org.onosproject.cluster.NodeId;
38import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090040import org.onosproject.net.DeviceId;
Jian Li60312252018-05-10 18:40:32 +090041import org.onosproject.net.Host;
daniel parkb5817102018-02-15 00:18:51 +090042import org.onosproject.net.PortNumber;
Jian Li60312252018-05-10 18:40:32 +090043import org.onosproject.net.flow.DefaultTrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090044import org.onosproject.net.flow.DefaultTrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090045import org.onosproject.net.flow.TrafficSelector;
Daniel Park81a61a12016-02-26 08:24:44 +090046import org.onosproject.net.flow.TrafficTreatment;
Jian Li60312252018-05-10 18:40:32 +090047import org.onosproject.net.host.HostEvent;
48import org.onosproject.net.host.HostListener;
49import org.onosproject.net.host.HostService;
Daniel Park81a61a12016-02-26 08:24:44 +090050import org.onosproject.net.packet.DefaultOutboundPacket;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070051import org.onosproject.net.packet.InboundPacket;
Daniel Park81a61a12016-02-26 08:24:44 +090052import org.onosproject.net.packet.PacketContext;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070053import org.onosproject.net.packet.PacketProcessor;
Daniel Park81a61a12016-02-26 08:24:44 +090054import org.onosproject.net.packet.PacketService;
Hyunsun Moon05400872017-02-07 17:11:25 +090055import org.onosproject.openstacknetworking.api.Constants;
Jian Li60312252018-05-10 18:40:32 +090056import org.onosproject.openstacknetworking.api.InstancePort;
57import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090058import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Jian Li60312252018-05-10 18:40:32 +090059import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
60import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
daniel parkeeb8e042018-02-21 14:06:58 +090061import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090062import org.onosproject.openstacknode.api.OpenstackNode;
63import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090064import org.openstack4j.model.network.ExternalGateway;
daniel parkeeb8e042018-02-21 14:06:58 +090065import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090066import org.openstack4j.model.network.Port;
67import org.openstack4j.model.network.Router;
68import org.openstack4j.model.network.Subnet;
69import org.osgi.service.component.ComponentContext;
Daniel Park81a61a12016-02-26 08:24:44 +090070import org.slf4j.Logger;
71
72import java.nio.ByteBuffer;
Jian Li60312252018-05-10 18:40:32 +090073import java.util.Dictionary;
74import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090075import java.util.Objects;
Jian Li60312252018-05-10 18:40:32 +090076import java.util.Optional;
Hyunsun Moon0d457362017-06-27 17:19:41 +090077import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070078import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090079import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090080
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070081import static java.util.concurrent.Executors.newSingleThreadExecutor;
82import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090083import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
84import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
85import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
86import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
87import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
88import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
89import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
90import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
91import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Hyunsun Moon0d457362017-06-27 17:19:41 +090092import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Daniel Park81a61a12016-02-26 08:24:44 +090093import static org.slf4j.LoggerFactory.getLogger;
94
95/**
Hyunsun Moon44aac662017-02-18 02:07:01 +090096 * Handle ARP requests from gateway nodes.
Daniel Park81a61a12016-02-26 08:24:44 +090097 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070098@Component(immediate = true)
Daniel Park81a61a12016-02-26 08:24:44 +090099public class OpenstackRoutingArpHandler {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900100
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700101 private final Logger log = getLogger(getClass());
Daniel Park81a61a12016-02-26 08:24:44 +0900102
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103 private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
104 private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
Jian Li60312252018-05-10 18:40:32 +0900105 private static final String ARP_MODE = "arpMode";
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900109
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900112
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900114 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700115
Hyunsun Moon44aac662017-02-18 02:07:01 +0900116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900117 protected OpenstackRouterService osRouterService;
118
daniel parkeeb8e042018-02-21 14:06:58 +0900119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900120 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900121
Jian Li60312252018-05-10 18:40:32 +0900122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected ClusterService clusterService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected LeadershipService leadershipService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected OpenstackFlowRuleService osFlowRuleService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected ComponentConfigService configService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected HostService hostService;
136
137 // TODO: need to find a way to unify aprMode and gatewayMac variables with
138 // that in SwitchingArpHandler
139 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
140 label = "ARP processing mode, proxy (default) | broadcast ")
141 protected String arpMode = DEFAULT_ARP_MODE_STR;
142
143 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
144
145 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
146 private final HostListener hostListener = new InternalHostListener();
147
148 private ApplicationId appId;
149 private NodeId localNodeId;
150 private Map<String, String> floatingIpMacMap = Maps.newConcurrentMap();
151
Hyunsun Moon44aac662017-02-18 02:07:01 +0900152 private final ExecutorService eventExecutor = newSingleThreadExecutor(
153 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700154
Hyunsun Moon0d457362017-06-27 17:19:41 +0900155 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700156
157 @Activate
158 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900159 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
160 configService.registerProperties(getClass());
161 localNodeId = clusterService.getLocalNode().id();
162 osRouterService.addListener(osRouterListener);
163 hostService.addListener(hostListener);
164 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700165 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
166 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900167 }
168
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700169 @Deactivate
170 protected void deactivate() {
171 packetService.removeProcessor(packetProcessor);
Jian Li60312252018-05-10 18:40:32 +0900172 hostService.removeListener(hostListener);
173 osRouterService.removeListener(osRouterListener);
174 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900175 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900176 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700177 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900178 }
179
Jian Li60312252018-05-10 18:40:32 +0900180 // TODO: need to find a way to unify aprMode and gatewayMac variables with
181 // that in SwitchingArpHandler
182 @Modified
183 void modified(ComponentContext context) {
184 Dictionary<?, ?> properties = context.getProperties();
185 String updateArpMode;
186
187 updateArpMode = Tools.get(properties, ARP_MODE);
188 if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
189 arpMode = updateArpMode;
190 }
191
192 log.info("Modified");
193 }
194
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700195 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900196 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900197
198 if (arp.getOpCode() == ARP.OP_REQUEST && arpMode.equals(ARP_PROXY_MODE)) {
daniel parkb5817102018-02-15 00:18:51 +0900199 if (log.isTraceEnabled()) {
200 log.trace("ARP request received from {} for {}",
201 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
202 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
203 }
204
205 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900206
207 MacAddress targetMac = null;
208
209 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
210 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
211 .findAny().orElse(null);
212
daniel park576969a2018-03-09 07:07:41 +0900213 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900214 if (floatingIP != null && floatingIP.getPortId() != null) {
Jian Li60312252018-05-10 18:40:32 +0900215 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
216 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900217 }
218
219 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
220 targetMac = Constants.DEFAULT_GATEWAY_MAC;
221 }
222
223 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900224 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
225 return;
226 }
227
daniel parkb5817102018-02-15 00:18:51 +0900228 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
229 targetMac, ethernet);
230
daniel park576969a2018-03-09 07:07:41 +0900231
daniel parkb5817102018-02-15 00:18:51 +0900232 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900233 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900234
235 packetService.emit(new DefaultOutboundPacket(
236 context.inPacket().receivedFrom().deviceId(),
237 treatment,
238 ByteBuffer.wrap(ethReply.serialize())));
239
240 context.block();
Jian Li60312252018-05-10 18:40:32 +0900241 }
242
243 if (arp.getOpCode() == ARP.OP_REPLY) {
daniel parkb5817102018-02-15 00:18:51 +0900244 PortNumber receivedPortNum = context.inPacket().receivedFrom().port();
245 log.debug("ARP reply ip: {}, mac: {}",
246 Ip4Address.valueOf(arp.getSenderProtocolAddress()),
247 MacAddress.valueOf(arp.getSenderHardwareAddress()));
248 try {
249 if (receivedPortNum.equals(
Jian Li60312252018-05-10 18:40:32 +0900250 osNodeService.node(context.inPacket().receivedFrom()
251 .deviceId()).uplinkPortNum())) {
daniel park32b42202018-03-14 16:53:44 +0900252 osNetworkAdminService.updateExternalPeerRouterMac(
daniel parkb5817102018-02-15 00:18:51 +0900253 Ip4Address.valueOf(arp.getSenderProtocolAddress()),
254 MacAddress.valueOf(arp.getSenderHardwareAddress()));
255 }
256 } catch (Exception e) {
257 log.error("Exception occurred because of {}", e.toString());
258 }
Daniel Park81a61a12016-02-26 08:24:44 +0900259 }
260
Daniel Park81a61a12016-02-26 08:24:44 +0900261 }
262
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700263 private class InternalPacketProcessor implements PacketProcessor {
264
265 @Override
266 public void process(PacketContext context) {
267 if (context.isHandled()) {
268 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900269 }
270
271 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
272 .stream().map(OpenstackNode::intgBridge)
273 .collect(Collectors.toSet());
274
275 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700276 // return if the packet is not from gateway nodes
277 return;
278 }
279
280 InboundPacket pkt = context.inPacket();
281 Ethernet ethernet = pkt.parsed();
282 if (ethernet != null &&
283 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900284 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700285 }
286 }
287 }
288
daniel parkeeb8e042018-02-21 14:06:58 +0900289 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900290 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900291 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900292 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900293 .flatMap(osPort -> osPort.getFixedIps().stream())
294 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900295 }
Jian Li60312252018-05-10 18:40:32 +0900296
297 // FIXME: need to find a way to invoke this method during node initialization
298 private void initFloatingIpMacMap() {
299 osRouterService.floatingIps().forEach(f -> {
300 if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
301 Port port = osNetworkAdminService.port(f.getPortId());
302 if (port != null && port.getMacAddress() != null) {
303 floatingIpMacMap.put(f.getFloatingIpAddress(), port.getMacAddress());
304 }
305 }
306 });
307 }
308
309 /**
310 * Installs static ARP rules used in ARP BROAD_CAST mode.
311 * Note that, those rules will be only matched ARP_REQUEST packets,
312 * used for telling gateway node the mapped MAC address of requested IP,
313 * without the helps from controller.
314 *
315 * @param fip floating IP address
316 * @param install flow rule installation flag
317 */
318 private void setFloatingIpArpRule(NetFloatingIP fip, boolean install) {
319 if (arpMode.equals(ARP_BROADCAST_MODE)) {
320
321 if (fip == null) {
322 log.warn("Failed to set ARP broadcast rule for floating IP");
323 return;
324 }
325
326 String macString;
327
328 if (install) {
329 if (fip.getPortId() != null) {
330 macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
331 floatingIpMacMap.put(fip.getFloatingIpAddress(), macString);
332 } else {
333 log.trace("Unknown target ARP request for {}, ignore it",
334 fip.getFloatingIpAddress());
335 return;
336 }
337 } else {
338 macString = floatingIpMacMap.get(fip.getFloatingIpAddress());
339 }
340
341 MacAddress targetMac = MacAddress.valueOf(macString);
342
343 TrafficSelector selector = DefaultTrafficSelector.builder()
344 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
345 .matchArpOp(ARP.OP_REQUEST)
346 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
347 .build();
348
349 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
350 .setArpOp(ARP.OP_REPLY)
351 .setArpSha(targetMac)
352 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
353 .setOutput(PortNumber.IN_PORT)
354 .build();
355
356 osNodeService.completeNodes(GATEWAY).forEach(n ->
357 osFlowRuleService.setRule(
358 appId,
359 n.intgBridge(),
360 selector,
361 treatment,
362 PRIORITY_ARP_GATEWAY_RULE,
363 GW_COMMON_TABLE,
364 install
365 )
366 );
367
368 if (install) {
369 log.info("Install ARP Rule for Floating IP {}",
370 fip.getFloatingIpAddress());
371 } else {
372 log.info("Uninstall ARP Rule for Floating IP {}",
373 fip.getFloatingIpAddress());
374 }
375 }
376 }
377
378 /**
379 * An internal router event listener, intended to install/uninstall
380 * ARP rules for forwarding packets created from floating IPs.
381 */
382 private class InternalRouterEventListener implements OpenstackRouterListener {
383
384 @Override
385 public boolean isRelevant(OpenstackRouterEvent event) {
386 // do not allow to proceed without leadership
387 NodeId leader = leadershipService.getLeader(appId.name());
388 return Objects.equals(localNodeId, leader);
389 }
390
391 @Override
392 public void event(OpenstackRouterEvent event) {
393 switch (event.type()) {
394 case OPENSTACK_ROUTER_CREATED:
395 eventExecutor.execute(() ->
396 // add a router with external gateway
397 setFakeGatewayArpRule(event.subject(), true)
398 );
399 break;
400 case OPENSTACK_ROUTER_REMOVED:
401 eventExecutor.execute(() ->
402 // remove a router with external gateway
403 setFakeGatewayArpRule(event.subject(), false)
404 );
405 break;
406 case OPENSTACK_ROUTER_GATEWAY_ADDED:
407 eventExecutor.execute(() ->
408 // add a gateway manually after adding a router
409 setFakeGatewayArpRule(event.externalGateway(), true)
410 );
411 break;
412 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
413 eventExecutor.execute(() ->
414 // remove a gateway from an existing router
415 setFakeGatewayArpRule(event.externalGateway(), false)
416 );
417 break;
418 case OPENSTACK_FLOATING_IP_ASSOCIATED:
419 eventExecutor.execute(() ->
420 // associate a floating IP with an existing VM
421 setFloatingIpArpRule(event.floatingIp(), true)
422 );
423 break;
424 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
425 eventExecutor.execute(() ->
426 // disassociate a floating IP with the existing VM
427 setFloatingIpArpRule(event.floatingIp(), false)
428 );
429 break;
430 case OPENSTACK_FLOATING_IP_CREATED:
431 eventExecutor.execute(() -> {
432 NetFloatingIP osFip = event.floatingIp();
433
434 // during floating IP creation, if the floating IP is
435 // associated with any port of VM, then we will set
436 // floating IP related ARP rules to gateway node
437 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
438 setFloatingIpArpRule(osFip, true);
439 }
440 });
441 break;
442 case OPENSTACK_FLOATING_IP_REMOVED:
443 eventExecutor.execute(() -> {
444 NetFloatingIP osFip = event.floatingIp();
445
446 // during floating IP deletion, if the floating IP is
447 // still associated with any port of VM, then we will
448 // remove floating IP related ARP rules from gateway node
449 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
450 setFloatingIpArpRule(event.floatingIp(), false);
451 }
452 });
453 break;
454 default:
455 // do nothing for the other events
456 break;
457 }
458 }
459
460 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
461 if (arpMode.equals(ARP_BROADCAST_MODE)) {
462
463 if (extGw == null) {
464 return;
465 }
466
467 Optional<Subnet> subnet = osNetworkAdminService.subnets(
468 extGw.getNetworkId()).stream().findFirst();
469 if (!subnet.isPresent()) {
470 return;
471 }
472
473 String gateway = subnet.get().getGateway();
474
475 TrafficSelector selector = DefaultTrafficSelector.builder()
476 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
477 .matchArpOp(ARP.OP_REQUEST)
478 .matchArpTpa(Ip4Address.valueOf(gateway))
479 .build();
480
481 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
482 .setArpOp(ARP.OP_REPLY)
483 .setArpSha(MacAddress.valueOf(gatewayMac))
484 .setArpSpa(Ip4Address.valueOf(gateway))
485 .setOutput(PortNumber.IN_PORT)
486 .build();
487
488 osNodeService.completeNodes(GATEWAY).forEach(n ->
489 osFlowRuleService.setRule(
490 appId,
491 n.intgBridge(),
492 selector,
493 treatment,
494 PRIORITY_ARP_GATEWAY_RULE,
495 GW_COMMON_TABLE,
496 install
497 )
498 );
499
500 if (install) {
501 log.info("Install ARP Rule for Gateway {}", gateway);
502 } else {
503 log.info("Uninstall ARP Rule for Gateway {}", gateway);
504 }
505 }
506 }
507
508 private void setFakeGatewayArpRule(Router router, boolean install) {
509 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
510 }
511 }
512
513 /**
514 * An internal host event listener, intended to uninstall
515 * ARP rules during host removal. Note that this is only valid when users
516 * remove host without disassociating floating IP with existing VM.
517 */
518 private class InternalHostListener implements HostListener {
519
520 @Override
521 public boolean isRelevant(HostEvent event) {
522 Host host = event.subject();
523 if (!isValidHost(host)) {
524 log.debug("Invalid host detected, ignore it {}", host);
525 return false;
526 }
527 return true;
528 }
529
530 @Override
531 public void event(HostEvent event) {
532 InstancePort instPort = HostBasedInstancePort.of(event.subject());
533 switch (event.type()) {
534 case HOST_REMOVED:
535 removeArpRuleByInstancePort(instPort);
536 break;
537 case HOST_UPDATED:
538 case HOST_ADDED:
539 default:
540 break;
541 }
542 }
543
544 private void removeArpRuleByInstancePort(InstancePort port) {
545 Set<NetFloatingIP> ips = osRouterService.floatingIps();
546 for (NetFloatingIP fip : ips) {
547 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
548 continue;
549 }
550 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
551 continue;
552 }
553 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
554 eventExecutor.execute(() ->
555 setFloatingIpArpRule(fip, false)
556 );
557 }
558 }
559 }
560
561 // TODO: should be extracted as an utility helper method sooner
562 private boolean isValidHost(Host host) {
563 return !host.ipAddresses().isEmpty() &&
564 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
565 host.annotations().value(ANNOTATION_PORT_ID) != null;
566 }
567 }
Daniel Park81a61a12016-02-26 08:24:44 +0900568}