blob: 42c1c77ad7a7aa08852de7400b07f62afe9a1898 [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;
Jian Lif96685c2018-05-21 14:14:16 +090063import org.onosproject.openstacknode.api.OpenstackNodeEvent;
64import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090065import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li60312252018-05-10 18:40:32 +090066import org.openstack4j.model.network.ExternalGateway;
daniel parkeeb8e042018-02-21 14:06:58 +090067import org.openstack4j.model.network.NetFloatingIP;
Jian Li60312252018-05-10 18:40:32 +090068import org.openstack4j.model.network.Port;
69import org.openstack4j.model.network.Router;
70import org.openstack4j.model.network.Subnet;
71import org.osgi.service.component.ComponentContext;
Daniel Park81a61a12016-02-26 08:24:44 +090072import org.slf4j.Logger;
73
74import java.nio.ByteBuffer;
Jian Li60312252018-05-10 18:40:32 +090075import java.util.Dictionary;
76import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090077import java.util.Objects;
Jian Li60312252018-05-10 18:40:32 +090078import java.util.Optional;
Hyunsun Moon0d457362017-06-27 17:19:41 +090079import java.util.Set;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070080import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090081import java.util.stream.Collectors;
Daniel Park81a61a12016-02-26 08:24:44 +090082
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070083import static java.util.concurrent.Executors.newSingleThreadExecutor;
84import static org.onlab.util.Tools.groupedThreads;
Jian Li60312252018-05-10 18:40:32 +090085import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
86import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
87import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_ARP_MODE_STR;
88import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
Jian Lif96685c2018-05-21 14:14:16 +090089import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
Jian Li60312252018-05-10 18:40:32 +090090import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
91import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Lif96685c2018-05-21 14:14:16 +090092import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li60312252018-05-10 18:40:32 +090093import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
94import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
95import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Hyunsun Moon0d457362017-06-27 17:19:41 +090096import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Daniel Park81a61a12016-02-26 08:24:44 +090097import static org.slf4j.LoggerFactory.getLogger;
98
99/**
Hyunsun Moon44aac662017-02-18 02:07:01 +0900100 * Handle ARP requests from gateway nodes.
Daniel Park81a61a12016-02-26 08:24:44 +0900101 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700102@Component(immediate = true)
Daniel Park81a61a12016-02-26 08:24:44 +0900103public class OpenstackRoutingArpHandler {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900104
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700105 private final Logger log = getLogger(getClass());
Daniel Park81a61a12016-02-26 08:24:44 +0900106
Hyunsun Moon44aac662017-02-18 02:07:01 +0900107 private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
108 private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
Jian Li60312252018-05-10 18:40:32 +0900109 private static final String ARP_MODE = "arpMode";
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900113
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected PacketService packetService;
Daniel Park81a61a12016-02-26 08:24:44 +0900116
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel park32b42202018-03-14 16:53:44 +0900118 protected OpenstackNetworkAdminService osNetworkAdminService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700119
Hyunsun Moon44aac662017-02-18 02:07:01 +0900120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parkeeb8e042018-02-21 14:06:58 +0900121 protected OpenstackRouterService osRouterService;
122
daniel parkeeb8e042018-02-21 14:06:58 +0900123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
daniel parke49eb382017-04-05 16:48:28 +0900124 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125
Jian Li60312252018-05-10 18:40:32 +0900126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected ClusterService clusterService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected LeadershipService leadershipService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected OpenstackFlowRuleService osFlowRuleService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected ComponentConfigService configService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected HostService hostService;
140
141 // TODO: need to find a way to unify aprMode and gatewayMac variables with
142 // that in SwitchingArpHandler
143 @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
144 label = "ARP processing mode, proxy (default) | broadcast ")
145 protected String arpMode = DEFAULT_ARP_MODE_STR;
146
147 protected String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
148
149 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
150 private final HostListener hostListener = new InternalHostListener();
Jian Lif96685c2018-05-21 14:14:16 +0900151 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
Jian Li60312252018-05-10 18:40:32 +0900152
153 private ApplicationId appId;
154 private NodeId localNodeId;
155 private Map<String, String> floatingIpMacMap = Maps.newConcurrentMap();
156
Hyunsun Moon44aac662017-02-18 02:07:01 +0900157 private final ExecutorService eventExecutor = newSingleThreadExecutor(
158 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700159
Hyunsun Moon0d457362017-06-27 17:19:41 +0900160 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700161
162 @Activate
163 protected void activate() {
Jian Li60312252018-05-10 18:40:32 +0900164 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
165 configService.registerProperties(getClass());
166 localNodeId = clusterService.getLocalNode().id();
167 osRouterService.addListener(osRouterListener);
168 hostService.addListener(hostListener);
Jian Lif96685c2018-05-21 14:14:16 +0900169 osNodeService.addListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900170 leadershipService.runForLeadership(appId.name());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700171 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
172 log.info("Started");
Daniel Park81a61a12016-02-26 08:24:44 +0900173 }
174
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700175 @Deactivate
176 protected void deactivate() {
177 packetService.removeProcessor(packetProcessor);
Jian Li60312252018-05-10 18:40:32 +0900178 hostService.removeListener(hostListener);
179 osRouterService.removeListener(osRouterListener);
Jian Lif96685c2018-05-21 14:14:16 +0900180 osNodeService.removeListener(osNodeListener);
Jian Li60312252018-05-10 18:40:32 +0900181 leadershipService.withdraw(appId.name());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900182 eventExecutor.shutdown();
Jian Li60312252018-05-10 18:40:32 +0900183 configService.unregisterProperties(getClass(), false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700184 log.info("Stopped");
Daniel Park81a61a12016-02-26 08:24:44 +0900185 }
186
Jian Li60312252018-05-10 18:40:32 +0900187 // TODO: need to find a way to unify aprMode and gatewayMac variables with
188 // that in SwitchingArpHandler
189 @Modified
190 void modified(ComponentContext context) {
191 Dictionary<?, ?> properties = context.getProperties();
192 String updateArpMode;
193
194 updateArpMode = Tools.get(properties, ARP_MODE);
195 if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
196 arpMode = updateArpMode;
197 }
198
199 log.info("Modified");
200 }
201
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700202 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Daniel Park81a61a12016-02-26 08:24:44 +0900203 ARP arp = (ARP) ethernet.getPayload();
Jian Li60312252018-05-10 18:40:32 +0900204
205 if (arp.getOpCode() == ARP.OP_REQUEST && arpMode.equals(ARP_PROXY_MODE)) {
daniel parkb5817102018-02-15 00:18:51 +0900206 if (log.isTraceEnabled()) {
207 log.trace("ARP request received from {} for {}",
208 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
209 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
210 }
211
212 IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900213
214 MacAddress targetMac = null;
215
216 NetFloatingIP floatingIP = osRouterService.floatingIps().stream()
217 .filter(ip -> ip.getFloatingIpAddress().equals(targetIp.toString()))
218 .findAny().orElse(null);
219
daniel park576969a2018-03-09 07:07:41 +0900220 //In case target ip is for associated floating ip, sets target mac to vm's.
daniel parkeeb8e042018-02-21 14:06:58 +0900221 if (floatingIP != null && floatingIP.getPortId() != null) {
Jian Li60312252018-05-10 18:40:32 +0900222 targetMac = MacAddress.valueOf(osNetworkAdminService.port(
223 floatingIP.getPortId()).getMacAddress());
daniel parkeeb8e042018-02-21 14:06:58 +0900224 }
225
226 if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
227 targetMac = Constants.DEFAULT_GATEWAY_MAC;
228 }
229
230 if (targetMac == null) {
daniel parkb5817102018-02-15 00:18:51 +0900231 log.trace("Unknown target ARP request for {}, ignore it", targetIp);
232 return;
233 }
234
daniel parkb5817102018-02-15 00:18:51 +0900235 Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
236 targetMac, ethernet);
237
daniel park576969a2018-03-09 07:07:41 +0900238
daniel parkb5817102018-02-15 00:18:51 +0900239 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel park576969a2018-03-09 07:07:41 +0900240 .setOutput(context.inPacket().receivedFrom().port()).build();
daniel parkb5817102018-02-15 00:18:51 +0900241
242 packetService.emit(new DefaultOutboundPacket(
243 context.inPacket().receivedFrom().deviceId(),
244 treatment,
245 ByteBuffer.wrap(ethReply.serialize())));
246
247 context.block();
Jian Li60312252018-05-10 18:40:32 +0900248 }
249
250 if (arp.getOpCode() == ARP.OP_REPLY) {
daniel parkb5817102018-02-15 00:18:51 +0900251 PortNumber receivedPortNum = context.inPacket().receivedFrom().port();
252 log.debug("ARP reply ip: {}, mac: {}",
253 Ip4Address.valueOf(arp.getSenderProtocolAddress()),
254 MacAddress.valueOf(arp.getSenderHardwareAddress()));
255 try {
256 if (receivedPortNum.equals(
Jian Li60312252018-05-10 18:40:32 +0900257 osNodeService.node(context.inPacket().receivedFrom()
258 .deviceId()).uplinkPortNum())) {
daniel park32b42202018-03-14 16:53:44 +0900259 osNetworkAdminService.updateExternalPeerRouterMac(
daniel parkb5817102018-02-15 00:18:51 +0900260 Ip4Address.valueOf(arp.getSenderProtocolAddress()),
261 MacAddress.valueOf(arp.getSenderHardwareAddress()));
262 }
263 } catch (Exception e) {
264 log.error("Exception occurred because of {}", e.toString());
265 }
Daniel Park81a61a12016-02-26 08:24:44 +0900266 }
267
Daniel Park81a61a12016-02-26 08:24:44 +0900268 }
269
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700270 private class InternalPacketProcessor implements PacketProcessor {
271
272 @Override
273 public void process(PacketContext context) {
274 if (context.isHandled()) {
275 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900276 }
277
278 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
279 .stream().map(OpenstackNode::intgBridge)
280 .collect(Collectors.toSet());
281
282 if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700283 // return if the packet is not from gateway nodes
284 return;
285 }
286
287 InboundPacket pkt = context.inPacket();
288 Ethernet ethernet = pkt.parsed();
289 if (ethernet != null &&
290 ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900291 eventExecutor.execute(() -> processArpPacket(context, ethernet));
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700292 }
293 }
294 }
295
daniel parkeeb8e042018-02-21 14:06:58 +0900296 private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
daniel park32b42202018-03-14 16:53:44 +0900297 return osNetworkAdminService.ports().stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900298 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
daniel parkeeb8e042018-02-21 14:06:58 +0900299 DEVICE_OWNER_ROUTER_GW))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900300 .flatMap(osPort -> osPort.getFixedIps().stream())
301 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900302 }
Jian Li60312252018-05-10 18:40:32 +0900303
304 // FIXME: need to find a way to invoke this method during node initialization
305 private void initFloatingIpMacMap() {
306 osRouterService.floatingIps().forEach(f -> {
307 if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
308 Port port = osNetworkAdminService.port(f.getPortId());
309 if (port != null && port.getMacAddress() != null) {
310 floatingIpMacMap.put(f.getFloatingIpAddress(), port.getMacAddress());
311 }
312 }
313 });
314 }
315
316 /**
317 * Installs static ARP rules used in ARP BROAD_CAST mode.
318 * Note that, those rules will be only matched ARP_REQUEST packets,
319 * used for telling gateway node the mapped MAC address of requested IP,
320 * without the helps from controller.
321 *
322 * @param fip floating IP address
323 * @param install flow rule installation flag
324 */
325 private void setFloatingIpArpRule(NetFloatingIP fip, boolean install) {
326 if (arpMode.equals(ARP_BROADCAST_MODE)) {
327
328 if (fip == null) {
329 log.warn("Failed to set ARP broadcast rule for floating IP");
330 return;
331 }
332
333 String macString;
334
335 if (install) {
336 if (fip.getPortId() != null) {
337 macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
338 floatingIpMacMap.put(fip.getFloatingIpAddress(), macString);
339 } else {
340 log.trace("Unknown target ARP request for {}, ignore it",
341 fip.getFloatingIpAddress());
342 return;
343 }
344 } else {
345 macString = floatingIpMacMap.get(fip.getFloatingIpAddress());
346 }
347
348 MacAddress targetMac = MacAddress.valueOf(macString);
349
350 TrafficSelector selector = DefaultTrafficSelector.builder()
351 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
352 .matchArpOp(ARP.OP_REQUEST)
353 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
354 .build();
355
356 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
357 .setArpOp(ARP.OP_REPLY)
358 .setArpSha(targetMac)
359 .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
360 .setOutput(PortNumber.IN_PORT)
361 .build();
362
363 osNodeService.completeNodes(GATEWAY).forEach(n ->
364 osFlowRuleService.setRule(
365 appId,
366 n.intgBridge(),
367 selector,
368 treatment,
369 PRIORITY_ARP_GATEWAY_RULE,
370 GW_COMMON_TABLE,
371 install
372 )
373 );
374
375 if (install) {
376 log.info("Install ARP Rule for Floating IP {}",
377 fip.getFloatingIpAddress());
378 } else {
379 log.info("Uninstall ARP Rule for Floating IP {}",
380 fip.getFloatingIpAddress());
381 }
382 }
383 }
384
385 /**
386 * An internal router event listener, intended to install/uninstall
387 * ARP rules for forwarding packets created from floating IPs.
388 */
389 private class InternalRouterEventListener implements OpenstackRouterListener {
390
391 @Override
392 public boolean isRelevant(OpenstackRouterEvent event) {
393 // do not allow to proceed without leadership
394 NodeId leader = leadershipService.getLeader(appId.name());
395 return Objects.equals(localNodeId, leader);
396 }
397
398 @Override
399 public void event(OpenstackRouterEvent event) {
400 switch (event.type()) {
401 case OPENSTACK_ROUTER_CREATED:
402 eventExecutor.execute(() ->
403 // add a router with external gateway
404 setFakeGatewayArpRule(event.subject(), true)
405 );
406 break;
407 case OPENSTACK_ROUTER_REMOVED:
408 eventExecutor.execute(() ->
409 // remove a router with external gateway
410 setFakeGatewayArpRule(event.subject(), false)
411 );
412 break;
413 case OPENSTACK_ROUTER_GATEWAY_ADDED:
414 eventExecutor.execute(() ->
415 // add a gateway manually after adding a router
416 setFakeGatewayArpRule(event.externalGateway(), true)
417 );
418 break;
419 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
420 eventExecutor.execute(() ->
421 // remove a gateway from an existing router
422 setFakeGatewayArpRule(event.externalGateway(), false)
423 );
424 break;
425 case OPENSTACK_FLOATING_IP_ASSOCIATED:
426 eventExecutor.execute(() ->
427 // associate a floating IP with an existing VM
428 setFloatingIpArpRule(event.floatingIp(), true)
429 );
430 break;
431 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
432 eventExecutor.execute(() ->
433 // disassociate a floating IP with the existing VM
434 setFloatingIpArpRule(event.floatingIp(), false)
435 );
436 break;
437 case OPENSTACK_FLOATING_IP_CREATED:
438 eventExecutor.execute(() -> {
439 NetFloatingIP osFip = event.floatingIp();
440
441 // during floating IP creation, if the floating IP is
442 // associated with any port of VM, then we will set
443 // floating IP related ARP rules to gateway node
444 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
445 setFloatingIpArpRule(osFip, true);
446 }
447 });
448 break;
449 case OPENSTACK_FLOATING_IP_REMOVED:
450 eventExecutor.execute(() -> {
451 NetFloatingIP osFip = event.floatingIp();
452
453 // during floating IP deletion, if the floating IP is
454 // still associated with any port of VM, then we will
455 // remove floating IP related ARP rules from gateway node
456 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
457 setFloatingIpArpRule(event.floatingIp(), false);
458 }
459 });
460 break;
461 default:
462 // do nothing for the other events
463 break;
464 }
465 }
466
467 private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
468 if (arpMode.equals(ARP_BROADCAST_MODE)) {
469
470 if (extGw == null) {
471 return;
472 }
473
474 Optional<Subnet> subnet = osNetworkAdminService.subnets(
475 extGw.getNetworkId()).stream().findFirst();
476 if (!subnet.isPresent()) {
477 return;
478 }
479
480 String gateway = subnet.get().getGateway();
481
482 TrafficSelector selector = DefaultTrafficSelector.builder()
483 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
484 .matchArpOp(ARP.OP_REQUEST)
485 .matchArpTpa(Ip4Address.valueOf(gateway))
486 .build();
487
488 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
489 .setArpOp(ARP.OP_REPLY)
490 .setArpSha(MacAddress.valueOf(gatewayMac))
491 .setArpSpa(Ip4Address.valueOf(gateway))
492 .setOutput(PortNumber.IN_PORT)
493 .build();
494
495 osNodeService.completeNodes(GATEWAY).forEach(n ->
496 osFlowRuleService.setRule(
497 appId,
498 n.intgBridge(),
499 selector,
500 treatment,
501 PRIORITY_ARP_GATEWAY_RULE,
502 GW_COMMON_TABLE,
503 install
504 )
505 );
506
507 if (install) {
508 log.info("Install ARP Rule for Gateway {}", gateway);
509 } else {
510 log.info("Uninstall ARP Rule for Gateway {}", gateway);
511 }
512 }
513 }
514
515 private void setFakeGatewayArpRule(Router router, boolean install) {
516 setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
517 }
518 }
519
520 /**
521 * An internal host event listener, intended to uninstall
522 * ARP rules during host removal. Note that this is only valid when users
523 * remove host without disassociating floating IP with existing VM.
524 */
525 private class InternalHostListener implements HostListener {
526
527 @Override
528 public boolean isRelevant(HostEvent event) {
529 Host host = event.subject();
530 if (!isValidHost(host)) {
531 log.debug("Invalid host detected, ignore it {}", host);
532 return false;
533 }
534 return true;
535 }
536
537 @Override
538 public void event(HostEvent event) {
539 InstancePort instPort = HostBasedInstancePort.of(event.subject());
540 switch (event.type()) {
541 case HOST_REMOVED:
542 removeArpRuleByInstancePort(instPort);
543 break;
544 case HOST_UPDATED:
545 case HOST_ADDED:
546 default:
547 break;
548 }
549 }
550
551 private void removeArpRuleByInstancePort(InstancePort port) {
552 Set<NetFloatingIP> ips = osRouterService.floatingIps();
553 for (NetFloatingIP fip : ips) {
554 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
555 continue;
556 }
557 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
558 continue;
559 }
560 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
561 eventExecutor.execute(() ->
562 setFloatingIpArpRule(fip, false)
563 );
564 }
565 }
566 }
567
568 // TODO: should be extracted as an utility helper method sooner
569 private boolean isValidHost(Host host) {
570 return !host.ipAddresses().isEmpty() &&
571 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
572 host.annotations().value(ANNOTATION_PORT_ID) != null;
573 }
574 }
Jian Lif96685c2018-05-21 14:14:16 +0900575
576 private class InternalNodeEventListener implements OpenstackNodeListener {
577
578 @Override
579 public boolean isRelevant(OpenstackNodeEvent event) {
580 // do not allow to proceed without leadership
581 NodeId leader = leadershipService.getLeader(appId.name());
582 return Objects.equals(localNodeId, leader);
583 }
584
585 @Override
586 public void event(OpenstackNodeEvent event) {
587 OpenstackNode osNode = event.subject();
588 switch (event.type()) {
589 case OPENSTACK_NODE_COMPLETE:
590 if (osNode.type().equals(GATEWAY)) {
591 setDefaultArpRule(osNode, true);
592 }
593 break;
594 case OPENSTACK_NODE_INCOMPLETE:
595 if (osNode.type().equals(GATEWAY)) {
596 setDefaultArpRule(osNode, false);
597 }
598 break;
599 default:
600 break;
601 }
602 }
603
604 private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
605 switch (arpMode) {
606 case ARP_PROXY_MODE:
607 setDefaultArpRuleForProxyMode(osNode, install);
608 break;
609 case ARP_BROADCAST_MODE:
610 setDefaultArpRuleForBroadcastMode(osNode, install);
611 break;
612 default:
613 log.warn("Invalid ARP mode {}. Please use either " +
614 "broadcast or proxy mode.", arpMode);
615 break;
616 }
617 }
618
619 private void setDefaultArpRuleForProxyMode(OpenstackNode osNode, boolean install) {
620 TrafficSelector selector = DefaultTrafficSelector.builder()
621 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
622 .build();
623
624 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
625 .punt()
626 .build();
627
628 osFlowRuleService.setRule(
629 appId,
630 osNode.intgBridge(),
631 selector,
632 treatment,
633 PRIORITY_ARP_CONTROL_RULE,
634 DHCP_ARP_TABLE,
635 install
636 );
637 }
638
639 private void setDefaultArpRuleForBroadcastMode(OpenstackNode osNode, boolean install) {
640 // we only match ARP_REPLY in gateway node, because controller
641 // somehow need to process ARP_REPLY which is issued from
642 // external router...
643 TrafficSelector selector = DefaultTrafficSelector.builder()
644 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
645 .matchArpOp(ARP.OP_REPLY)
646 .build();
647
648 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
649 .punt()
650 .build();
651
652 osFlowRuleService.setRule(
653 appId,
654 osNode.intgBridge(),
655 selector,
656 treatment,
657 PRIORITY_ARP_CONTROL_RULE,
658 DHCP_ARP_TABLE,
659 install
660 );
661 }
662 }
Daniel Park81a61a12016-02-26 08:24:44 +0900663}